run.js 31 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042
  1. #!/usr/bin/env node
  2. "use strict";
  3. process.on("unhandledRejection", exn => { throw exn; });
  4. var TIMEOUT_EXTRA_FACTOR = +process.env.TIMEOUT_EXTRA_FACTOR || 1;
  5. var MAX_PARALLEL_TESTS = +process.env.MAX_PARALLEL_TESTS || 4;
  6. var TEST_NAME = process.env.TEST_NAME;
  7. const TEST_RELEASE_BUILD = +process.env.TEST_RELEASE_BUILD;
  8. const VERBOSE = false;
  9. const RUN_SLOW_TESTS = false;
  10. try
  11. {
  12. var V86 = require(`../../build/${TEST_RELEASE_BUILD ? "libv86" : "libv86-debug"}.js`).V86;
  13. }
  14. catch(e)
  15. {
  16. console.error("Failed to import build/libv86-debug.js. Run `make build/libv86-debug.js first.");
  17. process.exit(1);
  18. }
  19. const assert = require("assert").strict;
  20. var cluster = require("cluster");
  21. var os = require("os");
  22. var fs = require("fs");
  23. var root_path = __dirname + "/../..";
  24. var SCREEN_WIDTH = 80;
  25. function get_line(screen, y)
  26. {
  27. return screen.subarray(y * SCREEN_WIDTH, (y + 1) * SCREEN_WIDTH);
  28. }
  29. function line_to_text(screen, y)
  30. {
  31. return bytearray_to_string(get_line(screen, y));
  32. }
  33. function string_to_bytearray(str)
  34. {
  35. return new Uint8Array(str.split("").map(chr => chr.charCodeAt(0)));
  36. }
  37. function bytearray_to_string(arr)
  38. {
  39. return String.fromCharCode.apply(String, arr);
  40. }
  41. function screen_to_text(s)
  42. {
  43. var result = [];
  44. result.push("+==================================== SCREEN ====================================+");
  45. for(var i = 0; i < 25; i++)
  46. {
  47. var line = line_to_text(s, i);
  48. result.push("|" + line + "|");
  49. }
  50. result.push("+================================================================================+");
  51. return result.join("\n");
  52. }
  53. function send_work_to_worker(worker, message)
  54. {
  55. if(current_test < tests.length)
  56. {
  57. worker.send(tests[current_test]);
  58. current_test++;
  59. }
  60. else
  61. {
  62. worker.disconnect();
  63. }
  64. }
  65. if(cluster.isMaster)
  66. {
  67. var tests = [
  68. {
  69. name: "FreeDOS boot",
  70. fda: root_path + "/images/freedos722.img",
  71. timeout: 20,
  72. expected_texts: [
  73. "Welcome to FreeDOS",
  74. ],
  75. },
  76. {
  77. name: "FreeDOS boot with Bochs BIOS",
  78. fda: root_path + "/images/freedos722.img",
  79. timeout: 20,
  80. alternative_bios: true,
  81. expected_texts: [
  82. "Welcome to FreeDOS",
  83. ],
  84. },
  85. {
  86. name: "Windows 1.01 boot",
  87. fda: root_path + "/images/windows101.img",
  88. timeout: 10,
  89. expect_graphical_mode: true,
  90. expect_mouse_registered: true,
  91. },
  92. {
  93. name: "Sol OS",
  94. fda: root_path + "/images/os8.img",
  95. timeout: 20,
  96. expect_graphical_mode: true,
  97. expect_mouse_registered: true,
  98. actions: [
  99. {
  100. on_text: " or press",
  101. run: "\n"
  102. },
  103. ],
  104. },
  105. {
  106. name: "Linux",
  107. cdrom: root_path + "/images/linux.iso",
  108. timeout: 90,
  109. expected_texts: [
  110. "/root%",
  111. "test passed",
  112. ],
  113. actions: [
  114. {
  115. on_text: "/root%",
  116. run: "cd tests; ./test-i386 > emu.test; diff emu.test reference.test && echo test pas''sed || echo failed\n",
  117. },
  118. ],
  119. },
  120. //{
  121. // name: "Windows 98",
  122. // skip_if_disk_image_missing: true,
  123. // hda: root_path + "/images/windows98.img",
  124. // timeout: 60,
  125. // expect_graphical_mode: true,
  126. // expect_graphical_size: [800, 600],
  127. // expect_mouse_registered: true,
  128. // failure_allowed: true,
  129. //},
  130. {
  131. name: "Windows 95",
  132. skip_if_disk_image_missing: true,
  133. hda: root_path + "/images/w95.img",
  134. timeout: 60,
  135. expect_graphical_mode: true,
  136. expect_graphical_size: [1024, 768],
  137. expect_mouse_registered: true,
  138. failure_allowed: true,
  139. },
  140. {
  141. name: "Oberon",
  142. skip_if_disk_image_missing: true,
  143. hda: root_path + "/images/oberon.img",
  144. timeout: 30,
  145. expect_graphical_mode: true,
  146. expect_mouse_registered: true,
  147. },
  148. {
  149. name: "Linux 3",
  150. skip_if_disk_image_missing: true,
  151. cdrom: root_path + "/images/linux3.iso",
  152. timeout: 200,
  153. expected_texts: [
  154. "test passed",
  155. ],
  156. actions: [
  157. {
  158. on_text: "~%",
  159. run: "head -c 10000 /dev/urandom > rand; echo test pas''sed\n",
  160. after: 1000,
  161. },
  162. ],
  163. },
  164. {
  165. name: "KolibriOS",
  166. fda: root_path + "/images/kolibri.img",
  167. timeout: 120,
  168. expect_graphical_mode: true,
  169. expect_mouse_registered: true,
  170. },
  171. {
  172. name: "Linux with Bochs BIOS",
  173. cdrom: root_path + "/images/linux.iso",
  174. timeout: 90,
  175. expected_texts: [
  176. "/root%",
  177. "test passed",
  178. ],
  179. alternative_bios: true,
  180. actions: [
  181. {
  182. on_text: "/root%",
  183. run: "cd tests; ./test-i386 > emu.test; diff emu.test reference.test && echo test pas''sed || echo failed\n",
  184. },
  185. ],
  186. },
  187. {
  188. name: "MS-DOS",
  189. skip_if_disk_image_missing: true,
  190. hda: root_path + "/images/msdos.img",
  191. timeout: 90,
  192. expected_texts: [
  193. "C:\\>",
  194. ],
  195. },
  196. {
  197. name: "Linux 4",
  198. skip_if_disk_image_missing: true,
  199. cdrom: root_path + "/images/linux4.iso",
  200. timeout: 200,
  201. expected_texts: [
  202. "~%",
  203. ],
  204. expected_serial_text: [
  205. "Files send via emulator appear in",
  206. ],
  207. expect_mouse_registered: true,
  208. },
  209. {
  210. name: "Linux bzImage",
  211. bzimage: root_path + "/images/buildroot-bzimage.bin",
  212. cmdline: "auto",
  213. timeout: 200,
  214. expected_texts: [
  215. "~%",
  216. ],
  217. expected_serial_text: [
  218. "Files send via emulator appear in",
  219. ],
  220. expect_mouse_registered: true,
  221. },
  222. {
  223. name: "Linux with bzImage from filesystem",
  224. bzimage_initrd_from_filesystem: true,
  225. filesystem: {
  226. basefs: root_path + "/build/integration-test-fs/fs.json",
  227. baseurl: root_path + "/build/integration-test-fs/flat/",
  228. },
  229. cmdline: "auto",
  230. timeout: 200,
  231. expected_texts: [
  232. "~%",
  233. ],
  234. expected_serial_text: [
  235. "Files send via emulator appear in",
  236. ],
  237. expect_mouse_registered: true,
  238. },
  239. {
  240. name: "QNX",
  241. skip_if_disk_image_missing: true,
  242. fda: root_path + "/images/qnx-demo-network-4.05.img",
  243. timeout: 300,
  244. expect_mouse_registered: true,
  245. expect_graphical_mode: true,
  246. expect_graphical_size: [640, 480],
  247. actions: [
  248. { run: " ", after: 30 * 1000 },
  249. { run: " ", after: 15 * 1000 },
  250. { run: " ", after: 15 * 1000 },
  251. { run: " ", after: 15 * 1000 },
  252. { run: " ", after: 15 * 1000 },
  253. { run: " ", after: 15 * 1000 },
  254. { run: " ", after: 15 * 1000 },
  255. ],
  256. },
  257. {
  258. name: "OpenBSD Floppy",
  259. fda: root_path + "/images/openbsd-floppy.img",
  260. timeout: 180,
  261. expected_texts: ["(I)nstall, (U)pgrade or (S)hell"],
  262. },
  263. {
  264. name: "OpenBSD",
  265. skip_if_disk_image_missing: true,
  266. hda: root_path + "/images/openbsd.img",
  267. timeout: 300,
  268. actions: [
  269. {
  270. on_text: "boot>",
  271. run: "boot -c\n",
  272. },
  273. {
  274. on_text: "UKC>",
  275. run: "disable mpbios\nexit\n",
  276. },
  277. {
  278. on_text: "login:",
  279. run: "root\n",
  280. },
  281. {
  282. on_text: "Password:",
  283. run: "root\n",
  284. },
  285. ],
  286. expected_texts: ["nyu# "],
  287. },
  288. {
  289. name: "Windows 3.0",
  290. slow: 1,
  291. skip_if_disk_image_missing: true,
  292. timeout: 10 * 60,
  293. cdrom: root_path + "/images/experimental/os/Win30.iso",
  294. expected_texts: [
  295. "Press any key to continue",
  296. " **************************************************",
  297. ],
  298. expect_graphical_mode: true,
  299. expect_mouse_registered: true,
  300. actions: [
  301. {
  302. on_text: "Press any key to continue . . .",
  303. after: 1000,
  304. run: "x",
  305. },
  306. {
  307. on_text: " **************************************************",
  308. after: 1000,
  309. run: "x",
  310. },
  311. {
  312. on_text: "C> ",
  313. after: 1000,
  314. run: "win\n",
  315. },
  316. ],
  317. },
  318. {
  319. name: "FreeBSD",
  320. skip_if_disk_image_missing: true,
  321. timeout: 15 * 60,
  322. hda: root_path + "/images/internal/freebsd/freebsd.img",
  323. expected_texts: [
  324. "FreeBSD/i386 (nyu) (ttyv0)",
  325. "root@nyu:~ #",
  326. ],
  327. actions: [
  328. {
  329. on_text: " Autoboot in",
  330. run: "\n",
  331. },
  332. {
  333. // workaround for freebsd not accepting key inputs just before the boot prompt
  334. // (probably needs delay between keydown and keyup)
  335. on_text: "FreeBSD/i386 (nyu) (ttyv0)",
  336. run: "\x08", // backspace to avoid messing with login prompt
  337. },
  338. {
  339. on_text: "login:",
  340. after: 1000,
  341. run: "root\n",
  342. },
  343. {
  344. on_text: "Password:",
  345. after: 1000,
  346. run: "\n",
  347. },
  348. ],
  349. },
  350. {
  351. name: "FreeBSD cdrom",
  352. skip_if_disk_image_missing: true,
  353. slow: 1,
  354. timeout: 10 * 60,
  355. cdrom: root_path + "/images/experimental/os/FreeBSD-11.0-RELEASE-i386-bootonly.iso",
  356. expected_texts: ["Welcome to FreeBSD!"],
  357. actions: [
  358. {
  359. on_text: " Autoboot in ",
  360. run: "\n",
  361. }
  362. ],
  363. },
  364. {
  365. name: "Arch Linux",
  366. skip_if_disk_image_missing: true,
  367. timeout: 20 * 60,
  368. bzimage_initrd_from_filesystem: true,
  369. cmdline: [
  370. "rw apm=off vga=0x344 video=vesafb:ypan,vremap:8",
  371. "root=host9p rootfstype=9p rootflags=trans=virtio,cache=loose mitigations=off",
  372. "audit=0 init=/usr/bin/init-openrc net.ifnames=0 biosdevname=0",
  373. ].join(" "),
  374. filesystem: {
  375. basefs: "images/fs.json",
  376. baseurl: "images/arch-nongz/",
  377. },
  378. expected_texts: [
  379. "root@localhost",
  380. "aaaaaaaaaaaaaaaaaaaa",
  381. "Hello, world",
  382. "Hello from JS",
  383. "Hello from OCaml",
  384. "Compress okay",
  385. ],
  386. actions: [
  387. {
  388. on_text: "root@localhost",
  389. run: `python -c 'print(100 * "a")'\n`,
  390. },
  391. {
  392. on_text: "aaaaaaaaaaaaaaaaaaaa",
  393. run: `gcc hello.c && ./a.out\n`,
  394. },
  395. {
  396. on_text: "Hello, world",
  397. run: `echo 'console.log("Hello from JS")' | node\n`,
  398. },
  399. {
  400. on_text: "Hello from JS",
  401. run: `echo 'print_endline "Hello from OCaml"' > hello.ml && ocamlopt hello.ml && ./a.out\n`,
  402. },
  403. {
  404. on_text: "Hello from OCaml",
  405. run:
  406. "zstd hello.c && gzip -k hello.c && bzip2 -k hello.c && xz -k hello.c && lzma -k hello.c && " +
  407. "zstdcat hello.c.zst && zcat hello.c.gz && bzcat hello.c.bz2 && xzcat hello.c.xz && lzmadec hello.c.lzma && " +
  408. "echo Compress okay\n",
  409. },
  410. {
  411. on_text: "Compress okay",
  412. run: "./startx.sh\n",
  413. },
  414. ],
  415. expect_graphical_mode: true,
  416. expect_graphical_size: [1024, 768],
  417. expect_mouse_registered: true,
  418. },
  419. {
  420. name: "FreeGEM",
  421. skip_if_disk_image_missing: true,
  422. timeout: 60,
  423. hda: root_path + "/images/experimental/os/freegem.bin",
  424. expect_graphical_mode: true,
  425. expect_mouse_registered: true,
  426. actions: [
  427. {
  428. on_text: " Select from Menu",
  429. run: "3",
  430. }
  431. ],
  432. },
  433. {
  434. name: "Haiku",
  435. skip_if_disk_image_missing: true,
  436. timeout: 15 * 60,
  437. memory_size: 512 * 1024 * 1024,
  438. hda: root_path + "/images/haiku-r1beta2-hrev54154_111-x86_gcc2h-anyboot.iso",
  439. expected_serial_text: [
  440. "init_hardware()",
  441. "Running post install script /boot/system/boot/post-install/sshd_keymaker.sh",
  442. // After pressing enter in the boot dialog:
  443. "Running first login script /boot/system/boot/first-login/default_deskbar_items.sh",
  444. ],
  445. expect_graphical_mode: true,
  446. expect_graphical_size: [1024, 768],
  447. expect_mouse_registered: true,
  448. actions: [
  449. { after: 1 * 60 * 1000, run: "\n" },
  450. { after: 2 * 60 * 1000, run: "\n" },
  451. { after: 3 * 60 * 1000, run: "\n" },
  452. { after: 4 * 60 * 1000, run: "\n" },
  453. { after: 5 * 60 * 1000, run: "\n" },
  454. { after: 6 * 60 * 1000, run: "\n" },
  455. { after: 7 * 60 * 1000, run: "\n" },
  456. { after: 8 * 60 * 1000, run: "\n" },
  457. ],
  458. },
  459. {
  460. name: "9front",
  461. skip_if_disk_image_missing: true,
  462. acpi: true,
  463. timeout: 5 * 60,
  464. hda: root_path + "/images/9front-7781.38dcaeaa222c.386.iso",
  465. expect_graphical_mode: true,
  466. expect_graphical_size: [1024, 768],
  467. expect_mouse_registered: true,
  468. actions: [
  469. { after: 60 * 1000, run: "\n" },
  470. { after: 65 * 1000, run: "\n" },
  471. { after: 70 * 1000, run: "\n" },
  472. { after: 75 * 1000, run: "\n" },
  473. { after: 80 * 1000, run: "\n" },
  474. { after: 85 * 1000, run: "\n" },
  475. { after: 90 * 1000, run: "\n" },
  476. { after: 95 * 1000, run: "\n" },
  477. { after: 100 * 1000, run: "\n" },
  478. { after: 105 * 1000, run: "\n" },
  479. { after: 110 * 1000, run: "\n" },
  480. { after: 115 * 1000, run: "\n" },
  481. { after: 120 * 1000, run: "\n" },
  482. ],
  483. },
  484. {
  485. name: "ReactOS",
  486. skip_if_disk_image_missing: true,
  487. timeout: 10 * 60,
  488. hda: root_path + "/images/reactos-livecd-0.4.15-dev-73-g03c09c9-x86-gcc-lin-dbg.iso",
  489. expect_graphical_mode: true,
  490. expect_graphical_size: [800, 600],
  491. expect_mouse_registered: true,
  492. actions: [
  493. { after: 1 * 60 * 1000, run: "\n" },
  494. { after: 2 * 60 * 1000, run: "\n" },
  495. { after: 3 * 60 * 1000, run: "\n" },
  496. { after: 4 * 60 * 1000, run: "\n" },
  497. { after: 5 * 60 * 1000, run: "\n" },
  498. { after: 6 * 60 * 1000, run: "\n" },
  499. { after: 7 * 60 * 1000, run: "\n" },
  500. { after: 8 * 60 * 1000, run: "\n" },
  501. ],
  502. expected_serial_text: [
  503. "DnsIntCacheInitialize()",
  504. // when desktop is rendered:
  505. "err: Attempted to close thread desktop",
  506. ],
  507. },
  508. {
  509. name: "ReactOS CD",
  510. skip_if_disk_image_missing: true,
  511. timeout: 10 * 60,
  512. cdrom: root_path + "/images/reactos-livecd-0.4.15-dev-73-g03c09c9-x86-gcc-lin-dbg.iso",
  513. expect_graphical_mode: true,
  514. expect_graphical_size: [800, 600],
  515. expect_mouse_registered: true,
  516. expected_serial_text: ["DnsIntCacheInitialize()"],
  517. },
  518. {
  519. name: "HelenOS",
  520. skip_if_disk_image_missing: true,
  521. timeout: 3 * 60,
  522. cdrom: root_path + "/images/experimental/os/HelenOS-0.5.0-ia32.iso",
  523. expect_graphical_mode: true,
  524. expect_mouse_registered: true,
  525. },
  526. {
  527. name: "Minix",
  528. skip_if_disk_image_missing: true,
  529. timeout: 60,
  530. hda: root_path + "/images/experimental/os/minix2hd.img",
  531. actions: [
  532. {
  533. on_text: " = Start Minix",
  534. run: "=",
  535. },
  536. {
  537. on_text: "noname login:",
  538. run: "root\n",
  539. },
  540. ],
  541. expected_texts: ["noname login:", "# "],
  542. },
  543. {
  544. name: "Minix CD",
  545. skip_if_disk_image_missing: true,
  546. timeout: 3 * 60,
  547. cdrom: root_path + "/images/minix-3.3.0.iso",
  548. actions: [
  549. {
  550. on_text: "login:",
  551. run: "root\n",
  552. },
  553. ],
  554. expected_texts: ["login:", "We'd like your feedback", "# "],
  555. },
  556. {
  557. name: "Mobius",
  558. skip_if_disk_image_missing: true,
  559. timeout: 2 * 60,
  560. fda: root_path + "/images/mobius-fd-release5.img",
  561. expect_graphical_mode: true,
  562. actions: [
  563. {
  564. on_text: " The highlighted entry will be booted automatically",
  565. run: "\n",
  566. },
  567. ],
  568. },
  569. {
  570. name: "Tiny Core 11 CD",
  571. skip_if_disk_image_missing: 1,
  572. timeout: 5 * 60,
  573. cdrom: root_path + "/images/TinyCore-11.0.iso",
  574. expect_graphical_mode: true,
  575. expect_mouse_registered: true,
  576. actions: [{ on_text: " BIOS default device boot in", run: "\n", after: 5000 }],
  577. },
  578. {
  579. name: "Tiny Core 11 HD",
  580. skip_if_disk_image_missing: 1,
  581. timeout: 5 * 60,
  582. cdrom: root_path + "/images/TinyCore-11.0.iso",
  583. expect_graphical_mode: true,
  584. expect_mouse_registered: true,
  585. actions: [{ on_text: " BIOS default device boot in", run: "\n", after: 5000 }],
  586. },
  587. {
  588. name: "Core 9",
  589. skip_if_disk_image_missing: 1,
  590. timeout: 5 * 60,
  591. cdrom: root_path + "/images/experimental/os/Core-9.0.iso",
  592. expected_texts: ["tc@box"],
  593. actions: [{ on_text: "boot:", run: "\n" }],
  594. },
  595. {
  596. name: "Core 8",
  597. skip_if_disk_image_missing: 1,
  598. timeout: 5 * 60,
  599. cdrom: root_path + "/images/experimental/os/Core-8.0.iso",
  600. expected_texts: ["tc@box"],
  601. actions: [{ on_text: "boot:", run: "\n" }],
  602. },
  603. {
  604. name: "Core 7",
  605. skip_if_disk_image_missing: 1,
  606. timeout: 5 * 60,
  607. cdrom: root_path + "/images/experimental/os/Core-7.2.iso",
  608. expected_texts: ["tc@box"],
  609. actions: [{ on_text: "boot:", run: "\n" }],
  610. },
  611. {
  612. name: "Core 6",
  613. skip_if_disk_image_missing: 1,
  614. timeout: 5 * 60,
  615. cdrom: root_path + "/images/experimental/os/Core-6.4.1.iso",
  616. expected_texts: ["tc@box"],
  617. actions: [{ on_text: "boot:", run: "\n" }],
  618. },
  619. {
  620. name: "Core 5",
  621. skip_if_disk_image_missing: 1,
  622. timeout: 5 * 60,
  623. cdrom: root_path + "/images/experimental/os/Core-5.4.iso",
  624. expected_texts: ["tc@box"],
  625. actions: [{ on_text: "boot:", run: "\n" }],
  626. },
  627. {
  628. name: "Core 4",
  629. skip_if_disk_image_missing: 1,
  630. timeout: 5 * 60,
  631. cdrom: root_path + "/images/experimental/os/Core-4.7.7.iso",
  632. expected_texts: ["tc@box"],
  633. actions: [{ on_text: "boot:", run: "\n" }],
  634. },
  635. {
  636. name: "Damn Small Linux",
  637. skip_if_disk_image_missing: 1,
  638. timeout: 5 * 60,
  639. cdrom: root_path + "/images/dsl-4.11.rc2.iso",
  640. expect_graphical_mode: true,
  641. expect_graphical_size: [1024, 768],
  642. expect_mouse_registered: true,
  643. },
  644. ];
  645. if(TEST_NAME)
  646. {
  647. tests = tests.filter(test => test.name === TEST_NAME);
  648. }
  649. var nr_of_cpus = Math.min(Math.round(os.cpus().length / 2) || 1, tests.length, MAX_PARALLEL_TESTS);
  650. console.log("Using %d cpus", nr_of_cpus);
  651. var current_test = 0;
  652. for(var i = 0; i < nr_of_cpus; i++)
  653. {
  654. var worker = cluster.fork();
  655. worker.on("message", send_work_to_worker.bind(null, worker));
  656. worker.on("online", send_work_to_worker.bind(null, worker));
  657. worker.on("exit", function(code, signal)
  658. {
  659. if(signal)
  660. {
  661. console.warn("Worker killed by signal " + signal);
  662. process.exit(1);
  663. }
  664. else if(code !== 0)
  665. {
  666. process.exit(code);
  667. }
  668. });
  669. worker.on("error", function(error)
  670. {
  671. console.error("Worker error: ", error.toString(), error);
  672. process.exit(1);
  673. });
  674. }
  675. }
  676. else
  677. {
  678. cluster.worker.on("message", function(test_case)
  679. {
  680. run_test(test_case, function()
  681. {
  682. process.send("I'm done");
  683. });
  684. });
  685. }
  686. function bytearray_starts_with(arr, search)
  687. {
  688. for(var i = 0; i < search.length; i++)
  689. {
  690. if(arr[i] !== search[i])
  691. {
  692. return false;
  693. }
  694. }
  695. return true;
  696. }
  697. function run_test(test, done)
  698. {
  699. console.log("Starting test: %s", test.name);
  700. let image = test.fda || test.hda || test.cdrom || test.bzimage || test.filesystem.basefs;
  701. assert(image, "Bootable drive expected");
  702. if(!fs.existsSync(image))
  703. {
  704. if(test.skip_if_disk_image_missing)
  705. {
  706. console.warn("Missing disk image: " + image + ", test skipped");
  707. console.warn();
  708. done();
  709. return;
  710. }
  711. else
  712. {
  713. console.warn("Missing disk image: " + image);
  714. process.exit(1);
  715. }
  716. }
  717. if(test.slow && !RUN_SLOW_TESTS)
  718. {
  719. console.warn("Slow test: " + test.name + ", skipped");
  720. console.warn();
  721. done();
  722. return;
  723. }
  724. if(test.alternative_bios)
  725. {
  726. var bios = root_path + "/bios/bochs-bios.bin";
  727. var vga_bios = root_path + "/bios/bochs-vgabios.bin";
  728. }
  729. else if(TEST_RELEASE_BUILD)
  730. {
  731. var bios = root_path + "/bios/seabios.bin";
  732. var vga_bios = root_path + "/bios/vgabios.bin";
  733. }
  734. else
  735. {
  736. var bios = root_path + "/bios/seabios-debug.bin";
  737. var vga_bios = root_path + "/bios/vgabios-debug.bin";
  738. }
  739. var settings = {
  740. bios: { url: bios },
  741. vga_bios: { url: vga_bios },
  742. autostart: true,
  743. memory_size: test.memory_size || 128 * 1024 * 1024,
  744. log_level: 0,
  745. cmdline: test.cmdline,
  746. };
  747. if(test.cdrom)
  748. {
  749. settings.cdrom = { url: test.cdrom };
  750. }
  751. if(test.fda)
  752. {
  753. settings.fda = { url: test.fda };
  754. }
  755. if(test.hda)
  756. {
  757. settings.hda = { url: test.hda, async: true };
  758. }
  759. if(test.bzimage)
  760. {
  761. settings.bzimage = { url: test.bzimage };
  762. }
  763. if(test.filesystem)
  764. {
  765. settings.filesystem = test.filesystem;
  766. }
  767. settings.cmdline = test.cmdline;
  768. settings.bzimage_initrd_from_filesystem = test.bzimage_initrd_from_filesystem;
  769. settings.acpi = test.acpi;
  770. if(test.expected_texts)
  771. {
  772. test.expected_texts = test.expected_texts.map(string_to_bytearray);
  773. }
  774. else
  775. {
  776. test.expected_texts = [];
  777. }
  778. if(!test.expected_serial_text)
  779. {
  780. test.expected_serial_text = [];
  781. }
  782. var emulator = new V86(settings);
  783. var screen = new Uint8Array(SCREEN_WIDTH * 25);
  784. function check_text_test_done()
  785. {
  786. return test.expected_texts.length === 0;
  787. }
  788. function check_serial_test_done()
  789. {
  790. return test.expected_serial_text.length === 0;
  791. }
  792. var mouse_test_done = false;
  793. function check_mouse_test_done()
  794. {
  795. return !test.expect_mouse_registered || mouse_test_done;
  796. }
  797. var graphical_test_done = false;
  798. var size_test_done = false;
  799. function check_grapical_test_done()
  800. {
  801. return !test.expect_graphical_mode || (graphical_test_done && (!test.expect_graphical_size || size_test_done));
  802. }
  803. var test_start = Date.now();
  804. var timeout_seconds = test.timeout * TIMEOUT_EXTRA_FACTOR;
  805. var timeout = setTimeout(check_test_done, (timeout_seconds + 1) * 1000);
  806. var timeouts = [timeout];
  807. var on_text = [];
  808. var stopped = false;
  809. function check_test_done()
  810. {
  811. if(stopped)
  812. {
  813. return;
  814. }
  815. if(check_text_test_done() &&
  816. check_mouse_test_done() &&
  817. check_grapical_test_done() &&
  818. check_serial_test_done())
  819. {
  820. var end = Date.now();
  821. for(let timeout of timeouts) clearTimeout(timeout);
  822. stopped = true;
  823. emulator.stop();
  824. console.warn("Passed test: %s (took %ds)", test.name, (end - test_start) / 1000);
  825. console.warn();
  826. done();
  827. }
  828. else if(Date.now() >= test_start + timeout_seconds * 1000)
  829. {
  830. for(let timeout of timeouts) clearTimeout(timeout);
  831. stopped = true;
  832. emulator.stop();
  833. emulator.destroy();
  834. if(test.failure_allowed)
  835. {
  836. console.warn("Test failed: %s (failure allowed)\n", test.name);
  837. }
  838. else
  839. {
  840. console.warn(screen_to_text(screen));
  841. console.warn("Test failed: %s\n", test.name);
  842. }
  843. if(!check_text_test_done())
  844. {
  845. console.warn('Expected text "%s" after %d seconds.', bytearray_to_string(test.expected_texts[0]), timeout_seconds);
  846. }
  847. if(!check_grapical_test_done())
  848. {
  849. console.warn("Expected graphical mode after %d seconds.", timeout_seconds);
  850. }
  851. if(!check_mouse_test_done())
  852. {
  853. console.warn("Expected mouse activation after %d seconds.", timeout_seconds);
  854. }
  855. if(!check_serial_test_done())
  856. {
  857. console.warn('Expected serial text "%s" after %d seconds.', test.expected_serial_text, timeout_seconds);
  858. }
  859. if(on_text.length)
  860. {
  861. console.warn(`Note: Expected text "${bytearray_to_string(on_text[0].text)}" to run "${on_text[0].run}"`);
  862. }
  863. if(!test.failure_allowed)
  864. {
  865. process.exit(1);
  866. }
  867. else
  868. {
  869. done();
  870. }
  871. }
  872. }
  873. emulator.add_listener("mouse-enable", function()
  874. {
  875. mouse_test_done = true;
  876. check_test_done();
  877. });
  878. emulator.add_listener("screen-set-mode", function(is_graphical)
  879. {
  880. graphical_test_done = is_graphical;
  881. check_test_done();
  882. });
  883. emulator.add_listener("screen-set-size-graphical", function(size)
  884. {
  885. if(test.expect_graphical_size)
  886. {
  887. size_test_done = size[0] === test.expect_graphical_size[0] &&
  888. size[1] === test.expect_graphical_size[1];
  889. check_test_done();
  890. }
  891. });
  892. emulator.add_listener("screen-put-char", function(chr)
  893. {
  894. var y = chr[0];
  895. var x = chr[1];
  896. var code = chr[2];
  897. screen[x + SCREEN_WIDTH * y] = code;
  898. var line = get_line(screen, y);
  899. if(!check_text_test_done())
  900. {
  901. let expected = test.expected_texts[0];
  902. if(x < expected.length && bytearray_starts_with(line, expected))
  903. {
  904. test.expected_texts.shift();
  905. if(VERBOSE) console.log(`Passed: "${bytearray_to_string(expected)}" on screen (${test.name})`);
  906. check_test_done();
  907. }
  908. }
  909. if(on_text.length)
  910. {
  911. let expected = on_text[0].text;
  912. if(x < expected.length && bytearray_starts_with(line, expected))
  913. {
  914. var action = on_text.shift();
  915. timeouts.push(
  916. setTimeout(() => {
  917. if(VERBOSE) console.error("Sending '%s'", action.run);
  918. emulator.keyboard_send_text(action.run);
  919. }, action.after || 0)
  920. );
  921. }
  922. }
  923. });
  924. //if(VERBOSE)
  925. //{
  926. // setInterval(() => {
  927. // console.warn(screen_to_text(screen));
  928. // }, 10000);
  929. //}
  930. let serial_line = "";
  931. emulator.add_listener("serial0-output-char", function(c)
  932. {
  933. if(c === "\n")
  934. {
  935. if(VERBOSE)
  936. {
  937. console.log(`Serial (${test.name}):`, serial_line);
  938. }
  939. if(test.expected_serial_text.length)
  940. {
  941. const expected = test.expected_serial_text[0];
  942. if(serial_line.includes(expected))
  943. {
  944. test.expected_serial_text.shift();
  945. if(VERBOSE) console.log(`Passed: "${expected}" on serial (${test.name})`);
  946. check_test_done();
  947. }
  948. }
  949. serial_line = "";
  950. }
  951. else if(c >= " " && c <= "~")
  952. {
  953. serial_line += c;
  954. }
  955. });
  956. test.actions && test.actions.forEach(function(action)
  957. {
  958. if(action.on_text)
  959. {
  960. on_text.push({ text: string_to_bytearray(action.on_text), run: action.run, after: action.after });
  961. }
  962. else
  963. {
  964. timeouts.push(
  965. setTimeout(() => {
  966. if(VERBOSE) console.error("Sending '%s'", action.run);
  967. emulator.keyboard_send_text(action.run);
  968. }, action.after || 0)
  969. );
  970. }
  971. });
  972. }