diff --git a/novelwriter/core/doctools.py b/novelwriter/core/doctools.py index 75a4c3b7..f6716590 100644 --- a/novelwriter/core/doctools.py +++ b/novelwriter/core/doctools.py @@ -110,7 +110,7 @@ class DocMerger: if docText: self._targetText.insert(0, docText) - status = outDoc.writeDocument("\n\n".join(self._targetText)) + status = outDoc.writeDocument("\n\n".join(self._targetText) + "\n\n") if not status: self._error = outDoc.getError() diff --git a/novelwriter/core/project.py b/novelwriter/core/project.py index a6f88a7e..475300fd 100644 --- a/novelwriter/core/project.py +++ b/novelwriter/core/project.py @@ -176,7 +176,7 @@ class NWProject(): self._projTree.updateItemData(newItem.itemHandle) return newItem.itemHandle - def writeNewFile(self, tHandle, hLevel, isDocument): + def writeNewFile(self, tHandle, hLevel, isDocument, addText=""): """Write content to a new document after it is created. This will not run if the file exists and is not empty. """ @@ -191,7 +191,7 @@ class NWProject(): return False hshText = "#"*minmax(hLevel, 1, 4) - newText = f"{hshText} {tItem.itemName}\n\n" + newText = f"{hshText} {tItem.itemName}\n\n{addText}" if tItem.isNovelLike() and isDocument: tItem.setLayout(nwItemLayout.DOCUMENT) else: diff --git a/tests/conftest.py b/tests/conftest.py index 769f855c..40725923 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -190,14 +190,20 @@ def mockRnd(monkeypatch): from 0. This one will generate status/importance flags and handles in a predictable sequence. """ - def rnd(n): - for x in range(n): - yield x + class MockRnd: - gen = rnd(1000) - monkeypatch.setattr("random.getrandbits", lambda *a: next(gen)) + def __init__(self): + self.reset() - return + def _rnd(self, n): + for x in range(n): + yield x + + def reset(self): + gen = self._rnd(1000) + monkeypatch.setattr("random.getrandbits", lambda *a: next(gen)) + + return MockRnd() ## diff --git a/tests/reference/coreDocTools_DocMerger_0000000000010.nwd b/tests/reference/coreDocTools_DocMerger_0000000000010.nwd new file mode 100644 index 00000000..6dac570d --- /dev/null +++ b/tests/reference/coreDocTools_DocMerger_0000000000010.nwd @@ -0,0 +1,33 @@ +%%~name: Chapter 1 +%%~path: 0000000000008/0000000000010 +%%~kind: NOVEL/DOCUMENT +## Chapter 1 + +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc maximus justo non dictum commodo. Curabitur lacinia tempor orci vel luctus. Phasellus porta metus eu massa luctus, eget euismod risus rhoncus. Vestibulum sed arcu nisi. Maecenas pretium facilisis velit, vel semper lacus aliquam sit amet. Vestibulum vulputate neque ligula, rhoncus blandit turpis consequat id. Mauris sagittis vehicula imperdiet. Duis sed nunc pretium, ornare purus vel, sodales augue. Maecenas a suscipit risus. Quisque volutpat justo eleifend est ullamcorper fermentum. Donec ullamcorper et tortor a laoreet. Nam id risus nisi. Vivamus non imperdiet erat, sit amet imperdiet felis. Mauris vitae neque et est aliquam scelerisque non non ipsum. + +Nullam laoreet lorem nec malesuada vehicula. Vivamus tempus sodales lectus sed viverra. Aenean lacinia sollicitudin quam, quis tempus eros suscipit id. Duis sed rutrum nisi, ut pulvinar magna. Nam et cursus tortor. Phasellus ac odio tellus. Nullam in iaculis ipsum. Vivamus ante sem, ultricies sed varius quis, tristique nec tellus. Nullam eu urna vitae lacus hendrerit gravida. Quisque pulvinar erat ex, id efficitur velit sodales vitae. Proin vestibulum, sapien eget mattis euismod, tortor quam viverra risus, at congue mauris tortor eu nunc. Mauris pellentesque elit leo, quis eleifend sem placerat a. Vivamus iaculis dui eget tellus volutpat, ac varius nisi facilisis. + +% Merge Novel Document: Scene 1.1 [New] + +### Scene 1.1 + +Nullam laoreet lorem nec malesuada vehicula. Vivamus tempus sodales lectus sed viverra. Aenean lacinia sollicitudin quam, quis tempus eros suscipit id. Duis sed rutrum nisi, ut pulvinar magna. Nam et cursus tortor. Phasellus ac odio tellus. Nullam in iaculis ipsum. Vivamus ante sem, ultricies sed varius quis, tristique nec tellus. Nullam eu urna vitae lacus hendrerit gravida. Quisque pulvinar erat ex, id efficitur velit sodales vitae. Proin vestibulum, sapien eget mattis euismod, tortor quam viverra risus, at congue mauris tortor eu nunc. Mauris pellentesque elit leo, quis eleifend sem placerat a. Vivamus iaculis dui eget tellus volutpat, ac varius nisi facilisis. + +Nullam a nisl magna. Praesent commodo nec diam aliquet vestibulum. In sapien velit, sodales feugiat porta ut, rhoncus a elit. Quisque egestas nisi eu eros laoreet, quis facilisis est pretium. Nullam bibendum sed tellus nec lobortis. Duis elit massa, volutpat a lacinia a, ullamcorper in dui. Suspendisse ac laoreet dui. Curabitur elementum, tortor elementum ultricies laoreet, nunc massa vulputate augue, vitae tincidunt nunc enim eget nisl. + +% Merge Novel Document: Scene 1.2 [New] + +### Scene 1.2 + +Nullam a nisl magna. Praesent commodo nec diam aliquet vestibulum. In sapien velit, sodales feugiat porta ut, rhoncus a elit. Quisque egestas nisi eu eros laoreet, quis facilisis est pretium. Nullam bibendum sed tellus nec lobortis. Duis elit massa, volutpat a lacinia a, ullamcorper in dui. Suspendisse ac laoreet dui. Curabitur elementum, tortor elementum ultricies laoreet, nunc massa vulputate augue, vitae tincidunt nunc enim eget nisl. + +Pellentesque nibh urna, volutpat et feugiat porta, rutrum sed lectus. Aliquam eget risus id orci tincidunt condimentum et sit amet purus. Curabitur tincidunt odio vel ante feugiat feugiat. Proin nunc lorem, molestie a sapien et, varius elementum nunc. Donec non fermentum nisl. In et massa placerat, faucibus felis eu, congue nisi. Proin sed tortor non lorem mattis cursus. Vestibulum magna neque, bibendum vel nibh et, tincidunt rhoncus nisi. Duis pulvinar mi a quam rutrum maximus. Nunc sollicitudin, urna in cursus facilisis, augue neque imperdiet metus, ac finibus lorem ante id nulla. Sed maximus eleifend justo id feugiat. Cras eget diam vel est blandit tempor nec a leo. Mauris risus est, fringilla in aliquam a, sagittis vel enim. Nullam sodales id erat placerat lobortis. + +% Merge Novel Document: Scene 1.3 [New] + +### Scene 1.3 + +Pellentesque nibh urna, volutpat et feugiat porta, rutrum sed lectus. Aliquam eget risus id orci tincidunt condimentum et sit amet purus. Curabitur tincidunt odio vel ante feugiat feugiat. Proin nunc lorem, molestie a sapien et, varius elementum nunc. Donec non fermentum nisl. In et massa placerat, faucibus felis eu, congue nisi. Proin sed tortor non lorem mattis cursus. Vestibulum magna neque, bibendum vel nibh et, tincidunt rhoncus nisi. Duis pulvinar mi a quam rutrum maximus. Nunc sollicitudin, urna in cursus facilisis, augue neque imperdiet metus, ac finibus lorem ante id nulla. Sed maximus eleifend justo id feugiat. Cras eget diam vel est blandit tempor nec a leo. Mauris risus est, fringilla in aliquam a, sagittis vel enim. Nullam sodales id erat placerat lobortis. + +Integer ac gravida quam. Quisque eleifend nisl nec pretium tincidunt. Quisque sollicitudin nisi in hendrerit scelerisque. Sed ornare nisl lacus, sit amet consectetur lectus egestas et. Vivamus nec arcu lorem. Donec rhoncus, purus a porta accumsan, nunc lectus iaculis libero, et fringilla tellus augue et velit. Integer varius felis scelerisque, vulputate tellus eu, laoreet justo. Suspendisse sit amet sem vehicula, auctor odio sed, aliquet enim. In ac tortor sed tortor fringilla elementum. Nulla non odio at magna vulputate scelerisque. Nam elementum diam eu rutrum scelerisque. Sed fermentum, felis quis vulputate fermentum, libero metus sollicitudin est, in faucibus purus nulla non dolor. Ut vitae felis porta, feugiat nunc et, bibendum neque. Nullam nec lorem nec metus ullamcorper malesuada ut a nisl. Etiam eget tristique dui. Nulla sed mi finibus, venenatis tellus non, maximus enim. + diff --git a/tests/reference/coreDocTools_DocMerger_0000000000014.nwd b/tests/reference/coreDocTools_DocMerger_0000000000014.nwd new file mode 100644 index 00000000..6a0d3548 --- /dev/null +++ b/tests/reference/coreDocTools_DocMerger_0000000000014.nwd @@ -0,0 +1,35 @@ +%%~name: All of Chapter 1 +%%~path: 0000000000008/0000000000014 +%%~kind: NOVEL/DOCUMENT +% Merge Novel Document: Chapter 1 [New] + +## Chapter 1 + +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc maximus justo non dictum commodo. Curabitur lacinia tempor orci vel luctus. Phasellus porta metus eu massa luctus, eget euismod risus rhoncus. Vestibulum sed arcu nisi. Maecenas pretium facilisis velit, vel semper lacus aliquam sit amet. Vestibulum vulputate neque ligula, rhoncus blandit turpis consequat id. Mauris sagittis vehicula imperdiet. Duis sed nunc pretium, ornare purus vel, sodales augue. Maecenas a suscipit risus. Quisque volutpat justo eleifend est ullamcorper fermentum. Donec ullamcorper et tortor a laoreet. Nam id risus nisi. Vivamus non imperdiet erat, sit amet imperdiet felis. Mauris vitae neque et est aliquam scelerisque non non ipsum. + +Nullam laoreet lorem nec malesuada vehicula. Vivamus tempus sodales lectus sed viverra. Aenean lacinia sollicitudin quam, quis tempus eros suscipit id. Duis sed rutrum nisi, ut pulvinar magna. Nam et cursus tortor. Phasellus ac odio tellus. Nullam in iaculis ipsum. Vivamus ante sem, ultricies sed varius quis, tristique nec tellus. Nullam eu urna vitae lacus hendrerit gravida. Quisque pulvinar erat ex, id efficitur velit sodales vitae. Proin vestibulum, sapien eget mattis euismod, tortor quam viverra risus, at congue mauris tortor eu nunc. Mauris pellentesque elit leo, quis eleifend sem placerat a. Vivamus iaculis dui eget tellus volutpat, ac varius nisi facilisis. + +% Merge Novel Document: Scene 1.1 [New] + +### Scene 1.1 + +Nullam laoreet lorem nec malesuada vehicula. Vivamus tempus sodales lectus sed viverra. Aenean lacinia sollicitudin quam, quis tempus eros suscipit id. Duis sed rutrum nisi, ut pulvinar magna. Nam et cursus tortor. Phasellus ac odio tellus. Nullam in iaculis ipsum. Vivamus ante sem, ultricies sed varius quis, tristique nec tellus. Nullam eu urna vitae lacus hendrerit gravida. Quisque pulvinar erat ex, id efficitur velit sodales vitae. Proin vestibulum, sapien eget mattis euismod, tortor quam viverra risus, at congue mauris tortor eu nunc. Mauris pellentesque elit leo, quis eleifend sem placerat a. Vivamus iaculis dui eget tellus volutpat, ac varius nisi facilisis. + +Nullam a nisl magna. Praesent commodo nec diam aliquet vestibulum. In sapien velit, sodales feugiat porta ut, rhoncus a elit. Quisque egestas nisi eu eros laoreet, quis facilisis est pretium. Nullam bibendum sed tellus nec lobortis. Duis elit massa, volutpat a lacinia a, ullamcorper in dui. Suspendisse ac laoreet dui. Curabitur elementum, tortor elementum ultricies laoreet, nunc massa vulputate augue, vitae tincidunt nunc enim eget nisl. + +% Merge Novel Document: Scene 1.2 [New] + +### Scene 1.2 + +Nullam a nisl magna. Praesent commodo nec diam aliquet vestibulum. In sapien velit, sodales feugiat porta ut, rhoncus a elit. Quisque egestas nisi eu eros laoreet, quis facilisis est pretium. Nullam bibendum sed tellus nec lobortis. Duis elit massa, volutpat a lacinia a, ullamcorper in dui. Suspendisse ac laoreet dui. Curabitur elementum, tortor elementum ultricies laoreet, nunc massa vulputate augue, vitae tincidunt nunc enim eget nisl. + +Pellentesque nibh urna, volutpat et feugiat porta, rutrum sed lectus. Aliquam eget risus id orci tincidunt condimentum et sit amet purus. Curabitur tincidunt odio vel ante feugiat feugiat. Proin nunc lorem, molestie a sapien et, varius elementum nunc. Donec non fermentum nisl. In et massa placerat, faucibus felis eu, congue nisi. Proin sed tortor non lorem mattis cursus. Vestibulum magna neque, bibendum vel nibh et, tincidunt rhoncus nisi. Duis pulvinar mi a quam rutrum maximus. Nunc sollicitudin, urna in cursus facilisis, augue neque imperdiet metus, ac finibus lorem ante id nulla. Sed maximus eleifend justo id feugiat. Cras eget diam vel est blandit tempor nec a leo. Mauris risus est, fringilla in aliquam a, sagittis vel enim. Nullam sodales id erat placerat lobortis. + +% Merge Novel Document: Scene 1.3 [New] + +### Scene 1.3 + +Pellentesque nibh urna, volutpat et feugiat porta, rutrum sed lectus. Aliquam eget risus id orci tincidunt condimentum et sit amet purus. Curabitur tincidunt odio vel ante feugiat feugiat. Proin nunc lorem, molestie a sapien et, varius elementum nunc. Donec non fermentum nisl. In et massa placerat, faucibus felis eu, congue nisi. Proin sed tortor non lorem mattis cursus. Vestibulum magna neque, bibendum vel nibh et, tincidunt rhoncus nisi. Duis pulvinar mi a quam rutrum maximus. Nunc sollicitudin, urna in cursus facilisis, augue neque imperdiet metus, ac finibus lorem ante id nulla. Sed maximus eleifend justo id feugiat. Cras eget diam vel est blandit tempor nec a leo. Mauris risus est, fringilla in aliquam a, sagittis vel enim. Nullam sodales id erat placerat lobortis. + +Integer ac gravida quam. Quisque eleifend nisl nec pretium tincidunt. Quisque sollicitudin nisi in hendrerit scelerisque. Sed ornare nisl lacus, sit amet consectetur lectus egestas et. Vivamus nec arcu lorem. Donec rhoncus, purus a porta accumsan, nunc lectus iaculis libero, et fringilla tellus augue et velit. Integer varius felis scelerisque, vulputate tellus eu, laoreet justo. Suspendisse sit amet sem vehicula, auctor odio sed, aliquet enim. In ac tortor sed tortor fringilla elementum. Nulla non odio at magna vulputate scelerisque. Nam elementum diam eu rutrum scelerisque. Sed fermentum, felis quis vulputate fermentum, libero metus sollicitudin est, in faucibus purus nulla non dolor. Ut vitae felis porta, feugiat nunc et, bibendum neque. Nullam nec lorem nec metus ullamcorper malesuada ut a nisl. Etiam eget tristique dui. Nulla sed mi finibus, venenatis tellus non, maximus enim. + diff --git a/tests/test_core/test_core_doctools.py b/tests/test_core/test_core_doctools.py new file mode 100644 index 00000000..0d93fcb9 --- /dev/null +++ b/tests/test_core/test_core_doctools.py @@ -0,0 +1,120 @@ +""" +novelWriter – Project Document Tools Tester +=========================================== + +This file is a part of novelWriter +Copyright 2018–2022, Veronica Berglyd Olsen + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +""" + +import os +import pytest + +from shutil import copyfile + +from mock import causeOSError +from tools import C, buildTestProject, cmpFiles + +from novelwriter.core.project import NWProject +from novelwriter.core.doctools import DocMerger + + +@pytest.mark.core +def testCoreDocTools_DocMerger(monkeypatch, mockGUI, fncDir, outDir, refDir, mockRnd, ipsumText): + """Test the DocMerger utility. + """ + theProject = NWProject(mockGUI) + mockRnd.reset() + buildTestProject(theProject, fncDir) + + # Create File to Merge + # ==================== + + hChapter1 = theProject.newFile("Chapter 1", C.hNovelRoot) + hSceneOne11 = theProject.newFile("Scene 1.1", hChapter1) + hSceneOne12 = theProject.newFile("Scene 1.2", hChapter1) + hSceneOne13 = theProject.newFile("Scene 1.3", hChapter1) + + docText1 = "\n\n".join(ipsumText[0:2]) + "\n\n" + docText2 = "\n\n".join(ipsumText[1:3]) + "\n\n" + docText3 = "\n\n".join(ipsumText[2:4]) + "\n\n" + docText4 = "\n\n".join(ipsumText[3:5]) + "\n\n" + + theProject.writeNewFile(hChapter1, 2, True, docText1) + theProject.writeNewFile(hSceneOne11, 3, True, docText2) + theProject.writeNewFile(hSceneOne12, 3, True, docText3) + theProject.writeNewFile(hSceneOne13, 3, True, docText4) + + # Basic Checks + # ============ + + docMerger = DocMerger(theProject) + + # No writing without a target set + assert docMerger.writeTargetDoc() is False + + # Cannot append invalid handle + assert docMerger.appendText(C.hInvalid, True, "Merge") is False + + # Cannot create new target from invalid handle + assert docMerger.newTargetDoc(C.hInvalid, "Test") is None + + # Merge to New + # ============ + + saveFile = os.path.join(fncDir, "content", "0000000000014.nwd") + testFile = os.path.join(outDir, "coreDocTools_DocMerger_0000000000014.nwd") + compFile = os.path.join(refDir, "coreDocTools_DocMerger_0000000000014.nwd") + + assert docMerger.newTargetDoc(hChapter1, "All of Chapter 1") == "0000000000014" + + assert docMerger.appendText(hChapter1, True, "Merge") is True + assert docMerger.appendText(hSceneOne11, True, "Merge") is True + assert docMerger.appendText(hSceneOne12, True, "Merge") is True + assert docMerger.appendText(hSceneOne13, True, "Merge") is True + + # Block writing and check error handling + with monkeypatch.context() as mp: + mp.setattr("builtins.open", causeOSError) + assert docMerger.writeTargetDoc() is False + assert not os.path.isfile(saveFile) + assert docMerger.getError() != "" + + # Write properly, and compare + assert docMerger.writeTargetDoc() is True + copyfile(saveFile, testFile) + assert cmpFiles(testFile, compFile) + + # Merge into Existing + # =================== + + saveFile = os.path.join(fncDir, "content", "0000000000010.nwd") + testFile = os.path.join(outDir, "coreDocTools_DocMerger_0000000000010.nwd") + compFile = os.path.join(refDir, "coreDocTools_DocMerger_0000000000010.nwd") + + docMerger.setTargetDoc(hChapter1) + + assert docMerger.appendText(hSceneOne11, True, "Merge") is True + assert docMerger.appendText(hSceneOne12, True, "Merge") is True + assert docMerger.appendText(hSceneOne13, True, "Merge") is True + + assert docMerger.writeTargetDoc() is True + copyfile(saveFile, testFile) + assert cmpFiles(testFile, compFile) + + # Just for debugging + docMerger.writeTargetDoc() + +# END Test testCoreDocTools_DocMerger diff --git a/tests/test_core/test_core_project.py b/tests/test_core/test_core_project.py index 3be812e1..5ea25676 100644 --- a/tests/test_core/test_core_project.py +++ b/tests/test_core/test_core_project.py @@ -22,9 +22,9 @@ along with this program. If not, see . import os import pytest +from lxml import etree from shutil import copyfile from zipfile import ZipFile -from lxml import etree from tools import cmpFiles, writeFile, readFile, buildTestProject, XML_IGNORE from mock import causeOSError