← Back to team overview

gtg team mailing list archive

[Merge] lp:~izidor/gtg/test-cleanup into lp:gtg

 

Izidor Matušov has proposed merging lp:~izidor/gtg/test-cleanup into lp:gtg.

Requested reviews:
  Gtg developers (gtg)

For more details, see:
https://code.launchpad.net/~izidor/gtg/test-cleanup/+merge/210090

Clean up tests massively! Nobody was touching tests because:

1) They were ugly and huge
2) They took about 15 seconds to run

This request removes many of tests and cleans up the rest. Yes, we have less code coverage (it was already pretty low). However, 70 tests run in 0.275 seconds.

We should add test coverage as we will go.

BTW: Change test runner to use nose. You don't need to register your tests in order to run.
-- 
https://code.launchpad.net/~izidor/gtg/test-cleanup/+merge/210090
Your team Gtg developers is requested to review the proposed merge of lp:~izidor/gtg/test-cleanup into lp:gtg.
=== modified file 'GTG/backends/backend_gnote.py'
--- GTG/backends/backend_gnote.py	2013-11-23 14:40:23 +0000
+++ GTG/backends/backend_gnote.py	2014-03-09 17:00:11 +0000
@@ -54,6 +54,6 @@
             GenericBackend.PARAM_DEFAULT_VALUE: ["@GTG-Gnote"]},
     }
 
-    _BUS_ADDRESS = ("org.gnome.Gnote",
-                    "/org/gnome/Gnote/RemoteControl",
-                    "org.gnome.Gnote.RemoteControl")
+    BUS_ADDRESS = ("org.gnome.Gnote",
+                   "/org/gnome/Gnote/RemoteControl",
+                   "org.gnome.Gnote.RemoteControl")

=== modified file 'GTG/backends/backend_launchpad.py'
--- GTG/backends/backend_launchpad.py	2014-03-09 12:59:59 +0000
+++ GTG/backends/backend_launchpad.py	2014-03-09 17:00:11 +0000
@@ -26,12 +26,9 @@
 import uuid
 import datetime
 from xdg.BaseDirectory import xdg_cache_home
-from launchpadlib.launchpad import Launchpad, \
-    STAGING_SERVICE_ROOT, \
-    EDGE_SERVICE_ROOT
+from launchpadlib.launchpad import Launchpad, EDGE_SERVICE_ROOT
 
 from GTG.core.task import Task
-from GTG.tools.testingmode import TestingMode
 from GTG import _
 from GTG.backends.genericbackend import GenericBackend
 from GTG.backends.backendsignals import BackendSignals
@@ -122,15 +119,10 @@
         # Connecting to Launchpad
         CACHE_DIR = os.path.join(xdg_cache_home, 'gtg/backends/',
                                  self.get_id())
-        if TestingMode().get_testing_mode():
-            SERVICE_ROOT = STAGING_SERVICE_ROOT
-        else:
-            SERVICE_ROOT = EDGE_SERVICE_ROOT
         try:
             self.cancellation_point()
-            self.launchpad = Launchpad.login_anonymously(GTG_NAME,
-                                                         SERVICE_ROOT,
-                                                         CACHE_DIR)
+            self.launchpad = Launchpad.login_anonymously(
+                GTG_NAME, EDGE_SERVICE_ROOT, CACHE_DIR)
         except:
             # The connection is not working (the exception type can be
             # anything)

=== modified file 'GTG/backends/backend_tomboy.py'
--- GTG/backends/backend_tomboy.py	2013-11-23 14:40:23 +0000
+++ GTG/backends/backend_tomboy.py	2014-03-09 17:00:11 +0000
@@ -53,6 +53,6 @@
             GenericBackend.PARAM_DEFAULT_VALUE: ["@GTG-Tomboy"]},
     }
 
-    _BUS_ADDRESS = ("org.gnome.Tomboy",
-                    "/org/gnome/Tomboy/RemoteControl",
-                    "org.gnome.Tomboy.RemoteControl")
+    BUS_ADDRESS = ("org.gnome.Tomboy",
+                   "/org/gnome/Tomboy/RemoteControl",
+                   "org.gnome.Tomboy.RemoteControl")

=== modified file 'GTG/backends/generictomboy.py'
--- GTG/backends/generictomboy.py	2014-01-12 07:15:09 +0000
+++ GTG/backends/generictomboy.py	2014-03-09 17:00:11 +0000
@@ -30,7 +30,6 @@
 import datetime
 import unicodedata
 
-from GTG.tools.testingmode import TestingMode
 from GTG.tools.borg import Borg
 from GTG.backends.genericbackend import GenericBackend
 from GTG.backends.backendsignals import BackendSignals
@@ -57,14 +56,6 @@
                                       "sync_engine-" + self.get_id())
         self.sync_engine = self._load_pickled_file(self.data_path,
                                                    SyncEngine())
-        # if the backend is being tested, we connect to a different DBus
-        # interface to avoid clashing with a running instance of Tomboy
-        if TestingMode().get_testing_mode():
-            # just used for testing purposes
-            self.BUS_ADDRESS = \
-                self._parameters["use this fake connection instead"]
-        else:
-            self.BUS_ADDRESS = self._BUS_ADDRESS
         # we let some time pass before considering a tomboy task for importing,
         # as the user may still be editing it. Here, we store the Timer objects
         # that will execute after some time after each tomboy signal.

=== modified file 'GTG/core/__init__.py'
--- GTG/core/__init__.py	2013-11-25 02:37:46 +0000
+++ GTG/core/__init__.py	2014-03-09 17:00:11 +0000
@@ -43,7 +43,6 @@
 import os
 
 from GTG.tools.borg import Borg
-from GTG.tools.testingmode import TestingMode
 import GTG
 
 DEFAULTS = {
@@ -219,13 +218,8 @@
         if hasattr(self, 'data_dir'):
             # Borg has already been initialized
             return
-        if TestingMode().get_testing_mode():
-            # we avoid running tests in the user data dir
-            self.data_dir = '/tmp/GTG_TESTS/data'
-            self.conf_dir = '/tmp/GTG_TESTS/conf'
-        else:
-            self.data_dir = os.path.join(xdg_data_home, 'gtg/')
-            self.conf_dir = os.path.join(xdg_config_home, 'gtg/')
+        self.data_dir = os.path.join(xdg_data_home, 'gtg/')
+        self.conf_dir = os.path.join(xdg_config_home, 'gtg/')
         if not os.path.exists(self.conf_dir):
             os.makedirs(self.conf_dir)
         if not os.path.exists(self.data_dir):

=== modified file 'GTG/core/tag.py'
--- GTG/core/tag.py	2013-11-25 02:37:46 +0000
+++ GTG/core/tag.py	2014-03-09 17:00:11 +0000
@@ -120,7 +120,7 @@
         """
         modified = False
         if att_name == "name":
-            raise Set_Name_Attribute_Error(
+            raise KeyError(
                 "The name of tag cannot be set manually")
         elif att_name == "parent":
             self.add_parent(att_value)
@@ -246,8 +246,3 @@
 
     def __str__(self):
         return "Tag: %s" % self.get_name()
-
-
-class Set_Name_Attribute_Error(Exception):
-    """Exception raised when try to set attribute to name"""
-    pass

=== renamed file 'GTG/tests/signals_testing.py' => 'GTG/tests/signals_testing.py.THIS'
=== removed file 'GTG/tests/test_apidocs.py'
--- GTG/tests/test_apidocs.py	2013-11-23 14:40:23 +0000
+++ GTG/tests/test_apidocs.py	1970-01-01 00:00:00 +0000
@@ -1,57 +0,0 @@
-# -*- coding: utf-8 -*-
-# -----------------------------------------------------------------------------
-# Getting Things GNOME! - a personal organizer for the GNOME desktop
-# Copyright (c) 2008-2013 - Lionel Dricot & Bertrand Rousseau
-#
-# This program is free software: you can redistribute it and/or modify it under
-# the terms of the GNU General Public License as published by the Free Software
-# Foundation, either version 3 of the License, or (at your option) any later
-# version.
-#
-# This program is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
-# details.
-#
-# You should have received a copy of the GNU General Public License along with
-# this program.  If not, see <http://www.gnu.org/licenses/>.
-# -----------------------------------------------------------------------------
-
-""" Tests for the documentation. """
-
-import unittest
-
-import subprocess
-import GTG
-import os.path
-import shutil
-import uuid
-
-
-class TestApiDocs(unittest.TestCase):
-    """ Test if the documentation still builds. """
-
-    def test_pydoctor(self):
-        if int(subprocess.call(['which', 'pydoctor'], stdout=subprocess.PIPE)):
-            # if no pydoctor is present, abort the test w/out giving error
-            return
-        GTG_basedir = os.path.dirname(GTG.__file__)
-        api_dir = os.path.join(GTG_basedir, '..', 'tmp',
-                               'test_build_api-' + str(uuid.uuid4()))
-        if not os.path.isdir(api_dir):
-            os.makedirs(api_dir)
-        args = ['pydoctor',
-                '--add-package', GTG_basedir,
-                '--make-html',
-                '--html-output=' + api_dir,
-                '--project-name=GTG',
-                '--project-url=http://gtg.fritalk.com/']
-        # we suppress printing of errors to keep a clean output
-        assert(int(subprocess.call(args,
-                                   stdout=subprocess.PIPE,
-                                   stderr=subprocess.PIPE)) == 0)
-        shutil.rmtree(api_dir)
-
-
-def test_suite():
-    return unittest.TestLoader().loadTestsFromTestCase(TestApiDocs)

=== renamed file 'GTG/tests/test_backend_tomboy.py' => 'GTG/tests/test_backend_tomboy.py.THIS'
=== removed file 'GTG/tests/test_backends.py'
--- GTG/tests/test_backends.py	2013-11-25 02:37:46 +0000
+++ GTG/tests/test_backends.py	1970-01-01 00:00:00 +0000
@@ -1,139 +0,0 @@
-# -*- coding: utf-8 -*-
-# -----------------------------------------------------------------------------
-# Getting Things GNOME! - a personal organizer for the GNOME desktop
-# Copyright (c) 2008-2013 - Lionel Dricot & Bertrand Rousseau
-#
-# This program is free software: you can redistribute it and/or modify it under
-# the terms of the GNU General Public License as published by the Free Software
-# Foundation, either version 3 of the License, or (at your option) any later
-# version.
-#
-# This program is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
-# details.
-#
-# You should have received a copy of the GNU General Public License along with
-# this program.  If not, see <http://www.gnu.org/licenses/>.
-# -----------------------------------------------------------------------------
-
-""" Tests for GTG backends.
-
-Some of these tests will generate files in
-xdg.BaseDirectory.xdg_data_home/gtg directory.
-"""
-
-# Standard imports
-import unittest
-import os
-import xdg
-
-# GTG imports
-from GTG.backends import backend_localfile as localfile
-from GTG.tools import cleanxml
-from GTG.core import CoreConfig
-
-
-class GtgBackendsUniTests(unittest.TestCase):
-    """Tests for GTG backends."""
-
-    def __init__(self, test):
-        unittest.TestCase.__init__(self, test)
-        self.taskfile = ''
-        self.datafile = ''
-        self.taskpath = ''
-        self.datapath = ''
-
-    def SetUp(self):
-        CoreConfig().set_data_dir("./test_data")
-        CoreConfig().set_conf_dir("./test_data")
-
-    def test_localfile_get_name(self):
-        """Tests for localfile/get_name function :
-        - a string is expected.
-        """
-        res = localfile.Backend.get_name()
-        expectedres = "backend_localfile"
-        self.assertEqual(res, expectedres)
-
-    def test_localfile_get_description(self):
-        """Tests for localfile/get_description function :
-        - a string is expected.
-        """
-        res = localfile.Backend.get_description()
-        expectedres = "Your tasks are saved"
-        self.assertEqual(res[:len(expectedres)], expectedres)
-
-    def test_localfile_get_static_parameters(self):
-        """Tests for localfile/get_static_parameters function:
-        - a string is expected.
-        """
-        res = localfile.Backend.get_static_parameters()
-        self.assertEqual(res['path']['type'], "string")
-
-    def test_localfile_get_type(self):
-        """Tests for localfile/get_type function:
-        - a string is expected.
-        """
-        res = localfile.Backend.get_type()
-        expectedres = "readwrite"
-        self.assertEqual(res, expectedres)
-
-    def test_localfile_backend_method3(self):
-        """Tests for localfile/Backend/remove_task method:
-        - parse task file to check if task has been removed.
-        """
-        self.create_test_environment()
-        doc, configxml = cleanxml.openxmlfile(self.datapath, 'config')
-        xmlproject = doc.getElementsByTagName('backend')
-        for domobj in xmlproject:
-            dic = {}
-            if domobj.hasAttribute("module"):
-                dic["module"] = str(domobj.getAttribute("module"))
-                dic["pid"] = str(domobj.getAttribute("pid"))
-                dic["xmlobject"] = domobj
-                dic["enabled"] = True
-                dic["path"] = self.taskpath
-        beobj = localfile.Backend(dic)
-        expectedres = True
-        beobj.remove_task("0@1")
-        beobj.quit()
-        dataline = open(self.taskpath, 'r').read()
-        if "0@1" in dataline:
-            res = False
-        else:
-            res = True
-        expectedres = True
-        self.assertEqual(res, expectedres)
-
-    def create_test_environment(self):
-        """Create the test environment"""
-        self.taskfile = 'test.xml'
-        self.datafile = 'projectstest.xml'
-        tasks = [
-            '<?xml version="1.0" ?>\n',
-            '<project>\n',
-            '\t<task id="0@1" status="Active" tags="">\n',
-            '\t\t<title>\n',
-            '\t\t\tCeci est un test\n',
-            '\t\t</title>\n',
-            '\t</task>\n',
-            '</project>\n',
-        ]
-        data = [
-            '<?xml version="1.0" ?>\n',
-            '<config>\n',
-            '\t<backend filename="test.xml" module="localfile" pid="1"/>\n',
-            '</config>\n',
-        ]
-        self.testdir = os.path.join(xdg.BaseDirectory.xdg_data_home, 'gtg')
-        if not os.path.exists(self.testdir):
-            os.makedirs(self.testdir)
-        self.taskpath = os.path.join(self.testdir, self.taskfile)
-        self.datapath = os.path.join(self.testdir, self.datafile)
-        open(self.taskpath, 'w').writelines(tasks)
-        open(self.datapath, 'w').writelines(data)
-
-
-def test_suite():
-    return unittest.TestLoader().loadTestsFromName(__name__)

=== removed file 'GTG/tests/test_bidict.py'
--- GTG/tests/test_bidict.py	2013-11-25 02:37:46 +0000
+++ GTG/tests/test_bidict.py	1970-01-01 00:00:00 +0000
@@ -1,69 +0,0 @@
-# -*- coding: utf-8 -*-
-# -----------------------------------------------------------------------------
-# Getting Things GNOME! - a personal organizer for the GNOME desktop
-# Copyright (c) 2008-2013 - Lionel Dricot & Bertrand Rousseau
-#
-# This program is free software: you can redistribute it and/or modify it under
-# the terms of the GNU General Public License as published by the Free Software
-# Foundation, either version 3 of the License, or (at your option) any later
-# version.
-#
-# This program is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
-# details.
-#
-# You should have received a copy of the GNU General Public License along with
-# this program.  If not, see <http://www.gnu.org/licenses/>.
-# -----------------------------------------------------------------------------
-
-""" Tests for the BiDict class """
-
-import unittest
-import uuid
-
-from GTG.tools.bidict import BiDict
-
-
-class TestBiDict(unittest.TestCase):
-    """ Tests for the BiDict object."""
-
-    def test_add_and_gets(self):
-        """ Test for the __init__, _get_by_first, _get_by_second function """
-        pairs = [(uuid.uuid4(), uuid.uuid4()) for a in range(10)]
-        bidict = BiDict(*pairs)
-        for pair in pairs:
-            self.assertEqual(bidict._get_by_first(pair[0]), pair[1])
-            self.assertEqual(bidict._get_by_second(pair[1]), pair[0])
-
-    def test_remove_by_first_or_second(self):
-        """ Tests for removing elements from the biDict """
-        pair_first = (1, 'one')
-        pair_second = (2, 'two')
-        bidict = BiDict(pair_first, pair_second)
-        bidict._remove_by_first(pair_first[0])
-        bidict._remove_by_second(pair_second[1])
-        missing_first = 0
-        missing_second = 0
-        try:
-            bidict._get_by_first(pair_first[0])
-        except KeyError:
-            missing_first += 1
-        try:
-            bidict._get_by_first(pair_second[0])
-        except KeyError:
-            missing_first += 1
-        try:
-            bidict._get_by_second(pair_first[1])
-        except KeyError:
-            missing_second += 1
-        try:
-            bidict._get_by_second(pair_second[1])
-        except KeyError:
-            missing_second += 1
-        self.assertEqual(missing_first, 2)
-        self.assertEqual(missing_second, 2)
-
-
-def test_suite():
-    return unittest.TestLoader().loadTestsFromTestCase(TestBiDict)

=== removed file 'GTG/tests/test_datastore.py'
--- GTG/tests/test_datastore.py	2013-11-25 02:37:46 +0000
+++ GTG/tests/test_datastore.py	1970-01-01 00:00:00 +0000
@@ -1,346 +0,0 @@
-# -*- coding: utf-8 -*-
-# -----------------------------------------------------------------------------
-# Getting Things GNOME! - a personal organizer for the GNOME desktop
-# Copyright (c) 2008-2013 - Lionel Dricot & Bertrand Rousseau
-#
-# This program is free software: you can redistribute it and/or modify it under
-# the terms of the GNU General Public License as published by the Free Software
-# Foundation, either version 3 of the License, or (at your option) any later
-# version.
-#
-# This program is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
-# details.
-#
-# You should have received a copy of the GNU General Public License along with
-# this program.  If not, see <http://www.gnu.org/licenses/>.
-# -----------------------------------------------------------------------------
-
-""" Tests for the datastore """
-
-import unittest
-import uuid
-import time
-from random import randint
-from gi.repository import GObject
-
-import GTG
-from GTG.core.datastore import DataStore
-from GTG.backends.genericbackend import GenericBackend
-from GTG.core import CoreConfig
-from liblarch import Tree
-
-
-def sleep_within_loop(duration):
-    main_loop = GObject.MainLoop()
-    GObject.timeout_add(duration * 1000, main_loop.quit)
-    # NOTE: I am not sure why, but I need add this
-    # dumb thing to run _process method of LibLarch
-    GObject.idle_add(lambda: True)
-    main_loop.run()
-
-
-class TestDatastore(unittest.TestCase):
-    """ Tests for the DataStore object.  """
-
-    def setUp(self):
-        """
-        Creates the environment for the tests
-        @returns: None
-        """
-        self.datastore = DataStore()
-        self.requester = self.datastore.get_requester()
-
-    def test_task_factory(self):
-        """ Test for the task_factory function """
-        # generate a Task with a random id
-        tid = str(uuid.uuid4())
-        task = self.datastore.task_factory(tid, newtask=True)
-        self.assertTrue(isinstance(task, GTG.core.task.Task))
-        self.assertEqual(task.get_id(), tid)
-        self.assertEqual(task.is_new(), True)
-        tid = str(uuid.uuid4())
-        task = self.datastore.task_factory(tid, newtask=False)
-        self.assertEqual(task.is_new(), False)
-
-    def test_new_task_and_has_task(self):
-        """ Tests the new_task function """
-        task = self.datastore.new_task()
-        tid = task.get_id()
-        self.assertTrue(isinstance(tid, str))
-        self.assertTrue(tid != '')
-        self.assertTrue(task.is_new())
-        self.assertTrue(self.datastore.has_task(tid))
-        self.assertTrue(len(self.datastore.get_all_tasks()) == 1)
-
-    def test_get_all_tasks(self):
-        """ Tests the get_all_tasks function """
-        task_ids = []
-        for i in range(1, 10):
-            task = self.datastore.new_task()
-            task_ids.append(task.get_id())
-            return_list = self.datastore.get_all_tasks()
-            self.assertEqual(len(return_list), i)
-            task_ids.sort()
-            return_list.sort()
-            self.assertEqual(task_ids, return_list)
-
-    def test_get_task(self):
-        '''
-        Tests the get_task function
-        '''
-        task = self.datastore.new_task()
-        self.assertTrue(isinstance(self.datastore.get_task(task.get_id()),
-                                   GTG.core.task.Task))
-        self.assertEqual(self.datastore.get_task(task.get_id()), task)
-
-    def test_get_requester(self):
-        '''
-        Tests the get_requester function
-        '''
-        requester = self.datastore.get_requester()
-        self.assertTrue(isinstance(requester, GTG.core.requester.Requester))
-
-    def test_get_tasks_tree(self):
-        '''
-        Tests the get_tasks_tree function
-        '''
-        tasks_tree = self.datastore.get_tasks_tree()
-        self.assertTrue(isinstance(tasks_tree, Tree))
-
-    def test_push_task(self):
-        '''
-        Tests the push_task function
-        '''
-        task_ids = []
-        for i in range(1, 10):
-            tid = str(uuid.uuid4())
-            if tid not in task_ids:
-                task_ids.append(tid)
-            task = self.datastore.task_factory(tid)
-            return_value1 = self.datastore.push_task(task)
-            self.assertTrue(return_value1)
-            # we do it twice, but it should be pushed only once if it's
-            # working correctly (the second should be discarded)
-            return_value2 = self.datastore.push_task(task)
-            self.assertFalse(return_value2)
-            stored_tasks = self.datastore.get_all_tasks()
-            task_ids.sort()
-            stored_tasks.sort()
-            self.assertEqual(task_ids, stored_tasks)
-
-    def test_register_backend(self):
-        '''
-        Tests the register_backend function. It also tests the
-        get_all_backends and get_backend function as a side effect
-        '''
-        # create a simple backend dictionary
-        backend = FakeBackend(enabled=True)
-        tasks_in_backend_count = randint(1, 20)
-        for temp in range(0, tasks_in_backend_count):
-            backend.fake_add_random_task()
-        backend_dic = {'backend': backend, 'pid': 'a'}
-        self.datastore.register_backend(backend_dic)
-        all_backends = self.datastore.get_all_backends(disabled=True)
-        self.assertEqual(len(all_backends), 1)
-        registered_backend = self.datastore.get_backend(backend.get_id())
-        self.assertEqual(backend.get_id(), registered_backend.get_id())
-        self.assertTrue(isinstance(registered_backend,
-                                   GTG.core.datastore.TaskSource))
-        self.assertTrue(registered_backend.is_enabled())
-        self.assertEqual(registered_backend.fake_get_initialized_count(), 1)
-        # we give some time for the backend to push all its tasks
-        sleep_within_loop(1)
-        self.assertEqual(len(self.datastore.get_all_tasks()),
-                         tasks_in_backend_count)
-
-        # same test, disabled backend
-        backend = FakeBackend(enabled=False)
-        for temp in range(1, randint(2, 20)):
-            backend.fake_add_random_task()
-        backend_dic = {'backend': backend, 'pid': 'b'}
-        self.datastore.register_backend(backend_dic)
-        all_backends = self.datastore.get_all_backends(disabled=True)
-        self.assertEqual(len(all_backends), 2)
-        all_backends = self.datastore.get_all_backends(disabled=False)
-        self.assertEqual(len(all_backends), 1)
-        registered_backend = self.datastore.get_backend(backend.get_id())
-        self.assertEqual(backend.get_id(), registered_backend.get_id())
-        self.assertTrue(isinstance(registered_backend,
-                                   GTG.core.datastore.TaskSource))
-        self.assertFalse(registered_backend.is_enabled())
-        self.assertEqual(registered_backend.fake_get_initialized_count(), 0)
-        # we give some time for the backend to push all its tasks (is
-        # shouldn't, since it's disabled, but we give time anyway
-        time.sleep(1)
-        self.assertEqual(len(self.datastore.get_all_tasks()),
-                         tasks_in_backend_count)
-
-    def test_set_backend_enabled(self):
-        '''
-        Tests the set_backend_enabled function
-        '''
-        enabled_backend = FakeBackend(enabled=True)
-        disabled_backend = FakeBackend(enabled=False)
-        self.datastore.register_backend({'backend': enabled_backend,
-                                         'pid': str(uuid.uuid4()),
-                                         GenericBackend.KEY_DEFAULT_BACKEND:
-                                         False})
-        self.datastore.register_backend({'backend': disabled_backend,
-                                         'pid': str(uuid.uuid4()),
-                                         GenericBackend.KEY_DEFAULT_BACKEND:
-                                         False})
-        # enabling an enabled backend
-        self.datastore.set_backend_enabled(enabled_backend.get_id(), True)
-        self.assertEqual(enabled_backend.fake_get_initialized_count(), 1)
-        self.assertTrue(enabled_backend.is_enabled())
-        # disabling a disabled backend
-        self.datastore.set_backend_enabled(disabled_backend.get_id(), False)
-        self.assertEqual(disabled_backend.fake_get_initialized_count(), 0)
-        self.assertFalse(disabled_backend.is_enabled())
-        # disabling an enabled backend
-        self.datastore.set_backend_enabled(enabled_backend.get_id(), False)
-        self.assertEqual(enabled_backend.fake_get_initialized_count(), 1)
-        countdown = 10
-        while countdown >= 0 and enabled_backend.is_enabled():
-            time.sleep(0.1)
-        self.assertFalse(enabled_backend.is_enabled())
-#        #enabling a disabled backend
-#        self.datastore.set_backend_enabled(disabled_backend.get_id(), True)
-#        self.assertEqual(disabled_backend.fake_get_initialized_count(), 1)
-#        self.assertTrue(disabled_backend.is_enabled())
-
-    def test_remove_backend(self):
-        """ Tests the remove_backend function """
-        enabled_backend = FakeBackend(enabled=True)
-        disabled_backend = FakeBackend(enabled=False)
-        self.datastore.register_backend({'backend': enabled_backend,
-                                         'pid': str(uuid.uuid4()),
-                                         GenericBackend.KEY_DEFAULT_BACKEND:
-                                         False})
-        self.datastore.register_backend({'backend': disabled_backend,
-                                         'pid': str(uuid.uuid4()),
-                                         GenericBackend.KEY_DEFAULT_BACKEND:
-                                         False})
-        # removing an enabled backend
-        self.datastore.remove_backend(enabled_backend.get_id())
-        # waiting
-        countdown = 10
-        while countdown >= 0 and enabled_backend.is_enabled():
-            time.sleep(0.1)
-        self.assertFalse(enabled_backend.is_enabled())
-        self.assertEqual(
-            len(self.datastore.get_all_backends(disabled=True)), 1)
-        # removing a disabled backend
-        self.datastore.remove_backend(disabled_backend.get_id())
-        self.assertFalse(disabled_backend.is_enabled())
-        self.assertEqual(
-            len(self.datastore.get_all_backends(disabled=True)), 0)
-
-    def test_flush_all_tasks(self):
-        '''
-        Tests the flush_all_tasks function
-        '''
-        # we add some tasks in the datastore
-        tasks_in_datastore_count = 10  # randint(1, 20)
-        for temp in range(0, tasks_in_datastore_count):
-            self.datastore.new_task()
-        datastore_stored_tids = self.datastore.get_all_tasks()
-        self.assertEqual(tasks_in_datastore_count, len(datastore_stored_tids))
-
-        # we enable a backend
-        backend = FakeBackend(enabled=True)
-        self.datastore.register_backend({'backend': backend, 'pid': 'a'})
-        # we wait for the signal storm to wear off
-        sleep_within_loop(2)
-        # we sync
-        self.datastore.get_backend(backend.get_id()).sync()
-        # and we inject task in the backend
-        tasks_in_backend_count = 5  # randint(1, 20)
-        for temp in range(0, tasks_in_backend_count):
-            backend.fake_add_random_task()
-        backend_stored_tids = backend.fake_get_task_ids()
-        self.assertEqual(tasks_in_backend_count, len(backend_stored_tids))
-        self.datastore.flush_all_tasks(backend.get_id())
-        # we wait for the signal storm to wear off
-        sleep_within_loop(2)
-        # we sync
-        self.datastore.get_backend(backend.get_id()).sync()
-        all_tasks_count = tasks_in_backend_count + tasks_in_datastore_count
-        new_datastore_stored_tids = self.datastore.get_all_tasks()
-        new_backend_stored_tids = backend.fake_get_task_ids()
-        self.assertEqual(len(new_backend_stored_tids), all_tasks_count)
-        self.assertEqual(len(new_datastore_stored_tids), all_tasks_count)
-        new_datastore_stored_tids.sort()
-        new_backend_stored_tids.sort()
-        self.assertEqual(new_backend_stored_tids, new_datastore_stored_tids)
-
-
-def test_suite():
-    return unittest.TestLoader().loadTestsFromTestCase(TestDatastore)
-
-
-class FakeBackend(unittest.TestCase):
-    '''
-    Mimics the behavior of a simple backend. Just used for testing
-    '''
-
-    def __init__(self, enabled=True):
-        self.enabled = enabled
-        self.initialized_count = 0
-        self.tasks_ids = []
-        self.backend_id = str(uuid.uuid4())
-        self.purged = False
-
-    def is_enabled(self):
-        return self.enabled
-
-    def initialize(self):
-        self.initialized_count += 1
-        self.enabled = True
-
-    def queue_set_task(self, task):
-        if task.get_id() not in self.tasks_ids:
-            self.tasks_ids.append(task.get_id())
-
-    def has_task(self, task_id):
-        return task_id in self.tasks_ids
-
-    def queue_remove_task(self, task_id):
-        self.tasks_ids.remove(task_id)
-
-    def get_id(self):
-        return self.backend_id
-
-    def start_get_tasks(self):
-        for task_id in self.tasks_ids:
-            self.datastore.push_task(self.datastore.task_factory(task_id))
-
-    def quit(self, disabled=False):
-        self.enabled = not disabled
-
-    def is_default(self):
-        return True
-
-    def set_parameter(self, param_name, param_value):
-        pass
-
-    def get_attached_tags(self):
-        return [CoreConfig.ALLTASKS_TAG]
-
-    def register_datastore(self, datastore):
-        self.datastore = datastore
-
-    ##########################################################################
-    # The following are used just for testing, they're not present inside a
-    # normal backend
-    ##########################################################################
-    def fake_get_initialized_count(self):
-        return self.initialized_count
-
-    def fake_get_task_ids(self):
-        return self.tasks_ids
-
-    def fake_add_random_task(self):
-        self.tasks_ids.append(str(uuid.uuid4()))

=== removed file 'GTG/tests/test_signal_testing.py'
--- GTG/tests/test_signal_testing.py	2013-11-25 02:37:46 +0000
+++ GTG/tests/test_signal_testing.py	1970-01-01 00:00:00 +0000
@@ -1,60 +0,0 @@
-# -*- coding: utf-8 -*-
-# -----------------------------------------------------------------------------
-# Getting Things GNOME! - a personal organizer for the GNOME desktop
-# Copyright (c) 2008-2013 - Lionel Dricot & Bertrand Rousseau
-#
-# This program is free software: you can redistribute it and/or modify it under
-# the terms of the GNU General Public License as published by the Free Software
-# Foundation, either version 3 of the License, or (at your option) any later
-# version.
-#
-# This program is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
-# details.
-#
-# You should have received a copy of the GNU General Public License along with
-# this program.  If not, see <http://www.gnu.org/licenses/>.
-# -----------------------------------------------------------------------------
-
-from gi.repository import GObject
-import unittest
-import uuid
-
-from GTG.tests.signals_testing import SignalCatcher, GobjectSignalsManager
-
-
-class TestSignalTesting(unittest.TestCase):
-
-    def setUp(self):
-        self.gobject_signal_manager = GobjectSignalsManager()
-        self.gobject_signal_manager.init_signals()
-
-    def tearDown(self):
-        self.gobject_signal_manager.terminate_signals()
-
-    def test_signal_catching(self):
-        generator = FakeGobject()
-        arg = str(uuid.uuid4())
-        with SignalCatcher(self, generator, 'one') \
-                as [signal_catched_event, signal_arguments]:
-            generator.emit_signal('one', arg)
-            signal_catched_event.wait()
-        self.assertEqual(len(signal_arguments), 1)
-        self.assertEqual(len(signal_arguments[0]), 1)
-        one_signal_arguments = signal_arguments[0]
-        self.assertEqual(arg, one_signal_arguments[0])
-
-
-class FakeGobject(GObject.GObject):
-    __gsignals__ = {'one': (GObject.SignalFlags.RUN_FIRST,
-                            None, (str, )),
-                    'two': (GObject.SignalFlags.RUN_FIRST,
-                            None, (str, ))}
-
-    def emit_signal(self, signal_name, argument):
-        GObject.idle_add(self.emit, signal_name, argument)
-
-
-def test_suite():
-    return unittest.TestLoader().loadTestsFromTestCase(TestSignalTesting)

=== removed file 'GTG/tests/test_syncengine.py'
--- GTG/tests/test_syncengine.py	2013-11-23 14:40:23 +0000
+++ GTG/tests/test_syncengine.py	1970-01-01 00:00:00 +0000
@@ -1,206 +0,0 @@
-# -*- coding: utf-8 -*-
-# -----------------------------------------------------------------------------
-# Getting Things GNOME! - a personal organizer for the GNOME desktop
-# Copyright (c) 2008-2013 - Lionel Dricot & Bertrand Rousseau
-#
-# This program is free software: you can redistribute it and/or modify it under
-# the terms of the GNU General Public License as published by the Free Software
-# Foundation, either version 3 of the License, or (at your option) any later
-# version.
-#
-# This program is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
-# details.
-#
-# You should have received a copy of the GNU General Public License along with
-# this program.  If not, see <http://www.gnu.org/licenses/>.
-# -----------------------------------------------------------------------------
-
-""" Tests for the SyncEngine class """
-
-import unittest
-import uuid
-
-from GTG.backends.syncengine import SyncEngine
-
-
-class TestSyncEngine(unittest.TestCase):
-    """ Tests for the SyncEngine object. """
-
-    def setUp(self):
-        self.ftp_local = FakeTaskProvider()
-        self.ftp_remote = FakeTaskProvider()
-        self.sync_engine = SyncEngine()
-
-    def test_analyze_element_and_record_and_break_relationship(self):
-        """ Test for the _analyze_element, analyze_remote_id, analyze_local_id,
-        record_relationship, break_relationship """
-        # adding a new local task
-        has_local_task = self.ftp_local.has_task
-        has_remote_task = self.ftp_remote.has_task
-        local_id = uuid.uuid4()
-        self.ftp_local.fake_add_task(local_id)
-        self.assertEqual(self.sync_engine.analyze_local_id(local_id,
-                                                           has_local_task,
-                                                           has_remote_task),
-                         (SyncEngine.ADD, None))
-        # creating the related remote task
-        remote_id = uuid.uuid4()
-        self.ftp_remote.fake_add_task(remote_id)
-        # informing the sync_engine about that
-        self.sync_engine.record_relationship(local_id, remote_id, object())
-        # verifying that it understood that
-        self.assertEqual(self.sync_engine.analyze_local_id(local_id,
-                                                           has_local_task,
-                                                           has_remote_task),
-                         (SyncEngine.UPDATE, remote_id))
-        self.assertEqual(self.sync_engine.analyze_remote_id(remote_id,
-                                                            has_local_task,
-                                                            has_remote_task),
-                         (SyncEngine.UPDATE, local_id))
-        # and not the reverse
-        self.assertEqual(self.sync_engine.analyze_remote_id(local_id,
-                                                            has_local_task,
-                                                            has_remote_task),
-                         (SyncEngine.ADD, None))
-        self.assertEqual(self.sync_engine.analyze_local_id(remote_id,
-                                                           has_local_task,
-                                                           has_remote_task),
-                         (SyncEngine.ADD, None))
-        # now we remove the remote task
-        self.ftp_remote.fake_remove_task(remote_id)
-        self.assertEqual(self.sync_engine.analyze_local_id(local_id,
-                                                           has_local_task,
-                                                           has_remote_task),
-                         (SyncEngine.REMOVE, None))
-        self.sync_engine.break_relationship(local_id=local_id)
-        self.assertEqual(self.sync_engine.analyze_local_id(local_id,
-                                                           has_local_task,
-                                                           has_remote_task),
-                         (SyncEngine.ADD, None))
-        self.assertEqual(self.sync_engine.analyze_remote_id(remote_id,
-                                                            has_local_task,
-                                                            has_remote_task),
-                         (SyncEngine.ADD, None))
-        # we add them back and remove giving the remote id as key to find
-        # what to delete
-        self.ftp_local.fake_add_task(local_id)
-        self.ftp_remote.fake_add_task(remote_id)
-        self.ftp_remote.fake_remove_task(remote_id)
-        self.sync_engine.record_relationship(local_id, remote_id, object)
-        self.sync_engine.break_relationship(remote_id=remote_id)
-        self.assertEqual(self.sync_engine.analyze_local_id(local_id,
-                                                           has_local_task,
-                                                           has_remote_task),
-                         (SyncEngine.ADD, None))
-        self.assertEqual(self.sync_engine.analyze_remote_id(remote_id,
-                                                            has_local_task,
-                                                            has_remote_task),
-                         (SyncEngine.ADD, None))
-
-    def test_syncability(self):
-        """ Test for the _analyze_element, analyze_remote_id, analyze_local_id.
-        Checks that the is_syncable parameter is used correctly """
-        # adding a new local task unsyncable
-        has_local_task = self.ftp_local.has_task
-        has_remote_task = self.ftp_remote.has_task
-        local_id = uuid.uuid4()
-        self.ftp_local.fake_add_task(local_id)
-        self.assertEqual(self.sync_engine.analyze_local_id(local_id,
-                                                           has_local_task,
-                                                           has_remote_task,
-                                                           False),
-                         (None, None))
-        # adding a new local task, syncable
-        local_id = uuid.uuid4()
-        self.ftp_local.fake_add_task(local_id)
-        self.assertEqual(self.sync_engine.analyze_local_id(local_id,
-                                                           has_local_task,
-                                                           has_remote_task),
-                         (SyncEngine.ADD, None))
-        # creating the related remote task
-        remote_id = uuid.uuid4()
-        self.ftp_remote.fake_add_task(remote_id)
-        # informing the sync_engine about that
-        self.sync_engine.record_relationship(local_id, remote_id, object())
-        # checking that it behaves correctly with established relationships
-        self.assertEqual(self.sync_engine.analyze_local_id(local_id,
-                                                           has_local_task,
-                                                           has_remote_task,
-                                                           True),
-                         (SyncEngine.UPDATE, remote_id))
-        self.assertEqual(self.sync_engine.analyze_local_id(local_id,
-                                                           has_local_task,
-                                                           has_remote_task,
-                                                           False),
-                         (SyncEngine.LOST_SYNCABILITY, remote_id))
-        self.assertEqual(self.sync_engine.analyze_remote_id(remote_id,
-                                                            has_local_task,
-                                                            has_remote_task,
-                                                            True),
-                         (SyncEngine.UPDATE, local_id))
-        self.assertEqual(self.sync_engine.analyze_remote_id(remote_id,
-                                                            has_local_task,
-                                                            has_remote_task,
-                                                            False),
-                         (SyncEngine.LOST_SYNCABILITY, local_id))
-        # now we remove the remote task
-        self.ftp_remote.fake_remove_task(remote_id)
-        self.assertEqual(self.sync_engine.analyze_local_id(local_id,
-                                                           has_local_task,
-                                                           has_remote_task,
-                                                           True),
-                         (SyncEngine.REMOVE, None))
-        self.assertEqual(self.sync_engine.analyze_local_id(local_id,
-                                                           has_local_task,
-                                                           has_remote_task,
-                                                           False),
-                         (SyncEngine.REMOVE, None))
-        self.sync_engine.break_relationship(local_id=local_id)
-        self.assertEqual(self.sync_engine.analyze_local_id(local_id,
-                                                           has_local_task,
-                                                           has_remote_task,
-                                                           True),
-                         (SyncEngine.ADD, None))
-        self.assertEqual(self.sync_engine.analyze_local_id(local_id,
-                                                           has_local_task,
-                                                           has_remote_task,
-                                                           False),
-                         (None, None))
-        self.assertEqual(self.sync_engine.analyze_remote_id(remote_id,
-                                                            has_local_task,
-                                                            has_remote_task,
-                                                            True),
-                         (SyncEngine.ADD, None))
-        self.assertEqual(self.sync_engine.analyze_remote_id(remote_id,
-                                                            has_local_task,
-                                                            has_remote_task,
-                                                            False),
-                         (None, None))
-
-
-def test_suite():
-    return unittest.TestLoader().loadTestsFromTestCase(TestSyncEngine)
-
-
-class FakeTaskProvider(object):
-
-    def __init__(self):
-        self.dic = {}
-
-    def has_task(self, tid):
-        return tid in self.dic
-
-##############################################################################
-### Function with the fake_ prefix are here to assist in testing, they do not
-### need to be present in the real class
-##############################################################################
-    def fake_add_task(self, tid):
-        self.dic[tid] = "something"
-
-    def fake_get_task(self, tid):
-        return self.dic[tid]
-
-    def fake_remove_task(self, tid):
-        del self.dic[tid]

=== removed file 'GTG/tests/test_syncmeme.py'
--- GTG/tests/test_syncmeme.py	2013-11-23 14:40:23 +0000
+++ GTG/tests/test_syncmeme.py	1970-01-01 00:00:00 +0000
@@ -1,55 +0,0 @@
-# -*- coding: utf-8 -*-
-# -----------------------------------------------------------------------------
-# Getting Things GNOME! - a personal organizer for the GNOME desktop
-# Copyright (c) 2008-2013 - Lionel Dricot & Bertrand Rousseau
-#
-# This program is free software: you can redistribute it and/or modify it under
-# the terms of the GNU General Public License as published by the Free Software
-# Foundation, either version 3 of the License, or (at your option) any later
-# version.
-#
-# This program is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
-# details.
-#
-# You should have received a copy of the GNU General Public License along with
-# this program.  If not, see <http://www.gnu.org/licenses/>.
-# -----------------------------------------------------------------------------
-
-""" Tests for the SyncMeme class """
-
-import unittest
-import datetime
-
-from GTG.backends.syncengine import SyncMeme
-
-
-class TestSyncMeme(unittest.TestCase):
-    """ Tests for the SyncEngine object. """
-
-    def test_which_is_newest(self):
-        """ test the which_is_newest function """
-        meme = SyncMeme()
-        # tasks have not changed
-        local_modified = datetime.datetime.now()
-        remote_modified = datetime.datetime.now()
-        meme.set_local_last_modified(local_modified)
-        meme.set_remote_last_modified(remote_modified)
-        self.assertEqual(
-            meme.which_is_newest(local_modified, remote_modified),
-            None)
-        # we update the local
-        local_modified = datetime.datetime.now()
-        self.assertEqual(
-            meme.which_is_newest(local_modified, remote_modified),
-            'local')
-        # we update the remote
-        remote_modified = datetime.datetime.now()
-        self.assertEqual(
-            meme.which_is_newest(local_modified, remote_modified),
-            'remote')
-
-
-def test_suite():
-    return unittest.TestLoader().loadTestsFromTestCase(TestSyncMeme)

=== removed file 'GTG/tests/test_tool_tags.py'
--- GTG/tests/test_tool_tags.py	2013-11-23 14:40:23 +0000
+++ GTG/tests/test_tool_tags.py	1970-01-01 00:00:00 +0000
@@ -1,82 +0,0 @@
-# -*- coding: utf-8 -*-
-# -----------------------------------------------------------------------------
-# Getting Things GNOME! - a personal organizer for the GNOME desktop
-# Copyright (c) 2008-2013 - Lionel Dricot & Bertrand Rousseau
-#
-# This program is free software: you can redistribute it and/or modify it under
-# the terms of the GNU General Public License as published by the Free Software
-# Foundation, either version 3 of the License, or (at your option) any later
-# version.
-#
-# This program is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
-# details.
-#
-# You should have received a copy of the GNU General Public License along with
-# this program.  If not, see <http://www.gnu.org/licenses/>.
-# -----------------------------------------------------------------------------
-""" Tests for the tags utilities """
-
-import unittest
-
-from GTG.tools.tags import extract_tags_from_text
-from GTG.tools.tags import parse_tag_list
-
-
-class TestToolTags(unittest.TestCase):
-    """ Tests for the tags utilities """
-
-    def test_extract_tags_from_text(self):
-        """ Test for extracting tags from a string """
-        tests = (
-            ("@mamma mia", ["@mamma"]),
-            ("vive le @roy", ["@roy"]),
-            ("hey @mr. jack!", ["@mr"]),
-            ("no @emails allowed: invernizzi.l@xxxxxxxxx", ["@emails"]),
-            ("and no @@diff stuff", []),
-            ("@we @do @love @tags!", ["@we", "@do", "@love", "@tags"]),
-        )
-        for text, tags in tests:
-            self.assertEqual(extract_tags_from_text(text), tags)
-
-    def test_parse_tag_list(self):
-        """ Test parsing tag list"""
-        ptl = parse_tag_list
-
-        self.assertEqual(ptl("tag"), [("@tag", True)])
-        self.assertEqual(ptl("@tag"), [("@tag", True)])
-
-        self.assertEqual(ptl("!tag"), [("@tag", False)])
-        self.assertEqual(ptl("!@tag"), [("@tag", False)])
-
-        self.assertEqual(ptl("a b c"),
-                         [("@a", True), ("@b", True), ("@c", True)])
-        self.assertEqual(ptl("a @b c"),
-                         [("@a", True), ("@b", True), ("@c", True)])
-        self.assertEqual(ptl("@a b @c"),
-                         [("@a", True), ("@b", True), ("@c", True)])
-        self.assertEqual(ptl("@a @b @c"),
-                         [("@a", True), ("@b", True), ("@c", True)])
-
-        self.assertEqual(ptl("!a !b !c"),
-                         [("@a", False), ("@b", False), ("@c", False)])
-        self.assertEqual(ptl("!a !@b !c"),
-                         [("@a", False), ("@b", False), ("@c", False)])
-        self.assertEqual(ptl("!@a !b !@c"),
-                         [("@a", False), ("@b", False), ("@c", False)])
-        self.assertEqual(ptl("!@a !@b !@c"),
-                         [("@a", False), ("@b", False), ("@c", False)])
-
-        self.assertEqual(ptl("add !remove"),
-                         [("@add", True), ("@remove", False)])
-        self.assertEqual(ptl("@add !@remove"),
-                         [("@add", True), ("@remove", False)])
-        self.assertEqual(ptl("!remove add"),
-                         [("@remove", False), ("@add", True)])
-        self.assertEqual(ptl("!@remove @add"),
-                         [("@remove", False), ("@add", True)])
-
-
-def test_suite():
-    return unittest.TestLoader().loadTestsFromTestCase(TestToolTags)

=== removed file 'GTG/tests/test_tools_tags.py'
--- GTG/tests/test_tools_tags.py	2013-11-23 14:40:23 +0000
+++ GTG/tests/test_tools_tags.py	1970-01-01 00:00:00 +0000
@@ -1,70 +0,0 @@
-# -*- coding: utf-8 -*-
-# -----------------------------------------------------------------------------
-# Getting Things GNOME! - a personal organizer for the GNOME desktop
-# Copyright (c) 2008-2013 - Lionel Dricot & Bertrand Rousseau
-#
-# This program is free software: you can redistribute it and/or modify it under
-# the terms of the GNU General Public License as published by the Free Software
-# Foundation, either version 3 of the License, or (at your option) any later
-# version.
-#
-# This program is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
-# details.
-#
-# You should have received a copy of the GNU General Public License along with
-# this program.  If not, see <http://www.gnu.org/licenses/>.
-# -----------------------------------------------------------------------------
-
-""" Tests for extract_tags_from_text """
-
-import unittest
-from GTG.tools.tags import extract_tags_from_text as tags
-
-
-class TestExtractTags(unittest.TestCase):
-
-    def test_empty(self):
-        self.assertEqual(tags(""), [])
-
-    def test_tag_at_beginning(self):
-        self.assertEqual(tags("@tag some other text"), ["@tag"])
-
-    def test_tag_at_end(self):
-        self.assertEqual(tags("some text ended with @endtag"), ["@endtag"])
-
-    def test_hypen_in_tag(self):
-        self.assertEqual(
-            tags("@tag, @my-tag, bla bla @do-this-today,\
-                 it has @con--tinuous---hypen-s-"),
-            ["@tag", "@my-tag", "@do-this-today", "@con--tinuous---hypen-s"])
-
-        self.assertEqual(tags("@hypen-at-end- some other text"),
-                         ["@hypen-at-end"])
-        self.assertEqual(tags("@hypen-at-end-, with comma"), ["@hypen-at-end"]
-                         )
-
-    def test_dot(self):
-        self.assertEqual(tags("text @gtg-0.3"), ["@gtg-0.3"])
-        self.assertEqual(
-            tags("@tag., @my.tag, bla bla @do.this.today,\
-                 also contains @hy-pen-.s"),
-            ["@tag", "@my.tag", "@do.this.today", "@hy-pen-.s"])
-
-    def test_slash(self):
-        self.assertEqual(
-            tags("@tag/, @my/tag, bla bla @do/this/today/,\
-                 @hy-p-/ens with @slash/es/"),
-            ["@tag", "@my/tag", "@do/this/today", "@hy-p-/ens", "@slash/es"])
-
-    def test_colon(self):
-        self.assertEqual(
-            tags("@tag:, @my:tag, bla bla @do:this:today:, @co:l-on/s-,\
-                 @:dot/s:, with @com,mas"),
-            ["@tag", "@my:tag", "@do:this:today", "@co:l-on/s", "@:dot/s",
-             "@com"])
-
-
-def test_suite():
-    return unittest.TestLoader().loadTestsFromName(__name__)

=== removed file 'GTG/tests/test_twokeydict.py'
--- GTG/tests/test_twokeydict.py	2013-11-25 02:37:46 +0000
+++ GTG/tests/test_twokeydict.py	1970-01-01 00:00:00 +0000
@@ -1,98 +0,0 @@
-# -*- coding: utf-8 -*-
-# -----------------------------------------------------------------------------
-# Getting Things GNOME! - a personal organizer for the GNOME desktop
-# Copyright (c) 2008-2013 - Lionel Dricot & Bertrand Rousseau
-#
-# This program is free software: you can redistribute it and/or modify it under
-# the terms of the GNU General Public License as published by the Free Software
-# Foundation, either version 3 of the License, or (at your option) any later
-# version.
-#
-# This program is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
-# details.
-#
-# You should have received a copy of the GNU General Public License along with
-# this program.  If not, see <http://www.gnu.org/licenses/>.
-# -----------------------------------------------------------------------------
-
-""" Tests for the TwoKeyDict class """
-
-import unittest
-import uuid
-
-from GTG.tools.twokeydict import TwoKeyDict
-
-
-class TestTwoKeyDict(unittest.TestCase):
-    """ Tests for the TwoKeyDict object. """
-
-    def test_add_and_gets(self):
-        """ Test for the __init__, _get_by_first, _get_by_second function """
-        triplets = [(uuid.uuid4(), uuid.uuid4(), uuid.uuid4())
-                    for a in range(10)]
-        tw_dict = TwoKeyDict(*triplets)
-        for triplet in triplets:
-            self.assertEqual(tw_dict._get_by_primary(triplet[0]), triplet[2])
-            self.assertEqual(tw_dict._get_by_secondary(triplet[1]), triplet[2])
-
-    def test_remove_by_first_or_second(self):
-        """ Test for removing triplets form the TwoKeyDict """
-        triplet_first = (1, 'I', 'one')
-        triplet_second = (2, 'II', 'two')
-        tw_dict = TwoKeyDict(triplet_first, triplet_second)
-        tw_dict._remove_by_primary(triplet_first[0])
-        tw_dict._remove_by_secondary(triplet_second[1])
-        missing_first = 0
-        missing_second = 0
-        try:
-            tw_dict._get_by_primary(triplet_first[0])
-        except KeyError:
-            missing_first += 1
-        try:
-            tw_dict._get_by_secondary(triplet_second[0])
-        except KeyError:
-            missing_first += 1
-        try:
-            tw_dict._get_by_secondary(triplet_first[1])
-        except KeyError:
-            missing_second += 1
-        try:
-            tw_dict._get_by_secondary(triplet_second[1])
-        except KeyError:
-            missing_second += 1
-        self.assertEqual(missing_first, 2)
-        self.assertEqual(missing_second, 2)
-        # check for memory leaks
-        dict_len = 0
-        for key in tw_dict._primary_to_value.keys():
-            dict_len += 1
-        self.assertEqual(dict_len, 0)
-
-    def test_get_primary_and_secondary_key(self):
-        """ Test for fetching the objects stored in the TwoKeyDict """
-        triplets = [(uuid.uuid4(), uuid.uuid4(), uuid.uuid4())
-                    for a in range(10)]
-        tw_dict = TwoKeyDict(*triplets)
-        for triplet in triplets:
-            self.assertEqual(tw_dict._get_secondary_key(triplet[0]),
-                             triplet[1])
-            self.assertEqual(tw_dict._get_primary_key(triplet[1]),
-                             triplet[0])
-
-    def test_missing_and_then_add(self):
-        # Primary
-        local_id = '3ea957cb-417f-4944-be6c-02a1b0a84bd2'
-        # Secondary
-        remote_id = 'https://api.launchpad.net/1.0/bugs/345808'
-        value = "Hello world"
-
-        tw_dict = TwoKeyDict()
-        self.assertRaises(KeyError, tw_dict._get_secondary_key, remote_id)
-        tw_dict.add((local_id, remote_id, value))
-        self.assertEqual(remote_id, tw_dict._get_secondary_key(local_id))
-
-
-def test_suite():
-    return unittest.TestLoader().loadTestsFromTestCase(TestTwoKeyDict)

=== removed file 'GTG/tests/tree_testing.py'
--- GTG/tests/tree_testing.py	2013-11-25 02:37:46 +0000
+++ GTG/tests/tree_testing.py	1970-01-01 00:00:00 +0000
@@ -1,230 +0,0 @@
-# -*- coding: utf-8 -*-
-# -----------------------------------------------------------------------------
-# Getting Things GNOME! - a personal organizer for the GNOME desktop
-# Copyright (c) 2008-2013 - Lionel Dricot & Bertrand Rousseau
-#
-# This program is free software: you can redistribute it and/or modify it under
-# the terms of the GNU General Public License as published by the Free Software
-# Foundation, either version 3 of the License, or (at your option) any later
-# version.
-#
-# This program is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
-# details.
-#
-# You should have received a copy of the GNU General Public License along with
-# this program.  If not, see <http://www.gnu.org/licenses/>.
-# -----------------------------------------------------------------------------
-
-# If True, the TreeTester will automatically reorder node on the same level
-# as a deleted node. If False, it means that Liblarch has the responsability
-# to handle that itself.
-REORDER_ON_DELETE = False
-
-
-class TreeTester:
-    """ A class that will check if a tree implementation is consistent
-    by connecting to emitted signals and crashing on any problem """
-
-    def __init__(self, viewtree):
-        self.tree = viewtree
-        # both dict should always be synchronized
-        # They are the internal representation of the tree,
-        # based only on received signals
-        self.nodes = {}
-        self.paths = {}
-        self.tree.register_cllbck('node-added-inview', self.add)
-        self.tree.register_cllbck('node-deleted-inview', self.delete)
-        self.tree.register_cllbck('node-modified-inview', self.update)
-        self.tree.register_cllbck('node-children-reordered', self.reordered)
-        self.trace = "* * * * * * * *\n"
-
-    def add(self, nid, path):
-        self.trace += "adding %s to path %s\n" % (nid, str(path))
-        currentnode = self.paths.get(path, None)
-        if currentnode and currentnode != nid:
-            raise Exception('path %s is already occupied by %s' % (
-                str(path), nid))
-        if nid in self.nodes:
-            node = self.nodes[nid]
-        else:
-            node = []
-            self.nodes[nid] = node
-        if path not in node:
-            node.append(path)
-        self.paths[path] = nid
-
-    def delete(self, nid, path):
-        self.trace += "removing %s from path %s\n" % (nid, str(path))
-        if nid != self.paths.get(path, None):
-            error = '%s is not assigned to path %s\n' % (nid, str(path))
-            error += self.print_tree()
-            raise Exception(error)
-        if path not in self.nodes.get(nid, []):
-            raise Exception('%s is not a path of node %s' % (str(path), nid))
-        if REORDER_ON_DELETE:
-            index = path[-1:]
-            print("reorder on delete not yet implemented")
-        self.nodes[nid].remove(path)
-        if len(self.nodes[nid]) == 0:
-            self.nodes.pop(nid)
-
-        self.paths.pop(path)
-
-        # Move other paths lower like in real TreeModel
-        path_prefix = path[:-1]
-        index = path[-1]
-
-        assert path_prefix + (index, ) == path, "%s vs %s" % (
-            path_prefix + (index, ), path)
-
-        def check_prefix(path):
-            """ Is this path affected by the change?
-            Conditions:
-              * the same prefix
-                (3, 1, 2, 3) vs (3,1,2,4) OK
-                (3, 1, 2, 3) vs (3,1,2,4,0) OK
-                (3, 1, 2, 3) vs (3,2,2,4) FALSE
-              * higher index
-                (3, 1, 2, 3) vs (3,1,2,2) FALSE
-            """
-            if len(path) <= len(path_prefix):
-                return False
-
-            for i, pos in enumerate(path_prefix):
-                if path[i] != pos:
-                    return False
-
-            return path[len(path_prefix)] > index
-
-        paths = list(self.paths.keys())
-        paths.sort()
-
-        for path in paths:
-            old_path = path
-            if check_prefix(path) and len(path_prefix) > 1:
-                new_path = list(path)
-                print("new_path: %s" % str(new_path))
-                index = len(path_prefix)
-                new_path[index] = str(int(new_path[index]) - 1)
-                new_path = tuple(new_path)
-
-                print("new_path: %s" % str(new_path))
-                print("self.paths: %s" % str(self.paths))
-
-                assert new_path not in self.paths
-
-                nid = self.paths[old_path]
-                self.nodes[nid].remove(old_path)
-                del self.paths[old_path]
-                self.nodes[nid].append(new_path)
-                self.paths[new_path] = nid
-
-    def update(self, nid, path):
-##        self.tree.flush()
-#        self.trace += "updating %s in path %s\n" %(nid, str(path))
-#        error = "updating node %s for path %s\n" %(nid, str(path))
-#        if not self.nodes.has_key(nid):
-#            error += "%s is not in nodes !\n" %nid
-#            error += self.print_tree()
-#            raise Exception(error)
-#        #Nothing to do, we just update.
-#        for p in self.nodes[nid]:
-#            if self.paths[p] != nid:
-#                raise Exception('Mismatching path for %s'%nid)
-#        if not self.paths.has_key(path):
-#            error += '%s is not in stored paths (node %s)\n'%(str(path),nid)
-#            error += self.print_tree()
-#            raise Exception(error)
-#        n = self.paths[path]
-#        if path not in self.nodes[n] or n != nid:
-#            raise Exception('Mismatching node for path %s'%str(p))
-
-        # Because of the asynchronousness of update, this test
-        # doesn't work anymore
-        pass
-
-    def reordered(self, nid, path, neworder):
-        print("reordering")
-        self.trace += "reordering children of %s (%s) : %s\n" % (nid,
-                                                                 str(path),
-                                                                 neworder)
-        self.trace += "VR is %s\n" % self.tree.node_all_children()
-        if not path:
-            path = ()
-        i = 0
-        newpaths = {}
-        toremove = []
-        # we first update self.nodes with the new paths
-        while i < len(neworder):
-            if i != neworder[i]:
-                old = neworder[i]
-                oldp = path + (old, )
-                newp = path + (i, )
-                le = len(newp)
-                for pp in list(self.paths.keys()):
-                    if pp[0:le] == oldp:
-                        n = self.paths[pp]
-                        self.nodes[n].remove(pp)
-                        newpp = newp + pp[le:]
-                        self.nodes[n].append(newpp)
-                        self.trace += "    change %s path from %s to %s\n" % (
-                            n, pp, newpp)
-                        newpaths[newpp] = n
-                        toremove.append(pp)
-            i += 1
-        # now we can update self.paths
-        for p in toremove:
-            self.paths.pop(p)
-        for p in newpaths:
-            self.trace += "    adding %s to paths %s\n" % (newpaths[p], str(p))
-            self.paths[p] = newpaths[p]
-
-    def test_validity(self):
-        for n in list(self.nodes.keys()):
-            paths = self.tree.get_paths_for_node(n)
-            if len(self.nodes[n]) == 0:
-                raise Exception('Node %s is stored without any path' % n)
-            for p in self.nodes[n]:
-                if self.paths[p] != n:
-                    raise Exception('Mismatching path for %s' % n)
-                if p not in paths:
-                    error = 'we have a unknown stored path for %s\n' % n
-                    nn = self.tree.get_node_for_path(p)
-                    parent = self.tree.get_node_for_path(p[:-1])
-                    error += '  path %s is the path of %s\n' % (
-                        str(p), str(nn))
-                    error += '  parent is %s' % parent
-#                    error += self.trace
-                    raise Exception(error)
-                paths.remove(p)
-            if len(paths) > 0:
-                raise Exception('why is this path existing for %s' % n)
-        for p in list(self.paths.keys()):
-            node = self.tree.get_node_for_path(p)
-            n = self.paths[p]
-            if n != node:
-                error = 'Node for path is %s but should be %s' % (node, n)
-                raise Exception(error)
-            if p not in self.nodes[n]:
-                error = 'Mismatching node for path %s\n' % str(p)
-                error += self.print_tree()
-                raise Exception(error)
-            if len(p) == 1 and len(self.nodes[n]) > 1:
-                error = 'Node %s has multiple paths and is in the VR\n' % n
-                error += self.print_tree()
-                raise Exception(error)
-        return True
-
-    def print_tree(self):
-        st = self.trace
-        st += "nodes are %s\n" % self.nodes
-        st += "paths are %s\n" % self.paths
-        return st
-
-    def quit(self):
-        self.tree.deregister_cllbck('node-added-inview', self.add)
-        self.tree.deregister_cllbck('node-deleted-inview', self.delete)
-        self.tree.deregister_cllbck('node-modified-inview', self.update)
-        self.tree.deregister_cllbck('node-children-reordered', self.reordered)

=== removed directory 'GTG/tests/xdg'
=== removed directory 'GTG/tests/xdg/config'
=== removed directory 'GTG/tests/xdg/config/gtg'
=== removed file 'GTG/tests/xdg/xdg.txt'
--- GTG/tests/xdg/xdg.txt	2012-05-23 08:55:31 +0000
+++ GTG/tests/xdg/xdg.txt	1970-01-01 00:00:00 +0000
@@ -1,6 +0,0 @@
-This folder does not contain any code.
-
-This folder is intented to contain a typical configuration example using for debugging purpose (by launching debug.sh).
-
-For informations on the XDG folders, see :
-http://standards.freedesktop.org/basedir-spec/basedir-spec-0.6.html

=== removed file 'GTG/tools/testingmode.py'
--- GTG/tools/testingmode.py	2013-11-23 14:40:23 +0000
+++ GTG/tools/testingmode.py	1970-01-01 00:00:00 +0000
@@ -1,32 +0,0 @@
-# -*- coding: utf-8 -*-
-# -----------------------------------------------------------------------------
-# Getting Things GNOME! - a personal organizer for the GNOME desktop
-# Copyright (c) 2008-2013 - Lionel Dricot & Bertrand Rousseau
-#
-# This program is free software: you can redistribute it and/or modify it under
-# the terms of the GNU General Public License as published by the Free Software
-# Foundation, either version 3 of the License, or (at your option) any later
-# version.
-#
-# This program is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
-# details.
-#
-# You should have received a copy of the GNU General Public License along with
-# this program.  If not, see <http://www.gnu.org/licenses/>.
-# -----------------------------------------------------------------------------
-
-from GTG.tools.borg import Borg
-
-
-class TestingMode(Borg):
-
-    def set_testing_mode(self, value):
-        self._testing_mode = value
-
-    def get_testing_mode(self):
-        try:
-            return self._testing_mode
-        except:
-            return False

=== modified file 'HACKING'
--- HACKING	2013-11-23 14:40:23 +0000
+++ HACKING	2014-03-09 17:00:11 +0000
@@ -161,7 +161,7 @@
 # -*- coding: utf-8 -*-
 # -----------------------------------------------------------------------------
 # Gettings Things Gnome! - a personal organizer for the GNOME desktop
-# Copyright (c) 2008-2013 - Lionel Dricot & Bertrand Rousseau
+# Copyright (c) 2008-2014 - Lionel Dricot & Bertrand Rousseau
 #
 # 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
@@ -202,40 +202,3 @@
   6. Update ``AUTHORS`` if the patch author is not already in there.
 
   7. Commit your changes and propose a merge request.
-
-
-Documentation
--------------
-
-We'd love it so much if you could improve the docstrings that you find in GTG.
-
-We use the docstring conventions outlined in PEP 8
-<http://www.python.org/dev/peps/pep-0008/> and PEP 257
-<http://www.python.org/dev/peps/pep-0257/>, except modified to support epytext
-markup. Information on epytext markup can be found at
-<http://epydoc.sourceforge.net/epytext.html>.
-
-You can generate the API docs by installing pydoctor
-(``python-pydoctor`` in debian/ubuntu) and then running::
-
-  make apidocs
-
-This builds the API documentation inside ``doc/api`` in your working tree. If
-you are running GNOME, you can open it in your browser with::
-
-  gnome-open doc/api/index.html
-
-You can also run::
-
-  make edit-apidocs
-
-To launch a pydoctor server at http://localhost:8080/ that will let you edit
-API documentation in place. The feature has a few rough edges, but can be
-really useful for editing and extending API docs. To actually apply your edits
-to the tree::
-
-  wget http://localhost:8080/rawBigDiff -q -O- | bzr patch
-
-Remember, docstrings should be written for people who don't know much about
-the code. Focus on ''why'' things are the way they are, and on describing
-''what'' they are in the first place.

=== modified file 'Makefile'
--- Makefile	2014-02-01 14:47:33 +0000
+++ Makefile	2014-03-09 17:00:11 +0000
@@ -18,21 +18,22 @@
 
 PEP8=pep8
 PYFLAKES=pyflakes
-PYDOCTOR=pydoctor
 
 check: tests pep8 pyflakes
 
+install:
+	pip3 install -r requirements.txt
+
 # Run all of the tests.
 tests:
 	./run-tests
 
-# Get rid of stale files or files made during testing.
+# Remove all temporary files
 clean:
 	rm -rf tmp
-	rm -rf doc/api
-	find . -name '*.pyc' -print0 | xargs -0 rm -f
-	find . -name '*~' -print0 | xargs -0 rm -f
-	find . -name '.*.swp' -print0 | xargs -0 rm -f
+	find -type f -name '*~' -or -name '.*.sw*' -print | xargs rm -f
+	find -type f -name '*.pyc' -print | xargs rm -f
+	find -type d -name '__pycache__' -print | xargs rm -rf
 
 # Check for common & easily catchable Python mistakes.
 pyflakes:
@@ -42,19 +43,7 @@
 pep8:
 	$(PEP8) --statistics --count gtg gtcli gtg_new_task GTG
 
-# Build API documentation.
-apidocs:
-	$(PYDOCTOR) --add-package GTG --make-html --html-output=doc/api \
-		--project-name=GTG --project-url=http://gtg.fritalk.com/
-
-edit-apidocs:
-	$(PYDOCTOR) --add-package GTG --make-html --html-output=doc/api \
-		--project-name=GTG --project-url=http://gtg.fritalk.com/ \
-		--verbose-about=epydoc2stan2 --verbose-about=epydoc2stan2 \
-		--verbose-about=server --verbose-about=server --local-only \
-		--server --edit
-
 # Check for coding standard violations & flakes.
 lint: pyflakes pep8
 
-.PHONY: tests check lint pyflakes pep8 apidocs edit-apidocs clean
+.PHONY: install tests check lint pyflakes pep8 clean

=== modified file 'README'
--- README	2013-08-17 06:27:09 +0000
+++ README	2014-03-09 17:00:11 +0000
@@ -20,8 +20,6 @@
  * python-liblarch 
  * yelp (to read GTG documentation)
 
-To generate the API documentation, you'll also need to install 'pydoctor'.
-
 Please refer to your system documentation for information on how to install
 these modules if they're not currently available.
 
@@ -30,9 +28,6 @@
     $ sudo apt-get install python-support python-gtk2 python-gnome2 \
          python-glade2 python-xdg python-dbus python-liblarch yelp
 
-To enable API documentation generation, execute this command:
-    $ sudo apt-get install python-pydoctor
-
 There are additional plugins (modules for extending the user interface) and
 synchronization services (modules for importing/exporting tasks from/to
 external services) which needs additional packages to work correctly.

=== renamed directory 'test/data' => 'data/test-data'
=== added file 'requirements.txt'
--- requirements.txt	1970-01-01 00:00:00 +0000
+++ requirements.txt	2014-03-09 17:00:11 +0000
@@ -0,0 +1,1 @@
+nose

=== modified file 'run-tests'
--- run-tests	2013-11-25 02:37:46 +0000
+++ run-tests	2014-03-09 17:00:11 +0000
@@ -2,7 +2,7 @@
 # -*- coding: utf-8 -*-
 # -----------------------------------------------------------------------------
 # Getting Things GNOME! - a personal organizer for the GNOME desktop
-# Copyright (c) 2008-2013 - Lionel Dricot & Bertrand Rousseau
+# Copyright (c) 2008-2014 - Lionel Dricot & Bertrand Rousseau
 #
 # 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
@@ -18,81 +18,13 @@
 # this program.  If not, see <http://www.gnu.org/licenses/>.
 # -----------------------------------------------------------------------------
 
-"""Runs the GTG unit tests."""
-
 import sys
-import unittest
-
-from GTG.tests import test_suite
-from GTG.tools.testingmode import TestingMode
-from GTG.tools.import_liblarch import import_liblarch
-
-TEST_MODULE_PREFIX = "GTG.tests."
-
-def main(args):
-    runner = unittest.TextTestRunner(
-                    stream = sys.stdout,
-                    descriptions = True,
-                    verbosity = 1)
-    if "-h" in args:
-        print("USAGE:")
-        print("./run_tests  to run all tests")
-        print("./run_tests  [module_name ..] to run tests from the listed " + \
-              "modules")
-        print("./run_tests  [function_path ..] to run the selected " + \
-              "functions as tests")
-        return
-    if "-l" in args:
-        args.remove("-l")
-    # fake modules that mimic the behaviour of external libraries ...
-    TestingMode().set_testing_mode(True)
-    if args:
-        #if we have specified the name of the test in the command line
-        suites = []
-        for arg in args:
-            #each arg can be a module name (as test_liblarch), or a module name
-            # "dot" a function name (as test_liblarch.test_something)
-            arg_content = arg.split(".")
-            module_name = arg_content[0]
-            #load the module
-            module_path = TEST_MODULE_PREFIX + module_name
-            module = __import__(module_path)
-            sys.modules[module_path] = module
-            globals()[module_path] = module
-            tests = getattr(module, "tests")
-            a_test = getattr(tests, module_name)
-            if len(arg_content) > 1:
-                path = TEST_MODULE_PREFIX + arg
-                suites.append(unittest.TestLoader().loadTestsFromName(path))
-                #NOTE: this can be done easily with pattern matching with 
-                #      unittest.TestLoader().discover(..), but that's only from 
-                #      python 2.7 on. 
-            else:
-                suites.append(getattr(a_test, "test_suite")())
-    else:
-        #otherwise, run all tests
-        suites = [test_suite()]
-
-    #run the tests
-    tests_are_successful = True
-    for suite in suites:
-        result = runner.run(suite)
-        tests_are_successful &= result.wasSuccessful()
-
-    #show the aggregated result of all the tests
-    if tests_are_successful:
-        print("ALL TESTS ARE SUCCESSFUL")
-        return 0
-    else:
-        print("""
-        ****************************************
-        SOME TESTS *FAILED*!
-        ****************************************
-        """)
-        return 1
-
-if __name__ == '__main__':
-    use_local = "-l" in sys.argv[1:] or "--local-liblarch" in sys.argv[1:]
-
-    if import_liblarch(use_local):
-        sys.exit(main(sys.argv[1:]))
+import nose
+
+if __name__ == "__main__":
+    # By default, use spec plugin on tests folder
+    if len(sys.argv) == 1:
+        sys.argv.append('tests')
+        sys.argv.append('--with-specplugin')
+
+    nose.main()

=== modified file 'scripts/debug.sh'
--- scripts/debug.sh	2012-06-07 14:00:34 +0000
+++ scripts/debug.sh	2014-03-09 17:00:11 +0000
@@ -37,7 +37,7 @@
 if [  $dataset != "default" -a ! -d "./tmp/$dataset" ]
 then
     echo "Copying $dataset dataset to ./tmp/"
-    cp -r test/data/$dataset tmp/
+    cp -r data/test-data/$dataset tmp/
 fi
 
 echo "Setting XDG vars to use $dataset dataset."
@@ -73,7 +73,6 @@
         python -m cProfile -o gtg.prof ./gtg $args -t "$title"
         python ./scripts/profile_interpret.sh
     else
-	./gtg $args -t "$title"
+    ./gtg $args -t "$title"
     fi
 fi
-

=== modified file 'scripts/generate_mofile.sh'
--- scripts/generate_mofile.sh	2011-11-28 21:43:47 +0000
+++ scripts/generate_mofile.sh	2014-03-09 17:00:11 +0000
@@ -3,6 +3,6 @@
 LANGUAGES=$(ls po/*.po | sed 's/po\/\(.*\).po/\1/')
 
 for i in $LANGUAGES; do
-	mkdir po/$i/LC_MESSAGES/ --parents
-	msgfmt po/$i.po --output-file=po/$i/LC_MESSAGES/gtg.mo
+    mkdir po/$i/LC_MESSAGES/ --parents
+    msgfmt po/$i.po --output-file=po/$i/LC_MESSAGES/gtg.mo
 done

=== renamed directory 'test' => 'tests'
=== renamed file 'GTG/tests/__init__.py' => 'tests/__init__.py'
--- GTG/tests/__init__.py	2014-03-09 12:59:59 +0000
+++ tests/__init__.py	2014-03-09 17:00:11 +0000
@@ -1,3 +1,4 @@
+<<<<<<< TREE
 # -*- coding: utf-8 -*-
 # -----------------------------------------------------------------------------
 # Getting Things GNOME! - a personal organizer for the GNOME desktop
@@ -58,3 +59,5 @@
             test_suite.addTest(getattr(a_test, "test_suite")())
 
     return test_suite
+=======
+>>>>>>> MERGE-SOURCE

=== added directory 'tests/core'
=== added file 'tests/core/__init__.py'
=== renamed file 'GTG/tests/test_search_filter.py' => 'tests/core/test_search_filter.py'
--- GTG/tests/test_search_filter.py	2013-11-23 14:40:23 +0000
+++ tests/core/test_search_filter.py	2014-03-09 17:00:11 +0000
@@ -17,16 +17,15 @@
 # this program.  If not, see <http://www.gnu.org/licenses/>.
 # -----------------------------------------------------------------------------
 
-""" Tests for search filter """
+from unittest import TestCase
 
-import unittest
 from GTG.core.search import search_filter
 from GTG.tools.dates import Date
 
 d = Date.parse
 
 
-class FakeTask:
+class FakeTask(object):
 
     def __init__(self, title="", body="", tags=[], due_date=""):
         self.title = title
@@ -47,7 +46,7 @@
         return self.due_date
 
 
-class TestSearchFilter(unittest.TestCase):
+class TestSearchFilter(TestCase):
 
     def test_empty(self):
         self.assertFalse(search_filter(FakeTask()))
@@ -207,7 +206,3 @@
                                       {'q': [("soon", True)]}))
         self.assertTrue(search_filter(FakeTask(due_date="someday"),
                                       {'q': [("someday", True)]}))
-
-
-def test_suite():
-    return unittest.TestLoader().loadTestsFromName(__name__)

=== renamed file 'GTG/tests/test_search_query.py' => 'tests/core/test_search_query.py'
--- GTG/tests/test_search_query.py	2014-03-09 12:59:59 +0000
+++ tests/core/test_search_query.py	2014-03-09 17:00:11 +0000
@@ -17,9 +17,7 @@
 # this program.  If not, see <http://www.gnu.org/licenses/>.
 # -----------------------------------------------------------------------------
 
-""" Tests for parsing searching query """
-
-import unittest
+from unittest import TestCase
 from GTG.core.search import parse_search_query, InvalidQuery
 from GTG.tools.dates import Date
 
@@ -27,7 +25,7 @@
 d = Date.parse
 
 
-class TestSearchQuery(unittest.TestCase):
+class TestSearchQuery(TestCase):
 
     def test_word_query(self):
         self.assertEqual(parse("query"),
@@ -222,7 +220,3 @@
                          {'q': [('today', False)]})
         self.assertEqual(parse('word !today'),
                          {'q': [('word', True, 'word'), ('today', True)]})
-
-
-def test_suite():
-    return unittest.TestLoader().loadTestsFromName(__name__)

=== renamed file 'GTG/tests/test_tag.py' => 'tests/core/test_tag.py'
--- GTG/tests/test_tag.py	2013-11-23 14:40:23 +0000
+++ tests/core/test_tag.py	2014-03-09 17:00:11 +0000
@@ -17,134 +17,53 @@
 # this program.  If not, see <http://www.gnu.org/licenses/>.
 # -----------------------------------------------------------------------------
 
-"""Tests for the tags"""
-
-import unittest
-
-from GTG.core.tag import Tag, Set_Name_Attribute_Error
-from GTG.core.datastore import DataStore
-
-from GTG.tests.signals_testing import GobjectSignalsManager
-
-
-class TestTag(unittest.TestCase):
-    """Tests for `Tag`."""
-
+from unittest import TestCase
+
+from GTG.core.tag import Tag
+
+
+class TestTag(TestCase):
     def setUp(self):
-        ds = DataStore()
-        self.req = ds.get_requester()
-        # initalize gobject signaling system
-        self.gobject_signal_manager = GobjectSignalsManager()
-        self.gobject_signal_manager.init_signals()
-        # refresh the viewtree for tasks
-        tt = self.req.get_tasks_tree()
-        tt.reset_filters()
-
-    def tearDown(self):
-#        finally:
-        # stopping gobject main loop
-        self.gobject_signal_manager.terminate_signals()
-
-    def test_name(self):
-        # The first argument to the Tag constructor is the name, which you can
-        # get with get_name().
-        tag = Tag('foo', self.req)
-        self.assertEqual('foo', tag.get_name())
+        self.tag = Tag('foo', None)
+        # Do not allow notifying related tasks
+        self.tag.notify_related_tasks = lambda: None
+
+    def test_has_name(self):
+        self.assertEqual('foo', self.tag.get_name())
 
     def test_name_is_attribute(self):
-        # The name of the tag is also stored as an attribute.
-        tag = Tag('foo', self.req)
-        self.assertEqual('foo', tag.get_attribute('name'))
+        self.assertEqual('foo', self.tag.get_attribute('name'))
 
     def test_missing_attribute_returns_none(self):
-        # If get_attribute is called for an attribute that doesn't exist, it
-        # returns None.
-        tag = Tag('whatever', self.req)
-        result = tag.get_attribute('no-such-attribute')
-        self.assertEqual(None, result)
+        self.assertEqual(None, self.tag.get_attribute('no-such-attribute'))
 
     def test_set_then_get_attribute(self):
-        # Attributes set with set_attribute can be retrieved with
-        # get_attribute.
-        tag = Tag('whatever', self.req)
-        tag.set_attribute('new-attribute', 'value')
-        result = tag.get_attribute('new-attribute')
-        self.assertEqual('value', result)
+        self.tag.set_attribute('new-attribute', 'value')
+        attr = self.tag.get_attribute('new-attribute')
+        self.assertEqual('value', attr)
 
     def test_set_non_str_attribute_casts_to_string(self):
-        # If the value of the attribute passed to set_attribute is not a
-        # string, it's cast to a string.
-        tag = Tag('whatever', self.req)
-        tag.set_attribute('new-attribute', 42)
-        result = tag.get_attribute('new-attribute')
-        self.assertEqual('42', result)
-
-    def test_get_all_attributes_initial(self):
-        # Initially, a Tag has only the name attribute.
-        tag = Tag('foo', self.req)
-        self.assertEqual(['name'], tag.get_all_attributes())
-
-    def test_get_all_attributes_after_setting(self):
-        # After attributes are set, get_all_attributes includes those
-        # attributes. The order is not guaranteed.
-        tag = Tag('foo', self.req)
-        tag.set_attribute('bar', 'baz')
-        self.assertEqual(set(['name', 'bar']), set(tag.get_all_attributes()))
-
-    def test_get_all_but_name(self):
-        # If 'butname' is True, then exclude the 'name' attribute.
-        tag = Tag('foo', self.req)
-        self.assertEqual([], tag.get_all_attributes(butname=True))
-        tag.set_attribute('bar', 'baz')
-        self.assertEqual(['bar'], tag.get_all_attributes(butname=True))
-
-    def test_str(self):
-        # str(tag) is 'Tag: <name>'
-        tag = Tag('foo', self.req)
-        self.assertEqual('Tag: foo', str(tag))
-
-    def test_set_name_attribute_does_nothing(self):
-        # The 'name' attribute is set by the constructor. After it is set, it
-        # cannot be changed with further calls to set_attribute.
-        tag = Tag('old', self.req)
-        try:
-            tag.set_attribute('name', 'new')
-        except Set_Name_Attribute_Error:
-            pass
-        self.assertEqual('old', tag.get_name())
-        self.assertEqual('old', tag.get_attribute('name'))
-
-    # XXX: The following tests check the current behaviour of the Tag class,
-    # but I'm not sure if they're correct behaviour. -- jml, 2009-07-17
-    def test_save_not_called_on_construction(self):
-        # The save callback isn't called by the constructor, despite the fact
-        # that it sets the name attribute.
-        save_calls = []
-        Tag('old', self.req)
-        self.assertEqual(0, len(save_calls))
-
-    def test_set_name_doesnt_call_save(self):
-        # Setting the name attribute doesn't call save.
-        save_calls = []
-        tag = Tag('old', self.req)
-        try:
-            tag.set_attribute('name', 'new')
-        except Set_Name_Attribute_Error:
-            pass
-        self.assertEqual(0, len(save_calls))
-
-    def test_intask_counting_after_rename(self):
-        '''We test that the task counting for tags work
-        even after tag renaming (stuttering tag bug)'''
-        t = self.req.new_task(tags=['@testtag'])
-        t.modified()
-        tag = self.req.get_tag('@testtag')
-        self.assertEqual(tag.get_active_tasks_count(), 1)
-        t.rename_tag('@testtag', '@test')
-        tag2 = self.req.get_tag('@test')
-        self.assertEqual(tag2.get_active_tasks_count(), 1)
-        self.assertEqual(tag.get_active_tasks_count(), 0)
-
-
-def test_suite():
-    return unittest.TestLoader().loadTestsFromTestCase(TestTag)
+        self.tag.set_attribute('new-attribute', 42)
+        attr = self.tag.get_attribute('new-attribute')
+        self.assertEqual('42', attr)
+
+    def test_initial_attribute_is_name_only(self):
+        self.assertEqual(['name'], self.tag.get_all_attributes())
+
+    def test_can_add_new_attributes(self):
+        self.tag.set_attribute('bar', 'baz')
+        self.assertEqual({'name', 'bar'}, set(self.tag.get_all_attributes()))
+
+    def test_get_all_attributes_but_name(self):
+        self.assertEqual([], self.tag.get_all_attributes(butname=True))
+        self.tag.set_attribute('bar', 'baz')
+        self.assertEqual(['bar'], self.tag.get_all_attributes(butname=True))
+
+    def test_name_cannot_be_changed(self):
+        self.assertEqual('foo', self.tag.get_name())
+
+        with self.assertRaises(KeyError):
+            self.tag.set_attribute('name', 'new')
+
+        self.assertEqual('foo', self.tag.get_name())
+        self.assertEqual('foo', self.tag.get_attribute('name'))

=== added directory 'tests/tools'
=== added file 'tests/tools/__init__.py'
=== renamed file 'GTG/tests/test_dates.py' => 'tests/tools/test_dates.py'
--- GTG/tests/test_dates.py	2013-11-23 14:40:23 +0000
+++ tests/tools/test_dates.py	2014-03-09 17:00:11 +0000
@@ -17,11 +17,7 @@
 # this program.  If not, see <http://www.gnu.org/licenses/>.
 # -----------------------------------------------------------------------------
 
-'''
-Tests for the various Date classes
-'''
-
-import unittest
+from unittest import TestCase
 from datetime import date, timedelta
 
 from GTG import _
@@ -42,19 +38,22 @@
         return aday.replace(day=day, month=aday.month + 1)
 
 
-class TestDates(unittest.TestCase):
-    """ Tests for the various Date classes """
+class TestDates(TestCase):
 
-    def test_parse_dates(self):
-        """ Parse common numeric date """
+    def test_parses_common_formats(self):
         self.assertEqual(str(Date.parse("1985-03-29")), "1985-03-29")
         self.assertEqual(str(Date.parse("19850329")), "1985-03-29")
         self.assertEqual(str(Date.parse("1985/03/29")), "1985-03-29")
 
+    def test_parses_todays_month_day_format(self):
         today = date.today()
         parse_string = "%02d%02d" % (today.month, today.day)
         self.assertEqual(Date.parse(parse_string), today)
 
+    def test_parses_today_as_today(self):
+        today = date.today()
+        self.assertEqual(Date(today), today)
+
     def test_parse_fuzzy_dates(self):
         """ Parse fuzzy dates like now, soon, later, someday """
         self.assertEqual(Date.parse("now"), Date.now())
@@ -129,13 +128,3 @@
                 aday = aday.replace(day=i)
 
             self.assertEqual(Date.parse(str(i)), aday)
-
-    def test_prevent_regression(self):
-        """ A day represented in GTG Date must be still the same """
-        aday = date.today()
-        self.assertEqual(Date(aday), aday)
-
-
-def test_suite():
-    """ Return unittests """
-    return unittest.TestLoader().loadTestsFromTestCase(TestDates)

=== renamed file 'GTG/tests/test_interruptible.py' => 'tests/tools/test_interruptible.py'
--- GTG/tests/test_interruptible.py	2013-11-23 14:40:23 +0000
+++ tests/tools/test_interruptible.py	2014-03-09 17:00:11 +0000
@@ -17,41 +17,39 @@
 # this program.  If not, see <http://www.gnu.org/licenses/>.
 # -----------------------------------------------------------------------------
 
-""" Tests for interrupting cooperative threads """
-
 from threading import Thread, Event
+from unittest import TestCase
 import time
-import unittest
 
 from GTG.tools.interruptible import interruptible, _cancellation_point
 
 
-class TestInterruptible(unittest.TestCase):
-    """ Tests for interrupting cooperative threads """
+class TestInterruptibleDecorator(TestCase):
+
+    def setUp(self):
+        self.quit_condition = False
+        self.thread_started = Event()
+
+    @interruptible
+    def never_ending(self, cancellation_point):
+        self.thread_started.set()
+        while True:
+            time.sleep(0.01)
+            cancellation_point()
 
     def test_interruptible_decorator(self):
         """ Tests for the @interruptible decorator. """
-        self.quit_condition = False
         cancellation_point = lambda: _cancellation_point(
             lambda: self.quit_condition)
-        self.thread_started = Event()
-
-        @interruptible
-        def never_ending(cancellation_point):
-            self.thread_started.set()
-            while True:
-                time.sleep(0.1)
-                cancellation_point()
-        thread = Thread(target=never_ending, args=(cancellation_point, ))
+        thread = Thread(target=self.never_ending, args=(cancellation_point,))
         thread.start()
+
+        # Wait until thread comes to live
         self.thread_started.wait()
+
+        # Ask to it to quit within 20ms
         self.quit_condition = True
-        countdown = 10
-        while thread.is_alive() and countdown > 0:
-            time.sleep(0.1)
-            countdown -= 1
+        time.sleep(0.02)
+
+        # Thread is finished
         self.assertFalse(thread.is_alive())
-
-
-def test_suite():
-    return unittest.TestLoader().loadTestsFromTestCase(TestInterruptible)

=== renamed file 'GTG/tests/test_networkmanager.py' => 'tests/tools/test_networkmanager.py'
--- GTG/tests/test_networkmanager.py	2013-11-23 14:40:23 +0000
+++ tests/tools/test_networkmanager.py	2014-03-09 17:00:11 +0000
@@ -17,21 +17,12 @@
 # this program.  If not, see <http://www.gnu.org/licenses/>.
 # -----------------------------------------------------------------------------
 
-""" Tests for Network Manager """
-
-import unittest
+from unittest import TestCase
 
 from GTG.tools.networkmanager import is_connection_up
 
 
-class TestNetworkManager(unittest.TestCase):
-    """ Test network manager tool code """
+class TestNetworkManager(TestCase):
 
-    def test_is_connection_up_dont_throw_exception(self):
-        """ is_connection_up() returns a boolean value and
-        don't throw any exception """
+    def test_is_connection_up_and_doesnt_throw_exception(self):
         self.assertIn(is_connection_up(), [True, False])
-
-
-def test_suite():
-    return unittest.TestLoader().loadTestsFromName(__name__)

=== added file 'tests/tools/test_tags.py'
--- tests/tools/test_tags.py	1970-01-01 00:00:00 +0000
+++ tests/tools/test_tags.py	2014-03-09 17:00:11 +0000
@@ -0,0 +1,114 @@
+# -*- coding: utf-8 -*-
+# -----------------------------------------------------------------------------
+# Getting Things GNOME! - a personal organizer for the GNOME desktop
+# Copyright (c) 2008-2013 - Lionel Dricot & Bertrand Rousseau
+#
+# This program is free software: you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free Software
+# Foundation, either version 3 of the License, or (at your option) any later
+# version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+# details.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program.  If not, see <http://www.gnu.org/licenses/>.
+# -----------------------------------------------------------------------------
+
+from unittest import TestCase
+
+from GTG.tools.tags import extract_tags_from_text, parse_tag_list
+
+
+class TestExtractTags(TestCase):
+    """ extract_tags_from_text """
+
+    def assertTags(self, text, expected_tags):
+        tag_list = extract_tags_from_text(text)
+        self.assertEqual(expected_tags, tag_list)
+
+    def test_doesnt_find_empty_tag(self):
+        self.assertTags("", [])
+
+    def test_finds_tag_at_beginning(self):
+        self.assertTags("@tag some other text", ["@tag"])
+
+    def test_finds_tag_at_end(self):
+        self.assertTags("some text ended with @endtag", ["@endtag"])
+
+    def test_ignores_emails(self):
+        self.assertTags(
+            "no @emails allowed: invernizzi.l@xxxxxxxxx", ["@emails"])
+
+    def test_ignores_diffs(self):
+        self.assertTags("no @@diff stuff", [])
+
+    def test_accepts_hypen_in_tag(self):
+        self.assertTags("@do-this-today", ["@do-this-today"])
+        self.assertTags("@con--tinuous---hypen-s", ["@con--tinuous---hypen-s"])
+
+    def test_ignores_hypen_at_end_of_tag(self):
+        self.assertTags("@hypen-at-end- some other text", ["@hypen-at-end"])
+        self.assertTags("@hypen-at-end-, with comma", ["@hypen-at-end"])
+
+    def test_accepts_dot_in_tag(self):
+        self.assertTags("text @gtg-0.3", ["@gtg-0.3"])
+
+    def test_ignores_dot_at_end_of_tag(self):
+        self.assertTags("@tag.", ["@tag"])
+
+    def test_accepts_slash_in_tag(self):
+        self.assertTags("@do/this/today", ["@do/this/today"])
+
+    def test_ignores_slash_at_end_of_tag(self):
+        self.assertTags("@slash/es/", ["@slash/es"])
+
+    def test_accepts_colon_in_tag(self):
+        self.assertTags("@my:tag", ["@my:tag"])
+
+    def ignore_colon_at_end(self):
+        self.assertTags("@:a:b:c:", ["@:a:b:c"])
+
+
+class TestParseTagList(TestCase):
+    """ parse_tag_list """
+
+    def test_parses_positive_single_tag(self):
+        self.assertEqual(parse_tag_list("tag"), [("@tag", True)])
+        self.assertEqual(parse_tag_list("@tag"), [("@tag", True)])
+
+    def test_parses_postivie_tag_list(self):
+        self.assertEqual(
+            parse_tag_list("a b c"),
+            [("@a", True), ("@b", True), ("@c", True)],
+        )
+        self.assertEqual(
+            parse_tag_list("@a @b @c"),
+            [("@a", True), ("@b", True), ("@c", True)],
+        )
+
+    def test_parses_negative_single_tag(self):
+        self.assertEqual(parse_tag_list("!tag"), [("@tag", False)])
+        self.assertEqual(parse_tag_list("!@tag"), [("@tag", False)])
+
+    def test_parses_negative_tag_list(self):
+        self.assertEqual(
+            parse_tag_list("!a !b !c"),
+            [("@a", False), ("@b", False), ("@c", False)],
+        )
+        self.assertEqual(
+            parse_tag_list("!@a !@b !@c"),
+            [("@a", False), ("@b", False), ("@c", False)],
+        )
+
+    def test_parses_mixed_tags(self):
+        self.assertEqual(
+            parse_tag_list("add !remove"),
+            [("@add", True), ("@remove", False)],
+        )
+        self.assertEqual(
+            parse_tag_list("!@remove @add"),
+            [("@remove", False), ("@add", True)],
+        )

=== renamed file 'GTG/tests/test_urlregex.py' => 'tests/tools/test_urlregex.py'
--- GTG/tests/test_urlregex.py	2013-11-23 14:40:23 +0000
+++ tests/tools/test_urlregex.py	2014-03-09 17:00:11 +0000
@@ -17,20 +17,16 @@
 # this program.  If not, see <http://www.gnu.org/licenses/>.
 # -----------------------------------------------------------------------------
 
-""" Tests for URL regex """
+from unittest import TestCase
 
-import unittest
 from GTG.tools.urlregex import match
 
 
-class TestURLRegex(unittest.TestCase):
-    """ Test extractor of URL from text """
+class TestURLRegex(TestCase):
+    """ URL Regex """
 
-    def test_anchor_amperstand(self):
-        """ Reproducer for bug #1023555 """
+    def test_allows_ampersand_in_anchor(self):
+        # Reproducer for https://bugs.launchpad.net/gtg/+bug/1023555
         url = "http://test.com/#hi&there";
-        self.assertEqual(match(url).group(0), url)
-
-
-def test_suite():
-    return unittest.TestLoader().loadTestsFromName(__name__)
+        matched_url = match(url).group(0)
+        self.assertEqual(url, matched_url)


Follow ups