Emscripten: Testing with jest

Created on 5 Apr 2019  路  7Comments  路  Source: emscripten-core/emscripten

Hi,

I have emscripten working nicely (using emcc 1.38.30):

  • I have c++ functions and classes exported
  • They use binding in the c++ files like this
EMSCRIPTEN_BINDINGS(MyClass)
{
    class_<MyOperation>("MYOperation");

    class_<OperationArray>("OperationArray")
        .constructor<>()
        .function<int>("GetSize", &OperationArray::GetSize)
        .function<ApiOperation*&, int>("GetAt", &OperationArray::GetAt)
        ;
}
  • Then they are compiled with those arguments
    -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
  • And they are linked with -s WASM=1 --bind -fvisibility-ms-compat
  • All my functions are getting called from some served javascript successfully

Now 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

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:

/**
 * @jest-environment node
 */

All 7 comments

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"
        };
    }
});
Was this page helpful?
0 / 5 - 0 ratings

Related issues

kripken picture kripken  路  4Comments

ShawZG picture ShawZG  路  4Comments

rpellerin picture rpellerin  路  3Comments

id01 picture id01  路  3Comments

answer1103 picture answer1103  路  4Comments