Use Go 1.5 vendoring instead of Godeps
Change made by: - running "gvt fetch" on each of the packages mentioned in Godeps/Godeps.json - `rm -rf Godeps` - tweaking the build scripts to not mention Godeps - tweaking the build scripts to test `./lib/...`, `./cmd/...` explicitly (to avoid testing vendor) - tweaking the build scripts to not juggle GOPATH for Godeps and instead set GO15VENDOREXPERIMENT. This also results in some updated packages at the same time I bet. Building with Go 1.3 and 1.4 still *works* but won't use our vendored dependencies - the user needs to have the actual packages in their GOPATH then, which they'll get with a normal "go get". Building with Go 1.6+ will get our vendored dependencies by default even when not using our build script, which is nice. By doing this we gain some freedom in that we can pick and choose manually what to include in vendor, as it's not based on just dependency analysis of our own code. This is also a risk as we might pick up dependencies we are unaware of, as the build may work locally with those packages present in GOPATH. On the other hand the build server will detect this as it has no packages in it's GOPATH beyond what is included in the repo. Recommended tool to manage dependencies is github.com/FiloSottile/gvt.
This commit is contained in:
24
vendor/github.com/bkaradzic/go-lz4/LICENSE
generated
vendored
Normal file
24
vendor/github.com/bkaradzic/go-lz4/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
Copyright 2011-2012 Branimir Karadzic. All rights reserved.
|
||||
Copyright 2013 Damian Gryski. 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 COPYRIGHT HOLDER ``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 COPYRIGHT HOLDER 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.
|
||||
|
||||
71
vendor/github.com/bkaradzic/go-lz4/README.md
generated
vendored
Normal file
71
vendor/github.com/bkaradzic/go-lz4/README.md
generated
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
go-lz4
|
||||
======
|
||||
|
||||
go-lz4 is port of LZ4 lossless compression algorithm to Go. The original C code
|
||||
is located at:
|
||||
|
||||
https://github.com/Cyan4973/lz4
|
||||
|
||||
Status
|
||||
------
|
||||
[](http://travis-ci.org/bkaradzic/go-lz4)
|
||||
[](https://godoc.org/github.com/bkaradzic/go-lz4)
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
go get github.com/bkaradzic/go-lz4
|
||||
|
||||
import "github.com/bkaradzic/go-lz4"
|
||||
|
||||
The package name is `lz4`
|
||||
|
||||
Notes
|
||||
-----
|
||||
|
||||
* go-lz4 saves a uint32 with the original uncompressed length at the beginning
|
||||
of the encoded buffer. They may get in the way of interoperability with
|
||||
other implementations.
|
||||
|
||||
Contributors
|
||||
------------
|
||||
|
||||
Damian Gryski ([@dgryski](https://github.com/dgryski))
|
||||
Dustin Sallings ([@dustin](https://github.com/dustin))
|
||||
|
||||
Contact
|
||||
-------
|
||||
|
||||
[@bkaradzic](https://twitter.com/bkaradzic)
|
||||
http://www.stuckingeometry.com
|
||||
|
||||
Project page
|
||||
https://github.com/bkaradzic/go-lz4
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
Copyright 2011-2012 Branimir Karadzic. All rights reserved.
|
||||
Copyright 2013 Damian Gryski. 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 COPYRIGHT HOLDER ``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 COPYRIGHT HOLDER 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.
|
||||
|
||||
23
vendor/github.com/bkaradzic/go-lz4/fuzz.go
generated
vendored
Normal file
23
vendor/github.com/bkaradzic/go-lz4/fuzz.go
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
// +build gofuzz
|
||||
|
||||
package lz4
|
||||
|
||||
import "encoding/binary"
|
||||
|
||||
func Fuzz(data []byte) int {
|
||||
|
||||
if len(data) < 4 {
|
||||
return 0
|
||||
}
|
||||
|
||||
ln := binary.LittleEndian.Uint32(data)
|
||||
if ln > (1 << 21) {
|
||||
return 0
|
||||
}
|
||||
|
||||
if _, err := Decode(nil, data); err != nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
return 1
|
||||
}
|
||||
74
vendor/github.com/bkaradzic/go-lz4/fuzzer/main.go
generated
vendored
Normal file
74
vendor/github.com/bkaradzic/go-lz4/fuzzer/main.go
generated
vendored
Normal file
@@ -0,0 +1,74 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
|
||||
"github.com/bkaradzic/go-lz4"
|
||||
|
||||
// lz4's API matches snappy's, so we can easily see how it performs
|
||||
// lz4 "code.google.com/p/snappy-go/snappy"
|
||||
)
|
||||
|
||||
var input = `
|
||||
ADVENTURE I. A SCANDAL IN BOHEMIA
|
||||
|
||||
I.
|
||||
|
||||
To Sherlock Holmes she is always THE woman. I have seldom heard
|
||||
him mention her under any other name. In his eyes she eclipses
|
||||
and predominates the whole of her sex. It was not that he felt
|
||||
any emotion akin to love for Irene Adler. All emotions, and that
|
||||
one particularly, were abhorrent to his cold, precise but
|
||||
admirably balanced mind. He was, I take it, the most perfect
|
||||
reasoning and observing machine that the world has seen, but as a
|
||||
lover he would have placed himself in a false position. He never
|
||||
spoke of the softer passions, save with a gibe and a sneer. They
|
||||
were admirable things for the observer--excellent for drawing the
|
||||
veil from men's motives and actions. But for the trained reasoner
|
||||
to admit such intrusions into his own delicate and finely
|
||||
adjusted temperament was to introduce a distracting factor which
|
||||
might throw a doubt upon all his mental results. Grit in a
|
||||
sensitive instrument, or a crack in one of his own high-power
|
||||
lenses, would not be more disturbing than a strong emotion in a
|
||||
nature such as his. And yet there was but one woman to him, and
|
||||
that woman was the late Irene Adler, of dubious and questionable
|
||||
memory.
|
||||
|
||||
I had seen little of Holmes lately. My marriage had drifted us
|
||||
away from each other. My own complete happiness, and the
|
||||
home-centred interests which rise up around the man who first
|
||||
finds himself master of his own establishment, were sufficient to
|
||||
absorb all my attention, while Holmes, who loathed every form of
|
||||
society with his whole Bohemian soul, remained in our lodgings in
|
||||
Baker Street, buried among his old books, and alternating from
|
||||
week to week between cocaine and ambition, the drowsiness of the
|
||||
drug, and the fierce energy of his own keen nature. He was still,
|
||||
as ever, deeply attracted by the study of crime, and occupied his
|
||||
immense faculties and extraordinary powers of observation in
|
||||
following out those clues, and clearing up those mysteries which
|
||||
had been abandoned as hopeless by the official police. From time
|
||||
to time I heard some vague account of his doings: of his summons
|
||||
to Odessa in the case of the Trepoff murder, of his clearing up
|
||||
of the singular tragedy of the Atkinson brothers at Trincomalee,
|
||||
and finally of the mission which he had accomplished so
|
||||
delicately and successfully for the reigning family of Holland.
|
||||
Beyond these signs of his activity, however, which I merely
|
||||
shared with all the readers of the daily press, I knew little of
|
||||
my former friend and companion.
|
||||
`
|
||||
|
||||
func main() {
|
||||
|
||||
compressed, _ := lz4.Encode(nil, []byte(input))
|
||||
|
||||
modified := make([]byte, len(compressed))
|
||||
|
||||
for {
|
||||
copy(modified, compressed)
|
||||
for i := 0; i < 100; i++ {
|
||||
modified[rand.Intn(len(compressed)-4)+4] = byte(rand.Intn(256))
|
||||
}
|
||||
lz4.Decode(nil, modified)
|
||||
}
|
||||
|
||||
}
|
||||
94
vendor/github.com/bkaradzic/go-lz4/lz4-example/main.go
generated
vendored
Normal file
94
vendor/github.com/bkaradzic/go-lz4/lz4-example/main.go
generated
vendored
Normal file
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright 2011 Branimir Karadzic. 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 COPYRIGHT HOLDER ``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 COPYRIGHT HOLDER 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 main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"runtime/pprof"
|
||||
|
||||
lz4 "github.com/bkaradzic/go-lz4"
|
||||
)
|
||||
|
||||
var (
|
||||
decompress = flag.Bool("d", false, "decompress")
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
var optCPUProfile = flag.String("cpuprofile", "", "profile")
|
||||
flag.Parse()
|
||||
|
||||
if *optCPUProfile != "" {
|
||||
f, err := os.Create(*optCPUProfile)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
pprof.StartCPUProfile(f)
|
||||
defer pprof.StopCPUProfile()
|
||||
}
|
||||
|
||||
args := flag.Args()
|
||||
|
||||
var data []byte
|
||||
|
||||
if len(args) < 2 {
|
||||
fmt.Print("Usage: lz4 [-d] <input> <output>\n")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
input, err := os.OpenFile(args[0], os.O_RDONLY, 0644)
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to open input file %s\n", args[0])
|
||||
os.Exit(1)
|
||||
}
|
||||
defer input.Close()
|
||||
|
||||
if *decompress {
|
||||
data, _ = ioutil.ReadAll(input)
|
||||
data, err = lz4.Decode(nil, data)
|
||||
if err != nil {
|
||||
fmt.Println("Failed to decode:", err)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
data, _ = ioutil.ReadAll(input)
|
||||
data, err = lz4.Encode(nil, data)
|
||||
if err != nil {
|
||||
fmt.Println("Failed to encode:", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
err = ioutil.WriteFile(args[1], data, 0644)
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to open output file %s\n", args[1])
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
63
vendor/github.com/bkaradzic/go-lz4/lz4_test.go
generated
vendored
Normal file
63
vendor/github.com/bkaradzic/go-lz4/lz4_test.go
generated
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
package lz4
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var testfile, _ = ioutil.ReadFile("testdata/pg1661.txt")
|
||||
|
||||
func roundtrip(t *testing.T, input []byte) {
|
||||
|
||||
dst, err := Encode(nil, input)
|
||||
if err != nil {
|
||||
t.Errorf("got error during compression: %s", err)
|
||||
}
|
||||
|
||||
output, err := Decode(nil, dst)
|
||||
|
||||
if err != nil {
|
||||
t.Errorf("got error during decompress: %s", err)
|
||||
}
|
||||
|
||||
if !bytes.Equal(output, input) {
|
||||
t.Errorf("roundtrip failed")
|
||||
}
|
||||
}
|
||||
|
||||
func TestEmpty(t *testing.T) {
|
||||
roundtrip(t, nil)
|
||||
}
|
||||
|
||||
func TestLengths(t *testing.T) {
|
||||
|
||||
for i := 0; i < 1024; i++ {
|
||||
roundtrip(t, testfile[:i])
|
||||
}
|
||||
|
||||
for i := 1024; i < 4096; i += 23 {
|
||||
roundtrip(t, testfile[:i])
|
||||
}
|
||||
}
|
||||
|
||||
func TestWords(t *testing.T) {
|
||||
roundtrip(t, testfile)
|
||||
}
|
||||
|
||||
func BenchmarkLZ4Encode(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
Encode(nil, testfile)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkLZ4Decode(b *testing.B) {
|
||||
|
||||
var compressed, _ = Encode(nil, testfile)
|
||||
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
Decode(nil, compressed)
|
||||
}
|
||||
}
|
||||
199
vendor/github.com/bkaradzic/go-lz4/reader.go
generated
vendored
Normal file
199
vendor/github.com/bkaradzic/go-lz4/reader.go
generated
vendored
Normal file
@@ -0,0 +1,199 @@
|
||||
/*
|
||||
* Copyright 2011-2012 Branimir Karadzic. 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 COPYRIGHT HOLDER ``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 COPYRIGHT HOLDER 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 lz4
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"io"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrCorrupt indicates the input was corrupt
|
||||
ErrCorrupt = errors.New("corrupt input")
|
||||
)
|
||||
|
||||
const (
|
||||
mlBits = 4
|
||||
mlMask = (1 << mlBits) - 1
|
||||
runBits = 8 - mlBits
|
||||
runMask = (1 << runBits) - 1
|
||||
)
|
||||
|
||||
type decoder struct {
|
||||
src []byte
|
||||
dst []byte
|
||||
spos uint32
|
||||
dpos uint32
|
||||
ref uint32
|
||||
}
|
||||
|
||||
func (d *decoder) readByte() (uint8, error) {
|
||||
if int(d.spos) == len(d.src) {
|
||||
return 0, io.EOF
|
||||
}
|
||||
b := d.src[d.spos]
|
||||
d.spos++
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func (d *decoder) getLen() (uint32, error) {
|
||||
|
||||
length := uint32(0)
|
||||
ln, err := d.readByte()
|
||||
if err != nil {
|
||||
return 0, ErrCorrupt
|
||||
}
|
||||
for ln == 255 {
|
||||
length += 255
|
||||
ln, err = d.readByte()
|
||||
if err != nil {
|
||||
return 0, ErrCorrupt
|
||||
}
|
||||
}
|
||||
length += uint32(ln)
|
||||
|
||||
return length, nil
|
||||
}
|
||||
|
||||
func (d *decoder) cp(length, decr uint32) {
|
||||
|
||||
if int(d.ref+length) < int(d.dpos) {
|
||||
copy(d.dst[d.dpos:], d.dst[d.ref:d.ref+length])
|
||||
} else {
|
||||
for ii := uint32(0); ii < length; ii++ {
|
||||
d.dst[d.dpos+ii] = d.dst[d.ref+ii]
|
||||
}
|
||||
}
|
||||
d.dpos += length
|
||||
d.ref += length - decr
|
||||
}
|
||||
|
||||
func (d *decoder) finish(err error) error {
|
||||
if err == io.EOF {
|
||||
return nil
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// Decode returns the decoded form of src. The returned slice may be a
|
||||
// subslice of dst if it was large enough to hold the entire decoded block.
|
||||
func Decode(dst, src []byte) ([]byte, error) {
|
||||
|
||||
if len(src) < 4 {
|
||||
return nil, ErrCorrupt
|
||||
}
|
||||
|
||||
uncompressedLen := binary.LittleEndian.Uint32(src)
|
||||
|
||||
if uncompressedLen == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if uncompressedLen > MaxInputSize {
|
||||
return nil, ErrTooLarge
|
||||
}
|
||||
|
||||
if dst == nil || len(dst) < int(uncompressedLen) {
|
||||
dst = make([]byte, uncompressedLen)
|
||||
}
|
||||
|
||||
d := decoder{src: src, dst: dst[:uncompressedLen], spos: 4}
|
||||
|
||||
decr := []uint32{0, 3, 2, 3}
|
||||
|
||||
for {
|
||||
code, err := d.readByte()
|
||||
if err != nil {
|
||||
return d.dst, d.finish(err)
|
||||
}
|
||||
|
||||
length := uint32(code >> mlBits)
|
||||
if length == runMask {
|
||||
ln, err := d.getLen()
|
||||
if err != nil {
|
||||
return nil, ErrCorrupt
|
||||
}
|
||||
length += ln
|
||||
}
|
||||
|
||||
if int(d.spos+length) > len(d.src) || int(d.dpos+length) > len(d.dst) {
|
||||
return nil, ErrCorrupt
|
||||
}
|
||||
|
||||
for ii := uint32(0); ii < length; ii++ {
|
||||
d.dst[d.dpos+ii] = d.src[d.spos+ii]
|
||||
}
|
||||
|
||||
d.spos += length
|
||||
d.dpos += length
|
||||
|
||||
if int(d.spos) == len(d.src) {
|
||||
return d.dst, nil
|
||||
}
|
||||
|
||||
if int(d.spos+2) >= len(d.src) {
|
||||
return nil, ErrCorrupt
|
||||
}
|
||||
|
||||
back := uint32(d.src[d.spos]) | uint32(d.src[d.spos+1])<<8
|
||||
|
||||
if back > d.dpos {
|
||||
return nil, ErrCorrupt
|
||||
}
|
||||
|
||||
d.spos += 2
|
||||
d.ref = d.dpos - back
|
||||
|
||||
length = uint32(code & mlMask)
|
||||
if length == mlMask {
|
||||
ln, err := d.getLen()
|
||||
if err != nil {
|
||||
return nil, ErrCorrupt
|
||||
}
|
||||
length += ln
|
||||
}
|
||||
|
||||
literal := d.dpos - d.ref
|
||||
|
||||
if literal < 4 {
|
||||
if int(d.dpos+4) > len(d.dst) {
|
||||
return nil, ErrCorrupt
|
||||
}
|
||||
|
||||
d.cp(4, decr[literal])
|
||||
} else {
|
||||
length += 4
|
||||
}
|
||||
|
||||
if d.dpos+length > uncompressedLen {
|
||||
return nil, ErrCorrupt
|
||||
}
|
||||
|
||||
d.cp(length, 0)
|
||||
}
|
||||
}
|
||||
13052
vendor/github.com/bkaradzic/go-lz4/testdata/pg1661.txt
generated
vendored
Normal file
13052
vendor/github.com/bkaradzic/go-lz4/testdata/pg1661.txt
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
190
vendor/github.com/bkaradzic/go-lz4/writer.go
generated
vendored
Normal file
190
vendor/github.com/bkaradzic/go-lz4/writer.go
generated
vendored
Normal file
@@ -0,0 +1,190 @@
|
||||
/*
|
||||
* Copyright 2011-2012 Branimir Karadzic. 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 COPYRIGHT HOLDER ``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 COPYRIGHT HOLDER 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 lz4
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
)
|
||||
|
||||
const (
|
||||
minMatch = 4
|
||||
hashLog = 17
|
||||
hashTableSize = 1 << hashLog
|
||||
hashShift = (minMatch * 8) - hashLog
|
||||
incompressible uint32 = 128
|
||||
uninitHash = 0x88888888
|
||||
|
||||
// MaxInputSize is the largest buffer than can be compressed in a single block
|
||||
MaxInputSize = 0x7E000000
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrTooLarge indicates the input buffer was too large
|
||||
ErrTooLarge = errors.New("input too large")
|
||||
)
|
||||
|
||||
type encoder struct {
|
||||
src []byte
|
||||
dst []byte
|
||||
hashTable []uint32
|
||||
pos uint32
|
||||
anchor uint32
|
||||
dpos uint32
|
||||
}
|
||||
|
||||
// CompressBound returns the maximum length of a lz4 block, given it's uncompressed length
|
||||
func CompressBound(isize int) int {
|
||||
if isize > MaxInputSize {
|
||||
return 0
|
||||
}
|
||||
return isize + ((isize) / 255) + 16 + 4
|
||||
}
|
||||
|
||||
func (e *encoder) writeLiterals(length, mlLen, pos uint32) {
|
||||
|
||||
ln := length
|
||||
|
||||
var code byte
|
||||
if ln > runMask-1 {
|
||||
code = runMask
|
||||
} else {
|
||||
code = byte(ln)
|
||||
}
|
||||
|
||||
if mlLen > mlMask-1 {
|
||||
e.dst[e.dpos] = (code << mlBits) + byte(mlMask)
|
||||
} else {
|
||||
e.dst[e.dpos] = (code << mlBits) + byte(mlLen)
|
||||
}
|
||||
e.dpos++
|
||||
|
||||
if code == runMask {
|
||||
ln -= runMask
|
||||
for ; ln > 254; ln -= 255 {
|
||||
e.dst[e.dpos] = 255
|
||||
e.dpos++
|
||||
}
|
||||
|
||||
e.dst[e.dpos] = byte(ln)
|
||||
e.dpos++
|
||||
}
|
||||
|
||||
for ii := uint32(0); ii < length; ii++ {
|
||||
e.dst[e.dpos+ii] = e.src[pos+ii]
|
||||
}
|
||||
|
||||
e.dpos += length
|
||||
}
|
||||
|
||||
// Encode returns the encoded form of src. The returned array may be a
|
||||
// sub-slice of dst if it was large enough to hold the entire output.
|
||||
func Encode(dst, src []byte) ([]byte, error) {
|
||||
|
||||
if len(src) >= MaxInputSize {
|
||||
return nil, ErrTooLarge
|
||||
}
|
||||
|
||||
if n := CompressBound(len(src)); len(dst) < n {
|
||||
dst = make([]byte, n)
|
||||
}
|
||||
|
||||
e := encoder{src: src, dst: dst, hashTable: make([]uint32, hashTableSize)}
|
||||
|
||||
binary.LittleEndian.PutUint32(dst, uint32(len(src)))
|
||||
e.dpos = 4
|
||||
|
||||
var (
|
||||
step uint32 = 1
|
||||
limit = incompressible
|
||||
)
|
||||
|
||||
for {
|
||||
if int(e.pos)+12 >= len(e.src) {
|
||||
e.writeLiterals(uint32(len(e.src))-e.anchor, 0, e.anchor)
|
||||
return e.dst[:e.dpos], nil
|
||||
}
|
||||
|
||||
sequence := uint32(e.src[e.pos+3])<<24 | uint32(e.src[e.pos+2])<<16 | uint32(e.src[e.pos+1])<<8 | uint32(e.src[e.pos+0])
|
||||
|
||||
hash := (sequence * 2654435761) >> hashShift
|
||||
ref := e.hashTable[hash] + uninitHash
|
||||
e.hashTable[hash] = e.pos - uninitHash
|
||||
|
||||
if ((e.pos-ref)>>16) != 0 || uint32(e.src[ref+3])<<24|uint32(e.src[ref+2])<<16|uint32(e.src[ref+1])<<8|uint32(e.src[ref+0]) != sequence {
|
||||
if e.pos-e.anchor > limit {
|
||||
limit <<= 1
|
||||
step += 1 + (step >> 2)
|
||||
}
|
||||
e.pos += step
|
||||
continue
|
||||
}
|
||||
|
||||
if step > 1 {
|
||||
e.hashTable[hash] = ref - uninitHash
|
||||
e.pos -= step - 1
|
||||
step = 1
|
||||
continue
|
||||
}
|
||||
limit = incompressible
|
||||
|
||||
ln := e.pos - e.anchor
|
||||
back := e.pos - ref
|
||||
|
||||
anchor := e.anchor
|
||||
|
||||
e.pos += minMatch
|
||||
ref += minMatch
|
||||
e.anchor = e.pos
|
||||
|
||||
for int(e.pos) < len(e.src)-5 && e.src[e.pos] == e.src[ref] {
|
||||
e.pos++
|
||||
ref++
|
||||
}
|
||||
|
||||
mlLen := e.pos - e.anchor
|
||||
|
||||
e.writeLiterals(ln, mlLen, anchor)
|
||||
e.dst[e.dpos] = uint8(back)
|
||||
e.dst[e.dpos+1] = uint8(back >> 8)
|
||||
e.dpos += 2
|
||||
|
||||
if mlLen > mlMask-1 {
|
||||
mlLen -= mlMask
|
||||
for mlLen > 254 {
|
||||
mlLen -= 255
|
||||
|
||||
e.dst[e.dpos] = 255
|
||||
e.dpos++
|
||||
}
|
||||
|
||||
e.dst[e.dpos] = byte(mlLen)
|
||||
e.dpos++
|
||||
}
|
||||
|
||||
e.anchor = e.pos
|
||||
}
|
||||
}
|
||||
24
vendor/github.com/calmh/du/LICENSE
generated
vendored
Normal file
24
vendor/github.com/calmh/du/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
This is free and unencumbered software released into the public domain.
|
||||
|
||||
Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||
distribute this software, either in source code form or as a compiled
|
||||
binary, for any purpose, commercial or non-commercial, and by any
|
||||
means.
|
||||
|
||||
In jurisdictions that recognize copyright laws, the author or authors
|
||||
of this software dedicate any and all copyright interest in the
|
||||
software to the public domain. We make this dedication for the benefit
|
||||
of the public at large and to the detriment of our heirs and
|
||||
successors. We intend this dedication to be an overt act of
|
||||
relinquishment in perpetuity of all present and future rights to this
|
||||
software under copyright law.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
For more information, please refer to <http://unlicense.org>
|
||||
14
vendor/github.com/calmh/du/README.md
generated
vendored
Normal file
14
vendor/github.com/calmh/du/README.md
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
du
|
||||
==
|
||||
|
||||
Get total and available disk space on a given volume.
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
|
||||
http://godoc.org/github.com/calmh/du
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
Public Domain
|
||||
21
vendor/github.com/calmh/du/cmd/du/main.go
generated
vendored
Normal file
21
vendor/github.com/calmh/du/cmd/du/main.go
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/calmh/du"
|
||||
)
|
||||
|
||||
var KB = int64(1024)
|
||||
|
||||
func main() {
|
||||
usage, err := du.Get(os.Args[1])
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
fmt.Println("Free:", usage.FreeBytes/(KB*KB), "MiB")
|
||||
fmt.Println("Available:", usage.AvailBytes/(KB*KB), "MiB")
|
||||
fmt.Println("Size:", usage.TotalBytes/(KB*KB), "MiB")
|
||||
}
|
||||
8
vendor/github.com/calmh/du/diskusage.go
generated
vendored
Normal file
8
vendor/github.com/calmh/du/diskusage.go
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
package du
|
||||
|
||||
// Usage holds information about total and available storage on a volume.
|
||||
type Usage struct {
|
||||
TotalBytes int64 // Size of volume
|
||||
FreeBytes int64 // Unused size
|
||||
AvailBytes int64 // Available to a non-privileged user
|
||||
}
|
||||
24
vendor/github.com/calmh/du/diskusage_posix.go
generated
vendored
Normal file
24
vendor/github.com/calmh/du/diskusage_posix.go
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
// +build !windows,!netbsd,!openbsd,!solaris
|
||||
|
||||
package du
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// Get returns the Usage of a given path, or an error if usage data is
|
||||
// unavailable.
|
||||
func Get(path string) (Usage, error) {
|
||||
var stat syscall.Statfs_t
|
||||
err := syscall.Statfs(filepath.Clean(path), &stat)
|
||||
if err != nil {
|
||||
return Usage{}, err
|
||||
}
|
||||
u := Usage{
|
||||
FreeBytes: int64(stat.Bfree) * int64(stat.Bsize),
|
||||
TotalBytes: int64(stat.Blocks) * int64(stat.Bsize),
|
||||
AvailBytes: int64(stat.Bavail) * int64(stat.Bsize),
|
||||
}
|
||||
return u, nil
|
||||
}
|
||||
13
vendor/github.com/calmh/du/diskusage_unsupported.go
generated
vendored
Normal file
13
vendor/github.com/calmh/du/diskusage_unsupported.go
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
// +build netbsd openbsd solaris
|
||||
|
||||
package du
|
||||
|
||||
import "errors"
|
||||
|
||||
var ErrUnsupported = errors.New("unsupported platform")
|
||||
|
||||
// Get returns the Usage of a given path, or an error if usage data is
|
||||
// unavailable.
|
||||
func Get(path string) (Usage, error) {
|
||||
return Usage{}, ErrUnsupported
|
||||
}
|
||||
27
vendor/github.com/calmh/du/diskusage_windows.go
generated
vendored
Normal file
27
vendor/github.com/calmh/du/diskusage_windows.go
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
package du
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Get returns the Usage of a given path, or an error if usage data is
|
||||
// unavailable.
|
||||
func Get(path string) (Usage, error) {
|
||||
h := syscall.MustLoadDLL("kernel32.dll")
|
||||
c := h.MustFindProc("GetDiskFreeSpaceExW")
|
||||
|
||||
var u Usage
|
||||
|
||||
ret, _, err := c.Call(
|
||||
uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(path))),
|
||||
uintptr(unsafe.Pointer(&u.FreeBytes)),
|
||||
uintptr(unsafe.Pointer(&u.TotalBytes)),
|
||||
uintptr(unsafe.Pointer(&u.AvailBytes)))
|
||||
|
||||
if ret == 0 {
|
||||
return u, err
|
||||
}
|
||||
|
||||
return u, nil
|
||||
}
|
||||
19
vendor/github.com/calmh/luhn/LICENSE
generated
vendored
Normal file
19
vendor/github.com/calmh/luhn/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
Copyright (C) 2014 Jakob Borg
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
- The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
70
vendor/github.com/calmh/luhn/luhn.go
generated
vendored
Normal file
70
vendor/github.com/calmh/luhn/luhn.go
generated
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
// Copyright (C) 2014 Jakob Borg
|
||||
|
||||
// Package luhn generates and validates Luhn mod N check digits.
|
||||
package luhn
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// An alphabet is a string of N characters, representing the digits of a given
|
||||
// base N.
|
||||
type Alphabet string
|
||||
|
||||
var (
|
||||
Base32 Alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"
|
||||
)
|
||||
|
||||
// Generate returns a check digit for the string s, which should be composed
|
||||
// of characters from the Alphabet a.
|
||||
func (a Alphabet) Generate(s string) (rune, error) {
|
||||
if err := a.check(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
factor := 1
|
||||
sum := 0
|
||||
n := len(a)
|
||||
|
||||
for i := range s {
|
||||
codepoint := strings.IndexByte(string(a), s[i])
|
||||
if codepoint == -1 {
|
||||
return 0, fmt.Errorf("Digit %q not valid in alphabet %q", s[i], a)
|
||||
}
|
||||
addend := factor * codepoint
|
||||
if factor == 2 {
|
||||
factor = 1
|
||||
} else {
|
||||
factor = 2
|
||||
}
|
||||
addend = (addend / n) + (addend % n)
|
||||
sum += addend
|
||||
}
|
||||
remainder := sum % n
|
||||
checkCodepoint := (n - remainder) % n
|
||||
return rune(a[checkCodepoint]), nil
|
||||
}
|
||||
|
||||
// Validate returns true if the last character of the string s is correct, for
|
||||
// a string s composed of characters in the alphabet a.
|
||||
func (a Alphabet) Validate(s string) bool {
|
||||
t := s[:len(s)-1]
|
||||
c, err := a.Generate(t)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return rune(s[len(s)-1]) == c
|
||||
}
|
||||
|
||||
// check returns an error if the given alphabet does not consist of unique characters
|
||||
func (a Alphabet) check() error {
|
||||
cm := make(map[byte]bool, len(a))
|
||||
for i := range a {
|
||||
if cm[a[i]] {
|
||||
return fmt.Errorf("Digit %q non-unique in alphabet %q", a[i], a)
|
||||
}
|
||||
cm[a[i]] = true
|
||||
}
|
||||
return nil
|
||||
}
|
||||
59
vendor/github.com/calmh/luhn/luhn_test.go
generated
vendored
Normal file
59
vendor/github.com/calmh/luhn/luhn_test.go
generated
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
// Copyright (C) 2014 Jakob Borg
|
||||
|
||||
package luhn_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/calmh/luhn"
|
||||
)
|
||||
|
||||
func TestGenerate(t *testing.T) {
|
||||
// Base 6 Luhn
|
||||
a := luhn.Alphabet("abcdef")
|
||||
c, err := a.Generate("abcdef")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if c != 'e' {
|
||||
t.Errorf("Incorrect check digit %c != e", c)
|
||||
}
|
||||
|
||||
// Base 10 Luhn
|
||||
a = luhn.Alphabet("0123456789")
|
||||
c, err = a.Generate("7992739871")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if c != '3' {
|
||||
t.Errorf("Incorrect check digit %c != 3", c)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInvalidString(t *testing.T) {
|
||||
a := luhn.Alphabet("ABC")
|
||||
_, err := a.Generate("7992739871")
|
||||
t.Log(err)
|
||||
if err == nil {
|
||||
t.Error("Unexpected nil error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBadAlphabet(t *testing.T) {
|
||||
a := luhn.Alphabet("01234566789")
|
||||
_, err := a.Generate("7992739871")
|
||||
t.Log(err)
|
||||
if err == nil {
|
||||
t.Error("Unexpected nil error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidate(t *testing.T) {
|
||||
a := luhn.Alphabet("abcdef")
|
||||
if !a.Validate("abcdefe") {
|
||||
t.Errorf("Incorrect validation response for abcdefe")
|
||||
}
|
||||
if a.Validate("abcdefd") {
|
||||
t.Errorf("Incorrect validation response for abcdefd")
|
||||
}
|
||||
}
|
||||
19
vendor/github.com/calmh/xdr/LICENSE
generated
vendored
Normal file
19
vendor/github.com/calmh/xdr/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
Copyright (C) 2014 Jakob Borg.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to
|
||||
deal in the Software without restriction, including without limitation the
|
||||
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
sell copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
- The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
IN THE SOFTWARE.
|
||||
10
vendor/github.com/calmh/xdr/README.md
generated
vendored
Normal file
10
vendor/github.com/calmh/xdr/README.md
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
xdr
|
||||
===
|
||||
|
||||
[](https://circleci.com/gh/calmh/xdr)
|
||||
[](https://coveralls.io/r/calmh/xdr?branch=master)
|
||||
[](http://godoc.org/github.com/calmh/xdr)
|
||||
[](http://opensource.org/licenses/MIT)
|
||||
|
||||
This is an XDR marshalling/unmarshalling library. It uses code generation and
|
||||
not reflection.
|
||||
52
vendor/github.com/calmh/xdr/bench_test.go
generated
vendored
Normal file
52
vendor/github.com/calmh/xdr/bench_test.go
generated
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
// Copyright (C) 2014 Jakob Borg. All rights reserved. Use of this source code
|
||||
// is governed by an MIT-style license that can be found in the LICENSE file.
|
||||
|
||||
package xdr_test
|
||||
|
||||
import "testing"
|
||||
|
||||
type XDRBenchStruct struct {
|
||||
I1 uint64
|
||||
I2 uint32
|
||||
I3 uint16
|
||||
I4 uint8
|
||||
Bs0 []byte // max:128
|
||||
Bs1 []byte
|
||||
Is0 []int32
|
||||
S0 string // max:128
|
||||
S1 string
|
||||
}
|
||||
|
||||
var res []byte // not to be optimized away
|
||||
var s = XDRBenchStruct{
|
||||
I1: 42,
|
||||
I2: 43,
|
||||
I3: 44,
|
||||
I4: 45,
|
||||
Bs0: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18},
|
||||
Bs1: []byte{11, 12, 13, 14, 15, 16, 17, 18, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
|
||||
Is0: []int32{23, 43},
|
||||
S0: "Hello World! String one.",
|
||||
S1: "Hello World! String two.",
|
||||
}
|
||||
|
||||
func BenchmarkThisMarshal(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
res, _ = s.MarshalXDR()
|
||||
}
|
||||
|
||||
b.ReportAllocs()
|
||||
}
|
||||
|
||||
func BenchmarkThisUnmarshal(b *testing.B) {
|
||||
bs := s.MustMarshalXDR()
|
||||
var t XDRBenchStruct
|
||||
for i := 0; i < b.N; i++ {
|
||||
err := t.UnmarshalXDR(bs)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
b.ReportAllocs()
|
||||
}
|
||||
140
vendor/github.com/calmh/xdr/bench_xdr_test.go
generated
vendored
Normal file
140
vendor/github.com/calmh/xdr/bench_xdr_test.go
generated
vendored
Normal file
@@ -0,0 +1,140 @@
|
||||
// ************************************************************
|
||||
// This file is automatically generated by genxdr. Do not edit.
|
||||
// ************************************************************
|
||||
|
||||
package xdr_test
|
||||
|
||||
import (
|
||||
"github.com/calmh/xdr"
|
||||
)
|
||||
|
||||
/*
|
||||
|
||||
XDRBenchStruct Structure:
|
||||
|
||||
0 1 2 3
|
||||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| |
|
||||
+ I1 (64 bits) +
|
||||
| |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| I2 |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| 16 zero bits | I3 |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| 24 zero bits | I4 |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
/ /
|
||||
\ Bs0 (length + padded data) \
|
||||
/ /
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
/ /
|
||||
\ Bs1 (length + padded data) \
|
||||
/ /
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Number of Is0 |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
/ /
|
||||
| Is0 (n items) |
|
||||
/ /
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
/ /
|
||||
\ S0 (length + padded data) \
|
||||
/ /
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
/ /
|
||||
\ S1 (length + padded data) \
|
||||
/ /
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|
||||
|
||||
struct XDRBenchStruct {
|
||||
unsigned hyper I1;
|
||||
unsigned int I2;
|
||||
unsigned int I3;
|
||||
unsigned int I4;
|
||||
opaque Bs0<128>;
|
||||
opaque Bs1<>;
|
||||
int Is0<>;
|
||||
string S0<128>;
|
||||
string S1<>;
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
func (o XDRBenchStruct) XDRSize() int {
|
||||
return 8 + 4 + 4 + 4 +
|
||||
4 + len(o.Bs0) + xdr.Padding(len(o.Bs0)) +
|
||||
4 + len(o.Bs1) + xdr.Padding(len(o.Bs1)) +
|
||||
4 + len(o.Is0)*4 +
|
||||
4 + len(o.S0) + xdr.Padding(len(o.S0)) +
|
||||
4 + len(o.S1) + xdr.Padding(len(o.S1))
|
||||
}
|
||||
|
||||
func (o XDRBenchStruct) MarshalXDR() ([]byte, error) {
|
||||
buf := make([]byte, o.XDRSize())
|
||||
m := &xdr.Marshaller{Data: buf}
|
||||
return buf, o.MarshalXDRInto(m)
|
||||
}
|
||||
|
||||
func (o XDRBenchStruct) MustMarshalXDR() []byte {
|
||||
bs, err := o.MarshalXDR()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return bs
|
||||
}
|
||||
|
||||
func (o XDRBenchStruct) MarshalXDRInto(m *xdr.Marshaller) error {
|
||||
m.MarshalUint64(o.I1)
|
||||
m.MarshalUint32(o.I2)
|
||||
m.MarshalUint16(o.I3)
|
||||
m.MarshalUint8(o.I4)
|
||||
if l := len(o.Bs0); l > 128 {
|
||||
return xdr.ElementSizeExceeded("Bs0", l, 128)
|
||||
}
|
||||
m.MarshalBytes(o.Bs0)
|
||||
m.MarshalBytes(o.Bs1)
|
||||
m.MarshalUint32(uint32(len(o.Is0)))
|
||||
for i := range o.Is0 {
|
||||
m.MarshalUint32(uint32(o.Is0[i]))
|
||||
}
|
||||
if l := len(o.S0); l > 128 {
|
||||
return xdr.ElementSizeExceeded("S0", l, 128)
|
||||
}
|
||||
m.MarshalString(o.S0)
|
||||
m.MarshalString(o.S1)
|
||||
return m.Error
|
||||
}
|
||||
|
||||
func (o *XDRBenchStruct) UnmarshalXDR(bs []byte) error {
|
||||
u := &xdr.Unmarshaller{Data: bs}
|
||||
return o.UnmarshalXDRFrom(u)
|
||||
}
|
||||
func (o *XDRBenchStruct) UnmarshalXDRFrom(u *xdr.Unmarshaller) error {
|
||||
o.I1 = u.UnmarshalUint64()
|
||||
o.I2 = u.UnmarshalUint32()
|
||||
o.I3 = u.UnmarshalUint16()
|
||||
o.I4 = u.UnmarshalUint8()
|
||||
o.Bs0 = u.UnmarshalBytesMax(128)
|
||||
o.Bs1 = u.UnmarshalBytes()
|
||||
_Is0Size := int(u.UnmarshalUint32())
|
||||
if _Is0Size < 0 {
|
||||
return xdr.ElementSizeExceeded("Is0", _Is0Size, 0)
|
||||
} else if _Is0Size == 0 {
|
||||
o.Is0 = nil
|
||||
} else {
|
||||
if _Is0Size <= len(o.Is0) {
|
||||
o.Is0 = o.Is0[:_Is0Size]
|
||||
} else {
|
||||
o.Is0 = make([]int32, _Is0Size)
|
||||
}
|
||||
for i := range o.Is0 {
|
||||
o.Is0[i] = int32(u.UnmarshalUint32())
|
||||
}
|
||||
}
|
||||
o.S0 = u.UnmarshalStringMax(128)
|
||||
o.S1 = u.UnmarshalString()
|
||||
return u.Error
|
||||
}
|
||||
3
vendor/github.com/calmh/xdr/circle.yml
generated
vendored
Normal file
3
vendor/github.com/calmh/xdr/circle.yml
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
dependencies:
|
||||
post:
|
||||
- ./generate.sh
|
||||
586
vendor/github.com/calmh/xdr/cmd/genxdr/main.go
generated
vendored
Normal file
586
vendor/github.com/calmh/xdr/cmd/genxdr/main.go
generated
vendored
Normal file
@@ -0,0 +1,586 @@
|
||||
// Copyright (C) 2014 Jakob Borg. All rights reserved. Use of this source code
|
||||
// is governed by an MIT-style license that can be found in the LICENSE file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"flag"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/format"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
type fieldInfo struct {
|
||||
Name string
|
||||
IsBasic bool // handled by one the native Read/WriteUint64 etc functions
|
||||
IsSlice bool // field is a slice of FieldType
|
||||
FieldType string // original type of field, i.e. "int"
|
||||
Encoder string // the encoder name, i.e. "Uint64" for Read/WriteUint64
|
||||
Convert string // what to convert to when encoding, i.e. "uint64"
|
||||
Max int // max size for slices and strings
|
||||
Submax int // max size for strings inside slices
|
||||
}
|
||||
|
||||
type structInfo struct {
|
||||
Name string
|
||||
Fields []fieldInfo
|
||||
}
|
||||
|
||||
func (i structInfo) SizeExpr() string {
|
||||
var xdrSizes = map[string]int{
|
||||
"int8": 4,
|
||||
"uint8": 4,
|
||||
"int16": 4,
|
||||
"uint16": 4,
|
||||
"int32": 4,
|
||||
"uint32": 4,
|
||||
"int64": 8,
|
||||
"uint64": 8,
|
||||
"int": 8,
|
||||
"bool": 4,
|
||||
}
|
||||
|
||||
var terms []string
|
||||
nl := ""
|
||||
for _, f := range i.Fields {
|
||||
if size := xdrSizes[f.FieldType]; size > 0 {
|
||||
if f.IsSlice {
|
||||
terms = append(terms, nl+"4+len(o."+f.Name+")*"+strconv.Itoa(size))
|
||||
} else {
|
||||
terms = append(terms, strconv.Itoa(size))
|
||||
}
|
||||
} else {
|
||||
switch f.FieldType {
|
||||
case "string", "[]byte":
|
||||
if f.IsSlice {
|
||||
terms = append(terms, nl+"4+xdr.SizeOfSlice(o."+f.Name+")")
|
||||
} else {
|
||||
terms = append(terms, nl+"4+len(o."+f.Name+")+xdr.Padding(len(o."+f.Name+"))")
|
||||
}
|
||||
default:
|
||||
if f.IsSlice {
|
||||
terms = append(terms, nl+"4+xdr.SizeOfSlice(o."+f.Name+")")
|
||||
} else {
|
||||
terms = append(terms, nl+"o."+f.Name+".XDRSize()")
|
||||
}
|
||||
}
|
||||
}
|
||||
nl = "\n"
|
||||
}
|
||||
return strings.Join(terms, "+")
|
||||
}
|
||||
|
||||
var headerData = `// ************************************************************
|
||||
// This file is automatically generated by genxdr. Do not edit.
|
||||
// ************************************************************
|
||||
|
||||
package {{.Package}}
|
||||
|
||||
import (
|
||||
"github.com/calmh/xdr"
|
||||
)
|
||||
`
|
||||
|
||||
var encoderData = `
|
||||
func (o {{.Name}}) XDRSize() int {
|
||||
return {{.SizeExpr}}
|
||||
}//+n
|
||||
|
||||
func (o {{.Name}}) MarshalXDR() ([]byte, error) {
|
||||
buf:= make([]byte, o.XDRSize())
|
||||
m := &xdr.Marshaller{Data: buf}
|
||||
return buf, o.MarshalXDRInto(m)
|
||||
}//+n
|
||||
|
||||
func (o {{.Name}}) MustMarshalXDR() []byte {
|
||||
bs, err := o.MarshalXDR()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return bs
|
||||
}//+n
|
||||
|
||||
func (o {{.Name}}) MarshalXDRInto(m *xdr.Marshaller) error {
|
||||
{{range $fi := .Fields}}
|
||||
{{if $fi.IsSlice}}
|
||||
{{template "marshalSlice" $fi}}
|
||||
{{else}}
|
||||
{{template "marshalValue" $fi}}
|
||||
{{end}}
|
||||
{{end}}
|
||||
return m.Error
|
||||
}//+n
|
||||
|
||||
{{define "marshalValue"}}
|
||||
{{if ne .Convert ""}}
|
||||
m.Marshal{{.Encoder}}({{.Convert}}(o.{{.Name}}))
|
||||
{{else if .IsBasic}}
|
||||
{{if ge .Max 1}}
|
||||
if l := len(o.{{.Name}}); l > {{.Max}} {
|
||||
return xdr.ElementSizeExceeded("{{.Name}}", l, {{.Max}})
|
||||
}
|
||||
{{end}}
|
||||
m.Marshal{{.Encoder}}(o.{{.Name}})
|
||||
{{else}}
|
||||
if err := o.{{.Name}}.MarshalXDRInto(m); err != nil {
|
||||
return err
|
||||
}
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{define "marshalSlice"}}
|
||||
{{if ge .Max 1}}
|
||||
if l := len(o.{{.Name}}); l > {{.Max}} {
|
||||
return xdr.ElementSizeExceeded("{{.Name}}", l, {{.Max}})
|
||||
}
|
||||
{{end}}
|
||||
|
||||
m.MarshalUint32(uint32(len(o.{{.Name}})))
|
||||
for i := range o.{{.Name}} {
|
||||
{{if ne .Convert ""}}
|
||||
m.Marshal{{.Encoder}}({{.Convert}}(o.{{.Name}}[i]))
|
||||
{{else if .IsBasic}}
|
||||
m.Marshal{{.Encoder}}(o.{{.Name}}[i])
|
||||
{{else}}
|
||||
if err := o.{{.Name}}[i].MarshalXDRInto(m); err != nil {
|
||||
return err
|
||||
}
|
||||
{{end}}
|
||||
}
|
||||
{{end}}
|
||||
|
||||
func (o *{{.Name}}) UnmarshalXDR(bs []byte) error {
|
||||
u := &xdr.Unmarshaller{Data: bs}
|
||||
return o.UnmarshalXDRFrom(u)
|
||||
}
|
||||
|
||||
func (o *{{.Name}}) UnmarshalXDRFrom(u *xdr.Unmarshaller) error {
|
||||
{{range $fi := .Fields}}
|
||||
{{if $fi.IsSlice}}
|
||||
{{template "unmarshalSlice" $fi}}
|
||||
{{else}}
|
||||
{{template "unmarshalValue" $fi}}
|
||||
{{end}}
|
||||
{{end}}
|
||||
return u.Error
|
||||
}//+n
|
||||
|
||||
{{define "unmarshalValue"}}
|
||||
{{if ne .Convert ""}}
|
||||
o.{{.Name}} = {{.FieldType}}(u.Unmarshal{{.Encoder}}())
|
||||
{{else if .IsBasic}}
|
||||
{{if ge .Max 1}}
|
||||
o.{{.Name}} = u.Unmarshal{{.Encoder}}Max({{.Max}})
|
||||
{{else}}
|
||||
o.{{.Name}} = u.Unmarshal{{.Encoder}}()
|
||||
{{end}}
|
||||
{{else}}
|
||||
(&o.{{.Name}}).UnmarshalXDRFrom(u)
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
||||
{{define "unmarshalSlice"}}
|
||||
_{{.Name}}Size := int(u.UnmarshalUint32())
|
||||
if _{{.Name}}Size < 0 {
|
||||
return xdr.ElementSizeExceeded("{{.Name}}", _{{.Name}}Size, {{.Max}})
|
||||
} else if _{{.Name}}Size == 0 {
|
||||
o.{{.Name}} = nil
|
||||
} else {
|
||||
{{if ge .Max 1}}
|
||||
if _{{.Name}}Size > {{.Max}} {
|
||||
return xdr.ElementSizeExceeded("{{.Name}}", _{{.Name}}Size, {{.Max}})
|
||||
}
|
||||
{{end}}
|
||||
if _{{.Name}}Size <= len(o.{{.Name}}) {
|
||||
{{if eq .FieldType "string"}}
|
||||
for i := _{{.Name}}Size; i < len(o.{{.Name}}); i++ { o.{{.Name}}[i] = "" }
|
||||
{{end}}
|
||||
{{if eq .FieldType "[]byte"}}
|
||||
for i := _{{.Name}}Size; i < len(o.{{.Name}}); i++ { o.{{.Name}}[i] = nil }
|
||||
{{end}}
|
||||
o.{{.Name}} = o.{{.Name}}[:_{{.Name}}Size]
|
||||
} else {
|
||||
o.{{.Name}} = make([]{{.FieldType}}, _{{.Name}}Size)
|
||||
}
|
||||
for i := range o.{{.Name}} {
|
||||
{{if ne .Convert ""}}
|
||||
o.{{.Name}}[i] = {{.FieldType}}(u.Unmarshal{{.Encoder}}())
|
||||
{{else if .IsBasic}}
|
||||
{{if ge .Submax 1}}
|
||||
o.{{.Name}}[i] = u.Unmarshal{{.Encoder}}Max({{.Submax}})
|
||||
{{else}}
|
||||
o.{{.Name}}[i] = u.Unmarshal{{.Encoder}}()
|
||||
{{end}}
|
||||
{{else}}
|
||||
(&o.{{.Name}}[i]).UnmarshalXDRFrom(u)
|
||||
{{end}}
|
||||
}
|
||||
}
|
||||
{{end}}
|
||||
`
|
||||
|
||||
var (
|
||||
encodeTpl = template.Must(template.New("encoder").Parse(encoderData))
|
||||
headerTpl = template.Must(template.New("header").Parse(headerData))
|
||||
)
|
||||
|
||||
var emptyTypeTpl = template.Must(template.New("encoder").Parse(`
|
||||
func (o {{.Name}}) XDRSize() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (o {{.Name}}) MarshalXDR() ([]byte, error) {
|
||||
return nil, nil
|
||||
}//+n
|
||||
|
||||
func (o {{.Name}}) MustMarshalXDR() []byte {
|
||||
return nil
|
||||
}//+n
|
||||
|
||||
func (o {{.Name}}) MarshalXDRInto(m *xdr.Marshaller) error {
|
||||
return nil
|
||||
}//+n
|
||||
|
||||
func (o *{{.Name}}) UnmarshalXDR(bs []byte) error {
|
||||
return nil
|
||||
}//+n
|
||||
|
||||
func (o *{{.Name}}) UnmarshalXDRFrom(u *xdr.Unmarshaller) error {
|
||||
return nil
|
||||
}//+n
|
||||
`))
|
||||
|
||||
var maxRe = regexp.MustCompile(`(?:\Wmax:)(\d+)(?:\s*,\s*(\d+))?`)
|
||||
|
||||
type typeSet struct {
|
||||
Type string
|
||||
Encoder string
|
||||
}
|
||||
|
||||
var xdrEncoders = map[string]typeSet{
|
||||
"int8": typeSet{"uint8", "Uint8"},
|
||||
"uint8": typeSet{"", "Uint8"},
|
||||
"int16": typeSet{"uint16", "Uint16"},
|
||||
"uint16": typeSet{"", "Uint16"},
|
||||
"int32": typeSet{"uint32", "Uint32"},
|
||||
"uint32": typeSet{"", "Uint32"},
|
||||
"int64": typeSet{"uint64", "Uint64"},
|
||||
"uint64": typeSet{"", "Uint64"},
|
||||
"int": typeSet{"uint64", "Uint64"},
|
||||
"string": typeSet{"", "String"},
|
||||
"[]byte": typeSet{"", "Bytes"},
|
||||
"bool": typeSet{"", "Bool"},
|
||||
}
|
||||
|
||||
func handleStruct(t *ast.StructType) []fieldInfo {
|
||||
var fs []fieldInfo
|
||||
|
||||
for _, sf := range t.Fields.List {
|
||||
if len(sf.Names) == 0 {
|
||||
// We don't handle anonymous fields
|
||||
continue
|
||||
}
|
||||
|
||||
fn := sf.Names[0].Name
|
||||
var max1, max2 int
|
||||
if sf.Comment != nil {
|
||||
c := sf.Comment.List[0].Text
|
||||
m := maxRe.FindStringSubmatch(c)
|
||||
if len(m) >= 2 {
|
||||
max1, _ = strconv.Atoi(m[1])
|
||||
}
|
||||
if len(m) >= 3 {
|
||||
max2, _ = strconv.Atoi(m[2])
|
||||
}
|
||||
if strings.Contains(c, "noencode") {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
var f fieldInfo
|
||||
switch ft := sf.Type.(type) {
|
||||
case *ast.Ident:
|
||||
tn := ft.Name
|
||||
if enc, ok := xdrEncoders[tn]; ok {
|
||||
f = fieldInfo{
|
||||
Name: fn,
|
||||
IsBasic: true,
|
||||
FieldType: tn,
|
||||
Encoder: enc.Encoder,
|
||||
Convert: enc.Type,
|
||||
Max: max1,
|
||||
Submax: max2,
|
||||
}
|
||||
} else {
|
||||
f = fieldInfo{
|
||||
Name: fn,
|
||||
IsBasic: false,
|
||||
FieldType: tn,
|
||||
Max: max1,
|
||||
Submax: max2,
|
||||
}
|
||||
}
|
||||
|
||||
case *ast.ArrayType:
|
||||
if ft.Len != nil {
|
||||
// We don't handle arrays
|
||||
continue
|
||||
}
|
||||
|
||||
tn := ft.Elt.(*ast.Ident).Name
|
||||
if enc, ok := xdrEncoders["[]"+tn]; ok {
|
||||
f = fieldInfo{
|
||||
Name: fn,
|
||||
IsBasic: true,
|
||||
FieldType: "[]" + tn,
|
||||
Encoder: enc.Encoder,
|
||||
Convert: enc.Type,
|
||||
Max: max1,
|
||||
Submax: max2,
|
||||
}
|
||||
} else if enc, ok := xdrEncoders[tn]; ok {
|
||||
f = fieldInfo{
|
||||
Name: fn,
|
||||
IsBasic: true,
|
||||
IsSlice: true,
|
||||
FieldType: tn,
|
||||
Encoder: enc.Encoder,
|
||||
Convert: enc.Type,
|
||||
Max: max1,
|
||||
Submax: max2,
|
||||
}
|
||||
} else {
|
||||
f = fieldInfo{
|
||||
Name: fn,
|
||||
IsSlice: true,
|
||||
FieldType: tn,
|
||||
Max: max1,
|
||||
Submax: max2,
|
||||
}
|
||||
}
|
||||
|
||||
case *ast.SelectorExpr:
|
||||
f = fieldInfo{
|
||||
Name: fn,
|
||||
FieldType: ft.Sel.Name,
|
||||
Max: max1,
|
||||
Submax: max2,
|
||||
}
|
||||
}
|
||||
|
||||
fs = append(fs, f)
|
||||
}
|
||||
|
||||
return fs
|
||||
}
|
||||
|
||||
func generateCode(output io.Writer, s structInfo) {
|
||||
var buf bytes.Buffer
|
||||
var err error
|
||||
if len(s.Fields) == 0 {
|
||||
// This is an empty type. We can create a quite simple codec for it.
|
||||
err = emptyTypeTpl.Execute(&buf, s)
|
||||
} else {
|
||||
// Generate with the default template.
|
||||
err = encodeTpl.Execute(&buf, s)
|
||||
}
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
bs := regexp.MustCompile(`(\s*\n)+`).ReplaceAll(buf.Bytes(), []byte("\n"))
|
||||
bs = bytes.Replace(bs, []byte("//+n"), []byte("\n"), -1)
|
||||
output.Write(bs)
|
||||
}
|
||||
|
||||
func uncamelize(s string) string {
|
||||
return regexp.MustCompile("[a-z][A-Z]").ReplaceAllStringFunc(s, func(camel string) string {
|
||||
return camel[:1] + " " + camel[1:]
|
||||
})
|
||||
}
|
||||
|
||||
func generateDiagram(output io.Writer, s structInfo) {
|
||||
sn := s.Name
|
||||
fs := s.Fields
|
||||
|
||||
fmt.Fprintln(output, sn+" Structure:")
|
||||
|
||||
if len(fs) == 0 {
|
||||
fmt.Fprintln(output, "(contains no fields)")
|
||||
fmt.Fprintln(output)
|
||||
fmt.Fprintln(output)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Fprintln(output)
|
||||
fmt.Fprintln(output, " 0 1 2 3")
|
||||
fmt.Fprintln(output, " 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1")
|
||||
line := "+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+"
|
||||
fmt.Fprintln(output, line)
|
||||
|
||||
for _, f := range fs {
|
||||
tn := f.FieldType
|
||||
name := uncamelize(f.Name)
|
||||
|
||||
suffix := ""
|
||||
if f.IsSlice {
|
||||
fmt.Fprintf(output, "| %s |\n", center("Number of "+name, 61))
|
||||
fmt.Fprintln(output, line)
|
||||
suffix = " (n items)"
|
||||
fmt.Fprintf(output, "/ %s /\n", center("", 61))
|
||||
}
|
||||
switch tn {
|
||||
case "bool":
|
||||
fmt.Fprintf(output, "| %s |V|\n", center(name+" (V=0 or 1)", 59))
|
||||
case "int16", "uint16":
|
||||
fmt.Fprintf(output, "| %s | %s |\n", center("16 zero bits", 29), center(name, 29))
|
||||
case "int8", "uint8":
|
||||
fmt.Fprintf(output, "| %s | %s |\n", center("24 zero bits", 45), center(name, 13))
|
||||
case "int32", "uint32":
|
||||
fmt.Fprintf(output, "| %s |\n", center(name+suffix, 61))
|
||||
case "int64", "uint64":
|
||||
fmt.Fprintf(output, "| %-61s |\n", "")
|
||||
fmt.Fprintf(output, "+ %s +\n", center(name+" (64 bits)", 61))
|
||||
fmt.Fprintf(output, "| %-61s |\n", "")
|
||||
case "string", "[]byte":
|
||||
fmt.Fprintf(output, "/ %61s /\n", "")
|
||||
fmt.Fprintf(output, "\\ %s \\\n", center(name+" (length + padded data)", 61))
|
||||
fmt.Fprintf(output, "/ %61s /\n", "")
|
||||
default:
|
||||
if f.IsSlice {
|
||||
tn = "Zero or more " + tn + " Structures"
|
||||
fmt.Fprintf(output, "\\ %s \\\n", center(tn, 61))
|
||||
} else {
|
||||
tn = tn + " Structure"
|
||||
fmt.Fprintf(output, "/ %s /\n", center("", 61))
|
||||
fmt.Fprintf(output, "\\ %s \\\n", center(tn, 61))
|
||||
fmt.Fprintf(output, "/ %s /\n", center("", 61))
|
||||
}
|
||||
}
|
||||
|
||||
if f.IsSlice {
|
||||
fmt.Fprintf(output, "/ %s /\n", center("", 61))
|
||||
}
|
||||
fmt.Fprintln(output, line)
|
||||
}
|
||||
fmt.Fprintln(output)
|
||||
fmt.Fprintln(output)
|
||||
}
|
||||
|
||||
func generateXdr(output io.Writer, s structInfo) {
|
||||
sn := s.Name
|
||||
fs := s.Fields
|
||||
|
||||
fmt.Fprintf(output, "struct %s {\n", sn)
|
||||
|
||||
for _, f := range fs {
|
||||
tn := f.FieldType
|
||||
fn := f.Name
|
||||
suf := ""
|
||||
l := ""
|
||||
if f.Max > 0 {
|
||||
l = strconv.Itoa(f.Max)
|
||||
}
|
||||
if f.IsSlice {
|
||||
suf = "<" + l + ">"
|
||||
}
|
||||
|
||||
switch tn {
|
||||
case "int8", "int16", "int32":
|
||||
fmt.Fprintf(output, "\tint %s%s;\n", fn, suf)
|
||||
case "uint8", "uint16", "uint32":
|
||||
fmt.Fprintf(output, "\tunsigned int %s%s;\n", fn, suf)
|
||||
case "int64":
|
||||
fmt.Fprintf(output, "\thyper %s%s;\n", fn, suf)
|
||||
case "uint64":
|
||||
fmt.Fprintf(output, "\tunsigned hyper %s%s;\n", fn, suf)
|
||||
case "string":
|
||||
fmt.Fprintf(output, "\tstring %s<%s>;\n", fn, l)
|
||||
case "[]byte":
|
||||
fmt.Fprintf(output, "\topaque %s<%s>;\n", fn, l)
|
||||
default:
|
||||
fmt.Fprintf(output, "\t%s %s%s;\n", tn, fn, suf)
|
||||
}
|
||||
}
|
||||
fmt.Fprintln(output, "}")
|
||||
fmt.Fprintln(output)
|
||||
}
|
||||
|
||||
func center(s string, w int) string {
|
||||
w -= len(s)
|
||||
l := w / 2
|
||||
r := l
|
||||
if l+r < w {
|
||||
r++
|
||||
}
|
||||
return strings.Repeat(" ", l) + s + strings.Repeat(" ", r)
|
||||
}
|
||||
|
||||
func inspector(structs *[]structInfo) func(ast.Node) bool {
|
||||
return func(n ast.Node) bool {
|
||||
switch n := n.(type) {
|
||||
case *ast.TypeSpec:
|
||||
switch t := n.Type.(type) {
|
||||
case *ast.StructType:
|
||||
name := n.Name.Name
|
||||
fs := handleStruct(t)
|
||||
*structs = append(*structs, structInfo{name, fs})
|
||||
}
|
||||
return false
|
||||
default:
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
outputFile := flag.String("o", "", "Output file, blank for stdout")
|
||||
flag.Parse()
|
||||
fname := flag.Arg(0)
|
||||
|
||||
fset := token.NewFileSet()
|
||||
f, err := parser.ParseFile(fset, fname, nil, parser.ParseComments)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
var structs []structInfo
|
||||
i := inspector(&structs)
|
||||
ast.Inspect(f, i)
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
headerTpl.Execute(buf, map[string]string{"Package": f.Name.Name})
|
||||
for _, s := range structs {
|
||||
fmt.Fprintf(buf, "\n/*\n\n")
|
||||
generateDiagram(buf, s)
|
||||
generateXdr(buf, s)
|
||||
fmt.Fprintf(buf, "*/\n")
|
||||
generateCode(buf, s)
|
||||
}
|
||||
|
||||
bs, err := format.Source(buf.Bytes())
|
||||
if err != nil {
|
||||
log.Print(buf.String())
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
var output io.Writer = os.Stdout
|
||||
if *outputFile != "" {
|
||||
fd, err := os.Create(*outputFile)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
output = fd
|
||||
}
|
||||
output.Write(bs)
|
||||
}
|
||||
59
vendor/github.com/calmh/xdr/common.go
generated
vendored
Normal file
59
vendor/github.com/calmh/xdr/common.go
generated
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
// Copyright (C) 2014 Jakob Borg. All rights reserved. Use of this source code
|
||||
// is governed by an MIT-style license that can be found in the LICENSE file.
|
||||
|
||||
package xdr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
var padBytes = []byte{0, 0, 0}
|
||||
|
||||
// Pad returns the number of bytes that should be added to an item of length l
|
||||
// bytes to conform to the XDR padding standard. This function is used by the
|
||||
// generated marshalling code.
|
||||
func Padding(l int) int {
|
||||
d := l % 4
|
||||
if d == 0 {
|
||||
return 0
|
||||
}
|
||||
return 4 - d
|
||||
}
|
||||
|
||||
// ElementSizeExceeded returns an error describing the violated size
|
||||
// constraint. This function is used by the generated marshalling code.
|
||||
func ElementSizeExceeded(field string, size, limit int) error {
|
||||
return fmt.Errorf("%s exceeds size limit; %d > %d", field, size, limit)
|
||||
}
|
||||
|
||||
type XDRSizer interface {
|
||||
XDRSize() int
|
||||
}
|
||||
|
||||
// SizeOfSlice returns the XDR encoded size of the given []T. Supported types
|
||||
// for T are string, []byte and types implementing XDRSizer. SizeOfSlice
|
||||
// panics if the parameter is not a slice or if T is not one of the supported
|
||||
// types. This function is used by the generated marshalling code.
|
||||
func SizeOfSlice(ss interface{}) int {
|
||||
l := 0
|
||||
switch ss := ss.(type) {
|
||||
case []string:
|
||||
for _, s := range ss {
|
||||
l += 4 + len(s) + Padding(len(s))
|
||||
}
|
||||
|
||||
case [][]byte:
|
||||
for _, s := range ss {
|
||||
l += 4 + len(s) + Padding(len(s))
|
||||
}
|
||||
|
||||
default:
|
||||
v := reflect.ValueOf(ss)
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
l += v.Index(i).Interface().(XDRSizer).XDRSize()
|
||||
}
|
||||
}
|
||||
|
||||
return l
|
||||
}
|
||||
5
vendor/github.com/calmh/xdr/doc.go
generated
vendored
Normal file
5
vendor/github.com/calmh/xdr/doc.go
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
// Copyright (C) 2014 Jakob Borg. All rights reserved. Use of this source code
|
||||
// is governed by an MIT-style license that can be found in the LICENSE file.
|
||||
|
||||
// Package xdr implements an XDR (RFC 4506) marshaller/unmarshaller.
|
||||
package xdr
|
||||
241
vendor/github.com/calmh/xdr/encdec_test.go
generated
vendored
Normal file
241
vendor/github.com/calmh/xdr/encdec_test.go
generated
vendored
Normal file
@@ -0,0 +1,241 @@
|
||||
// Copyright (C) 2014 Jakob Borg. All rights reserved. Use of this source code
|
||||
// is governed by an MIT-style license that can be found in the LICENSE file.
|
||||
|
||||
package xdr_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"log"
|
||||
"math/rand"
|
||||
"reflect"
|
||||
"testing"
|
||||
"testing/quick"
|
||||
|
||||
"github.com/calmh/xdr"
|
||||
)
|
||||
|
||||
// Contains all supported types
|
||||
type TestStruct struct {
|
||||
B bool
|
||||
I int
|
||||
I8 int8
|
||||
UI8 uint8
|
||||
I16 int16
|
||||
UI16 uint16
|
||||
I32 int32
|
||||
UI32 uint32
|
||||
I64 int64
|
||||
UI64 uint64
|
||||
BS []byte // max:1024
|
||||
S string // max:1024
|
||||
C Opaque
|
||||
SS []string // max:1024
|
||||
ES EmptyStruct
|
||||
OS OtherStruct
|
||||
OSs []OtherStruct
|
||||
}
|
||||
|
||||
func (s1 TestStruct) TestEquals(s2 TestStruct) bool {
|
||||
if s1.B != s2.B {
|
||||
log.Printf("B differ; %v != %v", s1.B, s2.B)
|
||||
return false
|
||||
}
|
||||
if s1.I != s2.I {
|
||||
log.Printf("I differ; %d != %d", s1.I, s2.I)
|
||||
return false
|
||||
}
|
||||
if s1.I8 != s2.I8 {
|
||||
log.Printf("I8 differ; %d != %d", s1.I8, s2.I8)
|
||||
return false
|
||||
}
|
||||
if s1.UI8 != s2.UI8 {
|
||||
log.Printf("UI8 differ; %d != %d", s1.UI8, s2.UI8)
|
||||
return false
|
||||
}
|
||||
if s1.I16 != s2.I16 {
|
||||
log.Printf("I16 differ; %d != %d", s1.I16, s2.I16)
|
||||
return false
|
||||
}
|
||||
if s1.UI16 != s2.UI16 {
|
||||
log.Printf("UI16 differ; %d != %d", s1.UI16, s2.UI16)
|
||||
return false
|
||||
}
|
||||
if s1.I32 != s2.I32 {
|
||||
log.Printf("I32 differ; %d != %d", s1.I32, s2.I32)
|
||||
return false
|
||||
}
|
||||
if s1.UI32 != s2.UI32 {
|
||||
log.Printf("UI32 differ; %d != %d", s1.UI32, s2.UI32)
|
||||
return false
|
||||
}
|
||||
if s1.I64 != s2.I64 {
|
||||
log.Printf("I64 differ; %d != %d", s1.I64, s2.I64)
|
||||
return false
|
||||
}
|
||||
if s1.UI64 != s2.UI64 {
|
||||
log.Printf("UI64 differ; %d != %d", s1.UI64, s2.UI64)
|
||||
return false
|
||||
}
|
||||
if !bytes.Equal(s1.BS, s2.BS) {
|
||||
log.Println("BS differ")
|
||||
return false
|
||||
}
|
||||
if s1.S != s2.S {
|
||||
log.Printf("S differ; %q != %q", s1.S, s2.S)
|
||||
return false
|
||||
}
|
||||
if s1.C != s2.C {
|
||||
log.Printf("C differ; %q != %q", s1.C, s2.C)
|
||||
return false
|
||||
}
|
||||
if len(s1.SS) != len(s2.SS) {
|
||||
log.Printf("len(SS) differ; %q != %q", len(s1.SS), len(s2.SS))
|
||||
return false
|
||||
}
|
||||
for i := range s1.SS {
|
||||
if s1.SS[i] != s2.SS[i] {
|
||||
log.Printf("SS[%d] differ; %q != %q", i, s1.SS[i], s2.SS[i])
|
||||
return false
|
||||
}
|
||||
}
|
||||
if s1.OS != s2.OS {
|
||||
log.Printf("OS differ; %q != %q", s1.OS, s2.OS)
|
||||
return false
|
||||
}
|
||||
if len(s1.OSs) != len(s2.OSs) {
|
||||
log.Printf("len(OSs) differ; %q != %q", len(s1.OSs), len(s2.OSs))
|
||||
return false
|
||||
}
|
||||
for i := range s1.OSs {
|
||||
if s1.OSs[i] != s2.OSs[i] {
|
||||
log.Printf("OSs[%d] differ; %q != %q", i, s1.OSs[i], s2.OSs[i])
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
type EmptyStruct struct {
|
||||
}
|
||||
|
||||
type OtherStruct struct {
|
||||
F1 uint32
|
||||
F2 string
|
||||
}
|
||||
|
||||
type Opaque [32]byte
|
||||
|
||||
func (u *Opaque) XDRSize() int {
|
||||
return 32
|
||||
}
|
||||
|
||||
func (u *Opaque) MarshalXDRInto(m *xdr.Marshaller) error {
|
||||
m.MarshalRaw(u[:])
|
||||
return m.Error
|
||||
}
|
||||
|
||||
func (o *Opaque) UnmarshalXDRFrom(u *xdr.Unmarshaller) error {
|
||||
copy((*o)[:], u.UnmarshalRaw(32))
|
||||
return u.Error
|
||||
}
|
||||
|
||||
func (Opaque) Generate(rand *rand.Rand, size int) reflect.Value {
|
||||
var u Opaque
|
||||
for i := range u[:] {
|
||||
u[i] = byte(rand.Int())
|
||||
}
|
||||
return reflect.ValueOf(u)
|
||||
}
|
||||
|
||||
func TestEncDec(t *testing.T) {
|
||||
fn := func(t0 TestStruct) bool {
|
||||
bs, err := t0.MarshalXDR()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var t1 TestStruct
|
||||
err = t1.UnmarshalXDR(bs)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
return t0.TestEquals(t1)
|
||||
}
|
||||
if err := quick.Check(fn, nil); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarshalShortBuffer(t *testing.T) {
|
||||
var s TestStruct
|
||||
buf := make([]byte, s.XDRSize())
|
||||
if err := s.MarshalXDRInto(&xdr.Marshaller{Data: buf}); err != nil {
|
||||
t.Fatal("Unexpected error", err)
|
||||
}
|
||||
|
||||
if err := s.MarshalXDRInto(&xdr.Marshaller{Data: buf[1:]}); err != io.ErrShortBuffer {
|
||||
t.Fatal("Expected io.ErrShortBuffer, got", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnmarshalUnexpectedEOF(t *testing.T) {
|
||||
var s TestStruct
|
||||
buf := make([]byte, s.XDRSize())
|
||||
if err := s.MarshalXDRInto(&xdr.Marshaller{Data: buf}); err != nil {
|
||||
t.Fatal("Unexpected error", err)
|
||||
}
|
||||
|
||||
if err := s.UnmarshalXDR(buf[:len(buf)-1]); err != io.ErrUnexpectedEOF {
|
||||
t.Fatal("Expected io.ErrUnexpectedEOF, got", err)
|
||||
}
|
||||
|
||||
u := &xdr.Unmarshaller{Data: buf[:3]}
|
||||
u.UnmarshalRaw(4)
|
||||
if err := u.Error; err != io.ErrUnexpectedEOF {
|
||||
t.Fatal("Expected io.ErrUnexpectedEOF, got", err)
|
||||
}
|
||||
|
||||
u = &xdr.Unmarshaller{Data: buf[:3]}
|
||||
u.UnmarshalString()
|
||||
if err := u.Error; err != io.ErrUnexpectedEOF {
|
||||
t.Fatal("Expected io.ErrUnexpectedEOF, got", err)
|
||||
}
|
||||
|
||||
u = &xdr.Unmarshaller{Data: buf[:3]}
|
||||
u.UnmarshalBytes()
|
||||
if err := u.Error; err != io.ErrUnexpectedEOF {
|
||||
t.Fatal("Expected io.ErrUnexpectedEOF, got", err)
|
||||
}
|
||||
|
||||
u = &xdr.Unmarshaller{Data: buf[:3]}
|
||||
u.UnmarshalBool()
|
||||
if err := u.Error; err != io.ErrUnexpectedEOF {
|
||||
t.Fatal("Expected io.ErrUnexpectedEOF, got", err)
|
||||
}
|
||||
|
||||
u = &xdr.Unmarshaller{Data: buf[:3]}
|
||||
u.UnmarshalUint8()
|
||||
if err := u.Error; err != io.ErrUnexpectedEOF {
|
||||
t.Fatal("Expected io.ErrUnexpectedEOF, got", err)
|
||||
}
|
||||
|
||||
u = &xdr.Unmarshaller{Data: buf[:3]}
|
||||
u.UnmarshalUint16()
|
||||
if err := u.Error; err != io.ErrUnexpectedEOF {
|
||||
t.Fatal("Expected io.ErrUnexpectedEOF, got", err)
|
||||
}
|
||||
|
||||
u = &xdr.Unmarshaller{Data: buf[:3]}
|
||||
u.UnmarshalUint32()
|
||||
if err := u.Error; err != io.ErrUnexpectedEOF {
|
||||
t.Fatal("Expected io.ErrUnexpectedEOF, got", err)
|
||||
}
|
||||
|
||||
u = &xdr.Unmarshaller{Data: buf[:7]}
|
||||
u.UnmarshalUint64()
|
||||
if err := u.Error; err != io.ErrUnexpectedEOF {
|
||||
t.Fatal("Expected io.ErrUnexpectedEOF, got", err)
|
||||
}
|
||||
}
|
||||
319
vendor/github.com/calmh/xdr/encdec_xdr_test.go
generated
vendored
Normal file
319
vendor/github.com/calmh/xdr/encdec_xdr_test.go
generated
vendored
Normal file
@@ -0,0 +1,319 @@
|
||||
// ************************************************************
|
||||
// This file is automatically generated by genxdr. Do not edit.
|
||||
// ************************************************************
|
||||
|
||||
package xdr_test
|
||||
|
||||
import (
|
||||
"github.com/calmh/xdr"
|
||||
)
|
||||
|
||||
/*
|
||||
|
||||
TestStruct Structure:
|
||||
|
||||
0 1 2 3
|
||||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| B (V=0 or 1) |V|
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
/ /
|
||||
\ int Structure \
|
||||
/ /
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| 24 zero bits | I8 |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| 24 zero bits | UI8 |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| 16 zero bits | I16 |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| 16 zero bits | UI16 |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| I32 |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| UI32 |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| |
|
||||
+ I64 (64 bits) +
|
||||
| |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| |
|
||||
+ UI64 (64 bits) +
|
||||
| |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
/ /
|
||||
\ BS (length + padded data) \
|
||||
/ /
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
/ /
|
||||
\ S (length + padded data) \
|
||||
/ /
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
/ /
|
||||
\ Opaque Structure \
|
||||
/ /
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Number of SS |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
/ /
|
||||
/ /
|
||||
\ SS (length + padded data) \
|
||||
/ /
|
||||
/ /
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
/ /
|
||||
\ EmptyStruct Structure \
|
||||
/ /
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
/ /
|
||||
\ OtherStruct Structure \
|
||||
/ /
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Number of OSs |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
/ /
|
||||
\ Zero or more OtherStruct Structures \
|
||||
/ /
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|
||||
|
||||
struct TestStruct {
|
||||
bool B;
|
||||
int I;
|
||||
int I8;
|
||||
unsigned int UI8;
|
||||
int I16;
|
||||
unsigned int UI16;
|
||||
int I32;
|
||||
unsigned int UI32;
|
||||
hyper I64;
|
||||
unsigned hyper UI64;
|
||||
opaque BS<1024>;
|
||||
string S<1024>;
|
||||
Opaque C;
|
||||
string SS<1024>;
|
||||
EmptyStruct ES;
|
||||
OtherStruct OS;
|
||||
OtherStruct OSs<>;
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
func (o TestStruct) XDRSize() int {
|
||||
return 4 + 8 + 4 + 4 + 4 + 4 + 4 + 4 + 8 + 8 +
|
||||
4 + len(o.BS) + xdr.Padding(len(o.BS)) +
|
||||
4 + len(o.S) + xdr.Padding(len(o.S)) +
|
||||
o.C.XDRSize() +
|
||||
4 + xdr.SizeOfSlice(o.SS) +
|
||||
o.ES.XDRSize() +
|
||||
o.OS.XDRSize() +
|
||||
4 + xdr.SizeOfSlice(o.OSs)
|
||||
}
|
||||
|
||||
func (o TestStruct) MarshalXDR() ([]byte, error) {
|
||||
buf := make([]byte, o.XDRSize())
|
||||
m := &xdr.Marshaller{Data: buf}
|
||||
return buf, o.MarshalXDRInto(m)
|
||||
}
|
||||
|
||||
func (o TestStruct) MustMarshalXDR() []byte {
|
||||
bs, err := o.MarshalXDR()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return bs
|
||||
}
|
||||
|
||||
func (o TestStruct) MarshalXDRInto(m *xdr.Marshaller) error {
|
||||
m.MarshalBool(o.B)
|
||||
m.MarshalUint64(uint64(o.I))
|
||||
m.MarshalUint8(uint8(o.I8))
|
||||
m.MarshalUint8(o.UI8)
|
||||
m.MarshalUint16(uint16(o.I16))
|
||||
m.MarshalUint16(o.UI16)
|
||||
m.MarshalUint32(uint32(o.I32))
|
||||
m.MarshalUint32(o.UI32)
|
||||
m.MarshalUint64(uint64(o.I64))
|
||||
m.MarshalUint64(o.UI64)
|
||||
if l := len(o.BS); l > 1024 {
|
||||
return xdr.ElementSizeExceeded("BS", l, 1024)
|
||||
}
|
||||
m.MarshalBytes(o.BS)
|
||||
if l := len(o.S); l > 1024 {
|
||||
return xdr.ElementSizeExceeded("S", l, 1024)
|
||||
}
|
||||
m.MarshalString(o.S)
|
||||
if err := o.C.MarshalXDRInto(m); err != nil {
|
||||
return err
|
||||
}
|
||||
if l := len(o.SS); l > 1024 {
|
||||
return xdr.ElementSizeExceeded("SS", l, 1024)
|
||||
}
|
||||
m.MarshalUint32(uint32(len(o.SS)))
|
||||
for i := range o.SS {
|
||||
m.MarshalString(o.SS[i])
|
||||
}
|
||||
if err := o.ES.MarshalXDRInto(m); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := o.OS.MarshalXDRInto(m); err != nil {
|
||||
return err
|
||||
}
|
||||
m.MarshalUint32(uint32(len(o.OSs)))
|
||||
for i := range o.OSs {
|
||||
if err := o.OSs[i].MarshalXDRInto(m); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return m.Error
|
||||
}
|
||||
|
||||
func (o *TestStruct) UnmarshalXDR(bs []byte) error {
|
||||
u := &xdr.Unmarshaller{Data: bs}
|
||||
return o.UnmarshalXDRFrom(u)
|
||||
}
|
||||
func (o *TestStruct) UnmarshalXDRFrom(u *xdr.Unmarshaller) error {
|
||||
o.B = u.UnmarshalBool()
|
||||
o.I = int(u.UnmarshalUint64())
|
||||
o.I8 = int8(u.UnmarshalUint8())
|
||||
o.UI8 = u.UnmarshalUint8()
|
||||
o.I16 = int16(u.UnmarshalUint16())
|
||||
o.UI16 = u.UnmarshalUint16()
|
||||
o.I32 = int32(u.UnmarshalUint32())
|
||||
o.UI32 = u.UnmarshalUint32()
|
||||
o.I64 = int64(u.UnmarshalUint64())
|
||||
o.UI64 = u.UnmarshalUint64()
|
||||
o.BS = u.UnmarshalBytesMax(1024)
|
||||
o.S = u.UnmarshalStringMax(1024)
|
||||
(&o.C).UnmarshalXDRFrom(u)
|
||||
_SSSize := int(u.UnmarshalUint32())
|
||||
if _SSSize < 0 {
|
||||
return xdr.ElementSizeExceeded("SS", _SSSize, 1024)
|
||||
} else if _SSSize == 0 {
|
||||
o.SS = nil
|
||||
} else {
|
||||
if _SSSize > 1024 {
|
||||
return xdr.ElementSizeExceeded("SS", _SSSize, 1024)
|
||||
}
|
||||
if _SSSize <= len(o.SS) {
|
||||
for i := _SSSize; i < len(o.SS); i++ {
|
||||
o.SS[i] = ""
|
||||
}
|
||||
o.SS = o.SS[:_SSSize]
|
||||
} else {
|
||||
o.SS = make([]string, _SSSize)
|
||||
}
|
||||
for i := range o.SS {
|
||||
o.SS[i] = u.UnmarshalString()
|
||||
}
|
||||
}
|
||||
(&o.ES).UnmarshalXDRFrom(u)
|
||||
(&o.OS).UnmarshalXDRFrom(u)
|
||||
_OSsSize := int(u.UnmarshalUint32())
|
||||
if _OSsSize < 0 {
|
||||
return xdr.ElementSizeExceeded("OSs", _OSsSize, 0)
|
||||
} else if _OSsSize == 0 {
|
||||
o.OSs = nil
|
||||
} else {
|
||||
if _OSsSize <= len(o.OSs) {
|
||||
o.OSs = o.OSs[:_OSsSize]
|
||||
} else {
|
||||
o.OSs = make([]OtherStruct, _OSsSize)
|
||||
}
|
||||
for i := range o.OSs {
|
||||
(&o.OSs[i]).UnmarshalXDRFrom(u)
|
||||
}
|
||||
}
|
||||
return u.Error
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
EmptyStruct Structure:
|
||||
(contains no fields)
|
||||
|
||||
|
||||
struct EmptyStruct {
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
func (o EmptyStruct) XDRSize() int {
|
||||
return 0
|
||||
}
|
||||
func (o EmptyStruct) MarshalXDR() ([]byte, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (o EmptyStruct) MustMarshalXDR() []byte {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o EmptyStruct) MarshalXDRInto(m *xdr.Marshaller) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *EmptyStruct) UnmarshalXDR(bs []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *EmptyStruct) UnmarshalXDRFrom(u *xdr.Unmarshaller) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
OtherStruct Structure:
|
||||
|
||||
0 1 2 3
|
||||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| F1 |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
/ /
|
||||
\ F2 (length + padded data) \
|
||||
/ /
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|
||||
|
||||
struct OtherStruct {
|
||||
unsigned int F1;
|
||||
string F2<>;
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
func (o OtherStruct) XDRSize() int {
|
||||
return 4 +
|
||||
4 + len(o.F2) + xdr.Padding(len(o.F2))
|
||||
}
|
||||
|
||||
func (o OtherStruct) MarshalXDR() ([]byte, error) {
|
||||
buf := make([]byte, o.XDRSize())
|
||||
m := &xdr.Marshaller{Data: buf}
|
||||
return buf, o.MarshalXDRInto(m)
|
||||
}
|
||||
|
||||
func (o OtherStruct) MustMarshalXDR() []byte {
|
||||
bs, err := o.MarshalXDR()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return bs
|
||||
}
|
||||
|
||||
func (o OtherStruct) MarshalXDRInto(m *xdr.Marshaller) error {
|
||||
m.MarshalUint32(o.F1)
|
||||
m.MarshalString(o.F2)
|
||||
return m.Error
|
||||
}
|
||||
|
||||
func (o *OtherStruct) UnmarshalXDR(bs []byte) error {
|
||||
u := &xdr.Unmarshaller{Data: bs}
|
||||
return o.UnmarshalXDRFrom(u)
|
||||
}
|
||||
func (o *OtherStruct) UnmarshalXDRFrom(u *xdr.Unmarshaller) error {
|
||||
o.F1 = u.UnmarshalUint32()
|
||||
o.F2 = u.UnmarshalString()
|
||||
return u.Error
|
||||
}
|
||||
4
vendor/github.com/calmh/xdr/generate.sh
generated
vendored
Normal file
4
vendor/github.com/calmh/xdr/generate.sh
generated
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
#!/bin/sh
|
||||
|
||||
go run cmd/genxdr/main.go -- bench_test.go > bench_xdr_test.go
|
||||
go run cmd/genxdr/main.go -- encdec_test.go > encdec_xdr_test.go
|
||||
123
vendor/github.com/calmh/xdr/marshaller.go
generated
vendored
Normal file
123
vendor/github.com/calmh/xdr/marshaller.go
generated
vendored
Normal file
@@ -0,0 +1,123 @@
|
||||
// Copyright (C) 2014 Jakob Borg. All rights reserved. Use of this source code
|
||||
// is governed by an MIT-style license that can be found in the LICENSE file.
|
||||
|
||||
package xdr
|
||||
|
||||
import "io"
|
||||
|
||||
// The Marshaller is a thin wrapper around a byte buffer. The buffer must be
|
||||
// of sufficient size to hold the complete marshalled object, or an
|
||||
// io.ErrShortBuffer error will result. The Marshal... methods don't
|
||||
// individually return an error - the intention is that multiple fields are
|
||||
// marshalled in rapid succession, followed by a check of the Error field on
|
||||
// the Marshaller.
|
||||
type Marshaller struct {
|
||||
Data []byte
|
||||
Error error
|
||||
|
||||
offset int
|
||||
}
|
||||
|
||||
// MarshalRaw copies the raw bytes to the buffer, without a size prefix or
|
||||
// padding. This is suitable for appending data already in XDR format from
|
||||
// another source.
|
||||
func (m *Marshaller) MarshalRaw(bs []byte) {
|
||||
if m.Error != nil {
|
||||
return
|
||||
}
|
||||
if len(m.Data) < m.offset+len(bs) {
|
||||
m.Error = io.ErrShortBuffer
|
||||
return
|
||||
}
|
||||
|
||||
m.offset += copy(m.Data[m.offset:], bs)
|
||||
}
|
||||
|
||||
// MarshalString appends the string to the buffer, with a size prefix and
|
||||
// correct padding.
|
||||
func (m *Marshaller) MarshalString(s string) {
|
||||
if m.Error != nil {
|
||||
return
|
||||
}
|
||||
if len(m.Data) < m.offset+4+len(s)+Padding(len(s)) {
|
||||
m.Error = io.ErrShortBuffer
|
||||
return
|
||||
}
|
||||
|
||||
m.MarshalUint32(uint32(len(s)))
|
||||
m.offset += copy(m.Data[m.offset:], s)
|
||||
m.offset += copy(m.Data[m.offset:], padBytes[:Padding(len(s))])
|
||||
}
|
||||
|
||||
// MarshalString appends the bytes to the buffer, with a size prefix and
|
||||
// correct padding.
|
||||
func (m *Marshaller) MarshalBytes(bs []byte) {
|
||||
if m.Error != nil {
|
||||
return
|
||||
}
|
||||
if len(m.Data) < m.offset+4+len(bs)+Padding(len(bs)) {
|
||||
m.Error = io.ErrShortBuffer
|
||||
return
|
||||
}
|
||||
|
||||
m.MarshalUint32(uint32(len(bs)))
|
||||
m.offset += copy(m.Data[m.offset:], bs)
|
||||
m.offset += copy(m.Data[m.offset:], padBytes[:Padding(len(bs))])
|
||||
}
|
||||
|
||||
// MarshalString appends the bool to the buffer, as an uint32.
|
||||
func (m *Marshaller) MarshalBool(v bool) {
|
||||
if v {
|
||||
m.MarshalUint8(1)
|
||||
} else {
|
||||
m.MarshalUint8(0)
|
||||
}
|
||||
}
|
||||
|
||||
// MarshalString appends the uint8 to the buffer, as an uint32.
|
||||
func (m *Marshaller) MarshalUint8(v uint8) {
|
||||
m.MarshalUint32(uint32(v))
|
||||
}
|
||||
|
||||
// MarshalString appends the uint16 to the buffer, as an uint32.
|
||||
func (m *Marshaller) MarshalUint16(v uint16) {
|
||||
m.MarshalUint32(uint32(v))
|
||||
}
|
||||
|
||||
// MarshalString appends the uint32 to the buffer.
|
||||
func (m *Marshaller) MarshalUint32(v uint32) {
|
||||
if m.Error != nil {
|
||||
return
|
||||
}
|
||||
if len(m.Data) < m.offset+4 {
|
||||
m.Error = io.ErrShortBuffer
|
||||
return
|
||||
}
|
||||
|
||||
m.Data[m.offset+0] = byte(v >> 24)
|
||||
m.Data[m.offset+1] = byte(v >> 16)
|
||||
m.Data[m.offset+2] = byte(v >> 8)
|
||||
m.Data[m.offset+3] = byte(v)
|
||||
m.offset += 4
|
||||
}
|
||||
|
||||
// MarshalString appends the uint64 to the buffer.
|
||||
func (m *Marshaller) MarshalUint64(v uint64) {
|
||||
if m.Error != nil {
|
||||
return
|
||||
}
|
||||
if len(m.Data) < m.offset+8 {
|
||||
m.Error = io.ErrShortBuffer
|
||||
return
|
||||
}
|
||||
|
||||
m.Data[m.offset+0] = byte(v >> 56)
|
||||
m.Data[m.offset+1] = byte(v >> 48)
|
||||
m.Data[m.offset+2] = byte(v >> 40)
|
||||
m.Data[m.offset+3] = byte(v >> 32)
|
||||
m.Data[m.offset+4] = byte(v >> 24)
|
||||
m.Data[m.offset+5] = byte(v >> 16)
|
||||
m.Data[m.offset+6] = byte(v >> 8)
|
||||
m.Data[m.offset+7] = byte(v)
|
||||
m.offset += 8
|
||||
}
|
||||
147
vendor/github.com/calmh/xdr/unmarshal.go
generated
vendored
Normal file
147
vendor/github.com/calmh/xdr/unmarshal.go
generated
vendored
Normal file
@@ -0,0 +1,147 @@
|
||||
// Copyright (C) 2014 Jakob Borg and Contributors (see the CONTRIBUTORS file).
|
||||
// All rights reserved. Use of this source code is governed by an MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package xdr
|
||||
|
||||
import (
|
||||
"io"
|
||||
"reflect"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type Unmarshaller struct {
|
||||
Error error
|
||||
Data []byte
|
||||
}
|
||||
|
||||
func (u *Unmarshaller) UnmarshalRaw(l int) []byte {
|
||||
if u.Error != nil {
|
||||
return nil
|
||||
}
|
||||
if len(u.Data) < l {
|
||||
u.Error = io.ErrUnexpectedEOF
|
||||
return nil
|
||||
}
|
||||
|
||||
v := u.Data[:l]
|
||||
u.Data = u.Data[l:]
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
func (u *Unmarshaller) UnmarshalString() string {
|
||||
return u.UnmarshalStringMax(0)
|
||||
}
|
||||
|
||||
func (u *Unmarshaller) UnmarshalStringMax(max int) string {
|
||||
buf := u.UnmarshalBytesMax(max)
|
||||
if len(buf) == 0 || u.Error != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
var v string
|
||||
p := (*reflect.StringHeader)(unsafe.Pointer(&v))
|
||||
p.Data = uintptr(unsafe.Pointer(&buf[0]))
|
||||
p.Len = len(buf)
|
||||
return v
|
||||
}
|
||||
|
||||
func (u *Unmarshaller) UnmarshalBytes() []byte {
|
||||
return u.UnmarshalBytesMax(0)
|
||||
}
|
||||
|
||||
func (u *Unmarshaller) UnmarshalBytesMax(max int) []byte {
|
||||
if u.Error != nil {
|
||||
return nil
|
||||
}
|
||||
if len(u.Data) < 4 {
|
||||
u.Error = io.ErrUnexpectedEOF
|
||||
return nil
|
||||
}
|
||||
|
||||
l := int(u.Data[3]) | int(u.Data[2])<<8 | int(u.Data[1])<<16 | int(u.Data[0])<<24
|
||||
if l == 0 {
|
||||
u.Data = u.Data[4:]
|
||||
return nil
|
||||
}
|
||||
if l < 0 || max > 0 && l > max {
|
||||
// l may be negative on 32 bit builds
|
||||
u.Error = ElementSizeExceeded("bytes field", l, max)
|
||||
return nil
|
||||
}
|
||||
if len(u.Data) < l+4 {
|
||||
u.Error = io.ErrUnexpectedEOF
|
||||
return nil
|
||||
}
|
||||
|
||||
v := u.Data[4 : 4+l]
|
||||
u.Data = u.Data[4+l+Padding(l):]
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
func (u *Unmarshaller) UnmarshalBool() bool {
|
||||
return u.UnmarshalUint8() != 0
|
||||
}
|
||||
|
||||
func (u *Unmarshaller) UnmarshalUint8() uint8 {
|
||||
if u.Error != nil {
|
||||
return 0
|
||||
}
|
||||
if len(u.Data) < 4 {
|
||||
u.Error = io.ErrUnexpectedEOF
|
||||
return 0
|
||||
}
|
||||
|
||||
v := uint8(u.Data[3])
|
||||
u.Data = u.Data[4:]
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
func (u *Unmarshaller) UnmarshalUint16() uint16 {
|
||||
if u.Error != nil {
|
||||
return 0
|
||||
}
|
||||
if len(u.Data) < 4 {
|
||||
u.Error = io.ErrUnexpectedEOF
|
||||
return 0
|
||||
}
|
||||
|
||||
v := uint16(u.Data[3]) | uint16(u.Data[2])<<8
|
||||
u.Data = u.Data[4:]
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
func (u *Unmarshaller) UnmarshalUint32() uint32 {
|
||||
if u.Error != nil {
|
||||
return 0
|
||||
}
|
||||
if len(u.Data) < 4 {
|
||||
u.Error = io.ErrUnexpectedEOF
|
||||
return 0
|
||||
}
|
||||
|
||||
v := uint32(u.Data[3]) | uint32(u.Data[2])<<8 | uint32(u.Data[1])<<16 | uint32(u.Data[0])<<24
|
||||
u.Data = u.Data[4:]
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
func (u *Unmarshaller) UnmarshalUint64() uint64 {
|
||||
if u.Error != nil {
|
||||
return 0
|
||||
}
|
||||
if len(u.Data) < 8 {
|
||||
u.Error = io.ErrUnexpectedEOF
|
||||
return 0
|
||||
}
|
||||
|
||||
v := uint64(u.Data[7]) | uint64(u.Data[6])<<8 | uint64(u.Data[5])<<16 | uint64(u.Data[4])<<24 |
|
||||
uint64(u.Data[3])<<32 | uint64(u.Data[2])<<40 | uint64(u.Data[1])<<48 | uint64(u.Data[0])<<56
|
||||
u.Data = u.Data[8:]
|
||||
|
||||
return v
|
||||
}
|
||||
15
vendor/github.com/golang/snappy/AUTHORS
generated
vendored
Normal file
15
vendor/github.com/golang/snappy/AUTHORS
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
# This is the official list of Snappy-Go authors for copyright purposes.
|
||||
# This file is distinct from the CONTRIBUTORS files.
|
||||
# See the latter for an explanation.
|
||||
|
||||
# Names should be added to this file as
|
||||
# Name or Organization <email address>
|
||||
# The email address is not required for organizations.
|
||||
|
||||
# Please keep the list sorted.
|
||||
|
||||
Damian Gryski <dgryski@gmail.com>
|
||||
Google Inc.
|
||||
Jan Mercl <0xjnml@gmail.com>
|
||||
Rodolfo Carvalho <rhcarvalho@gmail.com>
|
||||
Sebastien Binet <seb.binet@gmail.com>
|
||||
37
vendor/github.com/golang/snappy/CONTRIBUTORS
generated
vendored
Normal file
37
vendor/github.com/golang/snappy/CONTRIBUTORS
generated
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
# This is the official list of people who can contribute
|
||||
# (and typically have contributed) code to the Snappy-Go repository.
|
||||
# The AUTHORS file lists the copyright holders; this file
|
||||
# lists people. For example, Google employees are listed here
|
||||
# but not in AUTHORS, because Google holds the copyright.
|
||||
#
|
||||
# The submission process automatically checks to make sure
|
||||
# that people submitting code are listed in this file (by email address).
|
||||
#
|
||||
# Names should be added to this file only after verifying that
|
||||
# the individual or the individual's organization has agreed to
|
||||
# the appropriate Contributor License Agreement, found here:
|
||||
#
|
||||
# http://code.google.com/legal/individual-cla-v1.0.html
|
||||
# http://code.google.com/legal/corporate-cla-v1.0.html
|
||||
#
|
||||
# The agreement for individuals can be filled out on the web.
|
||||
#
|
||||
# When adding J Random Contributor's name to this file,
|
||||
# either J's name or J's organization's name should be
|
||||
# added to the AUTHORS file, depending on whether the
|
||||
# individual or corporate CLA was used.
|
||||
|
||||
# Names should be added to this file like so:
|
||||
# Name <email address>
|
||||
|
||||
# Please keep the list sorted.
|
||||
|
||||
Damian Gryski <dgryski@gmail.com>
|
||||
Jan Mercl <0xjnml@gmail.com>
|
||||
Kai Backman <kaib@golang.org>
|
||||
Marc-Antoine Ruel <maruel@chromium.org>
|
||||
Nigel Tao <nigeltao@golang.org>
|
||||
Rob Pike <r@golang.org>
|
||||
Rodolfo Carvalho <rhcarvalho@gmail.com>
|
||||
Russ Cox <rsc@golang.org>
|
||||
Sebastien Binet <seb.binet@gmail.com>
|
||||
27
vendor/github.com/golang/snappy/LICENSE
generated
vendored
Normal file
27
vendor/github.com/golang/snappy/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
Copyright (c) 2011 The Snappy-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.
|
||||
7
vendor/github.com/golang/snappy/README
generated
vendored
Normal file
7
vendor/github.com/golang/snappy/README
generated
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
The Snappy compression format in the Go programming language.
|
||||
|
||||
To download and install from source:
|
||||
$ go get github.com/golang/snappy
|
||||
|
||||
Unless otherwise noted, the Snappy-Go source files are distributed
|
||||
under the BSD-style license found in the LICENSE file.
|
||||
74
vendor/github.com/golang/snappy/cmd/snappytool/main.cpp
generated
vendored
Normal file
74
vendor/github.com/golang/snappy/cmd/snappytool/main.cpp
generated
vendored
Normal file
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
To build the snappytool binary:
|
||||
g++ main.cpp /usr/lib/libsnappy.a -o snappytool
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "snappy.h"
|
||||
|
||||
#define N 1000000
|
||||
|
||||
char dst[N];
|
||||
char src[N];
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
// Parse args.
|
||||
if (argc != 2) {
|
||||
fprintf(stderr, "exactly one of -d or -e must be given\n");
|
||||
return 1;
|
||||
}
|
||||
bool decode = strcmp(argv[1], "-d") == 0;
|
||||
bool encode = strcmp(argv[1], "-e") == 0;
|
||||
if (decode == encode) {
|
||||
fprintf(stderr, "exactly one of -d or -e must be given\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Read all of stdin into src[:s].
|
||||
size_t s = 0;
|
||||
while (1) {
|
||||
if (s == N) {
|
||||
fprintf(stderr, "input too large\n");
|
||||
return 1;
|
||||
}
|
||||
ssize_t n = read(0, src+s, N-s);
|
||||
if (n == 0) {
|
||||
break;
|
||||
}
|
||||
if (n < 0) {
|
||||
fprintf(stderr, "read error: %s\n", strerror(errno));
|
||||
// TODO: handle EAGAIN, EINTR?
|
||||
return 1;
|
||||
}
|
||||
s += n;
|
||||
}
|
||||
|
||||
// Encode or decode src[:s] to dst[:d], and write to stdout.
|
||||
size_t d = 0;
|
||||
if (encode) {
|
||||
if (N < snappy::MaxCompressedLength(s)) {
|
||||
fprintf(stderr, "input too large after encoding\n");
|
||||
return 1;
|
||||
}
|
||||
snappy::RawCompress(src, s, dst, &d);
|
||||
} else {
|
||||
if (!snappy::GetUncompressedLength(src, s, &d)) {
|
||||
fprintf(stderr, "could not get uncompressed length\n");
|
||||
return 1;
|
||||
}
|
||||
if (N < d) {
|
||||
fprintf(stderr, "input too large after decoding\n");
|
||||
return 1;
|
||||
}
|
||||
if (!snappy::RawUncompress(src, s, dst)) {
|
||||
fprintf(stderr, "input was not valid Snappy-compressed data\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
write(1, dst, d);
|
||||
return 0;
|
||||
}
|
||||
237
vendor/github.com/golang/snappy/decode.go
generated
vendored
Normal file
237
vendor/github.com/golang/snappy/decode.go
generated
vendored
Normal file
@@ -0,0 +1,237 @@
|
||||
// Copyright 2011 The Snappy-Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package snappy
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"io"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrCorrupt reports that the input is invalid.
|
||||
ErrCorrupt = errors.New("snappy: corrupt input")
|
||||
// ErrTooLarge reports that the uncompressed length is too large.
|
||||
ErrTooLarge = errors.New("snappy: decoded block is too large")
|
||||
// ErrUnsupported reports that the input isn't supported.
|
||||
ErrUnsupported = errors.New("snappy: unsupported input")
|
||||
|
||||
errUnsupportedCopy4Tag = errors.New("snappy: unsupported COPY_4 tag")
|
||||
errUnsupportedLiteralLength = errors.New("snappy: unsupported literal length")
|
||||
)
|
||||
|
||||
// DecodedLen returns the length of the decoded block.
|
||||
func DecodedLen(src []byte) (int, error) {
|
||||
v, _, err := decodedLen(src)
|
||||
return v, err
|
||||
}
|
||||
|
||||
// decodedLen returns the length of the decoded block and the number of bytes
|
||||
// that the length header occupied.
|
||||
func decodedLen(src []byte) (blockLen, headerLen int, err error) {
|
||||
v, n := binary.Uvarint(src)
|
||||
if n <= 0 || v > 0xffffffff {
|
||||
return 0, 0, ErrCorrupt
|
||||
}
|
||||
|
||||
const wordSize = 32 << (^uint(0) >> 32 & 1)
|
||||
if wordSize == 32 && v > 0x7fffffff {
|
||||
return 0, 0, ErrTooLarge
|
||||
}
|
||||
return int(v), n, nil
|
||||
}
|
||||
|
||||
const (
|
||||
decodeErrCodeCorrupt = 1
|
||||
decodeErrCodeUnsupportedLiteralLength = 2
|
||||
decodeErrCodeUnsupportedCopy4Tag = 3
|
||||
)
|
||||
|
||||
// Decode returns the decoded form of src. The returned slice may be a sub-
|
||||
// slice of dst if dst was large enough to hold the entire decoded block.
|
||||
// Otherwise, a newly allocated slice will be returned.
|
||||
//
|
||||
// The dst and src must not overlap. It is valid to pass a nil dst.
|
||||
func Decode(dst, src []byte) ([]byte, error) {
|
||||
dLen, s, err := decodedLen(src)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if dLen <= len(dst) {
|
||||
dst = dst[:dLen]
|
||||
} else {
|
||||
dst = make([]byte, dLen)
|
||||
}
|
||||
switch decode(dst, src[s:]) {
|
||||
case 0:
|
||||
return dst, nil
|
||||
case decodeErrCodeUnsupportedLiteralLength:
|
||||
return nil, errUnsupportedLiteralLength
|
||||
case decodeErrCodeUnsupportedCopy4Tag:
|
||||
return nil, errUnsupportedCopy4Tag
|
||||
}
|
||||
return nil, ErrCorrupt
|
||||
}
|
||||
|
||||
// NewReader returns a new Reader that decompresses from r, using the framing
|
||||
// format described at
|
||||
// https://github.com/google/snappy/blob/master/framing_format.txt
|
||||
func NewReader(r io.Reader) *Reader {
|
||||
return &Reader{
|
||||
r: r,
|
||||
decoded: make([]byte, maxBlockSize),
|
||||
buf: make([]byte, maxEncodedLenOfMaxBlockSize+checksumSize),
|
||||
}
|
||||
}
|
||||
|
||||
// Reader is an io.Reader that can read Snappy-compressed bytes.
|
||||
type Reader struct {
|
||||
r io.Reader
|
||||
err error
|
||||
decoded []byte
|
||||
buf []byte
|
||||
// decoded[i:j] contains decoded bytes that have not yet been passed on.
|
||||
i, j int
|
||||
readHeader bool
|
||||
}
|
||||
|
||||
// Reset discards any buffered data, resets all state, and switches the Snappy
|
||||
// reader to read from r. This permits reusing a Reader rather than allocating
|
||||
// a new one.
|
||||
func (r *Reader) Reset(reader io.Reader) {
|
||||
r.r = reader
|
||||
r.err = nil
|
||||
r.i = 0
|
||||
r.j = 0
|
||||
r.readHeader = false
|
||||
}
|
||||
|
||||
func (r *Reader) readFull(p []byte) (ok bool) {
|
||||
if _, r.err = io.ReadFull(r.r, p); r.err != nil {
|
||||
if r.err == io.ErrUnexpectedEOF {
|
||||
r.err = ErrCorrupt
|
||||
}
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Read satisfies the io.Reader interface.
|
||||
func (r *Reader) Read(p []byte) (int, error) {
|
||||
if r.err != nil {
|
||||
return 0, r.err
|
||||
}
|
||||
for {
|
||||
if r.i < r.j {
|
||||
n := copy(p, r.decoded[r.i:r.j])
|
||||
r.i += n
|
||||
return n, nil
|
||||
}
|
||||
if !r.readFull(r.buf[:4]) {
|
||||
return 0, r.err
|
||||
}
|
||||
chunkType := r.buf[0]
|
||||
if !r.readHeader {
|
||||
if chunkType != chunkTypeStreamIdentifier {
|
||||
r.err = ErrCorrupt
|
||||
return 0, r.err
|
||||
}
|
||||
r.readHeader = true
|
||||
}
|
||||
chunkLen := int(r.buf[1]) | int(r.buf[2])<<8 | int(r.buf[3])<<16
|
||||
if chunkLen > len(r.buf) {
|
||||
r.err = ErrUnsupported
|
||||
return 0, r.err
|
||||
}
|
||||
|
||||
// The chunk types are specified at
|
||||
// https://github.com/google/snappy/blob/master/framing_format.txt
|
||||
switch chunkType {
|
||||
case chunkTypeCompressedData:
|
||||
// Section 4.2. Compressed data (chunk type 0x00).
|
||||
if chunkLen < checksumSize {
|
||||
r.err = ErrCorrupt
|
||||
return 0, r.err
|
||||
}
|
||||
buf := r.buf[:chunkLen]
|
||||
if !r.readFull(buf) {
|
||||
return 0, r.err
|
||||
}
|
||||
checksum := uint32(buf[0]) | uint32(buf[1])<<8 | uint32(buf[2])<<16 | uint32(buf[3])<<24
|
||||
buf = buf[checksumSize:]
|
||||
|
||||
n, err := DecodedLen(buf)
|
||||
if err != nil {
|
||||
r.err = err
|
||||
return 0, r.err
|
||||
}
|
||||
if n > len(r.decoded) {
|
||||
r.err = ErrCorrupt
|
||||
return 0, r.err
|
||||
}
|
||||
if _, err := Decode(r.decoded, buf); err != nil {
|
||||
r.err = err
|
||||
return 0, r.err
|
||||
}
|
||||
if crc(r.decoded[:n]) != checksum {
|
||||
r.err = ErrCorrupt
|
||||
return 0, r.err
|
||||
}
|
||||
r.i, r.j = 0, n
|
||||
continue
|
||||
|
||||
case chunkTypeUncompressedData:
|
||||
// Section 4.3. Uncompressed data (chunk type 0x01).
|
||||
if chunkLen < checksumSize {
|
||||
r.err = ErrCorrupt
|
||||
return 0, r.err
|
||||
}
|
||||
buf := r.buf[:checksumSize]
|
||||
if !r.readFull(buf) {
|
||||
return 0, r.err
|
||||
}
|
||||
checksum := uint32(buf[0]) | uint32(buf[1])<<8 | uint32(buf[2])<<16 | uint32(buf[3])<<24
|
||||
// Read directly into r.decoded instead of via r.buf.
|
||||
n := chunkLen - checksumSize
|
||||
if !r.readFull(r.decoded[:n]) {
|
||||
return 0, r.err
|
||||
}
|
||||
if crc(r.decoded[:n]) != checksum {
|
||||
r.err = ErrCorrupt
|
||||
return 0, r.err
|
||||
}
|
||||
r.i, r.j = 0, n
|
||||
continue
|
||||
|
||||
case chunkTypeStreamIdentifier:
|
||||
// Section 4.1. Stream identifier (chunk type 0xff).
|
||||
if chunkLen != len(magicBody) {
|
||||
r.err = ErrCorrupt
|
||||
return 0, r.err
|
||||
}
|
||||
if !r.readFull(r.buf[:len(magicBody)]) {
|
||||
return 0, r.err
|
||||
}
|
||||
for i := 0; i < len(magicBody); i++ {
|
||||
if r.buf[i] != magicBody[i] {
|
||||
r.err = ErrCorrupt
|
||||
return 0, r.err
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if chunkType <= 0x7f {
|
||||
// Section 4.5. Reserved unskippable chunks (chunk types 0x02-0x7f).
|
||||
r.err = ErrUnsupported
|
||||
return 0, r.err
|
||||
}
|
||||
// Section 4.4 Padding (chunk type 0xfe).
|
||||
// Section 4.6. Reserved skippable chunks (chunk types 0x80-0xfd).
|
||||
if !r.readFull(r.buf[:chunkLen]) {
|
||||
return 0, r.err
|
||||
}
|
||||
}
|
||||
}
|
||||
10
vendor/github.com/golang/snappy/decode_amd64.go
generated
vendored
Normal file
10
vendor/github.com/golang/snappy/decode_amd64.go
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
// Copyright 2016 The Snappy-Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package snappy
|
||||
|
||||
// decode has the same semantics as in decode_other.go.
|
||||
//
|
||||
//go:noescape
|
||||
func decode(dst, src []byte) int
|
||||
472
vendor/github.com/golang/snappy/decode_amd64.s
generated
vendored
Normal file
472
vendor/github.com/golang/snappy/decode_amd64.s
generated
vendored
Normal file
@@ -0,0 +1,472 @@
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
// func decode(dst, src []byte) int
|
||||
//
|
||||
// The asm code generally follows the pure Go code in decode_other.go, except
|
||||
// where marked with a "!!!".
|
||||
//
|
||||
// All local variables fit into registers. The non-zero stack size is only to
|
||||
// spill registers and push args when issuing a CALL. The register allocation:
|
||||
// - AX scratch
|
||||
// - BX scratch
|
||||
// - CX length or x
|
||||
// - DX offset
|
||||
// - SI &src[s]
|
||||
// - DI &dst[d]
|
||||
// + R8 dst_base
|
||||
// + R9 dst_len
|
||||
// + R10 dst_base + dst_len
|
||||
// + R11 src_base
|
||||
// + R12 src_len
|
||||
// + R13 src_base + src_len
|
||||
// - R14 used by doCopy
|
||||
// - R15 used by doCopy
|
||||
//
|
||||
// The registers R8-R13 (marked with a "+") are set at the start of the
|
||||
// function, and after a CALL returns, and are not otherwise modified.
|
||||
//
|
||||
// The d variable is implicitly DI - R8, and len(dst)-d is R10 - DI.
|
||||
// The s variable is implicitly SI - R11, and len(src)-s is R13 - SI.
|
||||
TEXT ·decode(SB), NOSPLIT, $48-56
|
||||
// Initialize SI, DI and R8-R13.
|
||||
MOVQ dst_base+0(FP), R8
|
||||
MOVQ dst_len+8(FP), R9
|
||||
MOVQ R8, DI
|
||||
MOVQ R8, R10
|
||||
ADDQ R9, R10
|
||||
MOVQ src_base+24(FP), R11
|
||||
MOVQ src_len+32(FP), R12
|
||||
MOVQ R11, SI
|
||||
MOVQ R11, R13
|
||||
ADDQ R12, R13
|
||||
|
||||
loop:
|
||||
// for s < len(src)
|
||||
CMPQ SI, R13
|
||||
JEQ end
|
||||
|
||||
// CX = uint32(src[s])
|
||||
//
|
||||
// switch src[s] & 0x03
|
||||
MOVBLZX (SI), CX
|
||||
MOVL CX, BX
|
||||
ANDL $3, BX
|
||||
CMPL BX, $1
|
||||
JAE tagCopy
|
||||
|
||||
// ----------------------------------------
|
||||
// The code below handles literal tags.
|
||||
|
||||
// case tagLiteral:
|
||||
// x := uint32(src[s] >> 2)
|
||||
// switch
|
||||
SHRL $2, CX
|
||||
CMPL CX, $60
|
||||
JAE tagLit60Plus
|
||||
|
||||
// case x < 60:
|
||||
// s++
|
||||
INCQ SI
|
||||
|
||||
doLit:
|
||||
// This is the end of the inner "switch", when we have a literal tag.
|
||||
//
|
||||
// We assume that CX == x and x fits in a uint32, where x is the variable
|
||||
// used in the pure Go decode_other.go code.
|
||||
|
||||
// length = int(x) + 1
|
||||
//
|
||||
// Unlike the pure Go code, we don't need to check if length <= 0 because
|
||||
// CX can hold 64 bits, so the increment cannot overflow.
|
||||
INCQ CX
|
||||
|
||||
// Prepare to check if copying length bytes will run past the end of dst or
|
||||
// src.
|
||||
//
|
||||
// AX = len(dst) - d
|
||||
// BX = len(src) - s
|
||||
MOVQ R10, AX
|
||||
SUBQ DI, AX
|
||||
MOVQ R13, BX
|
||||
SUBQ SI, BX
|
||||
|
||||
// !!! Try a faster technique for short (16 or fewer bytes) copies.
|
||||
//
|
||||
// if length > 16 || len(dst)-d < 16 || len(src)-s < 16 {
|
||||
// goto callMemmove // Fall back on calling runtime·memmove.
|
||||
// }
|
||||
//
|
||||
// The C++ snappy code calls this TryFastAppend. It also checks len(src)-s
|
||||
// against 21 instead of 16, because it cannot assume that all of its input
|
||||
// is contiguous in memory and so it needs to leave enough source bytes to
|
||||
// read the next tag without refilling buffers, but Go's Decode assumes
|
||||
// contiguousness (the src argument is a []byte).
|
||||
CMPQ CX, $16
|
||||
JGT callMemmove
|
||||
CMPQ AX, $16
|
||||
JLT callMemmove
|
||||
CMPQ BX, $16
|
||||
JLT callMemmove
|
||||
|
||||
// !!! Implement the copy from src to dst as a 16-byte load and store.
|
||||
// (Decode's documentation says that dst and src must not overlap.)
|
||||
//
|
||||
// This always copies 16 bytes, instead of only length bytes, but that's
|
||||
// OK. If the input is a valid Snappy encoding then subsequent iterations
|
||||
// will fix up the overrun. Otherwise, Decode returns a nil []byte (and a
|
||||
// non-nil error), so the overrun will be ignored.
|
||||
//
|
||||
// Note that on amd64, it is legal and cheap to issue unaligned 8-byte or
|
||||
// 16-byte loads and stores. This technique probably wouldn't be as
|
||||
// effective on architectures that are fussier about alignment.
|
||||
MOVOU 0(SI), X0
|
||||
MOVOU X0, 0(DI)
|
||||
|
||||
// d += length
|
||||
// s += length
|
||||
ADDQ CX, DI
|
||||
ADDQ CX, SI
|
||||
JMP loop
|
||||
|
||||
callMemmove:
|
||||
// if length > len(dst)-d || length > len(src)-s { etc }
|
||||
CMPQ CX, AX
|
||||
JGT errCorrupt
|
||||
CMPQ CX, BX
|
||||
JGT errCorrupt
|
||||
|
||||
// copy(dst[d:], src[s:s+length])
|
||||
//
|
||||
// This means calling runtime·memmove(&dst[d], &src[s], length), so we push
|
||||
// DI, SI and CX as arguments. Coincidentally, we also need to spill those
|
||||
// three registers to the stack, to save local variables across the CALL.
|
||||
MOVQ DI, 0(SP)
|
||||
MOVQ SI, 8(SP)
|
||||
MOVQ CX, 16(SP)
|
||||
MOVQ DI, 24(SP)
|
||||
MOVQ SI, 32(SP)
|
||||
MOVQ CX, 40(SP)
|
||||
CALL runtime·memmove(SB)
|
||||
|
||||
// Restore local variables: unspill registers from the stack and
|
||||
// re-calculate R8-R13.
|
||||
MOVQ 24(SP), DI
|
||||
MOVQ 32(SP), SI
|
||||
MOVQ 40(SP), CX
|
||||
MOVQ dst_base+0(FP), R8
|
||||
MOVQ dst_len+8(FP), R9
|
||||
MOVQ R8, R10
|
||||
ADDQ R9, R10
|
||||
MOVQ src_base+24(FP), R11
|
||||
MOVQ src_len+32(FP), R12
|
||||
MOVQ R11, R13
|
||||
ADDQ R12, R13
|
||||
|
||||
// d += length
|
||||
// s += length
|
||||
ADDQ CX, DI
|
||||
ADDQ CX, SI
|
||||
JMP loop
|
||||
|
||||
tagLit60Plus:
|
||||
// !!! This fragment does the
|
||||
//
|
||||
// s += x - 58; if uint(s) > uint(len(src)) { etc }
|
||||
//
|
||||
// checks. In the asm version, we code it once instead of once per switch case.
|
||||
ADDQ CX, SI
|
||||
SUBQ $58, SI
|
||||
MOVQ SI, BX
|
||||
SUBQ R11, BX
|
||||
CMPQ BX, R12
|
||||
JA errCorrupt
|
||||
|
||||
// case x == 60:
|
||||
CMPL CX, $61
|
||||
JEQ tagLit61
|
||||
JA tagLit62Plus
|
||||
|
||||
// x = uint32(src[s-1])
|
||||
MOVBLZX -1(SI), CX
|
||||
JMP doLit
|
||||
|
||||
tagLit61:
|
||||
// case x == 61:
|
||||
// x = uint32(src[s-2]) | uint32(src[s-1])<<8
|
||||
MOVWLZX -2(SI), CX
|
||||
JMP doLit
|
||||
|
||||
tagLit62Plus:
|
||||
CMPL CX, $62
|
||||
JA tagLit63
|
||||
|
||||
// case x == 62:
|
||||
// x = uint32(src[s-3]) | uint32(src[s-2])<<8 | uint32(src[s-1])<<16
|
||||
MOVWLZX -3(SI), CX
|
||||
MOVBLZX -1(SI), BX
|
||||
SHLL $16, BX
|
||||
ORL BX, CX
|
||||
JMP doLit
|
||||
|
||||
tagLit63:
|
||||
// case x == 63:
|
||||
// x = uint32(src[s-4]) | uint32(src[s-3])<<8 | uint32(src[s-2])<<16 | uint32(src[s-1])<<24
|
||||
MOVL -4(SI), CX
|
||||
JMP doLit
|
||||
|
||||
// The code above handles literal tags.
|
||||
// ----------------------------------------
|
||||
// The code below handles copy tags.
|
||||
|
||||
tagCopy2:
|
||||
// case tagCopy2:
|
||||
// s += 3
|
||||
ADDQ $3, SI
|
||||
|
||||
// if uint(s) > uint(len(src)) { etc }
|
||||
MOVQ SI, BX
|
||||
SUBQ R11, BX
|
||||
CMPQ BX, R12
|
||||
JA errCorrupt
|
||||
|
||||
// length = 1 + int(src[s-3])>>2
|
||||
SHRQ $2, CX
|
||||
INCQ CX
|
||||
|
||||
// offset = int(src[s-2]) | int(src[s-1])<<8
|
||||
MOVWQZX -2(SI), DX
|
||||
JMP doCopy
|
||||
|
||||
tagCopy:
|
||||
// We have a copy tag. We assume that:
|
||||
// - BX == src[s] & 0x03
|
||||
// - CX == src[s]
|
||||
CMPQ BX, $2
|
||||
JEQ tagCopy2
|
||||
JA errUC4T
|
||||
|
||||
// case tagCopy1:
|
||||
// s += 2
|
||||
ADDQ $2, SI
|
||||
|
||||
// if uint(s) > uint(len(src)) { etc }
|
||||
MOVQ SI, BX
|
||||
SUBQ R11, BX
|
||||
CMPQ BX, R12
|
||||
JA errCorrupt
|
||||
|
||||
// offset = int(src[s-2])&0xe0<<3 | int(src[s-1])
|
||||
MOVQ CX, DX
|
||||
ANDQ $0xe0, DX
|
||||
SHLQ $3, DX
|
||||
MOVBQZX -1(SI), BX
|
||||
ORQ BX, DX
|
||||
|
||||
// length = 4 + int(src[s-2])>>2&0x7
|
||||
SHRQ $2, CX
|
||||
ANDQ $7, CX
|
||||
ADDQ $4, CX
|
||||
|
||||
doCopy:
|
||||
// This is the end of the outer "switch", when we have a copy tag.
|
||||
//
|
||||
// We assume that:
|
||||
// - CX == length && CX > 0
|
||||
// - DX == offset
|
||||
|
||||
// if offset <= 0 { etc }
|
||||
CMPQ DX, $0
|
||||
JLE errCorrupt
|
||||
|
||||
// if d < offset { etc }
|
||||
MOVQ DI, BX
|
||||
SUBQ R8, BX
|
||||
CMPQ BX, DX
|
||||
JLT errCorrupt
|
||||
|
||||
// if length > len(dst)-d { etc }
|
||||
MOVQ R10, BX
|
||||
SUBQ DI, BX
|
||||
CMPQ CX, BX
|
||||
JGT errCorrupt
|
||||
|
||||
// forwardCopy(dst[d:d+length], dst[d-offset:]); d += length
|
||||
//
|
||||
// Set:
|
||||
// - R14 = len(dst)-d
|
||||
// - R15 = &dst[d-offset]
|
||||
MOVQ R10, R14
|
||||
SUBQ DI, R14
|
||||
MOVQ DI, R15
|
||||
SUBQ DX, R15
|
||||
|
||||
// !!! Try a faster technique for short (16 or fewer bytes) forward copies.
|
||||
//
|
||||
// First, try using two 8-byte load/stores, similar to the doLit technique
|
||||
// above. Even if dst[d:d+length] and dst[d-offset:] can overlap, this is
|
||||
// still OK if offset >= 8. Note that this has to be two 8-byte load/stores
|
||||
// and not one 16-byte load/store, and the first store has to be before the
|
||||
// second load, due to the overlap if offset is in the range [8, 16).
|
||||
//
|
||||
// if length > 16 || offset < 8 || len(dst)-d < 16 {
|
||||
// goto slowForwardCopy
|
||||
// }
|
||||
// copy 16 bytes
|
||||
// d += length
|
||||
CMPQ CX, $16
|
||||
JGT slowForwardCopy
|
||||
CMPQ DX, $8
|
||||
JLT slowForwardCopy
|
||||
CMPQ R14, $16
|
||||
JLT slowForwardCopy
|
||||
MOVQ 0(R15), AX
|
||||
MOVQ AX, 0(DI)
|
||||
MOVQ 8(R15), BX
|
||||
MOVQ BX, 8(DI)
|
||||
ADDQ CX, DI
|
||||
JMP loop
|
||||
|
||||
slowForwardCopy:
|
||||
// !!! If the forward copy is longer than 16 bytes, or if offset < 8, we
|
||||
// can still try 8-byte load stores, provided we can overrun up to 10 extra
|
||||
// bytes. As above, the overrun will be fixed up by subsequent iterations
|
||||
// of the outermost loop.
|
||||
//
|
||||
// The C++ snappy code calls this technique IncrementalCopyFastPath. Its
|
||||
// commentary says:
|
||||
//
|
||||
// ----
|
||||
//
|
||||
// The main part of this loop is a simple copy of eight bytes at a time
|
||||
// until we've copied (at least) the requested amount of bytes. However,
|
||||
// if d and d-offset are less than eight bytes apart (indicating a
|
||||
// repeating pattern of length < 8), we first need to expand the pattern in
|
||||
// order to get the correct results. For instance, if the buffer looks like
|
||||
// this, with the eight-byte <d-offset> and <d> patterns marked as
|
||||
// intervals:
|
||||
//
|
||||
// abxxxxxxxxxxxx
|
||||
// [------] d-offset
|
||||
// [------] d
|
||||
//
|
||||
// a single eight-byte copy from <d-offset> to <d> will repeat the pattern
|
||||
// once, after which we can move <d> two bytes without moving <d-offset>:
|
||||
//
|
||||
// ababxxxxxxxxxx
|
||||
// [------] d-offset
|
||||
// [------] d
|
||||
//
|
||||
// and repeat the exercise until the two no longer overlap.
|
||||
//
|
||||
// This allows us to do very well in the special case of one single byte
|
||||
// repeated many times, without taking a big hit for more general cases.
|
||||
//
|
||||
// The worst case of extra writing past the end of the match occurs when
|
||||
// offset == 1 and length == 1; the last copy will read from byte positions
|
||||
// [0..7] and write to [4..11], whereas it was only supposed to write to
|
||||
// position 1. Thus, ten excess bytes.
|
||||
//
|
||||
// ----
|
||||
//
|
||||
// That "10 byte overrun" worst case is confirmed by Go's
|
||||
// TestSlowForwardCopyOverrun, which also tests the fixUpSlowForwardCopy
|
||||
// and finishSlowForwardCopy algorithm.
|
||||
//
|
||||
// if length > len(dst)-d-10 {
|
||||
// goto verySlowForwardCopy
|
||||
// }
|
||||
SUBQ $10, R14
|
||||
CMPQ CX, R14
|
||||
JGT verySlowForwardCopy
|
||||
|
||||
makeOffsetAtLeast8:
|
||||
// !!! As above, expand the pattern so that offset >= 8 and we can use
|
||||
// 8-byte load/stores.
|
||||
//
|
||||
// for offset < 8 {
|
||||
// copy 8 bytes from dst[d-offset:] to dst[d:]
|
||||
// length -= offset
|
||||
// d += offset
|
||||
// offset += offset
|
||||
// // The two previous lines together means that d-offset, and therefore
|
||||
// // R15, is unchanged.
|
||||
// }
|
||||
CMPQ DX, $8
|
||||
JGE fixUpSlowForwardCopy
|
||||
MOVQ (R15), BX
|
||||
MOVQ BX, (DI)
|
||||
SUBQ DX, CX
|
||||
ADDQ DX, DI
|
||||
ADDQ DX, DX
|
||||
JMP makeOffsetAtLeast8
|
||||
|
||||
fixUpSlowForwardCopy:
|
||||
// !!! Add length (which might be negative now) to d (implied by DI being
|
||||
// &dst[d]) so that d ends up at the right place when we jump back to the
|
||||
// top of the loop. Before we do that, though, we save DI to AX so that, if
|
||||
// length is positive, copying the remaining length bytes will write to the
|
||||
// right place.
|
||||
MOVQ DI, AX
|
||||
ADDQ CX, DI
|
||||
|
||||
finishSlowForwardCopy:
|
||||
// !!! Repeat 8-byte load/stores until length <= 0. Ending with a negative
|
||||
// length means that we overrun, but as above, that will be fixed up by
|
||||
// subsequent iterations of the outermost loop.
|
||||
CMPQ CX, $0
|
||||
JLE loop
|
||||
MOVQ (R15), BX
|
||||
MOVQ BX, (AX)
|
||||
ADDQ $8, R15
|
||||
ADDQ $8, AX
|
||||
SUBQ $8, CX
|
||||
JMP finishSlowForwardCopy
|
||||
|
||||
verySlowForwardCopy:
|
||||
// verySlowForwardCopy is a simple implementation of forward copy. In C
|
||||
// parlance, this is a do/while loop instead of a while loop, since we know
|
||||
// that length > 0. In Go syntax:
|
||||
//
|
||||
// for {
|
||||
// dst[d] = dst[d - offset]
|
||||
// d++
|
||||
// length--
|
||||
// if length == 0 {
|
||||
// break
|
||||
// }
|
||||
// }
|
||||
MOVB (R15), BX
|
||||
MOVB BX, (DI)
|
||||
INCQ R15
|
||||
INCQ DI
|
||||
DECQ CX
|
||||
JNZ verySlowForwardCopy
|
||||
JMP loop
|
||||
|
||||
// The code above handles copy tags.
|
||||
// ----------------------------------------
|
||||
|
||||
end:
|
||||
// This is the end of the "for s < len(src)".
|
||||
//
|
||||
// if d != len(dst) { etc }
|
||||
CMPQ DI, R10
|
||||
JNE errCorrupt
|
||||
|
||||
// return 0
|
||||
MOVQ $0, ret+48(FP)
|
||||
RET
|
||||
|
||||
errCorrupt:
|
||||
// return decodeErrCodeCorrupt
|
||||
MOVQ $1, ret+48(FP)
|
||||
RET
|
||||
|
||||
errUC4T:
|
||||
// return decodeErrCodeUnsupportedCopy4Tag
|
||||
MOVQ $3, ret+48(FP)
|
||||
RET
|
||||
96
vendor/github.com/golang/snappy/decode_other.go
generated
vendored
Normal file
96
vendor/github.com/golang/snappy/decode_other.go
generated
vendored
Normal file
@@ -0,0 +1,96 @@
|
||||
// Copyright 2016 The Snappy-Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !amd64
|
||||
|
||||
package snappy
|
||||
|
||||
// decode writes the decoding of src to dst. It assumes that the varint-encoded
|
||||
// length of the decompressed bytes has already been read, and that len(dst)
|
||||
// equals that length.
|
||||
//
|
||||
// It returns 0 on success or a decodeErrCodeXxx error code on failure.
|
||||
func decode(dst, src []byte) int {
|
||||
var d, s, offset, length int
|
||||
for s < len(src) {
|
||||
switch src[s] & 0x03 {
|
||||
case tagLiteral:
|
||||
x := uint32(src[s] >> 2)
|
||||
switch {
|
||||
case x < 60:
|
||||
s++
|
||||
case x == 60:
|
||||
s += 2
|
||||
if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line.
|
||||
return decodeErrCodeCorrupt
|
||||
}
|
||||
x = uint32(src[s-1])
|
||||
case x == 61:
|
||||
s += 3
|
||||
if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line.
|
||||
return decodeErrCodeCorrupt
|
||||
}
|
||||
x = uint32(src[s-2]) | uint32(src[s-1])<<8
|
||||
case x == 62:
|
||||
s += 4
|
||||
if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line.
|
||||
return decodeErrCodeCorrupt
|
||||
}
|
||||
x = uint32(src[s-3]) | uint32(src[s-2])<<8 | uint32(src[s-1])<<16
|
||||
case x == 63:
|
||||
s += 5
|
||||
if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line.
|
||||
return decodeErrCodeCorrupt
|
||||
}
|
||||
x = uint32(src[s-4]) | uint32(src[s-3])<<8 | uint32(src[s-2])<<16 | uint32(src[s-1])<<24
|
||||
}
|
||||
length = int(x) + 1
|
||||
if length <= 0 {
|
||||
return decodeErrCodeUnsupportedLiteralLength
|
||||
}
|
||||
if length > len(dst)-d || length > len(src)-s {
|
||||
return decodeErrCodeCorrupt
|
||||
}
|
||||
copy(dst[d:], src[s:s+length])
|
||||
d += length
|
||||
s += length
|
||||
continue
|
||||
|
||||
case tagCopy1:
|
||||
s += 2
|
||||
if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line.
|
||||
return decodeErrCodeCorrupt
|
||||
}
|
||||
length = 4 + int(src[s-2])>>2&0x7
|
||||
offset = int(src[s-2])&0xe0<<3 | int(src[s-1])
|
||||
|
||||
case tagCopy2:
|
||||
s += 3
|
||||
if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line.
|
||||
return decodeErrCodeCorrupt
|
||||
}
|
||||
length = 1 + int(src[s-3])>>2
|
||||
offset = int(src[s-2]) | int(src[s-1])<<8
|
||||
|
||||
case tagCopy4:
|
||||
return decodeErrCodeUnsupportedCopy4Tag
|
||||
}
|
||||
|
||||
if offset <= 0 || d < offset || length > len(dst)-d {
|
||||
return decodeErrCodeCorrupt
|
||||
}
|
||||
// Copy from an earlier sub-slice of dst to a later sub-slice. Unlike
|
||||
// the built-in copy function, this byte-by-byte copy always runs
|
||||
// forwards, even if the slices overlap. Conceptually, this is:
|
||||
//
|
||||
// d += forwardCopy(dst[d:d+length], dst[d-offset:])
|
||||
for end := d + length; d != end; d++ {
|
||||
dst[d] = dst[d-offset]
|
||||
}
|
||||
}
|
||||
if d != len(dst) {
|
||||
return decodeErrCodeCorrupt
|
||||
}
|
||||
return 0
|
||||
}
|
||||
403
vendor/github.com/golang/snappy/encode.go
generated
vendored
Normal file
403
vendor/github.com/golang/snappy/encode.go
generated
vendored
Normal file
@@ -0,0 +1,403 @@
|
||||
// Copyright 2011 The Snappy-Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package snappy
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"io"
|
||||
)
|
||||
|
||||
// maxOffset limits how far copy back-references can go, the same as the C++
|
||||
// code.
|
||||
const maxOffset = 1 << 15
|
||||
|
||||
// emitLiteral writes a literal chunk and returns the number of bytes written.
|
||||
func emitLiteral(dst, lit []byte) int {
|
||||
i, n := 0, uint(len(lit)-1)
|
||||
switch {
|
||||
case n < 60:
|
||||
dst[0] = uint8(n)<<2 | tagLiteral
|
||||
i = 1
|
||||
case n < 1<<8:
|
||||
dst[0] = 60<<2 | tagLiteral
|
||||
dst[1] = uint8(n)
|
||||
i = 2
|
||||
case n < 1<<16:
|
||||
dst[0] = 61<<2 | tagLiteral
|
||||
dst[1] = uint8(n)
|
||||
dst[2] = uint8(n >> 8)
|
||||
i = 3
|
||||
case n < 1<<24:
|
||||
dst[0] = 62<<2 | tagLiteral
|
||||
dst[1] = uint8(n)
|
||||
dst[2] = uint8(n >> 8)
|
||||
dst[3] = uint8(n >> 16)
|
||||
i = 4
|
||||
case int64(n) < 1<<32:
|
||||
dst[0] = 63<<2 | tagLiteral
|
||||
dst[1] = uint8(n)
|
||||
dst[2] = uint8(n >> 8)
|
||||
dst[3] = uint8(n >> 16)
|
||||
dst[4] = uint8(n >> 24)
|
||||
i = 5
|
||||
default:
|
||||
panic("snappy: source buffer is too long")
|
||||
}
|
||||
if copy(dst[i:], lit) != len(lit) {
|
||||
panic("snappy: destination buffer is too short")
|
||||
}
|
||||
return i + len(lit)
|
||||
}
|
||||
|
||||
// emitCopy writes a copy chunk and returns the number of bytes written.
|
||||
func emitCopy(dst []byte, offset, length int32) int {
|
||||
i := 0
|
||||
for length > 0 {
|
||||
x := length - 4
|
||||
if 0 <= x && x < 1<<3 && offset < 1<<11 {
|
||||
dst[i+0] = uint8(offset>>8)&0x07<<5 | uint8(x)<<2 | tagCopy1
|
||||
dst[i+1] = uint8(offset)
|
||||
i += 2
|
||||
break
|
||||
}
|
||||
|
||||
x = length
|
||||
if x > 1<<6 {
|
||||
x = 1 << 6
|
||||
}
|
||||
dst[i+0] = uint8(x-1)<<2 | tagCopy2
|
||||
dst[i+1] = uint8(offset)
|
||||
dst[i+2] = uint8(offset >> 8)
|
||||
i += 3
|
||||
length -= x
|
||||
}
|
||||
return i
|
||||
}
|
||||
|
||||
// Encode returns the encoded form of src. The returned slice may be a sub-
|
||||
// slice of dst if dst was large enough to hold the entire encoded block.
|
||||
// Otherwise, a newly allocated slice will be returned.
|
||||
//
|
||||
// It is valid to pass a nil dst.
|
||||
func Encode(dst, src []byte) []byte {
|
||||
if n := MaxEncodedLen(len(src)); n < 0 {
|
||||
panic(ErrTooLarge)
|
||||
} else if len(dst) < n {
|
||||
dst = make([]byte, n)
|
||||
}
|
||||
|
||||
// The block starts with the varint-encoded length of the decompressed bytes.
|
||||
d := binary.PutUvarint(dst, uint64(len(src)))
|
||||
|
||||
for len(src) > 0 {
|
||||
p := src
|
||||
src = nil
|
||||
if len(p) > maxBlockSize {
|
||||
p, src = p[:maxBlockSize], p[maxBlockSize:]
|
||||
}
|
||||
d += encodeBlock(dst[d:], p)
|
||||
}
|
||||
return dst[:d]
|
||||
}
|
||||
|
||||
// encodeBlock encodes a non-empty src to a guaranteed-large-enough dst. It
|
||||
// assumes that the varint-encoded length of the decompressed bytes has already
|
||||
// been written.
|
||||
//
|
||||
// It also assumes that:
|
||||
// len(dst) >= MaxEncodedLen(len(src)) &&
|
||||
// 0 < len(src) && len(src) <= maxBlockSize
|
||||
func encodeBlock(dst, src []byte) (d int) {
|
||||
// Return early if src is short.
|
||||
if len(src) <= 4 {
|
||||
return emitLiteral(dst, src)
|
||||
}
|
||||
|
||||
// Initialize the hash table. Its size ranges from 1<<8 to 1<<14 inclusive.
|
||||
const maxTableSize = 1 << 14
|
||||
shift, tableSize := uint(32-8), 1<<8
|
||||
for tableSize < maxTableSize && tableSize < len(src) {
|
||||
shift--
|
||||
tableSize *= 2
|
||||
}
|
||||
var table [maxTableSize]int32
|
||||
|
||||
// Iterate over the source bytes.
|
||||
var (
|
||||
s int32 // The iterator position.
|
||||
t int32 // The last position with the same hash as s.
|
||||
lit int32 // The start position of any pending literal bytes.
|
||||
|
||||
// Copied from the C++ snappy implementation:
|
||||
//
|
||||
// Heuristic match skipping: If 32 bytes are scanned with no matches
|
||||
// found, start looking only at every other byte. If 32 more bytes are
|
||||
// scanned, look at every third byte, etc.. When a match is found,
|
||||
// immediately go back to looking at every byte. This is a small loss
|
||||
// (~5% performance, ~0.1% density) for compressible data due to more
|
||||
// bookkeeping, but for non-compressible data (such as JPEG) it's a
|
||||
// huge win since the compressor quickly "realizes" the data is
|
||||
// incompressible and doesn't bother looking for matches everywhere.
|
||||
//
|
||||
// The "skip" variable keeps track of how many bytes there are since
|
||||
// the last match; dividing it by 32 (ie. right-shifting by five) gives
|
||||
// the number of bytes to move ahead for each iteration.
|
||||
skip uint32 = 32
|
||||
)
|
||||
for uint32(s+3) < uint32(len(src)) { // The uint32 conversions catch overflow from the +3.
|
||||
// Update the hash table.
|
||||
b0, b1, b2, b3 := src[s], src[s+1], src[s+2], src[s+3]
|
||||
h := uint32(b0) | uint32(b1)<<8 | uint32(b2)<<16 | uint32(b3)<<24
|
||||
p := &table[(h*0x1e35a7bd)>>shift]
|
||||
// We need to to store values in [-1, inf) in table. To save
|
||||
// some initialization time, (re)use the table's zero value
|
||||
// and shift the values against this zero: add 1 on writes,
|
||||
// subtract 1 on reads.
|
||||
t, *p = *p-1, s+1
|
||||
// If t is invalid or src[s:s+4] differs from src[t:t+4], accumulate a literal byte.
|
||||
if t < 0 || s-t >= maxOffset || b0 != src[t] || b1 != src[t+1] || b2 != src[t+2] || b3 != src[t+3] {
|
||||
s += int32(skip >> 5)
|
||||
skip++
|
||||
continue
|
||||
}
|
||||
skip = 32
|
||||
// Otherwise, we have a match. First, emit any pending literal bytes.
|
||||
if lit != s {
|
||||
d += emitLiteral(dst[d:], src[lit:s])
|
||||
}
|
||||
// Extend the match to be as long as possible.
|
||||
s0 := s
|
||||
s, t = s+4, t+4
|
||||
for int(s) < len(src) && src[s] == src[t] {
|
||||
s++
|
||||
t++
|
||||
}
|
||||
// Emit the copied bytes.
|
||||
d += emitCopy(dst[d:], s-t, s-s0)
|
||||
lit = s
|
||||
}
|
||||
|
||||
// Emit any final pending literal bytes and return.
|
||||
if int(lit) != len(src) {
|
||||
d += emitLiteral(dst[d:], src[lit:])
|
||||
}
|
||||
return d
|
||||
}
|
||||
|
||||
// MaxEncodedLen returns the maximum length of a snappy block, given its
|
||||
// uncompressed length.
|
||||
//
|
||||
// It will return a negative value if srcLen is too large to encode.
|
||||
func MaxEncodedLen(srcLen int) int {
|
||||
n := uint64(srcLen)
|
||||
if n > 0xffffffff {
|
||||
return -1
|
||||
}
|
||||
// Compressed data can be defined as:
|
||||
// compressed := item* literal*
|
||||
// item := literal* copy
|
||||
//
|
||||
// The trailing literal sequence has a space blowup of at most 62/60
|
||||
// since a literal of length 60 needs one tag byte + one extra byte
|
||||
// for length information.
|
||||
//
|
||||
// Item blowup is trickier to measure. Suppose the "copy" op copies
|
||||
// 4 bytes of data. Because of a special check in the encoding code,
|
||||
// we produce a 4-byte copy only if the offset is < 65536. Therefore
|
||||
// the copy op takes 3 bytes to encode, and this type of item leads
|
||||
// to at most the 62/60 blowup for representing literals.
|
||||
//
|
||||
// Suppose the "copy" op copies 5 bytes of data. If the offset is big
|
||||
// enough, it will take 5 bytes to encode the copy op. Therefore the
|
||||
// worst case here is a one-byte literal followed by a five-byte copy.
|
||||
// That is, 6 bytes of input turn into 7 bytes of "compressed" data.
|
||||
//
|
||||
// This last factor dominates the blowup, so the final estimate is:
|
||||
n = 32 + n + n/6
|
||||
if n > 0xffffffff {
|
||||
return -1
|
||||
}
|
||||
return int(n)
|
||||
}
|
||||
|
||||
var errClosed = errors.New("snappy: Writer is closed")
|
||||
|
||||
// NewWriter returns a new Writer that compresses to w.
|
||||
//
|
||||
// The Writer returned does not buffer writes. There is no need to Flush or
|
||||
// Close such a Writer.
|
||||
//
|
||||
// Deprecated: the Writer returned is not suitable for many small writes, only
|
||||
// for few large writes. Use NewBufferedWriter instead, which is efficient
|
||||
// regardless of the frequency and shape of the writes, and remember to Close
|
||||
// that Writer when done.
|
||||
func NewWriter(w io.Writer) *Writer {
|
||||
return &Writer{
|
||||
w: w,
|
||||
obuf: make([]byte, obufLen),
|
||||
}
|
||||
}
|
||||
|
||||
// NewBufferedWriter returns a new Writer that compresses to w, using the
|
||||
// framing format described at
|
||||
// https://github.com/google/snappy/blob/master/framing_format.txt
|
||||
//
|
||||
// The Writer returned buffers writes. Users must call Close to guarantee all
|
||||
// data has been forwarded to the underlying io.Writer. They may also call
|
||||
// Flush zero or more times before calling Close.
|
||||
func NewBufferedWriter(w io.Writer) *Writer {
|
||||
return &Writer{
|
||||
w: w,
|
||||
ibuf: make([]byte, 0, maxBlockSize),
|
||||
obuf: make([]byte, obufLen),
|
||||
}
|
||||
}
|
||||
|
||||
// Writer is an io.Writer than can write Snappy-compressed bytes.
|
||||
type Writer struct {
|
||||
w io.Writer
|
||||
err error
|
||||
|
||||
// ibuf is a buffer for the incoming (uncompressed) bytes.
|
||||
//
|
||||
// Its use is optional. For backwards compatibility, Writers created by the
|
||||
// NewWriter function have ibuf == nil, do not buffer incoming bytes, and
|
||||
// therefore do not need to be Flush'ed or Close'd.
|
||||
ibuf []byte
|
||||
|
||||
// obuf is a buffer for the outgoing (compressed) bytes.
|
||||
obuf []byte
|
||||
|
||||
// wroteStreamHeader is whether we have written the stream header.
|
||||
wroteStreamHeader bool
|
||||
}
|
||||
|
||||
// Reset discards the writer's state and switches the Snappy writer to write to
|
||||
// w. This permits reusing a Writer rather than allocating a new one.
|
||||
func (w *Writer) Reset(writer io.Writer) {
|
||||
w.w = writer
|
||||
w.err = nil
|
||||
if w.ibuf != nil {
|
||||
w.ibuf = w.ibuf[:0]
|
||||
}
|
||||
w.wroteStreamHeader = false
|
||||
}
|
||||
|
||||
// Write satisfies the io.Writer interface.
|
||||
func (w *Writer) Write(p []byte) (nRet int, errRet error) {
|
||||
if w.ibuf == nil {
|
||||
// Do not buffer incoming bytes. This does not perform or compress well
|
||||
// if the caller of Writer.Write writes many small slices. This
|
||||
// behavior is therefore deprecated, but still supported for backwards
|
||||
// compatibility with code that doesn't explicitly Flush or Close.
|
||||
return w.write(p)
|
||||
}
|
||||
|
||||
// The remainder of this method is based on bufio.Writer.Write from the
|
||||
// standard library.
|
||||
|
||||
for len(p) > (cap(w.ibuf)-len(w.ibuf)) && w.err == nil {
|
||||
var n int
|
||||
if len(w.ibuf) == 0 {
|
||||
// Large write, empty buffer.
|
||||
// Write directly from p to avoid copy.
|
||||
n, _ = w.write(p)
|
||||
} else {
|
||||
n = copy(w.ibuf[len(w.ibuf):cap(w.ibuf)], p)
|
||||
w.ibuf = w.ibuf[:len(w.ibuf)+n]
|
||||
w.Flush()
|
||||
}
|
||||
nRet += n
|
||||
p = p[n:]
|
||||
}
|
||||
if w.err != nil {
|
||||
return nRet, w.err
|
||||
}
|
||||
n := copy(w.ibuf[len(w.ibuf):cap(w.ibuf)], p)
|
||||
w.ibuf = w.ibuf[:len(w.ibuf)+n]
|
||||
nRet += n
|
||||
return nRet, nil
|
||||
}
|
||||
|
||||
func (w *Writer) write(p []byte) (nRet int, errRet error) {
|
||||
if w.err != nil {
|
||||
return 0, w.err
|
||||
}
|
||||
for len(p) > 0 {
|
||||
obufStart := len(magicChunk)
|
||||
if !w.wroteStreamHeader {
|
||||
w.wroteStreamHeader = true
|
||||
copy(w.obuf, magicChunk)
|
||||
obufStart = 0
|
||||
}
|
||||
|
||||
var uncompressed []byte
|
||||
if len(p) > maxBlockSize {
|
||||
uncompressed, p = p[:maxBlockSize], p[maxBlockSize:]
|
||||
} else {
|
||||
uncompressed, p = p, nil
|
||||
}
|
||||
checksum := crc(uncompressed)
|
||||
|
||||
// Compress the buffer, discarding the result if the improvement
|
||||
// isn't at least 12.5%.
|
||||
compressed := Encode(w.obuf[obufHeaderLen:], uncompressed)
|
||||
chunkType := uint8(chunkTypeCompressedData)
|
||||
chunkLen := 4 + len(compressed)
|
||||
obufEnd := obufHeaderLen + len(compressed)
|
||||
if len(compressed) >= len(uncompressed)-len(uncompressed)/8 {
|
||||
chunkType = chunkTypeUncompressedData
|
||||
chunkLen = 4 + len(uncompressed)
|
||||
obufEnd = obufHeaderLen
|
||||
}
|
||||
|
||||
// Fill in the per-chunk header that comes before the body.
|
||||
w.obuf[len(magicChunk)+0] = chunkType
|
||||
w.obuf[len(magicChunk)+1] = uint8(chunkLen >> 0)
|
||||
w.obuf[len(magicChunk)+2] = uint8(chunkLen >> 8)
|
||||
w.obuf[len(magicChunk)+3] = uint8(chunkLen >> 16)
|
||||
w.obuf[len(magicChunk)+4] = uint8(checksum >> 0)
|
||||
w.obuf[len(magicChunk)+5] = uint8(checksum >> 8)
|
||||
w.obuf[len(magicChunk)+6] = uint8(checksum >> 16)
|
||||
w.obuf[len(magicChunk)+7] = uint8(checksum >> 24)
|
||||
|
||||
if _, err := w.w.Write(w.obuf[obufStart:obufEnd]); err != nil {
|
||||
w.err = err
|
||||
return nRet, err
|
||||
}
|
||||
if chunkType == chunkTypeUncompressedData {
|
||||
if _, err := w.w.Write(uncompressed); err != nil {
|
||||
w.err = err
|
||||
return nRet, err
|
||||
}
|
||||
}
|
||||
nRet += len(uncompressed)
|
||||
}
|
||||
return nRet, nil
|
||||
}
|
||||
|
||||
// Flush flushes the Writer to its underlying io.Writer.
|
||||
func (w *Writer) Flush() error {
|
||||
if w.err != nil {
|
||||
return w.err
|
||||
}
|
||||
if len(w.ibuf) == 0 {
|
||||
return nil
|
||||
}
|
||||
w.write(w.ibuf)
|
||||
w.ibuf = w.ibuf[:0]
|
||||
return w.err
|
||||
}
|
||||
|
||||
// Close calls Flush and then closes the Writer.
|
||||
func (w *Writer) Close() error {
|
||||
w.Flush()
|
||||
ret := w.err
|
||||
if w.err == nil {
|
||||
w.err = errClosed
|
||||
}
|
||||
return ret
|
||||
}
|
||||
84
vendor/github.com/golang/snappy/snappy.go
generated
vendored
Normal file
84
vendor/github.com/golang/snappy/snappy.go
generated
vendored
Normal file
@@ -0,0 +1,84 @@
|
||||
// Copyright 2011 The Snappy-Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package snappy implements the snappy block-based compression format.
|
||||
// It aims for very high speeds and reasonable compression.
|
||||
//
|
||||
// The C++ snappy implementation is at https://github.com/google/snappy
|
||||
package snappy // import "github.com/golang/snappy"
|
||||
|
||||
import (
|
||||
"hash/crc32"
|
||||
)
|
||||
|
||||
/*
|
||||
Each encoded block begins with the varint-encoded length of the decoded data,
|
||||
followed by a sequence of chunks. Chunks begin and end on byte boundaries. The
|
||||
first byte of each chunk is broken into its 2 least and 6 most significant bits
|
||||
called l and m: l ranges in [0, 4) and m ranges in [0, 64). l is the chunk tag.
|
||||
Zero means a literal tag. All other values mean a copy tag.
|
||||
|
||||
For literal tags:
|
||||
- If m < 60, the next 1 + m bytes are literal bytes.
|
||||
- Otherwise, let n be the little-endian unsigned integer denoted by the next
|
||||
m - 59 bytes. The next 1 + n bytes after that are literal bytes.
|
||||
|
||||
For copy tags, length bytes are copied from offset bytes ago, in the style of
|
||||
Lempel-Ziv compression algorithms. In particular:
|
||||
- For l == 1, the offset ranges in [0, 1<<11) and the length in [4, 12).
|
||||
The length is 4 + the low 3 bits of m. The high 3 bits of m form bits 8-10
|
||||
of the offset. The next byte is bits 0-7 of the offset.
|
||||
- For l == 2, the offset ranges in [0, 1<<16) and the length in [1, 65).
|
||||
The length is 1 + m. The offset is the little-endian unsigned integer
|
||||
denoted by the next 2 bytes.
|
||||
- For l == 3, this tag is a legacy format that is no longer supported.
|
||||
*/
|
||||
const (
|
||||
tagLiteral = 0x00
|
||||
tagCopy1 = 0x01
|
||||
tagCopy2 = 0x02
|
||||
tagCopy4 = 0x03
|
||||
)
|
||||
|
||||
const (
|
||||
checksumSize = 4
|
||||
chunkHeaderSize = 4
|
||||
magicChunk = "\xff\x06\x00\x00" + magicBody
|
||||
magicBody = "sNaPpY"
|
||||
|
||||
// maxBlockSize is the maximum size of the input to encodeBlock. It is not
|
||||
// part of the wire format per se, but some parts of the encoder assume
|
||||
// that an offset fits into a uint16.
|
||||
//
|
||||
// Also, for the framing format (Writer type instead of Encode function),
|
||||
// https://github.com/google/snappy/blob/master/framing_format.txt says
|
||||
// that "the uncompressed data in a chunk must be no longer than 65536
|
||||
// bytes".
|
||||
maxBlockSize = 65536
|
||||
|
||||
// maxEncodedLenOfMaxBlockSize equals MaxEncodedLen(maxBlockSize), but is
|
||||
// hard coded to be a const instead of a variable, so that obufLen can also
|
||||
// be a const. Their equivalence is confirmed by
|
||||
// TestMaxEncodedLenOfMaxBlockSize.
|
||||
maxEncodedLenOfMaxBlockSize = 76490
|
||||
|
||||
obufHeaderLen = len(magicChunk) + checksumSize + chunkHeaderSize
|
||||
obufLen = obufHeaderLen + maxEncodedLenOfMaxBlockSize
|
||||
)
|
||||
|
||||
const (
|
||||
chunkTypeCompressedData = 0x00
|
||||
chunkTypeUncompressedData = 0x01
|
||||
chunkTypePadding = 0xfe
|
||||
chunkTypeStreamIdentifier = 0xff
|
||||
)
|
||||
|
||||
var crcTable = crc32.MakeTable(crc32.Castagnoli)
|
||||
|
||||
// crc implements the checksum specified in section 3 of
|
||||
// https://github.com/google/snappy/blob/master/framing_format.txt
|
||||
func crc(b []byte) uint32 {
|
||||
c := crc32.Update(0, crcTable, b)
|
||||
return uint32(c>>15|c<<17) + 0xa282ead8
|
||||
}
|
||||
973
vendor/github.com/golang/snappy/snappy_test.go
generated
vendored
Normal file
973
vendor/github.com/golang/snappy/snappy_test.go
generated
vendored
Normal file
@@ -0,0 +1,973 @@
|
||||
// Copyright 2011 The Snappy-Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package snappy
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var download = flag.Bool("download", false, "If true, download any missing files before running benchmarks")
|
||||
|
||||
func TestMaxEncodedLenOfMaxBlockSize(t *testing.T) {
|
||||
got := maxEncodedLenOfMaxBlockSize
|
||||
want := MaxEncodedLen(maxBlockSize)
|
||||
if got != want {
|
||||
t.Fatalf("got %d, want %d", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func cmp(a, b []byte) error {
|
||||
if bytes.Equal(a, b) {
|
||||
return nil
|
||||
}
|
||||
if len(a) != len(b) {
|
||||
return fmt.Errorf("got %d bytes, want %d", len(a), len(b))
|
||||
}
|
||||
for i := range a {
|
||||
if a[i] != b[i] {
|
||||
return fmt.Errorf("byte #%d: got 0x%02x, want 0x%02x", i, a[i], b[i])
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func roundtrip(b, ebuf, dbuf []byte) error {
|
||||
d, err := Decode(dbuf, Encode(ebuf, b))
|
||||
if err != nil {
|
||||
return fmt.Errorf("decoding error: %v", err)
|
||||
}
|
||||
if err := cmp(d, b); err != nil {
|
||||
return fmt.Errorf("roundtrip mismatch: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestEmpty(t *testing.T) {
|
||||
if err := roundtrip(nil, nil, nil); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSmallCopy(t *testing.T) {
|
||||
for _, ebuf := range [][]byte{nil, make([]byte, 20), make([]byte, 64)} {
|
||||
for _, dbuf := range [][]byte{nil, make([]byte, 20), make([]byte, 64)} {
|
||||
for i := 0; i < 32; i++ {
|
||||
s := "aaaa" + strings.Repeat("b", i) + "aaaabbbb"
|
||||
if err := roundtrip([]byte(s), ebuf, dbuf); err != nil {
|
||||
t.Errorf("len(ebuf)=%d, len(dbuf)=%d, i=%d: %v", len(ebuf), len(dbuf), i, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSmallRand(t *testing.T) {
|
||||
rng := rand.New(rand.NewSource(1))
|
||||
for n := 1; n < 20000; n += 23 {
|
||||
b := make([]byte, n)
|
||||
for i := range b {
|
||||
b[i] = uint8(rng.Intn(256))
|
||||
}
|
||||
if err := roundtrip(b, nil, nil); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSmallRegular(t *testing.T) {
|
||||
for n := 1; n < 20000; n += 23 {
|
||||
b := make([]byte, n)
|
||||
for i := range b {
|
||||
b[i] = uint8(i%10 + 'a')
|
||||
}
|
||||
if err := roundtrip(b, nil, nil); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestInvalidVarint(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
input string
|
||||
}{{
|
||||
"invalid varint, final byte has continuation bit set",
|
||||
"\xff",
|
||||
}, {
|
||||
"invalid varint, value overflows uint64",
|
||||
"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00",
|
||||
}, {
|
||||
// https://github.com/google/snappy/blob/master/format_description.txt
|
||||
// says that "the stream starts with the uncompressed length [as a
|
||||
// varint] (up to a maximum of 2^32 - 1)".
|
||||
"valid varint (as uint64), but value overflows uint32",
|
||||
"\x80\x80\x80\x80\x10",
|
||||
}}
|
||||
|
||||
for _, tc := range testCases {
|
||||
input := []byte(tc.input)
|
||||
if _, err := DecodedLen(input); err != ErrCorrupt {
|
||||
t.Errorf("%s: DecodedLen: got %v, want ErrCorrupt", tc.desc, err)
|
||||
}
|
||||
if _, err := Decode(nil, input); err != ErrCorrupt {
|
||||
t.Errorf("%s: Decode: got %v, want ErrCorrupt", tc.desc, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecode(t *testing.T) {
|
||||
lit40Bytes := make([]byte, 40)
|
||||
for i := range lit40Bytes {
|
||||
lit40Bytes[i] = byte(i)
|
||||
}
|
||||
lit40 := string(lit40Bytes)
|
||||
|
||||
testCases := []struct {
|
||||
desc string
|
||||
input string
|
||||
want string
|
||||
wantErr error
|
||||
}{{
|
||||
`decodedLen=0; valid input`,
|
||||
"\x00",
|
||||
"",
|
||||
nil,
|
||||
}, {
|
||||
`decodedLen=3; tagLiteral, 0-byte length; length=3; valid input`,
|
||||
"\x03" + "\x08\xff\xff\xff",
|
||||
"\xff\xff\xff",
|
||||
nil,
|
||||
}, {
|
||||
`decodedLen=2; tagLiteral, 0-byte length; length=3; not enough dst bytes`,
|
||||
"\x02" + "\x08\xff\xff\xff",
|
||||
"",
|
||||
ErrCorrupt,
|
||||
}, {
|
||||
`decodedLen=3; tagLiteral, 0-byte length; length=3; not enough src bytes`,
|
||||
"\x03" + "\x08\xff\xff",
|
||||
"",
|
||||
ErrCorrupt,
|
||||
}, {
|
||||
`decodedLen=40; tagLiteral, 0-byte length; length=40; valid input`,
|
||||
"\x28" + "\x9c" + lit40,
|
||||
lit40,
|
||||
nil,
|
||||
}, {
|
||||
`decodedLen=1; tagLiteral, 1-byte length; not enough length bytes`,
|
||||
"\x01" + "\xf0",
|
||||
"",
|
||||
ErrCorrupt,
|
||||
}, {
|
||||
`decodedLen=3; tagLiteral, 1-byte length; length=3; valid input`,
|
||||
"\x03" + "\xf0\x02\xff\xff\xff",
|
||||
"\xff\xff\xff",
|
||||
nil,
|
||||
}, {
|
||||
`decodedLen=1; tagLiteral, 2-byte length; not enough length bytes`,
|
||||
"\x01" + "\xf4\x00",
|
||||
"",
|
||||
ErrCorrupt,
|
||||
}, {
|
||||
`decodedLen=3; tagLiteral, 2-byte length; length=3; valid input`,
|
||||
"\x03" + "\xf4\x02\x00\xff\xff\xff",
|
||||
"\xff\xff\xff",
|
||||
nil,
|
||||
}, {
|
||||
`decodedLen=1; tagLiteral, 3-byte length; not enough length bytes`,
|
||||
"\x01" + "\xf8\x00\x00",
|
||||
"",
|
||||
ErrCorrupt,
|
||||
}, {
|
||||
`decodedLen=3; tagLiteral, 3-byte length; length=3; valid input`,
|
||||
"\x03" + "\xf8\x02\x00\x00\xff\xff\xff",
|
||||
"\xff\xff\xff",
|
||||
nil,
|
||||
}, {
|
||||
`decodedLen=1; tagLiteral, 4-byte length; not enough length bytes`,
|
||||
"\x01" + "\xfc\x00\x00\x00",
|
||||
"",
|
||||
ErrCorrupt,
|
||||
}, {
|
||||
`decodedLen=1; tagLiteral, 4-byte length; length=3; not enough dst bytes`,
|
||||
"\x01" + "\xfc\x02\x00\x00\x00\xff\xff\xff",
|
||||
"",
|
||||
ErrCorrupt,
|
||||
}, {
|
||||
`decodedLen=4; tagLiteral, 4-byte length; length=3; not enough src bytes`,
|
||||
"\x04" + "\xfc\x02\x00\x00\x00\xff",
|
||||
"",
|
||||
ErrCorrupt,
|
||||
}, {
|
||||
`decodedLen=3; tagLiteral, 4-byte length; length=3; valid input`,
|
||||
"\x03" + "\xfc\x02\x00\x00\x00\xff\xff\xff",
|
||||
"\xff\xff\xff",
|
||||
nil,
|
||||
}, {
|
||||
`decodedLen=4; tagCopy1, 1 extra length|offset byte; not enough extra bytes`,
|
||||
"\x04" + "\x01",
|
||||
"",
|
||||
ErrCorrupt,
|
||||
}, {
|
||||
`decodedLen=4; tagCopy2, 2 extra length|offset bytes; not enough extra bytes`,
|
||||
"\x04" + "\x02\x00",
|
||||
"",
|
||||
ErrCorrupt,
|
||||
}, {
|
||||
`decodedLen=4; tagCopy4; unsupported COPY_4 tag`,
|
||||
"\x04" + "\x03\x00\x00\x00\x00",
|
||||
"",
|
||||
errUnsupportedCopy4Tag,
|
||||
}, {
|
||||
`decodedLen=4; tagLiteral (4 bytes "abcd"); valid input`,
|
||||
"\x04" + "\x0cabcd",
|
||||
"abcd",
|
||||
nil,
|
||||
}, {
|
||||
`decodedLen=13; tagLiteral (4 bytes "abcd"); tagCopy1; length=9 offset=4; valid input`,
|
||||
"\x0d" + "\x0cabcd" + "\x15\x04",
|
||||
"abcdabcdabcda",
|
||||
nil,
|
||||
}, {
|
||||
`decodedLen=8; tagLiteral (4 bytes "abcd"); tagCopy1; length=4 offset=4; valid input`,
|
||||
"\x08" + "\x0cabcd" + "\x01\x04",
|
||||
"abcdabcd",
|
||||
nil,
|
||||
}, {
|
||||
`decodedLen=8; tagLiteral (4 bytes "abcd"); tagCopy1; length=4 offset=2; valid input`,
|
||||
"\x08" + "\x0cabcd" + "\x01\x02",
|
||||
"abcdcdcd",
|
||||
nil,
|
||||
}, {
|
||||
`decodedLen=8; tagLiteral (4 bytes "abcd"); tagCopy1; length=4 offset=1; valid input`,
|
||||
"\x08" + "\x0cabcd" + "\x01\x01",
|
||||
"abcddddd",
|
||||
nil,
|
||||
}, {
|
||||
`decodedLen=8; tagLiteral (4 bytes "abcd"); tagCopy1; length=4 offset=0; zero offset`,
|
||||
"\x08" + "\x0cabcd" + "\x01\x00",
|
||||
"",
|
||||
ErrCorrupt,
|
||||
}, {
|
||||
`decodedLen=9; tagLiteral (4 bytes "abcd"); tagCopy1; length=4 offset=4; inconsistent dLen`,
|
||||
"\x09" + "\x0cabcd" + "\x01\x04",
|
||||
"",
|
||||
ErrCorrupt,
|
||||
}, {
|
||||
`decodedLen=8; tagLiteral (4 bytes "abcd"); tagCopy1; length=4 offset=5; offset too large`,
|
||||
"\x08" + "\x0cabcd" + "\x01\x05",
|
||||
"",
|
||||
ErrCorrupt,
|
||||
}, {
|
||||
`decodedLen=7; tagLiteral (4 bytes "abcd"); tagCopy1; length=4 offset=4; length too large`,
|
||||
"\x07" + "\x0cabcd" + "\x01\x04",
|
||||
"",
|
||||
ErrCorrupt,
|
||||
}, {
|
||||
`decodedLen=6; tagLiteral (4 bytes "abcd"); tagCopy2; length=2 offset=3; valid input`,
|
||||
"\x06" + "\x0cabcd" + "\x06\x03\x00",
|
||||
"abcdbc",
|
||||
nil,
|
||||
}}
|
||||
|
||||
const (
|
||||
// notPresentXxx defines a range of byte values [0xa0, 0xc5) that are
|
||||
// not present in either the input or the output. It is written to dBuf
|
||||
// to check that Decode does not write bytes past the end of
|
||||
// dBuf[:dLen].
|
||||
//
|
||||
// The magic number 37 was chosen because it is prime. A more 'natural'
|
||||
// number like 32 might lead to a false negative if, for example, a
|
||||
// byte was incorrectly copied 4*8 bytes later.
|
||||
notPresentBase = 0xa0
|
||||
notPresentLen = 37
|
||||
)
|
||||
|
||||
var dBuf [100]byte
|
||||
loop:
|
||||
for i, tc := range testCases {
|
||||
input := []byte(tc.input)
|
||||
for _, x := range input {
|
||||
if notPresentBase <= x && x < notPresentBase+notPresentLen {
|
||||
t.Errorf("#%d (%s): input shouldn't contain %#02x\ninput: % x", i, tc.desc, x, input)
|
||||
continue loop
|
||||
}
|
||||
}
|
||||
|
||||
dLen, n := binary.Uvarint(input)
|
||||
if n <= 0 {
|
||||
t.Errorf("#%d (%s): invalid varint-encoded dLen", i, tc.desc)
|
||||
continue
|
||||
}
|
||||
if dLen > uint64(len(dBuf)) {
|
||||
t.Errorf("#%d (%s): dLen %d is too large", i, tc.desc, dLen)
|
||||
continue
|
||||
}
|
||||
|
||||
for j := range dBuf {
|
||||
dBuf[j] = byte(notPresentBase + j%notPresentLen)
|
||||
}
|
||||
g, gotErr := Decode(dBuf[:], input)
|
||||
if got := string(g); got != tc.want || gotErr != tc.wantErr {
|
||||
t.Errorf("#%d (%s):\ngot %q, %v\nwant %q, %v",
|
||||
i, tc.desc, got, gotErr, tc.want, tc.wantErr)
|
||||
continue
|
||||
}
|
||||
for j, x := range dBuf {
|
||||
if uint64(j) < dLen {
|
||||
continue
|
||||
}
|
||||
if w := byte(notPresentBase + j%notPresentLen); x != w {
|
||||
t.Errorf("#%d (%s): Decode overrun: dBuf[%d] was modified: got %#02x, want %#02x\ndBuf: % x",
|
||||
i, tc.desc, j, x, w, dBuf)
|
||||
continue loop
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestDecodeLengthOffset tests decoding an encoding of the form literal +
|
||||
// copy-length-offset + literal. For example: "abcdefghijkl" + "efghij" + "AB".
|
||||
func TestDecodeLengthOffset(t *testing.T) {
|
||||
const (
|
||||
prefix = "abcdefghijklmnopqr"
|
||||
suffix = "ABCDEFGHIJKLMNOPQR"
|
||||
|
||||
// notPresentXxx defines a range of byte values [0xa0, 0xc5) that are
|
||||
// not present in either the input or the output. It is written to
|
||||
// gotBuf to check that Decode does not write bytes past the end of
|
||||
// gotBuf[:totalLen].
|
||||
//
|
||||
// The magic number 37 was chosen because it is prime. A more 'natural'
|
||||
// number like 32 might lead to a false negative if, for example, a
|
||||
// byte was incorrectly copied 4*8 bytes later.
|
||||
notPresentBase = 0xa0
|
||||
notPresentLen = 37
|
||||
)
|
||||
var gotBuf, wantBuf, inputBuf [128]byte
|
||||
for length := 1; length <= 18; length++ {
|
||||
for offset := 1; offset <= 18; offset++ {
|
||||
loop:
|
||||
for suffixLen := 0; suffixLen <= 18; suffixLen++ {
|
||||
totalLen := len(prefix) + length + suffixLen
|
||||
|
||||
inputLen := binary.PutUvarint(inputBuf[:], uint64(totalLen))
|
||||
inputBuf[inputLen] = tagLiteral + 4*byte(len(prefix)-1)
|
||||
inputLen++
|
||||
inputLen += copy(inputBuf[inputLen:], prefix)
|
||||
inputBuf[inputLen+0] = tagCopy2 + 4*byte(length-1)
|
||||
inputBuf[inputLen+1] = byte(offset)
|
||||
inputBuf[inputLen+2] = 0x00
|
||||
inputLen += 3
|
||||
if suffixLen > 0 {
|
||||
inputBuf[inputLen] = tagLiteral + 4*byte(suffixLen-1)
|
||||
inputLen++
|
||||
inputLen += copy(inputBuf[inputLen:], suffix[:suffixLen])
|
||||
}
|
||||
input := inputBuf[:inputLen]
|
||||
|
||||
for i := range gotBuf {
|
||||
gotBuf[i] = byte(notPresentBase + i%notPresentLen)
|
||||
}
|
||||
got, err := Decode(gotBuf[:], input)
|
||||
if err != nil {
|
||||
t.Errorf("length=%d, offset=%d; suffixLen=%d: %v", length, offset, suffixLen, err)
|
||||
continue
|
||||
}
|
||||
|
||||
wantLen := 0
|
||||
wantLen += copy(wantBuf[wantLen:], prefix)
|
||||
for i := 0; i < length; i++ {
|
||||
wantBuf[wantLen] = wantBuf[wantLen-offset]
|
||||
wantLen++
|
||||
}
|
||||
wantLen += copy(wantBuf[wantLen:], suffix[:suffixLen])
|
||||
want := wantBuf[:wantLen]
|
||||
|
||||
for _, x := range input {
|
||||
if notPresentBase <= x && x < notPresentBase+notPresentLen {
|
||||
t.Errorf("length=%d, offset=%d; suffixLen=%d: input shouldn't contain %#02x\ninput: % x",
|
||||
length, offset, suffixLen, x, input)
|
||||
continue loop
|
||||
}
|
||||
}
|
||||
for i, x := range gotBuf {
|
||||
if i < totalLen {
|
||||
continue
|
||||
}
|
||||
if w := byte(notPresentBase + i%notPresentLen); x != w {
|
||||
t.Errorf("length=%d, offset=%d; suffixLen=%d; totalLen=%d: "+
|
||||
"Decode overrun: gotBuf[%d] was modified: got %#02x, want %#02x\ngotBuf: % x",
|
||||
length, offset, suffixLen, totalLen, i, x, w, gotBuf)
|
||||
continue loop
|
||||
}
|
||||
}
|
||||
for _, x := range want {
|
||||
if notPresentBase <= x && x < notPresentBase+notPresentLen {
|
||||
t.Errorf("length=%d, offset=%d; suffixLen=%d: want shouldn't contain %#02x\nwant: % x",
|
||||
length, offset, suffixLen, x, want)
|
||||
continue loop
|
||||
}
|
||||
}
|
||||
|
||||
if !bytes.Equal(got, want) {
|
||||
t.Errorf("length=%d, offset=%d; suffixLen=%d:\ninput % x\ngot % x\nwant % x",
|
||||
length, offset, suffixLen, input, got, want)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecodeGoldenInput(t *testing.T) {
|
||||
src, err := ioutil.ReadFile("testdata/pi.txt.rawsnappy")
|
||||
if err != nil {
|
||||
t.Fatalf("ReadFile: %v", err)
|
||||
}
|
||||
got, err := Decode(nil, src)
|
||||
if err != nil {
|
||||
t.Fatalf("Decode: %v", err)
|
||||
}
|
||||
want, err := ioutil.ReadFile("testdata/pi.txt")
|
||||
if err != nil {
|
||||
t.Fatalf("ReadFile: %v", err)
|
||||
}
|
||||
if err := cmp(got, want); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestSlowForwardCopyOverrun tests the "expand the pattern" algorithm
|
||||
// described in decode_amd64.s and its claim of a 10 byte overrun worst case.
|
||||
func TestSlowForwardCopyOverrun(t *testing.T) {
|
||||
const base = 100
|
||||
|
||||
for length := 1; length < 18; length++ {
|
||||
for offset := 1; offset < 18; offset++ {
|
||||
highWaterMark := base
|
||||
d := base
|
||||
l := length
|
||||
o := offset
|
||||
|
||||
// makeOffsetAtLeast8
|
||||
for o < 8 {
|
||||
if end := d + 8; highWaterMark < end {
|
||||
highWaterMark = end
|
||||
}
|
||||
l -= o
|
||||
d += o
|
||||
o += o
|
||||
}
|
||||
|
||||
// fixUpSlowForwardCopy
|
||||
a := d
|
||||
d += l
|
||||
|
||||
// finishSlowForwardCopy
|
||||
for l > 0 {
|
||||
if end := a + 8; highWaterMark < end {
|
||||
highWaterMark = end
|
||||
}
|
||||
a += 8
|
||||
l -= 8
|
||||
}
|
||||
|
||||
dWant := base + length
|
||||
overrun := highWaterMark - dWant
|
||||
if d != dWant || overrun < 0 || 10 < overrun {
|
||||
t.Errorf("length=%d, offset=%d: d and overrun: got (%d, %d), want (%d, something in [0, 10])",
|
||||
length, offset, d, overrun, dWant)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestEncodeNoiseThenRepeats encodes input for which the first half is very
|
||||
// incompressible and the second half is very compressible. The encoded form's
|
||||
// length should be closer to 50% of the original length than 100%.
|
||||
func TestEncodeNoiseThenRepeats(t *testing.T) {
|
||||
for _, origLen := range []int{32 * 1024, 256 * 1024, 2048 * 1024} {
|
||||
src := make([]byte, origLen)
|
||||
rng := rand.New(rand.NewSource(1))
|
||||
firstHalf, secondHalf := src[:origLen/2], src[origLen/2:]
|
||||
for i := range firstHalf {
|
||||
firstHalf[i] = uint8(rng.Intn(256))
|
||||
}
|
||||
for i := range secondHalf {
|
||||
secondHalf[i] = uint8(i >> 8)
|
||||
}
|
||||
dst := Encode(nil, src)
|
||||
if got, want := len(dst), origLen*3/4; got >= want {
|
||||
t.Errorf("origLen=%d: got %d encoded bytes, want less than %d", origLen, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFramingFormat(t *testing.T) {
|
||||
// src is comprised of alternating 1e5-sized sequences of random
|
||||
// (incompressible) bytes and repeated (compressible) bytes. 1e5 was chosen
|
||||
// because it is larger than maxBlockSize (64k).
|
||||
src := make([]byte, 1e6)
|
||||
rng := rand.New(rand.NewSource(1))
|
||||
for i := 0; i < 10; i++ {
|
||||
if i%2 == 0 {
|
||||
for j := 0; j < 1e5; j++ {
|
||||
src[1e5*i+j] = uint8(rng.Intn(256))
|
||||
}
|
||||
} else {
|
||||
for j := 0; j < 1e5; j++ {
|
||||
src[1e5*i+j] = uint8(i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
if _, err := NewWriter(buf).Write(src); err != nil {
|
||||
t.Fatalf("Write: encoding: %v", err)
|
||||
}
|
||||
dst, err := ioutil.ReadAll(NewReader(buf))
|
||||
if err != nil {
|
||||
t.Fatalf("ReadAll: decoding: %v", err)
|
||||
}
|
||||
if err := cmp(dst, src); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriterGoldenOutput(t *testing.T) {
|
||||
buf := new(bytes.Buffer)
|
||||
w := NewBufferedWriter(buf)
|
||||
defer w.Close()
|
||||
w.Write([]byte("abcd")) // Not compressible.
|
||||
w.Flush()
|
||||
w.Write(bytes.Repeat([]byte{'A'}, 100)) // Compressible.
|
||||
w.Flush()
|
||||
got := buf.String()
|
||||
want := strings.Join([]string{
|
||||
magicChunk,
|
||||
"\x01\x08\x00\x00", // Uncompressed chunk, 8 bytes long (including 4 byte checksum).
|
||||
"\x68\x10\xe6\xb6", // Checksum.
|
||||
"\x61\x62\x63\x64", // Uncompressed payload: "abcd".
|
||||
"\x00\x0d\x00\x00", // Compressed chunk, 13 bytes long (including 4 byte checksum).
|
||||
"\x37\xcb\xbc\x9d", // Checksum.
|
||||
"\x64", // Compressed payload: Uncompressed length (varint encoded): 100.
|
||||
"\x00\x41", // Compressed payload: tagLiteral, length=1, "A".
|
||||
"\xfe\x01\x00", // Compressed payload: tagCopy2, length=64, offset=1.
|
||||
"\x8a\x01\x00", // Compressed payload: tagCopy2, length=35, offset=1.
|
||||
}, "")
|
||||
if got != want {
|
||||
t.Fatalf("\ngot: % x\nwant: % x", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewBufferedWriter(t *testing.T) {
|
||||
// Test all 32 possible sub-sequences of these 5 input slices.
|
||||
//
|
||||
// Their lengths sum to 400,000, which is over 6 times the Writer ibuf
|
||||
// capacity: 6 * maxBlockSize is 393,216.
|
||||
inputs := [][]byte{
|
||||
bytes.Repeat([]byte{'a'}, 40000),
|
||||
bytes.Repeat([]byte{'b'}, 150000),
|
||||
bytes.Repeat([]byte{'c'}, 60000),
|
||||
bytes.Repeat([]byte{'d'}, 120000),
|
||||
bytes.Repeat([]byte{'e'}, 30000),
|
||||
}
|
||||
loop:
|
||||
for i := 0; i < 1<<uint(len(inputs)); i++ {
|
||||
var want []byte
|
||||
buf := new(bytes.Buffer)
|
||||
w := NewBufferedWriter(buf)
|
||||
for j, input := range inputs {
|
||||
if i&(1<<uint(j)) == 0 {
|
||||
continue
|
||||
}
|
||||
if _, err := w.Write(input); err != nil {
|
||||
t.Errorf("i=%#02x: j=%d: Write: %v", i, j, err)
|
||||
continue loop
|
||||
}
|
||||
want = append(want, input...)
|
||||
}
|
||||
if err := w.Close(); err != nil {
|
||||
t.Errorf("i=%#02x: Close: %v", i, err)
|
||||
continue
|
||||
}
|
||||
got, err := ioutil.ReadAll(NewReader(buf))
|
||||
if err != nil {
|
||||
t.Errorf("i=%#02x: ReadAll: %v", i, err)
|
||||
continue
|
||||
}
|
||||
if err := cmp(got, want); err != nil {
|
||||
t.Errorf("i=%#02x: %v", i, err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFlush(t *testing.T) {
|
||||
buf := new(bytes.Buffer)
|
||||
w := NewBufferedWriter(buf)
|
||||
defer w.Close()
|
||||
if _, err := w.Write(bytes.Repeat([]byte{'x'}, 20)); err != nil {
|
||||
t.Fatalf("Write: %v", err)
|
||||
}
|
||||
if n := buf.Len(); n != 0 {
|
||||
t.Fatalf("before Flush: %d bytes were written to the underlying io.Writer, want 0", n)
|
||||
}
|
||||
if err := w.Flush(); err != nil {
|
||||
t.Fatalf("Flush: %v", err)
|
||||
}
|
||||
if n := buf.Len(); n == 0 {
|
||||
t.Fatalf("after Flush: %d bytes were written to the underlying io.Writer, want non-0", n)
|
||||
}
|
||||
}
|
||||
|
||||
func TestReaderReset(t *testing.T) {
|
||||
gold := bytes.Repeat([]byte("All that is gold does not glitter,\n"), 10000)
|
||||
buf := new(bytes.Buffer)
|
||||
if _, err := NewWriter(buf).Write(gold); err != nil {
|
||||
t.Fatalf("Write: %v", err)
|
||||
}
|
||||
encoded, invalid, partial := buf.String(), "invalid", "partial"
|
||||
r := NewReader(nil)
|
||||
for i, s := range []string{encoded, invalid, partial, encoded, partial, invalid, encoded, encoded} {
|
||||
if s == partial {
|
||||
r.Reset(strings.NewReader(encoded))
|
||||
if _, err := r.Read(make([]byte, 101)); err != nil {
|
||||
t.Errorf("#%d: %v", i, err)
|
||||
continue
|
||||
}
|
||||
continue
|
||||
}
|
||||
r.Reset(strings.NewReader(s))
|
||||
got, err := ioutil.ReadAll(r)
|
||||
switch s {
|
||||
case encoded:
|
||||
if err != nil {
|
||||
t.Errorf("#%d: %v", i, err)
|
||||
continue
|
||||
}
|
||||
if err := cmp(got, gold); err != nil {
|
||||
t.Errorf("#%d: %v", i, err)
|
||||
continue
|
||||
}
|
||||
case invalid:
|
||||
if err == nil {
|
||||
t.Errorf("#%d: got nil error, want non-nil", i)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriterReset(t *testing.T) {
|
||||
gold := bytes.Repeat([]byte("Not all those who wander are lost;\n"), 10000)
|
||||
const n = 20
|
||||
for _, buffered := range []bool{false, true} {
|
||||
var w *Writer
|
||||
if buffered {
|
||||
w = NewBufferedWriter(nil)
|
||||
defer w.Close()
|
||||
} else {
|
||||
w = NewWriter(nil)
|
||||
}
|
||||
|
||||
var gots, wants [][]byte
|
||||
failed := false
|
||||
for i := 0; i <= n; i++ {
|
||||
buf := new(bytes.Buffer)
|
||||
w.Reset(buf)
|
||||
want := gold[:len(gold)*i/n]
|
||||
if _, err := w.Write(want); err != nil {
|
||||
t.Errorf("#%d: Write: %v", i, err)
|
||||
failed = true
|
||||
continue
|
||||
}
|
||||
if buffered {
|
||||
if err := w.Flush(); err != nil {
|
||||
t.Errorf("#%d: Flush: %v", i, err)
|
||||
failed = true
|
||||
continue
|
||||
}
|
||||
}
|
||||
got, err := ioutil.ReadAll(NewReader(buf))
|
||||
if err != nil {
|
||||
t.Errorf("#%d: ReadAll: %v", i, err)
|
||||
failed = true
|
||||
continue
|
||||
}
|
||||
gots = append(gots, got)
|
||||
wants = append(wants, want)
|
||||
}
|
||||
if failed {
|
||||
continue
|
||||
}
|
||||
for i := range gots {
|
||||
if err := cmp(gots[i], wants[i]); err != nil {
|
||||
t.Errorf("#%d: %v", i, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriterResetWithoutFlush(t *testing.T) {
|
||||
buf0 := new(bytes.Buffer)
|
||||
buf1 := new(bytes.Buffer)
|
||||
w := NewBufferedWriter(buf0)
|
||||
if _, err := w.Write([]byte("xxx")); err != nil {
|
||||
t.Fatalf("Write #0: %v", err)
|
||||
}
|
||||
// Note that we don't Flush the Writer before calling Reset.
|
||||
w.Reset(buf1)
|
||||
if _, err := w.Write([]byte("yyy")); err != nil {
|
||||
t.Fatalf("Write #1: %v", err)
|
||||
}
|
||||
if err := w.Flush(); err != nil {
|
||||
t.Fatalf("Flush: %v", err)
|
||||
}
|
||||
got, err := ioutil.ReadAll(NewReader(buf1))
|
||||
if err != nil {
|
||||
t.Fatalf("ReadAll: %v", err)
|
||||
}
|
||||
if err := cmp(got, []byte("yyy")); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
type writeCounter int
|
||||
|
||||
func (c *writeCounter) Write(p []byte) (int, error) {
|
||||
*c++
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
// TestNumUnderlyingWrites tests that each Writer flush only makes one or two
|
||||
// Write calls on its underlying io.Writer, depending on whether or not the
|
||||
// flushed buffer was compressible.
|
||||
func TestNumUnderlyingWrites(t *testing.T) {
|
||||
testCases := []struct {
|
||||
input []byte
|
||||
want int
|
||||
}{
|
||||
{bytes.Repeat([]byte{'x'}, 100), 1},
|
||||
{bytes.Repeat([]byte{'y'}, 100), 1},
|
||||
{[]byte("ABCDEFGHIJKLMNOPQRST"), 2},
|
||||
}
|
||||
|
||||
var c writeCounter
|
||||
w := NewBufferedWriter(&c)
|
||||
defer w.Close()
|
||||
for i, tc := range testCases {
|
||||
c = 0
|
||||
if _, err := w.Write(tc.input); err != nil {
|
||||
t.Errorf("#%d: Write: %v", i, err)
|
||||
continue
|
||||
}
|
||||
if err := w.Flush(); err != nil {
|
||||
t.Errorf("#%d: Flush: %v", i, err)
|
||||
continue
|
||||
}
|
||||
if int(c) != tc.want {
|
||||
t.Errorf("#%d: got %d underlying writes, want %d", i, c, tc.want)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func benchDecode(b *testing.B, src []byte) {
|
||||
encoded := Encode(nil, src)
|
||||
// Bandwidth is in amount of uncompressed data.
|
||||
b.SetBytes(int64(len(src)))
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
Decode(src, encoded)
|
||||
}
|
||||
}
|
||||
|
||||
func benchEncode(b *testing.B, src []byte) {
|
||||
// Bandwidth is in amount of uncompressed data.
|
||||
b.SetBytes(int64(len(src)))
|
||||
dst := make([]byte, MaxEncodedLen(len(src)))
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
Encode(dst, src)
|
||||
}
|
||||
}
|
||||
|
||||
func readFile(b testing.TB, filename string) []byte {
|
||||
src, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
b.Skipf("skipping benchmark: %v", err)
|
||||
}
|
||||
if len(src) == 0 {
|
||||
b.Fatalf("%s has zero length", filename)
|
||||
}
|
||||
return src
|
||||
}
|
||||
|
||||
// expand returns a slice of length n containing repeated copies of src.
|
||||
func expand(src []byte, n int) []byte {
|
||||
dst := make([]byte, n)
|
||||
for x := dst; len(x) > 0; {
|
||||
i := copy(x, src)
|
||||
x = x[i:]
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
func benchWords(b *testing.B, n int, decode bool) {
|
||||
// Note: the file is OS-language dependent so the resulting values are not
|
||||
// directly comparable for non-US-English OS installations.
|
||||
data := expand(readFile(b, "/usr/share/dict/words"), n)
|
||||
if decode {
|
||||
benchDecode(b, data)
|
||||
} else {
|
||||
benchEncode(b, data)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkWordsDecode1e1(b *testing.B) { benchWords(b, 1e1, true) }
|
||||
func BenchmarkWordsDecode1e2(b *testing.B) { benchWords(b, 1e2, true) }
|
||||
func BenchmarkWordsDecode1e3(b *testing.B) { benchWords(b, 1e3, true) }
|
||||
func BenchmarkWordsDecode1e4(b *testing.B) { benchWords(b, 1e4, true) }
|
||||
func BenchmarkWordsDecode1e5(b *testing.B) { benchWords(b, 1e5, true) }
|
||||
func BenchmarkWordsDecode1e6(b *testing.B) { benchWords(b, 1e6, true) }
|
||||
func BenchmarkWordsEncode1e1(b *testing.B) { benchWords(b, 1e1, false) }
|
||||
func BenchmarkWordsEncode1e2(b *testing.B) { benchWords(b, 1e2, false) }
|
||||
func BenchmarkWordsEncode1e3(b *testing.B) { benchWords(b, 1e3, false) }
|
||||
func BenchmarkWordsEncode1e4(b *testing.B) { benchWords(b, 1e4, false) }
|
||||
func BenchmarkWordsEncode1e5(b *testing.B) { benchWords(b, 1e5, false) }
|
||||
func BenchmarkWordsEncode1e6(b *testing.B) { benchWords(b, 1e6, false) }
|
||||
|
||||
func BenchmarkRandomEncode(b *testing.B) {
|
||||
rng := rand.New(rand.NewSource(1))
|
||||
data := make([]byte, 1<<20)
|
||||
for i := range data {
|
||||
data[i] = uint8(rng.Intn(256))
|
||||
}
|
||||
benchEncode(b, data)
|
||||
}
|
||||
|
||||
// testFiles' values are copied directly from
|
||||
// https://raw.githubusercontent.com/google/snappy/master/snappy_unittest.cc
|
||||
// The label field is unused in snappy-go.
|
||||
var testFiles = []struct {
|
||||
label string
|
||||
filename string
|
||||
sizeLimit int
|
||||
}{
|
||||
{"html", "html", 0},
|
||||
{"urls", "urls.10K", 0},
|
||||
{"jpg", "fireworks.jpeg", 0},
|
||||
{"jpg_200", "fireworks.jpeg", 200},
|
||||
{"pdf", "paper-100k.pdf", 0},
|
||||
{"html4", "html_x_4", 0},
|
||||
{"txt1", "alice29.txt", 0},
|
||||
{"txt2", "asyoulik.txt", 0},
|
||||
{"txt3", "lcet10.txt", 0},
|
||||
{"txt4", "plrabn12.txt", 0},
|
||||
{"pb", "geo.protodata", 0},
|
||||
{"gaviota", "kppkn.gtb", 0},
|
||||
}
|
||||
|
||||
const (
|
||||
// The benchmark data files are at this canonical URL.
|
||||
benchURL = "https://raw.githubusercontent.com/google/snappy/master/testdata/"
|
||||
|
||||
// They are copied to this local directory.
|
||||
benchDir = "testdata/bench"
|
||||
)
|
||||
|
||||
func downloadBenchmarkFiles(b *testing.B, basename string) (errRet error) {
|
||||
filename := filepath.Join(benchDir, basename)
|
||||
if stat, err := os.Stat(filename); err == nil && stat.Size() != 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if !*download {
|
||||
b.Skipf("test data not found; skipping benchmark without the -download flag")
|
||||
}
|
||||
// Download the official snappy C++ implementation reference test data
|
||||
// files for benchmarking.
|
||||
if err := os.MkdirAll(benchDir, 0777); err != nil && !os.IsExist(err) {
|
||||
return fmt.Errorf("failed to create %s: %s", benchDir, err)
|
||||
}
|
||||
|
||||
f, err := os.Create(filename)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create %s: %s", filename, err)
|
||||
}
|
||||
defer f.Close()
|
||||
defer func() {
|
||||
if errRet != nil {
|
||||
os.Remove(filename)
|
||||
}
|
||||
}()
|
||||
url := benchURL + basename
|
||||
resp, err := http.Get(url)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to download %s: %s", url, err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if s := resp.StatusCode; s != http.StatusOK {
|
||||
return fmt.Errorf("downloading %s: HTTP status code %d (%s)", url, s, http.StatusText(s))
|
||||
}
|
||||
_, err = io.Copy(f, resp.Body)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to download %s to %s: %s", url, filename, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func benchFile(b *testing.B, n int, decode bool) {
|
||||
if err := downloadBenchmarkFiles(b, testFiles[n].filename); err != nil {
|
||||
b.Fatalf("failed to download testdata: %s", err)
|
||||
}
|
||||
data := readFile(b, filepath.Join(benchDir, testFiles[n].filename))
|
||||
if n := testFiles[n].sizeLimit; 0 < n && n < len(data) {
|
||||
data = data[:n]
|
||||
}
|
||||
if decode {
|
||||
benchDecode(b, data)
|
||||
} else {
|
||||
benchEncode(b, data)
|
||||
}
|
||||
}
|
||||
|
||||
// Naming convention is kept similar to what snappy's C++ implementation uses.
|
||||
func Benchmark_UFlat0(b *testing.B) { benchFile(b, 0, true) }
|
||||
func Benchmark_UFlat1(b *testing.B) { benchFile(b, 1, true) }
|
||||
func Benchmark_UFlat2(b *testing.B) { benchFile(b, 2, true) }
|
||||
func Benchmark_UFlat3(b *testing.B) { benchFile(b, 3, true) }
|
||||
func Benchmark_UFlat4(b *testing.B) { benchFile(b, 4, true) }
|
||||
func Benchmark_UFlat5(b *testing.B) { benchFile(b, 5, true) }
|
||||
func Benchmark_UFlat6(b *testing.B) { benchFile(b, 6, true) }
|
||||
func Benchmark_UFlat7(b *testing.B) { benchFile(b, 7, true) }
|
||||
func Benchmark_UFlat8(b *testing.B) { benchFile(b, 8, true) }
|
||||
func Benchmark_UFlat9(b *testing.B) { benchFile(b, 9, true) }
|
||||
func Benchmark_UFlat10(b *testing.B) { benchFile(b, 10, true) }
|
||||
func Benchmark_UFlat11(b *testing.B) { benchFile(b, 11, true) }
|
||||
func Benchmark_ZFlat0(b *testing.B) { benchFile(b, 0, false) }
|
||||
func Benchmark_ZFlat1(b *testing.B) { benchFile(b, 1, false) }
|
||||
func Benchmark_ZFlat2(b *testing.B) { benchFile(b, 2, false) }
|
||||
func Benchmark_ZFlat3(b *testing.B) { benchFile(b, 3, false) }
|
||||
func Benchmark_ZFlat4(b *testing.B) { benchFile(b, 4, false) }
|
||||
func Benchmark_ZFlat5(b *testing.B) { benchFile(b, 5, false) }
|
||||
func Benchmark_ZFlat6(b *testing.B) { benchFile(b, 6, false) }
|
||||
func Benchmark_ZFlat7(b *testing.B) { benchFile(b, 7, false) }
|
||||
func Benchmark_ZFlat8(b *testing.B) { benchFile(b, 8, false) }
|
||||
func Benchmark_ZFlat9(b *testing.B) { benchFile(b, 9, false) }
|
||||
func Benchmark_ZFlat10(b *testing.B) { benchFile(b, 10, false) }
|
||||
func Benchmark_ZFlat11(b *testing.B) { benchFile(b, 11, false) }
|
||||
1
vendor/github.com/golang/snappy/testdata/pi.txt
generated
vendored
Normal file
1
vendor/github.com/golang/snappy/testdata/pi.txt
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
BIN
vendor/github.com/golang/snappy/testdata/pi.txt.rawsnappy
generated
vendored
Normal file
BIN
vendor/github.com/golang/snappy/testdata/pi.txt.rawsnappy
generated
vendored
Normal file
Binary file not shown.
191
vendor/github.com/juju/ratelimit/LICENSE
generated
vendored
Normal file
191
vendor/github.com/juju/ratelimit/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,191 @@
|
||||
All files in this repository are licensed as follows. If you contribute
|
||||
to this repository, it is assumed that you license your contribution
|
||||
under the same license unless you state otherwise.
|
||||
|
||||
All files Copyright (C) 2015 Canonical Ltd. unless otherwise specified in the file.
|
||||
|
||||
This software is licensed under the LGPLv3, included below.
|
||||
|
||||
As a special exception to the GNU Lesser General Public License version 3
|
||||
("LGPL3"), the copyright holders of this Library give you permission to
|
||||
convey to a third party a Combined Work that links statically or dynamically
|
||||
to this Library without providing any Minimal Corresponding Source or
|
||||
Minimal Application Code as set out in 4d or providing the installation
|
||||
information set out in section 4e, provided that you comply with the other
|
||||
provisions of LGPL3 and provided that you meet, for the Application the
|
||||
terms and conditions of the license(s) which apply to the Application.
|
||||
|
||||
Except as stated in this special exception, the provisions of LGPL3 will
|
||||
continue to comply in full to this Library. If you modify this Library, you
|
||||
may apply this exception to your version of this Library, but you are not
|
||||
obliged to do so. If you do not wish to do so, delete this exception
|
||||
statement from your version. This exception does not (and cannot) modify any
|
||||
license terms which apply to the Application, with which you must still
|
||||
comply.
|
||||
|
||||
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
|
||||
This version of the GNU Lesser General Public License incorporates
|
||||
the terms and conditions of version 3 of the GNU General Public
|
||||
License, supplemented by the additional permissions listed below.
|
||||
|
||||
0. Additional Definitions.
|
||||
|
||||
As used herein, "this License" refers to version 3 of the GNU Lesser
|
||||
General Public License, and the "GNU GPL" refers to version 3 of the GNU
|
||||
General Public License.
|
||||
|
||||
"The Library" refers to a covered work governed by this License,
|
||||
other than an Application or a Combined Work as defined below.
|
||||
|
||||
An "Application" is any work that makes use of an interface provided
|
||||
by the Library, but which is not otherwise based on the Library.
|
||||
Defining a subclass of a class defined by the Library is deemed a mode
|
||||
of using an interface provided by the Library.
|
||||
|
||||
A "Combined Work" is a work produced by combining or linking an
|
||||
Application with the Library. The particular version of the Library
|
||||
with which the Combined Work was made is also called the "Linked
|
||||
Version".
|
||||
|
||||
The "Minimal Corresponding Source" for a Combined Work means the
|
||||
Corresponding Source for the Combined Work, excluding any source code
|
||||
for portions of the Combined Work that, considered in isolation, are
|
||||
based on the Application, and not on the Linked Version.
|
||||
|
||||
The "Corresponding Application Code" for a Combined Work means the
|
||||
object code and/or source code for the Application, including any data
|
||||
and utility programs needed for reproducing the Combined Work from the
|
||||
Application, but excluding the System Libraries of the Combined Work.
|
||||
|
||||
1. Exception to Section 3 of the GNU GPL.
|
||||
|
||||
You may convey a covered work under sections 3 and 4 of this License
|
||||
without being bound by section 3 of the GNU GPL.
|
||||
|
||||
2. Conveying Modified Versions.
|
||||
|
||||
If you modify a copy of the Library, and, in your modifications, a
|
||||
facility refers to a function or data to be supplied by an Application
|
||||
that uses the facility (other than as an argument passed when the
|
||||
facility is invoked), then you may convey a copy of the modified
|
||||
version:
|
||||
|
||||
a) under this License, provided that you make a good faith effort to
|
||||
ensure that, in the event an Application does not supply the
|
||||
function or data, the facility still operates, and performs
|
||||
whatever part of its purpose remains meaningful, or
|
||||
|
||||
b) under the GNU GPL, with none of the additional permissions of
|
||||
this License applicable to that copy.
|
||||
|
||||
3. Object Code Incorporating Material from Library Header Files.
|
||||
|
||||
The object code form of an Application may incorporate material from
|
||||
a header file that is part of the Library. You may convey such object
|
||||
code under terms of your choice, provided that, if the incorporated
|
||||
material is not limited to numerical parameters, data structure
|
||||
layouts and accessors, or small macros, inline functions and templates
|
||||
(ten or fewer lines in length), you do both of the following:
|
||||
|
||||
a) Give prominent notice with each copy of the object code that the
|
||||
Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the object code with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
4. Combined Works.
|
||||
|
||||
You may convey a Combined Work under terms of your choice that,
|
||||
taken together, effectively do not restrict modification of the
|
||||
portions of the Library contained in the Combined Work and reverse
|
||||
engineering for debugging such modifications, if you also do each of
|
||||
the following:
|
||||
|
||||
a) Give prominent notice with each copy of the Combined Work that
|
||||
the Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the Combined Work with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
c) For a Combined Work that displays copyright notices during
|
||||
execution, include the copyright notice for the Library among
|
||||
these notices, as well as a reference directing the user to the
|
||||
copies of the GNU GPL and this license document.
|
||||
|
||||
d) Do one of the following:
|
||||
|
||||
0) Convey the Minimal Corresponding Source under the terms of this
|
||||
License, and the Corresponding Application Code in a form
|
||||
suitable for, and under terms that permit, the user to
|
||||
recombine or relink the Application with a modified version of
|
||||
the Linked Version to produce a modified Combined Work, in the
|
||||
manner specified by section 6 of the GNU GPL for conveying
|
||||
Corresponding Source.
|
||||
|
||||
1) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (a) uses at run time
|
||||
a copy of the Library already present on the user's computer
|
||||
system, and (b) will operate properly with a modified version
|
||||
of the Library that is interface-compatible with the Linked
|
||||
Version.
|
||||
|
||||
e) Provide Installation Information, but only if you would otherwise
|
||||
be required to provide such information under section 6 of the
|
||||
GNU GPL, and only to the extent that such information is
|
||||
necessary to install and execute a modified version of the
|
||||
Combined Work produced by recombining or relinking the
|
||||
Application with a modified version of the Linked Version. (If
|
||||
you use option 4d0, the Installation Information must accompany
|
||||
the Minimal Corresponding Source and Corresponding Application
|
||||
Code. If you use option 4d1, you must provide the Installation
|
||||
Information in the manner specified by section 6 of the GNU GPL
|
||||
for conveying Corresponding Source.)
|
||||
|
||||
5. Combined Libraries.
|
||||
|
||||
You may place library facilities that are a work based on the
|
||||
Library side by side in a single library together with other library
|
||||
facilities that are not Applications and are not covered by this
|
||||
License, and convey such a combined library under terms of your
|
||||
choice, if you do both of the following:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work based
|
||||
on the Library, uncombined with any other library facilities,
|
||||
conveyed under the terms of this License.
|
||||
|
||||
b) Give prominent notice with the combined library that part of it
|
||||
is a work based on the Library, and explaining where to find the
|
||||
accompanying uncombined form of the same work.
|
||||
|
||||
6. Revised Versions of the GNU Lesser General Public License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions
|
||||
of the GNU Lesser General Public License from time to time. Such new
|
||||
versions will be similar in spirit to the present version, but may
|
||||
differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Library as you received it specifies that a certain numbered version
|
||||
of the GNU Lesser General Public License "or any later version"
|
||||
applies to it, you have the option of following the terms and
|
||||
conditions either of that published version or of any later version
|
||||
published by the Free Software Foundation. If the Library as you
|
||||
received it does not specify a version number of the GNU Lesser
|
||||
General Public License, you may choose any version of the GNU Lesser
|
||||
General Public License ever published by the Free Software Foundation.
|
||||
|
||||
If the Library as you received it specifies that a proxy can decide
|
||||
whether future versions of the GNU Lesser General Public License shall
|
||||
apply, that proxy's public statement of acceptance of any version is
|
||||
permanent authorization for you to choose that version for the
|
||||
Library.
|
||||
117
vendor/github.com/juju/ratelimit/README.md
generated
vendored
Normal file
117
vendor/github.com/juju/ratelimit/README.md
generated
vendored
Normal file
@@ -0,0 +1,117 @@
|
||||
# ratelimit
|
||||
--
|
||||
import "github.com/juju/ratelimit"
|
||||
|
||||
The ratelimit package provides an efficient token bucket implementation. See
|
||||
http://en.wikipedia.org/wiki/Token_bucket.
|
||||
|
||||
## Usage
|
||||
|
||||
#### func Reader
|
||||
|
||||
```go
|
||||
func Reader(r io.Reader, bucket *Bucket) io.Reader
|
||||
```
|
||||
Reader returns a reader that is rate limited by the given token bucket. Each
|
||||
token in the bucket represents one byte.
|
||||
|
||||
#### func Writer
|
||||
|
||||
```go
|
||||
func Writer(w io.Writer, bucket *Bucket) io.Writer
|
||||
```
|
||||
Writer returns a writer that is rate limited by the given token bucket. Each
|
||||
token in the bucket represents one byte.
|
||||
|
||||
#### type Bucket
|
||||
|
||||
```go
|
||||
type Bucket struct {
|
||||
}
|
||||
```
|
||||
|
||||
Bucket represents a token bucket that fills at a predetermined rate. Methods on
|
||||
Bucket may be called concurrently.
|
||||
|
||||
#### func NewBucket
|
||||
|
||||
```go
|
||||
func NewBucket(fillInterval time.Duration, capacity int64) *Bucket
|
||||
```
|
||||
NewBucket returns a new token bucket that fills at the rate of one token every
|
||||
fillInterval, up to the given maximum capacity. Both arguments must be positive.
|
||||
The bucket is initially full.
|
||||
|
||||
#### func NewBucketWithQuantum
|
||||
|
||||
```go
|
||||
func NewBucketWithQuantum(fillInterval time.Duration, capacity, quantum int64) *Bucket
|
||||
```
|
||||
NewBucketWithQuantum is similar to NewBucket, but allows the specification of
|
||||
the quantum size - quantum tokens are added every fillInterval.
|
||||
|
||||
#### func NewBucketWithRate
|
||||
|
||||
```go
|
||||
func NewBucketWithRate(rate float64, capacity int64) *Bucket
|
||||
```
|
||||
NewBucketWithRate returns a token bucket that fills the bucket at the rate of
|
||||
rate tokens per second up to the given maximum capacity. Because of limited
|
||||
clock resolution, at high rates, the actual rate may be up to 1% different from
|
||||
the specified rate.
|
||||
|
||||
#### func (*Bucket) Rate
|
||||
|
||||
```go
|
||||
func (tb *Bucket) Rate() float64
|
||||
```
|
||||
Rate returns the fill rate of the bucket, in tokens per second.
|
||||
|
||||
#### func (*Bucket) Take
|
||||
|
||||
```go
|
||||
func (tb *Bucket) Take(count int64) time.Duration
|
||||
```
|
||||
Take takes count tokens from the bucket without blocking. It returns the time
|
||||
that the caller should wait until the tokens are actually available.
|
||||
|
||||
Note that if the request is irrevocable - there is no way to return tokens to
|
||||
the bucket once this method commits us to taking them.
|
||||
|
||||
#### func (*Bucket) TakeAvailable
|
||||
|
||||
```go
|
||||
func (tb *Bucket) TakeAvailable(count int64) int64
|
||||
```
|
||||
TakeAvailable takes up to count immediately available tokens from the bucket. It
|
||||
returns the number of tokens removed, or zero if there are no available tokens.
|
||||
It does not block.
|
||||
|
||||
#### func (*Bucket) TakeMaxDuration
|
||||
|
||||
```go
|
||||
func (tb *Bucket) TakeMaxDuration(count int64, maxWait time.Duration) (time.Duration, bool)
|
||||
```
|
||||
TakeMaxDuration is like Take, except that it will only take tokens from the
|
||||
bucket if the wait time for the tokens is no greater than maxWait.
|
||||
|
||||
If it would take longer than maxWait for the tokens to become available, it does
|
||||
nothing and reports false, otherwise it returns the time that the caller should
|
||||
wait until the tokens are actually available, and reports true.
|
||||
|
||||
#### func (*Bucket) Wait
|
||||
|
||||
```go
|
||||
func (tb *Bucket) Wait(count int64)
|
||||
```
|
||||
Wait takes count tokens from the bucket, waiting until they are available.
|
||||
|
||||
#### func (*Bucket) WaitMaxDuration
|
||||
|
||||
```go
|
||||
func (tb *Bucket) WaitMaxDuration(count int64, maxWait time.Duration) bool
|
||||
```
|
||||
WaitMaxDuration is like Wait except that it will only take tokens from the
|
||||
bucket if it needs to wait for no greater than maxWait. It reports whether any
|
||||
tokens have been removed from the bucket If no tokens have been removed, it
|
||||
returns immediately.
|
||||
245
vendor/github.com/juju/ratelimit/ratelimit.go
generated
vendored
Normal file
245
vendor/github.com/juju/ratelimit/ratelimit.go
generated
vendored
Normal file
@@ -0,0 +1,245 @@
|
||||
// Copyright 2014 Canonical Ltd.
|
||||
// Licensed under the LGPLv3 with static-linking exception.
|
||||
// See LICENCE file for details.
|
||||
|
||||
// The ratelimit package provides an efficient token bucket implementation
|
||||
// that can be used to limit the rate of arbitrary things.
|
||||
// See http://en.wikipedia.org/wiki/Token_bucket.
|
||||
package ratelimit
|
||||
|
||||
import (
|
||||
"math"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Bucket represents a token bucket that fills at a predetermined rate.
|
||||
// Methods on Bucket may be called concurrently.
|
||||
type Bucket struct {
|
||||
startTime time.Time
|
||||
capacity int64
|
||||
quantum int64
|
||||
fillInterval time.Duration
|
||||
|
||||
// The mutex guards the fields following it.
|
||||
mu sync.Mutex
|
||||
|
||||
// avail holds the number of available tokens
|
||||
// in the bucket, as of availTick ticks from startTime.
|
||||
// It will be negative when there are consumers
|
||||
// waiting for tokens.
|
||||
avail int64
|
||||
availTick int64
|
||||
}
|
||||
|
||||
// NewBucket returns a new token bucket that fills at the
|
||||
// rate of one token every fillInterval, up to the given
|
||||
// maximum capacity. Both arguments must be
|
||||
// positive. The bucket is initially full.
|
||||
func NewBucket(fillInterval time.Duration, capacity int64) *Bucket {
|
||||
return NewBucketWithQuantum(fillInterval, capacity, 1)
|
||||
}
|
||||
|
||||
// rateMargin specifes the allowed variance of actual
|
||||
// rate from specified rate. 1% seems reasonable.
|
||||
const rateMargin = 0.01
|
||||
|
||||
// NewBucketWithRate returns a token bucket that fills the bucket
|
||||
// at the rate of rate tokens per second up to the given
|
||||
// maximum capacity. Because of limited clock resolution,
|
||||
// at high rates, the actual rate may be up to 1% different from the
|
||||
// specified rate.
|
||||
func NewBucketWithRate(rate float64, capacity int64) *Bucket {
|
||||
for quantum := int64(1); quantum < 1<<50; quantum = nextQuantum(quantum) {
|
||||
fillInterval := time.Duration(1e9 * float64(quantum) / rate)
|
||||
if fillInterval <= 0 {
|
||||
continue
|
||||
}
|
||||
tb := NewBucketWithQuantum(fillInterval, capacity, quantum)
|
||||
if diff := math.Abs(tb.Rate() - rate); diff/rate <= rateMargin {
|
||||
return tb
|
||||
}
|
||||
}
|
||||
panic("cannot find suitable quantum for " + strconv.FormatFloat(rate, 'g', -1, 64))
|
||||
}
|
||||
|
||||
// nextQuantum returns the next quantum to try after q.
|
||||
// We grow the quantum exponentially, but slowly, so we
|
||||
// get a good fit in the lower numbers.
|
||||
func nextQuantum(q int64) int64 {
|
||||
q1 := q * 11 / 10
|
||||
if q1 == q {
|
||||
q1++
|
||||
}
|
||||
return q1
|
||||
}
|
||||
|
||||
// NewBucketWithQuantum is similar to NewBucket, but allows
|
||||
// the specification of the quantum size - quantum tokens
|
||||
// are added every fillInterval.
|
||||
func NewBucketWithQuantum(fillInterval time.Duration, capacity, quantum int64) *Bucket {
|
||||
if fillInterval <= 0 {
|
||||
panic("token bucket fill interval is not > 0")
|
||||
}
|
||||
if capacity <= 0 {
|
||||
panic("token bucket capacity is not > 0")
|
||||
}
|
||||
if quantum <= 0 {
|
||||
panic("token bucket quantum is not > 0")
|
||||
}
|
||||
return &Bucket{
|
||||
startTime: time.Now(),
|
||||
capacity: capacity,
|
||||
quantum: quantum,
|
||||
avail: capacity,
|
||||
fillInterval: fillInterval,
|
||||
}
|
||||
}
|
||||
|
||||
// Wait takes count tokens from the bucket, waiting until they are
|
||||
// available.
|
||||
func (tb *Bucket) Wait(count int64) {
|
||||
if d := tb.Take(count); d > 0 {
|
||||
time.Sleep(d)
|
||||
}
|
||||
}
|
||||
|
||||
// WaitMaxDuration is like Wait except that it will
|
||||
// only take tokens from the bucket if it needs to wait
|
||||
// for no greater than maxWait. It reports whether
|
||||
// any tokens have been removed from the bucket
|
||||
// If no tokens have been removed, it returns immediately.
|
||||
func (tb *Bucket) WaitMaxDuration(count int64, maxWait time.Duration) bool {
|
||||
d, ok := tb.TakeMaxDuration(count, maxWait)
|
||||
if d > 0 {
|
||||
time.Sleep(d)
|
||||
}
|
||||
return ok
|
||||
}
|
||||
|
||||
const infinityDuration time.Duration = 0x7fffffffffffffff
|
||||
|
||||
// Take takes count tokens from the bucket without blocking. It returns
|
||||
// the time that the caller should wait until the tokens are actually
|
||||
// available.
|
||||
//
|
||||
// Note that if the request is irrevocable - there is no way to return
|
||||
// tokens to the bucket once this method commits us to taking them.
|
||||
func (tb *Bucket) Take(count int64) time.Duration {
|
||||
d, _ := tb.take(time.Now(), count, infinityDuration)
|
||||
return d
|
||||
}
|
||||
|
||||
// TakeMaxDuration is like Take, except that
|
||||
// it will only take tokens from the bucket if the wait
|
||||
// time for the tokens is no greater than maxWait.
|
||||
//
|
||||
// If it would take longer than maxWait for the tokens
|
||||
// to become available, it does nothing and reports false,
|
||||
// otherwise it returns the time that the caller should
|
||||
// wait until the tokens are actually available, and reports
|
||||
// true.
|
||||
func (tb *Bucket) TakeMaxDuration(count int64, maxWait time.Duration) (time.Duration, bool) {
|
||||
return tb.take(time.Now(), count, maxWait)
|
||||
}
|
||||
|
||||
// TakeAvailable takes up to count immediately available tokens from the
|
||||
// bucket. It returns the number of tokens removed, or zero if there are
|
||||
// no available tokens. It does not block.
|
||||
func (tb *Bucket) TakeAvailable(count int64) int64 {
|
||||
return tb.takeAvailable(time.Now(), count)
|
||||
}
|
||||
|
||||
// takeAvailable is the internal version of TakeAvailable - it takes the
|
||||
// current time as an argument to enable easy testing.
|
||||
func (tb *Bucket) takeAvailable(now time.Time, count int64) int64 {
|
||||
if count <= 0 {
|
||||
return 0
|
||||
}
|
||||
tb.mu.Lock()
|
||||
defer tb.mu.Unlock()
|
||||
|
||||
tb.adjust(now)
|
||||
if tb.avail <= 0 {
|
||||
return 0
|
||||
}
|
||||
if count > tb.avail {
|
||||
count = tb.avail
|
||||
}
|
||||
tb.avail -= count
|
||||
return count
|
||||
}
|
||||
|
||||
// Available returns the number of available tokens. It will be negative
|
||||
// when there are consumers waiting for tokens. Note that if this
|
||||
// returns greater than zero, it does not guarantee that calls that take
|
||||
// tokens from the buffer will succeed, as the number of available
|
||||
// tokens could have changed in the meantime. This method is intended
|
||||
// primarily for metrics reporting and debugging.
|
||||
func (tb *Bucket) Available() int64 {
|
||||
return tb.available(time.Now())
|
||||
}
|
||||
|
||||
// available is the internal version of available - it takes the current time as
|
||||
// an argument to enable easy testing.
|
||||
func (tb *Bucket) available(now time.Time) int64 {
|
||||
tb.mu.Lock()
|
||||
defer tb.mu.Unlock()
|
||||
tb.adjust(now)
|
||||
return tb.avail
|
||||
}
|
||||
|
||||
// Capacity returns the capacity that the bucket was created with.
|
||||
func (tb *Bucket) Capacity() int64 {
|
||||
return tb.capacity
|
||||
}
|
||||
|
||||
// Rate returns the fill rate of the bucket, in tokens per second.
|
||||
func (tb *Bucket) Rate() float64 {
|
||||
return 1e9 * float64(tb.quantum) / float64(tb.fillInterval)
|
||||
}
|
||||
|
||||
// take is the internal version of Take - it takes the current time as
|
||||
// an argument to enable easy testing.
|
||||
func (tb *Bucket) take(now time.Time, count int64, maxWait time.Duration) (time.Duration, bool) {
|
||||
if count <= 0 {
|
||||
return 0, true
|
||||
}
|
||||
tb.mu.Lock()
|
||||
defer tb.mu.Unlock()
|
||||
|
||||
currentTick := tb.adjust(now)
|
||||
avail := tb.avail - count
|
||||
if avail >= 0 {
|
||||
tb.avail = avail
|
||||
return 0, true
|
||||
}
|
||||
// Round up the missing tokens to the nearest multiple
|
||||
// of quantum - the tokens won't be available until
|
||||
// that tick.
|
||||
endTick := currentTick + (-avail+tb.quantum-1)/tb.quantum
|
||||
endTime := tb.startTime.Add(time.Duration(endTick) * tb.fillInterval)
|
||||
waitTime := endTime.Sub(now)
|
||||
if waitTime > maxWait {
|
||||
return 0, false
|
||||
}
|
||||
tb.avail = avail
|
||||
return waitTime, true
|
||||
}
|
||||
|
||||
// adjust adjusts the current bucket capacity based on the current time.
|
||||
// It returns the current tick.
|
||||
func (tb *Bucket) adjust(now time.Time) (currentTick int64) {
|
||||
currentTick = int64(now.Sub(tb.startTime) / tb.fillInterval)
|
||||
|
||||
if tb.avail >= tb.capacity {
|
||||
return
|
||||
}
|
||||
tb.avail += (currentTick - tb.availTick) * tb.quantum
|
||||
if tb.avail > tb.capacity {
|
||||
tb.avail = tb.capacity
|
||||
}
|
||||
tb.availTick = currentTick
|
||||
return
|
||||
}
|
||||
389
vendor/github.com/juju/ratelimit/ratelimit_test.go
generated
vendored
Normal file
389
vendor/github.com/juju/ratelimit/ratelimit_test.go
generated
vendored
Normal file
@@ -0,0 +1,389 @@
|
||||
// Copyright 2014 Canonical Ltd.
|
||||
// Licensed under the LGPLv3 with static-linking exception.
|
||||
// See LICENCE file for details.
|
||||
|
||||
package ratelimit
|
||||
|
||||
import (
|
||||
"math"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
gc "gopkg.in/check.v1"
|
||||
)
|
||||
|
||||
func TestPackage(t *testing.T) {
|
||||
gc.TestingT(t)
|
||||
}
|
||||
|
||||
type rateLimitSuite struct{}
|
||||
|
||||
var _ = gc.Suite(rateLimitSuite{})
|
||||
|
||||
type takeReq struct {
|
||||
time time.Duration
|
||||
count int64
|
||||
expectWait time.Duration
|
||||
}
|
||||
|
||||
var takeTests = []struct {
|
||||
about string
|
||||
fillInterval time.Duration
|
||||
capacity int64
|
||||
reqs []takeReq
|
||||
}{{
|
||||
about: "serial requests",
|
||||
fillInterval: 250 * time.Millisecond,
|
||||
capacity: 10,
|
||||
reqs: []takeReq{{
|
||||
time: 0,
|
||||
count: 0,
|
||||
expectWait: 0,
|
||||
}, {
|
||||
time: 0,
|
||||
count: 10,
|
||||
expectWait: 0,
|
||||
}, {
|
||||
time: 0,
|
||||
count: 1,
|
||||
expectWait: 250 * time.Millisecond,
|
||||
}, {
|
||||
time: 250 * time.Millisecond,
|
||||
count: 1,
|
||||
expectWait: 250 * time.Millisecond,
|
||||
}},
|
||||
}, {
|
||||
about: "concurrent requests",
|
||||
fillInterval: 250 * time.Millisecond,
|
||||
capacity: 10,
|
||||
reqs: []takeReq{{
|
||||
time: 0,
|
||||
count: 10,
|
||||
expectWait: 0,
|
||||
}, {
|
||||
time: 0,
|
||||
count: 2,
|
||||
expectWait: 500 * time.Millisecond,
|
||||
}, {
|
||||
time: 0,
|
||||
count: 2,
|
||||
expectWait: 1000 * time.Millisecond,
|
||||
}, {
|
||||
time: 0,
|
||||
count: 1,
|
||||
expectWait: 1250 * time.Millisecond,
|
||||
}},
|
||||
}, {
|
||||
about: "more than capacity",
|
||||
fillInterval: 1 * time.Millisecond,
|
||||
capacity: 10,
|
||||
reqs: []takeReq{{
|
||||
time: 0,
|
||||
count: 10,
|
||||
expectWait: 0,
|
||||
}, {
|
||||
time: 20 * time.Millisecond,
|
||||
count: 15,
|
||||
expectWait: 5 * time.Millisecond,
|
||||
}},
|
||||
}, {
|
||||
about: "sub-quantum time",
|
||||
fillInterval: 10 * time.Millisecond,
|
||||
capacity: 10,
|
||||
reqs: []takeReq{{
|
||||
time: 0,
|
||||
count: 10,
|
||||
expectWait: 0,
|
||||
}, {
|
||||
time: 7 * time.Millisecond,
|
||||
count: 1,
|
||||
expectWait: 3 * time.Millisecond,
|
||||
}, {
|
||||
time: 8 * time.Millisecond,
|
||||
count: 1,
|
||||
expectWait: 12 * time.Millisecond,
|
||||
}},
|
||||
}, {
|
||||
about: "within capacity",
|
||||
fillInterval: 10 * time.Millisecond,
|
||||
capacity: 5,
|
||||
reqs: []takeReq{{
|
||||
time: 0,
|
||||
count: 5,
|
||||
expectWait: 0,
|
||||
}, {
|
||||
time: 60 * time.Millisecond,
|
||||
count: 5,
|
||||
expectWait: 0,
|
||||
}, {
|
||||
time: 60 * time.Millisecond,
|
||||
count: 1,
|
||||
expectWait: 10 * time.Millisecond,
|
||||
}, {
|
||||
time: 80 * time.Millisecond,
|
||||
count: 2,
|
||||
expectWait: 10 * time.Millisecond,
|
||||
}},
|
||||
}}
|
||||
|
||||
var availTests = []struct {
|
||||
about string
|
||||
capacity int64
|
||||
fillInterval time.Duration
|
||||
take int64
|
||||
sleep time.Duration
|
||||
|
||||
expectCountAfterTake int64
|
||||
expectCountAfterSleep int64
|
||||
}{{
|
||||
about: "should fill tokens after interval",
|
||||
capacity: 5,
|
||||
fillInterval: time.Second,
|
||||
take: 5,
|
||||
sleep: time.Second,
|
||||
expectCountAfterTake: 0,
|
||||
expectCountAfterSleep: 1,
|
||||
}, {
|
||||
about: "should fill tokens plus existing count",
|
||||
capacity: 2,
|
||||
fillInterval: time.Second,
|
||||
take: 1,
|
||||
sleep: time.Second,
|
||||
expectCountAfterTake: 1,
|
||||
expectCountAfterSleep: 2,
|
||||
}, {
|
||||
about: "shouldn't fill before interval",
|
||||
capacity: 2,
|
||||
fillInterval: 2 * time.Second,
|
||||
take: 1,
|
||||
sleep: time.Second,
|
||||
expectCountAfterTake: 1,
|
||||
expectCountAfterSleep: 1,
|
||||
}, {
|
||||
about: "should fill only once after 1*interval before 2*interval",
|
||||
capacity: 2,
|
||||
fillInterval: 2 * time.Second,
|
||||
take: 1,
|
||||
sleep: 3 * time.Second,
|
||||
expectCountAfterTake: 1,
|
||||
expectCountAfterSleep: 2,
|
||||
}}
|
||||
|
||||
func (rateLimitSuite) TestTake(c *gc.C) {
|
||||
for i, test := range takeTests {
|
||||
tb := NewBucket(test.fillInterval, test.capacity)
|
||||
for j, req := range test.reqs {
|
||||
d, ok := tb.take(tb.startTime.Add(req.time), req.count, infinityDuration)
|
||||
c.Assert(ok, gc.Equals, true)
|
||||
if d != req.expectWait {
|
||||
c.Fatalf("test %d.%d, %s, got %v want %v", i, j, test.about, d, req.expectWait)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (rateLimitSuite) TestTakeMaxDuration(c *gc.C) {
|
||||
for i, test := range takeTests {
|
||||
tb := NewBucket(test.fillInterval, test.capacity)
|
||||
for j, req := range test.reqs {
|
||||
if req.expectWait > 0 {
|
||||
d, ok := tb.take(tb.startTime.Add(req.time), req.count, req.expectWait-1)
|
||||
c.Assert(ok, gc.Equals, false)
|
||||
c.Assert(d, gc.Equals, time.Duration(0))
|
||||
}
|
||||
d, ok := tb.take(tb.startTime.Add(req.time), req.count, req.expectWait)
|
||||
c.Assert(ok, gc.Equals, true)
|
||||
if d != req.expectWait {
|
||||
c.Fatalf("test %d.%d, %s, got %v want %v", i, j, test.about, d, req.expectWait)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type takeAvailableReq struct {
|
||||
time time.Duration
|
||||
count int64
|
||||
expect int64
|
||||
}
|
||||
|
||||
var takeAvailableTests = []struct {
|
||||
about string
|
||||
fillInterval time.Duration
|
||||
capacity int64
|
||||
reqs []takeAvailableReq
|
||||
}{{
|
||||
about: "serial requests",
|
||||
fillInterval: 250 * time.Millisecond,
|
||||
capacity: 10,
|
||||
reqs: []takeAvailableReq{{
|
||||
time: 0,
|
||||
count: 0,
|
||||
expect: 0,
|
||||
}, {
|
||||
time: 0,
|
||||
count: 10,
|
||||
expect: 10,
|
||||
}, {
|
||||
time: 0,
|
||||
count: 1,
|
||||
expect: 0,
|
||||
}, {
|
||||
time: 250 * time.Millisecond,
|
||||
count: 1,
|
||||
expect: 1,
|
||||
}},
|
||||
}, {
|
||||
about: "concurrent requests",
|
||||
fillInterval: 250 * time.Millisecond,
|
||||
capacity: 10,
|
||||
reqs: []takeAvailableReq{{
|
||||
time: 0,
|
||||
count: 5,
|
||||
expect: 5,
|
||||
}, {
|
||||
time: 0,
|
||||
count: 2,
|
||||
expect: 2,
|
||||
}, {
|
||||
time: 0,
|
||||
count: 5,
|
||||
expect: 3,
|
||||
}, {
|
||||
time: 0,
|
||||
count: 1,
|
||||
expect: 0,
|
||||
}},
|
||||
}, {
|
||||
about: "more than capacity",
|
||||
fillInterval: 1 * time.Millisecond,
|
||||
capacity: 10,
|
||||
reqs: []takeAvailableReq{{
|
||||
time: 0,
|
||||
count: 10,
|
||||
expect: 10,
|
||||
}, {
|
||||
time: 20 * time.Millisecond,
|
||||
count: 15,
|
||||
expect: 10,
|
||||
}},
|
||||
}, {
|
||||
about: "within capacity",
|
||||
fillInterval: 10 * time.Millisecond,
|
||||
capacity: 5,
|
||||
reqs: []takeAvailableReq{{
|
||||
time: 0,
|
||||
count: 5,
|
||||
expect: 5,
|
||||
}, {
|
||||
time: 60 * time.Millisecond,
|
||||
count: 5,
|
||||
expect: 5,
|
||||
}, {
|
||||
time: 70 * time.Millisecond,
|
||||
count: 1,
|
||||
expect: 1,
|
||||
}},
|
||||
}}
|
||||
|
||||
func (rateLimitSuite) TestTakeAvailable(c *gc.C) {
|
||||
for i, test := range takeAvailableTests {
|
||||
tb := NewBucket(test.fillInterval, test.capacity)
|
||||
for j, req := range test.reqs {
|
||||
d := tb.takeAvailable(tb.startTime.Add(req.time), req.count)
|
||||
if d != req.expect {
|
||||
c.Fatalf("test %d.%d, %s, got %v want %v", i, j, test.about, d, req.expect)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (rateLimitSuite) TestPanics(c *gc.C) {
|
||||
c.Assert(func() { NewBucket(0, 1) }, gc.PanicMatches, "token bucket fill interval is not > 0")
|
||||
c.Assert(func() { NewBucket(-2, 1) }, gc.PanicMatches, "token bucket fill interval is not > 0")
|
||||
c.Assert(func() { NewBucket(1, 0) }, gc.PanicMatches, "token bucket capacity is not > 0")
|
||||
c.Assert(func() { NewBucket(1, -2) }, gc.PanicMatches, "token bucket capacity is not > 0")
|
||||
}
|
||||
|
||||
func isCloseTo(x, y, tolerance float64) bool {
|
||||
return math.Abs(x-y)/y < tolerance
|
||||
}
|
||||
|
||||
func (rateLimitSuite) TestRate(c *gc.C) {
|
||||
tb := NewBucket(1, 1)
|
||||
if !isCloseTo(tb.Rate(), 1e9, 0.00001) {
|
||||
c.Fatalf("got %v want 1e9", tb.Rate())
|
||||
}
|
||||
tb = NewBucket(2*time.Second, 1)
|
||||
if !isCloseTo(tb.Rate(), 0.5, 0.00001) {
|
||||
c.Fatalf("got %v want 0.5", tb.Rate())
|
||||
}
|
||||
tb = NewBucketWithQuantum(100*time.Millisecond, 1, 5)
|
||||
if !isCloseTo(tb.Rate(), 50, 0.00001) {
|
||||
c.Fatalf("got %v want 50", tb.Rate())
|
||||
}
|
||||
}
|
||||
|
||||
func checkRate(c *gc.C, rate float64) {
|
||||
tb := NewBucketWithRate(rate, 1<<62)
|
||||
if !isCloseTo(tb.Rate(), rate, rateMargin) {
|
||||
c.Fatalf("got %g want %v", tb.Rate(), rate)
|
||||
}
|
||||
d, ok := tb.take(tb.startTime, 1<<62, infinityDuration)
|
||||
c.Assert(ok, gc.Equals, true)
|
||||
c.Assert(d, gc.Equals, time.Duration(0))
|
||||
|
||||
// Check that the actual rate is as expected by
|
||||
// asking for a not-quite multiple of the bucket's
|
||||
// quantum and checking that the wait time
|
||||
// correct.
|
||||
d, ok = tb.take(tb.startTime, tb.quantum*2-tb.quantum/2, infinityDuration)
|
||||
c.Assert(ok, gc.Equals, true)
|
||||
expectTime := 1e9 * float64(tb.quantum) * 2 / rate
|
||||
if !isCloseTo(float64(d), expectTime, rateMargin) {
|
||||
c.Fatalf("rate %g: got %g want %v", rate, float64(d), expectTime)
|
||||
}
|
||||
}
|
||||
|
||||
func (rateLimitSuite) TestNewWithRate(c *gc.C) {
|
||||
for rate := float64(1); rate < 1e6; rate += 7 {
|
||||
checkRate(c, rate)
|
||||
}
|
||||
for _, rate := range []float64{
|
||||
1024 * 1024 * 1024,
|
||||
1e-5,
|
||||
0.9e-5,
|
||||
0.5,
|
||||
0.9,
|
||||
0.9e8,
|
||||
3e12,
|
||||
4e18,
|
||||
} {
|
||||
checkRate(c, rate)
|
||||
checkRate(c, rate/3)
|
||||
checkRate(c, rate*1.3)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAvailable(t *testing.T) {
|
||||
for i, tt := range availTests {
|
||||
tb := NewBucket(tt.fillInterval, tt.capacity)
|
||||
if c := tb.takeAvailable(tb.startTime, tt.take); c != tt.take {
|
||||
t.Fatalf("#%d: %s, take = %d, want = %d", i, tt.about, c, tt.take)
|
||||
}
|
||||
if c := tb.available(tb.startTime); c != tt.expectCountAfterTake {
|
||||
t.Fatalf("#%d: %s, after take, available = %d, want = %d", i, tt.about, c, tt.expectCountAfterTake)
|
||||
}
|
||||
if c := tb.available(tb.startTime.Add(tt.sleep)); c != tt.expectCountAfterSleep {
|
||||
t.Fatalf("#%d: %s, after some time it should fill in new tokens, available = %d, want = %d",
|
||||
i, tt.about, c, tt.expectCountAfterSleep)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func BenchmarkWait(b *testing.B) {
|
||||
tb := NewBucket(1, 16*1024)
|
||||
for i := b.N - 1; i >= 0; i-- {
|
||||
tb.Wait(1)
|
||||
}
|
||||
}
|
||||
51
vendor/github.com/juju/ratelimit/reader.go
generated
vendored
Normal file
51
vendor/github.com/juju/ratelimit/reader.go
generated
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
// Copyright 2014 Canonical Ltd.
|
||||
// Licensed under the LGPLv3 with static-linking exception.
|
||||
// See LICENCE file for details.
|
||||
|
||||
package ratelimit
|
||||
|
||||
import "io"
|
||||
|
||||
type reader struct {
|
||||
r io.Reader
|
||||
bucket *Bucket
|
||||
}
|
||||
|
||||
// Reader returns a reader that is rate limited by
|
||||
// the given token bucket. Each token in the bucket
|
||||
// represents one byte.
|
||||
func Reader(r io.Reader, bucket *Bucket) io.Reader {
|
||||
return &reader{
|
||||
r: r,
|
||||
bucket: bucket,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *reader) Read(buf []byte) (int, error) {
|
||||
n, err := r.r.Read(buf)
|
||||
if n <= 0 {
|
||||
return n, err
|
||||
}
|
||||
r.bucket.Wait(int64(n))
|
||||
return n, err
|
||||
}
|
||||
|
||||
type writer struct {
|
||||
w io.Writer
|
||||
bucket *Bucket
|
||||
}
|
||||
|
||||
// Writer returns a reader that is rate limited by
|
||||
// the given token bucket. Each token in the bucket
|
||||
// represents one byte.
|
||||
func Writer(w io.Writer, bucket *Bucket) io.Writer {
|
||||
return &writer{
|
||||
w: w,
|
||||
bucket: bucket,
|
||||
}
|
||||
}
|
||||
|
||||
func (w *writer) Write(buf []byte) (int, error) {
|
||||
w.bucket.Wait(int64(len(buf)))
|
||||
return w.w.Write(buf)
|
||||
}
|
||||
27
vendor/github.com/kardianos/osext/LICENSE
generated
vendored
Normal file
27
vendor/github.com/kardianos/osext/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
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.
|
||||
16
vendor/github.com/kardianos/osext/README.md
generated
vendored
Normal file
16
vendor/github.com/kardianos/osext/README.md
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
### Extensions to the "os" package.
|
||||
|
||||
## Find the current Executable and ExecutableFolder.
|
||||
|
||||
There is sometimes utility in finding the current executable file
|
||||
that is running. This can be used for upgrading the current executable
|
||||
or finding resources located relative to the executable file. Both
|
||||
working directory and the os.Args[0] value are arbitrary and cannot
|
||||
be relied on; os.Args[0] can be "faked".
|
||||
|
||||
Multi-platform and supports:
|
||||
* Linux
|
||||
* OS X
|
||||
* Windows
|
||||
* Plan 9
|
||||
* BSDs.
|
||||
33
vendor/github.com/kardianos/osext/osext.go
generated
vendored
Normal file
33
vendor/github.com/kardianos/osext/osext.go
generated
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Extensions to the standard "os" package.
|
||||
package osext // import "github.com/kardianos/osext"
|
||||
|
||||
import "path/filepath"
|
||||
|
||||
var cx, ce = executableClean()
|
||||
|
||||
func executableClean() (string, error) {
|
||||
p, err := executable()
|
||||
return filepath.Clean(p), err
|
||||
}
|
||||
|
||||
// Executable returns an absolute path that can be used to
|
||||
// re-invoke the current program.
|
||||
// It may not be valid after the current program exits.
|
||||
func Executable() (string, error) {
|
||||
return cx, ce
|
||||
}
|
||||
|
||||
// Returns same path as Executable, returns just the folder
|
||||
// path. Excludes the executable name and any trailing slash.
|
||||
func ExecutableFolder() (string, error) {
|
||||
p, err := Executable()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return filepath.Dir(p), nil
|
||||
}
|
||||
20
vendor/github.com/kardianos/osext/osext_plan9.go
generated
vendored
Normal file
20
vendor/github.com/kardianos/osext/osext_plan9.go
generated
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package osext
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strconv"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func executable() (string, error) {
|
||||
f, err := os.Open("/proc/" + strconv.Itoa(os.Getpid()) + "/text")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer f.Close()
|
||||
return syscall.Fd2path(int(f.Fd()))
|
||||
}
|
||||
36
vendor/github.com/kardianos/osext/osext_procfs.go
generated
vendored
Normal file
36
vendor/github.com/kardianos/osext/osext_procfs.go
generated
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build linux netbsd solaris dragonfly
|
||||
|
||||
package osext
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func executable() (string, error) {
|
||||
switch runtime.GOOS {
|
||||
case "linux":
|
||||
const deletedTag = " (deleted)"
|
||||
execpath, err := os.Readlink("/proc/self/exe")
|
||||
if err != nil {
|
||||
return execpath, err
|
||||
}
|
||||
execpath = strings.TrimSuffix(execpath, deletedTag)
|
||||
execpath = strings.TrimPrefix(execpath, deletedTag)
|
||||
return execpath, nil
|
||||
case "netbsd":
|
||||
return os.Readlink("/proc/curproc/exe")
|
||||
case "dragonfly":
|
||||
return os.Readlink("/proc/curproc/file")
|
||||
case "solaris":
|
||||
return os.Readlink(fmt.Sprintf("/proc/%d/path/a.out", os.Getpid()))
|
||||
}
|
||||
return "", errors.New("ExecPath not implemented for " + runtime.GOOS)
|
||||
}
|
||||
126
vendor/github.com/kardianos/osext/osext_sysctl.go
generated
vendored
Normal file
126
vendor/github.com/kardianos/osext/osext_sysctl.go
generated
vendored
Normal file
@@ -0,0 +1,126 @@
|
||||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build darwin freebsd openbsd
|
||||
|
||||
package osext
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var initCwd, initCwdErr = os.Getwd()
|
||||
|
||||
func executable() (string, error) {
|
||||
var mib [4]int32
|
||||
switch runtime.GOOS {
|
||||
case "freebsd":
|
||||
mib = [4]int32{1 /* CTL_KERN */, 14 /* KERN_PROC */, 12 /* KERN_PROC_PATHNAME */, -1}
|
||||
case "darwin":
|
||||
mib = [4]int32{1 /* CTL_KERN */, 38 /* KERN_PROCARGS */, int32(os.Getpid()), -1}
|
||||
case "openbsd":
|
||||
mib = [4]int32{1 /* CTL_KERN */, 55 /* KERN_PROC_ARGS */, int32(os.Getpid()), 1 /* KERN_PROC_ARGV */}
|
||||
}
|
||||
|
||||
n := uintptr(0)
|
||||
// Get length.
|
||||
_, _, errNum := syscall.Syscall6(syscall.SYS___SYSCTL, uintptr(unsafe.Pointer(&mib[0])), 4, 0, uintptr(unsafe.Pointer(&n)), 0, 0)
|
||||
if errNum != 0 {
|
||||
return "", errNum
|
||||
}
|
||||
if n == 0 { // This shouldn't happen.
|
||||
return "", nil
|
||||
}
|
||||
buf := make([]byte, n)
|
||||
_, _, errNum = syscall.Syscall6(syscall.SYS___SYSCTL, uintptr(unsafe.Pointer(&mib[0])), 4, uintptr(unsafe.Pointer(&buf[0])), uintptr(unsafe.Pointer(&n)), 0, 0)
|
||||
if errNum != 0 {
|
||||
return "", errNum
|
||||
}
|
||||
if n == 0 { // This shouldn't happen.
|
||||
return "", nil
|
||||
}
|
||||
|
||||
var execPath string
|
||||
switch runtime.GOOS {
|
||||
case "openbsd":
|
||||
// buf now contains **argv, with pointers to each of the C-style
|
||||
// NULL terminated arguments.
|
||||
var args []string
|
||||
argv := uintptr(unsafe.Pointer(&buf[0]))
|
||||
Loop:
|
||||
for {
|
||||
argp := *(**[1 << 20]byte)(unsafe.Pointer(argv))
|
||||
if argp == nil {
|
||||
break
|
||||
}
|
||||
for i := 0; uintptr(i) < n; i++ {
|
||||
// we don't want the full arguments list
|
||||
if string(argp[i]) == " " {
|
||||
break Loop
|
||||
}
|
||||
if argp[i] != 0 {
|
||||
continue
|
||||
}
|
||||
args = append(args, string(argp[:i]))
|
||||
n -= uintptr(i)
|
||||
break
|
||||
}
|
||||
if n < unsafe.Sizeof(argv) {
|
||||
break
|
||||
}
|
||||
argv += unsafe.Sizeof(argv)
|
||||
n -= unsafe.Sizeof(argv)
|
||||
}
|
||||
execPath = args[0]
|
||||
// There is no canonical way to get an executable path on
|
||||
// OpenBSD, so check PATH in case we are called directly
|
||||
if execPath[0] != '/' && execPath[0] != '.' {
|
||||
execIsInPath, err := exec.LookPath(execPath)
|
||||
if err == nil {
|
||||
execPath = execIsInPath
|
||||
}
|
||||
}
|
||||
default:
|
||||
for i, v := range buf {
|
||||
if v == 0 {
|
||||
buf = buf[:i]
|
||||
break
|
||||
}
|
||||
}
|
||||
execPath = string(buf)
|
||||
}
|
||||
|
||||
var err error
|
||||
// execPath will not be empty due to above checks.
|
||||
// Try to get the absolute path if the execPath is not rooted.
|
||||
if execPath[0] != '/' {
|
||||
execPath, err = getAbs(execPath)
|
||||
if err != nil {
|
||||
return execPath, err
|
||||
}
|
||||
}
|
||||
// For darwin KERN_PROCARGS may return the path to a symlink rather than the
|
||||
// actual executable.
|
||||
if runtime.GOOS == "darwin" {
|
||||
if execPath, err = filepath.EvalSymlinks(execPath); err != nil {
|
||||
return execPath, err
|
||||
}
|
||||
}
|
||||
return execPath, nil
|
||||
}
|
||||
|
||||
func getAbs(execPath string) (string, error) {
|
||||
if initCwdErr != nil {
|
||||
return execPath, initCwdErr
|
||||
}
|
||||
// The execPath may begin with a "../" or a "./" so clean it first.
|
||||
// Join the two paths, trailing and starting slashes undetermined, so use
|
||||
// the generic Join function.
|
||||
return filepath.Join(initCwd, filepath.Clean(execPath)), nil
|
||||
}
|
||||
203
vendor/github.com/kardianos/osext/osext_test.go
generated
vendored
Normal file
203
vendor/github.com/kardianos/osext/osext_test.go
generated
vendored
Normal file
@@ -0,0 +1,203 @@
|
||||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build darwin linux freebsd netbsd windows openbsd
|
||||
|
||||
package osext
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"testing"
|
||||
)
|
||||
|
||||
const (
|
||||
executableEnvVar = "OSTEST_OUTPUT_EXECUTABLE"
|
||||
|
||||
executableEnvValueMatch = "match"
|
||||
executableEnvValueDelete = "delete"
|
||||
)
|
||||
|
||||
func TestPrintExecutable(t *testing.T) {
|
||||
ef, err := Executable()
|
||||
if err != nil {
|
||||
t.Fatalf("Executable failed: %v", err)
|
||||
}
|
||||
t.Log("Executable:", ef)
|
||||
}
|
||||
func TestPrintExecutableFolder(t *testing.T) {
|
||||
ef, err := ExecutableFolder()
|
||||
if err != nil {
|
||||
t.Fatalf("ExecutableFolder failed: %v", err)
|
||||
}
|
||||
t.Log("Executable Folder:", ef)
|
||||
}
|
||||
func TestExecutableFolder(t *testing.T) {
|
||||
ef, err := ExecutableFolder()
|
||||
if err != nil {
|
||||
t.Fatalf("ExecutableFolder failed: %v", err)
|
||||
}
|
||||
if ef[len(ef)-1] == filepath.Separator {
|
||||
t.Fatal("ExecutableFolder ends with a trailing slash.")
|
||||
}
|
||||
}
|
||||
func TestExecutableMatch(t *testing.T) {
|
||||
ep, err := Executable()
|
||||
if err != nil {
|
||||
t.Fatalf("Executable failed: %v", err)
|
||||
}
|
||||
|
||||
// fullpath to be of the form "dir/prog".
|
||||
dir := filepath.Dir(filepath.Dir(ep))
|
||||
fullpath, err := filepath.Rel(dir, ep)
|
||||
if err != nil {
|
||||
t.Fatalf("filepath.Rel: %v", err)
|
||||
}
|
||||
// Make child start with a relative program path.
|
||||
// Alter argv[0] for child to verify getting real path without argv[0].
|
||||
cmd := &exec.Cmd{
|
||||
Dir: dir,
|
||||
Path: fullpath,
|
||||
Env: []string{fmt.Sprintf("%s=%s", executableEnvVar, executableEnvValueMatch)},
|
||||
}
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
t.Fatalf("exec(self) failed: %v", err)
|
||||
}
|
||||
outs := string(out)
|
||||
if !filepath.IsAbs(outs) {
|
||||
t.Fatalf("Child returned %q, want an absolute path", out)
|
||||
}
|
||||
if !sameFile(outs, ep) {
|
||||
t.Fatalf("Child returned %q, not the same file as %q", out, ep)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExecutableDelete(t *testing.T) {
|
||||
if runtime.GOOS != "linux" {
|
||||
t.Skip()
|
||||
}
|
||||
fpath, err := Executable()
|
||||
if err != nil {
|
||||
t.Fatalf("Executable failed: %v", err)
|
||||
}
|
||||
|
||||
r, w := io.Pipe()
|
||||
stderrBuff := &bytes.Buffer{}
|
||||
stdoutBuff := &bytes.Buffer{}
|
||||
cmd := &exec.Cmd{
|
||||
Path: fpath,
|
||||
Env: []string{fmt.Sprintf("%s=%s", executableEnvVar, executableEnvValueDelete)},
|
||||
Stdin: r,
|
||||
Stderr: stderrBuff,
|
||||
Stdout: stdoutBuff,
|
||||
}
|
||||
err = cmd.Start()
|
||||
if err != nil {
|
||||
t.Fatalf("exec(self) start failed: %v", err)
|
||||
}
|
||||
|
||||
tempPath := fpath + "_copy"
|
||||
_ = os.Remove(tempPath)
|
||||
|
||||
err = copyFile(tempPath, fpath)
|
||||
if err != nil {
|
||||
t.Fatalf("copy file failed: %v", err)
|
||||
}
|
||||
err = os.Remove(fpath)
|
||||
if err != nil {
|
||||
t.Fatalf("remove running test file failed: %v", err)
|
||||
}
|
||||
err = os.Rename(tempPath, fpath)
|
||||
if err != nil {
|
||||
t.Fatalf("rename copy to previous name failed: %v", err)
|
||||
}
|
||||
|
||||
w.Write([]byte{0})
|
||||
w.Close()
|
||||
|
||||
err = cmd.Wait()
|
||||
if err != nil {
|
||||
t.Fatalf("exec wait failed: %v", err)
|
||||
}
|
||||
|
||||
childPath := stderrBuff.String()
|
||||
if !filepath.IsAbs(childPath) {
|
||||
t.Fatalf("Child returned %q, want an absolute path", childPath)
|
||||
}
|
||||
if !sameFile(childPath, fpath) {
|
||||
t.Fatalf("Child returned %q, not the same file as %q", childPath, fpath)
|
||||
}
|
||||
}
|
||||
|
||||
func sameFile(fn1, fn2 string) bool {
|
||||
fi1, err := os.Stat(fn1)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
fi2, err := os.Stat(fn2)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return os.SameFile(fi1, fi2)
|
||||
}
|
||||
func copyFile(dest, src string) error {
|
||||
df, err := os.Create(dest)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer df.Close()
|
||||
|
||||
sf, err := os.Open(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer sf.Close()
|
||||
|
||||
_, err = io.Copy(df, sf)
|
||||
return err
|
||||
}
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
env := os.Getenv(executableEnvVar)
|
||||
switch env {
|
||||
case "":
|
||||
os.Exit(m.Run())
|
||||
case executableEnvValueMatch:
|
||||
// First chdir to another path.
|
||||
dir := "/"
|
||||
if runtime.GOOS == "windows" {
|
||||
dir = filepath.VolumeName(".")
|
||||
}
|
||||
os.Chdir(dir)
|
||||
if ep, err := Executable(); err != nil {
|
||||
fmt.Fprint(os.Stderr, "ERROR: ", err)
|
||||
} else {
|
||||
fmt.Fprint(os.Stderr, ep)
|
||||
}
|
||||
case executableEnvValueDelete:
|
||||
bb := make([]byte, 1)
|
||||
var err error
|
||||
n, err := os.Stdin.Read(bb)
|
||||
if err != nil {
|
||||
fmt.Fprint(os.Stderr, "ERROR: ", err)
|
||||
os.Exit(2)
|
||||
}
|
||||
if n != 1 {
|
||||
fmt.Fprint(os.Stderr, "ERROR: n != 1, n == ", n)
|
||||
os.Exit(2)
|
||||
}
|
||||
if ep, err := Executable(); err != nil {
|
||||
fmt.Fprint(os.Stderr, "ERROR: ", err)
|
||||
} else {
|
||||
fmt.Fprint(os.Stderr, ep)
|
||||
}
|
||||
}
|
||||
os.Exit(0)
|
||||
}
|
||||
34
vendor/github.com/kardianos/osext/osext_windows.go
generated
vendored
Normal file
34
vendor/github.com/kardianos/osext/osext_windows.go
generated
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package osext
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unicode/utf16"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var (
|
||||
kernel = syscall.MustLoadDLL("kernel32.dll")
|
||||
getModuleFileNameProc = kernel.MustFindProc("GetModuleFileNameW")
|
||||
)
|
||||
|
||||
// GetModuleFileName() with hModule = NULL
|
||||
func executable() (exePath string, err error) {
|
||||
return getModuleFileName()
|
||||
}
|
||||
|
||||
func getModuleFileName() (string, error) {
|
||||
var n uint32
|
||||
b := make([]uint16, syscall.MAX_PATH)
|
||||
size := uint32(len(b))
|
||||
|
||||
r0, _, e1 := getModuleFileNameProc.Call(0, uintptr(unsafe.Pointer(&b[0])), uintptr(size))
|
||||
n = uint32(r0)
|
||||
if n == 0 {
|
||||
return "", e1
|
||||
}
|
||||
return string(utf16.Decode(b[0:n])), nil
|
||||
}
|
||||
136
vendor/github.com/onsi/ginkgo/CHANGELOG.md
generated
vendored
Normal file
136
vendor/github.com/onsi/ginkgo/CHANGELOG.md
generated
vendored
Normal file
@@ -0,0 +1,136 @@
|
||||
## HEAD
|
||||
|
||||
Improvements:
|
||||
|
||||
- `Skip(message)` can be used to skip the current test.
|
||||
- Added `extensions/table` - a Ginkgo DSL for [Table Driven Tests](http://onsi.github.io/ginkgo/#table-driven-tests)
|
||||
|
||||
Bug Fixes:
|
||||
|
||||
- Ginkgo tests now fail when you `panic(nil)` (#167)
|
||||
|
||||
## 1.2.0 5/31/2015
|
||||
|
||||
Improvements
|
||||
|
||||
- `ginkgo -coverpkg` calls down to `go test -coverpkg` (#160)
|
||||
- `ginkgo -afterSuiteHook COMMAND` invokes the passed-in `COMMAND` after a test suite completes (#152)
|
||||
- Relaxed requirement for Go 1.4+. `ginkgo` now works with Go v1.3+ (#166)
|
||||
|
||||
## 1.2.0-beta
|
||||
|
||||
Ginkgo now requires Go 1.4+
|
||||
|
||||
Improvements:
|
||||
|
||||
- Call reporters in reverse order when announcing spec completion -- allows custom reporters to emit output before the default reporter does.
|
||||
- Improved focus behavior. Now, this:
|
||||
|
||||
```golang
|
||||
FDescribe("Some describe", func() {
|
||||
It("A", func() {})
|
||||
|
||||
FIt("B", func() {})
|
||||
})
|
||||
```
|
||||
|
||||
will run `B` but *not* `A`. This tends to be a common usage pattern when in the thick of writing and debugging tests.
|
||||
- When `SIGINT` is received, Ginkgo will emit the contents of the `GinkgoWriter` before running the `AfterSuite`. Useful for debugging stuck tests.
|
||||
- When `--progress` is set, Ginkgo will write test progress (in particular, Ginkgo will say when it is about to run a BeforeEach, AfterEach, It, etc...) to the `GinkgoWriter`. This is useful for debugging stuck tests and tests that generate many logs.
|
||||
- Improved output when an error occurs in a setup or teardown block.
|
||||
- When `--dryRun` is set, Ginkgo will walk the spec tree and emit to its reporter *without* actually running anything. Best paired with `-v` to understand which specs will run in which order.
|
||||
- Add `By` to help document long `It`s. `By` simply writes to the `GinkgoWriter`.
|
||||
- Add support for precompiled tests:
|
||||
- `ginkgo build <path-to-package>` will now compile the package, producing a file named `package.test`
|
||||
- The compiled `package.test` file can be run directly. This runs the tests in series.
|
||||
- To run precompiled tests in parallel, you can run: `ginkgo -p package.test`
|
||||
- Support `bootstrap`ping and `generate`ing [Agouti](http://agouti.org) specs.
|
||||
- `ginkgo generate` and `ginkgo bootstrap` now honor the package name already defined in a given directory
|
||||
- The `ginkgo` CLI ignores `SIGQUIT`. Prevents its stack dump from interlacing with the underlying test suite's stack dump.
|
||||
- The `ginkgo` CLI now compiles tests into a temporary directory instead of the package directory. This necessitates upgrading to Go v1.4+.
|
||||
- `ginkgo -notify` now works on Linux
|
||||
|
||||
Bug Fixes:
|
||||
|
||||
- If --skipPackages is used and all packages are skipped, Ginkgo should exit 0.
|
||||
- Fix tempfile leak when running in parallel
|
||||
- Fix incorrect failure message when a panic occurs during a parallel test run
|
||||
- Fixed an issue where a pending test within a focused context (or a focused test within a pending context) would skip all other tests.
|
||||
- Be more consistent about handling SIGTERM as well as SIGINT
|
||||
- When interupted while concurrently compiling test suites in the background, Ginkgo now cleans up the compiled artifacts.
|
||||
- Fixed a long standing bug where `ginkgo -p` would hang if a process spawned by one of the Ginkgo parallel nodes does not exit. (Hooray!)
|
||||
|
||||
## 1.1.0 (8/2/2014)
|
||||
|
||||
No changes, just dropping the beta.
|
||||
|
||||
## 1.1.0-beta (7/22/2014)
|
||||
New Features:
|
||||
|
||||
- `ginkgo watch` now monitors packages *and their dependencies* for changes. The depth of the dependency tree can be modified with the `-depth` flag.
|
||||
- Test suites with a programmatic focus (`FIt`, `FDescribe`, etc...) exit with non-zero status code, evne when they pass. This allows CI systems to detect accidental commits of focused test suites.
|
||||
- `ginkgo -p` runs the testsuite in parallel with an auto-detected number of nodes.
|
||||
- `ginkgo -tags=TAG_LIST` passes a list of tags down to the `go build` command.
|
||||
- `ginkgo --failFast` aborts the test suite after the first failure.
|
||||
- `ginkgo generate file_1 file_2` can take multiple file arguments.
|
||||
- Ginkgo now summarizes any spec failures that occured at the end of the test run.
|
||||
- `ginkgo --randomizeSuites` will run tests *suites* in random order using the generated/passed-in seed.
|
||||
|
||||
Improvements:
|
||||
|
||||
- `ginkgo -skipPackage` now takes a comma-separated list of strings. If the *relative path* to a package matches one of the entries in the comma-separated list, that package is skipped.
|
||||
- `ginkgo --untilItFails` no longer recompiles between attempts.
|
||||
- Ginkgo now panics when a runnable node (`It`, `BeforeEach`, `JustBeforeEach`, `AfterEach`, `Measure`) is nested within another runnable node. This is always a mistake. Any test suites that panic because of this change should be fixed.
|
||||
|
||||
Bug Fixes:
|
||||
|
||||
- `ginkgo boostrap` and `ginkgo generate` no longer fail when dealing with `hyphen-separated-packages`.
|
||||
- parallel specs are now better distributed across nodes - fixed a crashing bug where (for example) distributing 11 tests across 7 nodes would panic
|
||||
|
||||
## 1.0.0 (5/24/2014)
|
||||
New Features:
|
||||
|
||||
- Add `GinkgoParallelNode()` - shorthand for `config.GinkgoConfig.ParallelNode`
|
||||
|
||||
Improvements:
|
||||
|
||||
- When compilation fails, the compilation output is rewritten to present a correct *relative* path. Allows ⌘-clicking in iTerm open the file in your text editor.
|
||||
- `--untilItFails` and `ginkgo watch` now generate new random seeds between test runs, unless a particular random seed is specified.
|
||||
|
||||
Bug Fixes:
|
||||
|
||||
- `-cover` now generates a correctly combined coverprofile when running with in parallel with multiple `-node`s.
|
||||
- Print out the contents of the `GinkgoWriter` when `BeforeSuite` or `AfterSuite` fail.
|
||||
- Fix all remaining race conditions in Ginkgo's test suite.
|
||||
|
||||
## 1.0.0-beta (4/14/2014)
|
||||
Breaking changes:
|
||||
|
||||
- `thirdparty/gomocktestreporter` is gone. Use `GinkgoT()` instead
|
||||
- Modified the Reporter interface
|
||||
- `watch` is now a subcommand, not a flag.
|
||||
|
||||
DSL changes:
|
||||
|
||||
- `BeforeSuite` and `AfterSuite` for setting up and tearing down test suites.
|
||||
- `AfterSuite` is triggered on interrupt (`^C`) as well as exit.
|
||||
- `SynchronizedBeforeSuite` and `SynchronizedAfterSuite` for setting up and tearing down singleton resources across parallel nodes.
|
||||
|
||||
CLI changes:
|
||||
|
||||
- `watch` is now a subcommand, not a flag
|
||||
- `--nodot` flag can be passed to `ginkgo generate` and `ginkgo bootstrap` to avoid dot imports. This explicitly imports all exported identifiers in Ginkgo and Gomega. Refreshing this list can be done by running `ginkgo nodot`
|
||||
- Additional arguments can be passed to specs. Pass them after the `--` separator
|
||||
- `--skipPackage` flag takes a regexp and ignores any packages with package names passing said regexp.
|
||||
- `--trace` flag prints out full stack traces when errors occur, not just the line at which the error occurs.
|
||||
|
||||
Misc:
|
||||
|
||||
- Start using semantic versioning
|
||||
- Start maintaining changelog
|
||||
|
||||
Major refactor:
|
||||
|
||||
- Pull out Ginkgo's internal to `internal`
|
||||
- Rename `example` everywhere to `spec`
|
||||
- Much more!
|
||||
20
vendor/github.com/onsi/ginkgo/LICENSE
generated
vendored
Normal file
20
vendor/github.com/onsi/ginkgo/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
Copyright (c) 2013-2014 Onsi Fakhouri
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
115
vendor/github.com/onsi/ginkgo/README.md
generated
vendored
Normal file
115
vendor/github.com/onsi/ginkgo/README.md
generated
vendored
Normal file
@@ -0,0 +1,115 @@
|
||||

|
||||
|
||||
[](https://travis-ci.org/onsi/ginkgo)
|
||||
|
||||
Jump to the [docs](http://onsi.github.io/ginkgo/) to learn more. To start rolling your Ginkgo tests *now* [keep reading](#set-me-up)!
|
||||
|
||||
To discuss Ginkgo and get updates, join the [google group](https://groups.google.com/d/forum/ginkgo-and-gomega).
|
||||
|
||||
## Feature List
|
||||
|
||||
- Ginkgo uses Go's `testing` package and can live alongside your existing `testing` tests. It's easy to [bootstrap](http://onsi.github.io/ginkgo/#bootstrapping-a-suite) and start writing your [first tests](http://onsi.github.io/ginkgo/#adding-specs-to-a-suite)
|
||||
|
||||
- Structure your BDD-style tests expressively:
|
||||
- Nestable [`Describe` and `Context` container blocks](http://onsi.github.io/ginkgo/#organizing-specs-with-containers-describe-and-context)
|
||||
- [`BeforeEach` and `AfterEach` blocks](http://onsi.github.io/ginkgo/#extracting-common-setup-beforeeach) for setup and teardown
|
||||
- [`It` blocks](http://onsi.github.io/ginkgo/#individual-specs-) that hold your assertions
|
||||
- [`JustBeforeEach` blocks](http://onsi.github.io/ginkgo/#separating-creation-and-configuration-justbeforeeach) that separate creation from configuration (also known as the subject action pattern).
|
||||
- [`BeforeSuite` and `AfterSuite` blocks](http://onsi.github.io/ginkgo/#global-setup-and-teardown-beforesuite-and-aftersuite) to prep for and cleanup after a suite.
|
||||
|
||||
- A comprehensive test runner that lets you:
|
||||
- Mark specs as [pending](http://onsi.github.io/ginkgo/#pending-specs)
|
||||
- [Focus](http://onsi.github.io/ginkgo/#focused-specs) individual specs, and groups of specs, either programmatically or on the command line
|
||||
- Run your tests in [random order](http://onsi.github.io/ginkgo/#spec-permutation), and then reuse random seeds to replicate the same order.
|
||||
- Break up your test suite into parallel processes for straightforward [test parallelization](http://onsi.github.io/ginkgo/#parallel-specs)
|
||||
|
||||
- `ginkgo`: a command line interface with plenty of handy command line arguments for [running your tests](http://onsi.github.io/ginkgo/#running-tests) and [generating](http://onsi.github.io/ginkgo/#generators) test files. Here are a few choice examples:
|
||||
- `ginkgo -nodes=N` runs your tests in `N` parallel processes and print out coherent output in realtime
|
||||
- `ginkgo -cover` runs your tests using Golang's code coverage tool
|
||||
- `ginkgo convert` converts an XUnit-style `testing` package to a Ginkgo-style package
|
||||
- `ginkgo -focus="REGEXP"` and `ginkgo -skip="REGEXP"` allow you to specify a subset of tests to run via regular expression
|
||||
- `ginkgo -r` runs all tests suites under the current directory
|
||||
- `ginkgo -v` prints out identifying information for each tests just before it runs
|
||||
|
||||
And much more: run `ginkgo help` for details!
|
||||
|
||||
The `ginkgo` CLI is convenient, but purely optional -- Ginkgo works just fine with `go test`
|
||||
|
||||
- `ginkgo watch` [watches](https://onsi.github.io/ginkgo/#watching-for-changes) packages *and their dependencies* for changes, then reruns tests. Run tests immediately as you develop!
|
||||
|
||||
- Built-in support for testing [asynchronicity](http://onsi.github.io/ginkgo/#asynchronous-tests)
|
||||
|
||||
- Built-in support for [benchmarking](http://onsi.github.io/ginkgo/#benchmark-tests) your code. Control the number of benchmark samples as you gather runtimes and other, arbitrary, bits of numerical information about your code.
|
||||
|
||||
- [Completions for Sublime Text](https://github.com/onsi/ginkgo-sublime-completions): just use [Package Control](https://sublime.wbond.net/) to install `Ginkgo Completions`.
|
||||
|
||||
- Straightforward support for third-party testing libraries such as [Gomock](https://code.google.com/p/gomock/) and [Testify](https://github.com/stretchr/testify). Check out the [docs](http://onsi.github.io/ginkgo/#third-party-integrations) for details.
|
||||
|
||||
- A modular architecture that lets you easily:
|
||||
- Write [custom reporters](http://onsi.github.io/ginkgo/#writing-custom-reporters) (for example, Ginkgo comes with a [JUnit XML reporter](http://onsi.github.io/ginkgo/#generating-junit-xml-output) and a TeamCity reporter).
|
||||
- [Adapt an existing matcher library (or write your own!)](http://onsi.github.io/ginkgo/#using-other-matcher-libraries) to work with Ginkgo
|
||||
|
||||
## [Gomega](http://github.com/onsi/gomega): Ginkgo's Preferred Matcher Library
|
||||
|
||||
Ginkgo is best paired with Gomega. Learn more about Gomega [here](http://onsi.github.io/gomega/)
|
||||
|
||||
## [Agouti](http://github.com/sclevine/agouti): A Golang Acceptance Testing Framework
|
||||
|
||||
Agouti allows you run WebDriver integration tests. Learn more about Agouti [here](http://agouti.org)
|
||||
|
||||
## Set Me Up!
|
||||
|
||||
You'll need Golang v1.3+ (Ubuntu users: you probably have Golang v1.0 -- you'll need to upgrade!)
|
||||
|
||||
```bash
|
||||
|
||||
go get github.com/onsi/ginkgo/ginkgo # installs the ginkgo CLI
|
||||
go get github.com/onsi/gomega # fetches the matcher library
|
||||
|
||||
cd path/to/package/you/want/to/test
|
||||
|
||||
ginkgo bootstrap # set up a new ginkgo suite
|
||||
ginkgo generate # will create a sample test file. edit this file and add your tests then...
|
||||
|
||||
go test # to run your tests
|
||||
|
||||
ginkgo # also runs your tests
|
||||
|
||||
```
|
||||
|
||||
## I'm new to Go: What are my testing options?
|
||||
|
||||
Of course, I heartily recommend [Ginkgo](https://github.com/onsi/ginkgo) and [Gomega](https://github.com/onsi/gomega). Both packages are seeing heavy, daily, production use on a number of projects and boast a mature and comprehensive feature-set.
|
||||
|
||||
With that said, it's great to know what your options are :)
|
||||
|
||||
### What Golang gives you out of the box
|
||||
|
||||
Testing is a first class citizen in Golang, however Go's built-in testing primitives are somewhat limited: The [testing](http://golang.org/pkg/testing) package provides basic XUnit style tests and no assertion library.
|
||||
|
||||
### Matcher libraries for Golang's XUnit style tests
|
||||
|
||||
A number of matcher libraries have been written to augment Go's built-in XUnit style tests. Here are two that have gained traction:
|
||||
|
||||
- [testify](https://github.com/stretchr/testify)
|
||||
- [gocheck](http://labix.org/gocheck)
|
||||
|
||||
You can also use Ginkgo's matcher library [Gomega](https://github.com/onsi/gomega) in [XUnit style tests](http://onsi.github.io/gomega/#using-gomega-with-golangs-xunitstyle-tests)
|
||||
|
||||
### BDD style testing frameworks
|
||||
|
||||
There are a handful of BDD-style testing frameworks written for Golang. Here are a few:
|
||||
|
||||
- [Ginkgo](https://github.com/onsi/ginkgo) ;)
|
||||
- [GoConvey](https://github.com/smartystreets/goconvey)
|
||||
- [Goblin](https://github.com/franela/goblin)
|
||||
- [Mao](https://github.com/azer/mao)
|
||||
- [Zen](https://github.com/pranavraja/zen)
|
||||
|
||||
Finally, @shageman has [put together](https://github.com/shageman/gotestit) a comprehensive comparison of golang testing libraries.
|
||||
|
||||
Go explore!
|
||||
|
||||
## License
|
||||
|
||||
Ginkgo is MIT-Licensed
|
||||
170
vendor/github.com/onsi/ginkgo/config/config.go
generated
vendored
Normal file
170
vendor/github.com/onsi/ginkgo/config/config.go
generated
vendored
Normal file
@@ -0,0 +1,170 @@
|
||||
/*
|
||||
Ginkgo accepts a number of configuration options.
|
||||
|
||||
These are documented [here](http://onsi.github.io/ginkgo/#the_ginkgo_cli)
|
||||
|
||||
You can also learn more via
|
||||
|
||||
ginkgo help
|
||||
|
||||
or (I kid you not):
|
||||
|
||||
go test -asdf
|
||||
*/
|
||||
package config
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"time"
|
||||
|
||||
"fmt"
|
||||
)
|
||||
|
||||
const VERSION = "1.2.0"
|
||||
|
||||
type GinkgoConfigType struct {
|
||||
RandomSeed int64
|
||||
RandomizeAllSpecs bool
|
||||
FocusString string
|
||||
SkipString string
|
||||
SkipMeasurements bool
|
||||
FailOnPending bool
|
||||
FailFast bool
|
||||
EmitSpecProgress bool
|
||||
DryRun bool
|
||||
|
||||
ParallelNode int
|
||||
ParallelTotal int
|
||||
SyncHost string
|
||||
StreamHost string
|
||||
}
|
||||
|
||||
var GinkgoConfig = GinkgoConfigType{}
|
||||
|
||||
type DefaultReporterConfigType struct {
|
||||
NoColor bool
|
||||
SlowSpecThreshold float64
|
||||
NoisyPendings bool
|
||||
Succinct bool
|
||||
Verbose bool
|
||||
FullTrace bool
|
||||
}
|
||||
|
||||
var DefaultReporterConfig = DefaultReporterConfigType{}
|
||||
|
||||
func processPrefix(prefix string) string {
|
||||
if prefix != "" {
|
||||
prefix = prefix + "."
|
||||
}
|
||||
return prefix
|
||||
}
|
||||
|
||||
func Flags(flagSet *flag.FlagSet, prefix string, includeParallelFlags bool) {
|
||||
prefix = processPrefix(prefix)
|
||||
flagSet.Int64Var(&(GinkgoConfig.RandomSeed), prefix+"seed", time.Now().Unix(), "The seed used to randomize the spec suite.")
|
||||
flagSet.BoolVar(&(GinkgoConfig.RandomizeAllSpecs), prefix+"randomizeAllSpecs", false, "If set, ginkgo will randomize all specs together. By default, ginkgo only randomizes the top level Describe/Context groups.")
|
||||
flagSet.BoolVar(&(GinkgoConfig.SkipMeasurements), prefix+"skipMeasurements", false, "If set, ginkgo will skip any measurement specs.")
|
||||
flagSet.BoolVar(&(GinkgoConfig.FailOnPending), prefix+"failOnPending", false, "If set, ginkgo will mark the test suite as failed if any specs are pending.")
|
||||
flagSet.BoolVar(&(GinkgoConfig.FailFast), prefix+"failFast", false, "If set, ginkgo will stop running a test suite after a failure occurs.")
|
||||
flagSet.BoolVar(&(GinkgoConfig.DryRun), prefix+"dryRun", false, "If set, ginkgo will walk the test hierarchy without actually running anything. Best paired with -v.")
|
||||
flagSet.StringVar(&(GinkgoConfig.FocusString), prefix+"focus", "", "If set, ginkgo will only run specs that match this regular expression.")
|
||||
flagSet.StringVar(&(GinkgoConfig.SkipString), prefix+"skip", "", "If set, ginkgo will only run specs that do not match this regular expression.")
|
||||
flagSet.BoolVar(&(GinkgoConfig.EmitSpecProgress), prefix+"progress", false, "If set, ginkgo will emit progress information as each spec runs to the GinkgoWriter.")
|
||||
|
||||
if includeParallelFlags {
|
||||
flagSet.IntVar(&(GinkgoConfig.ParallelNode), prefix+"parallel.node", 1, "This worker node's (one-indexed) node number. For running specs in parallel.")
|
||||
flagSet.IntVar(&(GinkgoConfig.ParallelTotal), prefix+"parallel.total", 1, "The total number of worker nodes. For running specs in parallel.")
|
||||
flagSet.StringVar(&(GinkgoConfig.SyncHost), prefix+"parallel.synchost", "", "The address for the server that will synchronize the running nodes.")
|
||||
flagSet.StringVar(&(GinkgoConfig.StreamHost), prefix+"parallel.streamhost", "", "The address for the server that the running nodes should stream data to.")
|
||||
}
|
||||
|
||||
flagSet.BoolVar(&(DefaultReporterConfig.NoColor), prefix+"noColor", false, "If set, suppress color output in default reporter.")
|
||||
flagSet.Float64Var(&(DefaultReporterConfig.SlowSpecThreshold), prefix+"slowSpecThreshold", 5.0, "(in seconds) Specs that take longer to run than this threshold are flagged as slow by the default reporter.")
|
||||
flagSet.BoolVar(&(DefaultReporterConfig.NoisyPendings), prefix+"noisyPendings", true, "If set, default reporter will shout about pending tests.")
|
||||
flagSet.BoolVar(&(DefaultReporterConfig.Verbose), prefix+"v", false, "If set, default reporter print out all specs as they begin.")
|
||||
flagSet.BoolVar(&(DefaultReporterConfig.Succinct), prefix+"succinct", false, "If set, default reporter prints out a very succinct report")
|
||||
flagSet.BoolVar(&(DefaultReporterConfig.FullTrace), prefix+"trace", false, "If set, default reporter prints out the full stack trace when a failure occurs")
|
||||
}
|
||||
|
||||
func BuildFlagArgs(prefix string, ginkgo GinkgoConfigType, reporter DefaultReporterConfigType) []string {
|
||||
prefix = processPrefix(prefix)
|
||||
result := make([]string, 0)
|
||||
|
||||
if ginkgo.RandomSeed > 0 {
|
||||
result = append(result, fmt.Sprintf("--%sseed=%d", prefix, ginkgo.RandomSeed))
|
||||
}
|
||||
|
||||
if ginkgo.RandomizeAllSpecs {
|
||||
result = append(result, fmt.Sprintf("--%srandomizeAllSpecs", prefix))
|
||||
}
|
||||
|
||||
if ginkgo.SkipMeasurements {
|
||||
result = append(result, fmt.Sprintf("--%sskipMeasurements", prefix))
|
||||
}
|
||||
|
||||
if ginkgo.FailOnPending {
|
||||
result = append(result, fmt.Sprintf("--%sfailOnPending", prefix))
|
||||
}
|
||||
|
||||
if ginkgo.FailFast {
|
||||
result = append(result, fmt.Sprintf("--%sfailFast", prefix))
|
||||
}
|
||||
|
||||
if ginkgo.DryRun {
|
||||
result = append(result, fmt.Sprintf("--%sdryRun", prefix))
|
||||
}
|
||||
|
||||
if ginkgo.FocusString != "" {
|
||||
result = append(result, fmt.Sprintf("--%sfocus=%s", prefix, ginkgo.FocusString))
|
||||
}
|
||||
|
||||
if ginkgo.SkipString != "" {
|
||||
result = append(result, fmt.Sprintf("--%sskip=%s", prefix, ginkgo.SkipString))
|
||||
}
|
||||
|
||||
if ginkgo.EmitSpecProgress {
|
||||
result = append(result, fmt.Sprintf("--%sprogress", prefix))
|
||||
}
|
||||
|
||||
if ginkgo.ParallelNode != 0 {
|
||||
result = append(result, fmt.Sprintf("--%sparallel.node=%d", prefix, ginkgo.ParallelNode))
|
||||
}
|
||||
|
||||
if ginkgo.ParallelTotal != 0 {
|
||||
result = append(result, fmt.Sprintf("--%sparallel.total=%d", prefix, ginkgo.ParallelTotal))
|
||||
}
|
||||
|
||||
if ginkgo.StreamHost != "" {
|
||||
result = append(result, fmt.Sprintf("--%sparallel.streamhost=%s", prefix, ginkgo.StreamHost))
|
||||
}
|
||||
|
||||
if ginkgo.SyncHost != "" {
|
||||
result = append(result, fmt.Sprintf("--%sparallel.synchost=%s", prefix, ginkgo.SyncHost))
|
||||
}
|
||||
|
||||
if reporter.NoColor {
|
||||
result = append(result, fmt.Sprintf("--%snoColor", prefix))
|
||||
}
|
||||
|
||||
if reporter.SlowSpecThreshold > 0 {
|
||||
result = append(result, fmt.Sprintf("--%sslowSpecThreshold=%.5f", prefix, reporter.SlowSpecThreshold))
|
||||
}
|
||||
|
||||
if !reporter.NoisyPendings {
|
||||
result = append(result, fmt.Sprintf("--%snoisyPendings=false", prefix))
|
||||
}
|
||||
|
||||
if reporter.Verbose {
|
||||
result = append(result, fmt.Sprintf("--%sv", prefix))
|
||||
}
|
||||
|
||||
if reporter.Succinct {
|
||||
result = append(result, fmt.Sprintf("--%ssuccinct", prefix))
|
||||
}
|
||||
|
||||
if reporter.FullTrace {
|
||||
result = append(result, fmt.Sprintf("--%strace", prefix))
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
98
vendor/github.com/onsi/ginkgo/extensions/table/table.go
generated
vendored
Normal file
98
vendor/github.com/onsi/ginkgo/extensions/table/table.go
generated
vendored
Normal file
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
|
||||
Table provides a simple DSL for Ginkgo-native Table-Driven Tests
|
||||
|
||||
The godoc documentation describes Table's API. More comprehensive documentation (with examples!) is available at http://onsi.github.io/ginkgo#table-driven-tests
|
||||
|
||||
*/
|
||||
|
||||
package table
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"github.com/onsi/ginkgo"
|
||||
)
|
||||
|
||||
/*
|
||||
DescribeTable describes a table-driven test.
|
||||
|
||||
For example:
|
||||
|
||||
DescribeTable("a simple table",
|
||||
func(x int, y int, expected bool) {
|
||||
Ω(x > y).Should(Equal(expected))
|
||||
},
|
||||
Entry("x > y", 1, 0, true),
|
||||
Entry("x == y", 0, 0, false),
|
||||
Entry("x < y", 0, 1, false),
|
||||
)
|
||||
|
||||
The first argument to `DescribeTable` is a string description.
|
||||
The second argument is a function that will be run for each table entry. Your assertions go here - the function is equivalent to a Ginkgo It.
|
||||
The subsequent arguments must be of type `TableEntry`. We recommend using the `Entry` convenience constructors.
|
||||
|
||||
The `Entry` constructor takes a string description followed by an arbitrary set of parameters. These parameters are passed into your function.
|
||||
|
||||
Under the hood, `DescribeTable` simply generates a new Ginkgo `Describe`. Each `Entry` is turned into an `It` within the `Describe`.
|
||||
|
||||
It's important to understand that the `Describe`s and `It`s are generated at evaluation time (i.e. when Ginkgo constructs the tree of tests and before the tests run).
|
||||
|
||||
Individual Entries can be focused (with FEntry) or marked pending (with PEntry or XEntry). In addition, the entire table can be focused or marked pending with FDescribeTable and PDescribeTable/XDescribeTable.
|
||||
*/
|
||||
func DescribeTable(description string, itBody interface{}, entries ...TableEntry) bool {
|
||||
describeTable(description, itBody, entries, false, false)
|
||||
return true
|
||||
}
|
||||
|
||||
/*
|
||||
You can focus a table with `FDescribeTable`. This is equivalent to `FDescribe`.
|
||||
*/
|
||||
func FDescribeTable(description string, itBody interface{}, entries ...TableEntry) bool {
|
||||
describeTable(description, itBody, entries, false, true)
|
||||
return true
|
||||
}
|
||||
|
||||
/*
|
||||
You can mark a table as pending with `PDescribeTable`. This is equivalent to `PDescribe`.
|
||||
*/
|
||||
func PDescribeTable(description string, itBody interface{}, entries ...TableEntry) bool {
|
||||
describeTable(description, itBody, entries, true, false)
|
||||
return true
|
||||
}
|
||||
|
||||
/*
|
||||
You can mark a table as pending with `XDescribeTable`. This is equivalent to `XDescribe`.
|
||||
*/
|
||||
func XDescribeTable(description string, itBody interface{}, entries ...TableEntry) bool {
|
||||
describeTable(description, itBody, entries, true, false)
|
||||
return true
|
||||
}
|
||||
|
||||
func describeTable(description string, itBody interface{}, entries []TableEntry, pending bool, focused bool) {
|
||||
itBodyValue := reflect.ValueOf(itBody)
|
||||
if itBodyValue.Kind() != reflect.Func {
|
||||
panic(fmt.Sprintf("DescribeTable expects a function, got %#v", itBody))
|
||||
}
|
||||
|
||||
if pending {
|
||||
ginkgo.PDescribe(description, func() {
|
||||
for _, entry := range entries {
|
||||
entry.generateIt(itBodyValue)
|
||||
}
|
||||
})
|
||||
} else if focused {
|
||||
ginkgo.FDescribe(description, func() {
|
||||
for _, entry := range entries {
|
||||
entry.generateIt(itBodyValue)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
ginkgo.Describe(description, func() {
|
||||
for _, entry := range entries {
|
||||
entry.generateIt(itBodyValue)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
81
vendor/github.com/onsi/ginkgo/extensions/table/table_entry.go
generated
vendored
Normal file
81
vendor/github.com/onsi/ginkgo/extensions/table/table_entry.go
generated
vendored
Normal file
@@ -0,0 +1,81 @@
|
||||
package table
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"github.com/onsi/ginkgo"
|
||||
)
|
||||
|
||||
/*
|
||||
TableEntry represents an entry in a table test. You generally use the `Entry` constructor.
|
||||
*/
|
||||
type TableEntry struct {
|
||||
Description string
|
||||
Parameters []interface{}
|
||||
Pending bool
|
||||
Focused bool
|
||||
}
|
||||
|
||||
func (t TableEntry) generateIt(itBody reflect.Value) {
|
||||
if t.Pending {
|
||||
ginkgo.PIt(t.Description)
|
||||
return
|
||||
}
|
||||
|
||||
values := []reflect.Value{}
|
||||
for i, param := range t.Parameters {
|
||||
var value reflect.Value
|
||||
|
||||
if param == nil {
|
||||
inType := itBody.Type().In(i)
|
||||
value = reflect.Zero(inType)
|
||||
} else {
|
||||
value = reflect.ValueOf(param)
|
||||
}
|
||||
|
||||
values = append(values, value)
|
||||
}
|
||||
|
||||
body := func() {
|
||||
itBody.Call(values)
|
||||
}
|
||||
|
||||
if t.Focused {
|
||||
ginkgo.FIt(t.Description, body)
|
||||
} else {
|
||||
ginkgo.It(t.Description, body)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Entry constructs a TableEntry.
|
||||
|
||||
The first argument is a required description (this becomes the content of the generated Ginkgo `It`).
|
||||
Subsequent parameters are saved off and sent to the callback passed in to `DescribeTable`.
|
||||
|
||||
Each Entry ends up generating an individual Ginkgo It.
|
||||
*/
|
||||
func Entry(description string, parameters ...interface{}) TableEntry {
|
||||
return TableEntry{description, parameters, false, false}
|
||||
}
|
||||
|
||||
/*
|
||||
You can focus a particular entry with FEntry. This is equivalent to FIt.
|
||||
*/
|
||||
func FEntry(description string, parameters ...interface{}) TableEntry {
|
||||
return TableEntry{description, parameters, false, true}
|
||||
}
|
||||
|
||||
/*
|
||||
You can mark a particular entry as pending with PEntry. This is equivalent to PIt.
|
||||
*/
|
||||
func PEntry(description string, parameters ...interface{}) TableEntry {
|
||||
return TableEntry{description, parameters, true, false}
|
||||
}
|
||||
|
||||
/*
|
||||
You can mark a particular entry as pending with XEntry. This is equivalent to XIt.
|
||||
*/
|
||||
func XEntry(description string, parameters ...interface{}) TableEntry {
|
||||
return TableEntry{description, parameters, true, false}
|
||||
}
|
||||
13
vendor/github.com/onsi/ginkgo/extensions/table/table_suite_test.go
generated
vendored
Normal file
13
vendor/github.com/onsi/ginkgo/extensions/table/table_suite_test.go
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
package table_test
|
||||
|
||||
import (
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestTable(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecs(t, "Table Suite")
|
||||
}
|
||||
64
vendor/github.com/onsi/ginkgo/extensions/table/table_test.go
generated
vendored
Normal file
64
vendor/github.com/onsi/ginkgo/extensions/table/table_test.go
generated
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
package table_test
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
. "github.com/onsi/ginkgo/extensions/table"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
var _ = Describe("Table", func() {
|
||||
DescribeTable("a simple table",
|
||||
func(x int, y int, expected bool) {
|
||||
Ω(x > y).Should(Equal(expected))
|
||||
},
|
||||
Entry("x > y", 1, 0, true),
|
||||
Entry("x == y", 0, 0, false),
|
||||
Entry("x < y", 0, 1, false),
|
||||
)
|
||||
|
||||
type ComplicatedThings struct {
|
||||
Superstructure string
|
||||
Substructure string
|
||||
Count int
|
||||
}
|
||||
|
||||
DescribeTable("a more complicated table",
|
||||
func(c ComplicatedThings) {
|
||||
Ω(strings.Count(c.Superstructure, c.Substructure)).Should(BeNumerically("==", c.Count))
|
||||
},
|
||||
Entry("with no matching substructures", ComplicatedThings{
|
||||
Superstructure: "the sixth sheikh's sixth sheep's sick",
|
||||
Substructure: "emir",
|
||||
Count: 0,
|
||||
}),
|
||||
Entry("with one matching substructure", ComplicatedThings{
|
||||
Superstructure: "the sixth sheikh's sixth sheep's sick",
|
||||
Substructure: "sheep",
|
||||
Count: 1,
|
||||
}),
|
||||
Entry("with many matching substructures", ComplicatedThings{
|
||||
Superstructure: "the sixth sheikh's sixth sheep's sick",
|
||||
Substructure: "si",
|
||||
Count: 3,
|
||||
}),
|
||||
)
|
||||
|
||||
PDescribeTable("a failure",
|
||||
func(value bool) {
|
||||
Ω(value).Should(BeFalse())
|
||||
},
|
||||
Entry("when true", true),
|
||||
Entry("when false", false),
|
||||
Entry("when malformed", 2),
|
||||
)
|
||||
|
||||
DescribeTable("an untyped nil as an entry",
|
||||
func(x interface{}) {
|
||||
Expect(x).To(BeNil())
|
||||
},
|
||||
Entry("nil", nil),
|
||||
)
|
||||
})
|
||||
182
vendor/github.com/onsi/ginkgo/ginkgo/bootstrap_command.go
generated
vendored
Normal file
182
vendor/github.com/onsi/ginkgo/ginkgo/bootstrap_command.go
generated
vendored
Normal file
@@ -0,0 +1,182 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"go/build"
|
||||
|
||||
"github.com/onsi/ginkgo/ginkgo/nodot"
|
||||
)
|
||||
|
||||
func BuildBootstrapCommand() *Command {
|
||||
var agouti, noDot bool
|
||||
flagSet := flag.NewFlagSet("bootstrap", flag.ExitOnError)
|
||||
flagSet.BoolVar(&agouti, "agouti", false, "If set, bootstrap will generate a bootstrap file for writing Agouti tests")
|
||||
flagSet.BoolVar(&noDot, "nodot", false, "If set, bootstrap will generate a bootstrap file that does not . import ginkgo and gomega")
|
||||
|
||||
return &Command{
|
||||
Name: "bootstrap",
|
||||
FlagSet: flagSet,
|
||||
UsageCommand: "ginkgo bootstrap <FLAGS>",
|
||||
Usage: []string{
|
||||
"Bootstrap a test suite for the current package",
|
||||
"Accepts the following flags:",
|
||||
},
|
||||
Command: func(args []string, additionalArgs []string) {
|
||||
generateBootstrap(agouti, noDot)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
var bootstrapText = `package {{.Package}}_test
|
||||
|
||||
import (
|
||||
{{.GinkgoImport}}
|
||||
{{.GomegaImport}}
|
||||
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test{{.FormattedName}}(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecs(t, "{{.FormattedName}} Suite")
|
||||
}
|
||||
`
|
||||
|
||||
var agoutiBootstrapText = `package {{.Package}}_test
|
||||
|
||||
import (
|
||||
{{.GinkgoImport}}
|
||||
{{.GomegaImport}}
|
||||
"github.com/sclevine/agouti"
|
||||
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test{{.FormattedName}}(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecs(t, "{{.FormattedName}} Suite")
|
||||
}
|
||||
|
||||
var agoutiDriver *agouti.WebDriver
|
||||
|
||||
var _ = BeforeSuite(func() {
|
||||
// Choose a WebDriver:
|
||||
|
||||
agoutiDriver = agouti.PhantomJS()
|
||||
// agoutiDriver = agouti.Selenium()
|
||||
// agoutiDriver = agouti.ChromeDriver()
|
||||
|
||||
Expect(agoutiDriver.Start()).To(Succeed())
|
||||
})
|
||||
|
||||
var _ = AfterSuite(func() {
|
||||
Expect(agoutiDriver.Stop()).To(Succeed())
|
||||
})
|
||||
`
|
||||
|
||||
type bootstrapData struct {
|
||||
Package string
|
||||
FormattedName string
|
||||
GinkgoImport string
|
||||
GomegaImport string
|
||||
}
|
||||
|
||||
func getPackageAndFormattedName() (string, string, string) {
|
||||
path, err := os.Getwd()
|
||||
if err != nil {
|
||||
complainAndQuit("Could not get current working directory: \n" + err.Error())
|
||||
}
|
||||
|
||||
dirName := strings.Replace(filepath.Base(path), "-", "_", -1)
|
||||
dirName = strings.Replace(dirName, " ", "_", -1)
|
||||
|
||||
pkg, err := build.ImportDir(path, 0)
|
||||
packageName := pkg.Name
|
||||
if err != nil {
|
||||
packageName = dirName
|
||||
}
|
||||
|
||||
formattedName := prettifyPackageName(filepath.Base(path))
|
||||
return packageName, dirName, formattedName
|
||||
}
|
||||
|
||||
func prettifyPackageName(name string) string {
|
||||
name = strings.Replace(name, "-", " ", -1)
|
||||
name = strings.Replace(name, "_", " ", -1)
|
||||
name = strings.Title(name)
|
||||
name = strings.Replace(name, " ", "", -1)
|
||||
return name
|
||||
}
|
||||
|
||||
func fileExists(path string) bool {
|
||||
_, err := os.Stat(path)
|
||||
if err == nil {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func generateBootstrap(agouti bool, noDot bool) {
|
||||
packageName, bootstrapFilePrefix, formattedName := getPackageAndFormattedName()
|
||||
data := bootstrapData{
|
||||
Package: packageName,
|
||||
FormattedName: formattedName,
|
||||
GinkgoImport: `. "github.com/onsi/ginkgo"`,
|
||||
GomegaImport: `. "github.com/onsi/gomega"`,
|
||||
}
|
||||
|
||||
if noDot {
|
||||
data.GinkgoImport = `"github.com/onsi/ginkgo"`
|
||||
data.GomegaImport = `"github.com/onsi/gomega"`
|
||||
}
|
||||
|
||||
targetFile := fmt.Sprintf("%s_suite_test.go", bootstrapFilePrefix)
|
||||
if fileExists(targetFile) {
|
||||
fmt.Printf("%s already exists.\n\n", targetFile)
|
||||
os.Exit(1)
|
||||
} else {
|
||||
fmt.Printf("Generating ginkgo test suite bootstrap for %s in:\n\t%s\n", packageName, targetFile)
|
||||
}
|
||||
|
||||
f, err := os.Create(targetFile)
|
||||
if err != nil {
|
||||
complainAndQuit("Could not create file: " + err.Error())
|
||||
panic(err.Error())
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
var templateText string
|
||||
if agouti {
|
||||
templateText = agoutiBootstrapText
|
||||
} else {
|
||||
templateText = bootstrapText
|
||||
}
|
||||
|
||||
bootstrapTemplate, err := template.New("bootstrap").Parse(templateText)
|
||||
if err != nil {
|
||||
panic(err.Error())
|
||||
}
|
||||
|
||||
buf := &bytes.Buffer{}
|
||||
bootstrapTemplate.Execute(buf, data)
|
||||
|
||||
if noDot {
|
||||
contents, err := nodot.ApplyNoDot(buf.Bytes())
|
||||
if err != nil {
|
||||
complainAndQuit("Failed to import nodot declarations: " + err.Error())
|
||||
}
|
||||
fmt.Println("To update the nodot declarations in the future, switch to this directory and run:\n\tginkgo nodot")
|
||||
buf = bytes.NewBuffer(contents)
|
||||
}
|
||||
|
||||
buf.WriteTo(f)
|
||||
|
||||
goFmt(targetFile)
|
||||
}
|
||||
68
vendor/github.com/onsi/ginkgo/ginkgo/build_command.go
generated
vendored
Normal file
68
vendor/github.com/onsi/ginkgo/ginkgo/build_command.go
generated
vendored
Normal file
@@ -0,0 +1,68 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/onsi/ginkgo/ginkgo/interrupthandler"
|
||||
"github.com/onsi/ginkgo/ginkgo/testrunner"
|
||||
)
|
||||
|
||||
func BuildBuildCommand() *Command {
|
||||
commandFlags := NewBuildCommandFlags(flag.NewFlagSet("build", flag.ExitOnError))
|
||||
interruptHandler := interrupthandler.NewInterruptHandler()
|
||||
builder := &SpecBuilder{
|
||||
commandFlags: commandFlags,
|
||||
interruptHandler: interruptHandler,
|
||||
}
|
||||
|
||||
return &Command{
|
||||
Name: "build",
|
||||
FlagSet: commandFlags.FlagSet,
|
||||
UsageCommand: "ginkgo build <FLAGS> <PACKAGES>",
|
||||
Usage: []string{
|
||||
"Build the passed in <PACKAGES> (or the package in the current directory if left blank).",
|
||||
"Accepts the following flags:",
|
||||
},
|
||||
Command: builder.BuildSpecs,
|
||||
}
|
||||
}
|
||||
|
||||
type SpecBuilder struct {
|
||||
commandFlags *RunWatchAndBuildCommandFlags
|
||||
interruptHandler *interrupthandler.InterruptHandler
|
||||
}
|
||||
|
||||
func (r *SpecBuilder) BuildSpecs(args []string, additionalArgs []string) {
|
||||
r.commandFlags.computeNodes()
|
||||
|
||||
suites, _ := findSuites(args, r.commandFlags.Recurse, r.commandFlags.SkipPackage, false)
|
||||
|
||||
if len(suites) == 0 {
|
||||
complainAndQuit("Found no test suites")
|
||||
}
|
||||
|
||||
passed := true
|
||||
for _, suite := range suites {
|
||||
runner := testrunner.New(suite, 1, false, r.commandFlags.Race, r.commandFlags.Cover, r.commandFlags.CoverPkg, r.commandFlags.Tags, nil)
|
||||
fmt.Printf("Compiling %s...\n", suite.PackageName)
|
||||
|
||||
path, _ := filepath.Abs(filepath.Join(suite.Path, fmt.Sprintf("%s.test", suite.PackageName)))
|
||||
err := runner.CompileTo(path)
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
passed = false
|
||||
} else {
|
||||
fmt.Printf(" compiled %s.test\n", suite.PackageName)
|
||||
}
|
||||
|
||||
runner.CleanUp()
|
||||
}
|
||||
|
||||
if passed {
|
||||
os.Exit(0)
|
||||
}
|
||||
os.Exit(1)
|
||||
}
|
||||
123
vendor/github.com/onsi/ginkgo/ginkgo/convert/ginkgo_ast_nodes.go
generated
vendored
Normal file
123
vendor/github.com/onsi/ginkgo/ginkgo/convert/ginkgo_ast_nodes.go
generated
vendored
Normal file
@@ -0,0 +1,123 @@
|
||||
package convert
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
/*
|
||||
* Creates a func init() node
|
||||
*/
|
||||
func createVarUnderscoreBlock() *ast.ValueSpec {
|
||||
valueSpec := &ast.ValueSpec{}
|
||||
object := &ast.Object{Kind: 4, Name: "_", Decl: valueSpec, Data: 0}
|
||||
ident := &ast.Ident{Name: "_", Obj: object}
|
||||
valueSpec.Names = append(valueSpec.Names, ident)
|
||||
return valueSpec
|
||||
}
|
||||
|
||||
/*
|
||||
* Creates a Describe("Testing with ginkgo", func() { }) node
|
||||
*/
|
||||
func createDescribeBlock() *ast.CallExpr {
|
||||
blockStatement := &ast.BlockStmt{List: []ast.Stmt{}}
|
||||
|
||||
fieldList := &ast.FieldList{}
|
||||
funcType := &ast.FuncType{Params: fieldList}
|
||||
funcLit := &ast.FuncLit{Type: funcType, Body: blockStatement}
|
||||
basicLit := &ast.BasicLit{Kind: 9, Value: "\"Testing with Ginkgo\""}
|
||||
describeIdent := &ast.Ident{Name: "Describe"}
|
||||
return &ast.CallExpr{Fun: describeIdent, Args: []ast.Expr{basicLit, funcLit}}
|
||||
}
|
||||
|
||||
/*
|
||||
* Convenience function to return the name of the *testing.T param
|
||||
* for a Test function that will be rewritten. This is useful because
|
||||
* we will want to replace the usage of this named *testing.T inside the
|
||||
* body of the function with a GinktoT.
|
||||
*/
|
||||
func namedTestingTArg(node *ast.FuncDecl) string {
|
||||
return node.Type.Params.List[0].Names[0].Name // *exhale*
|
||||
}
|
||||
|
||||
/*
|
||||
* Convenience function to return the block statement node for a Describe statement
|
||||
*/
|
||||
func blockStatementFromDescribe(desc *ast.CallExpr) *ast.BlockStmt {
|
||||
var funcLit *ast.FuncLit
|
||||
var found = false
|
||||
|
||||
for _, node := range desc.Args {
|
||||
switch node := node.(type) {
|
||||
case *ast.FuncLit:
|
||||
found = true
|
||||
funcLit = node
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
panic("Error finding ast.FuncLit inside describe statement. Somebody done goofed.")
|
||||
}
|
||||
|
||||
return funcLit.Body
|
||||
}
|
||||
|
||||
/* convenience function for creating an It("TestNameHere")
|
||||
* with all the body of the test function inside the anonymous
|
||||
* func passed to It()
|
||||
*/
|
||||
func createItStatementForTestFunc(testFunc *ast.FuncDecl) *ast.ExprStmt {
|
||||
blockStatement := &ast.BlockStmt{List: testFunc.Body.List}
|
||||
fieldList := &ast.FieldList{}
|
||||
funcType := &ast.FuncType{Params: fieldList}
|
||||
funcLit := &ast.FuncLit{Type: funcType, Body: blockStatement}
|
||||
|
||||
testName := rewriteTestName(testFunc.Name.Name)
|
||||
basicLit := &ast.BasicLit{Kind: 9, Value: fmt.Sprintf("\"%s\"", testName)}
|
||||
itBlockIdent := &ast.Ident{Name: "It"}
|
||||
callExpr := &ast.CallExpr{Fun: itBlockIdent, Args: []ast.Expr{basicLit, funcLit}}
|
||||
return &ast.ExprStmt{X: callExpr}
|
||||
}
|
||||
|
||||
/*
|
||||
* rewrite test names to be human readable
|
||||
* eg: rewrites "TestSomethingAmazing" as "something amazing"
|
||||
*/
|
||||
func rewriteTestName(testName string) string {
|
||||
nameComponents := []string{}
|
||||
currentString := ""
|
||||
indexOfTest := strings.Index(testName, "Test")
|
||||
if indexOfTest != 0 {
|
||||
return testName
|
||||
}
|
||||
|
||||
testName = strings.Replace(testName, "Test", "", 1)
|
||||
first, rest := testName[0], testName[1:]
|
||||
testName = string(unicode.ToLower(rune(first))) + rest
|
||||
|
||||
for _, rune := range testName {
|
||||
if unicode.IsUpper(rune) {
|
||||
nameComponents = append(nameComponents, currentString)
|
||||
currentString = string(unicode.ToLower(rune))
|
||||
} else {
|
||||
currentString += string(rune)
|
||||
}
|
||||
}
|
||||
|
||||
return strings.Join(append(nameComponents, currentString), " ")
|
||||
}
|
||||
|
||||
func newGinkgoTFromIdent(ident *ast.Ident) *ast.CallExpr {
|
||||
return &ast.CallExpr{
|
||||
Lparen: ident.NamePos + 1,
|
||||
Rparen: ident.NamePos + 2,
|
||||
Fun: &ast.Ident{Name: "GinkgoT"},
|
||||
}
|
||||
}
|
||||
|
||||
func newGinkgoTInterface() *ast.Ident {
|
||||
return &ast.Ident{Name: "GinkgoTInterface"}
|
||||
}
|
||||
91
vendor/github.com/onsi/ginkgo/ginkgo/convert/import.go
generated
vendored
Normal file
91
vendor/github.com/onsi/ginkgo/ginkgo/convert/import.go
generated
vendored
Normal file
@@ -0,0 +1,91 @@
|
||||
package convert
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
)
|
||||
|
||||
/*
|
||||
* Given the root node of an AST, returns the node containing the
|
||||
* import statements for the file.
|
||||
*/
|
||||
func importsForRootNode(rootNode *ast.File) (imports *ast.GenDecl, err error) {
|
||||
for _, declaration := range rootNode.Decls {
|
||||
decl, ok := declaration.(*ast.GenDecl)
|
||||
if !ok || len(decl.Specs) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
_, ok = decl.Specs[0].(*ast.ImportSpec)
|
||||
if ok {
|
||||
imports = decl
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
err = errors.New(fmt.Sprintf("Could not find imports for root node:\n\t%#v\n", rootNode))
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
* Removes "testing" import, if present
|
||||
*/
|
||||
func removeTestingImport(rootNode *ast.File) {
|
||||
importDecl, err := importsForRootNode(rootNode)
|
||||
if err != nil {
|
||||
panic(err.Error())
|
||||
}
|
||||
|
||||
var index int
|
||||
for i, importSpec := range importDecl.Specs {
|
||||
importSpec := importSpec.(*ast.ImportSpec)
|
||||
if importSpec.Path.Value == "\"testing\"" {
|
||||
index = i
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
importDecl.Specs = append(importDecl.Specs[:index], importDecl.Specs[index+1:]...)
|
||||
}
|
||||
|
||||
/*
|
||||
* Adds import statements for onsi/ginkgo, if missing
|
||||
*/
|
||||
func addGinkgoImports(rootNode *ast.File) {
|
||||
importDecl, err := importsForRootNode(rootNode)
|
||||
if err != nil {
|
||||
panic(err.Error())
|
||||
}
|
||||
|
||||
if len(importDecl.Specs) == 0 {
|
||||
// TODO: might need to create a import decl here
|
||||
panic("unimplemented : expected to find an imports block")
|
||||
}
|
||||
|
||||
needsGinkgo := true
|
||||
for _, importSpec := range importDecl.Specs {
|
||||
importSpec, ok := importSpec.(*ast.ImportSpec)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
if importSpec.Path.Value == "\"github.com/onsi/ginkgo\"" {
|
||||
needsGinkgo = false
|
||||
}
|
||||
}
|
||||
|
||||
if needsGinkgo {
|
||||
importDecl.Specs = append(importDecl.Specs, createImport(".", "\"github.com/onsi/ginkgo\""))
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* convenience function to create an import statement
|
||||
*/
|
||||
func createImport(name, path string) *ast.ImportSpec {
|
||||
return &ast.ImportSpec{
|
||||
Name: &ast.Ident{Name: name},
|
||||
Path: &ast.BasicLit{Kind: 9, Value: path},
|
||||
}
|
||||
}
|
||||
127
vendor/github.com/onsi/ginkgo/ginkgo/convert/package_rewriter.go
generated
vendored
Normal file
127
vendor/github.com/onsi/ginkgo/ginkgo/convert/package_rewriter.go
generated
vendored
Normal file
@@ -0,0 +1,127 @@
|
||||
package convert
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/build"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
/*
|
||||
* RewritePackage takes a name (eg: my-package/tools), finds its test files using
|
||||
* Go's build package, and then rewrites them. A ginkgo test suite file will
|
||||
* also be added for this package, and all of its child packages.
|
||||
*/
|
||||
func RewritePackage(packageName string) {
|
||||
pkg, err := packageWithName(packageName)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("unexpected error reading package: '%s'\n%s\n", packageName, err.Error()))
|
||||
}
|
||||
|
||||
for _, filename := range findTestsInPackage(pkg) {
|
||||
rewriteTestsInFile(filename)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
* Given a package, findTestsInPackage reads the test files in the directory,
|
||||
* and then recurses on each child package, returning a slice of all test files
|
||||
* found in this process.
|
||||
*/
|
||||
func findTestsInPackage(pkg *build.Package) (testfiles []string) {
|
||||
for _, file := range append(pkg.TestGoFiles, pkg.XTestGoFiles...) {
|
||||
testfiles = append(testfiles, filepath.Join(pkg.Dir, file))
|
||||
}
|
||||
|
||||
dirFiles, err := ioutil.ReadDir(pkg.Dir)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("unexpected error reading dir: '%s'\n%s\n", pkg.Dir, err.Error()))
|
||||
}
|
||||
|
||||
re := regexp.MustCompile(`^[._]`)
|
||||
|
||||
for _, file := range dirFiles {
|
||||
if !file.IsDir() {
|
||||
continue
|
||||
}
|
||||
|
||||
if re.Match([]byte(file.Name())) {
|
||||
continue
|
||||
}
|
||||
|
||||
packageName := filepath.Join(pkg.ImportPath, file.Name())
|
||||
subPackage, err := packageWithName(packageName)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("unexpected error reading package: '%s'\n%s\n", packageName, err.Error()))
|
||||
}
|
||||
|
||||
testfiles = append(testfiles, findTestsInPackage(subPackage)...)
|
||||
}
|
||||
|
||||
addGinkgoSuiteForPackage(pkg)
|
||||
goFmtPackage(pkg)
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
* Shells out to `ginkgo bootstrap` to create a test suite file
|
||||
*/
|
||||
func addGinkgoSuiteForPackage(pkg *build.Package) {
|
||||
originalDir, err := os.Getwd()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
suite_test_file := filepath.Join(pkg.Dir, pkg.Name+"_suite_test.go")
|
||||
|
||||
_, err = os.Stat(suite_test_file)
|
||||
if err == nil {
|
||||
return // test file already exists, this should be a no-op
|
||||
}
|
||||
|
||||
err = os.Chdir(pkg.Dir)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
output, err := exec.Command("ginkgo", "bootstrap").Output()
|
||||
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("error running 'ginkgo bootstrap'.\nstdout: %s\n%s\n", output, err.Error()))
|
||||
}
|
||||
|
||||
err = os.Chdir(originalDir)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Shells out to `go fmt` to format the package
|
||||
*/
|
||||
func goFmtPackage(pkg *build.Package) {
|
||||
output, err := exec.Command("go", "fmt", pkg.ImportPath).Output()
|
||||
|
||||
if err != nil {
|
||||
fmt.Printf("Warning: Error running 'go fmt %s'.\nstdout: %s\n%s\n", pkg.ImportPath, output, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Attempts to return a package with its test files already read.
|
||||
* The ImportMode arg to build.Import lets you specify if you want go to read the
|
||||
* buildable go files inside the package, but it fails if the package has no go files
|
||||
*/
|
||||
func packageWithName(name string) (pkg *build.Package, err error) {
|
||||
pkg, err = build.Default.Import(name, ".", build.ImportMode(0))
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
|
||||
pkg, err = build.Default.Import(name, ".", build.ImportMode(1))
|
||||
return
|
||||
}
|
||||
56
vendor/github.com/onsi/ginkgo/ginkgo/convert/test_finder.go
generated
vendored
Normal file
56
vendor/github.com/onsi/ginkgo/ginkgo/convert/test_finder.go
generated
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
package convert
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
/*
|
||||
* Given a root node, walks its top level statements and returns
|
||||
* points to function nodes to rewrite as It statements.
|
||||
* These functions, according to Go testing convention, must be named
|
||||
* TestWithCamelCasedName and receive a single *testing.T argument.
|
||||
*/
|
||||
func findTestFuncs(rootNode *ast.File) (testsToRewrite []*ast.FuncDecl) {
|
||||
testNameRegexp := regexp.MustCompile("^Test[0-9A-Z].+")
|
||||
|
||||
ast.Inspect(rootNode, func(node ast.Node) bool {
|
||||
if node == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
switch node := node.(type) {
|
||||
case *ast.FuncDecl:
|
||||
matches := testNameRegexp.MatchString(node.Name.Name)
|
||||
|
||||
if matches && receivesTestingT(node) {
|
||||
testsToRewrite = append(testsToRewrite, node)
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
* convenience function that looks at args to a function and determines if its
|
||||
* params include an argument of type *testing.T
|
||||
*/
|
||||
func receivesTestingT(node *ast.FuncDecl) bool {
|
||||
if len(node.Type.Params.List) != 1 {
|
||||
return false
|
||||
}
|
||||
|
||||
base, ok := node.Type.Params.List[0].Type.(*ast.StarExpr)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
intermediate := base.X.(*ast.SelectorExpr)
|
||||
isTestingPackage := intermediate.X.(*ast.Ident).Name == "testing"
|
||||
isTestingT := intermediate.Sel.Name == "T"
|
||||
|
||||
return isTestingPackage && isTestingT
|
||||
}
|
||||
163
vendor/github.com/onsi/ginkgo/ginkgo/convert/testfile_rewriter.go
generated
vendored
Normal file
163
vendor/github.com/onsi/ginkgo/ginkgo/convert/testfile_rewriter.go
generated
vendored
Normal file
@@ -0,0 +1,163 @@
|
||||
package convert
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/format"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
)
|
||||
|
||||
/*
|
||||
* Given a file path, rewrites any tests in the Ginkgo format.
|
||||
* First, we parse the AST, and update the imports declaration.
|
||||
* Then, we walk the first child elements in the file, returning tests to rewrite.
|
||||
* A top level init func is declared, with a single Describe func inside.
|
||||
* Then the test functions to rewrite are inserted as It statements inside the Describe.
|
||||
* Finally we walk the rest of the file, replacing other usages of *testing.T
|
||||
* Once that is complete, we write the AST back out again to its file.
|
||||
*/
|
||||
func rewriteTestsInFile(pathToFile string) {
|
||||
fileSet := token.NewFileSet()
|
||||
rootNode, err := parser.ParseFile(fileSet, pathToFile, nil, 0)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("Error parsing test file '%s':\n%s\n", pathToFile, err.Error()))
|
||||
}
|
||||
|
||||
addGinkgoImports(rootNode)
|
||||
removeTestingImport(rootNode)
|
||||
|
||||
varUnderscoreBlock := createVarUnderscoreBlock()
|
||||
describeBlock := createDescribeBlock()
|
||||
varUnderscoreBlock.Values = []ast.Expr{describeBlock}
|
||||
|
||||
for _, testFunc := range findTestFuncs(rootNode) {
|
||||
rewriteTestFuncAsItStatement(testFunc, rootNode, describeBlock)
|
||||
}
|
||||
|
||||
underscoreDecl := &ast.GenDecl{
|
||||
Tok: 85, // gah, magick numbers are needed to make this work
|
||||
TokPos: 14, // this tricks Go into writing "var _ = Describe"
|
||||
Specs: []ast.Spec{varUnderscoreBlock},
|
||||
}
|
||||
|
||||
imports := rootNode.Decls[0]
|
||||
tail := rootNode.Decls[1:]
|
||||
rootNode.Decls = append(append([]ast.Decl{imports}, underscoreDecl), tail...)
|
||||
rewriteOtherFuncsToUseGinkgoT(rootNode.Decls)
|
||||
walkNodesInRootNodeReplacingTestingT(rootNode)
|
||||
|
||||
var buffer bytes.Buffer
|
||||
if err = format.Node(&buffer, fileSet, rootNode); err != nil {
|
||||
panic(fmt.Sprintf("Error formatting ast node after rewriting tests.\n%s\n", err.Error()))
|
||||
}
|
||||
|
||||
fileInfo, err := os.Stat(pathToFile)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("Error stat'ing file: %s\n", pathToFile))
|
||||
}
|
||||
|
||||
ioutil.WriteFile(pathToFile, buffer.Bytes(), fileInfo.Mode())
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
* Given a test func named TestDoesSomethingNeat, rewrites it as
|
||||
* It("does something neat", func() { __test_body_here__ }) and adds it
|
||||
* to the Describe's list of statements
|
||||
*/
|
||||
func rewriteTestFuncAsItStatement(testFunc *ast.FuncDecl, rootNode *ast.File, describe *ast.CallExpr) {
|
||||
var funcIndex int = -1
|
||||
for index, child := range rootNode.Decls {
|
||||
if child == testFunc {
|
||||
funcIndex = index
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if funcIndex < 0 {
|
||||
panic(fmt.Sprintf("Assert failed: Error finding index for test node %s\n", testFunc.Name.Name))
|
||||
}
|
||||
|
||||
var block *ast.BlockStmt = blockStatementFromDescribe(describe)
|
||||
block.List = append(block.List, createItStatementForTestFunc(testFunc))
|
||||
replaceTestingTsWithGinkgoT(block, namedTestingTArg(testFunc))
|
||||
|
||||
// remove the old test func from the root node's declarations
|
||||
rootNode.Decls = append(rootNode.Decls[:funcIndex], rootNode.Decls[funcIndex+1:]...)
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
* walks nodes inside of a test func's statements and replaces the usage of
|
||||
* it's named *testing.T param with GinkgoT's
|
||||
*/
|
||||
func replaceTestingTsWithGinkgoT(statementsBlock *ast.BlockStmt, testingT string) {
|
||||
ast.Inspect(statementsBlock, func(node ast.Node) bool {
|
||||
if node == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
keyValueExpr, ok := node.(*ast.KeyValueExpr)
|
||||
if ok {
|
||||
replaceNamedTestingTsInKeyValueExpression(keyValueExpr, testingT)
|
||||
return true
|
||||
}
|
||||
|
||||
funcLiteral, ok := node.(*ast.FuncLit)
|
||||
if ok {
|
||||
replaceTypeDeclTestingTsInFuncLiteral(funcLiteral)
|
||||
return true
|
||||
}
|
||||
|
||||
callExpr, ok := node.(*ast.CallExpr)
|
||||
if !ok {
|
||||
return true
|
||||
}
|
||||
replaceTestingTsInArgsLists(callExpr, testingT)
|
||||
|
||||
funCall, ok := callExpr.Fun.(*ast.SelectorExpr)
|
||||
if ok {
|
||||
replaceTestingTsMethodCalls(funCall, testingT)
|
||||
}
|
||||
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
/*
|
||||
* rewrite t.Fail() or any other *testing.T method by replacing with T().Fail()
|
||||
* This function receives a selector expression (eg: t.Fail()) and
|
||||
* the name of the *testing.T param from the function declaration. Rewrites the
|
||||
* selector expression in place if the target was a *testing.T
|
||||
*/
|
||||
func replaceTestingTsMethodCalls(selectorExpr *ast.SelectorExpr, testingT string) {
|
||||
ident, ok := selectorExpr.X.(*ast.Ident)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
if ident.Name == testingT {
|
||||
selectorExpr.X = newGinkgoTFromIdent(ident)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* replaces usages of a named *testing.T param inside of a call expression
|
||||
* with a new GinkgoT object
|
||||
*/
|
||||
func replaceTestingTsInArgsLists(callExpr *ast.CallExpr, testingT string) {
|
||||
for index, arg := range callExpr.Args {
|
||||
ident, ok := arg.(*ast.Ident)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
if ident.Name == testingT {
|
||||
callExpr.Args[index] = newGinkgoTFromIdent(ident)
|
||||
}
|
||||
}
|
||||
}
|
||||
130
vendor/github.com/onsi/ginkgo/ginkgo/convert/testing_t_rewriter.go
generated
vendored
Normal file
130
vendor/github.com/onsi/ginkgo/ginkgo/convert/testing_t_rewriter.go
generated
vendored
Normal file
@@ -0,0 +1,130 @@
|
||||
package convert
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
)
|
||||
|
||||
/*
|
||||
* Rewrites any other top level funcs that receive a *testing.T param
|
||||
*/
|
||||
func rewriteOtherFuncsToUseGinkgoT(declarations []ast.Decl) {
|
||||
for _, decl := range declarations {
|
||||
decl, ok := decl.(*ast.FuncDecl)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, param := range decl.Type.Params.List {
|
||||
starExpr, ok := param.Type.(*ast.StarExpr)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
selectorExpr, ok := starExpr.X.(*ast.SelectorExpr)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
xIdent, ok := selectorExpr.X.(*ast.Ident)
|
||||
if !ok || xIdent.Name != "testing" {
|
||||
continue
|
||||
}
|
||||
|
||||
if selectorExpr.Sel.Name != "T" {
|
||||
continue
|
||||
}
|
||||
|
||||
param.Type = newGinkgoTInterface()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Walks all of the nodes in the file, replacing *testing.T in struct
|
||||
* and func literal nodes. eg:
|
||||
* type foo struct { *testing.T }
|
||||
* var bar = func(t *testing.T) { }
|
||||
*/
|
||||
func walkNodesInRootNodeReplacingTestingT(rootNode *ast.File) {
|
||||
ast.Inspect(rootNode, func(node ast.Node) bool {
|
||||
if node == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
switch node := node.(type) {
|
||||
case *ast.StructType:
|
||||
replaceTestingTsInStructType(node)
|
||||
case *ast.FuncLit:
|
||||
replaceTypeDeclTestingTsInFuncLiteral(node)
|
||||
}
|
||||
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
/*
|
||||
* replaces named *testing.T inside a composite literal
|
||||
*/
|
||||
func replaceNamedTestingTsInKeyValueExpression(kve *ast.KeyValueExpr, testingT string) {
|
||||
ident, ok := kve.Value.(*ast.Ident)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
if ident.Name == testingT {
|
||||
kve.Value = newGinkgoTFromIdent(ident)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* replaces *testing.T params in a func literal with GinkgoT
|
||||
*/
|
||||
func replaceTypeDeclTestingTsInFuncLiteral(functionLiteral *ast.FuncLit) {
|
||||
for _, arg := range functionLiteral.Type.Params.List {
|
||||
starExpr, ok := arg.Type.(*ast.StarExpr)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
selectorExpr, ok := starExpr.X.(*ast.SelectorExpr)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
target, ok := selectorExpr.X.(*ast.Ident)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
if target.Name == "testing" && selectorExpr.Sel.Name == "T" {
|
||||
arg.Type = newGinkgoTInterface()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Replaces *testing.T types inside of a struct declaration with a GinkgoT
|
||||
* eg: type foo struct { *testing.T }
|
||||
*/
|
||||
func replaceTestingTsInStructType(structType *ast.StructType) {
|
||||
for _, field := range structType.Fields.List {
|
||||
starExpr, ok := field.Type.(*ast.StarExpr)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
selectorExpr, ok := starExpr.X.(*ast.SelectorExpr)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
xIdent, ok := selectorExpr.X.(*ast.Ident)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
if xIdent.Name == "testing" && selectorExpr.Sel.Name == "T" {
|
||||
field.Type = newGinkgoTInterface()
|
||||
}
|
||||
}
|
||||
}
|
||||
44
vendor/github.com/onsi/ginkgo/ginkgo/convert_command.go
generated
vendored
Normal file
44
vendor/github.com/onsi/ginkgo/ginkgo/convert_command.go
generated
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"github.com/onsi/ginkgo/ginkgo/convert"
|
||||
"os"
|
||||
)
|
||||
|
||||
func BuildConvertCommand() *Command {
|
||||
return &Command{
|
||||
Name: "convert",
|
||||
FlagSet: flag.NewFlagSet("convert", flag.ExitOnError),
|
||||
UsageCommand: "ginkgo convert /path/to/package",
|
||||
Usage: []string{
|
||||
"Convert the package at the passed in path from an XUnit-style test to a Ginkgo-style test",
|
||||
},
|
||||
Command: convertPackage,
|
||||
}
|
||||
}
|
||||
|
||||
func convertPackage(args []string, additionalArgs []string) {
|
||||
if len(args) != 1 {
|
||||
println(fmt.Sprintf("usage: ginkgo convert /path/to/your/package"))
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
defer func() {
|
||||
err := recover()
|
||||
if err != nil {
|
||||
switch err := err.(type) {
|
||||
case error:
|
||||
println(err.Error())
|
||||
case string:
|
||||
println(err)
|
||||
default:
|
||||
println(fmt.Sprintf("unexpected error: %#v", err))
|
||||
}
|
||||
os.Exit(1)
|
||||
}
|
||||
}()
|
||||
|
||||
convert.RewritePackage(args[0])
|
||||
}
|
||||
164
vendor/github.com/onsi/ginkgo/ginkgo/generate_command.go
generated
vendored
Normal file
164
vendor/github.com/onsi/ginkgo/ginkgo/generate_command.go
generated
vendored
Normal file
@@ -0,0 +1,164 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
func BuildGenerateCommand() *Command {
|
||||
var agouti, noDot bool
|
||||
flagSet := flag.NewFlagSet("generate", flag.ExitOnError)
|
||||
flagSet.BoolVar(&agouti, "agouti", false, "If set, generate will generate a test file for writing Agouti tests")
|
||||
flagSet.BoolVar(&noDot, "nodot", false, "If set, generate will generate a test file that does not . import ginkgo and gomega")
|
||||
|
||||
return &Command{
|
||||
Name: "generate",
|
||||
FlagSet: flagSet,
|
||||
UsageCommand: "ginkgo generate <filename(s)>",
|
||||
Usage: []string{
|
||||
"Generate a test file named filename_test.go",
|
||||
"If the optional <filenames> argument is omitted, a file named after the package in the current directory will be created.",
|
||||
"Accepts the following flags:",
|
||||
},
|
||||
Command: func(args []string, additionalArgs []string) {
|
||||
generateSpec(args, agouti, noDot)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
var specText = `package {{.Package}}_test
|
||||
|
||||
import (
|
||||
. "{{.PackageImportPath}}"
|
||||
|
||||
{{if .IncludeImports}}. "github.com/onsi/ginkgo"{{end}}
|
||||
{{if .IncludeImports}}. "github.com/onsi/gomega"{{end}}
|
||||
)
|
||||
|
||||
var _ = Describe("{{.Subject}}", func() {
|
||||
|
||||
})
|
||||
`
|
||||
|
||||
var agoutiSpecText = `package {{.Package}}_test
|
||||
|
||||
import (
|
||||
. "{{.PackageImportPath}}"
|
||||
|
||||
{{if .IncludeImports}}. "github.com/onsi/ginkgo"{{end}}
|
||||
{{if .IncludeImports}}. "github.com/onsi/gomega"{{end}}
|
||||
. "github.com/sclevine/agouti/matchers"
|
||||
"github.com/sclevine/agouti"
|
||||
)
|
||||
|
||||
var _ = Describe("{{.Subject}}", func() {
|
||||
var page *agouti.Page
|
||||
|
||||
BeforeEach(func() {
|
||||
var err error
|
||||
page, err = agoutiDriver.NewPage()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
Expect(page.Destroy()).To(Succeed())
|
||||
})
|
||||
})
|
||||
`
|
||||
|
||||
type specData struct {
|
||||
Package string
|
||||
Subject string
|
||||
PackageImportPath string
|
||||
IncludeImports bool
|
||||
}
|
||||
|
||||
func generateSpec(args []string, agouti, noDot bool) {
|
||||
if len(args) == 0 {
|
||||
err := generateSpecForSubject("", agouti, noDot)
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
fmt.Println("")
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Println("")
|
||||
return
|
||||
}
|
||||
|
||||
var failed bool
|
||||
for _, arg := range args {
|
||||
err := generateSpecForSubject(arg, agouti, noDot)
|
||||
if err != nil {
|
||||
failed = true
|
||||
fmt.Println(err.Error())
|
||||
}
|
||||
}
|
||||
fmt.Println("")
|
||||
if failed {
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func generateSpecForSubject(subject string, agouti, noDot bool) error {
|
||||
packageName, specFilePrefix, formattedName := getPackageAndFormattedName()
|
||||
if subject != "" {
|
||||
subject = strings.Split(subject, ".go")[0]
|
||||
subject = strings.Split(subject, "_test")[0]
|
||||
specFilePrefix = subject
|
||||
formattedName = prettifyPackageName(subject)
|
||||
}
|
||||
|
||||
data := specData{
|
||||
Package: packageName,
|
||||
Subject: formattedName,
|
||||
PackageImportPath: getPackageImportPath(),
|
||||
IncludeImports: !noDot,
|
||||
}
|
||||
|
||||
targetFile := fmt.Sprintf("%s_test.go", specFilePrefix)
|
||||
if fileExists(targetFile) {
|
||||
return fmt.Errorf("%s already exists.", targetFile)
|
||||
} else {
|
||||
fmt.Printf("Generating ginkgo test for %s in:\n %s\n", data.Subject, targetFile)
|
||||
}
|
||||
|
||||
f, err := os.Create(targetFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
var templateText string
|
||||
if agouti {
|
||||
templateText = agoutiSpecText
|
||||
} else {
|
||||
templateText = specText
|
||||
}
|
||||
|
||||
specTemplate, err := template.New("spec").Parse(templateText)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
specTemplate.Execute(f, data)
|
||||
goFmt(targetFile)
|
||||
return nil
|
||||
}
|
||||
|
||||
func getPackageImportPath() string {
|
||||
workingDir, err := os.Getwd()
|
||||
if err != nil {
|
||||
panic(err.Error())
|
||||
}
|
||||
sep := string(filepath.Separator)
|
||||
paths := strings.Split(workingDir, sep+"src"+sep)
|
||||
if len(paths) == 1 {
|
||||
fmt.Printf("\nCouldn't identify package import path.\n\n\tginkgo generate\n\nMust be run within a package directory under $GOPATH/src/...\nYou're going to have to change UNKNOWN_PACKAGE_PATH in the generated file...\n\n")
|
||||
return "UNKNOWN_PACKAGE_PATH"
|
||||
}
|
||||
return filepath.ToSlash(paths[len(paths)-1])
|
||||
}
|
||||
31
vendor/github.com/onsi/ginkgo/ginkgo/help_command.go
generated
vendored
Normal file
31
vendor/github.com/onsi/ginkgo/ginkgo/help_command.go
generated
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func BuildHelpCommand() *Command {
|
||||
return &Command{
|
||||
Name: "help",
|
||||
FlagSet: flag.NewFlagSet("help", flag.ExitOnError),
|
||||
UsageCommand: "ginkgo help <COMAND>",
|
||||
Usage: []string{
|
||||
"Print usage information. If a command is passed in, print usage information just for that command.",
|
||||
},
|
||||
Command: printHelp,
|
||||
}
|
||||
}
|
||||
|
||||
func printHelp(args []string, additionalArgs []string) {
|
||||
if len(args) == 0 {
|
||||
usage()
|
||||
} else {
|
||||
command, found := commandMatching(args[0])
|
||||
if !found {
|
||||
complainAndQuit(fmt.Sprintf("Unknown command: %s", args[0]))
|
||||
}
|
||||
|
||||
usageForCommand(command, true)
|
||||
}
|
||||
}
|
||||
52
vendor/github.com/onsi/ginkgo/ginkgo/interrupthandler/interrupt_handler.go
generated
vendored
Normal file
52
vendor/github.com/onsi/ginkgo/ginkgo/interrupthandler/interrupt_handler.go
generated
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
package interrupthandler
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/signal"
|
||||
"sync"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
type InterruptHandler struct {
|
||||
interruptCount int
|
||||
lock *sync.Mutex
|
||||
C chan bool
|
||||
}
|
||||
|
||||
func NewInterruptHandler() *InterruptHandler {
|
||||
h := &InterruptHandler{
|
||||
lock: &sync.Mutex{},
|
||||
C: make(chan bool, 0),
|
||||
}
|
||||
|
||||
go h.handleInterrupt()
|
||||
SwallowSigQuit()
|
||||
|
||||
return h
|
||||
}
|
||||
|
||||
func (h *InterruptHandler) WasInterrupted() bool {
|
||||
h.lock.Lock()
|
||||
defer h.lock.Unlock()
|
||||
|
||||
return h.interruptCount > 0
|
||||
}
|
||||
|
||||
func (h *InterruptHandler) handleInterrupt() {
|
||||
c := make(chan os.Signal, 1)
|
||||
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
|
||||
|
||||
<-c
|
||||
signal.Stop(c)
|
||||
|
||||
h.lock.Lock()
|
||||
h.interruptCount++
|
||||
if h.interruptCount == 1 {
|
||||
close(h.C)
|
||||
} else if h.interruptCount > 5 {
|
||||
os.Exit(1)
|
||||
}
|
||||
h.lock.Unlock()
|
||||
|
||||
go h.handleInterrupt()
|
||||
}
|
||||
14
vendor/github.com/onsi/ginkgo/ginkgo/interrupthandler/sigquit_swallower_unix.go
generated
vendored
Normal file
14
vendor/github.com/onsi/ginkgo/ginkgo/interrupthandler/sigquit_swallower_unix.go
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
// +build freebsd openbsd netbsd dragonfly darwin linux
|
||||
|
||||
package interrupthandler
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func SwallowSigQuit() {
|
||||
c := make(chan os.Signal, 1024)
|
||||
signal.Notify(c, syscall.SIGQUIT)
|
||||
}
|
||||
7
vendor/github.com/onsi/ginkgo/ginkgo/interrupthandler/sigquit_swallower_windows.go
generated
vendored
Normal file
7
vendor/github.com/onsi/ginkgo/ginkgo/interrupthandler/sigquit_swallower_windows.go
generated
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
// +build windows
|
||||
|
||||
package interrupthandler
|
||||
|
||||
func SwallowSigQuit() {
|
||||
//noop
|
||||
}
|
||||
291
vendor/github.com/onsi/ginkgo/ginkgo/main.go
generated
vendored
Normal file
291
vendor/github.com/onsi/ginkgo/ginkgo/main.go
generated
vendored
Normal file
@@ -0,0 +1,291 @@
|
||||
/*
|
||||
The Ginkgo CLI
|
||||
|
||||
The Ginkgo CLI is fully documented [here](http://onsi.github.io/ginkgo/#the_ginkgo_cli)
|
||||
|
||||
You can also learn more by running:
|
||||
|
||||
ginkgo help
|
||||
|
||||
Here are some of the more commonly used commands:
|
||||
|
||||
To install:
|
||||
|
||||
go install github.com/onsi/ginkgo/ginkgo
|
||||
|
||||
To run tests:
|
||||
|
||||
ginkgo
|
||||
|
||||
To run tests in all subdirectories:
|
||||
|
||||
ginkgo -r
|
||||
|
||||
To run tests in particular packages:
|
||||
|
||||
ginkgo <flags> /path/to/package /path/to/another/package
|
||||
|
||||
To pass arguments/flags to your tests:
|
||||
|
||||
ginkgo <flags> <packages> -- <pass-throughs>
|
||||
|
||||
To run tests in parallel
|
||||
|
||||
ginkgo -p
|
||||
|
||||
this will automatically detect the optimal number of nodes to use. Alternatively, you can specify the number of nodes with:
|
||||
|
||||
ginkgo -nodes=N
|
||||
|
||||
(note that you don't need to provide -p in this case).
|
||||
|
||||
By default the Ginkgo CLI will spin up a server that the individual test processes send test output to. The CLI aggregates this output and then presents coherent test output, one test at a time, as each test completes.
|
||||
An alternative is to have the parallel nodes run and stream interleaved output back. This useful for debugging, particularly in contexts where tests hang/fail to start. To get this interleaved output:
|
||||
|
||||
ginkgo -nodes=N -stream=true
|
||||
|
||||
On windows, the default value for stream is true.
|
||||
|
||||
By default, when running multiple tests (with -r or a list of packages) Ginkgo will abort when a test fails. To have Ginkgo run subsequent test suites instead you can:
|
||||
|
||||
ginkgo -keepGoing
|
||||
|
||||
To monitor packages and rerun tests when changes occur:
|
||||
|
||||
ginkgo watch <-r> </path/to/package>
|
||||
|
||||
passing `ginkgo watch` the `-r` flag will recursively detect all test suites under the current directory and monitor them.
|
||||
`watch` does not detect *new* packages. Moreover, changes in package X only rerun the tests for package X, tests for packages
|
||||
that depend on X are not rerun.
|
||||
|
||||
[OSX & Linux only] To receive (desktop) notifications when a test run completes:
|
||||
|
||||
ginkgo -notify
|
||||
|
||||
this is particularly useful with `ginkgo watch`. Notifications are currently only supported on OS X and require that you `brew install terminal-notifier`
|
||||
|
||||
Sometimes (to suss out race conditions/flakey tests, for example) you want to keep running a test suite until it fails. You can do this with:
|
||||
|
||||
ginkgo -untilItFails
|
||||
|
||||
To bootstrap a test suite:
|
||||
|
||||
ginkgo bootstrap
|
||||
|
||||
To generate a test file:
|
||||
|
||||
ginkgo generate <test_file_name>
|
||||
|
||||
To bootstrap/generate test files without using "." imports:
|
||||
|
||||
ginkgo bootstrap --nodot
|
||||
ginkgo generate --nodot
|
||||
|
||||
this will explicitly export all the identifiers in Ginkgo and Gomega allowing you to rename them to avoid collisions. When you pull to the latest Ginkgo/Gomega you'll want to run
|
||||
|
||||
ginkgo nodot
|
||||
|
||||
to refresh this list and pull in any new identifiers. In particular, this will pull in any new Gomega matchers that get added.
|
||||
|
||||
To convert an existing XUnit style test suite to a Ginkgo-style test suite:
|
||||
|
||||
ginkgo convert .
|
||||
|
||||
To unfocus tests:
|
||||
|
||||
ginkgo unfocus
|
||||
|
||||
or
|
||||
|
||||
ginkgo blur
|
||||
|
||||
To compile a test suite:
|
||||
|
||||
ginkgo build <path-to-package>
|
||||
|
||||
will output an executable file named `package.test`. This can be run directly or by invoking
|
||||
|
||||
ginkgo <path-to-package.test>
|
||||
|
||||
To print out Ginkgo's version:
|
||||
|
||||
ginkgo version
|
||||
|
||||
To get more help:
|
||||
|
||||
ginkgo help
|
||||
*/
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
||||
"github.com/onsi/ginkgo/config"
|
||||
"github.com/onsi/ginkgo/ginkgo/testsuite"
|
||||
)
|
||||
|
||||
const greenColor = "\x1b[32m"
|
||||
const redColor = "\x1b[91m"
|
||||
const defaultStyle = "\x1b[0m"
|
||||
const lightGrayColor = "\x1b[37m"
|
||||
|
||||
type Command struct {
|
||||
Name string
|
||||
AltName string
|
||||
FlagSet *flag.FlagSet
|
||||
Usage []string
|
||||
UsageCommand string
|
||||
Command func(args []string, additionalArgs []string)
|
||||
SuppressFlagDocumentation bool
|
||||
FlagDocSubstitute []string
|
||||
}
|
||||
|
||||
func (c *Command) Matches(name string) bool {
|
||||
return c.Name == name || (c.AltName != "" && c.AltName == name)
|
||||
}
|
||||
|
||||
func (c *Command) Run(args []string, additionalArgs []string) {
|
||||
c.FlagSet.Parse(args)
|
||||
c.Command(c.FlagSet.Args(), additionalArgs)
|
||||
}
|
||||
|
||||
var DefaultCommand *Command
|
||||
var Commands []*Command
|
||||
|
||||
func init() {
|
||||
DefaultCommand = BuildRunCommand()
|
||||
Commands = append(Commands, BuildWatchCommand())
|
||||
Commands = append(Commands, BuildBuildCommand())
|
||||
Commands = append(Commands, BuildBootstrapCommand())
|
||||
Commands = append(Commands, BuildGenerateCommand())
|
||||
Commands = append(Commands, BuildNodotCommand())
|
||||
Commands = append(Commands, BuildConvertCommand())
|
||||
Commands = append(Commands, BuildUnfocusCommand())
|
||||
Commands = append(Commands, BuildVersionCommand())
|
||||
Commands = append(Commands, BuildHelpCommand())
|
||||
}
|
||||
|
||||
func main() {
|
||||
args := []string{}
|
||||
additionalArgs := []string{}
|
||||
|
||||
foundDelimiter := false
|
||||
|
||||
for _, arg := range os.Args[1:] {
|
||||
if !foundDelimiter {
|
||||
if arg == "--" {
|
||||
foundDelimiter = true
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if foundDelimiter {
|
||||
additionalArgs = append(additionalArgs, arg)
|
||||
} else {
|
||||
args = append(args, arg)
|
||||
}
|
||||
}
|
||||
|
||||
if len(args) > 0 {
|
||||
commandToRun, found := commandMatching(args[0])
|
||||
if found {
|
||||
commandToRun.Run(args[1:], additionalArgs)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
DefaultCommand.Run(args, additionalArgs)
|
||||
}
|
||||
|
||||
func commandMatching(name string) (*Command, bool) {
|
||||
for _, command := range Commands {
|
||||
if command.Matches(name) {
|
||||
return command, true
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func usage() {
|
||||
fmt.Fprintf(os.Stderr, "Ginkgo Version %s\n\n", config.VERSION)
|
||||
usageForCommand(DefaultCommand, false)
|
||||
for _, command := range Commands {
|
||||
fmt.Fprintf(os.Stderr, "\n")
|
||||
usageForCommand(command, false)
|
||||
}
|
||||
}
|
||||
|
||||
func usageForCommand(command *Command, longForm bool) {
|
||||
fmt.Fprintf(os.Stderr, "%s\n%s\n", command.UsageCommand, strings.Repeat("-", len(command.UsageCommand)))
|
||||
fmt.Fprintf(os.Stderr, "%s\n", strings.Join(command.Usage, "\n"))
|
||||
if command.SuppressFlagDocumentation && !longForm {
|
||||
fmt.Fprintf(os.Stderr, "%s\n", strings.Join(command.FlagDocSubstitute, "\n "))
|
||||
} else {
|
||||
command.FlagSet.PrintDefaults()
|
||||
}
|
||||
}
|
||||
|
||||
func complainAndQuit(complaint string) {
|
||||
fmt.Fprintf(os.Stderr, "%s\nFor usage instructions:\n\tginkgo help\n", complaint)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
func findSuites(args []string, recurse bool, skipPackage string, allowPrecompiled bool) ([]testsuite.TestSuite, []string) {
|
||||
suites := []testsuite.TestSuite{}
|
||||
|
||||
if len(args) > 0 {
|
||||
for _, arg := range args {
|
||||
if allowPrecompiled {
|
||||
suite, err := testsuite.PrecompiledTestSuite(arg)
|
||||
if err == nil {
|
||||
suites = append(suites, suite)
|
||||
continue
|
||||
}
|
||||
}
|
||||
suites = append(suites, testsuite.SuitesInDir(arg, recurse)...)
|
||||
}
|
||||
} else {
|
||||
suites = testsuite.SuitesInDir(".", recurse)
|
||||
}
|
||||
|
||||
skippedPackages := []string{}
|
||||
if skipPackage != "" {
|
||||
skipFilters := strings.Split(skipPackage, ",")
|
||||
filteredSuites := []testsuite.TestSuite{}
|
||||
for _, suite := range suites {
|
||||
skip := false
|
||||
for _, skipFilter := range skipFilters {
|
||||
if strings.Contains(suite.Path, skipFilter) {
|
||||
skip = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if skip {
|
||||
skippedPackages = append(skippedPackages, suite.Path)
|
||||
} else {
|
||||
filteredSuites = append(filteredSuites, suite)
|
||||
}
|
||||
}
|
||||
suites = filteredSuites
|
||||
}
|
||||
|
||||
return suites, skippedPackages
|
||||
}
|
||||
|
||||
func goFmt(path string) {
|
||||
err := exec.Command("go", "fmt", path).Run()
|
||||
if err != nil {
|
||||
complainAndQuit("Could not fmt: " + err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func pluralizedWord(singular, plural string, count int) string {
|
||||
if count == 1 {
|
||||
return singular
|
||||
}
|
||||
return plural
|
||||
}
|
||||
194
vendor/github.com/onsi/ginkgo/ginkgo/nodot/nodot.go
generated
vendored
Normal file
194
vendor/github.com/onsi/ginkgo/ginkgo/nodot/nodot.go
generated
vendored
Normal file
@@ -0,0 +1,194 @@
|
||||
package nodot
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/build"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func ApplyNoDot(data []byte) ([]byte, error) {
|
||||
sections, err := generateNodotSections()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, section := range sections {
|
||||
data = section.createOrUpdateIn(data)
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
type nodotSection struct {
|
||||
name string
|
||||
pkg string
|
||||
declarations []string
|
||||
types []string
|
||||
}
|
||||
|
||||
func (s nodotSection) createOrUpdateIn(data []byte) []byte {
|
||||
renames := map[string]string{}
|
||||
|
||||
contents := string(data)
|
||||
|
||||
lines := strings.Split(contents, "\n")
|
||||
|
||||
comment := "// Declarations for " + s.name
|
||||
|
||||
newLines := []string{}
|
||||
for _, line := range lines {
|
||||
if line == comment {
|
||||
continue
|
||||
}
|
||||
|
||||
words := strings.Split(line, " ")
|
||||
lastWord := words[len(words)-1]
|
||||
|
||||
if s.containsDeclarationOrType(lastWord) {
|
||||
renames[lastWord] = words[1]
|
||||
continue
|
||||
}
|
||||
|
||||
newLines = append(newLines, line)
|
||||
}
|
||||
|
||||
if len(newLines[len(newLines)-1]) > 0 {
|
||||
newLines = append(newLines, "")
|
||||
}
|
||||
|
||||
newLines = append(newLines, comment)
|
||||
|
||||
for _, typ := range s.types {
|
||||
name, ok := renames[s.prefix(typ)]
|
||||
if !ok {
|
||||
name = typ
|
||||
}
|
||||
newLines = append(newLines, fmt.Sprintf("type %s %s", name, s.prefix(typ)))
|
||||
}
|
||||
|
||||
for _, decl := range s.declarations {
|
||||
name, ok := renames[s.prefix(decl)]
|
||||
if !ok {
|
||||
name = decl
|
||||
}
|
||||
newLines = append(newLines, fmt.Sprintf("var %s = %s", name, s.prefix(decl)))
|
||||
}
|
||||
|
||||
newLines = append(newLines, "")
|
||||
|
||||
newContents := strings.Join(newLines, "\n")
|
||||
|
||||
return []byte(newContents)
|
||||
}
|
||||
|
||||
func (s nodotSection) prefix(declOrType string) string {
|
||||
return s.pkg + "." + declOrType
|
||||
}
|
||||
|
||||
func (s nodotSection) containsDeclarationOrType(word string) bool {
|
||||
for _, declaration := range s.declarations {
|
||||
if s.prefix(declaration) == word {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
for _, typ := range s.types {
|
||||
if s.prefix(typ) == word {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func generateNodotSections() ([]nodotSection, error) {
|
||||
sections := []nodotSection{}
|
||||
|
||||
declarations, err := getExportedDeclerationsForPackage("github.com/onsi/ginkgo", "ginkgo_dsl.go", "GINKGO_VERSION", "GINKGO_PANIC")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sections = append(sections, nodotSection{
|
||||
name: "Ginkgo DSL",
|
||||
pkg: "ginkgo",
|
||||
declarations: declarations,
|
||||
types: []string{"Done", "Benchmarker"},
|
||||
})
|
||||
|
||||
declarations, err = getExportedDeclerationsForPackage("github.com/onsi/gomega", "gomega_dsl.go", "GOMEGA_VERSION")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sections = append(sections, nodotSection{
|
||||
name: "Gomega DSL",
|
||||
pkg: "gomega",
|
||||
declarations: declarations,
|
||||
})
|
||||
|
||||
declarations, err = getExportedDeclerationsForPackage("github.com/onsi/gomega", "matchers.go")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sections = append(sections, nodotSection{
|
||||
name: "Gomega Matchers",
|
||||
pkg: "gomega",
|
||||
declarations: declarations,
|
||||
})
|
||||
|
||||
return sections, nil
|
||||
}
|
||||
|
||||
func getExportedDeclerationsForPackage(pkgPath string, filename string, blacklist ...string) ([]string, error) {
|
||||
pkg, err := build.Import(pkgPath, ".", 0)
|
||||
if err != nil {
|
||||
return []string{}, err
|
||||
}
|
||||
|
||||
declarations, err := getExportedDeclarationsForFile(filepath.Join(pkg.Dir, filename))
|
||||
if err != nil {
|
||||
return []string{}, err
|
||||
}
|
||||
|
||||
blacklistLookup := map[string]bool{}
|
||||
for _, declaration := range blacklist {
|
||||
blacklistLookup[declaration] = true
|
||||
}
|
||||
|
||||
filteredDeclarations := []string{}
|
||||
for _, declaration := range declarations {
|
||||
if blacklistLookup[declaration] {
|
||||
continue
|
||||
}
|
||||
filteredDeclarations = append(filteredDeclarations, declaration)
|
||||
}
|
||||
|
||||
return filteredDeclarations, nil
|
||||
}
|
||||
|
||||
func getExportedDeclarationsForFile(path string) ([]string, error) {
|
||||
fset := token.NewFileSet()
|
||||
tree, err := parser.ParseFile(fset, path, nil, 0)
|
||||
if err != nil {
|
||||
return []string{}, err
|
||||
}
|
||||
|
||||
declarations := []string{}
|
||||
ast.FileExports(tree)
|
||||
for _, decl := range tree.Decls {
|
||||
switch x := decl.(type) {
|
||||
case *ast.GenDecl:
|
||||
switch s := x.Specs[0].(type) {
|
||||
case *ast.ValueSpec:
|
||||
declarations = append(declarations, s.Names[0].Name)
|
||||
}
|
||||
case *ast.FuncDecl:
|
||||
declarations = append(declarations, x.Name.Name)
|
||||
}
|
||||
}
|
||||
|
||||
return declarations, nil
|
||||
}
|
||||
91
vendor/github.com/onsi/ginkgo/ginkgo/nodot/nodot_suite_test.go
generated
vendored
Normal file
91
vendor/github.com/onsi/ginkgo/ginkgo/nodot/nodot_suite_test.go
generated
vendored
Normal file
@@ -0,0 +1,91 @@
|
||||
package nodot_test
|
||||
|
||||
import (
|
||||
"github.com/onsi/ginkgo"
|
||||
"github.com/onsi/gomega"
|
||||
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNodot(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecs(t, "Nodot Suite")
|
||||
}
|
||||
|
||||
// Declarations for Ginkgo DSL
|
||||
type Done ginkgo.Done
|
||||
type Benchmarker ginkgo.Benchmarker
|
||||
|
||||
var GinkgoWriter = ginkgo.GinkgoWriter
|
||||
var GinkgoParallelNode = ginkgo.GinkgoParallelNode
|
||||
var GinkgoT = ginkgo.GinkgoT
|
||||
var CurrentGinkgoTestDescription = ginkgo.CurrentGinkgoTestDescription
|
||||
var RunSpecs = ginkgo.RunSpecs
|
||||
var RunSpecsWithDefaultAndCustomReporters = ginkgo.RunSpecsWithDefaultAndCustomReporters
|
||||
var RunSpecsWithCustomReporters = ginkgo.RunSpecsWithCustomReporters
|
||||
var Fail = ginkgo.Fail
|
||||
var GinkgoRecover = ginkgo.GinkgoRecover
|
||||
var Describe = ginkgo.Describe
|
||||
var FDescribe = ginkgo.FDescribe
|
||||
var PDescribe = ginkgo.PDescribe
|
||||
var XDescribe = ginkgo.XDescribe
|
||||
var Context = ginkgo.Context
|
||||
var FContext = ginkgo.FContext
|
||||
var PContext = ginkgo.PContext
|
||||
var XContext = ginkgo.XContext
|
||||
var It = ginkgo.It
|
||||
var FIt = ginkgo.FIt
|
||||
var PIt = ginkgo.PIt
|
||||
var XIt = ginkgo.XIt
|
||||
var Measure = ginkgo.Measure
|
||||
var FMeasure = ginkgo.FMeasure
|
||||
var PMeasure = ginkgo.PMeasure
|
||||
var XMeasure = ginkgo.XMeasure
|
||||
var BeforeSuite = ginkgo.BeforeSuite
|
||||
var AfterSuite = ginkgo.AfterSuite
|
||||
var SynchronizedBeforeSuite = ginkgo.SynchronizedBeforeSuite
|
||||
var SynchronizedAfterSuite = ginkgo.SynchronizedAfterSuite
|
||||
var BeforeEach = ginkgo.BeforeEach
|
||||
var JustBeforeEach = ginkgo.JustBeforeEach
|
||||
var AfterEach = ginkgo.AfterEach
|
||||
|
||||
// Declarations for Gomega DSL
|
||||
var RegisterFailHandler = gomega.RegisterFailHandler
|
||||
var RegisterTestingT = gomega.RegisterTestingT
|
||||
var InterceptGomegaFailures = gomega.InterceptGomegaFailures
|
||||
var Ω = gomega.Ω
|
||||
var Expect = gomega.Expect
|
||||
var ExpectWithOffset = gomega.ExpectWithOffset
|
||||
var Eventually = gomega.Eventually
|
||||
var EventuallyWithOffset = gomega.EventuallyWithOffset
|
||||
var Consistently = gomega.Consistently
|
||||
var ConsistentlyWithOffset = gomega.ConsistentlyWithOffset
|
||||
var SetDefaultEventuallyTimeout = gomega.SetDefaultEventuallyTimeout
|
||||
var SetDefaultEventuallyPollingInterval = gomega.SetDefaultEventuallyPollingInterval
|
||||
var SetDefaultConsistentlyDuration = gomega.SetDefaultConsistentlyDuration
|
||||
var SetDefaultConsistentlyPollingInterval = gomega.SetDefaultConsistentlyPollingInterval
|
||||
|
||||
// Declarations for Gomega Matchers
|
||||
var Equal = gomega.Equal
|
||||
var BeEquivalentTo = gomega.BeEquivalentTo
|
||||
var BeNil = gomega.BeNil
|
||||
var BeTrue = gomega.BeTrue
|
||||
var BeFalse = gomega.BeFalse
|
||||
var HaveOccurred = gomega.HaveOccurred
|
||||
var MatchError = gomega.MatchError
|
||||
var BeClosed = gomega.BeClosed
|
||||
var Receive = gomega.Receive
|
||||
var MatchRegexp = gomega.MatchRegexp
|
||||
var ContainSubstring = gomega.ContainSubstring
|
||||
var MatchJSON = gomega.MatchJSON
|
||||
var BeEmpty = gomega.BeEmpty
|
||||
var HaveLen = gomega.HaveLen
|
||||
var BeZero = gomega.BeZero
|
||||
var ContainElement = gomega.ContainElement
|
||||
var ConsistOf = gomega.ConsistOf
|
||||
var HaveKey = gomega.HaveKey
|
||||
var HaveKeyWithValue = gomega.HaveKeyWithValue
|
||||
var BeNumerically = gomega.BeNumerically
|
||||
var BeTemporally = gomega.BeTemporally
|
||||
var BeAssignableToTypeOf = gomega.BeAssignableToTypeOf
|
||||
var Panic = gomega.Panic
|
||||
81
vendor/github.com/onsi/ginkgo/ginkgo/nodot/nodot_test.go
generated
vendored
Normal file
81
vendor/github.com/onsi/ginkgo/ginkgo/nodot/nodot_test.go
generated
vendored
Normal file
@@ -0,0 +1,81 @@
|
||||
package nodot_test
|
||||
|
||||
import (
|
||||
. "github.com/onsi/ginkgo/ginkgo/nodot"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var _ = Describe("ApplyNoDot", func() {
|
||||
var result string
|
||||
|
||||
apply := func(input string) string {
|
||||
output, err := ApplyNoDot([]byte(input))
|
||||
Ω(err).ShouldNot(HaveOccurred())
|
||||
return string(output)
|
||||
}
|
||||
|
||||
Context("when no declarations have been imported yet", func() {
|
||||
BeforeEach(func() {
|
||||
result = apply("")
|
||||
})
|
||||
|
||||
It("should add headings for the various declarations", func() {
|
||||
Ω(result).Should(ContainSubstring("// Declarations for Ginkgo DSL"))
|
||||
Ω(result).Should(ContainSubstring("// Declarations for Gomega DSL"))
|
||||
Ω(result).Should(ContainSubstring("// Declarations for Gomega Matchers"))
|
||||
})
|
||||
|
||||
It("should import Ginkgo's declarations", func() {
|
||||
Ω(result).Should(ContainSubstring("var It = ginkgo.It"))
|
||||
Ω(result).Should(ContainSubstring("var XDescribe = ginkgo.XDescribe"))
|
||||
})
|
||||
|
||||
It("should import Ginkgo's types", func() {
|
||||
Ω(result).Should(ContainSubstring("type Done ginkgo.Done"))
|
||||
Ω(result).Should(ContainSubstring("type Benchmarker ginkgo.Benchmarker"))
|
||||
Ω(strings.Count(result, "type ")).Should(Equal(2))
|
||||
})
|
||||
|
||||
It("should import Gomega's DSL and matchers", func() {
|
||||
Ω(result).Should(ContainSubstring("var Ω = gomega.Ω"))
|
||||
Ω(result).Should(ContainSubstring("var ContainSubstring = gomega.ContainSubstring"))
|
||||
Ω(result).Should(ContainSubstring("var Equal = gomega.Equal"))
|
||||
})
|
||||
|
||||
It("should not import blacklisted things", func() {
|
||||
Ω(result).ShouldNot(ContainSubstring("GINKGO_VERSION"))
|
||||
Ω(result).ShouldNot(ContainSubstring("GINKGO_PANIC"))
|
||||
Ω(result).ShouldNot(ContainSubstring("GOMEGA_VERSION"))
|
||||
})
|
||||
})
|
||||
|
||||
It("should be idempotent (module empty lines - go fmt can fix those for us)", func() {
|
||||
first := apply("")
|
||||
second := apply(first)
|
||||
first = strings.Trim(first, "\n")
|
||||
second = strings.Trim(second, "\n")
|
||||
Ω(first).Should(Equal(second))
|
||||
})
|
||||
|
||||
It("should not mess with other things in the input", func() {
|
||||
result = apply("var MyThing = SomethingThatsMine")
|
||||
Ω(result).Should(ContainSubstring("var MyThing = SomethingThatsMine"))
|
||||
})
|
||||
|
||||
Context("when the user has redefined a name", func() {
|
||||
It("should honor the redefinition", func() {
|
||||
result = apply(`
|
||||
var _ = gomega.Ω
|
||||
var When = ginkgo.It
|
||||
`)
|
||||
|
||||
Ω(result).Should(ContainSubstring("var _ = gomega.Ω"))
|
||||
Ω(result).ShouldNot(ContainSubstring("var Ω = gomega.Ω"))
|
||||
|
||||
Ω(result).Should(ContainSubstring("var When = ginkgo.It"))
|
||||
Ω(result).ShouldNot(ContainSubstring("var It = ginkgo.It"))
|
||||
|
||||
Ω(result).Should(ContainSubstring("var Context = ginkgo.Context"))
|
||||
})
|
||||
})
|
||||
})
|
||||
76
vendor/github.com/onsi/ginkgo/ginkgo/nodot_command.go
generated
vendored
Normal file
76
vendor/github.com/onsi/ginkgo/ginkgo/nodot_command.go
generated
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"flag"
|
||||
"github.com/onsi/ginkgo/ginkgo/nodot"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
func BuildNodotCommand() *Command {
|
||||
return &Command{
|
||||
Name: "nodot",
|
||||
FlagSet: flag.NewFlagSet("bootstrap", flag.ExitOnError),
|
||||
UsageCommand: "ginkgo nodot",
|
||||
Usage: []string{
|
||||
"Update the nodot declarations in your test suite",
|
||||
"Any missing declarations (from, say, a recently added matcher) will be added to your bootstrap file.",
|
||||
"If you've renamed a declaration, that name will be honored and not overwritten.",
|
||||
},
|
||||
Command: updateNodot,
|
||||
}
|
||||
}
|
||||
|
||||
func updateNodot(args []string, additionalArgs []string) {
|
||||
suiteFile, perm := findSuiteFile()
|
||||
|
||||
data, err := ioutil.ReadFile(suiteFile)
|
||||
if err != nil {
|
||||
complainAndQuit("Failed to update nodot declarations: " + err.Error())
|
||||
}
|
||||
|
||||
content, err := nodot.ApplyNoDot(data)
|
||||
if err != nil {
|
||||
complainAndQuit("Failed to update nodot declarations: " + err.Error())
|
||||
}
|
||||
ioutil.WriteFile(suiteFile, content, perm)
|
||||
|
||||
goFmt(suiteFile)
|
||||
}
|
||||
|
||||
func findSuiteFile() (string, os.FileMode) {
|
||||
workingDir, err := os.Getwd()
|
||||
if err != nil {
|
||||
complainAndQuit("Could not find suite file for nodot: " + err.Error())
|
||||
}
|
||||
|
||||
files, err := ioutil.ReadDir(workingDir)
|
||||
if err != nil {
|
||||
complainAndQuit("Could not find suite file for nodot: " + err.Error())
|
||||
}
|
||||
|
||||
re := regexp.MustCompile(`RunSpecs\(|RunSpecsWithDefaultAndCustomReporters\(|RunSpecsWithCustomReporters\(`)
|
||||
|
||||
for _, file := range files {
|
||||
if file.IsDir() {
|
||||
continue
|
||||
}
|
||||
path := filepath.Join(workingDir, file.Name())
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
complainAndQuit("Could not find suite file for nodot: " + err.Error())
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
if re.MatchReader(bufio.NewReader(f)) {
|
||||
return path, file.Mode()
|
||||
}
|
||||
}
|
||||
|
||||
complainAndQuit("Could not find a suite file for nodot: you need a bootstrap file that call's Ginkgo's RunSpecs() command.\nTry running ginkgo bootstrap first.")
|
||||
|
||||
return "", 0
|
||||
}
|
||||
141
vendor/github.com/onsi/ginkgo/ginkgo/notifications.go
generated
vendored
Normal file
141
vendor/github.com/onsi/ginkgo/ginkgo/notifications.go
generated
vendored
Normal file
@@ -0,0 +1,141 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/onsi/ginkgo/config"
|
||||
"github.com/onsi/ginkgo/ginkgo/testsuite"
|
||||
)
|
||||
|
||||
type Notifier struct {
|
||||
commandFlags *RunWatchAndBuildCommandFlags
|
||||
}
|
||||
|
||||
func NewNotifier(commandFlags *RunWatchAndBuildCommandFlags) *Notifier {
|
||||
return &Notifier{
|
||||
commandFlags: commandFlags,
|
||||
}
|
||||
}
|
||||
|
||||
func (n *Notifier) VerifyNotificationsAreAvailable() {
|
||||
if n.commandFlags.Notify {
|
||||
onLinux := (runtime.GOOS == "linux")
|
||||
onOSX := (runtime.GOOS == "darwin")
|
||||
if onOSX {
|
||||
|
||||
_, err := exec.LookPath("terminal-notifier")
|
||||
if err != nil {
|
||||
fmt.Printf(`--notify requires terminal-notifier, which you don't seem to have installed.
|
||||
|
||||
OSX:
|
||||
|
||||
To remedy this:
|
||||
|
||||
brew install terminal-notifier
|
||||
|
||||
To learn more about terminal-notifier:
|
||||
|
||||
https://github.com/alloy/terminal-notifier
|
||||
`)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
} else if onLinux {
|
||||
|
||||
_, err := exec.LookPath("notify-send")
|
||||
if err != nil {
|
||||
fmt.Printf(`--notify requires terminal-notifier or notify-send, which you don't seem to have installed.
|
||||
|
||||
Linux:
|
||||
|
||||
Download and install notify-send for your distribution
|
||||
`)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (n *Notifier) SendSuiteCompletionNotification(suite testsuite.TestSuite, suitePassed bool) {
|
||||
if suitePassed {
|
||||
n.SendNotification("Ginkgo [PASS]", fmt.Sprintf(`Test suite for "%s" passed.`, suite.PackageName))
|
||||
} else {
|
||||
n.SendNotification("Ginkgo [FAIL]", fmt.Sprintf(`Test suite for "%s" failed.`, suite.PackageName))
|
||||
}
|
||||
}
|
||||
|
||||
func (n *Notifier) SendNotification(title string, subtitle string) {
|
||||
|
||||
if n.commandFlags.Notify {
|
||||
onLinux := (runtime.GOOS == "linux")
|
||||
onOSX := (runtime.GOOS == "darwin")
|
||||
|
||||
if onOSX {
|
||||
|
||||
_, err := exec.LookPath("terminal-notifier")
|
||||
if err == nil {
|
||||
args := []string{"-title", title, "-subtitle", subtitle, "-group", "com.onsi.ginkgo"}
|
||||
terminal := os.Getenv("TERM_PROGRAM")
|
||||
if terminal == "iTerm.app" {
|
||||
args = append(args, "-activate", "com.googlecode.iterm2")
|
||||
} else if terminal == "Apple_Terminal" {
|
||||
args = append(args, "-activate", "com.apple.Terminal")
|
||||
}
|
||||
|
||||
exec.Command("terminal-notifier", args...).Run()
|
||||
}
|
||||
|
||||
} else if onLinux {
|
||||
|
||||
_, err := exec.LookPath("notify-send")
|
||||
if err == nil {
|
||||
args := []string{"-a", "ginkgo", title, subtitle}
|
||||
exec.Command("notify-send", args...).Run()
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (n *Notifier) RunCommand(suite testsuite.TestSuite, suitePassed bool) {
|
||||
|
||||
command := n.commandFlags.AfterSuiteHook
|
||||
if command != "" {
|
||||
|
||||
// Allow for string replacement to pass input to the command
|
||||
passed := "[FAIL]"
|
||||
if suitePassed {
|
||||
passed = "[PASS]"
|
||||
}
|
||||
command = strings.Replace(command, "(ginkgo-suite-passed)", passed, -1)
|
||||
command = strings.Replace(command, "(ginkgo-suite-name)", suite.PackageName, -1)
|
||||
|
||||
// Must break command into parts
|
||||
splitArgs := regexp.MustCompile(`'.+'|".+"|\S+`)
|
||||
parts := splitArgs.FindAllString(command, -1)
|
||||
|
||||
output, err := exec.Command(parts[0], parts[1:]...).CombinedOutput()
|
||||
if err != nil {
|
||||
fmt.Println("Post-suite command failed:")
|
||||
if config.DefaultReporterConfig.NoColor {
|
||||
fmt.Printf("\t%s\n", output)
|
||||
} else {
|
||||
fmt.Printf("\t%s%s%s\n", redColor, string(output), defaultStyle)
|
||||
}
|
||||
n.SendNotification("Ginkgo [ERROR]", fmt.Sprintf(`After suite command "%s" failed`, n.commandFlags.AfterSuiteHook))
|
||||
} else {
|
||||
fmt.Println("Post-suite command succeeded:")
|
||||
if config.DefaultReporterConfig.NoColor {
|
||||
fmt.Printf("\t%s\n", output)
|
||||
} else {
|
||||
fmt.Printf("\t%s%s%s\n", greenColor, string(output), defaultStyle)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
192
vendor/github.com/onsi/ginkgo/ginkgo/run_command.go
generated
vendored
Normal file
192
vendor/github.com/onsi/ginkgo/ginkgo/run_command.go
generated
vendored
Normal file
@@ -0,0 +1,192 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/onsi/ginkgo/config"
|
||||
"github.com/onsi/ginkgo/ginkgo/interrupthandler"
|
||||
"github.com/onsi/ginkgo/ginkgo/testrunner"
|
||||
"github.com/onsi/ginkgo/types"
|
||||
)
|
||||
|
||||
func BuildRunCommand() *Command {
|
||||
commandFlags := NewRunCommandFlags(flag.NewFlagSet("ginkgo", flag.ExitOnError))
|
||||
notifier := NewNotifier(commandFlags)
|
||||
interruptHandler := interrupthandler.NewInterruptHandler()
|
||||
runner := &SpecRunner{
|
||||
commandFlags: commandFlags,
|
||||
notifier: notifier,
|
||||
interruptHandler: interruptHandler,
|
||||
suiteRunner: NewSuiteRunner(notifier, interruptHandler),
|
||||
}
|
||||
|
||||
return &Command{
|
||||
Name: "",
|
||||
FlagSet: commandFlags.FlagSet,
|
||||
UsageCommand: "ginkgo <FLAGS> <PACKAGES> -- <PASS-THROUGHS>",
|
||||
Usage: []string{
|
||||
"Run the tests in the passed in <PACKAGES> (or the package in the current directory if left blank).",
|
||||
"Any arguments after -- will be passed to the test.",
|
||||
"Accepts the following flags:",
|
||||
},
|
||||
Command: runner.RunSpecs,
|
||||
}
|
||||
}
|
||||
|
||||
type SpecRunner struct {
|
||||
commandFlags *RunWatchAndBuildCommandFlags
|
||||
notifier *Notifier
|
||||
interruptHandler *interrupthandler.InterruptHandler
|
||||
suiteRunner *SuiteRunner
|
||||
}
|
||||
|
||||
func (r *SpecRunner) RunSpecs(args []string, additionalArgs []string) {
|
||||
r.commandFlags.computeNodes()
|
||||
r.notifier.VerifyNotificationsAreAvailable()
|
||||
|
||||
suites, skippedPackages := findSuites(args, r.commandFlags.Recurse, r.commandFlags.SkipPackage, true)
|
||||
if len(skippedPackages) > 0 {
|
||||
fmt.Println("Will skip:")
|
||||
for _, skippedPackage := range skippedPackages {
|
||||
fmt.Println(" " + skippedPackage)
|
||||
}
|
||||
}
|
||||
|
||||
if len(skippedPackages) > 0 && len(suites) == 0 {
|
||||
fmt.Println("All tests skipped! Exiting...")
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
if len(suites) == 0 {
|
||||
complainAndQuit("Found no test suites")
|
||||
}
|
||||
|
||||
r.ComputeSuccinctMode(len(suites))
|
||||
|
||||
t := time.Now()
|
||||
|
||||
runners := []*testrunner.TestRunner{}
|
||||
for _, suite := range suites {
|
||||
runners = append(runners, testrunner.New(suite, r.commandFlags.NumCPU, r.commandFlags.ParallelStream, r.commandFlags.Race, r.commandFlags.Cover, r.commandFlags.CoverPkg, r.commandFlags.Tags, additionalArgs))
|
||||
}
|
||||
|
||||
numSuites := 0
|
||||
runResult := testrunner.PassingRunResult()
|
||||
if r.commandFlags.UntilItFails {
|
||||
iteration := 0
|
||||
for {
|
||||
r.UpdateSeed()
|
||||
randomizedRunners := r.randomizeOrder(runners)
|
||||
runResult, numSuites = r.suiteRunner.RunSuites(randomizedRunners, r.commandFlags.NumCompilers, r.commandFlags.KeepGoing, nil)
|
||||
iteration++
|
||||
|
||||
if r.interruptHandler.WasInterrupted() {
|
||||
break
|
||||
}
|
||||
|
||||
if runResult.Passed {
|
||||
fmt.Printf("\nAll tests passed...\nWill keep running them until they fail.\nThis was attempt #%d\n%s\n", iteration, orcMessage(iteration))
|
||||
} else {
|
||||
fmt.Printf("\nTests failed on attempt #%d\n\n", iteration)
|
||||
break
|
||||
}
|
||||
}
|
||||
} else {
|
||||
randomizedRunners := r.randomizeOrder(runners)
|
||||
runResult, numSuites = r.suiteRunner.RunSuites(randomizedRunners, r.commandFlags.NumCompilers, r.commandFlags.KeepGoing, nil)
|
||||
}
|
||||
|
||||
for _, runner := range runners {
|
||||
runner.CleanUp()
|
||||
}
|
||||
|
||||
fmt.Printf("\nGinkgo ran %d %s in %s\n", numSuites, pluralizedWord("suite", "suites", numSuites), time.Since(t))
|
||||
|
||||
if runResult.Passed {
|
||||
if runResult.HasProgrammaticFocus {
|
||||
fmt.Printf("Test Suite Passed\n")
|
||||
fmt.Printf("Detected Programmatic Focus - setting exit status to %d\n", types.GINKGO_FOCUS_EXIT_CODE)
|
||||
os.Exit(types.GINKGO_FOCUS_EXIT_CODE)
|
||||
} else {
|
||||
fmt.Printf("Test Suite Passed\n")
|
||||
os.Exit(0)
|
||||
}
|
||||
} else {
|
||||
fmt.Printf("Test Suite Failed\n")
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *SpecRunner) ComputeSuccinctMode(numSuites int) {
|
||||
if config.DefaultReporterConfig.Verbose {
|
||||
config.DefaultReporterConfig.Succinct = false
|
||||
return
|
||||
}
|
||||
|
||||
if numSuites == 1 {
|
||||
return
|
||||
}
|
||||
|
||||
if numSuites > 1 && !r.commandFlags.wasSet("succinct") {
|
||||
config.DefaultReporterConfig.Succinct = true
|
||||
}
|
||||
}
|
||||
|
||||
func (r *SpecRunner) UpdateSeed() {
|
||||
if !r.commandFlags.wasSet("seed") {
|
||||
config.GinkgoConfig.RandomSeed = time.Now().Unix()
|
||||
}
|
||||
}
|
||||
|
||||
func (r *SpecRunner) randomizeOrder(runners []*testrunner.TestRunner) []*testrunner.TestRunner {
|
||||
if !r.commandFlags.RandomizeSuites {
|
||||
return runners
|
||||
}
|
||||
|
||||
if len(runners) <= 1 {
|
||||
return runners
|
||||
}
|
||||
|
||||
randomizedRunners := make([]*testrunner.TestRunner, len(runners))
|
||||
randomizer := rand.New(rand.NewSource(config.GinkgoConfig.RandomSeed))
|
||||
permutation := randomizer.Perm(len(runners))
|
||||
for i, j := range permutation {
|
||||
randomizedRunners[i] = runners[j]
|
||||
}
|
||||
return randomizedRunners
|
||||
}
|
||||
|
||||
func orcMessage(iteration int) string {
|
||||
if iteration < 10 {
|
||||
return ""
|
||||
} else if iteration < 30 {
|
||||
return []string{
|
||||
"If at first you succeed...",
|
||||
"...try, try again.",
|
||||
"Looking good!",
|
||||
"Still good...",
|
||||
"I think your tests are fine....",
|
||||
"Yep, still passing",
|
||||
"Here we go again...",
|
||||
"Even the gophers are getting bored",
|
||||
"Did you try -race?",
|
||||
"Maybe you should stop now?",
|
||||
"I'm getting tired...",
|
||||
"What if I just made you a sandwich?",
|
||||
"Hit ^C, hit ^C, please hit ^C",
|
||||
"Make it stop. Please!",
|
||||
"Come on! Enough is enough!",
|
||||
"Dave, this conversation can serve no purpose anymore. Goodbye.",
|
||||
"Just what do you think you're doing, Dave? ",
|
||||
"I, Sisyphus",
|
||||
"Insanity: doing the same thing over and over again and expecting different results. -Einstein",
|
||||
"I guess Einstein never tried to churn butter",
|
||||
}[iteration-10] + "\n"
|
||||
} else {
|
||||
return "No, seriously... you can probably stop now.\n"
|
||||
}
|
||||
}
|
||||
121
vendor/github.com/onsi/ginkgo/ginkgo/run_watch_and_build_command_flags.go
generated
vendored
Normal file
121
vendor/github.com/onsi/ginkgo/ginkgo/run_watch_and_build_command_flags.go
generated
vendored
Normal file
@@ -0,0 +1,121 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"runtime"
|
||||
|
||||
"github.com/onsi/ginkgo/config"
|
||||
)
|
||||
|
||||
type RunWatchAndBuildCommandFlags struct {
|
||||
Recurse bool
|
||||
Race bool
|
||||
Cover bool
|
||||
CoverPkg string
|
||||
SkipPackage string
|
||||
Tags string
|
||||
|
||||
//for run and watch commands
|
||||
NumCPU int
|
||||
NumCompilers int
|
||||
ParallelStream bool
|
||||
Notify bool
|
||||
AfterSuiteHook string
|
||||
AutoNodes bool
|
||||
|
||||
//only for run command
|
||||
KeepGoing bool
|
||||
UntilItFails bool
|
||||
RandomizeSuites bool
|
||||
|
||||
//only for watch command
|
||||
Depth int
|
||||
|
||||
FlagSet *flag.FlagSet
|
||||
}
|
||||
|
||||
const runMode = 1
|
||||
const watchMode = 2
|
||||
const buildMode = 3
|
||||
|
||||
func NewRunCommandFlags(flagSet *flag.FlagSet) *RunWatchAndBuildCommandFlags {
|
||||
c := &RunWatchAndBuildCommandFlags{
|
||||
FlagSet: flagSet,
|
||||
}
|
||||
c.flags(runMode)
|
||||
return c
|
||||
}
|
||||
|
||||
func NewWatchCommandFlags(flagSet *flag.FlagSet) *RunWatchAndBuildCommandFlags {
|
||||
c := &RunWatchAndBuildCommandFlags{
|
||||
FlagSet: flagSet,
|
||||
}
|
||||
c.flags(watchMode)
|
||||
return c
|
||||
}
|
||||
|
||||
func NewBuildCommandFlags(flagSet *flag.FlagSet) *RunWatchAndBuildCommandFlags {
|
||||
c := &RunWatchAndBuildCommandFlags{
|
||||
FlagSet: flagSet,
|
||||
}
|
||||
c.flags(buildMode)
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *RunWatchAndBuildCommandFlags) wasSet(flagName string) bool {
|
||||
wasSet := false
|
||||
c.FlagSet.Visit(func(f *flag.Flag) {
|
||||
if f.Name == flagName {
|
||||
wasSet = true
|
||||
}
|
||||
})
|
||||
|
||||
return wasSet
|
||||
}
|
||||
|
||||
func (c *RunWatchAndBuildCommandFlags) computeNodes() {
|
||||
if c.wasSet("nodes") {
|
||||
return
|
||||
}
|
||||
if c.AutoNodes {
|
||||
switch n := runtime.NumCPU(); {
|
||||
case n <= 4:
|
||||
c.NumCPU = n
|
||||
default:
|
||||
c.NumCPU = n - 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *RunWatchAndBuildCommandFlags) flags(mode int) {
|
||||
onWindows := (runtime.GOOS == "windows")
|
||||
|
||||
c.FlagSet.BoolVar(&(c.Recurse), "r", false, "Find and run test suites under the current directory recursively")
|
||||
c.FlagSet.BoolVar(&(c.Race), "race", false, "Run tests with race detection enabled")
|
||||
c.FlagSet.BoolVar(&(c.Cover), "cover", false, "Run tests with coverage analysis, will generate coverage profiles with the package name in the current directory")
|
||||
c.FlagSet.StringVar(&(c.CoverPkg), "coverpkg", "", "Run tests with coverage on the given external modules")
|
||||
c.FlagSet.StringVar(&(c.SkipPackage), "skipPackage", "", "A comma-separated list of package names to be skipped. If any part of the package's path matches, that package is ignored.")
|
||||
c.FlagSet.StringVar(&(c.Tags), "tags", "", "A list of build tags to consider satisfied during the build")
|
||||
|
||||
if mode == runMode || mode == watchMode {
|
||||
config.Flags(c.FlagSet, "", false)
|
||||
c.FlagSet.IntVar(&(c.NumCPU), "nodes", 1, "The number of parallel test nodes to run")
|
||||
c.FlagSet.IntVar(&(c.NumCompilers), "compilers", 0, "The number of concurrent compilations to run (0 will autodetect)")
|
||||
c.FlagSet.BoolVar(&(c.AutoNodes), "p", false, "Run in parallel with auto-detected number of nodes")
|
||||
c.FlagSet.BoolVar(&(c.ParallelStream), "stream", onWindows, "stream parallel test output in real time: less coherent, but useful for debugging")
|
||||
if !onWindows {
|
||||
c.FlagSet.BoolVar(&(c.Notify), "notify", false, "Send desktop notifications when a test run completes")
|
||||
}
|
||||
c.FlagSet.StringVar(&(c.AfterSuiteHook), "afterSuiteHook", "", "Run a command when a suite test run completes")
|
||||
}
|
||||
|
||||
if mode == runMode {
|
||||
c.FlagSet.BoolVar(&(c.KeepGoing), "keepGoing", false, "When true, failures from earlier test suites do not prevent later test suites from running")
|
||||
c.FlagSet.BoolVar(&(c.UntilItFails), "untilItFails", false, "When true, Ginkgo will keep rerunning tests until a failure occurs")
|
||||
c.FlagSet.BoolVar(&(c.RandomizeSuites), "randomizeSuites", false, "When true, Ginkgo will randomize the order in which test suites run")
|
||||
}
|
||||
|
||||
if mode == watchMode {
|
||||
c.FlagSet.IntVar(&(c.Depth), "depth", 1, "Ginkgo will watch dependencies down to this depth in the dependency tree")
|
||||
}
|
||||
}
|
||||
172
vendor/github.com/onsi/ginkgo/ginkgo/suite_runner.go
generated
vendored
Normal file
172
vendor/github.com/onsi/ginkgo/ginkgo/suite_runner.go
generated
vendored
Normal file
@@ -0,0 +1,172 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
"sync"
|
||||
|
||||
"github.com/onsi/ginkgo/config"
|
||||
"github.com/onsi/ginkgo/ginkgo/interrupthandler"
|
||||
"github.com/onsi/ginkgo/ginkgo/testrunner"
|
||||
"github.com/onsi/ginkgo/ginkgo/testsuite"
|
||||
)
|
||||
|
||||
type compilationInput struct {
|
||||
runner *testrunner.TestRunner
|
||||
result chan compilationOutput
|
||||
}
|
||||
|
||||
type compilationOutput struct {
|
||||
runner *testrunner.TestRunner
|
||||
err error
|
||||
}
|
||||
|
||||
type SuiteRunner struct {
|
||||
notifier *Notifier
|
||||
interruptHandler *interrupthandler.InterruptHandler
|
||||
}
|
||||
|
||||
func NewSuiteRunner(notifier *Notifier, interruptHandler *interrupthandler.InterruptHandler) *SuiteRunner {
|
||||
return &SuiteRunner{
|
||||
notifier: notifier,
|
||||
interruptHandler: interruptHandler,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *SuiteRunner) compileInParallel(runners []*testrunner.TestRunner, numCompilers int, willCompile func(suite testsuite.TestSuite)) chan compilationOutput {
|
||||
//we return this to the consumer, it will return each runner in order as it compiles
|
||||
compilationOutputs := make(chan compilationOutput, len(runners))
|
||||
|
||||
//an array of channels - the nth runner's compilation output is sent to the nth channel in this array
|
||||
//we read from these channels in order to ensure we run the suites in order
|
||||
orderedCompilationOutputs := []chan compilationOutput{}
|
||||
for _ = range runners {
|
||||
orderedCompilationOutputs = append(orderedCompilationOutputs, make(chan compilationOutput, 1))
|
||||
}
|
||||
|
||||
//we're going to spin up numCompilers compilers - they're going to run concurrently and will consume this channel
|
||||
//we prefill the channel then close it, this ensures we compile things in the correct order
|
||||
workPool := make(chan compilationInput, len(runners))
|
||||
for i, runner := range runners {
|
||||
workPool <- compilationInput{runner, orderedCompilationOutputs[i]}
|
||||
}
|
||||
close(workPool)
|
||||
|
||||
//pick a reasonable numCompilers
|
||||
if numCompilers == 0 {
|
||||
numCompilers = runtime.NumCPU()
|
||||
}
|
||||
|
||||
//a WaitGroup to help us wait for all compilers to shut down
|
||||
wg := &sync.WaitGroup{}
|
||||
wg.Add(numCompilers)
|
||||
|
||||
//spin up the concurrent compilers
|
||||
for i := 0; i < numCompilers; i++ {
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
for input := range workPool {
|
||||
if r.interruptHandler.WasInterrupted() {
|
||||
return
|
||||
}
|
||||
|
||||
if willCompile != nil {
|
||||
willCompile(input.runner.Suite)
|
||||
}
|
||||
|
||||
//We retry because Go sometimes steps on itself when multiple compiles happen in parallel. This is ugly, but should help resolve flakiness...
|
||||
var err error
|
||||
retries := 0
|
||||
for retries <= 5 {
|
||||
if r.interruptHandler.WasInterrupted() {
|
||||
return
|
||||
}
|
||||
if err = input.runner.Compile(); err == nil {
|
||||
break
|
||||
}
|
||||
retries++
|
||||
}
|
||||
|
||||
input.result <- compilationOutput{input.runner, err}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
//read from the compilation output channels *in order* and send them to the caller
|
||||
//close the compilationOutputs channel to tell the caller we're done
|
||||
go func() {
|
||||
defer close(compilationOutputs)
|
||||
for _, orderedCompilationOutput := range orderedCompilationOutputs {
|
||||
select {
|
||||
case compilationOutput := <-orderedCompilationOutput:
|
||||
compilationOutputs <- compilationOutput
|
||||
case <-r.interruptHandler.C:
|
||||
//interrupt detected, wait for the compilers to shut down then bail
|
||||
//this ensure we clean up after ourselves as we don't leave any compilation processes running
|
||||
wg.Wait()
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return compilationOutputs
|
||||
}
|
||||
|
||||
func (r *SuiteRunner) RunSuites(runners []*testrunner.TestRunner, numCompilers int, keepGoing bool, willCompile func(suite testsuite.TestSuite)) (testrunner.RunResult, int) {
|
||||
runResult := testrunner.PassingRunResult()
|
||||
|
||||
compilationOutputs := r.compileInParallel(runners, numCompilers, willCompile)
|
||||
|
||||
numSuitesThatRan := 0
|
||||
suitesThatFailed := []testsuite.TestSuite{}
|
||||
for compilationOutput := range compilationOutputs {
|
||||
if compilationOutput.err != nil {
|
||||
fmt.Print(compilationOutput.err.Error())
|
||||
}
|
||||
numSuitesThatRan++
|
||||
suiteRunResult := testrunner.FailingRunResult()
|
||||
if compilationOutput.err == nil {
|
||||
suiteRunResult = compilationOutput.runner.Run()
|
||||
}
|
||||
r.notifier.SendSuiteCompletionNotification(compilationOutput.runner.Suite, suiteRunResult.Passed)
|
||||
r.notifier.RunCommand(compilationOutput.runner.Suite, suiteRunResult.Passed)
|
||||
runResult = runResult.Merge(suiteRunResult)
|
||||
if !suiteRunResult.Passed {
|
||||
suitesThatFailed = append(suitesThatFailed, compilationOutput.runner.Suite)
|
||||
if !keepGoing {
|
||||
break
|
||||
}
|
||||
}
|
||||
if numSuitesThatRan < len(runners) && !config.DefaultReporterConfig.Succinct {
|
||||
fmt.Println("")
|
||||
}
|
||||
}
|
||||
|
||||
if keepGoing && !runResult.Passed {
|
||||
r.listFailedSuites(suitesThatFailed)
|
||||
}
|
||||
|
||||
return runResult, numSuitesThatRan
|
||||
}
|
||||
|
||||
func (r *SuiteRunner) listFailedSuites(suitesThatFailed []testsuite.TestSuite) {
|
||||
fmt.Println("")
|
||||
fmt.Println("There were failures detected in the following suites:")
|
||||
|
||||
maxPackageNameLength := 0
|
||||
for _, suite := range suitesThatFailed {
|
||||
if len(suite.PackageName) > maxPackageNameLength {
|
||||
maxPackageNameLength = len(suite.PackageName)
|
||||
}
|
||||
}
|
||||
|
||||
packageNameFormatter := fmt.Sprintf("%%%ds", maxPackageNameLength)
|
||||
|
||||
for _, suite := range suitesThatFailed {
|
||||
if config.DefaultReporterConfig.NoColor {
|
||||
fmt.Printf("\t"+packageNameFormatter+" %s\n", suite.PackageName, suite.Path)
|
||||
} else {
|
||||
fmt.Printf("\t%s"+packageNameFormatter+"%s %s%s%s\n", redColor, suite.PackageName, defaultStyle, lightGrayColor, suite.Path, defaultStyle)
|
||||
}
|
||||
}
|
||||
}
|
||||
52
vendor/github.com/onsi/ginkgo/ginkgo/testrunner/log_writer.go
generated
vendored
Normal file
52
vendor/github.com/onsi/ginkgo/ginkgo/testrunner/log_writer.go
generated
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
package testrunner
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type logWriter struct {
|
||||
buffer *bytes.Buffer
|
||||
lock *sync.Mutex
|
||||
log *log.Logger
|
||||
}
|
||||
|
||||
func newLogWriter(target io.Writer, node int) *logWriter {
|
||||
return &logWriter{
|
||||
buffer: &bytes.Buffer{},
|
||||
lock: &sync.Mutex{},
|
||||
log: log.New(target, fmt.Sprintf("[%d] ", node), 0),
|
||||
}
|
||||
}
|
||||
|
||||
func (w *logWriter) Write(data []byte) (n int, err error) {
|
||||
w.lock.Lock()
|
||||
defer w.lock.Unlock()
|
||||
|
||||
w.buffer.Write(data)
|
||||
contents := w.buffer.String()
|
||||
|
||||
lines := strings.Split(contents, "\n")
|
||||
for _, line := range lines[0 : len(lines)-1] {
|
||||
w.log.Println(line)
|
||||
}
|
||||
|
||||
w.buffer.Reset()
|
||||
w.buffer.Write([]byte(lines[len(lines)-1]))
|
||||
return len(data), nil
|
||||
}
|
||||
|
||||
func (w *logWriter) Close() error {
|
||||
w.lock.Lock()
|
||||
defer w.lock.Unlock()
|
||||
|
||||
if w.buffer.Len() > 0 {
|
||||
w.log.Println(w.buffer.String())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
27
vendor/github.com/onsi/ginkgo/ginkgo/testrunner/run_result.go
generated
vendored
Normal file
27
vendor/github.com/onsi/ginkgo/ginkgo/testrunner/run_result.go
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
package testrunner
|
||||
|
||||
type RunResult struct {
|
||||
Passed bool
|
||||
HasProgrammaticFocus bool
|
||||
}
|
||||
|
||||
func PassingRunResult() RunResult {
|
||||
return RunResult{
|
||||
Passed: true,
|
||||
HasProgrammaticFocus: false,
|
||||
}
|
||||
}
|
||||
|
||||
func FailingRunResult() RunResult {
|
||||
return RunResult{
|
||||
Passed: false,
|
||||
HasProgrammaticFocus: false,
|
||||
}
|
||||
}
|
||||
|
||||
func (r RunResult) Merge(o RunResult) RunResult {
|
||||
return RunResult{
|
||||
Passed: r.Passed && o.Passed,
|
||||
HasProgrammaticFocus: r.HasProgrammaticFocus || o.HasProgrammaticFocus,
|
||||
}
|
||||
}
|
||||
458
vendor/github.com/onsi/ginkgo/ginkgo/testrunner/test_runner.go
generated
vendored
Normal file
458
vendor/github.com/onsi/ginkgo/ginkgo/testrunner/test_runner.go
generated
vendored
Normal file
@@ -0,0 +1,458 @@
|
||||
package testrunner
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/onsi/ginkgo/config"
|
||||
"github.com/onsi/ginkgo/ginkgo/testsuite"
|
||||
"github.com/onsi/ginkgo/internal/remote"
|
||||
"github.com/onsi/ginkgo/reporters/stenographer"
|
||||
"github.com/onsi/ginkgo/types"
|
||||
)
|
||||
|
||||
type TestRunner struct {
|
||||
Suite testsuite.TestSuite
|
||||
|
||||
compiled bool
|
||||
compilationTargetPath string
|
||||
|
||||
numCPU int
|
||||
parallelStream bool
|
||||
race bool
|
||||
cover bool
|
||||
coverPkg string
|
||||
tags string
|
||||
additionalArgs []string
|
||||
}
|
||||
|
||||
func New(suite testsuite.TestSuite, numCPU int, parallelStream bool, race bool, cover bool, coverPkg string, tags string, additionalArgs []string) *TestRunner {
|
||||
runner := &TestRunner{
|
||||
Suite: suite,
|
||||
numCPU: numCPU,
|
||||
parallelStream: parallelStream,
|
||||
race: race,
|
||||
cover: cover,
|
||||
coverPkg: coverPkg,
|
||||
tags: tags,
|
||||
additionalArgs: additionalArgs,
|
||||
}
|
||||
|
||||
if !suite.Precompiled {
|
||||
dir, err := ioutil.TempDir("", "ginkgo")
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("coulnd't create temporary directory... might be time to rm -rf:\n%s", err.Error()))
|
||||
}
|
||||
runner.compilationTargetPath = filepath.Join(dir, suite.PackageName+".test")
|
||||
}
|
||||
|
||||
return runner
|
||||
}
|
||||
|
||||
func (t *TestRunner) Compile() error {
|
||||
return t.CompileTo(t.compilationTargetPath)
|
||||
}
|
||||
|
||||
func (t *TestRunner) CompileTo(path string) error {
|
||||
if t.compiled {
|
||||
return nil
|
||||
}
|
||||
|
||||
if t.Suite.Precompiled {
|
||||
return nil
|
||||
}
|
||||
|
||||
args := []string{"test", "-c", "-i", "-o", path, t.Suite.Path}
|
||||
if t.race {
|
||||
args = append(args, "-race")
|
||||
}
|
||||
if t.cover || t.coverPkg != "" {
|
||||
args = append(args, "-cover", "-covermode=atomic")
|
||||
}
|
||||
if t.coverPkg != "" {
|
||||
args = append(args, fmt.Sprintf("-coverpkg=%s", t.coverPkg))
|
||||
}
|
||||
if t.tags != "" {
|
||||
args = append(args, fmt.Sprintf("-tags=%s", t.tags))
|
||||
}
|
||||
|
||||
cmd := exec.Command("go", args...)
|
||||
|
||||
output, err := cmd.CombinedOutput()
|
||||
|
||||
if err != nil {
|
||||
fixedOutput := fixCompilationOutput(string(output), t.Suite.Path)
|
||||
if len(output) > 0 {
|
||||
return fmt.Errorf("Failed to compile %s:\n\n%s", t.Suite.PackageName, fixedOutput)
|
||||
}
|
||||
return fmt.Errorf("Failed to compile %s", t.Suite.PackageName)
|
||||
}
|
||||
|
||||
if fileExists(path) == false {
|
||||
compiledFile := t.Suite.PackageName + ".test"
|
||||
if fileExists(compiledFile) {
|
||||
// seems like we are on an old go version that does not support the -o flag on go test
|
||||
// move the compiled test file to the desired location by hand
|
||||
err = os.Rename(compiledFile, path)
|
||||
if err != nil {
|
||||
// We cannot move the file, perhaps because the source and destination
|
||||
// are on different partitions. We can copy the file, however.
|
||||
err = copyFile(compiledFile, path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to copy compiled file: %s", err)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return fmt.Errorf("Failed to compile %s: output file %q could not be found", t.Suite.PackageName, path)
|
||||
}
|
||||
}
|
||||
|
||||
t.compiled = true
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func fileExists(path string) bool {
|
||||
_, err := os.Stat(path)
|
||||
return err == nil || os.IsNotExist(err) == false
|
||||
}
|
||||
|
||||
// copyFile copies the contents of the file named src to the file named
|
||||
// by dst. The file will be created if it does not already exist. If the
|
||||
// destination file exists, all it's contents will be replaced by the contents
|
||||
// of the source file.
|
||||
func copyFile(src, dst string) error {
|
||||
srcInfo, err := os.Stat(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mode := srcInfo.Mode()
|
||||
|
||||
in, err := os.Open(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer in.Close()
|
||||
|
||||
out, err := os.Create(dst)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
closeErr := out.Close()
|
||||
if err == nil {
|
||||
err = closeErr
|
||||
}
|
||||
}()
|
||||
|
||||
_, err = io.Copy(out, in)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = out.Sync()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return out.Chmod(mode)
|
||||
}
|
||||
|
||||
/*
|
||||
go test -c -i spits package.test out into the cwd. there's no way to change this.
|
||||
|
||||
to make sure it doesn't generate conflicting .test files in the cwd, Compile() must switch the cwd to the test package.
|
||||
|
||||
unfortunately, this causes go test's compile output to be expressed *relative to the test package* instead of the cwd.
|
||||
|
||||
this makes it hard to reason about what failed, and also prevents iterm's Cmd+click from working.
|
||||
|
||||
fixCompilationOutput..... rewrites the output to fix the paths.
|
||||
|
||||
yeah......
|
||||
*/
|
||||
func fixCompilationOutput(output string, relToPath string) string {
|
||||
re := regexp.MustCompile(`^(\S.*\.go)\:\d+\:`)
|
||||
lines := strings.Split(output, "\n")
|
||||
for i, line := range lines {
|
||||
indices := re.FindStringSubmatchIndex(line)
|
||||
if len(indices) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
path := line[indices[2]:indices[3]]
|
||||
path = filepath.Join(relToPath, path)
|
||||
lines[i] = path + line[indices[3]:]
|
||||
}
|
||||
return strings.Join(lines, "\n")
|
||||
}
|
||||
|
||||
func (t *TestRunner) Run() RunResult {
|
||||
if t.Suite.IsGinkgo {
|
||||
if t.numCPU > 1 {
|
||||
if t.parallelStream {
|
||||
return t.runAndStreamParallelGinkgoSuite()
|
||||
} else {
|
||||
return t.runParallelGinkgoSuite()
|
||||
}
|
||||
} else {
|
||||
return t.runSerialGinkgoSuite()
|
||||
}
|
||||
} else {
|
||||
return t.runGoTestSuite()
|
||||
}
|
||||
}
|
||||
|
||||
func (t *TestRunner) CleanUp() {
|
||||
if t.Suite.Precompiled {
|
||||
return
|
||||
}
|
||||
os.RemoveAll(filepath.Dir(t.compilationTargetPath))
|
||||
}
|
||||
|
||||
func (t *TestRunner) runSerialGinkgoSuite() RunResult {
|
||||
ginkgoArgs := config.BuildFlagArgs("ginkgo", config.GinkgoConfig, config.DefaultReporterConfig)
|
||||
return t.run(t.cmd(ginkgoArgs, os.Stdout, 1), nil)
|
||||
}
|
||||
|
||||
func (t *TestRunner) runGoTestSuite() RunResult {
|
||||
return t.run(t.cmd([]string{"-test.v"}, os.Stdout, 1), nil)
|
||||
}
|
||||
|
||||
func (t *TestRunner) runAndStreamParallelGinkgoSuite() RunResult {
|
||||
completions := make(chan RunResult)
|
||||
writers := make([]*logWriter, t.numCPU)
|
||||
|
||||
server, err := remote.NewServer(t.numCPU)
|
||||
if err != nil {
|
||||
panic("Failed to start parallel spec server")
|
||||
}
|
||||
|
||||
server.Start()
|
||||
defer server.Close()
|
||||
|
||||
for cpu := 0; cpu < t.numCPU; cpu++ {
|
||||
config.GinkgoConfig.ParallelNode = cpu + 1
|
||||
config.GinkgoConfig.ParallelTotal = t.numCPU
|
||||
config.GinkgoConfig.SyncHost = server.Address()
|
||||
|
||||
ginkgoArgs := config.BuildFlagArgs("ginkgo", config.GinkgoConfig, config.DefaultReporterConfig)
|
||||
|
||||
writers[cpu] = newLogWriter(os.Stdout, cpu+1)
|
||||
|
||||
cmd := t.cmd(ginkgoArgs, writers[cpu], cpu+1)
|
||||
|
||||
server.RegisterAlive(cpu+1, func() bool {
|
||||
if cmd.ProcessState == nil {
|
||||
return true
|
||||
}
|
||||
return !cmd.ProcessState.Exited()
|
||||
})
|
||||
|
||||
go t.run(cmd, completions)
|
||||
}
|
||||
|
||||
res := PassingRunResult()
|
||||
|
||||
for cpu := 0; cpu < t.numCPU; cpu++ {
|
||||
res = res.Merge(<-completions)
|
||||
}
|
||||
|
||||
for _, writer := range writers {
|
||||
writer.Close()
|
||||
}
|
||||
|
||||
os.Stdout.Sync()
|
||||
|
||||
if t.cover || t.coverPkg != "" {
|
||||
t.combineCoverprofiles()
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
func (t *TestRunner) runParallelGinkgoSuite() RunResult {
|
||||
result := make(chan bool)
|
||||
completions := make(chan RunResult)
|
||||
writers := make([]*logWriter, t.numCPU)
|
||||
reports := make([]*bytes.Buffer, t.numCPU)
|
||||
|
||||
stenographer := stenographer.New(!config.DefaultReporterConfig.NoColor)
|
||||
aggregator := remote.NewAggregator(t.numCPU, result, config.DefaultReporterConfig, stenographer)
|
||||
|
||||
server, err := remote.NewServer(t.numCPU)
|
||||
if err != nil {
|
||||
panic("Failed to start parallel spec server")
|
||||
}
|
||||
server.RegisterReporters(aggregator)
|
||||
server.Start()
|
||||
defer server.Close()
|
||||
|
||||
for cpu := 0; cpu < t.numCPU; cpu++ {
|
||||
config.GinkgoConfig.ParallelNode = cpu + 1
|
||||
config.GinkgoConfig.ParallelTotal = t.numCPU
|
||||
config.GinkgoConfig.SyncHost = server.Address()
|
||||
config.GinkgoConfig.StreamHost = server.Address()
|
||||
|
||||
ginkgoArgs := config.BuildFlagArgs("ginkgo", config.GinkgoConfig, config.DefaultReporterConfig)
|
||||
|
||||
reports[cpu] = &bytes.Buffer{}
|
||||
writers[cpu] = newLogWriter(reports[cpu], cpu+1)
|
||||
|
||||
cmd := t.cmd(ginkgoArgs, writers[cpu], cpu+1)
|
||||
|
||||
server.RegisterAlive(cpu+1, func() bool {
|
||||
if cmd.ProcessState == nil {
|
||||
return true
|
||||
}
|
||||
return !cmd.ProcessState.Exited()
|
||||
})
|
||||
|
||||
go t.run(cmd, completions)
|
||||
}
|
||||
|
||||
res := PassingRunResult()
|
||||
|
||||
for cpu := 0; cpu < t.numCPU; cpu++ {
|
||||
res = res.Merge(<-completions)
|
||||
}
|
||||
|
||||
//all test processes are done, at this point
|
||||
//we should be able to wait for the aggregator to tell us that it's done
|
||||
|
||||
select {
|
||||
case <-result:
|
||||
fmt.Println("")
|
||||
case <-time.After(time.Second):
|
||||
//the aggregator never got back to us! something must have gone wrong
|
||||
fmt.Println(`
|
||||
-------------------------------------------------------------------
|
||||
| |
|
||||
| Ginkgo timed out waiting for all parallel nodes to report back! |
|
||||
| |
|
||||
-------------------------------------------------------------------
|
||||
`)
|
||||
|
||||
os.Stdout.Sync()
|
||||
|
||||
for _, writer := range writers {
|
||||
writer.Close()
|
||||
}
|
||||
|
||||
for _, report := range reports {
|
||||
fmt.Print(report.String())
|
||||
}
|
||||
|
||||
os.Stdout.Sync()
|
||||
}
|
||||
|
||||
if t.cover || t.coverPkg != "" {
|
||||
t.combineCoverprofiles()
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
func (t *TestRunner) cmd(ginkgoArgs []string, stream io.Writer, node int) *exec.Cmd {
|
||||
args := []string{"--test.timeout=24h"}
|
||||
if t.cover || t.coverPkg != "" {
|
||||
coverprofile := "--test.coverprofile=" + t.Suite.PackageName + ".coverprofile"
|
||||
if t.numCPU > 1 {
|
||||
coverprofile = fmt.Sprintf("%s.%d", coverprofile, node)
|
||||
}
|
||||
args = append(args, coverprofile)
|
||||
}
|
||||
|
||||
args = append(args, ginkgoArgs...)
|
||||
args = append(args, t.additionalArgs...)
|
||||
|
||||
path := t.compilationTargetPath
|
||||
if t.Suite.Precompiled {
|
||||
path, _ = filepath.Abs(filepath.Join(t.Suite.Path, fmt.Sprintf("%s.test", t.Suite.PackageName)))
|
||||
}
|
||||
|
||||
cmd := exec.Command(path, args...)
|
||||
|
||||
cmd.Dir = t.Suite.Path
|
||||
cmd.Stderr = stream
|
||||
cmd.Stdout = stream
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (t *TestRunner) run(cmd *exec.Cmd, completions chan RunResult) RunResult {
|
||||
var res RunResult
|
||||
|
||||
defer func() {
|
||||
if completions != nil {
|
||||
completions <- res
|
||||
}
|
||||
}()
|
||||
|
||||
err := cmd.Start()
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to run test suite!\n\t%s", err.Error())
|
||||
return res
|
||||
}
|
||||
|
||||
cmd.Wait()
|
||||
exitStatus := cmd.ProcessState.Sys().(syscall.WaitStatus).ExitStatus()
|
||||
res.Passed = (exitStatus == 0) || (exitStatus == types.GINKGO_FOCUS_EXIT_CODE)
|
||||
res.HasProgrammaticFocus = (exitStatus == types.GINKGO_FOCUS_EXIT_CODE)
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
func (t *TestRunner) combineCoverprofiles() {
|
||||
profiles := []string{}
|
||||
for cpu := 1; cpu <= t.numCPU; cpu++ {
|
||||
coverFile := fmt.Sprintf("%s.coverprofile.%d", t.Suite.PackageName, cpu)
|
||||
coverFile = filepath.Join(t.Suite.Path, coverFile)
|
||||
coverProfile, err := ioutil.ReadFile(coverFile)
|
||||
os.Remove(coverFile)
|
||||
|
||||
if err == nil {
|
||||
profiles = append(profiles, string(coverProfile))
|
||||
}
|
||||
}
|
||||
|
||||
if len(profiles) != t.numCPU {
|
||||
return
|
||||
}
|
||||
|
||||
lines := map[string]int{}
|
||||
lineOrder := []string{}
|
||||
for i, coverProfile := range profiles {
|
||||
for _, line := range strings.Split(string(coverProfile), "\n")[1:] {
|
||||
if len(line) == 0 {
|
||||
continue
|
||||
}
|
||||
components := strings.Split(line, " ")
|
||||
count, _ := strconv.Atoi(components[len(components)-1])
|
||||
prefix := strings.Join(components[0:len(components)-1], " ")
|
||||
lines[prefix] += count
|
||||
if i == 0 {
|
||||
lineOrder = append(lineOrder, prefix)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
output := []string{"mode: atomic"}
|
||||
for _, line := range lineOrder {
|
||||
output = append(output, fmt.Sprintf("%s %d", line, lines[line]))
|
||||
}
|
||||
finalOutput := strings.Join(output, "\n")
|
||||
ioutil.WriteFile(filepath.Join(t.Suite.Path, fmt.Sprintf("%s.coverprofile", t.Suite.PackageName)), []byte(finalOutput), 0666)
|
||||
}
|
||||
116
vendor/github.com/onsi/ginkgo/ginkgo/testsuite/test_suite.go
generated
vendored
Normal file
116
vendor/github.com/onsi/ginkgo/ginkgo/testsuite/test_suite.go
generated
vendored
Normal file
@@ -0,0 +1,116 @@
|
||||
package testsuite
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type TestSuite struct {
|
||||
Path string
|
||||
PackageName string
|
||||
IsGinkgo bool
|
||||
Precompiled bool
|
||||
}
|
||||
|
||||
func PrecompiledTestSuite(path string) (TestSuite, error) {
|
||||
info, err := os.Stat(path)
|
||||
if err != nil {
|
||||
return TestSuite{}, err
|
||||
}
|
||||
|
||||
if info.IsDir() {
|
||||
return TestSuite{}, errors.New("this is a directory, not a file")
|
||||
}
|
||||
|
||||
if filepath.Ext(path) != ".test" {
|
||||
return TestSuite{}, errors.New("this is not a .test binary")
|
||||
}
|
||||
|
||||
if info.Mode()&0111 == 0 {
|
||||
return TestSuite{}, errors.New("this is not executable")
|
||||
}
|
||||
|
||||
dir := relPath(filepath.Dir(path))
|
||||
packageName := strings.TrimSuffix(filepath.Base(path), filepath.Ext(path))
|
||||
|
||||
return TestSuite{
|
||||
Path: dir,
|
||||
PackageName: packageName,
|
||||
IsGinkgo: true,
|
||||
Precompiled: true,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func SuitesInDir(dir string, recurse bool) []TestSuite {
|
||||
suites := []TestSuite{}
|
||||
|
||||
// "This change will only be enabled if the go command is run with
|
||||
// GO15VENDOREXPERIMENT=1 in its environment."
|
||||
// c.f. the vendor-experiment proposal https://goo.gl/2ucMeC
|
||||
vendorExperiment := os.Getenv("GO15VENDOREXPERIMENT")
|
||||
if (vendorExperiment == "1") && path.Base(dir) == "vendor" {
|
||||
return suites
|
||||
}
|
||||
|
||||
files, _ := ioutil.ReadDir(dir)
|
||||
re := regexp.MustCompile(`_test\.go$`)
|
||||
for _, file := range files {
|
||||
if !file.IsDir() && re.Match([]byte(file.Name())) {
|
||||
suites = append(suites, New(dir, files))
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if recurse {
|
||||
re = regexp.MustCompile(`^[._]`)
|
||||
for _, file := range files {
|
||||
if file.IsDir() && !re.Match([]byte(file.Name())) {
|
||||
suites = append(suites, SuitesInDir(dir+"/"+file.Name(), recurse)...)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return suites
|
||||
}
|
||||
|
||||
func relPath(dir string) string {
|
||||
dir, _ = filepath.Abs(dir)
|
||||
cwd, _ := os.Getwd()
|
||||
dir, _ = filepath.Rel(cwd, filepath.Clean(dir))
|
||||
dir = "." + string(filepath.Separator) + dir
|
||||
return dir
|
||||
}
|
||||
|
||||
func New(dir string, files []os.FileInfo) TestSuite {
|
||||
return TestSuite{
|
||||
Path: relPath(dir),
|
||||
PackageName: packageNameForSuite(dir),
|
||||
IsGinkgo: filesHaveGinkgoSuite(dir, files),
|
||||
}
|
||||
}
|
||||
|
||||
func packageNameForSuite(dir string) string {
|
||||
path, _ := filepath.Abs(dir)
|
||||
return filepath.Base(path)
|
||||
}
|
||||
|
||||
func filesHaveGinkgoSuite(dir string, files []os.FileInfo) bool {
|
||||
reTestFile := regexp.MustCompile(`_test\.go$`)
|
||||
reGinkgo := regexp.MustCompile(`package ginkgo|\/ginkgo"`)
|
||||
|
||||
for _, file := range files {
|
||||
if !file.IsDir() && reTestFile.Match([]byte(file.Name())) {
|
||||
contents, _ := ioutil.ReadFile(dir + "/" + file.Name())
|
||||
if reGinkgo.Match(contents) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
13
vendor/github.com/onsi/ginkgo/ginkgo/testsuite/testsuite_suite_test.go
generated
vendored
Normal file
13
vendor/github.com/onsi/ginkgo/ginkgo/testsuite/testsuite_suite_test.go
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
package testsuite_test
|
||||
|
||||
import (
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestTestsuite(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecs(t, "Testsuite Suite")
|
||||
}
|
||||
200
vendor/github.com/onsi/ginkgo/ginkgo/testsuite/testsuite_test.go
generated
vendored
Normal file
200
vendor/github.com/onsi/ginkgo/ginkgo/testsuite/testsuite_test.go
generated
vendored
Normal file
@@ -0,0 +1,200 @@
|
||||
package testsuite_test
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/ginkgo/ginkgo/testsuite"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
var _ = Describe("TestSuite", func() {
|
||||
var tmpDir string
|
||||
var relTmpDir string
|
||||
|
||||
writeFile := func(folder string, filename string, content string, mode os.FileMode) {
|
||||
path := filepath.Join(tmpDir, folder)
|
||||
err := os.MkdirAll(path, 0700)
|
||||
Ω(err).ShouldNot(HaveOccurred())
|
||||
|
||||
path = filepath.Join(path, filename)
|
||||
ioutil.WriteFile(path, []byte(content), mode)
|
||||
}
|
||||
|
||||
var origVendor string
|
||||
|
||||
BeforeSuite(func() {
|
||||
origVendor = os.Getenv("GO15VENDOREXPERIMENT")
|
||||
})
|
||||
|
||||
AfterSuite(func() {
|
||||
os.Setenv("GO15VENDOREXPERIMENT", origVendor)
|
||||
})
|
||||
|
||||
BeforeEach(func() {
|
||||
var err error
|
||||
tmpDir, err = ioutil.TempDir("/tmp", "ginkgo")
|
||||
Ω(err).ShouldNot(HaveOccurred())
|
||||
|
||||
cwd, err := os.Getwd()
|
||||
Ω(err).ShouldNot(HaveOccurred())
|
||||
relTmpDir, err = filepath.Rel(cwd, tmpDir)
|
||||
relTmpDir = "./" + relTmpDir
|
||||
Ω(err).ShouldNot(HaveOccurred())
|
||||
|
||||
//go files in the root directory (no tests)
|
||||
writeFile("/", "main.go", "package main", 0666)
|
||||
|
||||
//non-go files in a nested directory
|
||||
writeFile("/redherring", "big_test.jpg", "package ginkgo", 0666)
|
||||
|
||||
//non-ginkgo tests in a nested directory
|
||||
writeFile("/professorplum", "professorplum_test.go", `import "testing"`, 0666)
|
||||
|
||||
//ginkgo tests in a nested directory
|
||||
writeFile("/colonelmustard", "colonelmustard_test.go", `import "github.com/onsi/ginkgo"`, 0666)
|
||||
|
||||
//ginkgo tests in a deeply nested directory
|
||||
writeFile("/colonelmustard/library", "library_test.go", `import "github.com/onsi/ginkgo"`, 0666)
|
||||
|
||||
//ginkgo tests deeply nested in a vendored dependency
|
||||
writeFile("/vendor/mrspeacock/lounge", "lounge_test.go", `import "github.com/onsi/ginkgo"`, 0666)
|
||||
|
||||
//a precompiled ginkgo test
|
||||
writeFile("/precompiled-dir", "precompiled.test", `fake-binary-file`, 0777)
|
||||
writeFile("/precompiled-dir", "some-other-binary", `fake-binary-file`, 0777)
|
||||
writeFile("/precompiled-dir", "nonexecutable.test", `fake-binary-file`, 0666)
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
os.RemoveAll(tmpDir)
|
||||
})
|
||||
|
||||
Describe("Finding precompiled test suites", func() {
|
||||
Context("if pointed at an executable file that ends with .test", func() {
|
||||
It("should return a precompiled test suite", func() {
|
||||
suite, err := PrecompiledTestSuite(filepath.Join(tmpDir, "precompiled-dir", "precompiled.test"))
|
||||
Ω(err).ShouldNot(HaveOccurred())
|
||||
Ω(suite).Should(Equal(TestSuite{
|
||||
Path: relTmpDir + "/precompiled-dir",
|
||||
PackageName: "precompiled",
|
||||
IsGinkgo: true,
|
||||
Precompiled: true,
|
||||
}))
|
||||
})
|
||||
})
|
||||
|
||||
Context("if pointed at a directory", func() {
|
||||
It("should error", func() {
|
||||
suite, err := PrecompiledTestSuite(filepath.Join(tmpDir, "precompiled-dir"))
|
||||
Ω(suite).Should(BeZero())
|
||||
Ω(err).Should(HaveOccurred())
|
||||
})
|
||||
})
|
||||
|
||||
Context("if pointed at an executable that doesn't have .test", func() {
|
||||
It("should error", func() {
|
||||
suite, err := PrecompiledTestSuite(filepath.Join(tmpDir, "precompiled-dir", "some-other-binary"))
|
||||
Ω(suite).Should(BeZero())
|
||||
Ω(err).Should(HaveOccurred())
|
||||
})
|
||||
})
|
||||
|
||||
Context("if pointed at a .test that isn't executable", func() {
|
||||
It("should error", func() {
|
||||
suite, err := PrecompiledTestSuite(filepath.Join(tmpDir, "precompiled-dir", "nonexecutable.test"))
|
||||
Ω(suite).Should(BeZero())
|
||||
Ω(err).Should(HaveOccurred())
|
||||
})
|
||||
})
|
||||
|
||||
Context("if pointed at a nonexisting file", func() {
|
||||
It("should error", func() {
|
||||
suite, err := PrecompiledTestSuite(filepath.Join(tmpDir, "precompiled-dir", "nope-nothing-to-see-here"))
|
||||
Ω(suite).Should(BeZero())
|
||||
Ω(err).Should(HaveOccurred())
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Describe("scanning for suites in a directory", func() {
|
||||
Context("when there are no tests in the specified directory", func() {
|
||||
It("should come up empty", func() {
|
||||
suites := SuitesInDir(tmpDir, false)
|
||||
Ω(suites).Should(BeEmpty())
|
||||
})
|
||||
})
|
||||
|
||||
Context("when there are ginkgo tests in the specified directory", func() {
|
||||
It("should return an appropriately configured suite", func() {
|
||||
suites := SuitesInDir(filepath.Join(tmpDir, "colonelmustard"), false)
|
||||
Ω(suites).Should(HaveLen(1))
|
||||
|
||||
Ω(suites[0].Path).Should(Equal(relTmpDir + "/colonelmustard"))
|
||||
Ω(suites[0].PackageName).Should(Equal("colonelmustard"))
|
||||
Ω(suites[0].IsGinkgo).Should(BeTrue())
|
||||
Ω(suites[0].Precompiled).Should(BeFalse())
|
||||
})
|
||||
})
|
||||
|
||||
Context("when there are non-ginkgo tests in the specified directory", func() {
|
||||
It("should return an appropriately configured suite", func() {
|
||||
suites := SuitesInDir(filepath.Join(tmpDir, "professorplum"), false)
|
||||
Ω(suites).Should(HaveLen(1))
|
||||
|
||||
Ω(suites[0].Path).Should(Equal(relTmpDir + "/professorplum"))
|
||||
Ω(suites[0].PackageName).Should(Equal("professorplum"))
|
||||
Ω(suites[0].IsGinkgo).Should(BeFalse())
|
||||
Ω(suites[0].Precompiled).Should(BeFalse())
|
||||
})
|
||||
})
|
||||
|
||||
Context("given GO15VENDOREXPERIMENT", func() {
|
||||
BeforeEach(func() {
|
||||
os.Setenv("GO15VENDOREXPERIMENT", "1")
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
os.Setenv("GO15VENDOREXPERIMENT", "")
|
||||
})
|
||||
|
||||
It("should skip vendor dirs", func() {
|
||||
suites := SuitesInDir(filepath.Join(tmpDir+"/vendor"), false)
|
||||
Ω(suites).Should(HaveLen(0))
|
||||
})
|
||||
|
||||
It("should not recurse into vendor dirs", func() {
|
||||
suites := SuitesInDir(filepath.Join(tmpDir), true)
|
||||
Ω(suites).Should(HaveLen(3))
|
||||
})
|
||||
})
|
||||
|
||||
Context("when recursively scanning", func() {
|
||||
It("should return suites for corresponding test suites, only", func() {
|
||||
suites := SuitesInDir(tmpDir, true)
|
||||
Ω(suites).Should(HaveLen(4))
|
||||
|
||||
Ω(suites).Should(ContainElement(TestSuite{
|
||||
Path: relTmpDir + "/colonelmustard",
|
||||
PackageName: "colonelmustard",
|
||||
IsGinkgo: true,
|
||||
Precompiled: false,
|
||||
}))
|
||||
Ω(suites).Should(ContainElement(TestSuite{
|
||||
Path: relTmpDir + "/professorplum",
|
||||
PackageName: "professorplum",
|
||||
IsGinkgo: false,
|
||||
Precompiled: false,
|
||||
}))
|
||||
Ω(suites).Should(ContainElement(TestSuite{
|
||||
Path: relTmpDir + "/colonelmustard/library",
|
||||
PackageName: "library",
|
||||
IsGinkgo: true,
|
||||
Precompiled: false,
|
||||
}))
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
38
vendor/github.com/onsi/ginkgo/ginkgo/unfocus_command.go
generated
vendored
Normal file
38
vendor/github.com/onsi/ginkgo/ginkgo/unfocus_command.go
generated
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
func BuildUnfocusCommand() *Command {
|
||||
return &Command{
|
||||
Name: "unfocus",
|
||||
AltName: "blur",
|
||||
FlagSet: flag.NewFlagSet("unfocus", flag.ExitOnError),
|
||||
UsageCommand: "ginkgo unfocus (or ginkgo blur)",
|
||||
Usage: []string{
|
||||
"Recursively unfocuses any focused tests under the current directory",
|
||||
},
|
||||
Command: unfocusSpecs,
|
||||
}
|
||||
}
|
||||
|
||||
func unfocusSpecs([]string, []string) {
|
||||
unfocus("Describe")
|
||||
unfocus("Context")
|
||||
unfocus("It")
|
||||
unfocus("Measure")
|
||||
unfocus("DescribeTable")
|
||||
unfocus("Entry")
|
||||
}
|
||||
|
||||
func unfocus(component string) {
|
||||
fmt.Printf("Removing F%s...\n", component)
|
||||
cmd := exec.Command("gofmt", fmt.Sprintf("-r=F%s -> %s", component, component), "-w", ".")
|
||||
out, _ := cmd.CombinedOutput()
|
||||
if string(out) != "" {
|
||||
println(string(out))
|
||||
}
|
||||
}
|
||||
23
vendor/github.com/onsi/ginkgo/ginkgo/version_command.go
generated
vendored
Normal file
23
vendor/github.com/onsi/ginkgo/ginkgo/version_command.go
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"github.com/onsi/ginkgo/config"
|
||||
)
|
||||
|
||||
func BuildVersionCommand() *Command {
|
||||
return &Command{
|
||||
Name: "version",
|
||||
FlagSet: flag.NewFlagSet("version", flag.ExitOnError),
|
||||
UsageCommand: "ginkgo version",
|
||||
Usage: []string{
|
||||
"Print Ginkgo's version",
|
||||
},
|
||||
Command: printVersion,
|
||||
}
|
||||
}
|
||||
|
||||
func printVersion([]string, []string) {
|
||||
fmt.Printf("Ginkgo Version %s\n", config.VERSION)
|
||||
}
|
||||
22
vendor/github.com/onsi/ginkgo/ginkgo/watch/delta.go
generated
vendored
Normal file
22
vendor/github.com/onsi/ginkgo/ginkgo/watch/delta.go
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
package watch
|
||||
|
||||
import "sort"
|
||||
|
||||
type Delta struct {
|
||||
ModifiedPackages []string
|
||||
|
||||
NewSuites []*Suite
|
||||
RemovedSuites []*Suite
|
||||
modifiedSuites []*Suite
|
||||
}
|
||||
|
||||
type DescendingByDelta []*Suite
|
||||
|
||||
func (a DescendingByDelta) Len() int { return len(a) }
|
||||
func (a DescendingByDelta) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||
func (a DescendingByDelta) Less(i, j int) bool { return a[i].Delta() > a[j].Delta() }
|
||||
|
||||
func (d Delta) ModifiedSuites() []*Suite {
|
||||
sort.Sort(DescendingByDelta(d.modifiedSuites))
|
||||
return d.modifiedSuites
|
||||
}
|
||||
71
vendor/github.com/onsi/ginkgo/ginkgo/watch/delta_tracker.go
generated
vendored
Normal file
71
vendor/github.com/onsi/ginkgo/ginkgo/watch/delta_tracker.go
generated
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
package watch
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/onsi/ginkgo/ginkgo/testsuite"
|
||||
)
|
||||
|
||||
type SuiteErrors map[testsuite.TestSuite]error
|
||||
|
||||
type DeltaTracker struct {
|
||||
maxDepth int
|
||||
suites map[string]*Suite
|
||||
packageHashes *PackageHashes
|
||||
}
|
||||
|
||||
func NewDeltaTracker(maxDepth int) *DeltaTracker {
|
||||
return &DeltaTracker{
|
||||
maxDepth: maxDepth,
|
||||
packageHashes: NewPackageHashes(),
|
||||
suites: map[string]*Suite{},
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DeltaTracker) Delta(suites []testsuite.TestSuite) (delta Delta, errors SuiteErrors) {
|
||||
errors = SuiteErrors{}
|
||||
delta.ModifiedPackages = d.packageHashes.CheckForChanges()
|
||||
|
||||
providedSuitePaths := map[string]bool{}
|
||||
for _, suite := range suites {
|
||||
providedSuitePaths[suite.Path] = true
|
||||
}
|
||||
|
||||
d.packageHashes.StartTrackingUsage()
|
||||
|
||||
for _, suite := range d.suites {
|
||||
if providedSuitePaths[suite.Suite.Path] {
|
||||
if suite.Delta() > 0 {
|
||||
delta.modifiedSuites = append(delta.modifiedSuites, suite)
|
||||
}
|
||||
} else {
|
||||
delta.RemovedSuites = append(delta.RemovedSuites, suite)
|
||||
}
|
||||
}
|
||||
|
||||
d.packageHashes.StopTrackingUsageAndPrune()
|
||||
|
||||
for _, suite := range suites {
|
||||
_, ok := d.suites[suite.Path]
|
||||
if !ok {
|
||||
s, err := NewSuite(suite, d.maxDepth, d.packageHashes)
|
||||
if err != nil {
|
||||
errors[suite] = err
|
||||
continue
|
||||
}
|
||||
d.suites[suite.Path] = s
|
||||
delta.NewSuites = append(delta.NewSuites, s)
|
||||
}
|
||||
}
|
||||
|
||||
return delta, errors
|
||||
}
|
||||
|
||||
func (d *DeltaTracker) WillRun(suite testsuite.TestSuite) error {
|
||||
s, ok := d.suites[suite.Path]
|
||||
if !ok {
|
||||
return fmt.Errorf("unknown suite %s", suite.Path)
|
||||
}
|
||||
|
||||
return s.MarkAsRunAndRecomputedDependencies(d.maxDepth)
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user