func (f *validationFailer) Logf(format string, args ...interface{}) {
	f.mux.Lock()
	defer f.mux.Unlock()
	pc, file, line, _ := runtime.Caller(f.offset)
	_, fileName := path.Split(file)
	funcNameParts := strings.Split(runtime.FuncForPC(pc).Name(), ".")
	funcNamePartsIdx := len(funcNameParts) - 1
	funcName := funcNameParts[funcNamePartsIdx]
	prefix := fmt.Sprintf("%s:%d %s(): ", fileName, line, funcName)
	backendPrintf(prefix+format+"\n", args...)
}
Logf implements the Failer interface.
package audit
import (
	"bytes"
	"errors"
	"fmt"
	"path"
	"runtime"
	"strings"
	"sync"
	"time"
)
type Failer interface {
	IncrCallstackOffset() func()
	Logf(format string, args ...interface{})
	Fail(test Test, obtained, expected interface{}, msgs ...string) bool
}
type FailureDetail interface {
	Timestamp() time.Time
	Location() (string, int, string)
	Test() Test
	Error() error
	Message() string
}
type failureDetail struct {
	timestamp  time.Time
	fileName   string
	lineNumber int
	funcName   string
	test       Test
	err        error
	message    string
}
func (d *failureDetail) Timestamp() time.Time {
	return d.timestamp
}
func (d *failureDetail) Location() (string, int, string) {
	return d.fileName, d.lineNumber, d.funcName
}
func (d *failureDetail) Test() Test {
	return d.test
}
func (d *failureDetail) Error() error {
	return d.err
}
func (d *failureDetail) Message() string {
	return d.message
}
type Failures interface {
	HasErrors() bool
	Details() []FailureDetail
	Errors() []error
	Error() error
}
type panicFailer struct{}
func (f panicFailer) IncrCallstackOffset() func() {
	return func() {}
}
func (f panicFailer) Logf(format string, args ...interface{}) {
	backendPrintf(format+"\n", args...)
}
func (f panicFailer) Fail(test Test, obtained, expected interface{}, msgs ...string) bool {
	obex := obexString(test, obtained, expected)
	panic(failString(test, obex, msgs...))
}
func NewPanicAssertion() Assertion {
	return NewAssertion(&panicFailer{})
}
type validationFailer struct {
	mux     sync.Mutex
	offset  int
	details []FailureDetail
	errs    []error
}
func (f *validationFailer) HasErrors() bool {
	f.mux.Lock()
	defer f.mux.Unlock()
	return len(f.errs) > 0
}
func (f *validationFailer) Details() []FailureDetail {
	f.mux.Lock()
	defer f.mux.Unlock()
	return f.details
}
func (f *validationFailer) Errors() []error {
	f.mux.Lock()
	defer f.mux.Unlock()
	return f.errs
}
func (f *validationFailer) Error() error {
	f.mux.Lock()
	defer f.mux.Unlock()
	strs := []string{}
	for i, err := range f.errs {
		strs = append(strs, fmt.Sprintf("[%d] %v", i, err))
	}
	return errors.New(strings.Join(strs, " / "))
}
func (f *validationFailer) IncrCallstackOffset() func() {
	f.mux.Lock()
	defer f.mux.Unlock()
	offset := f.offset
	f.offset++
	return func() {
		f.mux.Lock()
		defer f.mux.Unlock()
		f.offset = offset
	}
}