Merge lp:~bigkevmcd/offspring/slave-testing-integration into lp:offspring

Proposed by Kevin McDermott
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
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.

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.

To post a comment you must log in.
139. By Kevin McDermott

Merge trunk and fix conflicts.

Revision history for this message
Cody A.W. Somerville (cody-somerville) wrote :
Download full text (3.6 KiB)

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'
>>> --- lib/offspring/slave/tests/test_slave.py 2011-12-08 17:29:41 +0000
>>> +++ lib/offspring/slave/tests/test_slave.py 2011-12-08 17:48:24 +0000
<snip>
>>> @@ -191,12 +193,41 @@
>>> """
>>> We can instantiate a new LexbuilderSlaveServer.
>>> """
>>> - log_file = self.capture_logging()
>>> + log_file = self.capture_logging(log_level=logging.DEBUG)
>>> +
>>> slave_server = self.create_slave_server()
>>> self.assertEqual(self.fake_slave, slave_server.slave)
>>> self.assertEqual(("127.0.0.1", 8760), slave_server.server_address)
>>> self.assertEqual(self.config, slave_server.config)
>>> - self.assertEqual("", log_file.getvalue())
>>> + self.assertEqual("Request timeout set to 60.0 seconds\n",
>>> + log_file.getvalue())
>>> +

To avoid causing unnecessary breakage if the value for request_timeout changes in LexbuilderSlaveTestMixin, 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):
>>> + """
>>> + LexbuilderSlaveServer should set the request timeout to the value
>>> + specified in the configuration.
>>> + """
>>> + log_file = self.capture_logging(log_level=logging.DEBUG)
>>> + self.config.set("slave", "request_timeout", "30.0")
>>> +
>>> + slave_server = LexbuilderSlaveServer(
>>> + self.fake_slave, ("localhost", 8760), self.config)
>>> + self.assertEqual("Request timeout set to 30.0 seconds\n",
>>> + log_file.getvalue())
>>> +

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 LexbuilderSlaveTestMixin.setUp?

>>> + def test_create_with_invalid_configuration(self):
>>> + """
>>> + LexbuilderSlaveServer should handle invalid request timeout values by
>>> + falling back to the default.
>>> + """
>>> + log_file = self.capture_logging(log_level=logging.DEBUG)
>>> + self.config.set("slave", "request_timeout", "broken value")
>>> +
>>> +
>>> + slave_server = LexbuilderSlaveServer(
>>> + self.fake_slave, ("localhost", 8760), self.config)
>>> + self.assertEqual("Request timeout set to 60.0 seconds\n",
>>> + log_file.getvalue())
>>>

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 LexbuilderSlaveServer 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...

Read more...

review: Approve
Revision history for this message
Kevin McDermott (bigkevmcd) wrote :

> >>> + self.assertEqual("Request timeout set to 60.0 seconds\n",
> >>> + log_file.getvalue())
> >>> +
>
> To avoid causing unnecessary breakage if the value for request_timeout changes
> in LexbuilderSlaveTestMixin, we should probably construct the string we
> 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.set("slave",
> "request_timeout", "60")' in LexbuilderSlaveTestMixin.setUp?

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
> LexbuilderSlaveServer or update it to use a constant that you can import.

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 LexbuilderXMLRPCServer to set the timeout (which needs tests written to do this).

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'lib/offspring/slave/tests/test_slave.py'
--- lib/offspring/slave/tests/test_slave.py 2011-12-08 17:29:41 +0000
+++ lib/offspring/slave/tests/test_slave.py 2011-12-08 17:48:24 +0000
@@ -1,12 +1,16 @@
1# Copyright 2011 Canonical Ltd. This software is licensed under the1# Copyright 2011 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
3import platform3import platform
4import logging
4from datetime import datetime5from datetime import datetime
5from ConfigParser import ConfigParser6from ConfigParser import ConfigParser
6import xmlrpclib7import xmlrpclib
78
9from mocker import MockerTestCase
10
8from offspring.tests.helpers import CaptureLoggingTestCase11from offspring.tests.helpers import CaptureLoggingTestCase
912
13from offspring.enums import ProjectBuildStates
10from offspring.slave import (14from offspring.slave import (
11 LexbuilderSlave, ProjectBuildProcess, LexbuilderSlaveServer,15 LexbuilderSlave, ProjectBuildProcess, LexbuilderSlaveServer,
12 ShutdownThread)16 ShutdownThread)
@@ -89,16 +93,13 @@
89 self.slave = LexbuilderSlave(("localhost", 8760), self.config,93 self.slave = LexbuilderSlave(("localhost", 8760), self.config,
90 self.fake_server)94 self.fake_server)
9195
92 def tearDown(self):
93 super(TestLexbuilderSlave, self).tearDown()
94
95 def test_slave_run_starts_server_and_logs(self):96 def test_slave_run_starts_server_and_logs(self):
96 """97 """
97 LexbuilderSlave.run should start the LexbuilderSlaveServer.98 LexbuilderSlave.run should start the LexbuilderSlaveServer.
98 """99 """
99 log_file = self.capture_logging()100 log_file = self.capture_logging()
100 self.slave.run()101 self.slave.run()
101 self.assertIn("Slave ready to accept builds", log_file.getvalue())102 self.assertEqual("Slave ready to accept builds\n", log_file.getvalue())
102 self.assertTrue(self.fake_server.started)103 self.assertTrue(self.fake_server.started)
103104
104 def test_slave_stop(self):105 def test_slave_stop(self):
@@ -155,11 +156,12 @@
155156
156class FakeLexbuilderSlave(object):157class FakeLexbuilderSlave(object):
157 """Fake slave for use in tests."""158 """Fake slave for use in tests."""
158 def __init__(self, state=LexbuilderSlave.STATE_IDLE):159 def __init__(self, config=None, state=LexbuilderSlave.STATE_IDLE):
159 self.project_name = None160 self.project_name = None
160 self.config_url = None161 self.config_url = None
161 self.stopped = False162 self.stopped = False
162 self.state = state163 self.state = state
164 self.config = config
163165
164 def startBuild(self, project_name, config_url):166 def startBuild(self, project_name, config_url):
165 self.project_name = project_name167 self.project_name = project_name
@@ -178,7 +180,7 @@
178180
179 def setUp(self):181 def setUp(self):
180 super(TestLexbuilderSlaveServer, self).setUp()182 super(TestLexbuilderSlaveServer, self).setUp()
181 self.fake_slave = FakeLexbuilderSlave()183 self.fake_slave = FakeLexbuilderSlave(self.config)
182184
183 def create_slave_server(self):185 def create_slave_server(self):
184 """186 """
@@ -191,12 +193,41 @@
191 """193 """
192 We can instantiate a new LexbuilderSlaveServer.194 We can instantiate a new LexbuilderSlaveServer.
193 """195 """
194 log_file = self.capture_logging()196 log_file = self.capture_logging(log_level=logging.DEBUG)
197
195 slave_server = self.create_slave_server()198 slave_server = self.create_slave_server()
196 self.assertEqual(self.fake_slave, slave_server.slave)199 self.assertEqual(self.fake_slave, slave_server.slave)
197 self.assertEqual(("127.0.0.1", 8760), slave_server.server_address)200 self.assertEqual(("127.0.0.1", 8760), slave_server.server_address)
198 self.assertEqual(self.config, slave_server.config)201 self.assertEqual(self.config, slave_server.config)
199 self.assertEqual("", log_file.getvalue())202 self.assertEqual("Request timeout set to 60.0 seconds\n",
203 log_file.getvalue())
204
205 def test_create_with_missing_request_timeout(self):
206 """
207 LexbuilderSlaveServer should set the request timeout to the value
208 specified in the configuration.
209 """
210 log_file = self.capture_logging(log_level=logging.DEBUG)
211 self.config.set("slave", "request_timeout", "30.0")
212
213 slave_server = LexbuilderSlaveServer(
214 self.fake_slave, ("localhost", 8760), self.config)
215 self.assertEqual("Request timeout set to 30.0 seconds\n",
216 log_file.getvalue())
217
218 def test_create_with_invalid_configuration(self):
219 """
220 LexbuilderSlaveServer should handle invalid request timeout values by
221 falling back to the default.
222 """
223 log_file = self.capture_logging(log_level=logging.DEBUG)
224 self.config.set("slave", "request_timeout", "broken value")
225
226
227 slave_server = LexbuilderSlaveServer(
228 self.fake_slave, ("localhost", 8760), self.config)
229 self.assertEqual("Request timeout set to 60.0 seconds\n",
230 log_file.getvalue())
200231
201 def test_api_start_build(self):232 def test_api_start_build(self):
202 """233 """
@@ -485,3 +516,189 @@
485 self.assertEqual(LexbuilderSlaveServer.REQUEST_OKAY,516 self.assertEqual(LexbuilderSlaveServer.REQUEST_OKAY,
486 slave_server.api_shutdown())517 slave_server.api_shutdown())
487 self.assertTrue(fake_shutdown.started)518 self.assertTrue(fake_shutdown.started)
519
520
521class TestProjectBuildProcess(LexbuilderSlaveTestMixin, CaptureLoggingTestCase):
522
523 def setUp(self):
524 super(TestProjectBuildProcess, self).setUp()
525 self.master = FakeLexbuilderSlave(config=self.config)
526 self.build_process = ProjectBuildProcess(
527 self.master, u"testing", "http://example.com/project")
528
529 def test_create(self):
530 """
531 We can instantiate a new ProjectBuildProcess.
532 """
533 self.assertEqual(u"testing", self.build_process.projectName)
534 self.assertEqual([u"testing", "http://example.com/project"],
535 self.build_process.arguments)
536
537 def test_get_state_currently_active(self):
538 """
539 If ProjectBuildProcess.state is STATE_ACTIVE then
540 ProjectBuildProcess.get_state() should return
541 ProjectBuildStates.PENDING.
542 """
543 self.build_process.state = self.build_process.STATE_ACTIVE
544 self.assertEqual(ProjectBuildStates.PENDING,
545 self.build_process.getState())
546
547 def test_get_state_currently_failed(self):
548 """
549 If ProjectBuildProcess.state is STATE_FAILED then
550 ProjectBuildProcess.get_state() should return
551 ProjectBuildStates.FAILED.
552 """
553 self.build_process.state = self.build_process.STATE_FAILED
554 self.assertEqual(ProjectBuildStates.FAILED,
555 self.build_process.getState())
556
557 def test_get_state_currently_success(self):
558 """
559 If ProjectBuildProcess.state is STATE_SUCCESS then
560 ProjectBuildProcess.get_state() should return
561 ProjectBuildStates.SUCCESS.
562 """
563 self.build_process.state = self.build_process.STATE_SUCCESS
564 self.assertEqual(ProjectBuildStates.SUCCESS,
565 self.build_process.getState())
566
567 def test_get_state_currently_unknown_state(self):
568 """
569 If ProjectBuildProcess.state is not one of STATE_ACTIVE, STATE_FAILED
570 or STATE_SUCCESS ProjectBuildProcess.get_state() should return
571 ProjectBuildStates.UNKNOWN.
572 """
573 self.build_process.state = None
574 self.assertEqual(ProjectBuildStates.UNKNOWN,
575 self.build_process.getState())
576
577 def test_pre_execution(self):
578 """
579 ProjectBuildProcess.preExecution should log the fact that the slave is
580 starting a build, and set the current state of the LexbuilderSlave to
581 STATE_BUILDING.
582 """
583 log_file = self.capture_logging(log_level=logging.DEBUG)
584
585 self.build_process.preExecution()
586 self.assertEqual(LexbuilderSlave.STATE_BUILDING,
587 self.build_process.master.state)
588 self.assertEqual("Setting state of slave to BUILDING\n",
589 log_file.getvalue())
590
591 def test_post_execution(self):
592 """
593 ProjectBuildProcess.preExecution should log the fact that the slave is
594 finishing a build, and set the current state of the LexbuilderSlave to
595 STATE_IDLE.
596 """
597 log_file = self.capture_logging(log_level=logging.DEBUG)
598
599 self.build_process.postExecution()
600 self.assertEqual(LexbuilderSlave.STATE_IDLE,
601 self.build_process.master.state)
602 self.assertEqual("Setting state of slave to IDLE\n",
603 log_file.getvalue())
604
605 def test_get_command_path(self):
606 """
607 ProjectBuildProcess.getCommandPath should return the configured
608 build_command.
609 """
610 self.config.set("slave", "build_command",
611 "/usr/bin/offspring-build")
612 self.assertEqual("/usr/bin/offspring-build",
613 self.build_process.getCommandPath())
614
615 def test_get_project_name(self):
616 """
617 ProjectBuildProcess.getProjectName should return the name of the
618 project currently configured for building.
619 """
620 self.build_process.projectName = u"testing"
621 self.assertEqual(u"testing",
622 self.build_process.getProjectName())
623
624 def test_get_build_name_with_known_build_name(self):
625 """
626 ProjectBuildProcess.getBuildName should return the name of the current
627 build if known.
628 """
629 self.build_process.buildName = u"testing"
630 self.assertEqual(u"testing", self.build_process.getBuildName())
631
632 def test_get_build_name_with_unknown_build_inactive_process(self):
633 """
634 If the build name is unknown, and the process is not currently active,
635 then we ProjectBuildProcess.getBuildName should examine the output of
636 the build process and look for START <projectname> <buildname> and
637 extract the buildname and return it.
638 """
639 self.build_process.stdoutdata = "START project testproject mybuild\n"
640 self.assertEqual("mybuild", self.build_process.getBuildName())
641
642 def test_get_build_name_with_unknown_build_active_process(self):
643 """
644 If the build name is unknown, and the process is currently active,
645 then we ProjectBuildProcess.getBuildName should examine the the build
646 statefile from the configuration and parse the project name and build
647 name from there.
648 """
649 self.build_process.state = self.build_process.STATE_ACTIVE
650 self.build_process.projectName = "newproject"
651 build_statefile_name = self.makeFile("newproject newbuild\n")
652 self.config.set("slave", "build_statefile", build_statefile_name)
653 self.assertEqual("newbuild", self.build_process.getBuildName())
654
655 def test_get_build_name_with_unknown_build_active_invalid_project(self):
656 """
657 If the build name is unknown, and the process is currently active,
658 then we ProjectBuildProcess.getBuildName should examine the the build
659 statefile from the configuration and parse the project name and build
660 name from there.
661
662 If the project name parsed from the build file doesn't match the
663 project we're building, then we should get an error.
664 """
665 self.build_process.state = self.build_process.STATE_ACTIVE
666 self.build_process.projectName = "anotherprojectentirely"
667 build_statefile_name = self.makeFile("newproject newbuild\n")
668 self.config.set("slave", "build_statefile", build_statefile_name)
669
670 self.assertEqual(LexbuilderSlaveServer.REQUEST_ERROR,
671 self.build_process.getBuildName())
672
673 def test_get_build_name_with_unknown_build_active_process_error(self):
674 """
675 If the build name is unknown, and the process is currently active,
676 then we ProjectBuildProcess.getBuildName should examine the the build
677 statefile from the configuration, any error reading this should result
678 in an error return.
679 """
680 self.build_process.state = self.build_process.STATE_ACTIVE
681 self.build_process.projectName = "newproject"
682 build_statefile_name = self.makeFile()
683 self.config.set("slave", "build_statefile", build_statefile_name)
684 self.assertEqual(LexbuilderSlaveServer.REQUEST_ERROR,
685 self.build_process.getBuildName())
686
687
688class TestShutdownThread(MockerTestCase):
689 """
690 Tests for the ShutdownThread class.
691 """
692
693 def test_run(self):
694 """
695 """
696 sleep_mock = self.mocker.replace("time.sleep")
697 sleep_mock(1)
698 self.mocker.replay()
699 master = FakeLexbuilderSlaveServer()
700
701 shutdown_thread = ShutdownThread(master)
702 shutdown_thread.run()
703
704 self.assertTrue(master.stopped)

Subscribers

People subscribed via source and target branches