Merge lp:~allenap/maas/remove-unused-testing-things into lp:~maas-committers/maas/trunk
- remove-unused-testing-things
- Merge into trunk
Proposed by
Gavin Panella
Status: | Merged |
---|---|
Approved by: | Gavin Panella |
Approved revision: | no longer in the source branch. |
Merged at revision: | 5649 |
Proposed branch: | lp:~allenap/maas/remove-unused-testing-things |
Merge into: | lp:~maas-committers/maas/trunk |
Diff against target: |
450 lines (+20/-285) 4 files modified
src/maasserver/testing/testcase.py (+1/-140) src/maastesting/tests/test_factory.py (+13/-10) src/maastesting/utils.py (+2/-128) src/provisioningserver/utils/tests/test_fs.py (+4/-7) |
To merge this branch: | bzr merge lp:~allenap/maas/remove-unused-testing-things |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Lee Trager (community) | Approve | ||
Review via email: mp+314210@code.launchpad.net |
Commit message
Remove SeleniumTestCase and other miscellaneous unused or little-used testing bits.
Description of the change
To post a comment you must log in.
Revision history for this message
Gavin Panella (allenap) wrote : | # |
I'm holding on this for a few days because I may end up using run_isolated elsewhere.
Revision history for this message
Gavin Panella (allenap) wrote : | # |
I didn't end up using run_isolated where I thought I might.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'src/maasserver/testing/testcase.py' |
2 | --- src/maasserver/testing/testcase.py 2017-01-05 16:18:56 +0000 |
3 | +++ src/maasserver/testing/testcase.py 2017-01-06 10:04:40 +0000 |
4 | @@ -1,4 +1,4 @@ |
5 | -# Copyright 2012-2016 Canonical Ltd. This software is licensed under the |
6 | +# Copyright 2012-2017 Canonical Ltd. This software is licensed under the |
7 | # GNU Affero General Public License version 3 (see the file LICENSE). |
8 | |
9 | """Custom test-case classes.""" |
10 | @@ -8,24 +8,15 @@ |
11 | 'MAASLegacyTransactionServerTestCase', |
12 | 'MAASServerTestCase', |
13 | 'MAASTransactionServerTestCase', |
14 | - 'SeleniumTestCase', |
15 | 'SerializationFailureTestCase', |
16 | - 'TestWithoutCrochetMixin', |
17 | 'UniqueViolationTestCase', |
18 | ] |
19 | |
20 | from itertools import count |
21 | -import socketserver |
22 | import sys |
23 | import threading |
24 | -from unittest import SkipTest |
25 | -from unittest.mock import Mock |
26 | import warnings |
27 | -import wsgiref |
28 | |
29 | -import crochet |
30 | -import django |
31 | -from django.core.urlresolvers import reverse |
32 | from django.db import ( |
33 | close_old_connections, |
34 | connection, |
35 | @@ -36,7 +27,6 @@ |
36 | IntegrityError, |
37 | OperationalError, |
38 | ) |
39 | -from fixtures import Fixture |
40 | from maasserver.fields import register_mac_type |
41 | from maasserver.testing.factory import factory |
42 | from maasserver.testing.fixtures import ( |
43 | @@ -54,9 +44,7 @@ |
44 | DjangoTestCase, |
45 | DjangoTransactionTestCase, |
46 | ) |
47 | -from maastesting.fixtures import DisplayFixture |
48 | from maastesting.testcase import MAASTestCase |
49 | -from maastesting.utils import run_isolated |
50 | |
51 | |
52 | class MAASRegionTestCaseBase(PostCommitHooksTestMixin): |
53 | @@ -215,133 +203,6 @@ |
54 | self.setUpFixtures() |
55 | |
56 | |
57 | -# Django supports Selenium tests only since version 1.4. |
58 | -django_supports_selenium = (django.VERSION >= (1, 4)) |
59 | - |
60 | -if django_supports_selenium: |
61 | - from django.test import LiveServerTestCase |
62 | - from selenium.webdriver.firefox.webdriver import WebDriver |
63 | -else: |
64 | - LiveServerTestCase = object # noqa |
65 | - |
66 | - |
67 | -class LogSilencerFixture(Fixture): |
68 | - |
69 | - old_handle_error = wsgiref.handlers.BaseHandler.handle_error |
70 | - old_log_exception = wsgiref.handlers.BaseHandler.log_exception |
71 | - |
72 | - def setUp(self): |
73 | - super(LogSilencerFixture, self).setUp() |
74 | - self.silence_loggers() |
75 | - self.addCleanup(self.unsilence_loggers) |
76 | - |
77 | - def silence_loggers(self): |
78 | - # Silence logging of errors to avoid the |
79 | - # "IOError: [Errno 32] Broken pipe" error. |
80 | - socketserver.BaseServer.handle_error = Mock() |
81 | - wsgiref.handlers.BaseHandler.log_exception = Mock() |
82 | - |
83 | - def unsilence_loggers(self): |
84 | - """Restore original handle_error/log_exception methods.""" |
85 | - socketserver.BaseServer.handle_error = self.old_handle_error |
86 | - wsgiref.handlers.BaseHandler.log_exception = self.old_log_exception |
87 | - |
88 | - |
89 | -class SeleniumTestCase( |
90 | - DjangoTransactionTestCase, LiveServerTestCase, |
91 | - PostCommitHooksTestMixin): |
92 | - """Selenium-enabled test case. |
93 | - |
94 | - Two users are pre-created: "user" for a regular user account, or "admin" |
95 | - for an administrator account. Both have the password "test". You can log |
96 | - in as either using `log_in`. |
97 | - """ |
98 | - |
99 | - # Load the selenium test fixture. |
100 | - fixtures = ['src/maastesting/protractor/fixture.yaml'] |
101 | - |
102 | - @classmethod |
103 | - def setUpClass(cls): |
104 | - if not django_supports_selenium: |
105 | - return |
106 | - cls.display = DisplayFixture() |
107 | - cls.display.__enter__() |
108 | - |
109 | - cls.silencer = LogSilencerFixture() |
110 | - cls.silencer.__enter__() |
111 | - |
112 | - cls.selenium = WebDriver() |
113 | - super(SeleniumTestCase, cls).setUpClass() |
114 | - |
115 | - def setUp(self): |
116 | - if not django_supports_selenium: |
117 | - raise SkipTest( |
118 | - "Live tests only enabled if Django.version >=1.4.") |
119 | - super(SeleniumTestCase, self).setUp() |
120 | - |
121 | - @classmethod |
122 | - def tearDownClass(cls): |
123 | - if not django_supports_selenium: |
124 | - return |
125 | - cls.selenium.quit() |
126 | - cls.display.__exit__(None, None, None) |
127 | - cls.silencer.__exit__(None, None, None) |
128 | - super(SeleniumTestCase, cls).tearDownClass() |
129 | - |
130 | - def log_in(self, user='user', password='test'): |
131 | - """Log in as the given user. Defaults to non-admin user.""" |
132 | - self.get_page('login') |
133 | - username_input = self.selenium.find_element_by_id("id_username") |
134 | - username_input.send_keys(user) |
135 | - password_input = self.selenium.find_element_by_id("id_password") |
136 | - password_input.send_keys(password) |
137 | - self.selenium.find_element_by_xpath('//input[@value="Login"]').click() |
138 | - |
139 | - def get_page(self, *reverse_args, **reverse_kwargs): |
140 | - """GET a page. Arguments are passed on to `reverse`.""" |
141 | - path = reverse(*reverse_args, **reverse_kwargs) |
142 | - return self.selenium.get("%s%s" % (self.live_server_url, path)) |
143 | - |
144 | - |
145 | -class TestWithoutCrochetMixin: |
146 | - """Ensure that Crochet's event-loop is not running. |
147 | - |
148 | - Crochet's event-loop cannot easily be resurrected, so this runs each |
149 | - test in a new subprocess. There we can stop Crochet without worrying |
150 | - about how to get it going again. |
151 | - |
152 | - Use this where tests must, for example, patch out global state |
153 | - during testing, where those patches coincide with things that |
154 | - Crochet expects to use too, ``time.sleep`` for example. |
155 | - """ |
156 | - |
157 | - _dead_thread = threading.Thread() |
158 | - _dead_thread.start() |
159 | - _dead_thread.join() |
160 | - |
161 | - def __call__(self, result=None): |
162 | - if result is None: |
163 | - result = self.defaultTestResult() |
164 | - # nose.proxy.ResultProxy.assertMyTest() is weird, and makes |
165 | - # things break, so we neutralise it here. |
166 | - result.assertMyTest = lambda test: None |
167 | - # Finally, run the test in a subprocess. |
168 | - up = super(TestWithoutCrochetMixin, self.__class__) |
169 | - run_isolated(up, self, result) |
170 | - |
171 | - run = __call__ |
172 | - |
173 | - def setUp(self): |
174 | - super(TestWithoutCrochetMixin, self).setUp() |
175 | - # Ensure that Crochet's event-loop has shutdown. The following |
176 | - # runs in the child process started by run_isolated() so we |
177 | - # don't need to repair the damage we do. |
178 | - if crochet._watchdog.is_alive(): |
179 | - crochet._watchdog._canary = self._dead_thread |
180 | - crochet._watchdog.join() # Wait for the watchdog to stop. |
181 | - self.assertFalse(crochet.reactor.running) |
182 | - |
183 | - |
184 | class SerializationFailureTestCase( |
185 | MAASTransactionServerTestCase, PostCommitHooksTestMixin): |
186 | |
187 | |
188 | === modified file 'src/maastesting/tests/test_factory.py' |
189 | --- src/maastesting/tests/test_factory.py 2016-08-09 15:56:46 +0000 |
190 | +++ src/maastesting/tests/test_factory.py 2017-01-06 10:04:40 +0000 |
191 | @@ -1,4 +1,4 @@ |
192 | -# Copyright 2012-2016 Canonical Ltd. This software is licensed under the |
193 | +# Copyright 2012-2017 Canonical Ltd. This software is licensed under the |
194 | # GNU Affero General Public License version 3 (see the file LICENSE). |
195 | |
196 | """Test the factory where appropriate. Don't overdo this.""" |
197 | @@ -8,11 +8,11 @@ |
198 | from datetime import datetime |
199 | from itertools import count |
200 | import os.path |
201 | -import random |
202 | from random import randint |
203 | import subprocess |
204 | from unittest.mock import sentinel |
205 | |
206 | +from maastesting import factory as factory_module |
207 | from maastesting.factory import ( |
208 | factory, |
209 | TooManyRandomRetries, |
210 | @@ -23,7 +23,6 @@ |
211 | MockCalledOnceWith, |
212 | ) |
213 | from maastesting.testcase import MAASTestCase |
214 | -from maastesting.utils import FakeRandInt |
215 | from netaddr import ( |
216 | IPAddress, |
217 | IPNetwork, |
218 | @@ -59,17 +58,21 @@ |
219 | # Artificially limit randint to a very narrow range, to guarantee |
220 | # some repetition in its output, and virtually guarantee that we test |
221 | # both outcomes of the flip-a-coin call in make_vlan_tag. |
222 | - self.patch(random, 'randint', FakeRandInt(random.randint, 0, 1)) |
223 | - outcomes = {factory.make_vlan_tag() for _ in range(1000)} |
224 | - self.assertEqual({1}, outcomes) |
225 | + random = self.patch(factory_module, "random") |
226 | + random.randint.side_effect = [1, 2] |
227 | + outcomes = {factory.make_vlan_tag(), factory.make_vlan_tag()} |
228 | + self.assertEqual({1, 2}, outcomes) |
229 | |
230 | def test_make_vlan_tag_includes_None_if_allow_none(self): |
231 | - self.patch(random, 'randint', FakeRandInt(random.randint, 0, 1)) |
232 | + random = self.patch(factory_module, "random") |
233 | + random.choice.side_effect = [True, False, False] |
234 | + random.randint.side_effect = [1, 2] |
235 | self.assertEqual( |
236 | - {None, 1}, |
237 | + {None, 1, 2}, |
238 | { |
239 | - factory.make_vlan_tag(allow_none=True) |
240 | - for _ in range(1000) |
241 | + factory.make_vlan_tag(allow_none=True), |
242 | + factory.make_vlan_tag(allow_none=True), |
243 | + factory.make_vlan_tag(allow_none=True), |
244 | }) |
245 | |
246 | def test_make_ipv4_address(self): |
247 | |
248 | === modified file 'src/maastesting/utils.py' |
249 | --- src/maastesting/utils.py 2015-12-01 18:12:59 +0000 |
250 | +++ src/maastesting/utils.py 2017-01-06 10:04:40 +0000 |
251 | @@ -1,32 +1,17 @@ |
252 | -# Copyright 2012-2015 Canonical Ltd. This software is licensed under the |
253 | +# Copyright 2012-2017 Canonical Ltd. This software is licensed under the |
254 | # GNU Affero General Public License version 3 (see the file LICENSE). |
255 | |
256 | """Testing utilities.""" |
257 | |
258 | __all__ = [ |
259 | "age_file", |
260 | - "content_from_file", |
261 | "extract_word_list", |
262 | - "get_write_time", |
263 | - "FakeRandInt", |
264 | - "preexec_fn", |
265 | - "run_isolated", |
266 | "sample_binary_data", |
267 | - ] |
268 | +] |
269 | |
270 | import codecs |
271 | import os |
272 | import re |
273 | -import signal |
274 | -from sys import ( |
275 | - stderr, |
276 | - stdout, |
277 | -) |
278 | -from traceback import print_exc |
279 | - |
280 | -import subunit |
281 | -from testtools.content import Content |
282 | -from testtools.content_type import UTF8_TEXT |
283 | |
284 | |
285 | def age_file(path, seconds): |
286 | @@ -37,26 +22,6 @@ |
287 | os.utime(path, (atime, mtime - seconds)) |
288 | |
289 | |
290 | -def get_write_time(path): |
291 | - """Return last modification time of file at `path`.""" |
292 | - return os.stat(path).st_mtime |
293 | - |
294 | - |
295 | -def content_from_file(path): |
296 | - """Alternative to testtools' version. |
297 | - |
298 | - This keeps an open file-handle, so it can obtain the log even when the |
299 | - file has been unlinked. |
300 | - """ |
301 | - fd = open(path, "rb") |
302 | - |
303 | - def iterate(): |
304 | - fd.seek(0) |
305 | - return iter(fd) |
306 | - |
307 | - return Content(UTF8_TEXT, iterate) |
308 | - |
309 | - |
310 | def extract_word_list(string): |
311 | """Return a list of words from a string. |
312 | |
313 | @@ -66,75 +31,6 @@ |
314 | return re.findall("[^,;\s]+", string) |
315 | |
316 | |
317 | -def preexec_fn(): |
318 | - # Revert Python's handling of SIGPIPE. See |
319 | - # http://bugs.python.org/issue1652 for more info. |
320 | - signal.signal(signal.SIGPIPE, signal.SIG_DFL) |
321 | - |
322 | - |
323 | -class BytesToStdout: |
324 | - """File-like object to forward bytes to a text-mode `stdout`. |
325 | - |
326 | - Bytes are decoded as ASCII and unrecognised characters are replaced. |
327 | - """ |
328 | - |
329 | - def write(self, data): |
330 | - string = data.decode("ascii", "replace") |
331 | - stdout.write(string) |
332 | - |
333 | - |
334 | -def run_isolated(cls, self, result): |
335 | - """Run a test suite or case in a subprocess. |
336 | - |
337 | - This is derived from ``subunit.run_isolated``. Subunit's version |
338 | - clobbers stdout by dup'ing the subunit's stream over the top, which |
339 | - prevents effective debugging at the terminal. This variant does not |
340 | - suffer from the same issue. |
341 | - """ |
342 | - c2pread, c2pwrite = os.pipe() |
343 | - pid = os.fork() |
344 | - if pid == 0: |
345 | - # Child: runs test and writes subunit to c2pwrite. |
346 | - try: |
347 | - os.close(c2pread) |
348 | - stream = os.fdopen(c2pwrite, 'wb') |
349 | - sender = subunit.TestProtocolClient(stream) |
350 | - cls.run(self, sender) |
351 | - stream.flush() |
352 | - stdout.flush() |
353 | - stderr.flush() |
354 | - except: |
355 | - # Print error and exit hard. |
356 | - try: |
357 | - print_exc(file=stderr) |
358 | - stderr.flush() |
359 | - finally: |
360 | - os._exit(2) |
361 | - finally: |
362 | - # Exit hard. |
363 | - os._exit(0) |
364 | - else: |
365 | - # TestProtocolServer, by default, will write non-subunit content to |
366 | - # stdout as *bytes*. In Python 3 it assumes that stdout has a `buffer` |
367 | - # attribute which can accept bytes. However, nose buffers test output, |
368 | - # and replaces sys.stdout with only a StringIO instance. |
369 | - try: |
370 | - stdout.write(b"") |
371 | - except TypeError: |
372 | - try: |
373 | - output = stdout.buffer |
374 | - except AttributeError: |
375 | - output = BytesToStdout() |
376 | - else: |
377 | - output = stdout |
378 | - # Parent: receives subunit from c2pread. |
379 | - os.close(c2pwrite) |
380 | - stream = os.fdopen(c2pread, 'rb') |
381 | - receiver = subunit.TestProtocolServer(result, output) |
382 | - receiver.readFrom(stream) |
383 | - os.waitpid(pid, 0) |
384 | - |
385 | - |
386 | # Some horrible binary data that could never, ever, under any encoding |
387 | # known to man(1) survive mis-interpretation as text. |
388 | # |
389 | @@ -146,25 +42,3 @@ |
390 | # (1) Provided, of course, that man know only about ASCII and |
391 | # UTF. |
392 | sample_binary_data = codecs.BOM64_LE + codecs.BOM64_BE + b'\x00\xff\x00' |
393 | - |
394 | - |
395 | -class FakeRandInt: |
396 | - """Fake `randint` with forced limitations on its range. |
397 | - |
398 | - This lets you set a forced minimum, and/or a forced maximum, on the range |
399 | - of any call. For example, if you pass `forced_maximum=3`, then a call |
400 | - will never return more than 3. If you don't set a maximum, or if the |
401 | - call's maximum argument is less than the forced maximum, then the call's |
402 | - maximum will be respected. |
403 | - """ |
404 | - def __init__(self, real_randint, forced_minimum=None, forced_maximum=None): |
405 | - self.real_randint = real_randint |
406 | - self.minimum = forced_minimum |
407 | - self.maximum = forced_maximum |
408 | - |
409 | - def __call__(self, minimum, maximum): |
410 | - if self.minimum is not None: |
411 | - minimum = max(minimum, self.minimum) |
412 | - if self.maximum is not None: |
413 | - maximum = min(maximum, self.maximum) |
414 | - return self.real_randint(minimum, maximum) |
415 | |
416 | === modified file 'src/provisioningserver/utils/tests/test_fs.py' |
417 | --- src/provisioningserver/utils/tests/test_fs.py 2016-12-14 08:43:09 +0000 |
418 | +++ src/provisioningserver/utils/tests/test_fs.py 2017-01-06 10:04:40 +0000 |
419 | @@ -1,4 +1,4 @@ |
420 | -# Copyright 2014-2016 Canonical Ltd. This software is licensed under the |
421 | +# Copyright 2014-2017 Canonical Ltd. This software is licensed under the |
422 | # GNU Affero General Public License version 3 (see the file LICENSE). |
423 | |
424 | """Tests for filesystem-related utilities.""" |
425 | @@ -31,10 +31,7 @@ |
426 | MockNotCalled, |
427 | ) |
428 | from maastesting.testcase import MAASTestCase |
429 | -from maastesting.utils import ( |
430 | - age_file, |
431 | - get_write_time, |
432 | -) |
433 | +from maastesting.utils import age_file |
434 | import provisioningserver.config |
435 | from provisioningserver.utils.fs import ( |
436 | atomic_copy, |
437 | @@ -217,11 +214,11 @@ |
438 | contents = factory.make_bytes() |
439 | dest = self.make_file(contents=contents) |
440 | age_file(dest, 100) |
441 | - original_write_time = get_write_time(dest) |
442 | + original_write_time = os.stat(dest).st_mtime |
443 | loader = self.make_file(contents=contents) |
444 | atomic_copy(loader, dest) |
445 | self.assertThat(dest, FileContains(contents)) |
446 | - self.assertEqual(original_write_time, get_write_time(dest)) |
447 | + self.assertEqual(original_write_time, os.stat(dest).st_mtime) |
448 | |
449 | def test__sweeps_aside_dot_new_if_any(self): |
450 | contents = factory.make_bytes() |
LGTM!