mirror of
https://github.com/zhigang1992/react-native.git
synced 2026-01-12 22:50:10 +08:00
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
This commit is contained in:
committed by
Facebook Github Bot
parent
3e4a8e35fe
commit
b10da79fb4
@@ -27,6 +27,7 @@ rn_xplat_cxx_library(
|
||||
exported_headers = subdir_glob(
|
||||
[
|
||||
("", "*.h"),
|
||||
("stubs", "*.h"),
|
||||
],
|
||||
prefix = "react/mounting",
|
||||
),
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
|
||||
#include "ShadowTree.h"
|
||||
|
||||
#include <glog/logging.h>
|
||||
|
||||
#include <react/components/root/RootComponentDescriptor.h>
|
||||
#include <react/core/LayoutContext.h>
|
||||
#include <react/core/LayoutPrimitives.h>
|
||||
@@ -194,6 +196,20 @@ bool ShadowTree::tryCommit(
|
||||
if (revision) {
|
||||
*revision = revision_;
|
||||
}
|
||||
|
||||
#ifdef RN_SHADOW_TREE_INTROSPECTION
|
||||
stubViewTree_.mutate(mutations);
|
||||
auto stubViewTree = stubViewTreeFromShadowNode(*rootShadowNode_);
|
||||
if (stubViewTree_ != stubViewTree) {
|
||||
LOG(ERROR) << "Old tree:"
|
||||
<< "\n"
|
||||
<< oldRootShadowNode->getDebugDescription() << "\n";
|
||||
LOG(ERROR) << "New tree:"
|
||||
<< "\n"
|
||||
<< newRootShadowNode->getDebugDescription() << "\n";
|
||||
assert(false);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
emitLayoutEvents(mutations);
|
||||
|
||||
@@ -5,6 +5,10 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef DEBUG
|
||||
#define RN_SHADOW_TREE_INTROSPECTION
|
||||
#endif
|
||||
|
||||
#include <better/mutex.h>
|
||||
#include <memory>
|
||||
|
||||
@@ -16,6 +20,10 @@
|
||||
#include <react/mounting/ShadowTreeDelegate.h>
|
||||
#include <react/mounting/ShadowViewMutation.h>
|
||||
|
||||
#ifdef RN_SHADOW_TREE_INTROSPECTION
|
||||
#include <react/mounting/stubs.h>
|
||||
#endif
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
@@ -87,6 +95,10 @@ class ShadowTree final {
|
||||
mutable SharedRootShadowNode rootShadowNode_; // Protected by `commitMutex_`.
|
||||
mutable int revision_{1}; // Protected by `commitMutex_`.
|
||||
ShadowTreeDelegate const *delegate_;
|
||||
|
||||
#ifdef RN_SHADOW_TREE_INTROSPECTION
|
||||
mutable StubViewTree stubViewTree_; // Protected by `commitMutex_`.
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace react
|
||||
|
||||
31
ReactCommon/fabric/mounting/stubs/StubView.cpp
Normal file
31
ReactCommon/fabric/mounting/stubs/StubView.cpp
Normal file
@@ -0,0 +1,31 @@
|
||||
// Copyright (c) Facebook, Inc. and its affiliates.
|
||||
|
||||
// This source code is licensed under the MIT license found in the
|
||||
// LICENSE file in the root directory of this source tree.
|
||||
|
||||
#include "StubView.h"
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
void StubView::update(ShadowView const &shadowView) {
|
||||
componentName = shadowView.componentName;
|
||||
componentHandle = shadowView.componentHandle;
|
||||
tag = shadowView.tag;
|
||||
props = shadowView.props;
|
||||
eventEmitter = shadowView.eventEmitter;
|
||||
layoutMetrics = shadowView.layoutMetrics;
|
||||
state = shadowView.state;
|
||||
}
|
||||
|
||||
bool operator==(StubView const &lhs, StubView const &rhs) {
|
||||
return std::tie(lhs.props, lhs.layoutMetrics) ==
|
||||
std::tie(rhs.props, rhs.layoutMetrics);
|
||||
}
|
||||
|
||||
bool operator!=(StubView const &lhs, StubView const &rhs) {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
} // namespace react
|
||||
} // namespace facebook
|
||||
41
ReactCommon/fabric/mounting/stubs/StubView.h
Normal file
41
ReactCommon/fabric/mounting/stubs/StubView.h
Normal file
@@ -0,0 +1,41 @@
|
||||
// Copyright (c) Facebook, Inc. and its affiliates.
|
||||
|
||||
// This source code is licensed under the MIT license found in the
|
||||
// LICENSE file in the root directory of this source tree.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include <react/core/LayoutMetrics.h>
|
||||
#include <react/core/State.h>
|
||||
#include <react/mounting/ShadowView.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
class StubView final {
|
||||
public:
|
||||
using Shared = std::shared_ptr<StubView>;
|
||||
|
||||
StubView() = default;
|
||||
StubView(StubView const &stubView) = default;
|
||||
|
||||
void update(ShadowView const &shadowView);
|
||||
|
||||
ComponentName componentName;
|
||||
ComponentHandle componentHandle;
|
||||
Tag tag;
|
||||
SharedProps props;
|
||||
SharedEventEmitter eventEmitter;
|
||||
LayoutMetrics layoutMetrics;
|
||||
State::Shared state;
|
||||
std::vector<StubView::Shared> children;
|
||||
};
|
||||
|
||||
bool operator==(StubView const &lhs, StubView const &rhs);
|
||||
bool operator!=(StubView const &lhs, StubView const &rhs);
|
||||
|
||||
} // namespace react
|
||||
} // namespace facebook
|
||||
104
ReactCommon/fabric/mounting/stubs/StubViewTree.cpp
Normal file
104
ReactCommon/fabric/mounting/stubs/StubViewTree.cpp
Normal file
@@ -0,0 +1,104 @@
|
||||
// Copyright (c) Facebook, Inc. and its affiliates.
|
||||
|
||||
// This source code is licensed under the MIT license found in the
|
||||
// LICENSE file in the root directory of this source tree.
|
||||
|
||||
#include "StubViewTree.h"
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
StubViewTree::StubViewTree(ShadowView const &shadowView) {
|
||||
auto view = std::make_shared<StubView>();
|
||||
view->update(shadowView);
|
||||
registry[shadowView.tag] = view;
|
||||
}
|
||||
|
||||
void StubViewTree::mutate(ShadowViewMutationList const &mutations) {
|
||||
for (auto const &mutation : mutations) {
|
||||
switch (mutation.type) {
|
||||
case ShadowViewMutation::Create: {
|
||||
assert(mutation.parentShadowView == ShadowView{});
|
||||
assert(mutation.oldChildShadowView == ShadowView{});
|
||||
auto stubView = std::make_shared<StubView>();
|
||||
auto tag = mutation.newChildShadowView.tag;
|
||||
assert(registry.find(tag) == registry.end());
|
||||
registry[tag] = stubView;
|
||||
break;
|
||||
}
|
||||
|
||||
case ShadowViewMutation::Delete: {
|
||||
assert(mutation.parentShadowView == ShadowView{});
|
||||
assert(mutation.newChildShadowView == ShadowView{});
|
||||
auto tag = mutation.oldChildShadowView.tag;
|
||||
assert(registry.find(tag) != registry.end());
|
||||
registry.erase(tag);
|
||||
break;
|
||||
}
|
||||
|
||||
case ShadowViewMutation::Insert: {
|
||||
assert(mutation.oldChildShadowView == ShadowView{});
|
||||
auto parentTag = mutation.parentShadowView.tag;
|
||||
assert(registry.find(parentTag) != registry.end());
|
||||
auto parentStubView = registry[parentTag];
|
||||
auto childTag = mutation.newChildShadowView.tag;
|
||||
assert(registry.find(childTag) != registry.end());
|
||||
auto childStubView = registry[childTag];
|
||||
childStubView->update(mutation.newChildShadowView);
|
||||
parentStubView->children.insert(
|
||||
parentStubView->children.begin() + mutation.index, childStubView);
|
||||
break;
|
||||
}
|
||||
|
||||
case ShadowViewMutation::Remove: {
|
||||
assert(mutation.newChildShadowView == ShadowView{});
|
||||
auto parentTag = mutation.parentShadowView.tag;
|
||||
assert(registry.find(parentTag) != registry.end());
|
||||
auto parentStubView = registry[parentTag];
|
||||
auto childTag = mutation.oldChildShadowView.tag;
|
||||
assert(registry.find(childTag) != registry.end());
|
||||
auto childStubView = registry[childTag];
|
||||
assert(
|
||||
parentStubView->children[mutation.index]->tag ==
|
||||
childStubView->tag);
|
||||
parentStubView->children.erase(
|
||||
parentStubView->children.begin() + mutation.index);
|
||||
break;
|
||||
}
|
||||
|
||||
case ShadowViewMutation::Update: {
|
||||
assert(
|
||||
mutation.newChildShadowView.tag == mutation.oldChildShadowView.tag);
|
||||
assert(
|
||||
registry.find(mutation.newChildShadowView.tag) != registry.end());
|
||||
auto stubView = registry[mutation.newChildShadowView.tag];
|
||||
stubView->update(mutation.newChildShadowView);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool operator==(StubViewTree const &lhs, StubViewTree const &rhs) {
|
||||
if (lhs.registry.size() != rhs.registry.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto const &pair : lhs.registry) {
|
||||
auto &lhsStubView = *lhs.registry.at(pair.first);
|
||||
auto &rhsStubView = *rhs.registry.at(pair.first);
|
||||
|
||||
if (lhsStubView != rhsStubView) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool operator!=(StubViewTree const &lhs, StubViewTree const &rhs) {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
} // namespace react
|
||||
} // namespace facebook
|
||||
31
ReactCommon/fabric/mounting/stubs/StubViewTree.h
Normal file
31
ReactCommon/fabric/mounting/stubs/StubViewTree.h
Normal file
@@ -0,0 +1,31 @@
|
||||
// Copyright (c) Facebook, Inc. and its affiliates.
|
||||
|
||||
// This source code is licensed under the MIT license found in the
|
||||
// LICENSE file in the root directory of this source tree.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <react/mounting/ShadowViewMutation.h>
|
||||
#include <react/mounting/StubView.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
class StubViewTree {
|
||||
public:
|
||||
StubViewTree() = default;
|
||||
StubViewTree(ShadowView const &shadowView);
|
||||
|
||||
void mutate(ShadowViewMutationList const &mutations);
|
||||
|
||||
std::unordered_map<Tag, StubView::Shared> registry{};
|
||||
};
|
||||
|
||||
bool operator==(StubViewTree const &lhs, StubViewTree const &rhs);
|
||||
bool operator!=(StubViewTree const &lhs, StubViewTree const &rhs);
|
||||
|
||||
} // namespace react
|
||||
} // namespace facebook
|
||||
30
ReactCommon/fabric/mounting/stubs/stubs.cpp
Normal file
30
ReactCommon/fabric/mounting/stubs/stubs.cpp
Normal file
@@ -0,0 +1,30 @@
|
||||
// Copyright (c) Facebook, Inc. and its affiliates.
|
||||
|
||||
// This source code is licensed under the MIT license found in the
|
||||
// LICENSE file in the root directory of this source tree.
|
||||
|
||||
#include "stubs.h"
|
||||
|
||||
#include <react/core/LayoutableShadowNode.h>
|
||||
#include <react/core/ShadowNodeFragment.h>
|
||||
#include <react/mounting/Differentiator.h>
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
StubViewTree stubViewTreeFromShadowNode(ShadowNode const &rootShadowNode) {
|
||||
auto emptyRootShadowNode = rootShadowNode.clone(
|
||||
ShadowNodeFragment{ShadowNodeFragment::tagPlaceholder(),
|
||||
ShadowNodeFragment::surfaceIdPlaceholder(),
|
||||
ShadowNodeFragment::propsPlaceholder(),
|
||||
ShadowNodeFragment::eventEmitterPlaceholder(),
|
||||
ShadowNode::emptySharedShadowNodeSharedList()});
|
||||
|
||||
auto stubViewTree = StubViewTree(ShadowView(*emptyRootShadowNode));
|
||||
stubViewTree.mutate(
|
||||
calculateShadowViewMutations(*emptyRootShadowNode, rootShadowNode));
|
||||
return stubViewTree;
|
||||
}
|
||||
|
||||
} // namespace react
|
||||
} // namespace facebook
|
||||
18
ReactCommon/fabric/mounting/stubs/stubs.h
Normal file
18
ReactCommon/fabric/mounting/stubs/stubs.h
Normal file
@@ -0,0 +1,18 @@
|
||||
// Copyright (c) Facebook, Inc. and its affiliates.
|
||||
|
||||
// This source code is licensed under the MIT license found in the
|
||||
// LICENSE file in the root directory of this source tree.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <react/core/ShadowNode.h>
|
||||
#include "StubView.h"
|
||||
#include "StubViewTree.h"
|
||||
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
StubViewTree stubViewTreeFromShadowNode(ShadowNode const &rootShadowNode);
|
||||
|
||||
} // namespace react
|
||||
} // namespace facebook
|
||||
Reference in New Issue
Block a user