diff --git a/.gitignore b/.gitignore index a695581..9d5507a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /target /Cargo.lock specification.json +rs-report.json diff --git a/.specrc b/.specrc new file mode 100644 index 0000000..fe66bad --- /dev/null +++ b/.specrc @@ -0,0 +1,5 @@ +[spec] +file_extension=rs +multiline_regex=#\[spec\((?P.*?)\)\] +number_subregex=number\s*=\s*"(.*?)" +text_subregex=text\s*=\s*["'](.*)["'] \ No newline at end of file diff --git a/spec_finder.py b/spec_finder.py deleted file mode 100755 index 65148f7..0000000 --- a/spec_finder.py +++ /dev/null @@ -1,111 +0,0 @@ -#!/usr/bin/env python -import urllib.request -import json -import re -import difflib -import os -import sys - -def _demarkdown(t): - return t.replace('**', '').replace('`', '').replace('"', '') - -def get_spec(force_refresh=False): - spec_path = './specification.json' - data = "" - if os.path.exists(spec_path) and not force_refresh: - with open(spec_path) as f: - data = ''.join(f.readlines()) - else: - # TODO: Status code check - spec_response = urllib.request.urlopen('https://raw.githubusercontent.com/open-feature/spec/main/specification.json') - raw = [] - for i in spec_response.readlines(): - raw.append(i.decode('utf-8')) - data = ''.join(raw) - with open(spec_path, 'w') as f: - f.write(data) - return json.loads(data) - - -def main(refresh_spec=False, diff_output=False, limit_numbers=None): - actual_spec = get_spec(refresh_spec) - - spec_map = {} - for entry in actual_spec['rules']: - number = re.search('[\d.]+', entry['id']).group() - if 'requirement' in entry['machine_id']: - spec_map[number] = _demarkdown(entry['content']) - - if len(entry['children']) > 0: - for ch in entry['children']: - number = re.search('[\d.]+', ch['id']).group() - if 'requirement' in ch['machine_id']: - spec_map[number] = _demarkdown(ch['content']) - - repo_specs = {} - missing = set(spec_map.keys()) - - for root, dirs, files in os.walk(".", topdown=False): - for name in files: - F = os.path.join(root, name) - if '.rs' not in name: - continue - with open(F) as f: - data = ''.join(f.readlines()) - - # if "#[spec" in data: - # import pdb; pdb.set_trace() - for match in re.findall('#\[spec\((?P.*?)\)\]', data.replace('\n', ''), re.MULTILINE | re.DOTALL): - number = re.findall('number\s*=\s*"(.*?)"', match)[0] - - - if number in missing: - missing.remove(number) - text_with_concat_chars = re.findall('text\s*=\s*(.*)', match) - try: - # We have to match for ") to capture text with parens inside, so we add the trailing " back in. - text = _demarkdown(eval(''.join(text_with_concat_chars) + '"')) - entry = repo_specs[number] = { - 'number': number, - 'text': text, - } - except: - print(f"Skipping {match} b/c we couldn't parse it") - - bad_num = len(missing) - for number, entry in sorted(repo_specs.items(), key=lambda x: x[0]): - if limit_numbers is not None and len(limit_numbers) > 0 and number not in limit_numbers: - continue - if number in spec_map: - txt = entry['text'] - if txt == spec_map[number]: - continue - else: - print(f"{number} is bad") - bad_num += 1 - if diff_output: - print(number + '\n' + '\n'.join([li for li in difflib.ndiff([txt], [spec_map[number]]) if not li.startswith(' ')])) - continue - - print(f"{number} is defined in our tests, but couldn't find it in the spec") - print("") - - if len(missing) > 0: - print('In the spec, but not in our tests: ') - for m in sorted(missing): - print(f" {m}: {spec_map[m]}") - - sys.exit(bad_num) - - -if __name__ == '__main__': - import argparse - - parser = argparse.ArgumentParser(description='Parse the spec to make sure our tests cover it') - parser.add_argument('--refresh-spec', action='store_true', help='Re-downloads the spec') - parser.add_argument('--diff-output', action='store_true', help='print the text differences') - parser.add_argument('specific_numbers', metavar='num', type=str, nargs='*', - help='limit this to specific numbers') - - args = parser.parse_args() - main(refresh_spec=args.refresh_spec, diff_output=args.diff_output, limit_numbers=args.specific_numbers)