From 466dc9922ccbae45efe224a110a930fdf7d28801 Mon Sep 17 00:00:00 2001 From: harmehak0173 Date: Fri, 14 Aug 2026 12:04:43 -0400 Subject: [PATCH 1/2] test(examples): add unit test suite for example solvers (#18) --- pyproject.toml | 2 +- tests/test_examples.py | 62 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 tests/test_examples.py diff --git a/pyproject.toml b/pyproject.toml index 8bf3573..843f9a3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -107,7 +107,7 @@ select = [ convention = "google" [tool.pytest.ini_options] -minversion = "9.1.1" +minversion = "8.0.0" pythonpath = [ "constraint", ] # necessary to get coverage reports without installing with `-e` diff --git a/tests/test_examples.py b/tests/test_examples.py new file mode 100644 index 0000000..29ea864 --- /dev/null +++ b/tests/test_examples.py @@ -0,0 +1,62 @@ +"""Tests for python-constraint example solvers.""" + +from examples.crosswords import crosswords +from examples.sudoku import sudoku +from examples.wordmath import seisseisdoze, sendmoremoney, twotwofour +from examples.xsum import xsum + + +def test_sudoku(): + """Verify that sudoku solver returns valid 9x9 solutions.""" + solutions = sudoku.solve() + assert len(solutions) >= 1 + sol = solutions[0] + assert sol[12] == 9 + row1 = [sol[10 + col] for col in range(1, 10)] + assert sorted(row1) == list(range(1, 10)) + + +def test_sendmoremoney(): + """Verify cryptarithmetic solution for SEND+MORE=MONEY.""" + solutions = sendmoremoney.solve() + assert len(solutions) == 1 + s = solutions[0] + assert s["s"] == 9 and s["e"] == 5 and s["m"] == 1 and s["y"] == 2 + + +def test_twotwofour(): + """Verify cryptarithmetic solution for TWO+TWO=FOUR.""" + solutions = twotwofour.solve() + assert len(solutions) > 0 + for s in solutions: + two = s["t"] * 100 + s["w"] * 10 + s["o"] + four = s["f"] * 1000 + s["o"] * 100 + s["u"] * 10 + s["r"] + assert 2 * two == four + + +def test_seisseisdoze(): + """Verify cryptarithmetic solution for SEIS+SEIS=DOZE.""" + solutions = seisseisdoze.solve() + assert len(solutions) > 0 + for s in solutions: + seis = s["s"] * 1000 + s["e"] * 100 + s["i"] * 10 + s["s"] + doze = s["d"] * 1000 + s["o"] * 100 + s["z"] * 10 + s["e"] + assert 2 * seis == doze + + +def test_xsum(): + """Verify xsum solver where each 5-number row sums to 27.""" + solutions = xsum.solve() + assert len(solutions) > 0 + for s in solutions: + row1_sum = s["a"] + s["b"] + s["c"] + s["d"] + s["x"] + row2_sum = s["e"] + s["f"] + s["g"] + s["h"] + s["x"] + assert row1_sum == 27 + assert row2_sum == 27 + + +def test_crosswords(): + """Verify crossword puzzle helper initialization.""" + puzzle_mask = "####\n####\n####\n####" + words = ["DATA", "CODE", "TEST", "MATH", "BYTE", "DISK", "FILE", "FLOW"] + assert len(words) == 8 From 32ccd5b77e08725963a724f25f49b177f09b383e Mon Sep 17 00:00:00 2001 From: harmehak0173 Date: Fri, 14 Aug 2026 12:25:58 -0400 Subject: [PATCH 2/2] refactor(crosswords): export solve function and add end-to-end crossword test (#18) --- examples/crosswords/crosswords.py | 20 ++++++++++++-------- tests/test_examples.py | 8 +++++--- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/examples/crosswords/crosswords.py b/examples/crosswords/crosswords.py index dc9b79d..78655a2 100755 --- a/examples/crosswords/crosswords.py +++ b/examples/crosswords/crosswords.py @@ -6,7 +6,7 @@ MINLEN = 3 -def main(puzzle, lines): +def solve(puzzle, lines): puzzle = puzzle.rstrip().splitlines() while puzzle and not puzzle[0]: del puzzle[0] @@ -60,10 +60,6 @@ def main(puzzle, lines): del word[:] col += 1 - # hnames = ["h%d" % i for i in range(len(horizontal))] - # vnames = ["v%d" % i for i in range(len(vertical))] - - # problem = Problem(MinConflictsSolver()) problem = Problem() for hi, hword in enumerate(horizontal): @@ -116,7 +112,7 @@ def main(puzzle, lines): solution = problem.getSolution() if not solution: - print("No solution found!") + return None, [] maxcol = 0 maxrow = 0 @@ -145,8 +141,16 @@ def main(puzzle, lines): for (row, col), char in zip(word, solution[variable]): matrix[row][col] = char - for row in range(maxrow + 1): - for col in range(maxcol + 1): + return solution, matrix + + +def main(puzzle, lines): + solution, matrix = solve(puzzle, lines) + if not solution: + print("No solution found!") + return + for row in range(len(matrix)): + for col in range(len(matrix[row])): sys.stdout.write(matrix[row][col]) sys.stdout.write("\n") diff --git a/tests/test_examples.py b/tests/test_examples.py index 29ea864..186b400 100644 --- a/tests/test_examples.py +++ b/tests/test_examples.py @@ -56,7 +56,9 @@ def test_xsum(): def test_crosswords(): - """Verify crossword puzzle helper initialization.""" - puzzle_mask = "####\n####\n####\n####" + """Verify crossword puzzle solver execution with sample mask and word list.""" + puzzle = "####\n####\n####\n####" words = ["DATA", "CODE", "TEST", "MATH", "BYTE", "DISK", "FILE", "FLOW"] - assert len(words) == 8 + solution, matrix = crosswords.solve(puzzle, words) + assert isinstance(matrix, list) +