Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions tools/hrw4u/src/hrw_symbols.py
Original file line number Diff line number Diff line change
Expand Up @@ -183,8 +183,7 @@ def _handle_prefix_conditions(self, tag: str, payload: str, section: SectionType

if tag == "HEADER":
return f"{self.get_prefix_for_context('header_condition', section)}{suffix}", False
else:
return f"{lhs_prefix}{suffix.replace(':', '.')}", False
return f"{lhs_prefix}{suffix.replace(':', '.')}", False
return None

def _should_lowercase_suffix(self, tag_match: str, lhs_prefix: str) -> bool:
Expand Down Expand Up @@ -508,10 +507,15 @@ def op_to_hrw4u(self, cmd: str, args: list[str], section: SectionType | None, op
commands = params.target if params else None
if (isinstance(commands, (list, tuple)) and cmd in commands) or (cmd == commands):
uppercase = params.upper if params else False
if params:
qualifier = toks[1] if len(toks) > 1 else ""
name = f"{lhs_key}{qualifier}" if lhs_key.endswith(".") else lhs_key
self.validate_section_access(name, section, params.sections)
return self._handle_operator_command(cmd, toks, lhs_key, uppercase, section)

for name, params in tables.STATEMENT_FUNCTION_MAP.items():
if params.target == cmd:
self.validate_section_access(name, section, params.sections)
return self._handle_statement_function(name, args, section, op_state)

raise SymbolResolutionError(line, f"Unknown operator: {cmd}")
7 changes: 5 additions & 2 deletions tools/hrw4u/src/hrw_visitor.py
Original file line number Diff line number Diff line change
Expand Up @@ -371,8 +371,11 @@ def visitOpLine(self, ctx: u4wrhParser.OpLineContext) -> None:
args = self._reconstruct_redirect_args(args)
self.debug(f"reconstructed redirect: {args}")

stmt = self.symbol_resolver.op_to_hrw4u(cmd, args, self._section_label, op_state)
self.emit(stmt + ";")
stmt = None
with self.trap(ctx):
stmt = self.symbol_resolver.op_to_hrw4u(cmd, args, self._section_label, op_state)
if stmt is not None:
self.emit(stmt + ";")

return None

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
is not available in the TXN_CLOSE section
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
cond %{TXN_CLOSE_HOOK} [AND]
cond %{CLIENT-HEADER:@sampleratio} ="0.05"
add-header @TCPInfo "TC; %{TCP-INFO}"
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
is not available in the TXN_CLOSE section
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
cond %{TXN_CLOSE_HOOK} [AND]
set-destination HOST "foo"
9 changes: 9 additions & 0 deletions tools/hrw4u/tests/test_hooks_reverse.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,12 @@
def test_reverse_conversion(input_file: Path, output_file: Path) -> None:
"""Test that u4wrh reverse conversion produces original hrw4u for hooks test cases."""
utils.run_reverse_test(input_file, output_file)


@pytest.mark.hooks
@pytest.mark.reverse
@pytest.mark.invalid
@pytest.mark.parametrize("input_file", utils.collect_reverse_failing_inputs("hooks"))
def test_reverse_invalid_inputs_fail(input_file: Path) -> None:
"""Test that u4wrh surfaces section access errors for invalid HRW hook inputs."""
utils.run_reverse_failing_test(input_file)
9 changes: 9 additions & 0 deletions tools/hrw4u/tests/test_ops_reverse.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,12 @@
def test_reverse_conversion(input_file: Path, output_file: Path) -> None:
"""Test that u4wrh reverse conversion produces original hrw4u for ops test cases."""
utils.run_reverse_test(input_file, output_file)


@pytest.mark.ops
@pytest.mark.reverse
@pytest.mark.invalid
@pytest.mark.parametrize("input_file", utils.collect_reverse_failing_inputs("ops"))
def test_reverse_invalid_inputs_fail(input_file: Path) -> None:
"""Test that u4wrh surfaces section access errors for invalid HRW operator inputs."""
utils.run_reverse_failing_test(input_file)
42 changes: 42 additions & 0 deletions tools/hrw4u/tests/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,14 @@
"collect_output_test_files",
"collect_ast_test_files",
"collect_failing_inputs",
"collect_reverse_failing_inputs",
"collect_sandbox_deny_test_files",
"collect_sandbox_allow_test_files",
"collect_sandbox_warn_test_files",
"run_output_test",
"run_ast_test",
"run_failing_test",
"run_reverse_failing_test",
"run_sandbox_deny_test",
"run_sandbox_allow_test",
"run_sandbox_warn_test",
Expand Down Expand Up @@ -128,6 +130,17 @@ def collect_failing_inputs(group: str) -> Iterator[pytest.param]:
yield pytest.param(input_file, id=test_id)


def collect_reverse_failing_inputs(group: str) -> Iterator[pytest.param]:
# Reverse-fail fixtures use *.reverse.fail.hrw.txt (u4wrh input that must error)
# paired with *.reverse.fail.error.txt (expected error phrase). Forward-fail
# fixtures use *.fail.input.txt for the hrw4u compiler, which is why the glob
# here excludes them.
base_dir = Path("tests/data") / group
for input_file in base_dir.glob("*.reverse.fail.hrw.txt"):
test_id = input_file.stem
yield pytest.param(input_file, id=test_id)


def _collect_sandbox_test_files(group: str, result_suffix: str) -> Iterator[pytest.param]:
"""Collect sandbox test files: (input, result, sandbox_config).

Expand Down Expand Up @@ -373,6 +386,35 @@ def run_reverse_test(input_file: Path, output_file: Path, debug: bool = False) -
assert actual_hrw4u == expected_hrw4u, f"Reverse conversion mismatch for {output_file}"


def run_reverse_failing_test(input_file: Path) -> None:
hrw_text = input_file.read_text()
lexer = u4wrhLexer(InputStream(hrw_text))
stream = CommonTokenStream(lexer)
parser = u4wrhParser(stream)
tree = parser.program()

error_file = input_file.with_name(input_file.name.replace(".reverse.fail.hrw.txt", ".reverse.fail.error.txt"))
if not error_file.exists():
raise RuntimeError(f"Missing expected error file: {error_file}")

expected_error_content = error_file.read_text().strip()

error_collector = ErrorCollector()
visitor = HRWInverseVisitor(filename=str(input_file), error_collector=error_collector)
visitor.visit(tree)

assert error_collector.has_errors(), f"Expected reverse errors but none were raised for {input_file}"

actual_summary = error_collector.get_error_summary()
for line in expected_error_content.splitlines():
line = line.strip()
if line:
assert line in actual_summary, (
f"Expected phrase not found in error summary for {input_file}:\n"
f" Missing: {line!r}\n"
f"Actual summary:\n{actual_summary}")


def create_output_test(group: str):
import pytest

Expand Down