|
| 1 | +import { fileURLToPath, URL } from 'node:url'; |
| 2 | +import { readFile, readdir, mkdir, writeFile } from 'node:fs/promises'; |
| 3 | + |
| 4 | +import { componentize } from '@bytecodealliance/componentize-js'; |
| 5 | +import { transpile } from '@bytecodealliance/jco'; |
| 6 | + |
| 7 | +import { suite, test } from 'vitest'; |
| 8 | + |
| 9 | +import { |
| 10 | + DEBUG_TRACING_ENABLED, |
| 11 | + WEVAL_TEST_ENABLED, |
| 12 | + DEBUG_TEST_ENABLED, |
| 13 | + maybeLogging, |
| 14 | +} from './util.js'; |
| 15 | + |
| 16 | +suite('Bindings', async () => { |
| 17 | + const bindingsCases = await readdir(new URL('./cases', import.meta.url)); |
| 18 | + |
| 19 | + for (const name of bindingsCases) { |
| 20 | + test.concurrent(name, async () => { |
| 21 | + const source = await readFile( |
| 22 | + new URL(`./cases/${name}/source.js`, import.meta.url), |
| 23 | + 'utf8', |
| 24 | + ); |
| 25 | + |
| 26 | + const test = await import(`./cases/${name}/test.js`); |
| 27 | + |
| 28 | + // Determine the relevant WIT world to use |
| 29 | + let witWorld, |
| 30 | + witPath, |
| 31 | + worldName, |
| 32 | + isWasiTarget = false; |
| 33 | + if (test.worldName) { |
| 34 | + witPath = fileURLToPath(new URL('./wit', import.meta.url)); |
| 35 | + worldName = test.worldName; |
| 36 | + isWasiTarget = true; |
| 37 | + } else { |
| 38 | + try { |
| 39 | + witWorld = await readFile( |
| 40 | + new URL(`./cases/${name}/world.wit`, import.meta.url), |
| 41 | + 'utf8', |
| 42 | + ); |
| 43 | + } catch (e) { |
| 44 | + if (e?.code == 'ENOENT') { |
| 45 | + try { |
| 46 | + isWasiTarget = true; |
| 47 | + witPath = fileURLToPath( |
| 48 | + new URL(`./cases/${name}/wit`, import.meta.url), |
| 49 | + ); |
| 50 | + await readdir(witPath); |
| 51 | + } catch (e) { |
| 52 | + if (e?.code === 'ENOENT') { |
| 53 | + witPath = fileURLToPath(new URL('./wit', import.meta.url)); |
| 54 | + worldName = 'test2'; |
| 55 | + } else { |
| 56 | + throw e; |
| 57 | + } |
| 58 | + } |
| 59 | + } else { |
| 60 | + throw e; |
| 61 | + } |
| 62 | + } |
| 63 | + } |
| 64 | + |
| 65 | + const enableFeatures = test.enableFeatures || ['http']; |
| 66 | + const disableFeatures = |
| 67 | + test.disableFeatures || |
| 68 | + (isWasiTarget ? [] : ['random', 'clocks', 'http', 'stdio']); |
| 69 | + |
| 70 | + let testArg; |
| 71 | + try { |
| 72 | + const { component, imports } = await componentize(source, { |
| 73 | + sourceName: `${name}.js`, |
| 74 | + witWorld, |
| 75 | + witPath, |
| 76 | + worldName, |
| 77 | + enableFeatures, |
| 78 | + disableFeatures: maybeLogging(disableFeatures), |
| 79 | + enableAot: WEVAL_TEST_ENABLED, |
| 80 | + debugBuild: DEBUG_TEST_ENABLED, |
| 81 | + }); |
| 82 | + const map = { |
| 83 | + 'wasi:cli-base/*': '@bytecodealliance/preview2-shim/cli-base#*', |
| 84 | + 'wasi:clocks/*': '@bytecodealliance/preview2-shim/clocks#*', |
| 85 | + 'wasi:filesystem/*': '@bytecodealliance/preview2-shim/filesystem#*', |
| 86 | + 'wasi:http/*': '@bytecodealliance/preview2-shim/http#*', |
| 87 | + 'wasi:io/*': '@bytecodealliance/preview2-shim/io#*', |
| 88 | + 'wasi:logging/*': '@bytecodealliance/preview2-shim/logging#*', |
| 89 | + 'wasi:poll/*': '@bytecodealliance/preview2-shim/poll#*', |
| 90 | + 'wasi:random/*': '@bytecodealliance/preview2-shim/random#*', |
| 91 | + 'wasi:sockets/*': '@bytecodealliance/preview2-shim/sockets#*', |
| 92 | + }; |
| 93 | + for (let [impt] of imports) { |
| 94 | + if (impt.startsWith('wasi:')) continue; |
| 95 | + if (impt.startsWith('[')) impt = impt.slice(impt.indexOf(']') + 1); |
| 96 | + let importName = impt.split('/').pop(); |
| 97 | + if (importName === 'test') importName = 'imports'; |
| 98 | + map[impt] = `../../cases/${name}/${importName}.js`; |
| 99 | + } |
| 100 | + |
| 101 | + const { |
| 102 | + files, |
| 103 | + imports: componentImports, |
| 104 | + exports: componentExports, |
| 105 | + } = await transpile(component, { |
| 106 | + name, |
| 107 | + map, |
| 108 | + wasiShim: true, |
| 109 | + validLiftingOptimization: false, |
| 110 | + tracing: DEBUG_TRACING_ENABLED, |
| 111 | + }); |
| 112 | + |
| 113 | + testArg = { imports, componentImports, componentExports }; |
| 114 | + |
| 115 | + await mkdir(new URL(`./output/${name}/interfaces`, import.meta.url), { |
| 116 | + recursive: true, |
| 117 | + }); |
| 118 | + |
| 119 | + await writeFile( |
| 120 | + new URL(`./output/${name}.component.wasm`, import.meta.url), |
| 121 | + component, |
| 122 | + ); |
| 123 | + |
| 124 | + for (const file of Object.keys(files)) { |
| 125 | + let source = files[file]; |
| 126 | + await writeFile( |
| 127 | + new URL(`./output/${name}/${file}`, import.meta.url), |
| 128 | + source, |
| 129 | + ); |
| 130 | + } |
| 131 | + |
| 132 | + var instance = await import(`./output/${name}/${name}.js`); |
| 133 | + } catch (e) { |
| 134 | + if (test.err) { |
| 135 | + test.err(e); |
| 136 | + return; |
| 137 | + } |
| 138 | + throw e; |
| 139 | + } |
| 140 | + await test.test(instance, testArg); |
| 141 | + }); |
| 142 | + } |
| 143 | +}); |
0 commit comments