Hi,
I have emscripten working nicely (using emcc 1.38.30):
EMSCRIPTEN_BINDINGS(MyClass)
{
class_<MyOperation>("MYOperation");
class_<OperationArray>("OperationArray")
.constructor<>()
.function<int>("GetSize", &OperationArray::GetSize)
.function<ApiOperation*&, int>("GetAt", &OperationArray::GetAt)
;
}
-Oz -s WASM=1 -s INLINING_LIMIT=1 -s ERROR_ON_UNDEFINED_SYMBOLS=1 -s ALLOW_MEMORY_GROWTH=1 -s DISABLE_EXCEPTION_CATCHING=0 -Wno-switch -std=c++11 -c --bind -fvisibility-ms-compat -s WASM=1 --bind -fvisibility-ms-compatNow to the problem.
We have found that the bindings are a bit fleaky and sometime some functions become not visible from our javascript code.
So we thought we would had some jest tests to check programmatically if all our functions are being correctly exported.
So I tried 2 things.
1, I have found some tutorials showing how to test wasm files by instanciating the wasm file directly (https://hub.packtpub.com/testing-webassembly-modules-with-jest-tutorial/), but when I try to do the same I get lots of link errors like this one:
LinkError: WebAssembly Instantiation: Import #1 module="env" function="nullFunc_di" error: function import requires a callable
I get this for about 20-30 functions, and true enough those functions are all defined in the js file generated by emcc
I have tried to create all the missing functions but then the error I was getting changed:
LinkError: WebAssembly Instantiation: Import #43 module="env" function="tempDoublePtr" error: global import must be a number or WebAssembly.Global object
Why are all those functions needed by my wasm file?
2 Then I tried to load the js file generated by emcc but I get this error message
console.log ../builds/wasmMyClass/MyClass.js:4292
both async and sync fetching of the wasm failed
Any idea on this error?
Is it possible at all to test wasm file from jest?
Would there be another way to test that the exported functions are working as expected in javascript?
Any help would be appreciated,
Thanks
About 1, the wasm depends on support from JS - could be syscalls, or other things. That interface is an emscripten internal detail, basically.
If you just want to check if a function is exported, you don't need to instantiate the module, though. You can parse the wasm binary, for example you can print it using wabt or binaryen, and scan the output for the exports.
About 2, the error logging should explain how it failed (was the wasm file missing, or the browser didn't support wasm, or something else). ASSERTIONS builds may have more info.
I don't have logs to see the issue since I am not serving the js file, I am importing it inside a jest test.
So it would suggest that I can't use jest since the js file needs to be on a webserver.
What do you think?
Thank you for your help,
Franck.
@franckyinuk Did you get any further with this? I am sitting with the same issue.
@franckyinuk If you are interested, I got it Jest working with something like this
var QuantLibModule = require("./quantlib");
var QuantLib = null;
describe("captor/quantlib", () => {
beforeAll(async () => {
var loader = QuantLibModule();
loader.ready = () =>
// https://github.com/emscripten-core/emscripten/issues/5820
new Promise((resolve, reject) => {
delete loader.then;
loader.onAbort = reject;
loader.addOnPostRun(() => {
resolve(loader);
});
});
QuantLib = await loader.ready();
});
test("Sweden Calendar", async () => {
expect(QuantLib.Sweden.name()).toBe("Sweden");
});
});
emcc ... -s "EXTRA_EXPORTED_RUNTIME_METHODS=['addOnPostRun']" ...
Jest by default uses jsdom to simulate running in a browser. This creates a Window object that the Emscripten code sees and thinks it's in a browser. I didn't debug through what happens to know exactly what the cause is (perhaps something to do with fetch and not having a web server running?)
Switching Jest to use Node.js fixed the issue on my side. Put the following at the very beginning of your JS test file to switch from jsdom to node:
/**
* @jest-environment node
*/
Use emcc ... -s MODULARIZE=1, and then you can:
const Module = require('./test.js');
Module().then(instance => {
// here you can do: new instance.MyClass(); if you exported a class, or instance.myFunction(); ...
});
If you want to use jsdom to test wasm, you can mock the fetch function to return the .wasm file. I used "jest-fetch-mock" for this.
// TypeScript types don't seem to work correctly
import { enableFetchMocks } from "jest-fetch-mock";
import { readFileSync } from "fs";
enableFetchMocks();
const file = readFileSync("./src/file.wasm");
(fetch as any).mockResponse(async (request: any) => {
if (request.url.endsWith("file.wasm")) {
return {
status: 200,
body: file as any
};
} else {
return {
status: 404,
body: "Not Found"
};
}
});
Most helpful comment
Jest by default uses jsdom to simulate running in a browser. This creates a Window object that the Emscripten code sees and thinks it's in a browser. I didn't debug through what happens to know exactly what the cause is (perhaps something to do with fetch and not having a web server running?)
Switching Jest to use Node.js fixed the issue on my side. Put the following at the very beginning of your JS test file to switch from jsdom to node: