From bed129a69fd1e29808361fba0fa08ed42c8be4d5 Mon Sep 17 00:00:00 2001 From: Ali Mohammad Pur Date: Tue, 22 Feb 2022 07:49:04 +0330 Subject: [PATCH] LibTest+Spreadsheet: Add some basic spreadsheet runtime behaviour tests As there's a somewhat active development going on, let's keep the expected behaviour under tests to make sure nothing blows up :^) --- Base/home/anon/.config/Tests.ini | 3 + Meta/Lagom/CMakeLists.txt | 13 ++ Meta/build-root-filesystem.sh | 1 + Tests/CMakeLists.txt | 1 + Tests/LibJS/test-js.cpp | 2 +- Tests/Spreadsheet/CMakeLists.txt | 3 + Tests/Spreadsheet/test-spreadsheet.cpp | 44 ++++ .../Applications/Spreadsheet/Tests/basic.js | 97 +++++++++ .../Spreadsheet/Tests/free-functions.js | 166 +++++++++++++++ .../Spreadsheet/Tests/mock.test-common.js | 192 ++++++++++++++++++ .../Spreadsheet/Tests/test-harness.js | 46 +++++ .../Libraries/LibTest/JavaScriptTestRunner.h | 20 +- .../LibTest/JavaScriptTestRunnerMain.cpp | 2 +- 13 files changed, 578 insertions(+), 12 deletions(-) create mode 100644 Tests/Spreadsheet/CMakeLists.txt create mode 100644 Tests/Spreadsheet/test-spreadsheet.cpp create mode 100644 Userland/Applications/Spreadsheet/Tests/basic.js create mode 100644 Userland/Applications/Spreadsheet/Tests/free-functions.js create mode 100644 Userland/Applications/Spreadsheet/Tests/mock.test-common.js create mode 100644 Userland/Applications/Spreadsheet/Tests/test-harness.js diff --git a/Base/home/anon/.config/Tests.ini b/Base/home/anon/.config/Tests.ini index 1298391211a..c60374fb72f 100644 --- a/Base/home/anon/.config/Tests.ini +++ b/Base/home/anon/.config/Tests.ini @@ -6,3 +6,6 @@ NotTestsPattern=^.*(txt|frm|inc)$ [test-js] Arguments=--show-progress=false + +[test-spreadsheet] +Arguments=--show-progress=false diff --git a/Meta/Lagom/CMakeLists.txt b/Meta/Lagom/CMakeLists.txt index b2c4b10c948..2058f84ace0 100644 --- a/Meta/Lagom/CMakeLists.txt +++ b/Meta/Lagom/CMakeLists.txt @@ -625,6 +625,19 @@ if (BUILD_LAGOM) lagom_test(../../Tests/LibJS/test-invalid-unicode-js.cpp LIBS LagomJS) lagom_test(../../Tests/LibJS/test-bytecode-js.cpp LIBS LagomJS) + # Spreadsheet + add_executable(test-spreadsheet_lagom + ../../Tests/Spreadsheet/test-spreadsheet.cpp + ../../Userland/Libraries/LibTest/JavaScriptTestRunnerMain.cpp) + set_target_properties(test-spreadsheet_lagom PROPERTIES OUTPUT_NAME test-spreadsheet) + target_link_libraries(test-spreadsheet_lagom LagomCore LagomTest LagomJS) + add_test( + NAME Spreadsheet + COMMAND test-spreadsheet_lagom --show-progress=false + ) + set_tests_properties(Spreadsheet PROPERTIES ENVIRONMENT SERENITY_SOURCE_DIR=${SERENITY_PROJECT_ROOT}) + + # Markdown include(commonmark_spec) file(GLOB LIBMARKDOWN_TEST_SOURCES CONFIGURE_DEPENDS "../../Tests/LibMarkdown/*.cpp") diff --git a/Meta/build-root-filesystem.sh b/Meta/build-root-filesystem.sh index fe26f639446..c8fe60db3bb 100755 --- a/Meta/build-root-filesystem.sh +++ b/Meta/build-root-filesystem.sh @@ -165,6 +165,7 @@ cp -r "$SERENITY_SOURCE_DIR"/Userland/Libraries/LibCpp/Tests/parser mnt/home/ano cp -r "$SERENITY_SOURCE_DIR"/Userland/Libraries/LibCpp/Tests/preprocessor mnt/home/anon/cpp-tests/preprocessor cp -r "$SERENITY_SOURCE_DIR"/Userland/Libraries/LibWasm/Tests mnt/home/anon/wasm-tests cp -r "$SERENITY_SOURCE_DIR"/Userland/Libraries/LibJS/Tests/test-common.js mnt/home/anon/wasm-tests +cp -r "$SERENITY_SOURCE_DIR"/Userland/Applications/Spreadsheet/Tests mnt/home/anon/spreadsheet-tests if [ -n "$SERENITY_COPY_SOURCE" ] ; then printf "\ncopying Serenity's source... " diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt index 17e076de140..4af1b491daf 100644 --- a/Tests/CMakeLists.txt +++ b/Tests/CMakeLists.txt @@ -27,3 +27,4 @@ if (${SERENITY_ARCH} STREQUAL "i686") endif() add_subdirectory(LibCrypto) add_subdirectory(LibTLS) +add_subdirectory(Spreadsheet) diff --git a/Tests/LibJS/test-js.cpp b/Tests/LibJS/test-js.cpp index 54e98eef0e0..c9936dd910d 100644 --- a/Tests/LibJS/test-js.cpp +++ b/Tests/LibJS/test-js.cpp @@ -76,7 +76,7 @@ TESTJS_GLOBAL_FUNCTION(mark_as_garbage, markAsGarbage) return JS::js_undefined(); } -TESTJS_RUN_FILE_FUNCTION(String const& test_file, JS::Interpreter& interpreter) +TESTJS_RUN_FILE_FUNCTION(String const& test_file, JS::Interpreter& interpreter, JS::ExecutionContext&) { if (!test262_parser_tests) return Test::JS::RunFileHookResult::RunAsNormal; diff --git a/Tests/Spreadsheet/CMakeLists.txt b/Tests/Spreadsheet/CMakeLists.txt new file mode 100644 index 00000000000..1a710f372ab --- /dev/null +++ b/Tests/Spreadsheet/CMakeLists.txt @@ -0,0 +1,3 @@ +serenity_testjs_test(test-spreadsheet.cpp test-spreadsheet) +install(TARGETS test-spreadsheet RUNTIME DESTINATION bin OPTIONAL) +link_with_unicode_data(test-spreadsheet) diff --git a/Tests/Spreadsheet/test-spreadsheet.cpp b/Tests/Spreadsheet/test-spreadsheet.cpp new file mode 100644 index 00000000000..e3d27895eaf --- /dev/null +++ b/Tests/Spreadsheet/test-spreadsheet.cpp @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2022, Ali Mohammad Pur + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include + +TEST_ROOT("Userland/Applications/Spreadsheet/Tests"); + +#ifdef __serenity__ +static constexpr auto s_spreadsheet_runtime_path = "/res/js/Spreadsheet/runtime.js"sv; +#else +static constexpr auto s_spreadsheet_runtime_path = "../../../../Base/res/js/Spreadsheet/runtime.js"sv; +#endif + +TESTJS_RUN_FILE_FUNCTION(String const&, JS::Interpreter& interpreter, JS::ExecutionContext& global_execution_context) +{ + auto run_file = [&](StringView name) { + auto result = Test::JS::parse_script(name, interpreter.realm()); + if (result.is_error()) { + warnln("Unable to parse {}", name); + warnln("{}", result.error().error.to_string()); + warnln("{}", result.error().hint); + Test::cleanup_and_exit(); + } + auto script = result.release_value(); + + interpreter.vm().push_execution_context(global_execution_context, interpreter.realm().global_object()); + MUST(interpreter.run(*script)); + interpreter.vm().pop_execution_context(); + }; + +#ifdef __serenity__ + run_file(s_spreadsheet_runtime_path); +#else + run_file(LexicalPath::join(Test::JS::g_test_root, s_spreadsheet_runtime_path).string()); +#endif + + run_file("mock.test-common.js"); + + return Test::JS::RunFileHookResult::RunAsNormal; +} diff --git a/Userland/Applications/Spreadsheet/Tests/basic.js b/Userland/Applications/Spreadsheet/Tests/basic.js new file mode 100644 index 00000000000..2f30be3bd5f --- /dev/null +++ b/Userland/Applications/Spreadsheet/Tests/basic.js @@ -0,0 +1,97 @@ +describe("Position", () => { + test("here", () => { + const workbook = createWorkbook(); + const sheet = createSheet(workbook, "Sheet 1"); + sheet.makeCurrent(); + + sheet.setCell("A", 0, "0"); + sheet.focusCell("A", 0); + + expect(here).toBeDefined(); + let position = here(); + expect(position).toBeDefined(); + + expect(position.column).toEqual("A"); + expect(position.name).toEqual("A0"); + expect(position.row).toEqual(0); + expect(position.sheet).toBe(sheet); + + expect(position.contents).toEqual("0"); + expect(position.value()).toEqual("0"); + expect(position.toString()).toEqual(""); + + position.contents = "=1 + 1"; + expect(position.contents).toEqual("=1 + 1"); + expect(position.value()).toEqual(2); + + expect(position.up().row).toEqual(0); + expect(position.down().row).toEqual(1); + expect(position.right().row).toEqual(0); + expect(position.left().row).toEqual(0); + + sheet.addColumn("B"); + expect(position.up().column).toEqual("A"); + expect(position.down().column).toEqual("A"); + expect(position.right().column).toEqual("B"); + expect(position.left().column).toEqual("A"); + }); + + test("Position.from_name", () => { + const workbook = createWorkbook(); + const sheet = createSheet(workbook, "Sheet 1"); + sheet.makeCurrent(); + + sheet.setCell("A", 0, "0"); + sheet.focusCell("A", 0); + + expect(Position.from_name).toBeDefined(); + let position = Position.from_name("A0"); + expect(position).toBeInstanceOf(Position); + + position = Position.from_name("A123"); + expect(position).toBeInstanceOf(Position); + }); +}); + +describe("Range", () => { + test("simple", () => { + const workbook = createWorkbook(); + const sheet = createSheet(workbook, "Sheet 1"); + sheet.makeCurrent(); + + sheet.setCell("A", 0, "0"); + sheet.setCell("A", 10, "0"); + sheet.setCell("B", 1, "0"); + sheet.focusCell("A", 0); + + expect(R).toBeDefined(); + let cellsVisited = 0; + R`A0:A10`.forEach(name => { + ++cellsVisited; + }); + expect(cellsVisited).toEqual(11); + + cellsVisited = 0; + R`A0:A10:1:2`.forEach(name => { + ++cellsVisited; + }); + expect(cellsVisited).toEqual(6); + }); + + test("Ranges", () => { + const workbook = createWorkbook(); + const sheet = createSheet(workbook, "Sheet 1"); + sheet.makeCurrent(); + + sheet.setCell("A", 0, "0"); + sheet.setCell("A", 10, "0"); + sheet.setCell("B", 1, "0"); + sheet.focusCell("A", 0); + + let cellsVisited = 0; + R`A0:A5`.union(R`A6:A10`).forEach(name => { + ++cellsVisited; + }); + expect(cellsVisited).toEqual(11); + }); +}); diff --git a/Userland/Applications/Spreadsheet/Tests/free-functions.js b/Userland/Applications/Spreadsheet/Tests/free-functions.js new file mode 100644 index 00000000000..80cc1b59f7d --- /dev/null +++ b/Userland/Applications/Spreadsheet/Tests/free-functions.js @@ -0,0 +1,166 @@ +describe("Basic functions", () => { + const workbook = createWorkbook(); + const sheet = createSheet(workbook, "Sheet 1"); + sheet.makeCurrent(); + + sheet.setCell("A", 0, "0"); + sheet.setCell("A", 1, "1"); + sheet.setCell("A", 2, "2"); + + test("select", () => { + expect(select).toBeDefined(); + expect(select(true, 1, 2)).toBe(1); + expect(select(false, 1, 2)).toBe(2); + }); + + test("choose", () => { + expect(choose).toBeDefined(); + expect(choose(0, 1, 2, 3)).toBe(1); + expect(choose(1, 1, 2, 3)).toBe(2); + expect(choose(3, 1, 2, 3)).toBeUndefined(); + expect(choose(-1, 1, 2, 3)).toBeUndefined(); + }); + + test("now", () => { + expect(now).toBeDefined(); + expect(now()).toBeInstanceOf(Date); + }); + + test("randRange", () => { + expect(randRange).toBeDefined(); + }); + + test("integer", () => { + expect(integer).toBeDefined(); + expect(integer("0")).toEqual(0); + expect(integer("32")).toEqual(32); + }); + + test("sheet", () => { + expect(globalThis.sheet).toBeDefined(); + expect(globalThis.sheet("Sheet 1")).toBe(sheet); + expect(globalThis.sheet("Not a sheet")).toBeUndefined(); + }); + + test("reduce", () => { + expect(reduce).toBeDefined(); + expect(reduce(acc => acc + 1, 0, [1, 2, 3, 4])).toEqual(4); + expect(reduce(acc => acc + 1, 0, [])).toEqual(0); + }); + + test("numericReduce", () => { + expect(numericReduce).toBeDefined(); + expect(numericReduce(acc => acc + 1, 0, [1, 2, 3, 4])).toEqual(4); + expect(numericReduce(acc => acc + 1, 0, [])).toEqual(0); + }); + + test("numericResolve", () => { + expect(numericResolve).toBeDefined(); + expect(numericResolve(["A0", "A1", "A2"])).toEqual([0, 1, 2]); + expect(numericResolve([])).toEqual([]); + }); + + test("resolve", () => { + expect(resolve).toBeDefined(); + expect(resolve(["A0", "A1", "A2"])).toEqual(["0", "1", "2"]); + expect(resolve([])).toEqual([]); + }); +}); + +describe("Statistics", () => { + const workbook = createWorkbook(); + const sheet = createSheet(workbook, "Sheet 1"); + sheet.makeCurrent(); + + for (let i = 0; i < 10; ++i) sheet.setCell("A", i, `${i}`); + + test("sum", () => { + expect(sum).toBeDefined(); + expect(sum(R`A0:A9`)).toEqual(45); + }); + + test("sumIf", () => { + expect(sumIf).toBeDefined(); + expect(sumIf(x => !Number.isNaN(x), R`A0:A10`)).toEqual(45); + }); + + test("count", () => { + expect(count).toBeDefined(); + expect(count(R`A0:A9`)).toEqual(10); + }); + + test("countIf", () => { + expect(countIf).toBeDefined(); + expect(countIf(x => x, R`A0:A10`)).toEqual(10); + }); + + test("average", () => { + expect(average).toBeDefined(); + expect(average(R`A0:A9`)).toEqual(4.5); + }); + + test("averageIf", () => { + expect(averageIf).toBeDefined(); + expect(averageIf(x => !Number.isNaN(x), R`A0:A10`)).toEqual(4.5); + }); + + test("median", () => { + expect(median).toBeDefined(); + expect(median(R`A0:A9`)).toEqual(4.5); + expect(median(R`A0:A2`)).toEqual(1); + }); + + test("variance", () => { + expect(variance).toBeDefined(); + expect(variance(R`A0:A0`)).toEqual(0); + expect(variance(R`A0:A9`)).toEqual(82.5); + }); + + test("mode", () => { + expect(mode).toBeDefined(); + expect(mode(R`A0:A0`.union(R`A0:A0`).union(R`A1:A9`))).toEqual(0); + }); + + test("stddev", () => { + expect(stddev).toBeDefined(); + expect(stddev(R`A0:A0`)).toEqual(0); + expect(stddev(R`A0:A9`)).toEqual(Math.sqrt(82.5)); + }); +}); + +describe("Lookup", () => { + const workbook = createWorkbook(); + const sheet = createSheet(workbook, "Sheet 1"); + sheet.makeCurrent(); + + for (let i = 0; i < 10; ++i) { + sheet.setCell("A", i, `${i}`); + sheet.setCell("B", i, `B${i}`); + } + + sheet.focusCell("A", 0); + + test("row", () => { + expect(row()).toEqual(0); + }); + + test("column", () => { + expect(column()).toEqual("A"); + }); + + test("lookup", () => { + expect(lookup).toBeDefined(); + // Note: String ordering. + expect(lookup("2", R`A0:A9`, R`B0:B9`)).toEqual("B2"); + expect(lookup("20", R`A0:A9`, R`B0:B9`)).toBeUndefined(); + expect(lookup("80", R`A0:A9`, R`B0:B9`, undefined, "nextlargest")).toEqual("B9"); + }); + + test("reflookup", () => { + expect(reflookup).toBeDefined(); + // Note: String ordering. + expect(reflookup("2", R`A0:A9`, R`B0:B9`).name).toEqual("B2"); + expect(reflookup("20", R`A0:A9`, R`B0:B9`)).toEqual(here()); + expect(reflookup("80", R`A0:A9`, R`B0:B9`, undefined, "nextlargest").name).toEqual("B9"); + }); +}); diff --git a/Userland/Applications/Spreadsheet/Tests/mock.test-common.js b/Userland/Applications/Spreadsheet/Tests/mock.test-common.js new file mode 100644 index 00000000000..4aa7dcaab5b --- /dev/null +++ b/Userland/Applications/Spreadsheet/Tests/mock.test-common.js @@ -0,0 +1,192 @@ +var thisSheet; +var workbook; + +var createWorkbook = () => { + return { + __sheets: new Map(), + sheet(nameOrIndex) { + if (typeof nameOrIndex !== "number") return this.__sheets.get(nameOrIndex); + for (const entry of this.__sheets) { + if (nameOrIndex === 0) return entry[1]; + nameOrIndex--; + } + return undefined; + }, + }; +}; + +function toBijectiveBase(number) { + const alpha = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + number += 1; + let c = 0; + let x = 1; + while (number >= x) { + ++c; + number -= x; + x *= 26; + } + + let s = ""; + for (let i = 0; i < c; i++) { + s = alpha.charAt(number % 26) + s; + number = Math.floor(number / 26); + } + + return s; +} + +function __evaluate(expression, that) { + const object = Object.create(null); + for (const entry of that.__cells) { + const cell = JSON.parse(entry[0]); + object[`${cell[0]}${cell[1]}`] = entry[1][1]; + } + + const sheetObject = that; + let __value; + + // Warning: Dragons and fire ahead. + with (that.__workbook) { + with (object) { + with (sheetObject) { + __value = eval(expression); + } + } + } + return __value; +} + +class Sheet { + constructor(workbook) { + this.__cells = new Map(); + this.__columns = new Set(); + this.__workbook = workbook; + this.__currentCellPosition = undefined; + } + + get_real_cell_contents(name) { + const cell = this.parse_cell_name(name); + if (cell === undefined) throw new TypeError("Invalid cell name"); + return this.getCell(cell.column, cell.row)[0]; + } + + set_real_cell_contents(name, value) { + const cell = this.parse_cell_name(name); + if (cell === undefined) throw new TypeError("Invalid cell name"); + + this.setCell(cell.column, cell.row, value); + } + + parse_cell_name(name) { + const match = /^([a-zA-Z]+)(\d+)$/.exec(name); + if (!Array.isArray(match)) return undefined; + + return { + column: match[1], + row: +match[2], + }; + } + + current_cell_position() { + return this.__currentCellPosition; + } + + column_index(name) { + let i = 0; + for (const column of this.__columns) { + if (column === name) return i; + ++i; + } + } + + column_arithmetic(name, offset) { + if (offset < 0) { + const columns = this.getColumns(); + let index = columns.indexOf(name); + if (index === -1) throw new TypeError(`${name} is not a valid column name`); + + index += offset; + if (index < 0) return columns[0]; + return columns[index]; + } + + let found = false; + for (const column of this.__columns) { + if (!found) found = column === name; + if (found) { + if (offset === 0) return column; + offset--; + } + } + + if (!found) throw new TypeError(`${name} is not a valid column name`); + + let newName; + for (let i = 0; i < offset; ++i) { + newName = toBijectiveBase(this.__columns.size); + this.addColumn(newName); + } + return newName; + } + + get_column_bound(name) { + let bound = 0; + for (const entry of this.__cells) { + const [column, row] = JSON.parse(entry[0]); + if (column !== name) continue; + bound = Math.max(bound, row); + } + return row; + } + + evaluate(currentColumn, currentRow, expression) { + const currentCellSave = this.__currentCellPosition; + this.__currentCellPosition = { column: currentColumn, row: currentRow }; + try { + return __evaluate(expression, this); + } finally { + this.__currentCellPosition = currentCellSave; + } + } + + addColumn(name) { + this.__columns.add(name); + } + + getColumns() { + return Array.from(this.__columns); + } + + setCell(column, row, source, value = undefined) { + this.addColumn(column); + source = `${source}`; + if (value === undefined) { + value = source; + if (value[0] === "=") value = this.evaluate(column, row, value.substr(1)); + } + + this.__cells.set(JSON.stringify([column, row]), [source, value]); + this[`${column}${row}`] = value; + } + + getCell(column, row) { + const data = this.__cells.get(JSON.stringify([column, row])); + if (data === undefined) return undefined; + return data; + } + + focusCell(column, row) { + this.__currentCellPosition = { column, row }; + } + + makeCurrent() { + thisSheet = this; + workbook = this.__workbook; + } +} + +var createSheet = (workbook, name) => { + const sheet = new Sheet(workbook); + workbook.__sheets.set(name, sheet); + return sheet; +}; diff --git a/Userland/Applications/Spreadsheet/Tests/test-harness.js b/Userland/Applications/Spreadsheet/Tests/test-harness.js new file mode 100644 index 00000000000..283d76a0008 --- /dev/null +++ b/Userland/Applications/Spreadsheet/Tests/test-harness.js @@ -0,0 +1,46 @@ +describe("Harness-defined functions", () => { + test("createWorkbook", () => { + expect(createWorkbook).toBeDefined(); + const workbook = createWorkbook(); + expect(workbook).toBeDefined(); + expect(workbook.sheet).toBeDefined(); + }); + test("createSheet", () => { + const workbook = createWorkbook(); + const sheet = createSheet(workbook, "foo"); + expect(sheet).toBeDefined(); + expect(sheet.get_real_cell_contents).toBeDefined(); + expect(sheet.set_real_cell_contents).toBeDefined(); + expect(sheet.parse_cell_name).toBeDefined(); + expect(sheet.current_cell_position).toBeDefined(); + expect(sheet.column_index).toBeDefined(); + expect(sheet.column_arithmetic).toBeDefined(); + expect(sheet.get_column_bound).toBeDefined(); + }); + test("Sheet mock behavior", () => { + const workbook = createWorkbook(); + const sheet = createSheet(workbook, "foo"); + sheet.setCell("A", 0, "10"); + expect(sheet.getCell("A", 0)).toEqual(["10", "10"]); + + sheet.setCell("A", 0, "=10"); + expect(sheet.getCell("A", 0)).toEqual(["=10", 10]); + + expect(sheet.getColumns()).toEqual(["A"]); + }); + test("Workbook mock behavior", () => { + const workbook = createWorkbook(); + const sheet = createSheet(workbook, "foo"); + expect(workbook.sheet("foo")).toBe(sheet); + expect(workbook.sheet(0)).toBe(sheet); + expect(workbook.sheet(1)).toBeUndefined(); + expect(workbook.sheet("bar")).toBeUndefined(); + }); + test("Referencing cells", () => { + const workbook = createWorkbook(); + const sheet = createSheet(workbook, "foo"); + sheet.setCell("A", 0, "42"); + sheet.setCell("A", 1, "=A0"); + expect(sheet.getCell("A", 1)).toEqual(["=A0", "42"]); + }); +}); diff --git a/Userland/Libraries/LibTest/JavaScriptTestRunner.h b/Userland/Libraries/LibTest/JavaScriptTestRunner.h index 69a1556be57..5b25df2c2b8 100644 --- a/Userland/Libraries/LibTest/JavaScriptTestRunner.h +++ b/Userland/Libraries/LibTest/JavaScriptTestRunner.h @@ -89,14 +89,14 @@ #define TEST_ROOT(path) \ String Test::JS::g_test_root_fragment = path -#define TESTJS_RUN_FILE_FUNCTION(...) \ - struct __TestJS_run_file { \ - __TestJS_run_file() \ - { \ - ::Test::JS::g_run_file = hook; \ - } \ - static ::Test::JS::IntermediateRunFileResult hook(const String&, JS::Interpreter&); \ - } __testjs_common_run_file {}; \ +#define TESTJS_RUN_FILE_FUNCTION(...) \ + struct __TestJS_run_file { \ + __TestJS_run_file() \ + { \ + ::Test::JS::g_run_file = hook; \ + } \ + static ::Test::JS::IntermediateRunFileResult hook(const String&, JS::Interpreter&, JS::ExecutionContext&); \ + } __testjs_common_run_file {}; \ ::Test::JS::IntermediateRunFileResult __TestJS_run_file::hook(__VA_ARGS__) #define TESTJS_CREATE_INTERPRETER_HOOK(...) \ @@ -163,7 +163,7 @@ enum class RunFileHookResult { }; using IntermediateRunFileResult = AK::Result; -extern IntermediateRunFileResult (*g_run_file)(const String&, JS::Interpreter&); +extern IntermediateRunFileResult (*g_run_file)(const String&, JS::Interpreter&, JS::ExecutionContext&); class TestRunner : public ::Test::TestRunner { public: @@ -300,7 +300,7 @@ inline JSFileResult TestRunner::run_file_test(const String& test_path) interpreter->heap().set_should_collect_on_every_allocation(g_collect_on_every_allocation); if (g_run_file) { - auto result = g_run_file(test_path, *interpreter); + auto result = g_run_file(test_path, *interpreter, global_execution_context); if (result.is_error() && result.error() == RunFileHookResult::SkipFile) { return { test_path, diff --git a/Userland/Libraries/LibTest/JavaScriptTestRunnerMain.cpp b/Userland/Libraries/LibTest/JavaScriptTestRunnerMain.cpp index 108a8ba21e4..ebff8e9e5a8 100644 --- a/Userland/Libraries/LibTest/JavaScriptTestRunnerMain.cpp +++ b/Userland/Libraries/LibTest/JavaScriptTestRunnerMain.cpp @@ -25,7 +25,7 @@ HashMap s_exposed_global_functions; Function g_main_hook; Function()> g_create_interpreter_hook; HashMap> g_extra_args; -IntermediateRunFileResult (*g_run_file)(const String&, JS::Interpreter&) = nullptr; +IntermediateRunFileResult (*g_run_file)(const String&, JS::Interpreter&, JS::ExecutionContext&) = nullptr; String g_test_root; int g_test_argc; char** g_test_argv;