Merged in feature/testingopts (pull request #129)

Testing Options and Clean Up

* bitsandbobs

* lint

* healthchecks

* imgintests

* singlegenerate

* testclean

* network

* networkclean

* cmds
This commit is contained in:
Michael McGuinness
2025-05-03 01:16:53 +00:00
parent 15fa3b49a5
commit dd2eebf13a
73 changed files with 4657 additions and 234 deletions
+178
View File
@@ -0,0 +1,178 @@
// Package logger contains different methods to log.
package logger
import (
"fmt"
"io"
"os"
"runtime"
"strings"
"sync"
gotesting "testing"
"time"
"github.com/gruntwork-io/terratest/modules/testing"
)
var (
// Default is the default logger that is used for the Logf function, if no one is provided. It uses the
// TerratestLogger to log messages. This can be overwritten to change the logging globally.
Default = New(terratestLogger{})
// Discard discards all logging.
Discard = New(discardLogger{})
// Terratest logs the given format and arguments, formatted using fmt.Sprintf, to stdout, along with a timestamp and
// information about what test and file is doing the logging. Before Go 1.14, this is an alternative to t.Logf as it
// logs to stdout immediately, rather than buffering all log output and only displaying it at the very end of the test.
// This is useful because:
//
// 1. It allows you to iterate faster locally, as you get feedback on whether your code changes are working as expected
// right away, rather than at the very end of the test run.
//
// 2. If you have a bug in your code that causes a test to never complete or if the test code crashes, t.Logf would
// show you no log output whatsoever, making debugging very hard, where as this method will show you all the log
// output available.
//
// 3. If you have a test that takes a long time to complete, some CI systems will kill the test suite prematurely
// because there is no log output with t.Logf (e.g., CircleCI kills tests after 10 minutes of no log output). With
// this log method, you get log output continuously.
//
Terratest = New(terratestLogger{})
// TestingT can be used to use Go's testing.T to log. If this is used, but no testing.T is provided, it will fallback
// to Default.
TestingT = New(testingT{})
)
type TestLogger interface {
Logf(t testing.TestingT, format string, args ...interface{})
}
type Logger struct {
l TestLogger
}
func New(l TestLogger) *Logger {
return &Logger{
l,
}
}
func (l *Logger) Logf(t testing.TestingT, format string, args ...interface{}) {
if tt, ok := t.(helper); ok {
tt.Helper()
}
// methods can be called on (typed) nil pointers. In this case, use the Default function to log. This enables the
// caller to do `var l *Logger` and then use the logger already.
if l == nil || l.l == nil {
Default.Logf(t, format, args...)
return
}
l.l.Logf(t, format, args...)
}
// helper is used to mark this library as a "helper", and thus not appearing in the line numbers. testing.T implements
// this interface, for example.
type helper interface {
Helper()
}
type discardLogger struct{}
func (_ discardLogger) Logf(_ testing.TestingT, format string, args ...interface{}) {}
type testingT struct{}
func (_ testingT) Logf(t testing.TestingT, format string, args ...interface{}) {
// this should never fail
tt, ok := t.(*gotesting.T)
if !ok {
// fallback
DoLog(t, 2, os.Stdout, fmt.Sprintf(format, args...))
return
}
tt.Helper()
tt.Logf(format, args...)
return
}
type terratestLogger struct{}
func (_ terratestLogger) Logf(t testing.TestingT, format string, args ...interface{}) {
DoLog(t, 3, os.Stdout, fmt.Sprintf(format, args...))
}
// Deprecated: use Logger instead, as it provides more flexibility on logging.
// Logf logs the given format and arguments, formatted using fmt.Sprintf, to stdout, along with a timestamp and information
// about what test and file is doing the logging. Before Go 1.14, this is an alternative to t.Logf as it logs to stdout
// immediately, rather than buffering all log output and only displaying it at the very end of the test. This is useful
// because:
//
// 1. It allows you to iterate faster locally, as you get feedback on whether your code changes are working as expected
// right away, rather than at the very end of the test run.
//
// 2. If you have a bug in your code that causes a test to never complete or if the test code crashes, t.Logf would
// show you no log output whatsoever, making debugging very hard, where as this method will show you all the log
// output available.
//
// 3. If you have a test that takes a long time to complete, some CI systems will kill the test suite prematurely
// because there is no log output with t.Logf (e.g., CircleCI kills tests after 10 minutes of no log output). With
// this log method, you get log output continuously.
//
// Although t.Logf now supports streaming output since Go 1.14, this is kept for compatibility purposes.
func Logf(t testing.TestingT, format string, args ...interface{}) {
if tt, ok := t.(helper); ok {
tt.Helper()
}
mutexStdout.Lock()
defer mutexStdout.Unlock()
DoLog(t, 2, os.Stdout, fmt.Sprintf(format, args...))
}
// Log logs the given arguments to stdout, along with a timestamp and information about what test and file is doing the
// logging. This is an alternative to t.Logf that logs to stdout immediately, rather than buffering all log output and
// only displaying it at the very end of the test. See the Logf method for more info.
func Log(t testing.TestingT, args ...interface{}) {
if tt, ok := t.(helper); ok {
tt.Helper()
}
mutexStdout.Lock()
defer mutexStdout.Unlock()
DoLog(t, 2, os.Stdout, args...)
}
var mutexStdout sync.Mutex
// DoLog logs the given arguments to the given writer, along with a timestamp and information about what test and file is
// doing the logging.
func DoLog(t testing.TestingT, callDepth int, writer io.Writer, args ...interface{}) {
date := time.Now()
prefix := fmt.Sprintf("%s %s %s:", t.Name(), date.Format(time.RFC3339), CallerPrefix(callDepth+1))
allArgs := append([]interface{}{prefix}, args...)
fmt.Fprintln(writer, allArgs...)
}
// CallerPrefix returns the file and line number information about the methods that called this method, based on the current
// goroutine's stack. The argument callDepth is the number of stack frames to ascend, with 0 identifying the method
// that called CallerPrefix, 1 identifying the method that called that method, and so on.
//
// This code is adapted from testing.go, where it is in a private method called decorate.
func CallerPrefix(callDepth int) string {
_, file, line, ok := runtime.Caller(callDepth)
if ok {
// Truncate file name at last file name separator.
if index := strings.LastIndex(file, "/"); index >= 0 {
file = file[index+1:]
} else if index = strings.LastIndex(file, "\\"); index >= 0 {
file = file[index+1:]
}
} else {
file = "???"
line = 1
}
return fmt.Sprintf("%s:%d", file, line)
}