diff --git a/python/BUILD.bazel b/python/BUILD.bazel index fc003335..df169761 100644 --- a/python/BUILD.bazel +++ b/python/BUILD.bazel @@ -158,6 +158,14 @@ py_test( ], ) +py_test( + name = "convert_pbn_test", + size = "small", + main = "tests/test_convert_pbn.py", + srcs = ["tests/test_convert_pbn.py"], + deps = ["//python/examples:dd_table_for_deal_lib"], +) + py_wheel( name = "dds3_wheel", distribution = "dds3", diff --git a/python/examples/BUILD.bazel b/python/examples/BUILD.bazel index 39e7c152..8ef2d568 100644 --- a/python/examples/BUILD.bazel +++ b/python/examples/BUILD.bazel @@ -1,8 +1,16 @@ -load("@rules_python//python:defs.bzl", "py_binary") +load("@rules_python//python:defs.bzl", "py_binary", "py_library") + +py_library( + name = "dd_table_for_deal_lib", + srcs = ["dd_table_for_deal.py"], + imports = ["."], + deps = ["//python:dds3_lib"], + visibility = ["//python:__pkg__"], +) py_binary( name = "dd_table_for_deal", srcs = ["dd_table_for_deal.py"], main = "dd_table_for_deal.py", - deps = ["//python:dds3_lib"], + deps = [":dd_table_for_deal_lib"], ) diff --git a/python/examples/dd_table_for_deal.py b/python/examples/dd_table_for_deal.py index 53652505..1498841d 100644 --- a/python/examples/dd_table_for_deal.py +++ b/python/examples/dd_table_for_deal.py @@ -113,10 +113,11 @@ def _convert_pbn(pbn_deal: str) -> list[list[int]]: bp = 0 while ( bp < 3 + and bp < len(pbn_deal) and pbn_deal[bp] not in "NWESnwes" ): bp += 1 - if bp >= 3: + if bp >= 3 or bp >= len(pbn_deal): return remain first = {"N": 0, "E": 1, "S": 2, "W": 3}[pbn_deal[bp].upper()] diff --git a/python/tests/test_convert_pbn.py b/python/tests/test_convert_pbn.py new file mode 100644 index 00000000..905b9430 --- /dev/null +++ b/python/tests/test_convert_pbn.py @@ -0,0 +1,32 @@ +"""Tests for PBN-to-bitmask conversion in dd_table_for_deal.""" + +import unittest + +from dd_table_for_deal import _convert_pbn + +_EMPTY_REMAIN = [[0] * 4 for _ in range(4)] + +_EXAMPLE_DEAL = ( + "N:73.QJT.AQ54.T752 QT6.876.KJ9.AQ84 " + "5.A95432.7632.K6 AKJ9842.K.T8.J93" +) + + +class ConvertPbnTest(unittest.TestCase): + def test_short_deal_strings_return_empty_remain(self) -> None: + """Short or malformed PBN strings must not raise IndexError.""" + for deal in ("", "N", "N:", "12", "abc"): + with self.subTest(deal=deal): + self.assertEqual(_convert_pbn(deal), _EMPTY_REMAIN) + + def test_valid_deal_parses_card_bitmasks(self) -> None: + remain = _convert_pbn(_EXAMPLE_DEAL) + + # North's spades: 73 + self.assertEqual(remain[0][0], 0x0080 | 0x0008) + # North's hearts: QJT + self.assertEqual(remain[0][1], 0x1000 | 0x0800 | 0x0400) + + +if __name__ == "__main__": + unittest.main()