github.com/digdeepmining/go-atheios@v1.5.13-0.20180902133602-d5687a2e6f43/logger/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 stdLog "log" 23 "path/filepath" 24 "runtime" 25 "strconv" 26 "strings" 27 "testing" 28 "time" 29 ) 30 31 // Test that shortHostname works as advertised. 32 func TestShortHostname(t *testing.T) { 33 for hostname, expect := range map[string]string{ 34 "": "", 35 "host": "host", 36 "host.google.com": "host", 37 } { 38 if got := shortHostname(hostname); expect != got { 39 t.Errorf("shortHostname(%q): expected %q, got %q", hostname, expect, got) 40 } 41 } 42 } 43 44 // flushBuffer wraps a bytes.Buffer to satisfy flushSyncWriter. 45 type flushBuffer struct { 46 bytes.Buffer 47 } 48 49 func (f *flushBuffer) Flush() error { 50 return nil 51 } 52 53 func (f *flushBuffer) Sync() error { 54 return nil 55 } 56 57 // swap sets the log writers and returns the old array. 58 func (l *loggingT) swap(writers [numSeverity]flushSyncWriter) (old [numSeverity]flushSyncWriter) { 59 l.mu.Lock() 60 defer l.mu.Unlock() 61 old = l.file 62 for i, w := range writers { 63 logging.file[i] = w 64 } 65 return 66 } 67 68 // newBuffers sets the log writers to all new byte buffers and returns the old array. 69 func (l *loggingT) newBuffers() [numSeverity]flushSyncWriter { 70 return l.swap([numSeverity]flushSyncWriter{new(flushBuffer), new(flushBuffer), new(flushBuffer), new(flushBuffer)}) 71 } 72 73 // contents returns the specified log value as a string. 74 func contents(s severity) string { 75 return logging.file[s].(*flushBuffer).String() 76 } 77 78 // contains reports whether the string is contained in the log. 79 func contains(s severity, str string, t *testing.T) bool { 80 return strings.Contains(contents(s), str) 81 } 82 83 // setFlags configures the logging flags how the test expects them. 84 func setFlags() { 85 logging.toStderr = false 86 } 87 88 // Test that Info works as advertised. 89 func TestInfo(t *testing.T) { 90 setFlags() 91 defer logging.swap(logging.newBuffers()) 92 Info("test") 93 if !contains(infoLog, "I", t) { 94 t.Errorf("Info has wrong character: %q", contents(infoLog)) 95 } 96 if !contains(infoLog, "test", t) { 97 t.Error("Info failed") 98 } 99 } 100 101 func TestInfoDepth(t *testing.T) { 102 setFlags() 103 defer logging.swap(logging.newBuffers()) 104 105 f := func() { InfoDepth(1, "depth-test1") } 106 107 // The next three lines must stay together 108 _, _, wantLine, _ := runtime.Caller(0) 109 InfoDepth(0, "depth-test0") 110 f() 111 112 msgs := strings.Split(strings.TrimSuffix(contents(infoLog), "\n"), "\n") 113 if len(msgs) != 2 { 114 t.Fatalf("Got %d lines, expected 2", len(msgs)) 115 } 116 117 for i, m := range msgs { 118 if !strings.HasPrefix(m, "I") { 119 t.Errorf("InfoDepth[%d] has wrong character: %q", i, m) 120 } 121 w := fmt.Sprintf("depth-test%d", i) 122 if !strings.Contains(m, w) { 123 t.Errorf("InfoDepth[%d] missing %q: %q", i, w, m) 124 } 125 126 // pull out the line number (between : and ]) 127 msg := m[strings.LastIndex(m, ":")+1:] 128 x := strings.Index(msg, "]") 129 if x < 0 { 130 t.Errorf("InfoDepth[%d]: missing ']': %q", i, m) 131 continue 132 } 133 line, err := strconv.Atoi(msg[:x]) 134 if err != nil { 135 t.Errorf("InfoDepth[%d]: bad line number: %q", i, m) 136 continue 137 } 138 wantLine++ 139 if wantLine != line { 140 t.Errorf("InfoDepth[%d]: got line %d, want %d", i, line, wantLine) 141 } 142 } 143 } 144 145 func init() { 146 CopyStandardLogTo("INFO") 147 } 148 149 // Test that CopyStandardLogTo panics on bad input. 150 func TestCopyStandardLogToPanic(t *testing.T) { 151 defer func() { 152 if s, ok := recover().(string); !ok || !strings.Contains(s, "LOG") { 153 t.Errorf(`CopyStandardLogTo("LOG") should have panicked: %v`, s) 154 } 155 }() 156 CopyStandardLogTo("LOG") 157 } 158 159 // Test that using the standard log package logs to INFO. 160 func TestStandardLog(t *testing.T) { 161 setFlags() 162 defer logging.swap(logging.newBuffers()) 163 stdLog.Print("test") 164 if !contains(infoLog, "I", t) { 165 t.Errorf("Info has wrong character: %q", contents(infoLog)) 166 } 167 if !contains(infoLog, "test", t) { 168 t.Error("Info failed") 169 } 170 } 171 172 // Test that the header has the correct format. 173 func TestHeader(t *testing.T) { 174 setFlags() 175 defer logging.swap(logging.newBuffers()) 176 defer func(previous func() time.Time) { timeNow = previous }(timeNow) 177 timeNow = func() time.Time { 178 return time.Date(2006, 1, 2, 15, 4, 5, .067890e9, time.Local) 179 } 180 pid = 1234 181 Info("test") 182 var line int 183 format := "I0102 15:04:05.067890 logger/glog/glog_test.go:%d] test\n" 184 n, err := fmt.Sscanf(contents(infoLog), format, &line) 185 if n != 1 || err != nil { 186 t.Errorf("log format error: %d elements, error %s:\n%s", n, err, contents(infoLog)) 187 } 188 // Scanf treats multiple spaces as equivalent to a single space, 189 // so check for correct space-padding also. 190 want := fmt.Sprintf(format, line) 191 if contents(infoLog) != want { 192 t.Errorf("log format error: got:\n\t%q\nwant:\t%q", contents(infoLog), want) 193 } 194 } 195 196 // Test that an Error log goes to Warning and Info. 197 // Even in the Info log, the source character will be E, so the data should 198 // all be identical. 199 func TestError(t *testing.T) { 200 setFlags() 201 defer logging.swap(logging.newBuffers()) 202 Error("test") 203 if !contains(errorLog, "E", t) { 204 t.Errorf("Error has wrong character: %q", contents(errorLog)) 205 } 206 if !contains(errorLog, "test", t) { 207 t.Error("Error failed") 208 } 209 str := contents(errorLog) 210 if !contains(warningLog, str, t) { 211 t.Error("Warning failed") 212 } 213 if !contains(infoLog, str, t) { 214 t.Error("Info failed") 215 } 216 } 217 218 // Test that a Warning log goes to Info. 219 // Even in the Info log, the source character will be W, so the data should 220 // all be identical. 221 func TestWarning(t *testing.T) { 222 setFlags() 223 defer logging.swap(logging.newBuffers()) 224 Warning("test") 225 if !contains(warningLog, "W", t) { 226 t.Errorf("Warning has wrong character: %q", contents(warningLog)) 227 } 228 if !contains(warningLog, "test", t) { 229 t.Error("Warning failed") 230 } 231 str := contents(warningLog) 232 if !contains(infoLog, str, t) { 233 t.Error("Info failed") 234 } 235 } 236 237 // Test that a V log goes to Info. 238 func TestV(t *testing.T) { 239 setFlags() 240 defer logging.swap(logging.newBuffers()) 241 logging.verbosity.Set("2") 242 defer logging.verbosity.Set("0") 243 V(2).Info("test") 244 if !contains(infoLog, "I", t) { 245 t.Errorf("Info has wrong character: %q", contents(infoLog)) 246 } 247 if !contains(infoLog, "test", t) { 248 t.Error("Info failed") 249 } 250 } 251 252 // Test that a vmodule enables a log in this file. 253 func TestVmoduleOn(t *testing.T) { 254 setFlags() 255 defer logging.swap(logging.newBuffers()) 256 logging.vmodule.Set("glog_test.go=2") 257 defer logging.vmodule.Set("") 258 if !V(1) { 259 t.Error("V not enabled for 1") 260 } 261 if !V(2) { 262 t.Error("V not enabled for 2") 263 } 264 if V(3) { 265 t.Error("V enabled for 3") 266 } 267 V(2).Info("test") 268 if !contains(infoLog, "I", t) { 269 t.Errorf("Info has wrong character: %q", contents(infoLog)) 270 } 271 if !contains(infoLog, "test", t) { 272 t.Error("Info failed") 273 } 274 } 275 276 // Test that a vmodule of another file does not enable a log in this file. 277 func TestVmoduleOff(t *testing.T) { 278 setFlags() 279 defer logging.swap(logging.newBuffers()) 280 logging.vmodule.Set("notthisfile=2") 281 defer logging.vmodule.Set("") 282 for i := 1; i <= 3; i++ { 283 if V(Level(i)) { 284 t.Errorf("V enabled for %d", i) 285 } 286 } 287 V(2).Info("test") 288 if contents(infoLog) != "" { 289 t.Error("V logged incorrectly") 290 } 291 } 292 293 var patternTests = []struct{ input, want string }{ 294 {"foo/bar/x.go", ".*/foo/bar/x\\.go$"}, 295 {"foo/*/x.go", ".*/foo(/.*)?/x\\.go$"}, 296 {"foo/*", ".*/foo(/.*)?/[^/]+\\.go$"}, 297 } 298 299 func TestCompileModulePattern(t *testing.T) { 300 for _, test := range patternTests { 301 re, err := compileModulePattern(test.input) 302 if err != nil { 303 t.Fatalf("%s: %v", test.input, err) 304 } 305 if re.String() != test.want { 306 t.Errorf("mismatch for %q: got %q, want %q", test.input, re.String(), test.want) 307 } 308 } 309 } 310 311 // vGlobs are patterns that match/don't match this file at V=2. 312 var vGlobs = map[string]bool{ 313 // Easy to test the numeric match here. 314 "glog_test.go=1": false, // If -vmodule sets V to 1, V(2) will fail. 315 "glog_test.go=2": true, 316 "glog_test.go=3": true, // If -vmodule sets V to 1, V(3) will succeed. 317 318 // Import path prefix matching 319 "logger/glog=1": false, 320 "logger/glog=2": true, 321 "logger/glog=3": true, 322 323 // Import path glob matching 324 "logger/*=1": false, 325 "logger/*=2": true, 326 "logger/*=3": true, 327 328 // These all use 2 and check the patterns. 329 "*=2": true, 330 } 331 332 // Test that vmodule globbing works as advertised. 333 func testVmoduleGlob(pat string, match bool, t *testing.T) { 334 setFlags() 335 defer logging.swap(logging.newBuffers()) 336 defer logging.vmodule.Set("") 337 logging.vmodule.Set(pat) 338 if V(2) != Verbose(match) { 339 t.Errorf("incorrect match for %q: got %t expected %t", pat, V(2), match) 340 } 341 } 342 343 // Test that a vmodule globbing works as advertised. 344 func TestVmoduleGlob(t *testing.T) { 345 for glob, match := range vGlobs { 346 testVmoduleGlob(glob, match, t) 347 } 348 } 349 350 func TestRollover(t *testing.T) { 351 setFlags() 352 var err error 353 defer func(previous func(error)) { logExitFunc = previous }(logExitFunc) 354 logExitFunc = func(e error) { 355 err = e 356 } 357 defer func(previous uint64) { MaxSize = previous }(MaxSize) 358 MaxSize = 512 359 360 Info("x") // Be sure we have a file. 361 info, ok := logging.file[infoLog].(*syncBuffer) 362 if !ok { 363 t.Fatal("info wasn't created") 364 } 365 if err != nil { 366 t.Fatalf("info has initial error: %v", err) 367 } 368 fname0 := info.file.Name() 369 Info(strings.Repeat("x", int(MaxSize))) // force a rollover 370 if err != nil { 371 t.Fatalf("info has error after big write: %v", err) 372 } 373 374 // Make sure the next log file gets a file name with a different 375 // time stamp. 376 // 377 // TODO: determine whether we need to support subsecond log 378 // rotation. C++ does not appear to handle this case (nor does it 379 // handle Daylight Savings Time properly). 380 time.Sleep(1 * time.Second) 381 382 Info("x") // create a new file 383 if err != nil { 384 t.Fatalf("error after rotation: %v", err) 385 } 386 fname1 := info.file.Name() 387 if fname0 == fname1 { 388 t.Errorf("info.f.Name did not change: %v", fname0) 389 } 390 if info.nbytes >= MaxSize { 391 t.Errorf("file size was not reset: %d", info.nbytes) 392 } 393 } 394 395 func TestLogBacktraceAt(t *testing.T) { 396 setFlags() 397 defer logging.swap(logging.newBuffers()) 398 // The peculiar style of this code simplifies line counting and maintenance of the 399 // tracing block below. 400 var infoLine string 401 setTraceLocation := func(file string, line int, ok bool, delta int) { 402 if !ok { 403 t.Fatal("could not get file:line") 404 } 405 _, file = filepath.Split(file) 406 infoLine = fmt.Sprintf("%s:%d", file, line+delta) 407 err := logging.traceLocation.Set(infoLine) 408 if err != nil { 409 t.Fatal("error setting log_backtrace_at: ", err) 410 } 411 } 412 { 413 // Start of tracing block. These lines know about each other's relative position. 414 _, file, line, ok := runtime.Caller(0) 415 setTraceLocation(file, line, ok, +2) // Two lines between Caller and Info calls. 416 Info("we want a stack trace here") 417 } 418 numAppearances := strings.Count(contents(infoLog), infoLine) 419 if numAppearances < 2 { 420 // Need 2 appearances, one in the log header and one in the trace: 421 // log_test.go:281: I0511 16:36:06.952398 02238 log_test.go:280] we want a stack trace here 422 // ... 423 // github.com/glog/glog_test.go:280 (0x41ba91) 424 // ... 425 // We could be more precise but that would require knowing the details 426 // of the traceback format, which may not be dependable. 427 t.Fatal("got no trace back; log is ", contents(infoLog)) 428 } 429 } 430 431 func BenchmarkHeader(b *testing.B) { 432 for i := 0; i < b.N; i++ { 433 buf, _, _ := logging.header(infoLog, 0) 434 logging.putBuffer(buf) 435 } 436 }