Merge lp:~bigkevmcd/offspring/slave-testing-integration into lp:offspring
- slave-testing-integration
- Merge into trunk
Status: | Merged | ||||
---|---|---|---|---|---|
Merged at revision: | 108 | ||||
Proposed branch: | lp:~bigkevmcd/offspring/slave-testing-integration | ||||
Merge into: | lp:offspring | ||||
Prerequisite: | lp:~bigkevmcd/offspring/slave-testing-server | ||||
Diff against target: |
295 lines (+225/-8) 1 file modified
lib/offspring/slave/tests/test_slave.py (+225/-8) |
||||
To merge this branch: | bzr merge lp:~bigkevmcd/offspring/slave-testing-integration | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Cody A.W. Somerville | Approve | ||
Review via email: mp+84959@code.launchpad.net |
This proposal supersedes a proposal from 2011-12-07.
Commit message
Description of the change
More tests...now hit 100% coverage for the slave classes
Still to do, TaskMonitor and integration, I want to add at least one test that kicks off a script that "builds" successfully.
- 139. By Kevin McDermott
-
Merge trunk and fix conflicts.
Kevin McDermott (bigkevmcd) wrote : | # |
> >>> + self.assertEqua
> >>> + log_file.
> >>> +
>
> To avoid causing unnecessary breakage if the value for request_timeout changes
> in LexbuilderSlave
> compare against the log file using the value we configured.
Given that we're testing the code, and if we change the code, we should have changed the tests to match, this is expected behaviour we're testing for...if you want to change the coded default value, change the test first...
> Is the name of this test method accurate? It doesn't appear to be.
>
> Also, what is the value of this test since we call 'self.config.
> "request_timeout", "60")' in LexbuilderSlave
Ooops...dunno what I was thinking with that test, renamed the test, and added an additional test for when it's actually missing.
> >>>
>
> As with test_create, we should probably not assume the default value /
> configured value. In this case, you'd have to inspect the __init__ method on
> LexbuilderSlave
See above...
> Additionally, we don't actually get a connection and test that calling
> gettimeout on the socket returns the value we expect - I imagine this is
> something we should do to properly test this.
The Slave doesn't actually do this, it's the responsibility of LexbuilderXMLRP
Preview Diff
1 | === modified file 'lib/offspring/slave/tests/test_slave.py' |
2 | --- lib/offspring/slave/tests/test_slave.py 2011-12-08 17:29:41 +0000 |
3 | +++ lib/offspring/slave/tests/test_slave.py 2011-12-08 17:48:24 +0000 |
4 | @@ -1,12 +1,16 @@ |
5 | # Copyright 2011 Canonical Ltd. This software is licensed under the |
6 | # GNU Affero General Public License version 3 (see the file LICENSE). |
7 | import platform |
8 | +import logging |
9 | from datetime import datetime |
10 | from ConfigParser import ConfigParser |
11 | import xmlrpclib |
12 | |
13 | +from mocker import MockerTestCase |
14 | + |
15 | from offspring.tests.helpers import CaptureLoggingTestCase |
16 | |
17 | +from offspring.enums import ProjectBuildStates |
18 | from offspring.slave import ( |
19 | LexbuilderSlave, ProjectBuildProcess, LexbuilderSlaveServer, |
20 | ShutdownThread) |
21 | @@ -89,16 +93,13 @@ |
22 | self.slave = LexbuilderSlave(("localhost", 8760), self.config, |
23 | self.fake_server) |
24 | |
25 | - def tearDown(self): |
26 | - super(TestLexbuilderSlave, self).tearDown() |
27 | - |
28 | def test_slave_run_starts_server_and_logs(self): |
29 | """ |
30 | LexbuilderSlave.run should start the LexbuilderSlaveServer. |
31 | """ |
32 | log_file = self.capture_logging() |
33 | self.slave.run() |
34 | - self.assertIn("Slave ready to accept builds", log_file.getvalue()) |
35 | + self.assertEqual("Slave ready to accept builds\n", log_file.getvalue()) |
36 | self.assertTrue(self.fake_server.started) |
37 | |
38 | def test_slave_stop(self): |
39 | @@ -155,11 +156,12 @@ |
40 | |
41 | class FakeLexbuilderSlave(object): |
42 | """Fake slave for use in tests.""" |
43 | - def __init__(self, state=LexbuilderSlave.STATE_IDLE): |
44 | + def __init__(self, config=None, state=LexbuilderSlave.STATE_IDLE): |
45 | self.project_name = None |
46 | self.config_url = None |
47 | self.stopped = False |
48 | self.state = state |
49 | + self.config = config |
50 | |
51 | def startBuild(self, project_name, config_url): |
52 | self.project_name = project_name |
53 | @@ -178,7 +180,7 @@ |
54 | |
55 | def setUp(self): |
56 | super(TestLexbuilderSlaveServer, self).setUp() |
57 | - self.fake_slave = FakeLexbuilderSlave() |
58 | + self.fake_slave = FakeLexbuilderSlave(self.config) |
59 | |
60 | def create_slave_server(self): |
61 | """ |
62 | @@ -191,12 +193,41 @@ |
63 | """ |
64 | We can instantiate a new LexbuilderSlaveServer. |
65 | """ |
66 | - log_file = self.capture_logging() |
67 | + log_file = self.capture_logging(log_level=logging.DEBUG) |
68 | + |
69 | slave_server = self.create_slave_server() |
70 | self.assertEqual(self.fake_slave, slave_server.slave) |
71 | self.assertEqual(("127.0.0.1", 8760), slave_server.server_address) |
72 | self.assertEqual(self.config, slave_server.config) |
73 | - self.assertEqual("", log_file.getvalue()) |
74 | + self.assertEqual("Request timeout set to 60.0 seconds\n", |
75 | + log_file.getvalue()) |
76 | + |
77 | + def test_create_with_missing_request_timeout(self): |
78 | + """ |
79 | + LexbuilderSlaveServer should set the request timeout to the value |
80 | + specified in the configuration. |
81 | + """ |
82 | + log_file = self.capture_logging(log_level=logging.DEBUG) |
83 | + self.config.set("slave", "request_timeout", "30.0") |
84 | + |
85 | + slave_server = LexbuilderSlaveServer( |
86 | + self.fake_slave, ("localhost", 8760), self.config) |
87 | + self.assertEqual("Request timeout set to 30.0 seconds\n", |
88 | + log_file.getvalue()) |
89 | + |
90 | + def test_create_with_invalid_configuration(self): |
91 | + """ |
92 | + LexbuilderSlaveServer should handle invalid request timeout values by |
93 | + falling back to the default. |
94 | + """ |
95 | + log_file = self.capture_logging(log_level=logging.DEBUG) |
96 | + self.config.set("slave", "request_timeout", "broken value") |
97 | + |
98 | + |
99 | + slave_server = LexbuilderSlaveServer( |
100 | + self.fake_slave, ("localhost", 8760), self.config) |
101 | + self.assertEqual("Request timeout set to 60.0 seconds\n", |
102 | + log_file.getvalue()) |
103 | |
104 | def test_api_start_build(self): |
105 | """ |
106 | @@ -485,3 +516,189 @@ |
107 | self.assertEqual(LexbuilderSlaveServer.REQUEST_OKAY, |
108 | slave_server.api_shutdown()) |
109 | self.assertTrue(fake_shutdown.started) |
110 | + |
111 | + |
112 | +class TestProjectBuildProcess(LexbuilderSlaveTestMixin, CaptureLoggingTestCase): |
113 | + |
114 | + def setUp(self): |
115 | + super(TestProjectBuildProcess, self).setUp() |
116 | + self.master = FakeLexbuilderSlave(config=self.config) |
117 | + self.build_process = ProjectBuildProcess( |
118 | + self.master, u"testing", "http://example.com/project") |
119 | + |
120 | + def test_create(self): |
121 | + """ |
122 | + We can instantiate a new ProjectBuildProcess. |
123 | + """ |
124 | + self.assertEqual(u"testing", self.build_process.projectName) |
125 | + self.assertEqual([u"testing", "http://example.com/project"], |
126 | + self.build_process.arguments) |
127 | + |
128 | + def test_get_state_currently_active(self): |
129 | + """ |
130 | + If ProjectBuildProcess.state is STATE_ACTIVE then |
131 | + ProjectBuildProcess.get_state() should return |
132 | + ProjectBuildStates.PENDING. |
133 | + """ |
134 | + self.build_process.state = self.build_process.STATE_ACTIVE |
135 | + self.assertEqual(ProjectBuildStates.PENDING, |
136 | + self.build_process.getState()) |
137 | + |
138 | + def test_get_state_currently_failed(self): |
139 | + """ |
140 | + If ProjectBuildProcess.state is STATE_FAILED then |
141 | + ProjectBuildProcess.get_state() should return |
142 | + ProjectBuildStates.FAILED. |
143 | + """ |
144 | + self.build_process.state = self.build_process.STATE_FAILED |
145 | + self.assertEqual(ProjectBuildStates.FAILED, |
146 | + self.build_process.getState()) |
147 | + |
148 | + def test_get_state_currently_success(self): |
149 | + """ |
150 | + If ProjectBuildProcess.state is STATE_SUCCESS then |
151 | + ProjectBuildProcess.get_state() should return |
152 | + ProjectBuildStates.SUCCESS. |
153 | + """ |
154 | + self.build_process.state = self.build_process.STATE_SUCCESS |
155 | + self.assertEqual(ProjectBuildStates.SUCCESS, |
156 | + self.build_process.getState()) |
157 | + |
158 | + def test_get_state_currently_unknown_state(self): |
159 | + """ |
160 | + If ProjectBuildProcess.state is not one of STATE_ACTIVE, STATE_FAILED |
161 | + or STATE_SUCCESS ProjectBuildProcess.get_state() should return |
162 | + ProjectBuildStates.UNKNOWN. |
163 | + """ |
164 | + self.build_process.state = None |
165 | + self.assertEqual(ProjectBuildStates.UNKNOWN, |
166 | + self.build_process.getState()) |
167 | + |
168 | + def test_pre_execution(self): |
169 | + """ |
170 | + ProjectBuildProcess.preExecution should log the fact that the slave is |
171 | + starting a build, and set the current state of the LexbuilderSlave to |
172 | + STATE_BUILDING. |
173 | + """ |
174 | + log_file = self.capture_logging(log_level=logging.DEBUG) |
175 | + |
176 | + self.build_process.preExecution() |
177 | + self.assertEqual(LexbuilderSlave.STATE_BUILDING, |
178 | + self.build_process.master.state) |
179 | + self.assertEqual("Setting state of slave to BUILDING\n", |
180 | + log_file.getvalue()) |
181 | + |
182 | + def test_post_execution(self): |
183 | + """ |
184 | + ProjectBuildProcess.preExecution should log the fact that the slave is |
185 | + finishing a build, and set the current state of the LexbuilderSlave to |
186 | + STATE_IDLE. |
187 | + """ |
188 | + log_file = self.capture_logging(log_level=logging.DEBUG) |
189 | + |
190 | + self.build_process.postExecution() |
191 | + self.assertEqual(LexbuilderSlave.STATE_IDLE, |
192 | + self.build_process.master.state) |
193 | + self.assertEqual("Setting state of slave to IDLE\n", |
194 | + log_file.getvalue()) |
195 | + |
196 | + def test_get_command_path(self): |
197 | + """ |
198 | + ProjectBuildProcess.getCommandPath should return the configured |
199 | + build_command. |
200 | + """ |
201 | + self.config.set("slave", "build_command", |
202 | + "/usr/bin/offspring-build") |
203 | + self.assertEqual("/usr/bin/offspring-build", |
204 | + self.build_process.getCommandPath()) |
205 | + |
206 | + def test_get_project_name(self): |
207 | + """ |
208 | + ProjectBuildProcess.getProjectName should return the name of the |
209 | + project currently configured for building. |
210 | + """ |
211 | + self.build_process.projectName = u"testing" |
212 | + self.assertEqual(u"testing", |
213 | + self.build_process.getProjectName()) |
214 | + |
215 | + def test_get_build_name_with_known_build_name(self): |
216 | + """ |
217 | + ProjectBuildProcess.getBuildName should return the name of the current |
218 | + build if known. |
219 | + """ |
220 | + self.build_process.buildName = u"testing" |
221 | + self.assertEqual(u"testing", self.build_process.getBuildName()) |
222 | + |
223 | + def test_get_build_name_with_unknown_build_inactive_process(self): |
224 | + """ |
225 | + If the build name is unknown, and the process is not currently active, |
226 | + then we ProjectBuildProcess.getBuildName should examine the output of |
227 | + the build process and look for START <projectname> <buildname> and |
228 | + extract the buildname and return it. |
229 | + """ |
230 | + self.build_process.stdoutdata = "START project testproject mybuild\n" |
231 | + self.assertEqual("mybuild", self.build_process.getBuildName()) |
232 | + |
233 | + def test_get_build_name_with_unknown_build_active_process(self): |
234 | + """ |
235 | + If the build name is unknown, and the process is currently active, |
236 | + then we ProjectBuildProcess.getBuildName should examine the the build |
237 | + statefile from the configuration and parse the project name and build |
238 | + name from there. |
239 | + """ |
240 | + self.build_process.state = self.build_process.STATE_ACTIVE |
241 | + self.build_process.projectName = "newproject" |
242 | + build_statefile_name = self.makeFile("newproject newbuild\n") |
243 | + self.config.set("slave", "build_statefile", build_statefile_name) |
244 | + self.assertEqual("newbuild", self.build_process.getBuildName()) |
245 | + |
246 | + def test_get_build_name_with_unknown_build_active_invalid_project(self): |
247 | + """ |
248 | + If the build name is unknown, and the process is currently active, |
249 | + then we ProjectBuildProcess.getBuildName should examine the the build |
250 | + statefile from the configuration and parse the project name and build |
251 | + name from there. |
252 | + |
253 | + If the project name parsed from the build file doesn't match the |
254 | + project we're building, then we should get an error. |
255 | + """ |
256 | + self.build_process.state = self.build_process.STATE_ACTIVE |
257 | + self.build_process.projectName = "anotherprojectentirely" |
258 | + build_statefile_name = self.makeFile("newproject newbuild\n") |
259 | + self.config.set("slave", "build_statefile", build_statefile_name) |
260 | + |
261 | + self.assertEqual(LexbuilderSlaveServer.REQUEST_ERROR, |
262 | + self.build_process.getBuildName()) |
263 | + |
264 | + def test_get_build_name_with_unknown_build_active_process_error(self): |
265 | + """ |
266 | + If the build name is unknown, and the process is currently active, |
267 | + then we ProjectBuildProcess.getBuildName should examine the the build |
268 | + statefile from the configuration, any error reading this should result |
269 | + in an error return. |
270 | + """ |
271 | + self.build_process.state = self.build_process.STATE_ACTIVE |
272 | + self.build_process.projectName = "newproject" |
273 | + build_statefile_name = self.makeFile() |
274 | + self.config.set("slave", "build_statefile", build_statefile_name) |
275 | + self.assertEqual(LexbuilderSlaveServer.REQUEST_ERROR, |
276 | + self.build_process.getBuildName()) |
277 | + |
278 | + |
279 | +class TestShutdownThread(MockerTestCase): |
280 | + """ |
281 | + Tests for the ShutdownThread class. |
282 | + """ |
283 | + |
284 | + def test_run(self): |
285 | + """ |
286 | + """ |
287 | + sleep_mock = self.mocker.replace("time.sleep") |
288 | + sleep_mock(1) |
289 | + self.mocker.replay() |
290 | + master = FakeLexbuilderSlaveServer() |
291 | + |
292 | + shutdown_thread = ShutdownThread(master) |
293 | + shutdown_thread.run() |
294 | + |
295 | + self.assertTrue(master.stopped) |
I'm going to provide a review of the changes you make specifically in this merge proposal. I have been reviewing the other test code you've written and already merged and I do have some questions but I'll contact you regarding them OOB to this MP.
>>> === modified file 'lib/offspring/ slave/tests/ test_slave. py' slave/tests/ test_slave. py 2011-12-08 17:29:41 +0000 slave/tests/ test_slave. py 2011-12-08 17:48:24 +0000 Server. logging( ) logging( log_level= logging. DEBUG) slave_server( ) l(self. fake_slave, slave_server.slave) l(("127. 0.0.1", 8760), slave_server. server_ address) l(self. config, slave_server. config) l("", log_file. getvalue( )) l("Request timeout set to 60.0 seconds\n", getvalue( ))
>>> --- lib/offspring/
>>> +++ lib/offspring/
<snip>
>>> @@ -191,12 +193,41 @@
>>> """
>>> We can instantiate a new LexbuilderSlave
>>> """
>>> - log_file = self.capture_
>>> + log_file = self.capture_
>>> +
>>> slave_server = self.create_
>>> self.assertEqua
>>> self.assertEqua
>>> self.assertEqua
>>> - self.assertEqua
>>> + self.assertEqua
>>> + log_file.
>>> +
To avoid causing unnecessary breakage if the value for request_timeout changes in LexbuilderSlave TestMixin, we should probably construct the string we compare against the log file using the value we configured.
>>> + def test_create_ with_missing_ request_ timeout( self): Server should set the request timeout to the value logging( log_level= logging. DEBUG) set("slave" , "request_timeout", "30.0") Server( l("Request timeout set to 30.0 seconds\n", getvalue( ))
>>> + """
>>> + LexbuilderSlave
>>> + specified in the configuration.
>>> + """
>>> + log_file = self.capture_
>>> + self.config.
>>> +
>>> + slave_server = LexbuilderSlave
>>> + self.fake_slave, ("localhost", 8760), self.config)
>>> + self.assertEqua
>>> + log_file.
>>> +
Is the name of this test method accurate? It doesn't appear to be.
Also, what is the value of this test since we call 'self.config. set("slave" , "request_timeout", "60")' in LexbuilderSlave TestMixin. setUp?
>>> + def test_create_ with_invalid_ configuration( self): Server should handle invalid request timeout values by logging( log_level= logging. DEBUG) set("slave" , "request_timeout", "broken value") Server( l("Request timeout set to 60.0 seconds\n", getvalue( ))
>>> + """
>>> + LexbuilderSlave
>>> + falling back to the default.
>>> + """
>>> + log_file = self.capture_
>>> + self.config.
>>> +
>>> +
>>> + slave_server = LexbuilderSlave
>>> + self.fake_slave, ("localhost", 8760), self.config)
>>> + self.assertEqua
>>> + log_file.
>>>
As with test_create, we should probably not assume the default value / configured value. In this case, you'd have to inspect the __init__ method on LexbuilderSlave Server or update it to use a constant that you can import.
Additionally, we don't actually get a connection and test that calling gettimeout on the socket returns the value we expect - I imagine this is something we should do to...