zim-0.65/ 0000775 0001750 0001750 00000000000 12615422672 012124 5 ustar jaap jaap 0000000 0000000 zim-0.65/README.txt 0000664 0001750 0001750 00000012601 12400317054 013607 0 ustar jaap jaap 0000000 0000000 ====== ABOUT ======
Zim - A Desktop Wiki Editor
Zim is a graphical text editor used to maintain a collection of wiki pages.
Each page can contain links to other pages, simple formatting and images.
Pages are stored in a folder structure, like in an outliner, and can have
attachments. Creating a new page is as easy as linking to a nonexistent page.
All data is stored in plain text files with wiki formatting. Various plugins
provide additional functionality, like a task list manager, an equation
editor, a tray icon, and support for version control.
Zim can be used to:
* Keep an archive of notes
* Take notes during meetings or lectures
* Organize task lists
* Draft blog entries and emails
* Do brainstorming
====== COPYRIGHT ======
All files in this package, with the exception of those mentioned below
are copyrighted and licensed as follows:
Copyright 2008-2014 Jaap Karssenberg
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
MA 02110-1301, USA.
== Translations ==
Translations are copyrighted by their respective translators.
All translations that are entered through the launchpad website
are distributed under the BSD license.
See the translation files for detailed translator credits.
== Included Files ==
The following files were included from other sources:
* zim/inc/xdot.py - Copyright 2008 Jose Fonseca
* zim/inc/arithmetic.py - Copyright 2010, 2011 Patricio Paez
From the default Gnome icon theme:
* pixmaps/task-list.png (was: stock_todo.png)
* pixmaps/attachment.png (was: mail-attachment.png)
From Gtk+ 2.8
* pixmaps/link.png (was: stock_connect_24.png)
Other:
* pixmaps/calendar.png (was: stock_calendar-view-month.png)
Copyright 2007 by Jakub Steiner, released under GPL
modifications copyright 2009 by Gabriel Hurley
====== INSTALLING ======
NOTE: To test zim it is not needed to install. You should be able to run it
directly from the source directory by calling `./zim.py`. (To run a
translated version from the source first call `./setup.py build_trans`.)
First you should verify you have the dependencies zim needs. To list all
dependencies check `./setup.py --requires`.
You will at least need the following:
* gtk+ >= 2.6
* python >= 2.6
* python-gtk
* python-gobject
* python-xdg (optional, but recommended)
* xdg-utils (optional, but recommended)
To verify zim is working properly on your system you can call the test suite
using `./test.py`. Failures do not have to be critical, but in principle all
tests should pass.
Zim can be installed from source using:
./setup.py install
Most plugins have additional requirements. These are listed in the plugin
descriptions.
===== Ubuntu =====
On Ubuntu or other debian derived systems, the following packages should be
installed:
* python
* libgtk2.0-0
* python-gtk2
* python-xdg
===== Windows =====
To install gtk, python and python-gtk on Windows see the instructions at
http://www.pygtk.org . If you use python 2.5 you will also need to install the
python simplejson module. This can be obtained from http://pypi.python.org .
The python-xdg module is not usefull on Windows, so you can skip it.
Once the dependencies are fulfilled you can run zim directly from the source
directory.
===== Mac OS X =====
You can run zim on mac if you have the proper dependencies installed.
If you are using Mac Ports packages installing the following ports should work:
* python26
* py26-gtk
* py26-simplejson
* py26-xdg
===== Install Paths =====
If you install in a non-default location you may need to set the PYTHONPATH
environment variable in order for zim to find it's python modules.
For example, if you installed the modules below "/home/user/lib/zim" you need
to set:
PYTHONPATH=/home/user/lib
Also zim uses the XDG paths to locate data and config files. If you get
an error that zim can not find it's data files
For example, if you installed the zim data files to "/home/user/share/zim"
you need to set the data path like this:
XDG_DATA_DIRS=/home/user/share:/usr/local/share:/usr/share
====== PACKAGING ======
To build a tree in a target directory you can use:
./setup.py install --root=/path/to/package/build/dir --skip-cmd
Special attention may be needed to run xdg update commands in a post-install
script. Recommended commands are:
update-desktop-database
update-mime-database /usr/share/mime
====== TRANSLATING ======
To contribute to translations onlne please go to http://launchpad.net.
To test a new translation you can either download the snapshot from launchpad
and run:
./tools/import-launchpad-translations.py launchpad-export.tar.gz
Or you can edit the template zim.pot with your favourite editor. In that case
you should add you new .po file to the po/ directory.
After adding the .po file(s) you can compile the translation using:
./setup.py build_trans
zim-0.65/contrib/ 0000775 0001750 0001750 00000000000 12615422672 013564 5 ustar jaap jaap 0000000 0000000 zim-0.65/contrib/zim2trac.py 0000664 0001750 0001750 00000011533 12374655231 015675 0 ustar jaap jaap 0000000 0000000 # -*- coding: utf-8 -*-
# Copyright 2009 Pablo Angulo
'''Script to export zim wiki pages to trac / mediawiki
To use it, call
python trac2zim.py notebook output_folder prefix
where prefix is a string you put before each wiki page name. It will
fill output_folder with plain text files ready to be loaded with trac-admin:
trac-admin /path/to/project wiki load output_folder
zim links like [[:Software:note taking:zim|zim]] are flattened to wiki
entries like [Software_note_taking_zim zim].
'''
import re
import sys
import os
#buscaCabeceras=re.compile('(={1:5})([^=]*)(={1:5})')
def flatten(linkName):
'''Changes a zim link, possibly with categories, to a trac link
it also removes accents and other spanish special characters
'''
#remove final ':' character and
name=linkName[:-1] if linkName[-1]==':' else linkName
return removeSpecialChars(name.replace(':','_').replace(' ','_'))
def removeSpecialChars(s):
'''certain trac installation reported problems with special chars
other trac systems loaded all files without problem
the problem is only for file names and wiki pages names, not for content
'''
return s.replace('á','a').replace('é','e').replace('í','i').replace('ó','o').replace('ú','u').replace('ñ','n').replace('Á','A').replace('É','E').replace('Í','I').replace('Ó','O').replace('Ú','U').replace('Ñ','ñ')
cabecera=re.compile("(={1,6})([^=\/]+?)(={1,6})")
inlineVerbatim=re.compile("''([^']+?)''")
#~ multilineVerbatim=re.compile("\n[\t](.+?)\n")
negrita=re.compile('\*\*([^\*]+?)\*\*')
italic=re.compile('\/\/([^\/\n\]]+?)\/\/')
bracketedURL=re.compile('\[\[(http:\/\/[^\|]+)\|([^\|]+?)\]\]')
#TODO: separar links relativos y absolutos
simpleRelLink=re.compile('\[\[([^:][^\|]+?)\]\]')
namedRelLink=re.compile('\[\[([^:][^\|]+?)\|([^\|]+?)\]\]')
simpleAbsLink=re.compile('\[\[:([^\|]+?)\]\]')
namedAbsLink=re.compile('\[\[:([^\|]+?)\|([^\|]+?)\]\]')
images=re.compile('([^\{])\{\{\/(.+?)\}\}')
def translate(nota,prefix1,prefix2):
'''Takes a note in zim format and returns a note in trac format
'''
#duplicate all line breaks
nota=nota.replace('\n','\n\n')
# Headings
mm=cabecera.search(nota)
lista=[]
lastIndex=0
while mm:
lista.append(nota[lastIndex:mm.start()])
gg=mm.groups()
iguales=len(gg[0])
lista.append("="*(7-iguales)+gg[1]+"="*(7-iguales))
lastIndex=mm.end()
mm=cabecera.search(nota,lastIndex)
lista.append(nota[lastIndex:])
nota=''.join(lista)
#inlineVerbatim
nota=inlineVerbatim.sub("{{{\\1}}}",nota)
#multiline verbatim
#TODO
#bold
nota=negrita.sub("'''\\1'''",nota)
#italic
nota=italic.sub("''\\1''",nota)
#bracketedURL
nota = bracketedURL.sub("[\\1 \\2]",nota)
#~ #simple links
#~ nota=simpleLink.sub("[wiki:\\1]",nota)
#~ #named links
#~ nota=namedLink.sub("[wiki:\\1 \\2]",nota)
#simple relative links
mm=simpleRelLink.search(nota)
lista=[]
lastIndex=0
while mm:
lista.append(nota[lastIndex:mm.start()])
gg0=mm.groups()[0]
lista.append("[wiki:"+prefix1+prefix2+flatten(gg0)+" "+gg0+"]")
lastIndex=mm.end()
mm=simpleRelLink.search(nota,lastIndex)
lista.append(nota[lastIndex:])
nota=''.join(lista)
mm=simpleAbsLink.search(nota)
lista=[]
lastIndex=0
while mm:
lista.append(nota[lastIndex:mm.start()])
gg0=mm.groups()[0]
lista.append("[wiki:"+prefix1+flatten(gg0)+" "+gg0+"]")
lastIndex=mm.end()
mm=simpleAbsLink.search(nota,lastIndex)
lista.append(nota[lastIndex:])
nota=''.join(lista)
#named relativelinks
mm=namedRelLink.search(nota)
lista=[]
lastIndex=0
while mm:
lista.append(nota[lastIndex:mm.start()])
gg=mm.groups()
lista.append("[wiki:"+prefix1+prefix2+flatten(gg[0])+" "+gg[1]+"]")
lastIndex=mm.end()
mm=namedRelLink.search(nota,lastIndex)
lista.append(nota[lastIndex:])
nota=''.join(lista)
#named absolute links
mm=namedAbsLink.search(nota)
lista=[]
lastIndex=0
while mm:
lista.append(nota[lastIndex:mm.start()])
gg=mm.groups()
lista.append("[wiki:"+prefix1+flatten(gg[0])+" "+gg[1]+"]")
lastIndex=mm.end()
mm=namedAbsLink.search(nota,lastIndex)
lista.append(nota[lastIndex:])
nota=''.join(lista)
#lists
nota=nota.replace('\n* ','\n * ')
#images
nota=images.sub("\\1[[Image(\\2)]]",nota)
return nota
def processPath(pathin,pathout,prefix1,prefix2=''):
for archivo in os.listdir(pathin):
fullPath=os.path.join(pathin,archivo)
if archivo[-3:]=='txt':
fichero=open(fullPath,mode='r')
nota=fichero.read()
fichero.close()
nota_out=translate(nota,prefix1,prefix2)
#~ nameout= prefix+"_"+archivo[:-4] if prefix else archivo[:-4]
fichero=open(os.path.join(pathout,prefix1+prefix2+removeSpecialChars(archivo[:-4])),mode='w')
fichero.write(nota_out)
fichero.close()
elif os.path.isdir(fullPath):
print pathin,archivo,fullPath
processPath(fullPath,pathout,prefix1,prefix2+removeSpecialChars(archivo)+"_")
if __name__=='__main__':
pathin=sys.argv[1]
pathout=sys.argv[2]
prefix=sys.argv[3]
processPath(pathin,pathout,prefix)
zim-0.65/tests/ 0000775 0001750 0001750 00000000000 12615422672 013266 5 ustar jaap jaap 0000000 0000000 zim-0.65/tests/plugins.py 0000664 0001750 0001750 00000013025 12614412356 015317 0 ustar jaap jaap 0000000 0000000 # -*- coding: utf-8 -*-
# Copyright 2008-2013 Jaap Karssenberg
import tests
import os
from zim.plugins import *
from zim.fs import File
from zim.gui.propertiesdialog import PropertiesDialog
from tests.gui import setupGtkInterface
assert len(zim.plugins.__path__) > 1 # test __path__ magic
zim.plugins.__path__ = [os.path.abspath('./zim/plugins')] # set back default search path
class TestPluginClasses(tests.TestCase):
'''Test case to check coding and documentation of plugin classes'''
def runTest(self):
plugins = PluginManager.list_installed_plugins()
self.assertTrue(len(plugins) > 10)
self.assertTrue('spell' in plugins)
self.assertTrue('linkmap' in plugins)
pluginindex = File('data/manual/Plugins.txt').read()
seen = {
'name': set(),
'description': set(),
'help': set(),
}
for name in plugins:
#~ print '>>', name
klass = PluginManager.get_plugin_class(name)
# test plugin info
for key in ('name', 'description', 'author'):
self.assertTrue(
klass.plugin_info.get(key),
'Plugin %s misses info field \'%s\'' % (name, key)
)
for key in ('name', 'description', 'help'):
self.assertIn(key, klass.plugin_info, 'Plugin %s missing "%s"' % (name, key))
value = klass.plugin_info[key]
self.assertFalse(
value in seen[key],
'Value for \'%s\' in %s seen before - copy-paste error ?' % (key, name)
)
seen[key].add(value)
# test manual page present and at least documents preferences
page = klass.plugin_info['help']
self.assertTrue(page.startswith('Plugins:'), 'Help page for %s not valid' % name)
rellink = "+%s" % page[8:]
self.assertIn(rellink, pluginindex, 'Missing links "%s" in manual/Plugins.txt' % rellink)
file = File('data/manual/' + page.replace(':', '/').replace(' ', '_') + '.txt')
self.assertTrue(file.exists(), 'Missing file: %s' % file)
manual = file.read()
for pref in klass.plugin_preferences:
label = pref[2]
if '\n' in label:
label, x = label.split('\n', 1)
label = label.rstrip(',')
self.assertIn(label, manual, 'Preference "%s" for %s plugin not documented in manual page' % (label, name))
# test dependencies data
dep = klass.check_dependencies()
self.assertTrue(isinstance(dep,tuple))
check, dep = dep
self.assertTrue(isinstance(check,bool))
self.assertTrue(isinstance(dep,list))
for i in range(len(dep)):
self.assertTrue(isinstance(dep[i],tuple))
self.assertTrue(isinstance(dep[i][0],str))
self.assertTrue(isinstance(dep[i][1],bool))
self.assertTrue(isinstance(dep[i][2],bool))
class TestPluginManager(tests.TestCase):
'''Test case for TestManager infrastructure'''
def testLoadAndRemovePlugin(self):
manager = PluginManager()
self.assertEqual(len(manager), 0)
self.assertEqual(list(manager), [])
obj = manager.load_plugin('calendar')
self.assertEqual(len(manager), 1)
self.assertEqual(list(manager), ['calendar'])
self.assertEqual(manager['calendar'], obj)
obj1 = manager.load_plugin('calendar') # redundant call
self.assertEqual(obj1, obj)
self.assertEqual(len(manager), 1)
manager.remove_plugin('calendar')
self.assertEqual(len(manager), 0)
self.assertEqual(list(manager), [])
self.assertRaises(KeyError, manager.__getitem__, 'calendar')
manager.remove_plugin('calendar') # redundant call
def testLoadNonExistingPlugin(self):
manager = PluginManager()
self.assertRaises(ImportError, manager.load_plugin, 'nonexistingplugin')
def testProfileSwitch(self):
# Two lists of plugins without dependencies - with some overlap
list_a = ['attachmentbrowser', 'backlinkpane', 'calendar', 'distractionfree', 'insertsymbol']
list_b = ['calendar', 'distractionfree', 'insertsymbol', 'printtobrowser', 'quicknote']
manager = PluginManager()
for name in list_a:
manager.load_plugin(name)
self.assertEqual(manager.general_preferences['plugins'], list_a)
manager.general_preferences['plugins'] = list_b
self.assertEqual(sorted(manager._plugins), list_b)
class TestPlugins(tests.TestCase):
'''Test case to initiate all (loadable) plugins and load some extensions'''
def runTest(self):
manager = PluginManager()
preferences = manager.config.get_config_dict('/preferences.conf')
self.assertFalse(preferences.modified)
for name in PluginManager.list_installed_plugins():
klass = PluginManager.get_plugin_class(name)
if klass.check_dependencies_ok():
manager.load_plugin(name)
self.assertIn(name, manager)
self.assertFalse(preferences.modified,
'Plugin "%s" modified the preferences while loading' % name)
self.assertTrue(len(manager) > 3)
for i, name in enumerate(manager):
manager[name].preferences.emit('changed')
# Checking for exceptions and infinite recursion
self.assertTrue(i > 0)
#~ self.assertTrue(preferences.modified)
# If "False" the check while loading the plugins is not valid
# FIXME this detection is broken due to autosave in ConfigManager ...
notebook = tests.new_notebook(self.get_tmp_name())
ui = setupGtkInterface(self, notebook=notebook)
dialog = PropertiesDialog(ui) # random dialog
for obj in (
notebook,
notebook.index,
ui.mainwindow,
ui.mainwindow.pageview,
dialog,
):
manager.extend(obj)
for i, name in enumerate(manager):
manager[name].preferences.emit('changed')
# Checking for exceptions and infinite recursion
for name in manager:
#~ print "REMOVE:", name
self.assertIsInstance(manager[name], PluginClass)
manager.remove_plugin(name)
self.assertNotIn(name, manager)
self.assertTrue(len(manager) == 0)
zim-0.65/tests/bookmarksbar.py 0000664 0001750 0001750 00000020163 12600757677 016331 0 ustar jaap jaap 0000000 0000000 # -*- coding: utf-8 -*-
# Copyright 2015 Pavel_M .
# This is the test for BookmarksBar plugin.
# BookmarksBar is the plugin for Zim program
# by Jaap Karssenberg .
import tests
import gtk
from zim.notebook import Path
from zim.plugins.bookmarksbar import *
from zim.config import ConfigDict
from zim.gui.clipboard import Clipboard
import logging
logger = logging.getLogger('zim.plugins.bookmarksbar')
class TestBookmarksBar(tests.TestCase):
def setUp(self):
self.notebook = tests.new_notebook()
self.index = self.notebook.index
def runTest(self):
'''There is one long test.'''
ui = MockUI()
ui.notebook = self.notebook
ui.page = Path('Test:foo')
uistate = ConfigDict()
self.assertTrue(self.notebook.get_page(ui.page).exists())
PATHS = ('Parent:Daughter:Granddaughter',
'Test:tags', 'Test:foo', 'Books')
LEN_PATHS = len(PATHS)
PATHS_NAMES = {PATHS[0]:'name 1', PATHS[1]:'name 2', PATHS[2]:'name 3'}
# Check correctness of reading uistate.
uistate.setdefault('bookmarks', [])
uistate.setdefault('bookmarks_names', {})
uistate['bookmarks'] = list(PATHS)
uistate['bookmarks_names'] = dict(PATHS_NAMES)
Bar = BookmarkBar(ui, uistate, get_page_func = lambda: '')
self.assertTrue(Bar.paths == list(PATHS))
self.assertTrue(Bar.paths_names == PATHS_NAMES)
uistate['bookmarks'] = []
uistate['bookmarks_names'] = {}
Bar = BookmarkBar(ui, uistate, get_page_func = lambda: '')
self.assertTrue(Bar.paths == [])
self.assertTrue(Bar.paths_names == {})
# Add paths to the beginning of the bar.
for i, path in enumerate(PATHS):
Bar._add_new(path, add_bookmarks_to_beginning = True)
self.assertTrue(len(Bar.paths) == i + 1)
self.assertTrue(Bar.paths == list(reversed(PATHS)))
# Add paths to the end of the bar.
Bar.paths = []
for i, path in enumerate(PATHS):
Bar._add_new(path, add_bookmarks_to_beginning = False)
self.assertTrue(len(Bar.paths) == i + 1)
self.assertTrue(Bar.paths == list(PATHS))
# Check that the same path can't be added to the bar.
Bar._add_new(PATHS[0])
Bar._add_new(PATHS[1])
self.assertTrue(Bar.paths == list(PATHS))
# Delete paths from the bar.
for i, button in enumerate(Bar.container.get_children()[2:]):
path = button.zim_path
self.assertTrue(path in Bar.paths)
Bar.delete(button.zim_path)
self.assertTrue(len(Bar.paths) == LEN_PATHS - i - 1)
self.assertTrue(path not in Bar.paths)
self.assertTrue(Bar.paths == [])
# Check short page names.
uistate['show_full_page_name'] = False
for path in PATHS:
Bar._add_new(path)
self.assertTrue(Bar.paths == list(PATHS))
for i, button in enumerate(Bar.container.get_children()[2:]):
self.assertTrue(PATHS[i] == button.zim_path)
self.assertTrue(Path(PATHS[i]).basename == button.get_label())
uistate['show_full_page_name'] = True
# Delete all bookmarks from the bar.
Bar.delete_all()
self.assertTrue(Bar.paths == [])
# Check restriction of max bookmarks in the bar.
pagelist = set(self.index.list_pages(None))
_enhanced_pagelist = set()
for page in pagelist:
_enhanced_pagelist.update( set(self.index.list_pages(page)) )
if len(_enhanced_pagelist) > MAX_BOOKMARKS:
break
pagelist.update(_enhanced_pagelist)
self.assertTrue(len(pagelist) > MAX_BOOKMARKS)
pagelist = list(pagelist)
for page in pagelist:
Bar._add_new(page.name)
self.assertTrue(len(Bar.paths) == MAX_BOOKMARKS)
self.assertTrue(Bar.paths == [a.name for a in pagelist[:MAX_BOOKMARKS]])
Bar.delete_all()
# Check 'save' option in preferences.
for i, path in enumerate(PATHS):
Bar.on_preferences_changed({'save':False, 'add_bookmarks_to_beginning':False})
Bar._add_new(path)
self.assertTrue(uistate['bookmarks'] == [])
Bar.on_preferences_changed({'save':True, 'add_bookmarks_to_beginning':False})
self.assertTrue(uistate['bookmarks'] == list(PATHS[:i+1]))
self.assertTrue(uistate['bookmarks'] == list(PATHS))
# Check changing a bookmark.
self.assertTrue('Test' not in Bar.paths)
self.assertTrue('Books' in Bar.paths)
Bar.change_bookmark('Books', 'Books')
self.assertTrue(Bar.paths == list(PATHS))
_b_paths = [a for a in Bar.paths if a != 'Books']
Bar.change_bookmark('Books', 'Test')
self.assertTrue('Test' in Bar.paths)
self.assertTrue('Books' not in Bar.paths)
_e_paths = [a for a in Bar.paths if a != 'Test']
self.assertTrue(_b_paths == _e_paths)
Bar.change_bookmark('Test', 'Books')
self.assertTrue(Bar.paths == list(PATHS))
# Check deleting a bookmark after deleting a page in the notebook.
self.assertTrue(len(Bar.paths) == LEN_PATHS)
for i, path in enumerate(PATHS):
self.assertTrue(path in Bar.paths)
self.notebook.delete_page(Path(path))
self.assertTrue(path not in Bar.paths)
self.assertTrue(len(Bar.paths) == LEN_PATHS - i - 1)
self.assertTrue(Bar.paths == [])
# Check reordering bookmarks.
PATHS_2 = ('1','2','3','4','5')
PATHS_NAMES_2 = {PATHS_2[0]:'11', PATHS_2[1]:'22', PATHS_2[2]:'33'}
Bar.paths = list(PATHS_2)
Bar.move_bookmark(PATHS_2[2], PATHS_2[2], 'left')
self.assertTrue(Bar.paths == list(PATHS_2))
Bar.move_bookmark(PATHS_2[3], PATHS_2[3], 'right')
self.assertTrue(Bar.paths == list(PATHS_2))
Bar.move_bookmark('3', '1', 'left')
self.assertTrue(Bar.paths == ['3','1','2','4','5'])
Bar.move_bookmark('5', '1', 'left')
self.assertTrue(Bar.paths == ['3','5','1','2','4'])
Bar.move_bookmark('5', '1', 'right')
self.assertTrue(Bar.paths == ['3','1','5','2','4'])
Bar.move_bookmark('3', '4', 'right')
self.assertTrue(Bar.paths == ['1','5','2','4','3'])
Bar.move_bookmark('5', '4', '-')
self.assertTrue(Bar.paths == ['1','5','2','4','3'])
# CHECK RENAMING
# Check rename_bookmark and save options.
Bar.paths = list(PATHS_2)
button = gtk.Button(label = PATHS_2[0], use_underline = False)
button.zim_path = PATHS_2[0]
Bar.on_preferences_changed({'save':True, 'add_bookmarks_to_beginning':False})
Bar._reload_bar()
def rename_check(label, path, paths_names, path_names_uistate):
self.assertTrue(button.get_label() == label)
self.assertTrue(button.zim_path == path)
self.assertTrue(Bar.paths_names == paths_names)
self.assertTrue(uistate['bookmarks_names'] == path_names_uistate)
rename_check(PATHS_2[0], PATHS_2[0], {}, {})
Clipboard.set_text('new name')
Bar.rename_bookmark(button)
rename_check('new name', PATHS_2[0], {PATHS_2[0]:'new name'}, {PATHS_2[0]:'new name'})
Bar.on_preferences_changed({'save':False, 'add_bookmarks_to_beginning':False})
rename_check('new name', PATHS_2[0], {PATHS_2[0]:'new name'}, {})
Bar.on_preferences_changed({'save':True, 'add_bookmarks_to_beginning':False})
rename_check('new name', PATHS_2[0], {PATHS_2[0]:'new name'}, {PATHS_2[0]:'new name'})
Bar.rename_bookmark(button)
rename_check(PATHS_2[0], PATHS_2[0], {}, {})
# Check delete with renaming.
Bar.on_preferences_changed({'save':True, 'add_bookmarks_to_beginning':False})
paths_names_copy = dict(PATHS_NAMES_2)
Bar.paths_names = dict(PATHS_NAMES_2)
for key in PATHS_NAMES_2:
Bar.delete(key)
del paths_names_copy[key]
self.assertTrue(Bar.paths_names == paths_names_copy)
self.assertTrue(uistate['bookmarks_names'] == Bar.paths_names)
# Check delete all with renaming.
Bar.paths_names = dict(PATHS_NAMES_2)
Bar.delete_all()
self.assertTrue(Bar.paths_names == {})
self.assertTrue(uistate['bookmarks_names'] == {})
# Check change bookmark with renaming.
Bar.paths = list(PATHS_2)
Bar.paths_names = dict(PATHS_NAMES_2)
paths_names_copy = dict(PATHS_NAMES_2)
paths_names_copy.pop(PATHS_2[0], None)
Bar.change_bookmark(PATHS_2[0], 'new path')
self.assertTrue(Bar.paths_names == paths_names_copy)
self.assertTrue(Bar.paths == ['new path'] + list(PATHS_2[1:]))
# Check that paths and paths_names didn't change in the process.
self.assertTrue(PATHS_2 == ('1','2','3','4','5'))
self.assertTrue(PATHS_NAMES_2 == {PATHS_2[0]:'11', PATHS_2[1]:'22', PATHS_2[2]:'33'})
class MockUI(tests.MockObject):
page = None
notebook = None
zim-0.65/tests/formats.py 0000664 0001750 0001750 00000050233 12375712515 015317 0 ustar jaap jaap 0000000 0000000 # -*- coding: utf-8 -*-
# Copyright 2008-2012 Jaap Karssenberg
'''Test cases for the zim.formats module.'''
from __future__ import with_statement
import tests
from zim.formats import *
from zim.fs import File
from zim.notebook import Path, Link
from zim.parsing import link_type
from zim.templates import Template
if not ElementTreeModule.__name__.endswith('cElementTree'):
print 'WARNING: using ElementTree instead of cElementTree'
class TestFormatMixin(object):
'''Mixin for testing formats, uses data in C{tests/data/formats/}'''
reference_xml = File('tests/data/formats/parsetree.xml').read().rstrip('\n')
reference_data = {
'wiki': 'wiki.txt',
'plain': 'plain.txt',
'html': 'export.html',
'latex': 'export.tex',
'markdown': 'export.markdown',
'reST': 'export.rst',
}
def testFormatInfo(self):
for key in ('name', 'desc', 'mimetype', 'extension'):
self.assertIsInstance(self.format.info[key], basestring,
msg='Invalid key "%s" in format info' % key)
for key in ('native', 'import', 'export'):
self.assertIsInstance(self.format.info[key], bool,
msg='Invalid key "%s" in format info' % key)
if self.format.info['native'] or self.format.info['import']:
self.assertTrue(hasattr(self.format, 'Parser'))
if self.format.info['native'] or self.format.info['export']:
self.assertTrue(hasattr(self.format, 'Dumper'))
def getReferenceData(self):
'''Returns reference data from C{tests/data/formats/} for the
format being tested.
'''
name = self.format.info['name']
assert name in self.reference_data, 'No reference data for format "%s"' % name
path = 'tests/data/formats/' + self.reference_data[name]
text = File(path).read()
# No absolute paths ended up in reference
pwd = Dir('.')
self.assertFalse(pwd.path in text, 'Absolute path ended up in reference')
self.assertFalse(pwd.user_path in text, 'Absolute path ended up in reference')
return text
def testFormat(self):
'''Test if formats supports full syntax
Uses data in C{tests/data/formats} as reference data.
'''
# Dumper
wanted = self.getReferenceData()
reftree = tests.new_parsetree_from_xml(self.reference_xml)
linker = StubLinker(Dir('tests/data/formats'))
dumper = self.format.Dumper(linker=linker)
result = ''.join(dumper.dump(reftree))
#~ print '\n' + '>'*80 + '\n' + result + '\n' + '<'*80 + '\n'
self.assertMultiLineEqual(result, wanted)
self.assertNoTextMissing(result, reftree)
# Check that dumper did not modify the tree
self.assertMultiLineEqual(reftree.tostring(), self.reference_xml)
# partial dumper
parttree = tests.new_parsetree_from_xml("\ntry these bold, italic")
result = ''.join(dumper.dump(parttree))
#~ print ">>>%s<<<" % result
self.assertFalse(result.endswith('\n')) # partial should not end with "\n"
# Parser
if not hasattr(self.format, 'Parser'):
return
input = wanted
parser = self.format.Parser()
result = parser.parse(input)
if self.format.info['native']:
self.assertMultiLineEqual(result.tostring(), self.reference_xml)
else:
self.assertTrue(len(result.tostring().splitlines()) > 10)
# Quick check that we got back *something*
string = ''.join(dumper.dump(result))
# now we may have loss of formatting, but text should all be there
#~ print '\n' + '>'*80 + '\n' + string + '\n' + '<'*80 + '\n'
self.assertNoTextMissing(string, reftree)
_nonalpha_re = re.compile('\W')
def assertNoTextMissing(self, text, tree):
'''Assert that no plain text from C{tree} is missing in C{text}
intended to make sure that even for lossy formats all information
is preserved.
'''
# TODO how to handle objects ??
assert isinstance(text, basestring)
offset = 0
for elt in tree._etree.iter():
if elt.tag == 'img':
elttext = (elt.tail) # img text is optional
else:
elttext = (elt.text, elt.tail)
for wanted in elttext:
if not wanted:
continue
wanted = self._nonalpha_re.sub(' ', wanted)
# Non-alpha chars may be replaced with escapes
# so no way to hard test them
if wanted.isspace():
continue
for piece in wanted.strip().split():
#~ print "| >>%s<< @ offset %i" % (piece, offset)
try:
start = text.index(piece, offset)
except ValueError:
self.fail('Could not find text piece "%s" in text after offset %i\n>>>%s<<<' % (piece, offset, text[offset:offset+100]))
else:
offset = start + len(piece)
class TestListFormats(tests.TestCase):
def runTest(self):
for desc in list_formats(EXPORT_FORMAT):
name = canonical_name(desc)
format = get_format(name)
self.assertTrue(format.info['export'])
for desc in list_formats(TEXT_FORMAT):
name = canonical_name(desc)
format = get_format(name)
self.assertTrue(format.info['export'])
self.assertTrue(format.info['mimetype'].startswith('text/'))
class TestParseTree(tests.TestCase):
def setUp(self):
self.xml = '''\
Head 1
Head 2
Head 3
Head 4
Head 5
Head 6
Head 7
Head 8
'''
def teststring(self):
'''Test ParseTree.fromstring() and .tostring()'''
tree = ParseTree()
r = tree.fromstring(self.xml)
self.assertEqual(id(r), id(tree)) # check return value
text = tree.tostring()
self.assertEqual(text, self.xml)
def testcleanup_headings(self):
'''Test ParseTree.cleanup_headings()'''
tree = ParseTree().fromstring(self.xml)
wanted = '''\
Head 1
Head 2
Head 3
Head 4
Head 5
Head 6
Head 7
Head 8
'''
tree.cleanup_headings(offset=1, max=4)
text = tree.tostring()
self.assertEqual(text, wanted)
def testGetHeading(self):
'''Test that ParseTree.get_heading() returns the first header's text.
'''
tree = ParseTree().fromstring(self.xml)
self.assertEqual(tree.get_heading(), "Head 1")
def testSetHeading(self):
'''Test ParseTree.set_heading()'''
tree = ParseTree().fromstring(self.xml)
tree.set_heading('Foo')
wanted = '''\
Foo
Head 2
Head 3
Head 4
Head 5
Head 6
Head 7
Head 8
'''
text = tree.tostring()
self.assertEqual(text, wanted)
def testExtend(self):
tree1 = ParseTree().fromstring(self.xml)
tree2 = ParseTree().fromstring(self.xml)
tree = tree1 + tree2
wanted = '''\
Head 1
Head 2
Head 3
Head 4
Head 5
Head 6
Head 7
Head 8
Head 1
Head 2
Head 3
Head 4
Head 5
Head 6
Head 7
Head 8
'''
text = tree.tostring()
self.assertEqual(text, wanted)
def testGetEndsWithNewline(self):
for xml, newline in (
('foo', False),
('foo', False),
('foo\n', True),
('foo\n', True),
('foo\n
', False),
('foo', True),
('foo', True),
('foo', True),
):
tree = ParseTree().fromstring(xml)
self.assertEqual(tree.get_ends_with_newline(), newline)
def testGetObjects(self):
xml = File('tests/data/formats/parsetree.xml').read().rstrip('\n')
tree = tests.new_parsetree_from_xml(xml)
objects = list(tree.get_objects())
self.assertTrue(len(objects) >= 2)
def testFindall(self):
tree = ParseTree().fromstring(self.xml)
wanted = [
(1, 'Head 1'),
(2, 'Head 2'),
(3, 'Head 3'),
(2, 'Head 4'),
(5, 'Head 5'),
(4, 'Head 6'),
(5, 'Head 7'),
(6, 'Head 8'),
]
found = []
for elt in tree.findall(HEADING):
found.append((int(elt.get('level')), elt.gettext()))
self.assertEqual(found, wanted)
def testReplace(self):
def replace(elt):
# level 2 becomes 3
# level 3 is replaced by text
# level 4 is removed
# level 5 is skipped
# level 1 and 6 stay as is
level = int(elt.get('level'))
if level == 2:
elt.attrib['level'] = 3
return elt
elif level == 3:
return DocumentFragment(*elt)
elif level == 4:
return None
elif level == 5:
raise VisitorSkip
else:
return elt
tree = ParseTree().fromstring(self.xml)
wanted = '''\
Head 1
Head 2
Head 3
Head 4
Head 5
Head 7
Head 8
'''
tree.replace(HEADING, replace)
text = tree.tostring()
self.assertEqual(text, wanted)
class TestTextFormat(tests.TestCase, TestFormatMixin):
def setUp(self):
self.format = get_format('plain')
class TestWikiFormat(TestTextFormat):
def setUp(self):
self.format = get_format('wiki')
notebook = tests.new_notebook()
self.page = notebook.get_page(Path('Foo'))
#~ def testHeaders(self):
#~ text = '''\
#~ Content-Type: text/x-zim-wiki
#~ Wiki-Format: zim 0.26
#~ Creation-Date: Unkown
#~ Modification-Date: Wed, 06 Aug 2008 22:17:29 +0200
#~
#~ foo bar
#~ '''
#~ tree = self.format.Parser().parse(text)
#~ print '>>>\n'+tostring(tree)+'\n<<<\n'
#~ self.assertEquals(tree.getroot().attrib['Content-Type'], 'text/x-zim-wiki')
#~ output = self.format.Dumper().dump(tree)
#~ self.assertEqual(output, text.splitlines(True))
def testUnicodeBullet(self):
'''Test support for unicode bullets in source'''
input = u'''\
A list
• foo
• bar
• baz
'''
text = u'''\
A list
* foo
* bar
* baz
'''
tree = self.format.Parser().parse(input)
#~ print tree.tostring()
output = self.format.Dumper().dump(tree)
self.assertEqual(''.join(output), text)
def testLink(self):
'''Test iterator function for link'''
# + check for bugs in link encoding
text = '[[FooBar]] [[Foo|]] [[|Foo]] [[||]]'
tree = self.format.Parser().parse(text)
#~ print tree.tostring()
found = 0
for elt in tree.findall(LINK):
self.assertTrue(elt.gettext())
self.assertTrue(elt.get('href'))
link = Link(self.page, **elt.attrib)
self.assertEqual(elt.attrib['href'], link.href)
found += 1
self.assertEqual(found, 3)
def testBackward(self):
'''Test backward compatibility for wiki format'''
input = u'''\
test 1 2 3
Some Verbatim block
here ....
test 4 5 6
'''
wanted = u'''\
test 1 2 3
\'''
Some Verbatim block
here ....
\'''
test 4 5 6
'''
xml = '''\
test 1 2 3
Some Verbatim block
here ....
test 4 5 6
'''
t = self.format.Parser(version='Unknown').parse(input)
self.assertEqual(t.tostring(), xml)
output = self.format.Dumper().dump(t)
self.assertEqual(output, wanted.splitlines(True))
def testList(self):
def check(text, xml, wanted=None):
if wanted is None:
wanted = text
tree = self.format.Parser().parse(text)
#~ print '>>>\n' + tree.tostring() + '\n<<<'
self.assertEqual(tree.tostring(), xml)
lines = self.format.Dumper().dump(tree)
result = ''.join(lines)
#~ print '>>>\n' + result + '<<<'
self.assertEqual(result, wanted)
# Bullet list (unordered list)
text = '''\
* foo
* bar
* sub list
* here
* hmmm
'''
xml = '''\
'''
check(text, xml)
# Numbered list (ordered list)
text = '''\
1. foo
2. bar
a. sub list
b. here
3. hmmm
'''
xml = '''\
- foo
- bar
- sub list
- here
- hmmm
'''
check(text, xml)
text = '''\
A. foo
B. bar
C. hmmm
'''
xml = '''\
- foo
- bar
- hmmm
'''
check(text, xml)
text = '''\
10. foo
11. bar
12. hmmm
'''
xml = '''\
- foo
- bar
- hmmm
'''
check(text, xml)
# Inconsistent lists
# ( If first item is number, make all items numbered in sequence
# Otherwise numers will be turned into bullets )
text = '''\
1. foo
4. bar
* hmmm
a. dus
'''
xml = '''\
- foo
- bar
- hmmm
- dus
'''
wanted = '''\
1. foo
2. bar
3. hmmm
4. dus
'''
check(text, xml, wanted)
text = '''\
* foo
4. bar
a. hmmm
* dus
'''
xml = '''\
'''
wanted = '''\
* foo
* bar
* hmmm
* dus
'''
check(text, xml, wanted)
# Mixed sub-list
text = '''\
* foo
* bar
1. sub list
2. here
* hmmm
'''
xml = '''\
'''
check(text, xml)
# Indented list
text = '''\
* foo
* bar
1. sub list
2. here
* hmmm
'''
xml = '''\
'''
check(text, xml)
# Double indent sub-list ?
text = '''\
* foo
* bar
1. sub list
2. here
* hmmm
'''
xml = '''\
'''
check(text, xml)
# This is not a list
text = '''\
foo.
dus ja.
1.3
'''
xml = '''\
foo.
dus ja.
1.3
'''
check(text, xml)
def testIndent(self):
# Test some odditied pageview can give us
xml = '''\
foo
bar
sub list
here
hmmm
'''
wanted = '''\
foo
bar
sub list
here
hmmm
'''
tree = ParseTree()
tree.fromstring(xml)
text = ''.join( self.format.Dumper().dump(tree) )
self.assertEqual(text, wanted)
class TestHtmlFormat(tests.TestCase, TestFormatMixin):
def setUp(self):
self.format = get_format('html')
notebook = tests.new_notebook()
self.page = notebook.get_page(Path('Foo'))
def testEncoding(self):
'''Test HTML encoding'''
builder = ParseTreeBuilder()
builder.start(FORMATTEDTEXT)
builder.append(PARAGRAPH, None, '"foo" & "bar"\n')
builder.end(FORMATTEDTEXT)
tree = builder.get_parsetree()
html = self.format.Dumper(linker=StubLinker()).dump(tree)
self.assertEqual(''.join(html),
'\n<foo>"foo" & "bar"</foo>\n
\n')
# TODO add test using http://validator.w3.org
def testEmptyLines(self):
builder = ParseTreeBuilder()
builder.start(FORMATTEDTEXT)
builder.append(HEADING, {'level':1}, 'head1')
builder.text('\n')
builder.append(HEADING, {'level':2}, 'head2')
builder.end(FORMATTEDTEXT)
tree = builder.get_parsetree()
html = self.format.Dumper(
linker=StubLinker(),
template_options={'empty_lines': 'default'}
).dump(tree)
self.assertEqual(''.join(html),
'head1
\n\n'
'
\n\n'
'head2
\n\n'
)
html = self.format.Dumper(
linker=StubLinker(),
template_options={'empty_lines': 'remove'}
).dump(tree)
self.assertEqual(''.join(html),
'head1
\n\n'
'head2
\n\n'
)
html = self.format.Dumper(
linker=StubLinker(),
template_options={'empty_lines': 'Remove'} # case sensitive
).dump(tree)
self.assertEqual(''.join(html),
'head1
\n\n'
'head2
\n\n'
)
def testLineBreaks(self):
builder = ParseTreeBuilder()
builder.start(FORMATTEDTEXT)
builder.append(PARAGRAPH, None,
'bla bla bla\n'
'bla bla bla\n'
)
builder.end(FORMATTEDTEXT)
tree = builder.get_parsetree()
html = self.format.Dumper(
linker=StubLinker(),
template_options={'line_breaks': 'default'}
).dump(tree)
self.assertEqual(''.join(html),
'\n'
'bla bla bla
\n'
'bla bla bla\n'
'
\n'
)
html = self.format.Dumper(
linker=StubLinker(),
template_options={'line_breaks': 'remove'}
).dump(tree)
self.assertEqual(''.join(html),
'\n'
'bla bla bla\n'
'bla bla bla\n'
'
\n'
)
class TestMarkdownFormat(tests.TestCase, TestFormatMixin):
def setUp(self):
self.format = get_format('markdown')
class TestRstFormat(tests.TestCase, TestFormatMixin):
def setUp(self):
self.format = get_format('rst')
class LatexLoggingFilter(tests.LoggingFilter):
logger = 'zim.formats.latex'
message = ('No document type set in template', 'Could not find latex equation')
class TestLatexFormat(tests.TestCase, TestFormatMixin):
def setUp(self):
self.format = get_format('latex')
def testFormat(self):
with LatexLoggingFilter():
TestFormatMixin.testFormat(self)
def testFormatReference(self):
# Double check reference did not get broken in updating
text = self.getReferenceData()
# Inlined equation is there
self.assertFalse('equation001.png' in text, 'This equation should be inlined')
self.assertTrue(r'\begin{math}' in text)
self.assertTrue(r'\end{math}' in text)
def testEncode(self):
'''test the escaping of certain characters'''
format = get_format('latex')
input = r'\foo $ % ^ \% bar < >'
wanted = r'$\backslash$foo \$ \% \^{} $\backslash$\% bar \textless{} \textgreater{}'
self.assertEqual(format.Dumper.encode_text(PARAGRAPH, input), wanted)
def testDocumentType(self):
builder = ParseTreeBuilder()
builder.start(FORMATTEDTEXT)
builder.append(HEADING, {'level':1}, 'head1')
builder.text('\n')
builder.append(HEADING, {'level':2}, 'head2')
builder.end(FORMATTEDTEXT)
tree = builder.get_parsetree()
for type, head1 in (
('report', 'chapter'),
('article', 'section'),
('book', 'part'),
):
lines = self.format.Dumper(
linker=StubLinker(),
template_options={'document_type': type}
).dump(tree)
self.assertIn(head1, ''.join(lines))
class StubFile(object):
def __init__(self, path, text):
self.path = path
self.text = text
def read(self):
return self.text
class TestOldParseTreeBuilder(tests.TestCase):
def runTest(self):
'''Test OldParseTreeBuilder class'''
# - Test \n before and after h / p / pre
# - Test break line into lines
input = '''\
foobarbaz
dusja
hmm
foo
bar
dus ja hmm
dus ja
grrr
foo
bar
.
'''
wanted = '''\
foo
bar
baz
dus
ja
hmm
foo
bar
dus ja hmm
dus ja
grrr
foo
bar
.
'''
# For some reason this does not work with cElementTree.XMLBuilder ...
from xml.etree.ElementTree import XMLTreeBuilder
builder = XMLTreeBuilder(target=OldParseTreeBuilder())
builder.feed(input)
root = builder.close()
tree = ParseTree(root)
self.assertEqual(tree.tostring(), wanted)
zim-0.65/tests/objectmanager.py 0000664 0001750 0001750 00000004035 12521361062 016432 0 ustar jaap jaap 0000000 0000000 # -*- coding: utf-8 -*-
# Copyright 2011 Jaap Karssenberg
import tests
from zim.objectmanager import *
class TestObjectManager(tests.TestCase):
def runTest(self):
'''Test object manager for inline objects'''
manager = ObjectManager
# registering
self.assertFalse(manager.is_registered('classa'))
self.assertFalse(manager.is_registered('classb'))
self.assertFalse(manager.is_registered('foo'))
manager.register_object('classa', classafactory)
manager.register_object('classb', ClassB)
self.assertTrue(manager.is_registered('classa'))
self.assertTrue(manager.is_registered('classb'))
self.assertFalse(manager.is_registered('foo'))
# get objects
self.assertEqual(list(manager.get_active_objects('classa')), [])
self.assertEqual(list(manager.get_active_objects('classb')), [])
obj = manager.get_object('classa', {}, '')
self.assertTrue(isinstance(obj, ClassA))
self.assertEqual(list(manager.get_active_objects('classa')), [obj])
self.assertEqual(list(manager.get_active_objects('classb')), [])
self.assertTrue(isinstance(manager.get_object('classb', {}, ''), ClassB))
self.assertTrue(isinstance(manager.get_object('foo', {}, ''), FallbackObject))
# unregister
self.assertTrue(manager.is_registered('classa'))
self.assertTrue(manager.unregister_object('classa'))
self.assertFalse(manager.is_registered('classa'))
self.assertFalse(manager.unregister_object('classa'))
# find plugin
from zim.plugins.sourceview import SourceViewPlugin
self.assertEqual(
manager.find_plugin('code'),
('sourceview', 'Source View', True, SourceViewPlugin, None)
)
def classafactory(attrib, text):
return ClassA(attrib, text)
class ClassA(CustomObjectClass):
pass
class ClassB(CustomObjectClass):
pass
class TestFallbackObject(tests.TestCase):
def runTest(self):
attrib = {'type': 'foo', 'lang': 'text/html', 'foo': 'bar'}
text = '''test 123\n'''
obj = FallbackObject(attrib, text)
self.assertEqual(obj.get_data(), text)
self.assertEqual(obj.get_attrib(), attrib)
zim-0.65/tests/inlinecalculator.py 0000664 0001750 0001750 00000003312 12411243523 017155 0 ustar jaap jaap 0000000 0000000 # -*- coding: utf-8 -*-
# Copyright 2008 Jaap Karssenberg
import tests
from zim.plugins import PluginManager
@tests.slowTest
class TestPrintToBrowser(tests.TestCase):
def runTest(self):
'Test InlineCalculator plugin'
pluginklass = PluginManager.get_plugin_class('inlinecalculator')
plugin = pluginklass()
for text, wanted in (
('3 + 4 =', '3 + 4 = 7'),
('3 + 4 = 1', '3 + 4 = 7'),
('3 + 4 = 1 ', '3 + 4 = 7 '),
('10 / 3 =', '10 / 3 = 3.33333333333'), # divide integers to float !
('milage: 3 + 4 =', 'milage: 3 + 4 = 7'),
('3 + 4 = 7 + 0.5 = ', '3 + 4 = 7 + 0.5 = 7.5'),
('''\
5.5
4.3
3.1
--- +
''',
'''\
5.5
4.3
3.1
--- +
12.9
''' ),
):
result = plugin.process_text(text)
self.assertEqual(result, wanted)
# Tests from clac.py self test
for test in '''\
1+2 == 3
sqrt(-1) == j
-2*asin(-1) == pi
abs(sin(pi)) < 1e-9
abs(1-cos(0)) < 1e-9
round( 3.1 + -4.8j) == (3-5j)
ceil( 3.1 + -4.8j) == (4-4j)
abs( 3-4j) == 5
degrees(pi) == 180
radians(180) == pi
abs( exp(j*pi) + 1 ) < 1e-9
# pow(1.2,3.4) == 1.2**3.4
ldexp(1.2,3) == 1.2 * 2 ** 3
modf(1.2)[1] == 1
log(81,3) == 4
gcd(6,8) == 2
lcm(6,8) == 24
angle( exp( j*pi ) ) == pi
# log(-1)**2 == -1*pow(pi,2)
round( degrees(phase( e**(2j)))) == 115
# sum( [ round(42 * exp(j*2*x*pi/4)) for x in range(4)] ) == 0
oct(8) == '010'
0x42-042-42 == -10
# 1k == 1024
# 1m == 2**20
# 1g == 2**30
2**10-1 == 1023
# 2**1k == 2**1024
'''.splitlines():
if test.startswith('#'):
continue
#~ print 'TESTING:', test
self.assertTrue(plugin.safe_eval(test))
self.assertRaises(Exception, plugin.process_text, 'open("/etc/passwd")') # global
self.assertRaises(Exception, plugin.process_text, 'self') # local
zim-0.65/tests/templates.py 0000664 0001750 0001750 00000042203 12411261574 015633 0 ustar jaap jaap 0000000 0000000 # -*- coding: utf-8 -*-
# Copyright 2008-2014 Jaap Karssenberg
'''Test cases for the zim.templates module.'''
from __future__ import with_statement
import tests
from zim.fs import File, Dir, FileNotFoundError
from zim.templates import *
from zim.templates.parser import *
from zim.templates.expression import *
from zim.templates.expressionparser import *
from zim.templates.processor import *
from zim.parser import SimpleTreeElement, SimpleTreeBuilder, BuilderTextBuffer
E = SimpleTreeElement
class TestExpressionParser(tests.TestCase):
def runTest(self):
## Test atoms
p = ExpressionParser()
for text, wanted in (
('True', ExpressionLiteral(True)),
('False', ExpressionLiteral(False)),
('None', ExpressionLiteral(None)),
('"foo\\tbar"', ExpressionLiteral("foo\tbar")),
('123', ExpressionLiteral(123)),
('1.2', ExpressionLiteral(1.2)),
('1E+3', ExpressionLiteral(1E+3)),
('x', ExpressionParameter('x')),
('foo.bar', ExpressionParameter('foo.bar')),
):
self.assertEqual(p.parse(text), wanted)
## Test compound expressions
p = ExpressionParser()
for text, wanted in (
('x or y', ExpressionOperator(
operator.or_,
ExpressionParameter('x'),
ExpressionParameter('y')
)),
('x == y', ExpressionOperator(
operator.eq,
ExpressionParameter('x'),
ExpressionParameter('y')
)),
('not x', ExpressionUnaryOperator(
operator.not_,
ExpressionParameter('x')
)),
('[1, a, True]', ExpressionList([
ExpressionLiteral(1),
ExpressionParameter('a'),
ExpressionLiteral(True),
])),
('[[1, a], [True, False]]', ExpressionList([
ExpressionList([
ExpressionLiteral(1),
ExpressionParameter('a'),
]),
ExpressionList([
ExpressionLiteral(True),
ExpressionLiteral(False),
])
])),
('func(1, a)', ExpressionFunctionCall(
ExpressionParameter('func'),
ExpressionList([
ExpressionLiteral(1),
ExpressionParameter('a'),
])
)),
('func([1, a])', ExpressionFunctionCall(
ExpressionParameter('func'),
ExpressionList([
ExpressionList([
ExpressionLiteral(1),
ExpressionParameter('a'),
])
])
)),
('func(1, func(a))', ExpressionFunctionCall(
ExpressionParameter('func'),
ExpressionList([
ExpressionLiteral(1),
ExpressionFunctionCall(
ExpressionParameter('func'),
ExpressionList([
ExpressionParameter('a'),
])
)
])
)),
('[func(1, a), x == y]', ExpressionList([
ExpressionFunctionCall(
ExpressionParameter('func'),
ExpressionList([
ExpressionLiteral(1),
ExpressionParameter('a'),
])
),
ExpressionOperator(
operator.eq,
ExpressionParameter('x'),
ExpressionParameter('y')
)
])),
):
self.assertEqual(p.parse(text), wanted)
## Test operator precedence
expr = ExpressionParser().parse('a or b and not c < d and f or x')
# Read as: '(a or ((b and ((not (c < d)) and f)) or x))'
wanted = ExpressionOperator(
operator.or_,
ExpressionParameter('a'),
ExpressionOperator(
operator.or_,
ExpressionOperator(
operator.and_,
ExpressionParameter('b'),
ExpressionOperator(
operator.and_,
ExpressionUnaryOperator(
operator.not_,
ExpressionOperator(
operator.lt,
ExpressionParameter('c'),
ExpressionParameter('d')
)
),
ExpressionParameter('f')
)
),
ExpressionParameter('x')
)
)
#~ print '\nEXPRESSION:', expr
self.assertEqual(expr, wanted)
## Invalid syntaxes
p = ExpressionParser()
for t in (
'x > y > z', # chaining comparison operators not allowed
'x > not y', # 'not' has higher precendence, can not appear here
'not not x', # double operator
'x and and y', # double operator
'[x,,y]', # double "," - missing element
'(1,2)', # Tuple not supported
'1 2', # Two expressions, instead of one
'1, 2', # Two expressions, instead of one
'1.2.3', # Invalid literal
'<>', # just an operator
'', # empty expression has no meaning
):
self.assertRaises(ExpressionSyntaxError, p.parse, t)
# TODO check for meaningfull error messages for these
# TODO any edge cases ?
class TestExpression(tests.TestCase):
def runTest(self):
expr = ExpressionList([
ExpressionLiteral('foooo'),
ExpressionParameter('foo'),
ExpressionParameter('a.b'),
ExpressionOperator(
operator.le,
ExpressionParameter('n'),
ExpressionLiteral(2)
),
ExpressionFunctionCall(
ExpressionParameter('addone'),
ExpressionList([
ExpressionParameter('n')
])
),
])
result = expr( {
'foo': 'FOO',
'a': {
'b': 'BAR'
},
'n': 1,
'addone': ExpressionFunction(lambda a: a+1)
} )
wanted = ['foooo', 'FOO', 'BAR', True, 2]
self.assertEqual(result, wanted)
class TestExpressionFunctionCall(tests.TestCase):
def runTest(self):
class Foo(object):
def __init__(self, prefix):
self.prefix = prefix
@ExpressionFunction
def string(self, string):
return self.prefix + string
# Test ExpressionFunction works as decorator (bound method)
foo = Foo('FOO')
self.assertIsInstance(foo.string, ExpressionFunction)
self.assertEqual(foo.string('bar'), 'FOObar')
# Test get builtin from dict
mydict = {
'len': ExpressionFunction(lambda o: len(o)),
'mylist': ['a', 'b', 'c'],
}
args = ExpressionList([ExpressionParameter('mylist')])
var = ExpressionParameter('len')
func = ExpressionFunctionCall(var, args)
self.assertEqual(func(mydict), 3)
# Test get object method from attr
mydict = {'foo': foo}
args = ExpressionList([ExpressionLiteral('BAR')])
var = ExpressionParameter('foo.string')
func = ExpressionFunctionCall(var, args)
self.assertEqual(func(mydict), 'FOOBAR')
# Test implicit types
mydict = {
'somedict': {'a': 'AAA', 'b': 'BBB', 'c': 'CCC'},
'somelist': ['x', 'y', 'z'],
'somestring': 'FOOBAR',
}
args = ExpressionList() # empty args
for name, wanted in (
('somedict.sorted', ['a', 'b', 'c']),
('somelist.len', 3),
('somestring.lower', 'foobar'),
('somedict.b.lower', 'bbb'),
('somelist.1.upper', 'Y'),
):
var = ExpressionParameter(name)
func = ExpressionFunctionCall(var, args)
self.assertEqual(func(mydict), wanted)
class TestExpressionObjects(tests.TestCase):
def runTest(self):
# Test proper object type for attributes
for obj in (
ExpressionStringObject('foo'),
ExpressionDictObject({'foo': 'bar'}),
ExpressionListObject(['a', 'b', 'c']),
):
for name in obj._fmethods:
self.assertTrue(hasattr(obj, name))
function = getattr(obj, name)
self.assertIsInstance(function, ExpressionFunction)
# Test getitem, iter, len, str
# and one or two functions of each type
data = {'a': 'b', 'c': 'd', 'e': 'f'}
mydict = ExpressionDictObject(data)
self.assertEqual(mydict['c'], data['c'])
self.assertEqual(list(mydict), list(data))
self.assertEqual(len(mydict), len(data))
self.assertEqual(str(mydict), str(data))
self.assertEqual(mydict.get('c'), data.get('c'))
mylist = ExpressionListObject(['a', 'b', 'c'])
self.assertEqual(mylist[1], 'b')
self.assertEqual(mylist.get(1), 'b')
self.assertIsNone(mylist.get(5))
mystring = ExpressionStringObject('foo')
self.assertEqual(mystring.upper(), "FOO")
class TestTemplateBuilderTextBuffer(tests.TestCase):
def runTest(self):
builder = SimpleTreeBuilder()
buffer = TemplateBuilderTextBuffer(builder)
buffer.start('FOO')
buffer.text('foo\n\t\t')
buffer.rstrip()
buffer.append('BAR')
buffer.lstrip()
buffer.text(' \n\n\t\tdus\n\n')
buffer.rstrip()
buffer.append('BAR')
buffer.lstrip()
buffer.text('\n')
buffer.end('FOO')
result = builder.get_root()
#~ print result
self.assertEqual(result, [
E('FOO', None, [
u'foo',
E('BAR', None, []),
u'\n\t\tdus\n',
E('BAR', None, []),
])
])
class TestTemplateParser(tests.TestCase):
# Include all elements recognized by parser and various forms
# of whitespace stripping, no need to excersize all expressions
# - ExpressionParser is tested separately
TEMPLATE = '''\
[% foo %]
[% GET foo %]
[% bar = "test" %]
[% SET bar = "test" %]
DO SOMETHING
SOMETHING ELSE
[%- ELSE %]
YET SOMETHING ELSE
[% END %]
Switch: [% IF foo %]AAA[% ELSE %]BBB[% END %]
[% BLOCK bar -%]
BAR
[% END %]
[% FOR a IN b %]
AAA
[% END %]
[% FOREACH a IN b %]
AAA
[% END %]
[% FOREACH a = b %]
AAA
[% END %]
FOO
'''
WANTED = [
E('TEMPLATE', None, [
E('GET', {'expr': ExpressionParameter('foo')}, []),
'\n', # whitespace around GET remains intact
E('GET', {'expr': ExpressionParameter('foo')}, []),
'\n',
E('SET', {
'var': ExpressionParameter('bar'),
'expr': ExpressionLiteral('test')
}, []), # no whitespace here - SET chomps
E('SET', {
'var': ExpressionParameter('bar'),
'expr': ExpressionLiteral('test')
}, []),
'\n', # only one "\n" here!
# no indenting before block level items like IF
E('IF', {'expr': ExpressionParameter('foo')}, [
'\tDO SOMETHING\n' # indenting intact
]),
E('ELIF', {'expr': ExpressionParameter('foo')}, [
'SOMETHING ELSE' # stripped on both sides
]),
E('ELSE', None, [
'\tYET SOMETHING ELSE\n' # indenting intact
]),
'\nSwitch:\t',
E('IF', {'expr': ExpressionParameter('foo')}, [
'AAA'
]),
E('ELSE', None, [
'BBB'
]),
'\n\n', # two "\n" here because IF .. ELSE is inline
'\n', # another empty line after block is taken out
# 3 times same loop by different syntax
E('FOR', {
'var': ExpressionParameter('a'),
'expr': ExpressionParameter('b'),
}, [
'\tAAA\n'
]),
'\n',
E('FOR', {
'var': ExpressionParameter('a'),
'expr': ExpressionParameter('b'),
}, [
'\tAAA\n'
]),
'\n',
E('FOR', {
'var': ExpressionParameter('a'),
'expr': ExpressionParameter('b'),
}, [
'\tAAA\n'
]),
'\n',
]),
E('BLOCK', {'name': 'bar'}, ['BAR\n']),
# indenting before "[% BLOCK .." and before "BAR" both gone
E('BLOCK', {'name': 'foo'}, ['\tFOO\n']),
# indenting intact
]
def runTest(self):
parser = TemplateParser()
root = parser.parse(self.TEMPLATE)
#~ print root
self.assertEqual(root, self.WANTED)
# TODO Test exceptions
# - invalid expression
# - lower case keyword
# - invalide sequence IF / ELSE
class TestTemplateContextDict(tests.TestCase):
def runTest(self):
data = {'a': 'AAA', 'b': 'BBB', 'c': 'CCC'}
context = TemplateContextDict(data)
for name in context._fmethods:
func = getattr(context, name)
self.assertIsInstance(func, ExpressionFunction)
# make sure we can use as regular dict
context['d'] = 'DDD'
self.assertEqual(context.pop('d'), 'DDD')
class TestTemplateLoopState(tests.TestCase):
def runTest(self):
items = ['aaa', 'bbb', 'ccc']
loop = TemplateLoopState(len(items), None)
myiter = MovingWindowIter(items)
for i, stateitems in enumerate(myiter):
loop._update(i, myiter)
self.assertEqual(loop.size, 3)
self.assertEqual(loop.max, 2)
self.assertEqual(loop.prev, None if i == 0 else items[i-1])
self.assertEqual(loop.current, items[i])
self.assertEqual(loop.next, None if i == 2 else items[i+1])
self.assertEqual(loop.index, i)
self.assertEqual(loop.count, i+1)
self.assertEqual(loop.first, True if i == 0 else False)
self.assertEqual(loop.last, True if i == 2 else False)
self.assertEqual(loop.parity, 'odd' if i % 2 else 'even')
self.assertEqual(loop.even, False if i % 2 else True)
self.assertEqual(loop.odd, True if i % 2 else False)
self.assertEqual(i, 2)
class TestTemplateProcessor(tests.TestCase):
def testGetSet(self):
# test 'GET', 'SET'
processor = TemplateProcessor([
E('TEMPLATE', None, [
E('SET', {
'var': ExpressionParameter('aaa.bbb'),
'expr': ExpressionLiteral('foo')
}),
E('GET', {'expr': ExpressionParameter('aaa.bbb')}),
])
])
output = []
context = TemplateContextDict({'aaa': TemplateContextDict({})})
processor.process(output, context)
self.assertEqual(output, ['foo'])
output = []
context = TemplateContextDict({'aaa': {}})
with self.assertRaises(AssertionError):
processor.process(output, context)
def testIfElifElse(self):
# test 'IF', 'ELIF', 'ELSE',
processor = TemplateProcessor([
E('TEMPLATE', None, [
E('IF', {'expr': ExpressionParameter('a')}, ['A']),
E('ELIF', {'expr': ExpressionParameter('b')}, ['B']),
E('ELIF', {'expr': ExpressionParameter('c')}, ['C']),
E('ELSE', {}, ['D']),
])
])
for context, wanted in (
({'a': True}, ['A']),
({'a': False, 'b': True}, ['B']),
({'a': False, 'b': False, 'c': True}, ['C']),
({'a': False, 'b': False, 'c': False}, ['D']),
):
lines = []
processor.process(lines, TemplateContextDict(context))
self.assertEqual(lines, wanted)
def testFor(self):
# test 'FOR'
processor = TemplateProcessor([
E('TEMPLATE', None, [
E('FOR', {
'var': ExpressionParameter('iter'),
'expr': ExpressionParameter('items'),
}, [
E('GET', {'expr': ExpressionParameter('loop.count')}),
': ',
E('GET', {'expr': ExpressionParameter('iter')}),
'\n',
])
])
])
context = {'items': ['aaa', 'bbb', 'ccc']}
lines = []
processor.process(lines, TemplateContextDict(context))
self.assertEqual(''.join(lines), '1: aaa\n2: bbb\n3: ccc\n')
def testInclude(self):
# test 'INCLUDE',
processor = TemplateProcessor([
E('TEMPLATE', None, [
E('INCLUDE', {'expr': ExpressionParameter('foo')}),
E('INCLUDE', {'expr': ExpressionParameter('foo')}),
E('INCLUDE', {'expr': ExpressionParameter('foo')}),
]),
E('BLOCK', {'name': 'foo'}, 'FOO\n'),
])
lines = []
processor.process(lines, TemplateContextDict({'foo': 'foo'}))
self.assertEqual(''.join(lines), 'FOO\nFOO\nFOO\n')
class TestTemplateList(tests.TestCase):
def runTest(self):
categories = list_template_categories()
self.assertIn('html', categories)
self.assertIn('wiki', categories)
for cat in categories:
templates = list_templates(cat)
#~ print '>>', cat, templates
self.assertGreater(len(templates), 0)
for name, filename in templates:
template = get_template(cat, name)
self.assertIsInstance(template, Template)
class TestTemplateFunctions(tests.TestCase):
def testFuncLen(self):
func = build_template_functions()['len']
self.assertIsInstance(func, ExpressionFunction)
self.assertEqual(
func([1, 2, 3]),
3
)
def testFuncSorted(self):
func = build_template_functions()['sorted']
self.assertIsInstance(func, ExpressionFunction)
self.assertEqual(
func(['bbb', 'aaa', 'ccc']),
['aaa', 'bbb', 'ccc']
)
def testFuncReversed(self):
func = build_template_functions()['reversed']
self.assertIsInstance(func, ExpressionFunction)
self.assertEqual(
func(['bbb', 'aaa', 'ccc']),
['ccc', 'aaa', 'bbb']
)
def testFuncRange(self):
func = build_template_functions()['range']
self.assertIsInstance(func, ExpressionFunction)
self.assertEqual(
func(1, 10),
[1, 2, 3, 4, 5, 6, 7, 8, 9]
)
def testFuncStrftime(self):
from datetime import date
func = build_template_functions()['strftime']
self.assertIsInstance(func, ExpressionFunction)
self.assertTrue(func('%Y %m %d'))
self.assertEqual(
func('%Y %m %d', date(2014, 05, 26)),
'2014 05 26'
)
def testFuncStrfcal(self):
from datetime import date
func = build_template_functions()['strfcal']
self.assertIsInstance(func, ExpressionFunction)
self.assertTrue(func('%Y %W'))
self.assertEqual(
func('%Y %W', date(2014, 05, 26)),
'2014 22'
)
def testHTMLEncode(self):
func = build_template_functions()['html_encode']
self.assertIsInstance(func, ExpressionFunction)
self.assertEqual(func('foo'), '<a>foo</a>')
def testURLEncode(self):
func = build_template_functions()['url_encode']
self.assertIsInstance(func, ExpressionFunction)
self.assertEqual(func('/foo/bar baz'), '%2Ffoo%2Fbar%20baz')
class TestTemplate(tests.TestCase):
def runTest(self):
from pprint import pprint
from zim.fs import File
file = File('./tests/data/TestTemplate.html')
templ = Template(file)
#~ pprint(templ.parts) # parser output
output = []
templ.process(output, {
'title': 'THIS IS THE TITLE',
'generator': {
'name': 'ZIM VERSION',
},
'navigation': {
'prev': None,
'next': None,
},
'links': {},
'pages': [
{ # page
'name': 'page',
'heading': 'HEAD',
'body': 'BODY',
'properties': {
'type': 'PAGE',
},
'backlinks': [
{'name': 'LINK1'},
{'name': 'LINK2'},
{'name': 'LINK3'},
],
'attachments': [
{'name': 'FILE1', 'basename': 'FILE1', 'size': '1k'},
{'name': 'FILE2', 'basename': 'FILE2', 'size': '1k'},
],
},
],
'uri': ExpressionFunction(lambda l: "URL:%s" % l['name']),
'anchor': ExpressionFunction(lambda l: "ANCHOR:%s" % l['name']),
})
#~ print ''.join(output)
# TODO assert something
### Test empty template OK as well
dir = Dir(self.create_tmp_dir())
file = dir.file('empty.html')
self.assertRaises(FileNotFoundError, Template, file)
file.touch()
templ = Template(file)
output = []
templ.process(output, {})
self.assertEqual(output, [])
zim-0.65/tests/calendar.py 0000664 0001750 0001750 00000016317 12614412356 015416 0 ustar jaap jaap 0000000 0000000 # -*- coding: utf-8 -*-
# Copyright 2008,2014 Jaap Karssenberg
from __future__ import with_statement
import tests
from datetime import date as dateclass
import zim.datetimetz
from zim.plugins import PluginManager
from zim.notebook import Path
from zim.templates import get_template
from zim.formats import get_dumper
from zim.plugins.calendar import NotebookExtension, \
MainWindowExtensionDialog, MainWindowExtensionEmbedded, \
CalendarDialog
from tests.gui import setupGtkInterface
class TestCalendarFunctions(tests.TestCase):
def testDatesForWeeks(self):
from zim.plugins.calendar import dates_for_week
zim.datetimetz.FIRST_DAY_OF_WEEK = \
zim.datetimetz.MONDAY
start, end = dates_for_week(2012, 17)
self.assertEqual(start, dateclass(2012, 4, 23)) # a monday
self.assertEqual(end, dateclass(2012, 4, 29)) # a sunday
zim.datetimetz.FIRST_DAY_OF_WEEK = \
zim.datetimetz.SUNDAY
start, end = dates_for_week(2012, 17)
self.assertEqual(start, dateclass(2012, 4 ,22)) # a sunday
self.assertEqual(end, dateclass(2012, 4, 28)) # a saturday
start, end = dates_for_week(2013, 1)
self.assertEqual(start, dateclass(2012, 12 ,30)) # a sunday
self.assertEqual(end, dateclass(2013, 1, 5)) # a saturday
start, end = dates_for_week(2009, 53)
self.assertEqual(start, dateclass(2009, 12 ,27)) # a sunday
self.assertEqual(end, dateclass(2010, 1, 2)) # a saturday
def testWeekCalendar(self):
from zim.plugins.calendar import weekcalendar
sunday = dateclass(2012, 4 ,22)
monday = dateclass(2012, 4, 23)
nextsunday = dateclass(2012, 4, 29)
zim.datetimetz.FIRST_DAY_OF_WEEK = \
zim.datetimetz.MONDAY
self.assertEqual(weekcalendar(sunday), (2012, 16, 7))
self.assertEqual(weekcalendar(monday), (2012, 17, 1))
self.assertEqual(weekcalendar(nextsunday), (2012, 17, 7))
zim.datetimetz.FIRST_DAY_OF_WEEK = \
zim.datetimetz.SUNDAY
self.assertEqual(weekcalendar(sunday), (2012, 17, 1))
self.assertEqual(weekcalendar(monday), (2012, 17, 2))
self.assertEqual(weekcalendar(nextsunday), (2012, 18, 1))
dec31 = dateclass(2012, 12, 31)
jan1 = dateclass(2013, 1, 1)
self.assertEqual(weekcalendar(dec31), (2013, 1, 2))
self.assertEqual(weekcalendar(jan1), (2013, 1, 3))
dec31 = dateclass(2009, 12, 31)
jan1 = dateclass(2010, 1, 1)
self.assertEqual(weekcalendar(dec31), (2009, 53, 5))
self.assertEqual(weekcalendar(jan1), (2009, 53, 6))
def testDateRangeFromPath(self):
from zim.plugins.calendar import daterange_from_path
# Day
for path in (Path('Foo:2012:04:27'), Path('Foo:2012:4:27')):
type, start, end = daterange_from_path(path)
self.assertEqual(type, 'day')
self.assertEqual(start, dateclass(2012, 4, 27))
self.assertEqual(end, dateclass(2012, 4, 27))
# Week
zim.datetimetz.FIRST_DAY_OF_WEEK = \
zim.datetimetz.MONDAY
type, start, end = daterange_from_path(Path('Foo:2012:Week 17'))
self.assertEqual(type, 'week')
self.assertEqual(start, dateclass(2012, 4, 23)) # a monday
self.assertEqual(end, dateclass(2012, 4, 29)) # a sunday
# Month
for path in (Path('Foo:2012:04'), Path('Foo:2012:4')):
type, start, end = daterange_from_path(path)
self.assertEqual(type, 'month')
self.assertEqual(start, dateclass(2012, 4, 1))
self.assertEqual(end, dateclass(2012, 4, 30))
# Year
type, start, end = daterange_from_path(Path('Foo:2012'))
self.assertEqual(type, 'year')
self.assertEqual(start, dateclass(2012, 1, 1))
self.assertEqual(end, dateclass(2012, 12, 31))
@tests.slowTest
class TestCalendarPlugin(tests.TestCase):
def testMainWindowExtensions(self):
pluginklass = PluginManager.get_plugin_class('calendar')
plugin = pluginklass()
notebook = tests.new_notebook(self.get_tmp_name())
ui = setupGtkInterface(self, notebook=notebook)
plugin.preferences['embedded'] = True
self.assertEqual(plugin.extension_classes['MainWindow'], MainWindowExtensionEmbedded)
plugin.extend(ui.mainwindow)
ext = list(plugin.extensions)
self.assertEqual(len(ext), 1)
self.assertIsInstance(ext[0], MainWindowExtensionEmbedded)
plugin.preferences.changed() # make sure no errors are triggered
ext[0].go_page_today()
self.assertTrue(ui.page.name.startswith('Journal:'))
plugin.preferences['embedded'] = False
self.assertEqual(plugin.extension_classes['MainWindow'], MainWindowExtensionDialog)
plugin.extend(ui.mainwindow) # plugin does not remember objects, manager does that
ext = list(plugin.extensions)
self.assertEqual(len(ext), 1)
self.assertIsInstance(ext[0], MainWindowExtensionDialog)
plugin.preferences.changed() # make sure no errors are triggered
def test_dialog(dialog):
self.assertIsInstance(dialog, CalendarDialog)
dialog.do_today('xxx')
ui.open_page(Path('foo'))
with tests.DialogContext(test_dialog):
ext[0].show_calendar()
plugin.preferences['embedded'] = True # switch back
def testNotebookExtension(self):
pluginklass = PluginManager.get_plugin_class('calendar')
plugin = pluginklass()
notebook = tests.new_notebook(self.get_tmp_name())
plugin.extend(notebook)
ext = list(plugin.extensions)
self.assertEqual(len(ext), 1)
self.assertIsInstance(ext[0], NotebookExtension)
page = Path('Foo')
link = notebook.suggest_link(page, '2014-01-06')
self.assertEqual(link.name, 'Journal:2014:01:06')
link = notebook.suggest_link(page, 'foo')
self.assertIsNone(link)
def testNamespace(self):
pluginklass = PluginManager.get_plugin_class('calendar')
plugin = pluginklass()
today = dateclass.today()
for namespace in (Path('Calendar'), Path(':')):
plugin.preferences['namespace'] = namespace
path = plugin.path_from_date(today)
self.assertTrue(isinstance(path, Path))
self.assertTrue(path.ischild(namespace))
date = plugin.date_from_path(path)
self.assertTrue(isinstance(date, dateclass))
self.assertEqual(date, today)
from zim.plugins.calendar import DAY, WEEK, MONTH, YEAR
zim.datetimetz.FIRST_DAY_OF_WEEK = \
zim.datetimetz.MONDAY
plugin.preferences['namespace'] = Path('Calendar')
date = dateclass(2012, 4, 27)
for setting, wanted, start in (
(DAY, 'Calendar:2012:04:27', dateclass(2012, 4, 27)),
(WEEK, 'Calendar:2012:Week 17', dateclass(2012, 4, 23)),
(MONTH, 'Calendar:2012:04', dateclass(2012, 4, 1)),
(YEAR, 'Calendar:2012', dateclass(2012, 1, 1)),
):
plugin.preferences['granularity'] = setting
path = plugin.path_from_date(date)
self.assertEqual(path.name, wanted)
self.assertEqual(plugin.date_from_path(path), start)
path = plugin.path_for_month_from_date(date)
self.assertEqual(path.name, 'Calendar:2012:04')
def testTemplate(self):
pluginklass = PluginManager.get_plugin_class('calendar')
plugin = pluginklass()
plugin.preferences['namespace'] = Path('Calendar')
notebook = tests.new_notebook()
plugin.extend(notebook)
dumper = get_dumper('wiki')
zim.datetimetz.FIRST_DAY_OF_WEEK = \
zim.datetimetz.MONDAY
for path in (
Path('Calendar:2012'),
Path('Calendar:2012:04:27'),
Path('Calendar:2012:Week 17'),
Path('Calendar:2012:04'),
):
tree = notebook.get_template(path)
lines = dumper.dump(tree)
#~ print lines
self.assertTrue(not 'Created' in ''.join(lines)) # No fall back
if 'Week' in path.name:
days = [l for l in lines if l.startswith('=== ')]
self.assertEqual(len(days), 7)
zim-0.65/tests/errors.py 0000664 0001750 0001750 00000010525 12374655231 015160 0 ustar jaap jaap 0000000 0000000 # -*- coding: utf-8 -*-
# Copyright 2010 Jaap Karssenberg
from __future__ import with_statement
import tests
import logging
import zim.errors
from zim.gui.widgets import ErrorDialog
class StubError(zim.errors.Error):
description = '''\
Some description
here
'''
def __init__(self, i):
self.msg = 'Error %i' % i
class TestErrors(tests.TestCase):
def runTest(self):
'''Check base class for errors'''
wanted = '''\
Error 6
Some description
here
'''
self.assertEqual(str(StubError(6)), wanted)
self.assertEqual(unicode(StubError(6)), wanted)
self.assertEqual(repr(StubError(6)), '')
class CatchAllLogging(tests.LoggingFilter):
def __init__(self):
tests.LoggingFilter.__init__(self)
self.records = []
def filter(self, record):
self.records.append(record)
return False
class TestExceptionHandler(tests.TestCase):
def setUp(self):
logger = logging.getLogger('zim')
self.oldlevel = logger.getEffectiveLevel()
logger.setLevel(logging.DEBUG)
def tearDown(self):
logger = logging.getLogger('zim')
logger.setLevel(self.oldlevel)
def testExceptionHandlerWithGtk(self):
def error_dialog_with_trace(dialog):
self.assertIsInstance(dialog, ErrorDialog)
self.assertTrue(dialog.showing_trace)
def error_dialog_without_trace(dialog):
self.assertIsInstance(dialog, ErrorDialog)
self.assertFalse(dialog.showing_trace)
zim.errors.set_use_gtk(True)
try:
self.assertTrue(zim.errors.use_gtk_errordialog)
with tests.DialogContext(
error_dialog_with_trace,
error_dialog_with_trace,
error_dialog_without_trace,
error_dialog_without_trace,
):
with tests.LoggingFilter(
logger='zim.gui',
message='Running ErrorDialog'
):
self.testExceptionHandler()
except:
zim.errors.set_use_gtk(False)
raise
else:
zim.errors.set_use_gtk(False)
self.assertFalse(zim.errors.use_gtk_errordialog)
def testExceptionHandler(self):
## Handle unexpected error or bug
try:
raise AssertionError, 'My AssertionError'
except:
myfilter = CatchAllLogging()
with myfilter:
zim.errors.exception_handler('Test Error')
records = myfilter.records
# Should log one error message with traceback
self.assertEqual(len(records), 1)
self.assertEqual(records[0].getMessage(), 'Test Error')
self.assertEqual(records[0].levelno, logging.ERROR)
self.assertIsNotNone(records[0].exc_info)
else:
assert False
## Show caught bug
try:
raise AssertionError, 'My AssertionError'
except Exception, error:
myfilter = CatchAllLogging()
with myfilter:
zim.errors.show_error(error)
records = myfilter.records
# Should log one error message with traceback
self.assertEqual(len(records), 1)
self.assertEqual(records[0].getMessage(), 'Looks like you found a bug')
self.assertEqual(records[0].levelno, logging.ERROR)
self.assertIsNotNone(records[0].exc_info)
else:
assert False
## Handle normal application error
try:
raise zim.errors.Error('My normal Error')
except:
myfilter = CatchAllLogging()
with myfilter:
zim.errors.exception_handler('Test Error')
records = myfilter.records
# Should log a debug message with traceback
# and user error message without traceback
self.assertEqual(len(records), 2)
self.assertEqual(records[0].getMessage(), 'Test Error')
self.assertEqual(records[0].levelno, logging.DEBUG)
self.assertIsNotNone(records[0].exc_info)
self.assertEqual(records[1].getMessage(), 'My normal Error')
self.assertEqual(records[1].levelno, logging.ERROR)
self.assertIsNone(records[1].exc_info)
else:
assert False
## Handle normal IOError
try:
open('/some/non/existing/file/').read()
except:
myfilter = CatchAllLogging()
with myfilter:
zim.errors.exception_handler('Test IOError')
records = myfilter.records
# Should log a debug message with traceback
# and user error message without traceback
self.assertEqual(len(records), 2)
self.assertEqual(records[0].getMessage(), 'Test IOError')
self.assertEqual(records[0].levelno, logging.DEBUG)
self.assertIsNotNone(records[0].exc_info)
self.assertIn('/some/non/existing/file/', records[1].getMessage())
# do not test exact message - could be localized
self.assertEqual(records[1].levelno, logging.ERROR)
self.assertIsNone(records[1].exc_info)
else:
assert False
zim-0.65/tests/utils.py 0000664 0001750 0001750 00000007663 12374655231 015015 0 ustar jaap jaap 0000000 0000000 # -*- coding: utf-8 -*-
# Copyright 2012-2013 Jaap Karssenberg
import tests
from copy import copy
from zim.utils import *
class TestNaturalSorting(tests.TestCase):
def runTest(self):
input = [
'a', 'Aa', 'AA', # (these last 2 should be swapped due to case)
'1.1 foo', '10.1.1 bar', '2.1 dus', '1.01 foo',
'foo2bar', 'foo10bar', 'foo01bar',
]
wanted = [
'1.01 foo', '1.1 foo', '2.1 dus', '10.1.1 bar',
'a', 'AA', 'Aa',
'foo01bar', 'foo2bar', 'foo10bar',
]
# TODO: add utf-8 test data and set matching locale
result = natural_sorted(input)
self.assertEqual(result, wanted)
self.assertTrue(id(result) != id(input))
result = copy(input)
natural_sort(result)
self.assertEqual(result, wanted)
input = [(1, 'b'), (2, 'a')]
wanted = [(2, 'a'), (1, 'b')]
result = natural_sorted(input, key=lambda t: t[1])
self.assertEqual(result, wanted)
self.assertTrue(id(result) != id(input))
class TestOrderedDict(tests.TestCase):
def runTest(self):
items = [('foo', 1), ('bar', 2), ('baz', 3)]
mydict = OrderedDict(items)
self.assertIsInstance(repr(mydict), str)
self.assertEqual(mydict.items(), items)
self.assertEqual(list(mydict), [i[0] for i in items])
self.assertEqual(mydict.keys(), [i[0] for i in items])
mydict['bar'] = 'X'
mydict.setdefault('foo', 'dus')
items = [('foo', 1), ('bar', 'X'), ('baz', 3)]
self.assertEqual(mydict.items(), items)
self.assertEqual(list(mydict), [i[0] for i in items])
self.assertEqual(mydict.keys(), [i[0] for i in items])
del mydict['bar']
mydict['bar'] = 'Y'
items = [('foo', 1), ('baz', 3), ('bar', 'Y')]
self.assertEqual(mydict.items(), items)
self.assertEqual(list(mydict), [i[0] for i in items])
self.assertEqual(mydict.keys(), [i[0] for i in items])
mydict.pop('foo')
mydict.setdefault('foo', 'dus')
items = [('baz', 3), ('bar', 'Y'), ('foo', 'dus')]
self.assertEqual(mydict.items(), items)
self.assertEqual(list(mydict), [i[0] for i in items])
self.assertEqual(mydict.keys(), [i[0] for i in items])
class TestMovingWindowIterBuffer(tests.TestCase):
def runTest(self):
mylist = ['a', 'b', 'c', 'd']
myiter = MovingWindowIter(mylist)
self.assertEqual(iter(myiter), myiter, 'MovingWindowIter should be an iter, not an iterable')
seen = []
n = len(mylist)
for i, t in enumerate(myiter):
seen.append(t[1])
if i == 0:
self.assertEqual(t, (None, mylist[0], mylist[1]))
self.assertFalse(myiter.last)
elif i == n-1:
self.assertEqual(t, (mylist[-2], mylist[-1], None))
self.assertTrue(myiter.last)
else:
self.assertEqual(t, (mylist[i-1], mylist[i], mylist[i+1]))
self.assertFalse(myiter.last)
self.assertEqual(seen, mylist)
import threading
class TestFunctionThread(tests.TestCase):
def runTest(self):
def foo(*args):
return 'FOO: ' + ', '.join(args)
# Function OK, no lock
func = FunctionThread(foo, ('a', 'b', 'c'))
self.assertFalse(func.done)
func.start()
func.join()
self.assertTrue(func.done)
self.assertFalse(func.error)
self.assertEqual(func.result, 'FOO: a, b, c')
# Function OK, with lock
lock = threading.Lock()
func = FunctionThread(foo, ('a', 'b', 'c'), lock=lock)
self.assertFalse(func.done)
func.start()
lock.acquire()
self.assertTrue(func.done)
self.assertFalse(func.error)
self.assertEqual(func.result, 'FOO: a, b, c')
###
def error(*args):
raise AssertionError, 'FOO'
# Function raises, no lock
func = FunctionThread(error, ('a', 'b', 'c'))
self.assertFalse(func.done)
func.start()
func.join()
self.assertTrue(func.done)
self.assertTrue(func.error)
self.assertEqual(func.exc_info[0], AssertionError)
# Function raises, with lock
#~ lock = threading.Lock()
#~ func = FunctionThread(error, ('a', 'b', 'c'))
#~ self.assertFalse(func.done)
#~ func.start()
#~ lock.acquire()
#~ self.assertTrue(func.done)
#~ self.assertTrue(func.error)
#~ self.assertEqual(func.exc_info[0], AssertionError)
zim-0.65/tests/stores.py 0000664 0001750 0001750 00000031724 12374655231 015167 0 ustar jaap jaap 0000000 0000000 # -*- coding: utf-8 -*-
# Copyright 2008,2014 Jaap Karssenberg
'''Test cases for basic stores modules.'''
from __future__ import with_statement
import tests
import os
import time
from zim.fs import File, Dir, FileWriteError
from zim.errors import TrashNotSupportedError
from zim.notebook import Notebook, Path, LookupError, PageExistsError
import zim.stores
from zim.formats import ParseTree
def walk(store, namespace=None):
if namespace == None:
namespace = Path(':')
for page in store.get_pagelist(namespace):
yield namespace, page
if page.haschildren:
for parent, child in walk(store, page): # recurs
yield parent, child
def ascii_page_tree(store, namespace=None, level=0):
'''Returns an ascii page tree. Used for debugging the test'''
if namespace is None:
namespace = store.namespace
if namespace.isroot: basename = ''
else: basename = namespace.basename
text = ' '*level + basename + '\n'
level += 1
for page in store.get_pagelist(namespace):
if page.haschildren:
text += ascii_page_tree(store, page, level) # recurs
else:
text += ' '*level + page.basename + '\n'
return text
class FilterOverWriteWarning(tests.LoggingFilter):
logger = 'zim.fs'
message = 'mtime check failed'
class TestUtils(tests.TestCase):
def testFilenameEncodeing(self):
'''Test mapping page names to filenames'''
import zim.fs
realencoding = zim.fs.ENCODING
try:
zim.fs.ENCODING = 'utf-8'
pagename = u'utf8:\u03b1\u03b2\u03b3'
filename = zim.stores.encode_filename(pagename)
self.assertEqual(filename, u'utf8/\u03b1\u03b2\u03b3')
roundtrip = zim.stores.decode_filename(filename)
self.assertEqual(roundtrip, pagename)
zim.fs.ENCODING = 'ascii'
pagename = u'utf8:\u03b1\u03b2\u03b3'
filename = zim.stores.encode_filename(pagename)
self.assertEqual(filename, u'utf8/%CE%B1%CE%B2%CE%B3')
roundtrip = zim.stores.decode_filename(filename)
self.assertEqual(roundtrip, pagename)
zim.fs.ENCODING = 'gb2312'
pagename = u'utf8:\u2022:\u4e2d\u6587:foo' # u2022 can not be encoded in gb2312
filename = zim.stores.encode_filename(pagename)
self.assertEqual(filename, u'utf8/%E2%80%A2/\u4e2d\u6587/foo')
roundtrip = zim.stores.decode_filename(filename)
self.assertEqual(roundtrip, pagename)
except Exception:
zim.fs.ENCODING = realencoding
raise
else:
zim.fs.ENCODING = realencoding
# try roundtrip with actual current encoding
pagename = u'utf8:\u03b1\u03b2\u03b3:\u2022:\u4e2d\u6587:foo'
filename = zim.stores.encode_filename(pagename)
roundtrip = zim.stores.decode_filename(filename)
self.assertEqual(roundtrip, pagename)
class TestReadOnlyStore(object):
# This class does not inherit from TestCase itself as it is used
# as a mixin for TestCase classes below but isn't a test case
# in itself
def normalize_index(self):
'''Make sure the index conains namespaces for all page names'''
pages = self.index.copy()
for name in pages:
parts = name.split(':')
parts.pop()
while parts:
self.index.add(':'.join(parts))
parts.pop()
def testIndex(self):
'''Test we get a proper index for the memory store'''
names = set()
for parent, page in walk(self.store):
self.assertTrue(len(page.name) > 0)
self.assertTrue(len(page.basename) > 0)
self.assertTrue(page.namespace == parent.name)
names.add( page.name )
#~ import pprint
#~ pprint.pprint(self.index)
#~ pprint.pprint(names)
self.assertTrue(u'utf8:\u03b1\u03b2\u03b3' in names) # Check usage of unicode
self.assertEqual(names, self.index)
class TestStoresMemory(TestReadOnlyStore, tests.TestCase):
'''Test the store.memory module'''
def setUp(self):
klass = zim.stores.get_store('memory')
self.store = klass(path=Path(':'), notebook=Notebook())
self.index = set()
for name, text in tests.WikiTestData:
self.store.set_node(Path(name), text)
self.index.add(name)
self.normalize_index()
def testManipulate(self):
'''Test moving and deleting pages in the memory store'''
# Check we can get / store a page
page = self.store.get_page(Path('Test:foo'))
self.assertTrue(page.get_parsetree())
self.assertTrue('Foo' in ''.join(page.dump('plain')))
self.assertFalse(page.modified)
wikitext = tests.WikiTestData.get('roundtrip')
page.parse('wiki', wikitext)
self.assertEqual(''.join(page.dump('wiki')), wikitext)
self.assertTrue(page.modified)
self.store.store_page(page)
self.assertFalse(page.modified)
self.assertEqual(''.join(page.dump('wiki')), wikitext)
page = self.store.get_page(Path('Test:foo'))
self.assertEqual(''.join(page.dump('wiki')), wikitext)
page = self.store.get_page(Path('Test:foo'))
text = page.dump('plain')
newtext = ['Some new content\n']
assert newtext != text
self.assertEqual(page.dump('plain'), text)
page.parse('plain', newtext)
self.assertEqual(page.dump('plain'), newtext)
self.assertTrue(page.modified)
re = self.store.revert_page(page)
self.assertFalse(re) # no return value
self.assertEqual(page.dump('plain'), text) # object reverted
self.assertFalse(page.modified) # no longer modified
page = self.store.get_page(page) # new object
self.assertEqual(page.dump('plain'), text)
page.parse('plain', newtext)
self.assertEqual(page.dump('plain'), newtext)
self.store.store_page(page)
page = self.store.get_page(page) # new object
self.assertEqual(page.dump('plain'), newtext)
# check revert page triggers ui object
page._ui_object = tests.MockObject()
self.store.revert_page(page)
self.assertEqual(page._ui_object.mock_calls[-1][0], 'set_parsetree')
if hasattr(page, 'source') and isinstance(page.source, File):
# check revert also works when the actual file changed
# (and not trigger mtime check failure)
from tests.fs import modify_file_mtime, FilterOverWriteWarning
page = self.store.get_page(Path('Test:foo'))
text = page.dump('plain')
newtext = ['Foo bar baz\n']
othertext = ['Dus ja\n']
assert newtext != text
assert othertext != text
page.parse('plain', newtext)
modify_file_mtime(page.source.path, lambda p: open(p, 'w').writelines(othertext))
with FilterOverWriteWarning():
self.assertRaises(FileWriteError, self.store.store_page, page)
self.store.revert_page(page)
self.assertEqual(page.dump('plain'), othertext)
page.parse('plain', newtext)
self.store.store_page(page)
page = self.store.get_page(page) # new object
self.assertEqual(page.dump('plain'), newtext)
# check test setup OK
for path in (Path('Test:BAR'), Path('NewPage')):
page = self.store.get_page(path)
self.assertFalse(page.haschildren)
self.assertFalse(page.hascontent)
# check errors
self.assertRaises(LookupError,
self.store.move_page, Path('NewPage'), Path('Test:BAR'))
self.assertRaises(PageExistsError,
self.store.move_page, Path('Test:foo'), Path('TaskList'))
for oldpath, newpath in (
(Path('Test:foo'), Path('Test:BAR')),
(Path('TaskList'), Path('NewPage:Foo:Bar:Baz')),
):
page = self.store.get_page(oldpath)
text = page.dump('wiki')
self.assertTrue(page.haschildren)
#~ print ascii_page_tree(self.store)
self.store.move_page(oldpath, newpath)
#~ print ascii_page_tree(self.store)
# newpath should exist and look like the old one
page = self.store.get_page(newpath)
self.assertTrue(page.haschildren)
self.assertEqual(page.dump('wiki'), text)
# oldpath should be deleted
page = self.store.get_page(oldpath)
self.assertFalse(page.haschildren)
self.assertFalse(page.hascontent)
# let's delete the newpath again
page = self.store.get_page(newpath)
self.assertTrue(self.store.delete_page(page))
self.assertFalse(page.haschildren)
self.assertFalse(page.hascontent)
page = self.store.get_page(newpath)
self.assertFalse(page.haschildren)
self.assertFalse(page.hascontent)
# delete again should silently fail
self.assertFalse(self.store.delete_page(newpath))
# check cleaning up works OK
page = self.store.get_page(Path('NewPage'))
self.assertFalse(page.haschildren)
self.assertFalse(page.hascontent)
# check case-sensitive move
self.store.move_page(Path('utf8'), Path('UTF8'))
page = self.store.get_page(Path('utf8'))
# self.assertFalse(page.haschildren) - fails on case-insensitive FS
self.assertFalse(Path('utf8')
in list(self.store.get_pagelist(Path(':'))))
self.assertTrue(Path('UTF8')
in list(self.store.get_pagelist(Path(':'))))
newpage = self.store.get_page(Path('UTF8'))
self.assertTrue(newpage.haschildren)
self.assertFalse(newpage == page)
# TODO here we only move dir case insensitive - also test file
# check hascontents
page = self.store.get_page(Path('NewPage'))
tree = ParseTree().fromstring('')
self.assertFalse(tree.hascontent)
page.set_parsetree(tree)
self.assertFalse(page.hascontent)
self.store.store_page(page)
page = self.store.get_page(Path('NewPage'))
self.assertFalse(page.hascontent)
# check trashing
trashing = True
try:
page = self.store.get_page(Path('TrashMe'))
self.assertTrue(page.haschildren)
self.assertTrue(page.hascontent)
self.store.trash_page(page)
self.assertFalse(page.haschildren)
self.assertFalse(page.hascontent)
page = self.store.get_page(Path('TrashMe'))
self.assertFalse(page.haschildren)
self.assertFalse(page.hascontent)
except TrashNotSupportedError:
trashing = False
print '(trashing not supported for this store)'
self.assertTrue(page.haschildren)
self.assertTrue(page.hascontent)
page = self.store.get_page(Path('TrashMe'))
self.assertTrue(page.haschildren)
self.assertTrue(page.hascontent)
page = self.store.get_page(Path('NonExistingPage'))
if trashing:
# fail silently for non-existing page
self.assertFalse(self.store.trash_page(page))
else:
# check error consistent
self.assertRaises(TrashNotSupportedError, self.store.trash_page, page)
class TextXMLStore(TestReadOnlyStore, tests.TestCase):
xml = u'''\
Fooo!
Foooo Barrr
Fooo barrr bazzz
Utf8 content here
'''
def setUp(self):
buffer = StubFile(self.xml)
klass = zim.stores.get_store('xml')
self.store = klass(path=Path(':'), notebook=Notebook(), file=buffer)
self.index = set(['Foo', 'Foo:Bar', 'Baz', u'utf8:\u03b1\u03b2\u03b3'])
self.normalize_index()
def testIndex(self):
'''Test we get a proper index for the XML store'''
TestReadOnlyStore.testIndex(self)
def testContent(self):
page = self.store.get_page(Path('Foo:Bar'))
self.assertEqual(page.dump(format='wiki'), ['Foooo Barrr\n'])
ref = self.xml.replace("'", '"')
self.assertEqual(''.join(self.store.dump()), ref)
@tests.slowTest
class TestFiles(TestStoresMemory):
'''Test the store.files module'''
def setUp(self):
TestStoresMemory.setUp(self)
tmpdir = self.create_tmp_dir(u'_some_utf8_here_\u0421\u0430\u0439')
self.dir = Dir([tmpdir, 'store-files'])
self.mem = self.store
klass = zim.stores.get_store('files')
self.store = klass(path=Path(':'), notebook=Notebook(), dir=self.dir)
for parent, page in walk(self.mem):
if page.hascontent:
mypage = self.store.get_page(page)
mypage.set_parsetree(page.get_parsetree())
self.store.store_page(mypage)
def modify(self, path, func):
mtime = os.stat(path).st_mtime
m = mtime
i = 0
while m == mtime:
time.sleep(1)
func(path)
m = os.stat(path).st_mtime
i += 1
assert i < 5
#~ print '>>>', m, mtime
def testIndex(self):
'''Test we get a proper index for files store'''
TestStoresMemory.testIndex(self)
def testManipulate(self):
'''Test moving and deleting pages in the files store'''
TestStoresMemory.testManipulate(self)
# test overwrite check
page = self.store.get_page(Path('Test:overwrite'))
page.parse('plain', 'BARRR')
self.store.store_page(page)
self.assertTrue('BARRR' in ''.join(page.dump('plain')))
self.modify(page.source.path, lambda p: open(p, 'w').write('bar'))
with FilterOverWriteWarning():
self.assertRaises(FileWriteError, self.store.store_page, page)
# test headers
page = self.store.get_page(Path('Test:New'))
page.parse('plain', 'Foo Bar')
self.store.store_page(page)
self.assertEqual(page.properties['Content-Type'], 'text/x-zim-wiki')
self.assertEqual(page.properties['Wiki-Format'], 'zim 0.4')
self.assertTrue('Creation-Date' in page.properties)
firstline = page.source.readlines()[0]
self.assertEqual(firstline, 'Content-Type: text/x-zim-wiki\n')
# test moving page into itself
oldpath = Path('Test:New')
newpath = Path('Test:New:NewSub')
self.store.move_page(oldpath, newpath)
page = self.store.get_page(newpath)
self.assertEqual(page.dump('plain'), ['Foo Bar\n'])
class StubFile(File):
def __init__(self, text):
self.text = text
def read(self):
return self.text
def readlines(self):
return self.text.splitlines(True)
def write(self, *a):
assert False
def writelines(self, *a):
assert False
def open(self, *a):
assert False
def exists(self):
return len(self.text) > 0
zim-0.65/tests/translations.py 0000664 0001750 0001750 00000011115 12377624456 016371 0 ustar jaap jaap 0000000 0000000
import re
from glob import glob
from tests import TestCase
class TestTranslations(TestCase):
def runTest(self, verbose=False):
'''Sanity check translation files'''
pot_creation_date = None
for file in ['translations/zim.pot'] + glob('translations/*.po'):
if verbose:
print 'Checking %s' % file
t = TranslationFile(file)
if file == 'translations/zim.pot':
text = open(file).read()
self.assertFalse('namespace' in text.lower())
pot_creation_date = t.headers['POT-Creation-Date']
else:
if not t.headers['POT-Creation-Date'] == pot_creation_date:
print 'WARNING: Translation not based on up to date template: %s' % file
self.assertTrue(t.nplural > 0, 'Missing number of plurals: %s' % file)
t.assertValid()
class TranslationMessage(object):
@property
def nplural(self):
return len(self.msgstr)
def __init__(self, lineno, text):
self.lineno = lineno
self.msgid = None
self.msgid_plural = None
self.msgstr = []
self.comment = ''
text = text.replace('"\n"', '')
for line in text.splitlines():
if line.startswith('#'):
self.comment += line
else:
type, msg = line.split(' ', 1)
if type == 'msgid':
self.msgid = msg
elif type == 'msgid_plural':
self.msgid_plural = msg
elif type.startswith('msgstr'):
self.msgstr.append(msg)
else:
raise AssertionError, \
'Could not parse line: %s %s' % (type, msg)
assert self.msgid, 'No msgid found'
assert self.msgstr, 'No msgstr found'
_format_string_re = re.compile('%(?:\(\w+\))?\w')
# match "%s", "%d" etc. but also "%(foo)s" - but not just "%"
def check_nplural(self, nplural):
if self.msgid_plural and self.msgstr[0] != '""':
return self.nplural == nplural
else:
return True
def check_format_strings(self):
'''Check format strings in msgid_plural and msgstr'''
if 'strftime' in self.comment:
return self._check_strftime_string()
else:
return self._check_format_strings()
def _check_format_strings(self):
wanted = sorted( self._format_string_re.findall(self.msgid) )
if not wanted:
return True # no string format used
for msg in [self.msgid_plural] + self.msgstr:
if msg and not msg == '""':
got = sorted( self._format_string_re.findall(msg) )
if not got == wanted:
return False
else:
return True
def _check_strftime_string(self):
for msg in self.msgstr:
if msg and not msg == '""':
for c in re.findall('\%(.)', msg):
if c not in 'aAwdbBmyYHIpMSfzZjUWcxX%':
# valid charaters based on doc for datetime module
# other characters may be valid depending on platform
# but not robust
return False
else:
return True
class TranslationFile(object):
def __init__(self, file):
self.file = file
self.messages = []
buffer = []
lineno = 0
msgidlineno = 0
def flush():
if not buffer \
or all(line.startswith('#') for line in buffer):
return
try:
text = ''.join(buffer)
message = TranslationMessage(msgidlineno, text)
self.messages.append(message)
except AssertionError, error:
raise AssertionError, \
'Error while parsing %s msgid on line %i\n' % (self.file, msgidlineno) + error.message
for line in open(file):
lineno += 1
if not line or line.isspace():
flush()
buffer = []
else:
if line.startswith('msgid '):
msgidlineno = lineno
buffer.append(line)
else:
flush()
plural_forms = self.headers['Plural-Forms']
m = re.search(r'nplurals=(\d+);', plural_forms)
if m:
self.nplural = int( m.group(1) )
else:
self.nplural = None
@property
def headers(self):
message = self.get('""')
lines = message.msgstr[0].strip().strip('"').split('\\n')
headers = {}
for line in lines:
if not line:
continue
k, v = line.strip('"').replace('\\n', '').split(': ', 1)
headers[k] = v
return headers
def get(self, msgid):
for message in self.messages:
if message.msgid == msgid:
return message
else:
return None
def assertValid(self):
for message in self.messages:
if self.nplural and not message.check_nplural(self.nplural):
raise AssertionError, \
'Number of plural forms NOK in %s msgid on line %i\n' % (self.file, message.lineno) + message.msgid
if not message.check_format_strings():
raise AssertionError, \
'Error with format strings in %s msgid on line %i\n' % (self.file, message.lineno) + message.msgid
if __name__ == '__main__':
TestTranslations().runTest(verbose=True)
zim-0.65/tests/gui.py 0000664 0001750 0001750 00000064224 12614412356 014431 0 ustar jaap jaap 0000000 0000000 # -*- coding: utf-8 -*-
# Copyright 2009-2014 Jaap Karssenberg
from __future__ import with_statement
import tests
import os
import gtk
from zim.errors import Error
from zim.config import ConfigManager, VirtualConfigManager
from zim.notebook import get_notebook_list, Path, Page, NotebookInfo, NotebookConfig
from zim.formats import ParseTree
from zim.fs import File, Dir
from zim.gui.clipboard import Clipboard
import zim.gui
def setupGtkInterface(test, klass=None, notebook=None):
'''Setup a new GtkInterface object for testing.
Will have test notebook, and default preferences.
@param test: the test that wants to use this ui object
@param klass: the klass to use, defaults to L{GtkInterface}, but
could be partially mocked subclass
'''
if klass is None:
klass = zim.gui.GtkInterface
# start filtering
filter = FilterNoSuchImageWarning()
filter.wrap_test(test)
# create interface object with new notebook
if notebook is None:
dirpath = test.get_tmp_name()
notebook = tests.new_notebook(fakedir=dirpath)
config = VirtualConfigManager()
ui = klass(config=config, notebook=notebook)
ui.mainwindow.init_uistate()
ui.open_page(Path('Test:foo:bar'))
return ui
@tests.slowTest
class TestDialogs(tests.TestCase):
def setUp(self):
path = self.create_tmp_dir()
self.ui = MockUI('Test:foo:bar', fakedir=path)
def testOpenPageDialog(self):
'''Test OpenPageDialog dialog (Jump To...)'''
for name, path in (
(':foo', ':foo'),
('foo', ':Test:foo'),
('baz', ':Test:foo:baz'),
('+baz', ':Test:foo:bar:baz'),
):
dialog = zim.gui.OpenPageDialog(self.ui)
dialog.form.widgets['page'].set_text(name)
dialog.assert_response_ok()
self.assertEqual(self.ui.mock_calls[-1], ('open_page', Path(path)))
def testNewPageDialog(self):
'''Test NewPageDialog'''
self.ui.mainwindow = tests.MockObject()
self.ui.mainwindow.pageview = tests.MockObject()
for name, path in (
(':new', ':new'),
('foo:new', ':Test:foo:new'),
('new', ':Test:foo:new'),
('+new', ':Test:foo:bar:new'),
):
dialog = zim.gui.NewPageDialog(self.ui)
dialog.form.widgets['page'].set_text(name)
dialog.assert_response_ok()
self.assertEqual(self.ui.mock_calls[-1], ('save_page',))
self.assertEqual(self.ui.mock_calls[-2], ('open_page', Path(path)))
page = self.ui.notebook.get_page(Path(path))
self.assertTrue(page.exists())
page.modified = False # HACK so we can clean up
self.ui.notebook.delete_page(page)
for name, path in (
(':new', ':Test:foo:bar:new'),
('foo:new', ':Test:foo:bar:foo:new'),
('new', ':Test:foo:bar:new'),
('+new', ':Test:foo:bar:new'),
):
dialog = zim.gui.NewPageDialog(self.ui, subpage=True)
dialog.form.widgets['page'].set_text(name)
dialog.assert_response_ok()
self.assertEqual(self.ui.mock_calls[-1], ('save_page',))
self.assertEqual(self.ui.mock_calls[-2], ('open_page', Path(path)))
page = self.ui.notebook.get_page(Path(path))
self.assertTrue(page.exists())
page.modified = False # HACK so we can clean up
self.ui.notebook.delete_page(page)
dialog = zim.gui.NewPageDialog(self.ui)
dialog.form.widgets['page'].set_text(':Test:foo')
self.assertRaises(Error, dialog.assert_response_ok)
def testSaveCopyDialog(self):
'''Test SaveCopyDialog'''
tmp_dir = self.create_tmp_dir('testSaveCopyDialog')
file = File((tmp_dir, 'save_copy.txt'))
self.assertFalse(file.exists())
dialog = zim.gui.SaveCopyDialog(self.ui)
dialog.set_file(file)
#~ dialog.assert_response_ok()
#~ self.assertTrue(file.exists())
def testImportPageDialog(self):
'''Test ImportPageDialog'''
tmp_dir = self.create_tmp_dir('testImportPageDialog')
file = File((tmp_dir, 'import_page.txt'))
file.write('test 123\n')
self.assertTrue(file.exists())
dialog = zim.gui.ImportPageDialog(self.ui)
dialog.set_file(file)
#~ dialog.assert_response_ok()
#~ self.assertEqual(self.ui.mock_calls[-1], ('open_page', Path(':import_page')))
#~ self.assertTrue(self.ui.notebook.get_page(':import_page').exists())
def testMovePageDialog(self):
'''Test MovePageDialog'''
# Can't test much here except for the call to do_move_page
self.ui.mock_method('do_move_page', True)
dialog = zim.gui.MovePageDialog(self.ui, path=Path('Test:foo:bar'))
self.assertTrue(dialog.form['update'])
self.assertTrue(dialog.form.widgets['update'].get_property('sensitive'))
dialog.form['parent'] = Path('New')
dialog.assert_response_ok()
self.assertEqual(self.ui.mock_calls[-1],
('do_move_page', Path('Test:foo:bar'), Path('New:bar'), True))
dialog = zim.gui.MovePageDialog(self.ui, path=Path('New:bar'))
self.assertFalse(dialog.form['update'])
self.assertFalse(dialog.form.widgets['update'].get_property('sensitive'))
dialog.form['parent'] = Path('foo')
dialog.assert_response_ok()
self.assertEqual(self.ui.mock_calls[-1],
('do_move_page', Path('New:bar'), Path('foo:bar'), False))
def testRenamePageDialog(self):
'''Test RenamePageDialog'''
# Can't test much here except for the call to do_rename_page
self.ui.mock_method('do_rename_page', True)
dialog = zim.gui.RenamePageDialog(self.ui, path=Path('Test:foo:bar'))
self.assertTrue(dialog.form['update'])
self.assertTrue(dialog.form.widgets['update'].get_property('sensitive'))
self.assertFalse(dialog.form['head']) # There is no heading
self.assertTrue(dialog.form.widgets['head'].get_property('sensitive'))
dialog.form['name'] = 'New'
dialog.assert_response_ok()
self.assertEqual(self.ui.mock_calls[-1],
('do_rename_page', Path('Test:foo:bar'), 'New', False, True))
dialog = zim.gui.RenamePageDialog(self.ui, path=Path('New:bar'))
self.assertFalse(dialog.form['update'])
self.assertFalse(dialog.form.widgets['update'].get_property('sensitive'))
self.assertFalse(dialog.form['head'])
self.assertFalse(dialog.form.widgets['head'].get_property('sensitive'))
dialog.form['name'] = 'New'
dialog.assert_response_ok()
self.assertEqual(self.ui.mock_calls[-1],
('do_rename_page', Path('New:bar'), 'New', False, False))
def testRenamePageDialogWithHeadingChanges(self):
'''Test RenamePageDialog's heading auto-change option depending on
whether we have a changed heading or not.
'''
tree = ParseTree().fromstring('')
tree.set_heading("bar")
self.ui.page = Page(Path("Test:foo:bar"), parsetree=tree)
self.ui.notebook.get_page = lambda path: self.ui.page
dialog = zim.gui.RenamePageDialog(self.ui, path=Path("Test:foo:bar"))
self.assertTrue(dialog.form['head'])
tree.set_heading("different")
dialog = zim.gui.RenamePageDialog(self.ui, path=Path("Test:foo:bar"))
self.assertFalse(dialog.form['head'])
def testDeletePageDialog(self):
'''Test DeletePageDialog'''
# just check inputs are OK - skip output
dialog = zim.gui.DeletePageDialog(self.ui, path=Path('Test:foo:bar'))
self.assertTrue(dialog.links_checkbox.get_active())
self.assertTrue(dialog.links_checkbox.get_property('sensitive'))
dialog = zim.gui.DeletePageDialog(self.ui, path=Path('New'))
self.assertFalse(dialog.links_checkbox.get_active())
self.assertFalse(dialog.links_checkbox.get_property('sensitive'))
dialog.assert_response_ok()
def testAttachFileDialog(self):
'''Test AttachFileDialog'''
tmp_dir = self.create_tmp_dir('testAttachFileDialog')
file = File((tmp_dir, 'file_to_be_attached'))
file.write('Test 1 2 3\n')
newfile = File((tmp_dir, 'attachments', 'Test', 'foo', 'file_to_be_attached'))
self.assertTrue(file.exists())
self.assertFalse(newfile.exists())
dialog = zim.gui.AttachFileDialog(self.ui, path=Path('Test:foo'))
dialog.set_file(file)
#~ dialog.assert_response_ok()
#~ self.assertTrue(file.exists()) # No move or delete happened
#~ self.assertTrue(newfile.exists())
#~ self.assertTrue(newfile.compare(file))
def testSearchDialog(self):
'''Test SearchDialog'''
from zim.gui.searchdialog import SearchDialog
self.ui.notebook = tests.new_notebook()
dialog = SearchDialog(self.ui)
dialog.query_entry.set_text('Foo')
dialog.query_entry.activate()
model = dialog.results_treeview.get_model()
self.assertTrue(len(model) > 3)
self.ui.mainwindow = tests.MockObject()
self.ui.mainwindow.pageview = tests.MockObject()
col = dialog.results_treeview.get_column(0)
dialog.results_treeview.row_activated((0,), col)
def testCustomToolDialog(self):
'''Test CustomTool dialogs'''
from zim.gui.customtools import CustomToolManagerDialog
from zim.gui.customtools import EditCustomToolDialog
## CustomToolManager dialog
dialog = CustomToolManagerDialog(self.ui)
properties = {
'Name': 'Foo',
'Comment': 'Test Foo',
'X-Zim-ExecTool': 'foo %u',
'X-Zim-ReadOnly': False,
'X-Zim-ShowInToolBar': False,
'X-Zim-ReplaceSelection': False,
}
dialog.manager.create(**properties)
dialog.listview.refresh()
dialog.destroy()
## Edit custom tool dialog
dialog = EditCustomToolDialog(self.ui)
input = {
'Name': 'Foo',
'Comment': 'Test Foo',
'X-Zim-ExecTool': 'foo %u',
'X-Zim-ReadOnly': False,
'X-Zim-ShowInToolBar': False,
'X-Zim-ReplaceSelection': False,
}
dialog.form.update(input)
output = dialog.assert_response_ok()
input['Icon'] = None
self.assertEqual(output, input)
def testPropertiesDialog(self):
'''Test PropertiesDialog'''
from zim.gui.propertiesdialog import PropertiesDialog
self.ui.readonly = True
dialog = PropertiesDialog(self.ui)
dialog.assert_response_ok()
from zim.config import INIConfigFile
notebook = self.ui.notebook
file = notebook.dir.file('notebook.zim')
notebook.config = NotebookConfig(file)
self.ui.readonly = False
config1 = {
'name': 'Notebook Foo',
'interwiki': None,
'home': Path('Home'),
'icon': './icon.png',
'document_root': File('/foo').path, # win32 save test
'profile': None,
}
config2 = {
'name': 'Notebook Bar',
'interwiki': 'FooBar',
'home': Path('HomeSweetHome'),
'icon': './picture.png',
'document_root': File('/bar').path, # win32 save test
'profile': 'foo',
}
notebook.save_properties(**config1)
for key in config1:
self.assertEqual(notebook.config['Notebook'][key], config1[key])
dialog = PropertiesDialog(self.ui)
dialog.assert_response_ok()
for key in config1:
self.assertEqual(notebook.config['Notebook'][key], config1[key])
self.assertEqual(notebook.name, config1['name'])
self.assertEqual(notebook.get_home_page(), config1['home'])
self.assertEqual(notebook.icon, notebook.dir.file(config1['icon']).path)
self.assertEqual(notebook.document_root, Dir(config1['document_root']))
dialog = PropertiesDialog(self.ui)
dialog.form.update(config2)
dialog.assert_response_ok()
for key in config1:
self.assertEqual(notebook.config['Notebook'][key], config2[key])
self.assertEqual(notebook.name, config2['name'])
self.assertEqual(notebook.get_home_page(), config2['home'])
self.assertEqual(notebook.icon, notebook.dir.file(config2['icon']).path)
self.assertEqual(notebook.document_root, Dir(config2['document_root']))
def testPreferencesDialog(self):
'''Test PreferencesDialog'''
from zim.gui.preferencesdialog import PreferencesDialog, PluginConfigureDialog
self.clear_tmp_dir()
gui = setupGtkInterface(self)
gui.register_preferences('GtkInterface', zim.gui.ui_preferences)
gui.register_preferences('PageView', zim.gui.pageview.ui_preferences)
self.ui.preferences_register = gui.preferences_register
self.ui.preferences = gui.preferences
self.ui.plugins = gui.plugins
self.ui.config = gui.config
## Test get/set simple value
self.assertEquals(self.ui.preferences['GtkInterface']['toggle_on_ctrlspace'], False)
dialog = PreferencesDialog(self.ui)
self.assertEquals(dialog.forms['Interface']['toggle_on_ctrlspace'], False)
dialog.assert_response_ok()
self.assertEquals(self.ui.preferences['GtkInterface']['toggle_on_ctrlspace'], False)
dialog = PreferencesDialog(self.ui)
dialog.forms['Interface']['toggle_on_ctrlspace'] = True
dialog.assert_response_ok()
self.assertEquals(self.ui.preferences['GtkInterface']['toggle_on_ctrlspace'], True)
## Test font button
text_style = gui.config.get_config_dict('/style.conf')
text_style['TextView']['font'] = 'Sans 12'
dialog = PreferencesDialog(self.ui)
self.assertEquals(dialog.forms['Interface']['use_custom_font'], True)
dialog.assert_response_ok()
self.assertEqual(text_style['TextView']['font'], 'Sans 12')
self.assertFalse(any(['use_custom_font' in d for d in self.ui.preferences.values()]))
text_style['TextView']['font'] = 'Sans 12'
dialog = PreferencesDialog(self.ui)
self.assertEquals(dialog.forms['Interface']['use_custom_font'], True)
dialog.forms['Interface']['use_custom_font'] = False
dialog.assert_response_ok()
self.assertEqual(text_style['TextView']['font'], None)
self.assertFalse(any(['use_custom_font' in d for d in self.ui.preferences.values()]))
## Plugin Config dialog
from zim.plugins.calendar import CalendarPlugin
plugin = CalendarPlugin()
pref_dialog = PreferencesDialog(self.ui)
dialog = PluginConfigureDialog(pref_dialog, plugin)
dialog.assert_response_ok()
def testTemplateEditorDialog(self):
from zim.gui.templateeditordialog import TemplateEditorDialog
dialog = TemplateEditorDialog(self.ui)
# TODO what to test here ??
dialog.assert_response_ok()
def testRecentChangesDialog(self):
from zim.gui.recentchangesdialog import RecentChangesDialog
self.clear_tmp_dir()
ui = setupGtkInterface(self)
dialog = RecentChangesDialog(ui)
dialog.assert_response_ok()
# Test for ExportDialog can be found in test/export.py
# Test for NotebookDialog is in separate class below
class FilterNoSuchImageWarning(tests.LoggingFilter):
logger = 'zim.gui.pageview'
message = 'No such image:'
@tests.slowTest
class TestGtkInterface(tests.TestCase):
def setUp(self):
self.ui = setupGtkInterface(self)
def tearDown(self):
self.ui.close()
def testInitialization(self):
'''Test Gtk interface initialization'''
# test read only (starts readonly because notebook has no dir or file)
self.assertTrue(self.ui.readonly)
self.ui.set_readonly(False)
self.assertFalse(self.ui.readonly)
self.ui.set_readonly(True)
self.assertTrue(self.ui.readonly)
# TODO more tests for readonly pages etc.
# test populating menus
menu = gtk.Menu()
self.ui.populate_popup('page_popup', menu)
items = menu.get_children()
self.assertGreater(len(items), 3)
# check registering an URL handler
func = tests.Counter(True)
self.ui.register_url_handler('foo', func)
self.ui.open_url('foo://bar')
self.assertTrue(func.count == 1)
self.ui.unregister_url_handler(func)
# check default plugins are loaded
self.assertGreaterEqual(len(self.ui.plugins), 3)
def testMainWindow(self):
'''Test main window'''
path = Path('Test:foo:bar')
window = self.ui.mainwindow
self.assertTrue(window.uistate['show_menubar'])
window.toggle_menubar()
self.assertFalse(window.uistate['show_menubar'])
window.toggle_menubar()
self.assertTrue(window.uistate['show_menubar'])
self.assertTrue(window.uistate['show_toolbar'])
window.toggle_toolbar()
self.assertFalse(window.uistate['show_toolbar'])
window.toggle_toolbar()
self.assertTrue(window.uistate['show_toolbar'])
self.assertTrue(window.uistate['show_statusbar'])
window.toggle_statusbar()
self.assertFalse(window.uistate['show_statusbar'])
window.toggle_statusbar()
self.assertTrue(window.uistate['show_statusbar'])
self.assertTrue(window.uistate['left_pane'][0])
window.toggle_panes()
self.assertFalse(window.uistate['left_pane'][0])
window.toggle_panes()
self.assertTrue(window.uistate['left_pane'][0])
# note: focus starts at sidepane due to toggle_panes above
self.assertEqual(window.get_focus(), window.pageindex.treeview)
self.assertEqual(window.get_selected_path(), path)
window.toggle_sidepane_focus()
self.assertEqual(window.get_focus(), window.pageview.view)
self.assertEqual(window.get_selected_path(), path)
window.toggle_sidepane_focus()
self.assertEqual(window.get_focus(), window.pageindex.treeview)
# TODO also check this with "show_sidepane" off
self.assertEqual(window.uistate['pathbar_type'], zim.gui.PATHBAR_RECENT)
for style in (
zim.gui.PATHBAR_NONE,
zim.gui.PATHBAR_HISTORY,
zim.gui.PATHBAR_PATH,
zim.gui.PATHBAR_RECENT,
):
window.set_pathbar(style)
self.assertEqual(window.uistate['pathbar_type'], style)
# TODO specific test for pathbar to exercize history, add / move / remove pages etc.
# note: no default style here - system default unknown
for style in (
zim.gui.TOOLBAR_ICONS_AND_TEXT,
zim.gui.TOOLBAR_ICONS_ONLY,
zim.gui.TOOLBAR_TEXT_ONLY,
):
window.set_toolbar_style(style)
self.assertEqual(window.preferences['GtkInterface']['toolbar_style'], style)
# note: no default style here - system default unknown
for size in (
zim.gui.TOOLBAR_ICONS_LARGE,
zim.gui.TOOLBAR_ICONS_SMALL,
zim.gui.TOOLBAR_ICONS_TINY,
):
window.set_toolbar_size(size)
self.assertEqual(window.preferences['GtkInterface']['toolbar_size'], size)
# FIXME: test fails because "readonly" not active because notebook was already readonly, so action never activatable
#~ self.assertTrue(ui.readonly)
#~ self.assertTrue(window.uistate['readonly'])
#~ window.toggle_readonly()
#~ self.assertFalse(ui.readonly)
#~ self.assertFalse(window.uistate['readonly'])
#~ window.toggle_readonly()
#~ self.assertTrue(ui.readonly)
#~ self.assertTrue(window.uistate['readonly'])
def testNavigation(self):
'''Test navigating the notebook with gtk interface'''
# build up some history
history = (
Path('Test:foo:bar'),
Path('Test:'),
Path('Test:foo:'),
Path('Test:foo:bar'),
)
for path in history:
self.ui.open_page(path)
self.assertEqual(self.ui.page, path)
# check forward & backward
for path in reversed(history[:-1]):
self.assertTrue(self.ui.open_page_back())
self.assertEqual(self.ui.page, path)
self.assertFalse(self.ui.open_page_back())
for path in history[1:]:
self.assertTrue(self.ui.open_page_forward())
self.assertEqual(self.ui.page, path)
self.assertFalse(self.ui.open_page_forward())
# check upward and downward
for path in (Path('Test:foo:'), Path('Test:')):
self.assertTrue(self.ui.open_page_parent())
self.assertEqual(self.ui.page, path)
self.assertFalse(self.ui.open_page_parent())
for path in (Path('Test:foo:'), Path('Test:foo:bar')):
self.assertTrue(self.ui.open_page_child())
self.assertEqual(self.ui.page, path)
self.assertFalse(self.ui.open_page_child())
# previous and next
self.assertTrue(self.ui.open_page_previous())
self.assertTrue(self.ui.open_page_next())
self.assertEqual(self.ui.page, Path('Test:foo:bar'))
def testSave(self):
'''Test saving a page from the interface'''
self.ui.set_readonly(False)
self.ui.open_page(Path('Non-exsiting:page'))
self.assertFalse(self.ui.page.exists())
self.assertTrue(self.ui.page.get_parsetree() is None)
self.assertTrue(self.ui.mainwindow.pageview._showing_template) # check HACK
self.ui.save_page()
self.assertFalse(self.ui.page.get_parsetree() is None)
def testPageMove(self):
oldpath, newpath = Path('Movers:Stator:Mover'), Path('Movers:Mover')
# Open page and process message queue to sync tree view
indexpath = self.ui.notebook.index.lookup_path(oldpath)
self.ui.open_page(indexpath)
while gtk.events_pending():
gtk.main_iteration(False)
# Test actual moving
page = self.ui.notebook.get_page(oldpath)
text = page.dump('wiki')
self.ui.notebook.index.ensure_update()
self.ui.notebook.move_page(oldpath, newpath)
self.ui.notebook.index.ensure_update()
# newpath should exist and look like the old one
page = self.ui.notebook.get_page(newpath)
self.assertEqual(page.dump('wiki'), text)
# oldpath should be deleted
page = self.ui.notebook.get_page(oldpath)
self.assertFalse(page.haschildren)
self.assertFalse(page.hascontent)
# TODO notebook manipulation (new (sub)page, rename, delete ..)
# merge with tests for dialogs (?)
def testClipboard(self):
self.ui.copy_location()
self.assertEqual(Clipboard.get_text(), 'Test:foo:bar')
@tests.slowTest
class TestClickLink(tests.TestCase):
'''Test to check pageview and GtkInterface play together nicely when
a link is clicked
'''
def setUp(self):
class MyMock(zim.gui.GtkInterface, tests.MockObjectBase):
def __init__(self, *arg, **kwarg):
zim.gui.GtkInterface.__init__(self, *arg, **kwarg)
tests.MockObjectBase.__init__(self)
for method in (
'open_notebook',
'open_page',
'open_file',
'_open_with_emailclient',
'_open_with_webbrowser',
'_open_with_filebrowser',
'_open_with',
):
self.mock_method(method, None)
self.ui = setupGtkInterface(self, klass=MyMock)
def runTest(self):
self.assertRaises(AssertionError, self.ui.open_url, 'foo@bar.com')
# this is not a URI, "mailto:foo@bar.com" is
# Note: same list of test uris is testing in tests.parsing as well
for href, type in (
('zim+file://foo/bar?dus.txt', 'notebook'),
('file:///foo/bar', 'file'),
('http://foo/bar', 'http'),
('http://192.168.168.100', 'http'),
('file+ssh://foo/bar', 'file+ssh'),
('mailto:foo@bar.com', 'mailto'),
('mailto:foo.com', 'page'),
('foo@bar.com', 'mailto'),
('mailto:foo//bar@bar.com', 'mailto'), # is this a valid mailto uri ?
('mid:foo@bar.org', 'mid'),
('cid:foo@bar.org', 'cid'),
('./foo/bar', 'file'),
('/foo/bar', 'file'),
('~/foo', 'file'),
('C:\\foo', 'file'),
('wp?foo', 'interwiki'),
('http://foo?bar', 'http'),
('\\\\host\\foo\\bar', 'smb'),
('foo', 'page'),
('foo:bar', 'page'),
):
#~ print ">> LINK %s (%s)" % (href, type)
#~ self.ui.open_url(href)
self.ui.mainwindow.pageview.do_link_clicked({'href': href})
msg = "Clicked: \"%s\" resulted in: \"%s\"" % (href, self.ui.mock_calls[-1])
if type == 'notebook':
self.assertTrue(self.ui.mock_calls[-1][0] == 'open_notebook', msg=msg)
elif type == 'page':
self.assertTrue(self.ui.mock_calls[-1][0] == 'open_page', msg=msg)
elif type == 'file':
self.assertTrue(self.ui.mock_calls[-1][0] == 'open_file', msg=msg)
elif type == 'mailto':
self.assertTrue(self.ui.mock_calls[-1][0] in ('_open_with_emailclient', '_open_with'), msg=msg)
elif type == 'smb' and os.name == 'nt':
self.assertTrue(self.ui.mock_calls[-1][0] == '_open_with_filebrowser', msg=msg)
else:
self.assertTrue(self.ui.mock_calls[-1][0] in ('_open_with_webbrowser', '_open_with'), msg=msg)
self.ui.mock_calls = [] # reset
# Some more tests that may not be covered above
for href, type in (
('zim+file://foo/bar?dus.txt', 'notebook'),
('file:///foo/bar', 'file'),
('mailto:foo@bar.com', 'mailto'),
):
#~ print ">> OPEN_URL %s (%s)" % (href, type)
self.ui.open_url(href)
msg = "open_url('%s')\nResulted in: %s" % (href, self.ui.mock_calls[-1])
if type == 'notebook':
self.assertTrue(self.ui.mock_calls[-1][0] == 'open_notebook', msg=msg)
elif type == 'file':
self.assertTrue(self.ui.mock_calls[-1][0] == '_open_with_webbrowser', msg=msg)
elif type == 'mailto':
self.assertTrue(self.ui.mock_calls[-1][0] in ('_open_with_emailclient', '_open_with'), msg=msg)
self.ui.mock_calls = [] # reset
# TODO test plugin with custom handler
@tests.slowTest
class TestNotebookDialog(tests.TestCase):
def setUp(self):
config = ConfigManager()
list = config.get_config_file('notebooks.list')
file = list.file
if file.exists():
file.remove()
def runTest(self):
from zim.gui.notebookdialog import prompt_notebook, \
AddNotebookDialog, NotebookDialog
tmpdir = self.create_tmp_dir()
dir1 = Dir(tmpdir + '/mynotebook1')
dir2 = Dir(tmpdir + '/mynotebook2')
# First time we get directly the AddNotebookDialog
def doAddNotebook(dialog):
self.assertTrue(isinstance(dialog, AddNotebookDialog))
dialog.form['name'] = 'Foo'
dialog.form['folder'] = dir1.path
dialog.assert_response_ok()
with tests.DialogContext(doAddNotebook):
info = prompt_notebook()
self.assertIsNotNone(info)
self.assertEqual(info.uri, dir1.uri)
# Second time we get the list
def testNotebookDialog(dialog):
self.assertTrue(isinstance(dialog, NotebookDialog))
selection = dialog.treeview.get_selection()
selection.select_path((0,)) # select first and only notebook
dialog.assert_response_ok()
with tests.DialogContext(testNotebookDialog):
info = prompt_notebook()
self.assertIsNotNone(info)
self.assertEqual(info.uri, dir1.uri)
# Third time we add a notebook and set the default
def doAddNotebook(dialog):
self.assertTrue(isinstance(dialog, AddNotebookDialog))
dialog.form['name'] = 'Bar'
dialog.form['folder'] = dir2.path
dialog.assert_response_ok()
def testAddNotebook(dialog):
self.assertTrue(isinstance(dialog, NotebookDialog))
with tests.DialogContext(doAddNotebook):
dialog.do_add_notebook()
dialog.combobox.set_active(0)
selection = dialog.treeview.get_selection()
selection.select_path((1,)) # select newly added notebook
dialog.assert_response_ok()
with tests.DialogContext(testAddNotebook):
info = prompt_notebook()
self.assertIsNotNone(info)
self.assertEqual(info.uri, dir2.uri)
# Check the notebook exists and the notebook list looks like it should
for dir in (dir1, dir2):
self.assertTrue(dir.exists())
self.assertTrue(dir.file('notebook.zim').exists())
list = get_notebook_list()
self.assertTrue(len(list) == 2)
self.assertEqual(list[0], NotebookInfo(dir1.uri, name='Foo'))
self.assertEqual(list[1], NotebookInfo(dir2.uri, name='Bar'))
self.assertEqual(list.default, NotebookInfo(dir1.uri, name='Foo'))
# Now unset the default and again check the notebook list
def unsetDefault(dialog):
self.assertTrue(isinstance(dialog, NotebookDialog))
dialog.combobox.set_active(-1)
selection = dialog.treeview.get_selection()
selection.select_path((1,)) # select newly added notebook
dialog.assert_response_ok()
with tests.DialogContext(unsetDefault):
info = prompt_notebook()
self.assertIsNotNone(info)
self.assertEqual(info.uri, dir2.uri)
list = get_notebook_list()
self.assertTrue(len(list) == 2)
self.assertTrue(list.default is None)
class MockUI(tests.MockObject):
def __init__(self, page=None, fakedir=None):
tests.MockObject.__init__(self)
self.tmp_dir = self.create_tmp_dir()
if page and not isinstance(page, Path):
self.page = Path(page)
else:
self.page = page
self.mainwindow = None
self.notebook = tests.new_notebook(fakedir=fakedir)
zim-0.65/tests/widgets.py 0000664 0001750 0001750 00000021253 12411243031 015271 0 ustar jaap jaap 0000000 0000000 # -*- coding: utf-8 -*-
# Copyright 2011 Jaap Karssenberg
import tests
from zim.fs import File, Dir
from zim.notebook import Path
from zim.gui.widgets import *
class TestFunctions(tests.TestCase):
def runTest(self):
self.assertEqual(encode_markup_text(' &bar'), '<foo> &bar')
self.assertEqual(decode_markup_text('<foo> &bar'), ' &bar')
self.assertEqual(decode_markup_text('<foo> &bar'), ' &bar')
class TestInputEntry(tests.TestCase):
def runTest(self):
'''Test InputEntry widget'''
entry = InputEntry()
self.assertTrue(entry.get_input_valid())
self.assertEqual(entry.get_text(), '')
# test unicode nd whitespace
entry.set_text(u'\u2022 foo ')
text = entry.get_text()
self.assertTrue(isinstance(text, unicode))
self.assertEqual(text, u'\u2022 foo')
self.assertTrue(entry.get_input_valid())
# test set invalid + change
entry.set_input_valid(False)
self.assertFalse(entry.get_input_valid())
entry.set_text(u'foo bar')
self.assertTrue(entry.get_input_valid())
# test invalid but now with allow_empty=False
entry = InputEntry(allow_empty=False)
self.assertFalse(entry.get_input_valid())
entry.set_text(u'foo bar')
self.assertTrue(entry.get_input_valid())
entry.set_text(u'')
self.assertFalse(entry.get_input_valid())
# and with a function
entry = InputEntry(check_func=lambda text: text.startswith('a'))
self.assertFalse(entry.get_input_valid())
entry.set_text(u'foo bar')
self.assertFalse(entry.get_input_valid())
entry.set_text(u'aa foo bar')
self.assertTrue(entry.get_input_valid())
entry.set_text(u'')
self.assertFalse(entry.get_input_valid())
# and with placeholder text
entry = InputEntry(allow_empty=False, placeholder_text='PLACEHOLDER')
self.assertEqual(entry.get_text(), u'')
self.assertFalse(entry.get_input_valid())
entry.set_text(u'foo bar')
self.assertEqual(entry.get_text(), u'foo bar')
self.assertTrue(entry.get_input_valid())
entry.set_text(u'')
self.assertEqual(entry.get_text(), u'')
self.assertFalse(entry.get_input_valid())
class TestFileEntry(tests.TestCase):
def setUp(self):
path = self.get_tmp_name()
self.notebook = tests.new_notebook(fakedir=path)
self.entry = FileEntry()
def runTest(self):
'''Test FileEntry widget'''
path = Path('Foo:Bar')
entry = self.entry
entry.set_use_relative_paths(self.notebook, path)
home = Dir('~')
dir = self.notebook.dir
for file, text in (
(home.file('zim-test.txt'), '~/zim-test.txt'),
(dir.file('Foo/Bar/test.txt'), './test.txt'),
(File('/test.txt'), File('/test.txt').path), # win32 save
):
entry.set_file(file)
self.assertEqual(entry.get_text(), text)
self.assertEqual(entry.get_file(), file)
self.notebook.config['Notebook']['document_root'] = './notebook_document_root'
self.notebook.do_properties_changed() # parse config
doc_root = self.notebook.document_root
self.assertEqual(doc_root, dir.subdir('notebook_document_root'))
for file, text in (
(home.file('zim-test.txt'), '~/zim-test.txt'),
(dir.file('Foo/Bar/test.txt'), './test.txt'),
(File('/test.txt'), File('/test.txt').uri), # win32 save
(doc_root.file('test.txt'), '/test.txt'),
):
entry.set_file(file)
self.assertEqual(entry.get_text(), text)
self.assertEqual(entry.get_file(), file)
entry.set_use_relative_paths(self.notebook, None)
for file, text in (
(home.file('zim-test.txt'), '~/zim-test.txt'),
(dir.file('Foo/Bar/test.txt'), './Foo/Bar/test.txt'),
(File('/test.txt'), File('/test.txt').uri), # win32 save
(doc_root.file('test.txt'), '/test.txt'),
):
entry.set_file(file)
self.assertEqual(entry.get_text(), text)
self.assertEqual(entry.get_file(), file)
entry.set_use_relative_paths(notebook=None)
for file, text in (
(home.file('zim-test.txt'), '~/zim-test.txt'),
#~ (dir.file('Foo/Bar/test.txt'), './test.txt'),
(File('/test.txt'), File('/test.txt').path), # win32 save
):
entry.set_file(file)
self.assertEqual(entry.get_text(), text)
self.assertEqual(entry.get_file(), file)
class TestPageEntry(tests.TestCase):
entryklass = PageEntry
def setUp(self):
path = self.get_tmp_name()
self.notebook = tests.new_notebook(fakedir=path)
self.reference = Path('Test:foo')
self.entry = self.entryklass(self.notebook, self.reference)
def runTest(self):
'''Test PageEntry widget'''
entry = self.entry
reference = self.reference
entry.set_path(Path('Test'))
self.assertEqual(entry.get_text(), ':Test')
self.assertEqual(entry.get_path(), Path('Test'))
entry.set_text('bar')
self.assertEqual(entry.get_path(), Path('Bar')) # resolved due to placeholder
entry.set_text('non existing')
self.assertEqual(entry.get_path(), Path('Test:non existing'))
entry.set_text('+bar')
self.assertEqual(entry.get_path(), Path('Test:foo:bar'))
entry.set_text(':bar')
self.assertEqual(entry.get_path(), Path('Bar'))
## Test completion
def get_completions(entry):
completion = entry.get_completion()
model = completion.get_model()
return [r[0] for r in model]
entry.set_text('+T')
self.assertEqual(get_completions(entry), ['+bar'])
entry.set_text(':T')
completions = get_completions(entry)
self.assertTrue(len(completions) > 5 and ':Test' in completions)
entry.set_text('T')
self.assertTrue(len(completions) > 5 and ':Test' in completions)
# completion now has full notebook
entry.set_text('Test:')
self.assertEqual(get_completions(entry), ['Test:foo', 'Test:Foo Bar', 'Test:Foo(Bar)', 'Test:tags', 'Test:wiki'])
class TestNamespaceEntry(TestPageEntry):
entryklass = NamespaceEntry
def runTest(self):
'''Test NamespaceEntry widget'''
entry = self.entry
entry.set_text('')
entry.do_focus_in_event(gtk.gdk.Event(gtk.gdk.FOCUS_CHANGE))
self.assertTrue(entry.get_input_valid())
self.assertEqual(entry.get_text(), '') # No '' or something !
self.assertEqual(entry.get_path(), Path(':'))
entry.do_focus_out_event(gtk.gdk.Event(gtk.gdk.FOCUS_CHANGE))
self.assertTrue(entry.get_input_valid())
self.assertEqual(entry.get_text(), '') # No '' or something !
self.assertEqual(entry.get_path(), Path(':'))
TestPageEntry.runTest(self)
class TestLinkEntry(TestPageEntry, TestFileEntry):
entryklass = LinkEntry
def runTest(self):
'''Test LinkEntry widget'''
TestPageEntry.runTest(self)
TestFileEntry.runTest(self)
class TestInputForm(tests.TestCase):
def runTest(self):
'''Test InputForm widget'''
inputs = [
('foo', 'string', 'Foo'),
('bar', 'password', 'Bar'),
('check', 'bool', 'Check'),
('width', 'int', 'Width', (0, 10)),
('app', 'choice', 'Application', ['foo', 'bar', 'baz']),
('page', 'page', 'Page'),
('namespace', 'namespace', 'Namespace'),
#~ ('link', 'link', 'Link'),
('file', 'file', 'File'),
('image', 'image', 'Image'),
('folder', 'dir', 'Folder')
]
values1 = {
'foo': '',
'bar': 'dus',
'check': True,
'width': 1,
'app': 'foo',
'page': ':foo:bar:Baz', # explicit string input
'namespace': ':foo:bar:Baz',
#~ 'link': '+Baz',
'file': '/foo/bar',
'image': '/foo/bar.png',
'folder': '/foo/bar',
}
values2 = {
'foo': 'tja',
'bar': 'hmm',
'check': False,
'width': 3,
'app': 'bar',
'page': Path(':Dus:Baz'), # explicit Path input
'namespace': Path(':Dus:Baz'),
#~ 'link': ':Foo',
'file': '/foo/bar/baz',
'image': '/foo.png',
'folder': '/foo/bar/baz',
}
def assertEqual(U, V):
self.assertEqual(set(U.keys()), set(V.keys()))
for k, v in V.items():
if isinstance(U[k], Path) and isinstance(v, basestring):
v = Path(v)
elif isinstance(U[k], File) and isinstance(v, basestring):
v = File(v)
elif isinstance(U[k], Dir) and isinstance(v, basestring):
v = Dir(v)
self.assertEqual(U[k], v)
notebook = tests.new_notebook()
form = InputForm(inputs, values1, notebook=notebook)
for input in inputs:
name = input[0]
self.assertTrue(form.widgets[name], 'Missing input "%s"' % name)
assertEqual(form, values1)
form.update(values2)
assertEqual(form, values2)
config = {}
config.update(form)
assertEqual(config, values2)
form.show_all()
form.focus_first()
i = 0
while form.focus_next():
i += 1
self.assertEqual(i, 9)
@tests.slowTest
class TestFileDialog(tests.TestCase):
def runTest(self):
tmp_dir = self.create_tmp_dir()
file = File((tmp_dir, 'test.txt'))
file.write('test 123')
self.assertTrue(file.exists())
dialog = FileDialog(None, 'Test')
dialog.set_file(file)
#~ myfile = dialog.get_file()
#~ self.assertTrue(myfile)
#~ self.assertTrue(myfile == file)
#~ dialog.assert_response_ok()
#~ self.assertTrue(dialog.result == file)
# TODO select multiple
# TODO select folder
# TODO add filters
zim-0.65/tests/history.py 0000664 0001750 0001750 00000025142 12374655231 015346 0 ustar jaap jaap 0000000 0000000 # -*- coding: utf-8 -*-
# Copyright 2008-2013 Jaap Karssenberg
from __future__ import with_statement
import tests
import copy
import zim.history
from zim.history import History, HistoryPath, RecentPath
from zim.notebook import Path
from zim.config import INIConfigFile
class VirtualFile(object):
### TODO - proper class for this in zim.fs
### unify with code in config manager
def __init__(self, lines):
self.lines = lines
def readlines(self):
return self.lines
def connect(self, handler, *a):
pass
def disconnect(self, handler):
pass
class TestHistory(tests.TestCase):
def setUp(self):
zim.history.MAX_HISTORY = 100
self.notebook = tests.new_notebook()
self.pages = [self.notebook.get_page(Path(name))
for name in self.notebook.testdata_manifest]
def assertCurrentEquals(self, history, path):
current = history.get_current()
self.assertTrue(isinstance(current, HistoryPath))
self.assertEqual(current.name, path.name)
def assertHistoryEquals(self, history, pages):
self._checkPaths(history.get_history(), pages, HistoryPath)
def assertRecentEquals(self, history, pages):
self._checkPaths(history.get_recent(), pages, RecentPath)
def _checkPaths(self, paths, wanted, klass):
paths = list(paths)
paths.reverse()
self.assertTrue(any(isinstance(p, klass) for p in paths), 'All should have klass: %s' % klass)
self.assertEqual([p.name for p in paths], [p.name for p in wanted])
def testState(self):
history = History(self.notebook)
for page in self.pages:
history.append(page)
self.assertHistoryEquals(history, self.pages)
path = history.get_current()
self.assertEqual(history.get_state(path), (None, None))
path.cursor = 42
self.assertEqual(history.get_state(path), (42, None))
def testLinear(self):
'''Walk back and forth through the history'''
history = History(self.notebook)
self.assertTrue(history.get_current() is None)
for page in self.pages:
history.append(page)
self.assertHistoryEquals(history, self.pages)
self.assertCurrentEquals(history, self.pages[-1])
pages = list(history.get_history())
self.assertEqual(pages[0], history.get_current())
self.assertEqual(len(pages), len(self.pages))
self.assertEqual(pages[0].cursor, None)
# Newly appended pages should not have the cursor
# set - pageview has logic to do the right thing when
# no cursor is set. Setting default e.g. 0 will
# overrule this logic.
# walk backwards
for i in range(2, len(self.pages)+1):
prev = history.get_previous()
self.assertFalse(prev is None)
self.assertEqual(prev.name, self.pages[-i].name)
self.assertFalse(prev.is_last)
history.set_current(prev)
self.assertCurrentEquals(history, self.pages[0])
self.assertTrue(history.get_previous() is None)
self.assertTrue(prev.is_first)
self.assertHistoryEquals(history, self.pages)
# walk forward
for i in range(1, len(self.pages)):
next = history.get_next()
self.assertFalse(next is None)
self.assertEqual(next.name, self.pages[i].name)
self.assertFalse(next.is_first)
history.set_current(next)
self.assertCurrentEquals(history, self.pages[-1])
self.assertTrue(history.get_next() is None)
self.assertTrue(history.get_current().is_last)
self.assertHistoryEquals(history, self.pages)
# Add page multiple times
current = history.get_current()
path = Path(current.name)
for j in range(5):
history.append(path)
self.assertHistoryEquals(history, self.pages) # history does not store duplicates
self.assertEquals(history.get_current(), current)
# Test dropping forward stack
historylist = list(history.get_history())
path1 = historylist[10]
path2 = historylist[0]
history.set_current(path1)
self.assertEquals(history.get_current(), path1) # rewind
self.assertHistoryEquals(history, self.pages) # no change
history.append(path2) # new path - drop forward stack
i = len(pages) - 10
wanted = self.pages[:i] + [path2]
self.assertHistoryEquals(history, wanted)
# Test max entries
default_max_history = zim.history.MAX_HISTORY
zim.history.MAX_HISTORY = 3
for page in self.pages:
history.append(page)
zim.history.MAX_HISTORY = default_max_history
self.assertHistoryEquals(history, self.pages[-3:])
def testUnique(self):
'''Get recent pages from history'''
default_max_recent = zim.history.MAX_RECENT
zim.history.MAX_RECENT = len(self.pages) + 1
history = History(self.notebook)
for page in self.pages:
history.append(page)
self.assertHistoryEquals(history, self.pages)
unique = list(history.get_recent())
self.assertEqual(unique[0], history.get_current())
self.assertEqual(len(unique), len(self.pages))
for page in self.pages:
history.append(page)
self.assertHistoryEquals(history, 2 * self.pages)
unique = list(history.get_recent())
self.assertEqual(unique[0], history.get_current())
self.assertEqual(len(unique), len(self.pages))
unique = set([page.name for page in unique]) # collapse doubles
self.assertEqual(len(unique), len(self.pages))
zim.history.MAX_RECENT = 3
history = History(self.notebook)
for page in self.pages:
history.append(page)
zim.history.MAX_RECENT = default_max_recent
self.assertHistoryEquals(history, self.pages)
unique = list(history.get_recent())
self.assertEqual(unique[0], history.get_current())
self.assertEqual(len(unique), 3)
def testChildren(self):
'''Test getting namespace from history'''
history = History(self.notebook)
for name in ('Test:wiki', 'Test:foo:bar', 'Test:foo', 'TaskList:bar'):
page = self.notebook.get_page(Path(name))
history.append(page)
self.assertEqual(history.get_child(Path('Test')), Path('Test:foo'))
self.assertEqual(history.get_grandchild(Path('Test')), Path('Test:foo:bar'))
self.assertEqual(history.get_child(Path('NonExistent')), None)
self.assertEqual(history.get_grandchild(Path('NonExistent')), None)
history.append(self.notebook.get_page(Path('Test:wiki')))
self.assertEqual(history.get_child(Path('Test')), Path('Test:wiki'))
self.assertEqual(history.get_grandchild(Path('Test')), Path('Test:wiki'))
page = self.notebook.get_page(Path('Some:deep:nested:page'))
history.append(page)
self.assertEqual(history.get_child(Path('Some')), Path('Some:deep'))
self.assertEqual(history.get_grandchild(Path('Some')), Path('Some:deep:nested:page'))
def testMovePage(self):
'''Test history is updated for moved pages'''
history = History(self.notebook)
for page in self.pages:
history.append(page)
self.assertIn(Path('Test:wiki'), list(history.get_history()))
history._on_page_moved(self.notebook, Path('Test'), Path('New'), False)
self.assertNotIn(Path('Test:wiki'), list(history.get_history()))
self.assertIn(Path('New:wiki'), list(history.get_history()))
history._on_page_moved(self.notebook, Path('New'), Path('Test'), False)
self.assertNotIn(Path('New:wiki'), list(history.get_history()))
self.assertIn(Path('Test:wiki'), list(history.get_history()))
self.assertHistoryEquals(history, self.pages)
def testDeletedNotInUnique(self):
'''Test if deleted pages and their children show up in unique history list'''
zim.history.MAX_RECENT = len(self.pages) + 1
history = History(self.notebook)
for page in self.pages:
history.append(page)
for page in self.pages:
history.append(page)
self.assertHistoryEquals(history, 2 * self.pages)
uniques = list(history.get_recent())
self.assertEqual(len(uniques), len(self.pages))
page = history.get_current()
history._on_page_deleted(self.notebook, page)
uniques = list(history.get_recent())
self.assertTrue(len(uniques) < len(self.pages))
i = len(uniques)
history.set_current(page)
uniques = list(history.get_recent())
self.assertEqual(len(uniques), i + 1)
# Not same as len(self.pages) because of deleted children
for page in self.pages:
history._on_page_deleted(self.notebook, page)
uniques = list(history.get_recent())
self.assertEqual(len(uniques), 0)
self.assertEqual(
len(list(history.get_history())),
2 * len(self.pages) )
for page in history.get_history():
history.set_current(page)
uniques = list(history.get_recent())
self.assertEqual(len(uniques), len(self.pages))
def testSerialize(self):
'''Test parsing the history from the state file'''
uistate = INIConfigFile(VirtualFile([]))
history = History(self.notebook, uistate)
for page in self.pages:
history.append(page)
self.assertHistoryEquals(history, self.pages)
self.assertCurrentEquals(history, self.pages[-1])
# rewind 2
for i in range(2):
prev = history.get_previous()
history.set_current(prev)
# check state
#~ import pprint
#~ pprint.pprint(uistate)
self.assertHistoryEquals(history, uistate['History']['list'])
self.assertRecentEquals(history, uistate['History']['recent'])
self.assertEqual(uistate['History']['current'], len(self.pages) - 3)
# clone uistate by text
lines = uistate.dump()
newuistate = INIConfigFile(VirtualFile(lines))
newuistate['History'].setdefault('list', [])
newuistate['History'].setdefault('recent', [])
newuistate['History'].setdefault('current', 0)
# check new state
self.assertHistoryEquals(history, [Path(t[0]) for t in newuistate['History']['list']])
self.assertRecentEquals(history, [Path(t[0]) for t in newuistate['History']['recent']])
self.assertEqual(newuistate['History']['current'], len(self.pages) - 3)
# and compare resulting history object
newhistory = History(self.notebook, newuistate)
self.assertEqual(list(newhistory.get_history()), list(history.get_history()))
self.assertEqual(list(newhistory.get_recent()), list(history.get_recent()))
self.assertEqual(newhistory.get_current(), history.get_current())
# Check recent is initialized if needed
newuistate = INIConfigFile(VirtualFile(lines))
newuistate['History'].setdefault('recent', [])
newuistate['History'].pop('recent')
newhistory = History(self.notebook, newuistate)
self.assertEqual(list(newhistory.get_history()), list(history.get_history()))
self.assertEqual(list(newhistory.get_recent()), list(history.get_recent()))
self.assertEqual(newhistory.get_current(), history.get_current())
def testRobustness(self):
'''Test history can deal with garbage data'''
uistate = INIConfigFile(VirtualFile([]))
uistate['History'].input({
'list': 'FOOOO',
'recent': [["BARRRR", 0]],
'cursor': 'Not an integer',
})
with tests.LoggingFilter(
logger='zim.config',
message='Invalid config'
):
with tests.LoggingFilter(
logger='zim.history',
message='Could not parse'
):
history = History(self.notebook, uistate)
self.assertEqual(list(history.get_history()), [])
self.assertEqual(list(history.get_recent()), [])
self.assertIsNone(history.get_current())
zim-0.65/tests/datetimetz.py 0000664 0001750 0001750 00000003755 12374655231 016025 0 ustar jaap jaap 0000000 0000000 # -*- coding: utf-8 -*-
# Copyright 2014 Jaap Karssenberg
from __future__ import with_statement
import tests
import warnings
import zim.datetimetz as datetime
class TestDateTimeZ(tests.TestCase):
# FIXME would be better to test correctness of results
# but first check functions do not give errors
def setUp(self):
with warnings.catch_warnings():
warnings.simplefilter("ignore")
try:
import babel
except ImportError:
pass
def runTest(self):
# now()
dt = datetime.now()
s = dt.isoformat()
self.assertTrue(isinstance(s, basestring) and len(s) > 0)
s = dt.strftime("%z")
self.assertTrue(isinstance(s, basestring) and len(s) > 0)
s = dt.strftime("%Z")
self.assertTrue(isinstance(s, basestring) and len(s) > 0)
# strftime
s = datetime.strftime('%a', dt)
self.assertTrue(isinstance(s, basestring) and len(s) > 0)
s = datetime.strftime('%%', dt)
self.assertEqual(s, '%')
s = datetime.strftime('%u', dt)
self.assertTrue(isinstance(s, basestring) and len(s) > 0)
s = datetime.strftime('%V', dt)
self.assertTrue(isinstance(s, basestring) and len(s) > 0)
# strfcal
s = datetime.strfcal('%w', dt)
self.assertTrue(isinstance(s, basestring) and len(s) > 0)
s = datetime.strfcal('%W', dt)
self.assertTrue(isinstance(s, basestring) and len(s) > 0)
s = datetime.strfcal('%Y', dt)
self.assertTrue(isinstance(s, basestring) and len(s) > 0)
s = datetime.strfcal('%%', dt)
self.assertEqual(s, '%')
# weekcalendar
year, week, weekday = datetime.weekcalendar(dt)
self.assertTrue(isinstance(year, int) and 1900 < year and 3000 > year)
self.assertTrue(isinstance(week, int) and 1 <= week and 53 >= week)
self.assertTrue(isinstance(weekday, int) and 1 <= weekday and 7 >= weekday)
# dates_for_week
start, end = datetime.dates_for_week(year, week)
self.assertTrue(isinstance(start, datetime.date))
self.assertTrue(isinstance(end, datetime.date))
self.assertTrue(start <= dt.date() and end >= dt.date())
zim-0.65/tests/printtobrowser.py 0000664 0001750 0001750 00000001132 12411243417 016730 0 ustar jaap jaap 0000000 0000000 # -*- coding: utf-8 -*-
# Copyright 2008 Jaap Karssenberg
import tests
from zim.plugins import PluginManager
from zim.notebook import Path
@tests.slowTest
class TestPrintToBrowser(tests.TestCase):
def runTest(self):
'Test PrintToBrowser plugin'
pluginklass = PluginManager.get_plugin_class('printtobrowser')
plugin = pluginklass()
notebook = tests.new_notebook()
page = notebook.get_page(Path('Test:foo'))
file = plugin.print_to_file(notebook, page)
self.assertTrue(file.exists())
content = file.read()
self.assertTrue('Foo
' in content)
zim-0.65/tests/versioncontrol.py 0000664 0001750 0001750 00000045317 12614412356 016735 0 ustar jaap jaap 0000000 0000000 # -*- coding: utf-8 -*-
# Copyright 2009 Jaap Karssenberg
from __future__ import with_statement
import tests
from tests.gui import setupGtkInterface
import os
import tempfile
import gtk
from zim.fs import File, Dir
from zim.applications import Application
from zim.notebook import Path
from zim.plugins.versioncontrol import *
import zim.plugins.versioncontrol.bzr
import zim.plugins.versioncontrol.hg
import zim.plugins.versioncontrol.git
import zim.plugins.versioncontrol.fossil
# We define our own tmp dir here instead of using tests.create_tmp_dir
# because sources are probably under change control already - want to
# avoid mixing up the files
def get_tmp_dir(name):
if 'REAL_TMP' in os.environ: # Set in tests/__init__.py
dir = Dir(os.environ['REAL_TMP'])
else:
dir = Dir(tempfile.gettempdir())
#~ print "TMPDIR:", dir
dir = dir.subdir('test_versioncontrol').subdir(name)
if dir.exists():
dir.remove_children()
dir.remove()
assert not dir.exists()
return dir
WIKITEXT = File('tests/data/formats/wiki.txt').read() # Contains some unicode
UTF8_COMMENT = u'Commit \u03b1\u03b2\u03b3'
@tests.slowTest
class TestVCS(tests.TestCase):
def testDetectVCS(self):
root = Dir(self.create_tmp_dir())
root.subdir('.bzr').touch()
self.assertEqual(VCS._detect_in_folder(root), ('bzr', root))
subdir = root.subdir('Foo/Bar')
subdir.touch()
self.assertEqual(VCS._detect_in_folder(subdir), ('bzr', root))
subroot = root.subdir('subroot')
subroot.subdir('.git').touch()
self.assertEqual(VCS._detect_in_folder(subroot), ('git', subroot))
subdir = subroot.subdir('Foo/Bar')
subdir.touch()
self.assertEqual(VCS._detect_in_folder(subdir), ('git', subroot))
subroot = root.subdir('subfold')
subroot.file('.fslckout').touch()
self.assertEqual(VCS._detect_in_folder(subroot), ('fossil', subroot))
subdir = subroot.subdir('Foo/Bar')
subdir.touch()
self.assertEqual(VCS._detect_in_folder(subdir), ('fossil', subroot))
@tests.slowTest
@tests.skipUnless(
any(
map(VCS.check_dependencies, (VCS.BZR, VCS.GIT, VCS.HG))
), 'Missing dependencies')
class TestMainWindowExtension(tests.TestCase):
def runTest(self):
plugin = VersionControlPlugin()
dir = get_tmp_dir('versioncontrol_TestMainWindowExtension')
notebook = tests.new_files_notebook(dir)
ui = setupGtkInterface(self, notebook=notebook)
plugin.extend(notebook)
plugin.extend(ui.mainwindow)
notebook_ext = plugin.get_extension(NotebookExtension)
self.assertIsInstance(notebook_ext, NotebookExtension)
window_ext = plugin.get_extension(MainWindowExtension)
self.assertIsInstance(window_ext, MainWindowExtension)
## init & save version
self.assertIsNone(notebook_ext.vcs)
def init(dialog):
self.assertIsInstance(dialog, VersionControlInitDialog)
choice = dialog.combobox.get_active_text()
self.assertTrue(choice and not choice.isspace())
dialog.emit('response', gtk.RESPONSE_YES)
with tests.DialogContext(init, SaveVersionDialog):
window_ext.save_version()
self.assertIsNotNone(notebook_ext.vcs)
window_ext._autosave_thread.join()
self.assertFalse(notebook_ext.vcs.modified)
## save version again
page = notebook.get_page(Path('Foo'))
page.parse('wiki', 'foo!')
notebook.store_page(page)
self.assertTrue(notebook_ext.vcs.modified)
with tests.DialogContext(SaveVersionDialog):
window_ext.save_version()
window_ext._autosave_thread.join()
self.assertFalse(notebook_ext.vcs.modified)
## show versions
with tests.DialogContext(VersionsDialog):
window_ext.show_versions()
## auto-save
plugin.preferences['autosave'] = True
page = notebook.get_page(Path('Fooooo'))
page.parse('wiki', 'foo!')
notebook.store_page(page)
self.assertTrue(notebook_ext.vcs.modified)
ui.emit('quit')
self.assertFalse(notebook_ext.vcs.modified)
@tests.slowTest
class TestVersionsDialog(tests.TestCase):
def testSideBySide(self):
app = get_side_by_side_app()
if Application('meld').tryexec():
self.assertIsNotNone(app)
if app is None:
print '\nCould not find an application for side-by-side comparison'
else:
self.assertTrue(app.tryexec)
def testDialog(self):
pass # TODO test other dialog functions
class VersionControlBackendTests(object):
def setUp(self):
zim.plugins.versioncontrol.TEST_MODE = False
def tearDown(self):
zim.plugins.versioncontrol.TEST_MODE = True
# TODO - unify test cases with single interface test
#####################################################
#
# BAZAAR BACKEND TEST
#
#####################################################
@tests.slowTest
@tests.skipUnless(VCS.check_dependencies(VCS.BZR), 'Missing dependencies')
class TestBazaar(VersionControlBackendTests, tests.TestCase):
def runTest(self):
'''Test Bazaar version control'''
root = get_tmp_dir('versioncontrol_TestBazaar')
vcs = VCS.create(VCS.BZR, root, root)
vcs.init()
#~ for notebookdir in (root, root.subdir('foobar')):
#~ detected = VersionControlPlugin._detect_vcs(notebookdir)
#~ self.assertEqual(detected.__class__, BazaarVCS)
#~ del detected # don't keep multiple instances around
subdir = root.subdir('foo/bar')
file = subdir.file('baz.txt')
file.write('foo\nbar\n')
self.assertEqual(''.join(vcs.get_status()), '''\
added:
.bzrignore
foo/
foo/bar/
foo/bar/baz.txt
''' )
vcs.commit('test 1')
self.assertRaises(NoChangesError, vcs.commit, 'test 1')
ignorelines = lambda line: not (line.startswith('+++') or line.startswith('---'))
# these lines contain time stamps
diff = vcs.get_diff(versions=(0, 1))
diff = ''.join(filter(ignorelines, diff))
self.assertEqual(diff, '''\
=== added file '.bzrignore'
@@ -0,0 +1,1 @@
+**/.zim
=== added directory 'foo'
=== added directory 'foo/bar'
=== added file 'foo/bar/baz.txt'
@@ -0,0 +1,2 @@
+foo
+bar
''' )
file.write('foo\nbaz\n')
diff = vcs.get_diff()
diff = ''.join(filter(ignorelines, diff))
self.assertEqual(diff, '''\
=== modified file 'foo/bar/baz.txt'
@@ -1,2 +1,2 @@
foo
-bar
+baz
''' )
vcs.revert()
self.assertEqual(vcs.get_diff(), ['=== No Changes\n'])
file.write('foo\nbaz\n')
vcs.commit('test 2')
diff = vcs.get_diff(versions=(1, 2))
diff = ''.join(filter(ignorelines, diff))
self.assertEqual(diff, '''\
=== modified file 'foo/bar/baz.txt'
@@ -1,2 +1,2 @@
foo
-bar
+baz
''' )
versions = vcs.list_versions()
#~ print 'VERSIONS>>', versions
self.assertTrue(len(versions) == 2)
self.assertTrue(len(versions[0]) == 4)
self.assertEqual(versions[0][0], '1')
self.assertEqual(versions[0][3], u'test 1\n')
self.assertTrue(len(versions[1]) == 4)
self.assertEqual(versions[1][0], '2')
self.assertEqual(versions[1][3], u'test 2\n')
lines = vcs.get_version(file, version=1)
self.assertEqual(''.join(lines), '''\
foo
bar
''' )
annotated = vcs.get_annotated(file)
lines = []
for line in annotated:
# get rid of user name
ann, text = line.split('|')
lines.append(ann[0]+' |'+text)
self.assertEqual(''.join(lines), '''\
1 | foo
2 | baz
''' )
#~ print 'TODO - test moving a file'
file.rename(root.file('bar.txt'))
diff = vcs.get_diff()
diff = ''.join(filter(ignorelines, diff))
self.assertEqual(diff, '''\
=== renamed file 'foo/bar/baz.txt' => 'bar.txt'
''' )
# Test unicode support
file.write(WIKITEXT)
diff = vcs.get_diff()
diff = ''.join(diff)
self.assertIsInstance(diff, unicode)
vcs.commit(UTF8_COMMENT)
versions = vcs.list_versions()
self.assertTrue(UTF8_COMMENT in versions[-1][-1])
self.assertIsInstance(versions[-1][-1], unicode)
### Test delete ###
file.remove()
file.dir.cleanup()
diff = vcs.get_diff()
vcs.commit('deleted file')
#####################################################
#
# GIT BACKEND TEST
#
#####################################################
@tests.slowTest
@tests.skipUnless(VCS.check_dependencies(VCS.GIT), 'Missing dependencies')
class TestGit(VersionControlBackendTests, tests.TestCase):
def runTest(self):
'''Test Git version control'''
root = get_tmp_dir('versioncontrol_TestGit')
vcs = VCS.create(VCS.GIT, root, root)
vcs.init()
#~ for notebookdir in (root, root.subdir('foobar')):
#~ detected = VersionControlPlugin._detect_vcs(notebookdir)
#~ self.assertEqual(detected.__class__, BazaarVCS)
#~ del detected # don't keep multiple instances around
subdir = root.subdir('foo/bar')
file = subdir.file('baz.txt')
file.write('foo\nbar\n')
self.assertEqual(''.join(vcs.get_status(porcelain=True)),
'A .gitignore\n'
'A foo/bar/baz.txt\n'
)
vcs.update_staging()
vcs.commit('test 1')
#[master 0f4132e] test 1
# 1 files changed, 3 insertions(+), 0 deletions(-)
# create mode 100644 foo/bar/baz.txt
# git plugin doesnt support this atm
#self.assertRaises(NoChangesError, vcs.commit, 'test 1')
file = subdir.file('bar.txt')
file.write('second\ntest\n')
self.assertEqual(''.join(vcs.get_status(porcelain=True)),
'A foo/bar/bar.txt\n'
)
vcs.update_staging()
vcs.commit('test 2')
#[master dbebdf1] test 2
# 0 files changed, 0 insertions(+), 0 deletions(-)
# create mode 100644 foo/bar/bar.txt
# git plugin doesnt support this atm
#self.assertRaises(NoChangesError, vcs.commit, 'test 2')
# these lines contain file perms & hashes
ignorelines = lambda line: not (line.startswith('new') or line.startswith('index'))
diff = vcs.get_diff(versions=('HEAD'))
# john@joran:~/code/zim/TEST$ git diff master^
# diff --git a/foo/bar/bar.txt b/foo/bar/bar.txt
# new file mode 100644
# index 0000000..e69de29
diff = ''.join(filter(ignorelines, diff))
self.assertEqual(diff, '''\
diff --git a/foo/bar/bar.txt b/foo/bar/bar.txt
--- /dev/null
+++ b/foo/bar/bar.txt
@@ -0,0 +1,2 @@
+second
+test
''' )
file.write('second\nbaz\n')
diff = vcs.get_diff()
diff = ''.join(filter(ignorelines, diff))
self.assertEqual(diff, '''\
diff --git a/foo/bar/bar.txt b/foo/bar/bar.txt
--- a/foo/bar/bar.txt
+++ b/foo/bar/bar.txt
@@ -1,2 +1,2 @@
second
-test
+baz
''' )
vcs.revert()
self.assertEqual(vcs.get_status(porcelain=True), [])
file.write('second\nbaz\n')
vcs.commit('test 3')
diff = vcs.get_diff(versions=('HEAD', 'HEAD^'))
diff = ''.join(filter(ignorelines, diff))
self.assertEqual(diff, '''\
diff --git a/foo/bar/bar.txt b/foo/bar/bar.txt
--- a/foo/bar/bar.txt
+++ b/foo/bar/bar.txt
@@ -1,2 +1,2 @@
second
-test
+baz
''' )
versions = vcs.list_versions()
self.assertTrue(isinstance(versions,list))
#~ print 'VERSIONS>>', versions
self.assertTrue(len(versions) == 3)
self.assertTrue(isinstance(versions[0],tuple))
self.assertTrue(len(versions[0]) == 4)
self.assertTrue(isinstance(versions[0][0],basestring))
self.assertTrue(isinstance(versions[0][1],basestring))
self.assertTrue(isinstance(versions[0][2],basestring))
self.assertTrue(isinstance(versions[0][3],basestring))
self.assertEqual(versions[0][3], u'test 1\n')
self.assertTrue(len(versions[1]) == 4)
self.assertEqual(versions[1][3], u'test 2\n')
self.assertTrue(len(versions[2]) == 4)
self.assertEqual(versions[2][3], u'test 3\n')
# slightly different, we check the 2nd file
lines = vcs.get_version(file, version='HEAD^')
self.assertEqual(''.join(lines), '''\
second
test
''' )
#john@joran:/tmp/test_versioncontrol/versioncontrol_TestGit$ git annotate -t foo/bar/bar.txt
#09be0483 (John Drinkwater 1309533637 +0100 1)second
#526fb2b5 (John Drinkwater 1309533637 +0100 2)baz
#john@joran:/tmp/test_versioncontrol/versioncontrol_TestGit$ git blame -s foo/bar/bar.txt
#09be0483 1) second
#526fb2b5 2) baz
annotated = vcs.get_annotated(file)
lines = []
for line in annotated:
# get rid of commit hash, its unique
commit, num, text = line.split(' ')
lines.append(num+' '+text)
self.assertEqual(''.join(lines), '''\
1) second
2) baz
''' )
# Test unicode support
file.write(WIKITEXT)
diff = vcs.get_diff()
diff = ''.join(diff)
self.assertIsInstance(diff, unicode)
vcs.commit(UTF8_COMMENT)
versions = vcs.list_versions()
self.assertIn(UTF8_COMMENT, versions[-1][-1])
self.assertIsInstance(versions[-1][-1], unicode)
### Test delete ###
file.remove()
file.dir.cleanup()
diff = vcs.get_diff()
vcs.commit('deleted file')
# XXX ignore renames and deletions?
# Below is a test that we dont need to handle, as we can be quite ignorant of them. Especially considering
# how git tracks file moves, ie, it doesnt.
# file.rename(root.file('bar.txt'))
# diff = vcs.get_diff()
#john@joran:~/code/zim/TEST$ git diff
#diff --git a/foo/bar/bar.txt b/foo/bar/bar.txt
#deleted file mode 100644
#…
#john@joran:~/code/zim/TEST$ git commit -a -m "Moved test 4"
#[master b099d98] Moved test 4
# 1 files changed, 0 insertions(+), 0 deletions(-)
# rename foo/bar/{bar.txt => boo.txt} (100%)
#####################################################
#
# MERCURIAL BACKEND TEST
#
#####################################################
@tests.slowTest
@tests.skipUnless(VCS.check_dependencies(VCS.HG), 'Missing dependencies')
class TestMercurial(VersionControlBackendTests, tests.TestCase):
def runTest(self):
'''Test Mercurial version control'''
root = get_tmp_dir('versioncontrol_TestMercurial')
vcs = VCS.create(VCS.HG, root, root)
vcs.init()
#~ for notebookdir in (root, root.subdir('foobar')):
#~ detected = VersionControlPlugin._detect_vcs(notebookdir)
#~ self.assertEqual(detected.__class__, BazaarVCS)
#~ del detected # don't keep multiple instances around
subdir = root.subdir('foo/bar')
file = subdir.file('baz.txt')
file.write('foo\nbar\n')
self.assertEqual(''.join(vcs.get_status()), '''\
A .hgignore
A foo/bar/baz.txt
''' )
vcs.commit('test 1')
self.assertRaises(NoChangesError, vcs.commit, 'test 1')
ignorelines = lambda line: not (line.startswith('+++') or line.startswith('---'))
# these lines contain time stamps
file.write('foo\nbaz\n')
diff = vcs.get_diff()
diff = ''.join(filter(ignorelines, diff))
self.assertEqual(diff, '''\
diff --git a/foo/bar/baz.txt b/foo/bar/baz.txt
@@ -1,2 +1,2 @@
foo
-bar
+baz
''' )
vcs.revert()
self.assertEqual(vcs.get_diff(), ['=== No Changes\n'])
file.write('foo\nbaz\n')
vcs.commit('test 2')
diff = vcs.get_diff(versions=(0, 1))
diff = ''.join(filter(ignorelines, diff))
self.assertEqual(diff, '''\
diff --git a/foo/bar/baz.txt b/foo/bar/baz.txt
@@ -1,2 +1,2 @@
foo
-bar
+baz
''' )
versions = vcs.list_versions()
#~ print 'VERSIONS>>', versions
self.assertTrue(len(versions) == 2)
self.assertTrue(len(versions[0]) == 4)
self.assertEqual(versions[0][0], str(0))
self.assertEqual(versions[0][3], u'test 1')
self.assertTrue(len(versions[1]) == 4)
self.assertEqual(versions[1][0], str(1))
self.assertEqual(versions[1][3], u'test 2')
lines = vcs.get_version(file, version=0)
self.assertEqual(''.join(lines), '''\
foo
bar
''' )
annotated = vcs.get_annotated(file)
lines = []
for line in annotated:
# get rid of user name
ann, text = line.split(':')
lines.append(ann[0]+':'+text)
self.assertEqual(''.join(lines), '''\
0: foo
1: baz
''' )
#~ print 'TODO - test moving a file'
file.rename(root.file('bar.txt'))
diff = vcs.get_diff()
diff = ''.join(filter(ignorelines, diff))
self.assertEqual(diff, '''\
diff --git a/foo/bar/baz.txt b/bar.txt
rename from foo/bar/baz.txt
rename to bar.txt
''' )
# Test deleting file
root.file('bar.txt').remove()
vcs.commit('test deleting')
# Test unicode support
file.write(WIKITEXT)
diff = vcs.get_diff()
diff = ''.join(diff)
self.assertIsInstance(diff, unicode)
vcs.commit(UTF8_COMMENT)
versions = vcs.list_versions()
self.assertTrue(UTF8_COMMENT in versions[-1][-1])
self.assertIsInstance(versions[-1][-1], unicode)
### Test delete ###
file.remove()
file.dir.cleanup()
diff = vcs.get_diff()
vcs.commit('deleted file')
#####################################################
#
# FOSSIL BACKEND TEST
#
#####################################################
@tests.slowTest
@tests.skipUnless(VCS.check_dependencies(VCS.FOSSIL), 'Missing dependencies')
class TestFossil(VersionControlBackendTests, tests.TestCase):
def runTest(self):
'''Test Fossil version control'''
root = get_tmp_dir('versioncontrol_TestFossil')
vcs = VCS.create(VCS.FOSSIL, root, root)
vcs.init()
subdir = root.subdir('foo/bar')
file = subdir.file('baz.txt')
file.write('foo\nbar\n')
vcs.on_path_created(None,file)
self.assertEqual(''.join(vcs.get_status()),
'ADDED foo/bar/baz.txt\n'
)
vcs.update_staging()
vcs.commit('test 1')
file = subdir.file('bar.txt')
file.write('second\ntest\n')
vcs.on_path_created(None,file)
self.assertEqual(''.join(vcs.get_status()),
'ADDED foo/bar/bar.txt\n'
)
vcs.update_staging()
vcs.commit('test 2')
versions = vcs.list_versions()
self.assertTrue(isinstance(versions,list))
#~ print 'VERSIONS>>', versions
self.assertTrue(len(versions) == 3)
self.assertTrue(isinstance(versions[0],tuple))
self.assertTrue(len(versions[0]) == 4)
self.assertTrue(isinstance(versions[0][0],basestring))
self.assertTrue(isinstance(versions[0][1],basestring))
self.assertTrue(isinstance(versions[0][2],basestring))
self.assertTrue(isinstance(versions[0][3],basestring))
self.assertEqual(versions[0][3], u'test 2 ')
self.assertTrue(len(versions[1]) == 4)
self.assertEqual(versions[1][3], u'test 1 ')
# slightly different, we check the 2nd file
lines = vcs.get_version(file, version=versions[0][0])
self.assertEqual(''.join(lines), '''\
second
test
''' )
diff = vcs.get_diff(versions=(versions[2][0], versions[0][0]))
diff = ''.join(diff)
self.assertEqual(diff, '''\
ADDED foo/bar/bar.txt
ADDED foo/bar/baz.txt
''' )
file.write('second\nbaz\n')
diff = vcs.get_diff()
diff = ''.join(diff)
self.assertEqual(diff, '''\
Index: foo/bar/bar.txt
==================================================================
--- foo/bar/bar.txt
+++ foo/bar/bar.txt
@@ -1,2 +1,2 @@
second
-test
+baz
''' )
vcs.revert()
self.assertEqual(vcs.get_status(), [])
file.write('second\nbaz\n')
vcs.commit('test 3')
versions = vcs.list_versions()
diff = vcs.get_diff(versions=(versions[1][0], versions[0][0]))
diff = ''.join(diff)
self.assertEqual(diff, '''\
Index: foo/bar/bar.txt
==================================================================
--- foo/bar/bar.txt
+++ foo/bar/bar.txt
@@ -1,2 +1,2 @@
second
-test
+baz
''' )
annotated = vcs.get_annotated(file)
lines = []
for line in annotated:
# get rid of commit hash, its unique
commit, date, num, text = line.split(None, 4)
lines.append(num+' '+text)
self.assertEqual('\n'.join(lines), '''\
1: second
2: baz''' )
# Test unicode support
file.write(WIKITEXT)
diff = vcs.get_diff()
diff = ''.join(diff)
self.assertIsInstance(diff, unicode)
vcs.commit(UTF8_COMMENT)
versions = vcs.list_versions()
self.assertIn(UTF8_COMMENT, versions[0][-1])
self.assertIsInstance(versions[0][-1], unicode)
### Test delete ###
file.remove()
file.dir.cleanup()
diff = vcs.get_diff()
vcs.commit('deleted file')
zim-0.65/tests/__init__.py 0000664 0001750 0001750 00000037437 12614726741 015420 0 ustar jaap jaap 0000000 0000000 # -*- coding: utf-8 -*-
# Copyright 2008-2013 Jaap Karssenberg
'''Zim test suite'''
import os
import sys
import tempfile
import shutil
import logging
import gettext
import xml.etree.cElementTree as etree
import types
import glob
try:
import gtk
except ImportError:
gtk = None
if sys.version_info < (2, 7, 0):
try:
import unittest2 as unittest
from unittest2 import skip, skipIf, skipUnless
except ImportError:
print >>sys.stderr, '''\
For python versions < 2.7 the 'unittest2' module is needed to run
the test suite. On Ubuntu or Debian install package 'python-unittest2'.
'''
sys.exit(1)
else:
import unittest
from unittest import skip, skipIf, skipUnless
__unittest = 1 # needed to get stack trace OK for class TestCase
gettext.install('zim', unicode=True, names=('_', 'gettext', 'ngettext'))
FAST_TEST = False #: determines whether we skip slow tests or not
# This list also determines the order in which tests will executed
__all__ = [
'package', 'translations',
'datetimetz', 'utils', 'errors', 'signals', 'actions',
'environ', 'fs',
'config', 'applications',
'parsing', 'formats', 'templates', 'objectmanager',
'stores', 'index', 'notebook', 'history',
'export', 'www', 'search',
'widgets', 'gui', 'pageview', 'clipboard',
'main', 'plugins',
'calendar', 'printtobrowser', 'versioncontrol', 'inlinecalculator',
'tasklist', 'tags', 'imagegenerators', 'tableofcontents',
'quicknote', 'attachmentbrowser', 'insertsymbol',
'sourceview', 'tableeditor', 'bookmarksbar', 'spell',
'ipc'
]
mydir = os.path.dirname(__file__)
# when a test is missing from the list that should be detected
for file in glob.glob(os.path.dirname(__file__) + '/*.py'):
name = os.path.basename(file)[:-3]
if name != '__init__' and not name in __all__:
raise AssertionError, 'Test missing in __all__: %s' % name
# get our own data dir
DATADIR = os.path.abspath(os.path.join(mydir, 'data'))
# get our own tmpdir
TMPDIR = os.path.abspath(os.path.join(mydir, 'tmp'))
# Wanted to use tempfile.get_tempdir here to put everything in
# e.g. /tmp/zim but since /tmp is often mounted as special file
# system this conflicts with thrash support. For writing in source
# dir we have conflict with bazaar controls, this is worked around
# by a config mode switch in the bazaar backend of the version
# control plugin
if os.name == 'nt':
TMPDIR = unicode(TMPDIR)
else:
TMPDIR = TMPDIR.encode(sys.getfilesystemencoding())
# also get the default tmpdir and put a copy in the env
REAL_TMPDIR = tempfile.gettempdir()
def load_tests(loader, tests, pattern):
'''Load all test cases and return a unittest.TestSuite object.
The parameters 'tests' and 'pattern' are ignored.
'''
suite = unittest.TestSuite()
for name in ['tests.'+name for name in __all__ ]:
test = loader.loadTestsFromName(name)
suite.addTest(test)
return suite
def _setUpEnvironment():
'''Method to be run once before test suite starts'''
# In fact to be loaded before loading some of the zim modules
# like zim.config and any that export constants from it
system_data_dirs = os.environ.get('XDG_DATA_DIRS')
os.environ.update({
'ZIM_TEST_RUNNING': 'True',
'ZIM_TEST_ROOT': os.getcwd(),
'TMP': TMPDIR,
'REAL_TMP': REAL_TMPDIR,
'XDG_DATA_HOME': os.path.join(TMPDIR, 'data_home'),
'XDG_DATA_DIRS': os.path.join(TMPDIR, 'data_dir'),
'XDG_CONFIG_HOME': os.path.join(TMPDIR, 'config_home'),
'XDG_CONFIG_DIRS': os.path.join(TMPDIR, 'config_dir'),
'XDG_CACHE_HOME': os.path.join(TMPDIR, 'cache_home')
})
if os.path.isdir(TMPDIR):
shutil.rmtree(TMPDIR)
os.makedirs(TMPDIR)
hicolor = os.environ['XDG_DATA_DIRS'] + '/icons/hicolor'
os.makedirs(hicolor)
if system_data_dirs:
# Need these since gtk pixbuf loaders are in /usr/share in
# some setups, and this parameter is used to find them
os.environ['XDG_DATA_DIRS'] = os.pathsep.join(
(os.environ['XDG_DATA_DIRS'], system_data_dirs) )
if os.environ.get('ZIM_TEST_RUNNING') != 'True':
# Do this when loaded, but not re-do in sub processes
# (doing so will kill e.g. the ipc test...)
_setUpEnvironment()
_zim_pyfiles = []
def zim_pyfiles():
'''Returns a list with file paths for all the zim python files'''
if not _zim_pyfiles:
for d, dirs, files in os.walk('zim'):
_zim_pyfiles.extend([d+'/'+f for f in files if f.endswith('.py')])
_zim_pyfiles.sort()
for file in _zim_pyfiles:
yield file # shallow copy
def slowTest(obj):
'''Decorator for slow tests
Tests wrapped with this decorator are ignored when you run
C{test.py --fast}. You can either wrap whole test classes::
@tests.slowTest
class MyTest(tests.TestCase):
...
or individual test functions::
class MyTest(tests.TestCase):
@tests.slowTest
def testFoo(self):
...
def testBar(self):
...
'''
if FAST_TEST:
wrapper = skip('Slow test')
return wrapper(obj)
else:
return obj
class TestCase(unittest.TestCase):
'''Base class for test cases'''
maxDiff = None
@classmethod
def tearDownClass(cls):
if gtk is not None:
gtk_process_events() # flush any pending events / warnings
def assertEqual(self, first, second, msg=None):
## HACK to work around "feature" in unittest - it does not consider
## string and unicode to be of the same type and thus does not
## show diffs if the textual content differs
if type(first) in (str, unicode) \
and type(second) in (str, unicode):
self.assertMultiLineEqual(first, second, msg)
else:
unittest.TestCase.assertEqual(self, first, second, msg)
def create_tmp_dir(self, name=None):
'''Returns a path to a tmp dir where tests can write data.
The dir is removed and recreated empty every time this function
is called with the same name from the same class.
'''
self.clear_tmp_dir(name)
path = self._get_tmp_name(name)
os.makedirs(path)
assert os.path.exists(path) # make real sure
return path
def get_tmp_name(self, name=None):
'''Returns the same path as L{create_tmp_dir()} but without
touching it. This method will raise an exception when a file
or dir exists of the same name.
'''
path = self._get_tmp_name(name)
assert not os.path.exists(path), 'This path should not exist: %s' % path
return path
def clear_tmp_dir(self, name=None):
'''Clears the tmp dir for this test'''
path = self._get_tmp_name(name)
if os.path.exists(path):
shutil.rmtree(path)
assert not os.path.exists(path) # make real sure
def _get_tmp_name(self, name):
if name:
assert not os.path.sep in name, 'Don\'t use this method to get sub folders or files'
name = self.__class__.__name__ + '_' + name
else:
name = self.__class__.__name__
if os.name == 'nt':
name = unicode(name)
else:
name = name.encode(sys.getfilesystemencoding())
return os.path.join(TMPDIR, name)
class LoggingFilter(object):
'''Base class for logging filters that can be used as a context
using the "with" keyword. To subclass it you only need to set the
logger to be used and (the begin of) the message to filter.
The message can be a string, or a list or tuple of strings. Any
messages that start with this string or any of these strings are
surpressed.
Alternatively you can call L{wrap_test()} from test C{setUp}.
This will start the filter and make sure it is cleaned up again.
'''
logger = 'zim'
message = None
def __init__(self, logger=None, message=None):
if logger:
self.logger = logger
if message:
self.message = message
self.loggerobj = logging.getLogger(self.logger)
def __enter__(self):
self.loggerobj.addFilter(self)
def __exit__(self, *a):
self.loggerobj.removeFilter(self)
def filter(self, record):
msg = record.getMessage()
if self.message is None:
return False
elif isinstance(self.message, tuple):
return not any(msg.startswith(m) for m in self.message)
else:
return not msg.startswith(self.message)
def wrap_test(self, test):
self.__enter__()
test.addCleanup(self.__exit__)
class DialogContext(object):
'''Context manager to catch dialogs being opened
Inteded to be used like this::
def myCustomTest(dialog):
self.assertTrue(isinstance(dialog, CustomDialogClass))
# ...
dialog.assert_response_ok()
with DialogContext(
myCustomTest,
SomeOtherDialogClass
):
gui.show_dialogs()
In this example the first dialog that is run by C{gui.show_dialogs()}
is checked by the function C{myCustomTest()} while the second dialog
just needs to be of class C{SomeOtherDialogClass} and will then
be closed with C{assert_response_ok()} by the context manager.
This context only works for dialogs derived from zim's Dialog class
as it uses a special hook in L{zim.gui.widgets}.
'''
def __init__(self, *definitions):
'''Constructor
@param definitions: list of either classes or methods
'''
self.stack = list(definitions)
self.old_test_mode = None
def __enter__(self):
import zim.gui.widgets
self.old_test_mode = zim.gui.widgets.TEST_MODE
self.old_callback = zim.gui.widgets.TEST_MODE_RUN_CB
zim.gui.widgets.TEST_MODE = True
zim.gui.widgets.TEST_MODE_RUN_CB = self._callback
def _callback(self, dialog):
#~ print '>>>', dialog
if not self.stack:
raise AssertionError, 'Unexpected dialog run: %s' % dialog
handler = self.stack.pop(0)
if isinstance(handler, (type, types.ClassType)): # is a class
if not isinstance(dialog, handler):
raise AssertionError, 'Expected dialog of class %s, but got %s instead' % (handler, dialog.__class__)
dialog.assert_response_ok()
else: # assume a function
handler(dialog)
def __exit__(self, *error):
#~ print 'ERROR', error
import zim.gui.widgets
zim.gui.widgets.TEST_MODE = self.old_test_mode
zim.gui.widgets.TEST_MODE_RUN_CB = self.old_callback
has_error = bool([e for e in error if e is not None])
if self.stack and not has_error:
raise AssertionError, '%i expected dialog(s) not run' % len(self.stack)
return False # Raise any errors again outside context
class TestData(object):
'''Wrapper for a set of test data in tests/data'''
def __init__(self, format):
assert format == 'wiki', 'TODO: add other formats'
root = os.environ['ZIM_TEST_ROOT']
tree = etree.ElementTree(file=root+'/tests/data/notebook-wiki.xml')
test_data = []
for node in tree.getiterator(tag='page'):
name = node.attrib['name']
text = unicode(node.text.lstrip('\n'))
test_data.append((name, text))
self._test_data = tuple(test_data)
def __iter__(self):
'''Yield the test data as 2 tuple (pagename, text)'''
for name, text in self._test_data:
yield name, text # shallow copy
def get(self, pagename):
'''Return text for a specific pagename'''
for n, text in self._test_data:
if n == pagename:
return text
assert False, 'Could not find data for page: %s' % pagename
WikiTestData = TestData('wiki') #: singleton to be used by various tests
def _expand_manifest(names):
'''Build a set of all pages names and all namespaces that need to
exist to host those page names.
'''
manifest = set()
for name in names:
manifest.add(name)
while name.rfind(':') > 0:
i = name.rfind(':')
name = name[:i]
manifest.add(name)
return manifest
def new_parsetree():
'''Returns a new ParseTree object for testing
Uses data from L{WikiTestData}, page C{roundtrip}
'''
import zim.formats.wiki
parser = zim.formats.wiki.Parser()
text = WikiTestData.get('roundtrip')
tree = parser.parse(text)
return tree
def new_parsetree_from_text(text, format='wiki'):
import zim.formats
parser = zim.formats.get_format(format).Parser()
return parser.parse(text)
def new_parsetree_from_xml(xml):
# For some reason this does not work with cElementTree.XMLBuilder ...
from xml.etree.ElementTree import XMLTreeBuilder
from zim.formats import ParseTree
builder = XMLTreeBuilder()
builder.feed(xml)
root = builder.close()
return ParseTree(root)
def new_page():
from zim.notebook import Path, Page
page = Page(Path('roundtrip'))
page.readonly = False
page.set_parsetree(new_parsetree())
return page
def new_page_from_text(text, format='wiki'):
from zim.notebook import Path, Page
page = Page(Path('Test'))
page.readonly = False
page.set_parsetree(new_parsetree_from_text(text, format))
return page
def new_notebook(fakedir=None):
'''Returns a new Notebook object with all data in memory
Uses data from L{WikiTestData}
@param fakedir: optional parameter to set the 'dir' attribute for
the notebook and the main store which allows you to resolve file
paths etc. It will not automatically touch the dir
(hence it being 'fake').
'''
from zim.fs import Dir
from zim.notebook import Notebook, Path
from zim.index import Index
notebook = Notebook(index=Index(dbfile=':memory:'))
store = notebook.add_store(Path(':'), 'memory')
manifest = []
for name, text in WikiTestData:
manifest.append(name)
store.set_node(Path(name), text)
notebook.testdata_manifest = _expand_manifest(manifest)
notebook.index.update()
if fakedir:
dir = Dir(fakedir)
notebook.dir = dir
store.dir = dir
return notebook
def new_files_notebook(dir):
'''Returns a new Notebook object with a file store
Uses data from L{WikiTestData}
@param path: a folder path, e.g. created by L{TestCase.create_tmp_dir()}
'''
from zim.fs import Dir
from zim.notebook import init_notebook, Notebook, Path
from zim.index import Index
dir = Dir(dir)
init_notebook(dir)
notebook = Notebook(dir=dir)
store = notebook.get_store(':')
manifest = []
for name, text in WikiTestData:
manifest.append(name)
page = store.get_page(Path(name))
page.parse('wiki', text)
store.store_page(page)
notebook.testdata_manifest = _expand_manifest(manifest)
notebook.index.update()
return notebook
class Counter(object):
'''Object that is callable as a function and keeps count how often
it was called.
'''
def __init__(self, value=None):
'''Constructor
@param value: the value to return when called as a function
'''
self.value = value
self.count = 0
def __call__(self, *arg, **kwarg):
self.count += 1
return self.value
class MockObjectBase(object):
'''Base class for mock objects.
Mock methods can be installed with L{mock_method()}. All method
calls to mock methods are logged, so they can be inspected.
The attribute C{mock_calls} has a list of tuples with mock methods
and arguments in order they have been called.
'''
def __init__(self):
self.mock_calls = []
def mock_method(self, name, return_value):
'''Installs a mock method with a given name that returns
a given value.
'''
def my_mock_method(*arg, **kwarg):
call = [name] + list(arg)
if kwarg:
call.append(kwarg)
self.mock_calls.append(tuple(call))
return return_value
setattr(self, name, my_mock_method)
return my_mock_method
class MockObject(MockObjectBase):
'''Simple subclass of L{MockObjectBase} that automatically mocks a
method which returns C{None} for any non-existing attribute.
Attributes that are not methods need to be initialized explicitly.
'''
def __getattr__(self, name):
'''Automatically mock methods'''
if name == '__zim_extension_objects__':
raise AttributeError
else:
return self.mock_method(name, None)
def gtk_process_events(*a):
'''Method to simulate a few iterations of the gtk main loop'''
assert gtk is not None
while gtk.events_pending():
gtk.main_iteration(block=False)
return True # continue
def gtk_get_menu_item(menu, id):
'''Get a menu item from a C{gtk.Menu}
@param menu: a C{gtk.Menu}
@param id: either the menu item label or the stock id
@returns: a C{gtk.MenuItem} or C{None}
'''
items = menu.get_children()
ids = [i.get_property('label') for i in items]
# gtk.ImageMenuItems that have a stock id happen to use the
# 'label' property to store it...
assert id in ids, \
'Menu item "%s" not found, we got:\n' % id \
+ ''.join('- %s \n' % i for i in ids)
i = ids.index(id)
return items[i]
def gtk_activate_menu_item(menu, id):
'''Trigger the 'click' action an a menu item
@param menu: a C{gtk.Menu}
@param id: either the menu item label or the stock id
'''
item = gtk_get_menu_item(menu, id)
item.activate()
zim-0.65/tests/attachmentbrowser.py 0000664 0001750 0001750 00000014760 12606211247 017376 0 ustar jaap jaap 0000000 0000000 # -*- coding: utf-8 -*-
# Copyright 2012,2015 Jaap Karssenberg
from __future__ import with_statement
import tests
import time
from zim.fs import File, Dir
from zim.plugins.attachmentbrowser.thumbnailer import *
from zim.plugins.attachmentbrowser.filebrowser import FileBrowserIconView
@tests.slowTest
class TestThumbnailCreators(tests.TestCase):
creators = [pixbufThumbnailCreator]
def runTest(self):
for creator in self.creators:
thumbdir = Dir(self.create_tmp_dir(creator.__name__))
dir = Dir('./data/pixmaps')
for i, basename in enumerate(dir.list()):
file = dir.file(basename)
thumbfile = thumbdir.file('thumb--' + basename)
self.assertFalse(thumbfile.exists())
pixbuf = creator(file, thumbfile, THUMB_SIZE_NORMAL)
self.assertIsInstance(pixbuf, gtk.gdk.Pixbuf)
self.assertTrue(thumbfile.exists())
pixbuf = gtk.gdk.pixbuf_new_from_file(thumbfile.encodedpath)
self.assertEqual(pixbuf.get_option('tEXt::Thumb::URI'), file.uri)
self.assertTrue(pixbuf.get_option('tEXt::Thumb::URI').startswith('file:///'))
# Specific requirement of spec to use file:/// and not file://localhost/
self.assertEqual(int(pixbuf.get_option('tEXt::Thumb::MTime')), int(file.mtime()))
self.assertTrue(i > 3)
thumbfile = thumbdir.file('thumb-test.txt')
self.assertRaises(
ThumbnailCreatorFailure,
creator, File('./README.txt'), thumbfile, THUMB_SIZE_NORMAL
)
@tests.slowTest
class TestThumbnailManager(tests.TestCase):
def testThumbnailFile(self):
manager = ThumbnailManager()
file = File(u'./foo-\u00e8\u00e1\u00f1.png') # non-existing path with unicode name
self.assertTrue('%C3%A8%C3%A1%C3%B1' in file.uri) # utf encoded!
basename = hashlib.md5(file.uri).hexdigest() + '.png'
for file, size, wanted in (
(file, 28, LOCAL_THUMB_STORAGE_NORMAL.file(basename)),
(file, 64, LOCAL_THUMB_STORAGE_NORMAL.file(basename)),
(file, 128, LOCAL_THUMB_STORAGE_NORMAL.file(basename)),
(file, 200, LOCAL_THUMB_STORAGE_LARGE.file(basename)),
(file, 500, LOCAL_THUMB_STORAGE_LARGE.file(basename)),
):
thumbfile = manager.get_thumbnail_file(file, size)
self.assertEqual(thumbfile, wanted)
self.assertTrue(len(thumbfile.basename) == 32+4) # lenght hexdigest according to spec + ".png"
def removeThumbnail(self, manager, file):
# Remove and assert thumbnail does not exist
manager.remove_thumbnails(file)
for size in (THUMB_SIZE_NORMAL, THUMB_SIZE_LARGE):
thumbfile = manager.get_thumbnail_file(file, size)
self.assertFalse(thumbfile.exists(), msg="File exists: %s" % thumbfile)
def testCreateThumbnail(self):
manager = ThumbnailManager()
dir = Dir(self.create_tmp_dir())
file = dir.file('zim.png')
File('./data/zim.png').copyto(file)
self.assertTrue(file.exists())
self.assertTrue(file.isimage())
self.removeThumbnail(manager, file)
# Thumbfile does not exist
thumbfile, pixbuf = manager.get_thumbnail(file, 64, create=False)
self.assertEqual((thumbfile, pixbuf), (None, None))
thumbfile, pixbuf = manager.get_thumbnail(file, 64)
self.assertTrue(thumbfile.exists())
self.assertIsInstance(pixbuf, gtk.gdk.Pixbuf)
thumbfile, pixbuf = manager.get_thumbnail(file, 64)
self.assertTrue(thumbfile.exists())
self.assertIsInstance(pixbuf, gtk.gdk.Pixbuf)
import stat
mode = os.stat(thumbfile.encodedpath).st_mode
self.assertEqual(stat.S_IMODE(mode), 0600)
mode = os.stat(thumbfile.dir.dir.encodedpath).st_mode # thumnails dir
self.assertEqual(stat.S_IMODE(mode), 0700)
# Change mtime to make thumbfile invalid
oldmtime = file.mtime()
os.utime(file.encodedpath, None)
self.assertNotEqual(file.mtime(), oldmtime)
thumbfile, pixbuf = manager.get_thumbnail(file, 64, create=False)
self.assertEqual((thumbfile, pixbuf), (None, None))
thumbfile, pixbuf = manager.get_thumbnail(file, 64)
self.assertTrue(thumbfile.exists())
self.assertIsInstance(pixbuf, gtk.gdk.Pixbuf)
# ensure next call to get_thumbnail cannot call create_thumbnail
manager.create_thumbnail = None
thumbfile, pixbuf = manager.get_thumbnail(file, 64)
self.assertTrue(thumbfile.exists())
self.assertIsInstance(pixbuf, gtk.gdk.Pixbuf)
# Test remove
self.removeThumbnail(manager, file)
@tests.slowTest
class TestThumbnailQueue(tests.TestCase):
def testQueue(self):
queue = ThumbnailQueue()
self.assertTrue(queue.queue_empty())
# Test input / output
queue.queue_thumbnail_request(File('./README.txt'), 64)
# put an error in the queue
dir = Dir('./data/pixmaps')
pixmaps = set()
for basename in dir.list():
file = dir.file(basename)
pixmaps.add(file)
queue.queue_thumbnail_request(file, 64)
self.assertFalse(queue.queue_empty())
with tests.LoggingFilter('zim.plugins.attachmentbrowser', 'Exception'):
queue.start()
seen = set()
i = len(pixmaps)
while i > 0:
i -= 1
file, size, thumbfile, pixbuf, mtime = queue.get_ready_thumbnail(block=True)
seen.add(file)
self.assertEqual(size, 64)
self.assertTrue(thumbfile.exists())
self.assertIsInstance(pixbuf, gtk.gdk.Pixbuf)
self.assertEqual(mtime, file.mtime())
self.assertEqual(seen, pixmaps)
# Test clear
self.assertTrue(queue.queue_empty())
for file in pixmaps:
queue.queue_thumbnail_request(file, 64)
self.assertFalse(queue.queue_empty())
queue.start()
time.sleep(0.1)
queue.clear_queue()
self.assertTrue(queue.queue_empty())
def testError(self):
def creator_with_failure(*a):
raise ThumbnailCreatorFailure
def creator_with_error(*a):
raise ValueError
file = File('./data/zim.png')
self.assertTrue(file.exists())
self.assertTrue(file.isimage())
for creator in creator_with_failure, creator_with_error:
#~ print ">>", creator.__name__
queue = ThumbnailQueue(creator)
queue.queue_thumbnail_request(file, 64)
with tests.LoggingFilter('zim.plugins.attachmentbrowser', 'Exception'):
queue.start()
while not queue.queue_empty():
r = queue.get_ready_thumbnail()
self.assertIsNone(r[0], None)
@tests.slowTest
class TestFileBrowserIconView(tests.TestCase):
def runTest(self):
opener = tests.MockObject()
iconview = FileBrowserIconView(opener)
dir = Dir('./data/pixmaps')
iconview.set_folder(dir)
# simulate idle events
while not iconview._thumbnailer.queue_empty():
iconview._on_check_thumbnail_queue()
# refresh while nothing changed
iconview.refresh()
while not iconview._thumbnailer.queue_empty():
iconview._on_check_thumbnail_queue()
iconview.teardown_folder()
## Plugin & extention objects are loaded in generic "plugins" test ##
zim-0.65/tests/ipc.py 0000664 0001750 0001750 00000007045 12374655231 014422 0 ustar jaap jaap 0000000 0000000 # -*- coding: utf-8 -*-
# Copyright 2012 Jaap Karssenberg
import tests
import os
import sys
import signal
import gobject
import zim.ipc
from zim.ipc import *
import zim
from zim.fs import File, get_tmpdir
from zim.notebook import NotebookInfo, Path, Page
from zim.stores.files import FileStorePage
@tests.slowTest
class TestIPC(tests.TestCase):
def setUp(self):
self.OLD_SERVER_ADDRESS = zim.ipc.SERVER_ADDRESS
self.OLD_AUTHKEY_FILE = zim.ipc.AUTHKEY_FILE
zim.ipc.SERVER_ADDRESS += '-test-%i' % os.getpid()
zim.ipc.AUTHKEY_FILE = get_tmpdir().file('zim-server-authkey-test-%i' % os.getpid())
zim.ZIM_EXECUTABLE = './zim.py'
def tearDown(self):
stop_server_if_running()
zim.ipc.SERVER_ADDRESS = self.OLD_SERVER_ADDRESS
zim.ipc.AUTHKEY_FILE = self.OLD_AUTHKEY_FILE
zim.ZIM_EXECUTABLE = None
def runTest(self):
# Test setting up the server
start_server_if_not_running()
server = ServerProxy()
ack = server.ping()
self.assertEqual(ack[0], 'ACK')
start_server_if_not_running() # Should do nothing..
server = ServerProxy()
self.assertEqual(server.ping(), ack) # ack has pid, so we know still same server process
# Test adding a child and interact with it
child = server.get_proxy(RemoteObject('tests.ipc.ChildClass', 'file:///foo'))
ack = child.ping()
self.assertEqual(ack[0], 'CHILD')
child = server.get_proxy(RemoteObject('tests.ipc.ChildClass', 'file:///foo'))
# should not vivicate again, verify by pid in ack
self.assertEqual(child.ping(), ack)
# Error handling
self.assertRaises(ValueError, child.error)
# Add a second child
child2 = server.get_proxy(RemoteObject('tests.ipc.ChildClass', 'file:///bar'))
# should not vivicate again, verify by pid in ack
self.assertNotEqual(child2.ping(), ack)
children = server.list_objects()
children.sort(key=lambda c: c.id)
self.assertEqual(children, [
RemoteObject('tests.ipc.ChildClass', 'file:///bar'),
RemoteObject('tests.ipc.ChildClass', 'file:///foo')
])
# Test API for notebooks
server._notebookklass = 'tests.ipc.ChildClass' # HACK to test convenience methods
self.assertEqual(server.list_notebooks(), ['file:///bar', 'file:///foo'])
proxy = server.get_notebook('file:///foo')
self.assertEqual(child.ping(), ack)
# Test these are serializable
for obj in (
File('file:///test'),
NotebookInfo('file:///test'),
Path('foo'),
Page(Path('foo')),
FileStorePage(Path('foo'), File('file:///test'), File('file:///test'), format='wiki'),
):
#~ print ">>> %r" % obj
re = proxy.echo(obj)
self.assertEqual(re, obj)
# send a signal
n = child.get_n_signals()
server.emit('notebook-list-changed')
self.assertEqual(child.get_n_signals(), n+1)
# Wrap up
server.quit()
class ChildClass(object):
# Mock client for the daemon to run. It doesn't do much except
# telling you it's ID by touching a file.
def __init__(self, id):
self.id = id
self.n_signals = 0
def main(self):
zim.ipc.SERVER_CONTEXT._notebookklass = 'tests.ipc.ChildClass' # HACK to test convenience methods
ServerProxy().connect('notebook-list-changed', self)
gobject.MainLoop().run()
def quit(self):
gobject.MainLoop().quit()
os._exit(0) # just to be sure
def ping(self):
return ('CHILD', os.getpid())
def echo(self, value):
return value
def error(self):
raise ValueError, 'Test Error'
def on_notebook_list_changed(self):
notebooks = ServerProxy().list_notebooks()
assert len(notebooks) > 0, 'list_notebooks() returned: %s' % notebooks
self.n_signals += 1
def get_n_signals(self):
return self.n_signals
zim-0.65/tests/tasklist.py 0000664 0001750 0001750 00000017144 12430476224 015502 0 ustar jaap jaap 0000000 0000000 # -*- coding: utf-8 -*-
# Copyright 2011 Jaap Karssenberg
import tests
import zim.plugins
import zim.config
import zim.formats
from zim.plugins import PluginManager
from zim.parsing import parse_date
from zim.plugins.tasklist import *
class TestTaskList(tests.TestCase):
def testIndexing(self):
'''Check indexing of tasklist plugin'''
klass = PluginManager.get_plugin_class('tasklist')
plugin = klass()
notebook = tests.new_notebook()
plugin.extend(notebook.index)
index_ext = plugin.get_extension(IndexExtension)
self.assertIsNotNone(index_ext)
# Test indexing based on index signals
notebook.index.flush()
notebook.index.update()
self.assertTrue(index_ext.db_initialized)
tasks = list(index_ext.list_tasks())
self.assertTrue(len(tasks) > 5)
for task in tasks:
path = index_ext.get_path(task)
self.assertTrue(not path is None)
def testParsing(self):
klass = PluginManager.get_plugin_class('tasklist')
plugin = klass()
notebook = tests.new_notebook()
plugin.extend(notebook.index)
index_ext = plugin.get_extension(IndexExtension)
self.assertIsNotNone(index_ext)
# Test correctnest of parsing
NO_DATE = '9999'
def extract_tasks(text):
# Returns a nested list of tuples, where each node is
# like "(TASK, [CHILD, ...]) where each task (and child)
# is a tuple like (open, actionable, prio, due, description)
parser = zim.formats.get_format('wiki').Parser()
tree = parser.parse(text)
origtree = tree.tostring()
#~ print 'TREE', origtree
tasks = index_ext._extract_tasks(tree)
self.assertEqual(tree.tostring(), origtree)
# extract should not modify the tree
return tasks
def t(label, open=True, due=NO_DATE, prio=0, tags='', actionable=True):
# Generate a task tuple
# (open, actionable, prio, due, tags, description)
if tags:
tags = set(unicode(tags).split(','))
else:
tags = set()
return [open, actionable, prio, due, tags, unicode(label)]
# Note that this same text is in the test notebook
# so it gets run through the index as well - keep in sync
text = '''\
Try all kind of combos - see if the parser trips
TODO:
[ ] A
[ ] B
[ ] C
[ ] D
[ ] E
FIXME: dus
~~FIXME:~~ foo
[ ] Simple
[ ] List
[ ] List with
[ ] Nested items
[*] Some are done
[*] Done but with open child
[x] Others not
[ ] FOOOOO
[ ] Bar
[ ] And then there are @tags
[ ] Next: And due dates
[ ] Date [d: 11/12]
[ ] Date [d: 11/12/2012]
[ ] TODO: BAR !!!
TODO @home:
[ ] Some more tasks !!!
[ ] Foo !
* some sub item
* some other item
[ ] Bar
TODO: dus
FIXME: jaja - TODO !! @FIXME
~~TODO~~: Ignore this one - it is strike out
* TODO: dus - list item
* FIXME: jaja - TODO !! @FIXME - list item
* ~~TODO~~: Ignore this one - it is strike out - list item
* Bullet list
* With tasks as sub items
[ ] Sub item bullets
* dus
1. Numbered list
2. With tasks as sub items
[ ] Sub item numbered
3. dus
Test task inheritance:
[ ] Main @tag1 @tag2 !
[*] Sub1
[ ] Sub2 @tag3 !!!!
[*] Sub2-1
[*] Sub2-2 @tag4
[ ] Sub2-3
[ ] Sub3
TODO: @someday
[ ] A
[ ] B
[ ] B-1
[ ] C
TODO @home
[ ] main task
[x] do this
[ ] Next: do that
[ ] Next: do something else
'''
mydate = '%04i-%02i-%02i' % parse_date('11/12')
wanted = [
(t('A'), []),
(t('B'), []),
(t('C'), []),
(t('D'), []),
(t('E'), []),
(t('FIXME: dus'), []),
(t('Simple'), []),
(t('List'), []),
(t('List with'), [
(t('Nested items'), []),
(t('Some are done', open=False), []),
(t('Done but with open child', open=True), [
(t('Others not', open=False), []),
(t('FOOOOO'), []),
]),
]),
(t('Bar'), []),
(t('And then there are @tags', tags='tags'), []),
(t('Next: And due dates', actionable=False), []),
(t('Date [d: 11/12]', due=mydate), []),
(t('Date [d: 11/12/2012]', due='2012-12-11'), [
(t('TODO: BAR !!!', prio=3, due='2012-12-11'), []),
# due date is inherited
]),
# this list inherits the @home tag - and inherits prio
(t('Some more tasks !!!', prio=3, tags='home'), [
(t('Foo !', prio=1, tags='home'), []),
(t('Bar', prio=3, tags='home'), []),
]),
(t('TODO: dus'), []),
(t('FIXME: jaja - TODO !! @FIXME', prio=2, tags='FIXME'), []),
(t('TODO: dus - list item'), []),
(t('FIXME: jaja - TODO !! @FIXME - list item', prio=2, tags='FIXME'), []),
(t('Sub item bullets'), []),
(t('Sub item numbered'), []),
(t('Main @tag1 @tag2 !', prio=1, tags='tag1,tag2'), [
(t('Sub1', prio=1, open=False, tags='tag1,tag2'), []),
(t('Sub2 @tag3 !!!!', prio=4, tags='tag1,tag2,tag3'), [
(t('Sub2-1', prio=4, open=False, tags='tag1,tag2,tag3'), []),
(t('Sub2-2 @tag4', prio=4, open=False, tags='tag1,tag2,tag3,tag4'), []),
(t('Sub2-3', prio=4, tags='tag1,tag2,tag3'), []),
]),
(t('Sub3', prio=1, tags='tag1,tag2'), []),
]),
(t('A', tags='someday', actionable=False), []),
(t('B', tags='someday', actionable=False), [
(t('B-1', tags='someday', actionable=False), []),
]),
(t('C', tags='someday', actionable=False), []),
(t('main task', tags='home'), [
(t('do this', open=False, tags='home'), []),
(t('Next: do that', tags='home'), []),
(t('Next: do something else', tags='home', actionable=False), []),
])
]
plugin.preferences['nonactionable_tags'] = '@someday, @maybe'
index_ext._set_preferences()
tasks = extract_tasks(text)
self.assertEqual(tasks, wanted)
plugin.preferences['all_checkboxes'] = False
wanted = [
(t('A'), []),
(t('B'), []),
(t('C'), []),
(t('FIXME: dus'), []),
(t('Next: And due dates', actionable=False), []),
(t('TODO: BAR !!!', prio=3), []),
# this list inherits the @home tag - and inherits prio
(t('Some more tasks !!!', prio=3, tags='home'), [
(t('Foo !', prio=1, tags='home'), []),
(t('Bar', prio=3, tags='home'), []),
]),
(t('TODO: dus'), []),
(t('FIXME: jaja - TODO !! @FIXME', prio=2, tags='FIXME'), []),
(t('TODO: dus - list item'), []),
(t('FIXME: jaja - TODO !! @FIXME - list item', prio=2, tags='FIXME'), []),
(t('A', tags='someday', actionable=False), []),
(t('B', tags='someday', actionable=False), [
(t('B-1', tags='someday', actionable=False), []),
]),
(t('C', tags='someday', actionable=False), []),
(t('main task', tags='home'), [
(t('do this', open=False, tags='home'), []),
(t('Next: do that', tags='home'), []),
(t('Next: do something else', tags='home', actionable=False), []),
])
]
tasks = extract_tasks(text)
self.assertEqual(tasks, wanted)
# TODO: more tags, due dates, tags for whole list, etc. ?
#~ def testDialog(self):
#~ '''Check tasklist plugin dialog'''
#
# TODO
def testTaskListTreeView(self):
klass = PluginManager.get_plugin_class('tasklist')
plugin = klass()
notebook = tests.new_notebook()
plugin.extend(notebook.index)
index_ext = plugin.get_extension(IndexExtension)
self.assertIsNotNone(index_ext)
notebook.index.flush()
notebook.index.update()
from zim.plugins.tasklist import TaskListTreeView
opener = tests.MockObject()
treeview = TaskListTreeView(index_ext, opener)
menu = treeview.get_popup()
# Check these do not cause errors - how to verify state ?
tests.gtk_activate_menu_item(menu, _("Expand _All"))
tests.gtk_activate_menu_item(menu, _("_Collapse All"))
# Copy tasklist -> csv
from zim.gui.clipboard import Clipboard
tests.gtk_activate_menu_item(menu, 'gtk-copy')
text = Clipboard.get_text()
lines = text.splitlines()
self.assertTrue(len(lines) > 10)
self.assertTrue(len(lines[0].split(',')) > 3)
self.assertFalse(any('