Move testutil package to top-level
Since the common package no longer exists, the testutil package is being moved up to the root. Ideally, we don't have large omnibus packages, like testutil, but we can fix that in another refactoring round. Signed-off-by: Stephen J Day <stephen.day@docker.com>
This commit is contained in:
parent
d88884c51c
commit
adaa2246e7
6 changed files with 4 additions and 4 deletions
121
testutil/handler.go
Normal file
121
testutil/handler.go
Normal file
|
@ -0,0 +1,121 @@
|
|||
package testutil
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// RequestResponseMap is an ordered mapping from Requests to Responses
|
||||
type RequestResponseMap []RequestResponseMapping
|
||||
|
||||
// RequestResponseMapping defines a Response to be sent in response to a given
|
||||
// Request
|
||||
type RequestResponseMapping struct {
|
||||
Request Request
|
||||
Response Response
|
||||
}
|
||||
|
||||
// TODO(bbland): add support for request headers
|
||||
|
||||
// Request is a simplified http.Request object
|
||||
type Request struct {
|
||||
// Method is the http method of the request, for example GET
|
||||
Method string
|
||||
|
||||
// Route is the http route of this request
|
||||
Route string
|
||||
|
||||
// QueryParams are the query parameters of this request
|
||||
QueryParams map[string][]string
|
||||
|
||||
// Body is the byte contents of the http request
|
||||
Body []byte
|
||||
}
|
||||
|
||||
func (r Request) String() string {
|
||||
queryString := ""
|
||||
if len(r.QueryParams) > 0 {
|
||||
queryString = "?"
|
||||
keys := make([]string, 0, len(r.QueryParams))
|
||||
for k := range r.QueryParams {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
for _, k := range keys {
|
||||
queryString += strings.Join(r.QueryParams[k], "&") + "&"
|
||||
}
|
||||
queryString = queryString[:len(queryString)-1]
|
||||
}
|
||||
return fmt.Sprintf("%s %s%s\n%s", r.Method, r.Route, queryString, r.Body)
|
||||
}
|
||||
|
||||
// Response is a simplified http.Response object
|
||||
type Response struct {
|
||||
// Statuscode is the http status code of the Response
|
||||
StatusCode int
|
||||
|
||||
// Headers are the http headers of this Response
|
||||
Headers http.Header
|
||||
|
||||
// Body is the response body
|
||||
Body []byte
|
||||
}
|
||||
|
||||
// testHandler is an http.Handler with a defined mapping from Request to an
|
||||
// ordered list of Response objects
|
||||
type testHandler struct {
|
||||
responseMap map[string][]Response
|
||||
}
|
||||
|
||||
// NewHandler returns a new test handler that responds to defined requests
|
||||
// with specified responses
|
||||
// Each time a Request is received, the next Response is returned in the
|
||||
// mapping, until no Responses are defined, at which point a 404 is sent back
|
||||
func NewHandler(requestResponseMap RequestResponseMap) http.Handler {
|
||||
responseMap := make(map[string][]Response)
|
||||
for _, mapping := range requestResponseMap {
|
||||
responses, ok := responseMap[mapping.Request.String()]
|
||||
if ok {
|
||||
responseMap[mapping.Request.String()] = append(responses, mapping.Response)
|
||||
} else {
|
||||
responseMap[mapping.Request.String()] = []Response{mapping.Response}
|
||||
}
|
||||
}
|
||||
return &testHandler{responseMap: responseMap}
|
||||
}
|
||||
|
||||
func (app *testHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
defer r.Body.Close()
|
||||
|
||||
requestBody, _ := ioutil.ReadAll(r.Body)
|
||||
request := Request{
|
||||
Method: r.Method,
|
||||
Route: r.URL.Path,
|
||||
QueryParams: r.URL.Query(),
|
||||
Body: requestBody,
|
||||
}
|
||||
|
||||
responses, ok := app.responseMap[request.String()]
|
||||
|
||||
if !ok || len(responses) == 0 {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
response := responses[0]
|
||||
app.responseMap[request.String()] = responses[1:]
|
||||
|
||||
responseHeader := w.Header()
|
||||
for k, v := range response.Headers {
|
||||
responseHeader[k] = v
|
||||
}
|
||||
|
||||
w.WriteHeader(response.StatusCode)
|
||||
|
||||
io.Copy(w, bytes.NewReader(response.Body))
|
||||
}
|
95
testutil/tarfile.go
Normal file
95
testutil/tarfile.go
Normal file
|
@ -0,0 +1,95 @@
|
|||
package testutil
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
mrand "math/rand"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/pkg/tarsum"
|
||||
)
|
||||
|
||||
// CreateRandomTarFile creates a random tarfile, returning it as an
|
||||
// io.ReadSeeker along with its tarsum. An error is returned if there is a
|
||||
// problem generating valid content.
|
||||
func CreateRandomTarFile() (rs io.ReadSeeker, tarSum string, err error) {
|
||||
nFiles := mrand.Intn(10) + 10
|
||||
target := &bytes.Buffer{}
|
||||
wr := tar.NewWriter(target)
|
||||
|
||||
// Perturb this on each iteration of the loop below.
|
||||
header := &tar.Header{
|
||||
Mode: 0644,
|
||||
ModTime: time.Now(),
|
||||
Typeflag: tar.TypeReg,
|
||||
Uname: "randocalrissian",
|
||||
Gname: "cloudcity",
|
||||
AccessTime: time.Now(),
|
||||
ChangeTime: time.Now(),
|
||||
}
|
||||
|
||||
for fileNumber := 0; fileNumber < nFiles; fileNumber++ {
|
||||
fileSize := mrand.Int63n(1<<20) + 1<<20
|
||||
|
||||
header.Name = fmt.Sprint(fileNumber)
|
||||
header.Size = fileSize
|
||||
|
||||
if err := wr.WriteHeader(header); err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
randomData := make([]byte, fileSize)
|
||||
|
||||
// Fill up the buffer with some random data.
|
||||
n, err := rand.Read(randomData)
|
||||
|
||||
if n != len(randomData) {
|
||||
return nil, "", fmt.Errorf("short read creating random reader: %v bytes != %v bytes", n, len(randomData))
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
nn, err := io.Copy(wr, bytes.NewReader(randomData))
|
||||
if nn != fileSize {
|
||||
return nil, "", fmt.Errorf("short copy writing random file to tar")
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
if err := wr.Flush(); err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
}
|
||||
|
||||
if err := wr.Close(); err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
reader := bytes.NewReader(target.Bytes())
|
||||
|
||||
// A tar builder that supports tarsum inline calculation would be awesome
|
||||
// here.
|
||||
ts, err := tarsum.NewTarSum(reader, true, tarsum.Version1)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
nn, err := io.Copy(ioutil.Discard, ts)
|
||||
if nn != int64(len(target.Bytes())) {
|
||||
return nil, "", fmt.Errorf("short copy when getting tarsum of random layer: %v != %v", nn, len(target.Bytes()))
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
return bytes.NewReader(target.Bytes()), ts.Sum(nil), nil
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue