From 2e8aa6cb24bf883100ee3c7c9b7ded6e8d1ec38f Mon Sep 17 00:00:00 2001 From: Massimiliano Culpo Date: Tue, 27 Aug 2019 17:31:18 +0200 Subject: Configuration schema are checked against jsonschema draft 4 meta-schema (#10247) fixes #10246 --- lib/spack/spack/test/data/jsonschema_meta.json | 149 +++++++++++++++++++++++++ lib/spack/spack/test/python_version.py | 10 +- lib/spack/spack/test/schema.py | 41 ++++++- 3 files changed, 197 insertions(+), 3 deletions(-) create mode 100644 lib/spack/spack/test/data/jsonschema_meta.json (limited to 'lib') diff --git a/lib/spack/spack/test/data/jsonschema_meta.json b/lib/spack/spack/test/data/jsonschema_meta.json new file mode 100644 index 0000000000..bcbb84743e --- /dev/null +++ b/lib/spack/spack/test/data/jsonschema_meta.json @@ -0,0 +1,149 @@ +{ + "id": "http://json-schema.org/draft-04/schema#", + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "Core schema meta-schema", + "definitions": { + "schemaArray": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#" } + }, + "positiveInteger": { + "type": "integer", + "minimum": 0 + }, + "positiveIntegerDefault0": { + "allOf": [ { "$ref": "#/definitions/positiveInteger" }, { "default": 0 } ] + }, + "simpleTypes": { + "enum": [ "array", "boolean", "integer", "null", "number", "object", "string" ] + }, + "stringArray": { + "type": "array", + "items": { "type": "string" }, + "minItems": 1, + "uniqueItems": true + } + }, + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "$schema": { + "type": "string" + }, + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "default": {}, + "multipleOf": { + "type": "number", + "minimum": 0, + "exclusiveMinimum": true + }, + "maximum": { + "type": "number" + }, + "exclusiveMaximum": { + "type": "boolean", + "default": false + }, + "minimum": { + "type": "number" + }, + "exclusiveMinimum": { + "type": "boolean", + "default": false + }, + "maxLength": { "$ref": "#/definitions/positiveInteger" }, + "minLength": { "$ref": "#/definitions/positiveIntegerDefault0" }, + "pattern": { + "type": "string", + "format": "regex" + }, + "additionalItems": { + "anyOf": [ + { "type": "boolean" }, + { "$ref": "#" } + ], + "default": {} + }, + "items": { + "anyOf": [ + { "$ref": "#" }, + { "$ref": "#/definitions/schemaArray" } + ], + "default": {} + }, + "maxItems": { "$ref": "#/definitions/positiveInteger" }, + "minItems": { "$ref": "#/definitions/positiveIntegerDefault0" }, + "uniqueItems": { + "type": "boolean", + "default": false + }, + "maxProperties": { "$ref": "#/definitions/positiveInteger" }, + "minProperties": { "$ref": "#/definitions/positiveIntegerDefault0" }, + "required": { "$ref": "#/definitions/stringArray" }, + "additionalProperties": { + "anyOf": [ + { "type": "boolean" }, + { "$ref": "#" } + ], + "default": {} + }, + "definitions": { + "type": "object", + "additionalProperties": { "$ref": "#" }, + "default": {} + }, + "properties": { + "type": "object", + "additionalProperties": { "$ref": "#" }, + "default": {} + }, + "patternProperties": { + "type": "object", + "additionalProperties": { "$ref": "#" }, + "default": {} + }, + "dependencies": { + "type": "object", + "additionalProperties": { + "anyOf": [ + { "$ref": "#" }, + { "$ref": "#/definitions/stringArray" } + ] + } + }, + "enum": { + "type": "array", + "minItems": 1, + "uniqueItems": true + }, + "type": { + "anyOf": [ + { "$ref": "#/definitions/simpleTypes" }, + { + "type": "array", + "items": { "$ref": "#/definitions/simpleTypes" }, + "minItems": 1, + "uniqueItems": true + } + ] + }, + "format": { "type": "string" }, + "allOf": { "$ref": "#/definitions/schemaArray" }, + "anyOf": { "$ref": "#/definitions/schemaArray" }, + "oneOf": { "$ref": "#/definitions/schemaArray" }, + "not": { "$ref": "#" } + }, + "dependencies": { + "exclusiveMaximum": [ "maximum" ], + "exclusiveMinimum": [ "minimum" ] + }, + "default": {} +} diff --git a/lib/spack/spack/test/python_version.py b/lib/spack/spack/test/python_version.py index baee38a40a..6875aa4655 100644 --- a/lib/spack/spack/test/python_version.py +++ b/lib/spack/spack/test/python_version.py @@ -42,7 +42,10 @@ if sys.version_info[0] < 3: os.path.join(spack_lib_path, 'external', 'jinja2', 'asyncfilters.py'), os.path.join(spack_lib_path, 'external', 'jinja2', 'asyncsupport.py'), os.path.join(spack_lib_path, 'external', 'yaml', 'lib3'), - os.path.join(spack_lib_path, 'external', 'pyqver3.py')] + os.path.join(spack_lib_path, 'external', 'pyqver3.py'), + # Uses importlib + os.path.join(spack_lib_path, 'spack', 'test', 'schema.py') + ] else: import pyqver3 as pyqver @@ -55,7 +58,10 @@ else: os.path.join(spack_lib_path, 'external', 'jinja2', 'asyncfilters.py'), os.path.join(spack_lib_path, 'external', 'jinja2', 'asyncsupport.py'), os.path.join(spack_lib_path, 'external', 'yaml', 'lib'), - os.path.join(spack_lib_path, 'external', 'pyqver2.py')] + os.path.join(spack_lib_path, 'external', 'pyqver2.py'), + # Uses importlib + os.path.join(spack_lib_path, 'spack', 'test', 'schema.py') + ] def pyfiles(search_paths, exclude=()): diff --git a/lib/spack/spack/test/schema.py b/lib/spack/spack/test/schema.py index b638647641..88c824513a 100644 --- a/lib/spack/spack/test/schema.py +++ b/lib/spack/spack/test/schema.py @@ -3,10 +3,14 @@ # # SPDX-License-Identifier: (Apache-2.0 OR MIT) +import json +import os.path +import sys + import jsonschema import pytest - +import spack.paths import spack.schema @@ -50,6 +54,17 @@ def module_suffixes_schema(): } +@pytest.fixture(scope='module') +def meta_schema(): + """Meta schema for JSON schema validation (Draft 4)""" + meta_schema_file = os.path.join( + spack.paths.test_path, 'data', 'jsonschema_meta.json' + ) + with open(meta_schema_file) as f: + ms = json.load(f) + return ms + + @pytest.mark.regression('9857') def test_validate_spec(validate_spec_schema): v = spack.schema.Validator(validate_spec_schema) @@ -75,3 +90,27 @@ def test_module_suffixes(module_suffixes_schema): v.validate(data) assert 'is an invalid spec' in str(exc_err.value) + + +@pytest.mark.regression('10246') +@pytest.mark.skipif( + sys.version_info < (2, 7), + reason='requires python2.7 or higher because of importlib') +@pytest.mark.parametrize('config_name', [ + 'compilers', + 'config', + 'env', + 'merged', + 'mirrors', + 'modules', + 'packages', + 'repos' +]) +def test_schema_validation(meta_schema, config_name): + import importlib + module_name = 'spack.schema.{0}'.format(config_name) + module = importlib.import_module(module_name) + schema = getattr(module, 'schema') + + # If this validation throws the test won't pass + jsonschema.validate(schema, meta_schema) -- cgit v1.2.3-60-g2f50