Configuration schema are checked against jsonschema draft 4 meta-schema (#10247)
fixes #10246
This commit is contained in:
		 Massimiliano Culpo
					Massimiliano Culpo
				
			
				
					committed by
					
						 Todd Gamblin
						Todd Gamblin
					
				
			
			
				
	
			
			
			 Todd Gamblin
						Todd Gamblin
					
				
			
						parent
						
							dce9fc4d63
						
					
				
				
					commit
					2e8aa6cb24
				
			
							
								
								
									
										149
									
								
								lib/spack/spack/test/data/jsonschema_meta.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										149
									
								
								lib/spack/spack/test/data/jsonschema_meta.json
									
									
									
									
									
										Normal file
									
								
							| @@ -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": {} | ||||||
|  | } | ||||||
| @@ -42,7 +42,10 @@ | |||||||
|         os.path.join(spack_lib_path, 'external', 'jinja2', 'asyncfilters.py'), |         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', 'jinja2', 'asyncsupport.py'), | ||||||
|         os.path.join(spack_lib_path, 'external', 'yaml', 'lib3'), |         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: | else: | ||||||
|     import pyqver3 as pyqver |     import pyqver3 as pyqver | ||||||
| @@ -55,7 +58,10 @@ | |||||||
|         os.path.join(spack_lib_path, 'external', 'jinja2', 'asyncfilters.py'), |         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', 'jinja2', 'asyncsupport.py'), | ||||||
|         os.path.join(spack_lib_path, 'external', 'yaml', 'lib'), |         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=()): | def pyfiles(search_paths, exclude=()): | ||||||
|   | |||||||
| @@ -3,10 +3,14 @@ | |||||||
| # | # | ||||||
| # SPDX-License-Identifier: (Apache-2.0 OR MIT) | # SPDX-License-Identifier: (Apache-2.0 OR MIT) | ||||||
|  |  | ||||||
|  | import json | ||||||
|  | import os.path | ||||||
|  | import sys | ||||||
|  |  | ||||||
| import jsonschema | import jsonschema | ||||||
| import pytest | import pytest | ||||||
|  |  | ||||||
|  | import spack.paths | ||||||
| import spack.schema | 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') | @pytest.mark.regression('9857') | ||||||
| def test_validate_spec(validate_spec_schema): | def test_validate_spec(validate_spec_schema): | ||||||
|     v = spack.schema.Validator(validate_spec_schema) |     v = spack.schema.Validator(validate_spec_schema) | ||||||
| @@ -75,3 +90,27 @@ def test_module_suffixes(module_suffixes_schema): | |||||||
|         v.validate(data) |         v.validate(data) | ||||||
|  |  | ||||||
|     assert 'is an invalid spec' in str(exc_err.value) |     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) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user