Merge pull request #160 from mlaventure/integration-test
Integration test
This commit is contained in:
		
							
								
								
									
										29
									
								
								Dockerfile
									
									
									
									
									
								
							
							
						
						
									
										29
									
								
								Dockerfile
									
									
									
									
									
								
							| @@ -6,6 +6,9 @@ RUN apt-get update && apt-get install -y \ | ||||
| 	curl \ | ||||
| 	git \ | ||||
| 	make \ | ||||
| 	jq \ | ||||
| 	apparmor \ | ||||
| 	libapparmor-dev \ | ||||
| 	--no-install-recommends \ | ||||
| 	&& 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 GOPATH /go:/go/src/github.com/docker/containerd/vendor | ||||
|  | ||||
| WORKDIR /go/src/github.com/docker/containerd | ||||
|  | ||||
| # install golint/vet | ||||
| RUN go get github.com/golang/lint/golint \ | ||||
| 	&& 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 | ||||
|  | ||||
| WORKDIR /go/src/github.com/docker/containerd | ||||
|   | ||||
							
								
								
									
										23
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										23
									
								
								Makefile
									
									
									
									
									
								
							| @@ -1,5 +1,7 @@ | ||||
| BUILDTAGS= | ||||
|  | ||||
| PROJECT=github.com/docker/containerd | ||||
|  | ||||
| GIT_COMMIT := $(shell git rev-parse HEAD 2> /dev/null || true) | ||||
| GIT_BRANCH := $(shell git rev-parse --abbrev-ref HEAD 2> /dev/null) | ||||
|  | ||||
| @@ -13,8 +15,12 @@ ifeq ($(INTERACTIVE), 1) | ||||
| 	DOCKER_FLAGS += -t | ||||
| 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_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) | ||||
|  | ||||
| @@ -46,7 +52,13 @@ shim: bin | ||||
| shim-static: | ||||
| 	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)" . | ||||
|  | ||||
| dtest: dbuild | ||||
| @@ -68,7 +80,12 @@ shell: dbuild | ||||
| 	$(DOCKER_RUN) bash | ||||
|  | ||||
| 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 | ||||
|  | ||||
|   | ||||
| @@ -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) { | ||||
| 	processes, err := c.Processes() | ||||
| 	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 | ||||
| 	for _, p := range processes { | ||||
| @@ -148,7 +148,7 @@ func createAPIContainer(c runtime.Container, getPids bool) (*types.Container, er | ||||
| 	state := c.State() | ||||
| 	if getPids && (state == runtime.Running || state == runtime.Paused) { | ||||
| 		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{ | ||||
|   | ||||
| @@ -34,8 +34,10 @@ func setAppBefore(app *cli.App) { | ||||
| 	app.Before = func(context *cli.Context) error { | ||||
| 		if context.GlobalBool("debug") { | ||||
| 			logrus.SetLevel(logrus.DebugLevel) | ||||
| 			if err := debugMetrics(context.GlobalDuration("metrics-interval"), context.GlobalString("graphite-address")); err != nil { | ||||
| 				return err | ||||
| 			if context.GlobalDuration("metrics-interval") > 0 { | ||||
| 				if err := debugMetrics(context.GlobalDuration("metrics-interval"), context.GlobalString("graphite-address")); err != nil { | ||||
| 					return err | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		if err := checkLimits(); err != nil { | ||||
|   | ||||
| @@ -52,6 +52,7 @@ clean() { | ||||
| 	local packages=( | ||||
| 		"${PROJECT}/containerd" # package main | ||||
| 		"${PROJECT}/ctr" # package main | ||||
| 		"${PROJECT}/integration-test" # package main | ||||
| 	) | ||||
| 	local platforms=( linux/amd64 linux/386 windows/amd64 windows/386 darwin/amd64 ) | ||||
| 	local buildTagCombos=( | ||||
| @@ -77,8 +78,8 @@ clean() { | ||||
|  | ||||
| 	echo -n 'pruning unused packages, ' | ||||
| 	findArgs=( | ||||
| 		# This directory contains only .c and .h files which are necessary | ||||
| 		-path vendor/src/github.com/mattn/go-sqlite3/code | ||||
| 		# for some reason go list doesn't detect this as a dependency | ||||
| 		-path vendor/src/github.com/vdemeester/shakers | ||||
| 	) | ||||
| 	for import in "${imports[@]}"; do | ||||
| 		[ "${#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 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 | ||||
|   | ||||
							
								
								
									
										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/exec" | ||||
| 	"path/filepath" | ||||
| 	"strings" | ||||
| 	"syscall" | ||||
| 	"time" | ||||
|  | ||||
| @@ -218,7 +219,17 @@ func (c *container) startCmd(pid string, cmd *exec.Cmd, p *process) 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 { | ||||
| 		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 | ||||
| 🐹 + 🐙 = 😽 [](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 | ||||
| } | ||||
		Reference in New Issue
	
	Block a user
	 Arnaud Porterie
					Arnaud Porterie