Mercurial > bitten > bitten-test
annotate bitten/tests/master.py @ 446:a8c331c2d500
Attempt at a fix for #165.
author | cmlenz |
---|---|
date | Thu, 23 Aug 2007 17:59:58 +0000 |
parents | cfbc9ee622d5 |
children | c9ac97df8a5e |
rev | line source |
---|---|
392 | 1 # -*- coding: utf-8 -*- |
2 # | |
408
933105ab516b
Update file headers and other stuff pointing to the old home.
cmlenz
parents:
403
diff
changeset
|
3 # Copyright (C) 2005-2007 Christopher Lenz <cmlenz@gmx.de> |
933105ab516b
Update file headers and other stuff pointing to the old home.
cmlenz
parents:
403
diff
changeset
|
4 # Copyright (C) 2007 Edgewall Software |
392 | 5 # All rights reserved. |
6 # | |
7 # This software is licensed as described in the file COPYING, which | |
8 # you should have received as part of this distribution. The terms | |
408
933105ab516b
Update file headers and other stuff pointing to the old home.
cmlenz
parents:
403
diff
changeset
|
9 # are also available at http://bitten.edgewall.org/wiki/License. |
392 | 10 |
11 import re | |
12 import shutil | |
13 from StringIO import StringIO | |
14 import tempfile | |
15 import unittest | |
16 | |
410
7930cdd83d13
More restructuring: got rid of the `trac_ext` subpackage, which makes no sense now that the master is also coupled to Trac.
cmlenz
parents:
408
diff
changeset
|
17 from trac.db import DatabaseManager |
392 | 18 from trac.perm import PermissionCache, PermissionSystem |
19 from trac.test import EnvironmentStub, Mock | |
20 from trac.web.api import HTTPBadRequest, HTTPMethodNotAllowed, HTTPNotFound, \ | |
21 RequestDone | |
22 from trac.web.href import Href | |
23 | |
24 from bitten.master import BuildMaster | |
25 from bitten.model import BuildConfig, TargetPlatform, Build, BuildStep, \ | |
26 BuildLog, Report, schema | |
27 | |
28 | |
29 class BuildMasterTestCase(unittest.TestCase): | |
30 | |
31 def setUp(self): | |
32 self.env = EnvironmentStub() | |
33 self.env.path = tempfile.mkdtemp() | |
34 | |
35 PermissionSystem(self.env).grant_permission('hal', 'BUILD_EXEC') | |
36 | |
37 # Create tables | |
38 db = self.env.get_db_cnx() | |
39 cursor = db.cursor() | |
410
7930cdd83d13
More restructuring: got rid of the `trac_ext` subpackage, which makes no sense now that the master is also coupled to Trac.
cmlenz
parents:
408
diff
changeset
|
40 connector, _ = DatabaseManager(self.env)._get_connector() |
392 | 41 for table in schema: |
410
7930cdd83d13
More restructuring: got rid of the `trac_ext` subpackage, which makes no sense now that the master is also coupled to Trac.
cmlenz
parents:
408
diff
changeset
|
42 for stmt in connector.to_sql(table): |
392 | 43 cursor.execute(stmt) |
44 | |
45 self.repos = Mock() | |
46 self.env.get_repository = lambda authname=None: self.repos | |
47 | |
48 def tearDown(self): | |
49 shutil.rmtree(self.env.path) | |
50 | |
51 def test_create_build(self): | |
52 BuildConfig(self.env, 'test', path='somepath', active=True).insert() | |
53 platform = TargetPlatform(self.env, config='test', name="Unix") | |
54 platform.rules.append(('family', 'posix')) | |
55 platform.insert() | |
56 | |
57 self.repos = Mock( | |
58 get_node=lambda path, rev=None: Mock( | |
59 get_entries=lambda: [Mock(), Mock()], | |
60 get_history=lambda: [('somepath', 123, 'edit'), | |
61 ('somepath', 121, 'edit'), | |
62 ('somepath', 120, 'edit')] | |
63 ), | |
64 get_changeset=lambda rev: Mock(date=42), | |
65 normalize_path=lambda path: path, | |
66 rev_older_than=lambda rev1, rev2: rev1 < rev2 | |
67 ) | |
68 | |
69 inheaders = {'Content-Type': 'application/x-bitten+xml'} | |
70 inbody = StringIO("""<slave name="hal"> | |
71 <platform>Power Macintosh</platform> | |
72 <os family="posix" version="8.1.0">Darwin</os> | |
426 | 73 <package name="java" version="2.4.3"/> |
392 | 74 </slave>""") |
75 outheaders = {} | |
76 outbody = StringIO() | |
77 req = Mock(method='POST', base_path='', path_info='/builds', | |
78 href=Href('/trac'), abs_href=Href('http://example.org/trac'), | |
79 remote_addr='127.0.0.1', args={}, | |
80 perm=PermissionCache(self.env, 'hal'), | |
81 get_header=lambda x: inheaders.get(x), read=inbody.read, | |
82 send_response=lambda x: outheaders.setdefault('Status', x), | |
83 send_header=lambda x, y: outheaders.setdefault(x, y), | |
84 write=outbody.write) | |
85 | |
86 module = BuildMaster(self.env) | |
87 assert module.match_request(req) | |
88 try: | |
89 module.process_request(req) | |
90 self.fail('Expected RequestDone') | |
91 except RequestDone: | |
92 self.assertEqual(201, outheaders['Status']) | |
93 self.assertEqual('text/plain', outheaders['Content-Type']) | |
94 location = outheaders['Location'] | |
95 mo = re.match('http://example.org/trac/builds/(\d+)', location) | |
96 assert mo, 'Location was %r' % location | |
97 self.assertEqual('Build pending', outbody.getvalue()) | |
98 build = Build.fetch(self.env, int(mo.group(1))) | |
99 self.assertEqual(Build.IN_PROGRESS, build.status) | |
100 self.assertEqual('hal', build.slave) | |
101 | |
102 def test_create_build_invalid_xml(self): | |
103 inheaders = {'Content-Type': 'application/x-bitten+xml'} | |
104 inbody = StringIO('<slave></salve>') | |
105 req = Mock(method='POST', base_path='', path_info='/builds', | |
106 href=Href('/trac'), remote_addr='127.0.0.1', args={}, | |
107 perm=PermissionCache(self.env, 'hal'), | |
108 get_header=lambda x: inheaders.get(x), read=inbody.read) | |
109 | |
110 module = BuildMaster(self.env) | |
111 assert module.match_request(req) | |
112 try: | |
113 module.process_request(req) | |
114 self.fail('Expected HTTPBadRequest') | |
115 except HTTPBadRequest, e: | |
116 self.assertEqual('XML parser error', e.detail) | |
117 | |
118 def test_create_build_no_post(self): | |
119 req = Mock(method='GET', base_path='', path_info='/builds', | |
120 href=Href('/trac'), remote_addr='127.0.0.1', args={}, | |
121 perm=PermissionCache(self.env, 'hal')) | |
122 module = BuildMaster(self.env) | |
123 assert module.match_request(req) | |
124 try: | |
125 module.process_request(req) | |
126 self.fail('Expected HTTPMethodNotAllowed') | |
127 except HTTPMethodNotAllowed, e: | |
128 self.assertEqual('Method not allowed', e.detail) | |
129 | |
130 def test_create_build_no_match(self): | |
131 inheaders = {'Content-Type': 'application/x-bitten+xml'} | |
132 inbody = StringIO("""<slave name="hal"> | |
133 <platform>Power Macintosh</platform> | |
134 <os family="posix" version="8.1.0">Darwin</os> | |
135 </slave>""") | |
136 outheaders = {} | |
137 outbody = StringIO() | |
138 req = Mock(method='POST', base_path='', path_info='/builds', | |
139 href=Href('/trac'), remote_addr='127.0.0.1', args={}, | |
140 perm=PermissionCache(self.env, 'hal'), | |
141 get_header=lambda x: inheaders.get(x), read=inbody.read, | |
142 send_response=lambda x: outheaders.setdefault('Status', x), | |
143 send_header=lambda x, y: outheaders.setdefault(x, y), | |
144 write=outbody.write) | |
145 | |
146 module = BuildMaster(self.env) | |
147 assert module.match_request(req) | |
148 try: | |
149 module.process_request(req) | |
150 self.fail('Expected RequestDone') | |
151 except RequestDone: | |
152 self.assertEqual(204, outheaders['Status']) | |
420
23de253435b8
Slaves now attempt to explicitly cancel builds when they are interrupted.
cmlenz
parents:
410
diff
changeset
|
153 self.assertEqual('', outbody.getvalue()) |
23de253435b8
Slaves now attempt to explicitly cancel builds when they are interrupted.
cmlenz
parents:
410
diff
changeset
|
154 |
23de253435b8
Slaves now attempt to explicitly cancel builds when they are interrupted.
cmlenz
parents:
410
diff
changeset
|
155 def test_cancel_build(self): |
23de253435b8
Slaves now attempt to explicitly cancel builds when they are interrupted.
cmlenz
parents:
410
diff
changeset
|
156 config = BuildConfig(self.env, 'test', path='somepath', active=True, |
23de253435b8
Slaves now attempt to explicitly cancel builds when they are interrupted.
cmlenz
parents:
410
diff
changeset
|
157 recipe='<build></build>') |
23de253435b8
Slaves now attempt to explicitly cancel builds when they are interrupted.
cmlenz
parents:
410
diff
changeset
|
158 config.insert() |
23de253435b8
Slaves now attempt to explicitly cancel builds when they are interrupted.
cmlenz
parents:
410
diff
changeset
|
159 build = Build(self.env, 'test', '123', 1, slave='hal', rev_time=42, |
23de253435b8
Slaves now attempt to explicitly cancel builds when they are interrupted.
cmlenz
parents:
410
diff
changeset
|
160 status=Build.IN_PROGRESS, started=42) |
23de253435b8
Slaves now attempt to explicitly cancel builds when they are interrupted.
cmlenz
parents:
410
diff
changeset
|
161 build.insert() |
23de253435b8
Slaves now attempt to explicitly cancel builds when they are interrupted.
cmlenz
parents:
410
diff
changeset
|
162 |
23de253435b8
Slaves now attempt to explicitly cancel builds when they are interrupted.
cmlenz
parents:
410
diff
changeset
|
163 outheaders = {} |
23de253435b8
Slaves now attempt to explicitly cancel builds when they are interrupted.
cmlenz
parents:
410
diff
changeset
|
164 outbody = StringIO() |
23de253435b8
Slaves now attempt to explicitly cancel builds when they are interrupted.
cmlenz
parents:
410
diff
changeset
|
165 req = Mock(method='DELETE', base_path='', |
23de253435b8
Slaves now attempt to explicitly cancel builds when they are interrupted.
cmlenz
parents:
410
diff
changeset
|
166 path_info='/builds/%d' % build.id, |
23de253435b8
Slaves now attempt to explicitly cancel builds when they are interrupted.
cmlenz
parents:
410
diff
changeset
|
167 href=Href('/trac'), remote_addr='127.0.0.1', args={}, |
23de253435b8
Slaves now attempt to explicitly cancel builds when they are interrupted.
cmlenz
parents:
410
diff
changeset
|
168 perm=PermissionCache(self.env, 'hal'), |
23de253435b8
Slaves now attempt to explicitly cancel builds when they are interrupted.
cmlenz
parents:
410
diff
changeset
|
169 send_response=lambda x: outheaders.setdefault('Status', x), |
23de253435b8
Slaves now attempt to explicitly cancel builds when they are interrupted.
cmlenz
parents:
410
diff
changeset
|
170 send_header=lambda x, y: outheaders.setdefault(x, y), |
23de253435b8
Slaves now attempt to explicitly cancel builds when they are interrupted.
cmlenz
parents:
410
diff
changeset
|
171 write=outbody.write) |
23de253435b8
Slaves now attempt to explicitly cancel builds when they are interrupted.
cmlenz
parents:
410
diff
changeset
|
172 |
23de253435b8
Slaves now attempt to explicitly cancel builds when they are interrupted.
cmlenz
parents:
410
diff
changeset
|
173 module = BuildMaster(self.env) |
23de253435b8
Slaves now attempt to explicitly cancel builds when they are interrupted.
cmlenz
parents:
410
diff
changeset
|
174 assert module.match_request(req) |
23de253435b8
Slaves now attempt to explicitly cancel builds when they are interrupted.
cmlenz
parents:
410
diff
changeset
|
175 try: |
23de253435b8
Slaves now attempt to explicitly cancel builds when they are interrupted.
cmlenz
parents:
410
diff
changeset
|
176 module.process_request(req) |
23de253435b8
Slaves now attempt to explicitly cancel builds when they are interrupted.
cmlenz
parents:
410
diff
changeset
|
177 self.fail('Expected RequestDone') |
23de253435b8
Slaves now attempt to explicitly cancel builds when they are interrupted.
cmlenz
parents:
410
diff
changeset
|
178 except RequestDone: |
23de253435b8
Slaves now attempt to explicitly cancel builds when they are interrupted.
cmlenz
parents:
410
diff
changeset
|
179 self.assertEqual(204, outheaders['Status']) |
23de253435b8
Slaves now attempt to explicitly cancel builds when they are interrupted.
cmlenz
parents:
410
diff
changeset
|
180 self.assertEqual('', outbody.getvalue()) |
23de253435b8
Slaves now attempt to explicitly cancel builds when they are interrupted.
cmlenz
parents:
410
diff
changeset
|
181 |
23de253435b8
Slaves now attempt to explicitly cancel builds when they are interrupted.
cmlenz
parents:
410
diff
changeset
|
182 # Make sure the started timestamp has been set |
23de253435b8
Slaves now attempt to explicitly cancel builds when they are interrupted.
cmlenz
parents:
410
diff
changeset
|
183 build = Build.fetch(self.env, build.id) |
23de253435b8
Slaves now attempt to explicitly cancel builds when they are interrupted.
cmlenz
parents:
410
diff
changeset
|
184 self.assertEqual(Build.PENDING, build.status) |
23de253435b8
Slaves now attempt to explicitly cancel builds when they are interrupted.
cmlenz
parents:
410
diff
changeset
|
185 assert not build.started |
392 | 186 |
187 def test_initiate_build(self): | |
188 config = BuildConfig(self.env, 'test', path='somepath', active=True, | |
189 recipe='<build></build>') | |
190 config.insert() | |
191 platform = TargetPlatform(self.env, config='test', name="Unix") | |
192 platform.rules.append(('family', 'posix')) | |
193 platform.insert() | |
194 build = Build(self.env, 'test', '123', platform.id, slave='hal', | |
195 rev_time=42) | |
196 build.insert() | |
197 | |
198 outheaders = {} | |
199 outbody = StringIO() | |
200 | |
201 req = Mock(method='GET', base_path='', | |
202 path_info='/builds/%d' % build.id, | |
203 href=Href('/trac'), remote_addr='127.0.0.1', args={}, | |
204 perm=PermissionCache(self.env, 'hal'), | |
205 send_response=lambda x: outheaders.setdefault('Status', x), | |
206 send_header=lambda x, y: outheaders.setdefault(x, y), | |
207 write=outbody.write) | |
208 | |
209 module = BuildMaster(self.env) | |
210 assert module.match_request(req) | |
211 try: | |
212 module.process_request(req) | |
213 self.fail('Expected RequestDone') | |
214 except RequestDone: | |
215 self.assertEqual(200, outheaders['Status']) | |
216 self.assertEqual('39', outheaders['Content-Length']) | |
217 self.assertEqual('application/x-bitten+xml', | |
218 outheaders['Content-Type']) | |
219 self.assertEqual('attachment; filename=recipe_test_r123.xml', | |
220 outheaders['Content-Disposition']) | |
221 self.assertEqual('<build path="somepath" revision="123"/>', | |
222 outbody.getvalue()) | |
223 | |
224 # Make sure the started timestamp has been set | |
225 build = Build.fetch(self.env, build.id) | |
226 assert build.started | |
227 | |
228 def test_initiate_build_no_such_build(self): | |
229 req = Mock(method='GET', base_path='', | |
230 path_info='/builds/123', href=Href('/trac'), | |
231 remote_addr='127.0.0.1', args={}, | |
232 perm=PermissionCache(self.env, 'hal')) | |
233 | |
234 module = BuildMaster(self.env) | |
235 assert module.match_request(req) | |
236 try: | |
237 module.process_request(req) | |
238 self.fail('Expected HTTPNotFound') | |
239 except HTTPNotFound, e: | |
240 self.assertEqual('No such build', e.detail) | |
241 | |
242 def test_process_unknown_collection(self): | |
243 BuildConfig(self.env, 'test', path='somepath', active=True, | |
244 recipe='<build></build>').insert() | |
245 build = Build(self.env, 'test', '123', 1, slave='hal', rev_time=42) | |
246 build.insert() | |
247 | |
401
a10942252ebc
Use POST for submitting step results, instead of PUT.
cmlenz
parents:
392
diff
changeset
|
248 req = Mock(method='POST', base_path='', |
a10942252ebc
Use POST for submitting step results, instead of PUT.
cmlenz
parents:
392
diff
changeset
|
249 path_info='/builds/%d/files/' % build.id, |
392 | 250 href=Href('/trac'), remote_addr='127.0.0.1', args={}, |
251 perm=PermissionCache(self.env, 'hal')) | |
252 | |
253 module = BuildMaster(self.env) | |
254 assert module.match_request(req) | |
255 try: | |
256 module.process_request(req) | |
257 self.fail('Expected HTTPNotFound') | |
258 except HTTPNotFound, e: | |
259 self.assertEqual('No such collection', e.detail) | |
260 | |
261 def test_process_build_step_success(self): | |
262 recipe = """<build> | |
263 <step id="foo"> | |
264 </step> | |
265 </build>""" | |
266 BuildConfig(self.env, 'test', path='somepath', active=True, | |
267 recipe=recipe).insert() | |
268 build = Build(self.env, 'test', '123', 1, slave='hal', rev_time=42, | |
269 started=42) | |
270 build.insert() | |
271 | |
401
a10942252ebc
Use POST for submitting step results, instead of PUT.
cmlenz
parents:
392
diff
changeset
|
272 inbody = StringIO("""<result step="foo" status="success" |
392 | 273 time="2007-04-01T15:30:00.0000" |
274 duration="3.45"> | |
275 </result>""") | |
276 outheaders = {} | |
277 outbody = StringIO() | |
401
a10942252ebc
Use POST for submitting step results, instead of PUT.
cmlenz
parents:
392
diff
changeset
|
278 req = Mock(method='POST', base_path='', |
a10942252ebc
Use POST for submitting step results, instead of PUT.
cmlenz
parents:
392
diff
changeset
|
279 path_info='/builds/%d/steps/' % build.id, |
403 | 280 href=Href('/trac'), abs_href=Href('http://example.org/trac'), |
281 remote_addr='127.0.0.1', args={}, | |
392 | 282 perm=PermissionCache(self.env, 'hal'), |
283 read=inbody.read, | |
284 send_response=lambda x: outheaders.setdefault('Status', x), | |
285 send_header=lambda x, y: outheaders.setdefault(x, y), | |
286 write=outbody.write) | |
287 module = BuildMaster(self.env) | |
288 assert module.match_request(req) | |
289 try: | |
290 module.process_request(req) | |
291 self.fail('Expected RequestDone') | |
292 except RequestDone: | |
403 | 293 self.assertEqual(201, outheaders['Status']) |
392 | 294 self.assertEqual('20', outheaders['Content-Length']) |
295 self.assertEqual('text/plain', outheaders['Content-Type']) | |
296 self.assertEqual('Build step processed', outbody.getvalue()) | |
297 | |
298 build = Build.fetch(self.env, build.id) | |
299 self.assertEqual(Build.SUCCESS, build.status) | |
300 assert build.stopped | |
301 assert build.stopped > build.started | |
302 | |
303 steps = list(BuildStep.select(self.env, build.id)) | |
304 self.assertEqual(1, len(steps)) | |
305 self.assertEqual('foo', steps[0].name) | |
306 self.assertEqual(BuildStep.SUCCESS, steps[0].status) | |
307 | |
308 def test_process_build_step_success_with_log(self): | |
309 recipe = """<build> | |
310 <step id="foo"> | |
311 </step> | |
312 </build>""" | |
313 BuildConfig(self.env, 'test', path='somepath', active=True, | |
314 recipe=recipe).insert() | |
315 build = Build(self.env, 'test', '123', 1, slave='hal', rev_time=42, | |
316 started=42) | |
317 build.insert() | |
318 | |
401
a10942252ebc
Use POST for submitting step results, instead of PUT.
cmlenz
parents:
392
diff
changeset
|
319 inbody = StringIO("""<result step="foo" status="success" |
392 | 320 time="2007-04-01T15:30:00.0000" |
321 duration="3.45"> | |
322 <log generator="http://bitten.cmlenz.net/tools/python#unittest"> | |
323 <message level="info">Doing stuff</message> | |
324 <message level="error">Ouch that hurt</message> | |
325 </log> | |
326 </result>""") | |
327 outheaders = {} | |
328 outbody = StringIO() | |
401
a10942252ebc
Use POST for submitting step results, instead of PUT.
cmlenz
parents:
392
diff
changeset
|
329 req = Mock(method='POST', base_path='', |
a10942252ebc
Use POST for submitting step results, instead of PUT.
cmlenz
parents:
392
diff
changeset
|
330 path_info='/builds/%d/steps/' % build.id, |
403 | 331 href=Href('/trac'), abs_href=Href('http://example.org/trac'), |
332 remote_addr='127.0.0.1', args={}, | |
392 | 333 perm=PermissionCache(self.env, 'hal'), |
334 read=inbody.read, | |
335 send_response=lambda x: outheaders.setdefault('Status', x), | |
336 send_header=lambda x, y: outheaders.setdefault(x, y), | |
337 write=outbody.write) | |
338 module = BuildMaster(self.env) | |
339 assert module.match_request(req) | |
340 try: | |
341 module.process_request(req) | |
342 self.fail('Expected RequestDone') | |
343 except RequestDone: | |
403 | 344 self.assertEqual(201, outheaders['Status']) |
392 | 345 self.assertEqual('20', outheaders['Content-Length']) |
346 self.assertEqual('text/plain', outheaders['Content-Type']) | |
347 self.assertEqual('Build step processed', outbody.getvalue()) | |
348 | |
349 build = Build.fetch(self.env, build.id) | |
350 self.assertEqual(Build.SUCCESS, build.status) | |
351 assert build.stopped | |
352 assert build.stopped > build.started | |
353 | |
354 steps = list(BuildStep.select(self.env, build.id)) | |
355 self.assertEqual(1, len(steps)) | |
356 self.assertEqual('foo', steps[0].name) | |
357 self.assertEqual(BuildStep.SUCCESS, steps[0].status) | |
358 | |
359 logs = list(BuildLog.select(self.env, build=build.id, step='foo')) | |
360 self.assertEqual(1, len(logs)) | |
361 self.assertEqual('http://bitten.cmlenz.net/tools/python#unittest', | |
362 logs[0].generator) | |
363 self.assertEqual(2, len(logs[0].messages)) | |
364 self.assertEqual((u'info', u'Doing stuff'), logs[0].messages[0]) | |
365 self.assertEqual((u'error', u'Ouch that hurt'), logs[0].messages[1]) | |
366 | |
367 def test_process_build_step_success_with_report(self): | |
368 recipe = """<build> | |
369 <step id="foo"> | |
370 </step> | |
371 </build>""" | |
372 BuildConfig(self.env, 'test', path='somepath', active=True, | |
373 recipe=recipe).insert() | |
374 build = Build(self.env, 'test', '123', 1, slave='hal', rev_time=42, | |
375 started=42) | |
376 build.insert() | |
377 | |
401
a10942252ebc
Use POST for submitting step results, instead of PUT.
cmlenz
parents:
392
diff
changeset
|
378 inbody = StringIO("""<result step="foo" status="success" |
392 | 379 time="2007-04-01T15:30:00.0000" |
380 duration="3.45"> | |
381 <report category="test" | |
382 generator="http://bitten.cmlenz.net/tools/python#unittest"> | |
383 <test fixture="my.Fixture" file="my/test/file.py"> | |
384 <stdout>Doing my thing</stdout> | |
385 </test> | |
386 </report> | |
387 </result>""") | |
388 outheaders = {} | |
389 outbody = StringIO() | |
401
a10942252ebc
Use POST for submitting step results, instead of PUT.
cmlenz
parents:
392
diff
changeset
|
390 req = Mock(method='POST', base_path='', |
a10942252ebc
Use POST for submitting step results, instead of PUT.
cmlenz
parents:
392
diff
changeset
|
391 path_info='/builds/%d/steps/' % build.id, |
403 | 392 href=Href('/trac'), abs_href=Href('http://example.org/trac'), |
393 remote_addr='127.0.0.1', args={}, | |
392 | 394 perm=PermissionCache(self.env, 'hal'), |
395 read=inbody.read, | |
396 send_response=lambda x: outheaders.setdefault('Status', x), | |
397 send_header=lambda x, y: outheaders.setdefault(x, y), | |
398 write=outbody.write) | |
399 module = BuildMaster(self.env) | |
400 assert module.match_request(req) | |
401 try: | |
402 module.process_request(req) | |
403 self.fail('Expected RequestDone') | |
404 except RequestDone: | |
403 | 405 self.assertEqual(201, outheaders['Status']) |
392 | 406 self.assertEqual('20', outheaders['Content-Length']) |
407 self.assertEqual('text/plain', outheaders['Content-Type']) | |
408 self.assertEqual('Build step processed', outbody.getvalue()) | |
409 | |
410 build = Build.fetch(self.env, build.id) | |
411 self.assertEqual(Build.SUCCESS, build.status) | |
412 assert build.stopped | |
413 assert build.stopped > build.started | |
414 | |
415 steps = list(BuildStep.select(self.env, build.id)) | |
416 self.assertEqual(1, len(steps)) | |
417 self.assertEqual('foo', steps[0].name) | |
418 self.assertEqual(BuildStep.SUCCESS, steps[0].status) | |
419 | |
420 reports = list(Report.select(self.env, build=build.id, step='foo')) | |
421 self.assertEqual(1, len(reports)) | |
422 self.assertEqual('test', reports[0].category) | |
423 self.assertEqual('http://bitten.cmlenz.net/tools/python#unittest', | |
424 reports[0].generator) | |
425 self.assertEqual(1, len(reports[0].items)) | |
426 self.assertEqual({ | |
427 'fixture': 'my.Fixture', | |
428 'file': 'my/test/file.py', | |
429 'stdout': 'Doing my thing', | |
430 'type': 'test', | |
431 }, reports[0].items[0]) | |
432 | |
433 def test_process_build_step_failure(self): | |
434 recipe = """<build> | |
435 <step id="foo"> | |
436 </step> | |
437 </build>""" | |
438 BuildConfig(self.env, 'test', path='somepath', active=True, | |
439 recipe=recipe).insert() | |
440 build = Build(self.env, 'test', '123', 1, slave='hal', rev_time=42, | |
441 started=42) | |
442 build.insert() | |
443 | |
401
a10942252ebc
Use POST for submitting step results, instead of PUT.
cmlenz
parents:
392
diff
changeset
|
444 inbody = StringIO("""<result step="foo" status="failure" |
392 | 445 time="2007-04-01T15:30:00.0000" |
446 duration="3.45"> | |
447 </result>""") | |
448 outheaders = {} | |
449 outbody = StringIO() | |
401
a10942252ebc
Use POST for submitting step results, instead of PUT.
cmlenz
parents:
392
diff
changeset
|
450 req = Mock(method='POST', base_path='', |
a10942252ebc
Use POST for submitting step results, instead of PUT.
cmlenz
parents:
392
diff
changeset
|
451 path_info='/builds/%d/steps/' % build.id, |
403 | 452 href=Href('/trac'), abs_href=Href('http://example.org/trac'), |
453 remote_addr='127.0.0.1', args={}, | |
392 | 454 perm=PermissionCache(self.env, 'hal'), |
455 read=inbody.read, | |
456 send_response=lambda x: outheaders.setdefault('Status', x), | |
457 send_header=lambda x, y: outheaders.setdefault(x, y), | |
458 write=outbody.write) | |
459 module = BuildMaster(self.env) | |
460 assert module.match_request(req) | |
461 try: | |
462 module.process_request(req) | |
463 self.fail('Expected RequestDone') | |
464 except RequestDone: | |
403 | 465 self.assertEqual(201, outheaders['Status']) |
392 | 466 self.assertEqual('20', outheaders['Content-Length']) |
467 self.assertEqual('text/plain', outheaders['Content-Type']) | |
468 self.assertEqual('Build step processed', outbody.getvalue()) | |
469 | |
470 build = Build.fetch(self.env, build.id) | |
471 self.assertEqual(Build.FAILURE, build.status) | |
472 assert build.stopped | |
473 assert build.stopped > build.started | |
474 | |
475 steps = list(BuildStep.select(self.env, build.id)) | |
476 self.assertEqual(1, len(steps)) | |
477 self.assertEqual('foo', steps[0].name) | |
478 self.assertEqual(BuildStep.FAILURE, steps[0].status) | |
479 | |
480 def test_process_build_step_failure_ignored(self): | |
481 recipe = """<build> | |
482 <step id="foo" onerror="ignore"> | |
483 </step> | |
484 </build>""" | |
485 BuildConfig(self.env, 'test', path='somepath', active=True, | |
486 recipe=recipe).insert() | |
487 build = Build(self.env, 'test', '123', 1, slave='hal', rev_time=42, | |
488 started=42) | |
489 build.insert() | |
490 | |
401
a10942252ebc
Use POST for submitting step results, instead of PUT.
cmlenz
parents:
392
diff
changeset
|
491 inbody = StringIO("""<result step="foo" status="failure" |
392 | 492 time="2007-04-01T15:30:00.0000" |
493 duration="3.45"> | |
494 </result>""") | |
495 outheaders = {} | |
496 outbody = StringIO() | |
401
a10942252ebc
Use POST for submitting step results, instead of PUT.
cmlenz
parents:
392
diff
changeset
|
497 req = Mock(method='POST', base_path='', |
a10942252ebc
Use POST for submitting step results, instead of PUT.
cmlenz
parents:
392
diff
changeset
|
498 path_info='/builds/%d/steps/' % build.id, |
403 | 499 href=Href('/trac'), abs_href=Href('http://example.org/trac'), |
500 remote_addr='127.0.0.1', args={}, | |
392 | 501 perm=PermissionCache(self.env, 'hal'), |
502 read=inbody.read, | |
503 send_response=lambda x: outheaders.setdefault('Status', x), | |
504 send_header=lambda x, y: outheaders.setdefault(x, y), | |
505 write=outbody.write) | |
506 module = BuildMaster(self.env) | |
507 assert module.match_request(req) | |
508 try: | |
509 module.process_request(req) | |
510 self.fail('Expected RequestDone') | |
511 except RequestDone: | |
403 | 512 self.assertEqual(201, outheaders['Status']) |
392 | 513 self.assertEqual('20', outheaders['Content-Length']) |
514 self.assertEqual('text/plain', outheaders['Content-Type']) | |
515 self.assertEqual('Build step processed', outbody.getvalue()) | |
516 | |
517 build = Build.fetch(self.env, build.id) | |
518 self.assertEqual(Build.SUCCESS, build.status) | |
519 assert build.stopped | |
520 assert build.stopped > build.started | |
521 | |
522 steps = list(BuildStep.select(self.env, build.id)) | |
523 self.assertEqual(1, len(steps)) | |
524 self.assertEqual('foo', steps[0].name) | |
525 self.assertEqual(BuildStep.FAILURE, steps[0].status) | |
526 | |
527 def test_process_build_step_invalid_xml(self): | |
528 recipe = """<build> | |
529 <step id="foo"> | |
530 </step> | |
531 </build>""" | |
532 BuildConfig(self.env, 'test', path='somepath', active=True, | |
533 recipe=recipe).insert() | |
534 build = Build(self.env, 'test', '123', 1, slave='hal', rev_time=42, | |
535 started=42) | |
536 build.insert() | |
537 | |
538 inbody = StringIO("""<result></rsleut>""") | |
401
a10942252ebc
Use POST for submitting step results, instead of PUT.
cmlenz
parents:
392
diff
changeset
|
539 req = Mock(method='POST', base_path='', |
a10942252ebc
Use POST for submitting step results, instead of PUT.
cmlenz
parents:
392
diff
changeset
|
540 path_info='/builds/%d/steps/' % build.id, |
392 | 541 href=Href('/trac'), remote_addr='127.0.0.1', args={}, |
542 perm=PermissionCache(self.env, 'hal'), | |
543 read=inbody.read) | |
544 | |
545 module = BuildMaster(self.env) | |
546 assert module.match_request(req) | |
547 try: | |
548 module.process_request(req) | |
549 self.fail('Expected HTTPBadRequest') | |
550 except HTTPBadRequest, e: | |
551 self.assertEqual('XML parser error', e.detail) | |
552 | |
553 def test_process_build_step_invalid_datetime(self): | |
554 recipe = """<build> | |
555 <step id="foo"> | |
556 </step> | |
557 </build>""" | |
558 BuildConfig(self.env, 'test', path='somepath', active=True, | |
559 recipe=recipe).insert() | |
560 build = Build(self.env, 'test', '123', 1, slave='hal', rev_time=42, | |
561 started=42) | |
562 build.insert() | |
563 | |
401
a10942252ebc
Use POST for submitting step results, instead of PUT.
cmlenz
parents:
392
diff
changeset
|
564 inbody = StringIO("""<result step="foo" status="success" |
392 | 565 time="sometime tomorrow maybe" |
566 duration="3.45"> | |
567 </result>""") | |
401
a10942252ebc
Use POST for submitting step results, instead of PUT.
cmlenz
parents:
392
diff
changeset
|
568 req = Mock(method='POST', base_path='', |
a10942252ebc
Use POST for submitting step results, instead of PUT.
cmlenz
parents:
392
diff
changeset
|
569 path_info='/builds/%d/steps/' % build.id, |
392 | 570 href=Href('/trac'), remote_addr='127.0.0.1', args={}, |
571 perm=PermissionCache(self.env, 'hal'), | |
572 read=inbody.read) | |
573 | |
574 module = BuildMaster(self.env) | |
575 assert module.match_request(req) | |
576 try: | |
577 module.process_request(req) | |
578 self.fail('Expected HTTPBadRequest') | |
579 except HTTPBadRequest, e: | |
580 self.assertEqual("Invalid ISO date/time 'sometime tomorrow maybe'", | |
581 e.detail) | |
582 | |
401
a10942252ebc
Use POST for submitting step results, instead of PUT.
cmlenz
parents:
392
diff
changeset
|
583 def test_process_build_step_no_post(self): |
392 | 584 BuildConfig(self.env, 'test', path='somepath', active=True, |
585 recipe='<build></build>').insert() | |
586 build = Build(self.env, 'test', '123', 1, slave='hal', rev_time=42, | |
587 started=42) | |
588 build.insert() | |
589 | |
401
a10942252ebc
Use POST for submitting step results, instead of PUT.
cmlenz
parents:
392
diff
changeset
|
590 req = Mock(method='GET', base_path='', |
a10942252ebc
Use POST for submitting step results, instead of PUT.
cmlenz
parents:
392
diff
changeset
|
591 path_info='/builds/%d/steps/' % build.id, |
392 | 592 href=Href('/trac'), remote_addr='127.0.0.1', args={}, |
593 perm=PermissionCache(self.env, 'hal')) | |
594 | |
595 module = BuildMaster(self.env) | |
596 assert module.match_request(req) | |
597 try: | |
598 module.process_request(req) | |
599 self.fail('Expected HTTPMethodNotAllowed') | |
600 except HTTPMethodNotAllowed, e: | |
601 self.assertEqual('Method not allowed', e.detail) | |
602 | |
603 | |
604 def suite(): | |
605 suite = unittest.TestSuite() | |
606 suite.addTest(unittest.makeSuite(BuildMasterTestCase, 'test')) | |
607 return suite | |
608 | |
609 if __name__ == '__main__': | |
610 unittest.main(defaultTest='suite') |