From 2edd3e361bf9ec839b81db88af301df25edf08cc Mon Sep 17 00:00:00 2001 From: Cleber Rosa Date: Wed, 27 Mar 2024 11:31:11 -0400 Subject: [PATCH] Introduce runnable recipe schema validation The runnable recipe, that is, the representation of runnables in JSON files, maps very close to the avocado.core.nrunner.runnable.Runnable class, but there has been no actual verification of its structure. This introduces a JSON schema file that should better define the runnable recipe structure. It als adds a test that checks all the existing example runnable recipes. Signed-off-by: Cleber Rosa --- contrib/schemas/runnable-recipe.schema.json | 35 +++++++++++++++++++++ selftests/check.py | 2 +- selftests/unit/nrunner_schema.py | 35 +++++++++++++++++++++ 3 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 contrib/schemas/runnable-recipe.schema.json create mode 100644 selftests/unit/nrunner_schema.py diff --git a/contrib/schemas/runnable-recipe.schema.json b/contrib/schemas/runnable-recipe.schema.json new file mode 100644 index 0000000000..f0b24a690f --- /dev/null +++ b/contrib/schemas/runnable-recipe.schema.json @@ -0,0 +1,35 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://avocado-project.org/runnable-recipe.schema.json", + "title": "runnable-recipe", + "description": "Runnable serialized in a JSON based recipe file", + "type": "object", + "properties": { + "kind": { + "description": "The kind of runnable, which should be matched to a capable runner", + "type": "string" + }, + "uri": { + "description": "The main reference to what needs to be run. This is free form, but commonly set to the path to a file containing the test or being the test, or an actual URI with multiple parts", + "type": "string" + }, + "args": { + "description": "Sequence of arguments to be interpreted by the runner. For instance, exec-test turns these into positional command line arguments", + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": false + }, + "kwargs": { + "description": "Keyword based arguments, that is, a sequence of key and values. The exec-test, for instance, will turn these into environment variables", + "type": "object" + }, + "config": { + "description": "Avocado settings that should be applied to this runnable. At least the ones declared as CONFIGURATION_USED in the runner specific for this kind should be present", + "type": "object" + } + }, + "additionalProperties": false, + "required": [ "kind" ] +} diff --git a/selftests/check.py b/selftests/check.py index 5193281774..ced2206df8 100755 --- a/selftests/check.py +++ b/selftests/check.py @@ -27,7 +27,7 @@ "job-api-7": 1, "nrunner-interface": 70, "nrunner-requirement": 16, - "unit": 667, + "unit": 668, "jobs": 11, "functional-parallel": 301, "functional-serial": 4, diff --git a/selftests/unit/nrunner_schema.py b/selftests/unit/nrunner_schema.py new file mode 100644 index 0000000000..924924fc46 --- /dev/null +++ b/selftests/unit/nrunner_schema.py @@ -0,0 +1,35 @@ +import glob +import json +import os + +from avocado import Test, skipUnless +from selftests.utils import BASEDIR + +try: + import jsonschema + + JSONSCHEMA_AVAILABLE = True +except ImportError: + JSONSCHEMA_AVAILABLE = False + + +BASE_SCHEMA_DIR = os.path.join(BASEDIR, "contrib", "schemas") +BASE_RECIPE_DIR = os.path.join(BASEDIR, "examples", "nrunner", "recipes") + + +@skipUnless(JSONSCHEMA_AVAILABLE, "jsonschema module not available") +class Schema(Test): + def _test(self, schema_filename, recipe_path): + schema_path = os.path.join(BASE_SCHEMA_DIR, schema_filename) + with open(schema_path, "r", encoding="utf-8") as schema: + with open(recipe_path, "r", encoding="utf-8") as recipe: + try: + jsonschema.validate(json.load(recipe), json.load(schema)) + except jsonschema.exceptions.ValidationError as details: + self.fail(details) + + def test_runnable_recipes(self): + for recipe_path in glob.glob( + os.path.join(BASE_RECIPE_DIR, "runnables", "*.json") + ): + self._test("runnable-recipe.schema.json", recipe_path)