git » log » master » tree

[master] / log_test.go

package log

import (
	"io"
	"io/ioutil"
	"os"
	"regexp"
	"testing"
)

func mustNewFile(t *testing.T) (string, *Logger) {
	f, err := ioutil.TempFile("", "log_test-")
	if err != nil {
		t.Fatalf("failed to create temp file: %v", err)
	}

	l, err := NewFile(f.Name())
	if err != nil {
		t.Fatalf("failed to open new log file: %v", err)
	}

	return f.Name(), l
}

func checkContentsMatch(t *testing.T, name, path, expected string) {
	content, err := ioutil.ReadFile(path)
	if err != nil {
		panic(err)
	}

	got := string(content)
	if !regexp.MustCompile(expected).Match(content) {
		t.Errorf("%s: regexp %q did not match %q",
			name, expected, got)
	}
}

func testLogger(t *testing.T, fname string, l *Logger) {
	l.LogTime = false
	l.Infof("message %d", 1)
	checkContentsMatch(t, "info-no-time", fname,
		"^_ log_test.go:....   message 1\n")

	os.Truncate(fname, 0)
	l.Infof("message %d\n", 1)
	checkContentsMatch(t, "info-with-newline", fname,
		"^_ log_test.go:....   message 1\n")

	os.Truncate(fname, 0)
	l.LogTime = true
	l.Infof("message %d", 1)
	checkContentsMatch(t, "info-with-time", fname,
		`^....-..-.. ..:..:..\.\d{6} _ log_test.go:....   message 1\n`)

	os.Truncate(fname, 0)
	l.LogTime = false
	l.Errorf("error %d", 1)
	checkContentsMatch(t, "error", fname, `^E log_test.go:....   error 1\n`)

	if l.V(Debug) {
		t.Fatalf("Debug level enabled by default (level: %v)", l.Level)
	}

	os.Truncate(fname, 0)
	l.LogTime = false
	l.Debugf("debug %d", 1)
	checkContentsMatch(t, "debug-no-log", fname, `^$`)

	os.Truncate(fname, 0)
	l.Level = Debug
	l.Debugf("debug %d", 1)
	checkContentsMatch(t, "debug", fname, `^\. log_test.go:....   debug 1\n`)

	if !l.V(Debug) {
		t.Errorf("l.Level = Debug, but V(Debug) = false")
	}

	os.Truncate(fname, 0)
	l.Level = Info
	l.Log(Debug, 0, "log debug %d", 1)
	l.Log(Info, 0, "log info %d", 1)
	checkContentsMatch(t, "log", fname,
		`^_ log_test.go:....   log info 1\n`)

	os.Truncate(fname, 0)
	l.Level = Info
	l.Log(Fatal, 0, "log fatal %d", 1)
	checkContentsMatch(t, "log", fname,
		`^☠ log_test.go:....   log fatal 1\n`)

	// Test some combinations of options.
	cases := []struct {
		name      string
		logTime   bool
		logLevel  bool
		logCaller bool
		expected  string
	}{
		{
			"show everything",
			true, true, true,
			`^....-..-.. ..:..:..\.\d{6} _ log_test.go:....   message 1\n`,
		}, {
			"caller+level",
			false, true, true,
			`^_ log_test.go:....   message 1\n`,
		}, {
			"time",
			true, false, false,
			`^....-..-.. ..:..:..\.\d{6} message 1\n`,
		}, {
			"none",
			false, false, false,
			`message 1\n`,
		},
	}
	for _, c := range cases {
		os.Truncate(fname, 0)
		l.LogTime = c.logTime
		l.LogLevel = c.logLevel
		l.LogCaller = c.logCaller
		l.Infof("message %d", 1)
		checkContentsMatch(t, c.name, fname, c.expected)
	}
}

func TestBasic(t *testing.T) {
	fname, l := mustNewFile(t)
	defer l.Close()
	defer os.Remove(fname)

	testLogger(t, fname, l)
}

func TestDefaultFile(t *testing.T) {
	fname, l := mustNewFile(t)
	l.Close()
	defer os.Remove(fname)

	*logFile = fname

	Init()

	testLogger(t, fname, Default)
}

func TestReopen(t *testing.T) {
	fname, l := mustNewFile(t)
	defer l.Close()
	defer os.Remove(fname)
	l.LogTime = false

	l.Infof("pre rename")
	checkContentsMatch(t, "r", fname, `^_ log_test.go:....   pre rename\n`)

	os.Rename(fname, fname+"-m")
	defer os.Remove(fname + "-m")
	l.Infof("post rename")
	checkContentsMatch(t, "r", fname+"-m", `pre rename\n.* post rename`)

	if err := l.Reopen(); err != nil {
		t.Errorf("reopen: %v", err)
	}
	l.Infof("post reopen")
	checkContentsMatch(t, "r", fname, `^_ log_test.go:....   post reopen\n`)

	// NewFile with an absolute path should resolve it internally to a full
	// one, so reopen can work.
	l, err := NewFile("test-relative-file")
	defer l.Close()
	defer os.Remove("test-relative-file")

	if err != nil {
		t.Fatalf("failed to open file for testing: %v", err)
	}
	if l.fname[0] != '/' {
		t.Fatalf("internal fname is not absolute: %q", l.fname)
	}

}

type nopCloser struct {
	io.Writer
}

func (nopCloser) Close() error { return nil }

func TestReopenNull(t *testing.T) {
	l := New(nopCloser{ioutil.Discard})
	defer l.Close()
	if err := l.Reopen(); err != nil {
		t.Errorf("reopen: %v", err)
	}
}

// Benchmark a call below the verbosity level.
func BenchmarkDebugf(b *testing.B) {
	l := New(nopCloser{ioutil.Discard})
	defer l.Close()
	for i := 0; i < b.N; i++ {
		l.Debugf("test %d", i)
	}
}

// Benchmark a normal call.
func BenchmarkInfof(b *testing.B) {
	l := New(nopCloser{ioutil.Discard})
	defer l.Close()
	for i := 0; i < b.N; i++ {
		l.Infof("test %d", i)
	}
}