Spaces:
Runtime error
Runtime error
| import { setTimeout as pause } from "timers/promises"; | |
| import url from "node:url"; | |
| import fs from "node:fs"; | |
| const __dirname = url.fileURLToPath(new URL(".", import.meta.url)); | |
| const TEST_RELEASE_BUILD = +process.env.TEST_RELEASE_BUILD; | |
| const { V86 } = await import(TEST_RELEASE_BUILD ? "../../build/libv86.mjs" : "../../src/main.js"); | |
| process.on("unhandledRejection", exn => { throw exn; }); | |
| function regexp_escape(text) | |
| { | |
| // TODO: The official RegExp.escape() would be prefarrable to this, | |
| // but currently (Aug 2025) the May 2025 Baseline is not yet available | |
| // at github CI. | |
| return text.replace(/[/\-\\^$*+?.()|[\]{}]/g, "\\$&"); | |
| } | |
| async function exec_test(test_name, v86_config, timeout_sec, test_function) | |
| { | |
| console.log("Starting: " + test_name); | |
| const tm_start = performance.now(); | |
| const emulator = new V86(v86_config); | |
| const timeout = setTimeout(async () => { | |
| console.warn(emulator.screen_adapter.get_text_screen()); | |
| await emulator.destroy(); | |
| throw new Error("Timeout error in test " + test_name); | |
| }, timeout_sec * 1000); | |
| await new Promise(resolve => emulator.bus.register("emulator-started", () => resolve())); | |
| try | |
| { | |
| await test_function(emulator); | |
| } | |
| catch(err) | |
| { | |
| console.warn(emulator.screen_adapter.get_text_screen()); | |
| throw new Error("Error in test " + test_name, { cause: err }); | |
| } | |
| clearTimeout(timeout); | |
| await emulator.destroy(); | |
| console.log("Done: " + test_name + " (" + ((performance.now() - tm_start) / 1000).toFixed(2) + " sec)"); | |
| } | |
| /** | |
| * Execute given CLI command and wait for its completion. | |
| * | |
| * Injects command into the guest's keyboard buffer, then waits for both the | |
| * command and the expected response lines to show at the bottom of the VGA | |
| * screen. | |
| * | |
| * If command is empty then no command is executed and only the expected | |
| * response lines are waited for. If command contains only whitespace and/or | |
| * newline characters then it is send to the guest, but it does not become | |
| * part of the expected response. | |
| * | |
| * Allowed character set for command and expected is the printable subset | |
| * of 7-bit ASCII, use newline character "\n" to encode ENTER key. | |
| * | |
| * Throws an Error if the given timeout elapsed before the expected response | |
| * could be detected. | |
| * | |
| * @param {V86} emulator | |
| * @param {string} command | |
| * @param {Array<string|RegExp>} expected | |
| * @param {number} timeout_msec | |
| */ | |
| async function expect(emulator, command, expected, timeout_msec) | |
| { | |
| if(command) | |
| { | |
| for(const ch of command) | |
| { | |
| emulator.keyboard_send_text(ch); | |
| await pause(10); | |
| } | |
| expected = [new RegExp(regexp_escape(command.trimRight()) + "$"), ...expected]; | |
| await pause(100); | |
| } | |
| if(!await emulator.wait_until_vga_screen_contains(expected, {timeout_msec: timeout_msec})) | |
| { | |
| throw new Error("Timeout of " + timeout_msec + " msec expired"); | |
| } | |
| } | |
| const CONFIG_MSDOS622_HD = { | |
| bios: { url: __dirname + "/../../bios/seabios.bin" }, | |
| vga_bios: { url: __dirname + "/../../bios/vgabios.bin" }, | |
| hda: { url: __dirname + "/../../images/msdos622.img" }, | |
| autostart: true, | |
| memory_size: 32 * 1024 * 1024, | |
| log_level: 0, | |
| disable_jit: +process.env.DISABLE_JIT | |
| }; | |
| const CONFIG_TINYCORE_CD = { | |
| bios: { url: __dirname + "/../../bios/seabios.bin" }, | |
| vga_bios: { url: __dirname + "/../../bios/vgabios.bin" }, | |
| cdrom: { url: __dirname + "/../../images/TinyCore-11.0.iso" }, | |
| autostart: true, | |
| memory_size: 128 * 1024 * 1024, | |
| log_level: 0, | |
| disable_jit: +process.env.DISABLE_JIT | |
| }; | |
| await exec_test("floppy-insert-eject", CONFIG_MSDOS622_HD, 60, async emulator => | |
| { | |
| console.log("Waiting for C:\\>"); | |
| await expect(emulator, "", ["C:\\>"], 10000); | |
| console.log("Reading A:"); | |
| await expect(emulator, "dir A:\n", ["", "", "General failure reading drive A", "Abort, Retry, Fail?"], 5000); | |
| await expect(emulator, "A", ["", "C:\\>"], 1000); | |
| console.log("Inserting disk freedos722.img into drive fda"); | |
| await emulator.set_fda({ url: __dirname + "/../../images/freedos722.img" }); | |
| console.log("Reading A:X86TEST.ASM"); | |
| await expect(emulator, "dir /B A:X86TEST.ASM\n", ["X86TEST.ASM", "", "C:\\>"], 3000); | |
| console.log("Ejecting disk from drive A:"); | |
| emulator.eject_fda(); | |
| console.log("Reading A:"); | |
| await expect(emulator, "dir A:\n", ["", " Volume in drive A is FREEDOS", "", "General failure reading drive A", "Abort, Retry, Fail?"], 5000); | |
| }); | |
| await exec_test("floppy-insert-fdb", CONFIG_MSDOS622_HD, 60, async emulator => | |
| { | |
| console.log("Waiting for C:\\>"); | |
| await expect(emulator, "", ["C:\\>"], 10000); | |
| console.log("Inserting disk freedos722.img into drive fdb"); | |
| await emulator.set_fdb({ url: __dirname + "/../../images/freedos722.img" }); | |
| console.log("Reading B:X86TEST.ASM"); | |
| await expect(emulator, "dir /B B:X86TEST.ASM\n", ["X86TEST.ASM", "", "C:\\>"], 3000); | |
| console.log("Formatting B:"); | |
| await expect(emulator, "format /V:V86 /U B:\n", ["Insert new diskette for drive B:", "and press ENTER when ready..."], 3000); | |
| await expect(emulator, "\n", [/Volume Serial Number is [0-9A-F-]+/, "", "Format another (Y/N)?"], 3000); | |
| await expect(emulator, "N\n", ["", "", "C:\\>"], 3000); | |
| }); | |
| await exec_test("floppy-tinycore-linux", CONFIG_TINYCORE_CD, 60, async emulator => | |
| { | |
| console.log("Waiting for boot menu"); | |
| await expect(emulator, "", [/BIOS default device boot in \d+ seconds\.\.\./], 10000); | |
| // press arrow down key 3 times | |
| for(let i = 0; i < 3; i++) | |
| { | |
| emulator.keyboard_send_scancodes([ | |
| 0xe0, 0x50, // press | |
| 0xe0, 0x50 | 0x80, // release | |
| ]); | |
| await pause(600); | |
| } | |
| console.log("Waiting for tc@box:~$"); | |
| await expect(emulator, "\n", ["tc@box:~$"], 30000); | |
| console.log("Inserting disk windows101.img into drive fda"); | |
| await emulator.set_fda({ url: __dirname + "/../../images/windows101.img" }); | |
| console.log("Mounting /dev/fd0 into /mnt/fd0"); | |
| await expect(emulator, "mkdir /mnt/fd0\n", ["tc@box:~$"], 3000); | |
| await expect(emulator, "sudo mount /dev/fd0 /mnt/fd0\n", ["tc@box:~$"], 3000); | |
| console.log("Reading /mnt/fd0/COMMAND.COM"); | |
| await expect(emulator, "ls /mnt/fd0/COMMAND.COM\n", ["/mnt/fd0/COMMAND.COM", "tc@box:~$"], 3000); | |
| console.log("Unmounting fda"); | |
| await expect(emulator, "sudo umount /dev/fd0\n", ["tc@box:~$"], 3000); | |
| console.log("Formatting /dev/fd0"); | |
| await expect(emulator, "sudo mke2fs -q /dev/fd0\n", ["/dev/fd0 contains a vfat file system labelled 'WIN101'", "Proceed anyway? (y,N)"], 3000); | |
| await expect(emulator, "y\n", ["tc@box:~$"], 5000); | |
| console.log("Reading /mnt/fd0"); | |
| await expect(emulator, "sudo mount /dev/fd0 /mnt/fd0\n", ["tc@box:~$"], 3000); | |
| await expect(emulator, "ls /mnt/fd0\n", ["lost+found/", "tc@box:~$"], 3000); | |
| }); | |
| await exec_test("floppy-state-snapshot", CONFIG_MSDOS622_HD, 60, async emulator => | |
| { | |
| console.log("Waiting for C:\\>"); | |
| await expect(emulator, "", ["C:\\>"], 10000); | |
| console.log("Inserting disk freedos722.img into drive fda"); | |
| await emulator.set_fda({ url: __dirname + "/../../images/freedos722.img" }); | |
| console.log("Saving initial state"); | |
| const initial_state = await emulator.save_state(); | |
| console.log("Creating file A:V86TEST.TXT"); | |
| await expect(emulator, "echo v86test > A:V86TEST.TXT\n", ["", "C:\\>"], 3000); | |
| console.log("Saving modified state"); | |
| const modified_state = await emulator.save_state(); | |
| console.log("Restoring initial state"); | |
| await emulator.restore_state(initial_state); | |
| console.log("Reading A:V86TEST.TXT"); | |
| await expect(emulator, "dir /B A:V86TEST.TXT\n", ["", "C:\\>"], 3000); | |
| console.log("Restoring modified state"); | |
| await emulator.restore_state(modified_state); | |
| console.log("Reading A:V86TEST.TXT"); | |
| await expect(emulator, "dir /B A:V86TEST.TXT\n", ["V86TEST.TXT", "", "C:\\>"], 3000); | |
| }); | |