1// Copyright 2009 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package log
6
7// These tests are too simple.
8
9import (
10	"bytes"
11	"fmt"
12	"os"
13	"regexp"
14	"strings"
15	"testing"
16	"time"
17)
18
19const (
20	Rdate         = `[0-9][0-9][0-9][0-9]/[0-9][0-9]/[0-9][0-9]`
21	Rtime         = `[0-9][0-9]:[0-9][0-9]:[0-9][0-9]`
22	Rmicroseconds = `\.[0-9][0-9][0-9][0-9][0-9][0-9]`
23	Rline         = `(60|62):` // must update if the calls to l.Printf / l.Print below move
24	Rlongfile     = `.*/[A-Za-z0-9_\-]+\.go:` + Rline
25	Rshortfile    = `[A-Za-z0-9_\-]+\.go:` + Rline
26)
27
28type tester struct {
29	flag    int
30	prefix  string
31	pattern string // regexp that log output must match; we add ^ and expected_text$ always
32}
33
34var tests = []tester{
35	// individual pieces:
36	{0, "", ""},
37	{0, "XXX", "XXX"},
38	{Ldate, "", Rdate + " "},
39	{Ltime, "", Rtime + " "},
40	{Ltime | Lmsgprefix, "XXX", Rtime + " XXX"},
41	{Ltime | Lmicroseconds, "", Rtime + Rmicroseconds + " "},
42	{Lmicroseconds, "", Rtime + Rmicroseconds + " "}, // microsec implies time
43	{Llongfile, "", Rlongfile + " "},
44	{Lshortfile, "", Rshortfile + " "},
45	{Llongfile | Lshortfile, "", Rshortfile + " "}, // shortfile overrides longfile
46	// everything at once:
47	{Ldate | Ltime | Lmicroseconds | Llongfile, "XXX", "XXX" + Rdate + " " + Rtime + Rmicroseconds + " " + Rlongfile + " "},
48	{Ldate | Ltime | Lmicroseconds | Lshortfile, "XXX", "XXX" + Rdate + " " + Rtime + Rmicroseconds + " " + Rshortfile + " "},
49	{Ldate | Ltime | Lmicroseconds | Llongfile | Lmsgprefix, "XXX", Rdate + " " + Rtime + Rmicroseconds + " " + Rlongfile + " XXX"},
50	{Ldate | Ltime | Lmicroseconds | Lshortfile | Lmsgprefix, "XXX", Rdate + " " + Rtime + Rmicroseconds + " " + Rshortfile + " XXX"},
51}
52
53// Test using Println("hello", 23, "world") or using Printf("hello %d world", 23)
54func testPrint(t *testing.T, flag int, prefix string, pattern string, useFormat bool) {
55	buf := new(bytes.Buffer)
56	SetOutput(buf)
57	SetFlags(flag)
58	SetPrefix(prefix)
59	if useFormat {
60		Printf("hello %d world", 23)
61	} else {
62		Println("hello", 23, "world")
63	}
64	line := buf.String()
65	line = line[0 : len(line)-1]
66	pattern = "^" + pattern + "hello 23 world$"
67	matched, err := regexp.MatchString(pattern, line)
68	if err != nil {
69		t.Fatal("pattern did not compile:", err)
70	}
71	if !matched {
72		t.Errorf("log output should match %q is %q", pattern, line)
73	}
74	SetOutput(os.Stderr)
75}
76
77func TestDefault(t *testing.T) {
78	if got := Default(); got != std {
79		t.Errorf("Default [%p] should be std [%p]", got, std)
80	}
81}
82
83func TestAll(t *testing.T) {
84	for _, testcase := range tests {
85		testPrint(t, testcase.flag, testcase.prefix, testcase.pattern, false)
86		testPrint(t, testcase.flag, testcase.prefix, testcase.pattern, true)
87	}
88}
89
90func TestOutput(t *testing.T) {
91	const testString = "test"
92	var b bytes.Buffer
93	l := New(&b, "", 0)
94	l.Println(testString)
95	if expect := testString + "\n"; b.String() != expect {
96		t.Errorf("log output should match %q is %q", expect, b.String())
97	}
98}
99
100func TestOutputRace(t *testing.T) {
101	var b bytes.Buffer
102	l := New(&b, "", 0)
103	for i := 0; i < 100; i++ {
104		go func() {
105			l.SetFlags(0)
106		}()
107		l.Output(0, "")
108	}
109}
110
111func TestFlagAndPrefixSetting(t *testing.T) {
112	var b bytes.Buffer
113	l := New(&b, "Test:", LstdFlags)
114	f := l.Flags()
115	if f != LstdFlags {
116		t.Errorf("Flags 1: expected %x got %x", LstdFlags, f)
117	}
118	l.SetFlags(f | Lmicroseconds)
119	f = l.Flags()
120	if f != LstdFlags|Lmicroseconds {
121		t.Errorf("Flags 2: expected %x got %x", LstdFlags|Lmicroseconds, f)
122	}
123	p := l.Prefix()
124	if p != "Test:" {
125		t.Errorf(`Prefix: expected "Test:" got %q`, p)
126	}
127	l.SetPrefix("Reality:")
128	p = l.Prefix()
129	if p != "Reality:" {
130		t.Errorf(`Prefix: expected "Reality:" got %q`, p)
131	}
132	// Verify a log message looks right, with our prefix and microseconds present.
133	l.Print("hello")
134	pattern := "^Reality:" + Rdate + " " + Rtime + Rmicroseconds + " hello\n"
135	matched, err := regexp.Match(pattern, b.Bytes())
136	if err != nil {
137		t.Fatalf("pattern %q did not compile: %s", pattern, err)
138	}
139	if !matched {
140		t.Error("message did not match pattern")
141	}
142}
143
144func TestUTCFlag(t *testing.T) {
145	var b bytes.Buffer
146	l := New(&b, "Test:", LstdFlags)
147	l.SetFlags(Ldate | Ltime | LUTC)
148	// Verify a log message looks right in the right time zone. Quantize to the second only.
149	now := time.Now().UTC()
150	l.Print("hello")
151	want := fmt.Sprintf("Test:%d/%.2d/%.2d %.2d:%.2d:%.2d hello\n",
152		now.Year(), now.Month(), now.Day(), now.Hour(), now.Minute(), now.Second())
153	got := b.String()
154	if got == want {
155		return
156	}
157	// It's possible we crossed a second boundary between getting now and logging,
158	// so add a second and try again. This should very nearly always work.
159	now = now.Add(time.Second)
160	want = fmt.Sprintf("Test:%d/%.2d/%.2d %.2d:%.2d:%.2d hello\n",
161		now.Year(), now.Month(), now.Day(), now.Hour(), now.Minute(), now.Second())
162	if got == want {
163		return
164	}
165	t.Errorf("got %q; want %q", got, want)
166}
167
168func TestEmptyPrintCreatesLine(t *testing.T) {
169	var b bytes.Buffer
170	l := New(&b, "Header:", LstdFlags)
171	l.Print()
172	l.Println("non-empty")
173	output := b.String()
174	if n := strings.Count(output, "Header"); n != 2 {
175		t.Errorf("expected 2 headers, got %d", n)
176	}
177	if n := strings.Count(output, "\n"); n != 2 {
178		t.Errorf("expected 2 lines, got %d", n)
179	}
180}
181
182func BenchmarkItoa(b *testing.B) {
183	dst := make([]byte, 0, 64)
184	for i := 0; i < b.N; i++ {
185		dst = dst[0:0]
186		itoa(&dst, 2015, 4)   // year
187		itoa(&dst, 1, 2)      // month
188		itoa(&dst, 30, 2)     // day
189		itoa(&dst, 12, 2)     // hour
190		itoa(&dst, 56, 2)     // minute
191		itoa(&dst, 0, 2)      // second
192		itoa(&dst, 987654, 6) // microsecond
193	}
194}
195
196func BenchmarkPrintln(b *testing.B) {
197	const testString = "test"
198	var buf bytes.Buffer
199	l := New(&buf, "", LstdFlags)
200	for i := 0; i < b.N; i++ {
201		buf.Reset()
202		l.Println(testString)
203	}
204}
205
206func BenchmarkPrintlnNoFlags(b *testing.B) {
207	const testString = "test"
208	var buf bytes.Buffer
209	l := New(&buf, "", 0)
210	for i := 0; i < b.N; i++ {
211		buf.Reset()
212		l.Println(testString)
213	}
214}
215