Replace gotestyourself by gotest.tools

github.com/gotestyourself/gotestyourself moved to gotest.tools with
version 2.0.0. Moving to that one, bumping it to v2.1.0.

Signed-off-by: Vincent Demeester <vincent@sbr.pm>
This commit is contained in:
Vincent Demeester 2018-06-08 18:09:51 +02:00
parent c752b0991e
commit c147e9e1a4
No known key found for this signature in database
GPG Key ID: 083CC6FD6EB699A3
28 changed files with 1021 additions and 520 deletions

View File

@ -19,7 +19,7 @@ github.com/docker/go-units 9e638d38cf6977a37a8ea0078f3ee75a7cdb2dd1
github.com/docker/go-connections 7beb39f0b969b075d1325fecb092faf27fd357b6
golang.org/x/text f72d8390a633d5dfb0cc84043294db9f6c935756
github.com/pmezard/go-difflib v1.0.0
github.com/gotestyourself/gotestyourself cf3a5ab914a2efa8bc838d09f5918c1d44d029
gotest.tools v2.1.0
github.com/google/go-cmp v0.2.0
github.com/RackSec/srslog 456df3a81436d29ba874f3590eeeee25d666f8a5

View File

@ -1,33 +0,0 @@
# Go Test Yourself
A collection of packages compatible with `go test` to support common testing
patterns.
[![GoDoc](https://godoc.org/github.com/gotestyourself/gotestyourself?status.svg)](https://godoc.org/github.com/gotestyourself/gotestyourself)
[![CircleCI](https://circleci.com/gh/gotestyourself/gotestyourself/tree/master.svg?style=shield)](https://circleci.com/gh/gotestyourself/gotestyourself/tree/master)
[![Go Reportcard](https://goreportcard.com/badge/github.com/gotestyourself/gotestyourself)](https://goreportcard.com/report/github.com/gotestyourself/gotestyourself)
## Packages
* [assert](http://godoc.org/github.com/gotestyourself/gotestyourself/assert) -
compare values and fail the test when the comparison fails
* [env](http://godoc.org/github.com/gotestyourself/gotestyourself/env) -
test code that uses environment variables
* [fs](http://godoc.org/github.com/gotestyourself/gotestyourself/fs) -
create test files and directories
* [golden](http://godoc.org/github.com/gotestyourself/gotestyourself/golden) -
compare large multi-line strings
* [icmd](http://godoc.org/github.com/gotestyourself/gotestyourself/icmd) -
execute binaries and test the output
* [poll](http://godoc.org/github.com/gotestyourself/gotestyourself/poll) -
test asynchronous code by polling until a desired state is reached
* [skip](http://godoc.org/github.com/gotestyourself/gotestyourself/skip) -
skip tests based on conditions
* [testsum](http://godoc.org/github.com/gotestyourself/gotestyourself/testsum) -
a program to summarize `go test` output and test failures
## Related
* [maxbrunsfeld/counterfeiter](https://github.com/maxbrunsfeld/counterfeiter) - generate fakes for interfaces
* [jonboulle/clockwork](https://github.com/jonboulle/clockwork) - a fake clock for testing code that uses `time`

View File

@ -1,50 +0,0 @@
go-difflib
==========
[![Build Status](https://travis-ci.org/pmezard/go-difflib.png?branch=master)](https://travis-ci.org/pmezard/go-difflib)
[![GoDoc](https://godoc.org/github.com/pmezard/go-difflib/difflib?status.svg)](https://godoc.org/github.com/pmezard/go-difflib/difflib)
Go-difflib is a partial port of python 3 difflib package. Its main goal
was to make unified and context diff available in pure Go, mostly for
testing purposes.
The following class and functions (and related tests) have be ported:
* `SequenceMatcher`
* `unified_diff()`
* `context_diff()`
## Installation
```bash
$ go get github.com/pmezard/go-difflib/difflib
```
### Quick Start
Diffs are configured with Unified (or ContextDiff) structures, and can
be output to an io.Writer or returned as a string.
```Go
diff := UnifiedDiff{
A: difflib.SplitLines("foo\nbar\n"),
B: difflib.SplitLines("foo\nbaz\n"),
FromFile: "Original",
ToFile: "Current",
Context: 3,
}
text, _ := GetUnifiedDiffString(diff)
fmt.Printf(text)
```
would output:
```
--- Original
+++ Current
@@ -1,3 +1,3 @@
foo
-bar
+baz
```

31
vendor/gotest.tools/README.md vendored Normal file
View File

@ -0,0 +1,31 @@
# gotest.tools
A collection of packages to augment `testing` and support common patterns.
[![GoDoc](https://godoc.org/gotest.tools?status.svg)](https://godoc.org/gotest.tools)
[![CircleCI](https://circleci.com/gh/gotestyourself/gotestyourself/tree/master.svg?style=shield)](https://circleci.com/gh/gotestyourself/gotestyourself/tree/master)
[![Go Reportcard](https://goreportcard.com/badge/gotest.tools)](https://goreportcard.com/report/gotest.tools)
## Packages
* [assert](http://godoc.org/gotest.tools/assert) -
compare values and fail the test when a comparison fails
* [env](http://godoc.org/gotest.tools/env) -
test code which uses environment variables
* [fs](http://godoc.org/gotest.tools/fs) -
create temporary files and compare a filesystem tree to an expected value
* [golden](http://godoc.org/gotest.tools/golden) -
compare large multi-line strings against values frozen in golden files
* [icmd](http://godoc.org/gotest.tools/icmd) -
execute binaries and test the output
* [poll](http://godoc.org/gotest.tools/poll) -
test asynchronous code by polling until a desired state is reached
* [skip](http://godoc.org/gotest.tools/skip) -
skip a test and print the source code of the condition used to skip the test
## Related
* [gotest.tools/gotestsum](https://github.com/gotestyourself/gotestsum) - go test runner with custom output
* [maxbrunsfeld/counterfeiter](https://github.com/maxbrunsfeld/counterfeiter) - generate fakes for interfaces
* [jonboulle/clockwork](https://github.com/jonboulle/clockwork) - a fake clock for testing code that uses `time`

View File

@ -8,7 +8,7 @@ comparison fails. The one difference is that Assert() will end the test executio
immediately (using t.FailNow()) whereas Check() will fail the test (using t.Fail()),
return the value of the comparison, then proceed with the rest of the test case.
Example Usage
Example usage
The example below shows assert used with some common types.
@ -16,8 +16,8 @@ The example below shows assert used with some common types.
import (
"testing"
"github.com/gotestyourself/gotestyourself/assert"
is "github.com/gotestyourself/gotestyourself/assert/cmp"
"gotest.tools/assert"
is "gotest.tools/assert/cmp"
)
func TestEverything(t *testing.T) {
@ -49,12 +49,20 @@ The example below shows assert used with some common types.
Comparisons
https://godoc.org/github.com/gotestyourself/gotestyourself/assert/cmp provides
Package https://godoc.org/gotest.tools/assert/cmp provides
many common comparisons. Additional comparisons can be written to compare
values in other ways. See the example Assert (CustomComparison).
Automated migration from testify
gty-migrate-from-testify is a binary which can update source code which uses
testify assertions to use the assertions provided by this package.
See http://bit.do/cmd-gty-migrate-from-testify.
*/
package assert
package assert // import "gotest.tools/assert"
import (
"fmt"
@ -62,9 +70,9 @@ import (
"go/token"
gocmp "github.com/google/go-cmp/cmp"
"github.com/gotestyourself/gotestyourself/assert/cmp"
"github.com/gotestyourself/gotestyourself/internal/format"
"github.com/gotestyourself/gotestyourself/internal/source"
"gotest.tools/assert/cmp"
"gotest.tools/internal/format"
"gotest.tools/internal/source"
)
// BoolOrComparison can be a bool, or cmp.Comparison. See Assert() for usage.
@ -234,7 +242,17 @@ func NilError(t TestingT, err error, msgAndArgs ...interface{}) {
}
// Equal uses the == operator to assert two values are equal and fails the test
// if they are not equal. This is equivalent to Assert(t, cmp.Equal(x, y)).
// if they are not equal.
//
// If the comparison fails Equal will use the variable names for x and y as part
// of the failure message to identify the actual and expected values.
//
// If either x or y are a multi-line string the failure message will include a
// unified diff of the two values. If the values only differ by whitespace
// the unified diff will be augmented by replacing whitespace characters with
// visible characters to identify the whitespace difference.
//
// This is equivalent to Assert(t, cmp.Equal(x, y)).
func Equal(t TestingT, x, y interface{}, msgAndArgs ...interface{}) {
if ht, ok := t.(helperT); ok {
ht.Helper()
@ -242,8 +260,12 @@ func Equal(t TestingT, x, y interface{}, msgAndArgs ...interface{}) {
assert(t, t.FailNow, argsAfterT, cmp.Equal(x, y), msgAndArgs...)
}
// DeepEqual uses https://github.com/google/go-cmp/cmp to assert two values
// are equal and fails the test if they are not equal.
// DeepEqual uses google/go-cmp (http://bit.do/go-cmp) to assert two values are
// equal and fails the test if they are not equal.
//
// Package https://godoc.org/gotest.tools/assert/opt provides some additional
// commonly used Options.
//
// This is equivalent to Assert(t, cmp.DeepEqual(x, y)).
func DeepEqual(t TestingT, x, y interface{}, opts ...gocmp.Option) {
if ht, ok := t.(helperT); ok {
@ -276,7 +298,7 @@ func ErrorContains(t TestingT, err error, substring string, msgAndArgs ...interf
//
// Expected can be one of:
// a func(error) bool which returns true if the error is the expected type,
// an instance of a struct of the expected type,
// an instance of (or a pointer to) a struct of the expected type,
// a pointer to an interface the error is expected to implement,
// a reflect.Type of the expected struct or interface.
//

View File

@ -1,5 +1,5 @@
/*Package cmp provides Comparisons for Assert and Check*/
package cmp
package cmp // import "gotest.tools/assert/cmp"
import (
"fmt"
@ -7,7 +7,7 @@ import (
"strings"
"github.com/google/go-cmp/cmp"
"github.com/pmezard/go-difflib/difflib"
"gotest.tools/internal/format"
)
// Comparison is a function which compares values and returns ResultSuccess if
@ -15,10 +15,12 @@ import (
// Result will contain a message about why it failed.
type Comparison func() Result
// DeepEqual compares two values using https://godoc.org/github.com/google/go-cmp/cmp
// DeepEqual compares two values using google/go-cmp (http://bit.do/go-cmp)
// and succeeds if the values are equal.
//
// The comparison can be customized using comparison Options.
// Package https://godoc.org/gotest.tools/assert/opt provides some additional
// commonly used Options.
func DeepEqual(x, y interface{}, opts ...cmp.Option) Comparison {
return func() (result Result) {
defer func() {
@ -27,7 +29,10 @@ func DeepEqual(x, y interface{}, opts ...cmp.Option) Comparison {
}
}()
diff := cmp.Diff(x, y, opts...)
return toResult(diff == "", "\n"+diff)
if diff == "" {
return ResultSuccess
}
return multiLineDiffResult(diff)
}
}
@ -53,14 +58,15 @@ func toResult(success bool, msg string) Result {
return ResultFailure(msg)
}
// Equal succeeds if x == y.
// Equal succeeds if x == y. See assert.Equal for full documentation.
func Equal(x, y interface{}) Comparison {
return func() Result {
switch {
case x == y:
return ResultSuccess
case isMultiLineStringCompare(x, y):
return multiLineStringDiffResult(x.(string), y.(string))
diff := format.UnifiedDiff(format.DiffConfig{A: x.(string), B: y.(string)})
return multiLineDiffResult(diff)
}
return ResultFailureTemplate(`
{{- .Data.x}} (
@ -86,15 +92,7 @@ func isMultiLineStringCompare(x, y interface{}) bool {
return strings.Contains(strX, "\n") || strings.Contains(strY, "\n")
}
func multiLineStringDiffResult(x, y string) Result {
diff, err := difflib.GetUnifiedDiffString(difflib.UnifiedDiff{
A: difflib.SplitLines(x),
B: difflib.SplitLines(y),
Context: 3,
})
if err != nil {
return ResultFailure(fmt.Sprintf("failed to diff: %s", err))
}
func multiLineDiffResult(diff string) Result {
return ResultFailureTemplate(`
--- {{ with callArg 0 }}{{ formatNode . }}{{else}}{{end}}
+++ {{ with callArg 1 }}{{ formatNode . }}{{else}}{{end}}
@ -242,7 +240,7 @@ func isNil(obj interface{}, msgFunc func(reflect.Value) string) Comparison {
//
// Expected can be one of:
// a func(error) bool which returns true if the error is the expected type,
// an instance of a struct of the expected type,
// an instance of (or a pointer to) a struct of the expected type,
// a pointer to an interface the error is expected to implement,
// a reflect.Type of the expected struct or interface.
func ErrorType(err error, expected interface{}) Comparison {
@ -261,7 +259,7 @@ func ErrorType(err error, expected interface{}) Comparison {
expectedType := reflect.TypeOf(expected)
switch {
case expectedType.Kind() == reflect.Struct:
case expectedType.Kind() == reflect.Struct, isPtrToStruct(expectedType):
return cmpErrorTypeEqualType(err, expectedType)
case isPtrToInterface(expectedType):
return cmpErrorTypeImplementsType(err, expectedType.Elem())
@ -308,3 +306,7 @@ func cmpErrorTypeImplementsType(err error, expectedType reflect.Type) Result {
func isPtrToInterface(typ reflect.Type) bool {
return typ.Kind() == reflect.Ptr && typ.Elem().Kind() == reflect.Interface
}
func isPtrToStruct(typ reflect.Type) bool {
return typ.Kind() == reflect.Ptr && typ.Elem().Kind() == reflect.Struct
}

View File

@ -6,7 +6,7 @@ import (
"go/ast"
"text/template"
"github.com/gotestyourself/gotestyourself/internal/source"
"gotest.tools/internal/source"
)
// Result of a Comparison.

View File

@ -4,9 +4,9 @@ import (
"fmt"
"go/ast"
"github.com/gotestyourself/gotestyourself/assert/cmp"
"github.com/gotestyourself/gotestyourself/internal/format"
"github.com/gotestyourself/gotestyourself/internal/source"
"gotest.tools/assert/cmp"
"gotest.tools/internal/format"
"gotest.tools/internal/source"
)
func runComparison(

View File

@ -1,13 +1,14 @@
/*Package env provides functions to test code that read environment variables
or the current working directory.
*/
package env
package env // import "gotest.tools/env"
import (
"os"
"strings"
"github.com/gotestyourself/gotestyourself/assert"
"gotest.tools/assert"
"gotest.tools/x/subtest"
)
type helperT interface {
@ -23,7 +24,7 @@ func Patch(t assert.TestingT, key, value string) func() {
}
oldValue, ok := os.LookupEnv(key)
assert.NilError(t, os.Setenv(key, value))
return func() {
cleanup := func() {
if ht, ok := t.(helperT); ok {
ht.Helper()
}
@ -33,6 +34,10 @@ func Patch(t assert.TestingT, key, value string) func() {
}
assert.NilError(t, os.Setenv(key, oldValue))
}
if tc, ok := t.(subtest.TestContext); ok {
tc.AddCleanup(cleanup)
}
return cleanup
}
// PatchAll sets the environment to env, and returns a function which will
@ -47,7 +52,7 @@ func PatchAll(t assert.TestingT, env map[string]string) func() {
for key, value := range env {
assert.NilError(t, os.Setenv(key, value), "setenv %s=%s", key, value)
}
return func() {
cleanup := func() {
if ht, ok := t.(helperT); ok {
ht.Helper()
}
@ -56,6 +61,10 @@ func PatchAll(t assert.TestingT, env map[string]string) func() {
assert.NilError(t, os.Setenv(key, oldVal), "setenv %s=%s", key, oldVal)
}
}
if tc, ok := t.(subtest.TestContext); ok {
tc.AddCleanup(cleanup)
}
return cleanup
}
// ToMap takes a list of strings in the format returned by os.Environ() and
@ -89,10 +98,14 @@ func ChangeWorkingDir(t assert.TestingT, dir string) func() {
cwd, err := os.Getwd()
assert.NilError(t, err)
assert.NilError(t, os.Chdir(dir))
return func() {
cleanup := func() {
if ht, ok := t.(helperT); ok {
ht.Helper()
}
assert.NilError(t, os.Chdir(cwd))
}
if tc, ok := t.(subtest.TestContext); ok {
tc.AddCleanup(cleanup)
}
return cleanup
}

View File

@ -1,17 +1,20 @@
/*Package fs provides tools for creating and working with temporary files and
directories.
/*Package fs provides tools for creating temporary files, and testing the
contents and structure of a directory.
*/
package fs
package fs // import "gotest.tools/fs"
import (
"io/ioutil"
"os"
"path/filepath"
"github.com/gotestyourself/gotestyourself/assert"
"gotest.tools/assert"
"gotest.tools/x/subtest"
)
// Path objects return their filesystem path. Both File and Dir implement Path.
// Path objects return their filesystem path. Path may be implemented by a
// real filesystem object (such as File and Dir) or by a type which updates
// entries in a Manifest.
type Path interface {
Path() string
Remove()
@ -45,6 +48,9 @@ func NewFile(t assert.TestingT, prefix string, ops ...PathOp) *File {
for _, op := range ops {
assert.NilError(t, op(file))
}
if tc, ok := t.(subtest.TestContext); ok {
tc.AddCleanup(file.Remove)
}
return file
}
@ -77,6 +83,9 @@ func NewDir(t assert.TestingT, prefix string, ops ...PathOp) *Dir {
for _, op := range ops {
assert.NilError(t, op(dir))
}
if tc, ok := t.(subtest.TestContext); ok {
tc.AddCleanup(dir.Remove)
}
return dir
}

129
vendor/gotest.tools/fs/manifest.go vendored Normal file
View File

@ -0,0 +1,129 @@
package fs
import (
"io"
"io/ioutil"
"os"
"path/filepath"
"github.com/pkg/errors"
"gotest.tools/assert"
)
// Manifest stores the expected structure and properties of files and directories
// in a filesystem.
type Manifest struct {
root *directory
}
type resource struct {
mode os.FileMode
uid uint32
gid uint32
}
type file struct {
resource
content io.ReadCloser
}
func (f *file) Type() string {
return "file"
}
type symlink struct {
resource
target string
}
func (f *symlink) Type() string {
return "symlink"
}
type directory struct {
resource
items map[string]dirEntry
}
func (f *directory) Type() string {
return "directory"
}
type dirEntry interface {
Type() string
}
// ManifestFromDir creates a Manifest by reading the directory at path. The
// manifest stores the structure and properties of files in the directory.
// ManifestFromDir can be used with Equal to compare two directories.
func ManifestFromDir(t assert.TestingT, path string) Manifest {
if ht, ok := t.(helperT); ok {
ht.Helper()
}
manifest, err := manifestFromDir(path)
assert.NilError(t, err)
return manifest
}
func manifestFromDir(path string) (Manifest, error) {
info, err := os.Stat(path)
switch {
case err != nil:
return Manifest{}, err
case !info.IsDir():
return Manifest{}, errors.Errorf("path %s must be a directory", path)
}
directory, err := newDirectory(path, info)
return Manifest{root: directory}, err
}
func newDirectory(path string, info os.FileInfo) (*directory, error) {
items := make(map[string]dirEntry)
children, err := ioutil.ReadDir(path)
if err != nil {
return nil, err
}
for _, child := range children {
fullPath := filepath.Join(path, child.Name())
items[child.Name()], err = getTypedResource(fullPath, child)
if err != nil {
return nil, err
}
}
return &directory{
resource: newResourceFromInfo(info),
items: items,
}, nil
}
func getTypedResource(path string, info os.FileInfo) (dirEntry, error) {
switch {
case info.IsDir():
return newDirectory(path, info)
case info.Mode()&os.ModeSymlink != 0:
return newSymlink(path, info)
// TODO: devices, pipes?
default:
return newFile(path, info)
}
}
func newSymlink(path string, info os.FileInfo) (*symlink, error) {
target, err := os.Readlink(path)
return &symlink{
resource: newResourceFromInfo(info),
target: target,
}, err
}
func newFile(path string, info os.FileInfo) (*file, error) {
// TODO: defer file opening to reduce number of open FDs?
readCloser, err := os.Open(path)
return &file{
resource: newResourceFromInfo(info),
content: readCloser,
}, err
}

30
vendor/gotest.tools/fs/manifest_unix.go vendored Normal file
View File

@ -0,0 +1,30 @@
// +build !windows
package fs
import (
"os"
"syscall"
)
const (
defaultRootDirMode = os.ModeDir | 0700
defaultSymlinkMode = os.ModeSymlink | 0777
)
func newResourceFromInfo(info os.FileInfo) resource {
statT := info.Sys().(*syscall.Stat_t)
return resource{
mode: info.Mode(),
uid: statT.Uid,
gid: statT.Gid,
}
}
func (p *filePath) SetMode(mode os.FileMode) {
p.file.mode = mode
}
func (p *directoryPath) SetMode(mode os.FileMode) {
p.directory.mode = mode | os.ModeDir
}

View File

@ -0,0 +1,22 @@
package fs
import "os"
const (
defaultRootDirMode = os.ModeDir | 0777
defaultSymlinkMode = os.ModeSymlink | 0666
)
func newResourceFromInfo(info os.FileInfo) resource {
return resource{mode: info.Mode()}
}
func (p *filePath) SetMode(mode os.FileMode) {
bits := mode & 0600
p.file.mode = bits + bits/010 + bits/0100
}
// TODO: is mode ignored on windows?
func (p *directoryPath) SetMode(mode os.FileMode) {
p.directory.mode = defaultRootDirMode
}

View File

@ -1,32 +1,73 @@
package fs
import (
"bytes"
"io"
"io/ioutil"
"os"
"path/filepath"
"strings"
"time"
"github.com/pkg/errors"
)
// PathOp is a function which accepts a Path to perform some operation
const defaultFileMode = 0644
// PathOp is a function which accepts a Path and performs an operation on that
// path. When called with real filesystem objects (File or Dir) a PathOp modifies
// the filesystem at the path. When used with a Manifest object a PathOp updates
// the manifest to expect a value.
type PathOp func(path Path) error
type manifestResource interface {
SetMode(mode os.FileMode)
SetUID(uid uint32)
SetGID(gid uint32)
}
type manifestFile interface {
manifestResource
SetContent(content io.ReadCloser)
}
type manifestDirectory interface {
manifestResource
AddSymlink(path, target string) error
AddFile(path string, ops ...PathOp) error
AddDirectory(path string, ops ...PathOp) error
}
// WithContent writes content to a file at Path
func WithContent(content string) PathOp {
return func(path Path) error {
return ioutil.WriteFile(path.Path(), []byte(content), 0644)
if m, ok := path.(manifestFile); ok {
m.SetContent(ioutil.NopCloser(strings.NewReader(content)))
return nil
}
return ioutil.WriteFile(path.Path(), []byte(content), defaultFileMode)
}
}
// WithBytes write bytes to a file at Path
func WithBytes(raw []byte) PathOp {
return func(path Path) error {
return ioutil.WriteFile(path.Path(), raw, 0644)
if m, ok := path.(manifestFile); ok {
m.SetContent(ioutil.NopCloser(bytes.NewReader(raw)))
return nil
}
return ioutil.WriteFile(path.Path(), raw, defaultFileMode)
}
}
// AsUser changes ownership of the file system object at Path
func AsUser(uid, gid int) PathOp {
return func(path Path) error {
if m, ok := path.(manifestResource); ok {
m.SetUID(uint32(uid))
m.SetGID(uint32(gid))
return nil
}
return os.Chown(path.Path(), uid, gid)
}
}
@ -34,6 +75,11 @@ func AsUser(uid, gid int) PathOp {
// WithFile creates a file in the directory at path with content
func WithFile(filename, content string, ops ...PathOp) PathOp {
return func(path Path) error {
if m, ok := path.(manifestDirectory); ok {
ops = append([]PathOp{WithContent(content), WithMode(defaultFileMode)}, ops...)
return m.AddFile(filename, ops...)
}
fullpath := filepath.Join(path.Path(), filepath.FromSlash(filename))
if err := createFile(fullpath, content); err != nil {
return err
@ -43,12 +89,22 @@ func WithFile(filename, content string, ops ...PathOp) PathOp {
}
func createFile(fullpath string, content string) error {
return ioutil.WriteFile(fullpath, []byte(content), 0644)
return ioutil.WriteFile(fullpath, []byte(content), defaultFileMode)
}
// WithFiles creates all the files in the directory at path with their content
func WithFiles(files map[string]string) PathOp {
return func(path Path) error {
if m, ok := path.(manifestDirectory); ok {
for filename, content := range files {
// TODO: remove duplication with WithFile
if err := m.AddFile(filename, WithContent(content), WithMode(defaultFileMode)); err != nil {
return err
}
}
return nil
}
for filename, content := range files {
fullpath := filepath.Join(path.Path(), filepath.FromSlash(filename))
if err := createFile(fullpath, content); err != nil {
@ -62,6 +118,9 @@ func WithFiles(files map[string]string) PathOp {
// FromDir copies the directory tree from the source path into the new Dir
func FromDir(source string) PathOp {
return func(path Path) error {
if _, ok := path.(manifestDirectory); ok {
return errors.New("use manifest.FromDir")
}
return copyDirectory(source, path.Path())
}
}
@ -69,9 +128,15 @@ func FromDir(source string) PathOp {
// WithDir creates a subdirectory in the directory at path. Additional PathOp
// can be used to modify the subdirectory
func WithDir(name string, ops ...PathOp) PathOp {
const defaultMode = 0755
return func(path Path) error {
if m, ok := path.(manifestDirectory); ok {
ops = append([]PathOp{WithMode(defaultMode)}, ops...)
return m.AddDirectory(name, ops...)
}
fullpath := filepath.Join(path.Path(), filepath.FromSlash(name))
err := os.MkdirAll(fullpath, 0755)
err := os.MkdirAll(fullpath, defaultMode)
if err != nil {
return err
}
@ -91,6 +156,10 @@ func applyPathOps(path Path, ops []PathOp) error {
// WithMode sets the file mode on the directory or file at path
func WithMode(mode os.FileMode) PathOp {
return func(path Path) error {
if m, ok := path.(manifestResource); ok {
m.SetMode(mode)
return nil
}
return os.Chmod(path.Path(), mode)
}
}
@ -112,6 +181,7 @@ func copyDirectory(source, dest string) error {
}
continue
}
// TODO: handle symlinks
if err := copyFile(sourcePath, destPath); err != nil {
return err
}
@ -134,6 +204,9 @@ func copyFile(source, dest string) error {
// the other functions in this package.
func WithSymlink(path, target string) PathOp {
return func(root Path) error {
if v, ok := root.(manifestDirectory); ok {
return v.AddSymlink(path, target)
}
return os.Symlink(filepath.Join(root.Path(), target), filepath.Join(root.Path(), path))
}
}
@ -145,6 +218,9 @@ func WithSymlink(path, target string) PathOp {
// the other functions in this package.
func WithHardlink(path, target string) PathOp {
return func(root Path) error {
if _, ok := root.(manifestDirectory); ok {
return errors.New("WithHardlink yet implemented for manifests")
}
return os.Link(filepath.Join(root.Path(), target), filepath.Join(root.Path(), path))
}
}
@ -153,6 +229,9 @@ func WithHardlink(path, target string) PathOp {
// at path.
func WithTimestamps(atime, mtime time.Time) PathOp {
return func(root Path) error {
if _, ok := root.(manifestDirectory); ok {
return errors.New("WithTimestamp yet implemented for manifests")
}
return os.Chtimes(root.Path(), atime, mtime)
}
}

151
vendor/gotest.tools/fs/path.go vendored Normal file
View File

@ -0,0 +1,151 @@
package fs
import (
"bytes"
"io"
"io/ioutil"
"os"
"gotest.tools/assert"
)
// resourcePath is an adaptor for resources so they can be used as a Path
// with PathOps.
type resourcePath struct{}
func (p *resourcePath) Path() string {
return "manifest: not a filesystem path"
}
func (p *resourcePath) Remove() {}
type filePath struct {
resourcePath
file *file
}
func (p *filePath) SetContent(content io.ReadCloser) {
p.file.content = content
}
func (p *filePath) SetUID(uid uint32) {
p.file.uid = uid
}
func (p *filePath) SetGID(gid uint32) {
p.file.gid = gid
}
type directoryPath struct {
resourcePath
directory *directory
}
func (p *directoryPath) SetUID(uid uint32) {
p.directory.uid = uid
}
func (p *directoryPath) SetGID(gid uint32) {
p.directory.gid = gid
}
func (p *directoryPath) AddSymlink(path, target string) error {
p.directory.items[path] = &symlink{
resource: newResource(defaultSymlinkMode),
target: target,
}
return nil
}
func (p *directoryPath) AddFile(path string, ops ...PathOp) error {
newFile := &file{resource: newResource(0)}
p.directory.items[path] = newFile
exp := &filePath{file: newFile}
return applyPathOps(exp, ops)
}
func (p *directoryPath) AddDirectory(path string, ops ...PathOp) error {
newDir := newDirectoryWithDefaults()
p.directory.items[path] = newDir
exp := &directoryPath{directory: newDir}
return applyPathOps(exp, ops)
}
// Expected returns a Manifest with a directory structured created by ops. The
// PathOp operations are applied to the manifest as expectations of the
// filesystem structure and properties.
func Expected(t assert.TestingT, ops ...PathOp) Manifest {
if ht, ok := t.(helperT); ok {
ht.Helper()
}
newDir := newDirectoryWithDefaults()
e := &directoryPath{directory: newDir}
assert.NilError(t, applyPathOps(e, ops))
return Manifest{root: newDir}
}
func newDirectoryWithDefaults() *directory {
return &directory{
resource: newResource(defaultRootDirMode),
items: make(map[string]dirEntry),
}
}
func newResource(mode os.FileMode) resource {
return resource{
mode: mode,
uid: currentUID(),
gid: currentGID(),
}
}
func currentUID() uint32 {
return normalizeID(os.Getuid())
}
func currentGID() uint32 {
return normalizeID(os.Getgid())
}
func normalizeID(id int) uint32 {
// ids will be -1 on windows
if id < 0 {
return 0
}
return uint32(id)
}
var anyFileContent = ioutil.NopCloser(bytes.NewReader(nil))
// MatchAnyFileContent is a PathOp that updates a Manifest so that the file
// at path may contain any content.
func MatchAnyFileContent(path Path) error {
if m, ok := path.(*filePath); ok {
m.SetContent(anyFileContent)
}
return nil
}
const anyFile = "*"
// MatchExtraFiles is a PathOp that updates a Manifest to allow a directory
// to contain unspecified files.
func MatchExtraFiles(path Path) error {
if m, ok := path.(*directoryPath); ok {
m.AddFile(anyFile)
}
return nil
}
// anyFileMode is represented by uint32_max
const anyFileMode os.FileMode = 4294967295
// MatchAnyFileMode is a PathOp that updates a Manifest so that the resource at path
// will match any file mode.
func MatchAnyFileMode(path Path) error {
if m, ok := path.(manifestResource); ok {
m.SetMode(anyFileMode)
}
return nil
}

215
vendor/gotest.tools/fs/report.go vendored Normal file
View File

@ -0,0 +1,215 @@
package fs
import (
"bytes"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"sort"
"strings"
"gotest.tools/assert/cmp"
"gotest.tools/internal/format"
)
// Equal compares a directory to the expected structured described by a manifest
// and returns success if they match. If they do not match the failure message
// will contain all the differences between the directory structure and the
// expected structure defined by the Manifest.
//
// Equal is a cmp.Comparison which can be used with assert.Assert().
func Equal(path string, expected Manifest) cmp.Comparison {
return func() cmp.Result {
actual, err := manifestFromDir(path)
if err != nil {
return cmp.ResultFromError(err)
}
failures := eqDirectory(string(os.PathSeparator), expected.root, actual.root)
if len(failures) == 0 {
return cmp.ResultSuccess
}
msg := fmt.Sprintf("directory %s does not match expected:\n", path)
return cmp.ResultFailure(msg + formatFailures(failures))
}
}
type failure struct {
path string
problems []problem
}
type problem string
func notEqual(property string, x, y interface{}) problem {
return problem(fmt.Sprintf("%s: expected %s got %s", property, x, y))
}
func errProblem(reason string, err error) problem {
return problem(fmt.Sprintf("%s: %s", reason, err))
}
func existenceProblem(filename, reason string, args ...interface{}) problem {
return problem(filename + ": " + fmt.Sprintf(reason, args...))
}
func eqResource(x, y resource) []problem {
var p []problem
if x.uid != y.uid {
p = append(p, notEqual("uid", x.uid, y.uid))
}
if x.gid != y.gid {
p = append(p, notEqual("gid", x.gid, y.gid))
}
if x.mode != anyFileMode && x.mode != y.mode {
p = append(p, notEqual("mode", x.mode, y.mode))
}
return p
}
func eqFile(x, y *file) []problem {
p := eqResource(x.resource, y.resource)
switch {
case x.content == nil:
p = append(p, existenceProblem("content", "expected content is nil"))
return p
case x.content == anyFileContent:
return p
case y.content == nil:
p = append(p, existenceProblem("content", "actual content is nil"))
return p
}
xContent, xErr := ioutil.ReadAll(x.content)
defer x.content.Close()
yContent, yErr := ioutil.ReadAll(y.content)
defer y.content.Close()
if xErr != nil {
p = append(p, errProblem("failed to read expected content", xErr))
}
if yErr != nil {
p = append(p, errProblem("failed to read actual content", xErr))
}
if xErr != nil || yErr != nil {
return p
}
if !bytes.Equal(xContent, yContent) {
p = append(p, diffContent(xContent, yContent))
}
return p
}
func diffContent(x, y []byte) problem {
diff := format.UnifiedDiff(format.DiffConfig{
A: string(x),
B: string(y),
From: "expected",
To: "actual",
})
// Remove the trailing newline in the diff. A trailing newline is always
// added to a problem by formatFailures.
diff = strings.TrimSuffix(diff, "\n")
return problem("content:\n" + indent(diff, " "))
}
func indent(s, prefix string) string {
buf := new(bytes.Buffer)
lines := strings.SplitAfter(s, "\n")
for _, line := range lines {
buf.WriteString(prefix + line)
}
return buf.String()
}
func eqSymlink(x, y *symlink) []problem {
p := eqResource(x.resource, y.resource)
if x.target != y.target {
p = append(p, notEqual("target", x.target, y.target))
}
return p
}
func eqDirectory(path string, x, y *directory) []failure {
p := eqResource(x.resource, y.resource)
var f []failure
for _, name := range sortedKeys(x.items) {
if name == anyFile {
continue
}
xEntry := x.items[name]
yEntry, ok := y.items[name]
if !ok {
p = append(p, existenceProblem(name, "expected %s to exist", xEntry.Type()))
continue
}
if xEntry.Type() != yEntry.Type() {
p = append(p, notEqual(name, xEntry.Type(), yEntry.Type()))
continue
}
f = append(f, eqEntry(filepath.Join(path, name), xEntry, yEntry)...)
}
if _, ok := x.items[anyFile]; !ok {
for _, name := range sortedKeys(y.items) {
if _, ok := x.items[name]; !ok {
yEntry := y.items[name]
p = append(p, existenceProblem(name, "unexpected %s", yEntry.Type()))
}
}
}
if len(p) > 0 {
f = append(f, failure{path: path, problems: p})
}
return f
}
func sortedKeys(items map[string]dirEntry) []string {
var keys []string
for key := range items {
keys = append(keys, key)
}
sort.Strings(keys)
return keys
}
// eqEntry assumes x and y to be the same type
func eqEntry(path string, x, y dirEntry) []failure {
resp := func(problems []problem) []failure {
if len(problems) == 0 {
return nil
}
return []failure{{path: path, problems: problems}}
}
switch typed := x.(type) {
case *file:
return resp(eqFile(typed, y.(*file)))
case *symlink:
return resp(eqSymlink(typed, y.(*symlink)))
case *directory:
return eqDirectory(path, typed, y.(*directory))
}
return nil
}
func formatFailures(failures []failure) string {
sort.Slice(failures, func(i, j int) bool {
return failures[i].path < failures[j].path
})
buf := new(bytes.Buffer)
for _, failure := range failures {
buf.WriteString(failure.path + "\n")
for _, problem := range failure.problems {
buf.WriteString(" " + string(problem) + "\n")
}
}
return buf.String()
}

View File

@ -1,6 +1,6 @@
/*Package icmd executes binaries and provides convenient assertions for testing the results.
*/
package icmd
package icmd // import "gotest.tools/icmd"
import (
"bytes"
@ -11,8 +11,8 @@ import (
"sync"
"time"
"github.com/gotestyourself/gotestyourself/assert"
"github.com/gotestyourself/gotestyourself/assert/cmp"
"gotest.tools/assert"
"gotest.tools/assert/cmp"
)
type helperT interface {
@ -228,6 +228,7 @@ func StartCmd(cmd Cmd) *Result {
return result
}
// TODO: support exec.CommandContext
func buildCmd(cmd Cmd) *Result {
var execCmd *exec.Cmd
switch len(cmd.Command) {

View File

@ -1,27 +1,10 @@
// Package difflib is a partial port of Python difflib module.
//
// It provides tools to compare sequences of strings and generate textual diffs.
//
// The following class and functions have been ported:
//
// - SequenceMatcher
//
// - unified_diff
//
// - context_diff
//
// Getting unified diffs was the main goal of the port. Keep in mind this code
// is mostly suitable to output text differences in a human friendly way, there
// are no guarantees generated diffs are consumable by patch(1).
package difflib
/* Package difflib is a partial port of Python difflib module.
import (
"bufio"
"bytes"
"fmt"
"io"
"strings"
)
Original source: https://github.com/pmezard/go-difflib
This file is trimmed to only the parts used by this repository.
*/
package difflib // import "gotest.tools/internal/difflib"
func min(a, b int) int {
if a < b {
@ -37,13 +20,6 @@ func max(a, b int) int {
return b
}
func calculateRatio(matches, length int) float64 {
if length > 0 {
return 2.0 * float64(matches) / float64(length)
}
return 1.0
}
type Match struct {
A int
B int
@ -103,14 +79,6 @@ func NewMatcher(a, b []string) *SequenceMatcher {
return &m
}
func NewMatcherWithJunk(a, b []string, autoJunk bool,
isJunk func(string) bool) *SequenceMatcher {
m := SequenceMatcher{IsJunk: isJunk, autoJunk: autoJunk}
m.SetSeqs(a, b)
return &m
}
// Set two sequences to be compared.
func (m *SequenceMatcher) SetSeqs(a, b []string) {
m.SetSeq1(a)
@ -450,323 +418,3 @@ func (m *SequenceMatcher) GetGroupedOpCodes(n int) [][]OpCode {
}
return groups
}
// Return a measure of the sequences' similarity (float in [0,1]).
//
// Where T is the total number of elements in both sequences, and
// M is the number of matches, this is 2.0*M / T.
// Note that this is 1 if the sequences are identical, and 0 if
// they have nothing in common.
//
// .Ratio() is expensive to compute if you haven't already computed
// .GetMatchingBlocks() or .GetOpCodes(), in which case you may
// want to try .QuickRatio() or .RealQuickRation() first to get an
// upper bound.
func (m *SequenceMatcher) Ratio() float64 {
matches := 0
for _, m := range m.GetMatchingBlocks() {
matches += m.Size
}
return calculateRatio(matches, len(m.a)+len(m.b))
}
// Return an upper bound on ratio() relatively quickly.
//
// This isn't defined beyond that it is an upper bound on .Ratio(), and
// is faster to compute.
func (m *SequenceMatcher) QuickRatio() float64 {
// viewing a and b as multisets, set matches to the cardinality
// of their intersection; this counts the number of matches
// without regard to order, so is clearly an upper bound
if m.fullBCount == nil {
m.fullBCount = map[string]int{}
for _, s := range m.b {
m.fullBCount[s] = m.fullBCount[s] + 1
}
}
// avail[x] is the number of times x appears in 'b' less the
// number of times we've seen it in 'a' so far ... kinda
avail := map[string]int{}
matches := 0
for _, s := range m.a {
n, ok := avail[s]
if !ok {
n = m.fullBCount[s]
}
avail[s] = n - 1
if n > 0 {
matches += 1
}
}
return calculateRatio(matches, len(m.a)+len(m.b))
}
// Return an upper bound on ratio() very quickly.
//
// This isn't defined beyond that it is an upper bound on .Ratio(), and
// is faster to compute than either .Ratio() or .QuickRatio().
func (m *SequenceMatcher) RealQuickRatio() float64 {
la, lb := len(m.a), len(m.b)
return calculateRatio(min(la, lb), la+lb)
}
// Convert range to the "ed" format
func formatRangeUnified(start, stop int) string {
// Per the diff spec at http://www.unix.org/single_unix_specification/
beginning := start + 1 // lines start numbering with one
length := stop - start
if length == 1 {
return fmt.Sprintf("%d", beginning)
}
if length == 0 {
beginning -= 1 // empty ranges begin at line just before the range
}
return fmt.Sprintf("%d,%d", beginning, length)
}
// Unified diff parameters
type UnifiedDiff struct {
A []string // First sequence lines
FromFile string // First file name
FromDate string // First file time
B []string // Second sequence lines
ToFile string // Second file name
ToDate string // Second file time
Eol string // Headers end of line, defaults to LF
Context int // Number of context lines
}
// Compare two sequences of lines; generate the delta as a unified diff.
//
// Unified diffs are a compact way of showing line changes and a few
// lines of context. The number of context lines is set by 'n' which
// defaults to three.
//
// By default, the diff control lines (those with ---, +++, or @@) are
// created with a trailing newline. This is helpful so that inputs
// created from file.readlines() result in diffs that are suitable for
// file.writelines() since both the inputs and outputs have trailing
// newlines.
//
// For inputs that do not have trailing newlines, set the lineterm
// argument to "" so that the output will be uniformly newline free.
//
// The unidiff format normally has a header for filenames and modification
// times. Any or all of these may be specified using strings for
// 'fromfile', 'tofile', 'fromfiledate', and 'tofiledate'.
// The modification times are normally expressed in the ISO 8601 format.
func WriteUnifiedDiff(writer io.Writer, diff UnifiedDiff) error {
buf := bufio.NewWriter(writer)
defer buf.Flush()
wf := func(format string, args ...interface{}) error {
_, err := buf.WriteString(fmt.Sprintf(format, args...))
return err
}
ws := func(s string) error {
_, err := buf.WriteString(s)
return err
}
if len(diff.Eol) == 0 {
diff.Eol = "\n"
}
started := false
m := NewMatcher(diff.A, diff.B)
for _, g := range m.GetGroupedOpCodes(diff.Context) {
if !started {
started = true
fromDate := ""
if len(diff.FromDate) > 0 {
fromDate = "\t" + diff.FromDate
}
toDate := ""
if len(diff.ToDate) > 0 {
toDate = "\t" + diff.ToDate
}
if diff.FromFile != "" || diff.ToFile != "" {
err := wf("--- %s%s%s", diff.FromFile, fromDate, diff.Eol)
if err != nil {
return err
}
err = wf("+++ %s%s%s", diff.ToFile, toDate, diff.Eol)
if err != nil {
return err
}
}
}
first, last := g[0], g[len(g)-1]
range1 := formatRangeUnified(first.I1, last.I2)
range2 := formatRangeUnified(first.J1, last.J2)
if err := wf("@@ -%s +%s @@%s", range1, range2, diff.Eol); err != nil {
return err
}
for _, c := range g {
i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2
if c.Tag == 'e' {
for _, line := range diff.A[i1:i2] {
if err := ws(" " + line); err != nil {
return err
}
}
continue
}
if c.Tag == 'r' || c.Tag == 'd' {
for _, line := range diff.A[i1:i2] {
if err := ws("-" + line); err != nil {
return err
}
}
}
if c.Tag == 'r' || c.Tag == 'i' {
for _, line := range diff.B[j1:j2] {
if err := ws("+" + line); err != nil {
return err
}
}
}
}
}
return nil
}
// Like WriteUnifiedDiff but returns the diff a string.
func GetUnifiedDiffString(diff UnifiedDiff) (string, error) {
w := &bytes.Buffer{}
err := WriteUnifiedDiff(w, diff)
return string(w.Bytes()), err
}
// Convert range to the "ed" format.
func formatRangeContext(start, stop int) string {
// Per the diff spec at http://www.unix.org/single_unix_specification/
beginning := start + 1 // lines start numbering with one
length := stop - start
if length == 0 {
beginning -= 1 // empty ranges begin at line just before the range
}
if length <= 1 {
return fmt.Sprintf("%d", beginning)
}
return fmt.Sprintf("%d,%d", beginning, beginning+length-1)
}
type ContextDiff UnifiedDiff
// Compare two sequences of lines; generate the delta as a context diff.
//
// Context diffs are a compact way of showing line changes and a few
// lines of context. The number of context lines is set by diff.Context
// which defaults to three.
//
// By default, the diff control lines (those with *** or ---) are
// created with a trailing newline.
//
// For inputs that do not have trailing newlines, set the diff.Eol
// argument to "" so that the output will be uniformly newline free.
//
// The context diff format normally has a header for filenames and
// modification times. Any or all of these may be specified using
// strings for diff.FromFile, diff.ToFile, diff.FromDate, diff.ToDate.
// The modification times are normally expressed in the ISO 8601 format.
// If not specified, the strings default to blanks.
func WriteContextDiff(writer io.Writer, diff ContextDiff) error {
buf := bufio.NewWriter(writer)
defer buf.Flush()
var diffErr error
wf := func(format string, args ...interface{}) {
_, err := buf.WriteString(fmt.Sprintf(format, args...))
if diffErr == nil && err != nil {
diffErr = err
}
}
ws := func(s string) {
_, err := buf.WriteString(s)
if diffErr == nil && err != nil {
diffErr = err
}
}
if len(diff.Eol) == 0 {
diff.Eol = "\n"
}
prefix := map[byte]string{
'i': "+ ",
'd': "- ",
'r': "! ",
'e': " ",
}
started := false
m := NewMatcher(diff.A, diff.B)
for _, g := range m.GetGroupedOpCodes(diff.Context) {
if !started {
started = true
fromDate := ""
if len(diff.FromDate) > 0 {
fromDate = "\t" + diff.FromDate
}
toDate := ""
if len(diff.ToDate) > 0 {
toDate = "\t" + diff.ToDate
}
if diff.FromFile != "" || diff.ToFile != "" {
wf("*** %s%s%s", diff.FromFile, fromDate, diff.Eol)
wf("--- %s%s%s", diff.ToFile, toDate, diff.Eol)
}
}
first, last := g[0], g[len(g)-1]
ws("***************" + diff.Eol)
range1 := formatRangeContext(first.I1, last.I2)
wf("*** %s ****%s", range1, diff.Eol)
for _, c := range g {
if c.Tag == 'r' || c.Tag == 'd' {
for _, cc := range g {
if cc.Tag == 'i' {
continue
}
for _, line := range diff.A[cc.I1:cc.I2] {
ws(prefix[cc.Tag] + line)
}
}
break
}
}
range2 := formatRangeContext(first.J1, last.J2)
wf("--- %s ----%s", range2, diff.Eol)
for _, c := range g {
if c.Tag == 'r' || c.Tag == 'i' {
for _, cc := range g {
if cc.Tag == 'd' {
continue
}
for _, line := range diff.B[cc.J1:cc.J2] {
ws(prefix[cc.Tag] + line)
}
}
break
}
}
}
return diffErr
}
// Like WriteContextDiff but returns the diff a string.
func GetContextDiffString(diff ContextDiff) (string, error) {
w := &bytes.Buffer{}
err := WriteContextDiff(w, diff)
return string(w.Bytes()), err
}
// Split a string on "\n" while preserving them. The output can be used
// as input for UnifiedDiff and ContextDiff structures.
func SplitLines(s string) []string {
lines := strings.SplitAfter(s, "\n")
lines[len(lines)-1] += "\n"
return lines
}

View File

@ -0,0 +1,161 @@
package format
import (
"bytes"
"fmt"
"strings"
"unicode"
"gotest.tools/internal/difflib"
)
const (
contextLines = 2
)
// DiffConfig for a unified diff
type DiffConfig struct {
A string
B string
From string
To string
}
// UnifiedDiff is a modified version of difflib.WriteUnifiedDiff with better
// support for showing the whitespace differences.
func UnifiedDiff(conf DiffConfig) string {
a := strings.SplitAfter(conf.A, "\n")
b := strings.SplitAfter(conf.B, "\n")
groups := difflib.NewMatcher(a, b).GetGroupedOpCodes(contextLines)
if len(groups) == 0 {
return ""
}
buf := new(bytes.Buffer)
writeFormat := func(format string, args ...interface{}) {
buf.WriteString(fmt.Sprintf(format, args...))
}
writeLine := func(prefix string, s string) {
buf.WriteString(prefix + s)
}
if hasWhitespaceDiffLines(groups, a, b) {
writeLine = visibleWhitespaceLine(writeLine)
}
formatHeader(writeFormat, conf)
for _, group := range groups {
formatRangeLine(writeFormat, group)
for _, opCode := range group {
in, out := a[opCode.I1:opCode.I2], b[opCode.J1:opCode.J2]
switch opCode.Tag {
case 'e':
formatLines(writeLine, " ", in)
case 'r':
formatLines(writeLine, "-", in)
formatLines(writeLine, "+", out)
case 'd':
formatLines(writeLine, "-", in)
case 'i':
formatLines(writeLine, "+", out)
}
}
}
return buf.String()
}
// hasWhitespaceDiffLines returns true if any diff groups is only different
// because of whitespace characters.
func hasWhitespaceDiffLines(groups [][]difflib.OpCode, a, b []string) bool {
for _, group := range groups {
in, out := new(bytes.Buffer), new(bytes.Buffer)
for _, opCode := range group {
if opCode.Tag == 'e' {
continue
}
for _, line := range a[opCode.I1:opCode.I2] {
in.WriteString(line)
}
for _, line := range b[opCode.J1:opCode.J2] {
out.WriteString(line)
}
}
if removeWhitespace(in.String()) == removeWhitespace(out.String()) {
return true
}
}
return false
}
func removeWhitespace(s string) string {
var result []rune
for _, r := range s {
if !unicode.IsSpace(r) {
result = append(result, r)
}
}
return string(result)
}
func visibleWhitespaceLine(ws func(string, string)) func(string, string) {
mapToVisibleSpace := func(r rune) rune {
switch r {
case '\n':
case ' ':
return '·'
case '\t':
return '▷'
case '\v':
return '▽'
case '\r':
return '↵'
case '\f':
return '↓'
default:
if unicode.IsSpace(r) {
return '<27>'
}
}
return r
}
return func(prefix, s string) {
ws(prefix, strings.Map(mapToVisibleSpace, s))
}
}
func formatHeader(wf func(string, ...interface{}), conf DiffConfig) {
if conf.From != "" || conf.To != "" {
wf("--- %s\n", conf.From)
wf("+++ %s\n", conf.To)
}
}
func formatRangeLine(wf func(string, ...interface{}), group []difflib.OpCode) {
first, last := group[0], group[len(group)-1]
range1 := formatRangeUnified(first.I1, last.I2)
range2 := formatRangeUnified(first.J1, last.J2)
wf("@@ -%s +%s @@\n", range1, range2)
}
// Convert range to the "ed" format
func formatRangeUnified(start, stop int) string {
// Per the diff spec at http://www.unix.org/single_unix_specification/
beginning := start + 1 // lines start numbering with one
length := stop - start
if length == 1 {
return fmt.Sprintf("%d", beginning)
}
if length == 0 {
beginning-- // empty ranges begin at line just before the range
}
return fmt.Sprintf("%d,%d", beginning, length)
}
func formatLines(writeLine func(string, string), prefix string, lines []string) {
for _, line := range lines {
writeLine(prefix, line)
}
// Add a newline if the last line is missing one so that the diff displays
// properly.
if !strings.HasSuffix(lines[len(lines)-1], "\n") {
writeLine("", "\n")
}
}

View File

@ -1,4 +1,4 @@
package format
package format // import "gotest.tools/internal/format"
import "fmt"

View File

@ -1,4 +1,4 @@
package source
package source // import "gotest.tools/internal/source"
import (
"bytes"

View File

@ -1,6 +1,6 @@
/*Package poll provides tools for testing asynchronous code.
*/
package poll
package poll // import "gotest.tools/poll"
import (
"fmt"
@ -25,15 +25,15 @@ type helperT interface {
// Settings are used to configure the behaviour of WaitOn
type Settings struct {
// Timeout is the maximum time to wait for the condition. Defaults to 10s
// Timeout is the maximum time to wait for the condition. Defaults to 10s.
Timeout time.Duration
// Delay is the time to sleep between checking the condition. Detaults to
// 1ms
// Delay is the time to sleep between checking the condition. Defaults to
// 100ms.
Delay time.Duration
}
func defaultConfig() *Settings {
return &Settings{Timeout: 10 * time.Second, Delay: time.Millisecond}
return &Settings{Timeout: 10 * time.Second, Delay: 100 * time.Millisecond}
}
// SettingOp is a function which accepts and modifies Settings

View File

@ -1,6 +1,7 @@
/*Package skip provides functions for skipping based on a condition.
*/
package skip
/*Package skip provides functions for skipping a test and printing the source code
of the condition used to skip the test.
*/
package skip // import "gotest.tools/skip"
import (
"fmt"
@ -9,8 +10,8 @@ import (
"runtime"
"strings"
"github.com/gotestyourself/gotestyourself/internal/format"
"github.com/gotestyourself/gotestyourself/internal/source"
"gotest.tools/internal/format"
"gotest.tools/internal/source"
)
type skipT interface {
@ -25,9 +26,10 @@ type helperT interface {
// BoolOrCheckFunc can be a bool or func() bool, other types will panic
type BoolOrCheckFunc interface{}
// If skips the test if the check function returns true. The skip message will
// contain the name of the check function. Extra message text can be passed as a
// format string with args
// If the condition expression evaluates to true, or the condition function returns
// true, skip the test.
// The skip message will contain the source code of the expression.
// Extra message text can be passed as a format string with args
func If(t skipT, condition BoolOrCheckFunc, msgAndArgs ...interface{}) {
if ht, ok := t.(helperT); ok {
ht.Helper()
@ -49,18 +51,6 @@ func getFunctionName(function func() bool) string {
return strings.SplitN(path.Base(funcPath), ".", 2)[1]
}
// IfCondition skips the test if the condition is true. The skip message will
// contain the source of the expression passed as the condition. Extra message
// text can be passed as a format string with args.
//
// Deprecated: Use If() which now accepts bool arguments
func IfCondition(t skipT, condition bool, msgAndArgs ...interface{}) {
if ht, ok := t.(helperT); ok {
ht.Helper()
}
ifCondition(t, condition, msgAndArgs...)
}
func ifCondition(t skipT, condition bool, msgAndArgs ...interface{}) {
if ht, ok := t.(helperT); ok {
ht.Helper()

View File

@ -0,0 +1,81 @@
/*Package subtest provides a TestContext to subtests which handles cleanup, and
provides a testing.TB, and context.Context.
This package was inspired by github.com/frankban/quicktest.
*/
package subtest // import "gotest.tools/x/subtest"
import (
"context"
"testing"
)
type testcase struct {
testing.TB
ctx context.Context
cleanupFuncs []cleanupFunc
}
type cleanupFunc func()
func (tc *testcase) Ctx() context.Context {
if tc.ctx == nil {
var cancel func()
tc.ctx, cancel = context.WithCancel(context.Background())
tc.AddCleanup(cancel)
}
return tc.ctx
}
// Cleanup runs all cleanup functions. Functions are run in the opposite order
// in which they were added. Cleanup is called automatically before Run exits.
func (tc *testcase) Cleanup() {
for _, f := range tc.cleanupFuncs {
// Defer all cleanup functions so they all run even if one calls
// t.FailNow() or panics. Deferring them also runs them in reverse order.
defer f()
}
tc.cleanupFuncs = nil
}
func (tc *testcase) AddCleanup(f func()) {
tc.cleanupFuncs = append(tc.cleanupFuncs, f)
}
func (tc *testcase) Parallel() {
tp, ok := tc.TB.(parallel)
if !ok {
panic("Parallel called with a testing.B")
}
tp.Parallel()
}
type parallel interface {
Parallel()
}
// Run a subtest. When subtest exits, every cleanup function added with
// TestContext.AddCleanup will be run.
func Run(t *testing.T, name string, subtest func(t TestContext)) bool {
return t.Run(name, func(t *testing.T) {
tc := &testcase{TB: t}
defer tc.Cleanup()
subtest(tc)
})
}
// TestContext provides a testing.TB and a context.Context for a test case.
type TestContext interface {
testing.TB
// AddCleanup function which will be run when before Run returns.
AddCleanup(f func())
// Ctx returns a context for the test case. Multiple calls from the same subtest
// will return the same context. The context is cancelled when Run
// returns.
Ctx() context.Context
// Parallel calls t.Parallel on the testing.TB. Panics if testing.TB does
// not implement Parallel.
Parallel()
}
var _ TestContext = &testcase{}