#!/usr/bin/env node /** * WASM interface tests (run after WASM build) * Tests the WebAssembly integration and performance * Run with: node tests/integration/wasm.test.js */ const { strict: assert } = require('assert'); const fs = require('fs').promises; const path = require('path'); class WASMTestRunner { constructor() { this.tests = []; this.passed = 0; this.failed = 0; this.verbose = process.argv.includes('--verbose'); this.wasmBuilt = false; this.solverModule = null; } async setup() { // Check if WASM has been built const wasmPkgPath = path.join(__dirname, '../../pkg'); const jsWrapperPath = path.join(__dirname, '../../js/solver.js'); try { await fs.access(wasmPkgPath); await fs.access(jsWrapperPath); this.wasmBuilt = true; // Try to import the solver module try { this.solverModule = await import(jsWrapperPath); } catch (error) { console.warn('Warning: Could not import solver module:', error.message); this.wasmBuilt = false; } } catch (error) { this.wasmBuilt = false; } } test(name, fn) { this.tests.push({ name, fn }); } async run() { console.log('๐Ÿงช Running WASM Interface Tests'); console.log('================================\n'); await this.setup(); if (!this.wasmBuilt) { console.log('โš ๏ธ WASM package not built. Run the following to build:'); console.log(' 1. Install Rust: curl --proto "=https" --tlsv1.2 -sSf https://sh.rustup.rs | sh'); console.log(' 2. Add WASM target: rustup target add wasm32-unknown-unknown'); console.log(' 3. Install wasm-pack: cargo install wasm-pack'); console.log(' 4. Build WASM: ./scripts/build.sh'); console.log('\n๐Ÿ“ Running mock tests instead...\n'); } for (const { name, fn } of this.tests) { try { await fn(); this.passed++; console.log(`โœ… ${name}`); } catch (error) { this.failed++; console.log(`โŒ ${name}`); if (this.verbose) { console.log(` Error: ${error.message}`); console.log(` Stack: ${error.stack}\n`); } else { console.log(` Error: ${error.message}\n`); } } } this.printSummary(); return this.failed === 0; } printSummary() { console.log('\n๐Ÿ“Š Test Summary'); console.log('==============='); console.log(`โœ… Passed: ${this.passed}`); console.log(`โŒ Failed: ${this.failed}`); console.log(`๐Ÿ“ˆ Total: ${this.tests.length}`); console.log(`๐ŸŽฏ Success Rate: ${((this.passed / this.tests.length) * 100).toFixed(1)}%`); if (!this.wasmBuilt) { console.log('\n๐Ÿ”ง Build Requirements:'); console.log(' โ€ข Rust toolchain (rustc, cargo)'); console.log(' โ€ข wasm-pack'); console.log(' โ€ข wasm32-unknown-unknown target'); console.log(' โ€ข Run: npm run build'); } } // Create a mock WASM interface for testing when WASM is not built createMockWASMInterface() { return { Matrix: class { constructor(data, rows, cols) { this.data = data instanceof Float64Array ? data : new Float64Array(data); this.rows = rows; this.cols = cols; } static zeros(rows, cols) { return new this(new Float64Array(rows * cols), rows, cols); } static identity(size) { const data = new Float64Array(size * size); for (let i = 0; i < size; i++) { data[i * size + i] = 1.0; } return new this(data, size, size); } get(row, col) { return this.data[row * this.cols + col]; } set(row, col, value) { this.data[row * this.cols + col] = value; } }, SublinearSolver: class { constructor(config = {}) { this.config = config; this.initialized = false; } async initialize() { this.initialized = true; } async solve(matrix, vector) { if (!this.initialized) await this.initialize(); // Mock solution: identity mapping return new Float64Array(vector); } getMemoryUsage() { return { used: 1024, capacity: 2048, js: { allocations: 0, totalBytes: 0 } }; } dispose() { this.initialized = false; } }, Utils: { async getFeatures() { return { simd: false, threads: 1, mock: true }; }, async isSIMDEnabled() { return false; }, async benchmarkMatrixMultiply(size) { return { time: size * 0.001, operations: size * size }; }, async getWasmMemoryUsage() { return { used: 0, total: 0 }; } } }; } getModule() { return this.wasmBuilt ? this.solverModule : this.createMockWASMInterface(); } } const runner = new WASMTestRunner(); // WASM Build Verification Tests runner.test('WASM package structure exists', async () => { if (!runner.wasmBuilt) { // Mock test - verify expected structure would exist const expectedFiles = [ 'pkg/sublinear_time_solver.js', 'pkg/sublinear_time_solver_bg.wasm', 'pkg/sublinear_time_solver.d.ts', 'pkg/package.json' ]; console.log(' Expected files after build:', expectedFiles.join(', ')); return; // Skip actual verification } const pkgPath = path.join(__dirname, '../../pkg'); const files = await fs.readdir(pkgPath); // Check for essential WASM files assert.ok(files.some(f => f.endsWith('.wasm'))); assert.ok(files.some(f => f.endsWith('.js'))); assert.ok(files.some(f => f.endsWith('.d.ts'))); assert.ok(files.includes('package.json')); }); runner.test('JavaScript wrapper exists and is importable', async () => { const module = runner.getModule(); assert.ok(module); if (runner.wasmBuilt) { assert.ok(module.Matrix); assert.ok(module.SublinearSolver); assert.ok(module.Utils); } else { // Mock verification assert.ok(module.Matrix); assert.ok(module.SublinearSolver); assert.ok(module.Utils); } }); // WASM Matrix Interface Tests runner.test('WASM Matrix creation and basic operations', async () => { const module = runner.getModule(); const { Matrix } = module; // Test matrix creation const matrix = new Matrix([1, 2, 3, 4], 2, 2); assert.equal(matrix.rows, 2); assert.equal(matrix.cols, 2); assert.equal(matrix.get(0, 0), 1); assert.equal(matrix.get(1, 1), 4); // Test static methods const zeros = Matrix.zeros(3, 3); assert.equal(zeros.rows, 3); assert.equal(zeros.get(1, 1), 0); const identity = Matrix.identity(2); assert.equal(identity.get(0, 0), 1); assert.equal(identity.get(0, 1), 0); assert.equal(identity.get(1, 0), 0); assert.equal(identity.get(1, 1), 1); }); runner.test('WASM Matrix memory efficiency', async () => { const module = runner.getModule(); const { Matrix } = module; const size = 100; const matrix = Matrix.zeros(size, size); assert.ok(matrix.data instanceof Float64Array); assert.equal(matrix.data.length, size * size); if (runner.wasmBuilt) { // In real WASM, memory should be efficiently managed assert.equal(matrix.data.byteLength, size * size * 8); } }); // WASM Solver Interface Tests runner.test('WASM SublinearSolver initialization', async () => { const module = runner.getModule(); const { SublinearSolver } = module; const solver = new SublinearSolver({ maxIterations: 1000, tolerance: 1e-10, simdEnabled: true }); await solver.initialize(); assert.equal(solver.initialized, true); }); runner.test('WASM SublinearSolver basic solve operation', async () => { const module = runner.getModule(); const { SublinearSolver, Matrix } = module; const solver = new SublinearSolver(); const matrix = Matrix.identity(3); const vector = new Float64Array([1, 2, 3]); const solution = await solver.solve(matrix, vector); assert.ok(solution instanceof Float64Array); assert.equal(solution.length, 3); if (runner.wasmBuilt) { // With real WASM, we expect accurate solutions // For identity matrix, solution should equal input vector assert.ok(Math.abs(solution[0] - 1) < 1e-10); assert.ok(Math.abs(solution[1] - 2) < 1e-10); assert.ok(Math.abs(solution[2] - 3) < 1e-10); } }); runner.test('WASM memory usage tracking', async () => { const module = runner.getModule(); const { SublinearSolver } = module; const solver = new SublinearSolver(); await solver.initialize(); const memoryUsage = solver.getMemoryUsage(); assert.ok(typeof memoryUsage.used === 'number'); assert.ok(typeof memoryUsage.capacity === 'number'); assert.ok(memoryUsage.js); if (runner.wasmBuilt) { assert.ok(memoryUsage.used > 0); assert.ok(memoryUsage.capacity > 0); } }); // WASM Utils Interface Tests runner.test('WASM Utils feature detection', async () => { const module = runner.getModule(); const { Utils } = module; const features = await Utils.getFeatures(); assert.ok(typeof features === 'object'); if (runner.wasmBuilt) { assert.ok(typeof features.simd === 'boolean'); assert.ok(typeof features.threads === 'number'); } else { assert.ok(features.mock === true); } }); runner.test('WASM Utils SIMD detection', async () => { const module = runner.getModule(); const { Utils } = module; const simdEnabled = await Utils.isSIMDEnabled(); assert.ok(typeof simdEnabled === 'boolean'); }); runner.test('WASM Utils matrix multiply benchmark', async () => { const module = runner.getModule(); const { Utils } = module; const result = await Utils.benchmarkMatrixMultiply(100); assert.ok(typeof result.time === 'number'); assert.ok(typeof result.operations === 'number'); assert.ok(result.time > 0); assert.ok(result.operations > 0); }); runner.test('WASM Utils memory usage', async () => { const module = runner.getModule(); const { Utils } = module; const memoryUsage = await Utils.getWasmMemoryUsage(); assert.ok(typeof memoryUsage === 'object'); assert.ok(typeof memoryUsage.used === 'number'); assert.ok(typeof memoryUsage.total === 'number'); }); // WASM Performance Tests runner.test('WASM vs JS performance comparison', async () => { const module = runner.getModule(); const { Matrix, SublinearSolver } = module; const size = 50; const matrix = Matrix.identity(size); const vector = new Float64Array(size).fill(1); // Time WASM solver const solver = new SublinearSolver(); const startTime = Date.now(); await solver.solve(matrix, vector); const wasmTime = Date.now() - startTime; assert.ok(wasmTime >= 0); if (runner.wasmBuilt) { // WASM should be reasonably fast assert.ok(wasmTime < 1000, `WASM solve took too long: ${wasmTime}ms`); } console.log(` WASM solve time: ${wasmTime}ms`); }); runner.test('WASM large matrix handling', async () => { const module = runner.getModule(); const { Matrix, SublinearSolver } = module; const size = runner.wasmBuilt ? 200 : 50; // Smaller for mock tests const matrix = Matrix.identity(size); const vector = new Float64Array(size).fill(1); const solver = new SublinearSolver({ maxIterations: 100, tolerance: 1e-8 }); const solution = await solver.solve(matrix, vector); assert.equal(solution.length, size); const memoryUsage = solver.getMemoryUsage(); assert.ok(memoryUsage.used > 0); console.log(` Matrix size: ${size}x${size}, Memory used: ${memoryUsage.used} bytes`); }); // WASM Error Handling Tests runner.test('WASM graceful error handling', async () => { const module = runner.getModule(); const { SublinearSolver } = module; const solver = new SublinearSolver(); if (runner.wasmBuilt) { // Test with incompatible matrix/vector dimensions try { const matrix = module.Matrix.identity(3); const vector = new Float64Array([1, 2]); // Wrong size await solver.solve(matrix, vector); assert.fail('Should have thrown error for dimension mismatch'); } catch (error) { assert.ok(error.message.length > 0); } } else { // Mock test - just verify error handling structure exists assert.ok(typeof solver.solve === 'function'); } }); // WASM Resource Cleanup Tests runner.test('WASM resource cleanup', async () => { const module = runner.getModule(); const { SublinearSolver } = module; const solver = new SublinearSolver(); await solver.initialize(); const memoryBefore = solver.getMemoryUsage(); assert.ok(memoryBefore.used >= 0); solver.dispose(); assert.equal(solver.initialized, false); if (runner.wasmBuilt) { // After disposal, memory should be cleaned up // Note: This test might need adjustment based on actual WASM implementation const memoryAfter = solver.getMemoryUsage(); assert.ok(memoryAfter.used >= 0); } }); // WASM Integration Tests runner.test('WASM full workflow integration', async () => { const module = runner.getModule(); const { Matrix, SublinearSolver } = module; // Create a linear system const size = 4; const matrix = Matrix.identity(size); matrix.set(0, 1, 0.5); matrix.set(1, 0, 0.5); const vector = new Float64Array([1, 2, 3, 4]); // Solve the system const solver = new SublinearSolver({ maxIterations: 100, tolerance: 1e-10 }); const solution = await solver.solve(matrix, vector); // Verify solution assert.equal(solution.length, size); // Check memory usage const memory = solver.getMemoryUsage(); assert.ok(memory.used > 0); // Get features const features = await module.Utils.getFeatures(); assert.ok(features); // Cleanup solver.dispose(); assert.equal(solver.initialized, false); console.log(` Features: ${JSON.stringify(features)}`); console.log(` Memory used: ${memory.used} bytes`); }); // WASM Build Information Tests runner.test('WASM build information validation', async () => { if (!runner.wasmBuilt) { console.log(' Would validate build info after WASM build'); return; } const pkgPath = path.join(__dirname, '../../pkg/package.json'); try { const content = await fs.readFile(pkgPath, 'utf8'); const pkg = JSON.parse(content); assert.ok(pkg.name); assert.ok(pkg.version); assert.ok(pkg.files); } catch (error) { console.warn(' Could not read package.json from pkg directory'); } // Check for build info if available const buildInfoPath = path.join(__dirname, '../../pkg/build_info.json'); try { const content = await fs.readFile(buildInfoPath, 'utf8'); const buildInfo = JSON.parse(content); assert.ok(buildInfo.build_date); assert.ok(buildInfo.rust_version); assert.ok(buildInfo.target); console.log(` Build date: ${buildInfo.build_date}`); console.log(` Rust version: ${buildInfo.rust_version}`); } catch (error) { console.log(' Build info not available (expected for mock tests)'); } }); // Run all tests if (require.main === module) { runner.run().then(success => { process.exit(success ? 0 : 1); }).catch(error => { console.error('Test runner failed:', error); process.exit(1); }); } module.exports = { WASMTestRunner, runner };