Skip to content

Commit 2004dc2

Browse files
authored
Add tests for the year bundle files (#22)
1 parent 106ac22 commit 2004dc2

File tree

3 files changed

+181
-3
lines changed

3 files changed

+181
-3
lines changed

BUILD.bazel

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
load("@rules_python//python:defs.bzl", "py_binary")
22
load("@rules_python//python:pip.bzl", "compile_pip_requirements")
33
load("@vendor-json-repo-pip//:requirements.bzl", "requirement")
4-
load("//:test_utils.bzl", "vendordep_check_test")
4+
load("//:test_utils.bzl", "vendordep_check_test", "year_bundle_test")
55

66
# bazel run //:requirements.update
77
compile_pip_requirements(
@@ -21,19 +21,29 @@ py_binary(
2121
],
2222
)
2323

24+
py_binary(
25+
name = "check_year_bundle",
26+
srcs = ["check_year_bundle.py"],
27+
visibility = ["//visibility:public"],
28+
)
29+
2430
# Change this for local testing only.
2531
cache_directory = None
2632

33+
YEAR_2024_FILES = glob(["2024/*.json"])
2734
[vendordep_check_test(
2835
allowable_errors = 1,
2936
allowable_warnings = None,
3037
cache_directory = cache_directory,
3138
vendor_file = f,
32-
) for f in glob(["2024/*.json"])]
39+
) for f in YEAR_2024_FILES]
40+
# year_bundle_test(year="2024", year_files=YEAR_2024_FILES) # Disable the test because there a several invalid entries
3341

42+
YEAR_2025_FILES = glob(["2025/*.json"])
3443
[vendordep_check_test(
3544
allowable_errors = 0,
3645
allowable_warnings = None,
3746
cache_directory = cache_directory,
3847
vendor_file = f,
39-
) for f in glob(["2025/*.json"])]
48+
) for f in YEAR_2025_FILES]
49+
year_bundle_test(year="2025", year_files=YEAR_2025_FILES)

check_year_bundle.py

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
#!/usr/bin/env python3
2+
3+
import os
4+
import sys
5+
import argparse
6+
import pathlib
7+
import json
8+
from dataclasses import dataclass
9+
import uuid
10+
11+
12+
@dataclass
13+
class Results:
14+
uuid_errors: int = 0
15+
bad_file_errors: int = 0
16+
duplicate_version_errors: int = 0
17+
inconsistent_version_errors: int = 0
18+
missing_description_errors: int = 0
19+
missing_website_errors: int = 0
20+
uncovered_file_errors: int = 0
21+
22+
def is_valid(self):
23+
valid = True
24+
25+
valid = valid and self.uuid_errors == 0
26+
valid = valid and self.bad_file_errors == 0
27+
valid = valid and self.duplicate_version_errors == 0
28+
valid = valid and self.inconsistent_version_errors == 0
29+
valid = valid and self.missing_description_errors == 0
30+
valid = valid and self.missing_website_errors == 0
31+
valid = valid and self.uncovered_file_errors == 0
32+
33+
return valid
34+
35+
36+
def check_year_bundle(year: int) -> Results:
37+
bundled_json_file = pathlib.Path(f"{year}.json")
38+
json_data = json.loads(bundled_json_file.read_text())
39+
40+
vendordep_versions = {} # Name -> set[version]
41+
vendordep_uuid = {} # Name -> UUID
42+
43+
results = Results()
44+
covered_files = set()
45+
46+
for dep in json_data:
47+
if dep["name"] not in vendordep_versions:
48+
vendordep_versions[dep["name"]] = set()
49+
50+
resolved_path = dep["path"]
51+
file_json_data = None
52+
53+
# Check file existence
54+
if not os.path.exists(resolved_path):
55+
print(f"{dep['path']} - Could not find file")
56+
results.bad_file_errors += 1
57+
else:
58+
file_json_data = json.load(open(resolved_path))
59+
covered_files.add(resolved_path)
60+
61+
# Check description
62+
if "description" not in dep:
63+
print(f"{dep['path']} - Missing description")
64+
results.missing_description_errors += 1
65+
66+
# Check website
67+
if "website" not in dep:
68+
print(f"{dep['path']} - Missing documentation website")
69+
results.missing_website_errors += 1
70+
71+
# Check version
72+
if dep["version"] in vendordep_versions[dep["name"]]:
73+
print(f"{dep['path']} - Duplicated version {dep['version']}")
74+
results.duplicate_version_errors += 1
75+
vendordep_versions[dep["name"]].add(dep["version"])
76+
77+
if file_json_data is not None and file_json_data["version"] != dep["version"]:
78+
print(f"{dep['path']} - Version {dep['version']} does not match the version in the file {file_json_data['version']}")
79+
results.inconsistent_version_errors +=1
80+
81+
# Check UUID
82+
if dep["name"] not in vendordep_uuid:
83+
vendordep_uuid[dep["name"]] = dep["uuid"]
84+
85+
if vendordep_uuid[dep["name"]] != dep["uuid"]:
86+
print(f"{dep['path']} - UUID {dep['uuid']} has has changed from previously seen UUID {vendordep_uuid[dep['name']]}")
87+
results.uuid_errors += 1
88+
89+
if file_json_data is not None and file_json_data["uuid"] != dep["uuid"]:
90+
print(f"{dep['path']} - UUID {dep['uuid']} does not match the UUID in the file {file_json_data['uuid']}")
91+
results.uuid_errors += 1
92+
93+
try:
94+
uuid.UUID(dep["uuid"])
95+
except:
96+
print(f"{dep['path']} - UUID {dep['uuid']} is invalid")
97+
results.uuid_errors += 1
98+
99+
# Look for uncovered files
100+
all_files = set([os.path.join(str(year), x) for x in os.listdir(str(year))])
101+
uncovered_files = all_files.difference(covered_files)
102+
for f in uncovered_files:
103+
print(f"File {f} is not represented in the year bundle")
104+
results.uncovered_file_errors += 1
105+
106+
# Check that UUID's are unique across vendordeps
107+
if len(vendordep_uuid) != len(set(vendordep_uuid.values())):
108+
print(f"There are a different number of vendordeps ({len(vendordep_uuid)}) than there are UUIDs ({len(set(vendordep_uuid.values()))}), indicating UUID's have been reused between vendordeps")
109+
results.uuid_errors += 1
110+
111+
# Print known versions
112+
print("Known versions:")
113+
for k in vendordep_versions:
114+
print(f" {k} - {vendordep_versions[k]}")
115+
116+
return results
117+
118+
119+
120+
def main():
121+
parser = argparse.ArgumentParser(description='Checks a vendor json file')
122+
parser.add_argument('--year', '-y', required=True, help='FRC competition season year')
123+
args = parser.parse_args(sys.argv[1:])
124+
125+
results = check_year_bundle(args.year)
126+
127+
print(results)
128+
sys.exit(results.is_valid(args.disable_uuid_check))
129+
130+
131+
132+
if __name__ == "__main__":
133+
main()
134+

test_utils.bzl

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,3 +70,37 @@ if __name__ == "__main__":
7070
deps = ["//:check"],
7171
data = [vendor_file],
7272
)
73+
74+
def year_bundle_test(year, year_files):
75+
gen_name = year + ".gen"
76+
test_file_base = year + "_test"
77+
test_file_name = test_file_base + ".py"
78+
79+
test_contents = """
80+
81+
import unittest
82+
import pathlib
83+
from check_year_bundle import check_year_bundle
84+
85+
class VendordepCheck(unittest.TestCase):
86+
def test_check(self):
87+
results = check_year_bundle(pathlib.Path("{year}"))
88+
print(results)
89+
self.assertTrue(results.is_valid())
90+
91+
if __name__ == "__main__":
92+
unittest.main() # run all tests
93+
94+
""".format(year=year)
95+
96+
native.genrule(
97+
name = gen_name,
98+
outs = [test_file_name],
99+
cmd = "echo '{}' >> $@".format(test_contents),
100+
)
101+
py_test(
102+
name = test_file_base,
103+
srcs = [test_file_name],
104+
deps = ["//:check_year_bundle"],
105+
data = [str(year) + ".json"] + year_files,
106+
)

0 commit comments

Comments
 (0)