Merge lp:~thumper/juju-core/moar-checkers into lp:~go-bot/juju-core/trunk
- moar-checkers
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Tim Penhey |
Approved revision: | no longer in the source branch. |
Merged at revision: | 1311 |
Proposed branch: | lp:~thumper/juju-core/moar-checkers |
Merge into: | lp:~go-bot/juju-core/trunk |
Diff against target: |
402 lines (+368/-0) 7 files modified
testing/checkers/bool.go (+35/-0) testing/checkers/bool_test.go (+35/-0) testing/checkers/checker_test.go (+27/-0) testing/checkers/file.go (+98/-0) testing/checkers/file_test.go (+97/-0) testing/checkers/relop.go (+52/-0) testing/checkers/relop_test.go (+24/-0) |
To merge this branch: | bzr merge lp:~thumper/juju-core/moar-checkers |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Juju Engineering | Pending | ||
Review via email: mp+170227@code.launchpad.net |
Commit message
Some useful checkers.
Also some testing functions used later, with their own test.
Description of the change
Some useful checkers.
Also some testing functions used later, with their own test.
Tim Penhey (thumper) wrote : | # |
Tim Penhey (thumper) wrote : | # |
Please take a look.
Ian Booth (wallyworld) wrote : | # |
LGTM
https:/
File testing/
https:/
testing/
If possible, also add the value and type to this error message, like for
the other greaterThan checker
Tim Penhey (thumper) wrote : | # |
Please take a look.
https:/
File testing/
https:/
testing/
On 2013/06/19 00:50:23, wallyworld wrote:
> If possible, also add the value and type to this error message, like
for the
> other greaterThan checker
Done.
John A Meinel (jameinel) wrote : | # |
LGTM
I have some comments about it, but the only one I really want to see
fixed is s/recieved/
https:/
File testing/
https:/
testing/
'received' which I think means you need to update the checker as well.
Would it be better to have the message:
expected "true" (type bool), received string: "foo"
Or to make the two halves consistent:
`expected type bool "true", received string "foo"`
I don't think you need the colon and to quote the string. Though I'm
soft on that.
Mostly, I'd like that if we are going to explicitly list what we
obtained, we should explicitly list what we expected.
https:/
File testing/
https:/
testing/
I wonder if spelling this GreaterThan is best. Mostly because I'm
thinking of the permutations:
GreaterThanOrEq
Which is pretty commonly abbreviated GTE.
I'm also hoping the infrastructure can give us a nice "Between" (though
again you have to watch out for whether one or both ends are open or
closed a < x < b vs a <= x < b, etc)
https:/
File testing/file.go (right):
https:/
testing/file.go:17: }
Couldn't this be done as a checker too?
c.Assert(filename, IsNonEmptyFile) ?
I'm fine with this helper, but checkers do tend to be slightly more
consistent with how we do assertions.
Also, it lets you use "c.Check" when appropriate, rather than always
Assert (fail the test but apply more checks vs fail immediately and
stop).
Tim Penhey (thumper) wrote : | # |
https:/
File testing/
https:/
testing/
On 2013/06/19 04:32:28, jameinel wrote:
> 'received' which I think means you need to update the checker as well.
> Would it be better to have the message:
> expected "true" (type bool), received string: "foo"
> Or to make the two halves consistent:
> `expected type bool "true", received string "foo"`
> I don't think you need the colon and to quote the string. Though I'm
soft on
> that.
> Mostly, I'd like that if we are going to explicitly list what we
obtained, we
> should explicitly list what we expected.
The quotes are part of %#v, so if we had
42, that would become:
int:42
Fixed received spelling, went for:
`expected bool:true, received string:"foo"`
and added a test
`expected bool:true, received int:42`
https:/
File testing/
https:/
testing/
On 2013/06/19 04:32:28, jameinel wrote:
> I wonder if spelling this GreaterThan is best. Mostly because I'm
thinking of
> the permutations:
> GreaterThanOrEq
> Which is pretty commonly abbreviated GTE.
> I'm also hoping the infrastructure can give us a nice "Between"
(though again
> you have to watch out for whether one or both ends are open or closed
a < x < b
> vs a <= x < b, etc)
python testtools uses GreaterThan too :)
Yes, you can do a between, and I do have one for time.
https:/
File testing/file.go (right):
https:/
testing/file.go:17: }
On 2013/06/19 04:32:28, jameinel wrote:
> Couldn't this be done as a checker too?
> c.Assert(filename, IsNonEmptyFile) ?
> I'm fine with this helper, but checkers do tend to be slightly more
consistent
> with how we do assertions.
> Also, it lets you use "c.Check" when appropriate, rather than always
Assert
> (fail the test but apply more checks vs fail immediately and stop).
Yes... although I had trouble thinking of it at first.
Will fix.
Frank Mueller (themue) wrote : | # |
LGTM, thanks for moving it into an own CL.
https:/
File testing/
https:/
testing/
Not needed, for the other checkers too. But please add godoc comments
for the exported symbols like IsTrue or IsFalse.
Roger Peppe (rogpeppe) wrote : | # |
https:/
File testing/
https:/
testing/
bool, recieved %s:%#v", value.Kind(), params[0])
return false, fmt.Sprintf(
value.Type())
?
(gocheck itself will print the value for you - no need to print it
again).
https:/
File testing/
https:/
testing/
%s:%#v not supported", p0value.Kind(), params[0])
fmt.Sprintf(
?
https:/
File testing/
https:/
testing/
i'm not sure we gain much by having a separate test file for each kind
of checker. i'd put them all in one file and test suite
(checkers_test.go), making it easy to scan 'em all at once.
https:/
testing/
"launchpad.
I object to the spreading use of import to .
gocheck itself is bad enough.
Rather than polluting the local package name space,
could we choose a suitable short package identifier
or alias please? Or just use "checkers" itself.
if we adopted the fairly standard abbreviations
that john suggests, we could have something like:
c.Assert(45, checkers.GT, 42)
which isn't too much typing, and it's obvious where
the names come from.
Preview Diff
1 | === added file 'testing/checkers/bool.go' |
2 | --- testing/checkers/bool.go 1970-01-01 00:00:00 +0000 |
3 | +++ testing/checkers/bool.go 2013-06-19 05:41:26 +0000 |
4 | @@ -0,0 +1,35 @@ |
5 | +// Copyright 2013 Canonical Ltd. |
6 | +// Licensed under the AGPLv3, see LICENCE file for details. |
7 | + |
8 | +package checkers |
9 | + |
10 | +import ( |
11 | + "fmt" |
12 | + "reflect" |
13 | + |
14 | + . "launchpad.net/gocheck" |
15 | +) |
16 | + |
17 | +// IsTrue checker |
18 | + |
19 | +type isTrueChecker struct { |
20 | + *CheckerInfo |
21 | +} |
22 | + |
23 | +var IsTrue Checker = &isTrueChecker{ |
24 | + &CheckerInfo{Name: "IsTrue", Params: []string{"obtained"}}, |
25 | +} |
26 | + |
27 | +var IsFalse Checker = Not(IsTrue) |
28 | + |
29 | +func (checker *isTrueChecker) Check(params []interface{}, names []string) (result bool, error string) { |
30 | + |
31 | + value := reflect.ValueOf(params[0]) |
32 | + |
33 | + switch value.Kind() { |
34 | + case reflect.Bool: |
35 | + return value.Bool(), "" |
36 | + } |
37 | + |
38 | + return false, fmt.Sprintf("expected bool:true, received %s:%#v", value.Kind(), params[0]) |
39 | +} |
40 | |
41 | === added file 'testing/checkers/bool_test.go' |
42 | --- testing/checkers/bool_test.go 1970-01-01 00:00:00 +0000 |
43 | +++ testing/checkers/bool_test.go 2013-06-19 05:41:26 +0000 |
44 | @@ -0,0 +1,35 @@ |
45 | +// Copyright 2013 Canonical Ltd. |
46 | +// Licensed under the AGPLv3, see LICENCE file for details. |
47 | + |
48 | +package checkers_test |
49 | + |
50 | +import ( |
51 | + . "launchpad.net/gocheck" |
52 | + . "launchpad.net/juju-core/testing/checkers" |
53 | +) |
54 | + |
55 | +type BoolSuite struct{} |
56 | + |
57 | +var _ = Suite(&BoolSuite{}) |
58 | + |
59 | +func (s *BoolSuite) TestIsTrue(c *C) { |
60 | + c.Assert(true, IsTrue) |
61 | + c.Assert(false, Not(IsTrue)) |
62 | + |
63 | + result, msg := IsTrue.Check([]interface{}{false}, nil) |
64 | + c.Assert(result, Equals, false) |
65 | + c.Assert(msg, Equals, "") |
66 | + |
67 | + result, msg = IsTrue.Check([]interface{}{"foo"}, nil) |
68 | + c.Assert(result, Equals, false) |
69 | + c.Check(msg, Equals, `expected bool:true, received string:"foo"`) |
70 | + |
71 | + result, msg = IsTrue.Check([]interface{}{42}, nil) |
72 | + c.Assert(result, Equals, false) |
73 | + c.Assert(msg, Equals, `expected bool:true, received int:42`) |
74 | +} |
75 | + |
76 | +func (s *BoolSuite) TestIsFalse(c *C) { |
77 | + c.Assert(false, IsFalse) |
78 | + c.Assert(true, Not(IsFalse)) |
79 | +} |
80 | |
81 | === added file 'testing/checkers/checker_test.go' |
82 | --- testing/checkers/checker_test.go 1970-01-01 00:00:00 +0000 |
83 | +++ testing/checkers/checker_test.go 2013-06-19 05:41:26 +0000 |
84 | @@ -0,0 +1,27 @@ |
85 | +// Copyright 2013 Canonical Ltd. |
86 | +// Licensed under the AGPLv3, see LICENCE file for details. |
87 | + |
88 | +package checkers_test |
89 | + |
90 | +import ( |
91 | + "testing" |
92 | + |
93 | + . "launchpad.net/gocheck" |
94 | + . "launchpad.net/juju-core/testing/checkers" |
95 | +) |
96 | + |
97 | +func Test(t *testing.T) { TestingT(t) } |
98 | + |
99 | +type CheckerSuite struct{} |
100 | + |
101 | +var _ = Suite(&CheckerSuite{}) |
102 | + |
103 | +func (s *CheckerSuite) TestHasPrefix(c *C) { |
104 | + c.Assert("foo bar", HasPrefix, "foo") |
105 | + c.Assert("foo bar", Not(HasPrefix), "omg") |
106 | +} |
107 | + |
108 | +func (s *CheckerSuite) TestHasSuffix(c *C) { |
109 | + c.Assert("foo bar", HasSuffix, "bar") |
110 | + c.Assert("foo bar", Not(HasSuffix), "omg") |
111 | +} |
112 | |
113 | === added file 'testing/checkers/file.go' |
114 | --- testing/checkers/file.go 1970-01-01 00:00:00 +0000 |
115 | +++ testing/checkers/file.go 2013-06-19 05:41:26 +0000 |
116 | @@ -0,0 +1,98 @@ |
117 | +// Copyright 2013 Canonical Ltd. |
118 | +// Licensed under the AGPLv3, see LICENCE file for details. |
119 | + |
120 | +package checkers |
121 | + |
122 | +import ( |
123 | + "fmt" |
124 | + "os" |
125 | + "reflect" |
126 | + |
127 | + . "launchpad.net/gocheck" |
128 | +) |
129 | + |
130 | +// IsNonEmptyFile checker |
131 | + |
132 | +type isNonEmptyFileChecker struct { |
133 | + *CheckerInfo |
134 | +} |
135 | + |
136 | +var IsNonEmptyFile Checker = &isNonEmptyFileChecker{ |
137 | + &CheckerInfo{Name: "IsNonEmptyFile", Params: []string{"obtained"}}, |
138 | +} |
139 | + |
140 | +func (checker *isNonEmptyFileChecker) Check(params []interface{}, names []string) (result bool, error string) { |
141 | + filename, isString := stringOrStringer(params[0]) |
142 | + if isString { |
143 | + fileInfo, err := os.Stat(filename) |
144 | + if os.IsNotExist(err) { |
145 | + return false, fmt.Sprintf("%s does not exist", filename) |
146 | + } else if err != nil { |
147 | + return false, fmt.Sprintf("other stat error: %v", err) |
148 | + } |
149 | + if fileInfo.Size() > 0 { |
150 | + return true, "" |
151 | + } else { |
152 | + return false, fmt.Sprintf("%s is empty", filename) |
153 | + } |
154 | + } |
155 | + |
156 | + value := reflect.ValueOf(params[0]) |
157 | + return false, fmt.Sprintf("obtained value is not a string and has no .String(), %s:%#v", value.Kind(), params[0]) |
158 | +} |
159 | + |
160 | +// IsDirectory checker |
161 | + |
162 | +type isDirectoryChecker struct { |
163 | + *CheckerInfo |
164 | +} |
165 | + |
166 | +var IsDirectory Checker = &isDirectoryChecker{ |
167 | + &CheckerInfo{Name: "IsDirectory", Params: []string{"obtained"}}, |
168 | +} |
169 | + |
170 | +func (checker *isDirectoryChecker) Check(params []interface{}, names []string) (result bool, error string) { |
171 | + path, isString := stringOrStringer(params[0]) |
172 | + if isString { |
173 | + fileInfo, err := os.Stat(path) |
174 | + if os.IsNotExist(err) { |
175 | + return false, fmt.Sprintf("%s does not exist", path) |
176 | + } else if err != nil { |
177 | + return false, fmt.Sprintf("other stat error: %v", err) |
178 | + } |
179 | + if fileInfo.IsDir() { |
180 | + return true, "" |
181 | + } else { |
182 | + return false, fmt.Sprintf("%s is not a directory", path) |
183 | + } |
184 | + } |
185 | + |
186 | + value := reflect.ValueOf(params[0]) |
187 | + return false, fmt.Sprintf("obtained value is not a string and has no .String(), %s:%#v", value.Kind(), params[0]) |
188 | +} |
189 | + |
190 | +// DoesNotExist checker makes sure the path specified doesn't exist. |
191 | + |
192 | +type doesNotExistChecker struct { |
193 | + *CheckerInfo |
194 | +} |
195 | + |
196 | +var DoesNotExist Checker = &doesNotExistChecker{ |
197 | + &CheckerInfo{Name: "DoesNotExist", Params: []string{"obtained"}}, |
198 | +} |
199 | + |
200 | +func (checker *doesNotExistChecker) Check(params []interface{}, names []string) (result bool, error string) { |
201 | + path, isString := stringOrStringer(params[0]) |
202 | + if isString { |
203 | + _, err := os.Stat(path) |
204 | + if os.IsNotExist(err) { |
205 | + return true, "" |
206 | + } else if err != nil { |
207 | + return false, fmt.Sprintf("other stat error: %v", err) |
208 | + } |
209 | + return false, fmt.Sprintf("%s exists", path) |
210 | + } |
211 | + |
212 | + value := reflect.ValueOf(params[0]) |
213 | + return false, fmt.Sprintf("obtained value is not a string and has no .String(), %s:%#v", value.Kind(), params[0]) |
214 | +} |
215 | |
216 | === added file 'testing/checkers/file_test.go' |
217 | --- testing/checkers/file_test.go 1970-01-01 00:00:00 +0000 |
218 | +++ testing/checkers/file_test.go 2013-06-19 05:41:26 +0000 |
219 | @@ -0,0 +1,97 @@ |
220 | +// Copyright 2013 Canonical Ltd. |
221 | +// Licensed under the AGPLv3, see LICENCE file for details. |
222 | + |
223 | +package checkers_test |
224 | + |
225 | +import ( |
226 | + "fmt" |
227 | + "io/ioutil" |
228 | + "path/filepath" |
229 | + |
230 | + . "launchpad.net/gocheck" |
231 | + . "launchpad.net/juju-core/testing/checkers" |
232 | +) |
233 | + |
234 | +type FileSuite struct{} |
235 | + |
236 | +var _ = Suite(&FileSuite{}) |
237 | + |
238 | +func (s *FileSuite) TestIsNonEmptyFile(c *C) { |
239 | + file, err := ioutil.TempFile(c.MkDir(), "") |
240 | + c.Assert(err, IsNil) |
241 | + fmt.Fprintf(file, "something") |
242 | + file.Close() |
243 | + |
244 | + c.Assert(file.Name(), IsNonEmptyFile) |
245 | +} |
246 | + |
247 | +func (s *FileSuite) TestIsNonEmptyFileWithEmptyFile(c *C) { |
248 | + file, err := ioutil.TempFile(c.MkDir(), "") |
249 | + c.Assert(err, IsNil) |
250 | + file.Close() |
251 | + |
252 | + result, message := IsNonEmptyFile.Check([]interface{}{file.Name()}, nil) |
253 | + c.Assert(result, IsFalse) |
254 | + c.Assert(message, Equals, file.Name()+" is empty") |
255 | +} |
256 | + |
257 | +func (s *FileSuite) TestIsNonEmptyFileWithMissingFile(c *C) { |
258 | + name := filepath.Join(c.MkDir(), "missing") |
259 | + |
260 | + result, message := IsNonEmptyFile.Check([]interface{}{name}, nil) |
261 | + c.Assert(result, IsFalse) |
262 | + c.Assert(message, Equals, name+" does not exist") |
263 | +} |
264 | + |
265 | +func (s *FileSuite) TestIsNonEmptyFileWithNumber(c *C) { |
266 | + result, message := IsNonEmptyFile.Check([]interface{}{42}, nil) |
267 | + c.Assert(result, IsFalse) |
268 | + c.Assert(message, Equals, "obtained value is not a string and has no .String(), int:42") |
269 | +} |
270 | + |
271 | +func (s *FileSuite) TestIsDirectory(c *C) { |
272 | + dir := c.MkDir() |
273 | + c.Assert(dir, IsDirectory) |
274 | +} |
275 | + |
276 | +func (s *FileSuite) TestIsDirectoryMissing(c *C) { |
277 | + absentDir := filepath.Join(c.MkDir(), "foo") |
278 | + |
279 | + result, message := IsDirectory.Check([]interface{}{absentDir}, nil) |
280 | + c.Assert(result, IsFalse) |
281 | + c.Assert(message, Equals, absentDir+" does not exist") |
282 | +} |
283 | + |
284 | +func (s *FileSuite) TestIsDirectoryWithFile(c *C) { |
285 | + file, err := ioutil.TempFile(c.MkDir(), "") |
286 | + c.Assert(err, IsNil) |
287 | + file.Close() |
288 | + |
289 | + result, message := IsDirectory.Check([]interface{}{file.Name()}, nil) |
290 | + c.Assert(result, IsFalse) |
291 | + c.Assert(message, Equals, file.Name()+" is not a directory") |
292 | +} |
293 | + |
294 | +func (s *FileSuite) TestIsDirectoryWithNumber(c *C) { |
295 | + result, message := IsDirectory.Check([]interface{}{42}, nil) |
296 | + c.Assert(result, IsFalse) |
297 | + c.Assert(message, Equals, "obtained value is not a string and has no .String(), int:42") |
298 | +} |
299 | + |
300 | +func (s *FileSuite) TestDoesNotExist(c *C) { |
301 | + absentDir := filepath.Join(c.MkDir(), "foo") |
302 | + c.Assert(absentDir, DoesNotExist) |
303 | +} |
304 | + |
305 | +func (s *FileSuite) TestDoesNotExistWithPath(c *C) { |
306 | + dir := c.MkDir() |
307 | + result, message := DoesNotExist.Check([]interface{}{dir}, nil) |
308 | + c.Assert(result, IsFalse) |
309 | + c.Assert(message, Equals, dir+" exists") |
310 | +} |
311 | + |
312 | +func (s *FileSuite) TestDoesNotExistWithNumber(c *C) { |
313 | + result, message := DoesNotExist.Check([]interface{}{42}, nil) |
314 | + c.Assert(result, IsFalse) |
315 | + c.Assert(message, Equals, "obtained value is not a string and has no .String(), int:42") |
316 | +} |
317 | |
318 | === added file 'testing/checkers/relop.go' |
319 | --- testing/checkers/relop.go 1970-01-01 00:00:00 +0000 |
320 | +++ testing/checkers/relop.go 2013-06-19 05:41:26 +0000 |
321 | @@ -0,0 +1,52 @@ |
322 | +// Copyright 2013 Canonical Ltd. |
323 | +// Licensed under the AGPLv3, see LICENCE file for details. |
324 | + |
325 | +package checkers |
326 | + |
327 | +import ( |
328 | + "fmt" |
329 | + "reflect" |
330 | + |
331 | + . "launchpad.net/gocheck" |
332 | +) |
333 | + |
334 | +// GreaterThan checker |
335 | + |
336 | +type greaterThanChecker struct { |
337 | + *CheckerInfo |
338 | +} |
339 | + |
340 | +var GreaterThan Checker = &greaterThanChecker{ |
341 | + &CheckerInfo{Name: "GreaterThan", Params: []string{"obtained", "expected"}}, |
342 | +} |
343 | + |
344 | +func (checker *greaterThanChecker) Check(params []interface{}, names []string) (result bool, error string) { |
345 | + defer func() { |
346 | + if v := recover(); v != nil { |
347 | + result = false |
348 | + error = fmt.Sprint(v) |
349 | + } |
350 | + }() |
351 | + |
352 | + p0value := reflect.ValueOf(params[0]) |
353 | + p1value := reflect.ValueOf(params[1]) |
354 | + switch p0value.Kind() { |
355 | + case reflect.Int, |
356 | + reflect.Int8, |
357 | + reflect.Int16, |
358 | + reflect.Int32, |
359 | + reflect.Int64: |
360 | + return p0value.Int() > p1value.Int(), "" |
361 | + case reflect.Uint, |
362 | + reflect.Uint8, |
363 | + reflect.Uint16, |
364 | + reflect.Uint32, |
365 | + reflect.Uint64: |
366 | + return p0value.Uint() > p1value.Uint(), "" |
367 | + case reflect.Float32, |
368 | + reflect.Float64: |
369 | + return p0value.Float() > p1value.Float(), "" |
370 | + default: |
371 | + } |
372 | + return false, fmt.Sprintf("obtained value %s:%#v not supported", p0value.Kind(), params[0]) |
373 | +} |
374 | |
375 | === added file 'testing/checkers/relop_test.go' |
376 | --- testing/checkers/relop_test.go 1970-01-01 00:00:00 +0000 |
377 | +++ testing/checkers/relop_test.go 2013-06-19 05:41:26 +0000 |
378 | @@ -0,0 +1,24 @@ |
379 | +// Copyright 2013 Canonical Ltd. |
380 | +// Licensed under the AGPLv3, see LICENCE file for details. |
381 | + |
382 | +package checkers_test |
383 | + |
384 | +import ( |
385 | + . "launchpad.net/gocheck" |
386 | + . "launchpad.net/juju-core/testing/checkers" |
387 | +) |
388 | + |
389 | +type RelopSuite struct{} |
390 | + |
391 | +var _ = Suite(&RelopSuite{}) |
392 | + |
393 | +func (s *RelopSuite) TestGreaterThan(c *C) { |
394 | + c.Assert(45, GreaterThan, 42) |
395 | + c.Assert(2.25, GreaterThan, 1.0) |
396 | + c.Assert(42, Not(GreaterThan), 42) |
397 | + c.Assert(10, Not(GreaterThan), 42) |
398 | + |
399 | + result, msg := GreaterThan.Check([]interface{}{"Hello", "World"}, nil) |
400 | + c.Assert(result, IsFalse) |
401 | + c.Assert(msg, Equals, `obtained value string:"Hello" not supported`) |
402 | +} |
Reviewers: mp+170227_ code.launchpad. net,
Message:
Please take a look.
Description:
Some useful checkers.
Also some testing functions used later, with their own test.
https:/ /code.launchpad .net/~thumper/ juju-core/ moar-checkers/ +merge/ 170227
(do not edit description out of merge proposal)
Please review this at https:/ /codereview. appspot. com/10395044/
Affected files: checkers/ bool.go checkers/ bool_test. go checkers/ checker_ test.go checkers/ relop.go checkers/ relop_test. go file_test. go
A [revision details]
A testing/
A testing/
A testing/
A testing/
A testing/
A testing/file.go
A testing/