Blame tools/extra/packager/jsonschema-2.3.0/jsonschema/tests/test_jsonschema_test_suite.py

Packit 534379
"""
Packit 534379
Test runner for the JSON Schema official test suite
Packit 534379
Packit 534379
Tests comprehensive correctness of each draft's validator.
Packit 534379
Packit 534379
See https://github.com/json-schema/JSON-Schema-Test-Suite for details.
Packit 534379
Packit 534379
"""
Packit 534379
Packit 534379
from decimal import Decimal
Packit 534379
import glob
Packit 534379
import json
Packit 534379
import io
Packit 534379
import itertools
Packit 534379
import os
Packit 534379
import re
Packit 534379
import subprocess
Packit 534379
Packit 534379
try:
Packit 534379
    from sys import pypy_version_info
Packit 534379
except ImportError:
Packit 534379
    pypy_version_info = None
Packit 534379
Packit 534379
from jsonschema import (
Packit 534379
    FormatError, SchemaError, ValidationError, Draft3Validator,
Packit 534379
    Draft4Validator, FormatChecker, draft3_format_checker,
Packit 534379
    draft4_format_checker, validate,
Packit 534379
)
Packit 534379
from jsonschema.compat import PY3
Packit 534379
from jsonschema.tests.compat import mock, unittest
Packit 534379
import jsonschema
Packit 534379
Packit 534379
Packit 534379
REPO_ROOT = os.path.join(os.path.dirname(jsonschema.__file__), os.path.pardir)
Packit 534379
SUITE = os.getenv("JSON_SCHEMA_TEST_SUITE", os.path.join(REPO_ROOT, "json"))
Packit 534379
Packit 534379
if not os.path.isdir(SUITE):
Packit 534379
    raise ValueError(
Packit 534379
        "Can't find the JSON-Schema-Test-Suite directory. Set the "
Packit 534379
        "'JSON_SCHEMA_TEST_SUITE' environment variable or run the tests from "
Packit 534379
        "alongside a checkout of the suite."
Packit 534379
    )
Packit 534379
Packit 534379
TESTS_DIR = os.path.join(SUITE, "tests")
Packit 534379
JSONSCHEMA_SUITE = os.path.join(SUITE, "bin", "jsonschema_suite")
Packit 534379
Packit 534379
REMOTES = subprocess.Popen(
Packit 534379
    ["python", JSONSCHEMA_SUITE, "remotes"], stdout=subprocess.PIPE,
Packit 534379
).stdout
Packit 534379
if PY3:
Packit 534379
    REMOTES = io.TextIOWrapper(REMOTES)
Packit 534379
REMOTES = json.load(REMOTES)
Packit 534379
Packit 534379
Packit 534379
def make_case(schema, data, valid, name):
Packit 534379
    if valid:
Packit 534379
        def test_case(self):
Packit 534379
            kwargs = getattr(self, "validator_kwargs", {})
Packit 534379
            validate(data, schema, cls=self.validator_class, **kwargs)
Packit 534379
    else:
Packit 534379
        def test_case(self):
Packit 534379
            kwargs = getattr(self, "validator_kwargs", {})
Packit 534379
            with self.assertRaises(ValidationError):
Packit 534379
                validate(data, schema, cls=self.validator_class, **kwargs)
Packit 534379
Packit 534379
    if not PY3:
Packit 534379
        name = name.encode("utf-8")
Packit 534379
    test_case.__name__ = name
Packit 534379
Packit 534379
    return test_case
Packit 534379
Packit 534379
Packit 534379
def maybe_skip(skip, test, case):
Packit 534379
    if skip is not None:
Packit 534379
        reason = skip(case)
Packit 534379
        if reason is not None:
Packit 534379
            test = unittest.skip(reason)(test)
Packit 534379
    return test
Packit 534379
Packit 534379
Packit 534379
def load_json_cases(tests_glob, ignore_glob="", basedir=TESTS_DIR, skip=None):
Packit 534379
    if ignore_glob:
Packit 534379
        ignore_glob = os.path.join(basedir, ignore_glob)
Packit 534379
Packit 534379
    def add_test_methods(test_class):
Packit 534379
        ignored = set(glob.iglob(ignore_glob))
Packit 534379
Packit 534379
        for filename in glob.iglob(os.path.join(basedir, tests_glob)):
Packit 534379
            if filename in ignored:
Packit 534379
                continue
Packit 534379
Packit 534379
            validating, _ = os.path.splitext(os.path.basename(filename))
Packit 534379
            id = itertools.count(1)
Packit 534379
Packit 534379
            with open(filename) as test_file:
Packit 534379
                for case in json.load(test_file):
Packit 534379
                    for test in case["tests"]:
Packit 534379
                        name = "test_%s_%s_%s" % (
Packit 534379
                            validating,
Packit 534379
                            next(id),
Packit 534379
                            re.sub(r"[\W ]+", "_", test["description"]),
Packit 534379
                        )
Packit 534379
                        assert not hasattr(test_class, name), name
Packit 534379
Packit 534379
                        test_case = make_case(
Packit 534379
                            data=test["data"],
Packit 534379
                            schema=case["schema"],
Packit 534379
                            valid=test["valid"],
Packit 534379
                            name=name,
Packit 534379
                        )
Packit 534379
                        test_case = maybe_skip(skip, test_case, case)
Packit 534379
                        setattr(test_class, name, test_case)
Packit 534379
Packit 534379
        return test_class
Packit 534379
    return add_test_methods
Packit 534379
Packit 534379
Packit 534379
class TypesMixin(object):
Packit 534379
    @unittest.skipIf(PY3, "In Python 3 json.load always produces unicode")
Packit 534379
    def test_string_a_bytestring_is_a_string(self):
Packit 534379
        self.validator_class({"type": "string"}).validate(b"foo")
Packit 534379
Packit 534379
Packit 534379
class DecimalMixin(object):
Packit 534379
    def test_it_can_validate_with_decimals(self):
Packit 534379
        schema = {"type": "number"}
Packit 534379
        validator = self.validator_class(
Packit 534379
            schema, types={"number": (int, float, Decimal)}
Packit 534379
        )
Packit 534379
Packit 534379
        for valid in [1, 1.1, Decimal(1) / Decimal(8)]:
Packit 534379
            validator.validate(valid)
Packit 534379
Packit 534379
        for invalid in ["foo", {}, [], True, None]:
Packit 534379
            with self.assertRaises(ValidationError):
Packit 534379
                validator.validate(invalid)
Packit 534379
Packit 534379
Packit 534379
def missing_format(checker):
Packit 534379
    def missing_format(case):
Packit 534379
        format = case["schema"].get("format")
Packit 534379
        if format not in checker.checkers:
Packit 534379
            return "Format checker {0!r} not found.".format(format)
Packit 534379
        elif (
Packit 534379
            format == "date-time" and
Packit 534379
            pypy_version_info is not None and
Packit 534379
            pypy_version_info[:2] <= (1, 9)
Packit 534379
        ):
Packit 534379
            # datetime.datetime is overzealous about typechecking in <=1.9
Packit 534379
            return "datetime.datetime is broken on this version of PyPy."
Packit 534379
    return missing_format
Packit 534379
Packit 534379
Packit 534379
class FormatMixin(object):
Packit 534379
    def test_it_returns_true_for_formats_it_does_not_know_about(self):
Packit 534379
        validator = self.validator_class(
Packit 534379
            {"format": "carrot"}, format_checker=FormatChecker(),
Packit 534379
        )
Packit 534379
        validator.validate("bugs")
Packit 534379
Packit 534379
    def test_it_does_not_validate_formats_by_default(self):
Packit 534379
        validator = self.validator_class({})
Packit 534379
        self.assertIsNone(validator.format_checker)
Packit 534379
Packit 534379
    def test_it_validates_formats_if_a_checker_is_provided(self):
Packit 534379
        checker = mock.Mock(spec=FormatChecker)
Packit 534379
        validator = self.validator_class(
Packit 534379
            {"format": "foo"}, format_checker=checker,
Packit 534379
        )
Packit 534379
Packit 534379
        validator.validate("bar")
Packit 534379
Packit 534379
        checker.check.assert_called_once_with("bar", "foo")
Packit 534379
Packit 534379
        cause = ValueError()
Packit 534379
        checker.check.side_effect = FormatError('aoeu', cause=cause)
Packit 534379
Packit 534379
        with self.assertRaises(ValidationError) as cm:
Packit 534379
            validator.validate("bar")
Packit 534379
        # Make sure original cause is attached
Packit 534379
        self.assertIs(cm.exception.cause, cause)
Packit 534379
Packit 534379
    def test_it_validates_formats_of_any_type(self):
Packit 534379
        checker = mock.Mock(spec=FormatChecker)
Packit 534379
        validator = self.validator_class(
Packit 534379
            {"format": "foo"}, format_checker=checker,
Packit 534379
        )
Packit 534379
Packit 534379
        validator.validate([1, 2, 3])
Packit 534379
Packit 534379
        checker.check.assert_called_once_with([1, 2, 3], "foo")
Packit 534379
Packit 534379
        cause = ValueError()
Packit 534379
        checker.check.side_effect = FormatError('aoeu', cause=cause)
Packit 534379
Packit 534379
        with self.assertRaises(ValidationError) as cm:
Packit 534379
            validator.validate([1, 2, 3])
Packit 534379
        # Make sure original cause is attached
Packit 534379
        self.assertIs(cm.exception.cause, cause)
Packit 534379
Packit 534379
Packit 534379
@load_json_cases("draft3/*.json", ignore_glob="draft3/refRemote.json")
Packit 534379
@load_json_cases(
Packit 534379
    "draft3/optional/format.json", skip=missing_format(draft3_format_checker)
Packit 534379
)
Packit 534379
@load_json_cases("draft3/optional/bignum.json")
Packit 534379
@load_json_cases("draft3/optional/zeroTerminatedFloats.json")
Packit 534379
class TestDraft3(unittest.TestCase, TypesMixin, DecimalMixin, FormatMixin):
Packit 534379
    validator_class = Draft3Validator
Packit 534379
    validator_kwargs = {"format_checker": draft3_format_checker}
Packit 534379
Packit 534379
    def test_any_type_is_valid_for_type_any(self):
Packit 534379
        validator = self.validator_class({"type": "any"})
Packit 534379
        validator.validate(mock.Mock())
Packit 534379
Packit 534379
    # TODO: we're in need of more meta schema tests
Packit 534379
    def test_invalid_properties(self):
Packit 534379
        with self.assertRaises(SchemaError):
Packit 534379
            validate({}, {"properties": {"test": True}},
Packit 534379
                     cls=self.validator_class)
Packit 534379
Packit 534379
    def test_minItems_invalid_string(self):
Packit 534379
        with self.assertRaises(SchemaError):
Packit 534379
            # needs to be an integer
Packit 534379
            validate([1], {"minItems": "1"}, cls=self.validator_class)
Packit 534379
Packit 534379
Packit 534379
@load_json_cases("draft4/*.json", ignore_glob="draft4/refRemote.json")
Packit 534379
@load_json_cases(
Packit 534379
    "draft4/optional/format.json", skip=missing_format(draft4_format_checker)
Packit 534379
)
Packit 534379
@load_json_cases("draft4/optional/bignum.json")
Packit 534379
@load_json_cases("draft4/optional/zeroTerminatedFloats.json")
Packit 534379
class TestDraft4(unittest.TestCase, TypesMixin, DecimalMixin, FormatMixin):
Packit 534379
    validator_class = Draft4Validator
Packit 534379
    validator_kwargs = {"format_checker": draft4_format_checker}
Packit 534379
Packit 534379
    # TODO: we're in need of more meta schema tests
Packit 534379
    def test_invalid_properties(self):
Packit 534379
        with self.assertRaises(SchemaError):
Packit 534379
            validate({}, {"properties": {"test": True}},
Packit 534379
                     cls=self.validator_class)
Packit 534379
Packit 534379
    def test_minItems_invalid_string(self):
Packit 534379
        with self.assertRaises(SchemaError):
Packit 534379
            # needs to be an integer
Packit 534379
            validate([1], {"minItems": "1"}, cls=self.validator_class)
Packit 534379
Packit 534379
Packit 534379
class RemoteRefResolutionMixin(object):
Packit 534379
    def setUp(self):
Packit 534379
        patch = mock.patch("jsonschema.validators.requests")
Packit 534379
        requests = patch.start()
Packit 534379
        requests.get.side_effect = self.resolve
Packit 534379
        self.addCleanup(patch.stop)
Packit 534379
Packit 534379
    def resolve(self, reference):
Packit 534379
        _, _, reference = reference.partition("http://localhost:1234/")
Packit 534379
        return mock.Mock(**{"json.return_value": REMOTES.get(reference)})
Packit 534379
Packit 534379
Packit 534379
@load_json_cases("draft3/refRemote.json")
Packit 534379
class Draft3RemoteResolution(RemoteRefResolutionMixin, unittest.TestCase):
Packit 534379
    validator_class = Draft3Validator
Packit 534379
Packit 534379
Packit 534379
@load_json_cases("draft4/refRemote.json")
Packit 534379
class Draft4RemoteResolution(RemoteRefResolutionMixin, unittest.TestCase):
Packit 534379
    validator_class = Draft4Validator