From 71675ee4a1abf0b947141323adef9ecc1b40f599 Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Sat, 22 Feb 2025 15:52:47 +0100 Subject: [PATCH] Update test coverage --- novelwriter/core/index.py | 2 +- novelwriter/core/indexdata.py | 12 +-- tests/test_core/test_core_index.py | 20 ++++ tests/test_core/test_core_indexdata.py | 140 ++++++++++++++++++++++++- 4 files changed, 165 insertions(+), 9 deletions(-) diff --git a/novelwriter/core/index.py b/novelwriter/core/index.py index abff7e33..9a143028 100644 --- a/novelwriter/core/index.py +++ b/novelwriter/core/index.py @@ -987,7 +987,7 @@ class ItemIndex: def unpackData(self, data: dict) -> None: """Iterate through the itemIndex loaded from cache and check - that it's valid. This will raise errors if there is a problem. + that it's valid. This will raise errors if there are problems. """ self._items = {} if not isinstance(data, dict): diff --git a/novelwriter/core/indexdata.py b/novelwriter/core/indexdata.py index 06669126..1c307903 100644 --- a/novelwriter/core/indexdata.py +++ b/novelwriter/core/indexdata.py @@ -114,17 +114,17 @@ class IndexNode: self._headings[sTitle].setSynopsis(text) return - def setHeadingTag(self, sTitle: str, tagKey: str) -> None: + def setHeadingTag(self, sTitle: str, tag: str) -> None: """Set the tag of a heading.""" if sTitle in self._headings: - self._headings[sTitle].setTag(tagKey) + self._headings[sTitle].setTag(tag) return - def addHeadingRef(self, sTitle: str, tagKeys: list[str], refType: str) -> None: + def addHeadingRef(self, sTitle: str, tags: list[str], keyword: str) -> None: """Add a reference key and all its types to a heading.""" if sTitle in self._headings: - for tagKey in tagKeys: - self._headings[sTitle].addReference(tagKey, refType) + for tag in tags: + self._headings[sTitle].addReference(tag, keyword) return def addNoteKey(self, style: T_NoteTypes, key: str) -> None: @@ -187,7 +187,7 @@ class IndexNode: raise ValueError("The notes keys must be a list of strings") self._notes[style] = set(keys) else: - raise ValueError("The itemIndex contains an invalid title key") + raise KeyError("Index node contains an invalid key") return diff --git a/tests/test_core/test_core_index.py b/tests/test_core/test_core_index.py index 86e34915..0f83c34f 100644 --- a/tests/test_core/test_core_index.py +++ b/tests/test_core/test_core_index.py @@ -1310,3 +1310,23 @@ def testCoreIndex_ItemIndex(mockGUI, fncPath, mockRnd): # Delete new item del itemIndex[uHandle] # type: ignore assert uHandle not in itemIndex + + # Unpack Error Handling + # ===================== + + # Pack/unpack should restore state + content = itemIndex.packData() + itemIndex.clear() + itemIndex.unpackData(content) + assert itemIndex.packData() == content + itemIndex.clear() + + # Data must be dictionary + with pytest.raises(ValueError) as exc: + itemIndex.unpackData("stuff") # type: ignore + assert str(exc.value) == "itemIndex is not a dict" + + # Keys must be valid handles + with pytest.raises(ValueError) as exc: + itemIndex.unpackData({"stuff": "more stuff"}) + assert str(exc.value) == "itemIndex keys must be handles" diff --git a/tests/test_core/test_core_indexdata.py b/tests/test_core/test_core_indexdata.py index 7ca7fb78..57733765 100644 --- a/tests/test_core/test_core_indexdata.py +++ b/tests/test_core/test_core_indexdata.py @@ -22,7 +22,143 @@ from __future__ import annotations import pytest -from novelwriter.core.indexdata import IndexHeading +from novelwriter.core.indexdata import IndexHeading, IndexNode +from novelwriter.core.item import NWItem +from novelwriter.core.project import NWProject + + +@pytest.mark.core +def testCoreIndexData_IndexNode(mockGUI): + """Test the IndexNode class.""" + handle = "0123456789abc" + project = NWProject() + item = NWItem(project, handle) + + # Defaults + node = IndexNode(handle, item) + assert node.handle == handle + assert node.item is item + assert str(node) == f"" + assert repr(node) == f"" + assert len(node) == 1 + assert "T0000" in node # Placeholder heading + + # Add a heading + head1 = IndexHeading(node.nextHeading(), line=1, level="H1", title="Heading 1") + head2 = IndexHeading(node.nextHeading(), line=10, level="H2", title="Heading 2") + node.addHeading(head1) + node.addHeading(head2) + assert len(node) == 2 + assert "T0000" not in node # Placeholder heading should be gone + assert "T0001" in node + assert "T0002" in node + assert node["T0001"] is head1 + assert node["T0002"] is head2 + assert node.headings() == ["T0001", "T0002"] + assert dict(node.items()) == {"T0001": head1, "T0002": head2} + + # Next heading should be T0003 + assert node.nextHeading() == "T0003" + + # Set counts + node.setHeadingCounts("T0001", 42, 13, 3) + node.setHeadingCounts("T0002", 84, 26, 6) + assert head1.charCount == 42 + assert head1.wordCount == 13 + assert head1.paraCount == 3 + assert head2.charCount == 84 + assert head2.wordCount == 26 + assert head2.paraCount == 6 + + # Set synopsis + node.setHeadingSynopsis("T0001", "The first") + node.setHeadingSynopsis("T0002", "The second") + assert head1.synopsis == "The first" + assert head2.synopsis == "The second" + + # Set tags + node.setHeadingTag("T0001", "part1") + node.setHeadingTag("T0002", "part2") + assert head1.tag == "part1" + assert head2.tag == "part2" + assert node.allTags() == ["part1", "part2"] + + # Add references + node.addHeadingRef("T0001", ["jane"], "@pov") + node.addHeadingRef("T0001", ["jane", "john"], "@char") + node.addHeadingRef("T0002", ["earth", "space"], "@location") + assert head1.references["jane"] == {"@char", "@pov"} + assert head1.references["john"] == {"@char"} + assert head2.references["earth"] == {"@location"} + assert head2.references["space"] == {"@location"} + + # Add note keys + assert node.noteKeys("footnotes") == set() + node.addNoteKey("footnotes", "key1") + node.addNoteKey("footnotes", "key2") + assert node.noteKeys("footnotes") == {"key1", "key2"} + + +@pytest.mark.core +def testCoreIndexData_IndexNodePackUnpack(mockGUI): + """Test the pack and unpack methods of the IndexNode class.""" + handle = "0123456789abc" + project = NWProject() + item = NWItem(project, handle) + node = IndexNode(handle, item) + + # Add some headings and notes + head1 = IndexHeading(node.nextHeading(), line=1, level="H1", title="Heading 1") + head2 = IndexHeading(node.nextHeading(), line=10, level="H2", title="Heading 2") + node.addHeading(head1) + node.addHeading(head2) + node.setHeadingCounts("T0001", 42, 13, 3) + node.setHeadingCounts("T0002", 84, 26, 6) + node.addNoteKey("footnotes", "key1") + node.addNoteKey("footnotes", "key2") + + # Check packing + data = node.packData() + assert data["T0001"] == {"meta": { + "level": "H1", "title": "Heading 1", "line": 1, "tag": "", "counts": (42, 13, 3) + }} + assert data["T0002"] == {"meta": { + "level": "H2", "title": "Heading 2", "line": 10, "tag": "", "counts": (84, 26, 6) + }} + assert set(data["document"]["footnotes"]) == {"key1", "key2"} + + # Create a new node + new = IndexNode(handle, item) + + # Unpack heading one + data = {"T0001": {"meta": { + "level": "H1", "title": "Heading 1", "line": 1, "tag": "", "counts": (42, 13, 3) + }}} + new.unpackData(data) + data = new.packData() + assert data["T0001"] == {"meta": { + "level": "H1", "title": "Heading 1", "line": 1, "tag": "", "counts": (42, 13, 3) + }} + + # Unpack invalid key + data = {"stuff": "whatever"} + with pytest.raises(KeyError, match="Index node contains an invalid key"): + new.unpackData(data) + + # Unpack invalid document keys + data = {"document": {"whatever": []}} + with pytest.raises(ValueError, match="The notes style is invalid"): + new.unpackData(data) + + # Unpack invalid document values + data = {"document": {"footnotes": [1, 2, 3]}} + with pytest.raises(ValueError, match="The notes keys must be a list of strings"): + new.unpackData(data) + + # Unpack valid keys + data = {"document": {"footnotes": ["key1", "key2"]}} + new.unpackData(data) + assert set(new.packData()["document"]["footnotes"]) == {"key1", "key2"} @pytest.mark.core @@ -30,8 +166,8 @@ def testCoreIndexData_IndexHeading(): """Test the IndexHeading class.""" # Defaults head = IndexHeading("T0001") - assert repr(head) == "" assert str(head) == "" + assert repr(head) == "" assert head.key == "T0001" assert head.line == 0 assert head.level == "H0"