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
=== modified file 'logger/logger.go'
--- logger/logger.go 2014-01-16 13:36:32 +0000
+++ logger/logger.go 2014-02-04 21:37:48 +0000
@@ -27,6 +27,8 @@
2727
28// Logger is a simple logger interface with logging at levels.28// Logger is a simple logger interface with logging at levels.
29type Logger interface {29type Logger interface {
30 // (Re)xpose base Output for logging events.
31 Output(calldept int, s string) error
30 // Errorf logs an error.32 // Errorf logs an error.
31 Errorf(format string, v ...interface{})33 Errorf(format string, v ...interface{})
32 // Fatalf logs an error and exists the program with os.Exit(1).34 // Fatalf logs an error and exists the program with os.Exit(1).
@@ -41,8 +43,8 @@
41}43}
4244
43type simpleLogger struct {45type simpleLogger struct {
44 *log.Logger46 outputFunc func(calldepth int, s string) error
45 nlevel int47 nlevel int
46}48}
4749
48const (50const (
@@ -57,24 +59,41 @@
57 "debug": lDebug,59 "debug": lDebug,
58}60}
5961
60// NewSimpleLogger creates a logger logging only up to the given level.62// NewSimpleLoggerFromMinimalLogger creates a logger logging only up
61// level can be in order: "error", "info", "debug".63// to the given level. level can be in order: "error", "info",
62func NewSimpleLogger(w io.Writer, level string) Logger {64// "debug". It takes a value just implementing stlib Logger.Output().
65func NewSimpleLoggerFromMinimalLogger(minLog interface {
66 Output(calldepth int, s string) error
67}, level string) Logger {
63 nlevel := levelToNLevel[level]68 nlevel := levelToNLevel[level]
64 return &simpleLogger{69 return &simpleLogger{
70 minLog.Output,
71 nlevel,
72 }
73}
74
75// NewSimpleLogger creates a logger logging only up to the given
76// level. level can be in order: "error", "info", "debug". It takes a
77// io.Writer.
78func NewSimpleLogger(w io.Writer, level string) Logger {
79 return NewSimpleLoggerFromMinimalLogger(
65 log.New(w, "", log.Ldate|log.Ltime|log.Lmicroseconds),80 log.New(w, "", log.Ldate|log.Ltime|log.Lmicroseconds),
66 nlevel,81 level,
67 }82 )
83}
84
85func (lg *simpleLogger) Output(calldepth int, s string) error {
86 return lg.outputFunc(calldepth+2, s)
68}87}
6988
70func (lg *simpleLogger) Errorf(format string, v ...interface{}) {89func (lg *simpleLogger) Errorf(format string, v ...interface{}) {
71 lg.Printf("ERROR "+format, v...)90 lg.outputFunc(2, fmt.Sprintf("ERROR "+format, v...))
72}91}
7392
74var osExit = os.Exit // for testing93var osExit = os.Exit // for testing
7594
76func (lg *simpleLogger) Fatalf(format string, v ...interface{}) {95func (lg *simpleLogger) Fatalf(format string, v ...interface{}) {
77 lg.Printf("ERROR "+format, v...)96 lg.outputFunc(2, fmt.Sprintf("ERROR "+format, v...))
78 osExit(1)97 osExit(1)
79}98}
8099
@@ -83,17 +102,17 @@
83 stack := make([]byte, 8*1024) // Stack writes less but doesn't fail102 stack := make([]byte, 8*1024) // Stack writes less but doesn't fail
84 stackWritten := runtime.Stack(stack, false)103 stackWritten := runtime.Stack(stack, false)
85 stack = stack[:stackWritten]104 stack = stack[:stackWritten]
86 lg.Printf("ERROR(PANIC) %s:\n%s", msg, stack)105 lg.outputFunc(2, fmt.Sprintf("ERROR(PANIC) %s:\n%s", msg, stack))
87}106}
88107
89func (lg *simpleLogger) Infof(format string, v ...interface{}) {108func (lg *simpleLogger) Infof(format string, v ...interface{}) {
90 if lg.nlevel >= lInfo {109 if lg.nlevel >= lInfo {
91 lg.Printf("INFO "+format, v...)110 lg.outputFunc(2, fmt.Sprintf("INFO "+format, v...))
92 }111 }
93}112}
94113
95func (lg *simpleLogger) Debugf(format string, v ...interface{}) {114func (lg *simpleLogger) Debugf(format string, v ...interface{}) {
96 if lg.nlevel >= lDebug {115 if lg.nlevel >= lDebug {
97 lg.Printf("DEBUG "+format, v...)116 lg.outputFunc(2, fmt.Sprintf("DEBUG "+format, v...))
98 }117 }
99}118}
100119
=== modified file 'logger/logger_test.go'
--- logger/logger_test.go 2014-01-16 13:36:32 +0000
+++ logger/logger_test.go 2014-02-04 21:37:48 +0000
@@ -20,6 +20,7 @@
20 "bytes"20 "bytes"
21 "fmt"21 "fmt"
22 . "launchpad.net/gocheck"22 . "launchpad.net/gocheck"
23 "log"
23 "os"24 "os"
24 "runtime"25 "runtime"
25 "testing"26 "testing"
@@ -127,3 +128,12 @@
127 panicAndRecover(logger, 6, false, &line, &ok)128 panicAndRecover(logger, 6, false, &line, &ok)
128 c.Check(buf.String(), Equals, "")129 c.Check(buf.String(), Equals, "")
129}130}
131
132func (s *loggerSuite) TestReexposeOutput(c *C) {
133 buf := &bytes.Buffer{}
134 baselog := log.New(buf, "", log.Lshortfile)
135 logger := NewSimpleLoggerFromMinimalLogger(baselog, "error")
136 baselog.Output(1, "foobar")
137 logger.Output(1, "foobaz")
138 c.Check(buf.String(), Matches, "logger_test.go:[0-9]+: foobar\nlogger_test.go:[0-9]+: foobaz\n")
139}
130140
=== modified file 'server/broker/simple/suite_test.go'
--- server/broker/simple/suite_test.go 2014-02-03 15:13:00 +0000
+++ server/broker/simple/suite_test.go 2014-02-04 21:37:48 +0000
@@ -32,13 +32,13 @@
32}32}
3333
34var _ = Suite(&commonBrokerSuite{testsuite.CommonBrokerSuite{34var _ = Suite(&commonBrokerSuite{testsuite.CommonBrokerSuite{
35 func(sto store.PendingStore, cfg broker.BrokerConfig, log logger.Logger) testsuite.FullBroker {35 MakeBroker: func(sto store.PendingStore, cfg broker.BrokerConfig, log logger.Logger) testsuite.FullBroker {
36 return NewSimpleBroker(sto, cfg, log)36 return NewSimpleBroker(sto, cfg, log)
37 },37 },
38 func(b broker.Broker, deviceId string) broker.BrokerSession {38 RevealSession: func(b broker.Broker, deviceId string) broker.BrokerSession {
39 return b.(*SimpleBroker).registry[deviceId]39 return b.(*SimpleBroker).registry[deviceId]
40 },40 },
41 func(exchg broker.Exchange) *broker.BroadcastExchange {41 RevealBroadcastExchange: func(exchg broker.Exchange) *broker.BroadcastExchange {
42 return exchg.(*broker.BroadcastExchange)42 return exchg.(*broker.BroadcastExchange)
43 },43 },
44}})44}})
4545
=== modified file 'server/broker/testsuite/suite.go'
--- server/broker/testsuite/suite.go 2014-02-03 15:13:00 +0000
+++ server/broker/testsuite/suite.go 2014-02-04 21:37:48 +0000
@@ -48,6 +48,12 @@
48 RevealSession func(broker.Broker, string) broker.BrokerSession48 RevealSession func(broker.Broker, string) broker.BrokerSession
49 // Let us get to a broker.BroadcastExchange from an Exchange.49 // Let us get to a broker.BroadcastExchange from an Exchange.
50 RevealBroadcastExchange func(broker.Exchange) *broker.BroadcastExchange50 RevealBroadcastExchange func(broker.Exchange) *broker.BroadcastExchange
51 // private
52 testlog *helpers.TestLogger
53}
54
55func (s *CommonBrokerSuite) SetUpTest(c *C) {
56 s.testlog = helpers.NewTestLogger(c, "error")
51}57}
5258
53var testBrokerConfig = &testing.TestBrokerConfig{10, 5}59var testBrokerConfig = &testing.TestBrokerConfig{10, 5}
@@ -110,16 +116,14 @@
110}116}
111117
112func (s *CommonBrokerSuite) TestRegistrationFeedPendingError(c *C) {118func (s *CommonBrokerSuite) TestRegistrationFeedPendingError(c *C) {
113 buf := &helpers.SyncedLogBuffer{}
114 logger := logger.NewSimpleLogger(buf, "error")
115 sto := &testFailingStore{}119 sto := &testFailingStore{}
116 b := s.MakeBroker(sto, testBrokerConfig, logger)120 b := s.MakeBroker(sto, testBrokerConfig, s.testlog)
117 b.Start()121 b.Start()
118 defer b.Stop()122 defer b.Stop()
119 _, err := b.Register(&protocol.ConnectMsg{Type: "connect", DeviceId: "dev-1"})123 _, err := b.Register(&protocol.ConnectMsg{Type: "connect", DeviceId: "dev-1"})
120 c.Assert(err, IsNil)124 c.Assert(err, IsNil)
121 // but125 // but
122 c.Check(buf.String(), Matches, ".*ERROR unsuccessful feed pending, get channel snapshot for 0: get channel snapshot fail\n")126 c.Check(s.testlog.Captured(), Matches, "ERROR unsuccessful feed pending, get channel snapshot for 0: get channel snapshot fail\n")
123}127}
124128
125func (s *CommonBrokerSuite) TestRegistrationLastWins(c *C) {129func (s *CommonBrokerSuite) TestRegistrationLastWins(c *C) {
@@ -188,10 +192,9 @@
188}192}
189193
190func (s *CommonBrokerSuite) TestBroadcastFail(c *C) {194func (s *CommonBrokerSuite) TestBroadcastFail(c *C) {
191 buf := &helpers.SyncedLogBuffer{Written: make(chan bool, 1)}195 s.testlog.Written = make(chan bool, 1)
192 logger := logger.NewSimpleLogger(buf, "error")
193 sto := &testFailingStore{countdownToFail: 1}196 sto := &testFailingStore{countdownToFail: 1}
194 b := s.MakeBroker(sto, testBrokerConfig, logger)197 b := s.MakeBroker(sto, testBrokerConfig, s.testlog)
195 b.Start()198 b.Start()
196 defer b.Stop()199 defer b.Stop()
197 _, err := b.Register(&protocol.ConnectMsg{Type: "connect", DeviceId: "dev-1"})200 _, err := b.Register(&protocol.ConnectMsg{Type: "connect", DeviceId: "dev-1"})
@@ -200,7 +203,7 @@
200 select {203 select {
201 case <-time.After(5 * time.Second):204 case <-time.After(5 * time.Second):
202 c.Fatal("taking too long to log error")205 c.Fatal("taking too long to log error")
203 case <-buf.Written:206 case <-s.testlog.Written:
204 }207 }
205 c.Check(buf.String(), Matches, ".*ERROR unsuccessful broadcast, get channel snapshot for 0: get channel snapshot fail\n")208 c.Check(s.testlog.Captured(), Matches, "ERROR unsuccessful broadcast, get channel snapshot for 0: get channel snapshot fail\n")
206}209}
207210
=== modified file 'server/listener/listener_test.go'
--- server/listener/listener_test.go 2014-01-21 21:36:07 +0000
+++ server/listener/listener_test.go 2014-02-04 21:37:48 +0000
@@ -20,7 +20,6 @@
20 "crypto/tls"20 "crypto/tls"
21 "crypto/x509"21 "crypto/x509"
22 . "launchpad.net/gocheck"22 . "launchpad.net/gocheck"
23 "launchpad.net/ubuntu-push/logger"
24 helpers "launchpad.net/ubuntu-push/testing"23 helpers "launchpad.net/ubuntu-push/testing"
25 "net"24 "net"
26 "syscall"25 "syscall"
@@ -30,7 +29,9 @@
3029
31func TestListener(t *testing.T) { TestingT(t) }30func TestListener(t *testing.T) { TestingT(t) }
3231
33type listenerSuite struct{}32type listenerSuite struct {
33 testlog *helpers.TestLogger
34}
3435
35var _ = Suite(&listenerSuite{})36var _ = Suite(&listenerSuite{})
3637
@@ -50,6 +51,10 @@
50 }51 }
51}52}
5253
54func (s *listenerSuite) SetUpTest(c *C) {
55 s.testlog = helpers.NewTestLogger(c, "error")
56}
57
53type testDevListenerCfg struct {58type testDevListenerCfg struct {
54 addr string59 addr string
55}60}
@@ -135,14 +140,12 @@
135}140}
136141
137func (s *listenerSuite) TestDeviceAcceptLoop(c *C) {142func (s *listenerSuite) TestDeviceAcceptLoop(c *C) {
138 buf := &helpers.SyncedLogBuffer{}
139 logger := logger.NewSimpleLogger(buf, "error")
140 lst, err := DeviceListen(&testDevListenerCfg{"127.0.0.1:0"})143 lst, err := DeviceListen(&testDevListenerCfg{"127.0.0.1:0"})
141 c.Check(err, IsNil)144 c.Check(err, IsNil)
142 defer lst.Close()145 defer lst.Close()
143 errCh := make(chan error)146 errCh := make(chan error)
144 go func() {147 go func() {
145 errCh <- lst.AcceptLoop(testSession, logger)148 errCh <- lst.AcceptLoop(testSession, s.testlog)
146 }()149 }()
147 listenerAddr := lst.Addr().String()150 listenerAddr := lst.Addr().String()
148 conn1, err := testTlsDial(c, listenerAddr)151 conn1, err := testTlsDial(c, listenerAddr)
@@ -157,12 +160,10 @@
157 testReadByte(c, conn2, '2')160 testReadByte(c, conn2, '2')
158 lst.Close()161 lst.Close()
159 c.Check(<-errCh, ErrorMatches, ".*use of closed.*")162 c.Check(<-errCh, ErrorMatches, ".*use of closed.*")
160 c.Check(buf.String(), Equals, "")163 c.Check(s.testlog.Captured(), Equals, "")
161}164}
162165
163func (s *listenerSuite) TestDeviceAcceptLoopTemporaryError(c *C) {166func (s *listenerSuite) TestDeviceAcceptLoopTemporaryError(c *C) {
164 buf := &helpers.SyncedLogBuffer{}
165 logger := logger.NewSimpleLogger(buf, "error")
166 // ENFILE is not the temp network error we want to handle this way167 // ENFILE is not the temp network error we want to handle this way
167 // but is relatively easy to generate in a controlled way168 // but is relatively easy to generate in a controlled way
168 var err error169 var err error
@@ -171,7 +172,7 @@
171 defer lst.Close()172 defer lst.Close()
172 errCh := make(chan error)173 errCh := make(chan error)
173 go func() {174 go func() {
174 errCh <- lst.AcceptLoop(testSession, logger)175 errCh <- lst.AcceptLoop(testSession, s.testlog)
175 }()176 }()
176 listenerAddr := lst.Addr().String()177 listenerAddr := lst.Addr().String()
177 conns := make([]net.Conn, 0, NofileMax)178 conns := make([]net.Conn, 0, NofileMax)
@@ -195,12 +196,10 @@
195 testReadByte(c, conn2, '2')196 testReadByte(c, conn2, '2')
196 lst.Close()197 lst.Close()
197 c.Check(<-errCh, ErrorMatches, ".*use of closed.*")198 c.Check(<-errCh, ErrorMatches, ".*use of closed.*")
198 c.Check(buf.String(), Matches, ".*device listener:.*accept.*too many open.*-- retrying\n")199 c.Check(s.testlog.Captured(), Matches, ".*device listener:.*accept.*too many open.*-- retrying\n")
199}200}
200201
201func (s *listenerSuite) TestDeviceAcceptLoopPanic(c *C) {202func (s *listenerSuite) TestDeviceAcceptLoopPanic(c *C) {
202 buf := &helpers.SyncedLogBuffer{}
203 logger := logger.NewSimpleLogger(buf, "error")
204 lst, err := DeviceListen(&testDevListenerCfg{"127.0.0.1:0"})203 lst, err := DeviceListen(&testDevListenerCfg{"127.0.0.1:0"})
205 c.Check(err, IsNil)204 c.Check(err, IsNil)
206 defer lst.Close()205 defer lst.Close()
@@ -209,12 +208,12 @@
209 errCh <- lst.AcceptLoop(func(conn net.Conn) error {208 errCh <- lst.AcceptLoop(func(conn net.Conn) error {
210 defer conn.Close()209 defer conn.Close()
211 panic("session crash")210 panic("session crash")
212 }, logger)211 }, s.testlog)
213 }()212 }()
214 listenerAddr := lst.Addr().String()213 listenerAddr := lst.Addr().String()
215 _, err = testTlsDial(c, listenerAddr)214 _, err = testTlsDial(c, listenerAddr)
216 c.Assert(err, Not(IsNil))215 c.Assert(err, Not(IsNil))
217 lst.Close()216 lst.Close()
218 c.Check(<-errCh, ErrorMatches, ".*use of closed.*")217 c.Check(<-errCh, ErrorMatches, ".*use of closed.*")
219 c.Check(buf.String(), Matches, "(?s).* ERROR\\(PANIC\\) terminating device connection on: session crash:.*AcceptLoop.*")218 c.Check(s.testlog.Captured(), Matches, "(?s)ERROR\\(PANIC\\) terminating device connection on: session crash:.*AcceptLoop.*")
220}219}
221220
=== modified file 'testing/helpers.go'
--- testing/helpers.go 2014-01-21 21:36:07 +0000
+++ testing/helpers.go 2014-02-04 21:37:48 +0000
@@ -18,34 +18,74 @@
18package testing18package testing
1919
20import (20import (
21 "bytes"21 "launchpad.net/ubuntu-push/logger"
22 "path/filepath"22 "path/filepath"
23 "runtime"23 "runtime"
24 "strings"
24 "sync"25 "sync"
25)26)
2627
27// SyncedLogBuffer can be used with NewSimpleLogger avoiding races28type captureHelper struct {
28// when checking the logging done from different goroutines.29 outputFunc func(int, string) error
29type SyncedLogBuffer struct {30 lock sync.Mutex
30 bytes.Buffer31 logEvents []string
31 lock sync.Mutex32 written *chan bool
33}
34
35func (h *captureHelper) Output(calldepth int, s string) error {
36 err := h.outputFunc(calldepth+2, s)
37 if err == nil {
38 h.lock.Lock()
39 defer h.lock.Unlock()
40 if *h.written != nil {
41 *h.written <- true
42 }
43 h.logEvents = append(h.logEvents, s+"\n")
44 }
45 return err
46}
47
48func (h *captureHelper) captured() string {
49 h.lock.Lock()
50 defer h.lock.Unlock()
51 return strings.Join(h.logEvents, "")
52}
53
54func (h *captureHelper) reset() {
55 h.lock.Lock()
56 defer h.lock.Unlock()
57 h.logEvents = nil
58}
59
60// TestLogger implements logger.Logger using gocheck.C and supporting
61// capturing log strings.
62type TestLogger struct {
63 logger.Logger
64 helper *captureHelper
32 Written chan bool65 Written chan bool
33}66}
3467
35func (buf *SyncedLogBuffer) Write(b []byte) (int, error) {68// NewTestLogger can be used in tests instead of NewSimpleLogger(FromMinimalLogger).
36 buf.lock.Lock()69func NewTestLogger(minLog interface {
37 defer buf.lock.Unlock()70 Output(int, string) error
38 n, err := buf.Buffer.Write(b)71}, level string) *TestLogger {
39 if buf.Written != nil {72 h := &captureHelper{outputFunc: minLog.Output}
40 buf.Written <- true73 log := &TestLogger{
74 Logger: logger.NewSimpleLoggerFromMinimalLogger(h, level),
75 helper: h,
41 }76 }
42 return n, err77 h.written = &log.Written
43}78 return log
4479}
45func (buf *SyncedLogBuffer) String() string {80
46 buf.lock.Lock()81// Captured returns accumulated log events.
47 defer buf.lock.Unlock()82func (tlog *TestLogger) Captured() string {
48 return buf.Buffer.String()83 return tlog.helper.captured()
84}
85
86// Reset resets accumulated log events.
87func (tlog *TestLogger) ResetCapture() {
88 tlog.helper.reset()
49}89}
5090
51// SourceRelative produces a path relative to the source code, makes91// SourceRelative produces a path relative to the source code, makes

Subscribers

People subscribed via source and target branches