Merge lp:~james-w/linaro-image-tools/tarfile-matchers into lp:linaro-image-tools/11.11

Proposed by James Westby
Status: Merged
Approved by: Michael Hudson-Doyle
Approved revision: 84
Merged at revision: 55
Proposed branch: lp:~james-w/linaro-image-tools/tarfile-matchers
Merge into: lp:linaro-image-tools/11.11
Prerequisite: lp:~james-w/linaro-image-tools/tarfile-improvements
Diff against target: 423 lines (+398/-0)
4 files modified
hwpack/tarfile_matchers.py (+130/-0)
hwpack/testing.py (+39/-0)
hwpack/tests/__init__.py (+1/-0)
hwpack/tests/test_tarfile_matchers.py (+228/-0)
To merge this branch: bzr merge lp:~james-w/linaro-image-tools/tarfile-matchers
Reviewer Review Type Date Requested Status
Michael Hudson-Doyle (community) Approve
Review via email: mp+34147@code.launchpad.net

Description of the change

Hi,

This adds some matchers that I will use for testing the output of
hwpack-create. It's a lot easier to read and write the tests with this
style, rather than exercising the tarfile API directly.

I don't much like these tests though, suggestions for how to improve them
welcome.

Thanks,

James

To post a comment you must log in.
Revision history for this message
Michael Hudson-Doyle (mwhudson) wrote :

It looks mostly fine. I agree the tests are a bit horrible.

Could you fold this sort of thing:

+ backing_file = StringIO()
+ with writeable_tarfile(backing_file, default_mtime=12345) as tf:
+ tf.create_file_from_string("foo", "")
+ with standard_tarfile(backing_file) as tf:

into some kind of context manager that would let you write:

with test_tarfile(default_mtime=12345, contents=[('foo', '')]) as tf:

? That would help a bit.

As for the assertions, could you construct a mismatch using keyword arguments and then test that the mismatches match? I don't know if that would be better, but it seems worth a try.

review: Approve
77. By James Westby

Merged tarfile-improvements into tarfile-matchers.

78. By James Westby

Merged tarfile-improvements into tarfile-matchers.

79. By James Westby

Merged tarfile-improvements into tarfile-matchers.

80. By James Westby

Merged tarfile-improvements into tarfile-matchers.

81. By James Westby

Reduce duplication in the tests. Thanks Michael.

82. By James Westby

Add __eq__ and __hash__ on the mismatches.

83. By James Westby

Take advantage of the new __eq__ when checking TarfileWrongValueMismatch.

84. By James Westby

Also test the values set on TarfileMissingPathMismatch.

Revision history for this message
James Westby (james-w) wrote :

On Tue, 31 Aug 2010 02:44:41 -0000, Michael Hudson <email address hidden> wrote:
> Review: Approve
> It looks mostly fine. I agree the tests are a bit horrible.
>
> Could you fold this sort of thing:
>
> + backing_file = StringIO()
> + with writeable_tarfile(backing_file, default_mtime=12345) as tf:
> + tf.create_file_from_string("foo", "")
> + with standard_tarfile(backing_file) as tf:
>
> into some kind of context manager that would let you write:
>
> with test_tarfile(default_mtime=12345, contents=[('foo', '')]) as tf:
>
> ? That would help a bit.

Done, thanks for the suggestion.

> As for the assertions, could you construct a mismatch using keyword
> arguments and then test that the mismatches match? I don't know if
> that would be better, but it seems worth a try.

I took a stab at this, let me know what you think now.

Thanks,

James

Revision history for this message
Michael Hudson-Doyle (mwhudson) wrote :

I think the test_tarfile helper has improved things.

For the second of my suggestions, I actually meant constructing the mismatch in the test and then using assertEquals. But I don't know if that would be any better either (probably not).

I feel bad for causing you to write all those hash and eq tests!

85. By James Westby

Merged tarfile-improvements into tarfile-matchers.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== added file 'hwpack/tarfile_matchers.py'
--- hwpack/tarfile_matchers.py 1970-01-01 00:00:00 +0000
+++ hwpack/tarfile_matchers.py 2010-09-01 20:48:43 +0000
@@ -0,0 +1,130 @@
1from testtools.matchers import Matcher, Mismatch
2
3
4class TarfileMissingPathMismatch(Mismatch):
5 """A Mismatch indicating that a required path was missing from a tarfile.
6 """
7
8 def __init__(self, tarball, path):
9 """Create a TarfileMissingPathMismatch Mismatch.
10
11 :param tarball: the tarfile that was checked.
12 :param path: the path that was expected to be present.
13 """
14 self.tarball = tarball
15 self.path = path
16
17 def describe(self):
18 return '"%s" has no path "%s"' % (self.tarball, self.path)
19
20 def __eq__(self, other):
21 return self.tarball == other.tarball and self.path == other.path
22
23 def __hash__(self):
24 return hash((self.tarball, self.path))
25
26
27class TarfileWrongValueMismatch(Mismatch):
28 """A Mismatch indicating that an entry in a tarfile was not as expected.
29 """
30
31 def __init__(self, attribute, tarball, path, expected, actual):
32 """Create a TarfileWrongValueMismatch Mismatch.
33
34 :param attribute: the attribute that was not as expected.
35 :type attribute: str
36 :param tarball: the tarfile that was checked.
37 :param path: the path that was checked.
38 :param expected: the expected value of the attribute.
39 :param actual: the value that was found.
40 """
41 self.attribute = attribute
42 self.tarball = tarball
43 self.path = path
44 self.expected = expected
45 self.actual = actual
46
47 def describe(self):
48 return 'The path "%s" in "%s" has %s %s, expected %s' % (
49 self.path, self.tarball, self.attribute, self.actual,
50 self.expected)
51
52 def __eq__(self, other):
53 return (self.attribute == other.attribute
54 and self.tarball == other.tarball
55 and self.path == other.path
56 and self.expected == other.expected
57 and self.actual == other.actual)
58
59 def __hash__(self):
60 return hash(
61 (self.attribute, self.tarball, self.path, self.expected,
62 self.actual))
63
64
65class TarfileHasFile(Matcher):
66 """Check that a tarfile has an entry with certain values."""
67
68 def __init__(self, path, type=None, size=None, mtime=None, mode=None,
69 linkname=None, uid=None, gid=None, uname=None, gname=None,
70 content=None):
71 """Create a TarfileHasFile Matcher.
72
73 :param path: the path that must be present.
74 :type path: str
75 :param type: the type of TarInfo that must be at `path`, or None
76 to not check.
77 :param size: the size that the entry at `path` must have, or None
78 to not check.
79 :param mtime: the mtime that the entry at `path` must have, or None
80 to not check.
81 :param mode: the mode that the entry at `path` must have, or None
82 to not check.
83 :param linkname: the linkname that the entry at `path` must have,
84 or None to not check.
85 :param uid: the user id that the entry at `path` must have, or None
86 to not check.
87 :param gid: the group id that the entry at `path` must have, or None
88 to not check.
89 :param uname: the username that the entry at `path` must have, or
90 None to not check.
91 :param gname: the group name that the entry at `path` must have, or
92 None to not check.
93 :param content: the content that `path` must have when extracted,
94 or None to not check.
95 """
96 self.path = path
97 self.type = type
98 self.size = size
99 self.mtime = mtime
100 self.mode = mode
101 self.linkname = linkname
102 self.uid = uid
103 self.gid = gid
104 self.uname = uname
105 self.gname = gname
106 self.content = content
107
108 def match(self, tarball):
109 """Match a tarfile.TarFile against the expected values."""
110 if self.path not in tarball.getnames():
111 return TarfileMissingPathMismatch(tarball, self.path)
112 info = tarball.getmember(self.path)
113 for attr in (
114 "type", "size", "mtime", "mode", "linkname", "uid", "gid",
115 "uname", "gname"):
116 expected = getattr(self, attr, None)
117 if expected is not None:
118 actual = getattr(info, attr)
119 if expected != actual:
120 return TarfileWrongValueMismatch(
121 attr, tarball, self.path, expected, actual)
122 if self.content is not None:
123 actual = tarball.extractfile(self.path).read()
124 if actual != self.content:
125 return TarfileWrongValueMismatch(
126 "content", tarball, self.path, self.content, actual)
127 return None
128
129 def __str__(self):
130 return 'tarfile has file "%s"' % (self.path, )
0131
=== added file 'hwpack/testing.py'
--- hwpack/testing.py 1970-01-01 00:00:00 +0000
+++ hwpack/testing.py 2010-09-01 20:48:43 +0000
@@ -0,0 +1,39 @@
1from contextlib import contextmanager
2from StringIO import StringIO
3import tarfile
4
5from hwpack.better_tarfile import writeable_tarfile
6
7
8@contextmanager
9def test_tarfile(contents=[], **kwargs):
10 """Create a tarfile with the given contents, then re-open it for reading.
11
12 This context manager creates a tarfile with the given contents, then
13 reopens it for reading and yields it for use in a with block. When
14 the block ends the tarfile will be closed.
15
16 The contents can be specified as a list of tuples of (path, contents),
17 where if the path ends with '/' it is considered to be a directory and
18 the contents ignored.
19
20 :param contents: the contents to put in the tarball, defaults to the
21 empty list.
22 :type contents: a list of tuples of (str, str)
23 :param kwargs: keyword arguments for the better_tarfile.TarFile
24 constructor.
25 """
26 backing_file = StringIO()
27 with writeable_tarfile(backing_file, **kwargs) as tf:
28 for path, content in contents:
29 if path[-1] == "/":
30 tf.create_dir(path)
31 else:
32 tf.create_file_from_string(path, content)
33 if contents:
34 backing_file.seek(0)
35 tf = tarfile.TarFile.open(mode="r", fileobj=backing_file)
36 try:
37 yield tf
38 finally:
39 tf.close()
040
=== modified file 'hwpack/tests/__init__.py'
--- hwpack/tests/__init__.py 2010-09-01 20:48:43 +0000
+++ hwpack/tests/__init__.py 2010-09-01 20:48:43 +0000
@@ -3,6 +3,7 @@
3def test_suite():3def test_suite():
4 module_names = ['hwpack.tests.test_config',4 module_names = ['hwpack.tests.test_config',
5 'hwpack.tests.test_better_tarfile',5 'hwpack.tests.test_better_tarfile',
6 'hwpack.tests.test_tarfile_matchers',
6 ]7 ]
7 loader = unittest.TestLoader()8 loader = unittest.TestLoader()
8 suite = loader.loadTestsFromNames(module_names)9 suite = loader.loadTestsFromNames(module_names)
910
=== added file 'hwpack/tests/test_tarfile_matchers.py'
--- hwpack/tests/test_tarfile_matchers.py 1970-01-01 00:00:00 +0000
+++ hwpack/tests/test_tarfile_matchers.py 2010-09-01 20:48:43 +0000
@@ -0,0 +1,228 @@
1from StringIO import StringIO
2import tarfile
3
4from testtools import TestCase
5
6from hwpack.tarfile_matchers import (
7 TarfileHasFile,
8 TarfileMissingPathMismatch,
9 TarfileWrongValueMismatch,
10 )
11from hwpack.testing import test_tarfile
12
13
14class TarfileMissingPathMismatchTests(TestCase):
15
16 def test_describe(self):
17 mismatch = TarfileMissingPathMismatch("foo", "bar")
18 self.assertEqual('"foo" has no path "bar"', mismatch.describe())
19
20 def test_eq(self):
21 mismatch1 = TarfileMissingPathMismatch("foo", "bar")
22 mismatch2 = TarfileMissingPathMismatch("foo", "bar")
23 self.assertEqual(mismatch1, mismatch2)
24
25 def test_no_eq_different_tarball(self):
26 mismatch1 = TarfileMissingPathMismatch("foo", "bar")
27 mismatch2 = TarfileMissingPathMismatch("baz", "bar")
28 self.assertNotEqual(mismatch1, mismatch2)
29
30 def test_no_eq_different_path(self):
31 mismatch1 = TarfileMissingPathMismatch("foo", "bar")
32 mismatch2 = TarfileMissingPathMismatch("foo", "baz")
33 self.assertNotEqual(mismatch1, mismatch2)
34
35 def test_hash_equal(self):
36 mismatch1 = TarfileMissingPathMismatch("foo", "bar")
37 mismatch2 = TarfileMissingPathMismatch("foo", "bar")
38 self.assertEqual(hash(mismatch1), hash(mismatch2))
39
40 def test_different_tarball_different_hash(self):
41 mismatch1 = TarfileMissingPathMismatch("foo", "bar")
42 mismatch2 = TarfileMissingPathMismatch("baz", "bar")
43 self.assertNotEqual(hash(mismatch1), hash(mismatch2))
44
45 def test_different_path_different_hash(self):
46 mismatch1 = TarfileMissingPathMismatch("foo", "bar")
47 mismatch2 = TarfileMissingPathMismatch("foo", "baz")
48 self.assertNotEqual(hash(mismatch1), hash(mismatch2))
49
50
51class TarfileWrongTypeMismatchTests(TestCase):
52
53 def test_describe(self):
54 mismatch = TarfileWrongValueMismatch("type", "foo", "bar", 1, 2)
55 self.assertEqual(
56 'The path "bar" in "foo" has type 2, expected 1',
57 mismatch.describe())
58
59 def test_eq(self):
60 mismatch1 = TarfileWrongValueMismatch("type", "foo", "bar", 1, 2)
61 mismatch2 = TarfileWrongValueMismatch("type", "foo", "bar", 1, 2)
62 self.assertEqual(mismatch1, mismatch2)
63
64 def test_not_eq_different_attribute(self):
65 mismatch1 = TarfileWrongValueMismatch("type", "foo", "bar", 1, 2)
66 mismatch2 = TarfileWrongValueMismatch("size", "foo", "bar", 1, 2)
67 self.assertNotEqual(mismatch1, mismatch2)
68
69 def test_not_eq_different_tarball(self):
70 mismatch1 = TarfileWrongValueMismatch("type", "foo", "bar", 1, 2)
71 mismatch2 = TarfileWrongValueMismatch("type", "baz", "bar", 1, 2)
72 self.assertNotEqual(mismatch1, mismatch2)
73
74 def test_not_eq_different_path(self):
75 mismatch1 = TarfileWrongValueMismatch("type", "foo", "bar", 1, 2)
76 mismatch2 = TarfileWrongValueMismatch("type", "foo", "baz", 1, 2)
77 self.assertNotEqual(mismatch1, mismatch2)
78
79 def test_not_eq_different_expected(self):
80 mismatch1 = TarfileWrongValueMismatch("type", "foo", "bar", 1, 2)
81 mismatch2 = TarfileWrongValueMismatch("type", "foo", "bar", 3, 2)
82 self.assertNotEqual(mismatch1, mismatch2)
83
84 def test_not_eq_different_actual(self):
85 mismatch1 = TarfileWrongValueMismatch("type", "foo", "bar", 1, 2)
86 mismatch2 = TarfileWrongValueMismatch("type", "foo", "bar", 1, 3)
87 self.assertNotEqual(mismatch1, mismatch2)
88
89 def test_hash_equal(self):
90 mismatch1 = TarfileWrongValueMismatch("type", "foo", "bar", 1, 2)
91 mismatch2 = TarfileWrongValueMismatch("type", "foo", "bar", 1, 2)
92 self.assertEqual(hash(mismatch1), hash(mismatch2))
93
94 def test_different_attribute_different_hash(self):
95 mismatch1 = TarfileWrongValueMismatch("type", "foo", "bar", 1, 2)
96 mismatch2 = TarfileWrongValueMismatch("size", "foo", "bar", 1, 2)
97 self.assertNotEqual(hash(mismatch1), hash(mismatch2))
98
99 def test_different_tarball_different_hash(self):
100 mismatch1 = TarfileWrongValueMismatch("type", "foo", "bar", 1, 2)
101 mismatch2 = TarfileWrongValueMismatch("type", "baz", "bar", 1, 2)
102 self.assertNotEqual(hash(mismatch1), hash(mismatch2))
103
104 def test_different_path_different_hash(self):
105 mismatch1 = TarfileWrongValueMismatch("type", "foo", "bar", 1, 2)
106 mismatch2 = TarfileWrongValueMismatch("type", "foo", "baz", 1, 2)
107 self.assertNotEqual(hash(mismatch1), hash(mismatch2))
108
109 def test_different_expected_different_hash(self):
110 mismatch1 = TarfileWrongValueMismatch("type", "foo", "bar", 1, 2)
111 mismatch2 = TarfileWrongValueMismatch("type", "foo", "bar", 3, 2)
112 self.assertNotEqual(hash(mismatch1), hash(mismatch2))
113
114 def test_different_actual_different_hash(self):
115 mismatch1 = TarfileWrongValueMismatch("type", "foo", "bar", 1, 2)
116 mismatch2 = TarfileWrongValueMismatch("type", "foo", "bar", 1, 3)
117 self.assertNotEqual(hash(mismatch1), hash(mismatch2))
118
119
120class TarfileHasFileTests(TestCase):
121
122 def test_str(self):
123 matcher = TarfileHasFile("foo")
124 self.assertEqual('tarfile has file "foo"', str(matcher))
125
126 def test_matches(self):
127 backing_file = StringIO()
128 with test_tarfile(contents=[("foo", "")]) as tf:
129 matcher = TarfileHasFile("foo")
130 self.assertIs(None, matcher.match(tf))
131
132 def test_mismatches_missing_path(self):
133 backing_file = StringIO()
134 with test_tarfile() as tf:
135 matcher = TarfileHasFile("foo")
136 mismatch = matcher.match(tf)
137 self.assertIsInstance(mismatch, TarfileMissingPathMismatch)
138 self.assertEqual(TarfileMissingPathMismatch(tf, "foo"), mismatch)
139
140 def assertValueMismatch(self, mismatch, tarball, path, attribute,
141 expected, actual):
142 self.assertIsInstance(mismatch, TarfileWrongValueMismatch)
143 expected_mismatch = TarfileWrongValueMismatch(
144 attribute, tarball, path, expected, actual)
145 self.assertEqual(expected_mismatch, mismatch)
146
147 def test_mismatches_wrong_type(self):
148 backing_file = StringIO()
149 with test_tarfile(contents=[("foo", "")]) as tf:
150 matcher = TarfileHasFile("foo", type=tarfile.DIRTYPE)
151 mismatch = matcher.match(tf)
152 self.assertValueMismatch(
153 mismatch, tf, "foo", "type", tarfile.DIRTYPE,
154 tarfile.REGTYPE)
155
156 def test_mismatches_wrong_size(self):
157 backing_file = StringIO()
158 with test_tarfile(contents=[("foo", "")]) as tf:
159 matcher = TarfileHasFile("foo", size=1235)
160 mismatch = matcher.match(tf)
161 self.assertValueMismatch(
162 mismatch, tf, "foo", "size", 1235, 0)
163
164 def test_mismatches_wrong_mtime(self):
165 backing_file = StringIO()
166 with test_tarfile(contents=[("foo", "")], default_mtime=12345) as tf:
167 matcher = TarfileHasFile("foo", mtime=54321)
168 mismatch = matcher.match(tf)
169 self.assertValueMismatch(
170 mismatch, tf, "foo", "mtime", 54321, 12345)
171
172 def test_mismatches_wrong_mode(self):
173 backing_file = StringIO()
174 with test_tarfile(contents=[("foo", "")]) as tf:
175 matcher = TarfileHasFile("foo", mode=0000)
176 mismatch = matcher.match(tf)
177 self.assertValueMismatch(
178 mismatch, tf, "foo", "mode", 0000, 0644)
179
180 def test_mismatches_wrong_linkname(self):
181 backing_file = StringIO()
182 with test_tarfile(contents=[("foo", "")]) as tf:
183 matcher = TarfileHasFile("foo", linkname="somelink")
184 mismatch = matcher.match(tf)
185 self.assertValueMismatch(
186 mismatch, tf, "foo", "linkname", "somelink", "")
187
188 def test_mismatches_wrong_uid(self):
189 backing_file = StringIO()
190 with test_tarfile(contents=[("foo", "")], default_uid=100) as tf:
191 matcher = TarfileHasFile("foo", uid=99)
192 mismatch = matcher.match(tf)
193 self.assertValueMismatch(
194 mismatch, tf, "foo", "uid", 99, 100)
195
196 def test_mismatches_wrong_gid(self):
197 backing_file = StringIO()
198 with test_tarfile(contents=[("foo", "")], default_gid=100) as tf:
199 matcher = TarfileHasFile("foo", gid=99)
200 mismatch = matcher.match(tf)
201 self.assertValueMismatch(
202 mismatch, tf, "foo", "gid", 99, 100)
203
204 def test_mismatches_wrong_uname(self):
205 backing_file = StringIO()
206 with test_tarfile(
207 contents=[("foo", "")], default_uname="someuser") as tf:
208 matcher = TarfileHasFile("foo", uname="otheruser")
209 mismatch = matcher.match(tf)
210 self.assertValueMismatch(
211 mismatch, tf, "foo", "uname", "otheruser", "someuser")
212
213 def test_mismatches_wrong_gname(self):
214 backing_file = StringIO()
215 with test_tarfile(
216 contents=[("foo", "")], default_gname="somegroup") as tf:
217 matcher = TarfileHasFile("foo", gname="othergroup")
218 mismatch = matcher.match(tf)
219 self.assertValueMismatch(
220 mismatch, tf, "foo", "gname", "othergroup", "somegroup")
221
222 def test_mismatches_wrong_content(self):
223 backing_file = StringIO()
224 with test_tarfile(contents=[("foo", "somecontent")]) as tf:
225 matcher = TarfileHasFile("foo", content="othercontent")
226 mismatch = matcher.match(tf)
227 self.assertValueMismatch(
228 mismatch, tf, "foo", "content", "othercontent", "somecontent")

Subscribers

People subscribed via source and target branches