|
Packit |
534379 |
#! /usr/bin/env python
|
|
Packit |
534379 |
from __future__ import print_function
|
|
Packit |
534379 |
import sys
|
|
Packit |
534379 |
import textwrap
|
|
Packit |
534379 |
|
|
Packit |
534379 |
try:
|
|
Packit |
534379 |
import argparse
|
|
Packit |
534379 |
except ImportError:
|
|
Packit |
534379 |
print(textwrap.dedent("""
|
|
Packit |
534379 |
The argparse library could not be imported. jsonschema_suite requires
|
|
Packit |
534379 |
either Python 2.7 or for you to install argparse. You can do so by
|
|
Packit |
534379 |
running `pip install argparse`, `easy_install argparse` or by
|
|
Packit |
534379 |
downloading argparse and running `python2.6 setup.py install`.
|
|
Packit |
534379 |
|
|
Packit |
534379 |
See https://pypi.python.org/pypi/argparse for details.
|
|
Packit |
534379 |
""".strip("\n")))
|
|
Packit |
534379 |
sys.exit(1)
|
|
Packit |
534379 |
|
|
Packit |
534379 |
import errno
|
|
Packit |
534379 |
import fnmatch
|
|
Packit |
534379 |
import json
|
|
Packit |
534379 |
import os
|
|
Packit |
534379 |
import random
|
|
Packit |
534379 |
import shutil
|
|
Packit |
534379 |
import unittest
|
|
Packit |
534379 |
import warnings
|
|
Packit |
534379 |
|
|
Packit |
534379 |
if getattr(unittest, "skipIf", None) is None:
|
|
Packit |
534379 |
unittest.skipIf = lambda cond, msg : lambda fn : fn
|
|
Packit |
534379 |
|
|
Packit |
534379 |
try:
|
|
Packit |
534379 |
import jsonschema
|
|
Packit |
534379 |
except ImportError:
|
|
Packit |
534379 |
jsonschema = None
|
|
Packit |
534379 |
else:
|
|
Packit |
534379 |
validators = getattr(
|
|
Packit |
534379 |
jsonschema.validators, "validators", jsonschema.validators
|
|
Packit |
534379 |
)
|
|
Packit |
534379 |
|
|
Packit |
534379 |
|
|
Packit |
534379 |
ROOT_DIR = os.path.join(
|
|
Packit |
534379 |
os.path.dirname(__file__), os.pardir).rstrip("__pycache__")
|
|
Packit |
534379 |
SUITE_ROOT_DIR = os.path.join(ROOT_DIR, "tests")
|
|
Packit |
534379 |
|
|
Packit |
534379 |
REMOTES = {
|
|
Packit |
534379 |
"integer.json": {"type": "integer"},
|
|
Packit |
534379 |
"subSchemas.json": {
|
|
Packit |
534379 |
"integer": {"type": "integer"},
|
|
Packit |
534379 |
"refToInteger": {"$ref": "#/integer"},
|
|
Packit |
534379 |
},
|
|
Packit |
534379 |
"folder/folderInteger.json": {"type": "integer"}
|
|
Packit |
534379 |
}
|
|
Packit |
534379 |
REMOTES_DIR = os.path.join(ROOT_DIR, "remotes")
|
|
Packit |
534379 |
|
|
Packit |
534379 |
TESTSUITE_SCHEMA = {
|
|
Packit |
534379 |
"$schema": "http://json-schema.org/draft-03/schema#",
|
|
Packit |
534379 |
"type": "array",
|
|
Packit |
534379 |
"items": {
|
|
Packit |
534379 |
"type": "object",
|
|
Packit |
534379 |
"properties": {
|
|
Packit |
534379 |
"description": {"type": "string", "required": True},
|
|
Packit |
534379 |
"schema": {"required": True},
|
|
Packit |
534379 |
"tests": {
|
|
Packit |
534379 |
"type": "array",
|
|
Packit |
534379 |
"items": {
|
|
Packit |
534379 |
"type": "object",
|
|
Packit |
534379 |
"properties": {
|
|
Packit |
534379 |
"description": {"type": "string", "required": True},
|
|
Packit |
534379 |
"data": {"required": True},
|
|
Packit |
534379 |
"valid": {"type": "boolean", "required": True}
|
|
Packit |
534379 |
},
|
|
Packit |
534379 |
"additionalProperties": False
|
|
Packit |
534379 |
},
|
|
Packit |
534379 |
"minItems": 1
|
|
Packit |
534379 |
}
|
|
Packit |
534379 |
},
|
|
Packit |
534379 |
"additionalProperties": False,
|
|
Packit |
534379 |
"minItems": 1
|
|
Packit |
534379 |
}
|
|
Packit |
534379 |
}
|
|
Packit |
534379 |
|
|
Packit |
534379 |
|
|
Packit |
534379 |
def files(paths):
|
|
Packit |
534379 |
for path in paths:
|
|
Packit |
534379 |
with open(path) as test_file:
|
|
Packit |
534379 |
yield json.load(test_file)
|
|
Packit |
534379 |
|
|
Packit |
534379 |
|
|
Packit |
534379 |
def groups(paths):
|
|
Packit |
534379 |
for test_file in files(paths):
|
|
Packit |
534379 |
for group in test_file:
|
|
Packit |
534379 |
yield group
|
|
Packit |
534379 |
|
|
Packit |
534379 |
|
|
Packit |
534379 |
def cases(paths):
|
|
Packit |
534379 |
for test_group in groups(paths):
|
|
Packit |
534379 |
for test in test_group["tests"]:
|
|
Packit |
534379 |
test["schema"] = test_group["schema"]
|
|
Packit |
534379 |
yield test
|
|
Packit |
534379 |
|
|
Packit |
534379 |
|
|
Packit |
534379 |
def collect(root_dir):
|
|
Packit |
534379 |
for root, dirs, files in os.walk(root_dir):
|
|
Packit |
534379 |
for filename in fnmatch.filter(files, "*.json"):
|
|
Packit |
534379 |
yield os.path.join(root, filename)
|
|
Packit |
534379 |
|
|
Packit |
534379 |
|
|
Packit |
534379 |
class SanityTests(unittest.TestCase):
|
|
Packit |
534379 |
@classmethod
|
|
Packit |
534379 |
def setUpClass(cls):
|
|
Packit |
534379 |
print("Looking for tests in %s" % SUITE_ROOT_DIR)
|
|
Packit |
534379 |
cls.test_files = list(collect(SUITE_ROOT_DIR))
|
|
Packit |
534379 |
print("Found %s test files" % len(cls.test_files))
|
|
Packit |
534379 |
assert cls.test_files, "Didn't find the test files!"
|
|
Packit |
534379 |
|
|
Packit |
534379 |
def test_all_files_are_valid_json(self):
|
|
Packit |
534379 |
for path in self.test_files:
|
|
Packit |
534379 |
with open(path) as test_file:
|
|
Packit |
534379 |
try:
|
|
Packit |
534379 |
json.load(test_file)
|
|
Packit |
534379 |
except ValueError as error:
|
|
Packit |
534379 |
self.fail("%s contains invalid JSON (%s)" % (path, error))
|
|
Packit |
534379 |
|
|
Packit |
534379 |
def test_all_descriptions_have_reasonable_length(self):
|
|
Packit |
534379 |
for case in cases(self.test_files):
|
|
Packit |
534379 |
descript = case["description"]
|
|
Packit |
534379 |
self.assertLess(
|
|
Packit |
534379 |
len(descript),
|
|
Packit |
534379 |
60,
|
|
Packit |
534379 |
"%r is too long! (keep it to less than 60 chars)" % (descript,)
|
|
Packit |
534379 |
)
|
|
Packit |
534379 |
|
|
Packit |
534379 |
def test_all_descriptions_are_unique(self):
|
|
Packit |
534379 |
for group in groups(self.test_files):
|
|
Packit |
534379 |
descriptions = set(test["description"] for test in group["tests"])
|
|
Packit |
534379 |
self.assertEqual(
|
|
Packit |
534379 |
len(descriptions),
|
|
Packit |
534379 |
len(group["tests"]),
|
|
Packit |
534379 |
"%r contains a duplicate description" % (group,)
|
|
Packit |
534379 |
)
|
|
Packit |
534379 |
|
|
Packit |
534379 |
@unittest.skipIf(jsonschema is None, "Validation library not present!")
|
|
Packit |
534379 |
def test_all_schemas_are_valid(self):
|
|
Packit |
534379 |
for schema in os.listdir(SUITE_ROOT_DIR):
|
|
Packit |
534379 |
schema_validator = validators.get(schema)
|
|
Packit |
534379 |
if schema_validator is not None:
|
|
Packit |
534379 |
test_files = collect(os.path.join(SUITE_ROOT_DIR, schema))
|
|
Packit |
534379 |
for case in cases(test_files):
|
|
Packit |
534379 |
try:
|
|
Packit |
534379 |
schema_validator.check_schema(case["schema"])
|
|
Packit |
534379 |
except jsonschema.SchemaError as error:
|
|
Packit |
534379 |
self.fail("%s contains an invalid schema (%s)" %
|
|
Packit |
534379 |
(case, error))
|
|
Packit |
534379 |
else:
|
|
Packit |
534379 |
warnings.warn("No schema validator for %s" % schema)
|
|
Packit |
534379 |
|
|
Packit |
534379 |
@unittest.skipIf(jsonschema is None, "Validation library not present!")
|
|
Packit |
534379 |
def test_suites_are_valid(self):
|
|
Packit |
534379 |
validator = jsonschema.Draft3Validator(TESTSUITE_SCHEMA)
|
|
Packit |
534379 |
for tests in files(self.test_files):
|
|
Packit |
534379 |
try:
|
|
Packit |
534379 |
validator.validate(tests)
|
|
Packit |
534379 |
except jsonschema.ValidationError as error:
|
|
Packit |
534379 |
self.fail(str(error))
|
|
Packit |
534379 |
|
|
Packit |
534379 |
def test_remote_schemas_are_updated(self):
|
|
Packit |
534379 |
for url, schema in REMOTES.items():
|
|
Packit |
534379 |
filepath = os.path.join(REMOTES_DIR, url)
|
|
Packit |
534379 |
with open(filepath) as schema_file:
|
|
Packit |
534379 |
self.assertEqual(json.load(schema_file), schema)
|
|
Packit |
534379 |
|
|
Packit |
534379 |
|
|
Packit |
534379 |
def main(arguments):
|
|
Packit |
534379 |
if arguments.command == "check":
|
|
Packit |
534379 |
suite = unittest.TestLoader().loadTestsFromTestCase(SanityTests)
|
|
Packit |
534379 |
result = unittest.TextTestRunner(verbosity=2).run(suite)
|
|
Packit |
534379 |
sys.exit(not result.wasSuccessful())
|
|
Packit |
534379 |
elif arguments.command == "flatten":
|
|
Packit |
534379 |
selected_cases = [case for case in cases(collect(arguments.version))]
|
|
Packit |
534379 |
|
|
Packit |
534379 |
if arguments.randomize:
|
|
Packit |
534379 |
random.shuffle(selected_cases)
|
|
Packit |
534379 |
|
|
Packit |
534379 |
json.dump(selected_cases, sys.stdout, indent=4, sort_keys=True)
|
|
Packit |
534379 |
elif arguments.command == "remotes":
|
|
Packit |
534379 |
json.dump(REMOTES, sys.stdout, indent=4, sort_keys=True)
|
|
Packit |
534379 |
elif arguments.command == "dump_remotes":
|
|
Packit |
534379 |
if arguments.update:
|
|
Packit |
534379 |
shutil.rmtree(arguments.out_dir, ignore_errors=True)
|
|
Packit |
534379 |
|
|
Packit |
534379 |
try:
|
|
Packit |
534379 |
os.makedirs(arguments.out_dir)
|
|
Packit |
534379 |
except OSError as e:
|
|
Packit |
534379 |
if e.errno == errno.EEXIST:
|
|
Packit |
534379 |
print("%s already exists. Aborting." % arguments.out_dir)
|
|
Packit |
534379 |
sys.exit(1)
|
|
Packit |
534379 |
raise
|
|
Packit |
534379 |
|
|
Packit |
534379 |
for url, schema in REMOTES.items():
|
|
Packit |
534379 |
filepath = os.path.join(arguments.out_dir, url)
|
|
Packit |
534379 |
|
|
Packit |
534379 |
try:
|
|
Packit |
534379 |
os.makedirs(os.path.dirname(filepath))
|
|
Packit |
534379 |
except OSError as e:
|
|
Packit |
534379 |
if e.errno != errno.EEXIST:
|
|
Packit |
534379 |
raise
|
|
Packit |
534379 |
|
|
Packit |
534379 |
with open(filepath, "wb") as out_file:
|
|
Packit |
534379 |
json.dump(schema, out_file, indent=4, sort_keys=True)
|
|
Packit |
534379 |
elif arguments.command == "serve":
|
|
Packit |
534379 |
try:
|
|
Packit |
534379 |
from flask import Flask, jsonify
|
|
Packit |
534379 |
except ImportError:
|
|
Packit |
534379 |
print(textwrap.dedent("""
|
|
Packit |
534379 |
The Flask library is required to serve the remote schemas.
|
|
Packit |
534379 |
|
|
Packit |
534379 |
You can install it by running `pip install Flask`.
|
|
Packit |
534379 |
|
|
Packit |
534379 |
Alternatively, see the `jsonschema_suite remotes` or
|
|
Packit |
534379 |
`jsonschema_suite dump_remotes` commands to create static files
|
|
Packit |
534379 |
that can be served with your own web server.
|
|
Packit |
534379 |
""".strip("\n")))
|
|
Packit |
534379 |
sys.exit(1)
|
|
Packit |
534379 |
|
|
Packit |
534379 |
app = Flask(__name__)
|
|
Packit |
534379 |
|
|
Packit |
534379 |
@app.route("/<path:path>")
|
|
Packit |
534379 |
def serve_path(path):
|
|
Packit |
534379 |
if path in REMOTES:
|
|
Packit |
534379 |
return jsonify(REMOTES[path])
|
|
Packit |
534379 |
return "Document does not exist.", 404
|
|
Packit |
534379 |
|
|
Packit |
534379 |
app.run(port=1234)
|
|
Packit |
534379 |
|
|
Packit |
534379 |
|
|
Packit |
534379 |
parser = argparse.ArgumentParser(
|
|
Packit |
534379 |
description="JSON Schema Test Suite utilities",
|
|
Packit |
534379 |
)
|
|
Packit |
534379 |
subparsers = parser.add_subparsers(help="utility commands", dest="command")
|
|
Packit |
534379 |
|
|
Packit |
534379 |
check = subparsers.add_parser("check", help="Sanity check the test suite.")
|
|
Packit |
534379 |
|
|
Packit |
534379 |
flatten = subparsers.add_parser(
|
|
Packit |
534379 |
"flatten",
|
|
Packit |
534379 |
help="Output a flattened file containing a selected version's test cases."
|
|
Packit |
534379 |
)
|
|
Packit |
534379 |
flatten.add_argument(
|
|
Packit |
534379 |
"--randomize",
|
|
Packit |
534379 |
action="store_true",
|
|
Packit |
534379 |
help="Randomize the order of the outputted cases.",
|
|
Packit |
534379 |
)
|
|
Packit |
534379 |
flatten.add_argument(
|
|
Packit |
534379 |
"version", help="The directory containing the version to output",
|
|
Packit |
534379 |
)
|
|
Packit |
534379 |
|
|
Packit |
534379 |
remotes = subparsers.add_parser(
|
|
Packit |
534379 |
"remotes",
|
|
Packit |
534379 |
help="Output the expected URLs and their associated schemas for remote "
|
|
Packit |
534379 |
"ref tests as a JSON object."
|
|
Packit |
534379 |
)
|
|
Packit |
534379 |
|
|
Packit |
534379 |
dump_remotes = subparsers.add_parser(
|
|
Packit |
534379 |
"dump_remotes", help="Dump the remote ref schemas into a file tree",
|
|
Packit |
534379 |
)
|
|
Packit |
534379 |
dump_remotes.add_argument(
|
|
Packit |
534379 |
"--update",
|
|
Packit |
534379 |
action="store_true",
|
|
Packit |
534379 |
help="Update the remotes in an existing directory.",
|
|
Packit |
534379 |
)
|
|
Packit |
534379 |
dump_remotes.add_argument(
|
|
Packit |
534379 |
"--out-dir",
|
|
Packit |
534379 |
default=REMOTES_DIR,
|
|
Packit |
534379 |
type=os.path.abspath,
|
|
Packit |
534379 |
help="The output directory to create as the root of the file tree",
|
|
Packit |
534379 |
)
|
|
Packit |
534379 |
|
|
Packit |
534379 |
serve = subparsers.add_parser(
|
|
Packit |
534379 |
"serve",
|
|
Packit |
534379 |
help="Start a webserver to serve schemas used by remote ref tests."
|
|
Packit |
534379 |
)
|
|
Packit |
534379 |
|
|
Packit |
534379 |
if __name__ == "__main__":
|
|
Packit |
534379 |
main(parser.parse_args())
|