Merge lp:~facundo/magicicada-client/full-closed-venv into lp:magicicada-client

Proposed by Facundo Batista
Status: Merged
Approved by: Natalia Bidart
Approved revision: 1447
Merged at revision: 1447
Proposed branch: lp:~facundo/magicicada-client/full-closed-venv
Merge into: lp:magicicada-client
Diff against target: 4651 lines (+2923/-353)
62 files modified
Makefile (+1/-1)
contrib/devtools/__init__.py (+1/-0)
contrib/devtools/compat.py (+49/-0)
contrib/devtools/errors.py (+35/-0)
contrib/devtools/handlers.py (+97/-0)
contrib/devtools/reactors/__init__.py (+1/-0)
contrib/devtools/reactors/gi.py (+53/-0)
contrib/devtools/runners/__init__.py (+305/-0)
contrib/devtools/runners/txrunner.py (+133/-0)
contrib/devtools/services/__init__.py (+66/-0)
contrib/devtools/services/dbus.py (+119/-0)
contrib/devtools/testcases/__init__.py (+187/-0)
contrib/devtools/testcases/dbus.py (+138/-0)
contrib/devtools/testcases/txsocketserver.py (+358/-0)
contrib/devtools/testing/__init__.py (+1/-0)
contrib/devtools/testing/txcheck.py (+381/-0)
contrib/devtools/testing/txwebserver.py (+125/-0)
contrib/devtools/utils.py (+181/-0)
contrib/dirspec/__init__.py (+16/-0)
contrib/dirspec/basedir.py (+159/-0)
contrib/dirspec/utils.py (+188/-0)
contrib/u1trial (+41/-0)
dependencies-devel.txt (+0/-3)
dependencies.txt (+5/-8)
magicicadaclient/platform/tests/filesystem_notifications/__init__.py (+3/-2)
magicicadaclient/platform/tests/filesystem_notifications/common.py (+12/-13)
magicicadaclient/platform/tests/filesystem_notifications/test_darwin.py (+10/-9)
magicicadaclient/platform/tests/filesystem_notifications/test_filesystem_notifications.py (+2/-1)
magicicadaclient/platform/tests/filesystem_notifications/test_fsevents_daemon.py (+10/-15)
magicicadaclient/platform/tests/ipc/test_linux.py (+2/-4)
magicicadaclient/platform/tests/ipc/test_perspective_broker.py (+3/-8)
magicicadaclient/platform/tests/os_helper/test_darwin.py (+3/-7)
magicicadaclient/platform/tests/os_helper/test_linux.py (+3/-6)
magicicadaclient/platform/tests/session/test_common.py (+2/-1)
magicicadaclient/platform/tests/session/test_linux.py (+7/-13)
magicicadaclient/platform/tests/test_tools.py (+4/-6)
magicicadaclient/syncdaemon/tests/test_action_queue.py (+14/-14)
magicicadaclient/syncdaemon/tests/test_eq_inotify.py (+11/-15)
magicicadaclient/syncdaemon/tests/test_eventqueue.py (+2/-6)
magicicadaclient/syncdaemon/tests/test_fileshelf.py (+26/-31)
magicicadaclient/syncdaemon/tests/test_fsm.py (+36/-33)
magicicadaclient/syncdaemon/tests/test_hashqueue.py (+16/-16)
magicicadaclient/syncdaemon/tests/test_interaction_interfaces.py (+11/-15)
magicicadaclient/syncdaemon/tests/test_localrescan.py (+15/-15)
magicicadaclient/syncdaemon/tests/test_logger.py (+5/-7)
magicicadaclient/syncdaemon/tests/test_main.py (+5/-5)
magicicadaclient/syncdaemon/tests/test_offloadqueue.py (+3/-2)
magicicadaclient/syncdaemon/tests/test_pathlockingtree.py (+2/-5)
magicicadaclient/syncdaemon/tests/test_sync.py (+13/-9)
magicicadaclient/syncdaemon/tests/test_tritcask.py (+11/-13)
magicicadaclient/syncdaemon/tests/test_vm.py (+11/-9)
magicicadaclient/testing/testcase.py (+1/-1)
magicicadaclient/utils/__init__.py (+3/-4)
magicicadaclient/utils/tests/test_common.py (+4/-4)
magicicadaclient/utils/tests/test_ipc.py (+5/-5)
magicicadaclient/utils/tests/test_tcpactivation.py (+2/-5)
magicicadaclient/utils/tests/test_translation.py (+1/-1)
magicicadaclient/utils/tests/test_txsecrets.py (+2/-2)
requirements-devel.txt (+2/-0)
requirements.txt (+6/-1)
run-tests (+1/-1)
setup.py (+14/-37)
To merge this branch: bzr merge lp:~facundo/magicicada-client/full-closed-venv
Reviewer Review Type Date Requested Status
Natalia Bidart Approve
Review via email: mp+347341@code.launchpad.net

Commit message

Went full venv.

To post a comment you must log in.
Revision history for this message
Natalia Bidart (nataliabidart) wrote :

20:43 < nessita> revisé tu branch, todo ok, pero no entiendo cómo anda "from devtools import..." porque no veo que nadie meta contrib en el pythonpath
20:44 < nessita> (y de elegir, preferiría que los imports sean from contrib.devtools ...)
20:45 < Facu> nessita, hola! no sé, todo lo que puse en contrib se importó solo, así que ahí no toqué nada
20:46 < Facu>| digo, ese mecanismo ya estaba de antes
20:48 < nessita> Facu, lo puedo landear así pero me da cosa que no veo quien carajo lo mete en el pythonpath
21:02 < Facu> nessita, +1 a "sacar eso", pero se podría hacer después; en cualquier caso, tenemos cosas más "urgentes" para hacer, yo ni le pondría prioridad
21:03 < nessita> okis

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'Makefile'
--- Makefile 2018-04-14 23:34:20 +0000
+++ Makefile 2018-06-03 23:09:20 +0000
@@ -43,7 +43,7 @@
43 cat dependencies-devel.txt | xargs apt-get install -y --no-install-recommends43 cat dependencies-devel.txt | xargs apt-get install -y --no-install-recommends
4444
45venv: 45venv:
46 virtualenv --system-site-packages $(ENV)46 virtualenv $(ENV)
47 $(ENV)/bin/pip install -r requirements.txt -r requirements-devel.txt47 $(ENV)/bin/pip install -r requirements.txt -r requirements-devel.txt
4848
49lint:49lint:
5050
=== added directory 'contrib/devtools'
=== added file 'contrib/devtools/__init__.py'
--- contrib/devtools/__init__.py 1970-01-01 00:00:00 +0000
+++ contrib/devtools/__init__.py 2018-06-03 23:09:20 +0000
@@ -0,0 +1,1 @@
1"""Testing utilities for Ubuntu One client code."""
02
=== added file 'contrib/devtools/compat.py'
--- contrib/devtools/compat.py 1970-01-01 00:00:00 +0000
+++ contrib/devtools/compat.py 2018-06-03 23:09:20 +0000
@@ -0,0 +1,49 @@
1# -*- coding: utf-8 -*-
2#
3# Copyright 2012 Canonical Ltd.
4#
5# This program is free software: you can redistribute it and/or modify it
6# under the terms of the GNU General Public License version 3, as published
7# by the Free Software Foundation.
8#
9# This program is distributed in the hope that it will be useful, but
10# WITHOUT ANY WARRANTY; without even the implied warranties of
11# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
12# PURPOSE. See the GNU General Public License for more details.
13#
14# You should have received a copy of the GNU General Public License along
15# with this program. If not, see <http://www.gnu.org/licenses/>.
16#
17# In addition, as a special exception, the copyright holders give
18# permission to link the code of portions of this program with the
19# OpenSSL library under certain conditions as described in each
20# individual source file, and distribute linked combinations
21# including the two.
22# You must obey the GNU General Public License in all respects
23# for all of the code used other than OpenSSL. If you modify
24# file(s) with this exception, you may extend this exception to your
25# version of the file(s), but you are not obligated to do so. If you
26# do not wish to do so, delete this exception statement from your
27# version. If you delete this exception statement from all source
28# files in the program, then also delete it here.
29"""Python 2 and 3 compatibility."""
30
31from __future__ import unicode_literals
32
33# The following approach was outlined in Lennart Regebro's
34# "Porting to Python 3" book.
35# http://python3porting.com/noconv.html#more-bytes-strings-and-unicode
36
37import sys
38
39# Disable redefined builtin, invalid name warning
40# pylint: disable=W0622,C0103
41
42if sys.version_info < (3,):
43 text_type = unicode
44 binary_type = str
45 basestring = basestring
46else:
47 text_type = str
48 binary_type = bytes
49 basestring = str
050
=== added file 'contrib/devtools/errors.py'
--- contrib/devtools/errors.py 1970-01-01 00:00:00 +0000
+++ contrib/devtools/errors.py 2018-06-03 23:09:20 +0000
@@ -0,0 +1,35 @@
1# Copyright 2012 Canonical Ltd.
2#
3# This program is free software: you can redistribute it and/or modify it
4# under the terms of the GNU General Public License version 3, as published
5# by the Free Software Foundation.
6#
7# This program is distributed in the hope that it will be useful, but
8# WITHOUT ANY WARRANTY; without even the implied warranties of
9# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
10# PURPOSE. See the GNU General Public License for more details.
11#
12# You should have received a copy of the GNU General Public License along
13# with this program. If not, see <http://www.gnu.org/licenses/>.
14#
15# In addition, as a special exception, the copyright holders give
16# permission to link the code of portions of this program with the
17# OpenSSL library under certain conditions as described in each
18# individual source file, and distribute linked combinations
19# including the two.
20# You must obey the GNU General Public License in all respects
21# for all of the code used other than OpenSSL. If you modify
22# file(s) with this exception, you may extend this exception to your
23# version of the file(s), but you are not obligated to do so. If you
24# do not wish to do so, delete this exception statement from your
25# version. If you delete this exception statement from all source
26# files in the program, then also delete it here.
27"""Custom error types for Ubuntu One developer tools."""
28
29
30class TestError(Exception):
31 """An error occurred in attempting to load or start the tests."""
32
33
34class UsageError(Exception):
35 """An error occurred in parsing the command line arguments."""
036
=== added file 'contrib/devtools/handlers.py'
--- contrib/devtools/handlers.py 1970-01-01 00:00:00 +0000
+++ contrib/devtools/handlers.py 2018-06-03 23:09:20 +0000
@@ -0,0 +1,97 @@
1# -*- coding: utf-8 -*-
2
3# Author: Guillermo Gonzalez <guillermo.gonzalez@canonical.com>
4# Author: Facundo Batista <facundo@canonical.com>
5#
6# Copyright 2009-2012 Canonical Ltd.
7#
8# This program is free software: you can redistribute it and/or modify it
9# under the terms of the GNU General Public License version 3, as published
10# by the Free Software Foundation.
11#
12# This program is distributed in the hope that it will be useful, but
13# WITHOUT ANY WARRANTY; without even the implied warranties of
14# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
15# PURPOSE. See the GNU General Public License for more details.
16#
17# You should have received a copy of the GNU General Public License along
18# with this program. If not, see <http://www.gnu.org/licenses/>.
19#
20# In addition, as a special exception, the copyright holders give
21# permission to link the code of portions of this program with the
22# OpenSSL library under certain conditions as described in each
23# individual source file, and distribute linked combinations
24# including the two.
25# You must obey the GNU General Public License in all respects
26# for all of the code used other than OpenSSL. If you modify
27# file(s) with this exception, you may extend this exception to your
28# version of the file(s), but you are not obligated to do so. If you
29# do not wish to do so, delete this exception statement from your
30# version. If you delete this exception statement from all source
31# files in the program, then also delete it here.
32"""Set of helpers handlers."""
33
34from __future__ import print_function
35
36import logging
37
38
39class MementoHandler(logging.Handler):
40 """ A handler class which store logging records in a list """
41
42 def __init__(self, *args, **kwargs):
43 """ Create the instance, and add a records attribute. """
44 logging.Handler.__init__(self, *args, **kwargs)
45 self.records = []
46 self.debug = False
47
48 def emit(self, record):
49 """ Just add the record to self.records. """
50 self.format(record)
51 self.records.append(record)
52
53 def dump_contents(self, msgs):
54 """Dumps the contents of the MementoHandler."""
55 if self.debug:
56 print("Expecting:")
57 for msg in msgs:
58 print("\t", msg)
59 print("MementoHandler contents:")
60 for rec in self.records:
61 print("\t", rec.exc_info)
62 print("\t", logging.getLevelName(rec.levelno))
63 print("\t\t", rec.message)
64 print("\t\t", rec.exc_text)
65
66 def check(self, level, *msgs):
67 """Verifies that the msgs are logged in the specified level"""
68 for rec in self.records:
69 if rec.levelno == level and all(m in rec.message for m in msgs):
70 return rec
71 self.dump_contents(msgs)
72 return False
73
74 def check_debug(self, *msgs):
75 """Shortcut for checking in DEBUG."""
76 return self.check(logging.DEBUG, *msgs)
77
78 def check_info(self, *msgs):
79 """Shortcut for checking in INFO."""
80 return self.check(logging.INFO, *msgs)
81
82 def check_warning(self, *msgs):
83 """Shortcut for checking in WARNING."""
84 return self.check(logging.WARNING, *msgs)
85
86 def check_error(self, *msgs):
87 """Shortcut for checking in ERROR."""
88 return self.check(logging.ERROR, *msgs)
89
90 def check_exception(self, exception_info, *msgs):
91 """Shortcut for checking exceptions."""
92 for rec in self.records:
93 if rec.levelno == logging.ERROR and \
94 all(m in rec.exc_text + rec.message for m in msgs) and \
95 exception_info in rec.exc_info:
96 return True
97 return False
098
=== added directory 'contrib/devtools/reactors'
=== added file 'contrib/devtools/reactors/__init__.py'
--- contrib/devtools/reactors/__init__.py 1970-01-01 00:00:00 +0000
+++ contrib/devtools/reactors/__init__.py 2018-06-03 23:09:20 +0000
@@ -0,0 +1,1 @@
1"""Twisted reactors for testing."""
02
=== added file 'contrib/devtools/reactors/gi.py'
--- contrib/devtools/reactors/gi.py 1970-01-01 00:00:00 +0000
+++ contrib/devtools/reactors/gi.py 2018-06-03 23:09:20 +0000
@@ -0,0 +1,53 @@
1# Copyright 2009-2012 Canonical Ltd.
2#
3# This program is free software: you can redistribute it and/or modify it
4# under the terms of the GNU General Public License version 3, as published
5# by the Free Software Foundation.
6#
7# This program is distributed in the hope that it will be useful, but
8# WITHOUT ANY WARRANTY; without even the implied warranties of
9# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
10# PURPOSE. See the GNU General Public License for more details.
11#
12# You should have received a copy of the GNU General Public License along
13# with this program. If not, see <http://www.gnu.org/licenses/>.
14#
15# In addition, as a special exception, the copyright holders give
16# permission to link the code of portions of this program with the
17# OpenSSL library under certain conditions as described in each
18# individual source file, and distribute linked combinations
19# including the two.
20# You must obey the GNU General Public License in all respects
21# for all of the code used other than OpenSSL. If you modify
22# file(s) with this exception, you may extend this exception to your
23# version of the file(s), but you are not obligated to do so. If you
24# do not wish to do so, delete this exception statement from your
25# version. If you delete this exception statement from all source
26# files in the program, then also delete it here.
27"""The introspection based main loop integration reactor for testing."""
28
29REACTOR_URL = 'http://twistedmatrix.com/trac/ticket/4558'
30
31
32def load_reactor(reactor_name=None):
33 """Load the reactor module and return it."""
34 return __import__(reactor_name, None, None, [''])
35
36
37def install(options=None):
38 """Install the reactor and parse any options we might need."""
39 reactor = None
40 if options is not None and options['gui']:
41 try:
42 reactor = load_reactor('twisted.internet.gtk3reactor')
43 except ImportError:
44 print('Falling back to gtk2reactor module.')
45 reactor = load_reactor('twisted.internet.gtk2reactor')
46 else:
47 try:
48 reactor = load_reactor('twisted.internet.gireactor')
49 except ImportError:
50 print('Falling back to glib2reactor module.')
51 reactor = load_reactor('twisted.internet.glib2reactor')
52
53 reactor.install()
054
=== added directory 'contrib/devtools/runners'
=== added file 'contrib/devtools/runners/__init__.py'
--- contrib/devtools/runners/__init__.py 1970-01-01 00:00:00 +0000
+++ contrib/devtools/runners/__init__.py 2018-06-03 23:09:20 +0000
@@ -0,0 +1,305 @@
1# Copyright 2009-2012 Canonical Ltd.
2# Copyright 2018 Chicharreros (https://launchpad.net/~chicharreros)
3#
4# This program is free software: you can redistribute it and/or modify it
5# under the terms of the GNU General Public License version 3, as published
6# by the Free Software Foundation.
7#
8# This program is distributed in the hope that it will be useful, but
9# WITHOUT ANY WARRANTY; without even the implied warranties of
10# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11# PURPOSE. See the GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License along
14# with this program. If not, see <http://www.gnu.org/licenses/>.
15#
16# In addition, as a special exception, the copyright holders give
17# permission to link the code of portions of this program with the
18# OpenSSL library under certain conditions as described in each
19# individual source file, and distribute linked combinations
20# including the two.
21# You must obey the GNU General Public License in all respects
22# for all of the code used other than OpenSSL. If you modify
23# file(s) with this exception, you may extend this exception to your
24# version of the file(s), but you are not obligated to do so. If you
25# do not wish to do so, delete this exception statement from your
26# version. If you delete this exception statement from all source
27# files in the program, then also delete it here.
28"""The base test runner object."""
29
30from __future__ import print_function, unicode_literals
31
32import coverage
33import gc
34import inspect
35import os
36import re
37import sys
38import unittest
39
40from devtools.errors import TestError, UsageError
41from devtools.testing.txcheck import TXCheckSuite
42from devtools.utils import OptionParser
43from devtools.compat import text_type
44
45__all__ = ['BaseTestOptions', 'BaseTestRunner', 'main']
46
47
48def _is_in_ignored_path(testcase, paths):
49 """Return if the testcase is in one of the ignored paths."""
50 for ignored_path in paths:
51 if testcase.startswith(ignored_path):
52 return True
53 return False
54
55
56class BaseTestRunner(object):
57 """The base test runner type. Does not actually run tests."""
58
59 def __init__(self, options=None, *args, **kwargs):
60 super(BaseTestRunner, self).__init__(*args, **kwargs)
61
62 # set $HOME to the _trial_temp dir, to avoid breaking user files
63 trial_temp_dir = os.environ.get('TRIAL_TEMP_DIR', os.getcwd())
64 homedir = os.path.join(trial_temp_dir, options['temp-directory'])
65 os.environ['HOME'] = homedir
66
67 # setup $XDG_*_HOME variables and create the directories
68 xdg_cache = os.path.join(homedir, 'xdg_cache')
69 xdg_config = os.path.join(homedir, 'xdg_config')
70 xdg_data = os.path.join(homedir, 'xdg_data')
71 os.environ['XDG_CACHE_HOME'] = xdg_cache
72 os.environ['XDG_CONFIG_HOME'] = xdg_config
73 os.environ['XDG_DATA_HOME'] = xdg_data
74
75 if not os.path.exists(xdg_cache):
76 os.makedirs(xdg_cache)
77 if not os.path.exists(xdg_config):
78 os.makedirs(xdg_config)
79 if not os.path.exists(xdg_data):
80 os.makedirs(xdg_data)
81
82 # setup the ROOTDIR env var
83 os.environ['ROOTDIR'] = os.getcwd()
84
85 # Need an attribute for tempdir so we can use it later
86 self.tempdir = homedir
87 self.working_dir = os.path.join(self.tempdir, 'trial')
88
89 self.source_files = []
90 self.required_services = []
91
92 def _load_unittest(self, relpath):
93 """Load unit tests from a Python module with the given 'relpath'."""
94 assert relpath.endswith(".py"), (
95 "%s does not appear to be a Python module" % relpath)
96 if not os.path.basename(relpath).startswith('test_'):
97 return
98 modpath = relpath.replace(os.path.sep, ".")[:-3]
99 module = __import__(modpath, None, None, [""])
100
101 # If the module specifies required_services, make sure we get them
102 members = [x[1] for x in inspect.getmembers(module, inspect.isclass)]
103 for member_type in members:
104 if hasattr(member_type, 'required_services'):
105 member = member_type()
106 for service in member.required_services():
107 if service not in self.required_services:
108 self.required_services.append(service)
109 del member
110 gc.collect()
111
112 # If the module has a 'suite' or 'test_suite' function, use that
113 # to load the tests.
114 if hasattr(module, "suite"):
115 return module.suite()
116 elif hasattr(module, "test_suite"):
117 return module.test_suite()
118 else:
119 return unittest.defaultTestLoader.loadTestsFromModule(module)
120
121 def _collect_tests(self, path, test_pattern, ignored_modules,
122 ignored_paths):
123 """Return the set of unittests."""
124 suite = TXCheckSuite()
125 if test_pattern:
126 pattern = re.compile('.*%s.*' % test_pattern)
127 else:
128 pattern = None
129
130 # Disable this lint warning as we need to access _tests in the
131 # test suites, to collect the tests
132 # pylint: disable=W0212
133 if path:
134 try:
135 module_suite = self._load_unittest(path)
136 if pattern:
137 for inner_suite in module_suite._tests:
138 for test in inner_suite._tests:
139 if pattern.match(test.id()):
140 suite.addTest(test)
141 else:
142 suite.addTests(module_suite)
143 return suite
144 except AssertionError:
145 pass
146 else:
147 raise TestError('Path should be defined.')
148
149 # We don't use the dirs variable, so ignore the warning
150 # pylint: disable=W0612
151 for root, dirs, files in os.walk(path):
152 for test in files:
153 filepath = os.path.join(root, test)
154 if test.endswith(".py") and test not in ignored_modules and \
155 not _is_in_ignored_path(filepath, ignored_paths):
156 self.source_files.append(filepath)
157 if test.startswith("test_"):
158 module_suite = self._load_unittest(filepath)
159 if pattern:
160 for inner_suite in module_suite._tests:
161 for test in inner_suite._tests:
162 if pattern.match(test.id()):
163 suite.addTest(test)
164 else:
165 suite.addTests(module_suite)
166 return suite
167
168 def get_suite(self, config):
169 """Get the test suite to use."""
170 suite = unittest.TestSuite()
171 for path in config['tests']:
172 suite.addTest(self._collect_tests(path, config['test'],
173 config['ignore-modules'],
174 config['ignore-paths']))
175 if config['loop']:
176 old_suite = suite
177 suite = unittest.TestSuite()
178 for _ in range(config['loop']):
179 suite.addTest(old_suite)
180
181 return suite
182
183 def run_tests(self, suite):
184 """Run the test suite."""
185 return False
186
187
188class BaseTestOptions(OptionParser):
189 """Base options for our test runner."""
190
191 optFlags = [['coverage', 'c', 'Generate a coverage report for the tests.'],
192 ['gui', None, 'Use the GUI mode of some runners.'],
193 ['help', 'h', ''],
194 ['help-runners', None, 'List information about test runners.'],
195 ]
196
197 optParameters = [['test', 't', None, None],
198 ['loop', None, 1, None],
199 ['ignore-modules', 'i', '', None],
200 ['ignore-paths', 'p', '', None],
201 ['runner', None, 'txrunner', None],
202 ['temp-directory', None, b'_trial_temp', None],
203 ]
204
205 def __init__(self, *args, **kwargs):
206 super(BaseTestOptions, self).__init__(*args, **kwargs)
207
208 def opt_help_runners(self):
209 """List the runners which are supported."""
210 sys.exit(0)
211
212 def opt_ignore_modules(self, option):
213 """Comma-separate list of test modules to ignore,
214 e.g: test_gtk.py, test_account.py
215 """
216 self['ignore-modules'] = list(map(text_type.strip, option.split(',')))
217
218 def opt_ignore_paths(self, option):
219 """Comma-separated list of relative paths to ignore,
220 e.g: tests/platform/windows, tests/platform/macosx
221 """
222 self['ignore-paths'] = list(map(text_type.strip, option.split(',')))
223
224 def opt_loop(self, option):
225 """Loop tests the specified number of times."""
226 try:
227 self['loop'] = int(option)
228 except ValueError:
229 raise UsageError('A positive integer value must be specified.')
230
231 def opt_temp_directory(self, option):
232 """Path to use as a working directory for tests.
233 [default: _trial_temp]
234 """
235 self['temp-directory'] = option
236
237 def opt_test(self, option):
238 """Run specific tests, e.g: className.methodName"""
239 self['test'] = option
240
241 # We use some camelcase names for trial compatibility here.
242 def parseArgs(self, *args):
243 """Handle the extra arguments."""
244 if isinstance(self.tests, set):
245 self['tests'].update(args)
246 elif isinstance(self.tests, list):
247 self['tests'].extend(args)
248
249
250def _get_runner_options(runner_name):
251 """Return the test runner module, and its options object."""
252 module_name = 'devtools.runners.%s' % runner_name
253 runner = __import__(module_name, None, None, [''])
254 options = None
255 if getattr(runner, 'TestOptions', None) is not None:
256 options = runner.TestOptions()
257 if options is None:
258 options = BaseTestOptions()
259 return (runner, options)
260
261
262def main():
263 """Do the deed."""
264 if len(sys.argv) == 1:
265 sys.argv.append('--help')
266
267 try:
268 pos = sys.argv.index('--runner')
269 runner_name = sys.argv.pop(pos + 1)
270 sys.argv.pop(pos)
271 except ValueError:
272 runner_name = 'txrunner'
273 finally:
274 runner, options = _get_runner_options(runner_name)
275 options.parseOptions()
276
277 test_runner = runner.TestRunner(options=options)
278 suite = test_runner.get_suite(options)
279
280 if options['coverage']:
281 coverage.erase()
282 coverage.start()
283
284 running_services = []
285
286 succeeded = False
287 try:
288 # Start any required services
289 for service_obj in test_runner.required_services:
290 service = service_obj()
291 service.start_service(tempdir=test_runner.tempdir)
292 running_services.append(service)
293
294 succeeded = test_runner.run_tests(suite)
295 finally:
296 # Stop all the running services
297 for service in running_services:
298 service.stop_service()
299
300 if options['coverage']:
301 coverage.stop()
302 coverage.report(test_runner.source_files, ignore_errors=True,
303 show_missing=False)
304
305 sys.exit(not succeeded)
0306
=== added file 'contrib/devtools/runners/txrunner.py'
--- contrib/devtools/runners/txrunner.py 1970-01-01 00:00:00 +0000
+++ contrib/devtools/runners/txrunner.py 2018-06-03 23:09:20 +0000
@@ -0,0 +1,133 @@
1# Copyright 2009-2012 Canonical Ltd.
2# Copyright 2018 Chicharreros (https://launchpad.net/~chicharreros)
3#
4# This program is free software: you can redistribute it and/or modify it
5# under the terms of the GNU General Public License version 3, as published
6# by the Free Software Foundation.
7#
8# This program is distributed in the hope that it will be useful, but
9# WITHOUT ANY WARRANTY; without even the implied warranties of
10# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11# PURPOSE. See the GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License along
14# with this program. If not, see <http://www.gnu.org/licenses/>.
15#
16# In addition, as a special exception, the copyright holders give
17# permission to link the code of portions of this program with the
18# OpenSSL library under certain conditions as described in each
19# individual source file, and distribute linked combinations
20# including the two.
21# You must obey the GNU General Public License in all respects
22# for all of the code used other than OpenSSL. If you modify
23# file(s) with this exception, you may extend this exception to your
24# version of the file(s), but you are not obligated to do so. If you
25# do not wish to do so, delete this exception statement from your
26# version. If you delete this exception statement from all source
27# files in the program, then also delete it here.
28"""The twisted test runner and options."""
29
30from __future__ import print_function, unicode_literals
31
32import sys
33
34from twisted.scripts import trial
35from twisted.trial.runner import TrialRunner
36
37from devtools.errors import TestError
38from devtools.runners import BaseTestOptions, BaseTestRunner
39
40__all__ = ['TestRunner', 'TestOptions']
41
42
43class TestRunner(BaseTestRunner, TrialRunner):
44 """The twisted test runner implementation."""
45
46 def __init__(self, options=None):
47 # Handle running trial in debug or dry-run mode
48 self.config = options
49
50 try:
51 reactor_name = ('devtools.reactors.%s' % (self.config['reactor'],))
52 reactor = __import__(reactor_name, None, None, [''])
53 except ImportError:
54 raise TestError('The specified reactor is not supported.')
55 else:
56 try:
57 reactor.install(options=self.config)
58 except ImportError:
59 raise TestError(
60 'The Python package providing the requested reactor is '
61 'not installed. You can find it here: %s' %
62 reactor.REACTOR_URL)
63
64 mode = None
65 if self.config['debug']:
66 mode = TrialRunner.DEBUG
67 if self.config['dry-run']:
68 mode = TrialRunner.DRY_RUN
69
70 # Hook up to the parent test runner
71 super(TestRunner, self).__init__(
72 options=options,
73 reporterFactory=self.config['reporter'],
74 mode=mode,
75 profile=self.config['profile'],
76 logfile=self.config['logfile'],
77 tracebackFormat=self.config['tbformat'],
78 realTimeErrors=self.config['rterrors'],
79 uncleanWarnings=self.config['unclean-warnings'],
80 forceGarbageCollection=self.config['force-gc'])
81 # Named for trial compatibility.
82 # pylint: disable=C0103
83 self.workingDirectory = self.working_dir
84 # pylint: enable=C0103
85
86 def run_tests(self, suite):
87 """Run the twisted test suite."""
88 if self.config['until-failure']:
89 result = self.runUntilFailure(suite)
90 else:
91 result = self.run(suite)
92 return result.wasSuccessful()
93
94
95def _get_default_reactor():
96 """Return the platform-dependent default reactor to use."""
97 default_reactor = 'gi'
98 if sys.platform in ['darwin', 'win32']:
99 default_reactor = 'twisted'
100 return default_reactor
101
102
103class TestOptions(trial.Options, BaseTestOptions):
104 """Class for twisted options handling."""
105
106 optFlags = [["help-reactors", None],
107 ]
108
109 optParameters = [["reactor", "r", _get_default_reactor()],
110 ]
111
112 def __init__(self, *args, **kwargs):
113 super(TestOptions, self).__init__(*args, **kwargs)
114 self['rterrors'] = True
115
116 def opt_coverage(self, option):
117 """Handle special flags."""
118 self['coverage'] = True
119 opt_c = opt_coverage
120
121 def opt_help_reactors(self):
122 """Help on available reactors for use with tests"""
123 synopsis = ('')
124 print(synopsis)
125 print('Need to get list of reactors and print them here.\n')
126 sys.exit(0)
127
128 def opt_reactor(self, option):
129 """Which reactor to use (see --help-reactors for a list
130 of possibilities)
131 """
132 self['reactor'] = option
133 opt_r = opt_reactor
0134
=== added directory 'contrib/devtools/services'
=== added file 'contrib/devtools/services/__init__.py'
--- contrib/devtools/services/__init__.py 1970-01-01 00:00:00 +0000
+++ contrib/devtools/services/__init__.py 2018-06-03 23:09:20 +0000
@@ -0,0 +1,66 @@
1# Copyright 2011-2012 Canonical Ltd.
2# Copyright 2018 Chicharreros (https://launchpad.net/~chicharreros)
3#
4# This program is free software: you can redistribute it and/or modify it
5# under the terms of the GNU General Public License version 3, as published
6# by the Free Software Foundation.
7#
8# This program is distributed in the hope that it will be useful, but
9# WITHOUT ANY WARRANTY; without even the implied warranties of
10# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11# PURPOSE. See the GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License along
14# with this program. If not, see <http://www.gnu.org/licenses/>.
15#
16# In addition, as a special exception, the copyright holders give
17# permission to link the code of portions of this program with the
18# OpenSSL library under certain conditions as described in each
19# individual source file, and distribute linked combinations
20# including the two.
21# You must obey the GNU General Public License in all respects
22# for all of the code used other than OpenSSL. If you modify
23# file(s) with this exception, you may extend this exception to your
24# version of the file(s), but you are not obligated to do so. If you
25# do not wish to do so, delete this exception statement from your
26# version. If you delete this exception statement from all source
27# files in the program, then also delete it here.
28"""Service runners for testing."""
29
30import os
31import socket
32
33from dirspec.basedir import load_data_paths
34
35
36def find_config_file(in_config_file):
37 """Find the first appropriate conf to use."""
38 # In case we're running from within the source tree
39 path = os.path.abspath(os.path.join(os.path.dirname(__file__),
40 os.path.pardir, os.path.pardir,
41 os.path.pardir,
42 "data", in_config_file))
43 if not os.path.exists(path):
44 # Use the installed file in $pkgdatadir as source
45 for path in load_data_paths("ubuntuone-dev-tools", in_config_file):
46 if os.path.exists(path):
47 break
48
49 # Check to make sure we didn't just fall out of the loop
50 if not os.path.exists(path):
51 raise IOError('Could not locate suitable %s' % in_config_file)
52 return path
53
54
55def get_arbitrary_port():
56 """
57 Find an unused port, and return it.
58
59 There might be a small race condition here, but we aren't
60 worried about it.
61 """
62 sock = socket.socket()
63 sock.bind(('localhost', 0))
64 _, port = sock.getsockname()
65 sock.close()
66 return port
067
=== added file 'contrib/devtools/services/dbus.py'
--- contrib/devtools/services/dbus.py 1970-01-01 00:00:00 +0000
+++ contrib/devtools/services/dbus.py 2018-06-03 23:09:20 +0000
@@ -0,0 +1,119 @@
1# Copyright 2009-2012 Canonical Ltd.
2# Copyright 2018 Chicharreros (https://launchpad.net/~chicharreros)
3#
4# This program is free software: you can redistribute it and/or modify it
5# under the terms of the GNU General Public License version 3, as published
6# by the Free Software Foundation.
7#
8# This program is distributed in the hope that it will be useful, but
9# WITHOUT ANY WARRANTY; without even the implied warranties of
10# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11# PURPOSE. See the GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License along
14# with this program. If not, see <http://www.gnu.org/licenses/>.
15#
16# In addition, as a special exception, the copyright holders give
17# permission to link the code of portions of this program with the
18# OpenSSL library under certain conditions as described in each
19# individual source file, and distribute linked combinations
20# including the two.
21# You must obey the GNU General Public License in all respects
22# for all of the code used other than OpenSSL. If you modify
23# file(s) with this exception, you may extend this exception to your
24# version of the file(s), but you are not obligated to do so. If you
25# do not wish to do so, delete this exception statement from your
26# version. If you delete this exception statement from all source
27# files in the program, then also delete it here.
28"""Utilities for finding and running a dbus session bus for testing."""
29
30from __future__ import unicode_literals
31
32import os
33import signal
34import subprocess
35
36from distutils.spawn import find_executable
37
38# pylint: disable=F0401,E0611
39try:
40 from urllib.parse import quote
41except ImportError:
42 from urllib import quote
43# pylint: enable=F0401,E0611
44
45from devtools.services import find_config_file
46DBUS_CONFIG_FILE = 'dbus-session.conf.in'
47
48
49class DBusLaunchError(Exception):
50 """Error while launching dbus-daemon"""
51 pass
52
53
54class NotFoundError(Exception):
55 """Not found error"""
56 pass
57
58
59class DBusRunner(object):
60 """Class for running dbus-daemon with a private session."""
61
62 def __init__(self):
63 self.dbus_address = None
64 self.dbus_pid = None
65 self.running = False
66 self.config_file = None
67
68 def _generate_config_file(self, tempdir=None):
69 """Find the first appropriate dbus-session.conf to use."""
70 # load the config file
71 path = find_config_file(DBUS_CONFIG_FILE)
72 # replace config settings
73 self.config_file = os.path.join(tempdir, 'dbus-session.conf')
74 dbus_address = 'unix:tmpdir=%s' % quote(tempdir)
75 with open(path) as in_file:
76 content = in_file.read()
77 with open(self.config_file, 'w') as out_file:
78 out_file.write(content.replace('@ADDRESS@', dbus_address))
79
80 def start_service(self, tempdir=None):
81 """Start our own session bus daemon for testing."""
82 dbus = find_executable("dbus-daemon")
83 if not dbus:
84 raise NotFoundError("dbus-daemon was not found.")
85
86 self._generate_config_file(tempdir)
87
88 dbus_args = ["--fork",
89 "--config-file=" + self.config_file,
90 "--print-address=1",
91 "--print-pid=2"]
92 sp = subprocess.Popen([dbus] + dbus_args,
93 bufsize=4096, stdout=subprocess.PIPE,
94 stderr=subprocess.PIPE)
95
96 # Call wait here as under the qt4 reactor we get an error about
97 # interrupted system call if we don't.
98 sp.wait()
99 self.dbus_address = b"".join(sp.stdout.readlines()).strip()
100 self.dbus_pid = int(b"".join(sp.stderr.readlines()).strip())
101
102 if self.dbus_address != "":
103 os.environ["DBUS_SESSION_BUS_ADDRESS"] = \
104 self.dbus_address.decode("utf8")
105 else:
106 os.kill(self.dbus_pid, signal.SIGKILL)
107 raise DBusLaunchError("There was a problem launching dbus-daemon.")
108 self.running = True
109
110 def stop_service(self):
111 """Stop our DBus session bus daemon."""
112 try:
113 del os.environ["DBUS_SESSION_BUS_ADDRESS"]
114 except KeyError:
115 pass
116 os.kill(self.dbus_pid, signal.SIGKILL)
117 self.running = False
118 os.unlink(self.config_file)
119 self.config_file = None
0120
=== added directory 'contrib/devtools/testcases'
=== added file 'contrib/devtools/testcases/__init__.py'
--- contrib/devtools/testcases/__init__.py 1970-01-01 00:00:00 +0000
+++ contrib/devtools/testcases/__init__.py 2018-06-03 23:09:20 +0000
@@ -0,0 +1,187 @@
1# -*- coding: utf-8 -*-
2#
3# Copyright 2009-2012 Canonical Ltd.
4#
5# This program is free software: you can redistribute it and/or modify it
6# under the terms of the GNU General Public License version 3, as published
7# by the Free Software Foundation.
8#
9# This program is distributed in the hope that it will be useful, but
10# WITHOUT ANY WARRANTY; without even the implied warranties of
11# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
12# PURPOSE. See the GNU General Public License for more details.
13#
14# You should have received a copy of the GNU General Public License along
15# with this program. If not, see <http://www.gnu.org/licenses/>.
16#
17# In addition, as a special exception, the copyright holders give
18# permission to link the code of portions of this program with the
19# OpenSSL library under certain conditions as described in each
20# individual source file, and distribute linked combinations
21# including the two.
22# You must obey the GNU General Public License in all respects
23# for all of the code used other than OpenSSL. If you modify
24# file(s) with this exception, you may extend this exception to your
25# version of the file(s), but you are not obligated to do so. If you
26# do not wish to do so, delete this exception statement from your
27# version. If you delete this exception statement from all source
28# files in the program, then also delete it here.
29"""Base tests cases and test utilities."""
30
31from __future__ import with_statement
32
33import contextlib
34import os
35import shutil
36import sys
37
38from functools import wraps
39
40from twisted.trial.unittest import TestCase, SkipTest
41
42
43@contextlib.contextmanager
44def environ(env_var, new_value):
45 """context manager to replace/add an environ value"""
46 old_value = os.environ.get(env_var, None)
47 os.environ[env_var] = new_value
48 yield
49 if old_value is None:
50 os.environ.pop(env_var)
51 else:
52 os.environ[env_var] = old_value
53
54
55def _id(obj):
56 """Return the obj calling the funct."""
57 return obj
58
59
60# pylint: disable=C0103
61def skipTest(reason):
62 """Unconditionally skip a test."""
63
64 def decorator(test_item):
65 """Decorate the test so that it is skipped."""
66 if not (isinstance(test_item, type) and
67 issubclass(test_item, TestCase)):
68
69 @wraps(test_item)
70 def skip_wrapper(*args, **kwargs):
71 """Skip a test method raising an exception."""
72 raise SkipTest(reason)
73 test_item = skip_wrapper
74
75 # tell twisted.trial.unittest to skip the test, pylint will complain
76 # since it thinks we are redefining a name out of the scope
77 # pylint: disable=W0621,W0612
78 test_item.skip = reason
79 # pylint: enable=W0621,W0612
80 # because the item was skipped, we will make sure that no
81 # services are started for it
82 if hasattr(test_item, "required_services"):
83 # pylint: disable=W0612
84 test_item.required_services = lambda *args, **kwargs: []
85 # pylint: enable=W0612
86 return test_item
87 return decorator
88
89
90def skipIf(condition, reason):
91 """Skip a test if the condition is true."""
92 if condition:
93 return skipTest(reason)
94 return _id
95
96
97def skipIfOS(current_os, reason):
98 """Skip test for a particular os or lists of them."""
99 if os:
100 if sys.platform in current_os or sys.platform == current_os:
101 return skipTest(reason)
102 return _id
103 return _id
104
105
106def skipIfNotOS(current_os, reason):
107 """Skip test we are not in a particular os."""
108 if os:
109 if sys.platform not in current_os or \
110 sys.platform != current_os:
111 return skipTest(reason)
112 return _id
113 return _id
114
115
116def skipIfJenkins(current_os, reason):
117 """Skip test for a particular os or lists of them
118 when running on Jenkins."""
119 if os.getenv("JENKINS", False) and (sys.platform in current_os or
120 sys.platform == current_os):
121 return skipTest(reason)
122 return _id
123
124
125# pylint: enable=C0103
126
127
128class BaseTestCase(TestCase):
129 """Base TestCase with helper methods to handle temp dir.
130
131 This class provides:
132 mktemp(name): helper to create temporary dirs
133 rmtree(path): support read-only shares
134 makedirs(path): support read-only shares
135
136 """
137
138 def required_services(self):
139 """Return the list of required services for DBusTestCase."""
140 return []
141
142 def mktemp(self, name='temp'):
143 """Customized mktemp that accepts an optional name argument."""
144 tempdir = os.path.join(self.tmpdir, name)
145 if os.path.exists(tempdir):
146 self.rmtree(tempdir)
147 self.makedirs(tempdir)
148 return tempdir
149
150 @property
151 def tmpdir(self):
152 """Default tmpdir: module/class/test_method."""
153 # check if we already generated the root path
154 root_dir = getattr(self, '__root', None)
155 if root_dir:
156 return root_dir
157 max_filename = 32 # some platforms limit lengths of filenames
158 base = os.path.join(self.__class__.__module__[:max_filename],
159 self.__class__.__name__[:max_filename],
160 self._testMethodName[:max_filename])
161 # use _trial_temp dir, it should be os.gwtcwd()
162 # define the root temp dir of the testcase, pylint: disable=W0201
163 self.__root = os.path.join(os.getcwd(), base)
164 return self.__root
165
166 def rmtree(self, path):
167 """Custom rmtree that handle ro parent(s) and childs."""
168 if not os.path.exists(path):
169 return
170 # change perms to rw, so we can delete the temp dir
171 if path != getattr(self, '__root', None):
172 os.chmod(os.path.dirname(path), 0o755)
173 if not os.access(path, os.W_OK):
174 os.chmod(path, 0o755)
175 # pylint: disable=W0612
176 for dirpath, dirs, files in os.walk(path):
177 for dirname in dirs:
178 if not os.access(os.path.join(dirpath, dirname), os.W_OK):
179 os.chmod(os.path.join(dirpath, dirname), 0o777)
180 shutil.rmtree(path)
181
182 def makedirs(self, path):
183 """Custom makedirs that handle ro parent."""
184 parent = os.path.dirname(path)
185 if os.path.exists(parent):
186 os.chmod(parent, 0o755)
187 os.makedirs(path)
0188
=== added file 'contrib/devtools/testcases/dbus.py'
--- contrib/devtools/testcases/dbus.py 1970-01-01 00:00:00 +0000
+++ contrib/devtools/testcases/dbus.py 2018-06-03 23:09:20 +0000
@@ -0,0 +1,138 @@
1# -*- coding: utf-8 -*-
2# Copyright 2009-2012 Canonical Ltd.
3# Copyright 2018 Chicharreros (https://launchpad.net/~chicharreros)
4#
5# This program is free software: you can redistribute it and/or modify it
6# under the terms of the GNU General Public License version 3, as published
7# by the Free Software Foundation.
8#
9# This program is distributed in the hope that it will be useful, but
10# WITHOUT ANY WARRANTY; without even the implied warranties of
11# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
12# PURPOSE. See the GNU General Public License for more details.
13#
14# You should have received a copy of the GNU General Public License along
15# with this program. If not, see <http://www.gnu.org/licenses/>.
16#
17# In addition, as a special exception, the copyright holders give
18# permission to link the code of portions of this program with the
19# OpenSSL library under certain conditions as described in each
20# individual source file, and distribute linked combinations
21# including the two.
22# You must obey the GNU General Public License in all respects
23# for all of the code used other than OpenSSL. If you modify
24# file(s) with this exception, you may extend this exception to your
25# version of the file(s), but you are not obligated to do so. If you
26# do not wish to do so, delete this exception statement from your
27# version. If you delete this exception statement from all source
28# files in the program, then also delete it here.
29"""Base dbus tests cases and test utilities."""
30
31from __future__ import absolute_import, with_statement
32
33import os
34
35from twisted.internet import defer
36
37from devtools.testcases import BaseTestCase, skipIf
38
39# DBusRunner for DBusTestCase using tests
40from devtools.services.dbus import DBusRunner
41
42
43# pylint: disable=F0401,C0103,W0406,E0611
44try:
45 import dbus
46except ImportError as e:
47 dbus = None
48
49try:
50 import dbus.service as service
51except ImportError:
52 service = None
53
54try:
55 from dbus.mainloop.glib import DBusGMainLoop
56except ImportError:
57 DBusGMainLoop = None
58
59# pylint: enable=F0401,C0103,W0406,E0611
60
61
62class InvalidSessionBus(Exception):
63 """Error when we are connected to the wrong session bus in tests."""
64
65
66class FakeDBusInterface(object):
67 """A fake DBusInterface..."""
68
69 def shutdown(self, with_restart=False):
70 """...that only knows how to go away"""
71
72
73@skipIf(dbus is None or service is None or DBusGMainLoop is None,
74 "The test requires dbus.")
75class DBusTestCase(BaseTestCase):
76 """Test the DBus event handling."""
77
78 def required_services(self):
79 """Return the list of required services for DBusTestCase."""
80 services = super(DBusTestCase, self).required_services()
81 services.extend([DBusRunner])
82 return services
83
84 @defer.inlineCallbacks
85 def setUp(self):
86 """Setup the infrastructure fo the test (dbus service)."""
87 # Class 'BaseTestCase' has no 'setUp' member
88 # pylint: disable=E1101
89 # dbus modules will be imported by the decorator
90 # pylint: disable=E0602
91 yield super(DBusTestCase, self).setUp()
92
93 # We need to ensure DBUS_SESSION_BUS_ADDRESS is private here
94 # pylint: disable=F0401,E0611
95 try:
96 from urllib.parse import unquote
97 except ImportError:
98 from urllib import unquote
99 # pylint: enable=F0401,E0611
100
101 bus_address = os.environ.get('DBUS_SESSION_BUS_ADDRESS', None)
102 if os.path.dirname(unquote(bus_address.split(',')[0].split('=')[1])) \
103 != os.path.dirname(os.getcwd()):
104 raise InvalidSessionBus('DBUS_SESSION_BUS_ADDRESS is wrong.')
105
106 # Set up the main loop and bus connection
107 self.loop = DBusGMainLoop(set_as_default=True)
108
109 # NOTE: The address_or_type value must remain explicitly as
110 # str instead of anything from devtools.compat. dbus
111 # expects this to be str regardless of version.
112 self.bus = dbus.bus.BusConnection(address_or_type=str(bus_address),
113 mainloop=self.loop)
114
115 # Monkeypatch the dbus.SessionBus/SystemBus methods, to ensure we
116 # always point at our own private bus instance.
117 self.patch(dbus, 'SessionBus', lambda: self.bus)
118 self.patch(dbus, 'SystemBus', lambda: self.bus)
119
120 # Check that we are on the correct bus for real
121# Disable this for now, because our tests are extremely broken :(
122# bus_names = self.bus.list_names()
123# if len(bus_names) > 2:
124# raise InvalidSessionBus('Too many bus connections: %s (%r)' %
125# (len(bus_names), bus_names))
126
127 # monkeypatch busName.__del__ to avoid errors on gc
128 # we take care of releasing the name in shutdown
129 service.BusName.__del__ = lambda _: None
130 yield self.bus.set_exit_on_disconnect(False)
131 self.signal_receivers = set()
132
133 @defer.inlineCallbacks
134 def tearDown(self):
135 """Cleanup the test."""
136 yield self.bus.flush()
137 yield self.bus.close()
138 yield super(DBusTestCase, self).tearDown()
0139
=== added file 'contrib/devtools/testcases/txsocketserver.py'
--- contrib/devtools/testcases/txsocketserver.py 1970-01-01 00:00:00 +0000
+++ contrib/devtools/testcases/txsocketserver.py 2018-06-03 23:09:20 +0000
@@ -0,0 +1,358 @@
1# -*- coding: utf-8 -*-
2# Copyright 2012 Canonical Ltd.
3# Copyright 2018 Chicharreros (https://launchpad.net/~chicharreros)
4#
5# This program is free software: you can redistribute it and/or modify it
6# under the terms of the GNU General Public License version 3, as published
7# by the Free Software Foundation.
8#
9# This program is distributed in the hope that it will be useful, but
10# WITHOUT ANY WARRANTY; without even the implied warranties of
11# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
12# PURPOSE. See the GNU General Public License for more details.
13#
14# You should have received a copy of the GNU General Public License along
15# with this program. If not, see <http://www.gnu.org/licenses/>.
16#
17# In addition, as a special exception, the copyright holders give
18# permission to link the code of portions of this program with the
19# OpenSSL library under certain conditions as described in each
20# individual source file, and distribute linked combinations
21# including the two.
22# You must obey the GNU General Public License in all respects
23# for all of the code used other than OpenSSL. If you modify
24# file(s) with this exception, you may extend this exception to your
25# version of the file(s), but you are not obligated to do so. If you
26# do not wish to do so, delete this exception statement from your
27# version. If you delete this exception statement from all source
28# files in the program, then also delete it here.
29
30"""Base test case for twisted servers."""
31
32import os
33import shutil
34import tempfile
35
36from twisted.internet import defer, endpoints, protocol
37from twisted.spread import pb
38
39from devtools.testcases import BaseTestCase
40
41# no init method + twisted common warnings
42# pylint: disable=W0232, C0103, E1101
43
44
45def server_protocol_factory(cls):
46 """Factory to create tidy protocols."""
47
48 if cls is None:
49 cls = protocol.Protocol
50
51 class ServerTidyProtocol(cls):
52 """A tidy protocol."""
53
54 def connectionLost(self, *args):
55 """Lost the connection."""
56 cls.connectionLost(self, *args)
57 # lets tell everyone
58 # pylint: disable=W0212
59 if (self.factory._disconnecting and
60 self.factory.testserver_on_connection_lost is not None and
61 not self.factory.testserver_on_connection_lost.called):
62 self.factory.testserver_on_connection_lost.callback(self)
63 # pylint: enable=W0212
64
65 return ServerTidyProtocol
66
67
68def server_factory_factory(cls):
69 """Factory that creates special types of factories for tests."""
70
71 if cls is None:
72 cls = protocol.ServerFactory
73
74 class TidyServerFactory(cls):
75 """A tidy factory."""
76
77 testserver_on_connection_lost = None
78
79 def buildProtocol(self, addr):
80 prot = cls.buildProtocol(self, addr)
81 self.testserver_on_connection_lost = defer.Deferred()
82 return prot
83
84 return TidyServerFactory
85
86
87def client_protocol_factory(cls):
88 """Factory to create tidy protocols."""
89
90 if cls is None:
91 cls = protocol.Protocol
92
93 class ClientTidyProtocol(cls):
94 """A tidy protocol."""
95
96 def connectionLost(self, *a):
97 """Connection list."""
98 cls.connectionLost(self, *a)
99 # pylint: disable=W0212
100 if (self.factory._disconnecting and
101 self.factory.testserver_on_connection_lost is not None and
102 not self.factory.testserver_on_connection_lost.called):
103 self.factory.testserver_on_connection_lost.callback(self)
104 # pylint: enable=W0212
105
106 return ClientTidyProtocol
107
108
109class TidySocketServer(object):
110 """Ensure that twisted servers are correctly managed in tests.
111
112 Closing a twisted server is a complicated matter. In order to do so you
113 have to ensure that three different deferreds are fired:
114
115 1. The server must stop listening.
116 2. The client connection must disconnect.
117 3. The server connection must disconnect.
118
119 This class allows to create a server and a client that will ensure that
120 the reactor is left clean by following the pattern described at
121 http://mumak.net/stuff/twisted-disconnect.html
122 """
123 def __init__(self):
124 """Create a new instance."""
125 self.listener = None
126 self.server_factory = None
127
128 self.connector = None
129 self.client_factory = None
130
131 def get_server_endpoint(self):
132 """Return the server endpoint description."""
133 raise NotImplementedError('To be implemented by child classes.')
134
135 def get_client_endpoint(self):
136 """Return the client endpoint description."""
137 raise NotImplementedError('To be implemented by child classes.')
138
139 @defer.inlineCallbacks
140 def listen_server(self, server_class, *args, **kwargs):
141 """Start a server in a random port."""
142 from twisted.internet import reactor
143 tidy_class = server_factory_factory(server_class)
144 self.server_factory = tidy_class(*args, **kwargs)
145 self.server_factory._disconnecting = False
146 self.server_factory.protocol = server_protocol_factory(
147 self.server_factory.protocol)
148 endpoint = endpoints.serverFromString(reactor,
149 self.get_server_endpoint())
150 self.listener = yield endpoint.listen(self.server_factory)
151 defer.returnValue(self.server_factory)
152
153 @defer.inlineCallbacks
154 def connect_client(self, client_class, *args, **kwargs):
155 """Conect a client to a given server."""
156 from twisted.internet import reactor
157
158 if self.server_factory is None:
159 raise ValueError('Server Factory was not provided.')
160 if self.listener is None:
161 raise ValueError('%s has not started listening.',
162 self.server_factory)
163
164 self.client_factory = client_class(*args, **kwargs)
165 self.client_factory._disconnecting = False
166 self.client_factory.protocol = client_protocol_factory(
167 self.client_factory.protocol)
168 self.client_factory.testserver_on_connection_lost = defer.Deferred()
169 endpoint = endpoints.clientFromString(reactor,
170 self.get_client_endpoint())
171 self.connector = yield endpoint.connect(self.client_factory)
172 defer.returnValue(self.client_factory)
173
174 def clean_up(self):
175 """Action to be performed for clean up."""
176 if self.server_factory is None or self.listener is None:
177 # nothing to clean
178 return defer.succeed(None)
179
180 if self.listener and self.connector:
181 # clean client and server
182 self.server_factory._disconnecting = True
183 self.client_factory._disconnecting = True
184 d = defer.maybeDeferred(self.listener.stopListening)
185 self.connector.transport.loseConnection()
186 if self.server_factory.testserver_on_connection_lost:
187 return defer.gatherResults(
188 [d,
189 self.client_factory.testserver_on_connection_lost,
190 self.server_factory.testserver_on_connection_lost])
191 else:
192 return defer.gatherResults(
193 [d,
194 self.client_factory.testserver_on_connection_lost])
195 if self.listener:
196 # just clean the server since there is no client
197 # pylint: disable=W0201
198 self.server_factory._disconnecting = True
199 return defer.maybeDeferred(self.listener.stopListening)
200 # pylint: enable=W0201
201
202
203class TidyTCPServer(TidySocketServer):
204 """A tidy tcp domain sockets server."""
205
206 client_endpoint_pattern = 'tcp:host=127.0.0.1:port=%s'
207 server_endpoint_pattern = 'tcp:0:interface=127.0.0.1'
208
209 def get_server_endpoint(self):
210 """Return the server endpoint description."""
211 return self.server_endpoint_pattern
212
213 def get_client_endpoint(self):
214 """Return the client endpoint description."""
215 if self.server_factory is None:
216 raise ValueError('Server Factory was not provided.')
217 if self.listener is None:
218 raise ValueError('%s has not started listening.',
219 self.server_factory)
220 return self.client_endpoint_pattern % self.listener.getHost().port
221
222
223class TidyUnixServer(TidySocketServer):
224 """A tidy unix domain sockets server."""
225
226 client_endpoint_pattern = 'unix:path=%s'
227 server_endpoint_pattern = 'unix:%s'
228
229 def __init__(self):
230 """Create a new instance."""
231 super(TidyUnixServer, self).__init__()
232 self.temp_dir = tempfile.mkdtemp()
233 self.path = os.path.join(self.temp_dir, 'tidy_unix_server')
234
235 def get_server_endpoint(self):
236 """Return the server endpoint description."""
237 return self.server_endpoint_pattern % self.path
238
239 def get_client_endpoint(self):
240 """Return the client endpoint description."""
241 return self.client_endpoint_pattern % self.path
242
243 def clean_up(self):
244 """Action to be performed for clean up."""
245 result = super(TidyUnixServer, self).clean_up()
246 # remove the dir once we are disconnected
247 result.addCallback(lambda _: shutil.rmtree(self.temp_dir))
248 return result
249
250
251class ServerTestCase(BaseTestCase):
252 """Base test case for tidy servers."""
253
254 @defer.inlineCallbacks
255 def setUp(self):
256 """Set the diff tests."""
257 yield super(ServerTestCase, self).setUp()
258
259 try:
260 self.server_runner = self.get_server()
261 except NotImplementedError:
262 self.server_runner = None
263
264 self.server_factory = None
265 self.client_factory = None
266 self.server_disconnected = None
267 self.client_connected = None
268 self.client_disconnected = None
269 self.listener = None
270 self.connector = None
271 self.addCleanup(self.tear_down_server_client)
272
273 def get_server(self):
274 """Return the server to be used to run the tests."""
275 raise NotImplementedError('To be implemented by child classes.')
276
277 @defer.inlineCallbacks
278 def listen_server(self, server_class, *args, **kwargs):
279 """Listen a server.
280
281 The method takes the server class and the arguments that should be
282 passed to the server constructor.
283 """
284 self.server_factory = yield self.server_runner.listen_server(
285 server_class, *args, **kwargs)
286 self.server_disconnected = \
287 self.server_factory.testserver_on_connection_lost
288 self.listener = self.server_runner.listener
289
290 @defer.inlineCallbacks
291 def connect_client(self, client_class, *args, **kwargs):
292 """Connect the client.
293
294 The method takes the client factory class and the arguments that
295 should be passed to the client constructor.
296 """
297 self.client_factory = yield self.server_runner.connect_client(
298 client_class, *args, **kwargs)
299 self.client_disconnected = \
300 self.client_factory.testserver_on_connection_lost
301 self.connector = self.server_runner.connector
302
303 def tear_down_server_client(self):
304 """Clean the server and client."""
305 if self.server_runner:
306 return self.server_runner.clean_up()
307
308
309class TCPServerTestCase(ServerTestCase):
310 """Test that uses a single twisted server."""
311
312 def get_server(self):
313 """Return the server to be used to run the tests."""
314 return TidyTCPServer()
315
316
317class UnixServerTestCase(ServerTestCase):
318 """Test that uses a single twisted server."""
319
320 def get_server(self):
321 """Return the server to be used to run the tests."""
322 return TidyUnixServer()
323
324
325class PbServerTestCase(ServerTestCase):
326 """Test a pb server."""
327
328 def get_server(self):
329 """Return the server to be used to run the tests."""
330 raise NotImplementedError('To be implemented by child classes.')
331
332 @defer.inlineCallbacks
333 def listen_server(self, *args, **kwargs):
334 """Listen a pb server."""
335 yield super(PbServerTestCase, self).listen_server(pb.PBServerFactory,
336 *args, **kwargs)
337
338 @defer.inlineCallbacks
339 def connect_client(self, *args, **kwargs):
340 """Connect a pb client."""
341 yield super(PbServerTestCase, self).connect_client(pb.PBClientFactory,
342 *args, **kwargs)
343
344
345class TCPPbServerTestCase(PbServerTestCase):
346 """Test a pb server over TCP."""
347
348 def get_server(self):
349 """Return the server to be used to run the tests."""
350 return TidyTCPServer()
351
352
353class UnixPbServerTestCase(PbServerTestCase):
354 """Test a pb server over Unix domain sockets."""
355
356 def get_server(self):
357 """Return the server to be used to run the tests."""
358 return TidyUnixServer()
0359
=== added directory 'contrib/devtools/testing'
=== added file 'contrib/devtools/testing/__init__.py'
--- contrib/devtools/testing/__init__.py 1970-01-01 00:00:00 +0000
+++ contrib/devtools/testing/__init__.py 2018-06-03 23:09:20 +0000
@@ -0,0 +1,1 @@
1"""Testing helpers."""
02
=== added file 'contrib/devtools/testing/txcheck.py'
--- contrib/devtools/testing/txcheck.py 1970-01-01 00:00:00 +0000
+++ contrib/devtools/testing/txcheck.py 2018-06-03 23:09:20 +0000
@@ -0,0 +1,381 @@
1# -*- coding: utf-8 -*-
2
3# Author: Tim Cole <tim.cole@canonical.com>
4#
5# Copyright 2011-2012 Canonical Ltd.
6#
7# This program is free software: you can redistribute it and/or modify it
8# under the terms of the GNU General Public License version 3, as published
9# by the Free Software Foundation.
10#
11# This program is distributed in the hope that it will be useful, but
12# WITHOUT ANY WARRANTY; without even the implied warranties of
13# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
14# PURPOSE. See the GNU General Public License for more details.
15#
16# You should have received a copy of the GNU General Public License along
17# with this program. If not, see <http://www.gnu.org/licenses/>.
18#
19# In addition, as a special exception, the copyright holders give
20# permission to link the code of portions of this program with the
21# OpenSSL library under certain conditions as described in each
22# individual source file, and distribute linked combinations
23# including the two.
24# You must obey the GNU General Public License in all respects
25# for all of the code used other than OpenSSL. If you modify
26# file(s) with this exception, you may extend this exception to your
27# version of the file(s), but you are not obligated to do so. If you
28# do not wish to do so, delete this exception statement from your
29# version. If you delete this exception statement from all source
30# files in the program, then also delete it here.
31"""Utilities for performing correctness checks."""
32
33import sys
34import ast
35from inspect import getsource
36from textwrap import dedent
37from itertools import takewhile
38from unittest import TestCase, TestSuite, TestResult
39
40from twisted.trial.unittest import TestCase as TwistedTestCase
41
42
43def type_to_name(type_obj):
44 """Return a name for a type."""
45 package_name = getattr(type_obj, '__module__', None)
46 if package_name:
47 return "%s.%s" % (package_name, type_obj.__name__)
48 else:
49 return type_obj.__name__
50
51
52class Problem(AssertionError):
53 """An object representing a problem in a method."""
54
55 def __init__(self, method, test_class, ancestor_class):
56 """Initialize an instance."""
57 super(Problem, self).__init__()
58 self.method = method
59 self.test_class = test_class
60 self.ancestor_class = ancestor_class
61
62 def __eq__(self, other):
63 """Test equality."""
64 return type(self) == type(other) and self.__dict__ == other.__dict__
65
66 def __ne__(self, other):
67 """Test inequality."""
68 return not (self == other)
69
70 def __hash__(self):
71 """Return hash."""
72 member_hash = 0
73 for (key, value) in self.__dict__.items():
74 member_hash ^= hash(key) ^ hash(value)
75 return hash(type(self)) ^ member_hash
76
77 def __str__(self):
78 """Return a friendlier representation."""
79 if self.ancestor_class != self.test_class:
80 method_string = ("%s in ancestor method %s.%s" %
81 (type_to_name(self.test_class),
82 type_to_name(self.ancestor_class),
83 self.method))
84 else:
85 method_string = ("%s.%s" %
86 (type_to_name(self.test_class), self.method))
87 return ("%s for %s" % (type(self).__name__, method_string))
88
89 def __repr__(self):
90 """Return representation string."""
91 return "<%s %r>" % (type(self), self.__dict__)
92
93
94class MethodShadowed(Problem):
95 """Problem when trial's run method is shadowed."""
96
97
98class SuperResultDiscarded(Problem):
99 """Problem when callback chains are broken."""
100
101
102class SuperNotCalled(Problem):
103 """Problem when super isn't called."""
104
105
106class MissingInlineCallbacks(Problem):
107 """Problem when the inlineCallbacks decorator is missing."""
108
109
110class MissingReturnValue(Problem):
111 """Problem when there's no return value."""
112
113
114def match_type(expected_type):
115 """Return predicate matching nodes of given type."""
116 return lambda node: isinstance(node, expected_type)
117
118
119def match_equal(expected_value):
120 """Return predicate matching nodes equaling the given value."""
121 return lambda node: expected_value == node
122
123
124def match_in(expected_values):
125 """Return predicate matching node if in collection of expected values."""
126 return lambda node: node in expected_values
127
128
129def match_not_none():
130 """Returns a predicate matching nodes which are not None."""
131 return lambda node: node is not None
132
133
134def match_any(*subtests):
135 """Return short-circuiting predicate matching any given subpredicate."""
136 if len(subtests) == 1:
137 return subtests[0]
138 else:
139
140 def test(node):
141 """Try each subtest until we find one that passes."""
142 for subtest in subtests:
143 if subtest(node):
144 return True
145 return False
146
147 return test
148
149
150def match_all(*subtests):
151 """Return short-circuiting predicate matching all given subpredicates."""
152 if len(subtests) == 1:
153 return subtests[0]
154 else:
155
156 def test(node):
157 """Try each subtest until we find one that fails."""
158 for subtest in subtests:
159 if not subtest(node):
160 return False
161 return True
162
163 return test
164
165
166def match_attr(attr_name, *tests):
167 """Return predicate matching subpredicates against an attribute value."""
168 return lambda node: match_all(*tests)(getattr(node, attr_name))
169
170
171def match_path(initial_test, *components):
172 """Return predicate which recurses into the tree via given attributes."""
173 components = list(components)
174 components.reverse()
175
176 def test(node):
177 return True
178
179 for component in components:
180 attr_name = component[0]
181 subtests = component[1:]
182 test = match_attr(attr_name, match_all(match_all(*subtests), test))
183 return match_all(initial_test, test)
184
185
186def match_child(*tests):
187 """Return predicate which tests any child."""
188 subtest = match_all(*tests)
189
190 def test(node):
191 """Try each child until we find one that matches."""
192 for child in ast.iter_child_nodes(node):
193 if subtest(child):
194 return True
195 return False
196
197 return test
198
199
200def match_descendant(subtest, prune):
201 """Return predicate which tests a node and any descendants."""
202
203 def test(node):
204 """Recursively (breadth-first) search for a matching node."""
205 for child in ast.iter_child_nodes(node):
206 if prune(child):
207 continue
208 if subtest(child) or test(child):
209 return True
210 return False
211
212 return test
213
214
215def matches(node, *tests):
216 """Convenience function to try predicates on a node."""
217 return match_all(*tests)(node)
218
219
220def any_matches(nodes, *tests):
221 """Convenience function to try predicates on any of a sequence of nodes."""
222 test = match_all(*tests)
223 for node in nodes:
224 if test(node):
225 return True
226 return False
227
228
229def iter_matching_child_nodes(node, *tests):
230 """Yields every matching child node."""
231 test = match_all(*tests)
232 for child in ast.iter_child_nodes(node):
233 if test(child):
234 yield child
235
236
237SETUP_FUNCTION_NAMES = ('setUp', 'tearDown')
238SETUP_FUNCTION = match_path(match_type(ast.FunctionDef),
239 ('name', match_in(SETUP_FUNCTION_NAMES)))
240
241SUPER = match_path(match_type(ast.Call),
242 ('func', match_type(ast.Attribute)),
243 ('value', match_type(ast.Call)),
244 ('func', match_type(ast.Name)),
245 ('id', match_equal("super")))
246
247BARE_SUPER = match_path(match_type(ast.Expr),
248 ('value', SUPER))
249
250YIELD = match_type(ast.Yield)
251
252INLINE_CALLBACKS_DECORATOR = \
253 match_any(match_path(match_type(ast.Attribute),
254 ('attr', match_equal('inlineCallbacks'))),
255 match_path(match_type(ast.Name),
256 ('id', match_equal('inlineCallbacks'))))
257
258RETURN_VALUE = \
259 match_path(match_type(ast.Return),
260 ('value', match_not_none()))
261
262DEFS = match_any(match_type(ast.ClassDef),
263 match_type(ast.FunctionDef))
264
265
266def find_problems(class_to_check):
267 """Check twisted test setup in a given test class."""
268 mro = class_to_check.__mro__
269 if TwistedTestCase not in mro:
270 return set()
271
272 problems = set()
273
274 ancestry = takewhile(lambda c: c != TwistedTestCase, mro)
275 for ancestor_class in ancestry:
276 if 'run' in ancestor_class.__dict__:
277 problem = MethodShadowed(method='run',
278 test_class=class_to_check,
279 ancestor_class=ancestor_class)
280 problems.add(problem)
281
282 source = dedent(getsource(ancestor_class))
283 tree = ast.parse(source)
284 # the top level of the tree is a Module
285 class_node = tree.body[0]
286
287 # Check setUp/tearDown
288 for def_node in iter_matching_child_nodes(class_node, SETUP_FUNCTION):
289 if matches(def_node, match_child(BARE_SUPER)):
290 # Superclass method called, but its result wasn't used
291 problem = SuperResultDiscarded(method=def_node.name,
292 test_class=class_to_check,
293 ancestor_class=ancestor_class)
294 problems.add(problem)
295 if not matches(def_node, match_descendant(SUPER, DEFS)):
296 # The call to the overridden superclass method is missing
297 problem = SuperNotCalled(method=def_node.name,
298 test_class=class_to_check,
299 ancestor_class=ancestor_class)
300 problems.add(problem)
301
302 decorators = def_node.decorator_list
303
304 if matches(def_node, match_descendant(YIELD, DEFS)):
305 # Yield was used, making this a generator
306 if not any_matches(decorators, INLINE_CALLBACKS_DECORATOR):
307 # ...but the inlineCallbacks decorator is missing
308 problem = MissingInlineCallbacks(
309 method=def_node.name,
310 test_class=class_to_check,
311 ancestor_class=ancestor_class)
312 problems.add(problem)
313 else:
314 if not matches(def_node, match_descendant(RETURN_VALUE, DEFS)):
315 # The function fails to return a deferred
316 problem = MissingReturnValue(
317 method=def_node.name,
318 test_class=class_to_check,
319 ancestor_class=ancestor_class)
320 problems.add(problem)
321
322 return problems
323
324
325def get_test_classes(suite):
326 """Return all the unique test classes involved in a suite."""
327 classes = set()
328
329 def find_classes(suite_or_test):
330 """Recursively find all the test classes."""
331 if isinstance(suite_or_test, TestSuite):
332 for subtest in suite_or_test:
333 find_classes(subtest)
334 else:
335 classes.add(type(suite_or_test))
336
337 find_classes(suite)
338
339 return classes
340
341
342def make_check_testcase(tests):
343 """Make TestCase which checks the given twisted tests."""
344
345 class TXCheckTest(TestCase):
346 """Test case which checks the test classes for problems."""
347
348 def runTest(self): # pylint: disable=C0103
349 """Do nothing."""
350
351 def run(self, result=None):
352 """Check all the test classes for problems."""
353 if result is None:
354 result = TestResult()
355
356 test_classes = set()
357
358 for test_object in tests:
359 test_classes |= get_test_classes(test_object)
360
361 for test_class in test_classes:
362 problems = find_problems(test_class)
363 for problem in problems:
364 try:
365 raise problem
366 except Problem:
367 result.addFailure(self, sys.exc_info())
368
369 return TXCheckTest()
370
371
372class TXCheckSuite(TestSuite):
373 """Test suite which checks twisted tests."""
374
375 def __init__(self, tests=()):
376 """Initialize with the given tests, and add a special test."""
377
378 tests = list(tests)
379 tests.insert(0, make_check_testcase(self))
380
381 super(TXCheckSuite, self).__init__(tests)
0382
=== added file 'contrib/devtools/testing/txwebserver.py'
--- contrib/devtools/testing/txwebserver.py 1970-01-01 00:00:00 +0000
+++ contrib/devtools/testing/txwebserver.py 2018-06-03 23:09:20 +0000
@@ -0,0 +1,125 @@
1# -*- coding: utf-8 -*-
2# Copyright 2012 Canonical Ltd.
3# Copyright 2018 Chicharreros (https://launchpad.net/~chicharreros)
4#
5# This program is free software: you can redistribute it and/or modify it
6# under the terms of the GNU General Public License version 3, as published
7# by the Free Software Foundation.
8#
9# This program is distributed in the hope that it will be useful, but
10# WITHOUT ANY WARRANTY; without even the implied warranties of
11# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
12# PURPOSE. See the GNU General Public License for more details.
13#
14# You should have received a copy of the GNU General Public License along
15# with this program. If not, see <http://www.gnu.org/licenses/>.
16#
17# In addition, as a special exception, the copyright holders give
18# permission to link the code of portions of this program with the
19# OpenSSL library under certain conditions as described in each
20# individual source file, and distribute linked combinations
21# including the two.
22# You must obey the GNU General Public License in all respects
23# for all of the code used other than OpenSSL. If you modify
24# file(s) with this exception, you may extend this exception to your
25# version of the file(s), but you are not obligated to do so. If you
26# do not wish to do so, delete this exception statement from your
27# version. If you delete this exception statement from all source
28# files in the program, then also delete it here.
29
30"""A tx based web server."""
31
32from __future__ import unicode_literals
33
34from twisted.internet import defer, reactor, ssl
35from twisted.protocols.policies import WrappingFactory
36from twisted.web import server
37
38from devtools.testcases.txsocketserver import server_protocol_factory
39
40# no init method + twisted common warnings
41# pylint: disable=W0232, C0103, E1101
42
43
44class BaseWebServer(object):
45 """Webserver used to perform requests in tests."""
46
47 def __init__(self, root_resource, scheme):
48 """Create and start the instance.
49
50 The ssl_settings parameter contains a dictionary with the key and cert
51 that will be used to perform ssl connections. The root_resource
52 contains the resource with all its childre.
53 """
54 self.root = root_resource
55 self.scheme = scheme
56 self.port = None
57 # use an http.HTTPFactory that was modified to ensure that we have
58 # clean close connections
59 self.site = server.Site(self.root, timeout=None)
60 self.wrapper = WrappingFactory(self.site)
61 self.wrapper.testserver_on_connection_lost = defer.Deferred()
62 self.wrapper.protocol = server_protocol_factory(self.wrapper.protocol)
63 self.wrapper._disconnecting = False
64
65 def listen(self, site):
66 """Listen a port to allow the tests."""
67 raise NotImplementedError('Base abstract class.')
68
69 def get_iri(self):
70 """Build the iri for this mock server."""
71 return "{scheme}://127.0.0.1:{port}/".format(scheme=self.scheme,
72 port=self.get_port())
73
74 def get_port(self):
75 """Return the port where we are listening."""
76 return self.port.getHost().port
77
78 def start(self):
79 """Start the service."""
80 self.port = self.listen(self.wrapper)
81
82 def stop(self):
83 """Shut it down."""
84 if self.port:
85 self.wrapper._disconnecting = True
86 connected = self.wrapper.protocols.keys()
87 if connected:
88 for con in connected:
89 con.transport.loseConnection()
90 else:
91 self.wrapper.testserver_on_connection_lost = \
92 defer.succeed(None)
93 d = defer.maybeDeferred(self.port.stopListening)
94 return defer.gatherResults(
95 [d,
96 self.wrapper.testserver_on_connection_lost])
97 return defer.succeed(None)
98
99
100class HTTPWebServer(BaseWebServer):
101 """A Webserver that listens to http connections."""
102
103 def __init__(self, root_resource):
104 """Create a new instance."""
105 super(HTTPWebServer, self).__init__(root_resource, 'http')
106
107 def listen(self, site):
108 """Listen a port to allow the tests."""
109 return reactor.listenTCP(0, site)
110
111
112class HTTPSWebServer(BaseWebServer):
113 """A WebServer that listens to https connections."""
114
115 def __init__(self, root_resource, ssl_settings=None):
116 """Create a new instance."""
117 super(HTTPSWebServer, self).__init__(root_resource, 'https')
118 self.ssl_settings = ssl_settings
119
120 def listen(self, site):
121 """Listen a port to allow the tests."""
122 ssl_context = ssl.DefaultOpenSSLContextFactory(
123 self.ssl_settings['key'], self.ssl_settings['cert'])
124
125 return reactor.listenSSL(0, site, ssl_context)
0126
=== added file 'contrib/devtools/utils.py'
--- contrib/devtools/utils.py 1970-01-01 00:00:00 +0000
+++ contrib/devtools/utils.py 2018-06-03 23:09:20 +0000
@@ -0,0 +1,181 @@
1# Copyright 2009-2012 Canonical Ltd.
2# Copyright 2018 Chicharreros (https://launchpad.net/~chicharreros)
3#
4# This program is free software: you can redistribute it and/or modify it
5# under the terms of the GNU General Public License version 3, as published
6# by the Free Software Foundation.
7#
8# This program is distributed in the hope that it will be useful, but
9# WITHOUT ANY WARRANTY; without even the implied warranties of
10# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11# PURPOSE. See the GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License along
14# with this program. If not, see <http://www.gnu.org/licenses/>.
15#
16# In addition, as a special exception, the copyright holders give
17# permission to link the code of portions of this program with the
18# OpenSSL library under certain conditions as described in each
19# individual source file, and distribute linked combinations
20# including the two.
21# You must obey the GNU General Public License in all respects
22# for all of the code used other than OpenSSL. If you modify
23# file(s) with this exception, you may extend this exception to your
24# version of the file(s), but you are not obligated to do so. If you
25# do not wish to do so, delete this exception statement from your
26# version. If you delete this exception statement from all source
27# files in the program, then also delete it here.
28"""Utilities for Ubuntu One developer tools."""
29
30from __future__ import print_function, unicode_literals
31
32import getopt
33import sys
34
35from devtools.errors import UsageError
36__all__ = ['OptionParser']
37
38
39def accumulate_list_attr(class_obj, attr, list_obj, base_class=None):
40 """Get all of the list attributes of attr from the class hierarchy,
41 and return a list of the lists."""
42 for base in class_obj.__bases__:
43 accumulate_list_attr(base, attr, list_obj)
44 if base_class is None or base_class in class_obj.__bases__:
45 list_obj.extend(class_obj.__dict__.get(attr, []))
46
47
48def unpack_padded(length, sequence, default=None):
49 """Pads a sequence to length with value of default.
50
51 Returns a list containing the original and padded values.
52 """
53 newlist = [default] * length
54 newlist[:len(sequence)] = list(sequence)
55 return newlist
56
57
58class OptionParser(dict):
59 """Base options for our test runner."""
60
61 def __init__(self, *args, **kwargs):
62 super(OptionParser, self).__init__(*args, **kwargs)
63
64 # Store info about the options and defaults
65 self.long_opts = []
66 self.short_opts = ''
67 self.docs = {}
68 self.defaults = {}
69 self.synonyms = {}
70 self.dispatch = {}
71
72 # Get the options and defaults
73 for _get in [self._get_flags, self._get_params]:
74 # We don't use variable 'syns' here. It's just to pad the result.
75 # pylint: disable=W0612
76 (long_opts, short_opts, docs, defaults, syns, dispatch) = _get()
77 # pylint: enable=W0612
78 self.long_opts.extend(long_opts)
79 self.short_opts = self.short_opts + short_opts
80 self.docs.update(docs)
81 self.update(defaults)
82 self.defaults.update(defaults)
83 self.synonyms.update(syns)
84 self.dispatch.update(dispatch)
85
86 # We use some camelcase names for trial compatibility here.
87 # pylint: disable=C0103
88 def parseOptions(self, options=None):
89 """Parse the options."""
90 if options is None:
91 options = sys.argv[1:]
92
93 try:
94 opts, args = getopt.getopt(options,
95 self.short_opts, self.long_opts)
96 except getopt.error as e:
97 raise UsageError(e)
98
99 for opt, arg in opts:
100 if opt[1] == '-':
101 opt = opt[2:]
102 else:
103 opt = opt[1:]
104
105 if (opt not in self.synonyms.keys()):
106 raise UsageError('No such options: "%s"' % opt)
107
108 opt = self.synonyms[opt]
109 if self.defaults[opt] is False:
110 self[opt] = True
111 else:
112 self.dispatch[opt](arg)
113
114 try:
115 self.parseArgs(*args)
116 except TypeError:
117 raise UsageError('Wrong number of arguments.')
118
119 self.postOptions()
120
121 def postOptions(self):
122 """Called after options are parsed."""
123
124 def parseArgs(self, *args):
125 """Override to handle extra arguments specially."""
126 # pylint: enable=C0103
127
128 def _parse_arguments(self, arg_type=None, has_default=False):
129 """Parse the arguments as either flags or parameters."""
130 long_opts, short_opts = [], ''
131 docs, defaults, syns, dispatch = {}, {}, {}, {}
132
133 _args = []
134 accumulate_list_attr(self.__class__, arg_type, _args)
135
136 for _arg in _args:
137 try:
138 if has_default:
139 l_opt, s_opt, default, doc, _ = unpack_padded(5, _arg)
140 else:
141 default = False
142 l_opt, s_opt, doc, _ = unpack_padded(4, _arg)
143 except ValueError:
144 raise ValueError('Failed to parse argument: %s' % _arg)
145 if not l_opt:
146 raise ValueError('An option must have a long name.')
147
148 opt_m_name = 'opt_' + l_opt.replace('-', '_')
149 opt_method = getattr(self, opt_m_name, None)
150 if opt_method is not None:
151 docs[l_opt] = getattr(opt_method, '__doc__', None)
152 dispatch[l_opt] = opt_method
153 if docs[l_opt] is None:
154 docs[l_opt] = doc
155 else:
156 docs[l_opt] = doc
157 dispatch[l_opt] = lambda arg: True
158
159 defaults[l_opt] = default
160 if has_default:
161 long_opts.append(l_opt + '=')
162 else:
163 long_opts.append(l_opt)
164
165 syns[l_opt] = l_opt
166 if s_opt is not None:
167 short_opts = short_opts + s_opt
168 if has_default:
169 short_opts = short_opts + ':'
170 syns[s_opt] = l_opt
171
172 return long_opts, short_opts, docs, defaults, syns, dispatch
173
174 def _get_flags(self):
175 """Get the flag options."""
176 return self._parse_arguments(arg_type='optFlags', has_default=False)
177
178 def _get_params(self):
179 """Get the parameters options."""
180 return self._parse_arguments(arg_type='optParameters',
181 has_default=True)
0182
=== added directory 'contrib/dirspec'
=== added file 'contrib/dirspec/__init__.py'
--- contrib/dirspec/__init__.py 1970-01-01 00:00:00 +0000
+++ contrib/dirspec/__init__.py 2018-06-03 23:09:20 +0000
@@ -0,0 +1,16 @@
1# -*- coding: utf-8 -*-
2#
3# Copyright 2011 Canonical Ltd.
4#
5# This program is free software: you can redistribute it and/or modify
6# it under the terms of the GNU Lesser General Public License version 3
7# as published by the Free Software Foundation.
8#
9# This program is distributed in the hope that it will be useful,
10# but WITHOUT ANY WARRANTY; without even the implied warranty of
11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12# GNU Lesser General Public License for more details.
13#
14# You should have received a copy of the GNU Lesser General Public License
15# along with this program. If not, see <http://www.gnu.org/licenses/>.
16"""dirspec package."""
017
=== added file 'contrib/dirspec/basedir.py'
--- contrib/dirspec/basedir.py 1970-01-01 00:00:00 +0000
+++ contrib/dirspec/basedir.py 2018-06-03 23:09:20 +0000
@@ -0,0 +1,159 @@
1# -*- coding: utf-8 -*-
2#
3# Copyright 2011-2012 Canonical Ltd.
4#
5# This program is free software: you can redistribute it and/or modify
6# it under the terms of the GNU Lesser General Public License version 3
7# as published by the Free Software Foundation.
8#
9# This program is distributed in the hope that it will be useful,
10# but WITHOUT ANY WARRANTY; without even the implied warranty of
11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12# GNU Lesser General Public License for more details.
13#
14# You should have received a copy of the GNU Lesser General Public License
15# along with this program. If not, see <http://www.gnu.org/licenses/>.
16"""XDG Base Directory paths."""
17
18from __future__ import unicode_literals, print_function
19
20import os
21
22from dirspec.utils import (default_cache_home,
23 default_config_path, default_config_home,
24 default_data_path, default_data_home,
25 get_env_path, unicode_path)
26
27__all__ = ['xdg_cache_home',
28 'xdg_config_home',
29 'xdg_data_home',
30 'xdg_config_dirs',
31 'xdg_data_dirs',
32 'load_config_paths',
33 'load_data_paths',
34 'load_first_config',
35 'save_config_path',
36 'save_data_path',
37 ]
38
39
40def get_xdg_cache_home():
41 """Get the path for XDG cache directory in user's HOME."""
42 return get_env_path('XDG_CACHE_HOME', default_cache_home)
43
44
45def get_xdg_config_home():
46 """Get the path for XDG config directory in user's HOME."""
47 return get_env_path('XDG_CONFIG_HOME', default_config_home)
48
49
50def get_xdg_data_home():
51 """Get the path for XDG data directory in user's HOME."""
52 return get_env_path('XDG_DATA_HOME', default_data_home)
53
54
55def get_xdg_config_dirs():
56 """Get the paths for the XDG config directories."""
57 result = [get_xdg_config_home()]
58 result.extend([x.encode('utf-8') for x in get_env_path(
59 'XDG_CONFIG_DIRS',
60 default_config_path).decode('utf-8').split(os.pathsep)])
61 return result
62
63
64def get_xdg_data_dirs():
65 """Get the paths for the XDG data directories."""
66 result = [get_xdg_data_home()]
67 result.extend([x.encode('utf-8') for x in get_env_path(
68 'XDG_DATA_DIRS',
69 default_data_path).decode('utf-8').split(os.pathsep)])
70 return result
71
72
73def load_config_paths(*resource):
74 """Iterator of configuration paths.
75
76 Return an iterator which gives each directory named 'resource' in
77 the configuration search path. Information provided by earlier
78 directories should take precedence over later ones (ie, the user's
79 config dir comes first).
80 """
81 resource = os.path.join(*resource)
82 assert not resource.startswith('/')
83 for config_dir in get_xdg_config_dirs():
84 path = os.path.join(config_dir, resource.encode('utf-8'))
85 # access the file system always with unicode
86 # to properly behave in some operating systems
87 if os.path.exists(unicode_path(path)):
88 yield path
89
90
91def load_data_paths(*resource):
92 """Iterator of data paths.
93
94 Return an iterator which gives each directory named 'resource' in
95 the stored data search path. Information provided by earlier
96 directories should take precedence over later ones.
97 """
98 resource = os.path.join(*resource)
99 assert not resource.startswith('/')
100 for data_dir in get_xdg_data_dirs():
101 path = os.path.join(data_dir, resource.encode('utf-8'))
102 # access the file system always with unicode
103 # to properly behave in some operating systems
104 if os.path.exists(unicode_path(path)):
105 yield path
106
107
108def load_first_config(*resource):
109 """Returns the first result from load_config_paths, or None if nothing
110 is found to load.
111 """
112 for path in load_config_paths(*resource):
113 return path
114 return None
115
116
117def save_config_path(*resource):
118 """Path to save configuration.
119
120 Ensure $XDG_CONFIG_HOME/<resource>/ exists, and return its path.
121 'resource' should normally be the name of your application. Use this
122 when SAVING configuration settings. Use the xdg_config_dirs variable
123 for loading.
124 """
125 resource = os.path.join(*resource)
126 assert not resource.startswith('/')
127 path = os.path.join(get_xdg_config_home(), resource.encode('utf-8'))
128 # access the file system always with unicode
129 # to properly behave in some operating systems
130 if not os.path.isdir(unicode_path(path)):
131 os.makedirs(unicode_path(path), 0o700)
132 return path
133
134
135def save_data_path(*resource):
136 """Path to save data.
137
138 Ensure $XDG_DATA_HOME/<resource>/ exists, and return its path.
139 'resource' should normally be the name of your application. Use this
140 when STORING a resource. Use the xdg_data_dirs variable for loading.
141 """
142 resource = os.path.join(*resource)
143 assert not resource.startswith('/')
144 path = os.path.join(get_xdg_data_home(), resource.encode('utf-8'))
145 # access the file system always with unicode
146 # to properly behave in some operating systems
147 if not os.path.isdir(unicode_path(path)):
148 os.makedirs(unicode_path(path), 0o700)
149 return path
150
151
152# pylint: disable=C0103
153xdg_cache_home = get_xdg_cache_home()
154xdg_config_home = get_xdg_config_home()
155xdg_data_home = get_xdg_data_home()
156
157xdg_config_dirs = [x for x in get_xdg_config_dirs() if x]
158xdg_data_dirs = [x for x in get_xdg_data_dirs() if x]
159# pylint: disable=C0103
0160
=== added file 'contrib/dirspec/utils.py'
--- contrib/dirspec/utils.py 1970-01-01 00:00:00 +0000
+++ contrib/dirspec/utils.py 2018-06-03 23:09:20 +0000
@@ -0,0 +1,188 @@
1# -*- coding: utf-8 -*-
2#
3# Copyright 2011-2012 Canonical Ltd.
4#
5# This program is free software: you can redistribute it and/or modify
6# it under the terms of the GNU Lesser General Public License version 3
7# as published by the Free Software Foundation.
8#
9# This program is distributed in the hope that it will be useful,
10# but WITHOUT ANY WARRANTY; without even the implied warranty of
11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12# GNU Lesser General Public License for more details.
13#
14# You should have received a copy of the GNU Lesser General Public License
15# along with this program. If not, see <http://www.gnu.org/licenses/>.
16"""Utilities for multiplatform support of XDG directory handling."""
17
18from __future__ import unicode_literals, print_function
19
20import errno
21import os
22import sys
23
24__all__ = ['user_home',
25 'default_cache_home',
26 'default_config_home',
27 'default_config_path',
28 'default_data_home',
29 'default_data_path',
30 'get_env_path',
31 'get_program_path',
32 'unicode_path',
33 ]
34
35
36def _get_exe_path_frozen_win32(exe_name):
37 """Get path to the helper .exe on packaged windows."""
38 # all the .exes are in the same place on windows:
39 cur_exec_path = os.path.abspath(sys.executable)
40 exe_dir = os.path.dirname(cur_exec_path)
41 return os.path.join(exe_dir, exe_name + ".exe")
42
43
44def _get_exe_path_frozen_darwin(exe_name, app_names):
45 """Get path to the sub-app executable on packaged darwin."""
46
47 sub_app_name = app_names[exe_name]
48 main_app_dir = "".join(__file__.partition(".app")[:-1])
49 main_app_resources_dir = os.path.join(main_app_dir,
50 "Contents",
51 "Resources")
52 exe_bin = os.path.join(main_app_resources_dir,
53 sub_app_name,
54 "Contents", "MacOS",
55 exe_name)
56 return exe_bin
57
58
59def get_program_path(program_name, *args, **kwargs):
60 """Given a program name, returns the path to run that program.
61
62 Raises OSError if the program is not found.
63
64 :param program_name: The name of the program to find. For darwin and win32
65 platforms, the behavior is changed slightly, when sys.frozen is set,
66 to look in the packaged program locations for the program.
67 :param search_dirs: A list of directories to look for the program in. This
68 is only available as a keyword argument.
69 :param app_names: A dict of program names mapped to sub-app names. Used
70 for discovering paths in embedded .app bundles on the darwin platform.
71 This is only available as a keyword argument.
72 :return: The path to the discovered program.
73 """
74 search_dirs = kwargs.get('fallback_dirs', None)
75 app_names = kwargs.get('app_names', None)
76
77 if getattr(sys, "frozen", None) is not None:
78 if sys.platform == 'win32':
79 program_path = _get_exe_path_frozen_win32(program_name)
80 elif sys.platform == 'darwin':
81 program_path = _get_exe_path_frozen_darwin(program_name,
82 app_names)
83 else:
84 raise Exception("Unsupported platform for frozen execution: %r" %
85 sys.platform)
86 else:
87 if search_dirs is not None:
88 for dirname in search_dirs:
89 program_path = os.path.join(dirname, program_name)
90 if os.path.exists(program_path):
91 return program_path
92 else:
93 # Check in normal system $PATH, if no fallback dirs specified
94 from distutils.spawn import find_executable
95 program_path = find_executable(program_name)
96
97 if program_path is None or not os.path.exists(program_path):
98 raise OSError(errno.ENOENT,
99 "Could not find executable %r" % program_name)
100
101 return program_path
102
103
104def get_env_path(key, default):
105 """Get a UTF-8 encoded path from an environment variable."""
106 if key in os.environ:
107 # on windows, environment variables are mbcs bytes
108 # so we must turn them into utf-8 Syncdaemon paths
109 try:
110 path = os.environb.get(key.encode('utf-8'))
111 except AttributeError:
112 path = os.environ[key]
113 return path.decode(sys.getfilesystemencoding()).encode('utf-8')
114 else:
115 if not isinstance(default, bytes):
116 return default.encode('utf-8')
117 return default
118
119
120def unicode_path(utf8path):
121 """Turn an utf8 path into a unicode path."""
122 if isinstance(utf8path, bytes):
123 return utf8path.decode("utf-8")
124 return utf8path
125
126
127def get_special_folders():
128 """ Routine to grab all the Windows Special Folders locations.
129
130 If successful, returns dictionary
131 of shell folder locations indexed on Windows keyword for each;
132 otherwise, returns an empty dictionary.
133 """
134 # pylint: disable=W0621, F0401, E0611
135 special_folders = {}
136
137 if sys.platform == 'win32':
138 from win32com.shell import shell, shellcon
139 # CSIDL_LOCAL_APPDATA = C:\Users\<username>\AppData\Local
140 # CSIDL_PROFILE = C:\Users\<username>
141 # CSIDL_COMMON_APPDATA = C:\ProgramData
142 # More information on these constants at
143 # http://msdn.microsoft.com/en-us/library/bb762494
144
145 # per http://msdn.microsoft.com/en-us/library/windows/desktop/bb762181,
146 # SHGetFolderPath is deprecated, replaced by SHGetKnownFolderPath
147 # (http://msdn.microsoft.com/en-us/library/windows/desktop/bb762188)
148 def get_path(name):
149 return shell.SHGetFolderPath(0, getattr(shellcon, name),
150 None, 0).encode('utf8')
151 special_folders['Personal'] = get_path("CSIDL_PROFILE")
152 special_folders['Local AppData'] = get_path("CSIDL_LOCAL_APPDATA")
153 special_folders['AppData'] = os.path.dirname(
154 special_folders['Local AppData'])
155 special_folders['Common AppData'] = get_path("CSIDL_COMMON_APPDATA")
156
157 return special_folders
158
159
160# pylint: disable=C0103
161if sys.platform == 'win32':
162 special_folders = get_special_folders()
163 user_home = special_folders['Personal']
164 default_config_path = special_folders['Common AppData']
165 default_config_home = special_folders['Local AppData']
166 default_data_path = os.path.join(default_config_path, b'xdg')
167 default_data_home = os.path.join(default_config_home, b'xdg')
168 default_cache_home = os.path.join(default_data_home, b'cache')
169elif sys.platform == 'darwin':
170 user_home = os.path.expanduser(b'~')
171 default_cache_home = os.path.join(user_home, b'Library', b'Caches')
172 default_config_path = b'/Library/Preferences:/etc/xdg'
173 default_config_home = os.path.join(user_home, b'Library', b'Preferences')
174 default_data_path = b':'.join([b'/Library/Application Support',
175 b'/usr/local/share',
176 b'/usr/share'])
177 default_data_home = os.path.join(user_home, b'Library',
178 b'Application Support')
179else:
180 user_home = os.path.expanduser(b'~')
181 default_cache_home = os.path.join(user_home,
182 b'.cache')
183 default_config_path = b'/etc/xdg'
184 default_config_home = os.path.join(user_home,
185 b'.config')
186 default_data_path = b'/usr/local/share:/usr/share'
187 default_data_home = os.path.join(user_home,
188 b'.local', b'share')
0189
=== added file 'contrib/u1trial'
--- contrib/u1trial 1970-01-01 00:00:00 +0000
+++ contrib/u1trial 2018-06-03 23:09:20 +0000
@@ -0,0 +1,41 @@
1#! /usr/bin/python
2#
3# Copyright 2009-2012 Canonical Ltd.
4# Copyright 2018 Chicharreros (https://launchpad.net/~chicharreros)
5#
6# This program is free software: you can redistribute it and/or modify it
7# under the terms of the GNU General Public License version 3, as published
8# by the Free Software Foundation.
9#
10# This program is distributed in the hope that it will be useful, but
11# WITHOUT ANY WARRANTY; without even the implied warranties of
12# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
13# PURPOSE. See the GNU General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License along
16# with this program. If not, see <http://www.gnu.org/licenses/>.
17#
18# In addition, as a special exception, the copyright holders give
19# permission to link the code of portions of this program with the
20# OpenSSL library under certain conditions as described in each
21# individual source file, and distribute linked combinations
22# including the two.
23# You must obey the GNU General Public License in all respects
24# for all of the code used other than OpenSSL. If you modify
25# file(s) with this exception, you may extend this exception to your
26# version of the file(s), but you are not obligated to do so. If you
27# do not wish to do so, delete this exception statement from your
28# version. If you delete this exception statement from all source
29# files in the program, then also delete it here.
30"""Test runner which works with special services and main loops."""
31
32import os
33import sys
34
35sys.path.insert(0, os.path.abspath("."))
36
37from devtools.runners import main
38
39
40if __name__ == '__main__':
41 main()
042
=== modified file 'dependencies-devel.txt'
--- dependencies-devel.txt 2018-03-18 11:59:08 +0000
+++ dependencies-devel.txt 2018-06-03 23:09:20 +0000
@@ -1,5 +1,2 @@
1bzr1bzr
2make2make
3python-mocker
4ubuntuone-dev-tools
5virtualenv
63
=== modified file 'dependencies.txt'
--- dependencies.txt 2018-04-14 23:34:20 +0000
+++ dependencies.txt 2018-06-03 23:09:20 +0000
@@ -1,8 +1,5 @@
1gir1.2-soup-2.41libgirepository1.0-dev
2python-configglue2libgtk2.0-dev
3python-dirspec3pkg-config
4python-distutils-extra4python-dev
5python-gi5virtualenv
6python-protobuf
7python-pyinotify
8python-twisted
96
=== modified file 'magicicadaclient/platform/tests/filesystem_notifications/__init__.py'
--- magicicadaclient/platform/tests/filesystem_notifications/__init__.py 2018-05-19 20:44:54 +0000
+++ magicicadaclient/platform/tests/filesystem_notifications/__init__.py 2018-06-03 23:09:20 +0000
@@ -1,4 +1,5 @@
1# Copyright 2012 Canonical Ltd.1# Copyright 2012 Canonical Ltd.
2# Copyright 2018 Chicharreros (https://launchpad.net/~chicharreros)
2#3#
3# This program is free software: you can redistribute it and/or modify it4# This program is free software: you can redistribute it and/or modify it
4# under the terms of the GNU General Public License version 3, as published5# under the terms of the GNU General Public License version 3, as published
@@ -30,10 +31,10 @@
3031
31from twisted.internet import defer, reactor32from twisted.internet import defer, reactor
3233
33from magicicadaclient.testing import testcase34from devtools.handlers import MementoHandler
34from ubuntuone.devtools.handlers import MementoHandler
35from magicicadaclient.syncdaemon import event_queue, filesystem_manager35from magicicadaclient.syncdaemon import event_queue, filesystem_manager
36from magicicadaclient.syncdaemon.tritcask import Tritcask36from magicicadaclient.syncdaemon.tritcask import Tritcask
37from magicicadaclient.testing import testcase
3738
3839
39class BaseFSMonitorTestCase(testcase.BaseTwistedTestCase):40class BaseFSMonitorTestCase(testcase.BaseTwistedTestCase):
4041
=== modified file 'magicicadaclient/platform/tests/filesystem_notifications/common.py'
--- magicicadaclient/platform/tests/filesystem_notifications/common.py 2018-05-19 20:44:54 +0000
+++ magicicadaclient/platform/tests/filesystem_notifications/common.py 2018-06-03 23:09:20 +0000
@@ -1,8 +1,5 @@
1#
2# Authors: Manuel de la Pena <manuel@canonical.com>
3# Alejandro J. Cura <alecu@canonical.com>
4#
5# Copyright 2011-2012 Canonical Ltd.1# Copyright 2011-2012 Canonical Ltd.
2# Copyright 2018 Chicharreros (https://launchpad.net/~chicharreros)
6#3#
7# This program is free software: you can redistribute it and/or modify it4# This program is free software: you can redistribute it and/or modify it
8# under the terms of the GNU General Public License version 3, as published5# under the terms of the GNU General Public License version 3, as published
@@ -38,16 +35,18 @@
38import itertools35import itertools
3936
40from twisted.internet import defer37from twisted.internet import defer
38
39from devtools.handlers import MementoHandler
41from magicicadaclient.testing.testcase import BaseTwistedTestCase40from magicicadaclient.testing.testcase import BaseTwistedTestCase
42from ubuntuone.devtools.handlers import MementoHandler41from magicicadaclient.platform.filesystem_notifications.pyinotify_agnostic \
43from magicicadaclient.platform.filesystem_notifications.pyinotify_agnostic import (42 import (
44 EventsCodes,43 EventsCodes,
45 ProcessEvent,44 ProcessEvent,
46 IN_CLOSE_WRITE,45 IN_CLOSE_WRITE,
47 IN_CREATE,46 IN_CREATE,
48 IN_DELETE,47 IN_DELETE,
49 IN_OPEN,48 IN_OPEN,
50)49 )
51from magicicadaclient.platform.filesystem_notifications import notify_processor50from magicicadaclient.platform.filesystem_notifications import notify_processor
52from magicicadaclient.platform.filesystem_notifications.monitor.common import (51from magicicadaclient.platform.filesystem_notifications.monitor.common import (
53 FilesystemMonitor,52 FilesystemMonitor,
5453
=== modified file 'magicicadaclient/platform/tests/filesystem_notifications/test_darwin.py'
--- magicicadaclient/platform/tests/filesystem_notifications/test_darwin.py 2018-05-19 20:44:54 +0000
+++ magicicadaclient/platform/tests/filesystem_notifications/test_darwin.py 2018-06-03 23:09:20 +0000
@@ -1,6 +1,7 @@
1# -*- coding: utf-8 *-*1# -*- coding: utf-8 *-*
2#2#
3# Copyright 2012 Canonical Ltd.3# Copyright 2012 Canonical Ltd.
4# Copyright 2018 Chicharreros (https://launchpad.net/~chicharreros)
4#5#
5# This program is free software: you can redistribute it and/or modify it6# This program is free software: you can redistribute it and/or modify it
6# under the terms of the GNU General Public License version 3, as published7# under the terms of the GNU General Public License version 3, as published
@@ -38,9 +39,8 @@
3839
39import fsevents40import fsevents
4041
42from devtools.handlers import MementoHandler
41from magicicadaclient.testing.testcase import BaseTwistedTestCase43from magicicadaclient.testing.testcase import BaseTwistedTestCase
42
43from ubuntuone.devtools.handlers import MementoHandler
44from magicicadaclient.platform.filesystem_notifications.monitor import (44from magicicadaclient.platform.filesystem_notifications.monitor import (
45 common,45 common,
46)46)
@@ -52,13 +52,14 @@
52 Watch,52 Watch,
53 WatchManager,53 WatchManager,
54)54)
55from magicicadaclient.platform.filesystem_notifications.pyinotify_agnostic import (55from magicicadaclient.platform.filesystem_notifications.pyinotify_agnostic \
56 ProcessEvent,56 import (
57 IN_CLOSE_WRITE,57 ProcessEvent,
58 IN_CREATE,58 IN_CLOSE_WRITE,
59 IN_DELETE,59 IN_CREATE,
60 IN_OPEN,60 IN_DELETE,
61)61 IN_OPEN,
62 )
62from magicicadaclient.platform.tests.filesystem_notifications import (63from magicicadaclient.platform.tests.filesystem_notifications import (
63 BaseFSMonitorTestCase,64 BaseFSMonitorTestCase,
64 common as common_tests,65 common as common_tests,
6566
=== modified file 'magicicadaclient/platform/tests/filesystem_notifications/test_filesystem_notifications.py'
--- magicicadaclient/platform/tests/filesystem_notifications/test_filesystem_notifications.py 2018-05-19 20:44:54 +0000
+++ magicicadaclient/platform/tests/filesystem_notifications/test_filesystem_notifications.py 2018-06-03 23:09:20 +0000
@@ -1,6 +1,7 @@
1# -*- coding: utf-8 -*-1# -*- coding: utf-8 -*-
2#2#
3# Copyright 2009-2012 Canonical Ltd.3# Copyright 2009-2012 Canonical Ltd.
4# Copyright 2018 Chicharreros (https://launchpad.net/~chicharreros)
4#5#
5# This program is free software: you can redistribute it and/or modify it6# This program is free software: you can redistribute it and/or modify it
6# under the terms of the GNU General Public License version 3, as published7# under the terms of the GNU General Public License version 3, as published
@@ -33,8 +34,8 @@
3334
34from twisted.internet import defer, reactor35from twisted.internet import defer, reactor
35from twisted.trial import unittest36from twisted.trial import unittest
36from ubuntuone.devtools.handlers import MementoHandler
3737
38from devtools.handlers import MementoHandler
38from magicicadaclient.testing.testcase import (39from magicicadaclient.testing.testcase import (
39 BaseTwistedTestCase,40 BaseTwistedTestCase,
40 FakeVolumeManager,41 FakeVolumeManager,
4142
=== modified file 'magicicadaclient/platform/tests/filesystem_notifications/test_fsevents_daemon.py'
--- magicicadaclient/platform/tests/filesystem_notifications/test_fsevents_daemon.py 2018-05-19 20:44:54 +0000
+++ magicicadaclient/platform/tests/filesystem_notifications/test_fsevents_daemon.py 2018-06-03 23:09:20 +0000
@@ -1,6 +1,7 @@
1# -*- coding: utf-8 *-*1# -*- coding: utf-8 *-*
2#2#
3# Copyright 2012 Canonical Ltd.3# Copyright 2012 Canonical Ltd.
4# Copyright 2018 Chicharreros (https://launchpad.net/~chicharreros)
4#5#
5# This program is free software: you can redistribute it and/or modify it6# This program is free software: you can redistribute it and/or modify it
6# under the terms of the GNU General Public License version 3, as published7# under the terms of the GNU General Public License version 3, as published
@@ -32,24 +33,20 @@
3233
33from twisted.internet import defer, protocol34from twisted.internet import defer, protocol
3435
36from devtools.testcases.txsocketserver import TidyUnixServer
35from magicicadaclient.testing.testcase import BaseTwistedTestCase37from magicicadaclient.testing.testcase import BaseTwistedTestCase
36from magicicadaclient import fseventsd38from magicicadaclient import fseventsd
37try:
38 from ubuntuone.devtools.testcases import skipIf
39 from ubuntuone.devtools.testcases.txsocketserver import TidyUnixServer
40except ImportError:
41 from ubuntuone.devtools.testcase import skipIf
42 TidyUnixServer = None
43from magicicadaclient.platform.filesystem_notifications.monitor.darwin import (39from magicicadaclient.platform.filesystem_notifications.monitor.darwin import (
44 fsevents_daemon,40 fsevents_daemon,
45)41)
46from magicicadaclient.platform.filesystem_notifications.pyinotify_agnostic import (42from magicicadaclient.platform.filesystem_notifications.pyinotify_agnostic \
47 IN_CREATE,43 import (
48 IN_DELETE,44 IN_CREATE,
49 IN_MODIFY,45 IN_DELETE,
50 IN_MOVED_FROM,46 IN_MODIFY,
51 IN_MOVED_TO,47 IN_MOVED_FROM,
52)48 IN_MOVED_TO,
49 )
5350
5451
55class FakeServerProtocol(protocol.Protocol):52class FakeServerProtocol(protocol.Protocol):
@@ -469,8 +466,6 @@
469 yield self.monitor.add_watch(dirpath)466 yield self.monitor.add_watch(dirpath)
470 self.assertNotIn('add_path', self.protocol.called)467 self.assertNotIn('add_path', self.protocol.called)
471468
472 @skipIf(TidyUnixServer is None,
473 'Testcases from txsocketserver not availble.')
474 @defer.inlineCallbacks469 @defer.inlineCallbacks
475 def test_is_available_monitor_running(self):470 def test_is_available_monitor_running(self):
476 """Test the method when it is indeed running."""471 """Test the method when it is indeed running."""
477472
=== modified file 'magicicadaclient/platform/tests/ipc/test_linux.py'
--- magicicadaclient/platform/tests/ipc/test_linux.py 2018-05-19 20:44:54 +0000
+++ magicicadaclient/platform/tests/ipc/test_linux.py 2018-06-03 23:09:20 +0000
@@ -1,6 +1,7 @@
1# -*- coding: utf-8 -*-1# -*- coding: utf-8 -*-
2#2#
3# Copyright 2009-2012 Canonical Ltd.3# Copyright 2009-2012 Canonical Ltd.
4# Copyright 2018 Chicharreros (https://launchpad.net/~chicharreros)
4#5#
5# This program is free software: you can redistribute it and/or modify it6# This program is free software: you can redistribute it and/or modify it
6# under the terms of the GNU General Public License version 3, as published7# under the terms of the GNU General Public License version 3, as published
@@ -33,11 +34,8 @@
33import dbus34import dbus
3435
35from twisted.internet import defer36from twisted.internet import defer
36try:
37 from ubuntuone.devtools.testcases.dbus import DBusTestCase
38except ImportError:
39 from ubuntuone.devtools.testcase import DBusTestCase
4037
38from devtools.testcases.dbus import DBusTestCase
41from magicicadaclient.testing.testcase import (39from magicicadaclient.testing.testcase import (
42 FakeMainTestCase,40 FakeMainTestCase,
43 FakedService,41 FakedService,
4442
=== modified file 'magicicadaclient/platform/tests/ipc/test_perspective_broker.py'
--- magicicadaclient/platform/tests/ipc/test_perspective_broker.py 2018-05-19 20:44:54 +0000
+++ magicicadaclient/platform/tests/ipc/test_perspective_broker.py 2018-06-03 23:09:20 +0000
@@ -1,6 +1,7 @@
1# -*- coding: utf-8 -*-1# -*- coding: utf-8 -*-
2#2#
3# Copyright 2011-2012 Canonical Ltd.3# Copyright 2011-2012 Canonical Ltd.
4# Copyright 2018 Chicharreros (https://launchpad.net/~chicharreros)
4#5#
5# This program is free software: you can redistribute it and/or modify it6# This program is free software: you can redistribute it and/or modify it
6# under the terms of the GNU General Public License version 3, as published7# under the terms of the GNU General Public License version 3, as published
@@ -41,14 +42,8 @@
41)42)
42from twisted.trial.unittest import TestCase43from twisted.trial.unittest import TestCase
4344
44from magicicadaclient.testing.testcase import (45from devtools.testcases import skipIf, skipIfOS
45 FakedService,46from magicicadaclient.testing.testcase import FakedService, FakeMainTestCase
46 FakeMainTestCase,
47)
48try:
49 from ubuntuone.devtools.testcases import skipIf, skipIfOS
50except ImportError:
51 from ubuntuone.devtools.testcase import skipIf, skipIfOS
52from magicicadaclient.platform.ipc import perspective_broker as ipc47from magicicadaclient.platform.ipc import perspective_broker as ipc
53from magicicadaclient.platform.ipc.perspective_broker import (48from magicicadaclient.platform.ipc.perspective_broker import (
54 Config,49 Config,
5550
=== modified file 'magicicadaclient/platform/tests/os_helper/test_darwin.py'
--- magicicadaclient/platform/tests/os_helper/test_darwin.py 2018-05-19 20:44:54 +0000
+++ magicicadaclient/platform/tests/os_helper/test_darwin.py 2018-06-03 23:09:20 +0000
@@ -1,7 +1,7 @@
1# -*- encoding: utf-8 -*-1# -*- encoding: utf-8 -*-
2# tests.platform.os_helper - darwin platform tests
3#2#
4# Copyright 2012 Canonical Ltd.3# Copyright 2012 Canonical Ltd.
4# Copyright 2018 Chicharreros (https://launchpad.net/~chicharreros)
5#5#
6# This program is free software: you can redistribute it and/or modify it6# This program is free software: you can redistribute it and/or modify it
7# under the terms of the GNU General Public License version 3, as published7# under the terms of the GNU General Public License version 3, as published
@@ -33,13 +33,9 @@
33import os33import os
3434
35from twisted.internet import defer35from twisted.internet import defer
36from ubuntuone.devtools.handlers import MementoHandler
3736
38from magicicadaclient.platform import (37from devtools.handlers import MementoHandler
39 move_to_trash,38from magicicadaclient.platform import move_to_trash, open_file, stat_path
40 open_file,
41 stat_path,
42)
43from magicicadaclient.platform.os_helper import darwin39from magicicadaclient.platform.os_helper import darwin
44from magicicadaclient.platform.tests.os_helper import test_os_helper40from magicicadaclient.platform.tests.os_helper import test_os_helper
4541
4642
=== modified file 'magicicadaclient/platform/tests/os_helper/test_linux.py'
--- magicicadaclient/platform/tests/os_helper/test_linux.py 2018-05-19 20:44:54 +0000
+++ magicicadaclient/platform/tests/os_helper/test_linux.py 2018-06-03 23:09:20 +0000
@@ -1,4 +1,5 @@
1# Copyright 2010-2013 Canonical Ltd.1# Copyright 2010-2013 Canonical Ltd.
2# Copyright 2018 Chicharreros (https://launchpad.net/~chicharreros)
2#3#
3# This program is free software: you can redistribute it and/or modify it4# This program is free software: you can redistribute it and/or modify it
4# under the terms of the GNU General Public License version 3, as published5# under the terms of the GNU General Public License version 3, as published
@@ -30,15 +31,11 @@
30import os31import os
3132
32from twisted.internet import defer33from twisted.internet import defer
33from ubuntuone.devtools.handlers import MementoHandler
3434
35from devtools.handlers import MementoHandler
36from magicicadaclient.platform import move_to_trash, open_file, stat_path
35from magicicadaclient.platform.tests.os_helper import test_os_helper37from magicicadaclient.platform.tests.os_helper import test_os_helper
36from magicicadaclient.platform.os_helper import linux38from magicicadaclient.platform.os_helper import linux
37from magicicadaclient.platform import (
38 move_to_trash,
39 open_file,
40 stat_path,
41)
4239
4340
44class OSWrapperTests(test_os_helper.OSWrapperTests):41class OSWrapperTests(test_os_helper.OSWrapperTests):
4542
=== modified file 'magicicadaclient/platform/tests/session/test_common.py'
--- magicicadaclient/platform/tests/session/test_common.py 2018-05-19 20:44:54 +0000
+++ magicicadaclient/platform/tests/session/test_common.py 2018-06-03 23:09:20 +0000
@@ -1,5 +1,6 @@
1#1#
2# Copyright 2011-2012 Canonical Ltd.2# Copyright 2011-2012 Canonical Ltd.
3# Copyright 2018 Chicharreros (https://launchpad.net/~chicharreros)
3#4#
4# This program is free software: you can redistribute it and/or modify it5# This program is free software: you can redistribute it and/or modify it
5# under the terms of the GNU General Public License version 3, as published6# under the terms of the GNU General Public License version 3, as published
@@ -30,7 +31,7 @@
30from twisted.internet import defer31from twisted.internet import defer
31from twisted.trial.unittest import TestCase32from twisted.trial.unittest import TestCase
3233
33from ubuntuone.devtools.testcases import skipIfOS34from devtools.testcases import skipIfOS
34from magicicadaclient.platform.session import Inhibitor, INHIBIT_LOGOUT_SUSPEND35from magicicadaclient.platform.session import Inhibitor, INHIBIT_LOGOUT_SUSPEND
3536
3637
3738
=== modified file 'magicicadaclient/platform/tests/session/test_linux.py'
--- magicicadaclient/platform/tests/session/test_linux.py 2018-05-19 20:44:54 +0000
+++ magicicadaclient/platform/tests/session/test_linux.py 2018-06-03 23:09:20 +0000
@@ -1,8 +1,5 @@
1# tests.platform.linux.test_session
2#
3# Author: Alejandro J. Cura <alecu@canonical.com>
4#
5# Copyright 2011-2012 Canonical Ltd.1# Copyright 2011-2012 Canonical Ltd.
2# Copyright 2018 Chicharreros (https://launchpad.net/~chicharreros)
6#3#
7# This program is free software: you can redistribute it and/or modify it4# This program is free software: you can redistribute it and/or modify it
8# under the terms of the GNU General Public License version 3, as published5# under the terms of the GNU General Public License version 3, as published
@@ -30,15 +27,13 @@
30# files in the program, then also delete it here.27# files in the program, then also delete it here.
31"""Tests for the session inhibition DBus client."""28"""Tests for the session inhibition DBus client."""
3229
30import operator
31
33import dbus32import dbus
34import operator
35
36from dbus.mainloop.glib import DBusGMainLoop33from dbus.mainloop.glib import DBusGMainLoop
37from twisted.internet.defer import inlineCallbacks34from twisted.internet.defer import inlineCallbacks
38try:35
39 from ubuntuone.devtools.testcases.dbus import DBusTestCase36from devtools.testcases.dbus import DBusTestCase
40except ImportError:
41 from ubuntuone.devtools.testcase import DBusTestCase
42from magicicadaclient.platform import session37from magicicadaclient.platform import session
4338
44INHIBIT_ALL = (session.INHIBIT_LOGGING_OUT |39INHIBIT_ALL = (session.INHIBIT_LOGGING_OUT |
@@ -137,9 +132,8 @@
137 def test_uninhibit_call(self):132 def test_uninhibit_call(self):
138 """Test the uninhibit call."""133 """Test the uninhibit call."""
139 fakeinhibitor = self.register_fakeserver(134 fakeinhibitor = self.register_fakeserver(
140 session.SESSION_MANAGER_BUSNAME,135 session.SESSION_MANAGER_BUSNAME, session.SESSION_MANAGER_PATH,
141 session.SESSION_MANAGER_PATH,136 FakeGnomeSessionManagerInhibitor)
142 FakeGnomeSessionManagerInhibitor)
143 i = yield session.inhibit_logout_suspend("fake reason")137 i = yield session.inhibit_logout_suspend("fake reason")
144 yield i.cancel()138 yield i.cancel()
145 result = fakeinhibitor.IsInhibited(session.INHIBIT_LOGGING_OUT)139 result = fakeinhibitor.IsInhibited(session.INHIBIT_LOGGING_OUT)
146140
=== modified file 'magicicadaclient/platform/tests/test_tools.py'
--- magicicadaclient/platform/tests/test_tools.py 2018-05-19 20:44:54 +0000
+++ magicicadaclient/platform/tests/test_tools.py 2018-06-03 23:09:20 +0000
@@ -30,15 +30,15 @@
30"""Tests for the syncdaemon tools module."""30"""Tests for the syncdaemon tools module."""
3131
32import os32import os
33
34from collections import defaultdict33from collections import defaultdict
3534
36from twisted.internet import defer, reactor35from twisted.internet import defer, reactor
37from ubuntuone.devtools.handlers import MementoHandler
38from ubuntuone.devtools.testcases import skipTest, skipIfNotOS
3936
37from devtools.handlers import MementoHandler
38from devtools.testcases import skipTest, skipIfNotOS
40from magicicadaclient.testing.testcase import FakeCommand39from magicicadaclient.testing.testcase import FakeCommand
4140from magicicadaclient.platform import tools
41from magicicadaclient.platform.tests import IPCTestCase
42from magicicadaclient.syncdaemon import (42from magicicadaclient.syncdaemon import (
43 action_queue,43 action_queue,
44 event_queue,44 event_queue,
@@ -46,8 +46,6 @@
46 states,46 states,
47 volume_manager,47 volume_manager,
48)48)
49from magicicadaclient.platform import tools
50from magicicadaclient.platform.tests import IPCTestCase
5149
5250
53SOME_ERROR = 'CRASH BOOM BANG'51SOME_ERROR = 'CRASH BOOM BANG'
5452
=== modified file 'magicicadaclient/syncdaemon/tests/test_action_queue.py'
--- magicicadaclient/syncdaemon/tests/test_action_queue.py 2018-05-19 20:44:54 +0000
+++ magicicadaclient/syncdaemon/tests/test_action_queue.py 2018-06-03 23:09:20 +0000
@@ -60,16 +60,8 @@
60from twisted.trial.unittest import TestCase as TwistedTestCase60from twisted.trial.unittest import TestCase as TwistedTestCase
61from zope.interface.verify import verifyObject, verifyClass61from zope.interface.verify import verifyObject, verifyClass
6262
63from magicicadaclient.testing.testcase import (63from devtools import handlers
64 BaseTwistedTestCase,64from devtools.testcases import skipTest
65 DummyClass,
66 FakeActionQueue,
67 FakeCommand,
68 FakeMain,
69 FakeUpload,
70)
71from ubuntuone.devtools import handlers
72from ubuntuone.devtools.testcases import skipTest
73from magicicadaclient import logger, clientdefs65from magicicadaclient import logger, clientdefs
74from magicicadaclient.platform import open_file, platform, path_exists66from magicicadaclient.platform import open_file, platform, path_exists
75from magicicadaclient.syncdaemon import interfaces, config67from magicicadaclient.syncdaemon import interfaces, config
@@ -88,6 +80,14 @@
88from magicicadaclient.syncdaemon import offload_queue80from magicicadaclient.syncdaemon import offload_queue
89from magicicadaclient.syncdaemon.marker import MDMarker81from magicicadaclient.syncdaemon.marker import MDMarker
90from magicicadaclient.syncdaemon.volume_manager import ACCESS_LEVEL_RO82from magicicadaclient.syncdaemon.volume_manager import ACCESS_LEVEL_RO
83from magicicadaclient.testing.testcase import (
84 BaseTwistedTestCase,
85 DummyClass,
86 FakeActionQueue,
87 FakeCommand,
88 FakeMain,
89 FakeUpload,
90)
9191
92PATH = os.path.join(u'~', u'Documents', u'pdfs', u'moño', u'')92PATH = os.path.join(u'~', u'Documents', u'pdfs', u'moño', u'')
93NAME = u'UDF-me'93NAME = u'UDF-me'
@@ -3352,7 +3352,7 @@
33523352
3353 def test_progress_hook(self):3353 def test_progress_hook(self):
3354 """Test the progress hook."""3354 """Test the progress hook."""
3355 self.command.deflated_size = 2*TRANSFER_PROGRESS_THRESHOLD3355 self.command.deflated_size = 2 * TRANSFER_PROGRESS_THRESHOLD
3356 self.command.n_bytes_written_last = 03356 self.command.n_bytes_written_last = 0
33573357
3358 self.command.n_bytes_written = 53358 self.command.n_bytes_written = 5
@@ -3368,8 +3368,8 @@
3368 self.command.n_bytes_written = TRANSFER_PROGRESS_THRESHOLD + 53368 self.command.n_bytes_written = TRANSFER_PROGRESS_THRESHOLD + 5
3369 self.command.progress_hook()3369 self.command.progress_hook()
3370 kwargs = {'share_id': self.command.share_id, 'node_id': 'a_node_id',3370 kwargs = {'share_id': self.command.share_id, 'node_id': 'a_node_id',
3371 'deflated_size': 2*TRANSFER_PROGRESS_THRESHOLD,3371 'deflated_size': 2 * TRANSFER_PROGRESS_THRESHOLD,
3372 'n_bytes_written': 5+TRANSFER_PROGRESS_THRESHOLD}3372 'n_bytes_written': 5 + TRANSFER_PROGRESS_THRESHOLD}
3373 events = [('AQ_UPLOAD_FILE_PROGRESS', kwargs)]3373 events = [('AQ_UPLOAD_FILE_PROGRESS', kwargs)]
3374 self.assertEqual(events, self.command.action_queue.event_queue.events)3374 self.assertEqual(events, self.command.action_queue.event_queue.events)
3375 self.assertEqual(self.command.n_bytes_written_last,3375 self.assertEqual(self.command.n_bytes_written_last,
@@ -4932,7 +4932,7 @@
49324932
4933 def test_repr(self):4933 def test_repr(self):
4934 """A DeltaList has a short representation."""4934 """A DeltaList has a short representation."""
4935 a = DeltaList(["a"*1000])4935 a = DeltaList(["a" * 1000])
4936 self.assertTrue(len(repr(a)) < 100)4936 self.assertTrue(len(repr(a)) < 100)
4937 self.assertTrue(len(str(a)) < 100)4937 self.assertTrue(len(str(a)) < 100)
49384938
49394939
=== modified file 'magicicadaclient/syncdaemon/tests/test_eq_inotify.py'
--- magicicadaclient/syncdaemon/tests/test_eq_inotify.py 2018-05-19 20:44:54 +0000
+++ magicicadaclient/syncdaemon/tests/test_eq_inotify.py 2018-06-03 23:09:20 +0000
@@ -1,9 +1,5 @@
1# tests.syncdaemon.test_eq_inotify
2#
3# Authors: Facundo Batista <facundo@canonical.com>
4# Manuel de la Pena <manuel@canonical.com>
5#
6# Copyright 2009-2012 Canonical Ltd.1# Copyright 2009-2012 Canonical Ltd.
2# Copyright 2018 Chicharreros (https://launchpad.net/~chicharreros)
7#3#
8# This program is free software: you can redistribute it and/or modify it4# This program is free software: you can redistribute it and/or modify it
9# under the terms of the GNU General Public License version 3, as published5# under the terms of the GNU General Public License version 3, as published
@@ -37,17 +33,9 @@
37import sys33import sys
3834
39from twisted.internet import defer, reactor35from twisted.internet import defer, reactor
40from ubuntuone.devtools.handlers import MementoHandler
41from ubuntuone.devtools.testcases import skipIfOS, skipIfNotOS
4236
43from magicicadaclient.testing.testcase import (37from devtools.handlers import MementoHandler
44 BaseTwistedTestCase,38from devtools.testcases import skipIfOS, skipIfNotOS
45 FakeMain,
46 Listener,
47 skip_if_darwin_missing_fs_event,
48 skip_if_win32_missing_fs_event,
49)
50from magicicadaclient.syncdaemon.tests.test_eventqueue import BaseEQTestCase
51from magicicadaclient.platform import (39from magicicadaclient.platform import (
52 make_link,40 make_link,
53 make_dir,41 make_dir,
@@ -61,6 +49,14 @@
61 set_dir_readwrite,49 set_dir_readwrite,
62)50)
63from magicicadaclient.syncdaemon import event_queue, volume_manager51from magicicadaclient.syncdaemon import event_queue, volume_manager
52from magicicadaclient.syncdaemon.tests.test_eventqueue import BaseEQTestCase
53from magicicadaclient.testing.testcase import (
54 BaseTwistedTestCase,
55 FakeMain,
56 Listener,
57 skip_if_darwin_missing_fs_event,
58 skip_if_win32_missing_fs_event,
59)
6460
65# our logging level61# our logging level
66TRACE = logging.getLevelName('TRACE')62TRACE = logging.getLevelName('TRACE')
6763
=== modified file 'magicicadaclient/syncdaemon/tests/test_eventqueue.py'
--- magicicadaclient/syncdaemon/tests/test_eventqueue.py 2018-05-19 20:44:54 +0000
+++ magicicadaclient/syncdaemon/tests/test_eventqueue.py 2018-06-03 23:09:20 +0000
@@ -1,9 +1,5 @@
1#
2# Author: Facundo Batista <facundo@canonical.com>
3#
4# Author: Guillermo Gonzalez <guillermo.gonzalez@canonical.com>
5#
6# Copyright 2009-2012 Canonical Ltd.1# Copyright 2009-2012 Canonical Ltd.
2# Copyright 2018 Chicharreros (https://launchpad.net/~chicharreros)
7#3#
8# This program is free software: you can redistribute it and/or modify it4# This program is free software: you can redistribute it and/or modify it
9# under the terms of the GNU General Public License version 3, as published5# under the terms of the GNU General Public License version 3, as published
@@ -36,6 +32,7 @@
36from twisted.internet import defer32from twisted.internet import defer
37from twisted.trial.unittest import TestCase33from twisted.trial.unittest import TestCase
3834
35from devtools.handlers import MementoHandler
39from magicicadaclient.testing.testcase import (36from magicicadaclient.testing.testcase import (
40 BaseTwistedTestCase,37 BaseTwistedTestCase,
41 FakeMonitor,38 FakeMonitor,
@@ -49,7 +46,6 @@
49 filesystem_manager,46 filesystem_manager,
50 tritcask,47 tritcask,
51)48)
52from ubuntuone.devtools.handlers import MementoHandler
5349
5450
55class BaseEQTestCase(BaseTwistedTestCase):51class BaseEQTestCase(BaseTwistedTestCase):
5652
=== modified file 'magicicadaclient/syncdaemon/tests/test_fileshelf.py'
--- magicicadaclient/syncdaemon/tests/test_fileshelf.py 2018-05-19 20:44:54 +0000
+++ magicicadaclient/syncdaemon/tests/test_fileshelf.py 2018-06-03 23:09:20 +0000
@@ -1,7 +1,5 @@
1#
2# Author: Guillermo Gonzalez <guillermo.gonzalez@canonical.com>
3#
4# Copyright 2009-2012 Canonical Ltd.1# Copyright 2009-2012 Canonical Ltd.
2# Copyright 2018 Chicharreros (https://launchpad.net/~chicharreros)
5#3#
6# This program is free software: you can redistribute it and/or modify it4# This program is free software: you can redistribute it and/or modify it
7# under the terms of the GNU General Public License version 3, as published5# under the terms of the GNU General Public License version 3, as published
@@ -37,19 +35,16 @@
37import unittest35import unittest
3836
39from twisted.internet import defer37from twisted.internet import defer
40from ubuntuone.devtools.testcases import skipIfOS
4138
42from magicicadaclient.testing.testcase import BaseTwistedTestCase39from devtools.testcases import skipIfOS
43from magicicadaclient.platform import (40from magicicadaclient.platform import open_file, path_exists
44 open_file,
45 path_exists,
46)
47from magicicadaclient.syncdaemon.file_shelf import (41from magicicadaclient.syncdaemon.file_shelf import (
48 FileShelf,42 FileShelf,
49 CachedFileShelf,43 CachedFileShelf,
50 LRUCache,44 LRUCache,
51 CacheInconsistencyError,45 CacheInconsistencyError,
52)46)
47from magicicadaclient.testing.testcase import BaseTwistedTestCase
5348
5449
55BROKEN_PICKLE = '\axb80\x02}q\x01(U\x01aU\x04testq\x02U\x01bU\x06brokenq\x03u.'50BROKEN_PICKLE = '\axb80\x02}q\x01(U\x01aU\x04testq\x02U\x01bU\x06brokenq\x03u.'
@@ -168,11 +163,11 @@
168 """test that each time a metadata file is updated a .old is kept"""163 """test that each time a metadata file is updated a .old is kept"""
169 self.shelf['bad_file'] = {'value': 'old'}164 self.shelf['bad_file'] = {'value': 'old'}
170 path = self.shelf.key_file('bad_file')165 path = self.shelf.key_file('bad_file')
171 self.assertFalse(path_exists(path+'.old'))166 self.assertFalse(path_exists(path + '.old'))
172 self.assertEqual({'value': 'old'}, self.shelf['bad_file'])167 self.assertEqual({'value': 'old'}, self.shelf['bad_file'])
173 # force the creation of the .old file168 # force the creation of the .old file
174 self.shelf['bad_file'] = {'value': 'new'}169 self.shelf['bad_file'] = {'value': 'new'}
175 self.assertTrue(path_exists(path+'.old'))170 self.assertTrue(path_exists(path + '.old'))
176 # check that the new value is there171 # check that the new value is there
177 self.assertEqual({'value': 'new'}, self.shelf['bad_file'])172 self.assertEqual({'value': 'new'}, self.shelf['bad_file'])
178 # write the current md file fwith 0 bytes173 # write the current md file fwith 0 bytes
@@ -183,11 +178,11 @@
183 self.shelf['broken_pickle'] = {'value': 'old'}178 self.shelf['broken_pickle'] = {'value': 'old'}
184 path = self.shelf.key_file('broken_pickle')179 path = self.shelf.key_file('broken_pickle')
185 # check that .old don't exist180 # check that .old don't exist
186 self.assertFalse(path_exists(path+'.old'))181 self.assertFalse(path_exists(path + '.old'))
187 # force the creation of the .old file182 # force the creation of the .old file
188 self.shelf['broken_pickle'] = {'value': 'new'}183 self.shelf['broken_pickle'] = {'value': 'new'}
189 # check that .old exists184 # check that .old exists
190 self.assertTrue(path_exists(path+'.old'))185 self.assertTrue(path_exists(path + '.old'))
191 # check that the new value is there186 # check that the new value is there
192 self.assertEqual({'value': 'new'}, self.shelf['broken_pickle'])187 self.assertEqual({'value': 'new'}, self.shelf['broken_pickle'])
193 # write random bytes to the md file188 # write random bytes to the md file
@@ -200,10 +195,10 @@
200 """test keys() with .old and .new files around"""195 """test keys() with .old and .new files around"""
201 self.shelf["foo"] = "bar"196 self.shelf["foo"] = "bar"
202 self.shelf["foo1"] = "bar1"197 self.shelf["foo1"] = "bar1"
203 open_file(self.shelf.key_file('foo')+'.old', 'w').close()198 open_file(self.shelf.key_file('foo') + '.old', 'w').close()
204 open_file(self.shelf.key_file('foo1')+'.old', 'w').close()199 open_file(self.shelf.key_file('foo1') + '.old', 'w').close()
205 open_file(self.shelf.key_file('foo')+'.new', 'w').close()200 open_file(self.shelf.key_file('foo') + '.new', 'w').close()
206 open_file(self.shelf.key_file('foo1')+'.new', 'w').close()201 open_file(self.shelf.key_file('foo1') + '.new', 'w').close()
207 self.assertEqual(set(['foo', 'foo1']), set(self.shelf.keys()))202 self.assertEqual(set(['foo', 'foo1']), set(self.shelf.keys()))
208203
209 def test_corrupted_backup(self):204 def test_corrupted_backup(self):
@@ -212,7 +207,7 @@
212 # create the .old backup207 # create the .old backup
213 self.shelf["foo"] = "bar1"208 self.shelf["foo"] = "bar1"
214 # write 0 bytes to both209 # write 0 bytes to both
215 open_file(self.shelf.key_file('foo')+'.old', 'w').close()210 open_file(self.shelf.key_file('foo') + '.old', 'w').close()
216 open_file(self.shelf.key_file('foo'), 'w').close()211 open_file(self.shelf.key_file('foo'), 'w').close()
217 self.assertRaises(KeyError, self.shelf.__getitem__, 'foo')212 self.assertRaises(KeyError, self.shelf.__getitem__, 'foo')
218213
@@ -233,12 +228,12 @@
233 self.shelf["foo"] = "bar1"228 self.shelf["foo"] = "bar1"
234 path = self.shelf.key_file('foo')229 path = self.shelf.key_file('foo')
235 # create a .new file (a hard reboot during the rename dance)230 # create a .new file (a hard reboot during the rename dance)
236 open_file(path+'.new', 'w').close()231 open_file(path + '.new', 'w').close()
237 # write 0 bytes to both232 # write 0 bytes to both
238 del self.shelf['foo']233 del self.shelf['foo']
239 self.assertFalse(path_exists(path))234 self.assertFalse(path_exists(path))
240 self.assertFalse(path_exists(path+'.old'), 'there is a .old file!')235 self.assertFalse(path_exists(path + '.old'), 'there is a .old file!')
241 self.assertFalse(path_exists(path+'.new'), 'there is a .new file!')236 self.assertFalse(path_exists(path + '.new'), 'there is a .new file!')
242237
243 @skipIfOS('win32', 'Skipped because code is deprecated on Windows.')238 @skipIfOS('win32', 'Skipped because code is deprecated on Windows.')
244 def test_custom_unpickle(self):239 def test_custom_unpickle(self):
@@ -330,11 +325,11 @@
330 """overrides parent test as we have the value in the cache."""325 """overrides parent test as we have the value in the cache."""
331 self.shelf['bad_file'] = {'value': 'old'}326 self.shelf['bad_file'] = {'value': 'old'}
332 path = self.shelf.key_file('bad_file')327 path = self.shelf.key_file('bad_file')
333 self.assertFalse(path_exists(path+'.old'))328 self.assertFalse(path_exists(path + '.old'))
334 self.assertEqual({'value': 'old'}, self.shelf['bad_file'])329 self.assertEqual({'value': 'old'}, self.shelf['bad_file'])
335 # force the creation of the .old file330 # force the creation of the .old file
336 self.shelf['bad_file'] = {'value': 'new'}331 self.shelf['bad_file'] = {'value': 'new'}
337 self.assertTrue(path_exists(path+'.old'))332 self.assertTrue(path_exists(path + '.old'))
338 # check that the new value is there333 # check that the new value is there
339 self.assertEqual({'value': 'new'}, self.shelf['bad_file'])334 self.assertEqual({'value': 'new'}, self.shelf['bad_file'])
340 # write the current md file fwith 0 bytes335 # write the current md file fwith 0 bytes
@@ -346,11 +341,11 @@
346 self.shelf['broken_pickle'] = {'value': 'old'}341 self.shelf['broken_pickle'] = {'value': 'old'}
347 path = self.shelf.key_file('broken_pickle')342 path = self.shelf.key_file('broken_pickle')
348 # check that .old don't exist343 # check that .old don't exist
349 self.assertFalse(path_exists(path+'.old'))344 self.assertFalse(path_exists(path + '.old'))
350 # force the creation of the .old file345 # force the creation of the .old file
351 self.shelf['broken_pickle'] = {'value': 'new'}346 self.shelf['broken_pickle'] = {'value': 'new'}
352 # check that .old exists347 # check that .old exists
353 self.assertTrue(path_exists(path+'.old'))348 self.assertTrue(path_exists(path + '.old'))
354 # check that the new value is there349 # check that the new value is there
355 self.assertEqual({'value': 'new'}, self.shelf['broken_pickle'])350 self.assertEqual({'value': 'new'}, self.shelf['broken_pickle'])
356 # write random bytes to the md file351 # write random bytes to the md file
@@ -368,7 +363,7 @@
368 """test __delitem__ method"""363 """test __delitem__ method"""
369 cache = LRUCache(100, 4)364 cache = LRUCache(100, 4)
370 # set some data in the cache365 # set some data in the cache
371 values = [('key'+str(i), i) for i in range(100)]366 values = [('key' + str(i), i) for i in range(100)]
372 for i, j in values:367 for i, j in values:
373 cache[i] = j368 cache[i] = j
374 self.assertEqual(len(cache._queue), len(values))369 self.assertEqual(len(cache._queue), len(values))
@@ -378,7 +373,7 @@
378 """test __delitem__ method"""373 """test __delitem__ method"""
379 cache = LRUCache(100, 4)374 cache = LRUCache(100, 4)
380 # set some data in the cache375 # set some data in the cache
381 values = [('key'+str(i), i) for i in range(100)]376 values = [('key' + str(i), i) for i in range(100)]
382 for i, j in values:377 for i, j in values:
383 cache[i] = j378 cache[i] = j
384 self.assertEqual(len(cache._queue), len(values))379 self.assertEqual(len(cache._queue), len(values))
@@ -390,7 +385,7 @@
390 def test_delitem(self):385 def test_delitem(self):
391 """test __delitem__ method"""386 """test __delitem__ method"""
392 cache = LRUCache(100, 4)387 cache = LRUCache(100, 4)
393 values = [('key'+str(i), i) for i in range(100)]388 values = [('key' + str(i), i) for i in range(100)]
394 for i, j in values:389 for i, j in values:
395 cache[i] = j390 cache[i] = j
396 self.assertEqual(len(cache._queue), len(values))391 self.assertEqual(len(cache._queue), len(values))
@@ -415,7 +410,7 @@
415 def test_purge(self):410 def test_purge(self):
416 """Test the queue compact and cache purge"""411 """Test the queue compact and cache purge"""
417 cache = LRUCache(100, 4)412 cache = LRUCache(100, 4)
418 values = [('key'+str(i), j) for i in range(50) for j in range(8)]413 values = [('key' + str(i), j) for i in range(50) for j in range(8)]
419 for i, j in values:414 for i, j in values:
420 cache[i] = j415 cache[i] = j
421 # we hit the limit416 # we hit the limit
@@ -442,7 +437,7 @@
442 """Tests if the cache correclty keeps track of misses and hits."""437 """Tests if the cache correclty keeps track of misses and hits."""
443 cache = LRUCache(100, 4)438 cache = LRUCache(100, 4)
444 # set some data in the cache439 # set some data in the cache
445 values = [('key'+str(i), i) for i in range(10)]440 values = [('key' + str(i), i) for i in range(10)]
446 for i, j in values:441 for i, j in values:
447 cache[i] = j442 cache[i] = j
448 self.assertEqual(len(cache._queue), len(values))443 self.assertEqual(len(cache._queue), len(values))
@@ -454,7 +449,7 @@
454 self.assertEqual(cache.hits, 4)449 self.assertEqual(cache.hits, 4)
455 # try to get items not present in the cache450 # try to get items not present in the cache
456 for i, j in values[5:10]:451 for i, j in values[5:10]:
457 self.assertRaises(KeyError, cache.__getitem__, i*10)452 self.assertRaises(KeyError, cache.__getitem__, i * 10)
458 self.assertEqual(cache.misses, 5)453 self.assertEqual(cache.misses, 5)
459454
460 def test_inconsistency(self):455 def test_inconsistency(self):
461456
=== modified file 'magicicadaclient/syncdaemon/tests/test_fsm.py'
--- magicicadaclient/syncdaemon/tests/test_fsm.py 2018-05-19 20:44:54 +0000
+++ magicicadaclient/syncdaemon/tests/test_fsm.py 2018-06-03 23:09:20 +0000
@@ -1,6 +1,7 @@
1# -*- coding: utf-8 -*-1# -*- coding: utf-8 -*-
2#2#
3# Copyright 2009-2012 Canonical Ltd.3# Copyright 2009-2012 Canonical Ltd.
4# Copyright 2018 Chicharreros (https://launchpad.net/~chicharreros)
4#5#
5# This program is free software: you can redistribute it and/or modify it6# This program is free software: you can redistribute it and/or modify it
6# under the terms of the GNU General Public License version 3, as published7# under the terms of the GNU General Public License version 3, as published
@@ -47,7 +48,7 @@
47 skip_if_win32_and_uses_readonly,48 skip_if_win32_and_uses_readonly,
48)49)
4950
50from ubuntuone.devtools.handlers import MementoHandler51from devtools.handlers import MementoHandler
51from magicicadaclient.platform import (52from magicicadaclient.platform import (
52 listdir,53 listdir,
53 make_dir,54 make_dir,
@@ -229,7 +230,7 @@
229 self.assertEqual(mdobj.size, None)230 self.assertEqual(mdobj.size, None)
230 when = mdobj.info.created231 when = mdobj.info.created
231 now = time.time()232 now = time.time()
232 self.assertTrue(now-3 <= when <= now) # 3 seconds test range233 self.assertTrue(now - 3 <= when <= now) # 3 seconds test range
233234
234 # set uuid using valid path, but not twice235 # set uuid using valid path, but not twice
235 self.fsm.set_node_id(path, "uuid")236 self.fsm.set_node_id(path, "uuid")
@@ -237,7 +238,7 @@
237 mdobj = self.fsm.get_by_path(path)238 mdobj = self.fsm.get_by_path(path)
238 when = mdobj.info.node_id_assigned239 when = mdobj.info.node_id_assigned
239 now = time.time()240 now = time.time()
240 self.assertTrue(now-3 <= when <= now) # 3 seconds test range241 self.assertTrue(now - 3 <= when <= now) # 3 seconds test range
241242
242 def test_with_node_id(self):243 def test_with_node_id(self):
243 """Test creation with node_id"""244 """Test creation with node_id"""
@@ -255,14 +256,14 @@
255 self.assertEqual(mdobj.size, None)256 self.assertEqual(mdobj.size, None)
256 when = mdobj.info.created257 when = mdobj.info.created
257 now = time.time()258 now = time.time()
258 self.assertTrue(now-3 <= when <= now) # 3 seconds test range259 self.assertTrue(now - 3 <= when <= now) # 3 seconds test range
259260
260 # set uuid using valid path, but not twice261 # set uuid using valid path, but not twice
261 self.assertRaises(ValueError, self.fsm.set_node_id, path, "whatever")262 self.assertRaises(ValueError, self.fsm.set_node_id, path, "whatever")
262 mdobj = self.fsm.get_by_path(path)263 mdobj = self.fsm.get_by_path(path)
263 when = mdobj.info.node_id_assigned264 when = mdobj.info.node_id_assigned
264 now = time.time()265 now = time.time()
265 self.assertTrue(now-3 <= when <= now) # 3 seconds test range266 self.assertTrue(now - 3 <= when <= now) # 3 seconds test range
266267
267 def test_invalid_args(self):268 def test_invalid_args(self):
268 """Test using invalid args in set_node_id."""269 """Test using invalid args in set_node_id."""
@@ -351,7 +352,7 @@
351 old_fs[k] = v352 old_fs[k] = v
352353
353 # start up again, and check354 # start up again, and check
354 db = Tritcask(self.tritcask_path+'.new')355 db = Tritcask(self.tritcask_path + '.new')
355 self.addCleanup(db.shutdown)356 self.addCleanup(db.shutdown)
356 newfsm = FileSystemManager(self.fsmdir, self.partials_dir,357 newfsm = FileSystemManager(self.fsmdir, self.partials_dir,
357 self.fsm.vm, db)358 self.fsm.vm, db)
@@ -417,7 +418,7 @@
417 old_fs[k] = v418 old_fs[k] = v
418419
419 # start up again, and check420 # start up again, and check
420 db = Tritcask(self.tritcask_path+'.new')421 db = Tritcask(self.tritcask_path + '.new')
421 self.addCleanup(db.shutdown)422 self.addCleanup(db.shutdown)
422 newfsm = FileSystemManager(self.fsmdir, self.partials_dir,423 newfsm = FileSystemManager(self.fsmdir, self.partials_dir,
423 self.fsm.vm, db)424 self.fsm.vm, db)
@@ -474,7 +475,7 @@
474 old_fs[k] = v475 old_fs[k] = v
475476
476 # start up again, and check477 # start up again, and check
477 db = Tritcask(self.tritcask_path+'.new')478 db = Tritcask(self.tritcask_path + '.new')
478 self.addCleanup(db.shutdown)479 self.addCleanup(db.shutdown)
479 newfsm = FileSystemManager(self.fsmdir, self.partials_dir,480 newfsm = FileSystemManager(self.fsmdir, self.partials_dir,
480 self.fsm.vm, db)481 self.fsm.vm, db)
@@ -530,7 +531,7 @@
530 old_fs[k] = v531 old_fs[k] = v
531532
532 # start up again, and check533 # start up again, and check
533 db = Tritcask(self.tritcask_path+'.new')534 db = Tritcask(self.tritcask_path + '.new')
534 self.addCleanup(db.shutdown)535 self.addCleanup(db.shutdown)
535 newfsm = FileSystemManager(self.fsmdir, self.partials_dir,536 newfsm = FileSystemManager(self.fsmdir, self.partials_dir,
536 self.fsm.vm, db)537 self.fsm.vm, db)
@@ -595,7 +596,7 @@
595 old_mvlimbo[k] = v596 old_mvlimbo[k] = v
596597
597 # start up again, and check598 # start up again, and check
598 db = Tritcask(self.tritcask_path+'.new')599 db = Tritcask(self.tritcask_path + '.new')
599 self.addCleanup(db.shutdown)600 self.addCleanup(db.shutdown)
600 newfsm = FileSystemManager(self.fsmdir, self.partials_dir,601 newfsm = FileSystemManager(self.fsmdir, self.partials_dir,
601 self.fsm.vm, db)602 self.fsm.vm, db)
@@ -654,7 +655,7 @@
654 old_mvlimbo[k] = v655 old_mvlimbo[k] = v
655656
656 # start up again, and check657 # start up again, and check
657 db = Tritcask(self.tritcask_path+'.new')658 db = Tritcask(self.tritcask_path + '.new')
658 self.addCleanup(db.shutdown)659 self.addCleanup(db.shutdown)
659 newfsm = FileSystemManager(self.fsmdir, self.partials_dir,660 newfsm = FileSystemManager(self.fsmdir, self.partials_dir,
660 self.fsm.vm, db)661 self.fsm.vm, db)
@@ -710,7 +711,7 @@
710 remove_file(version_file)711 remove_file(version_file)
711712
712 # start up again, and check713 # start up again, and check
713 db = Tritcask(self.tritcask_path+'.new')714 db = Tritcask(self.tritcask_path + '.new')
714 self.addCleanup(db.shutdown)715 self.addCleanup(db.shutdown)
715 newfsm = FileSystemManager(self.fsmdir, self.partials_dir,716 newfsm = FileSystemManager(self.fsmdir, self.partials_dir,
716 self.fsm.vm, db)717 self.fsm.vm, db)
@@ -757,7 +758,7 @@
757 fh.write("1")758 fh.write("1")
758759
759 # start up again, and check760 # start up again, and check
760 db = Tritcask(self.tritcask_path+'.new')761 db = Tritcask(self.tritcask_path + '.new')
761 self.addCleanup(db.shutdown)762 self.addCleanup(db.shutdown)
762 newfsm = FileSystemManager(self.fsmdir, self.partials_dir,763 newfsm = FileSystemManager(self.fsmdir, self.partials_dir,
763 self.fsm.vm, db)764 self.fsm.vm, db)
@@ -809,7 +810,7 @@
809 os.fsync(f.fileno())810 os.fsync(f.fileno())
810811
811 # start up again, and check812 # start up again, and check
812 db = Tritcask(self.tritcask_path+'.new')813 db = Tritcask(self.tritcask_path + '.new')
813 self.addCleanup(db.shutdown)814 self.addCleanup(db.shutdown)
814 newfsm = FileSystemManager(self.fsmdir, self.partials_dir,815 newfsm = FileSystemManager(self.fsmdir, self.partials_dir,
815 self.fsm.vm, db)816 self.fsm.vm, db)
@@ -867,7 +868,7 @@
867 remove_file(version_file)868 remove_file(version_file)
868869
869 # start up again, and check870 # start up again, and check
870 db = Tritcask(self.tritcask_path+'.new')871 db = Tritcask(self.tritcask_path + '.new')
871 self.addCleanup(db.shutdown)872 self.addCleanup(db.shutdown)
872 newfsm = FileSystemManager(self.fsmdir, self.partials_dir,873 newfsm = FileSystemManager(self.fsmdir, self.partials_dir,
873 self.fsm.vm, db)874 self.fsm.vm, db)
@@ -928,7 +929,7 @@
928 fh.write("1")929 fh.write("1")
929930
930 # start up again, and check931 # start up again, and check
931 db = Tritcask(self.tritcask_path+'.new')932 db = Tritcask(self.tritcask_path + '.new')
932 self.addCleanup(db.shutdown)933 self.addCleanup(db.shutdown)
933 newfsm = FileSystemManager(self.fsmdir, self.partials_dir,934 newfsm = FileSystemManager(self.fsmdir, self.partials_dir,
934 self.fsm.vm, db)935 self.fsm.vm, db)
@@ -996,7 +997,7 @@
996 os.fsync(f.fileno())997 os.fsync(f.fileno())
997998
998 # start up again, and check999 # start up again, and check
999 db = Tritcask(self.tritcask_path+'.new')1000 db = Tritcask(self.tritcask_path + '.new')
1000 self.addCleanup(db.shutdown)1001 self.addCleanup(db.shutdown)
1001 newfsm = FileSystemManager(self.fsmdir, self.partials_dir,1002 newfsm = FileSystemManager(self.fsmdir, self.partials_dir,
1002 self.fsm.vm, db)1003 self.fsm.vm, db)
@@ -1548,7 +1549,7 @@
1548 """Test having similar paths (a/b, a/b1, a/b2)."""1549 """Test having similar paths (a/b, a/b1, a/b2)."""
1549 expected = [os.path.join('a', 'b', 'y.txt')]1550 expected = [os.path.join('a', 'b', 'y.txt')]
1550 actual = sorted([d.path for d in self.fsm.get_mdobjs_in_dir(1551 actual = sorted([d.path for d in self.fsm.get_mdobjs_in_dir(
1551 os.path.join(self.share.path, 'a', 'b'))])1552 os.path.join(self.share.path, 'a', 'b'))])
1552 self.assertEqual(expected, actual)1553 self.assertEqual(expected, actual)
15531554
1554 @defer.inlineCallbacks1555 @defer.inlineCallbacks
@@ -1719,7 +1720,7 @@
1719 mdobj = self.fsm.get_by_mdid(mdid)1720 mdobj = self.fsm.get_by_mdid(mdid)
1720 when = mdobj.info.last_partial_created1721 when = mdobj.info.last_partial_created
1721 now = time.time()1722 now = time.time()
1722 self.assertTrue(now-3 <= when <= now) # 3 seconds test range1723 self.assertTrue(now - 3 <= when <= now) # 3 seconds test range
17231724
1724 # invalid uuid1725 # invalid uuid
1725 self.assertRaises(KeyError, self.fsm.create_partial, "foo", "share")1726 self.assertRaises(KeyError, self.fsm.create_partial, "foo", "share")
@@ -1756,7 +1757,7 @@
1756 self.assertEqual(mdobj.local_hash, 9876)1757 self.assertEqual(mdobj.local_hash, 9876)
1757 when = mdobj.info.last_downloaded1758 when = mdobj.info.last_downloaded
1758 now = time.time()1759 now = time.time()
1759 self.assertTrue(now-3 <= when <= now) # 3 seconds test range1760 self.assertTrue(now - 3 <= when <= now) # 3 seconds test range
17601761
1761 # invalid uuid1762 # invalid uuid
1762 self.assertRaises(1763 self.assertRaises(
@@ -1796,7 +1797,7 @@
1796 self.assertFalse(mdobj.info.is_partial)1797 self.assertFalse(mdobj.info.is_partial)
1797 when = mdobj.info.last_partial_removed1798 when = mdobj.info.last_partial_removed
1798 now = time.time()1799 now = time.time()
1799 self.assertTrue(now-3 <= when <= now) # 3 seconds test range1800 self.assertTrue(now - 3 <= when <= now) # 3 seconds test range
18001801
1801 # invalid uuid1802 # invalid uuid
1802 self.assertRaises(KeyError, self.fsm.remove_partial, "foo", "share")1803 self.assertRaises(KeyError, self.fsm.remove_partial, "foo", "share")
@@ -1822,7 +1823,7 @@
1822 mdobj = self.fsm.get_by_mdid(mdid)1823 mdobj = self.fsm.get_by_mdid(mdid)
1823 when = mdobj.info.last_partial_created1824 when = mdobj.info.last_partial_created
1824 now = time.time()1825 now = time.time()
1825 self.assertTrue(now-3 <= when <= now) # 3 seconds test range1826 self.assertTrue(now - 3 <= when <= now) # 3 seconds test range
18261827
1827 # invalid uuid1828 # invalid uuid
1828 self.assertRaises(KeyError, self.fsm.create_partial, "foo", "share")1829 self.assertRaises(KeyError, self.fsm.create_partial, "foo", "share")
@@ -1847,7 +1848,7 @@
1847 mdobj = self.fsm.get_by_mdid(mdid)1848 mdobj = self.fsm.get_by_mdid(mdid)
1848 when = mdobj.info.last_partial_created1849 when = mdobj.info.last_partial_created
1849 now = time.time()1850 now = time.time()
1850 self.assertTrue(now-3 <= when <= now) # 3 seconds test range1851 self.assertTrue(now - 3 <= when <= now) # 3 seconds test range
18511852
1852 # invalid uuid1853 # invalid uuid
1853 self.assertRaises(KeyError, self.fsm.create_partial, "foo", "share")1854 self.assertRaises(KeyError, self.fsm.create_partial, "foo", "share")
@@ -1890,7 +1891,7 @@
1890 self.assertFalse(mdobj.info.is_partial)1891 self.assertFalse(mdobj.info.is_partial)
1891 when = mdobj.info.last_partial_removed1892 when = mdobj.info.last_partial_removed
1892 now = time.time()1893 now = time.time()
1893 self.assertTrue(now-3 <= when <= now) # 3 seconds test range1894 self.assertTrue(now - 3 <= when <= now) # 3 seconds test range
18941895
1895 # invalid uuid1896 # invalid uuid
1896 self.assertRaises(KeyError, self.fsm.remove_partial, "foo", "share")1897 self.assertRaises(KeyError, self.fsm.remove_partial, "foo", "share")
@@ -1948,7 +1949,7 @@
1948 # find a almost too long file1949 # find a almost too long file
1949 repeat = 3001950 repeat = 300
1950 while True:1951 while True:
1951 testfile = os.path.join(self.share_path, "x"*repeat)1952 testfile = os.path.join(self.share_path, "x" * repeat)
1952 try:1953 try:
1953 fh = open_file(testfile, 'w')1954 fh = open_file(testfile, 'w')
1954 except IOError, e:1955 except IOError, e:
@@ -2071,7 +2072,7 @@
2071 mdobj = self.fsm.get_by_mdid(mdid)2072 mdobj = self.fsm.get_by_mdid(mdid)
2072 when = mdobj.info.last_conflicted2073 when = mdobj.info.last_conflicted
2073 now = time.time()2074 now = time.time()
2074 self.assertTrue(now-3 <= when <= now) # 3 seconds test range2075 self.assertTrue(now - 3 <= when <= now) # 3 seconds test range
20752076
2076 # move second time, start the .N serie2077 # move second time, start the .N serie
2077 with open_file(testfile, "w") as fh:2078 with open_file(testfile, "w") as fh:
@@ -2145,7 +2146,7 @@
2145 self.assertEqual(mdobj.server_hash, 1234567890)2146 self.assertEqual(mdobj.server_hash, 1234567890)
2146 when = mdobj.info.last_uploaded2147 when = mdobj.info.last_uploaded
2147 now = time.time()2148 now = time.time()
2148 self.assertTrue(now-3 <= when <= now) # 3 seconds test range2149 self.assertTrue(now - 3 <= when <= now) # 3 seconds test range
21492150
2150 # invalid mdid2151 # invalid mdid
2151 self.assertRaises(KeyError, self.fsm.upload_finished,2152 self.assertRaises(KeyError, self.fsm.upload_finished,
@@ -2173,7 +2174,7 @@
2173 self.assertEqual(mdobj.info.last_moved_from, testfile)2174 self.assertEqual(mdobj.info.last_moved_from, testfile)
2174 when = mdobj.info.last_moved_time2175 when = mdobj.info.last_moved_time
2175 now = time.time()2176 now = time.time()
2176 self.assertTrue(now-3 <= when <= now) # 3 seconds test range2177 self.assertTrue(now - 3 <= when <= now) # 3 seconds test range
21772178
2178 # move again, to a directory2179 # move again, to a directory
2179 from_path = to_path2180 from_path = to_path
@@ -2244,7 +2245,7 @@
2244 self.assertEqual(mdobj.info.last_moved_from, from_path)2245 self.assertEqual(mdobj.info.last_moved_from, from_path)
2245 when = mdobj.info.last_moved_time2246 when = mdobj.info.last_moved_time
2246 now = time.time()2247 now = time.time()
2247 self.assertTrue(now-3 <= when <= now) # 3 seconds test range2248 self.assertTrue(now - 3 <= when <= now) # 3 seconds test range
22482249
2249 # move again, to a directory2250 # move again, to a directory
2250 from_path = to_path2251 from_path = to_path
@@ -2257,7 +2258,7 @@
2257 self.assertEqual(mdobj.info.last_moved_from, from_path)2258 self.assertEqual(mdobj.info.last_moved_from, from_path)
2258 when = mdobj.info.last_moved_time2259 when = mdobj.info.last_moved_time
2259 now = time.time()2260 now = time.time()
2260 self.assertTrue(now-3 <= when <= now) # 3 seconds test range2261 self.assertTrue(now - 3 <= when <= now) # 3 seconds test range
22612262
2262 def test_move_file_withfulldir(self):2263 def test_move_file_withfulldir(self):
2263 """Test that a dir is moved from even having a file inside."""2264 """Test that a dir is moved from even having a file inside."""
@@ -2288,7 +2289,7 @@
2288 self.assertEqual(mdobj.info.last_moved_from, from_path)2289 self.assertEqual(mdobj.info.last_moved_from, from_path)
2289 when = mdobj.info.last_moved_time2290 when = mdobj.info.last_moved_time
2290 now = time.time()2291 now = time.time()
2291 self.assertTrue(now-3 <= when <= now) # 3 seconds test range2292 self.assertTrue(now - 3 <= when <= now) # 3 seconds test range
22922293
2293 # check that file inside is ok2294 # check that file inside is ok
2294 newfilepath = os.path.join(to_path, "file.txt")2295 newfilepath = os.path.join(to_path, "file.txt")
@@ -4086,8 +4087,10 @@
4086 def setUp(self):4087 def setUp(self):
4087 """Set up."""4088 """Set up."""
4088 yield super(OsIntegrationTests, self).setUp()4089 yield super(OsIntegrationTests, self).setUp()
4089 self.open_file = self.mocker.replace('magicicadaclient.platform.open_file')4090 self.open_file = self.mocker.replace(
4090 self.normpath = self.mocker.replace('magicicadaclient.platform.normpath')4091 'magicicadaclient.platform.open_file')
4092 self.normpath = self.mocker.replace(
4093 'magicicadaclient.platform.normpath')
4091 self.listdir = self.mocker.replace('magicicadaclient.platform.listdir')4094 self.listdir = self.mocker.replace('magicicadaclient.platform.listdir')
40924095
4093 def test_get_partial_for_writing(self):4096 def test_get_partial_for_writing(self):
40944097
=== modified file 'magicicadaclient/syncdaemon/tests/test_hashqueue.py'
--- magicicadaclient/syncdaemon/tests/test_hashqueue.py 2018-05-19 20:44:54 +0000
+++ magicicadaclient/syncdaemon/tests/test_hashqueue.py 2018-06-03 23:09:20 +0000
@@ -41,13 +41,13 @@
41from magicicadaprotocol.content_hash import content_hash_factory, crc3241from magicicadaprotocol.content_hash import content_hash_factory, crc32
42from twisted.trial.unittest import TestCase as TwistedTestCase42from twisted.trial.unittest import TestCase as TwistedTestCase
43from twisted.internet import defer, reactor43from twisted.internet import defer, reactor
44from ubuntuone.devtools.handlers import MementoHandler
45from ubuntuone.devtools.testcases import skipTest
4644
47from magicicadaclient.testing.testcase import BaseTwistedTestCase45from devtools.handlers import MementoHandler
46from devtools.testcases import skipTest
48from magicicadaclient.platform import open_file, stat_path47from magicicadaclient.platform import open_file, stat_path
49from magicicadaclient.syncdaemon import hash_queue48from magicicadaclient.syncdaemon import hash_queue
50from magicicadaclient.syncdaemon.hash_queue import HASHQUEUE_DELAY49from magicicadaclient.syncdaemon.hash_queue import HASHQUEUE_DELAY
50from magicicadaclient.testing.testcase import BaseTwistedTestCase
5151
52FAKE_TIMESTAMP = 152FAKE_TIMESTAMP = 1
5353
@@ -244,11 +244,11 @@
244 should_be = []244 should_be = []
245 for i in range(10):245 for i in range(10):
246 hasher = content_hash_factory()246 hasher = content_hash_factory()
247 text = "supercalifragilistico"+str(i)247 text = "supercalifragilistico" + str(i)
248 hasher.hash_object.update(text)248 hasher.hash_object.update(text)
249 tfile = os.path.join(self.test_dir, "tfile"+str(i))249 tfile = os.path.join(self.test_dir, "tfile" + str(i))
250 with open_file(tfile, "wb") as fh:250 with open_file(tfile, "wb") as fh:
251 fh.write("supercalifragilistico"+str(i))251 fh.write("supercalifragilistico" + str(i))
252 d = dict(path=tfile, hash=hasher.content_hash(),252 d = dict(path=tfile, hash=hasher.content_hash(),
253 crc32=crc32(text), size=len(text), stat=stat_path(tfile))253 crc32=crc32(text), size=len(text), stat=stat_path(tfile))
254 should_be.append(("HQ_HASH_NEW", d))254 should_be.append(("HQ_HASH_NEW", d))
@@ -262,7 +262,7 @@
262262
263 # send what to hash263 # send what to hash
264 for i in range(10):264 for i in range(10):
265 tfile = os.path.join(self.test_dir, "tfile"+str(i))265 tfile = os.path.join(self.test_dir, "tfile" + str(i))
266 item = ((tfile, "mdid"), FAKE_TIMESTAMP)266 item = ((tfile, "mdid"), FAKE_TIMESTAMP)
267 queue.put(item)267 queue.put(item)
268268
@@ -554,11 +554,11 @@
554 should_be = []554 should_be = []
555 for i in range(10):555 for i in range(10):
556 hasher = content_hash_factory()556 hasher = content_hash_factory()
557 text = "supercalifragilistico"+str(i)557 text = "supercalifragilistico" + str(i)
558 hasher.hash_object.update(text)558 hasher.hash_object.update(text)
559 tfile = os.path.join(self.test_dir, "tfile"+str(i))559 tfile = os.path.join(self.test_dir, "tfile" + str(i))
560 with open_file(tfile, "wb") as fh:560 with open_file(tfile, "wb") as fh:
561 fh.write("supercalifragilistico"+str(i))561 fh.write("supercalifragilistico" + str(i))
562 d = dict(path=tfile, hash=hasher.content_hash(),562 d = dict(path=tfile, hash=hasher.content_hash(),
563 crc32=crc32(text), size=len(text), stat=stat_path(tfile))563 crc32=crc32(text), size=len(text), stat=stat_path(tfile))
564 should_be.append(("HQ_HASH_NEW", d))564 should_be.append(("HQ_HASH_NEW", d))
@@ -569,7 +569,7 @@
569569
570 # send what to hash570 # send what to hash
571 for i in range(10):571 for i in range(10):
572 tfile = os.path.join(self.test_dir, "tfile"+str(i))572 tfile = os.path.join(self.test_dir, "tfile" + str(i))
573 hq.insert(tfile, "mdid")573 hq.insert(tfile, "mdid")
574574
575 # release the processor and check575 # release the processor and check
@@ -583,11 +583,11 @@
583 should_be = []583 should_be = []
584 for i in range(10):584 for i in range(10):
585 hasher = content_hash_factory()585 hasher = content_hash_factory()
586 text = "supercalifragilistico"+str(i)586 text = "supercalifragilistico" + str(i)
587 hasher.hash_object.update(text)587 hasher.hash_object.update(text)
588 tfile = os.path.join(self.test_dir, "tfile"+str(i))588 tfile = os.path.join(self.test_dir, "tfile" + str(i))
589 with open_file(tfile, "wb") as fh:589 with open_file(tfile, "wb") as fh:
590 fh.write("supercalifragilistico"+str(i))590 fh.write("supercalifragilistico" + str(i))
591 d = dict(path=tfile, hash=hasher.content_hash(),591 d = dict(path=tfile, hash=hasher.content_hash(),
592 crc32=crc32(text), size=len(text), stat=stat_path(tfile))592 crc32=crc32(text), size=len(text), stat=stat_path(tfile))
593 should_be.append(("HQ_HASH_NEW", d))593 should_be.append(("HQ_HASH_NEW", d))
@@ -606,7 +606,7 @@
606606
607 # send to hash twice607 # send to hash twice
608 for i in range(10):608 for i in range(10):
609 tfile = os.path.join(self.test_dir, "tfile"+str(i))609 tfile = os.path.join(self.test_dir, "tfile" + str(i))
610 hq.insert(tfile, "mdid")610 hq.insert(tfile, "mdid")
611 hq.insert(tfile, "mdid")611 hq.insert(tfile, "mdid")
612 # start the hasher612 # start the hasher
@@ -615,7 +615,7 @@
615 # insert the last item to check the uniqueness in the queue while615 # insert the last item to check the uniqueness in the queue while
616 # the hasher is running616 # the hasher is running
617 for i in range(9, 10):617 for i in range(9, 10):
618 tfile = os.path.join(self.test_dir, "tfile"+str(i))618 tfile = os.path.join(self.test_dir, "tfile" + str(i))
619 hq.insert(tfile, "mdid")619 hq.insert(tfile, "mdid")
620620
621 # release the processor and check621 # release the processor and check
622622
=== modified file 'magicicadaclient/syncdaemon/tests/test_interaction_interfaces.py'
--- magicicadaclient/syncdaemon/tests/test_interaction_interfaces.py 2018-05-19 20:44:54 +0000
+++ magicicadaclient/syncdaemon/tests/test_interaction_interfaces.py 2018-06-03 23:09:20 +0000
@@ -34,27 +34,14 @@
3434
35from magicicadaprotocol.protocol_pb2 import AccountInfo35from magicicadaprotocol.protocol_pb2 import AccountInfo
36from twisted.internet import defer36from twisted.internet import defer
37from ubuntuone.devtools.handlers import MementoHandler
3837
39from magicicadaclient.testing.testcase import (38from devtools.handlers import MementoHandler
40 FAKED_CREDENTIALS,
41 FakeCommand,
42 FakeDownload,
43 FakeUpload,
44 FakedObject,
45 FakeMainTestCase,
46 skipIfOS,
47)
48from magicicadaclient.networkstate.networkstates import ONLINE39from magicicadaclient.networkstate.networkstates import ONLINE
49from magicicadaclient.platform import make_dir, make_link40from magicicadaclient.platform import make_dir, make_link
50from magicicadaclient.platform.tests.ipc.test_perspective_broker import (41from magicicadaclient.platform.tests.ipc.test_perspective_broker import (
51 FakeNetworkManagerState,42 FakeNetworkManagerState,
52)43)
53from magicicadaclient.syncdaemon import (44from magicicadaclient.syncdaemon import config, interaction_interfaces, states
54 config,
55 interaction_interfaces,
56 states,
57)
58from magicicadaclient.syncdaemon.interaction_interfaces import (45from magicicadaclient.syncdaemon.interaction_interfaces import (
59 bool_str,46 bool_str,
60 get_share_dict,47 get_share_dict,
@@ -82,6 +69,15 @@
82 UDF,69 UDF,
83 VolumeDoesNotExist,70 VolumeDoesNotExist,
84)71)
72from magicicadaclient.testing.testcase import (
73 FAKED_CREDENTIALS,
74 FakeCommand,
75 FakeDownload,
76 FakeUpload,
77 FakedObject,
78 FakeMainTestCase,
79 skipIfOS,
80)
8581
8682
87class CustomError(Exception):83class CustomError(Exception):
8884
=== modified file 'magicicadaclient/syncdaemon/tests/test_localrescan.py'
--- magicicadaclient/syncdaemon/tests/test_localrescan.py 2018-05-19 20:44:54 +0000
+++ magicicadaclient/syncdaemon/tests/test_localrescan.py 2018-06-03 23:09:20 +0000
@@ -37,14 +37,9 @@
3737
38from magicicadaprotocol import content_hash as storage_hash, volumes38from magicicadaprotocol import content_hash as storage_hash, volumes
39from twisted.internet import defer, reactor39from twisted.internet import defer, reactor
40from ubuntuone.devtools.handlers import MementoHandler
41from ubuntuone.devtools.testcases import skipIfOS
4240
43from magicicadaclient.testing.testcase import (41from devtools.handlers import MementoHandler
44 BaseTwistedTestCase,42from devtools.testcases import skipIfOS
45 FakeVolumeManager,
46 skip_if_win32_and_uses_readonly,
47)
48from magicicadaclient.platform import (43from magicicadaclient.platform import (
49 make_dir,44 make_dir,
50 make_link,45 make_link,
@@ -68,6 +63,11 @@
68 ACCESS_LEVEL_RO,63 ACCESS_LEVEL_RO,
69 ACCESS_LEVEL_RW,64 ACCESS_LEVEL_RW,
70)65)
66from magicicadaclient.testing.testcase import (
67 BaseTwistedTestCase,
68 FakeVolumeManager,
69 skip_if_win32_and_uses_readonly,
70)
7171
72# our logging level72# our logging level
73TRACE = logging.getLevelName('TRACE')73TRACE = logging.getLevelName('TRACE')
@@ -829,7 +829,7 @@
829 """Lot of files in a dir, and lots of dirs."""829 """Lot of files in a dir, and lots of dirs."""
830 # almost all known, to force the system to go deep830 # almost all known, to force the system to go deep
831 dirs = "abcdefghijklmnopq" * 20831 dirs = "abcdefghijklmnopq" * 20
832 for i in range(1, len(dirs)+1):832 for i in range(1, len(dirs) + 1):
833 dirpath = os.path.join(*dirs[:i])833 dirpath = os.path.join(*dirs[:i])
834 self.create_node(dirpath, is_dir=True)834 self.create_node(dirpath, is_dir=True)
835 basedir = os.path.join(*dirs)835 basedir = os.path.join(*dirs)
@@ -840,11 +840,11 @@
840 # some files in some dirs840 # some files in some dirs
841 files = "rstuvwxyz"841 files = "rstuvwxyz"
842 for f in files:842 for f in files:
843 path = os.path.join(*dirs[:3]+f)843 path = os.path.join(*dirs[:3] + f)
844 self.create_node(path, is_dir=False)844 self.create_node(path, is_dir=False)
845 path = os.path.join(*dirs[:6]+f)845 path = os.path.join(*dirs[:6] + f)
846 self.create_node(path, is_dir=False)846 self.create_node(path, is_dir=False)
847 sh2 = os.path.join(self.share.path, *dirs[:6]+"q")847 sh2 = os.path.join(self.share.path, *dirs[:6] + "q")
848 open_file(sh2, "w").close()848 open_file(sh2, "w").close()
849849
850 # scan!850 # scan!
@@ -1953,7 +1953,7 @@
1953 self.assertFalse(path_exists(partial_path))1953 self.assertFalse(path_exists(partial_path))
1954 # logged in warning1954 # logged in warning
1955 self.assertTrue(self.handler.check_warning(1955 self.assertTrue(self.handler.check_warning(
1956 "Found a directory in SERVER"))1956 "Found a directory in SERVER"))
19571957
1958 def test_check_stat_None(self):1958 def test_check_stat_None(self):
1959 """Test check_stat with oldstat = None."""1959 """Test check_stat with oldstat = None."""
@@ -2041,7 +2041,7 @@
2041 self.assertEqual(self.aq.unlinked, [(self.share.volume_id,2041 self.assertEqual(self.aq.unlinked, [(self.share.volume_id,
2042 "parent_id", "uuid", path, True)])2042 "parent_id", "uuid", path, True)])
2043 self.assertTrue(self.handler.check_info(2043 self.assertTrue(self.handler.check_info(
2044 "generating Unlink from trash"))2044 "generating Unlink from trash"))
20452045
2046 @defer.inlineCallbacks2046 @defer.inlineCallbacks
2047 def test_trash_two(self):2047 def test_trash_two(self):
20482048
=== modified file 'magicicadaclient/syncdaemon/tests/test_logger.py'
--- magicicadaclient/syncdaemon/tests/test_logger.py 2018-05-19 20:44:54 +0000
+++ magicicadaclient/syncdaemon/tests/test_logger.py 2018-06-03 23:09:20 +0000
@@ -1,8 +1,7 @@
1# -*- coding: utf-8 -*-1# -*- coding: utf-8 -*-
2#2#
3# Author: Guillermo Gonzalez <guillermo.gonzalez@canonical.com>
4#
5# Copyright 2009-2012 Canonical Ltd.3# Copyright 2009-2012 Canonical Ltd.
4# Copyright 2018 Chicharreros (https://launchpad.net/~chicharreros)
6#5#
7# This program is free software: you can redistribute it and/or modify it6# This program is free software: you can redistribute it and/or modify it
8# under the terms of the GNU General Public License version 3, as published7# under the terms of the GNU General Public License version 3, as published
@@ -37,9 +36,8 @@
37from twisted.internet import defer36from twisted.internet import defer
38from twisted.trial import unittest37from twisted.trial import unittest
3938
40from ubuntuone.devtools.handlers import MementoHandler39from devtools.handlers import MementoHandler
41from ubuntuone.devtools.testcases import skipIfOS40from devtools.testcases import skipIfOS
42
43from magicicadaclient.syncdaemon.logger import (41from magicicadaclient.syncdaemon.logger import (
44 DebugCapture,42 DebugCapture,
45 NOTE,43 NOTE,
@@ -302,7 +300,7 @@
302 self.handler.addFilter(MultiFilter([self.__class__.__name__]))300 self.handler.addFilter(MultiFilter([self.__class__.__name__]))
303 self.logger.debug('this msg should be logged')301 self.logger.debug('this msg should be logged')
304 self.assertEqual(1, len(self.handler.records))302 self.assertEqual(1, len(self.handler.records))
305 other_logger = logging.getLogger("NO_LOG."+self.__class__.__name__)303 other_logger = logging.getLogger("NO_LOG." + self.__class__.__name__)
306 other_logger.debug('this msg shouldn\'t be logged')304 other_logger.debug('this msg shouldn\'t be logged')
307 self.assertEqual(1, len(self.handler.records))305 self.assertEqual(1, len(self.handler.records))
308306
@@ -311,7 +309,7 @@
311 self.handler.addFilter(309 self.handler.addFilter(
312 MultiFilter([self.__class__.__name__,310 MultiFilter([self.__class__.__name__,
313 self.__class__.__name__ + ".child"]))311 self.__class__.__name__ + ".child"]))
314 no_logger = logging.getLogger("NO_LOG."+self.__class__.__name__)312 no_logger = logging.getLogger("NO_LOG." + self.__class__.__name__)
315 yes_logger = logging.getLogger(self.__class__.__name__ + '.child')313 yes_logger = logging.getLogger(self.__class__.__name__ + '.child')
316 self.logger.debug('this msg should be logged')314 self.logger.debug('this msg should be logged')
317 self.assertEqual(1, len(self.handler.records))315 self.assertEqual(1, len(self.handler.records))
318316
=== modified file 'magicicadaclient/syncdaemon/tests/test_main.py'
--- magicicadaclient/syncdaemon/tests/test_main.py 2018-05-19 20:44:54 +0000
+++ magicicadaclient/syncdaemon/tests/test_main.py 2018-06-03 23:09:20 +0000
@@ -33,15 +33,12 @@
33import os33import os
3434
35from twisted.internet import defer, reactor35from twisted.internet import defer, reactor
36from ubuntuone.devtools.handlers import MementoHandler
37from magicicadaclient.platform import expand_user
3836
39from magicicadaclient.testing.testcase import (37from devtools.handlers import MementoHandler
40 BaseTwistedTestCase, FAKED_CREDENTIALS, FakeMonitor
41)
42from magicicadaclient.clientdefs import VERSION38from magicicadaclient.clientdefs import VERSION
43from magicicadaclient.logger import NOTE39from magicicadaclient.logger import NOTE
44from magicicadaclient.platform import (40from magicicadaclient.platform import (
41 expand_user,
45 is_link,42 is_link,
46 make_dir,43 make_dir,
47 make_link,44 make_link,
@@ -49,6 +46,9 @@
49 remove_dir,46 remove_dir,
50)47)
51from magicicadaclient.syncdaemon import main as main_mod48from magicicadaclient.syncdaemon import main as main_mod
49from magicicadaclient.testing.testcase import (
50 BaseTwistedTestCase, FAKED_CREDENTIALS, FakeMonitor
51)
5252
5353
54class FakeListener(object):54class FakeListener(object):
5555
=== modified file 'magicicadaclient/syncdaemon/tests/test_offloadqueue.py'
--- magicicadaclient/syncdaemon/tests/test_offloadqueue.py 2018-05-19 20:44:54 +0000
+++ magicicadaclient/syncdaemon/tests/test_offloadqueue.py 2018-06-03 23:09:20 +0000
@@ -1,6 +1,7 @@
1# -*- coding: utf-8 -*-1# -*- coding: utf-8 -*-
2#2#
3# Copyright 2012 Canonical Ltd.3# Copyright 2012 Canonical Ltd.
4# Copyright 2018 Chicharreros (https://launchpad.net/~chicharreros)
4#5#
5# This program is free software: you can redistribute it and/or modify it6# This program is free software: you can redistribute it and/or modify it
6# under the terms of the GNU General Public License version 3, as published7# under the terms of the GNU General Public License version 3, as published
@@ -36,10 +37,10 @@
3637
37from twisted.trial.unittest import TestCase as TwistedTestCase38from twisted.trial.unittest import TestCase as TwistedTestCase
3839
39from ubuntuone.devtools.handlers import MementoHandler40from devtools.handlers import MementoHandler
40from magicicadaclient.syncdaemon.offload_queue import OffloadQueue, STRUCT_SIZE
41from magicicadaclient.syncdaemon.interfaces import IMarker41from magicicadaclient.syncdaemon.interfaces import IMarker
42from magicicadaclient.syncdaemon.marker import MDMarker42from magicicadaclient.syncdaemon.marker import MDMarker
43from magicicadaclient.syncdaemon.offload_queue import OffloadQueue, STRUCT_SIZE
4344
4445
45class OffloadQueueTestCase(TwistedTestCase):46class OffloadQueueTestCase(TwistedTestCase):
4647
=== modified file 'magicicadaclient/syncdaemon/tests/test_pathlockingtree.py'
--- magicicadaclient/syncdaemon/tests/test_pathlockingtree.py 2018-05-19 20:44:54 +0000
+++ magicicadaclient/syncdaemon/tests/test_pathlockingtree.py 2018-06-03 23:09:20 +0000
@@ -1,8 +1,5 @@
1# ubuntuone.syncdaemon.tests.test_pathlockingtree - PathLockingTree tests
2#
3# Author: Facundo Batista <facundo@canonical.com>
4#
5# Copyright 2011-2012 Canonical Ltd.1# Copyright 2011-2012 Canonical Ltd.
2# Copyright 2018 Chicharreros (https://launchpad.net/~chicharreros)
6#3#
7# This program is free software: you can redistribute it and/or modify it4# This program is free software: you can redistribute it and/or modify it
8# under the terms of the GNU General Public License version 3, as published5# under the terms of the GNU General Public License version 3, as published
@@ -35,7 +32,7 @@
35from twisted.internet import defer32from twisted.internet import defer
36from twisted.trial.unittest import TestCase as TwistedTestCase33from twisted.trial.unittest import TestCase as TwistedTestCase
3734
38from ubuntuone.devtools.handlers import MementoHandler35from devtools.handlers import MementoHandler
39from magicicadaclient.syncdaemon.action_queue import PathLockingTree36from magicicadaclient.syncdaemon.action_queue import PathLockingTree
4037
4138
4239
=== modified file 'magicicadaclient/syncdaemon/tests/test_sync.py'
--- magicicadaclient/syncdaemon/tests/test_sync.py 2018-05-19 20:44:54 +0000
+++ magicicadaclient/syncdaemon/tests/test_sync.py 2018-06-03 23:09:20 +0000
@@ -43,15 +43,9 @@
43from magicicadaprotocol.request import ROOT43from magicicadaprotocol.request import ROOT
44from twisted.internet import defer44from twisted.internet import defer
45from twisted.python.failure import Failure45from twisted.python.failure import Failure
46from ubuntuone.devtools.handlers import MementoHandler
47from ubuntuone.devtools.testcases import skipIfOS
4846
49from magicicadaclient.testing.testcase import (47from devtools.handlers import MementoHandler
50 FakeMain,48from devtools.testcases import skipIfOS
51 FakeVolumeManager,
52 BaseTwistedTestCase,
53 Listener,
54)
55from magicicadaclient.platform import (49from magicicadaclient.platform import (
56 make_dir,50 make_dir,
57 open_file,51 open_file,
@@ -61,10 +55,20 @@
61from magicicadaclient.syncdaemon.filesystem_manager import FileSystemManager55from magicicadaclient.syncdaemon.filesystem_manager import FileSystemManager
62from magicicadaclient.syncdaemon.tritcask import Tritcask56from magicicadaclient.syncdaemon.tritcask import Tritcask
63from magicicadaclient.syncdaemon.fsm import fsm as fsm_module57from magicicadaclient.syncdaemon.fsm import fsm as fsm_module
64from magicicadaclient.syncdaemon.sync import FSKey, Sync, SyncStateMachineRunner58from magicicadaclient.syncdaemon.sync import (
59 FSKey,
60 Sync,
61 SyncStateMachineRunner,
62)
65from magicicadaclient.syncdaemon.volume_manager import Share63from magicicadaclient.syncdaemon.volume_manager import Share
66from magicicadaclient.syncdaemon.event_queue import EventQueue, EVENTS64from magicicadaclient.syncdaemon.event_queue import EventQueue, EVENTS
67from magicicadaclient.syncdaemon.marker import MDMarker65from magicicadaclient.syncdaemon.marker import MDMarker
66from magicicadaclient.testing.testcase import (
67 FakeMain,
68 FakeVolumeManager,
69 BaseTwistedTestCase,
70 Listener,
71)
6872
6973
70class TestSyncClassAPI(unittest.TestCase):74class TestSyncClassAPI(unittest.TestCase):
7175
=== modified file 'magicicadaclient/syncdaemon/tests/test_tritcask.py'
--- magicicadaclient/syncdaemon/tests/test_tritcask.py 2018-05-19 20:44:54 +0000
+++ magicicadaclient/syncdaemon/tests/test_tritcask.py 2018-06-03 23:09:20 +0000
@@ -1,8 +1,5 @@
1# tests.syncdaemon.test_tritcask - tritcask tests
2#
3# Author: Guillermo Gonzalez <guillermo.gonzalez@canonical.com>
4#
5# Copyright 2010-2012 Canonical Ltd.1# Copyright 2010-2012 Canonical Ltd.
2# Copyright 2018 Chicharreros (https://launchpad.net/~chicharreros)
6#3#
7# This program is free software: you can redistribute it and/or modify it4# This program is free software: you can redistribute it and/or modify it
8# under the terms of the GNU General Public License version 3, as published5# under the terms of the GNU General Public License version 3, as published
@@ -42,8 +39,8 @@
42from operator import attrgetter39from operator import attrgetter
43from twisted.internet import defer40from twisted.internet import defer
4441
42from devtools.handlers import MementoHandler
45from magicicadaclient.testing.testcase import BaseTwistedTestCase43from magicicadaclient.testing.testcase import BaseTwistedTestCase
46from ubuntuone.devtools.handlers import MementoHandler
47from magicicadaclient.syncdaemon import tritcask44from magicicadaclient.syncdaemon import tritcask
48from magicicadaclient.syncdaemon.tritcask import (45from magicicadaclient.syncdaemon.tritcask import (
49 TOMBSTONE,46 TOMBSTONE,
@@ -343,7 +340,7 @@
343 fd.read(crc32_size + 4)340 fd.read(crc32_size + 4)
344 fd.truncate()341 fd.truncate()
345 # write a different value -> random bytes342 # write a different value -> random bytes
346 fd.write(os.urandom(header_size/2))343 fd.write(os.urandom(header_size / 2))
347 fd.flush()344 fd.flush()
348 fmap = mmap.mmap(fd.fileno(), 0, access=mmap.ACCESS_READ)345 fmap = mmap.mmap(fd.fileno(), 0, access=mmap.ACCESS_READ)
349 with contextlib.closing(fmap):346 with contextlib.closing(fmap):
@@ -1377,7 +1374,7 @@
1377 # check that the TOMBSTONE is there for these keys1374 # check that the TOMBSTONE is there for these keys
1378 with open(self.db.live_file.filename, 'r+b') as f:1375 with open(self.db.live_file.filename, 'r+b') as f:
1379 raw_data_len = len(key) + len(TOMBSTONE) + crc32_size + header_size1376 raw_data_len = len(key) + len(TOMBSTONE) + crc32_size + header_size
1380 f.seek(-1*raw_data_len, os.SEEK_END)1377 f.seek(-1 * raw_data_len, os.SEEK_END)
1381 raw_data = f.read(raw_data_len)1378 raw_data = f.read(raw_data_len)
1382 self.assertEqual(TOMBSTONE,1379 self.assertEqual(TOMBSTONE,
1383 raw_data[crc32_size + header_size + len(key):])1380 raw_data[crc32_size + header_size + len(key):])
@@ -1743,9 +1740,10 @@
1743 for i in range(20):1740 for i in range(20):
1744 keydir[(0, str(uuid.uuid4()))] = KeydirEntry(1741 keydir[(0, str(uuid.uuid4()))] = KeydirEntry(
1745 file_id_1, timestamp(), len(str(uuid.uuid4())), i + 10)1742 file_id_1, timestamp(), len(str(uuid.uuid4())), i + 10)
1746 entry_size = len(str(uuid.uuid4()))*2 + header_size + crc32_size1743 entry_size = len(str(uuid.uuid4())) * 2 + header_size + crc32_size
1747 self.assertEqual(entry_size*10, keydir._stats[file_id]['live_bytes'])1744 self.assertEqual(entry_size * 10, keydir._stats[file_id]['live_bytes'])
1748 self.assertEqual(entry_size*20, keydir._stats[file_id_1]['live_bytes'])1745 self.assertEqual(
1746 entry_size * 20, keydir._stats[file_id_1]['live_bytes'])
17491747
1750 def test_update_entry(self):1748 def test_update_entry(self):
1751 """Test that __setitem__ updates the stats for an entry."""1749 """Test that __setitem__ updates the stats for an entry."""
@@ -1796,10 +1794,10 @@
1796 len(str(uuid.uuid4())), i + 10)1794 len(str(uuid.uuid4())), i + 10)
1797 if i % 2:1795 if i % 2:
1798 keydir.remove((0, key))1796 keydir.remove((0, key))
1799 entry_size = len(str(uuid.uuid4()))*2 + header_size + crc32_size1797 entry_size = len(str(uuid.uuid4())) * 2 + header_size + crc32_size
1800 self.assertEqual(entry_size*(10/2),1798 self.assertEqual(entry_size * (10 / 2),
1801 keydir._stats[file_id]['live_bytes'])1799 keydir._stats[file_id]['live_bytes'])
1802 self.assertEqual(entry_size*(20/2),1800 self.assertEqual(entry_size * (20 / 2),
1803 keydir._stats[file_id_1]['live_bytes'])1801 keydir._stats[file_id_1]['live_bytes'])
18041802
1805 def test_remove_missing_key(self):1803 def test_remove_missing_key(self):
18061804
=== modified file 'magicicadaclient/syncdaemon/tests/test_vm.py'
--- magicicadaclient/syncdaemon/tests/test_vm.py 2018-05-19 20:44:54 +0000
+++ magicicadaclient/syncdaemon/tests/test_vm.py 2018-06-03 23:09:20 +0000
@@ -45,15 +45,16 @@
4545
46from mocker import Mocker, MATCH46from mocker import Mocker, MATCH
47from twisted.internet import defer, reactor47from twisted.internet import defer, reactor
48from ubuntuone.devtools.handlers import MementoHandler
49from ubuntuone.devtools.testcases import skipIfOS
5048
51from magicicadaclient.testing.testcase import (49from devtools.handlers import MementoHandler
52 BaseTwistedTestCase,50from devtools.testcases import skipIfOS
53 FakeMain,
54)
55from magicicadaclient import platform51from magicicadaclient import platform
56from magicicadaclient.syncdaemon import config, event_queue, tritcask, volume_manager52from magicicadaclient.syncdaemon import (
53 config,
54 event_queue,
55 tritcask,
56 volume_manager,
57)
57from magicicadaclient.syncdaemon.volume_manager import (58from magicicadaclient.syncdaemon.volume_manager import (
58 ACCESS_LEVEL_RO,59 ACCESS_LEVEL_RO,
59 ACCESS_LEVEL_RW,60 ACCESS_LEVEL_RW,
@@ -83,6 +84,7 @@
83 set_dir_readonly,84 set_dir_readonly,
84 set_dir_readwrite,85 set_dir_readwrite,
85)86)
87from magicicadaclient.testing.testcase import BaseTwistedTestCase, FakeMain
8688
87# grab the metadata version before tests fiddle with it89# grab the metadata version before tests fiddle with it
88CURRENT_METADATA_VERSION = VolumeManager.METADATA_VERSION90CURRENT_METADATA_VERSION = VolumeManager.METADATA_VERSION
@@ -2760,7 +2762,7 @@
2760 root_volume = volumes.RootVolume(uuid.uuid4(), None, 10)2762 root_volume = volumes.RootVolume(uuid.uuid4(), None, 10)
2761 d = defer.Deferred()2763 d = defer.Deferred()
2762 self.vm._got_root = lambda node_id, free_bytes: d.callback(2764 self.vm._got_root = lambda node_id, free_bytes: d.callback(
2763 (node_id, free_bytes))2765 (node_id, free_bytes))
2764 self.main.event_q.push('AQ_LIST_VOLUMES', volumes=[root_volume])2766 self.main.event_q.push('AQ_LIST_VOLUMES', volumes=[root_volume])
2765 root_node_id, free_bytes = yield d2767 root_node_id, free_bytes = yield d
2766 self.assertEqual(str(root_volume.node_id), root_node_id)2768 self.assertEqual(str(root_volume.node_id), root_node_id)
@@ -4172,7 +4174,7 @@
4172 self.patch(VolumeManager, "METADATA_VERSION", self.fake_version)4174 self.patch(VolumeManager, "METADATA_VERSION", self.fake_version)
4173 self.temp_dir = os.path.join(self.mktemp(), u"Ñandú")4175 self.temp_dir = os.path.join(self.mktemp(), u"Ñandú")
4174 self.version_file = os.path.join(self.temp_dir, ".version").encode(4176 self.version_file = os.path.join(self.temp_dir, ".version").encode(
4175 sys.getfilesystemencoding())4177 sys.getfilesystemencoding())
4176 self.md_upgrader = MetadataUpgrader(self.temp_dir.encode("utf-8"),4178 self.md_upgrader = MetadataUpgrader(self.temp_dir.encode("utf-8"),
4177 "", "", "", "", "", "", None)4179 "", "", "", "", "", "", None)
41784180
41794181
=== modified file 'magicicadaclient/testing/testcase.py'
--- magicicadaclient/testing/testcase.py 2018-05-19 20:44:54 +0000
+++ magicicadaclient/testing/testcase.py 2018-06-03 23:09:20 +0000
@@ -43,10 +43,10 @@
4343
44from twisted.internet import defer44from twisted.internet import defer
45from twisted.trial.unittest import TestCase as TwistedTestCase45from twisted.trial.unittest import TestCase as TwistedTestCase
46from ubuntuone.devtools.testcases import skipIfOS
47from zope.interface import implements46from zope.interface import implements
48from zope.interface.verify import verifyObject47from zope.interface.verify import verifyObject
4948
49from devtools.testcases import skipIfOS
50from magicicadaclient.syncdaemon import (50from magicicadaclient.syncdaemon import (
51 config,51 config,
52 action_queue,52 action_queue,
5353
=== modified file 'magicicadaclient/utils/__init__.py'
--- magicicadaclient/utils/__init__.py 2016-06-04 21:14:35 +0000
+++ magicicadaclient/utils/__init__.py 2018-06-03 23:09:20 +0000
@@ -73,8 +73,8 @@
7373
74 # otherwise, try to load 'dir_constant' from installation path74 # otherwise, try to load 'dir_constant' from installation path
75 try:75 try:
76 __import__('ubuntuone.clientdefs', None, None, [''])76 __import__('magicicadaclient.clientdefs', None, None, [''])
77 module = sys.modules.get('ubuntuone.clientdefs')77 module = sys.modules.get('magicicadaclient.clientdefs')
78 return getattr(module, dir_constant)78 return getattr(module, dir_constant)
79 except (ImportError, AttributeError):79 except (ImportError, AttributeError):
80 msg = '_get_dir: can not build a valid path. Giving up. ' \80 msg = '_get_dir: can not build a valid path. Giving up. ' \
@@ -135,8 +135,7 @@
135135
136 if getattr(sys, "frozen", None) is not None:136 if getattr(sys, "frozen", None) is not None:
137 if sys.platform == "win32":137 if sys.platform == "win32":
138 ssl_cert_location = list(load_config_paths(138 ssl_cert_location = list(load_config_paths("ubuntuone"))[1]
139 "ubuntuone"))[1]
140 elif sys.platform == "darwin":139 elif sys.platform == "darwin":
141 main_app_dir = "".join(__file__.partition(".app")[:-1])140 main_app_dir = "".join(__file__.partition(".app")[:-1])
142 main_app_resources_dir = os.path.join(main_app_dir,141 main_app_resources_dir = os.path.join(main_app_dir,
143142
=== modified file 'magicicadaclient/utils/tests/test_common.py'
--- magicicadaclient/utils/tests/test_common.py 2018-05-19 20:44:54 +0000
+++ magicicadaclient/utils/tests/test_common.py 2018-06-03 23:09:20 +0000
@@ -1,7 +1,7 @@
1# -*- coding: utf-8 -*-1# -*- coding: utf-8 -*-
2#2#
3# Copyright 2011-2012 Canonical Ltd.3# Copyright 2011-2012 Canonical Ltd.
4# Copyright 2015-2017 Chicharreros (https://launchpad.net/~chicharreros)4# Copyright 2015-2018 Chicharreros (https://launchpad.net/~chicharreros)
5#5#
6# This program is free software: you can redistribute it and/or modify it6# This program is free software: you can redistribute it and/or modify it
7# under the terms of the GNU General Public License version 3, as published7# under the terms of the GNU General Public License version 3, as published
@@ -38,13 +38,13 @@
3838
39from twisted.internet import defer39from twisted.internet import defer
40from twisted.web import resource40from twisted.web import resource
41from ubuntuone.devtools.handlers import MementoHandler
42from ubuntuone.devtools.testing.txwebserver import HTTPWebServer
4341
42from devtools.handlers import MementoHandler
43from devtools.testing.txwebserver import HTTPWebServer
44from magicicadaclient import utils44from magicicadaclient import utils
45from magicicadaclient.tests import TestCase45from magicicadaclient.tests import TestCase
4646
47CONSTANTS_MODULE = 'ubuntuone.clientdefs'47CONSTANTS_MODULE = 'magicicadaclient.clientdefs'
48NOT_DEFINED = object()48NOT_DEFINED = object()
4949
5050
5151
=== modified file 'magicicadaclient/utils/tests/test_ipc.py'
--- magicicadaclient/utils/tests/test_ipc.py 2018-05-19 20:44:54 +0000
+++ magicicadaclient/utils/tests/test_ipc.py 2018-06-03 23:09:20 +0000
@@ -1,7 +1,7 @@
1# -*- coding: utf-8 -*-1# -*- coding: utf-8 -*-
2#2#
3# Copyright 2011-2012 Canonical Ltd.3# Copyright 2011-2012 Canonical Ltd.
4# Copyright 2015-2016 Chicharreros (https://launchpad.net/~chicharreros)4# Copyright 2015-2018 Chicharreros (https://launchpad.net/~chicharreros)
5#5#
6# This program is free software: you can redistribute it and/or modify it6# This program is free software: you can redistribute it and/or modify it
7# under the terms of the GNU General Public License version 3, as published7# under the terms of the GNU General Public License version 3, as published
@@ -39,13 +39,13 @@
39 DeadReferenceError,39 DeadReferenceError,
40 NoSuchMethod,40 NoSuchMethod,
41)41)
42from ubuntuone.devtools.handlers import MementoHandler42
43from ubuntuone.devtools.testcases import skipIfOS43from devtools.handlers import MementoHandler
44from ubuntuone.devtools.testcases.txsocketserver import (44from devtools.testcases import skipIfOS
45from devtools.testcases.txsocketserver import (
45 TidyUnixServer,46 TidyUnixServer,
46 TCPPbServerTestCase,47 TCPPbServerTestCase,
47)48)
48
49from magicicadaclient.tests import TestCase49from magicicadaclient.tests import TestCase
50from magicicadaclient.utils import ipc50from magicicadaclient.utils import ipc
5151
5252
=== modified file 'magicicadaclient/utils/tests/test_tcpactivation.py'
--- magicicadaclient/utils/tests/test_tcpactivation.py 2018-05-19 20:44:54 +0000
+++ magicicadaclient/utils/tests/test_tcpactivation.py 2018-06-03 23:09:20 +0000
@@ -1,7 +1,7 @@
1# -*- coding: utf-8 -*-1# -*- coding: utf-8 -*-
2#2#
3# Copyright 2011-2012 Canonical Ltd.3# Copyright 2011-2012 Canonical Ltd.
4# Copyright 2015-2016 Chicharreros (https://launchpad.net/~chicharreros)4# Copyright 2015-2018 Chicharreros (https://launchpad.net/~chicharreros)
5#5#
6# This program is free software: you can redistribute it and/or modify it6# This program is free software: you can redistribute it and/or modify it
7# under the terms of the GNU General Public License version 3, as published7# under the terms of the GNU General Public License version 3, as published
@@ -36,11 +36,8 @@
36from twisted.internet import defer, protocol, task36from twisted.internet import defer, protocol, task
37from twisted.trial.unittest import TestCase37from twisted.trial.unittest import TestCase
3838
39from ubuntuone.devtools.testcases.txsocketserver import (
40 ServerTestCase,
41 TidyTCPServer,
42)
4339
40from devtools.testcases.txsocketserver import ServerTestCase, TidyTCPServer
44from magicicadaclient.utils import tcpactivation41from magicicadaclient.utils import tcpactivation
45from magicicadaclient.utils.tcpactivation import (42from magicicadaclient.utils.tcpactivation import (
46 ActivationClient,43 ActivationClient,
4744
=== modified file 'magicicadaclient/utils/tests/test_translation.py'
--- magicicadaclient/utils/tests/test_translation.py 2018-05-19 20:44:54 +0000
+++ magicicadaclient/utils/tests/test_translation.py 2018-06-03 23:09:20 +0000
@@ -34,8 +34,8 @@
34import sys34import sys
3535
36from twisted.internet import defer36from twisted.internet import defer
37from ubuntuone.devtools.testcases import TestCase, skipIfNotOS
3837
38from devtools.testcases import TestCase, skipIfNotOS
39from magicicadaclient.utils import translation39from magicicadaclient.utils import translation
4040
41TEST_DOMAIN = 'test-domain'41TEST_DOMAIN = 'test-domain'
4242
=== modified file 'magicicadaclient/utils/tests/test_txsecrets.py'
--- magicicadaclient/utils/tests/test_txsecrets.py 2018-05-19 20:44:54 +0000
+++ magicicadaclient/utils/tests/test_txsecrets.py 2018-06-03 23:09:20 +0000
@@ -1,7 +1,7 @@
1# -*- coding: utf-8 -*-1# -*- coding: utf-8 -*-
2#2#
3# Copyright 2010-2012 Canonical Ltd.3# Copyright 2010-2012 Canonical Ltd.
4# Copyright 2015-2016 Chicharreros (https://launchpad.net/~chicharreros)4# Copyright 2015-2018 Chicharreros (https://launchpad.net/~chicharreros)
5#5#
6# This program is free software: you can redistribute it and/or modify it6# This program is free software: you can redistribute it and/or modify it
7# under the terms of the GNU General Public License version 3, as published7# under the terms of the GNU General Public License version 3, as published
@@ -36,8 +36,8 @@
36import dbus.service36import dbus.service
3737
38from twisted.internet.defer import inlineCallbacks, returnValue38from twisted.internet.defer import inlineCallbacks, returnValue
39from ubuntuone.devtools.testcases.dbus import DBusTestCase
4039
40from devtools.testcases.dbus import DBusTestCase
41from magicicadaclient.utils import txsecrets41from magicicadaclient.utils import txsecrets
4242
43KEY_TYPE_ATTR = {"key-type": "Foo credentials"}43KEY_TYPE_ATTR = {"key-type": "Foo credentials"}
4444
=== modified file 'requirements-devel.txt'
--- requirements-devel.txt 2018-03-18 15:03:09 +0000
+++ requirements-devel.txt 2018-06-03 23:09:20 +0000
@@ -1,1 +1,3 @@
1coverage==3.7.1
1flake8==3.5.02flake8==3.5.0
3mocker==1.1.1
24
=== modified file 'requirements.txt'
--- requirements.txt 2018-04-14 23:34:20 +0000
+++ requirements.txt 2018-06-03 23:09:20 +0000
@@ -1,2 +1,7 @@
1configglue==1.1.3.post0
2dbus-python==1.2.8
3magicicadaprotocol==2.0
4PyGObject==3.28.2
5pyinotify==0.9.6
1Send2Trash==1.5.06Send2Trash==1.5.0
2magicicadaprotocol==2.07Twisted==18.4.0
38
=== modified file 'run-tests'
--- run-tests 2018-05-19 20:44:54 +0000
+++ run-tests 2018-06-03 23:09:20 +0000
@@ -49,5 +49,5 @@
4949
50echo "*** Running test suite for ""$MODULE"" ***"50echo "*** Running test suite for ""$MODULE"" ***"
51export SSL_CERTIFICATES_DIR=/etc/ssl/certs51export SSL_CERTIFICATES_DIR=/etc/ssl/certs
52.env/bin/python /usr/bin/u1trial -i "$IGNORE_FILES" -p "$IGNORE_PATHS" $MODULE52.env/bin/python contrib/u1trial -i "$IGNORE_FILES" -p "$IGNORE_PATHS" $MODULE
53rm -rf _trial_temp53rm -rf _trial_temp
5454
=== modified file 'setup.py'
--- setup.py 2018-05-31 16:29:41 +0000
+++ setup.py 2018-06-03 23:09:20 +0000
@@ -32,15 +32,9 @@
32import os32import os
33import sys33import sys
3434
35try:35from setuptools import setup
36 from DistUtilsExtra.command import build_extra, build_i18n36from setuptools.command.install import install
37 import DistUtilsExtra.auto37from distutils.command import build, clean
38except ImportError:
39 print >> sys.stderr, 'To build this program you need '\
40 'https://launchpad.net/python-distutils-extra'
41 raise
42assert DistUtilsExtra.auto.__version__ >= '2.18', \
43 'needs DistUtilsExtra.auto >= 2.18'
4438
4539
46PROJECT_NAME = 'magicicada-client'40PROJECT_NAME = 'magicicada-client'
@@ -83,7 +77,7 @@
83 out_file.write(content)77 out_file.write(content)
8478
8579
86class Install(DistUtilsExtra.auto.install_auto):80class Install(install):
87 """Class to install proper files."""81 """Class to install proper files."""
8882
89 def run(self):83 def run(self):
@@ -110,7 +104,8 @@
110 prefix = self.install_data.replace(104 prefix = self.install_data.replace(
111 self.root if self.root is not None else '', '')105 self.root if self.root is not None else '', '')
112 replace_variables(SERVICE_FILES, prefix)106 replace_variables(SERVICE_FILES, prefix)
113 DistUtilsExtra.auto.install_auto.run(self)107 install.run(self)
108
114 # Replace the CLIENTDEFS paths here, so that we can do it directly in109 # Replace the CLIENTDEFS paths here, so that we can do it directly in
115 # the installed copy, rather than the lcoal copy. This allows us to110 # the installed copy, rather than the lcoal copy. This allows us to
116 # have a semi-generated version for use in tests, and a full version111 # have a semi-generated version for use in tests, and a full version
@@ -127,7 +122,7 @@
127 out_file.write(content)122 out_file.write(content)
128123
129124
130class Build(build_extra.build_extra):125class Build(build.build):
131 """Build PyQt (.ui) files and resources."""126 """Build PyQt (.ui) files and resources."""
132127
133 description = "build PyQt GUIs (.ui) and resources (.qrc)"128 description = "build PyQt GUIs (.ui) and resources (.qrc)"
@@ -135,10 +130,10 @@
135 def run(self):130 def run(self):
136 """Execute the command."""131 """Execute the command."""
137 replace_variables(BUILD_FILES)132 replace_variables(BUILD_FILES)
138 build_extra.build_extra.run(self)133 build.build.run(self)
139134
140135
141class Clean(DistUtilsExtra.auto.clean_build_tree):136class Clean(clean.clean):
142 """Class to clean up after the build."""137 """Class to clean up after the build."""
143138
144 def run(self):139 def run(self):
@@ -147,24 +142,7 @@
147 if os.path.exists(built_file):142 if os.path.exists(built_file):
148 os.unlink(built_file)143 os.unlink(built_file)
149144
150 DistUtilsExtra.auto.clean_build_tree.run(self)145 clean.clean.run(self)
151
152
153class BuildLocale(build_i18n.build_i18n):
154 """Work around a bug in DistUtilsExtra."""
155
156 def run(self):
157 """Magic."""
158 build_i18n.build_i18n.run(self)
159 i = 0
160 for df in self.distribution.data_files:
161 if df[0].startswith('etc/xdg/'):
162 if sys.platform not in ('darwin', 'win32'):
163 new_df = (df[0].replace('etc/xdg/', '/etc/xdg/'), df[1])
164 self.distribution.data_files[i] = new_df
165 else:
166 self.distribution.data_files.pop(i)
167 i += 1
168146
169147
170def set_py2exe_paths():148def set_py2exe_paths():
@@ -191,10 +169,9 @@
191169
192170
193cmdclass = {171cmdclass = {
194 'install': Install,
195 'build': Build,172 'build': Build,
196 'clean': Clean,173 'clean': Clean,
197 'build_i18n': BuildLocale,174 'install': Install,
198}175}
199176
200bin_scripts = [177bin_scripts = [
@@ -236,7 +213,7 @@
236 scripts.extend(bin_scripts)213 scripts.extend(bin_scripts)
237 extra = {}214 extra = {}
238215
239DistUtilsExtra.auto.setup(216setup(
240 name=PROJECT_NAME,217 name=PROJECT_NAME,
241 version=VERSION,218 version=VERSION,
242 license='GPL v3',219 license='GPL v3',

Subscribers

People subscribed via source and target branches

to all changes: