1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206
|
import argparse
import json
import logging
import os
from dataclasses import dataclass
from pathlib import Path
from typing import Any, List, Optional
from ..manifest.item import SupportFile
from ..manifest.sourcefile import SourceFile
from ..metadata.yaml.load import load_data_to_dict
from ..web_features.web_feature_map import WebFeatureToTestsDirMapper, WebFeaturesMap
from .. import localpaths
from ..metadata.webfeatures.schema import WEB_FEATURES_YML_FILENAME, WebFeaturesFile
"""
This command generates a manifest file containing a mapping of web-feature
identifiers to test paths.
The web-feature identifiers are sourced from https://github.com/web-platform-dx/web-features.
They are used in WEB_FEATURES.yml files located throughout the WPT source code.
Each file defines which test files correspond to a specific identifier.
Refer to RFC 163 (https://github.com/web-platform-tests/rfcs/pull/163) for more
file details.
This command processes all WEB_FEATURES.yml files, extracts the list of test
paths from the test files, and writes them to a manifest file. The manifest
file maps web-feature identifiers to their corresponding test paths.
The file written is a JSON file. An example file looks like:
{
"version": 1,
"data": {
"async-clipboard": [
"/clipboard-apis/async-custom-formats-write-fail.tentative.https.html",
"/clipboard-apis/async-custom-formats-write-read-web-prefix.tentative.https.html"
],
"idle-detection": [
"/idle-detection/basics.tentative.https.window.html",
"/idle-detection/idle-detection-allowed-by-permissions-policy-attribute-redirect-on-load.https.sub.html"
]
}
}
The JSON Schema for the file format can be found at MANIFEST_SCHEMA.json
This file does not follow the same format as the original manifest file,
MANIFEST.json.
"""
logger = logging.getLogger(__name__)
MANIFEST_FILE_NAME = "WEB_FEATURES_MANIFEST.json"
def abs_path(path: str) -> str:
return os.path.abspath(os.path.expanduser(path))
def create_parser() -> argparse.ArgumentParser:
"""
Creates an argument parser for the script.
Returns:
argparse.ArgumentParser: The configured argument parser.
"""
parser = argparse.ArgumentParser(
description="Maps tests to web-features within a repo root."
)
parser.add_argument(
"-p", "--path", type=abs_path, help="Path to manifest file.")
return parser
def find_all_test_files_in_dir(root_dir: str, rel_dir_path: str, url_base: str) -> List[SourceFile]:
"""
Finds all test files within a given directory.
Ignores any SourceFiles that are marked as non_test or the type
is SupportFile.item_type
Args:
root_dir (str): The root directory of the repository.
rel_dir_path (str): The relative path of the directory to search.
url_base (str): Base url to use as the mount point for tests in this manifest.
Returns:
List[SourceFile]: A list of SourceFile objects representing the found test files.
"""
rv: List[SourceFile] = []
full_dir_path = os.path.join(root_dir, rel_dir_path)
for file in os.listdir(full_dir_path):
full_path = os.path.join(full_dir_path, file)
rel_file_path = os.path.relpath(full_path, root_dir)
source_file = SourceFile(root_dir, rel_file_path, url_base)
if not source_file.name_is_non_test and source_file.type != SupportFile.item_type:
rv.append(source_file)
return rv
@dataclass
class CmdConfig():
"""
Configuration for the command-line options.
"""
repo_root: str # The root directory of the WPT repository
url_base: str # Base URL used when converting file paths to urls
def map_tests_to_web_features(
cmd_cfg: CmdConfig,
rel_dir_path: str,
result: WebFeaturesMap,
prev_inherited_features: List[str] = []) -> None:
"""
Recursively maps tests to web-features within a directory structure.
Args:
cmd_cfg (CmdConfig): The configuration for the command-line options.
rel_dir_path (str): The relative path of the directory to process.
result (WebFeaturesMap): The object to store the mapping results.
prev_inherited_features (List[str], optional): A list of inherited web-features from parent directories. Defaults to [].
"""
# Sometimes it will add a . at the beginning. Let's resolve the absolute path to disambiguate.
# current_path = Path(os.path.join(cmd_cfg.repo_root, rel_dir_path)).resolve()
current_dir = str(Path(os.path.join(cmd_cfg.repo_root, rel_dir_path)).resolve())
# Create a copy that may be built upon or cleared during this iteration.
inherited_features = prev_inherited_features.copy()
rel_dir_path = os.path.relpath(current_dir, cmd_cfg.repo_root)
web_feature_yml_full_path = os.path.join(current_dir, WEB_FEATURES_YML_FILENAME)
web_feature_file: Optional[WebFeaturesFile] = None
if os.path.isfile(web_feature_yml_full_path):
try:
web_feature_file = WebFeaturesFile(load_data_to_dict(
open(web_feature_yml_full_path, "rb")))
except Exception as e:
raise e
WebFeatureToTestsDirMapper(
find_all_test_files_in_dir(cmd_cfg.repo_root, rel_dir_path, cmd_cfg.url_base),
web_feature_file
).run(result, inherited_features)
sub_dirs = [f for f in os.listdir(current_dir) if os.path.isdir(os.path.join(current_dir, f))]
for sub_dir in sub_dirs:
map_tests_to_web_features(
cmd_cfg,
os.path.join(rel_dir_path, sub_dir),
result,
inherited_features
)
class WebFeatureManifestEncoder(json.JSONEncoder):
"""
Custom JSON encoder.
WebFeaturesMap contains a dictionary where the value is of type set.
Sets cannot serialize to JSON by default. This encoder handles that by
calling WebFeaturesMap's to_dict() method.
"""
def default(self, obj: Any) -> Any:
if isinstance(obj, WebFeaturesMap):
return obj.to_dict()
return super().default(obj)
def write_manifest_file(path: str, web_features_map: WebFeaturesMap) -> None:
"""
Serializes the WebFeaturesMap to a JSON manifest file at the specified path.
The generated JSON file adheres to the schema defined in the "MANIFEST_SCHEMA.json" file. The
serialization process uses the custom `WebFeatureManifestEncoder` to ensure correct formatting.
Args:
path (str): The file path where the manifest file will be created or overwritten.
web_features_map (WebFeaturesMap): The object containing the mapping between
web-features and their corresponding test paths.
"""
with open(path, "w") as outfile:
outfile.write(
json.dumps(
{
"version": 1,
"data": web_features_map
}, cls=WebFeatureManifestEncoder, sort_keys=True))
def main(venv: Any = None, **kwargs: Any) -> int:
assert logger is not None
repo_root = localpaths.repo_root
url_base = "/"
path = kwargs.get("path") or os.path.join(repo_root, MANIFEST_FILE_NAME)
cmd_cfg = CmdConfig(repo_root, url_base)
feature_map = WebFeaturesMap()
map_tests_to_web_features(cmd_cfg, "", feature_map)
write_manifest_file(path, feature_map)
return 0
|