run.js 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. #!/usr/bin/env node
  2. "use strict";
  3. const fs = require("fs");
  4. const path = require("path");
  5. const process = require("process");
  6. const { spawnSync } = require("child_process");
  7. const libwabt = require("../../build/libwabt.js");
  8. try {
  9. var V86 = require("../../build/libv86-debug.js").V86;
  10. }
  11. catch(e) {
  12. console.error(e);
  13. console.error("Failed to import build/libv86-debug.js. Run " +
  14. "`make build/libv86-debug.js` first.");
  15. process.exit(1);
  16. }
  17. const LOG_LEVEL = 0;
  18. const GIT_DIFF_FLAGS = [ "--no-index", "--patience", "--color=always"];
  19. const TEST_DIR = path.join(__dirname, "tests");
  20. const BUILD_DIR = path.join(TEST_DIR, "build");
  21. function run_all()
  22. {
  23. const asm_files = fs.readdirSync(TEST_DIR).filter(filename => filename.endsWith(".asm"));
  24. const files = asm_files.map(asm_file => {
  25. const name = asm_file.slice(0, -4);
  26. return {
  27. name,
  28. expect_file: path.relative(".", path.join(TEST_DIR, name + ".wast")),
  29. actual_file: path.relative(".", path.join(BUILD_DIR, name + ".actual.wast")),
  30. actual_wasm: path.relative(".", path.join(BUILD_DIR, name + ".wasm")),
  31. asm_file: path.join(TEST_DIR, name + ".asm"),
  32. executable_file: path.join(BUILD_DIR, name + ".bin"),
  33. };
  34. });
  35. next_test(0);
  36. function next_test(i)
  37. {
  38. if(files[i])
  39. {
  40. run_test(files[i], () => next_test(i + 1));
  41. }
  42. }
  43. }
  44. let stdin_data = "";
  45. let stdin_buffer = Buffer.alloc(100);
  46. const stdin = fs.openSync("/dev/stdin", "r");
  47. function readline()
  48. {
  49. const bytesRead = fs.readSync(stdin, stdin_buffer, 0, stdin_buffer.length, null);
  50. stdin_data += stdin_buffer.slice(0, bytesRead).toString();
  51. const nl = stdin_data.indexOf("\n");
  52. if(nl === -1)
  53. {
  54. return readline();
  55. }
  56. const line = stdin_data.slice(0, nl);
  57. stdin_data = stdin_data.slice(nl + 1);
  58. return line;
  59. }
  60. function run_test({ name, executable_file, expect_file, actual_file, actual_wasm, asm_file }, onfinished)
  61. {
  62. const emulator = new V86({
  63. autostart: false,
  64. memory_size: 2 * 1024 * 1024,
  65. log_level: LOG_LEVEL,
  66. });
  67. const executable = fs.readFileSync(executable_file);
  68. const asm = fs.readFileSync(asm_file);
  69. const is_32 = asm.includes("BITS 32\n");
  70. emulator.add_listener("emulator-loaded", function()
  71. {
  72. const cpu = emulator.v86.cpu;
  73. const hook_not_called_timeout = setTimeout(() => {
  74. throw new Error("Hook for code generation not called");
  75. }, 1000);
  76. cpu.test_hook_did_generate_wasm = function(wasm)
  77. {
  78. const wast = disassemble_wasm(wasm);
  79. clearTimeout(hook_not_called_timeout);
  80. fs.writeFileSync(actual_file, wast);
  81. fs.writeFileSync(actual_wasm, wasm);
  82. cpu.test_hook_did_generate_wasm = function()
  83. {
  84. cpu.test_hook_did_generate_wasm = function() {};
  85. throw new Error("Hook for wasm generation called multiple times");
  86. };
  87. if(!fs.existsSync(expect_file))
  88. {
  89. // enhanced workflow: If file doesn't exist yet print full diff
  90. var expect_file_for_diff = "/dev/null";
  91. }
  92. else
  93. {
  94. expect_file_for_diff = expect_file;
  95. }
  96. const result = spawnSync("git",
  97. [].concat(
  98. "diff",
  99. GIT_DIFF_FLAGS,
  100. expect_file_for_diff,
  101. actual_file
  102. ),
  103. { encoding: "utf8" });
  104. if(result.status)
  105. {
  106. console.log(result.stdout);
  107. console.log(result.stderr);
  108. if(process.argv.includes("--interactive"))
  109. {
  110. while(true)
  111. {
  112. console.log("Pick: [y] Accept this change and overwrite, [n] Don't accept this change, [q] Quit");
  113. const choice = readline();
  114. if(choice === "y")
  115. {
  116. console.log(`Running: cp ${actual_file} ${expect_file}`);
  117. fs.copyFileSync(actual_file, expect_file);
  118. break;
  119. }
  120. else if(choice === "n")
  121. {
  122. break;
  123. }
  124. else if(choice === "q")
  125. {
  126. process.exit(1);
  127. }
  128. }
  129. }
  130. else
  131. {
  132. const failure_message = `${name}.asm failed:
  133. The code generator produced different code. If you believe this change is intentional,
  134. verify the diff above and run the following command to accept the change:
  135. cp ${actual_file} ${expect_file}
  136. When done, re-run this test to confirm that all expect-tests pass.
  137. Hint: Use tests/expect/run.js --interactive to interactively accept changes.
  138. `;
  139. console.log(failure_message);
  140. process.exit(1);
  141. }
  142. }
  143. else
  144. {
  145. console.log("%s ok", name);
  146. console.assert(!result.stdout);
  147. console.assert(!result.stderr);
  148. }
  149. onfinished();
  150. };
  151. if(is_32)
  152. {
  153. cpu.is_32[0] = true;
  154. cpu.stack_size_32[0] = true;
  155. }
  156. const START_ADDRESS = 0x1000;
  157. cpu.mem8.set(executable, START_ADDRESS);
  158. cpu.jit_force_generate(START_ADDRESS);
  159. });
  160. }
  161. function disassemble_wasm(wasm)
  162. {
  163. // Need to make a small copy otherwise libwabt goes nuts trying to copy
  164. // the whole underlying buffer
  165. wasm = wasm.slice();
  166. try
  167. {
  168. var module = libwabt.readWasm(wasm, { readDebugNames: false });
  169. module.generateNames();
  170. module.applyNames();
  171. return module.toText({ foldExprs: true, inlineExport: true });
  172. }
  173. finally
  174. {
  175. module && module.destroy();
  176. }
  177. }
  178. run_all();