aboutsummaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authornatsuoto <[email protected]>2026-07-10 16:19:01 +0530
committernatsuoto <[email protected]>2026-07-10 16:19:01 +0530
commit1ca22160898b7c9bcc945ac8d59d71b72f8a7116 (patch)
tree15ce457d4d7fd243c153d4b8e60e6e75bd714988 /tests
parent74d6b084993feb9c9dc1a72abeb3cf2497ac9171 (diff)
downloadedify-1ca22160898b7c9bcc945ac8d59d71b72f8a7116.tar.xz
edify-1ca22160898b7c9bcc945ac8d59d71b72f8a7116.zip
feat(builder): lazy Regex compile cache and __eq__/__hash__ diagnose unfinished builders (#127, #137)
Diffstat (limited to 'tests')
-rw-r--r--tests/builder/cache.test.py69
-rw-r--r--tests/builder/equality.test.py87
2 files changed, 156 insertions, 0 deletions
diff --git a/tests/builder/cache.test.py b/tests/builder/cache.test.py
new file mode 100644
index 0000000..80862d3
--- /dev/null
+++ b/tests/builder/cache.test.py
@@ -0,0 +1,69 @@
+"""Tests for the lazy :class:`Regex` cache on :class:`BuilderCore`."""
+
+from edify import Pattern, RegexBuilder
+
+
+def test_repeated_lazy_regex_calls_return_the_same_instance():
+ builder = RegexBuilder().digit()
+ first = builder._lazy_regex()
+ second = builder._lazy_regex()
+ third = builder._lazy_regex()
+ assert first is second
+ assert second is third
+
+
+def test_repeated_matcher_calls_reuse_the_cached_regex():
+ builder = RegexBuilder().digit()
+ builder.match("1")
+ cached = builder._cached_regex
+ builder.search("2")
+ builder.findall("3")
+ builder.test("4")
+ assert builder._cached_regex is cached
+
+
+def test_pattern_lazy_regex_is_memoised_too():
+ pattern = Pattern().word()
+ first = pattern._lazy_regex()
+ second = pattern._lazy_regex()
+ assert first is second
+
+
+def test_a_forked_builder_gets_a_fresh_cache_slot():
+ original = RegexBuilder().digit()
+ original.match("1")
+ assert original._cached_regex is not None
+ forked = original.fork()
+ assert forked._cached_regex is None
+
+
+def test_a_copied_builder_gets_a_fresh_cache_slot():
+ original = RegexBuilder().digit()
+ original.match("1")
+ copied = original.copy()
+ assert copied._cached_regex is None
+
+
+def test_a_chain_extension_gets_a_fresh_cache_slot():
+ original = RegexBuilder().digit()
+ original.match("1")
+ extended = original.word()
+ assert extended._cached_regex is None
+
+
+def test_a_fresh_builder_starts_without_a_cached_regex():
+ builder = RegexBuilder()
+ assert builder._cached_regex is None
+
+
+def test_calling_to_regex_directly_does_not_populate_the_cache():
+ builder = RegexBuilder().digit()
+ _ = builder.to_regex()
+ assert builder._cached_regex is None
+
+
+def test_lazy_regex_produces_a_regex_that_matches_the_pattern():
+ builder = RegexBuilder().digit()
+ result = builder._lazy_regex()
+ assert result.source == "\\d"
+ assert result.match("7") is not None
diff --git a/tests/builder/equality.test.py b/tests/builder/equality.test.py
index 717d1ae..8482b51 100644
--- a/tests/builder/equality.test.py
+++ b/tests/builder/equality.test.py
@@ -1,6 +1,12 @@
"""Tests for value-based ``__eq__`` and ``__hash__`` on :class:`BuilderCore`."""
+import pytest
+
from edify import Pattern, RegexBuilder
+from edify.errors.comparison import (
+ CannotCompareUnfinishedBuilderError,
+ CannotHashUnfinishedBuilderError,
+)
def test_two_builders_with_the_same_chain_are_equal():
@@ -48,3 +54,84 @@ def test_hash_of_two_flags_that_differ_are_distinct():
b = RegexBuilder().digit()
assert a != b
assert hash(a) != hash(b)
+
+
+def test_equality_uses_emitted_pattern_not_underlying_state():
+ from_chain = RegexBuilder().string("hi")
+ from_string_kwarg = RegexBuilder().string("hi")
+ assert from_chain.to_regex_string() == from_string_kwarg.to_regex_string()
+ assert from_chain == from_string_kwarg
+
+
+def test_hash_uses_emitted_pattern_and_flags_tuple():
+ a = RegexBuilder().string("hi")
+ b = RegexBuilder().string("hi")
+ assert hash(a) == hash((a.to_regex_string(), a._state.flags))
+ assert hash(a) == hash(b)
+
+
+def test_equality_raises_when_left_operand_has_open_frames():
+ left = RegexBuilder().any_of()
+ right = RegexBuilder().digit()
+ with pytest.raises(CannotCompareUnfinishedBuilderError):
+ _ = left == right
+
+
+def test_equality_raises_when_right_operand_has_open_frames():
+ left = RegexBuilder().digit()
+ right = RegexBuilder().any_of()
+ with pytest.raises(CannotCompareUnfinishedBuilderError):
+ _ = left == right
+
+
+def test_equality_raises_when_both_operands_are_unfinished():
+ left = RegexBuilder().any_of()
+ right = RegexBuilder().exactly(3)
+ with pytest.raises(CannotCompareUnfinishedBuilderError):
+ _ = left == right
+
+
+def test_hash_raises_when_frames_are_open():
+ with pytest.raises(CannotHashUnfinishedBuilderError):
+ hash(RegexBuilder().any_of())
+
+
+def test_equality_raises_when_a_quantifier_is_dangling():
+ left = RegexBuilder().exactly(3)
+ right = RegexBuilder().digit()
+ with pytest.raises(CannotCompareUnfinishedBuilderError):
+ _ = left == right
+
+
+def test_hash_raises_when_a_quantifier_is_dangling():
+ with pytest.raises(CannotHashUnfinishedBuilderError):
+ hash(RegexBuilder().exactly(3))
+
+
+def test_compare_error_message_names_the_open_frame():
+ with pytest.raises(CannotCompareUnfinishedBuilderError) as excinfo:
+ _ = RegexBuilder().named_capture("domain") == RegexBuilder().digit()
+ assert 'named_capture("domain")' in str(excinfo.value)
+ assert "frame opened here" in str(excinfo.value)
+ assert ".end()" in str(excinfo.value)
+
+
+def test_compare_error_message_names_the_dangling_quantifier():
+ with pytest.raises(CannotCompareUnfinishedBuilderError) as excinfo:
+ _ = RegexBuilder().digit() == RegexBuilder().exactly(4)
+ assert "exactly(4)" in str(excinfo.value)
+ assert "no element follows" in str(excinfo.value)
+ assert ".digit()" in str(excinfo.value)
+
+
+def test_hash_error_message_names_the_specific_problem():
+ with pytest.raises(CannotHashUnfinishedBuilderError) as excinfo:
+ hash(RegexBuilder().at_least(2))
+ assert "at_least(2)" in str(excinfo.value)
+
+
+def test_error_message_shows_a_source_pointer_when_called_from_user_code():
+ with pytest.raises(CannotHashUnfinishedBuilderError) as excinfo:
+ hash(RegexBuilder().any_of())
+ assert "-->" in str(excinfo.value)
+ assert __file__ in str(excinfo.value)