summaryrefslogtreecommitdiffstats
path: root/toolkit/components/extensions/webidl-api/test
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/components/extensions/webidl-api/test')
-rw-r--r--toolkit/components/extensions/webidl-api/test/README.md60
-rw-r--r--toolkit/components/extensions/webidl-api/test/conftest.py39
-rw-r--r--toolkit/components/extensions/webidl-api/test/helpers.py22
-rw-r--r--toolkit/components/extensions/webidl-api/test/python.ini7
-rw-r--r--toolkit/components/extensions/webidl-api/test/test_all_schemas_smoketest.py22
-rw-r--r--toolkit/components/extensions/webidl-api/test/test_json_schema_parsing.py215
-rw-r--r--toolkit/components/extensions/webidl-api/test/test_json_schema_platform_diffs.py153
-rw-r--r--toolkit/components/extensions/webidl-api/test/test_webidl_from_json_schema.py110
8 files changed, 628 insertions, 0 deletions
diff --git a/toolkit/components/extensions/webidl-api/test/README.md b/toolkit/components/extensions/webidl-api/test/README.md
new file mode 100644
index 0000000000..14baae0d82
--- /dev/null
+++ b/toolkit/components/extensions/webidl-api/test/README.md
@@ -0,0 +1,60 @@
+pytest test coverage for the GenerateWebIDLBindings.py script
+=============================================================
+
+This directory contains tests for the GenerateWebIDLBindings.py script,
+which is used to parse the WebExtensions APIs schema files and generate
+the corresponding WebIDL definitions.
+
+See ["WebIDL WebExtensions API Bindings" section from the Firefox Developer documentation](https://firefox-source-docs.mozilla.org/toolkit/components/extensions/webextensions/webidl_bindings.html)
+for more details about how the script is used, this README covers only how
+this test suite works.
+
+Run tests
+---------
+
+The tests part of this test suite can be executed locally using the following `mach` command:
+
+```
+mach python-test toolkit/components/extensions/webidl-api/test
+```
+
+Write a new test file
+---------------------
+
+To add a new test file to this test suite:
+- create a new python script file named as `test_....py`
+- add the test file to the `python.ini` manifest
+- In the new test file make sure to include:
+ - copyright notes as the other test file in this directory
+ - import the helper module and call its `setup()` method (`setup` makes sure to add
+ the directory where the target script is in the python library paths and the
+ `helpers` module does also enable the code coverage if the environment variable
+ is detected):
+ ```
+ import helpers # Import test helpers module.
+ ...
+
+ helpers.setup()
+ ```
+ - don't forget to call `mozunit.main` at the end of the test file:
+ ```
+ if __name__ == "__main__":
+ mozunit.main()
+ ```
+ - add new test cases by defining new functions named as `test_...`,
+ its parameter are the names of the pytest fixture functions to
+ be passed to the test case:
+ ```
+ def test_something(base_schema, write_jsonschema_fixtures):
+ ...
+ ```
+Create new test fixtures
+------------------------
+
+All the test fixture used by this set of tests are defined in `conftest.py`
+and decorated with `@pytest.fixture`.
+
+See the pytest documentation for more details about how the pytest fixture works:
+- https://docs.pytest.org/en/latest/explanation/fixtures.html
+- https://docs.pytest.org/en/latest/how-to/fixtures.html
+- https://docs.pytest.org/en/latest/reference/fixtures.html
diff --git a/toolkit/components/extensions/webidl-api/test/conftest.py b/toolkit/components/extensions/webidl-api/test/conftest.py
new file mode 100644
index 0000000000..1e41ed0690
--- /dev/null
+++ b/toolkit/components/extensions/webidl-api/test/conftest.py
@@ -0,0 +1,39 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+import os
+
+import pytest
+
+
+@pytest.fixture
+def base_schema():
+ def inner():
+ return {
+ "namespace": "testAPIName",
+ "permissions": [],
+ "types": [],
+ "functions": [],
+ "events": [],
+ }
+
+ return inner
+
+
+@pytest.fixture
+def write_jsonschema_fixtures(tmpdir):
+ """Write test schema data into per-testcase (in tmpdir or the given directory)"""
+
+ def inner(jsonschema_fixtures, targetdir=None):
+ assert jsonschema_fixtures
+ if targetdir is None:
+ targetdir = tmpdir
+ for filename, filecontent in jsonschema_fixtures.items():
+ assert isinstance(filename, str) and filename
+ assert isinstance(filecontent, str) and filecontent
+ with open(os.path.join(targetdir, filename), "w") as jsonfile:
+ jsonfile.write(filecontent)
+ return targetdir
+
+ return inner
diff --git a/toolkit/components/extensions/webidl-api/test/helpers.py b/toolkit/components/extensions/webidl-api/test/helpers.py
new file mode 100644
index 0000000000..e2ebec7103
--- /dev/null
+++ b/toolkit/components/extensions/webidl-api/test/helpers.py
@@ -0,0 +1,22 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+import sys
+
+import mozpack.path as mozpath
+
+setup_called = False
+
+
+def setup():
+ """Add the directory of the targeted python modules to the python sys.path"""
+
+ global setup_called
+ if setup_called:
+ return
+ setup_called = True
+
+ OUR_DIR = mozpath.abspath(mozpath.dirname(__file__))
+ TARGET_MOD_DIR = mozpath.normpath(mozpath.join(OUR_DIR, ".."))
+ sys.path.append(TARGET_MOD_DIR)
diff --git a/toolkit/components/extensions/webidl-api/test/python.ini b/toolkit/components/extensions/webidl-api/test/python.ini
new file mode 100644
index 0000000000..02315599c4
--- /dev/null
+++ b/toolkit/components/extensions/webidl-api/test/python.ini
@@ -0,0 +1,7 @@
+[DEFAULT]
+subsuite = webext-python
+
+[test_all_schemas_smoketest.py]
+[test_json_schema_parsing.py]
+[test_json_schema_platform_diffs.py]
+[test_webidl_from_json_schema.py]
diff --git a/toolkit/components/extensions/webidl-api/test/test_all_schemas_smoketest.py b/toolkit/components/extensions/webidl-api/test/test_all_schemas_smoketest.py
new file mode 100644
index 0000000000..f0ab6d496e
--- /dev/null
+++ b/toolkit/components/extensions/webidl-api/test/test_all_schemas_smoketest.py
@@ -0,0 +1,22 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+import helpers # Import test helpers module.
+import mozunit
+
+helpers.setup()
+
+from GenerateWebIDLBindings import load_and_parse_JSONSchema
+
+
+def test_all_jsonschema_load_and_parse_smoketest():
+ """Make sure it can load and parse all JSONSchema files successfully"""
+ schemas = load_and_parse_JSONSchema()
+ assert schemas
+ assert len(schemas.json_schemas) > 0
+ assert len(schemas.api_namespaces) > 0
+
+
+if __name__ == "__main__":
+ mozunit.main()
diff --git a/toolkit/components/extensions/webidl-api/test/test_json_schema_parsing.py b/toolkit/components/extensions/webidl-api/test/test_json_schema_parsing.py
new file mode 100644
index 0000000000..79b59bf928
--- /dev/null
+++ b/toolkit/components/extensions/webidl-api/test/test_json_schema_parsing.py
@@ -0,0 +1,215 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+import json
+import os
+from textwrap import dedent
+
+import helpers # Import test helpers module.
+import mozunit
+import pytest
+
+helpers.setup()
+
+from GenerateWebIDLBindings import APIEvent, APIFunction, APINamespace, APIType, Schemas
+
+
+def test_parse_simple_single_api_namespace(write_jsonschema_fixtures):
+ """
+ Test Basic loading and parsing a single API JSONSchema:
+ - single line comments outside of the json structure are ignored
+ - parse a simple namespace that includes one permission, type,
+ function and event
+ """
+ schema_dir = write_jsonschema_fixtures(
+ {
+ "test_api.json": dedent(
+ """
+ // Single line comments added before the JSON data are tolerated
+ // and ignored.
+ [
+ {
+ "namespace": "fantasyApi",
+ "permissions": ["fantasyPermission"],
+ "types": [
+ {
+ "id": "MyType",
+ "type": "string",
+ "choices": ["value1", "value2"]
+ }
+ ],
+ "functions": [
+ {
+ "name": "myMethod",
+ "type": "function",
+ "parameters": [
+ { "name": "fnParam", "type": "string" },
+ { "name": "fnRefParam", "$ref": "MyType" }
+ ]
+ }
+ ],
+ "events": [
+ {
+ "name": "onSomeEvent",
+ "type": "function",
+ "parameters": [
+ { "name": "evParam", "type": "string" },
+ { "name": "evRefParam", "$ref": "MyType" }
+ ]
+ }
+ ]
+ }
+ ]
+ """
+ ),
+ }
+ )
+
+ schemas = Schemas()
+ schemas.load_schemas(schema_dir, "toolkit")
+
+ assert schemas.get_all_namespace_names() == ["fantasyApi"]
+
+ apiNs = schemas.api_namespaces["fantasyApi"]
+ assert isinstance(apiNs, APINamespace)
+
+ # Properties related to where the JSON schema is coming from
+ # (toolkit, browser or mobile schema directories).
+ assert apiNs.in_toolkit
+ assert not apiNs.in_browser
+ assert not apiNs.in_mobile
+
+ # api_path_string is expected to be exactly the namespace name for
+ # non-nested API namespaces.
+ assert apiNs.api_path_string == "fantasyApi"
+
+ # parse the schema and verify it includes the expected types events and function.
+ schemas.parse_schemas()
+
+ assert set(["fantasyPermission"]) == apiNs.permissions
+ assert ["MyType"] == list(apiNs.types.keys())
+ assert ["myMethod"] == list(apiNs.functions.keys())
+ assert ["onSomeEvent"] == list(apiNs.events.keys())
+
+ type_entry = apiNs.types.get("MyType")
+ fn_entry = apiNs.functions.get("myMethod")
+ ev_entry = apiNs.events.get("onSomeEvent")
+
+ assert isinstance(type_entry, APIType)
+ assert isinstance(fn_entry, APIFunction)
+ assert isinstance(ev_entry, APIEvent)
+
+
+def test_parse_error_on_types_without_id_or_extend(
+ base_schema, write_jsonschema_fixtures
+):
+ """
+ Test parsing types without id or $extend raise an error while parsing.
+ """
+ schema_dir = write_jsonschema_fixtures(
+ {
+ "test_broken_types.json": json.dumps(
+ [
+ {
+ **base_schema(),
+ "namespace": "testBrokenTypeAPI",
+ "types": [
+ {
+ # type with no "id2 or "$ref" properties
+ }
+ ],
+ }
+ ]
+ )
+ }
+ )
+
+ schemas = Schemas()
+ schemas.load_schemas(schema_dir, "toolkit")
+
+ with pytest.raises(
+ Exception,
+ match=r"Error loading schema type data defined in 'toolkit testBrokenTypeAPI'",
+ ):
+ schemas.parse_schemas()
+
+
+def test_parse_ignores_unsupported_types(base_schema, write_jsonschema_fixtures):
+ """
+ Test parsing types without id or $extend raise an error while parsing.
+ """
+ schema_dir = write_jsonschema_fixtures(
+ {
+ "test_broken_types.json": json.dumps(
+ [
+ {
+ **base_schema(),
+ "namespace": "testUnsupportedTypesAPI",
+ "types": [
+ {
+ "id": "AnUnsupportedType",
+ "type": "string",
+ "unsupported": True,
+ },
+ {
+ # missing id or $ref shouldn't matter
+ # no parsing error expected.
+ "unsupported": True,
+ },
+ {"id": "ASupportedType", "type": "string"},
+ ],
+ }
+ ]
+ )
+ }
+ )
+
+ schemas = Schemas()
+ schemas.load_schemas(schema_dir, "toolkit")
+ schemas.parse_schemas()
+ apiNs = schemas.api_namespaces["testUnsupportedTypesAPI"]
+ assert set(apiNs.types.keys()) == set(["ASupportedType"])
+
+
+def test_parse_error_on_namespace_with_inconsistent_max_manifest_version(
+ base_schema, write_jsonschema_fixtures, tmpdir
+):
+ """
+ Test parsing types without id or $extend raise an error while parsing.
+ """
+ browser_schema_dir = os.path.join(tmpdir, "browser")
+ mobile_schema_dir = os.path.join(tmpdir, "mobile")
+ os.mkdir(browser_schema_dir)
+ os.mkdir(mobile_schema_dir)
+
+ base_namespace_schema = {
+ **base_schema(),
+ "namespace": "testInconsistentMaxManifestVersion",
+ }
+
+ browser_schema = {**base_namespace_schema, "max_manifest_version": 2}
+ mobile_schema = {**base_namespace_schema, "max_manifest_version": 3}
+
+ write_jsonschema_fixtures(
+ {"test_inconsistent_maxmv.json": json.dumps([browser_schema])},
+ browser_schema_dir,
+ )
+
+ write_jsonschema_fixtures(
+ {"test_inconsistent_maxmv.json": json.dumps([mobile_schema])}, mobile_schema_dir
+ )
+
+ schemas = Schemas()
+ schemas.load_schemas(browser_schema_dir, "browser")
+ schemas.load_schemas(mobile_schema_dir, "mobile")
+
+ with pytest.raises(
+ TypeError,
+ match=r"Error loading schema data - overwriting existing max_manifest_version value",
+ ):
+ schemas.parse_schemas()
+
+
+if __name__ == "__main__":
+ mozunit.main()
diff --git a/toolkit/components/extensions/webidl-api/test/test_json_schema_platform_diffs.py b/toolkit/components/extensions/webidl-api/test/test_json_schema_platform_diffs.py
new file mode 100644
index 0000000000..51ae918eb0
--- /dev/null
+++ b/toolkit/components/extensions/webidl-api/test/test_json_schema_platform_diffs.py
@@ -0,0 +1,153 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+import os
+import types
+from textwrap import dedent
+
+import helpers # Import test helpers module.
+import mozunit
+
+helpers.setup()
+
+from GenerateWebIDLBindings import Schemas
+from InspectJSONSchema import run_inspect_command
+
+
+def test_inspect_schema_platform_diffs(capsys, write_jsonschema_fixtures, tmpdir):
+ """
+ Test InspectJSONSchema --dump-platform-diff command.
+ """
+ browser_schema_dir = os.path.join(tmpdir, "browser")
+ mobile_schema_dir = os.path.join(tmpdir, "mobile")
+ os.mkdir(browser_schema_dir)
+ os.mkdir(mobile_schema_dir)
+
+ write_jsonschema_fixtures(
+ {
+ "test_api_browser.json": dedent(
+ """
+ [
+ {
+ "namespace": "apiWithDiff",
+ "functions": [
+ {
+ "name": "sharedMethod",
+ "type": "function",
+ "parameters": [
+ { "name": "sharedParam", "type": "string" },
+ { "name": "desktopOnlyMethodParam", "type": "string" }
+ ]
+ },
+ {
+ "name": "desktopMethod",
+ "type": "function",
+ "parameters": []
+ }
+ ],
+ "events": [
+ {
+ "name": "onSharedEvent",
+ "type": "function",
+ "parameters": [
+ { "name": "sharedParam", "type": "string" },
+ { "name": "desktopOnlyEventParam", "type": "string" }
+ ]
+ }
+ ],
+ "properties": {
+ "sharedProperty": { "type": "string", "value": "desktop-value" },
+ "desktopOnlyProperty": { "type": "string", "value": "desktop-only-value" }
+ }
+ }
+ ]
+ """
+ )
+ },
+ browser_schema_dir,
+ )
+
+ write_jsonschema_fixtures(
+ {
+ "test_api_mobile.json": dedent(
+ """
+ [
+ {
+ "namespace": "apiWithDiff",
+ "functions": [
+ {
+ "name": "sharedMethod",
+ "type": "function",
+ "parameters": [
+ { "name": "sharedParam", "type": "string" },
+ { "name": "mobileOnlyMethodParam", "type": "string" }
+ ]
+ },
+ {
+ "name": "mobileMethod",
+ "type": "function",
+ "parameters": []
+ }
+ ],
+ "events": [
+ {
+ "name": "onSharedEvent",
+ "type": "function",
+ "parameters": [
+ { "name": "sharedParam", "type": "string" },
+ { "name": "mobileOnlyEventParam", "type": "string" }
+ ]
+ }
+ ],
+ "properties": {
+ "sharedProperty": { "type": "string", "value": "mobile-value" },
+ "mobileOnlyProperty": { "type": "string", "value": "mobile-only-value" }
+ }
+ }
+ ]
+ """
+ )
+ },
+ mobile_schema_dir,
+ )
+
+ schemas = Schemas()
+ schemas.load_schemas(browser_schema_dir, "browser")
+ schemas.load_schemas(mobile_schema_dir, "mobile")
+
+ assert schemas.get_all_namespace_names() == ["apiWithDiff"]
+ apiNs = schemas.api_namespaces["apiWithDiff"]
+ assert apiNs.in_browser
+ assert apiNs.in_mobile
+
+ apiNs.parse_schemas()
+
+ fakeArgs = types.SimpleNamespace()
+ fakeArgs.dump_namespaces_info = False
+ fakeArgs.dump_platform_diffs = True
+ fakeArgs.only_if_webidl_diffs = False
+ fakeArgs.diff_command = None
+
+ fakeParser = types.SimpleNamespace()
+ fakeParser.print_help = lambda: None
+
+ run_inspect_command(fakeArgs, schemas, fakeParser)
+
+ captured = capsys.readouterr()
+ assert "API schema desktop vs. mobile for apiWithDiff.sharedMethod" in captured.out
+ assert '- "name": "desktopOnlyMethodParam",' in captured.out
+ assert '+ "name": "mobileOnlyMethodParam",' in captured.out
+ assert "API schema desktop vs. mobile for apiWithDiff.onSharedEvent" in captured.out
+ assert '- "name": "desktopOnlyEventParam",' in captured.out
+ assert '+ "name": "mobileOnlyEventParam",' in captured.out
+ assert (
+ "API schema desktop vs. mobile for apiWithDiff.sharedProperty" in captured.out
+ )
+ assert '- "value": "desktop-value"' in captured.out
+ assert '+ "value": "mobile-value"' in captured.out
+ assert captured.err == ""
+
+
+if __name__ == "__main__":
+ mozunit.main()
diff --git a/toolkit/components/extensions/webidl-api/test/test_webidl_from_json_schema.py b/toolkit/components/extensions/webidl-api/test/test_webidl_from_json_schema.py
new file mode 100644
index 0000000000..64cd7a7361
--- /dev/null
+++ b/toolkit/components/extensions/webidl-api/test/test_webidl_from_json_schema.py
@@ -0,0 +1,110 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+from textwrap import dedent
+
+import helpers # Import test helpers module.
+import mozunit
+
+helpers.setup()
+
+from GenerateWebIDLBindings import (
+ WEBEXT_STUBS_MAPPING,
+ APIFunction,
+ Schemas,
+ WebIDLHelpers,
+)
+
+original_stub_mapping_config = WEBEXT_STUBS_MAPPING.copy()
+
+
+def teardown_function():
+ WEBEXT_STUBS_MAPPING.clear()
+ for key in original_stub_mapping_config:
+ WEBEXT_STUBS_MAPPING[key] = original_stub_mapping_config[key]
+
+
+def test_ambiguous_stub_mappings(write_jsonschema_fixtures):
+ """
+ Test generated webidl for methods that are either
+ - being marked as ambiguous because of the "allowAmbiguousOptionalArguments" property
+ in their JSONSchema definition
+ - mapped to "AsyncAmbiguous" stub per WEBEXT_STUBS_MAPPING python script config
+ """
+
+ schema_dir = write_jsonschema_fixtures(
+ {
+ "test_api.json": dedent(
+ """
+ [
+ {
+ "namespace": "testAPINamespace",
+ "functions": [
+ {
+ "name": "jsonSchemaAmbiguousMethod",
+ "type": "function",
+ "allowAmbiguousOptionalArguments": true,
+ "async": true,
+ "parameters": [
+ {"type": "any", "name": "param1", "optional": true},
+ {"type": "any", "name": "param2", "optional": true},
+ {"type": "string", "name": "param3", "optional": true}
+ ]
+ },
+ {
+ "name": "configuredAsAmbiguousMethod",
+ "type": "function",
+ "async": "callback",
+ "parameters": [
+ {"name": "param1", "optional": true, "type": "object"},
+ {"name": "callback", "type": "function", "parameters": []}
+ ]
+ }
+ ]
+ }
+ ]
+ """
+ )
+ }
+ )
+
+ assert "testAPINamespace.configuredAsAmbiguousMethod" not in WEBEXT_STUBS_MAPPING
+ # NOTE: mocked config reverted in the teardown_method pytest hook.
+ WEBEXT_STUBS_MAPPING[
+ "testAPINamespace.configuredAsAmbiguousMethod"
+ ] = "AsyncAmbiguous"
+
+ schemas = Schemas()
+ schemas.load_schemas(schema_dir, "toolkit")
+
+ assert schemas.get_all_namespace_names() == ["testAPINamespace"]
+ schemas.parse_schemas()
+
+ apiNs = schemas.get_namespace("testAPINamespace")
+ fnAmbiguousBySchema = apiNs.functions.get("jsonSchemaAmbiguousMethod")
+
+ assert isinstance(fnAmbiguousBySchema, APIFunction)
+ generated_webidl = WebIDLHelpers.to_webidl_definition(fnAmbiguousBySchema, None)
+ expected_webidl = "\n".join(
+ [
+ ' [Throws, WebExtensionStub="AsyncAmbiguous"]',
+ " any jsonSchemaAmbiguousMethod(any... args);",
+ ]
+ )
+ assert generated_webidl == expected_webidl
+
+ fnAmbiguousByConfig = apiNs.functions.get("configuredAsAmbiguousMethod")
+ assert isinstance(fnAmbiguousByConfig, APIFunction)
+ generated_webidl = WebIDLHelpers.to_webidl_definition(fnAmbiguousByConfig, None)
+ expected_webidl = "\n".join(
+ [
+ ' [Throws, WebExtensionStub="AsyncAmbiguous"]',
+ " any configuredAsAmbiguousMethod(any... args);",
+ ]
+ )
+ assert generated_webidl == expected_webidl
+
+
+if __name__ == "__main__":
+ mozunit.main()