tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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