Checkpoint: revisions

This commit is contained in:
Olivier Keshavjee 2017-11-19 15:29:38 +01:00
parent a0fac27e07
commit 584b0b04a6
7 changed files with 136 additions and 43 deletions

View file

@ -14,8 +14,7 @@ from manuskript.version import getVersion
faulthandler.enable() faulthandler.enable()
def prepare():
def run():
app = QApplication(sys.argv) app = QApplication(sys.argv)
app.setOrganizationName("manuskript") app.setOrganizationName("manuskript")
app.setOrganizationDomain("www.theologeek.ch") app.setOrganizationDomain("www.theologeek.ch")
@ -57,25 +56,38 @@ def run():
QIcon.setThemeSearchPaths(QIcon.themeSearchPaths() + [appPath("icons")]) QIcon.setThemeSearchPaths(QIcon.themeSearchPaths() + [appPath("icons")])
QIcon.setThemeName("NumixMsk") QIcon.setThemeName("NumixMsk")
# qApp.setWindowIcon(QIcon.fromTheme("im-aim"))
# Seperating launch to avoid segfault, so it seem. # Main window
# Cf. http://stackoverflow.com/questions/12433491/is-this-pyqt-4-python-bug-or-wrongly-behaving-code
launch()
def launch():
from manuskript.mainWindow import MainWindow from manuskript.mainWindow import MainWindow
main = MainWindow() MW = MainWindow()
# We store the system default cursor flash time to be able to restore it # We store the system default cursor flash time to be able to restore it
# later if necessary # later if necessary
main._defaultCursorFlashTime = qApp.cursorFlashTime() MW._defaultCursorFlashTime = qApp.cursorFlashTime()
main.show()
return app, MW
def launch(MW = None):
if MW is None:
from manuskript.functions import mainWindow
MW = mainWindow()
MW.show()
qApp.exec_() qApp.exec_()
qApp.deleteLater() qApp.deleteLater()
def run():
"""
Run separates prepare and launch for two reasons:
1. I've read somewhere it helps with potential segfault (see comment below)
2. So that prepare can be used in tests, without running the whole thing
"""
# Need to return and keep `app` otherwise it gets deleted.
app, MW = prepare()
# Seperating launch to avoid segfault, so it seem.
# Cf. http://stackoverflow.com/questions/12433491/is-this-pyqt-4-python-bug-or-wrongly-behaving-code
launch(MW)
if __name__ == "__main__": if __name__ == "__main__":
run() run()

View file

@ -1416,4 +1416,3 @@ class MainWindow(QMainWindow, Ui_MainWindow):
r = self.dialog.geometry() r = self.dialog.geometry()
r2 = self.geometry() r2 = self.geometry()
self.dialog.move(r2.center() - r.center()) self.dialog.move(r2.center() - r.center())

View file

@ -7,5 +7,10 @@ import pytest
# We need a qApplication to be running, or all the calls to qApp # We need a qApplication to be running, or all the calls to qApp
# will throw a seg fault. # will throw a seg fault.
from PyQt5.QtWidgets import QApplication # from PyQt5.QtWidgets import QApplication
app = QApplication([]) # app = QApplication([])
# app.setOrganizationName("manuskript_tests")
# app.setApplicationName("manuskript_tests")
from manuskript import main
app, MW = main.prepare()

View file

@ -5,17 +5,32 @@
import pytest import pytest
@pytest.fixture(scope='session', autouse=True) # @pytest.fixture(scope='session', autouse=True)
def MW(): # def MW():
""" # """
Creates a mainWindow that can be used for the tests # Creates a mainWindow that can be used for the tests
Either with functions.mainWindow or by passing argument # Either with functions.mainWindow or by passing argument
MW to the test # MW to the test
""" # """
from manuskript.mainWindow import MainWindow # from manuskript.mainWindow import MainWindow
mw = MainWindow() # mw = MainWindow()
#
# yield
#
# # Properly destructed after. Otherwise: seg fault.
# mw.deleteLater()
yield @pytest.fixture
def MWEmptyProject():
"""
Sets the mainWindow to load an empty project.
"""
from manuskript.functions import mainWindow
MW = mainWindow()
# Properly destructed after. Otherwise: seg fault. import tempfile
mw.deleteLater() tf = tempfile.NamedTemporaryFile(suffix=".msk")
MW.welcome.createFile(tf.name, overwrite=True)
assert MW.currentProject is not None
return MW

View file

@ -7,13 +7,13 @@ import pytest
from manuskript.models import outlineModel, outlineItem from manuskript.models import outlineModel, outlineItem
@pytest.fixture @pytest.fixture
def outlineModelBasic(): def outlineModelBasic(MWEmptyProject):
"""Returns an outlineModel with a few items: """Returns an outlineModel with a few items:
* Folder * Folder
* Text * Text
* Text * Text
""" """
mdl = outlineModel(parent=None) mdl = MWEmptyProject.mdlOutline
root = mdl.rootItem root = mdl.rootItem
f = outlineItem(title="Folder", parent=root) f = outlineItem(title="Folder", parent=root)

View file

@ -18,53 +18,79 @@ def outlineItemText():
return outlineItem(title="Text", _type="md") return outlineItem(title="Text", _type="md")
def test_outlineItemsProperties(outlineItemFolder, outlineItemText): def test_outlineItemsProperties(outlineItemFolder, outlineItemText):
"""
Tests with simple items, without parent or models.
"""
# Simplification # Simplification
folder = outlineItemFolder folder = outlineItemFolder
text = outlineItemText text = outlineItemText
# Basic tests # getters
assert folder.isFolder() == True assert folder.isFolder() == True
assert text.isFolder() == False assert text.isFolder() == False
assert text.isText() == True assert text.isText() == True
assert text.isMD() == text.isMMD() == True assert text.isMD() == text.isMMD() == True
assert text.title() == "Text" assert text.title() == "Text"
assert text.compile() == True assert text.compile() == True
text.setData(text.enum.compile, 0)
assert text.compile() == False
assert folder.POV() == "" assert folder.POV() == ""
assert folder.status() == "" assert folder.status() == ""
assert folder.POV() == "" assert folder.label() == ""
assert folder.customIcon() == "" assert folder.customIcon() == ""
# setData and other setters
from PyQt5.QtCore import Qt
assert text.data(text.enum.compile, role=Qt.CheckStateRole) == Qt.Checked
text.setData(text.enum.compile, 0)
assert text.compile() == False
assert text.data(text.enum.compile, role=Qt.CheckStateRole) == Qt.Unchecked
folder.setCustomIcon("custom") folder.setCustomIcon("custom")
assert folder.customIcon() == "custom" assert folder.customIcon() == "custom"
folder.setData(folder.enum.text, "Some text")
assert folder.text() == "" # folders have no text
# wordCount
text.setData(text.enum.text, "Sample **text**.") text.setData(text.enum.text, "Sample **text**.")
assert text.wordCount() == 2 assert text.wordCount() == 2
assert text.data(text.enum.revisions) == [] text.setData(text.enum.goal, 4)
text.setData(text.enum.setGoal, 4)
assert text.data(text.enum.goalPercentage) == .5 assert text.data(text.enum.goalPercentage) == .5
# revisions
assert text.data(text.enum.revisions) == []
def test_modelStuff(outlineModelBasic): def test_modelStuff(outlineModelBasic):
"""
Tests with children items.
"""
# Simplification # Simplification
model = outlineModelBasic model = outlineModelBasic
# Child count
root = model.rootItem root = model.rootItem
assert len(root.children()) == 2 assert len(root.children()) == 2
folder = root.child(0) folder = root.child(0)
text1 = folder.child(0) text1 = folder.child(0)
text2 = root.child(1) text2 = root.child(1)
# Compile
assert text1.compile() == True assert text1.compile() == True
folder.setData(folder.enum.compile, 0) folder.setData(folder.enum.compile, 0)
assert text1.compile() == False assert text1.compile() == False
# Word count
text1.setData(text1.enum.text, "Sample text.") text1.setData(text1.enum.text, "Sample text.")
assert text1.wordCount() == 2 assert text1.wordCount() == 2
assert folder.wordCount() == 2 assert folder.wordCount() == 2
assert folder.stats() != "" statsWithGoal = folder.stats()
assert statsWithGoal != ""
text1.setData(text1.enum.setGoal, 4)
assert folder.data(folder.enum.goal) == 4
folder.setData(folder.enum.setGoal, 3)
assert folder.data(folder.enum.goal) == 3
assert folder.stats() != statsWithGoal
# Split and merge
text1.setData(text1.enum.text, "Sample\n---\ntext.") text1.setData(text1.enum.text, "Sample\n---\ntext.")
folder.split("invalid mark") folder.split("invalid mark")
assert folder.childCount() == 1 assert folder.childCount() == 1
@ -75,6 +101,40 @@ def test_modelStuff(outlineModelBasic):
text1.setData(text1.enum.text, "Sample\nNewTitle\ntext.") text1.setData(text1.enum.text, "Sample\nNewTitle\ntext.")
text1.splitAt(7, 8) text1.splitAt(7, 8)
assert folder.child(1).title() == "NewTitle" assert folder.child(1).title() == "NewTitle"
folder.child(1).splitAt(3)
assert folder.child(2).title() == "NewTitle_2"
folder.removeChild(2)
folder.removeChild(1) folder.removeChild(1)
folder.removeChild(0) folder.removeChild(0)
assert folder.childCount() == 0 assert folder.childCount() == 0
# Search
folder.appendChild(text2)
text2.setData(text2.enum.POV, 1)
folder.setData(folder.enum.POV, 1)
assert len(folder.findItemsByPOV(1)) == 2
folder.setData(folder.enum.label, 1) # Idea
folder.setData(folder.enum.status, 4) # Final
text2.setData(text2.enum.text, "Some final value.")
from manuskript.functions import MW
cols = [folder.enum.text, folder.enum.POV,
folder.enum.label, folder.enum.status]
assert folder.findItemsContaining("VALUE", cols, MW, True) == []
assert folder.findItemsContaining("VALUE", cols, MW, False) == [text2.ID()]
# Revisions
# from manuskript import settings
# settings.revisions["smartremove"] = False
# text2.setData(text2.enum.text, "Some value.")
# assert text2.revisions() == 1
# text2.clearAllRevisions()
# assert text2.revisions() == []
# text2.setData(text2.enum.text, "Some value.")
# assert len(text2.revisions()) == 1
# text2.setData(text2.enum.text, "Some new value.")
# assert len(text2.revisions()) == 1 # Auto clean
#TODO: copy (with children), IDs check, childcountrecursive
# (cf. abstractItem)

View file

@ -152,18 +152,20 @@ class welcome(QWidget, Ui_welcome):
pName=pName[:-4] pName=pName[:-4]
self.mw.setWindowTitle(pName + " - " + self.tr("Manuskript")) self.mw.setWindowTitle(pName + " - " + self.tr("Manuskript"))
def createFile(self): def createFile(self, filename=None, overwrite=False):
"""When starting a new project, ask for a place to save it. """When starting a new project, ask for a place to save it.
Datas are not loaded from file, so they must be populated another way.""" Datas are not loaded from file, so they must be populated another way."""
filename = QFileDialog.getSaveFileName(self, if filename is None:
self.tr("Create New Project"), filename = QFileDialog.getSaveFileName(
".", self,
self.tr("Manuskript project (*.msk)"))[0] self.tr("Create New Project"),
".",
self.tr("Manuskript project (*.msk)"))[0]
if filename: if filename:
if filename[-4:] != ".msk": if filename[-4:] != ".msk":
filename += ".msk" filename += ".msk"
if os.path.exists(filename): if os.path.exists(filename) and not overwrite:
# Check if okay to overwrite existing project # Check if okay to overwrite existing project
result = QMessageBox.warning(self, self.tr("Warning"), result = QMessageBox.warning(self, self.tr("Warning"),
self.tr("Overwrite existing project {} ?").format(filename), self.tr("Overwrite existing project {} ?").format(filename),