Skip to content
Closed
18 changes: 18 additions & 0 deletions packages/react-native-fantom/runner/EnvironmentOptions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict
* @format
* @oncall react_native
*/

export const printCLIOutput: boolean = Boolean(process.env.FANTOM_PRINT_OUTPUT);

export const logCommands: boolean = Boolean(process.env.FANTOM_LOG_COMMANDS);

export const enableCppDebugging: boolean = Boolean(
process.env.FANTOM_ENABLE_CPP_DEBUGGING,
);
152 changes: 99 additions & 53 deletions packages/react-native-fantom/runner/runner.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@
*/

import type {TestSuiteResult} from '../runtime/setup';
import type {AsyncCommandResult} from './utils';

import entrypointTemplate from './entrypoint-template';
import * as EnvironmentOptions from './EnvironmentOptions';
import getFantomTestConfig from './getFantomTestConfig';
import {FantomTestConfigMode} from './getFantomTestConfig';
import {
Expand All @@ -22,7 +24,9 @@ import {
getBuckModesForPlatform,
getDebugInfoFromCommandResult,
getShortHash,
printConsoleLog,
runBuck2,
runBuck2Sync,
symbolicateStackTrace,
} from './utils';
import fs from 'fs';
Expand All @@ -32,40 +36,89 @@ import {SnapshotState, buildSnapshotResolver} from 'jest-snapshot';
import Metro from 'metro';
import nullthrows from 'nullthrows';
import path from 'path';
import readline from 'readline';

const BUILD_OUTPUT_ROOT = path.resolve(__dirname, '..', 'build');
fs.mkdirSync(BUILD_OUTPUT_ROOT, {recursive: true});
const BUILD_OUTPUT_PATH = fs.mkdtempSync(
path.join(BUILD_OUTPUT_ROOT, `run-${Date.now()}-`),
);

const PRINT_FANTOM_OUTPUT: false = false;
async function processRNTesterCommandResult(
result: AsyncCommandResult,
): Promise<TestSuiteResult> {
const stdoutChunks = [];
const stderrChunks = [];

function parseRNTesterCommandResult(result: ReturnType<typeof runBuck2>): {
logs: string,
testResult: TestSuiteResult,
} {
const stdout = result.stdout.toString();

const outputArray = stdout
.trim()
.split('\n')
.filter(log => !log.startsWith('Running "')); // remove AppRegistry logs.
result.childProcess.stdout.on('data', chunk => {
stdoutChunks.push(chunk);
});

// The last line should be the test output in JSON format
const testResultJSON = outputArray.pop();
result.childProcess.stderr.on('data', chunk => {
stderrChunks.push(chunk);
});

let testResult;
try {
testResult = JSON.parse(nullthrows(testResultJSON));
} catch (error) {

const rl = readline.createInterface({input: result.childProcess.stdout});
rl.on('line', (rawLine: string) => {
const line = rawLine.trim();
if (!line) {
return;
}

let parsed;
try {
parsed = JSON.parse(line);
} catch {
parsed = {
type: 'console-log',
level: 'info',
message: line,
};
}

switch (parsed?.type) {
case 'test-result':
testResult = parsed;
break;
case 'console-log':
printConsoleLog(parsed);
break;
default:
printConsoleLog({
type: 'console-log',
level: 'info',
message: line,
});
break;
}
});

await result.done;

const getResultWithOutput = () => ({
...result,
stdout: stdoutChunks.join(''),
stderr: stderrChunks.join(''),
});

if (result.status !== 0) {
throw new Error(getDebugInfoFromCommandResult(getResultWithOutput()));
}

if (EnvironmentOptions.printCLIOutput) {
console.log(getDebugInfoFromCommandResult(getResultWithOutput()));
}

if (testResult == null) {
throw new Error(
'Failed to parse test results from RN tester binary result.\n' +
'Failed to find test results in RN tester binary output.\n' +
getDebugInfoFromCommandResult(result),
);
}

return {logs: outputArray.join('\n'), testResult};
return testResult;
}

function generateBytecodeBundle({
Expand All @@ -77,7 +130,7 @@ function generateBytecodeBundle({
bytecodePath: string,
isOptimizedMode: boolean,
}): void {
const hermesCompilerCommandResult = runBuck2(
const hermesCompilerCommandResult = runBuck2Sync(
[
'run',
...getBuckModesForPlatform(isOptimizedMode),
Expand Down Expand Up @@ -180,36 +233,33 @@ module.exports = async function runTest(
});
}

const rnTesterCommandResult = runBuck2([
'run',
...getBuckModesForPlatform(
testConfig.mode === FantomTestConfigMode.Optimized,
),
'//xplat/ReactNative/react-native-cxx/samples/tester:tester',
'--',
'--bundlePath',
testConfig.mode === FantomTestConfigMode.DevelopmentWithSource
? testJSBundlePath
: testBytecodeBundlePath,
'--featureFlags',
JSON.stringify(testConfig.flags.common),
'--minLogLevel',
PRINT_FANTOM_OUTPUT ? 'info' : 'error',
]);

if (rnTesterCommandResult.status !== 0) {
throw new Error(getDebugInfoFromCommandResult(rnTesterCommandResult));
}

if (PRINT_FANTOM_OUTPUT) {
console.log(getDebugInfoFromCommandResult(rnTesterCommandResult));
}
const rnTesterCommandResult = runBuck2(
[
'run',
...getBuckModesForPlatform(
testConfig.mode === FantomTestConfigMode.Optimized,
),
'//xplat/ReactNative/react-native-cxx/samples/tester:tester',
'--',
'--bundlePath',
testConfig.mode === FantomTestConfigMode.DevelopmentWithSource
? testJSBundlePath
: testBytecodeBundlePath,
'--featureFlags',
JSON.stringify(testConfig.flags.common),
'--minLogLevel',
EnvironmentOptions.printCLIOutput ? 'info' : 'error',
],
{
withFDB: EnvironmentOptions.enableCppDebugging,
},
);

const rnTesterParsedOutput = parseRNTesterCommandResult(
const processedResult = await processRNTesterCommandResult(
rnTesterCommandResult,
);

const testResultError = rnTesterParsedOutput.testResult.error;
const testResultError = processedResult.error;
if (testResultError) {
const error = new Error(testResultError.message);
error.stack = symbolicateStackTrace(sourceMapPath, testResultError.stack);
Expand All @@ -218,12 +268,8 @@ module.exports = async function runTest(

const endTime = Date.now();

if (process.env.SANDCASTLE == null) {
console.log(rnTesterParsedOutput.logs);
}

const testResults =
nullthrows(rnTesterParsedOutput.testResult.testResults).map(testResult => ({
nullthrows(processedResult.testResults).map(testResult => ({
ancestorTitles: [] as Array<string>,
failureDetails: [] as Array<string>,
testFilePath: testPath,
Expand All @@ -233,9 +279,9 @@ module.exports = async function runTest(
),
})) ?? [];

const snapshotResults = nullthrows(
rnTesterParsedOutput.testResult.testResults,
).map(testResult => testResult.snapshotResults);
const snapshotResults = nullthrows(processedResult.testResults).map(
testResult => testResult.snapshotResults,
);

const snapshotResult = updateSnapshotsAndGetJestSnapshotResult(
snapshotState,
Expand Down
Loading