Merge lp:~rvb/maas-test/display-maas-version into lp:maas-test

Proposed by Raphaël Badin
Status: Merged
Approved by: Raphaël Badin
Approved revision: 104
Merged at revision: 109
Proposed branch: lp:~rvb/maas-test/display-maas-version
Merge into: lp:maas-test
Diff against target: 132 lines (+73/-2)
4 files modified
maastest/maasfixture.py (+8/-1)
maastest/tests/test_maasfixture.py (+31/-0)
maastest/tests/test_utils.py (+18/-0)
maastest/utils.py (+16/-1)
To merge this branch: bzr merge lp:~rvb/maas-test/display-maas-version
Reviewer Review Type Date Requested Status
Julian Edwards (community) Approve
Review via email: mp+200863@code.launchpad.net

Commit message

Display the version of MAAS to be installed.

Description of the change

It would be nice to also display the source of the package but this is a start and should be good enough for now.

To post a comment you must log in.
Revision history for this message
Julian Edwards (julian-edwards) wrote :

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

 review: approve

Looks good, thanks for doing this very useful change. Just a couple
of small comments:

> === modified file 'maastest/tests/test_maasfixture.py' ---
> maastest/tests/test_maasfixture.py 2013-12-18 12:04:41 +0000 +++
> maastest/tests/test_maasfixture.py 2014-01-08 16:22:42 +0000 @@
> -335,6 +335,44 @@ check_call=True), )
>
> + def test_install_maas_logs_maas_version(self): +
> fixture = self.make_kvm_fixture() + maas_fixture =
> self.make_maas_fixture(fixture) + version =
> self.getUniqueString() + mock_get_maas_version =
> mock.MagicMock(return_value=version) +
> self.patch(maas_fixture, 'get_maas_version',
> mock_get_maas_version)

Because the mock is returned from patch(), you can avoid importing
MagickMock and just do:

   self.patch(maas_fixture, 'get_maas_version').return_value = version

> + mock_logging = mock.MagicMock() +
> self.patch(maasfixture.logging, 'info', mock_logging)

And this can be:

   mock_logging = self.patch(maasfixture.logging, 'info')

> + maas_fixture.install_maas() + + self.assertEqual( +
> ( + [mock.call()], + [ +
> mock.call("Installing MAAS (version %s)..." % version), +
> mock.call("Done installing MAAS."), + ]), +
> (mock_get_maas_version.mock_calls, mock_logging.mock_calls))

I think this is definitely a case where having more than one assertion
is better than compounding them into a single call. This is extremely
hard to read and digest. And ease of reading trumps everything else!

> + + def test_get_maas_version_returns_maas_version(self): +
> fixture = self.make_kvm_fixture() + maas_fixture =
> self.make_maas_fixture(fixture) + version =
> self.getUniqueString() + policy = "Candidate: %s\n" %
> version + self.patch(fixture, 'run_command',
> mock.MagicMock( + return_value=(0, policy, b'stderr')))
> + + extracted_version = maas_fixture.get_maas_version() + +
> fixture.run_command.assert_has_call( + mock.call( +
> maas_fixture.kvm_fixture._make_apt_get_install_command( +
> ['language-pack-en']), + check_call=True), +
> )

You could make this easier to read by assigning a variable to the
result of mock.call() and using that in the assert_has_call.

Cheers!
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.14 (GNU/Linux)
Comment: Using GnuPG with Thunderbird - http://www.enigmail.net/

iEYEARECAAYFAlLN8WUACgkQWhGlTF8G/HcjiQCffd/lSuhJw3fupbpVCefJLGuQ
eSEAn1YZRK6NYjQxqqARnVYeifwSGl+/
=CJXi
-----END PGP SIGNATURE-----

review: Approve
104. By Raphaël Badin

Review fixes.

Revision history for this message
Raphaël Badin (rvb) wrote :

 > Because the mock is returned from patch(), you can avoid importing
> MagickMock and just do:
>
>[...]

Well, actually, the mock object is *not* returned by patch(…). In MAAS it is, see src/maastesting/testcase.py:MAASTestCase.patch. But in maas-test, we don't have that utility

>
> > + maas_fixture.install_maas() + + self.assertEqual( +
> > ( + [mock.call()], + [ +
> > mock.call("Installing MAAS (version %s)..." % version), +
> > mock.call("Done installing MAAS."), + ]), +
> > (mock_get_maas_version.mock_calls, mock_logging.mock_calls))
>
> I think this is definitely a case where having more than one assertion
> is better than compounding them into a single call. This is extremely
> hard to read and digest. And ease of reading trumps everything else!

All right, you know I'm a sucker for compound assertions but in this instance, you're definitely right.

> [...]
> You could make this easier to read by assigning a variable to the
> result of mock.call() and using that in the assert_has_call.

Actually, I've refactored that code a bit and it's now a bit simpler.

Thanks for the review!

Revision history for this message
Julian Edwards (julian-edwards) wrote :

On 09/01/14 18:40, Raphaël Badin wrote:
> > Because the mock is returned from patch(), you can avoid importing
>> MagickMock and just do:
>>
>> [...]
>
> Well, actually, the mock object is *not* returned by patch(…). In MAAS it is, see src/maastesting/testcase.py:MAASTestCase.patch. But in maas-test, we don't have that utility

Ah! That's probably worth adding as a small change next time you're in
this code then.

> Thanks for the review!

NP.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'maastest/maasfixture.py'
2--- maastest/maasfixture.py 2014-01-08 17:31:43 +0000
3+++ maastest/maasfixture.py 2014-01-09 08:39:45 +0000
4@@ -99,14 +99,21 @@
5 # Install the English language pack first. If this is not done
6 # before postgres is installed, on some systems it won't be able to
7 # set up its main cluster.
8+ maas_version = self.get_maas_version()
9+ logging.info("Installing MAAS (version %s)..." % maas_version)
10 # TODO: Is there no better way to ensure this, e.g. through a
11 # dependency?
12- logging.info("Installing MAAS...")
13 self.kvm_fixture.install_packages(['language-pack-en'])
14 # Now we can install maas (which also installs postgres).
15 self.kvm_fixture.install_packages(['maas', 'maas-dhcp', 'maas-dns'])
16 logging.info("Done installing MAAS.")
17
18+ def get_maas_version(self):
19+ """Return the version of MAAS that is to be installed."""
20+ _, policy, _ = self.kvm_fixture.run_command(
21+ ['apt-cache', 'policy', 'maas'], check_call=True)
22+ return utils.extract_package_version(policy)
23+
24 def configure_default_maas_url(self):
25 """Set `DEFAULT_MAAS_URL` in the virtual machine.
26
27
28=== modified file 'maastest/tests/test_maasfixture.py'
29--- maastest/tests/test_maasfixture.py 2014-01-08 17:31:43 +0000
30+++ maastest/tests/test_maasfixture.py 2014-01-09 08:39:45 +0000
31@@ -335,6 +335,37 @@
32 check_call=True),
33 )
34
35+ def test_install_maas_logs_maas_version(self):
36+ fixture = self.make_kvm_fixture()
37+ maas_fixture = self.make_maas_fixture(fixture)
38+ version = self.getUniqueString()
39+ mock_get_maas_version = mock.MagicMock(return_value=version)
40+ self.patch(maas_fixture, 'get_maas_version', mock_get_maas_version)
41+ mock_logging = mock.MagicMock()
42+ self.patch(maasfixture.logging, 'info', mock_logging)
43+
44+ maas_fixture.install_maas()
45+
46+ self.assertEqual([mock.call()], mock_get_maas_version.mock_calls)
47+ self.assertEqual(
48+ [
49+ mock.call("Installing MAAS (version %s)..." % version),
50+ mock.call("Done installing MAAS."),
51+ ],
52+ mock_logging.mock_calls)
53+
54+ def test_get_maas_version_returns_maas_version(self):
55+ fixture = self.make_kvm_fixture()
56+ maas_fixture = self.make_maas_fixture(fixture)
57+ version = self.getUniqueString()
58+ policy = "Candidate: %s\n" % version
59+ self.patch(fixture, 'run_command', mock.MagicMock(
60+ return_value=(0, policy, b'stderr')))
61+
62+ extracted_version = maas_fixture.get_maas_version()
63+
64+ self.assertEqual(version, extracted_version)
65+
66 def test_install_maas_installs_language_pack_followed_by_maas(self):
67 # We need to do it in this order, or postgres (and other things) may
68 # break during installation.
69
70=== modified file 'maastest/tests/test_utils.py'
71--- maastest/tests/test_utils.py 2014-01-08 17:49:09 +0000
72+++ maastest/tests/test_utils.py 2014-01-09 08:39:45 +0000
73@@ -412,3 +412,21 @@
74 stream.foo_bar_baz = mock.MagicMock(return_value=some_string)
75 caching_stream = utils.CachingOutputStream(stream)
76 self.assertEqual(some_string, caching_stream.foo_bar_baz())
77+
78+
79+class TestExtractPackageVersion(testtools.TestCase):
80+
81+ def test_extracts_package_version(self):
82+ version = '1.4+bzr1693+dfsg-0ubuntu2.2'
83+ policy = dedent("""
84+ maas:
85+ Installed: (none)
86+ Candidate: %s
87+ Version table:
88+ 1.4+bzr1693+dfsg-0ubuntu2.2 0
89+ 500 http://example.com/ubuntu/ saucy/main amd64 Packages
90+ """) % version
91+ self.assertEqual(version, utils.extract_package_version(policy))
92+
93+ def test_returns_None_if_policy_is_unparseable(self):
94+ self.assertIsNone(utils.extract_package_version("unparsable"))
95
96=== modified file 'maastest/utils.py'
97--- maastest/utils.py 2014-01-09 07:19:20 +0000
98+++ maastest/utils.py 2014-01-09 08:39:45 +0000
99@@ -25,11 +25,12 @@
100 ]
101
102
103+import inspect
104 from io import BytesIO
105-import inspect
106 import logging
107 from pipes import quote
108 import platform
109+import re
110 from subprocess import (
111 PIPE,
112 Popen,
113@@ -256,6 +257,20 @@
114 return True
115
116
117+def extract_package_version(policy):
118+ """Get the version of the package to be installed from policy settings.
119+
120+ The given policy is assumed to be of the form returned by running:
121+ `apt-cache policy <packagename>`. This method returns None if the
122+ given policy string cannot be parsed.
123+ """
124+ match = re.search('Candidate: (.*)\n', policy)
125+ if match is not None:
126+ return match.group(1)
127+ else:
128+ return None
129+
130+
131 def virtualization_type():
132 """Returns the name of the virtualization type of this machine.
133

Subscribers

People subscribed via source and target branches