diff options
Diffstat (limited to 'tests')
-rw-r--r-- | tests/parser_unittest.py | 73 | ||||
-rw-r--r-- | tests/scheduler_brutal_unittest.py | 88 | ||||
-rw-r--r-- | tests/session_integrationtest.py | 47 | ||||
-rw-r--r-- | tests/state_json_unittest.py | 14 |
4 files changed, 222 insertions, 0 deletions
diff --git a/tests/parser_unittest.py b/tests/parser_unittest.py new file mode 100644 index 0000000..8076369 --- /dev/null +++ b/tests/parser_unittest.py @@ -0,0 +1,73 @@ +import pytest +from pathlib import Path + +from flashcards import parser + +# Happy path +def test_validFile(tmp_path): + file_contents = """ + +FRONT + + +Foo + +Bar + +BACK + + +Fizz + +Buzz + + + +FRONT + +Another card + +BACK + +Another back + + + """ + expected = { + ("Foo\n\nBar", "Fizz\n\nBuzz"), + ("Another card", "Another back") + } + + path = tmp_path / "valid_file.fcard" + with open(path, "w") as f: + f.write(file_contents) + + cards = parser.parseFile(path) + + assert expected == set(cards.values()) + +# Edge cases +def test_emptyFile(tmp_path): + path = tmp_path / "empty.fcard" + with open(path, "w") as f: + f.write("") + + cards = parser.parseFile(path) + assert cards == {} + +def checkException(tmp_path, file_contents): + path = tmp_path / "invalid_file.fcard" + with open(path, "w") as f: + f.write(file_contents) + + with pytest.raises(Exception): + cards = parser.parseFile(path) + +def test_doesNotStartWithFront(tmp_path): + checkException(tmp_path, "BACK\noops") + +def test_frontTwiceInARow(tmp_path): + checkException(tmp_path, "FRONT\noops\nFRONT\nbad") + +def test_doesNotEndWithBack(tmp_path): + checkException(tmp_path, "FRONT\ntest\nBACK\ntest\nFRONT\noops") diff --git a/tests/scheduler_brutal_unittest.py b/tests/scheduler_brutal_unittest.py new file mode 100644 index 0000000..a87e5e9 --- /dev/null +++ b/tests/scheduler_brutal_unittest.py @@ -0,0 +1,88 @@ +import pytest + +from flashcards import scheduler_brutal +from flashcards.scheduler_brutal import SchedulerBrutal as UUT +from flashcards.card import Card + +# Force HISTORY_DEPTH to simplify testing +scheduler_brutal.HISTORY_DEPTH = 3 + +#-------------------------------------------------------------------------- +# Scheduling behaviour +#-------------------------------------------------------------------------- +def test_scheduling(): + cards = {str(id): Card("", "") for id in range(0, 10)} + state = { + "0": [1, 1, 1], + "1": [0, 0, 0], + "2": [0, 0, 1], + "3": [1, 0, 0], + + "4": [None, None, 1 ], + "5": [None, 1, None], + "6": [1, None, None], + "7": [None, None, 0 ], + "8": [0, 0, None], + "9": [None, None, None], + } + + expected_priority = ["9", "6", "5", "7", "8", "4", "1", "3", "2", "0"] + + uut = UUT(cards, state) + + for i in range(0, len(expected_priority)): + assert set(uut.practice(i + 1)) == set(expected_priority[0:i + 1]) + +#-------------------------------------------------------------------------- +# State update +#-------------------------------------------------------------------------- +def test_stateUpdate(): + cards = {"0": Card("f", "b"), "1": Card("a", "b"), "2": Card("c", "d")} + state = {"0": [1, 0, 1], "1": [1, 0, 0], "2": [0, 0, 1]} + + uut = UUT(cards, state) + + # Unknown IDs in the result are silently ignored + result = {"0": 1, "1": 0, "3": 0} + expected_state = {"0": [0, 1, 1], "1": [0, 0, 0], "2": [0, 1, None]} + + uut.update(result) + + assert uut.getState() == expected_state + +#-------------------------------------------------------------------------- +# State corrections +#-------------------------------------------------------------------------- +def test_stateWhenCardsChanged(): + cards = {"0": Card("f", "b"), "1": Card("a", "b")} + + initial_state = {"0": [1, 0, 1], "2": [0, 0, 0]} + expected_state = {"0": [1, 0, 1], "1": [None, None, None]} + + uut = UUT(cards, initial_state) + + assert uut.getState() == expected_state + +def test_stateWhenHistoryDepthIncreased(): + scheduler_brutal.HISTORY_DEPTH = 5 + + cards = {"0": Card("f", "b"), "1": Card("a", "b"), "2": Card("new", "new")} + + initial_state = {"0": [1, 0, 1], "1": [0, 0, 0]} + expected_state = {"0": [None, None, 1, 0, 1], "1": [None, None, 0, 0, 0], "2": [None] * 5} + + uut = UUT(cards, initial_state) + + assert uut.getState() == expected_state + +def test_stateWhenHistoryDepthDecreased(): + scheduler_brutal.HISTORY_DEPTH = 1 + + cards = {"0": Card("f", "b"), "1": Card("a", "b"), "2": Card("new", "new")} + + initial_state = {"0": [1, 0, 0], "1": [0, 0, 1]} + expected_state = {"0": [0], "1": [1], "2": [None]} + + uut = UUT(cards, initial_state) + + assert uut.getState() == expected_state diff --git a/tests/session_integrationtest.py b/tests/session_integrationtest.py new file mode 100644 index 0000000..ddabff9 --- /dev/null +++ b/tests/session_integrationtest.py @@ -0,0 +1,47 @@ +import pytest +import json + +from flashcards.session import Session + +@pytest.fixture +def cardFiles(tmp_path): + card_files = [ + tmp_path / "test1.fcard", + tmp_path / "test2.fcard", + ] + + for i, c in enumerate(card_files): + with open(c, "w") as f: + for j in range(0, 3): + f.write(f"FRONT\nFile {i}, card {j} front\nBACK\nback\n") + + return card_files + +@pytest.fixture +def stateFile(tmp_path): + return tmp_path / "state.json" + +def test_practiceSession(cardFiles, stateFile): + session = Session("brutal", cardFiles, stateFile) + + c = 0 + for card in session.practice(5): + c += 1 + + assert c == 5 + +def test_testSession(cardFiles, stateFile): + session = Session("brutal", cardFiles, stateFile) + + c = 0 + for i, (card, correct) in enumerate(session.test(5)): + c += 1 + correct(i % 2 == 0) + + assert c == 5 + + with open(stateFile, "r") as f: + state = json.load(f) + latest_scores = [history[-1] for id, history in state.items()] + assert latest_scores.count(0) == 2 + assert latest_scores.count(1) == 3 diff --git a/tests/state_json_unittest.py b/tests/state_json_unittest.py new file mode 100644 index 0000000..03ea555 --- /dev/null +++ b/tests/state_json_unittest.py @@ -0,0 +1,14 @@ +import pytest + +from flashcards import state_json + +def test_saveAndLoad(tmp_path): + file = tmp_path / "test.json" + state = {"key": [10, 20, None], "another_key": "value"} + + state_json.save(file, state) + + assert state_json.load(file) == state + +def test_missingFile(tmp_path): + assert state_json.load(tmp_path / "missing.json") == {} |