예제 #1
0
def testCoreProject_NewFile(fncDir, outDir, refDir, mockGUI):
    """Check that new files can be added to the project.
    """
    projFile = os.path.join(fncDir, "nwProject.nwx")
    testFile = os.path.join(outDir, "coreProject_NewFile_nwProject.nwx")
    compFile = os.path.join(refDir, "coreProject_NewFile_nwProject.nwx")

    theProject = NWProject(mockGUI)
    theProject.projTree.setSeed(42)

    assert theProject.newProject({"projPath": fncDir}) is True
    assert theProject.setProjectPath(fncDir) is True
    assert theProject.saveProject() is True
    assert theProject.closeProject() is True
    assert theProject.openProject(projFile) is True

    assert isinstance(
        theProject.newFile("Hello", nwItemClass.NOVEL, "31489056e0916"), str)
    assert isinstance(
        theProject.newFile("Jane", nwItemClass.CHARACTER, "71ee45a3c0db9"),
        str)
    assert theProject.projChanged
    assert theProject.saveProject() is True
    assert theProject.closeProject() is True

    copyfile(projFile, testFile)
    assert cmpFiles(testFile, compFile, [2, 6, 7, 8])
    assert theProject.projChanged is False
예제 #2
0
def testCoreProject_NewSampleB(monkeypatch, fncDir, tmpConf, mockGUI, tmpDir):
    """Check that we can create a new project can be created from the
    provided sample project folder.
    """
    projData = {
        "projName": "Test Sample",
        "projTitle": "Test Novel",
        "projAuthors": "Jane Doe\nJohn Doh\n",
        "projPath": fncDir,
        "popSample": True,
        "popMinimal": False,
        "popCustom": False,
    }
    theProject = NWProject(mockGUI)
    theProject.projTree.setSeed(42)

    # Make sure we do not pick up the novelwriter/assets/sample.zip file
    tmpConf.assetPath = tmpDir

    # Set a fake project file name
    monkeypatch.setattr(nwFiles, "PROJ_FILE", "nothing.nwx")
    assert not theProject.newProject(projData)

    monkeypatch.setattr(nwFiles, "PROJ_FILE", "nwProject.nwx")
    assert theProject.newProject(projData) is True
    assert theProject.openProject(fncDir) is True
    assert theProject.projName == "Sample Project"
    assert theProject.saveProject() is True
    assert theProject.closeProject() is True

    # Misdirect the appRoot path so neither is possible
    tmpConf.appRoot = tmpDir
    assert not theProject.newProject(projData)
예제 #3
0
def testCoreProject_NewMinimal(fncDir, outDir, refDir, mockGUI):
    """Create a new project from a project wizard dictionary. With
    default setting, creating a Minimal project.
    """
    projFile = os.path.join(fncDir, "nwProject.nwx")
    testFile = os.path.join(outDir, "coreProject_NewMinimal_nwProject.nwx")
    compFile = os.path.join(refDir, "coreProject_NewMinimal_nwProject.nwx")

    theProject = NWProject(mockGUI)
    theProject.projTree.setSeed(42)

    # Setting no data should fail
    assert theProject.newProject({}) is False

    # Wrong type should also fail
    assert theProject.newProject("stuff") is False

    # Try again with a proper path
    assert theProject.newProject({"projPath": fncDir}) is True
    assert theProject.saveProject() is True
    assert theProject.closeProject() is True

    # Creating the project once more should fail
    assert theProject.newProject({"projPath": fncDir}) is False

    # Open again
    assert theProject.openProject(projFile) is True

    # Save and close
    assert theProject.saveProject() is True
    assert theProject.closeProject() is True
    copyfile(projFile, testFile)
    assert cmpFiles(testFile, compFile, [2, 6, 7, 8])
    assert theProject.projChanged is False

    # Open a second time
    assert theProject.openProject(projFile) is True
    assert theProject.openProject(projFile) is False
    assert theProject.openProject(projFile, overrideLock=True) is True
    assert theProject.saveProject() is True
    assert theProject.closeProject() is True
    copyfile(projFile, testFile)
    assert cmpFiles(testFile, compFile, [2, 6, 7, 8])
예제 #4
0
def testCoreProject_Save(monkeypatch, nwMinimal, mockGUI, refDir):
    """Test saving a project.
    """
    theProject = NWProject(mockGUI)
    testFile = os.path.join(nwMinimal, "nwProject.nwx")
    backFile = os.path.join(nwMinimal, "nwProject.bak")
    compFile = os.path.join(refDir, os.path.pardir, "minimal", "nwProject.nwx")

    # Nothing to save
    assert theProject.saveProject() is False

    # Open test project
    assert theProject.openProject(nwMinimal)

    # Fail on folder structure check
    with monkeypatch.context() as mp:
        mp.setattr("os.path.isdir", lambda *a: False)
        assert theProject.saveProject() is False

    # Fail on open file
    with monkeypatch.context() as mp:
        mp.setattr("builtins.open", causeOSError)
        assert theProject.saveProject() is False

    # Fail on creating .bak file
    with monkeypatch.context() as mp:
        mp.setattr("os.replace", causeOSError)
        assert theProject.saveProject() is False
        assert os.path.isfile(backFile) is False

    # Successful save
    saveCount = theProject.saveCount
    autoCount = theProject.autoCount
    assert theProject.saveProject() is True
    assert theProject.saveCount == saveCount + 1
    assert theProject.autoCount == autoCount
    assert cmpFiles(testFile, compFile, [2, 6, 7, 8, 9])

    # Check that a second save creates a .bak file
    assert os.path.isfile(backFile) is True

    # Successful autosave
    saveCount = theProject.saveCount
    autoCount = theProject.autoCount
    assert theProject.saveProject(autoSave=True) is True
    assert theProject.saveCount == saveCount
    assert theProject.autoCount == autoCount + 1
    assert cmpFiles(testFile, compFile, [2, 6, 7, 8, 9])

    # Close test project
    assert theProject.closeProject()
예제 #5
0
def testCoreProject_NewRoot(fncDir, outDir, refDir, mockGUI):
    """Check that new root folders can be added to the project.
    """
    projFile = os.path.join(fncDir, "nwProject.nwx")
    testFile = os.path.join(outDir, "coreProject_NewRoot_nwProject.nwx")
    compFile = os.path.join(refDir, "coreProject_NewRoot_nwProject.nwx")

    theProject = NWProject(mockGUI)
    theProject.projTree.setSeed(42)

    assert theProject.newProject({"projPath": fncDir}) is True
    assert theProject.setProjectPath(fncDir) is True
    assert theProject.saveProject() is True
    assert theProject.closeProject() is True
    assert theProject.openProject(projFile) is True

    assert isinstance(theProject.newRoot("Novel", nwItemClass.NOVEL),
                      type(None))
    assert isinstance(theProject.newRoot("Plot", nwItemClass.PLOT), type(None))
    assert isinstance(theProject.newRoot("Character", nwItemClass.CHARACTER),
                      type(None))
    assert isinstance(theProject.newRoot("World", nwItemClass.WORLD),
                      type(None))
    assert isinstance(theProject.newRoot("Timeline", nwItemClass.TIMELINE),
                      str)
    assert isinstance(theProject.newRoot("Object", nwItemClass.OBJECT), str)
    assert isinstance(theProject.newRoot("Custom1", nwItemClass.CUSTOM), str)
    assert isinstance(theProject.newRoot("Custom2", nwItemClass.CUSTOM), str)

    assert theProject.projChanged is True
    assert theProject.saveProject() is True
    assert theProject.closeProject() is True

    copyfile(projFile, testFile)
    assert cmpFiles(testFile, compFile, [2, 6, 7, 8])
    assert theProject.projChanged is False
예제 #6
0
def testCoreProject_NewCustomA(fncDir, outDir, refDir, mockGUI):
    """Create a new project from a project wizard dictionary.
    Custom type with chapters and scenes.
    """
    projFile = os.path.join(fncDir, "nwProject.nwx")
    testFile = os.path.join(outDir, "coreProject_NewCustomA_nwProject.nwx")
    compFile = os.path.join(refDir, "coreProject_NewCustomA_nwProject.nwx")

    projData = {
        "projName":
        "Test Custom",
        "projTitle":
        "Test Novel",
        "projAuthors":
        "Jane Doe\nJohn Doh\n",
        "projPath":
        fncDir,
        "popSample":
        False,
        "popMinimal":
        False,
        "popCustom":
        True,
        "addRoots": [
            nwItemClass.PLOT,
            nwItemClass.CHARACTER,
            nwItemClass.WORLD,
            nwItemClass.TIMELINE,
            nwItemClass.OBJECT,
            nwItemClass.ENTITY,
        ],
        "numChapters":
        3,
        "numScenes":
        3,
        "chFolders":
        True,
    }
    theProject = NWProject(mockGUI)
    theProject.projTree.setSeed(42)

    assert theProject.newProject(projData) is True
    assert theProject.saveProject() is True
    assert theProject.closeProject() is True

    copyfile(projFile, testFile)
    assert cmpFiles(testFile, compFile, [2, 6, 7, 8])
예제 #7
0
def testCoreIndex_ScanThis(nwMinimal, mockGUI):
    """Test the tag scanner function scanThis.
    """
    theProject = NWProject(mockGUI)
    theProject.projTree.setSeed(42)
    assert theProject.openProject(nwMinimal) is True

    theIndex = NWIndex(theProject)

    isValid, theBits, thePos = theIndex.scanThis("tag: this, and this")
    assert isValid is False

    isValid, theBits, thePos = theIndex.scanThis("@")
    assert isValid is False

    isValid, theBits, thePos = theIndex.scanThis("@:")
    assert isValid is False

    isValid, theBits, thePos = theIndex.scanThis(" @a: b")
    assert isValid is False

    isValid, theBits, thePos = theIndex.scanThis("@a:")
    assert isValid is True
    assert theBits == ["@a"]
    assert thePos == [0]

    isValid, theBits, thePos = theIndex.scanThis("@a:b")
    assert isValid is True
    assert theBits == ["@a", "b"]
    assert thePos == [0, 3]

    isValid, theBits, thePos = theIndex.scanThis("@a:b,c,d")
    assert isValid is True
    assert theBits == ["@a", "b", "c", "d"]
    assert thePos == [0, 3, 5, 7]

    isValid, theBits, thePos = theIndex.scanThis("@a : b , c , d")
    assert isValid is True
    assert theBits == ["@a", "b", "c", "d"]
    assert thePos == [0, 5, 9, 13]

    isValid, theBits, thePos = theIndex.scanThis("@tag: this, and this")
    assert isValid is True
    assert theBits == ["@tag", "this", "and this"]
    assert thePos == [0, 6, 12]

    assert theProject.closeProject() is True
예제 #8
0
def testCoreProject_NewSampleA(fncDir, tmpConf, mockGUI, tmpDir):
    """Check that we can create a new project can be created from the
    provided sample project via a zip file.
    """
    projData = {
        "projName": "Test Sample",
        "projTitle": "Test Novel",
        "projAuthors": "Jane Doe\nJohn Doh\n",
        "projPath": fncDir,
        "popSample": True,
        "popMinimal": False,
        "popCustom": False,
    }
    theProject = NWProject(mockGUI)
    theProject.projTree.setSeed(42)

    # Sample set, but no path
    assert not theProject.newProject({"popSample": True})

    # Force the lookup path for assets to our temp folder
    srcSample = os.path.abspath(os.path.join(tmpConf.appRoot, "sample"))
    dstSample = os.path.join(tmpDir, "sample.zip")
    tmpConf.assetPath = tmpDir

    # Create and open a defective zip file
    with open(dstSample, mode="w+") as outFile:
        outFile.write("foo")

    assert not theProject.newProject(projData)
    os.unlink(dstSample)

    # Create a real zip file, and unpack it
    with ZipFile(dstSample, "w") as zipObj:
        zipObj.write(os.path.join(srcSample, "nwProject.nwx"), "nwProject.nwx")
        for docFile in os.listdir(os.path.join(srcSample, "content")):
            srcDoc = os.path.join(srcSample, "content", docFile)
            zipObj.write(srcDoc, "content/" + docFile)

    assert theProject.newProject(projData) is True
    assert theProject.openProject(fncDir) is True
    assert theProject.projName == "Sample Project"
    assert theProject.saveProject() is True
    assert theProject.closeProject() is True
    os.unlink(dstSample)
예제 #9
0
def testCoreIndex_ScanThis(mockGUI):
    """Test the tag scanner function scanThis.
    """
    theProject = NWProject(mockGUI)
    theIndex = theProject.index

    isValid, theBits, thePos = theIndex.scanThis("tag: this, and this")
    assert isValid is False

    isValid, theBits, thePos = theIndex.scanThis("@")
    assert isValid is False

    isValid, theBits, thePos = theIndex.scanThis("@:")
    assert isValid is False

    isValid, theBits, thePos = theIndex.scanThis(" @a: b")
    assert isValid is False

    isValid, theBits, thePos = theIndex.scanThis("@a:")
    assert isValid is True
    assert theBits == ["@a"]
    assert thePos == [0]

    isValid, theBits, thePos = theIndex.scanThis("@a:b")
    assert isValid is True
    assert theBits == ["@a", "b"]
    assert thePos == [0, 3]

    isValid, theBits, thePos = theIndex.scanThis("@a:b,c,d")
    assert isValid is True
    assert theBits == ["@a", "b", "c", "d"]
    assert thePos == [0, 3, 5, 7]

    isValid, theBits, thePos = theIndex.scanThis("@a : b , c , d")
    assert isValid is True
    assert theBits == ["@a", "b", "c", "d"]
    assert thePos == [0, 5, 9, 13]

    isValid, theBits, thePos = theIndex.scanThis("@tag: this, and this")
    assert isValid is True
    assert theBits == ["@tag", "this", "and this"]
    assert thePos == [0, 6, 12]

    assert theProject.closeProject() is True
예제 #10
0
def testCoreIndex_CheckThese(nwMinimal, mockGUI):
    """Test the tag checker function checkThese.
    """
    theProject = NWProject(mockGUI)
    theProject.projTree.setSeed(42)
    assert theProject.openProject(nwMinimal) is True

    theIndex = NWIndex(theProject)
    nHandle = theProject.newFile("Hello", nwItemClass.NOVEL, "a508bb932959c")
    cHandle = theProject.newFile("Jane", nwItemClass.CHARACTER,
                                 "afb3043c7b2b3")
    nItem = theProject.projTree[nHandle]
    cItem = theProject.projTree[cHandle]

    assert theIndex.novelChangedSince(0) is False
    assert theIndex.notesChangedSince(0) is False
    assert theIndex.indexChangedSince(0) is False

    assert theIndex.scanText(cHandle, ("# Jane Smith\n"
                                       "@tag: Jane\n"
                                       "@tag:\n"
                                       "@:\n"))
    assert theIndex.scanText(
        nHandle,
        (
            "# Hello World!\n"
            "@pov: Jane\n"
            "@invalid: John\n"  # Checks for issue #688
        ))
    assert theIndex._tagIndex == {"Jane": [2, cHandle, "CHARACTER", "T000001"]}
    assert theIndex.getNovelData(nHandle, "T000001")["title"] == "Hello World!"
    assert theIndex.getReferences(nHandle, "T000001") == {
        "@char": [],
        "@custom": [],
        "@entity": [],
        "@focus": [],
        "@location": [],
        "@object": [],
        "@plot": [],
        "@pov": ["Jane"],
        "@time": []
    }

    assert theIndex.novelChangedSince(0) is True
    assert theIndex.notesChangedSince(0) is True
    assert theIndex.indexChangedSince(0) is True

    assert theIndex.getHandleHeaderLevel(cHandle) == "H1"
    assert theIndex.getHandleHeaderLevel(nHandle) == "H1"

    # Zero Items
    assert theIndex.checkThese([], cItem) == []

    # One Item
    assert theIndex.checkThese(["@tag"], cItem) == [True]
    assert theIndex.checkThese(["@who"], cItem) == [False]

    # Two Items
    assert theIndex.checkThese(["@tag", "Jane"], cItem) == [True, True]
    assert theIndex.checkThese(["@tag", "John"], cItem) == [True, True]
    assert theIndex.checkThese(["@tag", "Jane"], nItem) == [True, False]
    assert theIndex.checkThese(["@tag", "John"], nItem) == [True, True]
    assert theIndex.checkThese(["@pov", "John"], nItem) == [True, False]
    assert theIndex.checkThese(["@pov", "Jane"], nItem) == [True, True]
    assert theIndex.checkThese(["@ pov", "Jane"], nItem) == [False, False]
    assert theIndex.checkThese(["@what", "Jane"], nItem) == [False, False]

    # Three Items
    assert theIndex.checkThese(["@tag", "Jane", "John"],
                               cItem) == [True, True, False]
    assert theIndex.checkThese(["@who", "Jane", "John"],
                               cItem) == [False, False, False]
    assert theIndex.checkThese(["@pov", "Jane", "John"],
                               nItem) == [True, True, False]

    assert theProject.closeProject() is True
예제 #11
0
def testCoreIndex_ScanText(nwMinimal, mockGUI):
    """Check the index text scanner.
    """
    theProject = NWProject(mockGUI)
    theProject.projTree.setSeed(42)
    assert theProject.openProject(nwMinimal) is True

    theIndex = NWIndex(theProject)

    # Some items for fail to scan tests
    dHandle = theProject.newFolder("Folder", nwItemClass.NOVEL,
                                   "a508bb932959c")
    xHandle = theProject.newFile("No Layout", nwItemClass.NOVEL,
                                 "a508bb932959c")
    xItem = theProject.projTree[xHandle]
    xItem.setLayout(nwItemLayout.NO_LAYOUT)

    # Check invalid data
    assert theIndex.scanText(None, "Hello World!") is False
    assert theIndex.scanText(dHandle, "Hello World!") is False
    assert theIndex.scanText(xHandle, "Hello World!") is False

    xItem.setLayout(nwItemLayout.DOCUMENT)
    xItem.setParent(None)
    assert theIndex.scanText(xHandle, "Hello World!") is False

    # Create the trash folder
    tHandle = theProject.trashFolder()
    assert theProject.projTree[tHandle] is not None
    xItem.setParent(tHandle)
    assert theIndex.scanText(xHandle, "Hello World!") is False

    # Create the archive root
    aHandle = theProject.newRoot("Archive", nwItemClass.ARCHIVE)
    assert theProject.projTree[aHandle] is not None
    xItem.setParent(aHandle)
    assert theIndex.scanText(xHandle, "Hello World!") is False

    # Make some usable items
    tHandle = theProject.newFile("Title", nwItemClass.NOVEL, "a508bb932959c")
    pHandle = theProject.newFile("Page", nwItemClass.NOVEL, "a508bb932959c")
    nHandle = theProject.newFile("Hello", nwItemClass.NOVEL, "a508bb932959c")
    cHandle = theProject.newFile("Jane", nwItemClass.CHARACTER,
                                 "afb3043c7b2b3")
    sHandle = theProject.newFile("Scene", nwItemClass.NOVEL, "a508bb932959c")

    # Text Indexing
    # =============

    # Index correct text
    assert theIndex.scanText(cHandle, ("# Jane Smith\n" "@tag: Jane\n"))
    assert theIndex.scanText(nHandle, ("# Hello World!\n"
                                       "@pov: Jane\n"
                                       "@char: Jane\n\n"
                                       "% this is a comment\n\n"
                                       "This is a story about Jane Smith.\n\n"
                                       "Well, not really.\n"))
    assert theIndex._tagIndex == {"Jane": [2, cHandle, "CHARACTER", "T000001"]}
    assert theIndex.getNovelData(nHandle, "T000001")["title"] == "Hello World!"

    # Title Indexing
    # ==============

    # Document File
    assert theIndex.scanText(
        nHandle,
        (
            "# Title One\n\n"
            "% synopsis: Synopsis One.\n\n"
            "Paragraph One.\n\n"
            "## Title Two\n\n"
            "% synopsis: Synopsis Two.\n\n"
            "Paragraph Two.\n\n"
            "### Title Three\n\n"
            "% synopsis: Synopsis Three.\n\n"
            "Paragraph Three.\n\n"
            "#### Title Four\n\n"
            "% synopsis: Synopsis Four.\n\n"
            "Paragraph Four.\n\n"
            "##### Title Five\n\n"  # Not interpreted as a title, the hashes are counted as a word
            "Paragraph Five.\n\n"))
    assert nHandle not in theIndex._refIndex

    assert theIndex._fileIndex[nHandle]["T000001"]["level"] == "H1"
    assert theIndex._fileIndex[nHandle]["T000007"]["level"] == "H2"
    assert theIndex._fileIndex[nHandle]["T000013"]["level"] == "H3"
    assert theIndex._fileIndex[nHandle]["T000019"]["level"] == "H4"

    assert theIndex._fileIndex[nHandle]["T000001"]["title"] == "Title One"
    assert theIndex._fileIndex[nHandle]["T000007"]["title"] == "Title Two"
    assert theIndex._fileIndex[nHandle]["T000013"]["title"] == "Title Three"
    assert theIndex._fileIndex[nHandle]["T000019"]["title"] == "Title Four"

    assert theIndex._fileIndex[nHandle]["T000001"]["layout"] == "DOCUMENT"
    assert theIndex._fileIndex[nHandle]["T000007"]["layout"] == "DOCUMENT"
    assert theIndex._fileIndex[nHandle]["T000013"]["layout"] == "DOCUMENT"
    assert theIndex._fileIndex[nHandle]["T000019"]["layout"] == "DOCUMENT"

    assert theIndex._fileIndex[nHandle]["T000001"]["cCount"] == 23
    assert theIndex._fileIndex[nHandle]["T000007"]["cCount"] == 23
    assert theIndex._fileIndex[nHandle]["T000013"]["cCount"] == 27
    assert theIndex._fileIndex[nHandle]["T000019"]["cCount"] == 56

    assert theIndex._fileIndex[nHandle]["T000001"]["wCount"] == 4
    assert theIndex._fileIndex[nHandle]["T000007"]["wCount"] == 4
    assert theIndex._fileIndex[nHandle]["T000013"]["wCount"] == 4
    assert theIndex._fileIndex[nHandle]["T000019"]["wCount"] == 9

    assert theIndex._fileIndex[nHandle]["T000001"]["pCount"] == 1
    assert theIndex._fileIndex[nHandle]["T000007"]["pCount"] == 1
    assert theIndex._fileIndex[nHandle]["T000013"]["pCount"] == 1
    assert theIndex._fileIndex[nHandle]["T000019"]["pCount"] == 3

    assert theIndex._fileIndex[nHandle]["T000001"][
        "synopsis"] == "Synopsis One."
    assert theIndex._fileIndex[nHandle]["T000007"][
        "synopsis"] == "Synopsis Two."
    assert theIndex._fileIndex[nHandle]["T000013"][
        "synopsis"] == "Synopsis Three."
    assert theIndex._fileIndex[nHandle]["T000019"][
        "synopsis"] == "Synopsis Four."

    # Note File
    assert theIndex.scanText(cHandle, ("# Title One\n\n"
                                       "@tag: One\n\n"
                                       "% synopsis: Synopsis One.\n\n"
                                       "Paragraph One.\n\n"))
    assert cHandle not in theIndex._refIndex

    assert theIndex._fileIndex[cHandle]["T000001"]["level"] == "H1"
    assert theIndex._fileIndex[cHandle]["T000001"]["title"] == "Title One"
    assert theIndex._fileIndex[cHandle]["T000001"]["layout"] == "NOTE"
    assert theIndex._fileIndex[cHandle]["T000001"]["cCount"] == 23
    assert theIndex._fileIndex[cHandle]["T000001"]["wCount"] == 4
    assert theIndex._fileIndex[cHandle]["T000001"]["pCount"] == 1
    assert theIndex._fileIndex[cHandle]["T000001"][
        "synopsis"] == "Synopsis One."

    # Valid and Invalid References
    assert theIndex.scanText(
        sHandle,
        (
            "# Title One\n\n"
            "@pov: One\n\n"  # Valid
            "@char: Two\n\n"  # Invalid tag
            "@:\n\n"  # Invalid line
            "% synopsis: Synopsis One.\n\n"
            "Paragraph One.\n\n"))
    assert theIndex._refIndex[sHandle]["T000001"] == ([[3, "@pov", "One"],
                                                       [5, "@char", "Two"]])

    # Special Titles
    # ==============

    assert theIndex.scanText(tHandle, ("#! My Project\n\n"
                                       ">> By Jane Doe <<\n\n"))
    assert tHandle not in theIndex._refIndex

    assert theIndex._fileIndex[tHandle]["T000001"]["level"] == "H1"
    assert theIndex._fileIndex[tHandle]["T000001"]["title"] == "My Project"
    assert theIndex._fileIndex[tHandle]["T000001"]["layout"] == "DOCUMENT"
    assert theIndex._fileIndex[tHandle]["T000001"]["cCount"] == 21
    assert theIndex._fileIndex[tHandle]["T000001"]["wCount"] == 5
    assert theIndex._fileIndex[tHandle]["T000001"]["pCount"] == 1
    assert theIndex._fileIndex[tHandle]["T000001"]["synopsis"] == ""

    assert theIndex.scanText(tHandle,
                             ("##! Prologue\n\n"
                              "In the beginning there was time ...\n\n"))
    assert tHandle not in theIndex._refIndex

    assert theIndex._fileIndex[tHandle]["T000001"]["level"] == "H2"
    assert theIndex._fileIndex[tHandle]["T000001"]["title"] == "Prologue"
    assert theIndex._fileIndex[tHandle]["T000001"]["layout"] == "DOCUMENT"
    assert theIndex._fileIndex[tHandle]["T000001"]["cCount"] == 43
    assert theIndex._fileIndex[tHandle]["T000001"]["wCount"] == 8
    assert theIndex._fileIndex[tHandle]["T000001"]["pCount"] == 1
    assert theIndex._fileIndex[tHandle]["T000001"]["synopsis"] == ""

    # Page wo/Title
    # =============

    theProject.projTree[pHandle]._layout = nwItemLayout.DOCUMENT
    assert theIndex.scanText(pHandle,
                             ("This is a page with some text on it.\n\n"))
    assert pHandle in theIndex._fileIndex
    assert theIndex._fileIndex[pHandle]["T000000"]["level"] == "H0"
    assert theIndex._fileIndex[pHandle]["T000000"]["title"] == ""
    assert theIndex._fileIndex[pHandle]["T000000"]["layout"] == "DOCUMENT"
    assert theIndex._fileIndex[pHandle]["T000000"]["cCount"] == 36
    assert theIndex._fileIndex[pHandle]["T000000"]["wCount"] == 9
    assert theIndex._fileIndex[pHandle]["T000000"]["pCount"] == 1
    assert theIndex._fileIndex[pHandle]["T000000"]["synopsis"] == ""

    theProject.projTree[pHandle]._layout = nwItemLayout.NOTE
    assert theIndex.scanText(pHandle,
                             ("This is a page with some text on it.\n\n"))
    assert pHandle in theIndex._fileIndex
    assert theIndex._fileIndex[pHandle]["T000000"]["level"] == "H0"
    assert theIndex._fileIndex[pHandle]["T000000"]["title"] == ""
    assert theIndex._fileIndex[pHandle]["T000000"]["layout"] == "NOTE"
    assert theIndex._fileIndex[pHandle]["T000000"]["cCount"] == 36
    assert theIndex._fileIndex[pHandle]["T000000"]["wCount"] == 9
    assert theIndex._fileIndex[pHandle]["T000000"]["pCount"] == 1
    assert theIndex._fileIndex[pHandle]["T000000"]["synopsis"] == ""

    assert theProject.closeProject() is True
예제 #12
0
def testCoreProject_OrphanedFiles(mockGUI, nwLipsum):
    """Check that files in the content folder that are not tracked in
    the project XML file are handled correctly by the orphaned files
    function. It should also restore as much meta data as possible from
    the meta line at the top of the document file.
    """
    theProject = NWProject(mockGUI)

    assert theProject.openProject(nwLipsum)
    assert theProject.projTree["636b6aa9b697b"] is None
    assert theProject.closeProject()

    # First Item with Meta Data
    orphPath = os.path.join(nwLipsum, "content", "636b6aa9b697b.nwd")
    writeFile(orphPath, ("%%~name:[Recovered] Mars\n"
                         "%%~path:5eaea4e8cdee8/636b6aa9b697b\n"
                         "%%~kind:WORLD/NOTE\n"
                         "%%~invalid\n"
                         "\n"))

    # Second Item without Meta Data
    orphPath = os.path.join(nwLipsum, "content", "736b6aa9b697b.nwd")
    writeFile(orphPath, "\n")

    # Invalid File Name
    tstPath = os.path.join(nwLipsum, "content", "636b6aa9b697b.txt")
    writeFile(tstPath, "\n")

    # Invalid File Name
    tstPath = os.path.join(nwLipsum, "content", "636b6aa9b697bb.nwd")
    writeFile(tstPath, "\n")

    # Invalid File Name
    tstPath = os.path.join(nwLipsum, "content", "abcdefghijklm.nwd")
    writeFile(tstPath, "\n")

    assert theProject.openProject(nwLipsum)
    assert theProject.projPath is not None
    assert theProject.projTree["636b6aa9b697bb"] is None
    assert theProject.projTree["abcdefghijklm"] is None

    # First Item with Meta Data
    oItem = theProject.projTree["636b6aa9b697b"]
    assert oItem is not None
    assert oItem.itemName == "[Recovered] Mars"
    assert oItem.itemHandle == "636b6aa9b697b"
    assert oItem.itemParent == "60bdf227455cc"
    assert oItem.itemClass == nwItemClass.WORLD
    assert oItem.itemType == nwItemType.FILE
    assert oItem.itemLayout == nwItemLayout.NOTE

    # Second Item without Meta Data
    oItem = theProject.projTree["736b6aa9b697b"]
    assert oItem is not None
    assert oItem.itemName == "Recovered File 1"
    assert oItem.itemHandle == "736b6aa9b697b"
    assert oItem.itemParent == "b3643d0f92e32"
    assert oItem.itemClass == nwItemClass.NOVEL
    assert oItem.itemType == nwItemType.FILE
    assert oItem.itemLayout == nwItemLayout.NOTE

    assert theProject.saveProject(nwLipsum)
    assert theProject.closeProject()

    # Finally, check that the orphaned files function returns
    # if no project is open and no path is set
    assert not theProject._scanProjectFolder()
예제 #13
0
def testCoreIndex_ScanText(mockGUI, fncDir, mockRnd):
    """Check the index text scanner.
    """
    theProject = NWProject(mockGUI)
    buildTestProject(theProject, fncDir)
    theIndex = theProject.index

    # Some items for fail to scan tests
    dHandle = theProject.newFolder("Folder", "0000000000010")
    xHandle = theProject.newFile("No Layout", "0000000000010")
    xItem = theProject.tree[xHandle]
    xItem.setLayout(nwItemLayout.NO_LAYOUT)

    # Check invalid data
    assert theIndex.scanText(None, "Hello World!") is False
    assert theIndex.scanText(dHandle, "Hello World!") is False
    assert theIndex.scanText(xHandle, "Hello World!") is False

    xItem.setLayout(nwItemLayout.DOCUMENT)
    xItem.setParent(None)
    assert theIndex.scanText(xHandle, "Hello World!") is False

    # Create the trash folder
    tHandle = theProject.trashFolder()
    assert theProject.tree[tHandle] is not None
    xItem.setParent(tHandle)
    theProject.tree.updateItemData(xItem.itemHandle)
    assert xItem.itemRoot == tHandle
    assert xItem.itemClass == nwItemClass.TRASH
    assert theIndex.scanText(xHandle, "Hello World!") is False

    # Create the archive root
    aHandle = theProject.newRoot(nwItemClass.ARCHIVE)
    assert theProject.tree[aHandle] is not None
    xItem.setParent(aHandle)
    theProject.tree.updateItemData(xItem.itemHandle)
    assert theIndex.scanText(xHandle, "Hello World!") is False

    # Make some usable items
    tHandle = theProject.newFile("Title", "0000000000010")
    pHandle = theProject.newFile("Page", "0000000000010")
    nHandle = theProject.newFile("Hello", "0000000000010")
    cHandle = theProject.newFile("Jane", "0000000000012")
    sHandle = theProject.newFile("Scene", "0000000000010")

    # Text Indexing
    # =============

    # Index correct text
    assert theIndex.scanText(cHandle, ("# Jane Smith\n" "@tag: Jane\n"))
    assert theIndex.scanText(nHandle, ("# Hello World!\n"
                                       "@pov: Jane\n"
                                       "@char: Jane\n\n"
                                       "% this is a comment\n\n"
                                       "This is a story about Jane Smith.\n\n"
                                       "Well, not really.\n"))
    assert theIndex._tagsIndex.tagHandle("Jane") == cHandle
    assert theIndex._tagsIndex.tagHeading("Jane") == "T000001"
    assert theIndex._tagsIndex.tagClass("Jane") == "CHARACTER"
    assert theIndex.getNovelData(nHandle, "T000001").title == "Hello World!"

    # Title Indexing
    # ==============

    # Document File
    assert theIndex.scanText(
        nHandle,
        (
            "# Title One\n\n"
            "% synopsis: Synopsis One.\n\n"
            "Paragraph One.\n\n"
            "## Title Two\n\n"
            "% synopsis: Synopsis Two.\n\n"
            "Paragraph Two.\n\n"
            "### Title Three\n\n"
            "% synopsis: Synopsis Three.\n\n"
            "Paragraph Three.\n\n"
            "#### Title Four\n\n"
            "% synopsis: Synopsis Four.\n\n"
            "Paragraph Four.\n\n"
            "##### Title Five\n\n"  # Not interpreted as a title, the hashes are counted as a word
            "Paragraph Five.\n\n"))
    assert theIndex._itemIndex[nHandle]["T000001"].references == {}
    assert theIndex._itemIndex[nHandle]["T000007"].references == {}
    assert theIndex._itemIndex[nHandle]["T000013"].references == {}
    assert theIndex._itemIndex[nHandle]["T000019"].references == {}

    assert theIndex._itemIndex[nHandle]["T000001"].level == "H1"
    assert theIndex._itemIndex[nHandle]["T000007"].level == "H2"
    assert theIndex._itemIndex[nHandle]["T000013"].level == "H3"
    assert theIndex._itemIndex[nHandle]["T000019"].level == "H4"

    assert theIndex._itemIndex[nHandle]["T000001"].title == "Title One"
    assert theIndex._itemIndex[nHandle]["T000007"].title == "Title Two"
    assert theIndex._itemIndex[nHandle]["T000013"].title == "Title Three"
    assert theIndex._itemIndex[nHandle]["T000019"].title == "Title Four"

    assert theIndex._itemIndex[nHandle]["T000001"].charCount == 23
    assert theIndex._itemIndex[nHandle]["T000007"].charCount == 23
    assert theIndex._itemIndex[nHandle]["T000013"].charCount == 27
    assert theIndex._itemIndex[nHandle]["T000019"].charCount == 56

    assert theIndex._itemIndex[nHandle]["T000001"].wordCount == 4
    assert theIndex._itemIndex[nHandle]["T000007"].wordCount == 4
    assert theIndex._itemIndex[nHandle]["T000013"].wordCount == 4
    assert theIndex._itemIndex[nHandle]["T000019"].wordCount == 9

    assert theIndex._itemIndex[nHandle]["T000001"].paraCount == 1
    assert theIndex._itemIndex[nHandle]["T000007"].paraCount == 1
    assert theIndex._itemIndex[nHandle]["T000013"].paraCount == 1
    assert theIndex._itemIndex[nHandle]["T000019"].paraCount == 3

    assert theIndex._itemIndex[nHandle]["T000001"].synopsis == "Synopsis One."
    assert theIndex._itemIndex[nHandle]["T000007"].synopsis == "Synopsis Two."
    assert theIndex._itemIndex[nHandle][
        "T000013"].synopsis == "Synopsis Three."
    assert theIndex._itemIndex[nHandle]["T000019"].synopsis == "Synopsis Four."

    # Note File
    assert theIndex.scanText(cHandle, ("# Title One\n\n"
                                       "@tag: One\n\n"
                                       "% synopsis: Synopsis One.\n\n"
                                       "Paragraph One.\n\n"))
    assert theIndex._itemIndex[cHandle]["T000001"].references == {}
    assert theIndex._itemIndex[cHandle]["T000001"].level == "H1"
    assert theIndex._itemIndex[cHandle]["T000001"].title == "Title One"
    assert theIndex._itemIndex[cHandle]["T000001"].charCount == 23
    assert theIndex._itemIndex[cHandle]["T000001"].wordCount == 4
    assert theIndex._itemIndex[cHandle]["T000001"].paraCount == 1
    assert theIndex._itemIndex[cHandle]["T000001"].synopsis == "Synopsis One."

    # Valid and Invalid References
    assert theIndex.scanText(
        sHandle,
        (
            "# Title One\n\n"
            "@pov: One\n\n"  # Valid
            "@char: Two\n\n"  # Invalid tag
            "@:\n\n"  # Invalid line
            "% synopsis: Synopsis One.\n\n"
            "Paragraph One.\n\n"))
    assert theIndex._itemIndex[sHandle]["T000001"].references == {
        "One": {"@pov"},
        "Two": {"@char"}
    }

    # Special Titles
    # ==============

    assert theIndex.scanText(tHandle, ("#! My Project\n\n"
                                       ">> By Jane Doe <<\n\n"))
    assert theIndex._itemIndex[cHandle]["T000001"].references == {}
    assert theIndex._itemIndex[tHandle]["T000001"].level == "H1"
    assert theIndex._itemIndex[tHandle]["T000001"].title == "My Project"
    assert theIndex._itemIndex[tHandle]["T000001"].charCount == 21
    assert theIndex._itemIndex[tHandle]["T000001"].wordCount == 5
    assert theIndex._itemIndex[tHandle]["T000001"].paraCount == 1
    assert theIndex._itemIndex[tHandle]["T000001"].synopsis == ""

    assert theIndex.scanText(tHandle,
                             ("##! Prologue\n\n"
                              "In the beginning there was time ...\n\n"))
    assert theIndex._itemIndex[cHandle]["T000001"].references == {}
    assert theIndex._itemIndex[tHandle]["T000001"].level == "H2"
    assert theIndex._itemIndex[tHandle]["T000001"].title == "Prologue"
    assert theIndex._itemIndex[tHandle]["T000001"].charCount == 43
    assert theIndex._itemIndex[tHandle]["T000001"].wordCount == 8
    assert theIndex._itemIndex[tHandle]["T000001"].paraCount == 1
    assert theIndex._itemIndex[tHandle]["T000001"].synopsis == ""

    # Page wo/Title
    # =============

    theProject.tree[pHandle]._layout = nwItemLayout.DOCUMENT
    assert theIndex.scanText(pHandle,
                             ("This is a page with some text on it.\n\n"))
    assert theIndex._itemIndex[pHandle]["T000000"].references == {}
    assert theIndex._itemIndex[pHandle]["T000000"].level == "H0"
    assert theIndex._itemIndex[pHandle]["T000000"].title == ""
    assert theIndex._itemIndex[pHandle]["T000000"].charCount == 36
    assert theIndex._itemIndex[pHandle]["T000000"].wordCount == 9
    assert theIndex._itemIndex[pHandle]["T000000"].paraCount == 1
    assert theIndex._itemIndex[pHandle]["T000000"].synopsis == ""

    theProject.tree[pHandle]._layout = nwItemLayout.NOTE
    assert theIndex.scanText(pHandle,
                             ("This is a page with some text on it.\n\n"))
    assert theIndex._itemIndex[pHandle]["T000000"].references == {}
    assert theIndex._itemIndex[pHandle]["T000000"].level == "H0"
    assert theIndex._itemIndex[pHandle]["T000000"].title == ""
    assert theIndex._itemIndex[pHandle]["T000000"].charCount == 36
    assert theIndex._itemIndex[pHandle]["T000000"].wordCount == 9
    assert theIndex._itemIndex[pHandle]["T000000"].paraCount == 1
    assert theIndex._itemIndex[pHandle]["T000000"].synopsis == ""

    assert theProject.closeProject() is True
예제 #14
0
def testCoreIndex_LoadSave(monkeypatch, nwLipsum, mockGUI, outDir, refDir):
    """Test core functionality of scaning, saving, loading and checking
    the index cache file.
    """
    projFile = os.path.join(nwLipsum, "meta", "tagsIndex.json")
    testFile = os.path.join(outDir, "coreIndex_LoadSave_tagsIndex.json")
    compFile = os.path.join(refDir, "coreIndex_LoadSave_tagsIndex.json")

    theProject = NWProject(mockGUI)
    assert theProject.openProject(nwLipsum)

    theIndex = NWIndex(theProject)
    assert repr(theIndex) == "<NWIndex project='Lorem Ipsum'>"

    notIndexable = {
        "b3643d0f92e32": False,  # Novel ROOT
        "45e6b01ca35c1": False,  # Chapter One FOLDER
        "6bd935d2490cd": False,  # Chapter Two FOLDER
        "67a8707f2f249": False,  # Character ROOT
        "6c6afb1247750": False,  # Plot ROOT
        "60bdf227455cc": False,  # World ROOT
    }
    for tItem in theProject.tree:
        assert theIndex.reIndexHandle(tItem.itemHandle) is notIndexable.get(
            tItem.itemHandle, True)

    assert theIndex.reIndexHandle(None) is False

    # Make the save fail
    with monkeypatch.context() as mp:
        mp.setattr("builtins.open", causeException)
        assert theIndex.saveIndex() is False

    # Make the save pass
    assert theIndex.saveIndex() is True

    # Take a copy of the index
    tagIndex = str(theIndex._tagsIndex.packData())
    itemsIndex = str(theIndex._itemIndex.packData())

    # Delete a handle
    assert theIndex._tagsIndex["Bod"] is not None
    assert theIndex._itemIndex["4c4f28287af27"] is not None
    theIndex.deleteHandle("4c4f28287af27")
    assert theIndex._tagsIndex["Bod"] is None
    assert theIndex._itemIndex["4c4f28287af27"] is None

    # Clear the index
    theIndex.clearIndex()
    assert theIndex._tagsIndex._tags == {}
    assert theIndex._itemIndex._items == {}

    # Make the load fail
    with monkeypatch.context() as mp:
        mp.setattr(json, "load", causeException)
        assert theIndex.loadIndex() is False
        assert theIndex.indexBroken is True

    # Make the load pass
    assert theIndex.loadIndex() is True
    assert theIndex.indexBroken is False

    assert str(theIndex._tagsIndex.packData()) == tagIndex
    assert str(theIndex._itemIndex.packData()) == itemsIndex

    # Check File
    copyfile(projFile, testFile)
    assert cmpFiles(testFile, compFile)

    # Write an emtpy index file and load it
    writeFile(projFile, "{}")
    assert theIndex.loadIndex() is False
    assert theIndex.indexBroken is True

    # Write an index file that passes loading, but is still empty
    writeFile(projFile, '{"tagsIndex": {}, "itemIndex": {}}')
    assert theIndex.loadIndex() is True
    assert theIndex.indexBroken is False

    # Check that the index is re-populated
    assert "04468803b92e1" in theIndex._itemIndex
    assert "2426c6f0ca922" in theIndex._itemIndex
    assert "441420a886d82" in theIndex._itemIndex
    assert "47666c91c7ccf" in theIndex._itemIndex
    assert "4c4f28287af27" in theIndex._itemIndex
    assert "846352075de7d" in theIndex._itemIndex
    assert "88243afbe5ed8" in theIndex._itemIndex
    assert "88d59a277361b" in theIndex._itemIndex
    assert "8c58a65414c23" in theIndex._itemIndex
    assert "db7e733775d4d" in theIndex._itemIndex
    assert "eb103bc70c90c" in theIndex._itemIndex
    assert "f8c0562e50f1b" in theIndex._itemIndex
    assert "f96ec11c6a3da" in theIndex._itemIndex
    assert "fb609cd8319dc" in theIndex._itemIndex
    assert "7a992350f3eb6" in theIndex._itemIndex

    # Finalise
    assert theProject.closeProject() is True
예제 #15
0
def testCoreIndex_ExtractData(mockGUI, fncDir, mockRnd):
    """Check the index data extraction functions.
    """
    theProject = NWProject(mockGUI)
    buildTestProject(theProject, fncDir)

    theIndex = theProject.index
    theIndex.reIndexHandle("0000000000010")
    theIndex.reIndexHandle("0000000000011")
    theIndex.reIndexHandle("0000000000012")
    theIndex.reIndexHandle("0000000000013")
    theIndex.reIndexHandle("0000000000014")
    theIndex.reIndexHandle("0000000000015")
    theIndex.reIndexHandle("0000000000016")
    theIndex.reIndexHandle("0000000000017")

    nHandle = theProject.newFile("Hello", "0000000000010")
    cHandle = theProject.newFile("Jane", "0000000000012")

    assert theIndex.getNovelData("", "") is None
    assert theIndex.getNovelData("0000000000010", "") is None

    assert theIndex.scanText(cHandle, ("# Jane Smith\n" "@tag: Jane\n"))
    assert theIndex.scanText(nHandle, ("# Hello World!\n"
                                       "@pov: Jane\n"
                                       "@char: Jane\n\n"
                                       "% this is a comment\n\n"
                                       "This is a story about Jane Smith.\n\n"
                                       "Well, not really.\n"))

    # The novel structure should contain the pointer to the novel file header
    theKeys = []
    for aKey, _, _, _ in theIndex.novelStructure():
        theKeys.append(aKey)

    assert theKeys == [
        "0000000000014:T000001",
        "0000000000016:T000001",
        "0000000000017:T000001",
        "%s:T000001" % nHandle,
    ]

    # Check that excluded files can be skipped
    theProject.tree[nHandle].setExported(False)

    theKeys = []
    for aKey, _, _, _ in theIndex.novelStructure(skipExcl=False):
        theKeys.append(aKey)

    assert theKeys == [
        "0000000000014:T000001",
        "0000000000016:T000001",
        "0000000000017:T000001",
        "%s:T000001" % nHandle,
    ]

    theKeys = []
    for aKey, _, _, _ in theIndex.novelStructure(skipExcl=True):
        theKeys.append(aKey)

    assert theKeys == [
        "0000000000014:T000001",
        "0000000000016:T000001",
        "0000000000017:T000001",
    ]

    # The novel file should have the correct counts
    cC, wC, pC = theIndex.getCounts(nHandle)
    assert cC == 62  # Characters in text and title only
    assert wC == 12  # Words in text and title only
    assert pC == 2  # Paragraphs in text only

    # getReferences
    # =============

    # Look up an ivalid handle
    theRefs = theIndex.getReferences("Not a handle")
    assert theRefs["@pov"] == []
    assert theRefs["@char"] == []

    # The novel file should now refer to Jane as @pov and @char
    theRefs = theIndex.getReferences(nHandle)
    assert theRefs["@pov"] == ["Jane"]
    assert theRefs["@char"] == ["Jane"]

    # getBackReferenceList
    # ====================

    # None handle should return an empty dict
    assert theIndex.getBackReferenceList(None) == {}

    # The Title Page file should have no references as it has no tag
    assert theIndex.getBackReferenceList("0000000000014") == {}

    # The character file should have a record of the reference from the novel file
    theRefs = theIndex.getBackReferenceList(cHandle)
    assert theRefs == {nHandle: "T000001"}

    # getTagSource
    # ============

    assert theIndex.getTagSource("Jane") == (cHandle, "T000001")
    assert theIndex.getTagSource("John") == (None, "T000000")

    # getCounts
    # =========
    # For whole text and sections

    # Invalid handle or title should return 0s
    assert theIndex.getCounts("stuff") == (0, 0, 0)
    assert theIndex.getCounts(nHandle, "stuff") == (0, 0, 0)

    # Get section counts for a novel file
    assert theIndex.scanText(
        nHandle, ("# Hello World!\n"
                  "@pov: Jane\n"
                  "@char: Jane\n\n"
                  "% this is a comment\n\n"
                  "This is a story about Jane Smith.\n\n"
                  "Well, not really.\n\n"
                  "# Hello World!\n"
                  "@pov: Jane\n"
                  "@char: Jane\n\n"
                  "% this is a comment\n\n"
                  "This is a story about Jane Smith.\n\n"
                  "Well, not really. She's still awesome though.\n"))
    # Whole document
    cC, wC, pC = theIndex.getCounts(nHandle)
    assert cC == 152
    assert wC == 28
    assert pC == 4

    # First part
    cC, wC, pC = theIndex.getCounts(nHandle, "T000001")
    assert cC == 62
    assert wC == 12
    assert pC == 2

    # Second part
    cC, wC, pC = theIndex.getCounts(nHandle, "T000011")
    assert cC == 90
    assert wC == 16
    assert pC == 2

    # Get section counts for a note file
    assert theIndex.scanText(
        cHandle, ("# Hello World!\n"
                  "@pov: Jane\n"
                  "@char: Jane\n\n"
                  "% this is a comment\n\n"
                  "This is a story about Jane Smith.\n\n"
                  "Well, not really.\n\n"
                  "# Hello World!\n"
                  "@pov: Jane\n"
                  "@char: Jane\n\n"
                  "% this is a comment\n\n"
                  "This is a story about Jane Smith.\n\n"
                  "Well, not really. She's still awesome though.\n"))
    # Whole document
    cC, wC, pC = theIndex.getCounts(cHandle)
    assert cC == 152
    assert wC == 28
    assert pC == 4

    # First part
    cC, wC, pC = theIndex.getCounts(cHandle, "T000001")
    assert cC == 62
    assert wC == 12
    assert pC == 2

    # Second part
    cC, wC, pC = theIndex.getCounts(cHandle, "T000011")
    assert cC == 90
    assert wC == 16
    assert pC == 2

    # Novel Stats
    # ===========

    hHandle = theProject.newFile("Chapter", "0000000000010")
    sHandle = theProject.newFile("Scene One", "0000000000010")
    tHandle = theProject.newFile("Scene Two", "0000000000010")

    theProject.tree[hHandle].itemLayout == nwItemLayout.DOCUMENT
    theProject.tree[sHandle].itemLayout == nwItemLayout.DOCUMENT
    theProject.tree[tHandle].itemLayout == nwItemLayout.DOCUMENT

    assert theIndex.scanText(hHandle, "## Chapter One\n\n")
    assert theIndex.scanText(sHandle, "### Scene One\n\n")
    assert theIndex.scanText(tHandle, "### Scene Two\n\n")

    assert [
        (h, t)
        for h, t, _ in theIndex._itemIndex.iterNovelStructure(skipExcl=False)
    ] == [
        ("0000000000014", "T000001"),
        ("0000000000016", "T000001"),
        ("0000000000017", "T000001"),
        (nHandle, "T000001"),
        (nHandle, "T000011"),
        (hHandle, "T000001"),
        (sHandle, "T000001"),
        (tHandle, "T000001"),
    ]

    assert [
        (h, t)
        for h, t, _ in theIndex._itemIndex.iterNovelStructure(skipExcl=True)
    ] == [
        ("0000000000014", "T000001"),
        ("0000000000016", "T000001"),
        ("0000000000017", "T000001"),
        (hHandle, "T000001"),
        (sHandle, "T000001"),
        (tHandle, "T000001"),
    ]

    # Add a fake handle to the tree and check that it's ignored
    theProject.tree._treeOrder.append("0000000000000")
    assert [
        (h, t)
        for h, t, _ in theIndex._itemIndex.iterNovelStructure(skipExcl=False)
    ] == [
        ("0000000000014", "T000001"),
        ("0000000000016", "T000001"),
        ("0000000000017", "T000001"),
        (nHandle, "T000001"),
        (nHandle, "T000011"),
        (hHandle, "T000001"),
        (sHandle, "T000001"),
        (tHandle, "T000001"),
    ]
    theProject.tree._treeOrder.remove("0000000000000")

    # Extract stats
    assert theIndex.getNovelWordCount(skipExcl=False) == 43
    assert theIndex.getNovelWordCount(skipExcl=True) == 15
    assert theIndex.getNovelTitleCounts(skipExcl=False) == [0, 3, 2, 3, 0]
    assert theIndex.getNovelTitleCounts(skipExcl=True) == [0, 1, 2, 3, 0]

    # Table of Contents
    assert theIndex.getTableOfContents(0, skipExcl=True) == []
    assert theIndex.getTableOfContents(1, skipExcl=True) == [
        ("0000000000014:T000001", 1, "New Novel", 15),
    ]
    assert theIndex.getTableOfContents(2, skipExcl=True) == [
        ("0000000000014:T000001", 1, "New Novel", 5),
        ("0000000000016:T000001", 2, "New Chapter", 4),
        ("%s:T000001" % hHandle, 2, "Chapter One", 6),
    ]
    assert theIndex.getTableOfContents(3, skipExcl=True) == [
        ("0000000000014:T000001", 1, "New Novel", 5),
        ("0000000000016:T000001", 2, "New Chapter", 2),
        ("0000000000017:T000001", 3, "New Scene", 2),
        ("%s:T000001" % hHandle, 2, "Chapter One", 2),
        ("%s:T000001" % sHandle, 3, "Scene One", 2),
        ("%s:T000001" % tHandle, 3, "Scene Two", 2),
    ]

    assert theIndex.getTableOfContents(0, skipExcl=False) == []
    assert theIndex.getTableOfContents(1, skipExcl=False) == [
        ("0000000000014:T000001", 1, "New Novel", 9),
        ("%s:T000001" % nHandle, 1, "Hello World!", 12),
        ("%s:T000011" % nHandle, 1, "Hello World!", 22),
    ]

    # Header Word Counts
    bHandle = "0000000000000"
    assert theIndex.getHandleWordCounts(bHandle) == []
    assert theIndex.getHandleWordCounts(hHandle) == [("%s:T000001" % hHandle,
                                                      2)]
    assert theIndex.getHandleWordCounts(sHandle) == [("%s:T000001" % sHandle,
                                                      2)]
    assert theIndex.getHandleWordCounts(tHandle) == [("%s:T000001" % tHandle,
                                                      2)]
    assert theIndex.getHandleWordCounts(nHandle) == [
        ("%s:T000001" % nHandle, 12), ("%s:T000011" % nHandle, 16)
    ]

    assert theIndex.saveIndex() is True
    assert theProject.saveProject() is True
    assert theProject.closeProject() is True

    # Header Record
    bHandle = "0000000000000"
    assert theIndex.getHandleHeaders(bHandle) == []
    assert theIndex.getHandleHeaders(hHandle) == [("T000001", "H2",
                                                   "Chapter One")]
    assert theIndex.getHandleHeaders(sHandle) == [("T000001", "H3",
                                                   "Scene One")]
    assert theIndex.getHandleHeaders(tHandle) == [("T000001", "H3",
                                                   "Scene Two")]
    assert theIndex.getHandleHeaders(nHandle) == [
        ("T000001", "H1", "Hello World!"), ("T000011", "H1", "Hello World!")
    ]
예제 #16
0
def testCoreProject_OldFormat(mockGUI, nwOldProj):
    """Test that a project folder structure of version 1.0 can be
    converted to the latest folder structure. Version 1.0 split the
    documents into 'data_0' ... 'data_f' folders, which are now all
    contained in a single 'content' folder.
    """
    theProject = NWProject(mockGUI)

    # Create mock files for known legacy files
    deleteFiles = [
        os.path.join(nwOldProj, "cache", "nwProject.nwx.0"),
        os.path.join(nwOldProj, "cache", "nwProject.nwx.1"),
        os.path.join(nwOldProj, "cache", "nwProject.nwx.2"),
        os.path.join(nwOldProj, "cache", "nwProject.nwx.3"),
        os.path.join(nwOldProj, "cache", "nwProject.nwx.4"),
        os.path.join(nwOldProj, "cache", "nwProject.nwx.5"),
        os.path.join(nwOldProj, "cache", "nwProject.nwx.6"),
        os.path.join(nwOldProj, "cache", "nwProject.nwx.7"),
        os.path.join(nwOldProj, "cache", "nwProject.nwx.8"),
        os.path.join(nwOldProj, "cache", "nwProject.nwx.9"),
        os.path.join(nwOldProj, "meta", "mainOptions.json"),
        os.path.join(nwOldProj, "meta", "exportOptions.json"),
        os.path.join(nwOldProj, "meta", "outlineOptions.json"),
        os.path.join(nwOldProj, "meta", "timelineOptions.json"),
        os.path.join(nwOldProj, "meta", "docMergeOptions.json"),
        os.path.join(nwOldProj, "meta", "sessionLogOptions.json"),
    ]

    # Add some files that shouldn't be there
    deleteFiles.append(os.path.join(nwOldProj, "data_f", "whatnow.nwd"))
    deleteFiles.append(os.path.join(nwOldProj, "data_f", "whatnow.txt"))

    # Add some folders that shouldn't be there
    os.mkdir(os.path.join(nwOldProj, "stuff"))
    os.mkdir(os.path.join(nwOldProj, "data_1", "stuff"))

    # Create mock files
    os.mkdir(os.path.join(nwOldProj, "cache"))
    for aFile in deleteFiles:
        writeFile(aFile, "Hi")
    for aFile in deleteFiles:
        assert os.path.isfile(aFile)

    # Open project and check that files that are not supposed to be
    # there have been removed
    assert theProject.openProject(nwOldProj)
    for aFile in deleteFiles:
        assert not os.path.isfile(aFile)

    assert not os.path.isdir(os.path.join(nwOldProj, "data_1", "stuff"))
    assert not os.path.isdir(os.path.join(nwOldProj, "data_1"))
    assert not os.path.isdir(os.path.join(nwOldProj, "data_7"))
    assert not os.path.isdir(os.path.join(nwOldProj, "data_8"))
    assert not os.path.isdir(os.path.join(nwOldProj, "data_9"))
    assert not os.path.isdir(os.path.join(nwOldProj, "data_a"))
    assert not os.path.isdir(os.path.join(nwOldProj, "data_f"))

    # Check stuff that has been moved
    assert os.path.isdir(os.path.join(nwOldProj, "junk"))
    assert os.path.isdir(os.path.join(nwOldProj, "junk", "stuff"))
    assert os.path.isfile(os.path.join(nwOldProj, "junk", "whatnow.nwd"))
    assert os.path.isfile(os.path.join(nwOldProj, "junk", "whatnow.txt"))

    # Check that files we want to keep are in the right place
    assert os.path.isdir(os.path.join(nwOldProj, "cache"))
    assert os.path.isdir(os.path.join(nwOldProj, "content"))
    assert os.path.isdir(os.path.join(nwOldProj, "meta"))

    assert os.path.isfile(
        os.path.join(nwOldProj, "content", "f528d831f5b24.nwd"))
    assert os.path.isfile(
        os.path.join(nwOldProj, "content", "88124a4292d8b.nwd"))
    assert os.path.isfile(
        os.path.join(nwOldProj, "content", "91239bf2f8b69.nwd"))
    assert os.path.isfile(
        os.path.join(nwOldProj, "content", "19752e7f9d8af.nwd"))
    assert os.path.isfile(
        os.path.join(nwOldProj, "content", "a764d5acf5a21.nwd"))
    assert os.path.isfile(
        os.path.join(nwOldProj, "content", "9058ae29f0dfd.nwd"))
    assert os.path.isfile(
        os.path.join(nwOldProj, "content", "7ff63b8afc4cd.nwd"))

    assert os.path.isfile(os.path.join(nwOldProj, "meta", "tagsIndex.json"))
    assert os.path.isfile(os.path.join(nwOldProj, "meta", "sessionInfo.log"))

    # Close the project
    theProject.closeProject()

    # Check that new files have been created
    assert os.path.isfile(os.path.join(nwOldProj, "meta", "guiOptions.json"))
    assert os.path.isfile(os.path.join(nwOldProj, "ToC.txt"))
예제 #17
0
def testCoreIndex_CheckThese(mockGUI, fncDir, mockRnd):
    """Test the tag checker function checkThese.
    """
    theProject = NWProject(mockGUI)
    buildTestProject(theProject, fncDir)
    theIndex = theProject.index

    nHandle = theProject.newFile("Hello", "0000000000010")
    cHandle = theProject.newFile("Jane", "0000000000012")
    nItem = theProject.tree[nHandle]
    cItem = theProject.tree[cHandle]

    assert theIndex.rootChangedSince("0000000000010", 0) is False
    assert theIndex.indexChangedSince(0) is False

    assert theIndex.scanText(cHandle, ("# Jane Smith\n"
                                       "@tag: Jane\n"
                                       "@tag:\n"
                                       "@:\n"))
    assert theIndex.scanText(
        nHandle,
        (
            "# Hello World!\n"
            "@pov: Jane\n"
            "@invalid: John\n"  # Checks for issue #688
        ))
    assert theIndex._tagsIndex.tagHandle("Jane") == cHandle
    assert theIndex._tagsIndex.tagHeading("Jane") == "T000001"
    assert theIndex._tagsIndex.tagClass("Jane") == "CHARACTER"
    assert theIndex.getNovelData(nHandle, "T000001").title == "Hello World!"
    assert theIndex.getReferences(nHandle, "T000001") == {
        "@char": [],
        "@custom": [],
        "@entity": [],
        "@focus": [],
        "@location": [],
        "@object": [],
        "@plot": [],
        "@pov": ["Jane"],
        "@time": []
    }

    assert theIndex.rootChangedSince("0000000000010", 0) is True
    assert theIndex.indexChangedSince(0) is True

    assert theIndex.getHandleHeaderLevel(cHandle) == "H1"
    assert theIndex.getHandleHeaderLevel(nHandle) == "H1"

    # Zero Items
    assert theIndex.checkThese([], cItem) == []

    # One Item
    assert theIndex.checkThese(["@tag"], cItem) == [True]
    assert theIndex.checkThese(["@who"], cItem) == [False]

    # Two Items
    assert theIndex.checkThese(["@tag", "Jane"], cItem) == [True, True]
    assert theIndex.checkThese(["@tag", "John"], cItem) == [True, True]
    assert theIndex.checkThese(["@tag", "Jane"], nItem) == [True, False]
    assert theIndex.checkThese(["@tag", "John"], nItem) == [True, True]
    assert theIndex.checkThese(["@pov", "John"], nItem) == [True, False]
    assert theIndex.checkThese(["@pov", "Jane"], nItem) == [True, True]
    assert theIndex.checkThese(["@ pov", "Jane"], nItem) == [False, False]
    assert theIndex.checkThese(["@what", "Jane"], nItem) == [False, False]

    # Three Items
    assert theIndex.checkThese(["@tag", "Jane", "John"],
                               cItem) == [True, True, False]
    assert theIndex.checkThese(["@who", "Jane", "John"],
                               cItem) == [False, False, False]
    assert theIndex.checkThese(["@pov", "Jane", "John"],
                               nItem) == [True, True, False]

    assert theProject.closeProject() is True
예제 #18
0
def testCoreIndex_LoadSave(monkeypatch, nwLipsum, mockGUI, outDir, refDir):
    """Test core functionality of scaning, saving, loading and checking
    the index cache file.
    """
    projFile = os.path.join(nwLipsum, "meta", "tagsIndex.json")
    testFile = os.path.join(outDir, "coreIndex_LoadSave_tagsIndex.json")
    compFile = os.path.join(refDir, "coreIndex_LoadSave_tagsIndex.json")

    theProject = NWProject(mockGUI)
    theProject.projTree.setSeed(42)
    assert theProject.openProject(nwLipsum)

    theIndex = NWIndex(theProject)
    notIndexable = {
        "b3643d0f92e32": False,  # Novel ROOT
        "45e6b01ca35c1": False,  # Chapter One FOLDER
        "6bd935d2490cd": False,  # Chapter Two FOLDER
        "67a8707f2f249": False,  # Character ROOT
        "6c6afb1247750": False,  # Plot ROOT
        "60bdf227455cc": False,  # World ROOT
    }
    for tItem in theProject.projTree:
        assert theIndex.reIndexHandle(tItem.itemHandle) is notIndexable.get(
            tItem.itemHandle, True)

    assert theIndex.reIndexHandle(None) is False

    # Make the save fail
    with monkeypatch.context() as mp:
        mp.setattr("builtins.open", causeException)
        assert theIndex.saveIndex() is False

    # Make the save pass
    assert theIndex.saveIndex() is True

    # Take a copy of the index
    tagIndex = str(theIndex._tagIndex)
    refIndex = str(theIndex._refIndex)
    fileIndex = str(theIndex._fileIndex)
    textCounts = str(theIndex._fileMeta)

    # Delete a handle
    assert theIndex._tagIndex.get("Bod", None) is not None
    assert theIndex._refIndex.get("4c4f28287af27", None) is not None
    assert theIndex._fileIndex.get("4c4f28287af27", None) is not None
    assert theIndex._fileMeta.get("4c4f28287af27", None) is not None
    theIndex.deleteHandle("4c4f28287af27")
    assert theIndex._tagIndex.get("Bod", None) is None
    assert theIndex._refIndex.get("4c4f28287af27", None) is None
    assert theIndex._fileIndex.get("4c4f28287af27", None) is None
    assert theIndex._fileMeta.get("4c4f28287af27", None) is None

    # Clear the index
    theIndex.clearIndex()
    assert theIndex._tagIndex == {}
    assert theIndex._refIndex == {}
    assert theIndex._fileIndex == {}
    assert theIndex._fileMeta == {}

    # Make the load fail
    with monkeypatch.context() as mp:
        mp.setattr(json, "load", causeException)
        assert theIndex.loadIndex() is False

    # Make the load pass
    assert theIndex.loadIndex() is True

    assert str(theIndex._tagIndex) == tagIndex
    assert str(theIndex._refIndex) == refIndex
    assert str(theIndex._fileIndex) == fileIndex
    assert str(theIndex._fileMeta) == textCounts

    # Break the index and check that we notice
    assert theIndex.indexBroken is False
    theIndex._tagIndex["Bod"].append("Stuff")
    theIndex._checkIndex()
    assert theIndex.indexBroken is True

    # Finalise
    assert theProject.closeProject() is True

    copyfile(projFile, testFile)
    assert cmpFiles(testFile, compFile)
예제 #19
0
def testCoreIndex_ExtractData(nwMinimal, mockGUI):
    """Check the index data extraction functions.
    """
    theProject = NWProject(mockGUI)
    theProject.projTree.setSeed(42)
    assert theProject.openProject(nwMinimal) is True

    theIndex = NWIndex(theProject)
    nHandle = theProject.newFile("Hello", nwItemClass.NOVEL, "a508bb932959c")
    cHandle = theProject.newFile("Jane", nwItemClass.CHARACTER,
                                 "afb3043c7b2b3")

    assert theIndex.getNovelData("", "") is None
    assert theIndex.getNovelData("a508bb932959c", "") is None

    assert theIndex.scanText(cHandle, ("# Jane Smith\n" "@tag: Jane\n"))
    assert theIndex.scanText(nHandle, ("# Hello World!\n"
                                       "@pov: Jane\n"
                                       "@char: Jane\n\n"
                                       "% this is a comment\n\n"
                                       "This is a story about Jane Smith.\n\n"
                                       "Well, not really.\n"))

    # The novel structure should contain the pointer to the novel file header
    theKeys = []
    for aKey, _, _, _ in theIndex.novelStructure():
        theKeys.append(aKey)

    assert theKeys == ["%s:T000001" % nHandle]

    # Check that excluded files can be skipped
    theProject.projTree[nHandle].setExported(False)

    theKeys = []
    for aKey, _, _, _ in theIndex.novelStructure(skipExcluded=False):
        theKeys.append(aKey)

    assert theKeys == ["%s:T000001" % nHandle]

    theKeys = []
    for aKey, _, _, _ in theIndex.novelStructure(skipExcluded=True):
        theKeys.append(aKey)

    assert theKeys == []

    theKeys = []
    for aKey, _, _, _ in theIndex.novelStructure():
        theKeys.append(aKey)

    assert theKeys == []

    # The novel file should have the correct counts
    cC, wC, pC = theIndex.getCounts(nHandle)
    assert cC == 62  # Characters in text and title only
    assert wC == 12  # Words in text and title only
    assert pC == 2  # Paragraphs in text only

    # getReferences
    # =============

    # Look up an ivalid handle
    theRefs = theIndex.getReferences("Not a handle")
    assert theRefs["@pov"] == []
    assert theRefs["@char"] == []

    # The novel file should now refer to Jane as @pov and @char
    theRefs = theIndex.getReferences(nHandle)
    assert theRefs["@pov"] == ["Jane"]
    assert theRefs["@char"] == ["Jane"]

    # getBackReferenceList
    # ====================

    # None handle should return an empty dict
    assert theIndex.getBackReferenceList(None) == {}

    # The character file should have a record of the reference from the novel file
    theRefs = theIndex.getBackReferenceList(cHandle)
    assert theRefs == {nHandle: "T000001"}

    # getTagSource
    # ============

    assert theIndex.getTagSource("Jane") == (cHandle, 2, "T000001")
    assert theIndex.getTagSource("John") == (None, 0, "T000000")

    # getCounts
    # =========
    # For whole text and sections

    # Get section counts for a novel file
    assert theIndex.scanText(
        nHandle, ("# Hello World!\n"
                  "@pov: Jane\n"
                  "@char: Jane\n\n"
                  "% this is a comment\n\n"
                  "This is a story about Jane Smith.\n\n"
                  "Well, not really.\n\n"
                  "# Hello World!\n"
                  "@pov: Jane\n"
                  "@char: Jane\n\n"
                  "% this is a comment\n\n"
                  "This is a story about Jane Smith.\n\n"
                  "Well, not really. She's still awesome though.\n"))
    # Whole document
    cC, wC, pC = theIndex.getCounts(nHandle)
    assert cC == 152
    assert wC == 28
    assert pC == 4

    # First part
    cC, wC, pC = theIndex.getCounts(nHandle, "T000001")
    assert cC == 62
    assert wC == 12
    assert pC == 2

    # Second part
    cC, wC, pC = theIndex.getCounts(nHandle, "T000011")
    assert cC == 90
    assert wC == 16
    assert pC == 2

    # Get section counts for a note file
    assert theIndex.scanText(
        cHandle, ("# Hello World!\n"
                  "@pov: Jane\n"
                  "@char: Jane\n\n"
                  "% this is a comment\n\n"
                  "This is a story about Jane Smith.\n\n"
                  "Well, not really.\n\n"
                  "# Hello World!\n"
                  "@pov: Jane\n"
                  "@char: Jane\n\n"
                  "% this is a comment\n\n"
                  "This is a story about Jane Smith.\n\n"
                  "Well, not really. She's still awesome though.\n"))
    # Whole document
    cC, wC, pC = theIndex.getCounts(cHandle)
    assert cC == 152
    assert wC == 28
    assert pC == 4

    # First part
    cC, wC, pC = theIndex.getCounts(cHandle, "T000001")
    assert cC == 62
    assert wC == 12
    assert pC == 2

    # Second part
    cC, wC, pC = theIndex.getCounts(cHandle, "T000011")
    assert cC == 90
    assert wC == 16
    assert pC == 2

    # Novel Stats
    # ===========

    hHandle = theProject.newFile("Chapter", nwItemClass.NOVEL, "a508bb932959c")
    sHandle = theProject.newFile("Scene One", nwItemClass.NOVEL,
                                 "a508bb932959c")
    tHandle = theProject.newFile("Scene Two", nwItemClass.NOVEL,
                                 "a508bb932959c")

    theProject.projTree[hHandle].itemLayout == nwItemLayout.DOCUMENT
    theProject.projTree[sHandle].itemLayout == nwItemLayout.DOCUMENT
    theProject.projTree[tHandle].itemLayout == nwItemLayout.DOCUMENT

    assert theIndex.scanText(hHandle, "## Chapter One\n\n")
    assert theIndex.scanText(sHandle, "### Scene One\n\n")
    assert theIndex.scanText(tHandle, "### Scene Two\n\n")

    assert theIndex._listNovelHandles(False) == [
        nHandle, hHandle, sHandle, tHandle
    ]
    assert theIndex._listNovelHandles(True) == [hHandle, sHandle, tHandle]

    # Add a fake handle to the tree and check that it's ignored
    theProject.projTree._treeOrder.append("0000000000000")
    assert theIndex._listNovelHandles(False) == [
        nHandle, hHandle, sHandle, tHandle
    ]
    theProject.projTree._treeOrder.remove("0000000000000")

    # Extract stats
    assert theIndex.getNovelWordCount(False) == 34
    assert theIndex.getNovelWordCount(True) == 6
    assert theIndex.getNovelTitleCounts(False) == [0, 2, 1, 2, 0]
    assert theIndex.getNovelTitleCounts(True) == [0, 0, 1, 2, 0]

    # Table of Contents
    assert theIndex.getTableOfContents(0, True) == []
    assert theIndex.getTableOfContents(1, True) == []
    assert theIndex.getTableOfContents(2, True) == [
        ("%s:T000001" % hHandle, 2, "Chapter One", 6),
    ]
    assert theIndex.getTableOfContents(3, True) == [
        ("%s:T000001" % hHandle, 2, "Chapter One", 2),
        ("%s:T000001" % sHandle, 3, "Scene One", 2),
        ("%s:T000001" % tHandle, 3, "Scene Two", 2),
    ]

    assert theIndex.getTableOfContents(0, False) == []
    assert theIndex.getTableOfContents(1, False) == [
        ("%s:T000001" % nHandle, 1, "Hello World!", 12),
        ("%s:T000011" % nHandle, 1, "Hello World!", 22),
    ]

    # Header Word Counts
    bHandle = "0000000000000"
    assert theIndex.getHandleWordCounts(bHandle) == []
    assert theIndex.getHandleWordCounts(hHandle) == [("%s:T000001" % hHandle,
                                                      2)]
    assert theIndex.getHandleWordCounts(sHandle) == [("%s:T000001" % sHandle,
                                                      2)]
    assert theIndex.getHandleWordCounts(tHandle) == [("%s:T000001" % tHandle,
                                                      2)]
    assert theIndex.getHandleWordCounts(nHandle) == [
        ("%s:T000001" % nHandle, 12), ("%s:T000011" % nHandle, 16)
    ]

    assert theProject.closeProject()

    # Header Record
    bHandle = "0000000000000"
    assert theIndex.getHandleHeaders(bHandle) == []
    assert theIndex.getHandleHeaders(hHandle) == [("T000001", "H2",
                                                   "Chapter One")]
    assert theIndex.getHandleHeaders(sHandle) == [("T000001", "H3",
                                                   "Scene One")]
    assert theIndex.getHandleHeaders(tHandle) == [("T000001", "H3",
                                                   "Scene Two")]
    assert theIndex.getHandleHeaders(nHandle) == [
        ("T000001", "H1", "Hello World!"), ("T000011", "H1", "Hello World!")
    ]
예제 #20
0
def testCoreProject_Open(monkeypatch, nwMinimal, mockGUI):
    """Test opening a project.
    """
    theProject = NWProject(mockGUI)

    # Rename the project file to check handling
    rName = os.path.join(nwMinimal, nwFiles.PROJ_FILE)
    wName = os.path.join(nwMinimal, nwFiles.PROJ_FILE + "_sdfghj")
    os.rename(rName, wName)
    assert theProject.openProject(nwMinimal) is False
    os.rename(wName, rName)

    # Fail on folder structure check
    with monkeypatch.context() as mp:
        mp.setattr("os.mkdir", causeOSError)
        assert theProject.openProject(nwMinimal) is False

    # Fail on lock file
    theProject.setProjectPath(nwMinimal)
    assert theProject._writeLockFile()
    assert theProject.openProject(nwMinimal) is False

    # Fail to read lockfile (which still opens the project)
    with monkeypatch.context() as mp:
        mp.setattr("builtins.open", causeOSError)
        assert theProject.openProject(nwMinimal) is True
    assert theProject.closeProject()

    # Force open with lockfile
    theProject.setProjectPath(nwMinimal)
    assert theProject._writeLockFile()
    assert theProject.openProject(nwMinimal, overrideLock=True) is True
    assert theProject.closeProject()

    # Make a junk XML file
    oName = os.path.join(nwMinimal, nwFiles.PROJ_FILE[:-3] + "orig")
    bName = os.path.join(nwMinimal, nwFiles.PROJ_FILE[:-3] + "bak")
    os.rename(rName, oName)
    writeFile(rName, "stuff")
    assert theProject.openProject(nwMinimal) is False

    # Also write a jun XML backup file
    writeFile(bName, "stuff")
    assert theProject.openProject(nwMinimal) is False

    # Wrong root item
    writeFile(rName, "<not_novelWriterXML></not_novelWriterXML>\n")
    assert theProject.openProject(nwMinimal) is False

    # Wrong file version
    writeFile(rName, ("<?xml version='0.0' encoding='utf-8'?>\n"
                      "<novelWriterXML "
                      "appVersion=\"1.0\" "
                      "hexVersion=\"0x01000000\" "
                      "fileVersion=\"1.0\" "
                      "timeStamp=\"2020-01-01 00:00:00\">\n"
                      "</novelWriterXML>\n"))
    mockGUI.askResponse = False
    assert theProject.openProject(nwMinimal) is False
    mockGUI.undo()

    # Future file version
    writeFile(rName, ("<?xml version='1.0' encoding='utf-8'?>\n"
                      "<novelWriterXML "
                      "appVersion=\"1.0\" "
                      "hexVersion=\"0x01000000\" "
                      "fileVersion=\"99.99\" "
                      "timeStamp=\"2020-01-01 00:00:00\">\n"
                      "</novelWriterXML>\n"))
    assert theProject.openProject(nwMinimal) is False

    # Update file version
    writeFile(rName, ("<?xml version='1.0' encoding='utf-8'?>\n"
                      "<novelWriterXML "
                      "appVersion=\"1.0\" "
                      "hexVersion=\"0xffffffff\" "
                      "fileVersion=\"1.2\" "
                      "timeStamp=\"2020-01-01 00:00:00\">\n"
                      "</novelWriterXML>\n"))
    mockGUI.askResponse = False
    assert theProject.openProject(nwMinimal) is False
    assert mockGUI.lastQuestion[0] == "File Version"
    mockGUI.undo()

    # Larger hex version
    writeFile(rName, ("<?xml version='1.0' encoding='utf-8'?>\n"
                      "<novelWriterXML "
                      "appVersion=\"1.0\" "
                      "hexVersion=\"0xffffffff\" "
                      "fileVersion=\"%s\" "
                      "timeStamp=\"2020-01-01 00:00:00\">\n"
                      "</novelWriterXML>\n") % theProject.FILE_VERSION)
    mockGUI.askResponse = False
    assert theProject.openProject(nwMinimal) is False
    assert mockGUI.lastQuestion[0] == "Version Conflict"
    mockGUI.undo()

    # Test skipping XML entries
    writeFile(rName, ("<?xml version='1.0' encoding='utf-8'?>\n"
                      "<novelWriterXML "
                      "appVersion=\"1.0\" "
                      "hexVersion=\"0x01000000\" "
                      "fileVersion=\"1.2\" "
                      "timeStamp=\"2020-01-01 00:00:00\">\n"
                      "<project><stuff/></project>\n"
                      "<settings><stuff/></settings>\n"
                      "</novelWriterXML>\n"))
    assert theProject.openProject(nwMinimal) is True
    assert theProject.closeProject()

    # Clean up XML files
    os.unlink(rName)
    os.unlink(bName)
    os.rename(oName, rName)

    # Add some legacy stuff that cannot be removed
    writeFile(os.path.join(nwMinimal, "junk"), "stuff")
    os.mkdir(os.path.join(nwMinimal, "data_0"))
    writeFile(os.path.join(nwMinimal, "data_0", "junk"), "stuff")
    mockGUI.clear()
    assert theProject.openProject(nwMinimal) is True
    assert "data_0" in mockGUI.lastAlert
    assert theProject.closeProject()