1package clog
2
3import (
4	"bytes"
5	"fmt"
6	"io/ioutil"
7	"log"
8	"os"
9	"strings"
10	"testing"
11)
12
13func TestLastComponent(t *testing.T) {
14	tests := map[string]string{
15		"plain":     "plain",
16		"/a/b/c":    "c",
17		"\\a\\b\\c": "c",
18		"a/b/c":     "c",
19		"a\\b\\c":   "c",
20	}
21
22	for in, exp := range tests {
23		got := lastComponent(in)
24		if got != exp {
25			t.Errorf("Expected %q for %q, got %q", exp, in, got)
26		}
27	}
28}
29
30func TestFlags(t *testing.T) {
31	if Flags() != log.LstdFlags {
32		t.Errorf("Expected LstdFlags by default")
33	}
34	SetFlags(0x01234)
35	if Flags() != 0x01234 {
36		t.Errorf("Expected SetFlags() to work")
37	}
38	SetFlags(log.LstdFlags) // Leave clog as it was.
39}
40
41func TestParseLogFlags(t *testing.T) {
42	defer SetOutput(os.Stderr)
43	SetOutput(ioutil.Discard)
44	ParseLogFlag("parsetest1,parsetest2+,bw,notime")
45	exp := map[string]bool{"parsetest1": true, "parsetest1+": false,
46		"parsetest2": true, "parsetest2+": true,
47		"parsetest3": false,
48		"bw":         false,
49		"notime":     false}
50	for k, v := range exp {
51		if KeyEnabled(k) != v {
52			t.Errorf("Expected %v enabled=%v, was %v",
53				k, v, KeyEnabled(k))
54		}
55	}
56}
57
58func TestParseLogFlagsEmpty(t *testing.T) {
59	defer SetOutput(os.Stderr)
60	SetOutput(ioutil.Discard)
61	ParseLogFlag("")
62}
63
64func TestGetCallersName(t *testing.T) {
65	cn := getCallersName(0)
66	if lastComponent(cn.filename) != "clog_test.go" {
67		t.Errorf("Expected fn=clog_test.go, got %q",
68			lastComponent(cn.filename))
69	}
70	if lastComponent(cn.funcname) != "clog.TestGetCallersName" {
71		t.Errorf("Expected func=clog.TestGetCallersName, got %q",
72			lastComponent(cn.funcname))
73	}
74	cn.String() // for side effect
75	if cn = getCallersName(19); cn.String() != "???" {
76		t.Errorf("Expected unknown call, got %q", cn.String())
77	}
78}
79
80func TestKeyFlag(t *testing.T) {
81	EnableKey("x")
82	EnableKey("y")
83	DisableKey("y")
84
85	if !KeyEnabled("x") {
86		t.Errorf("x should be enabled, but isn't")
87	}
88	if KeyEnabled("y") {
89		t.Errorf("y should not be enabled, but is")
90	}
91	if KeyEnabled("z") {
92		t.Errorf("z should not be enabled, but is")
93	}
94}
95
96func TestOutput(t *testing.T) {
97	// reset the log when we're done
98	defer SetOutput(os.Stderr)
99
100	type niladic func()
101	tests := []struct {
102		f        niladic
103		output   string
104		exitVal  int
105		panicked bool
106	}{
107		{
108			func() {
109				Log("testing %s", "123")
110			},
111			"testing 123", -1, false,
112		},
113		{
114			func() {
115				EnableKey("private")
116				To("private", "testing %s", "123")
117			},
118			fgYellow + "private: " + reset + "testing 123", -1, false,
119		},
120		{
121			func() {
122				EnableKey("private")
123				DisableKey("private")
124				To("private", "testing %s", "123")
125			},
126			"", -1, false,
127		},
128		{
129			func() {
130				Printf("testing %s", "123")
131			},
132			"testing 123", -1, false,
133		},
134		{
135			func() {
136				Print("testing", "123")
137			},
138			"testing123", -1, false,
139		},
140		{
141			func() {
142				Error(fmt.Errorf("test error"))
143			},
144			fgRed + "ERRO: " + "test error" + reset, -1, false,
145		},
146		{
147			func() {
148				Errorf("TestOutput, err: %v", fmt.Errorf("test error"))
149			},
150			fgRed + "ERRO: " + "TestOutput, err: test error" + reset, -1, false,
151		},
152		{
153			func() {
154				Warnf("testing %s", "123")
155			},
156			fgRed + "WARN: " + "testing 123" + reset, -1, false,
157		},
158		{
159			func() {
160				Warn("testing", "123")
161			},
162			fgRed + "WARN: " + "testing123" + reset, -1, false,
163		},
164		{
165			func() {
166				TEMPf("testing %s", "123")
167			},
168			fgYellow + "TEMP: " + "testing 123" + reset, -1, false,
169		},
170		{
171			func() {
172				TEMP("testing", "123")
173			},
174			fgYellow + "TEMP: " + "testing123" + reset, -1, false,
175		},
176		{
177			func() {
178				Fatal("testing", "123")
179			},
180			fgYellow + "FATA: " + "testing123" + reset, 1, false,
181		},
182		{
183			func() {
184				Fatalf("testing12%d", 3)
185			},
186			fgYellow + "FATA: " + "testing123" + reset, 1, false,
187		},
188		{
189			func() {
190				Panic("testing", "123")
191			},
192			fgYellow + "CRIT: " + "testing123" + reset, -1, true,
193		},
194		{
195			func() {
196				Panicf("testing12%d", 3)
197			},
198			fgYellow + "CRIT: " + "testing123" + reset, -1, true,
199		},
200	}
201
202	exitVal := -1
203	exit = func(i int) { exitVal = i }
204	defer func() { exit = os.Exit }()
205
206	for _, test := range tests {
207		// reset our log buffer
208		buffer := &bytes.Buffer{}
209		SetOutput(buffer)
210		// disable time so we can more easily compare
211		DisableTime()
212		exitVal = -1
213		panicked := false
214		func() {
215			defer func() { panicked = recover() != nil }()
216			test.f()
217		}()
218		if panicked != test.panicked {
219			t.Errorf("Expected panic == %v, got %v", test.panicked, panicked)
220		}
221		if exitVal != test.exitVal {
222			t.Errorf("Expected exitVal == %v, but got %v", test.exitVal, exitVal)
223		}
224		if buffer.Len() > 0 {
225			usedBytes := buffer.Bytes()[:buffer.Len()-1]
226			output := string(usedBytes)
227			// strip off the caller info as we can't easily compare that
228			callerLocation := strings.LastIndex(output, dim+" -- ")
229			if callerLocation >= 0 {
230				output = output[:callerLocation]
231			}
232			if output != test.output {
233				t.Errorf("Expected '%s' got '%s'", test.output, output)
234			}
235		} else {
236			if test.output != "" {
237				t.Errorf("Expected output `%s`, got none", test.output)
238			}
239		}
240	}
241}
242
243func TestRedactions(t *testing.T) {
244	logCB := func(format string, args ...interface{}) string {
245		return fmt.Sprintf(format, args...)
246	}
247
248	str := logCB("test1: %s, key: %q, seq: %v", "error",
249		Tag(ContentCategory(100), []byte("k123")), 12312)
250	expect := "test1: error, key: \"k123\", seq: 12312"
251	if str != expect {
252		t.Errorf("Unexpected output: [%v != %v]", str, expect)
253	}
254
255	str = logCB("test2: %s, key: %q, seq: %v", "error",
256		Tag(UserData, "k123"), 12312)
257	expect = "test2: error, key: \"<ud>k123</ud>\", seq: 12312"
258	if str != expect {
259		t.Errorf("Unexpected output: [%v != %v]", str, expect)
260	}
261
262	str = logCB("test3: %s, key: %q, seq: %v", "error",
263		Tag(UserData, []byte("k123")), 12312)
264	expect = "test3: error, key: \"<ud>k123</ud>\", seq: 12312"
265	if str != expect {
266		t.Errorf("Unexpected output: [%v != %v]", str, expect)
267	}
268}
269
270func BenchmarkFlagLookupMiss(b *testing.B) {
271	for i := 0; i < b.N; i++ {
272		KeyEnabled("x")
273	}
274}
275
276func BenchmarkFlagLookupHit(b *testing.B) {
277	EnableKey("x")
278	b.ResetTimer()
279	for i := 0; i < b.N; i++ {
280		KeyEnabled("x")
281	}
282}
283
284func BenchmarkFlagSet(b *testing.B) {
285	for i := 0; i < b.N; i++ {
286		EnableKey("x")
287	}
288}
289
290func BenchmarkToDisabled(b *testing.B) {
291	defer SetOutput(os.Stderr)
292	SetOutput(ioutil.Discard)
293	DisableKey("btoe")
294	b.ResetTimer()
295
296	for i := 0; i < b.N; i++ {
297		To("btod", "thing")
298	}
299}
300
301func BenchmarkToEnabled(b *testing.B) {
302	defer SetOutput(os.Stderr)
303	SetOutput(ioutil.Discard)
304	EnableKey("btoe")
305	b.ResetTimer()
306
307	for i := 0; i < b.N; i++ {
308		To("btoe", "thing")
309	}
310}
311
312func BenchmarkToWithFmt(b *testing.B) {
313	defer SetOutput(os.Stderr)
314	SetOutput(ioutil.Discard)
315	EnableKey("btoe")
316	b.ResetTimer()
317
318	for i := 0; i < b.N; i++ {
319		To("btoe", "%s", "a string")
320	}
321}
322