1 package ldlogtest
2
3 import (
4 "fmt"
5 "io"
6 "os"
7 "regexp"
8 "strings"
9 "sync"
10 "testing"
11
12 "github.com/stretchr/testify/assert"
13
14 "github.com/launchdarkly/go-sdk-common/v3/ldlog"
15 )
16
17
18 type MockLogItem struct {
19 Level ldlog.LogLevel
20 Message string
21 }
22
23
24
25
26
27
28
29
30
31
32 type MockLog struct {
33
34 Loggers ldlog.Loggers
35
36 output map[ldlog.LogLevel][]string
37
38 allOutput []MockLogItem
39 lock sync.Mutex
40 }
41
42
43 func NewMockLog() *MockLog {
44 ret := &MockLog{output: make(map[ldlog.LogLevel][]string)}
45 for _, level := range []ldlog.LogLevel{ldlog.Debug, ldlog.Info, ldlog.Warn, ldlog.Error} {
46 ret.Loggers.SetBaseLoggerForLevel(level, mockBaseLogger{owner: ret, level: level})
47 }
48 return ret
49 }
50
51
52 func (ml *MockLog) GetOutput(level ldlog.LogLevel) []string {
53 ml.lock.Lock()
54 defer ml.lock.Unlock()
55 lines := ml.output[level]
56 ret := make([]string, len(lines))
57 copy(ret, lines)
58 return ret
59 }
60
61
62 func (ml *MockLog) GetAllOutput() []MockLogItem {
63 ml.lock.Lock()
64 defer ml.lock.Unlock()
65 ret := make([]MockLogItem, len(ml.allOutput))
66 copy(ret, ml.allOutput)
67 return ret
68 }
69
70
71 func (ml *MockLog) HasMessageMatch(level ldlog.LogLevel, pattern string) bool {
72 _, found := ml.findMessageMatching(level, pattern)
73 return found
74 }
75
76
77
78
79 func (ml *MockLog) AssertMessageMatch(t *testing.T, shouldMatch bool, level ldlog.LogLevel, pattern string) {
80 line, hasMatch := ml.findMessageMatching(level, pattern)
81 if hasMatch != shouldMatch {
82 if shouldMatch {
83 assert.Fail(
84 t,
85 "log did not contain expected message",
86 "level: %s, pattern: /%s/, messages: %v",
87 level,
88 pattern,
89 ml.GetOutput(level),
90 )
91 } else {
92 assert.Fail(
93 t,
94 "log contained unexpected message",
95 "level: %s, message: [%s]",
96 level,
97 line,
98 )
99 }
100 }
101 }
102
103
104 func (ml *MockLog) Dump(w io.Writer) {
105 for _, line := range ml.GetAllOutput() {
106 fmt.Fprintln(w, line.Level.Name()+": "+line.Message)
107 }
108 }
109
110
111
112
113
114
115
116
117
118
119
120
121 func (ml *MockLog) DumpIfTestFailed(t *testing.T) {
122 if t.Failed() {
123 ml.Dump(os.Stdout)
124 }
125 }
126
127 func (ml *MockLog) logLine(level ldlog.LogLevel, line string) {
128 ml.lock.Lock()
129 defer ml.lock.Unlock()
130 message := strings.TrimPrefix(line, strings.ToUpper(level.String())+": ")
131 ml.output[level] = append(ml.output[level], message)
132 ml.allOutput = append(ml.allOutput, MockLogItem{level, message})
133 }
134
135 func (ml *MockLog) findMessageMatching(level ldlog.LogLevel, pattern string) (string, bool) {
136 r := regexp.MustCompile(pattern)
137 for _, line := range ml.GetOutput(level) {
138 if r.MatchString(line) {
139 return line, true
140 }
141 }
142 return "", false
143 }
144
145 type mockBaseLogger struct {
146 owner *MockLog
147 level ldlog.LogLevel
148 }
149
150 func (l mockBaseLogger) Println(values ...interface{}) {
151 l.owner.logLine(l.level, strings.TrimSuffix(fmt.Sprintln(values...), "\n"))
152 }
153
154 func (l mockBaseLogger) Printf(format string, values ...interface{}) {
155 l.owner.logLine(l.level, fmt.Sprintf(format, values...))
156 }
157
View as plain text