From 87c38fee3ca813891d9fe92fcfa1ece2520cb7c3 Mon Sep 17 00:00:00 2001 From: shrinktofit Date: Fri, 15 Sep 2023 18:38:35 +0800 Subject: [PATCH 1/3] Use WebAssembly version libs in unit test --- jest.config.js | 5 ++- tests/constants-for-test.ts | 4 +- tests/init.ts | 64 ++----------------------------- tests/physics/physics.test.ts | 6 ++- tests/physics2d/physics2d.test.ts | 5 ++- tests/utils/pal-wasm-testing.ts | 53 +++++++++++++++++++++++++ 6 files changed, 72 insertions(+), 65 deletions(-) create mode 100644 tests/utils/pal-wasm-testing.ts diff --git a/jest.config.js b/jest.config.js index 4df7472cc5a..269b4da88c6 100644 --- a/jest.config.js +++ b/jest.config.js @@ -9,7 +9,10 @@ const { compilerOptions } = tsConfig.config; module.exports = { testEnvironment: './tests/test-environment.ts', testRegex: '/tests/.*\\.(test|spec)?\\.(ts|tsx)$', - moduleNameMapper: pathsToModuleNameMapper(compilerOptions.paths, { prefix: `${__dirname}/` }), + moduleNameMapper: { + ...pathsToModuleNameMapper(compilerOptions.paths, { prefix: `${__dirname}/` }), + 'external:(.*)': '/native/external/$1', + }, transformIgnorePatterns: [ // ignore everything in the node_modules EXCEPT for: // - @cocos/dragonbones-js diff --git a/tests/constants-for-test.ts b/tests/constants-for-test.ts index d2fd34792c6..5c7e25ad31d 100644 --- a/tests/constants-for-test.ts +++ b/tests/constants-for-test.ts @@ -1,3 +1,5 @@ +import { WebAssemblySupportMode } from "../cocos/misc/webassembly-support"; + const _globalThis = typeof global === 'undefined' ? globalThis : global; const _global = typeof window === 'undefined' ? _globalThis : window; @@ -39,4 +41,4 @@ export const VIVO = tryDefineGlobal('CC_VIVO', false); // @ts-expect-error: 'loadRuntime' exits only in runtime environment. export const SUPPORT_JIT = tryDefineGlobal('CC_SUPPORT_JIT', (typeof loadRuntime === 'function')); export const SERVER_MODE = false; -export const WASM_SUPPORT_MODE = 0; +export const WASM_SUPPORT_MODE = WebAssemblySupportMode.SUPPORT; diff --git a/tests/init.ts b/tests/init.ts index 1b826a307e0..83aec1ba28e 100644 --- a/tests/init.ts +++ b/tests/init.ts @@ -69,7 +69,7 @@ jest.mock( jest.mock( 'pal/wasm', - () => jest.requireActual('../pal/wasm/wasm-native'), // NOTE: fix CI, we used import.meta in wasm-web.ts + () => jest.requireActual('./utils/pal-wasm-testing'), // NOTE: fix CI, we used import.meta in wasm-web.ts { virtual: true, }, ); @@ -81,11 +81,11 @@ jest.mock( 'external:emscripten/physx/physx.release.wasm.wasm', 'external:emscripten/spine/spine.wasm', 'external:emscripten/box2d/box2d.release.wasm.wasm', -].forEach(moduleId => { - jest.mock(moduleId, +].forEach(mockModuleId => { + jest.mock(mockModuleId, () => ({ __esModule: true, - default: 'this should be a wasm url', + default: mockModuleId, }), { virtual: true, }, ); @@ -99,62 +99,6 @@ jest.mock('external:emscripten/meshopt/meshopt_decoder.wasm.wasm', { virtual: true, }, ); -// Mock external wasm js module here -[ - 'external:emscripten/webgpu/webgpu_wasm.js', - 'external:emscripten/webgpu/glslang.js', - 'external:emscripten/physx/physx.release.wasm.js', - 'external:emscripten/spine/spine.js', - 'external:emscripten/box2d/box2d.release.wasm.js', -].forEach(moduleId => { - jest.mock(moduleId, - () => ({ - __esModule: true, - default: function factory () { return Promise.resolve({}); }, - }), - { virtual: true, }, - ); -}); - -jest.mock('external:emscripten/meshopt/meshopt_decoder.wasm.js', - () => ({ - __esModule: true, - default: function factory () { return Promise.resolve({}); }, - }), - { virtual: true, }, -); - -jest.mock( - 'external:emscripten/physx/physx.release.asm.js', - () => jest.requireActual('../native/external/emscripten/physx/physx.release.asm.js'), - { virtual: true }, -); - - -jest.mock( - 'external:emscripten/bullet/bullet.asm.js', - () => jest.requireActual('../native/external/emscripten/bullet/bullet.asm.js'), - { virtual: true }, -); - -jest.mock( - 'external:emscripten/meshopt/meshopt_decoder.asm.js', - () => jest.requireActual('../native/external/emscripten/meshopt/meshopt_decoder.asm.js'), - { virtual: true }, -); - -jest.mock( - 'external:emscripten/spine/spine.asm.js', - () => jest.requireActual('../native/external/emscripten/spine/spine.asm.js'), - { virtual: true }, -); - -jest.mock( - 'external:emscripten/box2d/box2d.release.asm.js', - () => jest.requireActual('../native/external/emscripten/box2d/box2d.release.asm.js'), - { virtual: true }, -); - jest.mock('../cocos/core/platform/debug', () => { const result = { __esModule: true, // Use it when dealing with esModules diff --git a/tests/physics/physics.test.ts b/tests/physics/physics.test.ts index 22aef25172f..dbfc4c7c87b 100644 --- a/tests/physics/physics.test.ts +++ b/tests/physics/physics.test.ts @@ -20,8 +20,10 @@ import CharacterControllerTest from "./character-controller"; import { Node, Scene } from "../../cocos/scene-graph"; import { builtinResMgr } from "../../exports/base"; -waitForAmmoInstantiation(); -InitPhysXLibs(); +beforeAll(async () => { + await waitForAmmoInstantiation(); + await InitPhysXLibs(); +}); game.emit(Game.EVENT_PRE_SUBSYSTEM_INIT); // Manually construct and register the system diff --git a/tests/physics2d/physics2d.test.ts b/tests/physics2d/physics2d.test.ts index db993f0cd54..e7b758fce9c 100644 --- a/tests/physics2d/physics2d.test.ts +++ b/tests/physics2d/physics2d.test.ts @@ -4,7 +4,6 @@ import * as physics2d from "../../exports/physics-2d-framework"; import { Node, Scene } from "../../cocos/scene-graph"; import waitForBox2dWasmInstantiation from "../../exports/wait-for-box2d-instantiation"; -waitForBox2dWasmInstantiation(); import "../../exports/physics-2d-box2d-wasm"; import "../../exports/physics-2d-box2d"; import "../../exports/physics-2d-builtin"; @@ -17,6 +16,10 @@ import EventTest from "./events"; game.emit(Game.EVENT_PRE_SUBSYSTEM_INIT); physics2d.PhysicsSystem2D.constructAndRegister(); +beforeAll(async () => { + await waitForBox2dWasmInstantiation(); +}); + test(`physics2d test | utils`, done => { Utils(); done(); diff --git a/tests/utils/pal-wasm-testing.ts b/tests/utils/pal-wasm-testing.ts new file mode 100644 index 00000000000..f19a1ed14b6 --- /dev/null +++ b/tests/utils/pal-wasm-testing.ts @@ -0,0 +1,53 @@ +/* + Copyright (c) 2023 Xiamen Yaji Software Co., Ltd. + + https://www.cocos.com/ + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights to + use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + of the Software, and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +import { checkPalIntegrity, withImpl } from '@pal/utils'; +import { join } from 'node:path'; +import { readFileSync } from 'node:fs'; + +export async function instantiateWasm (wasmUrl: string, importObject: WebAssembly.Imports): Promise { + return fetchBuffer(wasmUrl).then((arrayBuffer) => WebAssembly.instantiate(arrayBuffer, importObject)); +} + +export async function fetchBuffer (binaryUrl: string): Promise { + const relativePathToExternal = /^external:(.*)/.exec(binaryUrl)?.[1]; + if (relativePathToExternal) { + const externalHome = join(__dirname, '..', '..', 'native', 'external'); + const path = join(externalHome, relativePathToExternal); + try { + const content = readFileSync(path); + return content.buffer; + } catch (err) { + throw new Error(`Unable to fetch buffer from ${binaryUrl}`, { cause: error }); + } + } + + throw new Error(`Don't know how to fetch buffer at url ${binaryUrl}`); +} + +export async function ensureWasmModuleReady() { + return Promise.resolve(); +} + +checkPalIntegrity(withImpl()); From 165bbfdc2f89f7c18020175f606be87b91fd40a0 Mon Sep 17 00:00:00 2001 From: shrinktofit Date: Fri, 15 Sep 2023 19:09:14 +0800 Subject: [PATCH 2/3] Fix --- tests/utils/pal-wasm-testing.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/utils/pal-wasm-testing.ts b/tests/utils/pal-wasm-testing.ts index f19a1ed14b6..3cd441fff95 100644 --- a/tests/utils/pal-wasm-testing.ts +++ b/tests/utils/pal-wasm-testing.ts @@ -22,7 +22,7 @@ THE SOFTWARE. */ -import { checkPalIntegrity, withImpl } from '@pal/utils'; +import { checkPalIntegrity, withImpl } from '../../pal/integrity-check'; import { join } from 'node:path'; import { readFileSync } from 'node:fs'; @@ -50,4 +50,4 @@ export async function ensureWasmModuleReady() { return Promise.resolve(); } -checkPalIntegrity(withImpl()); +checkPalIntegrity(withImpl()); From a4f83ad1a24068be3f5d17e9ffba6b30ccf15e0e Mon Sep 17 00:00:00 2001 From: shrinktofit Date: Mon, 18 Sep 2023 11:42:45 +0800 Subject: [PATCH 3/3] Fix --- jest.config.js | 5 ++++- tests/init.ts | 9 +-------- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/jest.config.js b/jest.config.js index 269b4da88c6..1eb9d794173 100644 --- a/jest.config.js +++ b/jest.config.js @@ -17,7 +17,10 @@ module.exports = { // ignore everything in the node_modules EXCEPT for: // - @cocos/dragonbones-js 'node_modules/(?!(@cocos/dragonbones-js)/)', - 'native/external/emscripten/', + // ignore everything in the native/external/emscripten EXCEPT for: + // - meshopt + // Since above packages are in ESM module format, whereas we currently use CJS for testing. + 'native/external/emscripten/(?!(meshopt)/)', ], setupFilesAfterEnv: [ "./tests/setup-after-env.ts", diff --git a/tests/init.ts b/tests/init.ts index 83aec1ba28e..b9fb0d18057 100644 --- a/tests/init.ts +++ b/tests/init.ts @@ -81,6 +81,7 @@ jest.mock( 'external:emscripten/physx/physx.release.wasm.wasm', 'external:emscripten/spine/spine.wasm', 'external:emscripten/box2d/box2d.release.wasm.wasm', + 'external:emscripten/meshopt/meshopt_decoder.wasm.wasm', ].forEach(mockModuleId => { jest.mock(mockModuleId, () => ({ @@ -91,14 +92,6 @@ jest.mock( ); }); -jest.mock('external:emscripten/meshopt/meshopt_decoder.wasm.wasm', - () => ({ - __esModule: true, - default: 'this should be a wasm url', - }), - { virtual: true, }, -); - jest.mock('../cocos/core/platform/debug', () => { const result = { __esModule: true, // Use it when dealing with esModules