metadata.py (5237B)
1 import argparse 2 import logging 3 import os 4 import re 5 from typing import Any, Dict, List, Optional, Mapping, Sequence, Set, Union 6 7 import pydantic 8 import yaml 9 from pydantic import BaseModel 10 11 12 from ..manifest import manifest 13 from .virtualenv import Virtualenv 14 15 here = os.path.dirname(__file__) 16 wpt_root = os.path.abspath(os.path.join(here, os.pardir, os.pardir)) 17 18 class UrlResultsModel(BaseModel): 19 model_config = pydantic.ConfigDict(extra='forbid') 20 21 test: str 22 subtest: Optional[str] = None 23 status: Optional[str] = None 24 25 26 class UrlLinkModel(BaseModel): 27 model_config = pydantic.ConfigDict(extra='forbid') 28 29 url: str 30 results: List[UrlResultsModel] 31 product: Optional[str] = None 32 33 34 class LabelResultsModel(BaseModel): 35 test: str 36 37 38 class LabelLinkModel(BaseModel): 39 label: str 40 results: List[LabelResultsModel] 41 42 43 class MetadataModel(BaseModel): 44 links: List[Union[LabelLinkModel, UrlLinkModel]] 45 46 47 def load_metadata_map(src_dir: str) -> Mapping[str, MetadataModel]: 48 rv = {} 49 for dir_path, dir_names, file_names in os.walk(src_dir): 50 if "META.yml" not in file_names: 51 continue 52 53 id_prefix = os.path.relpath(dir_path, src_dir).replace(os.path.sep, "/") + "/" 54 if id_prefix[0] != "/": 55 id_prefix = "/" + id_prefix 56 meta_path = os.path.join(dir_path, "META.yml") 57 58 with open(meta_path) as f: 59 data = yaml.safe_load(f) 60 try: 61 rv[id_prefix] = MetadataModel.model_validate(data) 62 except Exception as e: 63 logging.critical(f"Error validating metadata {meta_path}") 64 raise e 65 return rv 66 67 68 def get_all_tests(metadata_map: Mapping[str, MetadataModel]) -> Set[str]: 69 rv = set() 70 71 for id_prefix, metadata in metadata_map.items(): 72 for link in metadata.links: 73 for result in link.results: 74 if result.test != "*": 75 test_id = id_prefix + result.test 76 rv.add(test_id) 77 return rv 78 79 80 def get_labelled_tests( 81 metadata_map: Mapping[str, MetadataModel], 82 label_patterns: Sequence[Union[str, re.Pattern[Any]]] 83 ) -> Mapping[str, Set[str]]: 84 rv: Dict[str, Set[str]] = {} 85 for id_prefix, metadata in metadata_map.items(): 86 for link in metadata.links: 87 if isinstance(link, LabelLinkModel): 88 for label in label_patterns: 89 if (isinstance(label, str) and link.label == label or 90 isinstance(label, re.Pattern) and label.match(link.label) is not None): 91 if link.label not in rv: 92 rv[link.label] = set() 93 94 for result in link.results: 95 rv[link.label].add(id_prefix + result.test) 96 break 97 return rv 98 99 100 def get_parser_validate() -> argparse.ArgumentParser: 101 parser = argparse.ArgumentParser() 102 parser.add_argument("--manifest", dest="manifest_path", help="Path to MANIFEST.json") 103 parser.add_argument("metadata_path", help="Path to wpt metadata repository") 104 return parser 105 106 107 def run_validate(venv: Virtualenv, metadata_path: str, manifest_path: Optional[str] = None) -> int: 108 if manifest_path is None: 109 manifest_path = os.path.join(wpt_root, "MANIFEST.json") 110 wpt_manifest = manifest.load_and_update(wpt_root, manifest_path, "/") 111 metadata_map = load_metadata_map(metadata_path) 112 113 metadata_tests = get_all_tests(metadata_map) 114 for _type, _rel_path, tests in wpt_manifest: 115 for test in tests: 116 metadata_tests.discard(test.id) 117 118 if metadata_tests: 119 tests_str = "\n".join(metadata_map) 120 logging.error(f"The following tests were in metadata but not in the manifest:\n{tests_str}") 121 return 1 122 123 return 0 124 125 126 def get_parser_list() -> argparse.ArgumentParser: 127 parser = argparse.ArgumentParser() 128 parser.add_argument("--manifest", dest="manifest_path", help="Path to MANIFEST.json") 129 parser.add_argument("metadata_path", help="Path to wpt metadata repository") 130 parser.add_argument("labels", nargs="+", help="Regexp representing test labels to list") 131 return parser 132 133 134 def run_list(venv: Virtualenv, metadata_path: str, labels: List[str], manifest_path: Optional[str] = None) -> int: 135 if manifest_path is None: 136 manifest_path = os.path.join(wpt_root, "MANIFEST.json") 137 wpt_manifest = manifest.load_and_update(wpt_root, manifest_path, "/") 138 139 metadata_map = load_metadata_map(metadata_path) 140 label_patterns = [re.compile(item) for item in labels] 141 tests_by_label = get_labelled_tests(metadata_map, label_patterns) 142 all_labelled_tests = set() 143 tests_by_id = {} 144 for labelled_tests in tests_by_label.values(): 145 all_labelled_tests |= set(labelled_tests) 146 147 for test_type, _rel_path, tests in wpt_manifest: 148 for test in tests: 149 if test.id in all_labelled_tests: 150 tests_by_id[test.id] = (test_type, test) 151 152 153 for label in sorted(tests_by_label): 154 labelled_tests = tests_by_label[label] 155 print(f"{label}\t{len(labelled_tests)}") 156 for test in sorted(tests, key=lambda x:x.id): 157 print(f" {test}\t{tests_by_id[test.id][0]}") 158 return 0