Merge pull request #160 from mlaventure/integration-test
Integration test
This commit is contained in:
commit
53cfb9dfbe
35 changed files with 4068 additions and 10 deletions
29
Dockerfile
29
Dockerfile
|
@ -6,6 +6,9 @@ RUN apt-get update && apt-get install -y \
|
||||||
curl \
|
curl \
|
||||||
git \
|
git \
|
||||||
make \
|
make \
|
||||||
|
jq \
|
||||||
|
apparmor \
|
||||||
|
libapparmor-dev \
|
||||||
--no-install-recommends \
|
--no-install-recommends \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
@ -15,10 +18,36 @@ RUN curl -sSL "https://storage.googleapis.com/golang/go${GO_VERSION}.linux-amd6
|
||||||
ENV PATH /go/bin:/usr/local/go/bin:$PATH
|
ENV PATH /go/bin:/usr/local/go/bin:$PATH
|
||||||
ENV GOPATH /go:/go/src/github.com/docker/containerd/vendor
|
ENV GOPATH /go:/go/src/github.com/docker/containerd/vendor
|
||||||
|
|
||||||
|
WORKDIR /go/src/github.com/docker/containerd
|
||||||
|
|
||||||
# install golint/vet
|
# install golint/vet
|
||||||
RUN go get github.com/golang/lint/golint \
|
RUN go get github.com/golang/lint/golint \
|
||||||
&& go get golang.org/x/tools/cmd/vet
|
&& go get golang.org/x/tools/cmd/vet
|
||||||
|
|
||||||
|
# install seccomp: the version shipped in trusty is too old
|
||||||
|
ENV SECCOMP_VERSION 2.3.0
|
||||||
|
RUN set -x \
|
||||||
|
&& export SECCOMP_PATH="$(mktemp -d)" \
|
||||||
|
&& curl -fsSL "https://github.com/seccomp/libseccomp/releases/download/v${SECCOMP_VERSION}/libseccomp-${SECCOMP_VERSION}.tar.gz" \
|
||||||
|
| tar -xzC "$SECCOMP_PATH" --strip-components=1 \
|
||||||
|
&& ( \
|
||||||
|
cd "$SECCOMP_PATH" \
|
||||||
|
&& ./configure --prefix=/usr/local \
|
||||||
|
&& make \
|
||||||
|
&& make install \
|
||||||
|
&& ldconfig \
|
||||||
|
) \
|
||||||
|
&& rm -rf "$SECCOMP_PATH"
|
||||||
|
|
||||||
|
# Install runc
|
||||||
|
ENV RUNC_COMMIT bbde9c426ff363d813b8722f0744115c13b408b6
|
||||||
|
RUN set -x \
|
||||||
|
&& export GOPATH="$(mktemp -d)" \
|
||||||
|
&& git clone git://github.com/opencontainers/runc.git "$GOPATH/src/github.com/opencontainers/runc" \
|
||||||
|
&& cd "$GOPATH/src/github.com/opencontainers/runc" \
|
||||||
|
&& git checkout -q "$RUNC_COMMIT" \
|
||||||
|
&& make BUILDTAGS="seccomp apparmor selinux" && make install
|
||||||
|
|
||||||
COPY . /go/src/github.com/docker/containerd
|
COPY . /go/src/github.com/docker/containerd
|
||||||
|
|
||||||
WORKDIR /go/src/github.com/docker/containerd
|
WORKDIR /go/src/github.com/docker/containerd
|
||||||
|
|
23
Makefile
23
Makefile
|
@ -1,5 +1,7 @@
|
||||||
BUILDTAGS=
|
BUILDTAGS=
|
||||||
|
|
||||||
|
PROJECT=github.com/docker/containerd
|
||||||
|
|
||||||
GIT_COMMIT := $(shell git rev-parse HEAD 2> /dev/null || true)
|
GIT_COMMIT := $(shell git rev-parse HEAD 2> /dev/null || true)
|
||||||
GIT_BRANCH := $(shell git rev-parse --abbrev-ref HEAD 2> /dev/null)
|
GIT_BRANCH := $(shell git rev-parse --abbrev-ref HEAD 2> /dev/null)
|
||||||
|
|
||||||
|
@ -13,8 +15,12 @@ ifeq ($(INTERACTIVE), 1)
|
||||||
DOCKER_FLAGS += -t
|
DOCKER_FLAGS += -t
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
TEST_ARTIFACTS_DIR := integration-test/test-artifacts
|
||||||
|
BUNDLE_ARCHIVES_DIR := $(TEST_ARTIFACTS_DIR)/archives
|
||||||
|
|
||||||
DOCKER_IMAGE := containerd-dev$(if $(GIT_BRANCH),:$(GIT_BRANCH))
|
DOCKER_IMAGE := containerd-dev$(if $(GIT_BRANCH),:$(GIT_BRANCH))
|
||||||
DOCKER_RUN := docker run --rm -i $(DOCKER_FLAGS) "$(DOCKER_IMAGE)"
|
DOCKER_RUN := docker run --privileged --rm -i $(DOCKER_FLAGS) "$(DOCKER_IMAGE)"
|
||||||
|
|
||||||
|
|
||||||
export GOPATH:=$(CURDIR)/vendor:$(GOPATH)
|
export GOPATH:=$(CURDIR)/vendor:$(GOPATH)
|
||||||
|
|
||||||
|
@ -46,7 +52,13 @@ shim: bin
|
||||||
shim-static:
|
shim-static:
|
||||||
cd containerd-shim && go build -ldflags "-w -extldflags -static ${LDFLAGS}" -tags "$(BUILDTAGS)" -o ../bin/containerd-shim
|
cd containerd-shim && go build -ldflags "-w -extldflags -static ${LDFLAGS}" -tags "$(BUILDTAGS)" -o ../bin/containerd-shim
|
||||||
|
|
||||||
dbuild:
|
$(BUNDLE_ARCHIVES_DIR)/busybox.tar:
|
||||||
|
@mkdir -p $(BUNDLE_ARCHIVES_DIR)
|
||||||
|
curl -sSL 'https://github.com/jpetazzo/docker-busybox/raw/buildroot-2014.11/rootfs.tar' -o $(BUNDLE_ARCHIVES_DIR)/busybox.tar
|
||||||
|
|
||||||
|
bundles-rootfs: $(BUNDLE_ARCHIVES_DIR)/busybox.tar
|
||||||
|
|
||||||
|
dbuild: $(BUNDLE_ARCHIVES_DIR)/busybox.tar
|
||||||
@docker build --rm --force-rm -t "$(DOCKER_IMAGE)" .
|
@docker build --rm --force-rm -t "$(DOCKER_IMAGE)" .
|
||||||
|
|
||||||
dtest: dbuild
|
dtest: dbuild
|
||||||
|
@ -68,7 +80,12 @@ shell: dbuild
|
||||||
$(DOCKER_RUN) bash
|
$(DOCKER_RUN) bash
|
||||||
|
|
||||||
test: all validate
|
test: all validate
|
||||||
go test -v $(shell go list ./... | grep -v /vendor)
|
go test -v $(shell go list ./... | grep -v /vendor | grep -v /integration-test)
|
||||||
|
ifneq ($(wildcard /.dockerenv), )
|
||||||
|
$(MAKE) install bundles-rootfs
|
||||||
|
cd integration-test ; \
|
||||||
|
go test -check.v $(TESTFLAGS) github.com/docker/containerd/integration-test
|
||||||
|
endif
|
||||||
|
|
||||||
validate: fmt
|
validate: fmt
|
||||||
|
|
||||||
|
|
|
@ -124,7 +124,7 @@ func (s *apiServer) State(ctx context.Context, r *types.StateRequest) (*types.St
|
||||||
func createAPIContainer(c runtime.Container, getPids bool) (*types.Container, error) {
|
func createAPIContainer(c runtime.Container, getPids bool) (*types.Container, error) {
|
||||||
processes, err := c.Processes()
|
processes, err := c.Processes()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, grpc.Errorf(codes.Internal, "get processes for container")
|
return nil, grpc.Errorf(codes.Internal, "get processes for container: "+err.Error())
|
||||||
}
|
}
|
||||||
var procs []*types.Process
|
var procs []*types.Process
|
||||||
for _, p := range processes {
|
for _, p := range processes {
|
||||||
|
@ -148,7 +148,7 @@ func createAPIContainer(c runtime.Container, getPids bool) (*types.Container, er
|
||||||
state := c.State()
|
state := c.State()
|
||||||
if getPids && (state == runtime.Running || state == runtime.Paused) {
|
if getPids && (state == runtime.Running || state == runtime.Paused) {
|
||||||
if pids, err = c.Pids(); err != nil {
|
if pids, err = c.Pids(); err != nil {
|
||||||
return nil, grpc.Errorf(codes.Internal, "get all pids for container")
|
return nil, grpc.Errorf(codes.Internal, "get all pids for container: "+err.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return &types.Container{
|
return &types.Container{
|
||||||
|
|
|
@ -34,8 +34,10 @@ func setAppBefore(app *cli.App) {
|
||||||
app.Before = func(context *cli.Context) error {
|
app.Before = func(context *cli.Context) error {
|
||||||
if context.GlobalBool("debug") {
|
if context.GlobalBool("debug") {
|
||||||
logrus.SetLevel(logrus.DebugLevel)
|
logrus.SetLevel(logrus.DebugLevel)
|
||||||
if err := debugMetrics(context.GlobalDuration("metrics-interval"), context.GlobalString("graphite-address")); err != nil {
|
if context.GlobalDuration("metrics-interval") > 0 {
|
||||||
return err
|
if err := debugMetrics(context.GlobalDuration("metrics-interval"), context.GlobalString("graphite-address")); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err := checkLimits(); err != nil {
|
if err := checkLimits(); err != nil {
|
||||||
|
|
|
@ -52,6 +52,7 @@ clean() {
|
||||||
local packages=(
|
local packages=(
|
||||||
"${PROJECT}/containerd" # package main
|
"${PROJECT}/containerd" # package main
|
||||||
"${PROJECT}/ctr" # package main
|
"${PROJECT}/ctr" # package main
|
||||||
|
"${PROJECT}/integration-test" # package main
|
||||||
)
|
)
|
||||||
local platforms=( linux/amd64 linux/386 windows/amd64 windows/386 darwin/amd64 )
|
local platforms=( linux/amd64 linux/386 windows/amd64 windows/386 darwin/amd64 )
|
||||||
local buildTagCombos=(
|
local buildTagCombos=(
|
||||||
|
@ -77,8 +78,8 @@ clean() {
|
||||||
|
|
||||||
echo -n 'pruning unused packages, '
|
echo -n 'pruning unused packages, '
|
||||||
findArgs=(
|
findArgs=(
|
||||||
# This directory contains only .c and .h files which are necessary
|
# for some reason go list doesn't detect this as a dependency
|
||||||
-path vendor/src/github.com/mattn/go-sqlite3/code
|
-path vendor/src/github.com/vdemeester/shakers
|
||||||
)
|
)
|
||||||
for import in "${imports[@]}"; do
|
for import in "${imports[@]}"; do
|
||||||
[ "${#findArgs[@]}" -eq 0 ] || findArgs+=( -or )
|
[ "${#findArgs[@]}" -eq 0 ] || findArgs+=( -or )
|
||||||
|
|
|
@ -25,4 +25,7 @@ clone git golang.org/x/net 991d3e32f76f19ee6d9caadb3a22eae8d23315f7 https://gith
|
||||||
clone git google.golang.org/grpc a22b6611561e9f0a3e0919690dd2caf48f14c517 https://github.com/grpc/grpc-go.git
|
clone git google.golang.org/grpc a22b6611561e9f0a3e0919690dd2caf48f14c517 https://github.com/grpc/grpc-go.git
|
||||||
clone git github.com/seccomp/libseccomp-golang 1b506fc7c24eec5a3693cdcbed40d9c226cfc6a1
|
clone git github.com/seccomp/libseccomp-golang 1b506fc7c24eec5a3693cdcbed40d9c226cfc6a1
|
||||||
|
|
||||||
|
clone git github.com/vdemeester/shakers 24d7f1d6a71aa5d9cbe7390e4afb66b7eef9e1b3
|
||||||
|
clone git github.com/go-check/check a625211d932a2a643d0d17352095f03fb7774663 https://github.com/cpuguy83/check.git
|
||||||
|
|
||||||
clean
|
clean
|
||||||
|
|
107
integration-test/bundle_utils_test.go
Normal file
107
integration-test/bundle_utils_test.go
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"reflect"
|
||||||
|
|
||||||
|
ocs "github.com/opencontainers/specs/specs-go"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
bundlesDir = filepath.Join("test-artifacts", "oci-bundles")
|
||||||
|
refOciSpecsPath = filepath.Join(bundlesDir, "config.json")
|
||||||
|
)
|
||||||
|
|
||||||
|
type OciProcessArgs struct {
|
||||||
|
Cmd string
|
||||||
|
Args []string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Bundle struct {
|
||||||
|
Source string
|
||||||
|
Name string
|
||||||
|
Spec ocs.Spec
|
||||||
|
Path string
|
||||||
|
}
|
||||||
|
|
||||||
|
var bundleMap map[string]Bundle
|
||||||
|
|
||||||
|
// untarRootfs untars the given `source` tarPath into `destination/rootfs`
|
||||||
|
func untarRootfs(source string, destination string) error {
|
||||||
|
destination = filepath.Join(destination, "rootfs")
|
||||||
|
if err := os.MkdirAll(destination, 0755); err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
tar := exec.Command("tar", "-C", destination, "-xf", source)
|
||||||
|
return tar.Run()
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateBundleWithFilter generate a new oci-bundle named `name` from
|
||||||
|
// the provide `source` rootfs. It starts from the default spec
|
||||||
|
// generated by `runc spec`, overrides the `spec.Process.Args` value
|
||||||
|
// with `args` and set `spec.Process.Terminal` to false. It then apply
|
||||||
|
// `filter()` to the resulting spec if it is provided.
|
||||||
|
func CreateBundleWithFilter(source, name string, args []string, filter func(spec *ocs.Spec)) error {
|
||||||
|
// Generate the spec
|
||||||
|
var spec ocs.Spec
|
||||||
|
if f, err := os.Open(refOciSpecsPath); err != nil {
|
||||||
|
return fmt.Errorf("Failed to open default spec: %v", err)
|
||||||
|
} else {
|
||||||
|
if err := json.NewDecoder(f).Decode(&spec); err != nil {
|
||||||
|
return fmt.Errorf("Failed to load default spec: %v", err)
|
||||||
|
}
|
||||||
|
f.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
spec.Process.Args = args
|
||||||
|
spec.Process.Terminal = false
|
||||||
|
if filter != nil {
|
||||||
|
filter(&spec)
|
||||||
|
}
|
||||||
|
|
||||||
|
bundlePath := filepath.Join(bundlesDir, name)
|
||||||
|
nb := Bundle{source, name, spec, bundlePath}
|
||||||
|
|
||||||
|
// Check that we don't already have such a bundle
|
||||||
|
if b, ok := bundleMap[name]; ok {
|
||||||
|
if reflect.DeepEqual(b, nb) == false {
|
||||||
|
return fmt.Errorf("A bundle name named '%s' already exist but with different properties! %#v != %#v",
|
||||||
|
name, b, nb)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nothing should be there, but just in case
|
||||||
|
os.RemoveAll(bundlePath)
|
||||||
|
|
||||||
|
if err := untarRootfs(filepath.Join(archivesDir, source+".tar"), bundlePath); err != nil {
|
||||||
|
return fmt.Errorf("Failed to untar %s.tar: %v", source, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// create a place for the io fifo
|
||||||
|
if err := os.Mkdir(filepath.Join(bundlePath, "io"), 0755); err != nil {
|
||||||
|
return fmt.Errorf("Failed to create bundle io directory: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write the updated spec to the right location
|
||||||
|
config, e := os.Create(filepath.Join(bundlePath, "config.json"))
|
||||||
|
if e != nil {
|
||||||
|
return fmt.Errorf("Failed to create oci spec: %v", e)
|
||||||
|
}
|
||||||
|
defer config.Close()
|
||||||
|
|
||||||
|
if err := json.NewEncoder(config).Encode(&spec); err != nil {
|
||||||
|
return fmt.Errorf("Failed to encore oci spec: %v", e)
|
||||||
|
}
|
||||||
|
|
||||||
|
bundleMap[name] = nb
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateBusyboxBundle(name string, args []string) error {
|
||||||
|
return CreateBundleWithFilter("busybox", name, args, nil)
|
||||||
|
}
|
228
integration-test/check_test.go
Normal file
228
integration-test/check_test.go
Normal file
|
@ -0,0 +1,228 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"sync"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
"google.golang.org/grpc/grpclog"
|
||||||
|
|
||||||
|
"github.com/docker/containerd/api/grpc/types"
|
||||||
|
"github.com/go-check/check"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
outputDirFormat = filepath.Join("test-artifacts", "runs", "%s")
|
||||||
|
archivesDir = filepath.Join("test-artifacts", "archives")
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test(t *testing.T) {
|
||||||
|
check.TestingT(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
check.Suite(&ContainerdSuite{})
|
||||||
|
}
|
||||||
|
|
||||||
|
type ContainerdSuite struct {
|
||||||
|
cwd string
|
||||||
|
outputDir string
|
||||||
|
logFile *os.File
|
||||||
|
cd *exec.Cmd
|
||||||
|
syncChild chan error
|
||||||
|
grpcClient types.APIClient
|
||||||
|
eventFiltersMutex sync.Mutex
|
||||||
|
eventFilters map[string]func(event *types.Event)
|
||||||
|
}
|
||||||
|
|
||||||
|
// getClient returns a connection to the Suite containerd
|
||||||
|
func (cs *ContainerdSuite) getClient(socket string) error {
|
||||||
|
// reset the logger for grpc to log to dev/null so that it does not mess with our stdio
|
||||||
|
grpclog.SetLogger(log.New(ioutil.Discard, "", log.LstdFlags))
|
||||||
|
dialOpts := []grpc.DialOption{grpc.WithInsecure()}
|
||||||
|
dialOpts = append(dialOpts,
|
||||||
|
grpc.WithDialer(func(addr string, timeout time.Duration) (net.Conn, error) {
|
||||||
|
return net.DialTimeout("unix", addr, timeout)
|
||||||
|
},
|
||||||
|
))
|
||||||
|
conn, err := grpc.Dial(socket, dialOpts...)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
cs.grpcClient = types.NewAPIClient(conn)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContainerdEventsHandler will process all events coming from
|
||||||
|
// containerd. If a filter as been register for a given container id
|
||||||
|
// via `SetContainerEventFilter()`, it will be invoked every time an
|
||||||
|
// event for that id is received
|
||||||
|
func (cs *ContainerdSuite) ContainerdEventsHandler(events types.API_EventsClient) {
|
||||||
|
timestamp := uint64(time.Now().Unix())
|
||||||
|
for {
|
||||||
|
e, err := events.Recv()
|
||||||
|
if err != nil {
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
events, _ = cs.grpcClient.Events(context.Background(), &types.EventsRequest{Timestamp: timestamp})
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
timestamp = e.Timestamp
|
||||||
|
cs.eventFiltersMutex.Lock()
|
||||||
|
if f, ok := cs.eventFilters[e.Id]; ok {
|
||||||
|
f(e)
|
||||||
|
if e.Type == "exit" && e.Pid == "init" {
|
||||||
|
delete(cs.eventFilters, e.Id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cs.eventFiltersMutex.Unlock()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// generateReferencesSpecs invoke `runc spec` to produce the baseline
|
||||||
|
// specs from which all future bundle will be generated
|
||||||
|
func generateReferenceSpecs(destination string) error {
|
||||||
|
specs := exec.Command("runc", "spec")
|
||||||
|
specs.Dir = destination
|
||||||
|
return specs.Run()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cs *ContainerdSuite) SetUpSuite(c *check.C) {
|
||||||
|
bundleMap = make(map[string]Bundle)
|
||||||
|
cs.eventFilters = make(map[string]func(event *types.Event))
|
||||||
|
|
||||||
|
// Get our CWD
|
||||||
|
if cwd, err := os.Getwd(); err != nil {
|
||||||
|
c.Fatalf("Could not determine current working directory: %v", err)
|
||||||
|
} else {
|
||||||
|
cs.cwd = cwd
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clean old bundles
|
||||||
|
os.RemoveAll(bundlesDir)
|
||||||
|
|
||||||
|
// Ensure the oci bundles directory exists
|
||||||
|
if err := os.MkdirAll(bundlesDir, 0755); err != nil {
|
||||||
|
c.Fatalf("Failed to create bundles directory: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate the reference spec
|
||||||
|
if err := generateReferenceSpecs(bundlesDir); err != nil {
|
||||||
|
c.Fatalf("Unable to generate OCI reference spec: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create our output directory
|
||||||
|
od := fmt.Sprintf(outputDirFormat, time.Now().Format("2006-01-02_150405.000000"))
|
||||||
|
cdStateDir := fmt.Sprintf("%s/containerd-master", od)
|
||||||
|
if err := os.MkdirAll(cdStateDir, 0755); err != nil {
|
||||||
|
c.Fatalf("Unable to created output directory '%s': %v", cdStateDir, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cdGRPCSock := filepath.Join(od, "containerd-master", "containerd.sock")
|
||||||
|
cdLogFile := filepath.Join(od, "containerd-master", "containerd.log")
|
||||||
|
|
||||||
|
f, err := os.OpenFile(cdLogFile, os.O_CREATE|os.O_TRUNC|os.O_RDWR|os.O_SYNC, 0777)
|
||||||
|
if err != nil {
|
||||||
|
c.Fatalf("Failed to create master containerd log file: %v", err)
|
||||||
|
}
|
||||||
|
cs.logFile = f
|
||||||
|
|
||||||
|
cd := exec.Command("containerd", "--debug",
|
||||||
|
"--state-dir", cdStateDir,
|
||||||
|
"--listen", cdGRPCSock,
|
||||||
|
"--metrics-interval", "0m0s",
|
||||||
|
"--runtime-args", fmt.Sprintf("--root=%s", filepath.Join(cs.cwd, cdStateDir, "runc")),
|
||||||
|
)
|
||||||
|
cd.Stderr = f
|
||||||
|
cd.Stdout = f
|
||||||
|
|
||||||
|
if err := cd.Start(); err != nil {
|
||||||
|
c.Fatalf("Unable to start the master containerd: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cs.outputDir = od
|
||||||
|
cs.cd = cd
|
||||||
|
cs.syncChild = make(chan error)
|
||||||
|
if err := cs.getClient(cdGRPCSock); err != nil {
|
||||||
|
// Kill the daemon
|
||||||
|
cs.cd.Process.Kill()
|
||||||
|
c.Fatalf("Failed to connect to daemon: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Monitor events
|
||||||
|
events, err := cs.grpcClient.Events(context.Background(), &types.EventsRequest{})
|
||||||
|
if err != nil {
|
||||||
|
c.Fatalf("Could not register containerd event handler: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
go cs.ContainerdEventsHandler(events)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
cs.syncChild <- cd.Wait()
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cs *ContainerdSuite) TearDownSuite(c *check.C) {
|
||||||
|
|
||||||
|
// tell containerd to stop
|
||||||
|
if cs.cd != nil {
|
||||||
|
cs.cd.Process.Signal(os.Interrupt)
|
||||||
|
|
||||||
|
done := false
|
||||||
|
for done == false {
|
||||||
|
select {
|
||||||
|
case err := <-cs.syncChild:
|
||||||
|
if err != nil {
|
||||||
|
c.Errorf("master containerd did not exit cleanly: %v", err)
|
||||||
|
}
|
||||||
|
done = true
|
||||||
|
case <-time.After(3 * time.Second):
|
||||||
|
fmt.Println("Timeout while waiting for containerd to exit, killing it!")
|
||||||
|
cs.cd.Process.Kill()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if cs.logFile != nil {
|
||||||
|
cs.logFile.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cs *ContainerdSuite) SetContainerEventFilter(id string, filter func(event *types.Event)) {
|
||||||
|
cs.eventFiltersMutex.Lock()
|
||||||
|
cs.eventFilters[id] = filter
|
||||||
|
cs.eventFiltersMutex.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cs *ContainerdSuite) TearDownTest(c *check.C) {
|
||||||
|
ctrs, err := cs.ListRunningContainers()
|
||||||
|
if err != nil {
|
||||||
|
c.Fatalf("Unable to retrieve running containers: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Kill all containers that survived
|
||||||
|
for _, ctr := range ctrs {
|
||||||
|
ch := make(chan interface{})
|
||||||
|
cs.SetContainerEventFilter(ctr.Id, func(e *types.Event) {
|
||||||
|
if e.Type == "exit" && e.Pid == "init" {
|
||||||
|
ch <- nil
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if err := cs.KillContainer(ctr.Id); err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "Failed to cleanup leftover test containers: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
<-ch
|
||||||
|
}
|
||||||
|
}
|
249
integration-test/container_utils_test.go
Normal file
249
integration-test/container_utils_test.go
Normal file
|
@ -0,0 +1,249 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/docker/containerd/api/grpc/types"
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (cs *ContainerdSuite) ListRunningContainers() ([]*types.Container, error) {
|
||||||
|
resp, err := cs.grpcClient.State(context.Background(), &types.StateRequest{})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return resp.Containers, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cs *ContainerdSuite) SignalContainerProcess(id string, procId string, sig uint32) error {
|
||||||
|
_, err := cs.grpcClient.Signal(context.Background(), &types.SignalRequest{
|
||||||
|
Id: id,
|
||||||
|
Pid: procId,
|
||||||
|
Signal: sig,
|
||||||
|
})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cs *ContainerdSuite) SignalContainer(id string, sig uint32) error {
|
||||||
|
return cs.SignalContainerProcess(id, "init", sig)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cs *ContainerdSuite) KillContainer(id string) error {
|
||||||
|
return cs.SignalContainerProcess(id, "init", uint32(syscall.SIGKILL))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cs *ContainerdSuite) PauseContainer(id string) error {
|
||||||
|
_, err := cs.grpcClient.UpdateContainer(context.Background(), &types.UpdateContainerRequest{
|
||||||
|
Id: id,
|
||||||
|
Pid: "init",
|
||||||
|
Status: "paused",
|
||||||
|
})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cs *ContainerdSuite) ResumeContainer(id string) error {
|
||||||
|
_, err := cs.grpcClient.UpdateContainer(context.Background(), &types.UpdateContainerRequest{
|
||||||
|
Id: id,
|
||||||
|
Pid: "init",
|
||||||
|
Status: "running",
|
||||||
|
})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cs *ContainerdSuite) GetContainerStats(id string) (*types.StatsResponse, error) {
|
||||||
|
stats, err := cs.grpcClient.Stats(context.Background(), &types.StatsRequest{
|
||||||
|
Id: id,
|
||||||
|
})
|
||||||
|
return stats, err
|
||||||
|
}
|
||||||
|
|
||||||
|
type stdio struct {
|
||||||
|
stdin string
|
||||||
|
stdout string
|
||||||
|
stderr string
|
||||||
|
stdinf *os.File
|
||||||
|
stdoutf *os.File
|
||||||
|
stderrf *os.File
|
||||||
|
stdoutBuffer bytes.Buffer
|
||||||
|
stderrBuffer bytes.Buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
type containerProcess struct {
|
||||||
|
containerId string
|
||||||
|
pid string
|
||||||
|
bundle *Bundle
|
||||||
|
io stdio
|
||||||
|
eventsCh chan *types.Event
|
||||||
|
cs *ContainerdSuite
|
||||||
|
hasExited bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *containerProcess) openIo() (err error) {
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
c.Cleanup()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
c.io.stdinf, err = os.OpenFile(c.io.stdin, os.O_RDWR, 0)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
c.io.stdoutf, err = os.OpenFile(c.io.stdout, os.O_RDWR, 0)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
go io.Copy(&c.io.stdoutBuffer, c.io.stdoutf)
|
||||||
|
|
||||||
|
c.io.stderrf, err = os.OpenFile(c.io.stderr, os.O_RDWR, 0)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
go io.Copy(&c.io.stderrBuffer, c.io.stderrf)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *containerProcess) GetNextEvent() *types.Event {
|
||||||
|
e := <-c.eventsCh
|
||||||
|
|
||||||
|
if e.Type == "exit" && e.Pid == c.pid {
|
||||||
|
c.Cleanup()
|
||||||
|
c.hasExited = true
|
||||||
|
}
|
||||||
|
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *containerProcess) CloseStdin() error {
|
||||||
|
_, err := c.cs.grpcClient.UpdateProcess(context.Background(), &types.UpdateProcessRequest{
|
||||||
|
Id: c.containerId,
|
||||||
|
Pid: c.pid,
|
||||||
|
CloseStdin: true,
|
||||||
|
})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *containerProcess) Cleanup() {
|
||||||
|
for _, f := range []*os.File{
|
||||||
|
c.io.stdinf,
|
||||||
|
c.io.stdoutf,
|
||||||
|
c.io.stderrf,
|
||||||
|
} {
|
||||||
|
if f != nil {
|
||||||
|
f.Close()
|
||||||
|
f = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewContainerProcess(cs *ContainerdSuite, bundle *Bundle, cid, pid string) (c *containerProcess, err error) {
|
||||||
|
c = &containerProcess{
|
||||||
|
containerId: cid,
|
||||||
|
pid: "init",
|
||||||
|
bundle: bundle,
|
||||||
|
eventsCh: make(chan *types.Event, 8),
|
||||||
|
cs: cs,
|
||||||
|
hasExited: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, path := range map[string]*string{
|
||||||
|
"stdin": &c.io.stdin,
|
||||||
|
"stdout": &c.io.stdout,
|
||||||
|
"stderr": &c.io.stderr,
|
||||||
|
} {
|
||||||
|
*path = filepath.Join(bundle.Path, "io", cid+"-"+pid+"-"+name)
|
||||||
|
if err = syscall.Mkfifo(*path, 0755); err != nil && !os.IsExist(err) {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = c.openIo(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cs *ContainerdSuite) StartContainer(id, bundleName string) (c *containerProcess, err error) {
|
||||||
|
bundle, ok := bundleMap[bundleName]
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("No such bundle '%s'", bundleName)
|
||||||
|
}
|
||||||
|
|
||||||
|
c, err = NewContainerProcess(cs, &bundle, id, "init")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
r := &types.CreateContainerRequest{
|
||||||
|
Id: id,
|
||||||
|
BundlePath: filepath.Join(cs.cwd, bundle.Path),
|
||||||
|
Stdin: filepath.Join(cs.cwd, c.io.stdin),
|
||||||
|
Stdout: filepath.Join(cs.cwd, c.io.stdout),
|
||||||
|
Stderr: filepath.Join(cs.cwd, c.io.stderr),
|
||||||
|
}
|
||||||
|
|
||||||
|
cs.SetContainerEventFilter(id, func(event *types.Event) {
|
||||||
|
c.eventsCh <- event
|
||||||
|
})
|
||||||
|
|
||||||
|
if _, err := cs.grpcClient.CreateContainer(context.Background(), r); err != nil {
|
||||||
|
c.Cleanup()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cs *ContainerdSuite) RunContainer(id, bundleName string) (c *containerProcess, err error) {
|
||||||
|
c, err = cs.StartContainer(id, bundleName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
e := c.GetNextEvent()
|
||||||
|
if e.Type == "exit" && e.Pid == "init" {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return c, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cs *ContainerdSuite) AddProcessToContainer(init *containerProcess, pid, cwd string, env, args []string, uid, gid uint32) (c *containerProcess, err error) {
|
||||||
|
c, err = NewContainerProcess(cs, init.bundle, init.containerId, pid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
pr := &types.AddProcessRequest{
|
||||||
|
Id: init.containerId,
|
||||||
|
Pid: pid,
|
||||||
|
Args: args,
|
||||||
|
Cwd: cwd,
|
||||||
|
Env: env,
|
||||||
|
User: &types.User{
|
||||||
|
Uid: uid,
|
||||||
|
Gid: gid,
|
||||||
|
},
|
||||||
|
Stdin: filepath.Join(cs.cwd, c.io.stdin),
|
||||||
|
Stdout: filepath.Join(cs.cwd, c.io.stdout),
|
||||||
|
Stderr: filepath.Join(cs.cwd, c.io.stderr),
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = cs.grpcClient.AddProcess(context.Background(), pr)
|
||||||
|
if err != nil {
|
||||||
|
c.Cleanup()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return c, nil
|
||||||
|
}
|
58
integration-test/start_test.go
Normal file
58
integration-test/start_test.go
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/docker/docker/pkg/integration/checker"
|
||||||
|
"github.com/go-check/check"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (cs *ContainerdSuite) TestStartBusyboxLsSlash(t *check.C) {
|
||||||
|
expectedOutput := `bin
|
||||||
|
dev
|
||||||
|
etc
|
||||||
|
home
|
||||||
|
lib
|
||||||
|
lib64
|
||||||
|
linuxrc
|
||||||
|
media
|
||||||
|
mnt
|
||||||
|
opt
|
||||||
|
proc
|
||||||
|
root
|
||||||
|
run
|
||||||
|
sbin
|
||||||
|
sys
|
||||||
|
tmp
|
||||||
|
usr
|
||||||
|
var
|
||||||
|
`
|
||||||
|
if err := CreateBusyboxBundle("busybox-ls-slash", []string{"ls", "/"}); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
c, err := cs.RunContainer("myls", "busybox-ls-slash")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Assert(c.io.stdoutBuffer.String(), checker.Equals, expectedOutput)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cs *ContainerdSuite) TestStartBusyboxNoSuchFile(t *check.C) {
|
||||||
|
expectedOutput := `oci runtime error: exec: \"NoSuchFile\": executable file not found in $PATH`
|
||||||
|
|
||||||
|
if err := CreateBusyboxBundle("busybox-NoSuchFile", []string{"NoSuchFile"}); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := cs.RunContainer("NoSuchFile", "busybox-NoSuchFile")
|
||||||
|
t.Assert(err.Error(), checker.Contains, expectedOutput)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cs *ContainerdSuite) TestStartBusyboxTop(t *check.C) {
|
||||||
|
if err := CreateBusyboxBundle("busybox-top", []string{"top"}); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := cs.StartContainer("top", "busybox-top")
|
||||||
|
t.Assert(err, checker.Equals, nil)
|
||||||
|
}
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -218,7 +219,17 @@ func (c *container) startCmd(pid string, cmd *exec.Cmd, p *process) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *container) getLibctContainer() (libcontainer.Container, error) {
|
func (c *container) getLibctContainer() (libcontainer.Container, error) {
|
||||||
f, err := libcontainer.New("/run/runc", libcontainer.Cgroupfs)
|
runtimeRoot := "/run/runc"
|
||||||
|
|
||||||
|
// Check that the root wasn't changed
|
||||||
|
for _, opt := range c.runtimeArgs {
|
||||||
|
if strings.HasPrefix(opt, "--root=") {
|
||||||
|
runtimeRoot = strings.TrimPrefix(opt, "--root=")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
f, err := libcontainer.New(runtimeRoot, libcontainer.Cgroupfs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
46
vendor/src/github.com/docker/docker/pkg/integration/checker/checker.go
vendored
Normal file
46
vendor/src/github.com/docker/docker/pkg/integration/checker/checker.go
vendored
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
// Package checker provide Docker specific implementations of the go-check.Checker interface.
|
||||||
|
package checker
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/go-check/check"
|
||||||
|
"github.com/vdemeester/shakers"
|
||||||
|
)
|
||||||
|
|
||||||
|
// As a commodity, we bring all check.Checker variables into the current namespace to avoid having
|
||||||
|
// to think about check.X versus checker.X.
|
||||||
|
var (
|
||||||
|
DeepEquals = check.DeepEquals
|
||||||
|
ErrorMatches = check.ErrorMatches
|
||||||
|
FitsTypeOf = check.FitsTypeOf
|
||||||
|
HasLen = check.HasLen
|
||||||
|
Implements = check.Implements
|
||||||
|
IsNil = check.IsNil
|
||||||
|
Matches = check.Matches
|
||||||
|
Not = check.Not
|
||||||
|
NotNil = check.NotNil
|
||||||
|
PanicMatches = check.PanicMatches
|
||||||
|
Panics = check.Panics
|
||||||
|
|
||||||
|
Contains = shakers.Contains
|
||||||
|
ContainsAny = shakers.ContainsAny
|
||||||
|
Count = shakers.Count
|
||||||
|
Equals = shakers.Equals
|
||||||
|
EqualFold = shakers.EqualFold
|
||||||
|
False = shakers.False
|
||||||
|
GreaterOrEqualThan = shakers.GreaterOrEqualThan
|
||||||
|
GreaterThan = shakers.GreaterThan
|
||||||
|
HasPrefix = shakers.HasPrefix
|
||||||
|
HasSuffix = shakers.HasSuffix
|
||||||
|
Index = shakers.Index
|
||||||
|
IndexAny = shakers.IndexAny
|
||||||
|
IsAfter = shakers.IsAfter
|
||||||
|
IsBefore = shakers.IsBefore
|
||||||
|
IsBetween = shakers.IsBetween
|
||||||
|
IsLower = shakers.IsLower
|
||||||
|
IsUpper = shakers.IsUpper
|
||||||
|
LessOrEqualThan = shakers.LessOrEqualThan
|
||||||
|
LessThan = shakers.LessThan
|
||||||
|
TimeEquals = shakers.TimeEquals
|
||||||
|
True = shakers.True
|
||||||
|
TimeIgnore = shakers.TimeIgnore
|
||||||
|
)
|
4
vendor/src/github.com/go-check/check/.gitignore
vendored
Normal file
4
vendor/src/github.com/go-check/check/.gitignore
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
_*
|
||||||
|
*.swp
|
||||||
|
*.[568]
|
||||||
|
[568].out
|
10
vendor/src/github.com/go-check/check/.travis.yml
vendored
Normal file
10
vendor/src/github.com/go-check/check/.travis.yml
vendored
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
language: go
|
||||||
|
go:
|
||||||
|
- 1.5
|
||||||
|
- tip
|
||||||
|
script:
|
||||||
|
- go get -u github.com/golang/lint/golint
|
||||||
|
- # go vet ./...
|
||||||
|
- # test -z "$(golint ./... | tee /dev/stderr)"
|
||||||
|
- # test -z "$(gofmt -s -l . | tee /dev/stderr)"
|
||||||
|
- go test -v ./...
|
25
vendor/src/github.com/go-check/check/LICENSE
vendored
Normal file
25
vendor/src/github.com/go-check/check/LICENSE
vendored
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
Gocheck - A rich testing framework for Go
|
||||||
|
|
||||||
|
Copyright (c) 2010-2013 Gustavo Niemeyer <gustavo@niemeyer.net>
|
||||||
|
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice, this
|
||||||
|
list of conditions and the following disclaimer.
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation
|
||||||
|
and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||||
|
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
10
vendor/src/github.com/go-check/check/README.md
vendored
Normal file
10
vendor/src/github.com/go-check/check/README.md
vendored
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
Go-check
|
||||||
|
========
|
||||||
|
|
||||||
|
This is a fork of https://github.com/go-check/check
|
||||||
|
|
||||||
|
The intention of this fork is not to change any of the original behavior, but add
|
||||||
|
some specific behaviors needed for some of my projects already using this test suite.
|
||||||
|
For documentation on the main behavior of go-check see the aforementioned repo.
|
||||||
|
|
||||||
|
The original branch is intact at `orig_v1`
|
2
vendor/src/github.com/go-check/check/TODO
vendored
Normal file
2
vendor/src/github.com/go-check/check/TODO
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
- Assert(slice, Contains, item)
|
||||||
|
- Parallel test support
|
187
vendor/src/github.com/go-check/check/benchmark.go
vendored
Normal file
187
vendor/src/github.com/go-check/check/benchmark.go
vendored
Normal file
|
@ -0,0 +1,187 @@
|
||||||
|
// Copyright (c) 2012 The Go Authors. All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following disclaimer
|
||||||
|
// in the documentation and/or other materials provided with the
|
||||||
|
// distribution.
|
||||||
|
// * Neither the name of Google Inc. nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived from
|
||||||
|
// this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
package check
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"runtime"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var memStats runtime.MemStats
|
||||||
|
|
||||||
|
// testingB is a type passed to Benchmark functions to manage benchmark
|
||||||
|
// timing and to specify the number of iterations to run.
|
||||||
|
type timer struct {
|
||||||
|
start time.Time // Time test or benchmark started
|
||||||
|
duration time.Duration
|
||||||
|
N int
|
||||||
|
bytes int64
|
||||||
|
timerOn bool
|
||||||
|
benchTime time.Duration
|
||||||
|
// The initial states of memStats.Mallocs and memStats.TotalAlloc.
|
||||||
|
startAllocs uint64
|
||||||
|
startBytes uint64
|
||||||
|
// The net total of this test after being run.
|
||||||
|
netAllocs uint64
|
||||||
|
netBytes uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
// StartTimer starts timing a test. This function is called automatically
|
||||||
|
// before a benchmark starts, but it can also used to resume timing after
|
||||||
|
// a call to StopTimer.
|
||||||
|
func (c *C) StartTimer() {
|
||||||
|
if !c.timerOn {
|
||||||
|
c.start = time.Now()
|
||||||
|
c.timerOn = true
|
||||||
|
|
||||||
|
runtime.ReadMemStats(&memStats)
|
||||||
|
c.startAllocs = memStats.Mallocs
|
||||||
|
c.startBytes = memStats.TotalAlloc
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// StopTimer stops timing a test. This can be used to pause the timer
|
||||||
|
// while performing complex initialization that you don't
|
||||||
|
// want to measure.
|
||||||
|
func (c *C) StopTimer() {
|
||||||
|
if c.timerOn {
|
||||||
|
c.duration += time.Now().Sub(c.start)
|
||||||
|
c.timerOn = false
|
||||||
|
runtime.ReadMemStats(&memStats)
|
||||||
|
c.netAllocs += memStats.Mallocs - c.startAllocs
|
||||||
|
c.netBytes += memStats.TotalAlloc - c.startBytes
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResetTimer sets the elapsed benchmark time to zero.
|
||||||
|
// It does not affect whether the timer is running.
|
||||||
|
func (c *C) ResetTimer() {
|
||||||
|
if c.timerOn {
|
||||||
|
c.start = time.Now()
|
||||||
|
runtime.ReadMemStats(&memStats)
|
||||||
|
c.startAllocs = memStats.Mallocs
|
||||||
|
c.startBytes = memStats.TotalAlloc
|
||||||
|
}
|
||||||
|
c.duration = 0
|
||||||
|
c.netAllocs = 0
|
||||||
|
c.netBytes = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetBytes informs the number of bytes that the benchmark processes
|
||||||
|
// on each iteration. If this is called in a benchmark it will also
|
||||||
|
// report MB/s.
|
||||||
|
func (c *C) SetBytes(n int64) {
|
||||||
|
c.bytes = n
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *C) nsPerOp() int64 {
|
||||||
|
if c.N <= 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return c.duration.Nanoseconds() / int64(c.N)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *C) mbPerSec() float64 {
|
||||||
|
if c.bytes <= 0 || c.duration <= 0 || c.N <= 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return (float64(c.bytes) * float64(c.N) / 1e6) / c.duration.Seconds()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *C) timerString() string {
|
||||||
|
if c.N <= 0 {
|
||||||
|
return fmt.Sprintf("%3.3fs", float64(c.duration.Nanoseconds())/1e9)
|
||||||
|
}
|
||||||
|
mbs := c.mbPerSec()
|
||||||
|
mb := ""
|
||||||
|
if mbs != 0 {
|
||||||
|
mb = fmt.Sprintf("\t%7.2f MB/s", mbs)
|
||||||
|
}
|
||||||
|
nsop := c.nsPerOp()
|
||||||
|
ns := fmt.Sprintf("%10d ns/op", nsop)
|
||||||
|
if c.N > 0 && nsop < 100 {
|
||||||
|
// The format specifiers here make sure that
|
||||||
|
// the ones digits line up for all three possible formats.
|
||||||
|
if nsop < 10 {
|
||||||
|
ns = fmt.Sprintf("%13.2f ns/op", float64(c.duration.Nanoseconds())/float64(c.N))
|
||||||
|
} else {
|
||||||
|
ns = fmt.Sprintf("%12.1f ns/op", float64(c.duration.Nanoseconds())/float64(c.N))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
memStats := ""
|
||||||
|
if c.benchMem {
|
||||||
|
allocedBytes := fmt.Sprintf("%8d B/op", int64(c.netBytes)/int64(c.N))
|
||||||
|
allocs := fmt.Sprintf("%8d allocs/op", int64(c.netAllocs)/int64(c.N))
|
||||||
|
memStats = fmt.Sprintf("\t%s\t%s", allocedBytes, allocs)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%8d\t%s%s%s", c.N, ns, mb, memStats)
|
||||||
|
}
|
||||||
|
|
||||||
|
func min(x, y int) int {
|
||||||
|
if x > y {
|
||||||
|
return y
|
||||||
|
}
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
|
||||||
|
func max(x, y int) int {
|
||||||
|
if x < y {
|
||||||
|
return y
|
||||||
|
}
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
|
||||||
|
// roundDown10 rounds a number down to the nearest power of 10.
|
||||||
|
func roundDown10(n int) int {
|
||||||
|
var tens = 0
|
||||||
|
// tens = floor(log_10(n))
|
||||||
|
for n > 10 {
|
||||||
|
n = n / 10
|
||||||
|
tens++
|
||||||
|
}
|
||||||
|
// result = 10^tens
|
||||||
|
result := 1
|
||||||
|
for i := 0; i < tens; i++ {
|
||||||
|
result *= 10
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// roundUp rounds x up to a number of the form [1eX, 2eX, 5eX].
|
||||||
|
func roundUp(n int) int {
|
||||||
|
base := roundDown10(n)
|
||||||
|
if n < (2 * base) {
|
||||||
|
return 2 * base
|
||||||
|
}
|
||||||
|
if n < (5 * base) {
|
||||||
|
return 5 * base
|
||||||
|
}
|
||||||
|
return 10 * base
|
||||||
|
}
|
892
vendor/src/github.com/go-check/check/check.go
vendored
Normal file
892
vendor/src/github.com/go-check/check/check.go
vendored
Normal file
|
@ -0,0 +1,892 @@
|
||||||
|
// Package check is a rich testing extension for Go's testing package.
|
||||||
|
//
|
||||||
|
// For details about the project, see:
|
||||||
|
//
|
||||||
|
// http://labix.org/gocheck
|
||||||
|
//
|
||||||
|
package check
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"math/rand"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"path/filepath"
|
||||||
|
"reflect"
|
||||||
|
"regexp"
|
||||||
|
"runtime"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Internal type which deals with suite method calling.
|
||||||
|
|
||||||
|
const (
|
||||||
|
fixtureKd = iota
|
||||||
|
testKd
|
||||||
|
)
|
||||||
|
|
||||||
|
type funcKind int
|
||||||
|
|
||||||
|
const (
|
||||||
|
succeededSt = iota
|
||||||
|
failedSt
|
||||||
|
skippedSt
|
||||||
|
panickedSt
|
||||||
|
fixturePanickedSt
|
||||||
|
missedSt
|
||||||
|
)
|
||||||
|
|
||||||
|
type funcStatus uint32
|
||||||
|
|
||||||
|
// A method value can't reach its own Method structure.
|
||||||
|
type methodType struct {
|
||||||
|
reflect.Value
|
||||||
|
Info reflect.Method
|
||||||
|
}
|
||||||
|
|
||||||
|
func newMethod(receiver reflect.Value, i int) *methodType {
|
||||||
|
return &methodType{receiver.Method(i), receiver.Type().Method(i)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (method *methodType) PC() uintptr {
|
||||||
|
return method.Info.Func.Pointer()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (method *methodType) suiteName() string {
|
||||||
|
t := method.Info.Type.In(0)
|
||||||
|
if t.Kind() == reflect.Ptr {
|
||||||
|
t = t.Elem()
|
||||||
|
}
|
||||||
|
return t.Name()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (method *methodType) String() string {
|
||||||
|
return method.suiteName() + "." + method.Info.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (method *methodType) matches(re *regexp.Regexp) bool {
|
||||||
|
return (re.MatchString(method.Info.Name) ||
|
||||||
|
re.MatchString(method.suiteName()) ||
|
||||||
|
re.MatchString(method.String()))
|
||||||
|
}
|
||||||
|
|
||||||
|
type C struct {
|
||||||
|
method *methodType
|
||||||
|
kind funcKind
|
||||||
|
testName string
|
||||||
|
_status funcStatus
|
||||||
|
logb *logger
|
||||||
|
logw io.Writer
|
||||||
|
done chan *C
|
||||||
|
reason string
|
||||||
|
mustFail bool
|
||||||
|
tempDir *tempDir
|
||||||
|
benchMem bool
|
||||||
|
startTime time.Time
|
||||||
|
timer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *C) status() funcStatus {
|
||||||
|
return funcStatus(atomic.LoadUint32((*uint32)(&c._status)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *C) setStatus(s funcStatus) {
|
||||||
|
atomic.StoreUint32((*uint32)(&c._status), uint32(s))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *C) stopNow() {
|
||||||
|
runtime.Goexit()
|
||||||
|
}
|
||||||
|
|
||||||
|
// logger is a concurrency safe byte.Buffer
|
||||||
|
type logger struct {
|
||||||
|
sync.Mutex
|
||||||
|
writer bytes.Buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *logger) Write(buf []byte) (int, error) {
|
||||||
|
l.Lock()
|
||||||
|
defer l.Unlock()
|
||||||
|
return l.writer.Write(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *logger) WriteTo(w io.Writer) (int64, error) {
|
||||||
|
l.Lock()
|
||||||
|
defer l.Unlock()
|
||||||
|
return l.writer.WriteTo(w)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *logger) String() string {
|
||||||
|
l.Lock()
|
||||||
|
defer l.Unlock()
|
||||||
|
return l.writer.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Handling of temporary files and directories.
|
||||||
|
|
||||||
|
type tempDir struct {
|
||||||
|
sync.Mutex
|
||||||
|
path string
|
||||||
|
counter int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (td *tempDir) newPath() string {
|
||||||
|
td.Lock()
|
||||||
|
defer td.Unlock()
|
||||||
|
if td.path == "" {
|
||||||
|
var err error
|
||||||
|
for i := 0; i != 100; i++ {
|
||||||
|
path := fmt.Sprintf("%s%ccheck-%d", os.TempDir(), os.PathSeparator, rand.Int())
|
||||||
|
if err = os.Mkdir(path, 0700); err == nil {
|
||||||
|
td.path = path
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if td.path == "" {
|
||||||
|
panic("Couldn't create temporary directory: " + err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result := filepath.Join(td.path, strconv.Itoa(td.counter))
|
||||||
|
td.counter += 1
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (td *tempDir) removeAll() {
|
||||||
|
td.Lock()
|
||||||
|
defer td.Unlock()
|
||||||
|
if td.path != "" {
|
||||||
|
err := os.RemoveAll(td.path)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "WARNING: Error cleaning up temporaries: "+err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new temporary directory which is automatically removed after
|
||||||
|
// the suite finishes running.
|
||||||
|
func (c *C) MkDir() string {
|
||||||
|
path := c.tempDir.newPath()
|
||||||
|
if err := os.Mkdir(path, 0700); err != nil {
|
||||||
|
panic(fmt.Sprintf("Couldn't create temporary directory %s: %s", path, err.Error()))
|
||||||
|
}
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Low-level logging functions.
|
||||||
|
|
||||||
|
func (c *C) log(args ...interface{}) {
|
||||||
|
c.writeLog([]byte(fmt.Sprint(args...) + "\n"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *C) logf(format string, args ...interface{}) {
|
||||||
|
c.writeLog([]byte(fmt.Sprintf(format+"\n", args...)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *C) logNewLine() {
|
||||||
|
c.writeLog([]byte{'\n'})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *C) writeLog(buf []byte) {
|
||||||
|
c.logb.Write(buf)
|
||||||
|
if c.logw != nil {
|
||||||
|
c.logw.Write(buf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func hasStringOrError(x interface{}) (ok bool) {
|
||||||
|
_, ok = x.(fmt.Stringer)
|
||||||
|
if ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, ok = x.(error)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *C) logValue(label string, value interface{}) {
|
||||||
|
if label == "" {
|
||||||
|
if hasStringOrError(value) {
|
||||||
|
c.logf("... %#v (%q)", value, value)
|
||||||
|
} else {
|
||||||
|
c.logf("... %#v", value)
|
||||||
|
}
|
||||||
|
} else if value == nil {
|
||||||
|
c.logf("... %s = nil", label)
|
||||||
|
} else {
|
||||||
|
if hasStringOrError(value) {
|
||||||
|
fv := fmt.Sprintf("%#v", value)
|
||||||
|
qv := fmt.Sprintf("%q", value)
|
||||||
|
if fv != qv {
|
||||||
|
c.logf("... %s %s = %s (%s)", label, reflect.TypeOf(value), fv, qv)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if s, ok := value.(string); ok && isMultiLine(s) {
|
||||||
|
c.logf(`... %s %s = "" +`, label, reflect.TypeOf(value))
|
||||||
|
c.logMultiLine(s)
|
||||||
|
} else {
|
||||||
|
c.logf("... %s %s = %#v", label, reflect.TypeOf(value), value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *C) logMultiLine(s string) {
|
||||||
|
b := make([]byte, 0, len(s)*2)
|
||||||
|
i := 0
|
||||||
|
n := len(s)
|
||||||
|
for i < n {
|
||||||
|
j := i + 1
|
||||||
|
for j < n && s[j-1] != '\n' {
|
||||||
|
j++
|
||||||
|
}
|
||||||
|
b = append(b, "... "...)
|
||||||
|
b = strconv.AppendQuote(b, s[i:j])
|
||||||
|
if j < n {
|
||||||
|
b = append(b, " +"...)
|
||||||
|
}
|
||||||
|
b = append(b, '\n')
|
||||||
|
i = j
|
||||||
|
}
|
||||||
|
c.writeLog(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func isMultiLine(s string) bool {
|
||||||
|
for i := 0; i+1 < len(s); i++ {
|
||||||
|
if s[i] == '\n' {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *C) logString(issue string) {
|
||||||
|
c.log("... ", issue)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *C) logCaller(skip int) {
|
||||||
|
// This is a bit heavier than it ought to be.
|
||||||
|
skip += 1 // Our own frame.
|
||||||
|
pc, callerFile, callerLine, ok := runtime.Caller(skip)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var testFile string
|
||||||
|
var testLine int
|
||||||
|
testFunc := runtime.FuncForPC(c.method.PC())
|
||||||
|
if runtime.FuncForPC(pc) != testFunc {
|
||||||
|
for {
|
||||||
|
skip += 1
|
||||||
|
if pc, file, line, ok := runtime.Caller(skip); ok {
|
||||||
|
// Note that the test line may be different on
|
||||||
|
// distinct calls for the same test. Showing
|
||||||
|
// the "internal" line is helpful when debugging.
|
||||||
|
if runtime.FuncForPC(pc) == testFunc {
|
||||||
|
testFile, testLine = file, line
|
||||||
|
break
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if testFile != "" && (testFile != callerFile || testLine != callerLine) {
|
||||||
|
c.logCode(testFile, testLine)
|
||||||
|
}
|
||||||
|
c.logCode(callerFile, callerLine)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *C) logCode(path string, line int) {
|
||||||
|
c.logf("%s:%d:", nicePath(path), line)
|
||||||
|
code, err := printLine(path, line)
|
||||||
|
if code == "" {
|
||||||
|
code = "..." // XXX Open the file and take the raw line.
|
||||||
|
if err != nil {
|
||||||
|
code += err.Error()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c.log(indent(code, " "))
|
||||||
|
}
|
||||||
|
|
||||||
|
var valueGo = filepath.Join("reflect", "value.go")
|
||||||
|
var asmGo = filepath.Join("runtime", "asm_")
|
||||||
|
|
||||||
|
func (c *C) logPanic(skip int, value interface{}) {
|
||||||
|
skip++ // Our own frame.
|
||||||
|
initialSkip := skip
|
||||||
|
for ; ; skip++ {
|
||||||
|
if pc, file, line, ok := runtime.Caller(skip); ok {
|
||||||
|
if skip == initialSkip {
|
||||||
|
c.logf("... Panic: %s (PC=0x%X)\n", value, pc)
|
||||||
|
}
|
||||||
|
name := niceFuncName(pc)
|
||||||
|
path := nicePath(file)
|
||||||
|
if strings.Contains(path, "/gopkg.in/check.v") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if name == "Value.call" && strings.HasSuffix(path, valueGo) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (name == "call16" || name == "call32") && strings.Contains(path, asmGo) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
c.logf("%s:%d\n in %s", nicePath(file), line, name)
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *C) logSoftPanic(issue string) {
|
||||||
|
c.log("... Panic: ", issue)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *C) logArgPanic(method *methodType, expectedType string) {
|
||||||
|
c.logf("... Panic: %s argument should be %s",
|
||||||
|
niceFuncName(method.PC()), expectedType)
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Some simple formatting helpers.
|
||||||
|
|
||||||
|
var initWD, initWDErr = os.Getwd()
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
if initWDErr == nil {
|
||||||
|
initWD = strings.Replace(initWD, "\\", "/", -1) + "/"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func nicePath(path string) string {
|
||||||
|
if initWDErr == nil {
|
||||||
|
if strings.HasPrefix(path, initWD) {
|
||||||
|
return path[len(initWD):]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
|
||||||
|
func niceFuncPath(pc uintptr) string {
|
||||||
|
function := runtime.FuncForPC(pc)
|
||||||
|
if function != nil {
|
||||||
|
filename, line := function.FileLine(pc)
|
||||||
|
return fmt.Sprintf("%s:%d", nicePath(filename), line)
|
||||||
|
}
|
||||||
|
return "<unknown path>"
|
||||||
|
}
|
||||||
|
|
||||||
|
func niceFuncName(pc uintptr) string {
|
||||||
|
function := runtime.FuncForPC(pc)
|
||||||
|
if function != nil {
|
||||||
|
name := path.Base(function.Name())
|
||||||
|
if i := strings.Index(name, "."); i > 0 {
|
||||||
|
name = name[i+1:]
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(name, "(*") {
|
||||||
|
if i := strings.Index(name, ")"); i > 0 {
|
||||||
|
name = name[2:i] + name[i+1:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if i := strings.LastIndex(name, ".*"); i != -1 {
|
||||||
|
name = name[:i] + "." + name[i+2:]
|
||||||
|
}
|
||||||
|
if i := strings.LastIndex(name, "·"); i != -1 {
|
||||||
|
name = name[:i] + "." + name[i+2:]
|
||||||
|
}
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
return "<unknown function>"
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Result tracker to aggregate call results.
|
||||||
|
|
||||||
|
type Result struct {
|
||||||
|
Succeeded int
|
||||||
|
Failed int
|
||||||
|
Skipped int
|
||||||
|
Panicked int
|
||||||
|
FixturePanicked int
|
||||||
|
ExpectedFailures int
|
||||||
|
Missed int // Not even tried to run, related to a panic in the fixture.
|
||||||
|
RunError error // Houston, we've got a problem.
|
||||||
|
WorkDir string // If KeepWorkDir is true
|
||||||
|
}
|
||||||
|
|
||||||
|
type resultTracker struct {
|
||||||
|
result Result
|
||||||
|
_lastWasProblem bool
|
||||||
|
_waiting int
|
||||||
|
_missed int
|
||||||
|
_expectChan chan *C
|
||||||
|
_doneChan chan *C
|
||||||
|
_stopChan chan bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func newResultTracker() *resultTracker {
|
||||||
|
return &resultTracker{_expectChan: make(chan *C), // Synchronous
|
||||||
|
_doneChan: make(chan *C, 32), // Asynchronous
|
||||||
|
_stopChan: make(chan bool)} // Synchronous
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tracker *resultTracker) start() {
|
||||||
|
go tracker._loopRoutine()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tracker *resultTracker) waitAndStop() {
|
||||||
|
<-tracker._stopChan
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tracker *resultTracker) expectCall(c *C) {
|
||||||
|
tracker._expectChan <- c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tracker *resultTracker) callDone(c *C) {
|
||||||
|
tracker._doneChan <- c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tracker *resultTracker) _loopRoutine() {
|
||||||
|
for {
|
||||||
|
var c *C
|
||||||
|
if tracker._waiting > 0 {
|
||||||
|
// Calls still running. Can't stop.
|
||||||
|
select {
|
||||||
|
// XXX Reindent this (not now to make diff clear)
|
||||||
|
case c = <-tracker._expectChan:
|
||||||
|
tracker._waiting += 1
|
||||||
|
case c = <-tracker._doneChan:
|
||||||
|
tracker._waiting -= 1
|
||||||
|
switch c.status() {
|
||||||
|
case succeededSt:
|
||||||
|
if c.kind == testKd {
|
||||||
|
if c.mustFail {
|
||||||
|
tracker.result.ExpectedFailures++
|
||||||
|
} else {
|
||||||
|
tracker.result.Succeeded++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case failedSt:
|
||||||
|
tracker.result.Failed++
|
||||||
|
case panickedSt:
|
||||||
|
if c.kind == fixtureKd {
|
||||||
|
tracker.result.FixturePanicked++
|
||||||
|
} else {
|
||||||
|
tracker.result.Panicked++
|
||||||
|
}
|
||||||
|
case fixturePanickedSt:
|
||||||
|
// Track it as missed, since the panic
|
||||||
|
// was on the fixture, not on the test.
|
||||||
|
tracker.result.Missed++
|
||||||
|
case missedSt:
|
||||||
|
tracker.result.Missed++
|
||||||
|
case skippedSt:
|
||||||
|
if c.kind == testKd {
|
||||||
|
tracker.result.Skipped++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// No calls. Can stop, but no done calls here.
|
||||||
|
select {
|
||||||
|
case tracker._stopChan <- true:
|
||||||
|
return
|
||||||
|
case c = <-tracker._expectChan:
|
||||||
|
tracker._waiting += 1
|
||||||
|
case c = <-tracker._doneChan:
|
||||||
|
panic("Tracker got an unexpected done call.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// The underlying suite runner.
|
||||||
|
|
||||||
|
type suiteRunner struct {
|
||||||
|
suite interface{}
|
||||||
|
setUpSuite, tearDownSuite *methodType
|
||||||
|
setUpTest, tearDownTest *methodType
|
||||||
|
tests []*methodType
|
||||||
|
tracker *resultTracker
|
||||||
|
tempDir *tempDir
|
||||||
|
keepDir bool
|
||||||
|
output *outputWriter
|
||||||
|
reportedProblemLast bool
|
||||||
|
benchTime time.Duration
|
||||||
|
benchMem bool
|
||||||
|
checkTimeout time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
type RunConf struct {
|
||||||
|
Output io.Writer
|
||||||
|
Stream bool
|
||||||
|
Verbose bool
|
||||||
|
Filter string
|
||||||
|
Benchmark bool
|
||||||
|
BenchmarkTime time.Duration // Defaults to 1 second
|
||||||
|
BenchmarkMem bool
|
||||||
|
KeepWorkDir bool
|
||||||
|
CheckTimeout time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new suiteRunner able to run all methods in the given suite.
|
||||||
|
func newSuiteRunner(suite interface{}, runConf *RunConf) *suiteRunner {
|
||||||
|
var conf RunConf
|
||||||
|
if runConf != nil {
|
||||||
|
conf = *runConf
|
||||||
|
}
|
||||||
|
if conf.Output == nil {
|
||||||
|
conf.Output = os.Stdout
|
||||||
|
}
|
||||||
|
if conf.Benchmark {
|
||||||
|
conf.Verbose = true
|
||||||
|
}
|
||||||
|
|
||||||
|
suiteType := reflect.TypeOf(suite)
|
||||||
|
suiteNumMethods := suiteType.NumMethod()
|
||||||
|
suiteValue := reflect.ValueOf(suite)
|
||||||
|
|
||||||
|
runner := &suiteRunner{
|
||||||
|
suite: suite,
|
||||||
|
output: newOutputWriter(conf.Output, conf.Stream, conf.Verbose),
|
||||||
|
tracker: newResultTracker(),
|
||||||
|
benchTime: conf.BenchmarkTime,
|
||||||
|
benchMem: conf.BenchmarkMem,
|
||||||
|
tempDir: &tempDir{},
|
||||||
|
keepDir: conf.KeepWorkDir,
|
||||||
|
tests: make([]*methodType, 0, suiteNumMethods),
|
||||||
|
checkTimeout: conf.CheckTimeout,
|
||||||
|
}
|
||||||
|
if runner.benchTime == 0 {
|
||||||
|
runner.benchTime = 1 * time.Second
|
||||||
|
}
|
||||||
|
|
||||||
|
var filterRegexp *regexp.Regexp
|
||||||
|
if conf.Filter != "" {
|
||||||
|
if regexp, err := regexp.Compile(conf.Filter); err != nil {
|
||||||
|
msg := "Bad filter expression: " + err.Error()
|
||||||
|
runner.tracker.result.RunError = errors.New(msg)
|
||||||
|
return runner
|
||||||
|
} else {
|
||||||
|
filterRegexp = regexp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i != suiteNumMethods; i++ {
|
||||||
|
method := newMethod(suiteValue, i)
|
||||||
|
switch method.Info.Name {
|
||||||
|
case "SetUpSuite":
|
||||||
|
runner.setUpSuite = method
|
||||||
|
case "TearDownSuite":
|
||||||
|
runner.tearDownSuite = method
|
||||||
|
case "SetUpTest":
|
||||||
|
runner.setUpTest = method
|
||||||
|
case "TearDownTest":
|
||||||
|
runner.tearDownTest = method
|
||||||
|
default:
|
||||||
|
prefix := "Test"
|
||||||
|
if conf.Benchmark {
|
||||||
|
prefix = "Benchmark"
|
||||||
|
}
|
||||||
|
if !strings.HasPrefix(method.Info.Name, prefix) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if filterRegexp == nil || method.matches(filterRegexp) {
|
||||||
|
runner.tests = append(runner.tests, method)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return runner
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run all methods in the given suite.
|
||||||
|
func (runner *suiteRunner) run() *Result {
|
||||||
|
if runner.tracker.result.RunError == nil && len(runner.tests) > 0 {
|
||||||
|
runner.tracker.start()
|
||||||
|
if runner.checkFixtureArgs() {
|
||||||
|
c := runner.runFixture(runner.setUpSuite, "", nil)
|
||||||
|
if c == nil || c.status() == succeededSt {
|
||||||
|
for i := 0; i != len(runner.tests); i++ {
|
||||||
|
c := runner.runTest(runner.tests[i])
|
||||||
|
if c.status() == fixturePanickedSt {
|
||||||
|
runner.skipTests(missedSt, runner.tests[i+1:])
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if c != nil && c.status() == skippedSt {
|
||||||
|
runner.skipTests(skippedSt, runner.tests)
|
||||||
|
} else {
|
||||||
|
runner.skipTests(missedSt, runner.tests)
|
||||||
|
}
|
||||||
|
runner.runFixture(runner.tearDownSuite, "", nil)
|
||||||
|
} else {
|
||||||
|
runner.skipTests(missedSt, runner.tests)
|
||||||
|
}
|
||||||
|
runner.tracker.waitAndStop()
|
||||||
|
if runner.keepDir {
|
||||||
|
runner.tracker.result.WorkDir = runner.tempDir.path
|
||||||
|
} else {
|
||||||
|
runner.tempDir.removeAll()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &runner.tracker.result
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a call object with the given suite method, and fork a
|
||||||
|
// goroutine with the provided dispatcher for running it.
|
||||||
|
func (runner *suiteRunner) forkCall(method *methodType, kind funcKind, testName string, logb *logger, dispatcher func(c *C)) *C {
|
||||||
|
var logw io.Writer
|
||||||
|
if runner.output.Stream {
|
||||||
|
logw = runner.output
|
||||||
|
}
|
||||||
|
if logb == nil {
|
||||||
|
logb = new(logger)
|
||||||
|
}
|
||||||
|
c := &C{
|
||||||
|
method: method,
|
||||||
|
kind: kind,
|
||||||
|
testName: testName,
|
||||||
|
logb: logb,
|
||||||
|
logw: logw,
|
||||||
|
tempDir: runner.tempDir,
|
||||||
|
done: make(chan *C, 1),
|
||||||
|
timer: timer{benchTime: runner.benchTime},
|
||||||
|
startTime: time.Now(),
|
||||||
|
benchMem: runner.benchMem,
|
||||||
|
}
|
||||||
|
runner.tracker.expectCall(c)
|
||||||
|
go (func() {
|
||||||
|
runner.reportCallStarted(c)
|
||||||
|
defer runner.callDone(c)
|
||||||
|
dispatcher(c)
|
||||||
|
})()
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// Same as forkCall(), but wait for call to finish before returning.
|
||||||
|
func (runner *suiteRunner) runFunc(method *methodType, kind funcKind, testName string, logb *logger, dispatcher func(c *C)) *C {
|
||||||
|
var timeout <-chan time.Time
|
||||||
|
if runner.checkTimeout != 0 {
|
||||||
|
timeout = time.After(runner.checkTimeout)
|
||||||
|
}
|
||||||
|
c := runner.forkCall(method, kind, testName, logb, dispatcher)
|
||||||
|
select {
|
||||||
|
case <-c.done:
|
||||||
|
case <-timeout:
|
||||||
|
panic(fmt.Sprintf("test timed out after %v", runner.checkTimeout))
|
||||||
|
}
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle a finished call. If there were any panics, update the call status
|
||||||
|
// accordingly. Then, mark the call as done and report to the tracker.
|
||||||
|
func (runner *suiteRunner) callDone(c *C) {
|
||||||
|
value := recover()
|
||||||
|
if value != nil {
|
||||||
|
switch v := value.(type) {
|
||||||
|
case *fixturePanic:
|
||||||
|
if v.status == skippedSt {
|
||||||
|
c.setStatus(skippedSt)
|
||||||
|
} else {
|
||||||
|
c.logSoftPanic("Fixture has panicked (see related PANIC)")
|
||||||
|
c.setStatus(fixturePanickedSt)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
c.logPanic(1, value)
|
||||||
|
c.setStatus(panickedSt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if c.mustFail {
|
||||||
|
switch c.status() {
|
||||||
|
case failedSt:
|
||||||
|
c.setStatus(succeededSt)
|
||||||
|
case succeededSt:
|
||||||
|
c.setStatus(failedSt)
|
||||||
|
c.logString("Error: Test succeeded, but was expected to fail")
|
||||||
|
c.logString("Reason: " + c.reason)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
runner.reportCallDone(c)
|
||||||
|
c.done <- c
|
||||||
|
}
|
||||||
|
|
||||||
|
// Runs a fixture call synchronously. The fixture will still be run in a
|
||||||
|
// goroutine like all suite methods, but this method will not return
|
||||||
|
// while the fixture goroutine is not done, because the fixture must be
|
||||||
|
// run in a desired order.
|
||||||
|
func (runner *suiteRunner) runFixture(method *methodType, testName string, logb *logger) *C {
|
||||||
|
if method != nil {
|
||||||
|
c := runner.runFunc(method, fixtureKd, testName, logb, func(c *C) {
|
||||||
|
c.ResetTimer()
|
||||||
|
c.StartTimer()
|
||||||
|
defer c.StopTimer()
|
||||||
|
c.method.Call([]reflect.Value{reflect.ValueOf(c)})
|
||||||
|
})
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run the fixture method with runFixture(), but panic with a fixturePanic{}
|
||||||
|
// in case the fixture method panics. This makes it easier to track the
|
||||||
|
// fixture panic together with other call panics within forkTest().
|
||||||
|
func (runner *suiteRunner) runFixtureWithPanic(method *methodType, testName string, logb *logger, skipped *bool) *C {
|
||||||
|
if skipped != nil && *skipped {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
c := runner.runFixture(method, testName, logb)
|
||||||
|
if c != nil && c.status() != succeededSt {
|
||||||
|
if skipped != nil {
|
||||||
|
*skipped = c.status() == skippedSt
|
||||||
|
}
|
||||||
|
panic(&fixturePanic{c.status(), method})
|
||||||
|
}
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
type fixturePanic struct {
|
||||||
|
status funcStatus
|
||||||
|
method *methodType
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run the suite test method, together with the test-specific fixture,
|
||||||
|
// asynchronously.
|
||||||
|
func (runner *suiteRunner) forkTest(method *methodType) *C {
|
||||||
|
testName := method.String()
|
||||||
|
return runner.forkCall(method, testKd, testName, nil, func(c *C) {
|
||||||
|
var skipped bool
|
||||||
|
defer runner.runFixtureWithPanic(runner.tearDownTest, testName, nil, &skipped)
|
||||||
|
defer c.StopTimer()
|
||||||
|
benchN := 1
|
||||||
|
for {
|
||||||
|
runner.runFixtureWithPanic(runner.setUpTest, testName, c.logb, &skipped)
|
||||||
|
mt := c.method.Type()
|
||||||
|
if mt.NumIn() != 1 || mt.In(0) != reflect.TypeOf(c) {
|
||||||
|
// Rather than a plain panic, provide a more helpful message when
|
||||||
|
// the argument type is incorrect.
|
||||||
|
c.setStatus(panickedSt)
|
||||||
|
c.logArgPanic(c.method, "*check.C")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(c.method.Info.Name, "Test") {
|
||||||
|
c.ResetTimer()
|
||||||
|
c.StartTimer()
|
||||||
|
c.method.Call([]reflect.Value{reflect.ValueOf(c)})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !strings.HasPrefix(c.method.Info.Name, "Benchmark") {
|
||||||
|
panic("unexpected method prefix: " + c.method.Info.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
runtime.GC()
|
||||||
|
c.N = benchN
|
||||||
|
c.ResetTimer()
|
||||||
|
c.StartTimer()
|
||||||
|
c.method.Call([]reflect.Value{reflect.ValueOf(c)})
|
||||||
|
c.StopTimer()
|
||||||
|
if c.status() != succeededSt || c.duration >= c.benchTime || benchN >= 1e9 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
perOpN := int(1e9)
|
||||||
|
if c.nsPerOp() != 0 {
|
||||||
|
perOpN = int(c.benchTime.Nanoseconds() / c.nsPerOp())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Logic taken from the stock testing package:
|
||||||
|
// - Run more iterations than we think we'll need for a second (1.5x).
|
||||||
|
// - Don't grow too fast in case we had timing errors previously.
|
||||||
|
// - Be sure to run at least one more than last time.
|
||||||
|
benchN = max(min(perOpN+perOpN/2, 100*benchN), benchN+1)
|
||||||
|
benchN = roundUp(benchN)
|
||||||
|
|
||||||
|
skipped = true // Don't run the deferred one if this panics.
|
||||||
|
runner.runFixtureWithPanic(runner.tearDownTest, testName, nil, nil)
|
||||||
|
skipped = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Same as forkTest(), but wait for the test to finish before returning.
|
||||||
|
func (runner *suiteRunner) runTest(method *methodType) *C {
|
||||||
|
var timeout <-chan time.Time
|
||||||
|
if runner.checkTimeout != 0 {
|
||||||
|
timeout = time.After(runner.checkTimeout)
|
||||||
|
}
|
||||||
|
c := runner.forkTest(method)
|
||||||
|
select {
|
||||||
|
case <-c.done:
|
||||||
|
case <-timeout:
|
||||||
|
panic(fmt.Sprintf("test timed out after %v", runner.checkTimeout))
|
||||||
|
}
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper to mark tests as skipped or missed. A bit heavy for what
|
||||||
|
// it does, but it enables homogeneous handling of tracking, including
|
||||||
|
// nice verbose output.
|
||||||
|
func (runner *suiteRunner) skipTests(status funcStatus, methods []*methodType) {
|
||||||
|
for _, method := range methods {
|
||||||
|
runner.runFunc(method, testKd, "", nil, func(c *C) {
|
||||||
|
c.setStatus(status)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify if the fixture arguments are *check.C. In case of errors,
|
||||||
|
// log the error as a panic in the fixture method call, and return false.
|
||||||
|
func (runner *suiteRunner) checkFixtureArgs() bool {
|
||||||
|
succeeded := true
|
||||||
|
argType := reflect.TypeOf(&C{})
|
||||||
|
for _, method := range []*methodType{runner.setUpSuite, runner.tearDownSuite, runner.setUpTest, runner.tearDownTest} {
|
||||||
|
if method != nil {
|
||||||
|
mt := method.Type()
|
||||||
|
if mt.NumIn() != 1 || mt.In(0) != argType {
|
||||||
|
succeeded = false
|
||||||
|
runner.runFunc(method, fixtureKd, "", nil, func(c *C) {
|
||||||
|
c.logArgPanic(method, "*check.C")
|
||||||
|
c.setStatus(panickedSt)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return succeeded
|
||||||
|
}
|
||||||
|
|
||||||
|
func (runner *suiteRunner) reportCallStarted(c *C) {
|
||||||
|
runner.output.WriteCallStarted("START", c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (runner *suiteRunner) reportCallDone(c *C) {
|
||||||
|
runner.tracker.callDone(c)
|
||||||
|
switch c.status() {
|
||||||
|
case succeededSt:
|
||||||
|
if c.mustFail {
|
||||||
|
runner.output.WriteCallSuccess("FAIL EXPECTED", c)
|
||||||
|
} else {
|
||||||
|
runner.output.WriteCallSuccess("PASS", c)
|
||||||
|
}
|
||||||
|
case skippedSt:
|
||||||
|
runner.output.WriteCallSuccess("SKIP", c)
|
||||||
|
case failedSt:
|
||||||
|
runner.output.WriteCallProblem("FAIL", c)
|
||||||
|
case panickedSt:
|
||||||
|
runner.output.WriteCallProblem("PANIC", c)
|
||||||
|
case fixturePanickedSt:
|
||||||
|
// That's a testKd call reporting that its fixture
|
||||||
|
// has panicked. The fixture call which caused the
|
||||||
|
// panic itself was tracked above. We'll report to
|
||||||
|
// aid debugging.
|
||||||
|
runner.output.WriteCallProblem("PANIC", c)
|
||||||
|
case missedSt:
|
||||||
|
runner.output.WriteCallSuccess("MISS", c)
|
||||||
|
}
|
||||||
|
}
|
458
vendor/src/github.com/go-check/check/checkers.go
vendored
Normal file
458
vendor/src/github.com/go-check/check/checkers.go
vendored
Normal file
|
@ -0,0 +1,458 @@
|
||||||
|
package check
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"regexp"
|
||||||
|
)
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// CommentInterface and Commentf helper, to attach extra information to checks.
|
||||||
|
|
||||||
|
type comment struct {
|
||||||
|
format string
|
||||||
|
args []interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Commentf returns an infomational value to use with Assert or Check calls.
|
||||||
|
// If the checker test fails, the provided arguments will be passed to
|
||||||
|
// fmt.Sprintf, and will be presented next to the logged failure.
|
||||||
|
//
|
||||||
|
// For example:
|
||||||
|
//
|
||||||
|
// c.Assert(v, Equals, 42, Commentf("Iteration #%d failed.", i))
|
||||||
|
//
|
||||||
|
// Note that if the comment is constant, a better option is to
|
||||||
|
// simply use a normal comment right above or next to the line, as
|
||||||
|
// it will also get printed with any errors:
|
||||||
|
//
|
||||||
|
// c.Assert(l, Equals, 8192) // Ensure buffer size is correct (bug #123)
|
||||||
|
//
|
||||||
|
func Commentf(format string, args ...interface{}) CommentInterface {
|
||||||
|
return &comment{format, args}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CommentInterface must be implemented by types that attach extra
|
||||||
|
// information to failed checks. See the Commentf function for details.
|
||||||
|
type CommentInterface interface {
|
||||||
|
CheckCommentString() string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *comment) CheckCommentString() string {
|
||||||
|
return fmt.Sprintf(c.format, c.args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// The Checker interface.
|
||||||
|
|
||||||
|
// The Checker interface must be provided by checkers used with
|
||||||
|
// the Assert and Check verification methods.
|
||||||
|
type Checker interface {
|
||||||
|
Info() *CheckerInfo
|
||||||
|
Check(params []interface{}, names []string) (result bool, error string)
|
||||||
|
}
|
||||||
|
|
||||||
|
// See the Checker interface.
|
||||||
|
type CheckerInfo struct {
|
||||||
|
Name string
|
||||||
|
Params []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (info *CheckerInfo) Info() *CheckerInfo {
|
||||||
|
return info
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Not checker logic inverter.
|
||||||
|
|
||||||
|
// The Not checker inverts the logic of the provided checker. The
|
||||||
|
// resulting checker will succeed where the original one failed, and
|
||||||
|
// vice-versa.
|
||||||
|
//
|
||||||
|
// For example:
|
||||||
|
//
|
||||||
|
// c.Assert(a, Not(Equals), b)
|
||||||
|
//
|
||||||
|
func Not(checker Checker) Checker {
|
||||||
|
return ¬Checker{checker}
|
||||||
|
}
|
||||||
|
|
||||||
|
type notChecker struct {
|
||||||
|
sub Checker
|
||||||
|
}
|
||||||
|
|
||||||
|
func (checker *notChecker) Info() *CheckerInfo {
|
||||||
|
info := *checker.sub.Info()
|
||||||
|
info.Name = "Not(" + info.Name + ")"
|
||||||
|
return &info
|
||||||
|
}
|
||||||
|
|
||||||
|
func (checker *notChecker) Check(params []interface{}, names []string) (result bool, error string) {
|
||||||
|
result, error = checker.sub.Check(params, names)
|
||||||
|
result = !result
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// IsNil checker.
|
||||||
|
|
||||||
|
type isNilChecker struct {
|
||||||
|
*CheckerInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
// The IsNil checker tests whether the obtained value is nil.
|
||||||
|
//
|
||||||
|
// For example:
|
||||||
|
//
|
||||||
|
// c.Assert(err, IsNil)
|
||||||
|
//
|
||||||
|
var IsNil Checker = &isNilChecker{
|
||||||
|
&CheckerInfo{Name: "IsNil", Params: []string{"value"}},
|
||||||
|
}
|
||||||
|
|
||||||
|
func (checker *isNilChecker) Check(params []interface{}, names []string) (result bool, error string) {
|
||||||
|
return isNil(params[0]), ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func isNil(obtained interface{}) (result bool) {
|
||||||
|
if obtained == nil {
|
||||||
|
result = true
|
||||||
|
} else {
|
||||||
|
switch v := reflect.ValueOf(obtained); v.Kind() {
|
||||||
|
case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
|
||||||
|
return v.IsNil()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// NotNil checker. Alias for Not(IsNil), since it's so common.
|
||||||
|
|
||||||
|
type notNilChecker struct {
|
||||||
|
*CheckerInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
// The NotNil checker verifies that the obtained value is not nil.
|
||||||
|
//
|
||||||
|
// For example:
|
||||||
|
//
|
||||||
|
// c.Assert(iface, NotNil)
|
||||||
|
//
|
||||||
|
// This is an alias for Not(IsNil), made available since it's a
|
||||||
|
// fairly common check.
|
||||||
|
//
|
||||||
|
var NotNil Checker = ¬NilChecker{
|
||||||
|
&CheckerInfo{Name: "NotNil", Params: []string{"value"}},
|
||||||
|
}
|
||||||
|
|
||||||
|
func (checker *notNilChecker) Check(params []interface{}, names []string) (result bool, error string) {
|
||||||
|
return !isNil(params[0]), ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Equals checker.
|
||||||
|
|
||||||
|
type equalsChecker struct {
|
||||||
|
*CheckerInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
// The Equals checker verifies that the obtained value is equal to
|
||||||
|
// the expected value, according to usual Go semantics for ==.
|
||||||
|
//
|
||||||
|
// For example:
|
||||||
|
//
|
||||||
|
// c.Assert(value, Equals, 42)
|
||||||
|
//
|
||||||
|
var Equals Checker = &equalsChecker{
|
||||||
|
&CheckerInfo{Name: "Equals", Params: []string{"obtained", "expected"}},
|
||||||
|
}
|
||||||
|
|
||||||
|
func (checker *equalsChecker) Check(params []interface{}, names []string) (result bool, error string) {
|
||||||
|
defer func() {
|
||||||
|
if v := recover(); v != nil {
|
||||||
|
result = false
|
||||||
|
error = fmt.Sprint(v)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
return params[0] == params[1], ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// DeepEquals checker.
|
||||||
|
|
||||||
|
type deepEqualsChecker struct {
|
||||||
|
*CheckerInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
// The DeepEquals checker verifies that the obtained value is deep-equal to
|
||||||
|
// the expected value. The check will work correctly even when facing
|
||||||
|
// slices, interfaces, and values of different types (which always fail
|
||||||
|
// the test).
|
||||||
|
//
|
||||||
|
// For example:
|
||||||
|
//
|
||||||
|
// c.Assert(value, DeepEquals, 42)
|
||||||
|
// c.Assert(array, DeepEquals, []string{"hi", "there"})
|
||||||
|
//
|
||||||
|
var DeepEquals Checker = &deepEqualsChecker{
|
||||||
|
&CheckerInfo{Name: "DeepEquals", Params: []string{"obtained", "expected"}},
|
||||||
|
}
|
||||||
|
|
||||||
|
func (checker *deepEqualsChecker) Check(params []interface{}, names []string) (result bool, error string) {
|
||||||
|
return reflect.DeepEqual(params[0], params[1]), ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// HasLen checker.
|
||||||
|
|
||||||
|
type hasLenChecker struct {
|
||||||
|
*CheckerInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
// The HasLen checker verifies that the obtained value has the
|
||||||
|
// provided length. In many cases this is superior to using Equals
|
||||||
|
// in conjuction with the len function because in case the check
|
||||||
|
// fails the value itself will be printed, instead of its length,
|
||||||
|
// providing more details for figuring the problem.
|
||||||
|
//
|
||||||
|
// For example:
|
||||||
|
//
|
||||||
|
// c.Assert(list, HasLen, 5)
|
||||||
|
//
|
||||||
|
var HasLen Checker = &hasLenChecker{
|
||||||
|
&CheckerInfo{Name: "HasLen", Params: []string{"obtained", "n"}},
|
||||||
|
}
|
||||||
|
|
||||||
|
func (checker *hasLenChecker) Check(params []interface{}, names []string) (result bool, error string) {
|
||||||
|
n, ok := params[1].(int)
|
||||||
|
if !ok {
|
||||||
|
return false, "n must be an int"
|
||||||
|
}
|
||||||
|
value := reflect.ValueOf(params[0])
|
||||||
|
switch value.Kind() {
|
||||||
|
case reflect.Map, reflect.Array, reflect.Slice, reflect.Chan, reflect.String:
|
||||||
|
default:
|
||||||
|
return false, "obtained value type has no length"
|
||||||
|
}
|
||||||
|
return value.Len() == n, ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// ErrorMatches checker.
|
||||||
|
|
||||||
|
type errorMatchesChecker struct {
|
||||||
|
*CheckerInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
// The ErrorMatches checker verifies that the error value
|
||||||
|
// is non nil and matches the regular expression provided.
|
||||||
|
//
|
||||||
|
// For example:
|
||||||
|
//
|
||||||
|
// c.Assert(err, ErrorMatches, "perm.*denied")
|
||||||
|
//
|
||||||
|
var ErrorMatches Checker = errorMatchesChecker{
|
||||||
|
&CheckerInfo{Name: "ErrorMatches", Params: []string{"value", "regex"}},
|
||||||
|
}
|
||||||
|
|
||||||
|
func (checker errorMatchesChecker) Check(params []interface{}, names []string) (result bool, errStr string) {
|
||||||
|
if params[0] == nil {
|
||||||
|
return false, "Error value is nil"
|
||||||
|
}
|
||||||
|
err, ok := params[0].(error)
|
||||||
|
if !ok {
|
||||||
|
return false, "Value is not an error"
|
||||||
|
}
|
||||||
|
params[0] = err.Error()
|
||||||
|
names[0] = "error"
|
||||||
|
return matches(params[0], params[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Matches checker.
|
||||||
|
|
||||||
|
type matchesChecker struct {
|
||||||
|
*CheckerInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
// The Matches checker verifies that the string provided as the obtained
|
||||||
|
// value (or the string resulting from obtained.String()) matches the
|
||||||
|
// regular expression provided.
|
||||||
|
//
|
||||||
|
// For example:
|
||||||
|
//
|
||||||
|
// c.Assert(err, Matches, "perm.*denied")
|
||||||
|
//
|
||||||
|
var Matches Checker = &matchesChecker{
|
||||||
|
&CheckerInfo{Name: "Matches", Params: []string{"value", "regex"}},
|
||||||
|
}
|
||||||
|
|
||||||
|
func (checker *matchesChecker) Check(params []interface{}, names []string) (result bool, error string) {
|
||||||
|
return matches(params[0], params[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
func matches(value, regex interface{}) (result bool, error string) {
|
||||||
|
reStr, ok := regex.(string)
|
||||||
|
if !ok {
|
||||||
|
return false, "Regex must be a string"
|
||||||
|
}
|
||||||
|
valueStr, valueIsStr := value.(string)
|
||||||
|
if !valueIsStr {
|
||||||
|
if valueWithStr, valueHasStr := value.(fmt.Stringer); valueHasStr {
|
||||||
|
valueStr, valueIsStr = valueWithStr.String(), true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if valueIsStr {
|
||||||
|
matches, err := regexp.MatchString("^"+reStr+"$", valueStr)
|
||||||
|
if err != nil {
|
||||||
|
return false, "Can't compile regex: " + err.Error()
|
||||||
|
}
|
||||||
|
return matches, ""
|
||||||
|
}
|
||||||
|
return false, "Obtained value is not a string and has no .String()"
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Panics checker.
|
||||||
|
|
||||||
|
type panicsChecker struct {
|
||||||
|
*CheckerInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
// The Panics checker verifies that calling the provided zero-argument
|
||||||
|
// function will cause a panic which is deep-equal to the provided value.
|
||||||
|
//
|
||||||
|
// For example:
|
||||||
|
//
|
||||||
|
// c.Assert(func() { f(1, 2) }, Panics, &SomeErrorType{"BOOM"}).
|
||||||
|
//
|
||||||
|
//
|
||||||
|
var Panics Checker = &panicsChecker{
|
||||||
|
&CheckerInfo{Name: "Panics", Params: []string{"function", "expected"}},
|
||||||
|
}
|
||||||
|
|
||||||
|
func (checker *panicsChecker) Check(params []interface{}, names []string) (result bool, error string) {
|
||||||
|
f := reflect.ValueOf(params[0])
|
||||||
|
if f.Kind() != reflect.Func || f.Type().NumIn() != 0 {
|
||||||
|
return false, "Function must take zero arguments"
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
// If the function has not panicked, then don't do the check.
|
||||||
|
if error != "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
params[0] = recover()
|
||||||
|
names[0] = "panic"
|
||||||
|
result = reflect.DeepEqual(params[0], params[1])
|
||||||
|
}()
|
||||||
|
f.Call(nil)
|
||||||
|
return false, "Function has not panicked"
|
||||||
|
}
|
||||||
|
|
||||||
|
type panicMatchesChecker struct {
|
||||||
|
*CheckerInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
// The PanicMatches checker verifies that calling the provided zero-argument
|
||||||
|
// function will cause a panic with an error value matching
|
||||||
|
// the regular expression provided.
|
||||||
|
//
|
||||||
|
// For example:
|
||||||
|
//
|
||||||
|
// c.Assert(func() { f(1, 2) }, PanicMatches, `open.*: no such file or directory`).
|
||||||
|
//
|
||||||
|
//
|
||||||
|
var PanicMatches Checker = &panicMatchesChecker{
|
||||||
|
&CheckerInfo{Name: "PanicMatches", Params: []string{"function", "expected"}},
|
||||||
|
}
|
||||||
|
|
||||||
|
func (checker *panicMatchesChecker) Check(params []interface{}, names []string) (result bool, errmsg string) {
|
||||||
|
f := reflect.ValueOf(params[0])
|
||||||
|
if f.Kind() != reflect.Func || f.Type().NumIn() != 0 {
|
||||||
|
return false, "Function must take zero arguments"
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
// If the function has not panicked, then don't do the check.
|
||||||
|
if errmsg != "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
obtained := recover()
|
||||||
|
names[0] = "panic"
|
||||||
|
if e, ok := obtained.(error); ok {
|
||||||
|
params[0] = e.Error()
|
||||||
|
} else if _, ok := obtained.(string); ok {
|
||||||
|
params[0] = obtained
|
||||||
|
} else {
|
||||||
|
errmsg = "Panic value is not a string or an error"
|
||||||
|
return
|
||||||
|
}
|
||||||
|
result, errmsg = matches(params[0], params[1])
|
||||||
|
}()
|
||||||
|
f.Call(nil)
|
||||||
|
return false, "Function has not panicked"
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// FitsTypeOf checker.
|
||||||
|
|
||||||
|
type fitsTypeChecker struct {
|
||||||
|
*CheckerInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
// The FitsTypeOf checker verifies that the obtained value is
|
||||||
|
// assignable to a variable with the same type as the provided
|
||||||
|
// sample value.
|
||||||
|
//
|
||||||
|
// For example:
|
||||||
|
//
|
||||||
|
// c.Assert(value, FitsTypeOf, int64(0))
|
||||||
|
// c.Assert(value, FitsTypeOf, os.Error(nil))
|
||||||
|
//
|
||||||
|
var FitsTypeOf Checker = &fitsTypeChecker{
|
||||||
|
&CheckerInfo{Name: "FitsTypeOf", Params: []string{"obtained", "sample"}},
|
||||||
|
}
|
||||||
|
|
||||||
|
func (checker *fitsTypeChecker) Check(params []interface{}, names []string) (result bool, error string) {
|
||||||
|
obtained := reflect.ValueOf(params[0])
|
||||||
|
sample := reflect.ValueOf(params[1])
|
||||||
|
if !obtained.IsValid() {
|
||||||
|
return false, ""
|
||||||
|
}
|
||||||
|
if !sample.IsValid() {
|
||||||
|
return false, "Invalid sample value"
|
||||||
|
}
|
||||||
|
return obtained.Type().AssignableTo(sample.Type()), ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Implements checker.
|
||||||
|
|
||||||
|
type implementsChecker struct {
|
||||||
|
*CheckerInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
// The Implements checker verifies that the obtained value
|
||||||
|
// implements the interface specified via a pointer to an interface
|
||||||
|
// variable.
|
||||||
|
//
|
||||||
|
// For example:
|
||||||
|
//
|
||||||
|
// var e os.Error
|
||||||
|
// c.Assert(err, Implements, &e)
|
||||||
|
//
|
||||||
|
var Implements Checker = &implementsChecker{
|
||||||
|
&CheckerInfo{Name: "Implements", Params: []string{"obtained", "ifaceptr"}},
|
||||||
|
}
|
||||||
|
|
||||||
|
func (checker *implementsChecker) Check(params []interface{}, names []string) (result bool, error string) {
|
||||||
|
obtained := reflect.ValueOf(params[0])
|
||||||
|
ifaceptr := reflect.ValueOf(params[1])
|
||||||
|
if !obtained.IsValid() {
|
||||||
|
return false, ""
|
||||||
|
}
|
||||||
|
if !ifaceptr.IsValid() || ifaceptr.Kind() != reflect.Ptr || ifaceptr.Elem().Kind() != reflect.Interface {
|
||||||
|
return false, "ifaceptr should be a pointer to an interface variable"
|
||||||
|
}
|
||||||
|
return obtained.Type().Implements(ifaceptr.Elem().Type()), ""
|
||||||
|
}
|
231
vendor/src/github.com/go-check/check/helpers.go
vendored
Normal file
231
vendor/src/github.com/go-check/check/helpers.go
vendored
Normal file
|
@ -0,0 +1,231 @@
|
||||||
|
package check
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestName returns the current test name in the form "SuiteName.TestName"
|
||||||
|
func (c *C) TestName() string {
|
||||||
|
return c.testName
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Basic succeeding/failing logic.
|
||||||
|
|
||||||
|
// Failed returns whether the currently running test has already failed.
|
||||||
|
func (c *C) Failed() bool {
|
||||||
|
return c.status() == failedSt
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fail marks the currently running test as failed.
|
||||||
|
//
|
||||||
|
// Something ought to have been previously logged so the developer can tell
|
||||||
|
// what went wrong. The higher level helper functions will fail the test
|
||||||
|
// and do the logging properly.
|
||||||
|
func (c *C) Fail() {
|
||||||
|
c.setStatus(failedSt)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FailNow marks the currently running test as failed and stops running it.
|
||||||
|
// Something ought to have been previously logged so the developer can tell
|
||||||
|
// what went wrong. The higher level helper functions will fail the test
|
||||||
|
// and do the logging properly.
|
||||||
|
func (c *C) FailNow() {
|
||||||
|
c.Fail()
|
||||||
|
c.stopNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Succeed marks the currently running test as succeeded, undoing any
|
||||||
|
// previous failures.
|
||||||
|
func (c *C) Succeed() {
|
||||||
|
c.setStatus(succeededSt)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SucceedNow marks the currently running test as succeeded, undoing any
|
||||||
|
// previous failures, and stops running the test.
|
||||||
|
func (c *C) SucceedNow() {
|
||||||
|
c.Succeed()
|
||||||
|
c.stopNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExpectFailure informs that the running test is knowingly broken for
|
||||||
|
// the provided reason. If the test does not fail, an error will be reported
|
||||||
|
// to raise attention to this fact. This method is useful to temporarily
|
||||||
|
// disable tests which cover well known problems until a better time to
|
||||||
|
// fix the problem is found, without forgetting about the fact that a
|
||||||
|
// failure still exists.
|
||||||
|
func (c *C) ExpectFailure(reason string) {
|
||||||
|
if reason == "" {
|
||||||
|
panic("Missing reason why the test is expected to fail")
|
||||||
|
}
|
||||||
|
c.mustFail = true
|
||||||
|
c.reason = reason
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip skips the running test for the provided reason. If run from within
|
||||||
|
// SetUpTest, the individual test being set up will be skipped, and if run
|
||||||
|
// from within SetUpSuite, the whole suite is skipped.
|
||||||
|
func (c *C) Skip(reason string) {
|
||||||
|
if reason == "" {
|
||||||
|
panic("Missing reason why the test is being skipped")
|
||||||
|
}
|
||||||
|
c.reason = reason
|
||||||
|
c.setStatus(skippedSt)
|
||||||
|
c.stopNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Basic logging.
|
||||||
|
|
||||||
|
// GetTestLog returns the current test error output.
|
||||||
|
func (c *C) GetTestLog() string {
|
||||||
|
return c.logb.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log logs some information into the test error output.
|
||||||
|
// The provided arguments are assembled together into a string with fmt.Sprint.
|
||||||
|
func (c *C) Log(args ...interface{}) {
|
||||||
|
c.log(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log logs some information into the test error output.
|
||||||
|
// The provided arguments are assembled together into a string with fmt.Sprintf.
|
||||||
|
func (c *C) Logf(format string, args ...interface{}) {
|
||||||
|
c.logf(format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Output enables *C to be used as a logger in functions that require only
|
||||||
|
// the minimum interface of *log.Logger.
|
||||||
|
func (c *C) Output(calldepth int, s string) error {
|
||||||
|
d := time.Now().Sub(c.startTime)
|
||||||
|
msec := d / time.Millisecond
|
||||||
|
sec := d / time.Second
|
||||||
|
min := d / time.Minute
|
||||||
|
|
||||||
|
c.Logf("[LOG] %d:%02d.%03d %s", min, sec%60, msec%1000, s)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error logs an error into the test error output and marks the test as failed.
|
||||||
|
// The provided arguments are assembled together into a string with fmt.Sprint.
|
||||||
|
func (c *C) Error(args ...interface{}) {
|
||||||
|
c.logCaller(1)
|
||||||
|
c.logString(fmt.Sprint("Error: ", fmt.Sprint(args...)))
|
||||||
|
c.logNewLine()
|
||||||
|
c.Fail()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Errorf logs an error into the test error output and marks the test as failed.
|
||||||
|
// The provided arguments are assembled together into a string with fmt.Sprintf.
|
||||||
|
func (c *C) Errorf(format string, args ...interface{}) {
|
||||||
|
c.logCaller(1)
|
||||||
|
c.logString(fmt.Sprintf("Error: "+format, args...))
|
||||||
|
c.logNewLine()
|
||||||
|
c.Fail()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fatal logs an error into the test error output, marks the test as failed, and
|
||||||
|
// stops the test execution. The provided arguments are assembled together into
|
||||||
|
// a string with fmt.Sprint.
|
||||||
|
func (c *C) Fatal(args ...interface{}) {
|
||||||
|
c.logCaller(1)
|
||||||
|
c.logString(fmt.Sprint("Error: ", fmt.Sprint(args...)))
|
||||||
|
c.logNewLine()
|
||||||
|
c.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fatlaf logs an error into the test error output, marks the test as failed, and
|
||||||
|
// stops the test execution. The provided arguments are assembled together into
|
||||||
|
// a string with fmt.Sprintf.
|
||||||
|
func (c *C) Fatalf(format string, args ...interface{}) {
|
||||||
|
c.logCaller(1)
|
||||||
|
c.logString(fmt.Sprint("Error: ", fmt.Sprintf(format, args...)))
|
||||||
|
c.logNewLine()
|
||||||
|
c.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Generic checks and assertions based on checkers.
|
||||||
|
|
||||||
|
// Check verifies if the first value matches the expected value according
|
||||||
|
// to the provided checker. If they do not match, an error is logged, the
|
||||||
|
// test is marked as failed, and the test execution continues.
|
||||||
|
//
|
||||||
|
// Some checkers may not need the expected argument (e.g. IsNil).
|
||||||
|
//
|
||||||
|
// Extra arguments provided to the function are logged next to the reported
|
||||||
|
// problem when the matching fails.
|
||||||
|
func (c *C) Check(obtained interface{}, checker Checker, args ...interface{}) bool {
|
||||||
|
return c.internalCheck("Check", obtained, checker, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assert ensures that the first value matches the expected value according
|
||||||
|
// to the provided checker. If they do not match, an error is logged, the
|
||||||
|
// test is marked as failed, and the test execution stops.
|
||||||
|
//
|
||||||
|
// Some checkers may not need the expected argument (e.g. IsNil).
|
||||||
|
//
|
||||||
|
// Extra arguments provided to the function are logged next to the reported
|
||||||
|
// problem when the matching fails.
|
||||||
|
func (c *C) Assert(obtained interface{}, checker Checker, args ...interface{}) {
|
||||||
|
if !c.internalCheck("Assert", obtained, checker, args...) {
|
||||||
|
c.stopNow()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *C) internalCheck(funcName string, obtained interface{}, checker Checker, args ...interface{}) bool {
|
||||||
|
if checker == nil {
|
||||||
|
c.logCaller(2)
|
||||||
|
c.logString(fmt.Sprintf("%s(obtained, nil!?, ...):", funcName))
|
||||||
|
c.logString("Oops.. you've provided a nil checker!")
|
||||||
|
c.logNewLine()
|
||||||
|
c.Fail()
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the last argument is a bug info, extract it out.
|
||||||
|
var comment CommentInterface
|
||||||
|
if len(args) > 0 {
|
||||||
|
if c, ok := args[len(args)-1].(CommentInterface); ok {
|
||||||
|
comment = c
|
||||||
|
args = args[:len(args)-1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
params := append([]interface{}{obtained}, args...)
|
||||||
|
info := checker.Info()
|
||||||
|
|
||||||
|
if len(params) != len(info.Params) {
|
||||||
|
names := append([]string{info.Params[0], info.Name}, info.Params[1:]...)
|
||||||
|
c.logCaller(2)
|
||||||
|
c.logString(fmt.Sprintf("%s(%s):", funcName, strings.Join(names, ", ")))
|
||||||
|
c.logString(fmt.Sprintf("Wrong number of parameters for %s: want %d, got %d", info.Name, len(names), len(params)+1))
|
||||||
|
c.logNewLine()
|
||||||
|
c.Fail()
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy since it may be mutated by Check.
|
||||||
|
names := append([]string{}, info.Params...)
|
||||||
|
|
||||||
|
// Do the actual check.
|
||||||
|
result, error := checker.Check(params, names)
|
||||||
|
if !result || error != "" {
|
||||||
|
c.logCaller(2)
|
||||||
|
for i := 0; i != len(params); i++ {
|
||||||
|
c.logValue(names[i], params[i])
|
||||||
|
}
|
||||||
|
if comment != nil {
|
||||||
|
c.logString(comment.CheckCommentString())
|
||||||
|
}
|
||||||
|
if error != "" {
|
||||||
|
c.logString(error)
|
||||||
|
}
|
||||||
|
c.logNewLine()
|
||||||
|
c.Fail()
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
168
vendor/src/github.com/go-check/check/printer.go
vendored
Normal file
168
vendor/src/github.com/go-check/check/printer.go
vendored
Normal file
|
@ -0,0 +1,168 @@
|
||||||
|
package check
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"go/ast"
|
||||||
|
"go/parser"
|
||||||
|
"go/printer"
|
||||||
|
"go/token"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
func indent(s, with string) (r string) {
|
||||||
|
eol := true
|
||||||
|
for i := 0; i != len(s); i++ {
|
||||||
|
c := s[i]
|
||||||
|
switch {
|
||||||
|
case eol && c == '\n' || c == '\r':
|
||||||
|
case c == '\n' || c == '\r':
|
||||||
|
eol = true
|
||||||
|
case eol:
|
||||||
|
eol = false
|
||||||
|
s = s[:i] + with + s[i:]
|
||||||
|
i += len(with)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func printLine(filename string, line int) (string, error) {
|
||||||
|
fset := token.NewFileSet()
|
||||||
|
file, err := os.Open(filename)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
fnode, err := parser.ParseFile(fset, filename, file, parser.ParseComments)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
config := &printer.Config{Mode: printer.UseSpaces, Tabwidth: 4}
|
||||||
|
lp := &linePrinter{fset: fset, fnode: fnode, line: line, config: config}
|
||||||
|
ast.Walk(lp, fnode)
|
||||||
|
result := lp.output.Bytes()
|
||||||
|
// Comments leave \n at the end.
|
||||||
|
n := len(result)
|
||||||
|
for n > 0 && result[n-1] == '\n' {
|
||||||
|
n--
|
||||||
|
}
|
||||||
|
return string(result[:n]), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type linePrinter struct {
|
||||||
|
config *printer.Config
|
||||||
|
fset *token.FileSet
|
||||||
|
fnode *ast.File
|
||||||
|
line int
|
||||||
|
output bytes.Buffer
|
||||||
|
stmt ast.Stmt
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lp *linePrinter) emit() bool {
|
||||||
|
if lp.stmt != nil {
|
||||||
|
lp.trim(lp.stmt)
|
||||||
|
lp.printWithComments(lp.stmt)
|
||||||
|
lp.stmt = nil
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lp *linePrinter) printWithComments(n ast.Node) {
|
||||||
|
nfirst := lp.fset.Position(n.Pos()).Line
|
||||||
|
nlast := lp.fset.Position(n.End()).Line
|
||||||
|
for _, g := range lp.fnode.Comments {
|
||||||
|
cfirst := lp.fset.Position(g.Pos()).Line
|
||||||
|
clast := lp.fset.Position(g.End()).Line
|
||||||
|
if clast == nfirst-1 && lp.fset.Position(n.Pos()).Column == lp.fset.Position(g.Pos()).Column {
|
||||||
|
for _, c := range g.List {
|
||||||
|
lp.output.WriteString(c.Text)
|
||||||
|
lp.output.WriteByte('\n')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if cfirst >= nfirst && cfirst <= nlast && n.End() <= g.List[0].Slash {
|
||||||
|
// The printer will not include the comment if it starts past
|
||||||
|
// the node itself. Trick it into printing by overlapping the
|
||||||
|
// slash with the end of the statement.
|
||||||
|
g.List[0].Slash = n.End() - 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
node := &printer.CommentedNode{n, lp.fnode.Comments}
|
||||||
|
lp.config.Fprint(&lp.output, lp.fset, node)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lp *linePrinter) Visit(n ast.Node) (w ast.Visitor) {
|
||||||
|
if n == nil {
|
||||||
|
if lp.output.Len() == 0 {
|
||||||
|
lp.emit()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
first := lp.fset.Position(n.Pos()).Line
|
||||||
|
last := lp.fset.Position(n.End()).Line
|
||||||
|
if first <= lp.line && last >= lp.line {
|
||||||
|
// Print the innermost statement containing the line.
|
||||||
|
if stmt, ok := n.(ast.Stmt); ok {
|
||||||
|
if _, ok := n.(*ast.BlockStmt); !ok {
|
||||||
|
lp.stmt = stmt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if first == lp.line && lp.emit() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return lp
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lp *linePrinter) trim(n ast.Node) bool {
|
||||||
|
stmt, ok := n.(ast.Stmt)
|
||||||
|
if !ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
line := lp.fset.Position(n.Pos()).Line
|
||||||
|
if line != lp.line {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
switch stmt := stmt.(type) {
|
||||||
|
case *ast.IfStmt:
|
||||||
|
stmt.Body = lp.trimBlock(stmt.Body)
|
||||||
|
case *ast.SwitchStmt:
|
||||||
|
stmt.Body = lp.trimBlock(stmt.Body)
|
||||||
|
case *ast.TypeSwitchStmt:
|
||||||
|
stmt.Body = lp.trimBlock(stmt.Body)
|
||||||
|
case *ast.CaseClause:
|
||||||
|
stmt.Body = lp.trimList(stmt.Body)
|
||||||
|
case *ast.CommClause:
|
||||||
|
stmt.Body = lp.trimList(stmt.Body)
|
||||||
|
case *ast.BlockStmt:
|
||||||
|
stmt.List = lp.trimList(stmt.List)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lp *linePrinter) trimBlock(stmt *ast.BlockStmt) *ast.BlockStmt {
|
||||||
|
if !lp.trim(stmt) {
|
||||||
|
return lp.emptyBlock(stmt)
|
||||||
|
}
|
||||||
|
stmt.Rbrace = stmt.Lbrace
|
||||||
|
return stmt
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lp *linePrinter) trimList(stmts []ast.Stmt) []ast.Stmt {
|
||||||
|
for i := 0; i != len(stmts); i++ {
|
||||||
|
if !lp.trim(stmts[i]) {
|
||||||
|
stmts[i] = lp.emptyStmt(stmts[i])
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return stmts
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lp *linePrinter) emptyStmt(n ast.Node) *ast.ExprStmt {
|
||||||
|
return &ast.ExprStmt{&ast.Ellipsis{n.Pos(), nil}}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lp *linePrinter) emptyBlock(n ast.Node) *ast.BlockStmt {
|
||||||
|
p := n.Pos()
|
||||||
|
return &ast.BlockStmt{p, []ast.Stmt{lp.emptyStmt(n)}, p}
|
||||||
|
}
|
88
vendor/src/github.com/go-check/check/reporter.go
vendored
Normal file
88
vendor/src/github.com/go-check/check/reporter.go
vendored
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
package check
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Output writer manages atomic output writing according to settings.
|
||||||
|
|
||||||
|
type outputWriter struct {
|
||||||
|
m sync.Mutex
|
||||||
|
writer io.Writer
|
||||||
|
wroteCallProblemLast bool
|
||||||
|
Stream bool
|
||||||
|
Verbose bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func newOutputWriter(writer io.Writer, stream, verbose bool) *outputWriter {
|
||||||
|
return &outputWriter{writer: writer, Stream: stream, Verbose: verbose}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ow *outputWriter) Write(content []byte) (n int, err error) {
|
||||||
|
ow.m.Lock()
|
||||||
|
n, err = ow.writer.Write(content)
|
||||||
|
ow.m.Unlock()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ow *outputWriter) WriteCallStarted(label string, c *C) {
|
||||||
|
if ow.Stream {
|
||||||
|
header := renderCallHeader(label, c, "", "\n")
|
||||||
|
ow.m.Lock()
|
||||||
|
ow.writer.Write([]byte(header))
|
||||||
|
ow.m.Unlock()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ow *outputWriter) WriteCallProblem(label string, c *C) {
|
||||||
|
var prefix string
|
||||||
|
if !ow.Stream {
|
||||||
|
prefix = "\n-----------------------------------" +
|
||||||
|
"-----------------------------------\n"
|
||||||
|
}
|
||||||
|
header := renderCallHeader(label, c, prefix, "\n\n")
|
||||||
|
ow.m.Lock()
|
||||||
|
ow.wroteCallProblemLast = true
|
||||||
|
ow.writer.Write([]byte(header))
|
||||||
|
if !ow.Stream {
|
||||||
|
c.logb.WriteTo(ow.writer)
|
||||||
|
}
|
||||||
|
ow.m.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ow *outputWriter) WriteCallSuccess(label string, c *C) {
|
||||||
|
if ow.Stream || (ow.Verbose && c.kind == testKd) {
|
||||||
|
// TODO Use a buffer here.
|
||||||
|
var suffix string
|
||||||
|
if c.reason != "" {
|
||||||
|
suffix = " (" + c.reason + ")"
|
||||||
|
}
|
||||||
|
if c.status() == succeededSt {
|
||||||
|
suffix += "\t" + c.timerString()
|
||||||
|
}
|
||||||
|
suffix += "\n"
|
||||||
|
if ow.Stream {
|
||||||
|
suffix += "\n"
|
||||||
|
}
|
||||||
|
header := renderCallHeader(label, c, "", suffix)
|
||||||
|
ow.m.Lock()
|
||||||
|
// Resist temptation of using line as prefix above due to race.
|
||||||
|
if !ow.Stream && ow.wroteCallProblemLast {
|
||||||
|
header = "\n-----------------------------------" +
|
||||||
|
"-----------------------------------\n" +
|
||||||
|
header
|
||||||
|
}
|
||||||
|
ow.wroteCallProblemLast = false
|
||||||
|
ow.writer.Write([]byte(header))
|
||||||
|
ow.m.Unlock()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func renderCallHeader(label string, c *C, prefix, suffix string) string {
|
||||||
|
pc := c.method.PC()
|
||||||
|
return fmt.Sprintf("%s%s: %s: %s%s", prefix, label, niceFuncPath(pc),
|
||||||
|
niceFuncName(pc), suffix)
|
||||||
|
}
|
183
vendor/src/github.com/go-check/check/run.go
vendored
Normal file
183
vendor/src/github.com/go-check/check/run.go
vendored
Normal file
|
@ -0,0 +1,183 @@
|
||||||
|
package check
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Test suite registry.
|
||||||
|
|
||||||
|
var allSuites []interface{}
|
||||||
|
|
||||||
|
// Suite registers the given value as a test suite to be run. Any methods
|
||||||
|
// starting with the Test prefix in the given value will be considered as
|
||||||
|
// a test method.
|
||||||
|
func Suite(suite interface{}) interface{} {
|
||||||
|
allSuites = append(allSuites, suite)
|
||||||
|
return suite
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Public running interface.
|
||||||
|
|
||||||
|
var (
|
||||||
|
oldFilterFlag = flag.String("gocheck.f", "", "Regular expression selecting which tests and/or suites to run")
|
||||||
|
oldVerboseFlag = flag.Bool("gocheck.v", false, "Verbose mode")
|
||||||
|
oldStreamFlag = flag.Bool("gocheck.vv", false, "Super verbose mode (disables output caching)")
|
||||||
|
oldBenchFlag = flag.Bool("gocheck.b", false, "Run benchmarks")
|
||||||
|
oldBenchTime = flag.Duration("gocheck.btime", 1*time.Second, "approximate run time for each benchmark")
|
||||||
|
oldListFlag = flag.Bool("gocheck.list", false, "List the names of all tests that will be run")
|
||||||
|
oldWorkFlag = flag.Bool("gocheck.work", false, "Display and do not remove the test working directory")
|
||||||
|
|
||||||
|
newFilterFlag = flag.String("check.f", "", "Regular expression selecting which tests and/or suites to run")
|
||||||
|
newVerboseFlag = flag.Bool("check.v", false, "Verbose mode")
|
||||||
|
newStreamFlag = flag.Bool("check.vv", false, "Super verbose mode (disables output caching)")
|
||||||
|
newBenchFlag = flag.Bool("check.b", false, "Run benchmarks")
|
||||||
|
newBenchTime = flag.Duration("check.btime", 1*time.Second, "approximate run time for each benchmark")
|
||||||
|
newBenchMem = flag.Bool("check.bmem", false, "Report memory benchmarks")
|
||||||
|
newListFlag = flag.Bool("check.list", false, "List the names of all tests that will be run")
|
||||||
|
newWorkFlag = flag.Bool("check.work", false, "Display and do not remove the test working directory")
|
||||||
|
checkTimeout = flag.String("check.timeout", "", "Panic if test runs longer than specified duration")
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestingT runs all test suites registered with the Suite function,
|
||||||
|
// printing results to stdout, and reporting any failures back to
|
||||||
|
// the "testing" package.
|
||||||
|
func TestingT(testingT *testing.T) {
|
||||||
|
benchTime := *newBenchTime
|
||||||
|
if benchTime == 1*time.Second {
|
||||||
|
benchTime = *oldBenchTime
|
||||||
|
}
|
||||||
|
conf := &RunConf{
|
||||||
|
Filter: *oldFilterFlag + *newFilterFlag,
|
||||||
|
Verbose: *oldVerboseFlag || *newVerboseFlag,
|
||||||
|
Stream: *oldStreamFlag || *newStreamFlag,
|
||||||
|
Benchmark: *oldBenchFlag || *newBenchFlag,
|
||||||
|
BenchmarkTime: benchTime,
|
||||||
|
BenchmarkMem: *newBenchMem,
|
||||||
|
KeepWorkDir: *oldWorkFlag || *newWorkFlag,
|
||||||
|
}
|
||||||
|
if *checkTimeout != "" {
|
||||||
|
timeout, err := time.ParseDuration(*checkTimeout)
|
||||||
|
if err != nil {
|
||||||
|
testingT.Fatalf("error parsing specified timeout flag: %v", err)
|
||||||
|
}
|
||||||
|
conf.CheckTimeout = timeout
|
||||||
|
}
|
||||||
|
if *oldListFlag || *newListFlag {
|
||||||
|
w := bufio.NewWriter(os.Stdout)
|
||||||
|
for _, name := range ListAll(conf) {
|
||||||
|
fmt.Fprintln(w, name)
|
||||||
|
}
|
||||||
|
w.Flush()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
result := RunAll(conf)
|
||||||
|
println(result.String())
|
||||||
|
if !result.Passed() {
|
||||||
|
testingT.Fail()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RunAll runs all test suites registered with the Suite function, using the
|
||||||
|
// provided run configuration.
|
||||||
|
func RunAll(runConf *RunConf) *Result {
|
||||||
|
result := Result{}
|
||||||
|
for _, suite := range allSuites {
|
||||||
|
result.Add(Run(suite, runConf))
|
||||||
|
}
|
||||||
|
return &result
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run runs the provided test suite using the provided run configuration.
|
||||||
|
func Run(suite interface{}, runConf *RunConf) *Result {
|
||||||
|
runner := newSuiteRunner(suite, runConf)
|
||||||
|
return runner.run()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListAll returns the names of all the test functions registered with the
|
||||||
|
// Suite function that will be run with the provided run configuration.
|
||||||
|
func ListAll(runConf *RunConf) []string {
|
||||||
|
var names []string
|
||||||
|
for _, suite := range allSuites {
|
||||||
|
names = append(names, List(suite, runConf)...)
|
||||||
|
}
|
||||||
|
return names
|
||||||
|
}
|
||||||
|
|
||||||
|
// List returns the names of the test functions in the given
|
||||||
|
// suite that will be run with the provided run configuration.
|
||||||
|
func List(suite interface{}, runConf *RunConf) []string {
|
||||||
|
var names []string
|
||||||
|
runner := newSuiteRunner(suite, runConf)
|
||||||
|
for _, t := range runner.tests {
|
||||||
|
names = append(names, t.String())
|
||||||
|
}
|
||||||
|
return names
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// Result methods.
|
||||||
|
|
||||||
|
func (r *Result) Add(other *Result) {
|
||||||
|
r.Succeeded += other.Succeeded
|
||||||
|
r.Skipped += other.Skipped
|
||||||
|
r.Failed += other.Failed
|
||||||
|
r.Panicked += other.Panicked
|
||||||
|
r.FixturePanicked += other.FixturePanicked
|
||||||
|
r.ExpectedFailures += other.ExpectedFailures
|
||||||
|
r.Missed += other.Missed
|
||||||
|
if r.WorkDir != "" && other.WorkDir != "" {
|
||||||
|
r.WorkDir += ":" + other.WorkDir
|
||||||
|
} else if other.WorkDir != "" {
|
||||||
|
r.WorkDir = other.WorkDir
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Result) Passed() bool {
|
||||||
|
return (r.Failed == 0 && r.Panicked == 0 &&
|
||||||
|
r.FixturePanicked == 0 && r.Missed == 0 &&
|
||||||
|
r.RunError == nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Result) String() string {
|
||||||
|
if r.RunError != nil {
|
||||||
|
return "ERROR: " + r.RunError.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
var value string
|
||||||
|
if r.Failed == 0 && r.Panicked == 0 && r.FixturePanicked == 0 &&
|
||||||
|
r.Missed == 0 {
|
||||||
|
value = "OK: "
|
||||||
|
} else {
|
||||||
|
value = "OOPS: "
|
||||||
|
}
|
||||||
|
value += fmt.Sprintf("%d passed", r.Succeeded)
|
||||||
|
if r.Skipped != 0 {
|
||||||
|
value += fmt.Sprintf(", %d skipped", r.Skipped)
|
||||||
|
}
|
||||||
|
if r.ExpectedFailures != 0 {
|
||||||
|
value += fmt.Sprintf(", %d expected failures", r.ExpectedFailures)
|
||||||
|
}
|
||||||
|
if r.Failed != 0 {
|
||||||
|
value += fmt.Sprintf(", %d FAILED", r.Failed)
|
||||||
|
}
|
||||||
|
if r.Panicked != 0 {
|
||||||
|
value += fmt.Sprintf(", %d PANICKED", r.Panicked)
|
||||||
|
}
|
||||||
|
if r.FixturePanicked != 0 {
|
||||||
|
value += fmt.Sprintf(", %d FIXTURE-PANICKED", r.FixturePanicked)
|
||||||
|
}
|
||||||
|
if r.Missed != 0 {
|
||||||
|
value += fmt.Sprintf(", %d MISSED", r.Missed)
|
||||||
|
}
|
||||||
|
if r.WorkDir != "" {
|
||||||
|
value += "\nWORK=" + r.WorkDir
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}
|
2
vendor/src/github.com/vdemeester/shakers/.gitignore
vendored
Normal file
2
vendor/src/github.com/vdemeester/shakers/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
vendor
|
||||||
|
*.test
|
16
vendor/src/github.com/vdemeester/shakers/Dockerfile
vendored
Normal file
16
vendor/src/github.com/vdemeester/shakers/Dockerfile
vendored
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
FROM golang:1.5
|
||||||
|
|
||||||
|
RUN go get golang.org/x/tools/cmd/cover
|
||||||
|
RUN go get github.com/golang/lint/golint
|
||||||
|
RUN go get golang.org/x/tools/cmd/vet
|
||||||
|
RUN go get github.com/Masterminds/glide
|
||||||
|
|
||||||
|
WORKDIR /go/src/github.com/vdemeester/shakers
|
||||||
|
|
||||||
|
# enable GO15VENDOREXPERIMENT
|
||||||
|
ENV GO15VENDOREXPERIMENT 1
|
||||||
|
|
||||||
|
COPY glide.yaml glide.yaml
|
||||||
|
RUN glide up
|
||||||
|
|
||||||
|
COPY . /go/src/github.com/vdemeester/shakers
|
191
vendor/src/github.com/vdemeester/shakers/LICENSE
vendored
Normal file
191
vendor/src/github.com/vdemeester/shakers/LICENSE
vendored
Normal file
|
@ -0,0 +1,191 @@
|
||||||
|
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
https://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
Copyright 2015-2016 Vincent Demeester
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
https://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.
|
37
vendor/src/github.com/vdemeester/shakers/Makefile
vendored
Normal file
37
vendor/src/github.com/vdemeester/shakers/Makefile
vendored
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
.PHONY: all
|
||||||
|
|
||||||
|
SHAKERS_MOUNT := $(if $(BIND_DIR),-v "$(CURDIR)/$(BIND_DIR):/go/src/github.com/vdemeester/shakers/$(BIND_DIR)")
|
||||||
|
|
||||||
|
GIT_BRANCH := $(shell git rev-parse --abbrev-ref HEAD 2>/dev/null)
|
||||||
|
SHAKERS_DEV_IMAGE := shakers-dev$(if $(GIT_BRANCH),:$(GIT_BRANCH))
|
||||||
|
|
||||||
|
DOCKER_RUN_SHAKERS := docker run $(if $(CIRCLECI),,--rm) -it $(SHAKERS_ENVS) $(SHAKERS_MOUNT) "$(SHAKERS_DEV_IMAGE)"
|
||||||
|
|
||||||
|
print-%: ; @echo $*=$($*)
|
||||||
|
|
||||||
|
default: all
|
||||||
|
|
||||||
|
all: build
|
||||||
|
$(DOCKER_RUN_SHAKERS) ./script/make.sh
|
||||||
|
|
||||||
|
test-unit: build
|
||||||
|
$(DOCKER_RUN_SHAKERS) ./script/make.sh test-unit
|
||||||
|
|
||||||
|
validate: build
|
||||||
|
$(DOCKER_RUN_SHAKERS) ./script/make.sh validate-gofmt validate-golint validate-govet
|
||||||
|
|
||||||
|
validate-govet: build
|
||||||
|
$(DOCKER_RUN_SHAKERS) ./script/make.sh validate-govet
|
||||||
|
|
||||||
|
validate-golint: build
|
||||||
|
$(DOCKER_RUN_SHAKERS) ./script/make.sh validate-golint
|
||||||
|
|
||||||
|
validate-gofmt: build
|
||||||
|
$(DOCKER_RUN_SHAKERS) ./script/make.sh validate-gofmt
|
||||||
|
|
||||||
|
build:
|
||||||
|
docker build -t "$(SHAKERS_DEV_IMAGE)" .
|
||||||
|
|
||||||
|
shell: build
|
||||||
|
$(DOCKER_RUN_SHAKERS) /bin/bash
|
||||||
|
|
30
vendor/src/github.com/vdemeester/shakers/README.md
vendored
Normal file
30
vendor/src/github.com/vdemeester/shakers/README.md
vendored
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
# Shakers
|
||||||
|
🐹 + 🐙 = 😽 [![Circle CI](https://circleci.com/gh/vdemeester/shakers.svg?style=svg)](https://circleci.com/gh/vdemeester/shakers)
|
||||||
|
|
||||||
|
A collection of `go-check` Checkers to ease the use of it.
|
||||||
|
|
||||||
|
## Building and testing it
|
||||||
|
|
||||||
|
You need either [docker](https://github.com/docker/docker), or `go`
|
||||||
|
and `glide` in order to build and test shakers.
|
||||||
|
|
||||||
|
### Using Docker and Makefile
|
||||||
|
|
||||||
|
You need to run the ``test-unit`` target.
|
||||||
|
```bash
|
||||||
|
$ make test-unit
|
||||||
|
docker build -t "shakers-dev:master" .
|
||||||
|
# […]
|
||||||
|
docker run --rm -it "shakers-dev:master" ./script/make.sh test-unit
|
||||||
|
---> Making bundle: test-unit (in .)
|
||||||
|
+ go test -cover -coverprofile=cover.out .
|
||||||
|
ok github.com/vdemeester/shakers 0.015s coverage: 96.0% of statements
|
||||||
|
|
||||||
|
Test success
|
||||||
|
```
|
||||||
|
|
||||||
|
### Using glide and `GO15VENDOREXPERIMENT`
|
||||||
|
|
||||||
|
- Get the dependencies with `glide up` (or use `go get` but you have no garantuees over the version of the dependencies)
|
||||||
|
- If you're using glide (and not standard `go get`) export `GO15VENDOREXPERIMENT` with `export GO15VENDOREXPERIMENT=1`
|
||||||
|
- Run tests with `go test .`
|
46
vendor/src/github.com/vdemeester/shakers/bool.go
vendored
Normal file
46
vendor/src/github.com/vdemeester/shakers/bool.go
vendored
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
package shakers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/go-check/check"
|
||||||
|
)
|
||||||
|
|
||||||
|
// True checker verifies the obtained value is true
|
||||||
|
//
|
||||||
|
// c.Assert(myBool, True)
|
||||||
|
//
|
||||||
|
var True check.Checker = &boolChecker{
|
||||||
|
&check.CheckerInfo{
|
||||||
|
Name: "True",
|
||||||
|
Params: []string{"obtained"},
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
}
|
||||||
|
|
||||||
|
// False checker verifies the obtained value is false
|
||||||
|
//
|
||||||
|
// c.Assert(myBool, False)
|
||||||
|
//
|
||||||
|
var False check.Checker = &boolChecker{
|
||||||
|
&check.CheckerInfo{
|
||||||
|
Name: "False",
|
||||||
|
Params: []string{"obtained"},
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
}
|
||||||
|
|
||||||
|
type boolChecker struct {
|
||||||
|
*check.CheckerInfo
|
||||||
|
expected bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (checker *boolChecker) Check(params []interface{}, names []string) (bool, string) {
|
||||||
|
return is(checker.expected, params[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
func is(expected bool, obtained interface{}) (bool, string) {
|
||||||
|
obtainedBool, ok := obtained.(bool)
|
||||||
|
if !ok {
|
||||||
|
return false, "obtained value must be a bool."
|
||||||
|
}
|
||||||
|
return obtainedBool == expected, ""
|
||||||
|
}
|
11
vendor/src/github.com/vdemeester/shakers/circle.yml
vendored
Normal file
11
vendor/src/github.com/vdemeester/shakers/circle.yml
vendored
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
machine:
|
||||||
|
services:
|
||||||
|
- docker
|
||||||
|
|
||||||
|
dependencies:
|
||||||
|
override:
|
||||||
|
- make validate
|
||||||
|
|
||||||
|
test:
|
||||||
|
override:
|
||||||
|
- make test-unit
|
310
vendor/src/github.com/vdemeester/shakers/common.go
vendored
Normal file
310
vendor/src/github.com/vdemeester/shakers/common.go
vendored
Normal file
|
@ -0,0 +1,310 @@
|
||||||
|
package shakers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-check/check"
|
||||||
|
)
|
||||||
|
|
||||||
|
// As a commodity, we bring all check.Checker variables into the current namespace to avoid having
|
||||||
|
// to think about check.X versus checker.X.
|
||||||
|
var (
|
||||||
|
DeepEquals = check.DeepEquals
|
||||||
|
ErrorMatches = check.ErrorMatches
|
||||||
|
FitsTypeOf = check.FitsTypeOf
|
||||||
|
HasLen = check.HasLen
|
||||||
|
Implements = check.Implements
|
||||||
|
IsNil = check.IsNil
|
||||||
|
Matches = check.Matches
|
||||||
|
Not = check.Not
|
||||||
|
NotNil = check.NotNil
|
||||||
|
PanicMatches = check.PanicMatches
|
||||||
|
Panics = check.Panics
|
||||||
|
)
|
||||||
|
|
||||||
|
// Equaler is an interface implemented if the type has a Equal method.
|
||||||
|
// This is used to compare struct using shakers.Equals.
|
||||||
|
type Equaler interface {
|
||||||
|
Equal(Equaler) bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equals checker verifies the obtained value is equal to the specified one.
|
||||||
|
// It's is smart in a wait that it supports several *types* (built-in, Equaler,
|
||||||
|
// time.Time)
|
||||||
|
//
|
||||||
|
// c.Assert(myStruct, Equals, aStruct, check.Commentf("bouuuhh"))
|
||||||
|
// c.Assert(myTime, Equals, aTime, check.Commentf("bouuuhh"))
|
||||||
|
//
|
||||||
|
var Equals check.Checker = &equalChecker{
|
||||||
|
&check.CheckerInfo{
|
||||||
|
Name: "Equals",
|
||||||
|
Params: []string{"obtained", "expected"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
type equalChecker struct {
|
||||||
|
*check.CheckerInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
func (checker *equalChecker) Check(params []interface{}, names []string) (bool, string) {
|
||||||
|
return isEqual(params[0], params[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
func isEqual(obtained, expected interface{}) (bool, string) {
|
||||||
|
switch obtained.(type) {
|
||||||
|
case time.Time:
|
||||||
|
return timeEquals(obtained, expected)
|
||||||
|
case Equaler:
|
||||||
|
return equalerEquals(obtained, expected)
|
||||||
|
default:
|
||||||
|
if reflect.TypeOf(obtained) != reflect.TypeOf(expected) {
|
||||||
|
return false, "obtained value and expected value have not the same type."
|
||||||
|
}
|
||||||
|
return obtained == expected, ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func equalerEquals(obtained, expected interface{}) (bool, string) {
|
||||||
|
expectedEqualer, ok := expected.(Equaler)
|
||||||
|
if !ok {
|
||||||
|
return false, "expected value must be an Equaler - implementing Equal(Equaler)."
|
||||||
|
}
|
||||||
|
obtainedEqualer, ok := obtained.(Equaler)
|
||||||
|
if !ok {
|
||||||
|
return false, "obtained value must be an Equaler - implementing Equal(Equaler)."
|
||||||
|
}
|
||||||
|
return obtainedEqualer.Equal(expectedEqualer), ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// GreaterThan checker verifies the obtained value is greater than the specified one.
|
||||||
|
// It's is smart in a wait that it supports several *types* (built-in, time.Time)
|
||||||
|
//
|
||||||
|
// c.Assert(myTime, GreaterThan, aTime, check.Commentf("bouuuhh"))
|
||||||
|
// c.Assert(myInt, GreaterThan, 2, check.Commentf("bouuuhh"))
|
||||||
|
//
|
||||||
|
var GreaterThan check.Checker = &greaterThanChecker{
|
||||||
|
&check.CheckerInfo{
|
||||||
|
Name: "GreaterThan",
|
||||||
|
Params: []string{"obtained", "expected"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
type greaterThanChecker struct {
|
||||||
|
*check.CheckerInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
func (checker *greaterThanChecker) Check(params []interface{}, names []string) (bool, string) {
|
||||||
|
return greaterThan(params[0], params[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
func greaterThan(obtained, expected interface{}) (bool, string) {
|
||||||
|
if _, ok := obtained.(time.Time); ok {
|
||||||
|
return isAfter(obtained, expected)
|
||||||
|
}
|
||||||
|
if reflect.TypeOf(obtained) != reflect.TypeOf(expected) {
|
||||||
|
return false, "obtained value and expected value have not the same type."
|
||||||
|
}
|
||||||
|
switch v := obtained.(type) {
|
||||||
|
case float32:
|
||||||
|
return v > expected.(float32), ""
|
||||||
|
case float64:
|
||||||
|
return v > expected.(float64), ""
|
||||||
|
case int:
|
||||||
|
return v > expected.(int), ""
|
||||||
|
case int8:
|
||||||
|
return v > expected.(int8), ""
|
||||||
|
case int16:
|
||||||
|
return v > expected.(int16), ""
|
||||||
|
case int32:
|
||||||
|
return v > expected.(int32), ""
|
||||||
|
case int64:
|
||||||
|
return v > expected.(int64), ""
|
||||||
|
case uint:
|
||||||
|
return v > expected.(uint), ""
|
||||||
|
case uint8:
|
||||||
|
return v > expected.(uint8), ""
|
||||||
|
case uint16:
|
||||||
|
return v > expected.(uint16), ""
|
||||||
|
case uint32:
|
||||||
|
return v > expected.(uint32), ""
|
||||||
|
case uint64:
|
||||||
|
return v > expected.(uint64), ""
|
||||||
|
default:
|
||||||
|
return false, "obtained value type not supported."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GreaterOrEqualThan checker verifies the obtained value is greater or equal than the specified one.
|
||||||
|
// It's is smart in a wait that it supports several *types* (built-in, time.Time)
|
||||||
|
//
|
||||||
|
// c.Assert(myTime, GreaterOrEqualThan, aTime, check.Commentf("bouuuhh"))
|
||||||
|
// c.Assert(myInt, GreaterOrEqualThan, 2, check.Commentf("bouuuhh"))
|
||||||
|
//
|
||||||
|
var GreaterOrEqualThan check.Checker = &greaterOrEqualThanChecker{
|
||||||
|
&check.CheckerInfo{
|
||||||
|
Name: "GreaterOrEqualThan",
|
||||||
|
Params: []string{"obtained", "expected"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
type greaterOrEqualThanChecker struct {
|
||||||
|
*check.CheckerInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
func (checker *greaterOrEqualThanChecker) Check(params []interface{}, names []string) (bool, string) {
|
||||||
|
return greaterOrEqualThan(params[0], params[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
func greaterOrEqualThan(obtained, expected interface{}) (bool, string) {
|
||||||
|
if _, ok := obtained.(time.Time); ok {
|
||||||
|
return isAfter(obtained, expected)
|
||||||
|
}
|
||||||
|
if reflect.TypeOf(obtained) != reflect.TypeOf(expected) {
|
||||||
|
return false, "obtained value and expected value have not the same type."
|
||||||
|
}
|
||||||
|
switch v := obtained.(type) {
|
||||||
|
case float32:
|
||||||
|
return v >= expected.(float32), ""
|
||||||
|
case float64:
|
||||||
|
return v >= expected.(float64), ""
|
||||||
|
case int:
|
||||||
|
return v >= expected.(int), ""
|
||||||
|
case int8:
|
||||||
|
return v >= expected.(int8), ""
|
||||||
|
case int16:
|
||||||
|
return v >= expected.(int16), ""
|
||||||
|
case int32:
|
||||||
|
return v >= expected.(int32), ""
|
||||||
|
case int64:
|
||||||
|
return v >= expected.(int64), ""
|
||||||
|
case uint:
|
||||||
|
return v >= expected.(uint), ""
|
||||||
|
case uint8:
|
||||||
|
return v >= expected.(uint8), ""
|
||||||
|
case uint16:
|
||||||
|
return v >= expected.(uint16), ""
|
||||||
|
case uint32:
|
||||||
|
return v >= expected.(uint32), ""
|
||||||
|
case uint64:
|
||||||
|
return v >= expected.(uint64), ""
|
||||||
|
default:
|
||||||
|
return false, "obtained value type not supported."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// LessThan checker verifies the obtained value is less than the specified one.
|
||||||
|
// It's is smart in a wait that it supports several *types* (built-in, time.Time)
|
||||||
|
//
|
||||||
|
// c.Assert(myTime, LessThan, aTime, check.Commentf("bouuuhh"))
|
||||||
|
// c.Assert(myInt, LessThan, 2, check.Commentf("bouuuhh"))
|
||||||
|
//
|
||||||
|
var LessThan check.Checker = &lessThanChecker{
|
||||||
|
&check.CheckerInfo{
|
||||||
|
Name: "LessThan",
|
||||||
|
Params: []string{"obtained", "expected"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
type lessThanChecker struct {
|
||||||
|
*check.CheckerInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
func (checker *lessThanChecker) Check(params []interface{}, names []string) (bool, string) {
|
||||||
|
return lessThan(params[0], params[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
func lessThan(obtained, expected interface{}) (bool, string) {
|
||||||
|
if _, ok := obtained.(time.Time); ok {
|
||||||
|
return isBefore(obtained, expected)
|
||||||
|
}
|
||||||
|
if reflect.TypeOf(obtained) != reflect.TypeOf(expected) {
|
||||||
|
return false, "obtained value and expected value have not the same type."
|
||||||
|
}
|
||||||
|
switch v := obtained.(type) {
|
||||||
|
case float32:
|
||||||
|
return v < expected.(float32), ""
|
||||||
|
case float64:
|
||||||
|
return v < expected.(float64), ""
|
||||||
|
case int:
|
||||||
|
return v < expected.(int), ""
|
||||||
|
case int8:
|
||||||
|
return v < expected.(int8), ""
|
||||||
|
case int16:
|
||||||
|
return v < expected.(int16), ""
|
||||||
|
case int32:
|
||||||
|
return v < expected.(int32), ""
|
||||||
|
case int64:
|
||||||
|
return v < expected.(int64), ""
|
||||||
|
case uint:
|
||||||
|
return v < expected.(uint), ""
|
||||||
|
case uint8:
|
||||||
|
return v < expected.(uint8), ""
|
||||||
|
case uint16:
|
||||||
|
return v < expected.(uint16), ""
|
||||||
|
case uint32:
|
||||||
|
return v < expected.(uint32), ""
|
||||||
|
case uint64:
|
||||||
|
return v < expected.(uint64), ""
|
||||||
|
default:
|
||||||
|
return false, "obtained value type not supported."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// LessOrEqualThan checker verifies the obtained value is less or equal than the specified one.
|
||||||
|
// It's is smart in a wait that it supports several *types* (built-in, time.Time)
|
||||||
|
//
|
||||||
|
// c.Assert(myTime, LessThan, aTime, check.Commentf("bouuuhh"))
|
||||||
|
// c.Assert(myInt, LessThan, 2, check.Commentf("bouuuhh"))
|
||||||
|
//
|
||||||
|
var LessOrEqualThan check.Checker = &lessOrEqualThanChecker{
|
||||||
|
&check.CheckerInfo{
|
||||||
|
Name: "LessOrEqualThan",
|
||||||
|
Params: []string{"obtained", "expected"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
type lessOrEqualThanChecker struct {
|
||||||
|
*check.CheckerInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
func (checker *lessOrEqualThanChecker) Check(params []interface{}, names []string) (bool, string) {
|
||||||
|
return lessOrEqualThan(params[0], params[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
func lessOrEqualThan(obtained, expected interface{}) (bool, string) {
|
||||||
|
if _, ok := obtained.(time.Time); ok {
|
||||||
|
return isBefore(obtained, expected)
|
||||||
|
}
|
||||||
|
if reflect.TypeOf(obtained) != reflect.TypeOf(expected) {
|
||||||
|
return false, "obtained value and expected value have not the same type."
|
||||||
|
}
|
||||||
|
switch v := obtained.(type) {
|
||||||
|
case float32:
|
||||||
|
return v <= expected.(float32), ""
|
||||||
|
case float64:
|
||||||
|
return v <= expected.(float64), ""
|
||||||
|
case int:
|
||||||
|
return v <= expected.(int), ""
|
||||||
|
case int8:
|
||||||
|
return v <= expected.(int8), ""
|
||||||
|
case int16:
|
||||||
|
return v <= expected.(int16), ""
|
||||||
|
case int32:
|
||||||
|
return v <= expected.(int32), ""
|
||||||
|
case int64:
|
||||||
|
return v <= expected.(int64), ""
|
||||||
|
case uint:
|
||||||
|
return v <= expected.(uint), ""
|
||||||
|
case uint8:
|
||||||
|
return v <= expected.(uint8), ""
|
||||||
|
case uint16:
|
||||||
|
return v <= expected.(uint16), ""
|
||||||
|
case uint32:
|
||||||
|
return v <= expected.(uint32), ""
|
||||||
|
case uint64:
|
||||||
|
return v <= expected.(uint64), ""
|
||||||
|
default:
|
||||||
|
return false, "obtained value type not supported."
|
||||||
|
}
|
||||||
|
}
|
4
vendor/src/github.com/vdemeester/shakers/glide.yaml
vendored
Normal file
4
vendor/src/github.com/vdemeester/shakers/glide.yaml
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
package: main
|
||||||
|
import:
|
||||||
|
- package: github.com/go-check/check
|
||||||
|
ref: 11d3bc7aa68e238947792f30573146a3231fc0f1
|
168
vendor/src/github.com/vdemeester/shakers/string.go
vendored
Normal file
168
vendor/src/github.com/vdemeester/shakers/string.go
vendored
Normal file
|
@ -0,0 +1,168 @@
|
||||||
|
// Package shakers provide some checker implementation the go-check.Checker interface.
|
||||||
|
package shakers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/go-check/check"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Contains checker verifies that obtained value contains a substring.
|
||||||
|
var Contains check.Checker = &substringChecker{
|
||||||
|
&check.CheckerInfo{
|
||||||
|
Name: "Contains",
|
||||||
|
Params: []string{"obtained", "substring"},
|
||||||
|
},
|
||||||
|
strings.Contains,
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContainsAny checker verifies that any Unicode code points in chars
|
||||||
|
// are in the obtained string.
|
||||||
|
var ContainsAny check.Checker = &substringChecker{
|
||||||
|
&check.CheckerInfo{
|
||||||
|
Name: "ContainsAny",
|
||||||
|
Params: []string{"obtained", "chars"},
|
||||||
|
},
|
||||||
|
strings.ContainsAny,
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasPrefix checker verifies that obtained value has the specified substring as prefix
|
||||||
|
var HasPrefix check.Checker = &substringChecker{
|
||||||
|
&check.CheckerInfo{
|
||||||
|
Name: "HasPrefix",
|
||||||
|
Params: []string{"obtained", "prefix"},
|
||||||
|
},
|
||||||
|
strings.HasPrefix,
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasSuffix checker verifies that obtained value has the specified substring as prefix
|
||||||
|
var HasSuffix check.Checker = &substringChecker{
|
||||||
|
&check.CheckerInfo{
|
||||||
|
Name: "HasSuffix",
|
||||||
|
Params: []string{"obtained", "suffix"},
|
||||||
|
},
|
||||||
|
strings.HasSuffix,
|
||||||
|
}
|
||||||
|
|
||||||
|
// EqualFold checker verifies that obtained value is, interpreted as UTF-8 strings, are equal under Unicode case-folding.
|
||||||
|
var EqualFold check.Checker = &substringChecker{
|
||||||
|
&check.CheckerInfo{
|
||||||
|
Name: "EqualFold",
|
||||||
|
Params: []string{"obtained", "expected"},
|
||||||
|
},
|
||||||
|
strings.EqualFold,
|
||||||
|
}
|
||||||
|
|
||||||
|
type substringChecker struct {
|
||||||
|
*check.CheckerInfo
|
||||||
|
substringFunction func(string, string) bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (checker *substringChecker) Check(params []interface{}, names []string) (bool, string) {
|
||||||
|
obtained := params[0]
|
||||||
|
substring := params[1]
|
||||||
|
substringStr, ok := substring.(string)
|
||||||
|
if !ok {
|
||||||
|
return false, fmt.Sprintf("%s value must be a string.", names[1])
|
||||||
|
}
|
||||||
|
obtainedString, obtainedIsStr := obtained.(string)
|
||||||
|
if !obtainedIsStr {
|
||||||
|
if obtainedWithStringer, obtainedHasStringer := obtained.(fmt.Stringer); obtainedHasStringer {
|
||||||
|
obtainedString, obtainedIsStr = obtainedWithStringer.String(), true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if obtainedIsStr {
|
||||||
|
return checker.substringFunction(obtainedString, substringStr), ""
|
||||||
|
}
|
||||||
|
return false, "obtained value is not a string and has no .String()."
|
||||||
|
}
|
||||||
|
|
||||||
|
// IndexAny checker verifies that the index of the first instance of any Unicode code point from chars in the obtained value is equal to expected
|
||||||
|
var IndexAny check.Checker = &substringCountChecker{
|
||||||
|
&check.CheckerInfo{
|
||||||
|
Name: "IndexAny",
|
||||||
|
Params: []string{"obtained", "chars", "expected"},
|
||||||
|
},
|
||||||
|
strings.IndexAny,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Index checker verifies that the index of the first instance of sep in the obtained value is equal to expected
|
||||||
|
var Index check.Checker = &substringCountChecker{
|
||||||
|
&check.CheckerInfo{
|
||||||
|
Name: "Index",
|
||||||
|
Params: []string{"obtained", "sep", "expected"},
|
||||||
|
},
|
||||||
|
strings.Index,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Count checker verifies that obtained value has the specified number of non-overlapping instances of sep
|
||||||
|
var Count check.Checker = &substringCountChecker{
|
||||||
|
&check.CheckerInfo{
|
||||||
|
Name: "Count",
|
||||||
|
Params: []string{"obtained", "sep", "expected"},
|
||||||
|
},
|
||||||
|
strings.Count,
|
||||||
|
}
|
||||||
|
|
||||||
|
type substringCountChecker struct {
|
||||||
|
*check.CheckerInfo
|
||||||
|
substringFunction func(string, string) int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (checker *substringCountChecker) Check(params []interface{}, names []string) (bool, string) {
|
||||||
|
obtained := params[0]
|
||||||
|
substring := params[1]
|
||||||
|
expected := params[2]
|
||||||
|
substringStr, ok := substring.(string)
|
||||||
|
if !ok {
|
||||||
|
return false, fmt.Sprintf("%s value must be a string.", names[1])
|
||||||
|
}
|
||||||
|
obtainedString, obtainedIsStr := obtained.(string)
|
||||||
|
if !obtainedIsStr {
|
||||||
|
if obtainedWithStringer, obtainedHasStringer := obtained.(fmt.Stringer); obtainedHasStringer {
|
||||||
|
obtainedString, obtainedIsStr = obtainedWithStringer.String(), true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if obtainedIsStr {
|
||||||
|
return checker.substringFunction(obtainedString, substringStr) == expected, ""
|
||||||
|
}
|
||||||
|
return false, "obtained value is not a string and has no .String()."
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsLower checker verifies that the obtained value is in lower case
|
||||||
|
var IsLower check.Checker = &stringTransformChecker{
|
||||||
|
&check.CheckerInfo{
|
||||||
|
Name: "IsLower",
|
||||||
|
Params: []string{"obtained"},
|
||||||
|
},
|
||||||
|
strings.ToLower,
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsUpper checker verifies that the obtained value is in lower case
|
||||||
|
var IsUpper check.Checker = &stringTransformChecker{
|
||||||
|
&check.CheckerInfo{
|
||||||
|
Name: "IsUpper",
|
||||||
|
Params: []string{"obtained"},
|
||||||
|
},
|
||||||
|
strings.ToUpper,
|
||||||
|
}
|
||||||
|
|
||||||
|
type stringTransformChecker struct {
|
||||||
|
*check.CheckerInfo
|
||||||
|
stringFunction func(string) string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (checker *stringTransformChecker) Check(params []interface{}, names []string) (bool, string) {
|
||||||
|
obtained := params[0]
|
||||||
|
obtainedString, obtainedIsStr := obtained.(string)
|
||||||
|
if !obtainedIsStr {
|
||||||
|
if obtainedWithStringer, obtainedHasStringer := obtained.(fmt.Stringer); obtainedHasStringer {
|
||||||
|
obtainedString, obtainedIsStr = obtainedWithStringer.String(), true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if obtainedIsStr {
|
||||||
|
return checker.stringFunction(obtainedString) == obtainedString, ""
|
||||||
|
}
|
||||||
|
return false, "obtained value is not a string and has no .String()."
|
||||||
|
}
|
234
vendor/src/github.com/vdemeester/shakers/time.go
vendored
Normal file
234
vendor/src/github.com/vdemeester/shakers/time.go
vendored
Normal file
|
@ -0,0 +1,234 @@
|
||||||
|
package shakers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-check/check"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Default format when parsing (in addition to RFC and default time formats..)
|
||||||
|
const shortForm = "2006-01-02"
|
||||||
|
|
||||||
|
// IsBefore checker verifies the specified value is before the specified time.
|
||||||
|
// It is exclusive.
|
||||||
|
//
|
||||||
|
// c.Assert(myTime, IsBefore, theTime, check.Commentf("bouuuhhh"))
|
||||||
|
//
|
||||||
|
var IsBefore check.Checker = &isBeforeChecker{
|
||||||
|
&check.CheckerInfo{
|
||||||
|
Name: "IsBefore",
|
||||||
|
Params: []string{"obtained", "expected"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
type isBeforeChecker struct {
|
||||||
|
*check.CheckerInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
func (checker *isBeforeChecker) Check(params []interface{}, names []string) (bool, string) {
|
||||||
|
return isBefore(params[0], params[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
func isBefore(value, t interface{}) (bool, string) {
|
||||||
|
tTime, ok := parseTime(t)
|
||||||
|
if !ok {
|
||||||
|
return false, "expected must be a Time struct, or parseable."
|
||||||
|
}
|
||||||
|
valueTime, valueIsTime := parseTime(value)
|
||||||
|
if valueIsTime {
|
||||||
|
return valueTime.Before(tTime), ""
|
||||||
|
}
|
||||||
|
return false, "obtained value is not a time.Time struct or parseable as a time."
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsAfter checker verifies the specified value is before the specified time.
|
||||||
|
// It is exclusive.
|
||||||
|
//
|
||||||
|
// c.Assert(myTime, IsAfter, theTime, check.Commentf("bouuuhhh"))
|
||||||
|
//
|
||||||
|
var IsAfter check.Checker = &isAfterChecker{
|
||||||
|
&check.CheckerInfo{
|
||||||
|
Name: "IsAfter",
|
||||||
|
Params: []string{"obtained", "expected"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
type isAfterChecker struct {
|
||||||
|
*check.CheckerInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
func (checker *isAfterChecker) Check(params []interface{}, names []string) (bool, string) {
|
||||||
|
return isAfter(params[0], params[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
func isAfter(value, t interface{}) (bool, string) {
|
||||||
|
tTime, ok := parseTime(t)
|
||||||
|
if !ok {
|
||||||
|
return false, "expected must be a Time struct, or parseable."
|
||||||
|
}
|
||||||
|
valueTime, valueIsTime := parseTime(value)
|
||||||
|
if valueIsTime {
|
||||||
|
return valueTime.After(tTime), ""
|
||||||
|
}
|
||||||
|
return false, "obtained value is not a time.Time struct or parseable as a time."
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsBetween checker verifies the specified time is between the specified start
|
||||||
|
// and end. It's exclusive so if the specified time is at the tip of the interval.
|
||||||
|
//
|
||||||
|
// c.Assert(myTime, IsBetween, startTime, endTime, check.Commentf("bouuuhhh"))
|
||||||
|
//
|
||||||
|
var IsBetween check.Checker = &isBetweenChecker{
|
||||||
|
&check.CheckerInfo{
|
||||||
|
Name: "IsBetween",
|
||||||
|
Params: []string{"obtained", "start", "end"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
type isBetweenChecker struct {
|
||||||
|
*check.CheckerInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
func (checker *isBetweenChecker) Check(params []interface{}, names []string) (bool, string) {
|
||||||
|
return isBetween(params[0], params[1], params[2])
|
||||||
|
}
|
||||||
|
|
||||||
|
func isBetween(value, start, end interface{}) (bool, string) {
|
||||||
|
startTime, ok := parseTime(start)
|
||||||
|
if !ok {
|
||||||
|
return false, "start must be a Time struct, or parseable."
|
||||||
|
}
|
||||||
|
endTime, ok := parseTime(end)
|
||||||
|
if !ok {
|
||||||
|
return false, "end must be a Time struct, or parseable."
|
||||||
|
}
|
||||||
|
valueTime, valueIsTime := parseTime(value)
|
||||||
|
if valueIsTime {
|
||||||
|
return valueTime.After(startTime) && valueTime.Before(endTime), ""
|
||||||
|
}
|
||||||
|
return false, "obtained value is not a time.Time struct or parseable as a time."
|
||||||
|
}
|
||||||
|
|
||||||
|
// TimeEquals checker verifies the specified time is the equal to the expected
|
||||||
|
// time.
|
||||||
|
//
|
||||||
|
// c.Assert(myTime, TimeEquals, expected, check.Commentf("bouhhh"))
|
||||||
|
//
|
||||||
|
// It's possible to ignore some part of the time (like hours, minutes, etc..) using
|
||||||
|
// the TimeIgnore checker with it.
|
||||||
|
//
|
||||||
|
// c.Assert(myTime, TimeIgnore(TimeEquals, time.Hour), expected, check.Commentf("... bouh.."))
|
||||||
|
//
|
||||||
|
var TimeEquals check.Checker = &timeEqualsChecker{
|
||||||
|
&check.CheckerInfo{
|
||||||
|
Name: "TimeEquals",
|
||||||
|
Params: []string{"obtained", "expected"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
type timeEqualsChecker struct {
|
||||||
|
*check.CheckerInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
func (checker *timeEqualsChecker) Check(params []interface{}, names []string) (bool, string) {
|
||||||
|
return timeEquals(params[0], params[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
func timeEquals(obtained, expected interface{}) (bool, string) {
|
||||||
|
expectedTime, ok := parseTime(expected)
|
||||||
|
if !ok {
|
||||||
|
return false, "expected must be a Time struct, or parseable."
|
||||||
|
}
|
||||||
|
valueTime, valueIsTime := parseTime(obtained)
|
||||||
|
if valueIsTime {
|
||||||
|
return valueTime.Equal(expectedTime), ""
|
||||||
|
}
|
||||||
|
return false, "obtained value is not a time.Time struct or parseable as a time."
|
||||||
|
}
|
||||||
|
|
||||||
|
// TimeIgnore checker will ignore some part of the time on the encapsulated checker.
|
||||||
|
//
|
||||||
|
// c.Assert(myTime, TimeIgnore(IsBetween, time.Second), start, end)
|
||||||
|
//
|
||||||
|
// FIXME use interface{} for ignore (to enable "Month", ..
|
||||||
|
func TimeIgnore(checker check.Checker, ignore time.Duration) check.Checker {
|
||||||
|
return &timeIgnoreChecker{
|
||||||
|
sub: checker,
|
||||||
|
ignore: ignore,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type timeIgnoreChecker struct {
|
||||||
|
sub check.Checker
|
||||||
|
ignore time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
func (checker *timeIgnoreChecker) Info() *check.CheckerInfo {
|
||||||
|
info := *checker.sub.Info()
|
||||||
|
info.Name = fmt.Sprintf("TimeIgnore(%s, %v)", info.Name, checker.ignore)
|
||||||
|
return &info
|
||||||
|
}
|
||||||
|
|
||||||
|
func (checker *timeIgnoreChecker) Check(params []interface{}, names []string) (bool, string) {
|
||||||
|
// Naive implementation : all params are supposed to be date
|
||||||
|
mParams := make([]interface{}, len(params))
|
||||||
|
for index, param := range params {
|
||||||
|
paramTime, ok := parseTime(param)
|
||||||
|
if !ok {
|
||||||
|
return false, fmt.Sprintf("%s must be a Time struct, or parseable.", names[index])
|
||||||
|
}
|
||||||
|
year := paramTime.Year()
|
||||||
|
month := paramTime.Month()
|
||||||
|
day := paramTime.Day()
|
||||||
|
hour := paramTime.Hour()
|
||||||
|
min := paramTime.Minute()
|
||||||
|
sec := paramTime.Second()
|
||||||
|
nsec := paramTime.Nanosecond()
|
||||||
|
location := paramTime.Location()
|
||||||
|
switch checker.ignore {
|
||||||
|
case time.Hour:
|
||||||
|
hour = 0
|
||||||
|
fallthrough
|
||||||
|
case time.Minute:
|
||||||
|
min = 0
|
||||||
|
fallthrough
|
||||||
|
case time.Second:
|
||||||
|
sec = 0
|
||||||
|
fallthrough
|
||||||
|
case time.Millisecond:
|
||||||
|
fallthrough
|
||||||
|
case time.Microsecond:
|
||||||
|
fallthrough
|
||||||
|
case time.Nanosecond:
|
||||||
|
nsec = 0
|
||||||
|
}
|
||||||
|
mParams[index] = time.Date(year, month, day, hour, min, sec, nsec, location)
|
||||||
|
}
|
||||||
|
return checker.sub.Check(mParams, names)
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseTime(datetime interface{}) (time.Time, bool) {
|
||||||
|
switch datetime.(type) {
|
||||||
|
case time.Time:
|
||||||
|
return datetime.(time.Time), true
|
||||||
|
case string:
|
||||||
|
return parseTimeAsString(datetime.(string))
|
||||||
|
default:
|
||||||
|
if datetimeWithStr, ok := datetime.(fmt.Stringer); ok {
|
||||||
|
return parseTimeAsString(datetimeWithStr.String())
|
||||||
|
}
|
||||||
|
return time.Time{}, false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseTimeAsString(timeAsStr string) (time.Time, bool) {
|
||||||
|
forms := []string{shortForm, time.RFC3339, time.RFC3339Nano, time.RFC822, time.RFC822Z}
|
||||||
|
for _, form := range forms {
|
||||||
|
datetime, err := time.Parse(form, timeAsStr)
|
||||||
|
if err == nil {
|
||||||
|
return datetime, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return time.Time{}, false
|
||||||
|
}
|
Loading…
Reference in a new issue