Exemple #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
Exemple #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)
Exemple #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])
Exemple #4
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
Exemple #5
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])
Exemple #6
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)
Exemple #7
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()
Exemple #8
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()
Exemple #9
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!")
    ]