From b590eaa2f64ead3384eadc6fe58f6358aa1a0478 Mon Sep 17 00:00:00 2001 From: Wojtek Kosior Date: Wed, 22 Dec 2021 16:39:34 +0100 Subject: reworked build system; added missing license notices --- test/script_loader.py | 55 ++++++++++------------------------- test/unit/conftest.py | 8 ----- test/unit/test_basic.py | 6 ++-- test/unit/test_broadcast.py | 54 ++++++++++++++++------------------ test/unit/test_indexeddb.py | 32 +++++++++----------- test/unit/test_patterns.py | 12 +++----- test/unit/test_patterns_query_tree.py | 52 +++++++++++++++------------------ 7 files changed, 85 insertions(+), 134 deletions(-) (limited to 'test') diff --git a/test/script_loader.py b/test/script_loader.py index 8f30944..edf8143 100644 --- a/test/script_loader.py +++ b/test/script_loader.py @@ -41,54 +41,29 @@ def make_relative_path(path): return path -"""Used to ignore hidden files and emacs auto-save files.""" -script_name_regex = re.compile(r'^[^.#].*\.js$') +script_cache = {} -def available_scripts(directory): - for script in directory.rglob('*.js'): - if script_name_regex.match(script.name): - yield script - -def wrapped_script(script_path, wrap_partially=True): - if script_path == 'exports_init.js': - if not (script_root / 'exports_init.js').exists(): - subprocess.run([str(script_root / 'write_exports_init.sh'), - 'mozilla', '.', 'default_settings.json'], - cwd=script_root, check=True) - - with open(script_root / 'exports_init.js') as script: - return script.read() - - command = 'partially_wrapped_code' if wrap_partially else 'wrapped_code' - awk_command = ['awk', '-f', str(awk_script), command, str(script_path)] - awk = subprocess.run(awk_command, stdout=subprocess.PIPE, cwd=script_root, - check=True) - - return awk.stdout.decode() - -def load_script(path, import_dirs): +def load_script(path): """ - `path` and `import_dirs` are .js file path and a list of directory paths, - respectively. They may be absolute or specified relative to Haketilo's - project directory. + `path` is a .js file path in Haketilo sources. It may be absolute or + specified relative to Haketilo's project directory. Return a string containing script from `path` together with all other - scripts it depends. Dependencies are wrapped in the same way Haketilo's + scripts it depends on. Dependencies are wrapped in the same way Haketilo's build system wraps them, with imports properly satisfied. The main script being loaded is wrapped partially - it also has its imports satisfied, but - its code is not placed inside an anonymous function, so the + its code is executed in global scope instead of within an anonymous function + and imported variables are defined with `let` instead of `const` to allow + a dependency to be substituted by a mocked value. """ path = make_relative_path(path) + if str(path) in script_cache: + return script_cache[str(path)] - import_dirs = [make_relative_path(dir) for dir in import_dirs] - available = [s for dir in import_dirs for s in available_scripts(dir)] - - awk = subprocess.run(['awk', '-f', str(awk_script), 'script_dependencies', - str(path), *[str(s) for s in available]], + awk = subprocess.run(['awk', '-f', str(awk_script), '--', '-D', 'MOZILLA', + '-D', 'MV2', '--output=amalgamate-js:' + str(path)], stdout=subprocess.PIPE, cwd=script_root, check=True) + script = awk.stdout.decode() + script_cache[str(path)] = script - to_load = awk.stdout.decode().split() - texts = [wrapped_script(path, wrap_partially=(i == len(to_load) - 1)) - for i, path in enumerate(to_load)] - - return '\n'.join(texts) + return script diff --git a/test/unit/conftest.py b/test/unit/conftest.py index eec311c..f9a17f8 100644 --- a/test/unit/conftest.py +++ b/test/unit/conftest.py @@ -33,7 +33,6 @@ from selenium.webdriver.support import expected_conditions as EC from ..profiles import firefox_safe_mode from ..server import do_an_internet -from ..script_loader import load_script from ..extension_crafting import make_extension @pytest.fixture(scope="package") @@ -139,13 +138,6 @@ def execute_in_page(driver): yield do_execute -@pytest.fixture() -def load_into_page(driver): - def do_load(path, import_dirs, *args): - _execute_in_page_context(driver, load_script(path, import_dirs), args) - - yield do_load - @pytest.fixture() def wait_elem_text(driver): def do_wait(id, text): diff --git a/test/unit/test_basic.py b/test/unit/test_basic.py index ca956e7..2564e9d 100644 --- a/test/unit/test_basic.py +++ b/test/unit/test_basic.py @@ -19,6 +19,8 @@ Haketilo unit tests - base import pytest +from ..script_loader import load_script + def test_driver(driver): """ A trivial test case that verifies mocked web pages served by proxy can be @@ -32,12 +34,12 @@ def test_driver(driver): assert "Schrodinger's Document" in title @pytest.mark.get_page('https://gotmyowndoma.in') -def test_script_loader(execute_in_page, load_into_page): +def test_script_loader(execute_in_page): """ A trivial test case that verifies Haketilo's .js files can be properly loaded into a test page together with their dependencies. """ - load_into_page('common/stored_types.js', ['common']) + execute_in_page(load_script('common/stored_types.js')) assert execute_in_page('returnval(TYPE_PREFIX.VAR);') == '_' diff --git a/test/unit/test_broadcast.py b/test/unit/test_broadcast.py index 11a61b0..1213b17 100644 --- a/test/unit/test_broadcast.py +++ b/test/unit/test_broadcast.py @@ -21,12 +21,9 @@ import pytest from ..script_loader import load_script -def broker_js(): - js = load_script('background/broadcast_broker.js', ['common', 'background']) - return js + ';start_broadcast_broker();' +broker_js = lambda: load_script('background/broadcast_broker.js') + ';start();' -def broadcast_js(): - return load_script('common/broadcast.js', ['common']) +broadcast_js = lambda: load_script('common/broadcast.js') test_page_html = ''' @@ -53,7 +50,6 @@ def test_broadcast(driver, execute_in_page, wait_elem_text): API and implemented in `background/broadcast_broker.js` and `common/broadcast.js` works correctly. """ - # The broadcast facility is meant to enable message distribution between # multiple contexts (e.g. different tabs/windows). Let's open the same # extension's test page in a second window. @@ -71,15 +67,15 @@ def test_broadcast(driver, execute_in_page, wait_elem_text): ''' const divs = [0, 1, 2].map(n => document.getElementById("d" + n)); let appender = n => (t => divs[n].append("\\n" + `[${t[0]}, ${t[1]}]`)); - let listener0 = broadcast.listener_connection(appender(0)); - broadcast.subscribe(listener0, "somebodyoncetoldme"); + let listener0 = listener_connection(appender(0)); + subscribe(listener0, "somebodyoncetoldme"); ''') driver.switch_to.window(windows[1]) execute_in_page( ''' - let sender0 = broadcast.sender_connection(); - broadcast.out(sender0, "somebodyoncetoldme", "iaintthesharpesttool"); + let sender0 = sender_connection(); + out(sender0, "somebodyoncetoldme", "iaintthesharpesttool"); ''') driver.switch_to.window(windows[0]) @@ -89,11 +85,11 @@ def test_broadcast(driver, execute_in_page, wait_elem_text): driver.switch_to.window(windows[0]) execute_in_page( ''' - let listener1 = broadcast.listener_connection(appender(1)); - broadcast.subscribe(listener1, "worldisgonnarollme"); - let listener2 = broadcast.listener_connection(appender(2)); - broadcast.subscribe(listener2, "worldisgonnarollme"); - broadcast.subscribe(listener2, "somebodyoncetoldme"); + let listener1 = listener_connection(appender(1)); + subscribe(listener1, "worldisgonnarollme"); + let listener2 = listener_connection(appender(2)); + subscribe(listener2, "worldisgonnarollme"); + subscribe(listener2, "somebodyoncetoldme"); ''') # Let's send one message to one channel and one to the other. Verify they @@ -101,8 +97,8 @@ def test_broadcast(driver, execute_in_page, wait_elem_text): driver.switch_to.window(windows[1]) execute_in_page( ''' - broadcast.out(sender0, "somebodyoncetoldme", "intheshed"); - broadcast.out(sender0, "worldisgonnarollme", "shewaslooking"); + out(sender0, "somebodyoncetoldme", "intheshed"); + out(sender0, "worldisgonnarollme", "shewaslooking"); ''') driver.switch_to.window(windows[0]) @@ -121,9 +117,9 @@ def test_broadcast(driver, execute_in_page, wait_elem_text): driver.switch_to.window(windows[2]) execute_in_page( ''' - let sender1 = broadcast.sender_connection(); - broadcast.prepare(sender1, "somebodyoncetoldme", "kindadumb"); - broadcast.out(sender1, "worldisgonnarollme", "withherfinger"); + let sender1 = sender_connection(); + prepare(sender1, "somebodyoncetoldme", "kindadumb"); + out(sender1, "worldisgonnarollme", "withherfinger"); ''') driver.switch_to.window(windows[0]) @@ -132,7 +128,7 @@ def test_broadcast(driver, execute_in_page, wait_elem_text): assert 'kindadumb' not in text driver.switch_to.window(windows[2]) - execute_in_page('broadcast.flush(sender1);') + execute_in_page('flush(sender1);') driver.switch_to.window(windows[0]) wait_elem_text('d0', 'kindadumb') @@ -142,10 +138,10 @@ def test_broadcast(driver, execute_in_page, wait_elem_text): driver.switch_to.window(windows[2]) execute_in_page( ''' - broadcast.prepare(sender1, "somebodyoncetoldme", "andherthumb"); - broadcast.discard(sender1); - broadcast.prepare(sender1, "somebodyoncetoldme", "andhermiddlefinger"); - broadcast.flush(sender1); + prepare(sender1, "somebodyoncetoldme", "andherthumb"); + discard(sender1); + prepare(sender1, "somebodyoncetoldme", "andhermiddlefinger"); + flush(sender1); ''') driver.switch_to.window(windows[0]) @@ -158,7 +154,7 @@ def test_broadcast(driver, execute_in_page, wait_elem_text): driver.switch_to.window(windows[2]) execute_in_page( ''' - broadcast.prepare(sender1, "worldisgonnarollme", "intheshape", 500); + prepare(sender1, "worldisgonnarollme", "intheshape", 500); ''') driver.close() @@ -166,11 +162,11 @@ def test_broadcast(driver, execute_in_page, wait_elem_text): wait_elem_text('d2', 'intheshape') # Verify listener's connection gets closed properly. - execute_in_page('broadcast.close(listener0); broadcast.close(listener1);') + execute_in_page('close(listener0); close(listener1);') driver.switch_to.window(windows[1]) - execute_in_page('broadcast.out(sender0, "worldisgonnarollme", "ofanL");') - execute_in_page('broadcast.out(sender0, "somebodyoncetoldme", "forehead");') + execute_in_page('out(sender0, "worldisgonnarollme", "ofanL");') + execute_in_page('out(sender0, "somebodyoncetoldme", "forehead");') driver.switch_to.window(windows[0]) wait_elem_text('d2', 'ofanL') diff --git a/test/unit/test_indexeddb.py b/test/unit/test_indexeddb.py index 3604ee9..af60e1c 100644 --- a/test/unit/test_indexeddb.py +++ b/test/unit/test_indexeddb.py @@ -27,8 +27,8 @@ from selenium.common.exceptions import WebDriverException from ..script_loader import load_script -def indexeddb_js(): - return load_script('common/indexeddb.js', ['common']) +indexeddb_js = lambda: load_script('common/indexeddb.js') +broker_js = lambda: load_script('background/broadcast_broker.js') + ';start();' def sample_file(contents): return { @@ -118,7 +118,7 @@ def test_haketilodb_save_remove(execute_in_page): ''' async function get_database_contents() { - const db = await haketilodb.get(); + const db = await get_db(); const transaction = db.transaction(db.objectStoreNames); const store_names_reqs = [...db.objectStoreNames] @@ -226,7 +226,7 @@ def test_haketilodb_save_remove(execute_in_page): for i, item_type in enumerate(['resource', 'mapping']): results[i] = execute_in_page( f'''{{ - const remover = haketilodb.remove_{item_type}; + const remover = remove_{item_type}; const promise = start_items_transaction(["{item_type}s"], {{}}) .then(ctx => remover('helloapple', ctx).then(() => ctx)) @@ -282,10 +282,6 @@ def test_haketilodb_save_remove(execute_in_page): assert database_contents['resources'] == [sample_resource] assert database_contents['mappings'] == [sample_mapping] -def broker_js(): - js = load_script('background/broadcast_broker.js', ['common', 'background']) - return js + ';start_broadcast_broker();' - test_page_html = ''' @@ -336,8 +332,7 @@ def test_haketilodb_track(driver, execute_in_page, wait_elem_text): driver.switch_to.window(window) execute_in_page('initial_data = arguments[0];', initial_data) - # See if haketilodb.track_*() functions properly return the already-existing - # items. + # See if track_*() functions properly return the already-existing items. execute_in_page( ''' function update_item(store_name, change) @@ -361,9 +356,9 @@ def test_haketilodb_track(driver, execute_in_page, wait_elem_text): const update_mapping = change => update_item("mappings", change); [resource_tracking, resource_items] = - await haketilodb.track_resources(update_resource); + await track_resources(update_resource); [mapping_tracking, mapping_items] = - await haketilodb.track_mappings(update_mapping); + await track_mappings(update_mapping); for (const item of resource_items) update_resource({identifier: item.identifier, new_val: item}); @@ -404,8 +399,7 @@ def test_haketilodb_track(driver, execute_in_page, wait_elem_text): }, 'files': sample_files_by_hash } - execute_in_page('returnval(haketilodb.save_items(arguments[0]));', - sample_data) + execute_in_page('returnval(save_items(arguments[0]));', sample_data) driver.switch_to.window(windows[0]) driver.implicitly_wait(10) @@ -421,11 +415,11 @@ def test_haketilodb_track(driver, execute_in_page, wait_elem_text): '''{ async function remove_items() { - const store_names = ["resources", "mappings"]; - const ctx = await haketilodb.start_items_transaction(store_names, {}); - await haketilodb.remove_resource("helloapple", ctx); - await haketilodb.remove_mapping("helloapple-copy", ctx); - await haketilodb.finalize_items_transaction(ctx); + const store_names = ["resources", "mappings"]; + const ctx = await start_items_transaction(store_names, {}); + await remove_resource("helloapple", ctx); + await remove_mapping("helloapple-copy", ctx); + await finalize_items_transaction(ctx); } returnval(remove_items()); }''') diff --git a/test/unit/test_patterns.py b/test/unit/test_patterns.py index 99e1ed5..f2eeaf8 100644 --- a/test/unit/test_patterns.py +++ b/test/unit/test_patterns.py @@ -21,17 +21,13 @@ import pytest from ..script_loader import load_script -@pytest.fixture(scope="session") -def patterns_code(): - yield load_script('common/patterns.js', ['common']) - @pytest.mark.get_page('https://gotmyowndoma.in') -def test_regexes(execute_in_page, patterns_code): +def test_regexes(execute_in_page): """ patterns.js contains regexes used for URL parsing. Verify they work properly. """ - execute_in_page(patterns_code) + execute_in_page(load_script('common/patterns.js')) valid_url = 'https://example.com/a/b?ver=1.2.3#heading2' valid_url_rest = 'example.com/a/b?ver=1.2.3#heading2' @@ -92,12 +88,12 @@ def test_regexes(execute_in_page, patterns_code): assert match is None @pytest.mark.get_page('https://gotmyowndoma.in') -def test_deconstruct_url(execute_in_page, patterns_code): +def test_deconstruct_url(execute_in_page): """ patterns.js contains deconstruct_url() function that handles URL parsing. Verify it works properly. """ - execute_in_page(patterns_code) + execute_in_page(load_script('common/patterns.js')) deco = execute_in_page('returnval(deconstruct_url(arguments[0]));', 'https://eXaMpLe.com/a/b?ver=1.2.3#heading2') diff --git a/test/unit/test_patterns_query_tree.py b/test/unit/test_patterns_query_tree.py index a67e22f..80bf554 100644 --- a/test/unit/test_patterns_query_tree.py +++ b/test/unit/test_patterns_query_tree.py @@ -21,18 +21,14 @@ import pytest from ..script_loader import load_script -@pytest.fixture(scope="session") -def patterns_tree_code(): - yield load_script('common/patterns_query_tree.js', ['common']) - @pytest.mark.get_page('https://gotmyowndoma.in') -def test_modify_branch(execute_in_page, patterns_tree_code): +def test_modify_branch(execute_in_page): """ patterns_query_tree.js contains Pattern Tree data structure that allows arrays of string labels to be mapped to items. Verify operations modifying a single branch of such tree work properly. """ - execute_in_page(patterns_tree_code) + execute_in_page(load_script('common/patterns_query_tree.js')) execute_in_page( ''' let items_added; @@ -197,13 +193,13 @@ def test_modify_branch(execute_in_page, patterns_tree_code): } @pytest.mark.get_page('https://gotmyowndoma.in') -def test_search_branch(execute_in_page, patterns_tree_code): +def test_search_branch(execute_in_page): """ patterns_query_tree.js contains Pattern Tree data structure that allows arrays of string labels to be mapped to items. Verify searching a single branch of such tree work properly. """ - execute_in_page(patterns_tree_code) + execute_in_page(load_script('common/patterns_query_tree.js')) execute_in_page( ''' const item_adder = item => (array => [...(array || []), item]); @@ -285,13 +281,13 @@ def test_search_branch(execute_in_page, patterns_tree_code): raise e from None @pytest.mark.get_page('https://gotmyowndoma.in') -def test_pattern_tree(execute_in_page, patterns_tree_code): +def test_pattern_tree(execute_in_page): """ patterns_query_tree.js contains Pattern Tree data structure that allows arrays of string labels to be mapped to items. Verify operations on entire such tree work properly. """ - execute_in_page(patterns_tree_code) + execute_in_page(load_script('common/patterns_query_tree.js')) # Perform tests with all possible patterns for a simple URL. url = 'https://example.com' @@ -315,12 +311,12 @@ def test_pattern_tree(execute_in_page, patterns_tree_code): tree, result = execute_in_page( '''{ - const tree = pattern_tree.make(); + const tree = pattern_tree_make(); for (const pattern of arguments[0].concat(arguments[1])) { - pattern_tree.register(tree, pattern, 'key', pattern); - pattern_tree.register(tree, pattern + '/', 'key', pattern + '/'); + pattern_tree_register(tree, pattern, 'key', pattern); + pattern_tree_register(tree, pattern + '/', 'key', pattern + '/'); } - returnval([tree, [...pattern_tree.search(tree, arguments[2])]]); + returnval([tree, [...pattern_tree_search(tree, arguments[2])]]); }''', patterns, bad_patterns, url) assert expected == result @@ -333,10 +329,10 @@ def test_pattern_tree(execute_in_page, patterns_tree_code): '''{ const tree = arguments[0]; for (const pattern of arguments[1]) { - pattern_tree.deregister(tree, pattern, 'key'); - pattern_tree.deregister(tree, pattern + '/', 'key'); + pattern_tree_deregister(tree, pattern, 'key'); + pattern_tree_deregister(tree, pattern + '/', 'key'); } - returnval([tree, [...pattern_tree.search(tree, arguments[2])]]); + returnval([tree, [...pattern_tree_search(tree, arguments[2])]]); }''', tree, patterns_removed, url) assert expected == result @@ -346,8 +342,8 @@ def test_pattern_tree(execute_in_page, patterns_tree_code): '''{ const tree = arguments[0]; for (const pattern of arguments[1].concat(arguments[2])) { - pattern_tree.deregister(tree, pattern, 'key'); - pattern_tree.deregister(tree, pattern + '/', 'key'); + pattern_tree_deregister(tree, pattern, 'key'); + pattern_tree_deregister(tree, pattern + '/', 'key'); } returnval(tree); }''', @@ -439,12 +435,12 @@ def test_pattern_tree(execute_in_page, patterns_tree_code): tree, result = execute_in_page( '''{ - const tree = pattern_tree.make(); + const tree = pattern_tree_make(); for (const pattern of arguments[0].concat(arguments[1])) { - pattern_tree.register(tree, pattern, 'key', pattern); - pattern_tree.register(tree, pattern + '/', 'key', pattern + '/'); + pattern_tree_register(tree, pattern, 'key', pattern); + pattern_tree_register(tree, pattern + '/', 'key', pattern + '/'); } - returnval([tree, [...pattern_tree.search(tree, arguments[2])]]); + returnval([tree, [...pattern_tree_search(tree, arguments[2])]]); }''', patterns, bad_patterns, url) assert expected == result @@ -456,8 +452,8 @@ def test_pattern_tree(execute_in_page, patterns_tree_code): '''{ const tree = arguments[0]; for (const pattern of arguments[1]) - pattern_tree.deregister(tree, pattern + '/', 'key'); - returnval([tree, [...pattern_tree.search(tree, arguments[2])]]); + pattern_tree_deregister(tree, pattern + '/', 'key'); + returnval([tree, [...pattern_tree_search(tree, arguments[2])]]); }''', tree, patterns, url) assert expected == result @@ -467,10 +463,10 @@ def test_pattern_tree(execute_in_page, patterns_tree_code): '''{ const tree = arguments[0]; for (const pattern of arguments[1]) - pattern_tree.deregister(tree, pattern, 'key'); + pattern_tree_deregister(tree, pattern, 'key'); for (const pattern of arguments[2]) { - pattern_tree.deregister(tree, pattern, 'key'); - pattern_tree.deregister(tree, pattern + '/', 'key'); + pattern_tree_deregister(tree, pattern, 'key'); + pattern_tree_deregister(tree, pattern + '/', 'key'); } returnval(tree); }''', -- cgit v1.2.3