This is an automated email from the ASF dual-hosted git repository.

zrhoffman pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficcontrol.git


The following commit(s) were added to refs/heads/master by this push:
     new 5c1d852c55 Add a way to use a testing.T for our go-log library in 
tests (#6882)
5c1d852c55 is described below

commit 5c1d852c5563132b6f0bcc3b7b557516ede1623b
Author: ocket8888 <[email protected]>
AuthorDate: Tue Sep 26 19:01:52 2023 -0600

    Add a way to use a testing.T for our go-log library in tests (#6882)
    
    (cherry picked from commit 43ba2b72f8e7684de379694466b431d95ed1a998)
---
 lib/go-log/test.logger.go      |  62 +++++++++++
 lib/go-log/test.logger_test.go | 242 +++++++++++++++++++++++++++++++++++++++++
 2 files changed, 304 insertions(+)

diff --git a/lib/go-log/test.logger.go b/lib/go-log/test.logger.go
new file mode 100644
index 0000000000..99ac93152f
--- /dev/null
+++ b/lib/go-log/test.logger.go
@@ -0,0 +1,62 @@
+package log
+
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+*/
+
+import "testing"
+
+type testErrorLogger struct {
+       t testing.TB
+}
+
+func (tl testErrorLogger) Write(data []byte) (int, error) {
+       tl.t.Error(string(data))
+       return len(data), nil
+}
+func (tl testErrorLogger) Close() error {
+       return nil
+}
+
+type testLogger struct {
+       t testing.TB
+}
+
+func (tl testLogger) Write(data []byte) (int, error) {
+       tl.t.Log(string(data))
+       return len(data), nil
+}
+func (tl testLogger) Close() error {
+       return nil
+}
+
+// InitTestingLogging initializes all logging functions to write their outputs
+// to the logging output of the given testing context. If warningsAreErrors is
+// true, warnings are logged using t.Error - marking the test as failed.
+// Otherwise, errors are logged using t.Log. In either case, any errors logged
+// cause the test to be marked as failed and are logged using t.Error, and the
+// Info, Event, and Debug streams are always logged using t.Log.
+func InitTestingLogging(tb testing.TB, warningsAreErrors bool) {
+       tb.Helper()
+       errWriter := testErrorLogger{t: tb}
+       if writer := (testLogger{t: tb}); warningsAreErrors {
+               Init(writer, errWriter, errWriter, writer, writer)
+       } else {
+               Init(writer, errWriter, writer, writer, writer)
+       }
+}
diff --git a/lib/go-log/test.logger_test.go b/lib/go-log/test.logger_test.go
new file mode 100644
index 0000000000..96dbd63291
--- /dev/null
+++ b/lib/go-log/test.logger_test.go
@@ -0,0 +1,242 @@
+package log
+
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+*/
+
+import (
+       "fmt"
+       "regexp"
+       "strings"
+       "testing"
+       "time"
+)
+
+type mockTester struct {
+       logbuf       string
+       errbuf       string
+       failed       bool
+       helperCalled bool
+       *testing.T
+}
+
+/*** testing.TB method overrides ***/
+func (m *mockTester) Helper() {
+       m.helperCalled = true
+}
+func (m *mockTester) Failed() bool {
+       return m.failed
+}
+func (m *mockTester) Error(args ...any) {
+       m.failed = true
+       m.errbuf = fmt.Sprint(args...)
+}
+func (m *mockTester) Log(args ...any) {
+       m.logbuf = fmt.Sprint(args...)
+}
+
+/*** None of these should ever be called ***/
+func (m *mockTester) Fail() {
+       m.failed = true
+       m.T.Error("'Fail' called")
+}
+func (m *mockTester) FailNow() {
+       m.failed = true
+       m.T.Error("'FailNow' called")
+}
+func (m *mockTester) Fatal(args ...any) {
+       m.failed = true
+       m.T.Error("'Fatal' called")
+}
+func (m *mockTester) Errorf(fmtstr string, args ...any) {
+       m.failed = true
+       m.T.Error("'Errorf' called")
+}
+func (m *mockTester) Fatalf(fmtstr string, args ...any) {
+       m.failed = true
+       m.T.Error("'Fatalf' called")
+}
+func (m *mockTester) Logf(fmtstr string, args ...any) {
+       m.failed = true
+       m.T.Error("'Logf' called")
+}
+func (m *mockTester) Name() string {
+       m.failed = true
+       m.T.Error("'Name' called")
+       return ""
+}
+func (m *mockTester) Skip(args ...any) {
+       m.failed = true
+       m.T.Error("'Skip' called")
+}
+func (m *mockTester) Skipf(fmtstr string, args ...any) {
+       m.failed = true
+       m.T.Error("'Skipf' called")
+}
+func (m *mockTester) SkipNow() {
+       m.failed = true
+       m.T.Error("'SkipNow' called")
+}
+func (m *mockTester) Skipped() bool {
+       m.failed = true
+       m.T.Error("'Skipped' called")
+       return false
+}
+
+const datePattern = `\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?Z`
+
+func mkPattern(prefix, msg string) string {
+       msg = strings.ReplaceAll(msg, `\`, `\\`)
+       msg = 
strings.ReplaceAll(strings.ReplaceAll(strings.ReplaceAll(strings.ReplaceAll(msg,
 "[", `\[`), "]", `\]`), "(", `\(`), ")", `\)`)
+       msg = 
strings.ReplaceAll(strings.ReplaceAll(strings.ReplaceAll(strings.ReplaceAll(msg,
 "{", `\{`), "}", `\}`), ".", `\.`), "^", `\^`)
+       msg = 
strings.ReplaceAll(strings.ReplaceAll(strings.ReplaceAll(strings.ReplaceAll(msg,
 "$", `\$`), "+", `\+`), "*", `\*`), "?", `\?`)
+
+       return "^" + prefix + "test\\.logger_test\\.go:\\d+: " + datePattern + 
": " + msg + "\n$"
+}
+
+func matches(pattern, str string, t *testing.T) bool {
+       match, err := regexp.Match(pattern, []byte(str))
+       if err != nil {
+               t.Errorf("failed to compile pattern '%s': %v", pattern, err)
+               return false
+       }
+       return match
+}
+
+func TestInitTestLogging(t *testing.T) {
+       m := mockTester{T: t}
+       InitTestingLogging(&m, false)
+       msg := "testing message"
+       Debugln(msg)
+       pattern := mkPattern(DebugPrefix, msg)
+       if !matches(pattern, m.logbuf, t) {
+               t.Errorf("expected last log message to match '%s', got: '%s'", 
pattern, m.logbuf)
+       }
+       Debugf("formatted: %d, %+v, %t", 1, []byte{1}, true)
+       msg = "formatted: 1, [1], true"
+       pattern = mkPattern(DebugPrefix, msg)
+       if !matches(pattern, m.logbuf, t) {
+               t.Errorf("expected last log message to match '%s', got: '%s'", 
pattern, m.logbuf)
+       }
+
+       msg = "testing message"
+       Infoln(msg)
+       pattern = mkPattern(InfoPrefix, msg)
+       if !matches(pattern, m.logbuf, t) {
+               t.Errorf("expected last log message to match '%s', got: '%s'", 
pattern, m.logbuf)
+       }
+       Infof("formatted: %d, %+v, %t", 1, []byte{1}, true)
+       msg = "formatted: 1, [1], true"
+       pattern = mkPattern(InfoPrefix, msg)
+       if !matches(pattern, m.logbuf, t) {
+               t.Errorf("expected last log message to match '%s', got: '%s'", 
pattern, m.logbuf)
+       }
+
+       msg = "testing message"
+       Warnln(msg)
+       pattern = mkPattern(WarnPrefix, msg)
+       if !matches(pattern, m.logbuf, t) {
+               t.Errorf("expected last log message to match '%s', got: '%s'", 
pattern, m.logbuf)
+       }
+       Warnf("formatted: %d, %+v, %t", 1, []byte{1}, true)
+       msg = "formatted: 1, [1], true"
+       pattern = mkPattern(WarnPrefix, msg)
+       if !matches(pattern, m.logbuf, t) {
+               t.Errorf("expected last log message to match '%s', got: '%s'", 
pattern, m.logbuf)
+       }
+
+       msg = "testing message"
+       EventRaw(msg)
+       if m.logbuf != msg+"\n" {
+               t.Errorf("expected last log message to be exactly '%s', got: 
'%s'", msg, m.logbuf)
+       }
+       Eventf(time.Time{}, "formatted: %d, %+v, %t", 1, []byte{1}, true)
+       msg = "formatted: 1, [1], true"
+       pattern = fmt.Sprintf(eventFormat, eventTime(time.Time{}), "formatted: 
1, [1], true\n")
+       if pattern != m.logbuf {
+               t.Errorf("expected last log message to be exactly '%s', got: 
'%s'", pattern, m.logbuf)
+       }
+
+       if m.Failed() {
+               t.Error("none of the non-error logging functions should have 
caused a failure")
+       }
+       if m.errbuf != "" {
+               t.Error("none of the non-error logging functions should have 
populated the error buffer:", m.errbuf)
+       }
+
+       m.logbuf = ""
+       msg = "testing message"
+       Errorln(msg)
+       if m.logbuf != "" {
+               t.Error("error should've been logged to the error stream, not 
the logging stream")
+               m.logbuf = ""
+       }
+       if !m.Failed() {
+               t.Error("error logging should have caused a failure")
+       } else {
+               m.failed = false
+       }
+       pattern = mkPattern(ErrPrefix, msg)
+       if !matches(pattern, m.errbuf, t) {
+               t.Errorf("expected last error message to match '%s', got: 
'%s'", pattern, m.errbuf)
+       }
+       Errorf("formatted: %d, %+v, %t", 1, []byte{1}, true)
+       if m.logbuf != "" {
+               t.Error("error should've been logged to the error stream, not 
the logging stream")
+       }
+       if !m.Failed() {
+               t.Error("error logging should have caused a failure")
+       }
+       msg = "formatted: 1, [1], true"
+       pattern = mkPattern(ErrPrefix, msg)
+       if !matches(pattern, m.errbuf, t) {
+               t.Errorf("expected last error message to match '%s', got: 
'%s'", pattern, m.errbuf)
+       }
+}
+
+func TestLoggingWarningsAsErrors(t *testing.T) {
+       m := mockTester{T: t}
+       InitTestingLogging(&m, true)
+       msg := "testing message"
+       Warnln(msg)
+       if m.logbuf != "" {
+               t.Error("warning should've been logged to the error stream, not 
the logging stream")
+               m.logbuf = ""
+       }
+       if !m.Failed() {
+               t.Error("warning logging should have caused a failure")
+       } else {
+               m.failed = false
+       }
+       pattern := mkPattern(WarnPrefix, msg)
+       if !matches(pattern, m.errbuf, t) {
+               t.Errorf("expected last error message to match '%s', got: 
'%s'", pattern, m.errbuf)
+       }
+       Warnf("formatted: %d, %+v, %t", 1, []byte{1}, true)
+       if m.logbuf != "" {
+               t.Error("warning should've been logged to the error stream, not 
the logging stream")
+       }
+       if !m.Failed() {
+               t.Error("warning logging should have caused a failure")
+       }
+       msg = "formatted: 1, [1], true"
+       pattern = mkPattern(WarnPrefix, msg)
+       if !matches(pattern, m.errbuf, t) {
+               t.Errorf("expected last error message to match '%s', got: 
'%s'", pattern, m.errbuf)
+       }
+}

Reply via email to