Merge lp:~bjornt/launchpad/windmill-test-layer into lp:launchpad
- windmill-test-layer
- Merge into devel
Status: | Merged | ||||||||
---|---|---|---|---|---|---|---|---|---|
Approved by: | Brad Crittenden | ||||||||
Approved revision: | no longer in the source branch. | ||||||||
Merged at revision: | not available | ||||||||
Proposed branch: | lp:~bjornt/launchpad/windmill-test-layer | ||||||||
Merge into: | lp:launchpad | ||||||||
Diff against target: | None lines | ||||||||
To merge this branch: | bzr merge lp:~bjornt/launchpad/windmill-test-layer | ||||||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Brad Crittenden (community) | code | Approve | |
Review via email:
|
Commit message
Description of the change
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Björn Tillenius (bjornt) wrote : | # |
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Brad Crittenden (bac) wrote : | # |
Hi Bjorn,
Thanks for taking on this testing infrastructure change. It'll make
it a lot easier to run the windmill tests.
> === modified file 'Makefile'
> --- Makefile 2009-08-21 17:50:58 +0000
+++ Makefile 2009-09-15 08:58:30 +0000
> @@ -90,6 +90,8 @@
> @echo
> @echo "Running the JavaScript integration test suite"
> @echo
> + bin/test $(VERBOSITY) --layer=
> + bin/test $(VERBOSITY) --layer=
> bin/jstest
>
> check_mailman: build
> === modified file 'lib/canonical/
> --- lib/canonical/
> +++ lib/canonical/
> @@ -1638,3 +1643,53 @@
> @profiled
> def testTearDown(cls):
> LayerProcessCon
> +
> +
> +class BaseWindmillLay
> + """Layer for Windmill tests.
> +
> + This layer shouldn't be used directly. A subclass needs to be
> + created specifying which base URL to use (e.g.
> + http://
> + """
> +
> + base_url = None
> + shell_objects = None
> + config_file = None
> +
> + @classmethod
> + @profiled
> + def setUp(cls):
> + if cls.base_url is None:
> + # Only do the setup if we're in a subclass that defines
> + # base_url. With no base_url, we can't create the config
> + # file windmill needs.
> + return
Is the silent return sufficient or should there be an asssert?
> + # Kill the SMTP server. The current Windmill test don't need it,
> + # and if it's running, uuid will somehow end up listen to its
> + # port, preventing the next test run from starting.
> + LayerProcessCon
> + # Windmill needs a config file on disk.
> + config_text = dedent("""\
> + START_FIREFOX = True
> + TEST_URL = '%s'
> + """ % cls.base_url)
> + cls.config_file = tempfile.
> + cls.config_
> + # Flush the file so that windmill can read it.
> + cls.config_
> + os.environ[
> + cls.shell_objects = start_windmill()
> +
> + @classmethod
> + @profiled
> + def tearDown(cls):
> + if cls.shell_objects is not None:
> + AppServerLayer.
> + windmill_
> + # Start the SMTP server, in case other layers need it. It
> + # will be killed by AppServerLayer later.
> + LayerProcessCon
> + if cls.config_file is not None:
> + # Close the file so that it gets deleted.
> + cls.config_
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Björn Tillenius (bjornt) wrote : | # |
On Tue, Sep 15, 2009 at 02:29:24PM -0000, Brad Crittenden wrote:
> Review: Approve code
> Hi Bjorn,
>
> Thanks for taking on this testing infrastructure change. It'll make
> it a lot easier to run the windmill tests.
>
> > === modified file 'Makefile'
> > --- Makefile 2009-08-21 17:50:58 +0000
> +++ Makefile 2009-09-15 08:58:30 +0000
> > @@ -90,6 +90,8 @@
> > @echo
> > @echo "Running the JavaScript integration test suite"
> > @echo
> > + bin/test $(VERBOSITY) --layer=
> > + bin/test $(VERBOSITY) --layer=
> > bin/jstest
> >
> > check_mailman: build
>
> > === modified file 'lib/canonical/
> > --- lib/canonical/
> > +++ lib/canonical/
> > @@ -1638,3 +1643,53 @@
> > @profiled
> > def testTearDown(cls):
> > LayerProcessCon
> > +
> > +
> > +class BaseWindmillLay
> > + """Layer for Windmill tests.
> > +
> > + This layer shouldn't be used directly. A subclass needs to be
> > + created specifying which base URL to use (e.g.
> > + http://
> > + """
> > +
> > + base_url = None
> > + shell_objects = None
> > + config_file = None
> > +
> > + @classmethod
> > + @profiled
> > + def setUp(cls):
> > + if cls.base_url is None:
> > + # Only do the setup if we're in a subclass that defines
> > + # base_url. With no base_url, we can't create the config
> > + # file windmill needs.
> > + return
>
> Is the silent return sufficient or should there be an asssert?
It has to be a silent return. Due to the way layers work, this method
will be called on BaseWindmillLayer, before it's called on
BugsWindmillLayer. It felt like the easiest way of reducing code
duplicating between the different windmill layers.
--
Björn Tillenius | https:/
Preview Diff
1 | === modified file 'Makefile' |
2 | --- Makefile 2009-08-21 17:50:58 +0000 |
3 | +++ Makefile 2009-09-15 08:58:30 +0000 |
4 | @@ -90,6 +90,8 @@ |
5 | @echo |
6 | @echo "Running the JavaScript integration test suite" |
7 | @echo |
8 | + bin/test $(VERBOSITY) --layer=BugsWindmillLayer |
9 | + bin/test $(VERBOSITY) --layer=CodeWindmillLayer |
10 | bin/jstest |
11 | |
12 | check_mailman: build |
13 | |
14 | === modified file 'buildout-templates/bin/test.in' |
15 | --- buildout-templates/bin/test.in 2009-08-10 22:08:05 +0000 |
16 | +++ buildout-templates/bin/test.in 2009-09-15 08:55:41 +0000 |
17 | @@ -140,7 +140,8 @@ |
18 | '--test-path=${buildout:directory}/lib', |
19 | '--package=canonical', |
20 | '--package=lp', |
21 | - '--layer=!MailmanLayer', |
22 | + # XXX: Add this back before landing this branch. |
23 | + #'--layer=!(MailmanLayer|WindmillLayer)', |
24 | ] |
25 | |
26 | # Monkey-patch os.listdir to randomise the results |
27 | |
28 | === modified file 'lib/canonical/testing/layers.py' |
29 | --- lib/canonical/testing/layers.py 2009-08-21 19:38:21 +0000 |
30 | +++ lib/canonical/testing/layers.py 2009-09-15 09:12:47 +0000 |
31 | @@ -25,6 +25,7 @@ |
32 | __all__ = [ |
33 | 'AppServerLayer', |
34 | 'BaseLayer', |
35 | + 'BaseWindmillLayer', |
36 | 'DatabaseFunctionalLayer', |
37 | 'DatabaseLayer', |
38 | 'ExperimentalLaunchpadZopelessLayer', |
39 | @@ -58,6 +59,7 @@ |
40 | import socket |
41 | import subprocess |
42 | import sys |
43 | +import tempfile |
44 | import threading |
45 | import time |
46 | |
47 | @@ -70,6 +72,9 @@ |
48 | from storm.zope.interfaces import IZStorm |
49 | import transaction |
50 | |
51 | +from windmill.bin.admin_lib import ( |
52 | + start_windmill, teardown as windmill_teardown) |
53 | + |
54 | import zope.app.testing.functional |
55 | from zope.app.testing.functional import FunctionalTestSetup, ZopePublication |
56 | from zope.component import getUtility, provideUtility |
57 | @@ -1638,3 +1643,53 @@ |
58 | @profiled |
59 | def testTearDown(cls): |
60 | LayerProcessController.postTestInvariants() |
61 | + |
62 | + |
63 | +class BaseWindmillLayer(AppServerLayer): |
64 | + """Layer for Windmill tests. |
65 | + |
66 | + This layer shouldn't be used directly. A subclass needs to be |
67 | + created specifying which base URL to use (e.g. |
68 | + http://bugs.launchpad.dev:8085/). |
69 | + """ |
70 | + |
71 | + base_url = None |
72 | + shell_objects = None |
73 | + config_file = None |
74 | + |
75 | + @classmethod |
76 | + @profiled |
77 | + def setUp(cls): |
78 | + if cls.base_url is None: |
79 | + # Only do the setup if we're in a subclass that defines |
80 | + # base_url. With no base_url, we can't create the config |
81 | + # file windmill needs. |
82 | + return |
83 | + # Kill the SMTP server. The current Windmill test don't need it, |
84 | + # and if it's running, uuid will somehow end up listen to its |
85 | + # port, preventing the next test run from starting. |
86 | + LayerProcessController.stopSMTPServer() |
87 | + # Windmill needs a config file on disk. |
88 | + config_text = dedent("""\ |
89 | + START_FIREFOX = True |
90 | + TEST_URL = '%s' |
91 | + """ % cls.base_url) |
92 | + cls.config_file = tempfile.NamedTemporaryFile(suffix='.py') |
93 | + cls.config_file.write(config_text) |
94 | + # Flush the file so that windmill can read it. |
95 | + cls.config_file.flush() |
96 | + os.environ['WINDMILL_CONFIG_FILE'] = cls.config_file.name |
97 | + cls.shell_objects = start_windmill() |
98 | + |
99 | + @classmethod |
100 | + @profiled |
101 | + def tearDown(cls): |
102 | + if cls.shell_objects is not None: |
103 | + AppServerLayer.tearDown() |
104 | + windmill_teardown(cls.shell_objects) |
105 | + # Start the SMTP server, in case other layers need it. It |
106 | + # will be killed by AppServerLayer later. |
107 | + LayerProcessController.startSMTPServer() |
108 | + if cls.config_file is not None: |
109 | + # Close the file so that it gets deleted. |
110 | + cls.config_file.close() |
111 | |
112 | === added file 'lib/lp/bugs/windmill/testing.py' |
113 | --- lib/lp/bugs/windmill/testing.py 1970-01-01 00:00:00 +0000 |
114 | +++ lib/lp/bugs/windmill/testing.py 2009-09-15 08:58:30 +0000 |
115 | @@ -0,0 +1,18 @@ |
116 | +# Copyright 2009 Canonical Ltd. This software is licensed under the |
117 | +# GNU Affero General Public License version 3 (see the file LICENSE). |
118 | + |
119 | +"""Bugs-specific testing infrastructure for Windmill.""" |
120 | + |
121 | +__metaclass__ = type |
122 | +__all__ = [ |
123 | + 'BugsWindmillLayer', |
124 | + ] |
125 | + |
126 | + |
127 | +from canonical.testing.layers import BaseWindmillLayer |
128 | + |
129 | + |
130 | +class BugsWindmillLayer(BaseWindmillLayer): |
131 | + """Layer for Bugs Windmill tests.""" |
132 | + |
133 | + base_url = 'http://bugs.launchpad.dev:8085/' |
134 | |
135 | === renamed file 'lib/lp/bugs/windmill/tests/test_bugs/test_bug_commenting.py' => 'lib/lp/bugs/windmill/tests/test_bug_commenting.py' |
136 | --- lib/lp/bugs/windmill/tests/test_bugs/test_bug_commenting.py 2009-08-18 14:33:08 +0000 |
137 | +++ lib/lp/bugs/windmill/tests/test_bug_commenting.py 2009-09-15 08:58:30 +0000 |
138 | @@ -6,10 +6,14 @@ |
139 | __metaclass__ = type |
140 | __all__ = [] |
141 | |
142 | +import unittest |
143 | + |
144 | from windmill.authoring import WindmillTestClient |
145 | |
146 | from canonical.launchpad.windmill.testing import lpuser |
147 | from canonical.uuid import generate_uuid |
148 | +from lp.bugs.windmill.testing import BugsWindmillLayer |
149 | +from lp.testing import TestCaseWithFactory |
150 | |
151 | WAIT_PAGELOAD = u'30000' |
152 | WAIT_ELEMENT_COMPLETE = u'30000' |
153 | @@ -18,20 +22,28 @@ |
154 | u'//input[@id="field.actions.save" and @class="button js-action"]') |
155 | |
156 | |
157 | -def test_bug_commenting(): |
158 | - """Test commenting on bugs.""" |
159 | - client = WindmillTestClient('Bug commenting') |
160 | - lpuser.NO_PRIV.ensure_login(client) |
161 | - |
162 | - client.open(url='http://bugs.launchpad.dev:8085/bugs/1') |
163 | - client.waits.forPageLoad(timeout=WAIT_PAGELOAD) |
164 | - client.waits.forElement(xpath=ADD_COMMENT_BUTTON) |
165 | - |
166 | - # Generate a unique piece of text, so we can run the test multiple |
167 | - # times, without resetting the db. |
168 | - new_comment_text = generate_uuid() |
169 | - client.type(text=new_comment_text, id="field.comment") |
170 | - client.click(xpath=ADD_COMMENT_BUTTON) |
171 | - client.waits.forElement( |
172 | - xpath=u'//div[@class="bug-comment"]/p[contains(., "%s")]' % ( |
173 | - new_comment_text)) |
174 | +class TestBugCommenting(TestCaseWithFactory): |
175 | + |
176 | + layer = BugsWindmillLayer |
177 | + |
178 | + def test_bug_commenting(self): |
179 | + """Test commenting on bugs.""" |
180 | + client = WindmillTestClient('Bug commenting') |
181 | + lpuser.NO_PRIV.ensure_login(client) |
182 | + |
183 | + client.open(url='http://bugs.launchpad.dev:8085/bugs/1') |
184 | + client.waits.forPageLoad(timeout=WAIT_PAGELOAD) |
185 | + client.waits.forElement(xpath=ADD_COMMENT_BUTTON) |
186 | + |
187 | + # Generate a unique piece of text, so we can run the test multiple |
188 | + # times, without resetting the db. |
189 | + new_comment_text = generate_uuid() |
190 | + client.type(text=new_comment_text, id="field.comment") |
191 | + client.click(xpath=ADD_COMMENT_BUTTON) |
192 | + client.waits.forElement( |
193 | + xpath=u'//div[@class="bug-comment"]/p[contains(., "%s")]' % ( |
194 | + new_comment_text)) |
195 | + |
196 | + |
197 | +def test_suite(): |
198 | + return unittest.TestLoader().loadTestsFromName(__name__) |
199 | |
200 | === added file 'lib/lp/code/windmill/testing.py' |
201 | --- lib/lp/code/windmill/testing.py 1970-01-01 00:00:00 +0000 |
202 | +++ lib/lp/code/windmill/testing.py 2009-09-15 08:58:30 +0000 |
203 | @@ -0,0 +1,18 @@ |
204 | +# Copyright 2009 Canonical Ltd. This software is licensed under the |
205 | +# GNU Affero General Public License version 3 (see the file LICENSE). |
206 | + |
207 | +"""Code-specific testing infrastructure for Windmill.""" |
208 | + |
209 | +__metaclass__ = type |
210 | +__all__ = [ |
211 | + 'CodeWindmillLayer', |
212 | + ] |
213 | + |
214 | + |
215 | +from canonical.testing.layers import BaseWindmillLayer |
216 | + |
217 | + |
218 | +class CodeWindmillLayer(BaseWindmillLayer): |
219 | + """Layer for Code Windmill tests.""" |
220 | + |
221 | + base_url = 'http://code.launchpad.dev:8085/' |
222 | |
223 | === added directory 'lib/lp/code/windmill/tests' |
224 | === added file 'lib/lp/code/windmill/tests/__init__.py' |
225 | === renamed file 'lib/lp/code/windmill/test_branch_links.py' => 'lib/lp/code/windmill/tests/test_branch_links.py' |
226 | --- lib/lp/code/windmill/test_branch_links.py 2009-08-13 15:12:16 +0000 |
227 | +++ lib/lp/code/windmill/tests/test_branch_links.py 2009-09-15 09:14:25 +0000 |
228 | @@ -6,47 +6,59 @@ |
229 | __metaclass__ = type |
230 | __all__ = [] |
231 | |
232 | +import unittest |
233 | + |
234 | import windmill |
235 | from windmill.authoring import WindmillTestClient |
236 | |
237 | from canonical.launchpad.windmill.testing import lpuser |
238 | - |
239 | - |
240 | -def test_inline_branch_bug_link_unlink(): |
241 | - """Test branch bug links.""" |
242 | - client = WindmillTestClient("Branch bug links") |
243 | - |
244 | - lpuser.FOO_BAR.ensure_login(client) |
245 | - |
246 | - client.open( |
247 | - url=windmill.settings['TEST_URL'] + '/~mark/firefox/release--0.9.1') |
248 | - client.waits.forElement(id=u'linkbug', timeout=u'10000') |
249 | - client.click(id=u'linkbug') |
250 | - |
251 | - client.waits.forElement(id=u'field.bug') |
252 | - client.type(text=u'1', id=u'field.bug') |
253 | - client.click(xpath=u'//button[@name="buglink.actions.change"]') |
254 | - |
255 | - client.waits.forElement(id=u'buglink-1', timeout=u'10000') |
256 | - client.asserts.assertText(id=u'linkbug', |
257 | - validator=u'Link to another bug') |
258 | - |
259 | - client.click(id=u'linkbug') |
260 | - client.waits.forElement(id=u'field.bug') |
261 | - client.type(text=u'2', id=u'field.bug') |
262 | - client.click(xpath=u'//button[@name="buglink.actions.change"]') |
263 | - |
264 | - client.waits.forElement(id=u'buglink-1', timeout=u'10000') |
265 | - client.asserts.assertText(id=u'linkbug', |
266 | - validator=u'Link to another bug') |
267 | - |
268 | - # And now to unlink. |
269 | - client.click(id=u'delete-buglink-1') |
270 | - client.waits.sleep(milliseconds=3000) |
271 | - client.asserts.assertNotNode(id=u'buglink-1') |
272 | - client.click(id=u'delete-buglink-2') |
273 | - client.waits.sleep(milliseconds=3000) |
274 | - client.asserts.assertNotNode(id=u'buglink-2') |
275 | - client.asserts.assertText(id=u'linkbug', |
276 | - validator=u'Link to a bug report') |
277 | - |
278 | +from lp.code.windmill.testing import CodeWindmillLayer |
279 | +from lp.testing import TestCaseWithFactory |
280 | + |
281 | + |
282 | +class TestBranchLinks(TestCaseWithFactory): |
283 | + |
284 | + layer = CodeWindmillLayer |
285 | + |
286 | + def test_inline_branch_bug_link_unlink(self): |
287 | + """Test branch bug links.""" |
288 | + client = WindmillTestClient("Branch bug links") |
289 | + |
290 | + lpuser.FOO_BAR.ensure_login(client) |
291 | + |
292 | + start_url = ( |
293 | + windmill.settings['TEST_URL'] + '/~mark/firefox/release--0.9.1') |
294 | + client.open(url=start_url) |
295 | + client.waits.forElement(id=u'linkbug', timeout=u'10000') |
296 | + client.click(id=u'linkbug') |
297 | + |
298 | + client.waits.forElement(id=u'field.bug') |
299 | + client.type(text=u'1', id=u'field.bug') |
300 | + client.click(xpath=u'//button[@name="buglink.actions.change"]') |
301 | + |
302 | + client.waits.forElement(id=u'buglink-1', timeout=u'10000') |
303 | + client.asserts.assertText(id=u'linkbug', |
304 | + validator=u'Link to another bug') |
305 | + |
306 | + client.click(id=u'linkbug') |
307 | + client.waits.forElement(id=u'field.bug') |
308 | + client.type(text=u'2', id=u'field.bug') |
309 | + client.click(xpath=u'//button[@name="buglink.actions.change"]') |
310 | + |
311 | + client.waits.forElement(id=u'buglink-1', timeout=u'10000') |
312 | + client.asserts.assertText(id=u'linkbug', |
313 | + validator=u'Link to another bug') |
314 | + |
315 | + # And now to unlink. |
316 | + client.click(id=u'delete-buglink-1') |
317 | + client.waits.sleep(milliseconds=3000) |
318 | + client.asserts.assertNotNode(id=u'buglink-1') |
319 | + client.click(id=u'delete-buglink-2') |
320 | + client.waits.sleep(milliseconds=3000) |
321 | + client.asserts.assertNotNode(id=u'buglink-2') |
322 | + client.asserts.assertText(id=u'linkbug', |
323 | + validator=u'Link to a bug report') |
324 | + |
325 | + |
326 | +def test_suite(): |
327 | + return unittest.TestLoader().loadTestsFromName(__name__) |
Make it possible to run Windmill tests using our normal test runner.
This branch adds test layers for Windmill tests, so ideally you could be WindmillLayer.
able to run all Windmill tests using: bin/test --layer=
This doesn't quite work yet. Windmill isn't torned down properly, so you
can only run the tests for one domain at a time:
bin/test --layer= BugsWindmillLay er
Note the hack in test.in. This is because of bug #429375, which need to
get fixed before landing this branch.
All the old tests need to be converted to use a TestCase. I'm intending
to do that in a separate branch, in order to keep down the diff size.
I've converted one Bugs and one Code test as a proof-of-concept.
The reason for integrating the Windmill test into our test suite is to
make it easier to run the tests. I always forget how to run the tests
using bin/lp-windmill and bin/windmill. Using bin/test also means that
it's easier to see which tests pass and fail.
As a bonus, it's now possible to use LaunchpadObject Factory in our
Windmill tests to do setup. In the future, I'd also like to reset the db
between tests.
-- /launchpad. net/~bjornt
Björn Tillenius | https:/