tor-browser

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

web_feature_map.py (5358B)


      1 import itertools
      2 
      3 from collections import OrderedDict
      4 from os.path import basename
      5 from typing import Dict, List, Optional, Sequence, Set
      6 
      7 from ..manifest.item import ManifestItem, URLManifestItem
      8 from ..manifest.sourcefile import SourceFile
      9 from ..metadata.webfeatures.schema import FeatureEntry, FeatureFile, FileMatchingMode, WebFeaturesFile
     10 
     11 
     12 class WebFeaturesMap:
     13    """
     14    Stores a mapping of web-features to their associated test paths.
     15    """
     16 
     17    def __init__(self) -> None:
     18        """
     19        Initializes the WebFeaturesMap with an OrderedDict to maintain feature order.
     20        """
     21        self._feature_tests_map_: OrderedDict[str, Set[str]] = OrderedDict()
     22 
     23 
     24    def add(self, feature: str, manifest_items: List[ManifestItem]) -> None:
     25        """
     26        Adds a web feature and its associated test paths to the map.
     27 
     28        Args:
     29            feature: The web-features identifier.
     30            manifest_items: The ManifestItem objects representing the test paths.
     31        """
     32        tests = self._feature_tests_map_.get(feature, set())
     33        self._feature_tests_map_[feature] = tests.union([
     34            manifest_item.url for manifest_item in manifest_items if isinstance(manifest_item, URLManifestItem)])
     35 
     36 
     37    def to_dict(self) -> Dict[str, List[str]]:
     38        """
     39        Returns:
     40            The plain dictionary representation of the map.
     41        """
     42        rv: Dict[str, List[str]] = {}
     43        for feature, manifest_items in self._feature_tests_map_.items():
     44            # Sort the list to keep output stable
     45            rv[feature] = sorted(manifest_items)
     46        return rv
     47 
     48 
     49 class WebFeatureToTestsDirMapper:
     50    """
     51    Maps web-features to tests within a specified directory.
     52    """
     53 
     54    def __init__(
     55            self,
     56            all_test_files_in_dir: List[SourceFile],
     57            web_feature_file: Optional[WebFeaturesFile]):
     58        """
     59        Initializes the mapper with test paths and web feature information.
     60        """
     61 
     62        self.all_test_files_in_dir = all_test_files_in_dir
     63        self.test_path_to_manifest_items_map = dict([(basename(f.path), f.manifest_items()[1]) for f in self.all_test_files_in_dir])
     64        # Used to check if the current directory has a WEB_FEATURE_FILENAME
     65        self.web_feature_file = web_feature_file
     66        # Gets the manifest items for each test path and returns them into a single list.
     67        self. get_all_manifest_items_for_dir = list(itertools.chain.from_iterable([
     68            items for _, items in self.test_path_to_manifest_items_map.items()]))
     69 
     70 
     71    def _process_inherited_features(
     72            self,
     73            inherited_features: List[str],
     74            result: WebFeaturesMap) -> None:
     75        # No WEB_FEATURE.yml in this directory. Simply add the current features to the inherited features
     76        for inherited_feature in inherited_features:
     77            result.add(inherited_feature, self.get_all_manifest_items_for_dir)
     78 
     79    def _process_recursive_feature(
     80            self,
     81            inherited_features: List[str],
     82            feature: FeatureEntry,
     83            result: WebFeaturesMap) -> None:
     84        inherited_features.append(feature.name)
     85        result.add(feature.name, self.get_all_manifest_items_for_dir)
     86 
     87    def _process_non_recursive_feature(
     88            self,
     89            feature_name: str,
     90            files: Sequence[FeatureFile],
     91            result: WebFeaturesMap) -> None:
     92        # If the feature does not apply recursively, look at the individual
     93        # files and match them against all_test_files_in_dir.
     94        final_test_file_paths: List[ManifestItem] = []
     95        test_file_paths: Set[str] = set()
     96        excluded_test_file_paths: Set[str] = set()
     97        base_test_file_names = [basename(f.path) for f in self.all_test_files_in_dir]
     98        for test_file in files:
     99            matched_base_file_names = test_file.match_files(base_test_file_names)
    100            if test_file.matching_mode == FileMatchingMode.INCLUDE:
    101                test_file_paths.update(matched_base_file_names)
    102            elif test_file.matching_mode == FileMatchingMode.EXCLUDE:
    103                excluded_test_file_paths.update(matched_base_file_names)
    104        final_test_file_paths_set = test_file_paths - excluded_test_file_paths
    105        final_test_file_paths.extend(itertools.chain.from_iterable([
    106            self.test_path_to_manifest_items_map[f] for f in final_test_file_paths_set]))
    107 
    108        result.add(feature_name, final_test_file_paths)
    109 
    110    def run(self, result: WebFeaturesMap, inherited_features: List[str]) -> None:
    111        if self.web_feature_file:
    112            # Do not copy the inherited features because the presence of a
    113            # WEB_FEATURES.yml file indicates new instructions.
    114            inherited_features.clear()
    115 
    116            # Iterate over all the features in this new file
    117            for feature in self.web_feature_file.features:
    118                # Handle the "**" case
    119                if feature.does_feature_apply_recursively():
    120                    self._process_recursive_feature(inherited_features, feature, result)
    121 
    122                # Handle the non recursive case.
    123                elif isinstance(feature.files, List) and feature.files:
    124                    self._process_non_recursive_feature(feature.name, feature.files, result)
    125        else:
    126            self._process_inherited_features(inherited_features, result)