aboutsummaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authornatsuoto <[email protected]>2026-07-15 15:15:21 +0530
committernatsuoto <[email protected]>2026-07-15 15:15:21 +0530
commit91942559f53003d2ef7a524456aef84f93b7f319 (patch)
treed4064676c1188ff23541a1c50072af448ae74068 /tests
parentc343f05467045bd369aa8ecd6273924fe7c89f95 (diff)
downloadedify-91942559f53003d2ef7a524456aef84f93b7f319.tar.xz
edify-91942559f53003d2ef7a524456aef84f93b7f319.zip
feat(errors): structured open-frame stack with per-frame call sites; DanglingQuantifierError names the pending quantifier
Diffstat (limited to 'tests')
-rw-r--r--tests/errors/errors.test.py23
-rw-r--r--tests/errors/frames.test.py69
2 files changed, 89 insertions, 3 deletions
diff --git a/tests/errors/errors.test.py b/tests/errors/errors.test.py
index 632045d..82eb8fe 100644
--- a/tests/errors/errors.test.py
+++ b/tests/errors/errors.test.py
@@ -24,6 +24,7 @@ from edify.errors.naming import (
from edify.errors.structure import (
CannotCallSubexpressionError,
CannotEndWhileBuildingRootExpressionError,
+ OpenFrameInfo,
)
@@ -176,10 +177,26 @@ def test_cannot_end_while_building_root_expression():
def test_cannot_call_subexpression():
- error = CannotCallSubexpressionError("capture")
+ frames = (OpenFrameInfo(kind="capture", opened_at=None),)
+ error = CannotCallSubexpressionError(frames)
text = str(error)
assert "cannot merge a subexpression that has an unclosed frame" in text
- assert "capture" in text
+ assert "1 open frame" in text
+ assert ".capture()" in text
+
+
+def test_cannot_call_subexpression_lists_every_open_frame_in_the_stack():
+ frames = (
+ OpenFrameInfo(kind="capture", opened_at=None),
+ OpenFrameInfo(kind="assert_ahead", opened_at=None),
+ OpenFrameInfo(kind="group", opened_at=None),
+ )
+ error = CannotCallSubexpressionError(frames)
+ text = str(error)
+ assert "3 open frame" in text
+ assert "1. .capture()" in text
+ assert "2. .assert_ahead()" in text
+ assert "3. .group()" in text
def test_every_annotated_error_message_starts_with_error_prefix():
@@ -202,7 +219,7 @@ def test_every_annotated_error_message_starts_with_error_prefix():
CannotCreateDuplicateNamedGroupError("x"),
NamedGroupDoesNotExistError("x"),
CannotEndWhileBuildingRootExpressionError(),
- CannotCallSubexpressionError("capture"),
+ CannotCallSubexpressionError((OpenFrameInfo(kind="capture", opened_at=None),)),
]
for error in errors:
text = str(error)
diff --git a/tests/errors/frames.test.py b/tests/errors/frames.test.py
new file mode 100644
index 0000000..b4077ba
--- /dev/null
+++ b/tests/errors/frames.test.py
@@ -0,0 +1,69 @@
+"""Tests for the structured open-frame stack surfaced by CannotCallSubexpressionError."""
+
+import pytest
+
+from edify import RegexBuilder
+from edify.errors.structure import CannotCallSubexpressionError
+
+
+def test_single_open_capture_frame_is_named_in_the_error_message():
+ unfinished = RegexBuilder().capture().digit()
+ with pytest.raises(CannotCallSubexpressionError) as excinfo:
+ unfinished.to_regex_string()
+ text = str(excinfo.value)
+ assert "1 open frame" in text
+ assert "1. .capture()" in text
+
+
+def test_nested_open_frames_are_listed_innermost_first():
+ unfinished = RegexBuilder().capture().group().assert_ahead().digit()
+ with pytest.raises(CannotCallSubexpressionError) as excinfo:
+ unfinished.to_regex_string()
+ text = str(excinfo.value)
+ assert "3 open frame" in text
+ innermost_index = text.index("1. .assert_ahead()")
+ middle_index = text.index("2. .group()")
+ outermost_index = text.index("3. .capture()")
+ assert innermost_index < middle_index < outermost_index
+
+
+def test_named_capture_frame_reports_the_declared_group_name():
+ unfinished = RegexBuilder().named_capture("username").letter()
+ with pytest.raises(CannotCallSubexpressionError) as excinfo:
+ unfinished.to_regex_string()
+ text = str(excinfo.value)
+ assert '.named_capture("username")' in text
+
+
+def test_open_frames_carry_a_file_line_column_pointer_when_the_context_is_captured():
+ unfinished = RegexBuilder().assert_behind().digit()
+ with pytest.raises(CannotCallSubexpressionError) as excinfo:
+ unfinished.to_regex_string()
+ text = str(excinfo.value)
+ assert "opened at" in text
+ assert __file__ in text or "frames.test.py" in text
+
+
+def test_subexpression_lists_open_frames_from_the_argument_not_the_receiver():
+ unfinished = RegexBuilder().named_capture("group").digit()
+ parent = RegexBuilder()
+ with pytest.raises(CannotCallSubexpressionError) as excinfo:
+ parent.subexpression(unfinished)
+ assert 'named_capture("group")' in str(excinfo.value)
+
+
+def test_dangling_quantifier_reports_the_specific_pending_quantifier_name():
+ from edify.errors.quantifier import DanglingQuantifierError
+
+ with pytest.raises(DanglingQuantifierError) as excinfo:
+ RegexBuilder().exactly(3).to_regex_string()
+ text = str(excinfo.value)
+ assert ".exactly(3)" in text
+
+
+def test_dangling_quantifier_falls_back_to_generic_summary_when_name_unknown():
+ from edify.errors.quantifier import DanglingQuantifierError
+
+ error = DanglingQuantifierError()
+ text = str(error)
+ assert "dangling quantifier" in text