/
githubmr
/
facebook-react-native
Обзор
Документация
Войти
/
githubmr
/
facebook-react-native
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
main
packages/react-native/scripts/hermes/__tests__/hermes-utils-test.js
449 строк
14 KB
Jakub Piasecki
Change leftover references to hermes.framework (#53390)
21 авг 2025, 16:24
21 авг 2025, 16:24
7f051c5
Код
Авторство
О чём код?
/** * 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-local * @format */ import * as path from 'path'; const { configureMakeForPrebuiltHermesC, copyBuildScripts, copyPodSpec, createHermesPrebuiltArtifactsTarball, createTarballFromDirectory, downloadHermesSourceTarball, expandHermesSourceTarball, getHermesPrebuiltArtifactsTarballName, getHermesTagSHA, getHermesTarballDownloadPath, readHermesTag, setHermesTag, shouldUsePrebuiltHermesC, } = require('../hermes-utils'); // $FlowFixMe[untyped-import] (OSS) memfs const {memfs} = require('memfs'); const os = require('os'); const hermesTag = 'hermes-2022-04-28-RNv0.69.0-15d07c2edd29a4ea0b8f15ab0588a0c1adb1200f'; const tarballContents = 'dummy string'; const hermescContents = 'dummy string'; const hermesTagSha = '5244f819b2f3949ca94a3a1bf75d54a8ed59d94a'; const ROOT_DIR = path.normalize(path.join(__dirname, '../../..')); const SDKS_DIR = path.join(ROOT_DIR, 'sdks'); let execCalls: $FlowFixMe; let spawnCalls: $FlowFixMe; let fs: $FlowFixMe; jest.mock('child_process', () => ({ execSync: jest.fn((command, options) => { // git is used in getHermesTagSHA to obtain the commit sha for the latest commit to Hermes main if (command.startsWith('git')) { execCalls.git = true; return hermesTagSha + '\n'; } }), spawnSync: jest.fn((command, args, options) => { // curl is used in downloadHermesSourceTarball to fetch the source code from github.com/facebook/hermes for a specific Hermes commit sha if (command === 'curl') { const downloadPath = args[2]; fs.writeFileSync(downloadPath, tarballContents); spawnCalls.curl = true; return {code: 0}; } // tar is used in createTarballFromDirectory if (command === 'tar') { spawnCalls.tar = true; if (args[0] === '-zxf') { // We are expanding the tarball fs.mkdirSync(path.join(SDKS_DIR, 'hermes/utils'), { recursive: true, }); fs.writeFileSync(path.join(SDKS_DIR, 'hermes/package.json'), '{}'); return {code: 0}; } else if (args[2] === '-czvf') { // We are creating the tarball const filename = args[3]; fs.writeFileSync(filename, tarballContents); return {code: 0}; } } // rsync is used in createHermesPrebuiltArtifactsTarball if (command === 'rsync') { spawnCalls.rsync = true; spawnCalls.rsyncArgs = args; const destination = args[args.length - 1]; // Create destination directory fs.mkdirSync(path.join(options.cwd, destination), { recursive: true, }); } }), })); function populateMockFilesystemWithHermesBuildScripts() { fs.mkdirSync(path.join(SDKS_DIR, 'hermes-engine/utils'), { recursive: true, }); fs.writeFileSync( path.join(SDKS_DIR, 'hermes-engine/utils/build-apple-framework.sh'), 'Dummy file', ); fs.writeFileSync( path.join(SDKS_DIR, 'hermes-engine/utils/build-ios-framework.sh'), 'Dummy file', ); fs.writeFileSync( path.join(SDKS_DIR, 'hermes-engine/utils/build-mac-framework.sh'), 'Dummy file', ); fs.writeFileSync( path.join(SDKS_DIR, 'hermes-engine/hermes-engine.podspec'), 'Dummy file', ); fs.writeFileSync( path.join(SDKS_DIR, 'hermes-engine/hermes-utils.rb'), 'Dummy file', ); } function populateMockFilesystemWithHermesBuildArtifacts() { fs.mkdirSync(os.tmpdir(), {recursive: true}); const frameworksDir = path.join( SDKS_DIR, 'hermes/destroot/Library/Frameworks', ); fs.mkdirSync(path.join(frameworksDir, 'macosx/hermesvm.framework'), { recursive: true, }); fs.mkdirSync(path.join(frameworksDir, 'universal/hermesvm.xcframework'), { recursive: true, }); const dsymsDirs = [ 'macosx', 'universal/hermesvm.xcframework/ios-arm64/dSYMs', 'universal/hermesvm.xcframework/ios-arm64_x86_64-simulator/dSYMs', 'universal/hermesvm.xcframework/ios-arm64_x86_64-maccatalyst/dSYMs', ]; for (const dsymsDir of dsymsDirs) { fs.mkdirSync( path.join(frameworksDir, dsymsDir, 'hermesvm.framework.dSYM'), { recursive: true, }, ); } } describe('hermes-utils', () => { beforeEach(() => { jest.resetModules(); jest.mock('fs', () => memfs().fs); fs = require('fs'); populateMockFilesystemWithHermesBuildScripts(); execCalls = Object.create(null); spawnCalls = Object.create(null); // Silence logs. jest.spyOn(console, 'log').mockImplementation(() => {}); jest.spyOn(console, 'info').mockImplementation(() => {}); }); describe('Versioning Hermes', () => { describe('readHermesTag', () => { it('should return main if .hermesversion does not exist', () => { expect(readHermesTag()).toEqual('main'); }); it('should fail if hermes tag is empty', () => { fs.writeFileSync(path.join(SDKS_DIR, '.hermesversion'), ''); expect(() => { readHermesTag(); }).toThrow('[Hermes] .hermesversion file is empty.'); }); it('should return tag from .hermesversion if file exists', () => { fs.writeFileSync(path.join(SDKS_DIR, '.hermesversion'), hermesTag); expect(readHermesTag()).toEqual(hermesTag); }); }); describe('setHermesTag', () => { it('should write tag to .hermesversion file', () => { setHermesTag(hermesTag); expect( fs.readFileSync(path.join(SDKS_DIR, '.hermesversion'), { encoding: 'utf8', flag: 'r', }), ).toEqual(hermesTag); }); it('should set Hermes tag and read it back', () => { setHermesTag(hermesTag); expect(readHermesTag()).toEqual(hermesTag); }); }); describe('getHermesTagSHA', () => { it('should return trimmed commit SHA for Hermes tag', () => { expect(getHermesTagSHA(hermesTag)).toEqual(hermesTagSha); expect(execCalls.git).toBe(true); }); }); }); describe('Downloading Hermes', () => { describe('getHermesTarballDownloadPath', () => { it('returns download path with Hermes tag sha', () => { const hermesTarballDownloadPath = getHermesTarballDownloadPath(hermesTag); expect(hermesTarballDownloadPath).toEqual( path.join( SDKS_DIR, 'download', `hermes-${getHermesTagSHA(hermesTag)}.tgz`, ), ); }); }); describe('downloadHermesSourceTarball', () => { it('should download Hermes source tarball to download dir', () => { fs.writeFileSync(path.join(SDKS_DIR, '.hermesversion'), hermesTag); const hermesTarballDownloadPath = getHermesTarballDownloadPath(hermesTag); downloadHermesSourceTarball(); expect(spawnCalls.curl).toBe(true); expect( fs.readFileSync(hermesTarballDownloadPath, { encoding: 'utf8', flag: 'r', }), ).toEqual(tarballContents); }); it('should not re-download Hermes source tarball if tarball exists', () => { fs.mkdirSync(path.join(SDKS_DIR, 'download'), {recursive: true}); fs.writeFileSync( path.join(SDKS_DIR, 'download', `hermes-${hermesTagSha}.tgz`), tarballContents, ); downloadHermesSourceTarball(); expect(spawnCalls.curl).toBeUndefined(); }); }); describe('expandHermesSourceTarball', () => { it('should expand Hermes source tarball to Hermes source dir', () => { fs.mkdirSync(path.join(SDKS_DIR, 'download'), {recursive: true}); fs.writeFileSync( path.join(SDKS_DIR, 'download', `hermes-${hermesTagSha}.tgz`), tarballContents, ); expect(fs.existsSync(path.join(SDKS_DIR, 'hermes'))).toBeFalsy(); expandHermesSourceTarball(); expect(fs.existsSync(path.join(SDKS_DIR, 'hermes'))).toBe(true); }); it('should fail if Hermes source tarball does not exist', () => { expect(() => { expandHermesSourceTarball(); }).toThrow('[Hermes] Could not locate Hermes tarball.'); }); }); }); describe('Configuring Hermes Build', () => { describe('copyBuildScripts', () => { it('should copy React Native Hermes build scripts to Hermes source directory', () => { copyBuildScripts(); [ 'build-apple-framework.sh', 'build-ios-framework.sh', 'build-mac-framework.sh', ].forEach(buildScript => { expect( fs.readFileSync( path.join(ROOT_DIR, 'sdks/hermes/utils', buildScript), { encoding: 'utf8', flag: 'r', }, ), ).toEqual( fs.readFileSync( path.join(ROOT_DIR, 'sdks/hermes-engine/utils', buildScript), { encoding: 'utf8', flag: 'r', }, ), ); }); }); }); describe('copyPodSpec', () => { it('should copy React Native Hermes Podspec to Hermes source directory', () => { copyPodSpec(); expect( fs.readFileSync(path.join(SDKS_DIR, 'hermes/hermes-engine.podspec'), { encoding: 'utf8', flag: 'r', }), ).toEqual( fs.readFileSync( path.join(SDKS_DIR, 'hermes-engine/hermes-engine.podspec'), { encoding: 'utf8', flag: 'r', }, ), ); }); it('should copy hermes-utils.rb to Hermes source directory', () => { copyPodSpec(); expect( fs.readFileSync(path.join(SDKS_DIR, 'hermes/hermes-utils.rb'), { encoding: 'utf8', flag: 'r', }), ).toEqual( fs.readFileSync( path.join(SDKS_DIR, 'hermes-engine/hermes-utils.rb'), { encoding: 'utf8', flag: 'r', }, ), ); }); }); describe('shouldUsePrebuiltHermesC', () => { it('returns false if path to osx hermesc does not exist', () => { expect(shouldUsePrebuiltHermesC('macos')).toBeFalsy(); }); it('returns false for non-macOS', () => { expect(shouldUsePrebuiltHermesC('windows')).toBeFalsy(); }); it('return true only if path to hermesc exists', () => { fs.mkdirSync(path.join(SDKS_DIR, 'hermesc/osx-bin'), { recursive: true, }); fs.writeFileSync( path.join(SDKS_DIR, 'hermesc/osx-bin/hermesc'), hermescContents, ); expect(shouldUsePrebuiltHermesC('macos')).toBe(true); }); }); describe('configureMakeForPrebuiltHermesC', () => { it('creates ImportHermesC file', () => { fs.mkdirSync(path.join(SDKS_DIR, 'hermesc/osx-bin'), { recursive: true, }); configureMakeForPrebuiltHermesC(); expect( fs.existsSync( path.join(SDKS_DIR, 'hermesc/osx-bin/ImportHostCompilers.cmake'), ), ).toBe(true); }); }); }); describe('Packaging Hermes', () => { beforeEach(() => { populateMockFilesystemWithHermesBuildArtifacts(); }); describe('createTarballFromDirectory', () => { it('should create the tarball', () => { fs.mkdirSync(path.join(SDKS_DIR, 'downloads'), {recursive: true}); const tarballFilename = path.join( SDKS_DIR, 'downloads/hermes-runtime-darwin.tar.gz', ); createTarballFromDirectory( path.join(SDKS_DIR, 'hermes/destroot'), tarballFilename, ); expect(spawnCalls.tar).toBe(true); expect(fs.existsSync(tarballFilename)).toBe(true); }); }); describe('getHermesPrebuiltArtifactsTarballName', () => { it('should return Hermes prebuilts tarball name', () => { expect(getHermesPrebuiltArtifactsTarballName('Debug')).toEqual( 'hermes-ios-debug.tar.gz', ); }); it('should throw if build type is undefined', () => { expect(() => { getHermesPrebuiltArtifactsTarballName(); }).toThrow('Did not specify build type.'); }); }); describe('createHermesPrebuiltArtifactsTarball', () => { it('creates tarball', () => { const tarballOutputDir = fs.mkdtempSync( path.join(os.tmpdir(), 'hermes-prebuilts-'), ); fs.mkdirSync(tarballOutputDir, { recursive: true, }); const excludeDebugSymbols = false; const tarballOutputPath = createHermesPrebuiltArtifactsTarball( path.join(SDKS_DIR, 'hermes'), 'Debug', tarballOutputDir, excludeDebugSymbols, ); expect(fs.existsSync(tarballOutputPath)).toBe(true); expect(spawnCalls.rsync).toBe(true); // rsync -a src dest expect(spawnCalls.rsyncArgs.length).toEqual(3); }); it('creates tarball with debug symbols excluded', () => { const tarballOutputDir = fs.mkdtempSync( path.join(os.tmpdir(), 'hermes-prebuilts-'), ); fs.mkdirSync(tarballOutputDir, { recursive: true, }); const excludeDebugSymbols = true; const tarballOutputPath = createHermesPrebuiltArtifactsTarball( path.join(SDKS_DIR, 'hermes'), 'Debug', tarballOutputDir, excludeDebugSymbols, ); expect(fs.existsSync(tarballOutputPath)).toBe(true); expect(spawnCalls.rsync).toBe(true); // When the debug symbols are excluded, we pass an additional two parameters to rsync: // rsync -a --exclude=dSYMs/ --exclude=*.dSYM/ src dest expect(spawnCalls.rsyncArgs.length).toEqual(5); expect(spawnCalls.rsyncArgs[1]).toEqual('--exclude=dSYMs/'); expect(spawnCalls.rsyncArgs[2]).toEqual('--exclude=*.dSYM/'); }); }); }); });