2 # Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2010
4 # This program is free software; you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
6 # the Free Software Foundation; either version 3 of the License, or
7 # (at your option) any later version.
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 General Public License for more details.
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 from cStringIO import StringIO
23 from buildfarm.build import (
28 UploadBuildResultStore,
29 build_status_from_logs,
32 from buildfarm import BuildFarm
33 from buildfarm.tests import BuildFarmTestCase
36 class NonexistantTests(unittest.TestCase):
38 def test_nonexistant(self):
40 Exception, BuildResultStore, "somedirthatdoesn'texist", None)
43 class BuildResultStoreTestBase(object):
46 self.write_compilers(["cc", "gcc"])
47 self.write_hosts({"charis": "Some machine",
48 "myhost": "Another host"})
50 def test_build_fname(self):
52 self.x.build_fname("mytree", "myhost", "cc", 123),
53 "%s/data/oldrevs/build.mytree.myhost.cc-123" % self.path)
55 def test_build_remove(self):
56 path = self.upload_mock_logfile(self.x, "tdb", "charis", "cc",
57 "BUILD COMMIT REVISION: 12\n")
58 build = self.x.get_build("tdb", "charis", "cc", "12")
59 logname = build.basename + ".log"
61 self.assertFalse(os.path.exists(logname))
62 self.assertRaises(NoSuchBuildError, self.x.get_build, "tdb", "charis", "cc", "12")
64 def test_build_repr(self):
65 path = self.upload_mock_logfile(self.x, "tdb", "charis", "cc",
66 "BUILD COMMIT REVISION: 12\n")
67 build = self.x.get_build("tdb", "charis", "cc", "12")
68 self.assertEquals("<%s: revision 12 of tdb on charis using cc>" % build.__class__.__name__, repr(build))
70 def test_get_build_nonexistant(self):
71 self.assertRaises(NoSuchBuildError, self.x.get_build, "tdb",
74 def test_build_upload_time(self):
75 path = self.upload_mock_logfile(self.x, "tdb", "charis", "cc",
76 "BUILD COMMIT REVISION: 12\n", mtime=5)
77 build = self.x.get_build("tdb", "charis", "cc", "12")
78 self.assertEquals(5, build.upload_time)
80 def test_read_log(self):
81 path = self.upload_mock_logfile(self.x, "tdb", "charis", "cc",
82 stdout_contents="This is what a log file looks like.\n"
83 "BUILD COMMIT REVISION: 12\n")
84 build = self.x.get_build("tdb", "charis", "cc", "12")
85 self.assertEquals("This is what a log file looks like.\n"
86 "BUILD COMMIT REVISION: 12\n",
87 build.read_log().read())
89 def test_read_err(self):
90 self.upload_mock_logfile(self.x, "tdb", "charis", "cc",
91 stdout_contents="BUILD COMMIT REVISION: 12\n",
92 stderr_contents="This is what an stderr file looks like.")
93 build = self.x.get_build("tdb", "charis", "cc", "12")
94 self.assertEquals("This is what an stderr file looks like.",
95 build.read_err().read())
97 def test_read_err_nofile(self):
98 self.upload_mock_logfile(self.x, "tdb", "charis", "cc",
99 stdout_contents="BUILD COMMIT REVISION: 12\n")
100 build = self.x.get_build("tdb", "charis", "cc", "12")
101 self.assertEquals("", build.read_err().read())
103 def test_revision_details(self):
104 self.upload_mock_logfile(self.x, "tdb", "charis", "cc", stdout_contents="""
105 BUILD COMMIT REVISION: 43
107 BUILD COMMIT TIME: 3 August 2010
109 build = self.x.get_build("tdb", "charis", "cc", "43")
110 rev = build.revision_details()
111 self.assertIsInstance(rev, str)
112 self.assertEquals("43", rev)
114 def test_revision_details_no_timestamp(self):
115 self.upload_mock_logfile(self.x, "tdb", "charis", "cc", stdout_contents="""
116 BUILD COMMIT REVISION: 43
120 build = self.x.get_build("tdb", "charis", "cc", "43")
121 self.assertEquals("43", build.revision_details())
123 def test_err_count(self):
124 self.upload_mock_logfile(self.x, "tdb", "charis", "cc",
125 stdout_contents="BUILD COMMIT REVISION: 12\n",
126 stderr_contents="""error1
129 build = self.x.get_build("tdb", "charis", "cc", "12")
130 self.assertEquals(3, build.err_count())
132 def test_upload_build(self):
133 path = self.create_mock_logfile("tdb", "charis", "cc", contents="""
134 BUILD COMMIT REVISION: myrev
136 build = Build(path[:-4], "tdb", "charis", "cc")
137 self.x.upload_build(build)
138 uploaded_build = self.x.get_build("tdb", "charis", "cc", "myrev")
139 self.assertEquals(uploaded_build.log_checksum(), build.log_checksum())
141 def test_upload_build_no_rev(self):
142 path = self.create_mock_logfile("tdb", "charis", "cc", contents="""
144 build = Build(path[:-4], "tdb", "charis", "cc")
145 self.assertRaises(Exception, self.x.upload_build, build)
147 def test_get_previous_revision(self):
148 self.assertRaises(NoSuchBuildError, self.x.get_previous_revision, "tdb", "charis", "cc", "12")
150 def test_get_latest_revision_none(self):
151 self.assertRaises(NoSuchBuildError, self.x.get_latest_revision, "tdb", "charis", "cc")
153 def test_get_old_builds_none(self):
154 self.assertEquals([],
155 list(self.x.get_old_builds("tdb", "charis", "gcc")))
157 def test_get_old_builds(self):
158 path = self.create_mock_logfile("tdb", "charis", "cc",
160 BUILD COMMIT REVISION: 12
162 build = Build(path[:-4], "tdb", "charis", "cc")
163 b1 = self.x.upload_build(build)
164 path = self.create_mock_logfile("tdb", "charis", "cc",
166 BUILD COMMIT REVISION: 15
168 build = Build(path[:-4], "tdb", "charis", "cc")
169 b2 = self.x.upload_build(build)
170 path = self.create_mock_logfile("tdb", "charis", "cc",
172 BUILD COMMIT REVISION: 15
174 self.assertEquals([b1, b2],
175 list(self.x.get_old_builds("tdb", "charis", "cc")))
178 class BuildResultStoreTests(BuildFarmTestCase,BuildResultStoreTestBase):
181 BuildFarmTestCase.setUp(self)
182 BuildResultStoreTestBase.setUp(self)
184 self.buildfarm = BuildFarm(self.path)
186 self.x = BuildResultStore(
187 os.path.join(self.path, "data", "oldrevs"))
190 class BuildStatusFromLogs(testtools.TestCase):
192 def parse_logs(self, log, err):
193 return build_status_from_logs(StringIO(log), StringIO(err))
195 def test_nothing(self):
196 s = self.parse_logs("", "")
197 self.assertEquals([], s.stages)
198 self.assertEquals(set(), s.other_failures)
200 def test_disk_full(self):
201 self.assertEquals(set(["disk full"]),
202 self.parse_logs("foo\nbar\nNo space left on device\nla\n",
204 self.assertEquals(set(["disk full"]),
206 "", "foo\nbar\nNo space left on device\nla\n").other_failures)
208 def test_timeout(self):
209 self.assertEquals(set(["timeout"]),
210 self.parse_logs("foo\nbar\nmaximum runtime exceeded\nla\n",
213 def test_failed_test(self):
217 res = self.parse_logs(log, "")
218 self.assertEquals(res.stages, [
221 def test_failed_test_whitespace(self):
225 res = self.parse_logs(log, "")
226 self.assertEquals(res.stages,
229 def test_failed_test_noise(self):
235 res = self.parse_logs(log, "")
236 self.assertEquals(res.stages,
237 [("CONFIGURE", 2), ("TEST", 1), ("CC_CHECKER", 2)])
239 def test_no_test_output(self):
245 res = self.parse_logs(log, "")
246 self.assertEquals(res.stages,
247 [("CONFIGURE", 2), ("TEST", 0), ("CC_CHECKER", 2)])
249 def test_granular_test(self):
252 testsuite-success: toto
253 testsuite-failure: foo
254 testsuite-failure: bar
255 testsuite-failure: biz
259 res = self.parse_logs(log, "")
260 self.assertEquals(res.stages,
261 [("CONFIGURE", 2), ("TEST", 3), ("CC_CHECKER", 2)])
264 class BuildStatusTest(testtools.TestCase):
266 def test_cmp_equal(self):
267 a = BuildStatus([("CONFIGURE", 2), ("TEST", 3), ("CC_CHECKER", 2)])
268 b = BuildStatus([("CONFIGURE", 2), ("TEST", 3), ("CC_CHECKER", 2)])
270 self.assertEquals(cmp(a, b), 0)
272 def test_cmp_empty(self):
273 self.assertEquals(cmp(BuildStatus(), BuildStatus()), 0)
275 def test_cmp_other_failures(self):
276 self.assertEquals(cmp(
277 BuildStatus((), set(["foo"])), BuildStatus((), set(["foo"]))),
280 def test_cmp_intermediate_errors(self):
281 a = BuildStatus([("CONFIGURE", 2), ("TEST", 3), ("CC_CHECKER", 3)])
282 b = BuildStatus([("CONFIGURE", 2), ("TEST", 7), ("CC_CHECKER", 3)])
283 self.assertEquals(cmp(a, b), 1)
285 def test_cmp_bigger(self):
286 a = BuildStatus([("CONFIGURE", 2), ("TEST", 3), ("CC_CHECKER", 3)])
287 b = BuildStatus([("CONFIGURE", 2), ("TEST", 3), ("CC_CHECKER", 2)])
288 c = BuildStatus([("CONFIGURE", 2), ("TEST", 3)])
289 d = BuildStatus([], set(["super error"]))
290 e = BuildStatus([("CONFIGURE", 2), ("TEST", 3), ("CC_CHECKER", 1)], set(["super error"]))
292 # less stage means smaller, more error/higher error code means smaller as well
293 self.assertEquals(cmp(b, a), 1)
295 self.assertEquals(cmp(a, c), 1)
297 self.assertEquals(cmp(a, d), 1)
299 self.assertEquals(cmp(b, e), 1)
301 def test_cmp_smaller(self):
302 a = BuildStatus([("CONFIGURE", 2), ("TEST", 3), ("CC_CHECKER", 2)])
303 b = BuildStatus([("CONFIGURE", 2), ("TEST", 3), ("CC_CHECKER", 1)])
304 c = BuildStatus([("CONFIGURE", 2), ("TEST", 3)])
305 d = BuildStatus([], set(["super error"]))
306 e = BuildStatus([("CONFIGURE", 2), ("TEST", 3), ("CC_CHECKER", 1)], set(["super error"]))
308 # less stage means smaller, more error/higher error code means smaller as well
309 self.assertEquals(cmp(a, b), -1)
311 self.assertEquals(cmp(c, b), -1)
313 self.assertEquals(cmp(d, c), -1)
315 self.assertEquals(cmp(e, c), -1)
317 def test_cmp_with_other_failures(self):
318 d = BuildStatus([], set(["super error"]))
319 e = BuildStatus([("CONFIGURE", 2), ("TEST", 3), ("CC_CHECKER", 1)], set(["super error"]))
320 self.assertEquals(cmp(d, e), -1)
323 a = BuildStatus([("CONFIGURE", 3), ("BUILD", 2)])
324 self.assertEquals("3/2", str(a))
326 def test_str_other_failures(self):
327 a = BuildStatus([("CONFIGURE", 3), ("BUILD", 2)], set(["panic"]))
328 self.assertEquals("panic", str(a))
331 class BuildStatusRegressedSinceTests(testtools.TestCase):
333 def assertRegressedSince(self, expected, old_status, new_status):
334 (stages1, other_failures1) = old_status
335 (stages2, other_failures2) = new_status
336 a = BuildStatus(stages1, set(other_failures1))
337 b = BuildStatus(stages2, set(other_failures2))
338 self.assertEquals(expected, b.regressed_since(a))
341 self.assertRegressedSince(
343 ([("CONFIGURE", 2)], []),
344 ([("CONFIGURE", 2)], []))
346 def test_same_panic(self):
347 self.assertRegressedSince(
349 ([("CONFIGURE", 2)], ["panic"]),
350 ([("CONFIGURE", 2)], ["panic"]))
352 def test_other_failures_gone(self):
353 self.assertRegressedSince(
355 ([("CONFIGURE", 0)], ["panic"]),
356 ([("CONFIGURE", 2)], ["panic"]))
358 def test_more_stages_completed(self):
359 self.assertRegressedSince(
361 ([("CONFIGURE", 0)], []),
362 ([("CONFIGURE", 0), ("BUILD", 0)], []))
364 def test_less_errors(self):
365 self.assertRegressedSince(
367 ([("CONFIGURE", 0), ("BUILD", 0), ("TEST", 0), ("INSTALL", 1)], []),
368 ([("CONFIGURE", 0), ("BUILD", 0), ("TEST", 0), ("INSTALL", 0)], []))
370 def test_no_longer_inconsistent(self):
371 self.assertRegressedSince(
373 ([("CONFIGURE", 0)], ["inconsistent test result"]),
374 ([("CONFIGURE", 0)], []))
377 class UploadBuildResultStoreTestBase(object):
379 def test_build_fname(self):
381 self.x.build_fname("mytree", "myhost", "cc"),
382 "%s/data/upload/build.mytree.myhost.cc" % self.path)
384 def test_get_all_builds(self):
385 self.assertEquals([], list(self.x.get_all_builds()))
386 path = self.create_mock_logfile("tdb", "charis", "cc")
387 new_builds = list(self.x.get_all_builds())
388 self.assertEquals(1, len(new_builds))
389 self.assertEquals("tdb", new_builds[0].tree)
390 self.assertEquals("charis", new_builds[0].host)
391 self.assertEquals("cc", new_builds[0].compiler)
394 class UploadBuildResultStoreTests(UploadBuildResultStoreTestBase,BuildFarmTestCase):
397 super(UploadBuildResultStoreTests, self).setUp()
399 self.x = UploadBuildResultStore(
400 os.path.join(self.path, "data", "upload"))