Merge ~cjwatson/launchpad:improve-web-browser-layer-handling into launchpad:master

Proposed by Colin Watson
Status: Merged
Approved by: Colin Watson
Approved revision: 7005d66b28d8c690a8c04b8ef2b406504709a04d
Merge reported by: Otto Co-Pilot
Merged at revision: not available
Proposed branch: ~cjwatson/launchpad:improve-web-browser-layer-handling
Merge into: launchpad:master
Diff against target: 136 lines (+41/-12)
4 files modified
lib/lp/testing/__init__.py (+5/-2)
lib/lp/testing/html5browser.py (+1/-1)
lib/lp/testing/layers.py (+28/-2)
lib/lp/testing/tests/test_html5browser.py (+7/-7)
Reviewer Review Type Date Requested Status
Guruprasad Approve
Review via email: mp+451761@code.launchpad.net

Commit message

Improve handling of Firefox subprocesses in tests

Description of the change

Firefox is run via Selenium by some test cases. Since it's rather heavyweight, we run it in a layer so that it can be started up and shut down just once for a group of test cases.

I happened to notice recently that Firefox wasn't actually being shut down correctly, and continued to run long after any test cases that used it had finished. This turned out to be because the `YUITestLayer` teardown process was calling `selenium.webdriver.Firefox.close` (which closes the current window) rather than `selenium.webdriver.Firefox.quit` (which quits the driver and closes every associated window).

In the process, I also noticed that `lp.testing.tests.test_html5browser` started a separate browser for each individual test case, which seemed inefficient. I've split out a new `WebBrowserLayer` to fix this, which can be used to run tests of just the web browser part without bringing in the extra baggage of `FunctionalLayer` the way that `YUITestLayer` does.

I've verified that the process management now works correctly by running `while :; do date; ps xf; sleep 2; echo; done` in parallel with the test suite, and checking both that it uses a common browser instance and that it cleans it up when the relevant layer is torn down.

To post a comment you must log in.
Revision history for this message
Guruprasad (lgp171188) wrote :

LGTM 👍

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/lib/lp/testing/__init__.py b/lib/lp/testing/__init__.py
2index 9f9ccdc..d8cbff9 100644
3--- a/lib/lp/testing/__init__.py
4+++ b/lib/lp/testing/__init__.py
5@@ -1108,8 +1108,11 @@ class AbstractYUITestCase(TestCase):
6 The tests are run during `setUp()`, but failures need to be reported
7 from here.
8 """
9- assert self.layer.browser
10- results = self.layer.browser.run_tests(
11+ # Circular import.
12+ from lp.testing.layers import WebBrowserLayer
13+
14+ assert WebBrowserLayer.browser
15+ results = WebBrowserLayer.browser.run_tests(
16 self.html_uri,
17 timeout=self.suite_timeout,
18 incremental_timeout=self.incremental_timeout,
19diff --git a/lib/lp/testing/html5browser.py b/lib/lp/testing/html5browser.py
20index c559c64..678eeab 100644
21--- a/lib/lp/testing/html5browser.py
22+++ b/lib/lp/testing/html5browser.py
23@@ -125,4 +125,4 @@ class Browser:
24 return json.loads(results)
25
26 def close(self):
27- self.driver.close()
28+ self.driver.quit()
29diff --git a/lib/lp/testing/layers.py b/lib/lp/testing/layers.py
30index ce68de6..cf94f50 100644
31--- a/lib/lp/testing/layers.py
32+++ b/lib/lp/testing/layers.py
33@@ -1938,8 +1938,8 @@ class ZopelessAppServerLayer(LaunchpadZopelessLayer):
34 LayerProcessController.postTestInvariants()
35
36
37-class YUITestLayer(FunctionalLayer):
38- """The layer for all YUITests cases."""
39+class WebBrowserLayer(BaseLayer):
40+ """A layer that runs a web browser."""
41
42 browser = None
43
44@@ -1966,6 +1966,32 @@ class YUITestLayer(FunctionalLayer):
45 pass
46
47
48+class YUITestLayer(FunctionalLayer, WebBrowserLayer):
49+ """The layer for all YUI test cases."""
50+
51+ browser = None
52+
53+ @classmethod
54+ @profiled
55+ def setUp(cls):
56+ pass
57+
58+ @classmethod
59+ @profiled
60+ def tearDown(cls):
61+ pass
62+
63+ @classmethod
64+ @profiled
65+ def testSetUp(cls):
66+ pass
67+
68+ @classmethod
69+ @profiled
70+ def testTearDown(cls):
71+ pass
72+
73+
74 class YUIAppServerLayer(MemcachedLayer):
75 """The layer for all YUIAppServer test cases."""
76
77diff --git a/lib/lp/testing/tests/test_html5browser.py b/lib/lp/testing/tests/test_html5browser.py
78index bdc6229..d6ebc11 100644
79--- a/lib/lp/testing/tests/test_html5browser.py
80+++ b/lib/lp/testing/tests/test_html5browser.py
81@@ -4,12 +4,14 @@
82 from tempfile import NamedTemporaryFile
83
84 from lp.testing import TestCase
85-from lp.testing.html5browser import Browser
86+from lp.testing.layers import WebBrowserLayer
87
88
89 class TestBrowser(TestCase):
90 """Verify Browser methods."""
91
92+ layer = WebBrowserLayer
93+
94 def setUp(self):
95 super().setUp()
96 self.file = NamedTemporaryFile(
97@@ -51,11 +53,9 @@ class TestBrowser(TestCase):
98 self.file.flush()
99 self.file_uri = "file://{}".format(self.file.name)
100 self.addCleanup(self.file.close)
101- self.browser = Browser()
102- self.addCleanup(self.browser.close)
103
104 def test_load_test_results(self):
105- results = self.browser.run_tests(self.file_uri, timeout=10000)
106+ results = self.layer.browser.run_tests(self.file_uri, timeout=10000)
107 self.assertEqual(results.status, results.Status.SUCCESS)
108 self.assertEqual(
109 results.results,
110@@ -66,7 +66,7 @@ class TestBrowser(TestCase):
111 )
112
113 def test_timeout_error(self):
114- results = self.browser.run_tests(self.file_uri, timeout=1500)
115+ results = self.layer.browser.run_tests(self.file_uri, timeout=1500)
116 self.assertEqual(results.status, results.Status.TIMEOUT)
117 self.assertIsNone(results.results)
118 self.assertEqual(
119@@ -75,7 +75,7 @@ class TestBrowser(TestCase):
120 )
121
122 def test_incremental_timeout_success(self):
123- results = self.browser.run_tests(
124+ results = self.layer.browser.run_tests(
125 self.file_uri, timeout=10000, incremental_timeout=3000
126 )
127 self.assertEqual(results.status, results.Status.SUCCESS)
128@@ -88,7 +88,7 @@ class TestBrowser(TestCase):
129 )
130
131 def test_incremental_timeout_error(self):
132- results = self.browser.run_tests(
133+ results = self.layer.browser.run_tests(
134 self.file_uri, timeout=10000, incremental_timeout=1500
135 )
136 self.assertEqual(results.status, results.Status.TIMEOUT)

Subscribers

People subscribed via source and target branches

to status/vote changes: