Skip to content

Commit c09578b

Browse files
committed
Squashed 'json/' changes from b7d13f4b..69acf529
69acf529 Merge pull request #572 from json-schema-org/default-reviewers 26a51ca3 Merge pull request #575 from jdesrosiers/add-package-json d0e80a96 Re-add package.json 88d69482 Merge pull request #573 from json-schema-org/handling-nulls-in-instances bd653f34 fixed findings by julian eab34ba6 fix `prefixItems` tests to actually use it (instead of `items`, copy-paste error) 6680a003 fixed typo in `type` declaration: surround with quotes 257f5220 added null-handling tests for unevaluatedItems e3c007dd added null-handling tests for unevaluatedProperties 3d3bdf4e added null-handling tests for properties 54cc5703 added null-handling tests for patternProperties 28419842 added null-handling tests for contains 4d1a916a added null-handling tests for additionalItems 6e47a99a added null-handling tests for prefixItems; fixed indentation on previous commit 761d4b2b added null-handling tests for array-form items 6315a51d added null-handling tests for single-form items 278e5b3b added null-handling tests for additionalProperties bce6b82a Set up default reviewers (the whole test suite team). 9cb7da92 Merge pull request #564 from json-schema-org/sanity-check-terminology 2a316683 Merge pull request #569 from spacether/feat_adds_new_python_consumer fae0017d Removes idea files 8af12a3f Adds python-experimental to the users list c7a0f3e9 Reconcile terminology in the sanity checker with the README git-subtree-dir: json git-subtree-split: 69acf52990b004240839ae19b4bec8fb01d50876
1 parent 10104f7 commit c09578b

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+852
-62
lines changed

.github/CODEOWNERS

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Ping the entire test suite team by default.
2+
* @json-schema-org/test-suite-team

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,7 @@ Node-specific support is maintained in a [separate repository](https://github.co
286286
* [fastjsonschema](https://github.com/seznam/python-fastjsonschema)
287287
* [hypothesis-jsonschema](https://github.com/Zac-HD/hypothesis-jsonschema)
288288
* [jschon](https://github.com/marksparkza/jschon)
289+
* [python-experimental, OpenAPI Generator](https://github.com/OpenAPITools/openapi-generator/blob/master/docs/generators/python-experimental.md)
289290

290291
### Ruby
291292

bin/jsonschema_suite

+73-62
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#! /usr/bin/env python3
2+
from pathlib import Path
23
import argparse
34
import errno
4-
import fnmatch
55
import json
66
import os
77
import random
@@ -28,119 +28,133 @@ else:
2828
}
2929

3030

31-
ROOT_DIR = os.path.abspath(
32-
os.path.join(os.path.dirname(__file__), os.pardir).rstrip("__pycache__"),
33-
)
34-
SUITE_ROOT_DIR = os.path.join(ROOT_DIR, "tests")
35-
REMOTES_DIR = os.path.join(ROOT_DIR, "remotes")
36-
31+
ROOT_DIR = Path(__file__).parent.parent
32+
SUITE_ROOT_DIR = ROOT_DIR / "tests"
33+
REMOTES_DIR = ROOT_DIR / "remotes"
3734

38-
with open(os.path.join(ROOT_DIR, "test-schema.json")) as schema:
39-
TESTSUITE_SCHEMA = json.load(schema)
35+
TESTSUITE_SCHEMA = json.loads((ROOT_DIR / "test-schema.json").read_text())
4036

4137

4238
def files(paths):
4339
"""
44-
Each test file in the provided paths.
40+
Each test file in the provided paths, as an array of test cases.
4541
"""
4642
for path in paths:
47-
with open(path) as test_file:
48-
yield json.load(test_file)
43+
yield json.loads(path.read_text())
4944

5045

51-
def groups(paths):
46+
def cases(paths):
5247
"""
53-
Each test group within each file in the provided paths.
48+
Each test case within each file in the provided paths.
5449
"""
5550
for test_file in files(paths):
56-
for group in test_file:
57-
yield group
51+
yield from test_file
5852

5953

60-
def cases(paths):
54+
def tests(paths):
6155
"""
62-
Each individual test case within all groups within the provided paths.
56+
Each individual test within all cases within the provided paths.
6357
"""
64-
for test_group in groups(paths):
65-
for test in test_group["tests"]:
66-
test["schema"] = test_group["schema"]
58+
for case in cases(paths):
59+
for test in case["tests"]:
60+
test["schema"] = case["schema"]
6761
yield test
6862

6963

7064
def collect(root_dir):
7165
"""
7266
All of the test file paths within the given root directory, recursively.
7367
"""
74-
for root, _, files in os.walk(root_dir):
75-
for filename in fnmatch.filter(files, "*.json"):
76-
yield os.path.join(root, filename)
68+
return root_dir.glob("**/*.json")
7769

7870

7971
class SanityTests(unittest.TestCase):
8072
@classmethod
8173
def setUpClass(cls):
82-
print("Looking for tests in %s" % SUITE_ROOT_DIR)
83-
print("Looking for remotes in %s" % REMOTES_DIR)
74+
print(f"Looking for tests in {SUITE_ROOT_DIR}")
75+
print(f"Looking for remotes in {REMOTES_DIR}")
76+
8477
cls.test_files = list(collect(SUITE_ROOT_DIR))
85-
cls.remote_files = list(collect(REMOTES_DIR))
86-
print("Found %s test files" % len(cls.test_files))
87-
print("Found %s remote files" % len(cls.remote_files))
8878
assert cls.test_files, "Didn't find the test files!"
79+
print(f"Found {len(cls.test_files)} test files")
80+
81+
cls.remote_files = list(collect(REMOTES_DIR))
8982
assert cls.remote_files, "Didn't find the remote files!"
83+
print(f"Found {len(cls.remote_files)} remote files")
9084

9185
def test_all_test_files_are_valid_json(self):
86+
"""
87+
All test files contain valid JSON.
88+
"""
9289
for path in self.test_files:
93-
with open(path) as test_file:
94-
try:
95-
json.load(test_file)
96-
except ValueError as error:
97-
self.fail("%s contains invalid JSON (%s)" % (path, error))
90+
try:
91+
json.loads(path.read_text())
92+
except ValueError as error:
93+
self.fail(f"{path} contains invalid JSON ({error})")
9894

9995
def test_all_remote_files_are_valid_json(self):
96+
"""
97+
All remote files contain valid JSON.
98+
"""
10099
for path in self.remote_files:
101-
with open(path) as remote_file:
102-
try:
103-
json.load(remote_file)
104-
except ValueError as error:
105-
self.fail("%s contains invalid JSON (%s)" % (path, error))
100+
try:
101+
json.loads(path.read_text())
102+
except ValueError as error:
103+
self.fail(f"{path} contains invalid JSON ({error})")
106104

107105
def test_all_descriptions_have_reasonable_length(self):
108-
for case in cases(self.test_files):
109-
description = case["description"]
106+
"""
107+
All tests have reasonably long descriptions.
108+
"""
109+
for count, test in enumerate(tests(self.test_files)):
110+
description = test["description"]
110111
self.assertLess(
111112
len(description),
112113
70,
113-
"%r is too long! (keep it to less than 70 chars)" % (
114-
description,
115-
),
114+
f"{description!r} is too long! (keep it to less than 70 chars)"
116115
)
116+
print(f"Found {count} tests.")
117117

118118
def test_all_descriptions_are_unique(self):
119-
for group in groups(self.test_files):
120-
descriptions = set(test["description"] for test in group["tests"])
119+
"""
120+
All test cases have unique test descriptions in their tests.
121+
"""
122+
for count, case in enumerate(cases(self.test_files)):
123+
descriptions = set(test["description"] for test in case["tests"])
121124
self.assertEqual(
122125
len(descriptions),
123-
len(group["tests"]),
124-
"%r contains a duplicate description" % (group,)
126+
len(case["tests"]),
127+
f"{case!r} contains a duplicate description",
125128
)
129+
print(f"Found {count} test cases.")
126130

127131
@unittest.skipIf(jsonschema is None, "Validation library not present!")
128132
def test_all_schemas_are_valid(self):
129-
for version in os.listdir(SUITE_ROOT_DIR):
130-
Validator = VALIDATORS.get(version)
133+
"""
134+
All schemas are valid under their metaschemas.
135+
"""
136+
for version in SUITE_ROOT_DIR.iterdir():
137+
if not version.is_dir():
138+
continue
139+
140+
Validator = VALIDATORS.get(version.name)
131141
if Validator is not None:
132-
test_files = collect(os.path.join(SUITE_ROOT_DIR, version))
142+
test_files = collect(version)
133143
for case in cases(test_files):
134144
try:
135145
Validator.check_schema(case["schema"])
136146
except jsonschema.SchemaError as error:
137-
self.fail("%s contains an invalid schema (%s)" %
138-
(case, error))
147+
self.fail(
148+
f"{case} contains an invalid schema ({error})",
149+
)
139150
else:
140-
warnings.warn("No schema validator for %s" % schema)
151+
warnings.warn(f"No schema validator for {version.name}")
141152

142153
@unittest.skipIf(jsonschema is None, "Validation library not present!")
143154
def test_suites_are_valid(self):
155+
"""
156+
All test files are valid under test-schema.json.
157+
"""
144158
Validator = jsonschema.validators.validator_for(TESTSUITE_SCHEMA)
145159
validator = Validator(TESTSUITE_SCHEMA)
146160
for tests in files(self.test_files):
@@ -153,7 +167,7 @@ class SanityTests(unittest.TestCase):
153167
def main(arguments):
154168
if arguments.command == "check":
155169
suite = unittest.TestLoader().loadTestsFromTestCase(SanityTests)
156-
result = unittest.TextTestRunner(verbosity=2).run(suite)
170+
result = unittest.TextTestRunner().run(suite)
157171
sys.exit(not result.wasSuccessful())
158172
elif arguments.command == "flatten":
159173
selected_cases = [case for case in cases(collect(arguments.version))]
@@ -166,20 +180,17 @@ def main(arguments):
166180
remotes = {}
167181
for path in collect(REMOTES_DIR):
168182
relative_path = os.path.relpath(path, REMOTES_DIR)
169-
with open(path) as schema_file:
170-
remotes[relative_path] = json.load(schema_file)
183+
remotes[relative_path] = json.loads(path.read_text())
171184
json.dump(remotes, sys.stdout, indent=4, sort_keys=True)
172185
elif arguments.command == "dump_remotes":
173186
if arguments.update:
174187
shutil.rmtree(arguments.out_dir, ignore_errors=True)
175188

176189
try:
177190
shutil.copytree(REMOTES_DIR, arguments.out_dir)
178-
except OSError as e:
179-
if e.errno == errno.EEXIST:
180-
print("%s already exists. Aborting." % arguments.out_dir)
181-
sys.exit(1)
182-
raise
191+
except FileExistsError:
192+
print(f"{arguments.out_dir} already exists. Aborting.")
193+
sys.exit(1)
183194
elif arguments.command == "serve":
184195
try:
185196
import flask

package.json

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"name": "json-schema-test-suite",
3+
"version": "0.1.0",
4+
"description": "A language agnostic test suite for the JSON Schema specifications",
5+
"repository": "github:json-schema-org/JSON-Schema-Test-Suite",
6+
"keywords": [
7+
"json-schema",
8+
"tests"
9+
],
10+
"author": "http://json-schema.org",
11+
"license": "MIT"
12+
}

tests/draft-next/additionalProperties.json

+15
Original file line numberDiff line numberDiff line change
@@ -129,5 +129,20 @@
129129
"valid": false
130130
}
131131
]
132+
},
133+
{
134+
"description": "additionalProperties should properly handle null data",
135+
"schema": {
136+
"additionalProperties": {
137+
"type": "null"
138+
}
139+
},
140+
"tests": [
141+
{
142+
"description": "null properties allowed",
143+
"data": {"foo": null},
144+
"valid": true
145+
}
146+
]
132147
}
133148
]

tests/draft-next/contains.json

+15
Original file line numberDiff line numberDiff line change
@@ -237,5 +237,20 @@
237237
"valid": false
238238
}
239239
]
240+
},
241+
{
242+
"description": "contains should properly handle null data",
243+
"schema": {
244+
"contains": {
245+
"type": "null"
246+
}
247+
},
248+
"tests": [
249+
{
250+
"description": "null items allowed",
251+
"data": [ null ],
252+
"valid": true
253+
}
254+
]
240255
}
241256
]

tests/draft-next/items.json

+15
Original file line numberDiff line numberDiff line change
@@ -252,5 +252,20 @@
252252
"valid": false
253253
}
254254
]
255+
},
256+
{
257+
"description": "items should properly handle null data",
258+
"schema": {
259+
"items": {
260+
"type": "null"
261+
}
262+
},
263+
"tests": [
264+
{
265+
"description": "null items allowed",
266+
"data": [ null ],
267+
"valid": true
268+
}
269+
]
255270
}
256271
]

tests/draft-next/patternProperties.json

+15
Original file line numberDiff line numberDiff line change
@@ -152,5 +152,20 @@
152152
"valid": true
153153
}
154154
]
155+
},
156+
{
157+
"description": "patternProperties should properly handle null data",
158+
"schema": {
159+
"patternProperties": {
160+
"^.*bar$": {"type": "null"}
161+
}
162+
},
163+
"tests": [
164+
{
165+
"description": "null properties allowed",
166+
"data": {"foobar": null},
167+
"valid": true
168+
}
169+
]
155170
}
156171
]

tests/draft-next/prefixItems.json

+17
Original file line numberDiff line numberDiff line change
@@ -77,5 +77,22 @@
7777
"valid": true
7878
}
7979
]
80+
},
81+
{
82+
"description": "prefixItems should properly handle null data",
83+
"schema": {
84+
"prefixItems": [
85+
{
86+
"type": "null"
87+
}
88+
]
89+
},
90+
"tests": [
91+
{
92+
"description": "null items allowed",
93+
"data": [ null ],
94+
"valid": true
95+
}
96+
]
8097
}
8198
]

tests/draft-next/properties.json

+15
Original file line numberDiff line numberDiff line change
@@ -163,5 +163,20 @@
163163
"valid": false
164164
}
165165
]
166+
},
167+
{
168+
"description": "properties should properly handle null data",
169+
"schema": {
170+
"properties": {
171+
"foo": {"type": "null"}
172+
}
173+
},
174+
"tests": [
175+
{
176+
"description": "null properties allowed",
177+
"data": {"foo": null},
178+
"valid": true
179+
}
180+
]
166181
}
167182
]

0 commit comments

Comments
 (0)