testinglog

package
v0.1.7 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 10, 2020 License: Apache-2.0 Imports: 11 Imported by: 0

Documentation

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type FieldIgnoreFunc

type FieldIgnoreFunc func(fields map[string]string) []string

FieldIgnoreFunc is a function that decides which fields' values should be ignored in a log message.

type Logger

type Logger struct {
	// contains filtered or unexported fields
}

Logger logs messages to a test runner, and can optionally report differences between log messages received and those expected.

Example
/*
   Copyright (2020) Cobalt Speech and Language Inc.

   Licensed 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.
*/

package main

import (
	"bytes"
	"fmt"
)

// fakeRunner implements TestRunner, for testing the Logger.
type fakeRunner struct {
	b      bytes.Buffer
	failed bool
}

func (r *fakeRunner) Fail() {
	r.failed = true
}

func (r *fakeRunner) Log(args ...interface{}) {
	fmt.Fprint(&r.b, args...)
}

func main() {
	runner := &fakeRunner{}
	logger, _ := NewLogger(runner)

	logger.Error("msg", "There was a problem.", "data", 3.14)
	logger.Debug("msg", "Here's some pertinent information.", "numCalls", 17)

	logger.Done()

	fmt.Println(runner.b.String())
}
Output:
error {"msg":"There was a problem.","data":"3.14"}
debug {"msg":"Here's some pertinent information.","numCalls":"17"}

func NewLogger

func NewLogger(runner TestRunner, opts ...LoggerOption) (*Logger, error)

NewLogger creates a logger that reports all log messages to the provided test runner.

func (*Logger) Debug

func (l *Logger) Debug(keyvals ...interface{})

Debug checks whether the Logger expected a debug log line next. If not, it's reported to the test runner.

func (*Logger) Done

func (l *Logger) Done()

Done signals to the Logger that no more log methods will be called. It checks whether the Logger expected more log messages, and calls runner.Fail if any differences were reported.

func (*Logger) Error

func (l *Logger) Error(keyvals ...interface{})

Error checks whether the Logger expected an error log line next. If not, it's reported to the test runner.

func (*Logger) Info

func (l *Logger) Info(keyvals ...interface{})

Info checks whether the Logger expected an info log line next. If not, it's reported to the test runner.

func (*Logger) Trace

func (l *Logger) Trace(keyvals ...interface{})

Trace checks whether the Logger expected a trace log line next. If not, it's reported to the test runner.

type LoggerOption

type LoggerOption func(*Logger) error

func WithActualOutputFile

func WithActualOutputFile(file string) LoggerOption

WithActualOutputFile specifies a file path where all actual log messages are written. If a truth file has been provided with WithTruthFile, the actual output file is only created if actual log messages differ from those in the truth file.

Example
hypFile, remove, err := writeTemporaryFile(strings.Join([]string{
	`error {"msg":"There was a problem.","data":"3.14"}`,
	`debug {"msg":"Here's the number of calls.","numCalls":"18"}`,
}, "\n"))
if err != nil {
	fmt.Println(err)
	return
}

defer remove()

// Get a file we can use for the actual log output.
var actualFile string
actualFile, remove, err = writeTemporaryFile("")

if err != nil {
	fmt.Println(err)
	return
}

defer remove()

runner := fakeRunner{}

logger, err := NewLogger(&runner, WithTruthFile(hypFile), WithActualOutputFile(actualFile))
if err != nil {
	fmt.Println(err)
	return
}

logger.Error("msg", "There was a problem.", "data", 3.14)
logger.Debug("msg", "Here's some pertinent information.", "numCalls", 18) // doesn't match hyp
logger.Trace("msg", "This trace message shouldn't be here.")

logger.Done()

// Get the actual log output.
var actualBytes []byte
actualBytes, err = ioutil.ReadFile(actualFile)

if err != nil {
	fmt.Println(err)
	return
}

fmt.Print(runner.b.String())
fmt.Print(string(actualBytes))
fmt.Println(runner.failed)
Output:
unexpected log message (-want +got):
  string(
- 	`debug {"msg":"Here's the number of calls.","numCalls":"18"}`,
+ 	`debug {"msg":"Here's some pertinent information.","numCalls":"18"}`,
  )
unexpected log message (-want +got):
  string(
- 	"",
+ 	`trace {"msg":"This trace message shouldn't be here."}`,
  )
error {"msg":"There was a problem.","data":"3.14"}
debug {"msg":"Here's some pertinent information.","numCalls":"18"}
trace {"msg":"This trace message shouldn't be here."}
true

func WithFieldIgnoreFunc

func WithFieldIgnoreFunc(ignorer FieldIgnoreFunc) LoggerOption

WithFieldIgnoreFunc provides the Logger with a function that returns a list of fields to ignore in each log message. The keys will still be checked, but not the values. The keys and values in the log message are formatted as strings before being provided to the FieldIgnorer.

If ignorer is nil, this option has no effect.

Example
hypFile, remove, err := writeTemporaryFile(strings.Join([]string{
	`trace {"msg":"An ID was generated.","id":"brh634381n1ts5ibr1eg"}`,
	`debug {"msg":"This ID is deterministic.","id":"42"}`,
}, "\n"))
if err != nil {
	fmt.Println(err)
	return
}

defer remove()

runner := fakeRunner{}

ignorer := func(fields map[string]string) []string {
	msg, ok := fields["msg"]

	// Ignore only the "id" field in the non-deterministic ID log line.
	if ok && msg == "An ID was generated." {
		return []string{"id"}
	}

	return nil
}

logger, err := NewLogger(&runner, WithTruthFile(hypFile), WithFieldIgnoreFunc(ignorer))
if err != nil {
	fmt.Println(err)
	return
}

// This is not deterministic.
id := strconv.Itoa(rand.Intn(10000000))

logger.Trace("msg", "An ID was generated.", "id", id)
logger.Debug("msg", "This ID is deterministic.", "id", 12) // doesn't match hyp

logger.Done()

fmt.Print(strings.Replace(runner.b.String(), id, "<id removed>", 1))
Output:
trace {"msg":"An ID was generated.","id":"<id removed>"}
debug {"msg":"This ID is deterministic.","id":"12"}
unexpected log message (-want +got):
  string(
- 	`debug {"msg":"This ID is deterministic.","id":"42"}`,
+ 	`debug {"msg":"This ID is deterministic.","id":"12"}`,
  )

func WithTruthFile

func WithTruthFile(file string) LoggerOption

WithTruthFile sets the Logger to compare each log message with the log text in the provided file. Each expected log line should be separated from the next by a newline. If there are any differences between expected and received log lines, they are reported to the test runner and the runner's Fail method will be called when Done is called (unless WithoutFailure is used in conjunction with this option).

This function does not handle cases where the logging output order is non-deterministic (e.g. if the function being tested uses multiple goroutines).

If the provided file does not exist, the Logger will not expect any log lines.

Example
// Write an example file.
hypFile, remove, err := writeTemporaryFile(strings.Join([]string{
	`error {"msg":"There was a problem.","data":"3.14"}`,
	`debug {"msg":"Here's the number of calls.","numCalls":"17"}`,
}, "\n"))
if err != nil {
	fmt.Println(err)
	return
}

defer remove()

runner := fakeRunner{}

logger, err := NewLogger(&runner, WithTruthFile(hypFile))
if err != nil {
	fmt.Println(err)
	return
}

logger.Error("msg", "There was a problem.", "data", 3.14)
logger.Debug("msg", "Here's some pertinent information.", "numCalls", 17) // doesn't match hyp
logger.Trace("msg", "This trace message shouldn't be here.")

logger.Done()

fmt.Print(runner.b.String())
fmt.Println(runner.failed)
Output:
error {"msg":"There was a problem.","data":"3.14"}
debug {"msg":"Here's some pertinent information.","numCalls":"17"}
unexpected log message (-want +got):
  string(
- 	`debug {"msg":"Here's the number of calls.","numCalls":"17"}`,
+ 	`debug {"msg":"Here's some pertinent information.","numCalls":"17"}`,
  )
trace {"msg":"This trace message shouldn't be here."}
unexpected log message (-want +got):
  string(
- 	"",
+ 	`trace {"msg":"This trace message shouldn't be here."}`,
  )
true

func WithoutFailure

func WithoutFailure() LoggerOption

WithoutFailure sets the Logger to not call Fail() on the test runner even if log messages are different than expected.

Example
hypFile, remove, err := writeTemporaryFile(strings.Join([]string{
	`error {"msg":"There was a problem.","data":"3.14"}`,
	`debug {"msg":"Here's the number of calls.","numCalls":"19"}`,
}, "\n"))
if err != nil {
	fmt.Println(err)
	return
}

defer remove()

runner := fakeRunner{}

logger, err := NewLogger(&runner, WithTruthFile(hypFile), WithoutFailure())
if err != nil {
	fmt.Println(err)
	return
}

logger.Error("msg", "There was a problem.", "data", 3.14)

// This log message doesn't match, but we still want the test to pass.
logger.Debug("msg", "Here's some pertinent information.", "numCalls", 19)

logger.Done()

fmt.Println(runner.failed)
Output:
false

type TestRunner

type TestRunner interface {
	Log(args ...interface{})
	Fail()
}

TestRunner is an interface for an object that can receive reports of test failure and logging. It is the subset of testing.TB that the Logger requires.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL