Merge lp:~jameinel/juju-core/bad-formatting-1216285 into lp:~go-bot/juju-core/trunk

Proposed by John A Meinel
Status: Merged
Approved by: John A Meinel
Approved revision: no longer in the source branch.
Merged at revision: 1723
Proposed branch: lp:~jameinel/juju-core/bad-formatting-1216285
Merge into: lp:~go-bot/juju-core/trunk
Diff against target: 176 lines (+137/-1)
4 files modified
environs/tools/tools.go (+1/-1)
environs/tools/tools_test.go (+19/-0)
testing/checkers/log.go (+60/-0)
testing/checkers/log_test.go (+57/-0)
To merge this branch: bzr merge lp:~jameinel/juju-core/bad-formatting-1216285
Reviewer Review Type Date Requested Status
Juju Engineering Pending
Review via email: mp+181983@code.launchpad.net

Commit message

environs/tools: print out the correct version

Bug #1216285 is because we were miss-formatting a line about what
version we were filtering. It turns out that while we were printing out
an int as "%s" we were also not priting out what the filter would
actually match. (If you pass a version in that field, we match the exact
version, not just the major version.)

In the process, I realized we don't have much infrastructure for testing
what log messages we are writing, so I added a LogMatches checker. I
think it is something that actually fits better inside the loggo
package, but I wanted to give it some real experience before I tried to
get it into upstream.

https://codereview.appspot.com/12744051/

Description of the change

environs/tools: print out the correct version

Bug #1216285 is because we were miss-formatting a line about what
version we were filtering. It turns out that while we were printing out
an int as "%s" we were also not priting out what the filter would
actually match. (If you pass a version in that field, we match the exact
version, not just the major version.)

In the process, I realized we don't have much infrastructure for testing
what log messages we are writing, so I added a LogMatches checker. I
think it is something that actually fits better inside the loggo
package, but I wanted to give it some real experience before I tried to
get it into upstream.

https://codereview.appspot.com/12744051/

To post a comment you must log in.
Revision history for this message
John A Meinel (jameinel) wrote :

Reviewers: mp+181983_code.launchpad.net,

Message:
Please take a look.

Description:
environs/tools: print out the correct version

Bug #1216285 is because we were miss-formatting a line about what
version we were filtering. It turns out that while we were printing out
an int as "%s" we were also not priting out what the filter would
actually match. (If you pass a version in that field, we match the exact
version, not just the major version.)

In the process, I realized we don't have much infrastructure for testing
what log messages we are writing, so I added a LogMatches checker. I
think it is something that actually fits better inside the loggo
package, but I wanted to give it some real experience before I tried to
get it into upstream.

https://code.launchpad.net/~jameinel/juju-core/bad-formatting-1216285/+merge/181983

(do not edit description out of merge proposal)

Please review this at https://codereview.appspot.com/12744051/

Affected files:
   A [revision details]
   M environs/tools/tools.go
   M environs/tools/tools_test.go
   A testing/checkers/log.go
   A testing/checkers/log_test.go

Revision history for this message
Roger Peppe (rogpeppe) wrote :

Thanks for the log fix - it had niggled at me but I hadn't filed a bug
or fixed it (except in passing in an outstanding CL).

I have some thoughts on the log checker - what do you think?

https://codereview.appspot.com/12744051/diff/1/environs/tools/tools.go
File environs/tools/tools.go (right):

https://codereview.appspot.com/12744051/diff/1/environs/tools/tools.go#newcode33
environs/tools/tools.go:33: logger.Infof("filtering tools by version:
%s", filter.Number.String())
s/.String()//

https://codereview.appspot.com/12744051/diff/1/testing/checkers/log.go
File testing/checkers/log.go (right):

https://codereview.appspot.com/12744051/diff/1/testing/checkers/log.go#newcode41
testing/checkers/log.go:41: return reflect.DeepEqual(obtained,
params[1]), ""
I think this (and the below test for []string
should be a little more sophisticated and
allow other (unknown) messages to intersperse the
ones we're checking for.

That will make our tests less fragile when extra logging
messages are added, but still allow us to check
that the log produced at least the required messages.

Another thought is to allow specification by
regexp (which actually would fit the "Match" name
convention) so that it's possible to check
log messages that print pointers or other
transient identifiers.

How about simplifying things by saying
that we *always* check against a []string
containing regexps, with the obtained values
being formed in a consistent way before matching.

That way we can easily make the
matching strings ignore the level
if we want, but we don't have to muck about
with dynamic types so much.

c.Assert(c.Check(tw.Log, jc.LogMatches, []string{
        `INFO: reading tools with major version 1`,
        `.*: filtering tools by version: 1\.2\.3`, // ignore level
        etc,
}

https://codereview.appspot.com/12744051/

Revision history for this message
William Reade (fwereade) wrote :

LGTM. Rog's concerns are reasonable, and a better log checker might be
something we need one day, but I think that day is the day the fragility
has an actual impact. I wouldn't expect to see logs checked except in
very narrow situations, so that day may take a while to arrive.

https://codereview.appspot.com/12744051/diff/1/testing/checkers/log.go
File testing/checkers/log.go (right):

https://codereview.appspot.com/12744051/diff/1/testing/checkers/log.go#newcode13
testing/checkers/log.go:13: type SimpleMessages struct {
SimpleMessage?

https://codereview.appspot.com/12744051/diff/1/testing/checkers/log.go#newcode51
testing/checkers/log.go:51: return false, "bork bork bork"
Can we just stop before this line, or can the compiler not detect that
there's no way out of the switch? I think we should panic("unreachable")
rather than returning "bork bork bork" regardless...

https://codereview.appspot.com/12744051/

Revision history for this message
John A Meinel (jameinel) wrote :

Please take a look.

https://codereview.appspot.com/12744051/diff/1/environs/tools/tools.go
File environs/tools/tools.go (right):

https://codereview.appspot.com/12744051/diff/1/environs/tools/tools.go#newcode33
environs/tools/tools.go:33: logger.Infof("filtering tools by version:
%s", filter.Number.String())
On 2013/08/25 14:33:54, rog wrote:
> s/.String()//

Done.

https://codereview.appspot.com/12744051/diff/1/testing/checkers/log.go
File testing/checkers/log.go (right):

https://codereview.appspot.com/12744051/diff/1/testing/checkers/log.go#newcode13
testing/checkers/log.go:13: type SimpleMessages struct {
On 2013/08/28 10:58:24, fwereade wrote:
> SimpleMessage?

Done.

https://codereview.appspot.com/12744051/diff/1/testing/checkers/log.go#newcode41
testing/checkers/log.go:41: return reflect.DeepEqual(obtained,
params[1]), ""

...

> c.Assert(c.Check(tw.Log, jc.LogMatches, []string{
> `INFO: reading tools with major version 1`,
> `.*: filtering tools by version: 1\.2\.3`, // ignore level
> etc,
> }

I do like this a bit, but it is a fair effort to code, and I'd rather
see smart stuff land in loggo itself. I've put forward a request to get
feedback from Tim about how that would look. (haven't heard back yet).

https://codereview.appspot.com/12744051/diff/1/testing/checkers/log.go#newcode51
testing/checkers/log.go:51: return false, "bork bork bork"
On 2013/08/28 10:58:24, fwereade wrote:
> Can we just stop before this line, or can the compiler not detect that
there's
> no way out of the switch? I think we should panic("unreachable")
rather than
> returning "bork bork bork" regardless...

Just old code that I hadn't cleaned out. default() should be hitting
this case. Removed.

https://codereview.appspot.com/12744051/

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'environs/tools/tools.go'
2--- environs/tools/tools.go 2013-08-26 03:34:00 +0000
3+++ environs/tools/tools.go 2013-08-28 12:04:57 +0000
4@@ -30,7 +30,7 @@
5 // Construct a tools filter.
6 // Discard all that are known to be irrelevant.
7 if filter.Number != version.Zero {
8- logger.Infof("filtering tools by version: %s", filter.Number.Major)
9+ logger.Infof("filtering tools by version: %s", filter.Number)
10 }
11 if filter.Series != "" {
12 logger.Infof("filtering tools by series: %s", filter.Series)
13
14=== modified file 'environs/tools/tools_test.go'
15--- environs/tools/tools_test.go 2013-08-24 13:50:05 +0000
16+++ environs/tools/tools_test.go 2013-08-28 12:04:57 +0000
17@@ -5,6 +5,7 @@
18
19 import (
20 gc "launchpad.net/gocheck"
21+ "launchpad.net/loggo"
22
23 "launchpad.net/juju-core/constraints"
24 "launchpad.net/juju-core/environs"
25@@ -190,6 +191,24 @@
26 }
27 }
28
29+func (s *ToolsSuite) TestFindToolsFiltering(c *gc.C) {
30+ tw := &loggo.TestWriter{}
31+ c.Assert(loggo.RegisterWriter("filter-tester", tw, loggo.DEBUG), gc.IsNil)
32+ defer loggo.RemoveWriter("filter-tester")
33+ _, err := envtools.FindTools(s.env, 1, coretools.Filter{Number: version.Number{Major: 1, Minor: 2, Patch: 3}})
34+ c.Assert(err, jc.Satisfies, errors.IsNotFoundError)
35+ // This is slightly overly prescriptive, but feel free to change or add
36+ // messages. This still helps to ensure that all log messages are
37+ // properly formed.
38+ c.Check(tw.Log, jc.LogMatches, []jc.SimpleMessage{
39+ {loggo.INFO, "reading tools with major version 1"},
40+ {loggo.INFO, "filtering tools by version: 1.2.3"},
41+ {loggo.DEBUG, "reading v1.* tools"},
42+ {loggo.INFO, "falling back to public bucket"},
43+ {loggo.DEBUG, "reading v1.* tools"},
44+ })
45+}
46+
47 var findBootstrapToolsTests = []struct {
48 info string
49 available []version.Binary
50
51=== added file 'testing/checkers/log.go'
52--- testing/checkers/log.go 1970-01-01 00:00:00 +0000
53+++ testing/checkers/log.go 2013-08-28 12:04:57 +0000
54@@ -0,0 +1,60 @@
55+// Copyright 2012, 2013 Canonical Ltd.
56+// Licensed under the AGPLv3, see LICENCE file for details.
57+
58+package checkers
59+
60+import (
61+ "reflect"
62+
63+ gc "launchpad.net/gocheck"
64+ "launchpad.net/loggo"
65+)
66+
67+type SimpleMessage struct {
68+ Level loggo.Level
69+ Message string
70+}
71+
72+func logToSimpleMessages(log []loggo.TestLogValues) []SimpleMessage {
73+ out := make([]SimpleMessage, len(log))
74+ for i, val := range log {
75+ out[i].Level = val.Level
76+ out[i].Message = val.Message
77+ }
78+ return out
79+}
80+
81+type logMatches struct {
82+ *gc.CheckerInfo
83+}
84+
85+func (checker *logMatches) Check(params []interface{}, names []string) (result bool, error string) {
86+ var obtained []SimpleMessage
87+ switch params[0].(type) {
88+ case []loggo.TestLogValues:
89+ obtained = logToSimpleMessages(params[0].([]loggo.TestLogValues))
90+ default:
91+ return false, "Obtained value must be of type []loggo.TestLogValues or SimpleMessage"
92+ }
93+ switch params[1].(type) {
94+ case []SimpleMessage:
95+ return reflect.DeepEqual(obtained, params[1]), ""
96+ case []string:
97+ asString := make([]string, len(obtained))
98+ for i, val := range obtained {
99+ asString[i] = val.Message
100+ }
101+ return reflect.DeepEqual(asString, params[1]), ""
102+ default:
103+ return false, "Expected value must be of type []string or []SimpleMessage"
104+ }
105+}
106+
107+// LogMatches checks whether a given TestLogValues actually contains the log
108+// messages we expected. If you compare it against a list of strings, we only
109+// compare that the strings in the messages are correct. You can alternatively
110+// pass a slice of SimpleMessage and we will check that the log levels are
111+// also correct.
112+var LogMatches gc.Checker = &logMatches{
113+ &gc.CheckerInfo{Name: "LogMatches", Params: []string{"obtained", "expected"}},
114+}
115
116=== added file 'testing/checkers/log_test.go'
117--- testing/checkers/log_test.go 1970-01-01 00:00:00 +0000
118+++ testing/checkers/log_test.go 2013-08-28 12:04:57 +0000
119@@ -0,0 +1,57 @@
120+// Copyright 2013 Canonical Ltd.
121+// Licensed under the AGPLv3, see LICENCE file for details.
122+
123+package checkers_test
124+
125+import (
126+ gc "launchpad.net/gocheck"
127+ "launchpad.net/loggo"
128+
129+ jc "launchpad.net/juju-core/testing/checkers"
130+)
131+
132+type LogMatchesSuite struct{}
133+
134+var _ = gc.Suite(&LogMatchesSuite{})
135+
136+func (s *LogMatchesSuite) TestMatchSimpleMessage(c *gc.C) {
137+ log := []loggo.TestLogValues{
138+ {Level: loggo.INFO, Message: "foo"},
139+ {Level: loggo.INFO, Message: "bar"},
140+ }
141+ c.Check(log, jc.LogMatches, []jc.SimpleMessage{
142+ {loggo.INFO, "foo"},
143+ {loggo.INFO, "bar"},
144+ })
145+ c.Check(log, gc.Not(jc.LogMatches), []jc.SimpleMessage{
146+ {loggo.INFO, "foo"},
147+ {loggo.DEBUG, "bar"},
148+ })
149+}
150+
151+func (s *LogMatchesSuite) TestMatchStrings(c *gc.C) {
152+ log := []loggo.TestLogValues{
153+ {Level: loggo.INFO, Message: "foo"},
154+ {Level: loggo.INFO, Message: "bar"},
155+ }
156+ c.Check(log, jc.LogMatches, []string{"foo", "bar"})
157+ c.Check(log, gc.Not(jc.LogMatches), []string{"baz", "bing"})
158+}
159+
160+func (s *LogMatchesSuite) TestFromLogMatches(c *gc.C) {
161+ tw := &loggo.TestWriter{}
162+ _, err := loggo.ReplaceDefaultWriter(tw)
163+ c.Assert(err, gc.IsNil)
164+ defer loggo.ResetWriters()
165+ logger := loggo.GetLogger("test")
166+ logger.SetLogLevel(loggo.DEBUG)
167+ logger.Infof("foo")
168+ logger.Debugf("bar")
169+ logger.Tracef("hidden")
170+ c.Check(tw.Log, jc.LogMatches, []string{"foo", "bar"})
171+ c.Check(tw.Log, gc.Not(jc.LogMatches), []string{"foo", "bad"})
172+ c.Check(tw.Log, gc.Not(jc.LogMatches), []jc.SimpleMessage{
173+ {loggo.INFO, "foo"},
174+ {loggo.INFO, "bar"},
175+ })
176+}

Subscribers

People subscribed via source and target branches

to status/vote changes: