Coverage for /pythoncovmergedfiles/medio/medio/src/turbohtml/tools/fuzz/css_structure_generators.py: 67%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1"""Charge complete CSS syntax trees so lexical helpers do not consume fictitious nodes."""
3from __future__ import annotations
5import argparse
6import random
7import re
8from pathlib import Path
9from typing import TYPE_CHECKING, Final, TypeAlias
11from fuzz.css_generation_tables import CSS_ROWS, SOURCE_URLS, CssRow
12from fuzz.structure_generators import (
13 BudgetError,
14 Generated,
15 GenerationBudget,
16 Grammar,
17 GrammarError,
18 Identifier,
19 Production,
20 ProductionFloorError,
21 Reference,
22 compile_grammar,
23 generate,
24 generation_sweep,
25 write_corpus,
26)
28if TYPE_CHECKING:
29 from collections.abc import Sequence
32def main(argv: Sequence[str] | None = None) -> int:
33 """Preserve source bytes for the coverage-guided corpus."""
34 parser: Final = argparse.ArgumentParser()
35 parser.add_argument("--output", type=Path, required=True)
36 parser.add_argument("--budget", type=int, default=512)
37 parser.add_argument("--steps", type=int, default=128)
38 parser.add_argument("--count", type=int, default=0)
39 parser.add_argument("--seed", type=int, default=0)
40 args: Final = parser.parse_args(argv)
41 if args.count < 0:
42 parser.error("count must be nonnegative")
43 try:
44 cases: Final = (
45 tuple(
46 css_generate(random.Random(args.seed + index), args.budget, steps=args.steps)
47 for index in range(args.count)
48 )
49 if args.count
50 else generation_sweep(_GRAMMAR, budget=GenerationBudget(args.budget, args.steps))
51 )
52 except (BudgetError, GrammarError, ProductionFloorError) as error:
53 parser.error(str(error))
54 write_corpus(cases, args.output)
55 return 0
58def css_generate(rng: random.Random, budget: int = 96, *, steps: int = 64) -> Generated:
59 """Reserve node and expansion budgets across nested at-rules and sibling sheets."""
60 return generate(_GRAMMAR, rng, GenerationBudget(budget, steps))
63def css_grammar(rows: Sequence[CssRow] = CSS_ROWS) -> Grammar:
64 """Retain source alternatives as distinct productions for forced coverage."""
65 return compile_grammar(
66 (
67 Production("css:sheet", "root", (*_PREFIX, Reference("body")), 30, _STANDARD, 7),
68 Production("css:leaf", "body", (b"a{color:red}",), 8, _STANDARD, 4),
69 Production("css:siblings", "body", (Reference("body", 0), Reference("body", 0)), 0, _STANDARD),
70 *(
71 Production(
72 f"css:nested:{name}",
73 "body",
74 (prefix, Reference("body", 2), b"}"),
75 nodes,
76 _STANDARD,
77 height,
78 )
79 for name, prefix, nodes, height in (
80 ("media", b"@media all{", 5, 3),
81 ("supports", b"@supports(display:grid){", 8, 6),
82 ("container", b"@container(width>1px){", 7, 4),
83 ("layer", b"@layer x{", 5, 3),
84 )
85 ),
86 *(_production(row) for row in rows),
87 ),
88 "root",
89 identifiers=("x", "y", "z", "u", "v"),
90 )
93def _production(row: CssRow) -> Production:
94 origin: Final = SOURCE_URLS[row[0]] + f"#L{row[1]}"
95 name: Final = f"css:{'blink' if row[0] == 'blink-css.dict' else 'domato:' + row[0]}:{row[1]}"
96 parts: Final = _references(row[4])
97 if row[9] or row[10]:
98 return Production(
99 name,
100 "root",
101 (*parts, *_PREFIX) if row[10] else (*_PREFIX, *parts),
102 row[5],
103 origin,
104 row[6],
105 )
106 return Production(name, "body", parts, row[7] - 1, origin, row[8] - 1)
109def _references(source: str) -> Parts:
110 parts: Final[list[bytes | Identifier]] = []
111 position = 0
112 for token in _BINDING.finditer(source):
113 parts.append(source[position : token.start()].encode("utf-8"))
114 if token[0].startswith("--"):
115 parts.append(b"--")
116 parts.append(Identifier(token[0].removeprefix("--"), definition=False))
117 position = token.end()
118 parts.append(source[position:].encode("utf-8"))
119 return tuple(parts)
122def css_source_generate(rng: random.Random) -> str:
123 """Use whole sheets so at-rules reach minification."""
124 return css_generate(rng).data.decode("utf-8")
127def css_source_seeds() -> list[str]:
128 """Force pinned alternatives through their shortest emitting root paths."""
129 return [case.data.decode("utf-8") for case in generation_sweep(_GRAMMAR, budget=GenerationBudget(512, 128))]
132Parts: TypeAlias = tuple[bytes | Identifier, ...]
133_STANDARD: Final = "https://www.w3.org/TR/2021/CRD-css-syntax-3-20211224/"
134_BINDING: Final = re.compile(r"(?<![a-zA-Z0-9_-])(?:--cssvar[a-d]|anim)(?![a-zA-Z0-9_-])")
135_PREFIX: Final[Parts] = (
136 b":root{",
137 *(
138 part
139 for name in ("cssvara", "cssvarb", "cssvarc", "cssvard")
140 for part in (b"--", Identifier(name, definition=True), b":red;")
141 ),
142 b"}@keyframes ",
143 Identifier("anim", definition=True),
144 b"{from{opacity:0}}",
145)
146_GRAMMAR: Final = css_grammar()
148__all__ = ["css_generate", "css_grammar", "css_source_generate", "css_source_seeds", "main"]
150if __name__ == "__main__":
151 raise SystemExit(main())