Skip to content

Commit 6831a00

Browse files
andrewdacenkofacebook-github-bot
authored andcommitted
Move fantom into OSS
Summary: Changelog: [Internal] Moving fantom tester into OSS. Reviewed By: rubennorte Differential Revision: D76928252
1 parent 41d6aec commit 6831a00

22 files changed

+1425
-62
lines changed

private/react-native-fantom/runner/runner.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ import nullthrows from 'nullthrows';
4545
import path from 'path';
4646
import readline from 'readline';
4747

48-
const BUILD_OUTPUT_ROOT = path.resolve(__dirname, '..', 'build');
48+
const BUILD_OUTPUT_ROOT = path.resolve(__dirname, '..', 'build', 'js');
4949
fs.mkdirSync(BUILD_OUTPUT_ROOT, {recursive: true});
5050
const BUILD_OUTPUT_PATH = fs.mkdtempSync(
5151
path.join(BUILD_OUTPUT_ROOT, `run-${Date.now()}-`),
@@ -268,7 +268,7 @@ module.exports = async function runTest(
268268
testConfig.mode === FantomTestConfigMode.Optimized,
269269
),
270270
...getBuckOptionsForHermes(testConfig.hermesVariant),
271-
'//xplat/ReactNative/react-native-cxx/samples/tester:tester',
271+
'//xplat/js/react-native-github/private/react-native-fantom/tester:tester',
272272
'--',
273273
'--bundlePath',
274274
testConfig.mode === FantomTestConfigMode.DevelopmentWithSource

private/react-native-fantom/runner/warmup/warmup.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ function warmUpRNTesterCLI(isOptimizedMode: boolean): void {
110110
const buildRNTesterCommandResult = runBuck2Sync([
111111
'build',
112112
...getBuckModesForPlatform(isOptimizedMode),
113-
'//xplat/ReactNative/react-native-cxx/samples/tester:tester',
113+
'//xplat/js/react-native-github/private/react-native-fantom/tester:tester',
114114
]);
115115

116116
if (buildRNTesterCommandResult.status !== 0) {
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
#include "AppSettings.h"
9+
#include <folly/json/json.h>
10+
#include <gflags/gflags.h>
11+
#include <glog/logging.h>
12+
13+
static constexpr int DEFAULT_WINDOW_WIDTH = 1280;
14+
static constexpr int DEFAULT_WINDOW_HEIGHT = 720;
15+
16+
DEFINE_uint32(windowWidth, DEFAULT_WINDOW_WIDTH, "Application window width");
17+
DEFINE_uint32(windowHeight, DEFAULT_WINDOW_HEIGHT, "Application window height");
18+
DEFINE_string(bundlePath, "", "Default path to the application's bundle");
19+
DEFINE_string(
20+
featureFlags,
21+
"",
22+
"JSON representation of the common feature flags to set for the app");
23+
DEFINE_string(
24+
minLogLevel,
25+
"",
26+
"Minimum log level to be used by the app. One of: [info, warning, error, fatal]");
27+
28+
namespace facebook::react {
29+
30+
unsigned int AppSettings::windowWidth{DEFAULT_WINDOW_WIDTH};
31+
unsigned int AppSettings::windowHeight{DEFAULT_WINDOW_HEIGHT};
32+
std::string AppSettings::defaultBundlePath{};
33+
std::optional<folly::dynamic> AppSettings::dynamicFeatureFlags;
34+
int AppSettings::minLogLevel{google::GLOG_INFO};
35+
36+
void AppSettings::init(int argc, char** argv) {
37+
if (argc > 0 && argv != nullptr) {
38+
// Don't exit app on unknown flags, as some of those may be provided when
39+
// debugging via XCode:
40+
gflags::AllowCommandLineReparsing();
41+
gflags::ParseCommandLineFlags(&argc, &argv, false);
42+
}
43+
initInternal();
44+
}
45+
46+
void AppSettings::initInternal() {
47+
windowWidth = FLAGS_windowWidth;
48+
windowHeight = FLAGS_windowHeight;
49+
if (!FLAGS_featureFlags.empty()) {
50+
dynamicFeatureFlags = folly::parseJson(FLAGS_featureFlags);
51+
}
52+
if (!FLAGS_bundlePath.empty()) {
53+
defaultBundlePath = FLAGS_bundlePath;
54+
}
55+
if (!FLAGS_minLogLevel.empty()) {
56+
if (FLAGS_minLogLevel == "info") {
57+
minLogLevel = google::GLOG_INFO;
58+
} else if (FLAGS_minLogLevel == "warning") {
59+
minLogLevel = google::GLOG_WARNING;
60+
} else if (FLAGS_minLogLevel == "error") {
61+
minLogLevel = google::GLOG_ERROR;
62+
} else if (FLAGS_minLogLevel == "fatal") {
63+
minLogLevel = google::GLOG_FATAL;
64+
} else {
65+
LOG(ERROR) << "Invalid min log level: " << FLAGS_minLogLevel;
66+
}
67+
}
68+
}
69+
70+
} // namespace facebook::react
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
#include <folly/dynamic.h>
9+
10+
#include <optional>
11+
#include <string>
12+
13+
namespace facebook::react {
14+
class AppSettings {
15+
public:
16+
static unsigned int windowWidth;
17+
static unsigned int windowHeight;
18+
19+
// Minimum log level for the messages logged to glog/logging.
20+
static int minLogLevel;
21+
22+
static std::string defaultBundlePath;
23+
24+
static std::optional<folly::dynamic> dynamicFeatureFlags;
25+
26+
static void init(int argc, char* argv[]);
27+
28+
private:
29+
static void initInternal();
30+
};
31+
} // namespace facebook::react
Lines changed: 250 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,250 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
#include "NativeFantom.h"
9+
10+
#include <jsi/JSIDynamic.h>
11+
#include <react/bridging/Bridging.h>
12+
#include <react/renderer/components/modal/ModalHostViewShadowNode.h>
13+
#include <react/renderer/components/scrollview/ScrollViewShadowNode.h>
14+
#include <react/renderer/uimanager/UIManagerBinding.h>
15+
#include <iostream>
16+
17+
#include "TesterAppDelegate.h"
18+
19+
#include <jsi/instrumentation.h>
20+
#include "render/RenderFormatOptions.h"
21+
#include "render/RenderOutput.h"
22+
23+
namespace facebook::react {
24+
25+
NativeFantom::NativeFantom(
26+
TesterAppDelegate& appDelegate,
27+
std::shared_ptr<CallInvoker> jsInvoker)
28+
: NativeFantomCxxSpec<NativeFantom>(std::move(jsInvoker)),
29+
appDelegate_(appDelegate) {}
30+
31+
SurfaceId NativeFantom::startSurface(
32+
jsi::Runtime& runtime,
33+
double viewportWidth,
34+
double viewportHeight,
35+
double devicePixelRatio,
36+
double viewportOffsetX,
37+
double viewportOffsetY) {
38+
SurfaceId surfaceId = nextSurfaceId_;
39+
nextSurfaceId_ += 10;
40+
appDelegate_.startSurface(
41+
runtime,
42+
static_cast<float>(viewportWidth),
43+
static_cast<float>(viewportHeight),
44+
surfaceId,
45+
static_cast<float>(devicePixelRatio),
46+
static_cast<float>(viewportOffsetX),
47+
static_cast<float>(viewportOffsetY));
48+
return surfaceId;
49+
}
50+
51+
void NativeFantom::stopSurface(jsi::Runtime& /*runtime*/, SurfaceId surfaceId) {
52+
appDelegate_.stopSurface(surfaceId);
53+
}
54+
55+
void NativeFantom::produceFramesForDuration(
56+
jsi::Runtime& /*runtime*/,
57+
double milliseconds) {
58+
appDelegate_.produceFramesForDuration(milliseconds);
59+
}
60+
61+
void NativeFantom::flushMessageQueue(jsi::Runtime& /*runtime*/) {
62+
appDelegate_.flushMessageQueue();
63+
}
64+
65+
void NativeFantom::flushEventQueue(jsi::Runtime& /*runtime*/) {
66+
appDelegate_.onRender();
67+
}
68+
69+
void NativeFantom::validateEmptyMessageQueue(jsi::Runtime& /*runtime*/) {
70+
if (appDelegate_.hasPendingTasksInMessageQueue()) {
71+
throw std::runtime_error("MessageQueue is not empty");
72+
}
73+
}
74+
75+
std::vector<std::string> NativeFantom::takeMountingManagerLogs(
76+
jsi::Runtime& /*runtime*/,
77+
SurfaceId surfaceId) {
78+
return appDelegate_.mountingManager_->takeMountingLogs(surfaceId);
79+
}
80+
81+
std::string NativeFantom::getRenderedOutput(
82+
jsi::Runtime& /*runtime*/,
83+
SurfaceId surfaceId,
84+
NativeFantomGetRenderedOutputRenderFormatOptions options) {
85+
RenderFormatOptions formatOptions{
86+
options.includeRoot, options.includeLayoutMetrics};
87+
88+
auto viewTree = appDelegate_.mountingManager_->getViewTree(surfaceId);
89+
return RenderOutput::render(viewTree, formatOptions);
90+
}
91+
92+
void NativeFantom::reportTestSuiteResultsJSON(
93+
jsi::Runtime& /*runtime*/,
94+
std::string testSuiteResultsJSON) {
95+
std::cout << testSuiteResultsJSON << std::endl;
96+
}
97+
98+
jsi::Object NativeFantom::getDirectManipulationProps(
99+
jsi::Runtime& runtime,
100+
const ShadowNode::Shared& shadowNode) {
101+
auto props = appDelegate_.mountingManager_->getViewDirectManipulationProps(
102+
shadowNode->getTag());
103+
return facebook::jsi::valueFromDynamic(runtime, props).asObject(runtime);
104+
}
105+
106+
jsi::Object NativeFantom::getFabricUpdateProps(
107+
jsi::Runtime& runtime,
108+
const ShadowNode::Shared& shadowNode) {
109+
auto props = appDelegate_.mountingManager_->getViewFabricUpdateProps(
110+
shadowNode->getTag());
111+
return facebook::jsi::valueFromDynamic(runtime, props).asObject(runtime);
112+
}
113+
114+
void NativeFantom::enqueueNativeEvent(
115+
jsi::Runtime& /*runtime*/,
116+
ShadowNode::Shared shadowNode,
117+
std::string type,
118+
const std::optional<folly::dynamic>& payload,
119+
std::optional<RawEvent::Category> category,
120+
std::optional<bool> isUnique) {
121+
if (isUnique.value_or(false)) {
122+
shadowNode->getEventEmitter()->dispatchUniqueEvent(
123+
std::move(type), payload.value_or(folly::dynamic::object()));
124+
} else {
125+
shadowNode->getEventEmitter()->dispatchEvent(
126+
std::move(type),
127+
payload.value_or(folly::dynamic::object()),
128+
category.value_or(RawEvent::Category::Unspecified));
129+
}
130+
}
131+
132+
void NativeFantom::enqueueScrollEvent(
133+
jsi::Runtime& /*runtime*/,
134+
ShadowNode::Shared shadowNode,
135+
ScrollOptions options) {
136+
const auto* scrollViewShadowNode =
137+
dynamic_cast<const ScrollViewShadowNode*>(&*shadowNode);
138+
139+
if (scrollViewShadowNode == nullptr) {
140+
throw std::runtime_error(
141+
"enqueueScrollEvent() can only be called on <ScrollView />");
142+
}
143+
144+
auto point = Point{
145+
.x = options.x,
146+
.y = options.y,
147+
};
148+
149+
auto scrollEvent = ScrollEvent();
150+
151+
scrollEvent.contentOffset = point;
152+
scrollEvent.contentSize =
153+
scrollViewShadowNode->getStateData().getContentSize();
154+
scrollEvent.containerSize =
155+
scrollViewShadowNode->getLayoutMetrics().frame.size;
156+
scrollEvent.contentInset =
157+
scrollViewShadowNode->getConcreteProps().contentInset;
158+
scrollEvent.zoomScale = options.zoomScale.value_or(scrollEvent.zoomScale);
159+
160+
scrollViewShadowNode->getConcreteEventEmitter().onScroll(scrollEvent);
161+
162+
auto state =
163+
std::static_pointer_cast<const ScrollViewShadowNode::ConcreteState>(
164+
scrollViewShadowNode->getState());
165+
state->updateState(
166+
[point](const ScrollViewShadowNode::ConcreteState::Data& oldData)
167+
-> ScrollViewShadowNode::ConcreteState::SharedData {
168+
auto newData = oldData;
169+
newData.contentOffset = point;
170+
return std::make_shared<
171+
const ScrollViewShadowNode::ConcreteState::Data>(newData);
172+
});
173+
}
174+
175+
void NativeFantom::enqueueModalSizeUpdate(
176+
jsi::Runtime& /*runtime*/,
177+
ShadowNode::Shared shadowNode,
178+
double width,
179+
double height) {
180+
const auto* modalHostViewShadowNode =
181+
dynamic_cast<const ModalHostViewShadowNode*>(&*shadowNode);
182+
183+
if (modalHostViewShadowNode == nullptr) {
184+
throw std::runtime_error(
185+
"enqueueModalSizeUpdate() can only be called on <Modal />");
186+
}
187+
188+
auto state =
189+
std::static_pointer_cast<const ModalHostViewShadowNode::ConcreteState>(
190+
modalHostViewShadowNode->getState());
191+
192+
state->updateState(ModalHostViewState(
193+
{.width = static_cast<Float>(width),
194+
.height = static_cast<Float>(height)}));
195+
}
196+
197+
jsi::Function NativeFantom::createShadowNodeReferenceCounter(
198+
jsi::Runtime& runtime,
199+
ShadowNode::Shared shadowNode) {
200+
auto weakShadowNode = std::weak_ptr<const ShadowNode>(shadowNode);
201+
202+
return jsi::Function::createFromHostFunction(
203+
runtime,
204+
jsi::PropNameID::forAscii(runtime, "getReferenceCount"),
205+
0,
206+
[weakShadowNode](
207+
jsi::Runtime&, const jsi::Value&, const jsi::Value*, size_t)
208+
-> jsi::Value { return {(int)weakShadowNode.use_count()}; });
209+
}
210+
211+
jsi::Function NativeFantom::createShadowNodeRevisionGetter(
212+
jsi::Runtime& runtime,
213+
ShadowNode::Shared shadowNode) {
214+
#if RN_DEBUG_STRING_CONVERTIBLE
215+
auto weakShadowNode = std::weak_ptr<const ShadowNode>(shadowNode);
216+
217+
return jsi::Function::createFromHostFunction(
218+
runtime,
219+
jsi::PropNameID::forAscii(runtime, "getRevision"),
220+
0,
221+
[weakShadowNode](
222+
jsi::Runtime& runtime, const jsi::Value&, const jsi::Value*, size_t)
223+
-> jsi::Value {
224+
if (auto strongShadowNode = weakShadowNode.lock()) {
225+
const auto& uiManager =
226+
UIManagerBinding::getBinding(runtime)->getUIManager();
227+
228+
const auto& currentRevision =
229+
*uiManager.getNewestCloneOfShadowNode(*strongShadowNode);
230+
return currentRevision.revision_;
231+
} else {
232+
return jsi::Value::null();
233+
}
234+
});
235+
#else
236+
// TODO(T225400348): Remove this when revision_ is available in optimised
237+
// builds.
238+
throw std::runtime_error(
239+
"createShadowNodeRevisionGetter() is only available in debug builds");
240+
#endif
241+
}
242+
243+
void NativeFantom::saveJSMemoryHeapSnapshot(
244+
jsi::Runtime& runtime,
245+
std::string filePath) {
246+
runtime.instrumentation().collectGarbage("heapsnapshot");
247+
runtime.instrumentation().createSnapshotToFile(filePath);
248+
}
249+
250+
} // namespace facebook::react

0 commit comments

Comments
 (0)