Merge lp:~gmb/lpsetup/death-to-doctests-in-subcommands into lp:lpsetup

Proposed by Graham Binns
Status: Merged
Approved by: Graham Binns
Approved revision: 52
Merged at revision: 51
Proposed branch: lp:~gmb/lpsetup/death-to-doctests-in-subcommands
Merge into: lp:lpsetup
Diff against target: 236 lines (+114/-83)
2 files modified
lpsetup/subcommands/inithost.py (+2/-82)
lpsetup/tests/subcommands/test_inithost.py (+112/-1)
To merge this branch: bzr merge lp:~gmb/lpsetup/death-to-doctests-in-subcommands
Reviewer Review Type Date Requested Status
Brad Crittenden (community) code Approve
Review via email: mp+114393@code.launchpad.net

Commit message

The doctests in the subcommands package have been turned into unit tests.

Description of the change

This branch removes all the subcommands doctests and turns them into unit tests. I'll tackle other doctests in separate branches.

To post a comment you must log in.
50. By Graham Binns

Fixed conflicts and lint.

Revision history for this message
Brad Crittenden (bac) wrote :

Looks good Graham.

On IRC we discussed:

1) ORing the expected file stat masks together and then using XOR with the actual mode to allow a single assertion.

2) Taking into account the new behavior of write_file_contents where it no longer added the \n.

Thanks,
Brad

review: Approve (code)
51. By Graham Binns

Bitwise funk.

52. By Graham Binns

Test tweak.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lpsetup/subcommands/inithost.py'
2--- lpsetup/subcommands/inithost.py 2012-07-10 19:06:12 +0000
3+++ lpsetup/subcommands/inithost.py 2012-07-11 11:50:23 +0000
4@@ -53,39 +53,7 @@
5
6
7 def write_file_contents(filename, contents, mode, header=None):
8- """Write `contents` to filename and chmod it to 0644.
9-
10- >>> write_file_contents('/tmp/foo', 'Hello, world', 'w')
11- >>> print open('/tmp/foo', 'r').read()
12- Hello, world
13- >>> os.remove('/tmp/foo')
14-
15- If the file already exists, `mode` is set to 'w', and the file's
16- contents differ from `contents`, write_file_contents() raises an
17- Exception.
18-
19- >>> write_file_contents('/tmp/foo', 'Hello, world', 'w')
20- >>> write_file_contents(
21- ... '/tmp/foo', 'Hello again, world', 'w') # doctest: +ELLIPSIS
22- Traceback (most recent call last):
23- Exception: File /tmp/foo already exists with different contents...
24- >>> os.remove('/tmp/foo')
25-
26- If the existing file's contents don't differ from `contents`,
27- write_file_contents() will simply return.
28-
29- >>> write_file_contents('/tmp/foo', 'Hello, world', 'w')
30- >>> write_file_contents(
31- ... '/tmp/foo', 'Hello, world', 'w') # doctest: +ELLIPSIS
32- >>> os.remove('/tmp/foo')
33-
34- Passing a `mode` other than "a" or "w" will cause an Exception.
35-
36- >>> write_file_contents(
37- ... '/tmp/foo', 'Hello, world', 'w+') # doctest: +ELLIPSIS
38- Traceback (most recent call last):
39- AssertionError: Mode should be 'w' or 'a' only...
40- """
41+ """Write `contents` to filename and chmod it to 0644."""
42 assert mode in ('w', 'a'), "Mode should be 'w' or 'a' only."
43 if mode == 'w' and os.path.exists(filename):
44 with open(filename, 'r') as existing_file:
45@@ -104,55 +72,7 @@
46
47 def ensure_ssh_keys(ssh_dir, private_key, public_key, valid_ssh_keys,
48 ssh_key_path):
49- """Ensure that SSH is configured correctly for the user.
50-
51- If valid SSH keys are passed to the function, they will be written
52- out to `ssh_key_path`
53-
54- >>> public = "Public key"
55- >>> private = "Private key"
56- >>> ssh_dir = "/tmp"
57- >>> ensure_ssh_keys(ssh_dir, private, public, True, '/tmp/test_key')
58- >>> public_keyfile = open('/tmp/test_key.pub', 'r')
59- >>> print public_keyfile.read() # doctest: +ELLIPSIS
60- # This file created at ...
61- Public key
62- >>> private_keyfile = open('/tmp/test_key', 'r')
63- >>> print private_keyfile.read() # doctest: +ELLIPSIS
64- # This file created at ...
65- Private key
66-
67- Two other files will have been generated: authorized_keys and known_hosts
68-
69- >>> print open(
70- ... '/tmp/authorized_keys', 'r').read() # doctest: +ELLIPSIS
71- # This file created at ...
72- Public key
73- >>> print open('/tmp/known_hosts', 'r').read() # doctest: +ELLIPSIS
74- # This file created at ...
75- bazaar.launchpad.net ...
76- >>> os.remove('/tmp/test_key')
77- >>> os.remove('/tmp/test_key.pub')
78- >>> os.remove('/tmp/authorized_keys')
79- >>> os.remove('/tmp/known_hosts')
80-
81- If valid keys are not passed, new keys will be generated.
82-
83- >>> ensure_ssh_keys(ssh_dir, None, None, False, '/tmp/test_key')
84- >>> os.path.exists('/tmp/test_key')
85- True
86- >>> os.path.exists('/tmp/test_key.pub')
87- True
88- >>> os.path.exists('/tmp/authorized_keys')
89- True
90- >>> os.path.exists('/tmp/known_hosts')
91- True
92- >>> os.remove('/tmp/test_key')
93- >>> os.remove('/tmp/test_key.pub')
94- >>> os.remove('/tmp/authorized_keys')
95- >>> os.remove('/tmp/known_hosts')
96-
97- """
98+ """Ensure that SSH is configured correctly for the user."""
99 # Set up the user's ssh directory. The ssh key must be associated
100 # with the lpuser's Launchpad account.
101 mkdirs(ssh_dir)
102
103=== modified file 'lpsetup/tests/subcommands/test_inithost.py'
104--- lpsetup/tests/subcommands/test_inithost.py 2012-07-09 21:18:21 +0000
105+++ lpsetup/tests/subcommands/test_inithost.py 2012-07-11 11:50:23 +0000
106@@ -4,6 +4,10 @@
107
108 """Tests for the inithost sub command."""
109
110+import os
111+import shutil
112+import stat
113+import tempfile
114 import unittest
115
116 from lpsetup import handlers
117@@ -36,7 +40,7 @@
118 '-v', private_key, '-b', public_key, '-S', ssh_key_name)
119
120
121-class InithostTest(StepsBasedSubCommandTestMixin, unittest.TestCase):
122+class InithostSmokeTest(StepsBasedSubCommandTestMixin, unittest.TestCase):
123
124 sub_command_class = inithost.SubCommand
125 expected_arguments = get_arguments()
126@@ -51,3 +55,110 @@
127 initialize_lxc_step,
128 setup_apt_step,)
129 needs_root = True
130+
131+
132+class WriteFileContentsTestCase(unittest.TestCase):
133+ """Tests for inithost.write_file_contents()."""
134+
135+ def setUp(self):
136+ temp_file = tempfile.NamedTemporaryFile()
137+ # Calling close() on the temp_file will delete it, but its name
138+ # will live on...
139+ temp_file.close()
140+ self.temp_filename = temp_file.name
141+
142+ def test_write_file_contents_writes_file_contents(self):
143+ # inithost.write_file_contents writes the supplied file contents
144+ # to a given file using the mode provided and chmods the file to
145+ # 0644 for the current user.
146+ self.addCleanup(os.remove, self.temp_filename)
147+ inithost.write_file_contents(self.temp_filename, 'Hello, world!', 'w')
148+ with open(self.temp_filename, 'r') as the_file:
149+ contents = the_file.read()
150+ self.assertEqual('Hello, world!', contents)
151+ file_mode = os.stat(self.temp_filename).st_mode
152+ # The file permissions should be 0644. So, we do a bitwise
153+ # dance...
154+ expected_mode = (
155+ stat.S_IWUSR | stat.S_IRUSR | stat.S_IRGRP | stat.S_IWGRP |
156+ stat.S_IROTH | ~ stat.S_IXUSR | ~ stat.S_IWGRP |
157+ ~ stat.S_IXGRP | ~ stat.S_IWOTH | ~ stat.S_IXOTH)
158+ self.assertTrue(file_mode ^ expected_mode)
159+
160+ def test_write_file_contents_wont_overwrite_existing_files(self):
161+ # If the file already exists, `mode` is set to 'w', and the file's
162+ # contents differ from `contents`, write_file_contents() raises an
163+ # Exception.
164+ self.addCleanup(os.remove, self.temp_filename)
165+ inithost.write_file_contents(self.temp_filename, 'Hello, world!', 'w')
166+ self.assertRaises(
167+ Exception, inithost.write_file_contents, self.temp_filename,
168+ 'Hello again!', 'w')
169+
170+ def test_write_file_contents_returns_if_file_contents_dont_change(self):
171+ # If the existing file's contents don't differ from `contents`,
172+ # write_file_contents() will simply return.
173+ self.addCleanup(os.remove, self.temp_filename)
174+ inithost.write_file_contents(self.temp_filename, 'Hello, world!', 'w')
175+ # This second call shouldn't raise an exception.
176+ inithost.write_file_contents(self.temp_filename, 'Hello, world!', 'w')
177+
178+ def test_write_file_contents_accepts_modes_a_or_w(self):
179+ # Passing a `mode` other than "a" or "w" to write_file_contents()
180+ # will cause an Exception.
181+ self.assertRaises(
182+ AssertionError, inithost.write_file_contents,
183+ '/tmp/foo', 'Hello, world!', 'a+')
184+
185+
186+class EnsureSSHKeysTestCase(unittest.TestCase):
187+ """Tests for inithost.ensure_ssh_keys()."""
188+
189+ def setUp(self):
190+ temp_file = tempfile.NamedTemporaryFile()
191+ temp_file.close()
192+ self.temp_filename = temp_file.name
193+ self.ssh_dir = tempfile.mkdtemp()
194+ self.addCleanup(shutil.rmtree, self.ssh_dir)
195+ self.addCleanup(os.remove, self.temp_filename)
196+ self.addCleanup(os.remove, self.temp_filename + ".pub")
197+
198+ def test_ensure_ssh_keys_writes_to_ssh_key_path(self):
199+ # If inithost.ensure_ssh_keys() is told that the keys it has
200+ # been passed are valid, it will write them out to the provided
201+ # ssh_key_path.
202+ public_key = "Public"
203+ private_key = "Private"
204+ inithost.ensure_ssh_keys(
205+ self.ssh_dir, private_key, public_key, True, self.temp_filename)
206+ with open(self.temp_filename + '.pub', 'r') as pub_file:
207+ self.assertIn(public_key, pub_file.read())
208+ with open(self.temp_filename, 'r') as priv_file:
209+ self.assertIn(private_key, priv_file.read())
210+
211+ def test_ensure_ssh_keys_generates_keys_if_not_passed(self):
212+ # If SSH keys aren't passed to ensure_ssh_keys(), the function
213+ # will generate some.
214+ inithost.ensure_ssh_keys(
215+ self.ssh_dir, None, None, False, self.temp_filename)
216+ self.assertTrue(os.path.exists(self.temp_filename))
217+ self.assertTrue(os.path.exists(self.temp_filename + ".pub"))
218+
219+ def test_ensure_ssh_keys_generates_other_files(self):
220+ # ensure_ssh_keys() also generates an authorized_keys file and a
221+ # known_hosts file in the ssh dir.
222+ inithost.ensure_ssh_keys(
223+ self.ssh_dir, None, None, False, self.temp_filename)
224+ self.assertTrue(
225+ os.path.exists(os.path.join(self.ssh_dir, 'authorized_keys')))
226+ self.assertTrue(
227+ os.path.exists(os.path.join(self.ssh_dir, 'known_hosts')))
228+
229+ def test_ensure_ssh_keys_creates_ssh_dir(self):
230+ # If the ssh_dir passed to ensure_ssh_keys() doesn't exist, it
231+ # will be created.
232+ shutil.rmtree(self.ssh_dir)
233+ inithost.ensure_ssh_keys(
234+ self.ssh_dir, None, None, False, self.temp_filename)
235+ self.assertTrue(os.path.exists(self.ssh_dir))
236+ self.assertTrue(os.path.isdir(self.ssh_dir))

Subscribers

People subscribed via source and target branches