Merge lp:~pedronis/ubuntu-push/testlog into lp:ubuntu-push

Proposed by Samuele Pedroni
Status: Merged
Approved by: Samuele Pedroni
Approved revision: 54
Merged at revision: 52
Proposed branch: lp:~pedronis/ubuntu-push/testlog
Merge into: lp:ubuntu-push
Diff against target: 398 lines (+128/-57)
6 files modified
logger/logger.go (+31/-12)
logger/logger_test.go (+10/-0)
server/broker/simple/suite_test.go (+3/-3)
server/broker/testsuite/suite.go (+12/-9)
server/listener/listener_test.go (+13/-14)
testing/helpers.go (+59/-19)
To merge this branch: bzr merge lp:~pedronis/ubuntu-push/testlog
Reviewer Review Type Date Requested Status
John Lenton (community) Approve
Review via email: mp+204783@code.launchpad.net

Commit message

base the simple logger only on having a log.Logger-style Output(); given this we can introduce a testing.TestLogger that does redirecting of logs to the gocheck.C object if wired in SetUpTest and also does its own capture

Description of the change

base the simple logger only on having a log.Logger-style Output()

reexport that as we will need it for mgo logging,

given this we can introduce a testing.TestLogger that does redirecting of logs to the gocheck.C object if wired in SetUpTest and also does its own capture.

start using that in the couple of places that were depending on SyncedLogBuffer

To post a comment you must log in.
lp:~pedronis/ubuntu-push/testlog updated
54. By Samuele Pedroni

period

Revision history for this message
John Lenton (chipaca) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'logger/logger.go'
2--- logger/logger.go 2014-01-16 13:36:32 +0000
3+++ logger/logger.go 2014-02-04 21:37:48 +0000
4@@ -27,6 +27,8 @@
5
6 // Logger is a simple logger interface with logging at levels.
7 type Logger interface {
8+ // (Re)xpose base Output for logging events.
9+ Output(calldept int, s string) error
10 // Errorf logs an error.
11 Errorf(format string, v ...interface{})
12 // Fatalf logs an error and exists the program with os.Exit(1).
13@@ -41,8 +43,8 @@
14 }
15
16 type simpleLogger struct {
17- *log.Logger
18- nlevel int
19+ outputFunc func(calldepth int, s string) error
20+ nlevel int
21 }
22
23 const (
24@@ -57,24 +59,41 @@
25 "debug": lDebug,
26 }
27
28-// NewSimpleLogger creates a logger logging only up to the given level.
29-// level can be in order: "error", "info", "debug".
30-func NewSimpleLogger(w io.Writer, level string) Logger {
31+// NewSimpleLoggerFromMinimalLogger creates a logger logging only up
32+// to the given level. level can be in order: "error", "info",
33+// "debug". It takes a value just implementing stlib Logger.Output().
34+func NewSimpleLoggerFromMinimalLogger(minLog interface {
35+ Output(calldepth int, s string) error
36+}, level string) Logger {
37 nlevel := levelToNLevel[level]
38 return &simpleLogger{
39+ minLog.Output,
40+ nlevel,
41+ }
42+}
43+
44+// NewSimpleLogger creates a logger logging only up to the given
45+// level. level can be in order: "error", "info", "debug". It takes a
46+// io.Writer.
47+func NewSimpleLogger(w io.Writer, level string) Logger {
48+ return NewSimpleLoggerFromMinimalLogger(
49 log.New(w, "", log.Ldate|log.Ltime|log.Lmicroseconds),
50- nlevel,
51- }
52+ level,
53+ )
54+}
55+
56+func (lg *simpleLogger) Output(calldepth int, s string) error {
57+ return lg.outputFunc(calldepth+2, s)
58 }
59
60 func (lg *simpleLogger) Errorf(format string, v ...interface{}) {
61- lg.Printf("ERROR "+format, v...)
62+ lg.outputFunc(2, fmt.Sprintf("ERROR "+format, v...))
63 }
64
65 var osExit = os.Exit // for testing
66
67 func (lg *simpleLogger) Fatalf(format string, v ...interface{}) {
68- lg.Printf("ERROR "+format, v...)
69+ lg.outputFunc(2, fmt.Sprintf("ERROR "+format, v...))
70 osExit(1)
71 }
72
73@@ -83,17 +102,17 @@
74 stack := make([]byte, 8*1024) // Stack writes less but doesn't fail
75 stackWritten := runtime.Stack(stack, false)
76 stack = stack[:stackWritten]
77- lg.Printf("ERROR(PANIC) %s:\n%s", msg, stack)
78+ lg.outputFunc(2, fmt.Sprintf("ERROR(PANIC) %s:\n%s", msg, stack))
79 }
80
81 func (lg *simpleLogger) Infof(format string, v ...interface{}) {
82 if lg.nlevel >= lInfo {
83- lg.Printf("INFO "+format, v...)
84+ lg.outputFunc(2, fmt.Sprintf("INFO "+format, v...))
85 }
86 }
87
88 func (lg *simpleLogger) Debugf(format string, v ...interface{}) {
89 if lg.nlevel >= lDebug {
90- lg.Printf("DEBUG "+format, v...)
91+ lg.outputFunc(2, fmt.Sprintf("DEBUG "+format, v...))
92 }
93 }
94
95=== modified file 'logger/logger_test.go'
96--- logger/logger_test.go 2014-01-16 13:36:32 +0000
97+++ logger/logger_test.go 2014-02-04 21:37:48 +0000
98@@ -20,6 +20,7 @@
99 "bytes"
100 "fmt"
101 . "launchpad.net/gocheck"
102+ "log"
103 "os"
104 "runtime"
105 "testing"
106@@ -127,3 +128,12 @@
107 panicAndRecover(logger, 6, false, &line, &ok)
108 c.Check(buf.String(), Equals, "")
109 }
110+
111+func (s *loggerSuite) TestReexposeOutput(c *C) {
112+ buf := &bytes.Buffer{}
113+ baselog := log.New(buf, "", log.Lshortfile)
114+ logger := NewSimpleLoggerFromMinimalLogger(baselog, "error")
115+ baselog.Output(1, "foobar")
116+ logger.Output(1, "foobaz")
117+ c.Check(buf.String(), Matches, "logger_test.go:[0-9]+: foobar\nlogger_test.go:[0-9]+: foobaz\n")
118+}
119
120=== modified file 'server/broker/simple/suite_test.go'
121--- server/broker/simple/suite_test.go 2014-02-03 15:13:00 +0000
122+++ server/broker/simple/suite_test.go 2014-02-04 21:37:48 +0000
123@@ -32,13 +32,13 @@
124 }
125
126 var _ = Suite(&commonBrokerSuite{testsuite.CommonBrokerSuite{
127- func(sto store.PendingStore, cfg broker.BrokerConfig, log logger.Logger) testsuite.FullBroker {
128+ MakeBroker: func(sto store.PendingStore, cfg broker.BrokerConfig, log logger.Logger) testsuite.FullBroker {
129 return NewSimpleBroker(sto, cfg, log)
130 },
131- func(b broker.Broker, deviceId string) broker.BrokerSession {
132+ RevealSession: func(b broker.Broker, deviceId string) broker.BrokerSession {
133 return b.(*SimpleBroker).registry[deviceId]
134 },
135- func(exchg broker.Exchange) *broker.BroadcastExchange {
136+ RevealBroadcastExchange: func(exchg broker.Exchange) *broker.BroadcastExchange {
137 return exchg.(*broker.BroadcastExchange)
138 },
139 }})
140
141=== modified file 'server/broker/testsuite/suite.go'
142--- server/broker/testsuite/suite.go 2014-02-03 15:13:00 +0000
143+++ server/broker/testsuite/suite.go 2014-02-04 21:37:48 +0000
144@@ -48,6 +48,12 @@
145 RevealSession func(broker.Broker, string) broker.BrokerSession
146 // Let us get to a broker.BroadcastExchange from an Exchange.
147 RevealBroadcastExchange func(broker.Exchange) *broker.BroadcastExchange
148+ // private
149+ testlog *helpers.TestLogger
150+}
151+
152+func (s *CommonBrokerSuite) SetUpTest(c *C) {
153+ s.testlog = helpers.NewTestLogger(c, "error")
154 }
155
156 var testBrokerConfig = &testing.TestBrokerConfig{10, 5}
157@@ -110,16 +116,14 @@
158 }
159
160 func (s *CommonBrokerSuite) TestRegistrationFeedPendingError(c *C) {
161- buf := &helpers.SyncedLogBuffer{}
162- logger := logger.NewSimpleLogger(buf, "error")
163 sto := &testFailingStore{}
164- b := s.MakeBroker(sto, testBrokerConfig, logger)
165+ b := s.MakeBroker(sto, testBrokerConfig, s.testlog)
166 b.Start()
167 defer b.Stop()
168 _, err := b.Register(&protocol.ConnectMsg{Type: "connect", DeviceId: "dev-1"})
169 c.Assert(err, IsNil)
170 // but
171- c.Check(buf.String(), Matches, ".*ERROR unsuccessful feed pending, get channel snapshot for 0: get channel snapshot fail\n")
172+ c.Check(s.testlog.Captured(), Matches, "ERROR unsuccessful feed pending, get channel snapshot for 0: get channel snapshot fail\n")
173 }
174
175 func (s *CommonBrokerSuite) TestRegistrationLastWins(c *C) {
176@@ -188,10 +192,9 @@
177 }
178
179 func (s *CommonBrokerSuite) TestBroadcastFail(c *C) {
180- buf := &helpers.SyncedLogBuffer{Written: make(chan bool, 1)}
181- logger := logger.NewSimpleLogger(buf, "error")
182+ s.testlog.Written = make(chan bool, 1)
183 sto := &testFailingStore{countdownToFail: 1}
184- b := s.MakeBroker(sto, testBrokerConfig, logger)
185+ b := s.MakeBroker(sto, testBrokerConfig, s.testlog)
186 b.Start()
187 defer b.Stop()
188 _, err := b.Register(&protocol.ConnectMsg{Type: "connect", DeviceId: "dev-1"})
189@@ -200,7 +203,7 @@
190 select {
191 case <-time.After(5 * time.Second):
192 c.Fatal("taking too long to log error")
193- case <-buf.Written:
194+ case <-s.testlog.Written:
195 }
196- c.Check(buf.String(), Matches, ".*ERROR unsuccessful broadcast, get channel snapshot for 0: get channel snapshot fail\n")
197+ c.Check(s.testlog.Captured(), Matches, "ERROR unsuccessful broadcast, get channel snapshot for 0: get channel snapshot fail\n")
198 }
199
200=== modified file 'server/listener/listener_test.go'
201--- server/listener/listener_test.go 2014-01-21 21:36:07 +0000
202+++ server/listener/listener_test.go 2014-02-04 21:37:48 +0000
203@@ -20,7 +20,6 @@
204 "crypto/tls"
205 "crypto/x509"
206 . "launchpad.net/gocheck"
207- "launchpad.net/ubuntu-push/logger"
208 helpers "launchpad.net/ubuntu-push/testing"
209 "net"
210 "syscall"
211@@ -30,7 +29,9 @@
212
213 func TestListener(t *testing.T) { TestingT(t) }
214
215-type listenerSuite struct{}
216+type listenerSuite struct {
217+ testlog *helpers.TestLogger
218+}
219
220 var _ = Suite(&listenerSuite{})
221
222@@ -50,6 +51,10 @@
223 }
224 }
225
226+func (s *listenerSuite) SetUpTest(c *C) {
227+ s.testlog = helpers.NewTestLogger(c, "error")
228+}
229+
230 type testDevListenerCfg struct {
231 addr string
232 }
233@@ -135,14 +140,12 @@
234 }
235
236 func (s *listenerSuite) TestDeviceAcceptLoop(c *C) {
237- buf := &helpers.SyncedLogBuffer{}
238- logger := logger.NewSimpleLogger(buf, "error")
239 lst, err := DeviceListen(&testDevListenerCfg{"127.0.0.1:0"})
240 c.Check(err, IsNil)
241 defer lst.Close()
242 errCh := make(chan error)
243 go func() {
244- errCh <- lst.AcceptLoop(testSession, logger)
245+ errCh <- lst.AcceptLoop(testSession, s.testlog)
246 }()
247 listenerAddr := lst.Addr().String()
248 conn1, err := testTlsDial(c, listenerAddr)
249@@ -157,12 +160,10 @@
250 testReadByte(c, conn2, '2')
251 lst.Close()
252 c.Check(<-errCh, ErrorMatches, ".*use of closed.*")
253- c.Check(buf.String(), Equals, "")
254+ c.Check(s.testlog.Captured(), Equals, "")
255 }
256
257 func (s *listenerSuite) TestDeviceAcceptLoopTemporaryError(c *C) {
258- buf := &helpers.SyncedLogBuffer{}
259- logger := logger.NewSimpleLogger(buf, "error")
260 // ENFILE is not the temp network error we want to handle this way
261 // but is relatively easy to generate in a controlled way
262 var err error
263@@ -171,7 +172,7 @@
264 defer lst.Close()
265 errCh := make(chan error)
266 go func() {
267- errCh <- lst.AcceptLoop(testSession, logger)
268+ errCh <- lst.AcceptLoop(testSession, s.testlog)
269 }()
270 listenerAddr := lst.Addr().String()
271 conns := make([]net.Conn, 0, NofileMax)
272@@ -195,12 +196,10 @@
273 testReadByte(c, conn2, '2')
274 lst.Close()
275 c.Check(<-errCh, ErrorMatches, ".*use of closed.*")
276- c.Check(buf.String(), Matches, ".*device listener:.*accept.*too many open.*-- retrying\n")
277+ c.Check(s.testlog.Captured(), Matches, ".*device listener:.*accept.*too many open.*-- retrying\n")
278 }
279
280 func (s *listenerSuite) TestDeviceAcceptLoopPanic(c *C) {
281- buf := &helpers.SyncedLogBuffer{}
282- logger := logger.NewSimpleLogger(buf, "error")
283 lst, err := DeviceListen(&testDevListenerCfg{"127.0.0.1:0"})
284 c.Check(err, IsNil)
285 defer lst.Close()
286@@ -209,12 +208,12 @@
287 errCh <- lst.AcceptLoop(func(conn net.Conn) error {
288 defer conn.Close()
289 panic("session crash")
290- }, logger)
291+ }, s.testlog)
292 }()
293 listenerAddr := lst.Addr().String()
294 _, err = testTlsDial(c, listenerAddr)
295 c.Assert(err, Not(IsNil))
296 lst.Close()
297 c.Check(<-errCh, ErrorMatches, ".*use of closed.*")
298- c.Check(buf.String(), Matches, "(?s).* ERROR\\(PANIC\\) terminating device connection on: session crash:.*AcceptLoop.*")
299+ c.Check(s.testlog.Captured(), Matches, "(?s)ERROR\\(PANIC\\) terminating device connection on: session crash:.*AcceptLoop.*")
300 }
301
302=== modified file 'testing/helpers.go'
303--- testing/helpers.go 2014-01-21 21:36:07 +0000
304+++ testing/helpers.go 2014-02-04 21:37:48 +0000
305@@ -18,34 +18,74 @@
306 package testing
307
308 import (
309- "bytes"
310+ "launchpad.net/ubuntu-push/logger"
311 "path/filepath"
312 "runtime"
313+ "strings"
314 "sync"
315 )
316
317-// SyncedLogBuffer can be used with NewSimpleLogger avoiding races
318-// when checking the logging done from different goroutines.
319-type SyncedLogBuffer struct {
320- bytes.Buffer
321- lock sync.Mutex
322+type captureHelper struct {
323+ outputFunc func(int, string) error
324+ lock sync.Mutex
325+ logEvents []string
326+ written *chan bool
327+}
328+
329+func (h *captureHelper) Output(calldepth int, s string) error {
330+ err := h.outputFunc(calldepth+2, s)
331+ if err == nil {
332+ h.lock.Lock()
333+ defer h.lock.Unlock()
334+ if *h.written != nil {
335+ *h.written <- true
336+ }
337+ h.logEvents = append(h.logEvents, s+"\n")
338+ }
339+ return err
340+}
341+
342+func (h *captureHelper) captured() string {
343+ h.lock.Lock()
344+ defer h.lock.Unlock()
345+ return strings.Join(h.logEvents, "")
346+}
347+
348+func (h *captureHelper) reset() {
349+ h.lock.Lock()
350+ defer h.lock.Unlock()
351+ h.logEvents = nil
352+}
353+
354+// TestLogger implements logger.Logger using gocheck.C and supporting
355+// capturing log strings.
356+type TestLogger struct {
357+ logger.Logger
358+ helper *captureHelper
359 Written chan bool
360 }
361
362-func (buf *SyncedLogBuffer) Write(b []byte) (int, error) {
363- buf.lock.Lock()
364- defer buf.lock.Unlock()
365- n, err := buf.Buffer.Write(b)
366- if buf.Written != nil {
367- buf.Written <- true
368+// NewTestLogger can be used in tests instead of NewSimpleLogger(FromMinimalLogger).
369+func NewTestLogger(minLog interface {
370+ Output(int, string) error
371+}, level string) *TestLogger {
372+ h := &captureHelper{outputFunc: minLog.Output}
373+ log := &TestLogger{
374+ Logger: logger.NewSimpleLoggerFromMinimalLogger(h, level),
375+ helper: h,
376 }
377- return n, err
378-}
379-
380-func (buf *SyncedLogBuffer) String() string {
381- buf.lock.Lock()
382- defer buf.lock.Unlock()
383- return buf.Buffer.String()
384+ h.written = &log.Written
385+ return log
386+}
387+
388+// Captured returns accumulated log events.
389+func (tlog *TestLogger) Captured() string {
390+ return tlog.helper.captured()
391+}
392+
393+// Reset resets accumulated log events.
394+func (tlog *TestLogger) ResetCapture() {
395+ tlog.helper.reset()
396 }
397
398 // SourceRelative produces a path relative to the source code, makes

Subscribers

People subscribed via source and target branches