github.com/cellofellow/gopkg@v0.0.0-20140722061823-eec0544a62ad/glog/glog_test.go (about)

     1  // Go support for leveled logs, analogous to https://code.google.com/p/google-glog/
     2  //
     3  // Copyright 2013 Google Inc. All Rights Reserved.
     4  //
     5  // Licensed under the Apache License, Version 2.0 (the "License");
     6  // you may not use this file except in compliance with the License.
     7  // You may obtain a copy of the License at
     8  //
     9  //     http://www.apache.org/licenses/LICENSE-2.0
    10  //
    11  // Unless required by applicable law or agreed to in writing, software
    12  // distributed under the License is distributed on an "AS IS" BASIS,
    13  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14  // See the License for the specific language governing permissions and
    15  // limitations under the License.
    16  
    17  package glog
    18  
    19  import (
    20  	"bytes"
    21  	"fmt"
    22  	"path/filepath"
    23  	"runtime"
    24  	"strings"
    25  	"testing"
    26  	"time"
    27  )
    28  
    29  // Test that shortHostname works as advertised.
    30  func TestShortHostname(t *testing.T) {
    31  	for hostname, expect := range map[string]string{
    32  		"":                "",
    33  		"host":            "host",
    34  		"host.google.com": "host",
    35  	} {
    36  		if got := shortHostname(hostname); expect != got {
    37  			t.Errorf("shortHostname(%q): expected %q, got %q", hostname, expect, got)
    38  		}
    39  	}
    40  }
    41  
    42  // flushBuffer wraps a bytes.Buffer to satisfy flushSyncWriter.
    43  type flushBuffer struct {
    44  	bytes.Buffer
    45  }
    46  
    47  func (f *flushBuffer) Flush() error {
    48  	return nil
    49  }
    50  
    51  func (f *flushBuffer) Sync() error {
    52  	return nil
    53  }
    54  
    55  // swap sets the log writers and returns the old array.
    56  func (l *loggingT) swap(writers [numSeverity]flushSyncWriter) (old [numSeverity]flushSyncWriter) {
    57  	l.mu.Lock()
    58  	defer l.mu.Unlock()
    59  	old = l.file
    60  	for i, w := range writers {
    61  		logging.file[i] = w
    62  	}
    63  	return
    64  }
    65  
    66  // newBuffers sets the log writers to all new byte buffers and returns the old array.
    67  func (l *loggingT) newBuffers() [numSeverity]flushSyncWriter {
    68  	return l.swap([numSeverity]flushSyncWriter{new(flushBuffer), new(flushBuffer), new(flushBuffer), new(flushBuffer)})
    69  }
    70  
    71  // contents returns the specified log value as a string.
    72  func contents(s severity) string {
    73  	return logging.file[s].(*flushBuffer).String()
    74  }
    75  
    76  // contains reports whether the string is contained in the log.
    77  func contains(s severity, str string, t *testing.T) bool {
    78  	return strings.Contains(contents(s), str)
    79  }
    80  
    81  // setFlags configures the logging flags how the test expects them.
    82  func setFlags() {
    83  	logging.toStderr = false
    84  }
    85  
    86  // Test that Info works as advertised.
    87  func TestInfo(t *testing.T) {
    88  	setFlags()
    89  	defer logging.swap(logging.newBuffers())
    90  	Info("test")
    91  	if !contains(infoLog, "I", t) {
    92  		t.Errorf("Info has wrong character: %q", contents(infoLog))
    93  	}
    94  	if !contains(infoLog, "test", t) {
    95  		t.Error("Info failed")
    96  	}
    97  }
    98  
    99  // Test that the header has the correct format.
   100  func TestHeader(t *testing.T) {
   101  	setFlags()
   102  	defer logging.swap(logging.newBuffers())
   103  	defer func(previous func() time.Time) { timeNow = previous }(timeNow)
   104  	timeNow = func() time.Time {
   105  		return time.Date(2006, 1, 2, 15, 4, 5, .678901e9, time.Local)
   106  	}
   107  	Info("test")
   108  	var line, pid int
   109  	n, err := fmt.Sscanf(contents(infoLog), "I0102 15:04:05.678901 %d glog_test.go:%d] test\n", &pid, &line)
   110  	if n != 2 || err != nil {
   111  		t.Errorf("log format error: %d elements, error %s:\n%s", n, err, contents(infoLog))
   112  	}
   113  }
   114  
   115  // Test that an Error log goes to Warning and Info.
   116  // Even in the Info log, the source character will be E, so the data should
   117  // all be identical.
   118  func TestError(t *testing.T) {
   119  	setFlags()
   120  	defer logging.swap(logging.newBuffers())
   121  	Error("test")
   122  	if !contains(errorLog, "E", t) {
   123  		t.Errorf("Error has wrong character: %q", contents(errorLog))
   124  	}
   125  	if !contains(errorLog, "test", t) {
   126  		t.Error("Error failed")
   127  	}
   128  	str := contents(errorLog)
   129  	if !contains(warningLog, str, t) {
   130  		t.Error("Warning failed")
   131  	}
   132  	if !contains(infoLog, str, t) {
   133  		t.Error("Info failed")
   134  	}
   135  }
   136  
   137  // Test that a Warning log goes to Info.
   138  // Even in the Info log, the source character will be W, so the data should
   139  // all be identical.
   140  func TestWarning(t *testing.T) {
   141  	setFlags()
   142  	defer logging.swap(logging.newBuffers())
   143  	Warning("test")
   144  	if !contains(warningLog, "W", t) {
   145  		t.Errorf("Warning has wrong character: %q", contents(warningLog))
   146  	}
   147  	if !contains(warningLog, "test", t) {
   148  		t.Error("Warning failed")
   149  	}
   150  	str := contents(warningLog)
   151  	if !contains(infoLog, str, t) {
   152  		t.Error("Info failed")
   153  	}
   154  }
   155  
   156  // Test that a V log goes to Info.
   157  func TestV(t *testing.T) {
   158  	setFlags()
   159  	defer logging.swap(logging.newBuffers())
   160  	logging.verbosity.Set("2")
   161  	defer logging.verbosity.Set("0")
   162  	V(2).Info("test")
   163  	if !contains(infoLog, "I", t) {
   164  		t.Errorf("Info has wrong character: %q", contents(infoLog))
   165  	}
   166  	if !contains(infoLog, "test", t) {
   167  		t.Error("Info failed")
   168  	}
   169  }
   170  
   171  // Test that a vmodule enables a log in this file.
   172  func TestVmoduleOn(t *testing.T) {
   173  	setFlags()
   174  	defer logging.swap(logging.newBuffers())
   175  	logging.vmodule.Set("glog_test=2")
   176  	defer logging.vmodule.Set("")
   177  	if !V(1) {
   178  		t.Error("V not enabled for 1")
   179  	}
   180  	if !V(2) {
   181  		t.Error("V not enabled for 2")
   182  	}
   183  	if V(3) {
   184  		t.Error("V enabled for 3")
   185  	}
   186  	V(2).Info("test")
   187  	if !contains(infoLog, "I", t) {
   188  		t.Errorf("Info has wrong character: %q", contents(infoLog))
   189  	}
   190  	if !contains(infoLog, "test", t) {
   191  		t.Error("Info failed")
   192  	}
   193  }
   194  
   195  // Test that a vmodule of another file does not enable a log in this file.
   196  func TestVmoduleOff(t *testing.T) {
   197  	setFlags()
   198  	defer logging.swap(logging.newBuffers())
   199  	logging.vmodule.Set("notthisfile=2")
   200  	defer logging.vmodule.Set("")
   201  	for i := 1; i <= 3; i++ {
   202  		if V(Level(i)) {
   203  			t.Errorf("V enabled for %d", i)
   204  		}
   205  	}
   206  	V(2).Info("test")
   207  	if contents(infoLog) != "" {
   208  		t.Error("V logged incorrectly")
   209  	}
   210  }
   211  
   212  // vGlobs are patterns that match/don't match this file at V=2.
   213  var vGlobs = map[string]bool{
   214  	// Easy to test the numeric match here.
   215  	"glog_test=1": false, // If -vmodule sets V to 1, V(2) will fail.
   216  	"glog_test=2": true,
   217  	"glog_test=3": true, // If -vmodule sets V to 1, V(3) will succeed.
   218  	// These all use 2 and check the patterns. All are true.
   219  	"*=2":           true,
   220  	"?l*=2":         true,
   221  	"????_*=2":      true,
   222  	"??[mno]?_*t=2": true,
   223  	// These all use 2 and check the patterns. All are false.
   224  	"*x=2":         false,
   225  	"m*=2":         false,
   226  	"??_*=2":       false,
   227  	"?[abc]?_*t=2": false,
   228  }
   229  
   230  // Test that vmodule globbing works as advertised.
   231  func testVmoduleGlob(pat string, match bool, t *testing.T) {
   232  	setFlags()
   233  	defer logging.swap(logging.newBuffers())
   234  	defer logging.vmodule.Set("")
   235  	logging.vmodule.Set(pat)
   236  	if V(2) != Verbose(match) {
   237  		t.Errorf("incorrect match for %q: got %t expected %t", pat, V(2), match)
   238  	}
   239  }
   240  
   241  // Test that a vmodule globbing works as advertised.
   242  func TestVmoduleGlob(t *testing.T) {
   243  	for glob, match := range vGlobs {
   244  		testVmoduleGlob(glob, match, t)
   245  	}
   246  }
   247  
   248  func TestRollover(t *testing.T) {
   249  	setFlags()
   250  	var err error
   251  	defer func(previous func(error)) { logExitFunc = previous }(logExitFunc)
   252  	logExitFunc = func(e error) {
   253  		err = e
   254  	}
   255  	defer func(previous uint64) { MaxSize = previous }(MaxSize)
   256  	MaxSize = 512
   257  
   258  	Info("x") // Be sure we have a file.
   259  	info, ok := logging.file[infoLog].(*syncBuffer)
   260  	if !ok {
   261  		t.Fatal("info wasn't created")
   262  	}
   263  	if err != nil {
   264  		t.Fatalf("info has initial error: %v", err)
   265  	}
   266  	fname0 := info.file.Name()
   267  	Info(strings.Repeat("x", int(MaxSize))) // force a rollover
   268  	if err != nil {
   269  		t.Fatalf("info has error after big write: %v", err)
   270  	}
   271  
   272  	// Make sure the next log file gets a file name with a different
   273  	// time stamp.
   274  	//
   275  	// TODO: determine whether we need to support subsecond log
   276  	// rotation.  C++ does not appear to handle this case (nor does it
   277  	// handle Daylight Savings Time properly).
   278  	time.Sleep(1 * time.Second)
   279  
   280  	Info("x") // create a new file
   281  	if err != nil {
   282  		t.Fatalf("error after rotation: %v", err)
   283  	}
   284  	fname1 := info.file.Name()
   285  	if fname0 == fname1 {
   286  		t.Errorf("info.f.Name did not change: %v", fname0)
   287  	}
   288  	if info.nbytes >= MaxSize {
   289  		t.Errorf("file size was not reset: %d", info.nbytes)
   290  	}
   291  }
   292  
   293  func TestLogBacktraceAt(t *testing.T) {
   294  	setFlags()
   295  	defer logging.swap(logging.newBuffers())
   296  	// The peculiar style of this code simplifies line counting and maintenance of the
   297  	// tracing block below.
   298  	var infoLine string
   299  	setTraceLocation := func(file string, line int, ok bool, delta int) {
   300  		if !ok {
   301  			t.Fatal("could not get file:line")
   302  		}
   303  		_, file = filepath.Split(file)
   304  		infoLine = fmt.Sprintf("%s:%d", file, line+delta)
   305  		err := logging.traceLocation.Set(infoLine)
   306  		if err != nil {
   307  			t.Fatal("error setting log_backtrace_at: ", err)
   308  		}
   309  	}
   310  	{
   311  		// Start of tracing block. These lines know about each other's relative position.
   312  		_, file, line, ok := runtime.Caller(0)
   313  		setTraceLocation(file, line, ok, +2) // Two lines between Caller and Info calls.
   314  		Info("we want a stack trace here")
   315  	}
   316  	numAppearances := strings.Count(contents(infoLog), infoLine)
   317  	if numAppearances < 2 {
   318  		// Need 2 appearances, one in the log header and one in the trace:
   319  		//   log_test.go:281: I0511 16:36:06.952398 02238 log_test.go:280] we want a stack trace here
   320  		//   ...
   321  		//   github.com/glog/glog_test.go:280 (0x41ba91)
   322  		//   ...
   323  		// We could be more precise but that would require knowing the details
   324  		// of the traceback format, which may not be dependable.
   325  		t.Fatal("got no trace back; log is ", contents(infoLog))
   326  	}
   327  }
   328  
   329  func BenchmarkHeader(b *testing.B) {
   330  	for i := 0; i < b.N; i++ {
   331  		logging.putBuffer(logging.header(infoLog))
   332  	}
   333  }