Merge lp:~axwalk/juju-core/logmatches-inexact into lp:~go-bot/juju-core/trunk

Proposed by Andrew Wilkins
Status: Merged
Approved by: Andrew Wilkins
Approved revision: no longer in the source branch.
Merged at revision: 1986
Proposed branch: lp:~axwalk/juju-core/logmatches-inexact
Merge into: lp:~go-bot/juju-core/trunk
Diff against target: 160 lines (+83/-28)
2 files modified
testing/checkers/log.go (+33/-28)
testing/checkers/log_test.go (+50/-0)
To merge this branch: bzr merge lp:~axwalk/juju-core/logmatches-inexact
Reviewer Review Type Date Requested Status
Juju Engineering Pending
Review via email: mp+191092@code.launchpad.net

Commit message

testing/checkers: make LogMatches inexact

In the rare case that we want to check log messages,
we want to check a particular message has been logged,
not check the entirety of the log. This allows for
changes (unrelated additions/removals) in logging
without affecting tests.

https://codereview.appspot.com/14430064/

Description of the change

testing/checkers: make LogMatches inexact

In the rare case that we want to check log messages,
we want to check a particular message has been logged,
not check the entirety of the log. This allows for
changes (unrelated additions/removals) in logging
without affecting tests.

https://codereview.appspot.com/14430064/

To post a comment you must log in.
Revision history for this message
Andrew Wilkins (axwalk) wrote :
Download full text (4.8 KiB)

Reviewers: mp+191092_code.launchpad.net,

Message:
Please take a look.

Description:
testing/checkers: make LogMatches inexact

In the rare case that we want to check log messages,
we want to check a particular message has been logged,
not check the entirety of the log. This allows for
changes (unrelated additions/removals) in logging
without affecting tests.

https://code.launchpad.net/~axwalk/juju-core/logmatches-inexact/+merge/191092

(do not edit description out of merge proposal)

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

Affected files (+55, -30 lines):
   A [revision details]
   M testing/checkers/log.go
   M testing/checkers/log_test.go

Index: [revision details]
=== added file '[revision details]'
--- [revision details] 2012-01-01 00:00:00 +0000
+++ [revision details] 2012-01-01 00:00:00 +0000
@@ -0,0 +1,2 @@
+Old revision: tarmac-20131014040553-f18jp0xnxw45qkt2
+New revision: <email address hidden>

Index: testing/checkers/log.go
=== modified file 'testing/checkers/log.go'
--- testing/checkers/log.go 2013-10-01 16:46:31 +0000
+++ testing/checkers/log.go 2013-10-15 02:36:40 +0000
@@ -36,41 +36,38 @@
   default:
    return false, "Obtained value must be of type []loggo.TestLogValues or
SimpleMessage"
   }
- switch params[1].(type) {
+
+ var expected []SimpleMessage
+ switch param := params[1].(type) {
   case []SimpleMessage:
- expected := params[1].([]SimpleMessage)
- if len(obtained) != len(expected) {
- return false, ""
- }
- for i := 0; i < len(obtained); i++ {
- if obtained[i].Level != expected[i].Level {
- return false, ""
- }
- re := regexp.MustCompile(expected[i].Message)
- if !re.MatchString(obtained[i].Message) {
- return false, ""
- }
- }
- return true, ""
+ expected = param
   case []string:
- asString := make([]string, len(obtained))
- for i, val := range obtained {
- asString[i] = val.Message
- }
- expected := params[1].([]string)
- if len(obtained) != len(expected) {
- return false, ""
- }
- for i := 0; i < len(obtained); i++ {
- re := regexp.MustCompile(expected[i])
- if !re.MatchString(obtained[i].Message) {
- return false, ""
- }
- }
- return true, ""
+ expected = make([]SimpleMessage, len(param))
+ for i, s := range param {
+ expected[i].Message = s
+ }
   default:
    return false, "Expected value must be of type []string or
[]SimpleMessage"
   }
+
+ for len(expected) > 0 && len(obtained) >= len(expected) {
+ var msg SimpleMessage
+ msg, obtained = obtained[0], obtained[1:]
+ if expected[0].Level != loggo.UNSPECIFIED {
+ if msg.Level != expected[0].Level {
+ continue
+ }
+ }
+ re := regexp.MustCompile(expected[0].Message)
+ if !re.MatchString(msg.Message) {
+ continue
+ }
+ expected = expected[1:]
+ }
+ if len(obtained) < len(expected) {
+ return false, ""
+ }
+ return true, ""
  }

  // LogMatches checks whether a given TestLogValues actually contains the
log
@@ -78,6 +75,10 @@
  // compare that the strings in the messages are correct. You can
alternatively
  // pass a slice of SimpleMessage and we will check that the log levels are
  // also correct.
+//
+// The log may conta...

Read more...

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

Great stuff - you beat me to it!

LGTM modulo the below suggestions and remarks.

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

https://codereview.appspot.com/14430064/diff/1/testing/checkers/log.go#newcode47
testing/checkers/log.go:47: expected[i].Message = s
I think I'd like to see Level set explicitly to
UNSPECIFIED here, so that I don't need to
check to see if it's zero when checking
the logic below.

Perhaps:
expected[i] = SimpleMessage{
     Message: s,
     Level: loggo.UNSPECIFIED,
}

https://codereview.appspot.com/14430064/diff/1/testing/checkers/log.go#newcode56
testing/checkers/log.go:56: if expected[0].Level != loggo.UNSPECIFIED {
expect := expected[0]
if expect.Level != loggo.UNSPECIFIED && msg.Level != expect.Level {
     continue
}

might possibly read slightly better?
and then you can use expect rather than expected[0]
in a few places below too.

https://codereview.appspot.com/14430064/diff/1/testing/checkers/log.go#newcode61
testing/checkers/log.go:61: re :=
regexp.MustCompile(expected[0].Message)
I'm not sure we really want to panic if someone gets a pattern
wrong in the expected log messages.

How about:

ok, err := regexp.MatchString(expected[0].Message, msg.Message)
if err != nil {
     return false, "bad message regexp %q: %v", expected[0].Message, err)
}
if !ok {
     continue
}

?

https://codereview.appspot.com/14430064/diff/1/testing/checkers/log.go#newcode80
testing/checkers/log.go:80: // expected messages; the specified messages
will be matched to the left-most
I think you can lose the part after the semicolon - the semantics are
the same whether it's leftmost or rightmost match or anything in
between, aren't they?

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

https://codereview.appspot.com/14430064/diff/1/testing/checkers/log_test.go#newcode65
testing/checkers/log_test.go:65: c.Check(log, gc.Not(jc.LogMatches),
[]string{"67890", ".*"})
I'd like to see at least one test that tests log level matching in an
inexact context.

Also, perhaps a test that checks that {".*", "foo bar"} doesn't match.

https://codereview.appspot.com/14430064/

Revision history for this message
Andrew Wilkins (axwalk) wrote :

Please take a look.

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

https://codereview.appspot.com/14430064/diff/1/testing/checkers/log.go#newcode47
testing/checkers/log.go:47: expected[i].Message = s
On 2013/10/15 08:54:04, rog wrote:
> I think I'd like to see Level set explicitly to
> UNSPECIFIED here, so that I don't need to
> check to see if it's zero when checking
> the logic below.

> Perhaps:
> expected[i] = SimpleMessage{
> Message: s,
> Level: loggo.UNSPECIFIED,
> }

Done.

https://codereview.appspot.com/14430064/diff/1/testing/checkers/log.go#newcode56
testing/checkers/log.go:56: if expected[0].Level != loggo.UNSPECIFIED {
On 2013/10/15 08:54:04, rog wrote:
> expect := expected[0]
> if expect.Level != loggo.UNSPECIFIED && msg.Level != expect.Level {
> continue
> }

> might possibly read slightly better?
> and then you can use expect rather than expected[0]
> in a few places below too.

Done.

https://codereview.appspot.com/14430064/diff/1/testing/checkers/log.go#newcode61
testing/checkers/log.go:61: re :=
regexp.MustCompile(expected[0].Message)
On 2013/10/15 08:54:04, rog wrote:
> I'm not sure we really want to panic if someone gets a pattern
> wrong in the expected log messages.

> How about:

> ok, err := regexp.MatchString(expected[0].Message, msg.Message)
> if err != nil {
> return false, "bad message regexp %q: %v", expected[0].Message,
err)
> }
> if !ok {
> continue
> }

> ?

Done.

https://codereview.appspot.com/14430064/diff/1/testing/checkers/log.go#newcode80
testing/checkers/log.go:80: // expected messages; the specified messages
will be matched to the left-most
On 2013/10/15 08:54:04, rog wrote:
> I think you can lose the part after the semicolon - the semantics are
the same
> whether it's leftmost or rightmost match or anything in between,
aren't they?

Indeed, I didn't think that through properly. Removed, thanks.

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

https://codereview.appspot.com/14430064/diff/1/testing/checkers/log_test.go#newcode65
testing/checkers/log_test.go:65: c.Check(log, gc.Not(jc.LogMatches),
[]string{"67890", ".*"})
On 2013/10/15 08:54:04, rog wrote:
> I'd like to see at least one test that tests log level matching in an
inexact
> context.

> Also, perhaps a test that checks that {".*", "foo bar"} doesn't match.

Done.

https://codereview.appspot.com/14430064/

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'testing/checkers/log.go'
2--- testing/checkers/log.go 2013-10-01 16:46:31 +0000
3+++ testing/checkers/log.go 2013-10-16 02:08:15 +0000
4@@ -4,6 +4,7 @@
5 package checkers
6
7 import (
8+ "fmt"
9 "regexp"
10
11 gc "launchpad.net/gocheck"
12@@ -36,41 +37,42 @@
13 default:
14 return false, "Obtained value must be of type []loggo.TestLogValues or SimpleMessage"
15 }
16- switch params[1].(type) {
17+
18+ var expected []SimpleMessage
19+ switch param := params[1].(type) {
20 case []SimpleMessage:
21- expected := params[1].([]SimpleMessage)
22- if len(obtained) != len(expected) {
23- return false, ""
24- }
25- for i := 0; i < len(obtained); i++ {
26- if obtained[i].Level != expected[i].Level {
27- return false, ""
28- }
29- re := regexp.MustCompile(expected[i].Message)
30- if !re.MatchString(obtained[i].Message) {
31- return false, ""
32- }
33- }
34- return true, ""
35+ expected = param
36 case []string:
37- asString := make([]string, len(obtained))
38- for i, val := range obtained {
39- asString[i] = val.Message
40- }
41- expected := params[1].([]string)
42- if len(obtained) != len(expected) {
43- return false, ""
44- }
45- for i := 0; i < len(obtained); i++ {
46- re := regexp.MustCompile(expected[i])
47- if !re.MatchString(obtained[i].Message) {
48- return false, ""
49+ expected = make([]SimpleMessage, len(param))
50+ for i, s := range param {
51+ expected[i] = SimpleMessage{
52+ Message: s,
53+ Level: loggo.UNSPECIFIED,
54 }
55 }
56- return true, ""
57 default:
58 return false, "Expected value must be of type []string or []SimpleMessage"
59 }
60+
61+ for len(expected) > 0 && len(obtained) >= len(expected) {
62+ var msg SimpleMessage
63+ msg, obtained = obtained[0], obtained[1:]
64+ expect := expected[0]
65+ if expect.Level != loggo.UNSPECIFIED && msg.Level != expect.Level {
66+ continue
67+ }
68+ matched, err := regexp.MatchString(expect.Message, msg.Message)
69+ if err != nil {
70+ return false, fmt.Sprintf("bad message regexp %q: %v", expect.Message, err)
71+ } else if !matched {
72+ continue
73+ }
74+ expected = expected[1:]
75+ }
76+ if len(obtained) < len(expected) {
77+ return false, ""
78+ }
79+ return true, ""
80 }
81
82 // LogMatches checks whether a given TestLogValues actually contains the log
83@@ -78,6 +80,9 @@
84 // compare that the strings in the messages are correct. You can alternatively
85 // pass a slice of SimpleMessage and we will check that the log levels are
86 // also correct.
87+//
88+// The log may contain additional messages before and after each of the specified
89+// expected messages.
90 var LogMatches gc.Checker = &logMatches{
91 &gc.CheckerInfo{Name: "LogMatches", Params: []string{"obtained", "expected"}},
92 }
93
94=== modified file 'testing/checkers/log_test.go'
95--- testing/checkers/log_test.go 2013-10-01 16:46:31 +0000
96+++ testing/checkers/log_test.go 2013-10-16 02:08:15 +0000
97@@ -27,6 +27,12 @@
98 {loggo.INFO, "foo .*"},
99 {loggo.INFO, "12345"},
100 })
101+ // UNSPECIFIED means we don't care what the level is,
102+ // just check the message string matches.
103+ c.Check(log, jc.LogMatches, []jc.SimpleMessage{
104+ {loggo.UNSPECIFIED, "foo .*"},
105+ {loggo.INFO, "12345"},
106+ })
107 c.Check(log, gc.Not(jc.LogMatches), []jc.SimpleMessage{
108 {loggo.INFO, "foo bar"},
109 {loggo.DEBUG, "12345"},
110@@ -43,6 +49,50 @@
111 c.Check(log, gc.Not(jc.LogMatches), []string{"baz", "bing"})
112 }
113
114+func (s *LogMatchesSuite) TestMatchInexact(c *gc.C) {
115+ log := []loggo.TestLogValues{
116+ {Level: loggo.INFO, Message: "foo bar"},
117+ {Level: loggo.INFO, Message: "baz"},
118+ {Level: loggo.DEBUG, Message: "12345"},
119+ {Level: loggo.ERROR, Message: "12345"},
120+ {Level: loggo.INFO, Message: "67890"},
121+ }
122+ c.Check(log, jc.LogMatches, []string{"foo bar", "12345"})
123+ c.Check(log, jc.LogMatches, []string{"foo .*", "12345"})
124+ c.Check(log, jc.LogMatches, []string{"foo .*", "67890"})
125+ c.Check(log, jc.LogMatches, []string{"67890"})
126+
127+ // Matches are always left-most after the previous match.
128+ c.Check(log, jc.LogMatches, []string{".*", "baz"})
129+ c.Check(log, jc.LogMatches, []string{"foo bar", ".*", "12345"})
130+ c.Check(log, jc.LogMatches, []string{"foo bar", ".*", "67890"})
131+
132+ // Order is important: 67890 advances to the last item in obtained,
133+ // and so there's nothing after to match against ".*".
134+ c.Check(log, gc.Not(jc.LogMatches), []string{"67890", ".*"})
135+ // ALL specified patterns MUST match in the order given.
136+ c.Check(log, gc.Not(jc.LogMatches), []string{".*", "foo bar"})
137+
138+ // Check that levels are matched.
139+ c.Check(log, jc.LogMatches, []jc.SimpleMessage{
140+ {loggo.UNSPECIFIED, "12345"},
141+ {loggo.UNSPECIFIED, "12345"},
142+ })
143+ c.Check(log, jc.LogMatches, []jc.SimpleMessage{
144+ {loggo.DEBUG, "12345"},
145+ {loggo.ERROR, "12345"},
146+ })
147+ c.Check(log, jc.LogMatches, []jc.SimpleMessage{
148+ {loggo.DEBUG, "12345"},
149+ {loggo.INFO, ".*"},
150+ })
151+ c.Check(log, gc.Not(jc.LogMatches), []jc.SimpleMessage{
152+ {loggo.DEBUG, "12345"},
153+ {loggo.INFO, ".*"},
154+ {loggo.UNSPECIFIED, ".*"},
155+ })
156+}
157+
158 func (s *LogMatchesSuite) TestFromLogMatches(c *gc.C) {
159 tw := &loggo.TestWriter{}
160 _, err := loggo.ReplaceDefaultWriter(tw)

Subscribers

People subscribed via source and target branches

to status/vote changes: