diff options
| author | natsuoto <[email protected]> | 2026-07-10 16:19:01 +0530 |
|---|---|---|
| committer | natsuoto <[email protected]> | 2026-07-10 16:19:01 +0530 |
| commit | 1ca22160898b7c9bcc945ac8d59d71b72f8a7116 (patch) | |
| tree | 15ce457d4d7fd243c153d4b8e60e6e75bd714988 /tests | |
| parent | 74d6b084993feb9c9dc1a72abeb3cf2497ac9171 (diff) | |
| download | edify-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.py | 69 | ||||
| -rw-r--r-- | tests/builder/equality.test.py | 87 |
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) |
