Skip to content

Commit b10da79

Browse files
sherginfacebook-github-bot
authored andcommitted
Fabric: Introspection (self testing in debug mode) in ShadowTree
Summary: We suspect that we have some error in diffing algorithm that cause some crashes in mounting layer, so we decided to write a comprehensive unit tests for that. Writing them we realized that it would be cool to also enable that for normal app run in the debug more, so we can catch the problem in real environment when/if it happens. Reviewed By: mdvacca Differential Revision: D14587123 fbshipit-source-id: 6dcdf451b39489dec751cd6787de33f3b8ffb3fd
1 parent 3e4a8e3 commit b10da79

File tree

9 files changed

+284
-0
lines changed

9 files changed

+284
-0
lines changed

ReactCommon/fabric/mounting/BUCK

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ rn_xplat_cxx_library(
2727
exported_headers = subdir_glob(
2828
[
2929
("", "*.h"),
30+
("stubs", "*.h"),
3031
],
3132
prefix = "react/mounting",
3233
),

ReactCommon/fabric/mounting/ShadowTree.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55

66
#include "ShadowTree.h"
77

8+
#include <glog/logging.h>
9+
810
#include <react/components/root/RootComponentDescriptor.h>
911
#include <react/core/LayoutContext.h>
1012
#include <react/core/LayoutPrimitives.h>
@@ -194,6 +196,20 @@ bool ShadowTree::tryCommit(
194196
if (revision) {
195197
*revision = revision_;
196198
}
199+
200+
#ifdef RN_SHADOW_TREE_INTROSPECTION
201+
stubViewTree_.mutate(mutations);
202+
auto stubViewTree = stubViewTreeFromShadowNode(*rootShadowNode_);
203+
if (stubViewTree_ != stubViewTree) {
204+
LOG(ERROR) << "Old tree:"
205+
<< "\n"
206+
<< oldRootShadowNode->getDebugDescription() << "\n";
207+
LOG(ERROR) << "New tree:"
208+
<< "\n"
209+
<< newRootShadowNode->getDebugDescription() << "\n";
210+
assert(false);
211+
}
212+
#endif
197213
}
198214

199215
emitLayoutEvents(mutations);

ReactCommon/fabric/mounting/ShadowTree.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@
55

66
#pragma once
77

8+
#ifdef DEBUG
9+
#define RN_SHADOW_TREE_INTROSPECTION
10+
#endif
11+
812
#include <better/mutex.h>
913
#include <memory>
1014

@@ -16,6 +20,10 @@
1620
#include <react/mounting/ShadowTreeDelegate.h>
1721
#include <react/mounting/ShadowViewMutation.h>
1822

23+
#ifdef RN_SHADOW_TREE_INTROSPECTION
24+
#include <react/mounting/stubs.h>
25+
#endif
26+
1927
namespace facebook {
2028
namespace react {
2129

@@ -87,6 +95,10 @@ class ShadowTree final {
8795
mutable SharedRootShadowNode rootShadowNode_; // Protected by `commitMutex_`.
8896
mutable int revision_{1}; // Protected by `commitMutex_`.
8997
ShadowTreeDelegate const *delegate_;
98+
99+
#ifdef RN_SHADOW_TREE_INTROSPECTION
100+
mutable StubViewTree stubViewTree_; // Protected by `commitMutex_`.
101+
#endif
90102
};
91103

92104
} // namespace react
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Copyright (c) Facebook, Inc. and its affiliates.
2+
3+
// This source code is licensed under the MIT license found in the
4+
// LICENSE file in the root directory of this source tree.
5+
6+
#include "StubView.h"
7+
8+
namespace facebook {
9+
namespace react {
10+
11+
void StubView::update(ShadowView const &shadowView) {
12+
componentName = shadowView.componentName;
13+
componentHandle = shadowView.componentHandle;
14+
tag = shadowView.tag;
15+
props = shadowView.props;
16+
eventEmitter = shadowView.eventEmitter;
17+
layoutMetrics = shadowView.layoutMetrics;
18+
state = shadowView.state;
19+
}
20+
21+
bool operator==(StubView const &lhs, StubView const &rhs) {
22+
return std::tie(lhs.props, lhs.layoutMetrics) ==
23+
std::tie(rhs.props, rhs.layoutMetrics);
24+
}
25+
26+
bool operator!=(StubView const &lhs, StubView const &rhs) {
27+
return !(lhs == rhs);
28+
}
29+
30+
} // namespace react
31+
} // namespace facebook
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// Copyright (c) Facebook, Inc. and its affiliates.
2+
3+
// This source code is licensed under the MIT license found in the
4+
// LICENSE file in the root directory of this source tree.
5+
6+
#pragma once
7+
8+
#include <memory>
9+
#include <vector>
10+
11+
#include <react/core/LayoutMetrics.h>
12+
#include <react/core/State.h>
13+
#include <react/mounting/ShadowView.h>
14+
15+
namespace facebook {
16+
namespace react {
17+
18+
class StubView final {
19+
public:
20+
using Shared = std::shared_ptr<StubView>;
21+
22+
StubView() = default;
23+
StubView(StubView const &stubView) = default;
24+
25+
void update(ShadowView const &shadowView);
26+
27+
ComponentName componentName;
28+
ComponentHandle componentHandle;
29+
Tag tag;
30+
SharedProps props;
31+
SharedEventEmitter eventEmitter;
32+
LayoutMetrics layoutMetrics;
33+
State::Shared state;
34+
std::vector<StubView::Shared> children;
35+
};
36+
37+
bool operator==(StubView const &lhs, StubView const &rhs);
38+
bool operator!=(StubView const &lhs, StubView const &rhs);
39+
40+
} // namespace react
41+
} // namespace facebook
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
// Copyright (c) Facebook, Inc. and its affiliates.
2+
3+
// This source code is licensed under the MIT license found in the
4+
// LICENSE file in the root directory of this source tree.
5+
6+
#include "StubViewTree.h"
7+
8+
namespace facebook {
9+
namespace react {
10+
11+
StubViewTree::StubViewTree(ShadowView const &shadowView) {
12+
auto view = std::make_shared<StubView>();
13+
view->update(shadowView);
14+
registry[shadowView.tag] = view;
15+
}
16+
17+
void StubViewTree::mutate(ShadowViewMutationList const &mutations) {
18+
for (auto const &mutation : mutations) {
19+
switch (mutation.type) {
20+
case ShadowViewMutation::Create: {
21+
assert(mutation.parentShadowView == ShadowView{});
22+
assert(mutation.oldChildShadowView == ShadowView{});
23+
auto stubView = std::make_shared<StubView>();
24+
auto tag = mutation.newChildShadowView.tag;
25+
assert(registry.find(tag) == registry.end());
26+
registry[tag] = stubView;
27+
break;
28+
}
29+
30+
case ShadowViewMutation::Delete: {
31+
assert(mutation.parentShadowView == ShadowView{});
32+
assert(mutation.newChildShadowView == ShadowView{});
33+
auto tag = mutation.oldChildShadowView.tag;
34+
assert(registry.find(tag) != registry.end());
35+
registry.erase(tag);
36+
break;
37+
}
38+
39+
case ShadowViewMutation::Insert: {
40+
assert(mutation.oldChildShadowView == ShadowView{});
41+
auto parentTag = mutation.parentShadowView.tag;
42+
assert(registry.find(parentTag) != registry.end());
43+
auto parentStubView = registry[parentTag];
44+
auto childTag = mutation.newChildShadowView.tag;
45+
assert(registry.find(childTag) != registry.end());
46+
auto childStubView = registry[childTag];
47+
childStubView->update(mutation.newChildShadowView);
48+
parentStubView->children.insert(
49+
parentStubView->children.begin() + mutation.index, childStubView);
50+
break;
51+
}
52+
53+
case ShadowViewMutation::Remove: {
54+
assert(mutation.newChildShadowView == ShadowView{});
55+
auto parentTag = mutation.parentShadowView.tag;
56+
assert(registry.find(parentTag) != registry.end());
57+
auto parentStubView = registry[parentTag];
58+
auto childTag = mutation.oldChildShadowView.tag;
59+
assert(registry.find(childTag) != registry.end());
60+
auto childStubView = registry[childTag];
61+
assert(
62+
parentStubView->children[mutation.index]->tag ==
63+
childStubView->tag);
64+
parentStubView->children.erase(
65+
parentStubView->children.begin() + mutation.index);
66+
break;
67+
}
68+
69+
case ShadowViewMutation::Update: {
70+
assert(
71+
mutation.newChildShadowView.tag == mutation.oldChildShadowView.tag);
72+
assert(
73+
registry.find(mutation.newChildShadowView.tag) != registry.end());
74+
auto stubView = registry[mutation.newChildShadowView.tag];
75+
stubView->update(mutation.newChildShadowView);
76+
break;
77+
}
78+
}
79+
}
80+
}
81+
82+
bool operator==(StubViewTree const &lhs, StubViewTree const &rhs) {
83+
if (lhs.registry.size() != rhs.registry.size()) {
84+
return false;
85+
}
86+
87+
for (auto const &pair : lhs.registry) {
88+
auto &lhsStubView = *lhs.registry.at(pair.first);
89+
auto &rhsStubView = *rhs.registry.at(pair.first);
90+
91+
if (lhsStubView != rhsStubView) {
92+
return false;
93+
}
94+
}
95+
96+
return true;
97+
}
98+
99+
bool operator!=(StubViewTree const &lhs, StubViewTree const &rhs) {
100+
return !(lhs == rhs);
101+
}
102+
103+
} // namespace react
104+
} // namespace facebook
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Copyright (c) Facebook, Inc. and its affiliates.
2+
3+
// This source code is licensed under the MIT license found in the
4+
// LICENSE file in the root directory of this source tree.
5+
6+
#pragma once
7+
8+
#include <memory>
9+
#include <unordered_map>
10+
11+
#include <react/mounting/ShadowViewMutation.h>
12+
#include <react/mounting/StubView.h>
13+
14+
namespace facebook {
15+
namespace react {
16+
17+
class StubViewTree {
18+
public:
19+
StubViewTree() = default;
20+
StubViewTree(ShadowView const &shadowView);
21+
22+
void mutate(ShadowViewMutationList const &mutations);
23+
24+
std::unordered_map<Tag, StubView::Shared> registry{};
25+
};
26+
27+
bool operator==(StubViewTree const &lhs, StubViewTree const &rhs);
28+
bool operator!=(StubViewTree const &lhs, StubViewTree const &rhs);
29+
30+
} // namespace react
31+
} // namespace facebook
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Copyright (c) Facebook, Inc. and its affiliates.
2+
3+
// This source code is licensed under the MIT license found in the
4+
// LICENSE file in the root directory of this source tree.
5+
6+
#include "stubs.h"
7+
8+
#include <react/core/LayoutableShadowNode.h>
9+
#include <react/core/ShadowNodeFragment.h>
10+
#include <react/mounting/Differentiator.h>
11+
12+
namespace facebook {
13+
namespace react {
14+
15+
StubViewTree stubViewTreeFromShadowNode(ShadowNode const &rootShadowNode) {
16+
auto emptyRootShadowNode = rootShadowNode.clone(
17+
ShadowNodeFragment{ShadowNodeFragment::tagPlaceholder(),
18+
ShadowNodeFragment::surfaceIdPlaceholder(),
19+
ShadowNodeFragment::propsPlaceholder(),
20+
ShadowNodeFragment::eventEmitterPlaceholder(),
21+
ShadowNode::emptySharedShadowNodeSharedList()});
22+
23+
auto stubViewTree = StubViewTree(ShadowView(*emptyRootShadowNode));
24+
stubViewTree.mutate(
25+
calculateShadowViewMutations(*emptyRootShadowNode, rootShadowNode));
26+
return stubViewTree;
27+
}
28+
29+
} // namespace react
30+
} // namespace facebook
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Copyright (c) Facebook, Inc. and its affiliates.
2+
3+
// This source code is licensed under the MIT license found in the
4+
// LICENSE file in the root directory of this source tree.
5+
6+
#pragma once
7+
8+
#include <react/core/ShadowNode.h>
9+
#include "StubView.h"
10+
#include "StubViewTree.h"
11+
12+
namespace facebook {
13+
namespace react {
14+
15+
StubViewTree stubViewTreeFromShadowNode(ShadowNode const &rootShadowNode);
16+
17+
} // namespace react
18+
} // namespace facebook

0 commit comments

Comments
 (0)