mirror of
https://github.com/DNSCrypt/dnscrypt-proxy.git
synced 2025-03-03 18:04:40 +01:00
Add unit tests for sources.go
Tests cover most of the cache and download related code paths and specify the expected result of various starting states and external failure modes. Where the current code's behaviour doesn't match a test's expectations, the test is disabled and annotated with a TODO until it can be fixed. Added dependency on `github.com/powerman/check` and ran `go mod vendor`.
This commit is contained in:
parent
503bfb877b
commit
af0629856c
61 changed files with 7200 additions and 0 deletions
3
.gitattributes
vendored
3
.gitattributes
vendored
|
@ -1,3 +1,6 @@
|
|||
* text=auto
|
||||
*.go text diff=golang
|
||||
*.bat text eol=crlf
|
||||
|
||||
# Ensure test fixtures don't get mangled
|
||||
**/testdata/** -text
|
||||
|
|
398
dnscrypt-proxy/sources_test.go
Normal file
398
dnscrypt-proxy/sources_test.go
Normal file
|
@ -0,0 +1,398 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/jedisct1/go-minisign"
|
||||
"github.com/powerman/check"
|
||||
)
|
||||
|
||||
type SourceFixture struct {
|
||||
suffix string
|
||||
content []byte
|
||||
length string // HTTP Content-Length header
|
||||
perms os.FileMode
|
||||
mtime time.Time
|
||||
}
|
||||
type SourceTestState uint8
|
||||
|
||||
const (
|
||||
TestStateCorrect SourceTestState = iota // valid files
|
||||
TestStateExpired // modification time of files set in distant past (cache only)
|
||||
TestStatePartial // incomplete files
|
||||
TestStatePartialSig // incomplete .minisig
|
||||
TestStateMissing // non-existant files
|
||||
TestStateMissingSig // non-existant .minisig
|
||||
TestStateReadErr // I/O error on reading files (download only)
|
||||
TestStateReadSigErr // I/O error on reading .minisig (download only)
|
||||
TestStateOpenErr // I/O error on opening files
|
||||
TestStateOpenSigErr // I/O error on opening .minisig
|
||||
TestStatePathErr // unparseable path to files (download only)
|
||||
)
|
||||
|
||||
type SourceTestData struct {
|
||||
n int // subtest counter
|
||||
xTransport *XTransport
|
||||
key *minisign.PublicKey
|
||||
keyStr, tempDir string
|
||||
sources []string
|
||||
fixtures map[SourceTestState]map[string]SourceFixture
|
||||
timeNow, timeOld, timeUpd time.Time
|
||||
server *httptest.Server
|
||||
reqActual, reqExpect map[string]uint
|
||||
cacheTests map[string]SourceTestState
|
||||
downloadTests map[string][]SourceTestState
|
||||
}
|
||||
|
||||
type SourceTestExpect struct {
|
||||
success, download bool
|
||||
in, cachePath string
|
||||
cache []SourceFixture
|
||||
refresh time.Time
|
||||
urls []string
|
||||
prefetchUrls []URLToPrefetch
|
||||
Source *Source
|
||||
err string
|
||||
}
|
||||
|
||||
func readFixture(t *testing.T, name string) []byte {
|
||||
bin, err := ioutil.ReadFile(filepath.Join("testdata", name))
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to read test fixture %s: %v", name, err)
|
||||
}
|
||||
return bin
|
||||
}
|
||||
|
||||
func writeSourceCache(t *testing.T, basePath string, fixtures []SourceFixture) {
|
||||
for _, f := range fixtures {
|
||||
if f.content == nil {
|
||||
continue
|
||||
}
|
||||
path := basePath + f.suffix
|
||||
perms := f.perms
|
||||
if perms == 0 {
|
||||
perms = 0644
|
||||
}
|
||||
if err := ioutil.WriteFile(path, f.content, perms); err != nil {
|
||||
t.Fatalf("Unable to write cache file %s: %v", path, err)
|
||||
}
|
||||
if !f.mtime.IsZero() {
|
||||
if err := os.Chtimes(path, f.mtime, f.mtime); err != nil {
|
||||
t.Fatalf("Unable to touch cache file %s to %v: %v", path, f.mtime, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func checkSourceCache(c *check.C, basePath string, fixtures []SourceFixture) {
|
||||
for _, f := range fixtures {
|
||||
path := basePath + f.suffix
|
||||
_ = os.Chmod(path, 0644) // don't worry if this fails, reading it will catch the same problem
|
||||
got, err := ioutil.ReadFile(path)
|
||||
c.DeepEqual(got, f.content, "Cache file '%s', err %v", path, err)
|
||||
}
|
||||
}
|
||||
|
||||
func loadSnakeoil(t *testing.T, d *SourceTestData) {
|
||||
key, err := minisign.NewPublicKeyFromFile(filepath.Join("testdata", "snakeoil.pub"))
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to load snakeoil key: %v", err)
|
||||
}
|
||||
d.keyStr = string(bytes.SplitN(readFixture(t, "snakeoil.pub"), []byte("\n"), 2)[1])
|
||||
d.key = &key
|
||||
}
|
||||
|
||||
func loadTestSourceNames(t *testing.T, d *SourceTestData) {
|
||||
files, err := ioutil.ReadDir(filepath.Join("testdata", "sources"))
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to load list of test sources: %v", err)
|
||||
}
|
||||
for _, file := range files {
|
||||
if !file.IsDir() && strings.HasSuffix(file.Name(), ".minisig") {
|
||||
d.sources = append(d.sources, strings.TrimSuffix(file.Name(), ".minisig"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func loadFixtures(t *testing.T, d *SourceTestData) {
|
||||
d.fixtures = map[SourceTestState]map[string]SourceFixture{TestStateCorrect: map[string]SourceFixture{}}
|
||||
for _, source := range d.sources {
|
||||
for _, suffix := range [...]string{"", ".minisig"} {
|
||||
file := source + suffix
|
||||
d.fixtures[TestStateCorrect][file] = SourceFixture{suffix: suffix, content: readFixture(t, filepath.Join("sources", file))}
|
||||
for _, state := range [...]SourceTestState{TestStateExpired, TestStatePartial, TestStateReadErr, TestStateOpenErr,
|
||||
TestStatePartialSig, TestStateMissingSig, TestStateReadSigErr, TestStateOpenSigErr} {
|
||||
if _, ok := d.fixtures[state]; !ok {
|
||||
d.fixtures[state] = map[string]SourceFixture{}
|
||||
}
|
||||
switch state {
|
||||
case TestStatePartialSig, TestStateMissingSig, TestStateReadSigErr, TestStateOpenSigErr:
|
||||
if suffix != ".minisig" {
|
||||
d.fixtures[state][file] = d.fixtures[TestStateCorrect][file]
|
||||
continue
|
||||
}
|
||||
}
|
||||
f := SourceFixture{suffix: suffix}
|
||||
switch state {
|
||||
case TestStateExpired:
|
||||
f.content, f.mtime = d.fixtures[TestStateCorrect][file].content, d.timeOld
|
||||
case TestStatePartial, TestStatePartialSig:
|
||||
f.content = d.fixtures[TestStateCorrect][file].content[:1]
|
||||
case TestStateReadErr, TestStateReadSigErr:
|
||||
f.content, f.length = []byte{}, "1"
|
||||
case TestStateOpenErr, TestStateOpenSigErr:
|
||||
f.content, f.perms = d.fixtures[TestStateCorrect][file].content[:1], 0200
|
||||
}
|
||||
d.fixtures[state][file] = f
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func makeTempDir(t *testing.T, d *SourceTestData) {
|
||||
name, err := ioutil.TempDir("", "sources_test.go."+t.Name())
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to create temporary directory: %v", err)
|
||||
}
|
||||
d.tempDir = name
|
||||
}
|
||||
|
||||
func makeTestServer(t *testing.T, d *SourceTestData) {
|
||||
d.reqActual, d.reqExpect = map[string]uint{}, map[string]uint{}
|
||||
d.server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
var data []byte = nil
|
||||
d.reqActual[r.URL.Path]++
|
||||
pathParts := strings.SplitN(strings.TrimPrefix(r.URL.Path, "/"), "/", 2)
|
||||
state, _ := strconv.ParseUint(pathParts[0], 10, 8)
|
||||
if fixture, ok := d.fixtures[SourceTestState(state)][pathParts[1]]; ok {
|
||||
if len(fixture.length) > 0 {
|
||||
w.Header().Set("Content-Length", fixture.length) // client will return unexpected EOF
|
||||
}
|
||||
data = fixture.content
|
||||
}
|
||||
if data != nil {
|
||||
if _, err := w.Write(data); err != nil {
|
||||
t.Logf("Error writing HTTP response for request [%s]: %v", r.URL.Path, err)
|
||||
}
|
||||
} else {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
func checkTestServer(c *check.C, d *SourceTestData) {
|
||||
c.DeepEqual(d.reqActual, d.reqExpect, "Unexpected HTTP request log")
|
||||
d.reqActual, d.reqExpect = map[string]uint{}, map[string]uint{}
|
||||
}
|
||||
|
||||
func setupSourceTest(t *testing.T) (func(), *SourceTestData) {
|
||||
d := &SourceTestData{n: -1, xTransport: NewXTransport()}
|
||||
d.cacheTests = map[string]SourceTestState{ // determines cache files written to disk before each call
|
||||
//"correct": TestStateCorrect, // TODO: millisecond-level timing inconsistencies should not fail tests
|
||||
//"expired": TestStateExpired, // TODO: an expired cache should be used if no download passes signature verification
|
||||
//"partial": TestStatePartial, // TODO: failed signature verification should not abort before trying additional URLs and should not destroy the cache
|
||||
//"partial-sig": TestStatePartialSig, // TODO: failed signature verification should not abort before trying additional URLs and should not destroy the cache
|
||||
//"missing": TestStateMissing, // TODO: millisecond-level timing inconsistencies should not fail tests
|
||||
//"missing-sig": TestStateMissingSig, // TODO: a cache without a signature should cause an attempt to download both files (not just the signature), failed signature verification should not destroy the cache
|
||||
//"open-err": TestStateOpenErr, // TODO: millisecond-level timing inconsistencies should not fail tests
|
||||
//"open-sig-err": TestStateOpenSigErr, // TODO: a cache without a signature should cause an attempt to download both files (not just the signature), failed signature verification should not destroy the cache
|
||||
}
|
||||
d.downloadTests = map[string][]SourceTestState{ // determines the list of URLs passed in each call and how they will respond
|
||||
"correct": {TestStateCorrect},
|
||||
//"partial": {TestStatePartial}, // TODO: failed signature verification should not count as successfully prefetched and should not destroy the cache
|
||||
//"partial-sig": {TestStatePartialSig}, // TODO: failed signature verification should not count as successfully prefetched and should not destroy the cache
|
||||
//"missing": {TestStateMissing}, // TODO: a list download failure should not cause an attempt to download the signature for that URL
|
||||
//"missing-sig": {TestStateMissingSig}, // TODO: failed signature verification should not count as successfully prefetched and should not destroy the cache
|
||||
//"read-err": {TestStateReadErr}, // TODO: a list download failure should not cause an attempt to download the signature for that URL
|
||||
//"read-sig-err": {TestStateReadSigErr}, // TODO: failed signature verification should not count as successfully prefetched and should not destroy the cache
|
||||
"open-err": {TestStateOpenErr},
|
||||
//"open-sig-err": {TestStateOpenSigErr}, // TODO: failed signature verification should not count as successfully prefetched and should not destroy the cache
|
||||
//"path-err": {TestStatePathErr}, // TODO: invalid URLs should not be included in the prefetch list
|
||||
//"partial,correct": {TestStatePartial, TestStateCorrect}, // TODO: all URLs should be included in the prefetch list, failed signature verification should not abort before trying additional URLs and should not destroy the cache
|
||||
//"partial-sig,correct": {TestStatePartialSig, TestStateCorrect}, // TODO: all URLs should be included in the prefetch list, failed signature verification should not abort before trying additional URLs and should not destroy the cache
|
||||
//"missing,correct": {TestStateMissing, TestStateCorrect}, // TODO: all URLs should be included in the prefetch list
|
||||
//"missing-sig,correct": {TestStateMissingSig, TestStateCorrect}, // TODO: all URLs should be included in the prefetch list, failed signature verification should not abort before trying additional URLs and should not destroy the cache
|
||||
//"read-err,correct": {TestStateReadErr, TestStateCorrect}, // TODO: all URLs should be included in the prefetch list
|
||||
//"read-sig-err,correct": {TestStateReadSigErr, TestStateCorrect}, // TODO: all URLs should be included in the prefetch list, failed signature verification should not abort before trying additional URLs and should not destroy the cache
|
||||
//"open-err,correct": {TestStateOpenErr, TestStateCorrect}, // TODO: all URLs should be included in the prefetch list
|
||||
//"open-sig-err,correct": {TestStateOpenSigErr, TestStateCorrect}, // TODO: all URLs should be included in the prefetch list, failed signature verification should not abort before trying additional URLs and should not destroy the cache
|
||||
//"path-err,correct": {TestStatePathErr, TestStateCorrect}, // TODO: invalid URLs should not be included in the prefetch list
|
||||
"no-urls": {},
|
||||
}
|
||||
d.xTransport.rebuildTransport()
|
||||
d.timeNow = time.Now()
|
||||
d.timeOld = d.timeNow.Add(MinSourcesUpdateDelay * -4)
|
||||
d.timeUpd = d.timeNow.Add(MinSourcesUpdateDelay)
|
||||
makeTempDir(t, d)
|
||||
makeTestServer(t, d)
|
||||
loadSnakeoil(t, d)
|
||||
loadTestSourceNames(t, d)
|
||||
loadFixtures(t, d)
|
||||
return func() {
|
||||
os.RemoveAll(d.tempDir)
|
||||
d.server.Close()
|
||||
}, d
|
||||
}
|
||||
|
||||
func prepSourceTestCache(t *testing.T, d *SourceTestData, e *SourceTestExpect, source string, state SourceTestState) {
|
||||
e.cache = []SourceFixture{d.fixtures[state][source], d.fixtures[state][source+".minisig"]}
|
||||
switch state {
|
||||
case TestStateCorrect:
|
||||
e.in, e.success, e.refresh = string(e.cache[0].content), true, d.timeUpd
|
||||
case TestStateExpired:
|
||||
e.in, e.success = string(e.cache[0].content), true
|
||||
case TestStatePartial, TestStatePartialSig:
|
||||
e.err = "signature"
|
||||
case TestStateMissing, TestStateMissingSig:
|
||||
e.err = "not present"
|
||||
case TestStateOpenErr, TestStateOpenSigErr:
|
||||
e.err = os.ErrPermission.Error()
|
||||
}
|
||||
writeSourceCache(t, e.cachePath, e.cache)
|
||||
}
|
||||
|
||||
func prepSourceTestDownload(t *testing.T, d *SourceTestData, e *SourceTestExpect, source string, downloadTest []SourceTestState) {
|
||||
if len(downloadTest) > 0 {
|
||||
for _, state := range downloadTest {
|
||||
path := "/" + strconv.FormatUint(uint64(state), 10) + "/" + source
|
||||
if !e.success || !e.refresh.After(d.timeNow) {
|
||||
switch state {
|
||||
case TestStateCorrect:
|
||||
e.cache = []SourceFixture{d.fixtures[state][source], d.fixtures[state][source+".minisig"]}
|
||||
e.in, e.success, e.err, e.refresh = string(e.cache[0].content), true, "", d.timeUpd
|
||||
fallthrough
|
||||
case TestStateMissingSig, TestStatePartial, TestStatePartialSig, TestStateReadSigErr:
|
||||
d.reqExpect[path+".minisig"]++
|
||||
fallthrough
|
||||
case TestStateMissing, TestStateReadErr:
|
||||
d.reqExpect[path]++
|
||||
}
|
||||
}
|
||||
switch state {
|
||||
case TestStateMissing, TestStateMissingSig:
|
||||
e.err = "404 Not Found"
|
||||
case TestStatePartial, TestStatePartialSig:
|
||||
e.err = "signature"
|
||||
case TestStateReadErr, TestStateReadSigErr:
|
||||
e.err = "unexpected EOF"
|
||||
case TestStateOpenErr, TestStateOpenSigErr:
|
||||
path = "00000" + path // high numeric port is parsed but then fails to connect
|
||||
e.err = "invalid port"
|
||||
case TestStatePathErr:
|
||||
path = "..." + path // non-numeric port fails URL parsing
|
||||
e.err = "parse"
|
||||
}
|
||||
e.urls = append(e.urls, d.server.URL+path)
|
||||
if state != TestStatePathErr {
|
||||
e.prefetchUrls = append(e.prefetchUrls, URLToPrefetch{d.server.URL + path, e.cachePath, e.refresh})
|
||||
e.prefetchUrls = append(e.prefetchUrls, URLToPrefetch{d.server.URL + path + ".minisig", e.cachePath + ".minisig", e.refresh})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func setupSourceTestCase(t *testing.T, d *SourceTestData, i int,
|
||||
cacheTest *SourceTestState, downloadTest []SourceTestState) (id string, e *SourceTestExpect) {
|
||||
id = strconv.Itoa(d.n) + "-" + strconv.Itoa(i)
|
||||
e = &SourceTestExpect{
|
||||
cachePath: filepath.Join(d.tempDir, id),
|
||||
refresh: d.timeNow,
|
||||
urls: []string{},
|
||||
prefetchUrls: []URLToPrefetch{},
|
||||
}
|
||||
if cacheTest != nil {
|
||||
prepSourceTestCache(t, d, e, d.sources[i], *cacheTest)
|
||||
i = (i + 1) % len(d.sources) // make the cached and downloaded fixtures different
|
||||
}
|
||||
prepSourceTestDownload(t, d, e, d.sources[i], downloadTest)
|
||||
e.Source = &Source{e.urls, SourceFormatV2, e.in}
|
||||
return
|
||||
}
|
||||
|
||||
func TestNewSource(t *testing.T) {
|
||||
teardown, d := setupSourceTest(t)
|
||||
defer teardown()
|
||||
doTest := func(t *testing.T, e *SourceTestExpect, got Source, urls []URLToPrefetch, err error) {
|
||||
c := check.T(t)
|
||||
if len(e.err) > 0 {
|
||||
c.Match(err, e.err, "Unexpected error")
|
||||
} else {
|
||||
c.Nil(err, "Unexpected error")
|
||||
}
|
||||
c.DeepEqual(got, *e.Source, "Unexpected return Source")
|
||||
c.DeepEqual(urls, e.prefetchUrls, "Unexpected return prefetch URLs")
|
||||
checkTestServer(c, d)
|
||||
checkSourceCache(c, e.cachePath, e.cache)
|
||||
}
|
||||
d.n++
|
||||
for _, tt := range []struct {
|
||||
name, key, v string
|
||||
refresh time.Duration
|
||||
e *SourceTestExpect
|
||||
}{
|
||||
{"old format", d.keyStr, "v1", MinSourcesUpdateDelay * 3, &SourceTestExpect{
|
||||
Source: &Source{urls: nil}, prefetchUrls: []URLToPrefetch{}, err: "Unsupported source format"}},
|
||||
{"invalid public key", "", "v2", MinSourcesUpdateDelay * 3, &SourceTestExpect{
|
||||
Source: &Source{urls: nil}, prefetchUrls: []URLToPrefetch{}, err: "Invalid encoded public key"}},
|
||||
} {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, urls, err := NewSource(d.xTransport, tt.e.urls, tt.key, tt.e.cachePath, tt.v, tt.refresh)
|
||||
doTest(t, tt.e, got, urls, err)
|
||||
})
|
||||
}
|
||||
for cacheTestName, cacheTest := range d.cacheTests {
|
||||
for downloadTestName, downloadTest := range d.downloadTests {
|
||||
d.n++
|
||||
for i := range d.sources {
|
||||
id, e := setupSourceTestCase(t, d, i, &cacheTest, downloadTest)
|
||||
t.Run("cache "+cacheTestName+", download "+downloadTestName+"/"+id, func(t *testing.T) {
|
||||
got, urls, err := NewSource(d.xTransport, e.urls, d.keyStr, e.cachePath, "v2", MinSourcesUpdateDelay*3)
|
||||
doTest(t, e, got, urls, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrefetchSourceURL(t *testing.T) {
|
||||
teardown, d := setupSourceTest(t)
|
||||
defer teardown()
|
||||
doTest := func(t *testing.T, expects []*SourceTestExpect) {
|
||||
c := check.T(t)
|
||||
for _, e := range expects {
|
||||
for _, url := range e.urls {
|
||||
for _, suffix := range []string{"", ".minisig"} {
|
||||
pf := &URLToPrefetch{url + suffix, e.cachePath + suffix, d.timeOld}
|
||||
PrefetchSourceURL(d.xTransport, pf)
|
||||
c.InDelta(pf.when, e.refresh, time.Second, "Unexpected prefetch refresh time")
|
||||
}
|
||||
}
|
||||
}
|
||||
checkTestServer(c, d)
|
||||
for _, e := range expects {
|
||||
checkSourceCache(c, e.cachePath, e.cache)
|
||||
}
|
||||
}
|
||||
for downloadTestName, downloadTest := range d.downloadTests {
|
||||
d.n++
|
||||
expects := []*SourceTestExpect{}
|
||||
for i := range d.sources {
|
||||
_, e := setupSourceTestCase(t, d, i, nil, downloadTest)
|
||||
expects = append(expects, e)
|
||||
}
|
||||
t.Run("download "+downloadTestName, func(t *testing.T) {
|
||||
doTest(t, expects)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMain(m *testing.M) { check.TestMain(m) }
|
2
dnscrypt-proxy/testdata/snakeoil.key
vendored
Normal file
2
dnscrypt-proxy/testdata/snakeoil.key
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
untrusted comment: minisign encrypted secret key
|
||||
RWRTY0IyhB6jk0BvB9YnhvXfyLkbKj5CSoL4jZtHw7qidhhNW3sAAAACAAAAAAAAAEAAAAAAus/LOCZVIOcL/da0ldQytjgjKmd5D/C84VitiDP0Fe9zWfTxaGs6SXn4tk0ZYmh2CmHydeQstzpA8cTinbFZgb+gxMHu205cqwHr1wUMtBpKhgdwqPh1EWfokCzrGSCj2Vjxq/Fr0bQ=
|
2
dnscrypt-proxy/testdata/snakeoil.pub
vendored
Normal file
2
dnscrypt-proxy/testdata/snakeoil.pub
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
untrusted comment: minisign public key 956181C0EA8BF961
|
||||
RWRh+YvqwIFhlRUdNGI/u+EDEmFip5BjgHY/z1yQkmRUcLfeIDWBCxnP
|
2
dnscrypt-proxy/testdata/sources/empty.md
vendored
Normal file
2
dnscrypt-proxy/testdata/sources/empty.md
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
# Minimal example of an empty source list
|
||||
|
4
dnscrypt-proxy/testdata/sources/empty.md.minisig
vendored
Normal file
4
dnscrypt-proxy/testdata/sources/empty.md.minisig
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
untrusted comment: signature from minisign secret key
|
||||
RWRh+YvqwIFhlQu0PHH9BqqxLYYwmhA4TFMNmfj11kkEYZvu8atPqYVEEyEnLIZLUhx+MYHYoiYrRI88LSpoKxMVSr9jIcaGaAI=
|
||||
trusted comment: timestamp:1571786369 file:empty.md
|
||||
LTu6UMxo0VXIESDN3/vpM/A04L2RfJkorJNPhXMcYUFl4lRH2x7DSyqCK0k6L3fS7u5iz0+SPLxVx17oqw4aBg==
|
6
dnscrypt-proxy/testdata/sources/minimal_relay.md
vendored
Normal file
6
dnscrypt-proxy/testdata/sources/minimal_relay.md
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
# Minimal example of a relay source list
|
||||
|
||||
## Blank address
|
||||
|
||||
sdns://gQA
|
||||
|
4
dnscrypt-proxy/testdata/sources/minimal_relay.md.minisig
vendored
Normal file
4
dnscrypt-proxy/testdata/sources/minimal_relay.md.minisig
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
untrusted comment: signature from minisign secret key
|
||||
RWRh+YvqwIFhlR1wqxlVLJr+fgAwghbLrJXEykPosJGxWusxKQKOplS5R6XF1cJe5wl09YOZM/4k7UqX0yFq6Lm5M3aMTRYqlAA=
|
||||
trusted comment: timestamp:1571786378 file:minimal_relay.md
|
||||
IKt/hsPsemnqAM64sFOqr1/LZmyVqjNFH94I/QZ6gXZO8eHa4tJY3MInfbCkfZu7Sn2WgUB1GklzQ0ZwJO0kBg==
|
1
go.mod
1
go.mod
|
@ -19,6 +19,7 @@ require (
|
|||
github.com/k-sone/critbitgo v1.4.0
|
||||
github.com/kardianos/service v1.0.0
|
||||
github.com/miekg/dns v1.1.22
|
||||
github.com/powerman/check v1.2.1
|
||||
golang.org/x/crypto v0.0.0-20191029031824-8986dd9e96cf
|
||||
golang.org/x/net v0.0.0-20191101175033-0deb6923b6d9
|
||||
golang.org/x/sys v0.0.0-20191104094858-e8c54fb511f6
|
||||
|
|
21
go.sum
21
go.sum
|
@ -8,12 +8,17 @@ github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635 h1:52m0LGchQBBVqJRyY
|
|||
github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635/go.mod h1:lmLxL+FV291OopO93Bwf9fQLQeLyt33VJRUg5VJ30us=
|
||||
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf h1:iW4rZ826su+pqaw19uhpSCzhj44qo35pNgKFGqzDKkU=
|
||||
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dchest/safefile v0.0.0-20151022103144-855e8d98f185 h1:3T8ZyTDp5QxTx3NU48JVb2u+75xc040fofcBaN+6jPA=
|
||||
github.com/dchest/safefile v0.0.0-20151022103144-855e8d98f185/go.mod h1:cFRxtTwTOJkz2x3rQUNCYKWC93yP1VKjR8NUhqFxZNU=
|
||||
github.com/facebookgo/atomicfile v0.0.0-20151019160806-2de1f203e7d5 h1:BBso6MBKW8ncyZLv37o+KNyy0HrrHgfnOaGQC2qvN+A=
|
||||
github.com/facebookgo/atomicfile v0.0.0-20151019160806-2de1f203e7d5/go.mod h1:JpoxHjuQauoxiFMl1ie8Xc/7TfLuMZ5eOCONd1sUBHg=
|
||||
github.com/facebookgo/pidfile v0.0.0-20150612191647-f242e2999868 h1:KZ75X3ZCl6yy4jg9R1ziYoCZFDBRqildm+fGComWU7U=
|
||||
github.com/facebookgo/pidfile v0.0.0-20150612191647-f242e2999868/go.mod h1:3Hzo46xzfVpIdv4lJw7YBp9fUJ7HpUgbjH1fFDgy4qM=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c h1:7lF+Vz0LqiRidnzC1Oq86fpX1q/iEv2KJdrCtttYjT4=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/hashicorp/go-immutable-radix v1.1.0 h1:vN9wG1D6KG6YHRTWr8512cxGOVgTMEfgEdSj/hr8MPc=
|
||||
github.com/hashicorp/go-immutable-radix v1.1.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
||||
github.com/hashicorp/go-syslog v1.0.0 h1:KaodqZuhUoZereWVIYmpUgZysurB1kBLX2j0MwMrUAE=
|
||||
|
@ -33,18 +38,32 @@ github.com/jedisct1/go-minisign v0.0.0-20190909160543-45766022959e h1:UvSe12bq+U
|
|||
github.com/jedisct1/go-minisign v0.0.0-20190909160543-45766022959e/go.mod h1:G1CVv03EnqU1wYL2dFwXxW2An0az9JTl/ZsqXQeBlkU=
|
||||
github.com/jedisct1/xsecretbox v0.0.0-20190909160646-b731c21297f9 h1:nGfB2s9K0GyHuNkJmXkIjP+m7je6Q6gjirr+weAEtDo=
|
||||
github.com/jedisct1/xsecretbox v0.0.0-20190909160646-b731c21297f9/go.mod h1:MipBKo+gZlzpd1JXA1OliuwvtQizlFeu4aMAyTLh8bo=
|
||||
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/k-sone/critbitgo v1.4.0 h1:l71cTyBGeh6X5ATh6Fibgw3+rtNT80BA0uNNWgkPrbE=
|
||||
github.com/k-sone/critbitgo v1.4.0/go.mod h1:7E6pyoyADnFxlUBEKcnfS49b7SUAQGMK+OAp/UQvo0s=
|
||||
github.com/kardianos/service v1.0.0 h1:HgQS3mFfOlyntWX8Oke98JcJLqt1DBcHR4kxShpYef0=
|
||||
github.com/kardianos/service v1.0.0/go.mod h1:8CzDhVuCuugtsHyZoTvsOBuvonN/UDBvl0kH+BUxvbo=
|
||||
github.com/miekg/dns v1.1.22 h1:Jm64b3bO9kP43ddLjL2EY3Io6bmy1qGb9Xxz6TqS6rc=
|
||||
github.com/miekg/dns v1.1.22/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
|
||||
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/powerman/check v1.2.1 h1:x1io+fh3njxQEmbvONx5a9QWZXVwFP1BDPKNqK0uZAo=
|
||||
github.com/powerman/check v1.2.1/go.mod h1:IW+hYd9ihaKn7ri1+NGS8WWwrKdytbU8hgNC1wbcurc=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/assertions v0.0.0-20190401211740-f487f9de1cd3 h1:hBSHahWMEgzwRyS6dRpxY0XyjZsHyQ61s084wo5PJe0=
|
||||
github.com/smartystreets/assertions v0.0.0-20190401211740-f487f9de1cd3/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
|
||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190909091759-094676da4a83 h1:mgAKeshyNqWKdENOnQsg+8dRTwZFIwFaO3HNl52sweA=
|
||||
golang.org/x/crypto v0.0.0-20190909091759-094676da4a83/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
|
||||
golang.org/x/crypto v0.0.0-20191029031824-8986dd9e96cf h1:fnPsqIDRbCSgumaMCRpoIoF2s4qxv0xSSS0BVZUE/ss=
|
||||
golang.org/x/crypto v0.0.0-20191029031824-8986dd9e96cf/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
|
@ -59,6 +78,7 @@ golang.org/x/sys v0.0.0-20190909082730-f460065e899a h1:mIzbOulag9/gXacgxKlFVwpCO
|
|||
golang.org/x/sys v0.0.0-20190909082730-f460065e899a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191029155521-f43be2a4598c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191104094858-e8c54fb511f6 h1:ZJUmhYTp8GbGC0ViZRc2U+MIYQ8xx9MscsdXnclfIhw=
|
||||
golang.org/x/sys v0.0.0-20191104094858-e8c54fb511f6/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||
|
@ -66,6 +86,7 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
|||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
|
|
15
vendor/github.com/davecgh/go-spew/LICENSE
generated
vendored
Normal file
15
vendor/github.com/davecgh/go-spew/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,15 @@
|
|||
ISC License
|
||||
|
||||
Copyright (c) 2012-2016 Dave Collins <dave@davec.name>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
145
vendor/github.com/davecgh/go-spew/spew/bypass.go
generated
vendored
Normal file
145
vendor/github.com/davecgh/go-spew/spew/bypass.go
generated
vendored
Normal file
|
@ -0,0 +1,145 @@
|
|||
// Copyright (c) 2015-2016 Dave Collins <dave@davec.name>
|
||||
//
|
||||
// Permission to use, copy, modify, and distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice appear in all copies.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
// NOTE: Due to the following build constraints, this file will only be compiled
|
||||
// when the code is not running on Google App Engine, compiled by GopherJS, and
|
||||
// "-tags safe" is not added to the go build command line. The "disableunsafe"
|
||||
// tag is deprecated and thus should not be used.
|
||||
// Go versions prior to 1.4 are disabled because they use a different layout
|
||||
// for interfaces which make the implementation of unsafeReflectValue more complex.
|
||||
// +build !js,!appengine,!safe,!disableunsafe,go1.4
|
||||
|
||||
package spew
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
// UnsafeDisabled is a build-time constant which specifies whether or
|
||||
// not access to the unsafe package is available.
|
||||
UnsafeDisabled = false
|
||||
|
||||
// ptrSize is the size of a pointer on the current arch.
|
||||
ptrSize = unsafe.Sizeof((*byte)(nil))
|
||||
)
|
||||
|
||||
type flag uintptr
|
||||
|
||||
var (
|
||||
// flagRO indicates whether the value field of a reflect.Value
|
||||
// is read-only.
|
||||
flagRO flag
|
||||
|
||||
// flagAddr indicates whether the address of the reflect.Value's
|
||||
// value may be taken.
|
||||
flagAddr flag
|
||||
)
|
||||
|
||||
// flagKindMask holds the bits that make up the kind
|
||||
// part of the flags field. In all the supported versions,
|
||||
// it is in the lower 5 bits.
|
||||
const flagKindMask = flag(0x1f)
|
||||
|
||||
// Different versions of Go have used different
|
||||
// bit layouts for the flags type. This table
|
||||
// records the known combinations.
|
||||
var okFlags = []struct {
|
||||
ro, addr flag
|
||||
}{{
|
||||
// From Go 1.4 to 1.5
|
||||
ro: 1 << 5,
|
||||
addr: 1 << 7,
|
||||
}, {
|
||||
// Up to Go tip.
|
||||
ro: 1<<5 | 1<<6,
|
||||
addr: 1 << 8,
|
||||
}}
|
||||
|
||||
var flagValOffset = func() uintptr {
|
||||
field, ok := reflect.TypeOf(reflect.Value{}).FieldByName("flag")
|
||||
if !ok {
|
||||
panic("reflect.Value has no flag field")
|
||||
}
|
||||
return field.Offset
|
||||
}()
|
||||
|
||||
// flagField returns a pointer to the flag field of a reflect.Value.
|
||||
func flagField(v *reflect.Value) *flag {
|
||||
return (*flag)(unsafe.Pointer(uintptr(unsafe.Pointer(v)) + flagValOffset))
|
||||
}
|
||||
|
||||
// unsafeReflectValue converts the passed reflect.Value into a one that bypasses
|
||||
// the typical safety restrictions preventing access to unaddressable and
|
||||
// unexported data. It works by digging the raw pointer to the underlying
|
||||
// value out of the protected value and generating a new unprotected (unsafe)
|
||||
// reflect.Value to it.
|
||||
//
|
||||
// This allows us to check for implementations of the Stringer and error
|
||||
// interfaces to be used for pretty printing ordinarily unaddressable and
|
||||
// inaccessible values such as unexported struct fields.
|
||||
func unsafeReflectValue(v reflect.Value) reflect.Value {
|
||||
if !v.IsValid() || (v.CanInterface() && v.CanAddr()) {
|
||||
return v
|
||||
}
|
||||
flagFieldPtr := flagField(&v)
|
||||
*flagFieldPtr &^= flagRO
|
||||
*flagFieldPtr |= flagAddr
|
||||
return v
|
||||
}
|
||||
|
||||
// Sanity checks against future reflect package changes
|
||||
// to the type or semantics of the Value.flag field.
|
||||
func init() {
|
||||
field, ok := reflect.TypeOf(reflect.Value{}).FieldByName("flag")
|
||||
if !ok {
|
||||
panic("reflect.Value has no flag field")
|
||||
}
|
||||
if field.Type.Kind() != reflect.TypeOf(flag(0)).Kind() {
|
||||
panic("reflect.Value flag field has changed kind")
|
||||
}
|
||||
type t0 int
|
||||
var t struct {
|
||||
A t0
|
||||
// t0 will have flagEmbedRO set.
|
||||
t0
|
||||
// a will have flagStickyRO set
|
||||
a t0
|
||||
}
|
||||
vA := reflect.ValueOf(t).FieldByName("A")
|
||||
va := reflect.ValueOf(t).FieldByName("a")
|
||||
vt0 := reflect.ValueOf(t).FieldByName("t0")
|
||||
|
||||
// Infer flagRO from the difference between the flags
|
||||
// for the (otherwise identical) fields in t.
|
||||
flagPublic := *flagField(&vA)
|
||||
flagWithRO := *flagField(&va) | *flagField(&vt0)
|
||||
flagRO = flagPublic ^ flagWithRO
|
||||
|
||||
// Infer flagAddr from the difference between a value
|
||||
// taken from a pointer and not.
|
||||
vPtrA := reflect.ValueOf(&t).Elem().FieldByName("A")
|
||||
flagNoPtr := *flagField(&vA)
|
||||
flagPtr := *flagField(&vPtrA)
|
||||
flagAddr = flagNoPtr ^ flagPtr
|
||||
|
||||
// Check that the inferred flags tally with one of the known versions.
|
||||
for _, f := range okFlags {
|
||||
if flagRO == f.ro && flagAddr == f.addr {
|
||||
return
|
||||
}
|
||||
}
|
||||
panic("reflect.Value read-only flag has changed semantics")
|
||||
}
|
38
vendor/github.com/davecgh/go-spew/spew/bypasssafe.go
generated
vendored
Normal file
38
vendor/github.com/davecgh/go-spew/spew/bypasssafe.go
generated
vendored
Normal file
|
@ -0,0 +1,38 @@
|
|||
// Copyright (c) 2015-2016 Dave Collins <dave@davec.name>
|
||||
//
|
||||
// Permission to use, copy, modify, and distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice appear in all copies.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
// NOTE: Due to the following build constraints, this file will only be compiled
|
||||
// when the code is running on Google App Engine, compiled by GopherJS, or
|
||||
// "-tags safe" is added to the go build command line. The "disableunsafe"
|
||||
// tag is deprecated and thus should not be used.
|
||||
// +build js appengine safe disableunsafe !go1.4
|
||||
|
||||
package spew
|
||||
|
||||
import "reflect"
|
||||
|
||||
const (
|
||||
// UnsafeDisabled is a build-time constant which specifies whether or
|
||||
// not access to the unsafe package is available.
|
||||
UnsafeDisabled = true
|
||||
)
|
||||
|
||||
// unsafeReflectValue typically converts the passed reflect.Value into a one
|
||||
// that bypasses the typical safety restrictions preventing access to
|
||||
// unaddressable and unexported data. However, doing this relies on access to
|
||||
// the unsafe package. This is a stub version which simply returns the passed
|
||||
// reflect.Value when the unsafe package is not available.
|
||||
func unsafeReflectValue(v reflect.Value) reflect.Value {
|
||||
return v
|
||||
}
|
341
vendor/github.com/davecgh/go-spew/spew/common.go
generated
vendored
Normal file
341
vendor/github.com/davecgh/go-spew/spew/common.go
generated
vendored
Normal file
|
@ -0,0 +1,341 @@
|
|||
/*
|
||||
* Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
package spew
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// Some constants in the form of bytes to avoid string overhead. This mirrors
|
||||
// the technique used in the fmt package.
|
||||
var (
|
||||
panicBytes = []byte("(PANIC=")
|
||||
plusBytes = []byte("+")
|
||||
iBytes = []byte("i")
|
||||
trueBytes = []byte("true")
|
||||
falseBytes = []byte("false")
|
||||
interfaceBytes = []byte("(interface {})")
|
||||
commaNewlineBytes = []byte(",\n")
|
||||
newlineBytes = []byte("\n")
|
||||
openBraceBytes = []byte("{")
|
||||
openBraceNewlineBytes = []byte("{\n")
|
||||
closeBraceBytes = []byte("}")
|
||||
asteriskBytes = []byte("*")
|
||||
colonBytes = []byte(":")
|
||||
colonSpaceBytes = []byte(": ")
|
||||
openParenBytes = []byte("(")
|
||||
closeParenBytes = []byte(")")
|
||||
spaceBytes = []byte(" ")
|
||||
pointerChainBytes = []byte("->")
|
||||
nilAngleBytes = []byte("<nil>")
|
||||
maxNewlineBytes = []byte("<max depth reached>\n")
|
||||
maxShortBytes = []byte("<max>")
|
||||
circularBytes = []byte("<already shown>")
|
||||
circularShortBytes = []byte("<shown>")
|
||||
invalidAngleBytes = []byte("<invalid>")
|
||||
openBracketBytes = []byte("[")
|
||||
closeBracketBytes = []byte("]")
|
||||
percentBytes = []byte("%")
|
||||
precisionBytes = []byte(".")
|
||||
openAngleBytes = []byte("<")
|
||||
closeAngleBytes = []byte(">")
|
||||
openMapBytes = []byte("map[")
|
||||
closeMapBytes = []byte("]")
|
||||
lenEqualsBytes = []byte("len=")
|
||||
capEqualsBytes = []byte("cap=")
|
||||
)
|
||||
|
||||
// hexDigits is used to map a decimal value to a hex digit.
|
||||
var hexDigits = "0123456789abcdef"
|
||||
|
||||
// catchPanic handles any panics that might occur during the handleMethods
|
||||
// calls.
|
||||
func catchPanic(w io.Writer, v reflect.Value) {
|
||||
if err := recover(); err != nil {
|
||||
w.Write(panicBytes)
|
||||
fmt.Fprintf(w, "%v", err)
|
||||
w.Write(closeParenBytes)
|
||||
}
|
||||
}
|
||||
|
||||
// handleMethods attempts to call the Error and String methods on the underlying
|
||||
// type the passed reflect.Value represents and outputes the result to Writer w.
|
||||
//
|
||||
// It handles panics in any called methods by catching and displaying the error
|
||||
// as the formatted value.
|
||||
func handleMethods(cs *ConfigState, w io.Writer, v reflect.Value) (handled bool) {
|
||||
// We need an interface to check if the type implements the error or
|
||||
// Stringer interface. However, the reflect package won't give us an
|
||||
// interface on certain things like unexported struct fields in order
|
||||
// to enforce visibility rules. We use unsafe, when it's available,
|
||||
// to bypass these restrictions since this package does not mutate the
|
||||
// values.
|
||||
if !v.CanInterface() {
|
||||
if UnsafeDisabled {
|
||||
return false
|
||||
}
|
||||
|
||||
v = unsafeReflectValue(v)
|
||||
}
|
||||
|
||||
// Choose whether or not to do error and Stringer interface lookups against
|
||||
// the base type or a pointer to the base type depending on settings.
|
||||
// Technically calling one of these methods with a pointer receiver can
|
||||
// mutate the value, however, types which choose to satisify an error or
|
||||
// Stringer interface with a pointer receiver should not be mutating their
|
||||
// state inside these interface methods.
|
||||
if !cs.DisablePointerMethods && !UnsafeDisabled && !v.CanAddr() {
|
||||
v = unsafeReflectValue(v)
|
||||
}
|
||||
if v.CanAddr() {
|
||||
v = v.Addr()
|
||||
}
|
||||
|
||||
// Is it an error or Stringer?
|
||||
switch iface := v.Interface().(type) {
|
||||
case error:
|
||||
defer catchPanic(w, v)
|
||||
if cs.ContinueOnMethod {
|
||||
w.Write(openParenBytes)
|
||||
w.Write([]byte(iface.Error()))
|
||||
w.Write(closeParenBytes)
|
||||
w.Write(spaceBytes)
|
||||
return false
|
||||
}
|
||||
|
||||
w.Write([]byte(iface.Error()))
|
||||
return true
|
||||
|
||||
case fmt.Stringer:
|
||||
defer catchPanic(w, v)
|
||||
if cs.ContinueOnMethod {
|
||||
w.Write(openParenBytes)
|
||||
w.Write([]byte(iface.String()))
|
||||
w.Write(closeParenBytes)
|
||||
w.Write(spaceBytes)
|
||||
return false
|
||||
}
|
||||
w.Write([]byte(iface.String()))
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// printBool outputs a boolean value as true or false to Writer w.
|
||||
func printBool(w io.Writer, val bool) {
|
||||
if val {
|
||||
w.Write(trueBytes)
|
||||
} else {
|
||||
w.Write(falseBytes)
|
||||
}
|
||||
}
|
||||
|
||||
// printInt outputs a signed integer value to Writer w.
|
||||
func printInt(w io.Writer, val int64, base int) {
|
||||
w.Write([]byte(strconv.FormatInt(val, base)))
|
||||
}
|
||||
|
||||
// printUint outputs an unsigned integer value to Writer w.
|
||||
func printUint(w io.Writer, val uint64, base int) {
|
||||
w.Write([]byte(strconv.FormatUint(val, base)))
|
||||
}
|
||||
|
||||
// printFloat outputs a floating point value using the specified precision,
|
||||
// which is expected to be 32 or 64bit, to Writer w.
|
||||
func printFloat(w io.Writer, val float64, precision int) {
|
||||
w.Write([]byte(strconv.FormatFloat(val, 'g', -1, precision)))
|
||||
}
|
||||
|
||||
// printComplex outputs a complex value using the specified float precision
|
||||
// for the real and imaginary parts to Writer w.
|
||||
func printComplex(w io.Writer, c complex128, floatPrecision int) {
|
||||
r := real(c)
|
||||
w.Write(openParenBytes)
|
||||
w.Write([]byte(strconv.FormatFloat(r, 'g', -1, floatPrecision)))
|
||||
i := imag(c)
|
||||
if i >= 0 {
|
||||
w.Write(plusBytes)
|
||||
}
|
||||
w.Write([]byte(strconv.FormatFloat(i, 'g', -1, floatPrecision)))
|
||||
w.Write(iBytes)
|
||||
w.Write(closeParenBytes)
|
||||
}
|
||||
|
||||
// printHexPtr outputs a uintptr formatted as hexadecimal with a leading '0x'
|
||||
// prefix to Writer w.
|
||||
func printHexPtr(w io.Writer, p uintptr) {
|
||||
// Null pointer.
|
||||
num := uint64(p)
|
||||
if num == 0 {
|
||||
w.Write(nilAngleBytes)
|
||||
return
|
||||
}
|
||||
|
||||
// Max uint64 is 16 bytes in hex + 2 bytes for '0x' prefix
|
||||
buf := make([]byte, 18)
|
||||
|
||||
// It's simpler to construct the hex string right to left.
|
||||
base := uint64(16)
|
||||
i := len(buf) - 1
|
||||
for num >= base {
|
||||
buf[i] = hexDigits[num%base]
|
||||
num /= base
|
||||
i--
|
||||
}
|
||||
buf[i] = hexDigits[num]
|
||||
|
||||
// Add '0x' prefix.
|
||||
i--
|
||||
buf[i] = 'x'
|
||||
i--
|
||||
buf[i] = '0'
|
||||
|
||||
// Strip unused leading bytes.
|
||||
buf = buf[i:]
|
||||
w.Write(buf)
|
||||
}
|
||||
|
||||
// valuesSorter implements sort.Interface to allow a slice of reflect.Value
|
||||
// elements to be sorted.
|
||||
type valuesSorter struct {
|
||||
values []reflect.Value
|
||||
strings []string // either nil or same len and values
|
||||
cs *ConfigState
|
||||
}
|
||||
|
||||
// newValuesSorter initializes a valuesSorter instance, which holds a set of
|
||||
// surrogate keys on which the data should be sorted. It uses flags in
|
||||
// ConfigState to decide if and how to populate those surrogate keys.
|
||||
func newValuesSorter(values []reflect.Value, cs *ConfigState) sort.Interface {
|
||||
vs := &valuesSorter{values: values, cs: cs}
|
||||
if canSortSimply(vs.values[0].Kind()) {
|
||||
return vs
|
||||
}
|
||||
if !cs.DisableMethods {
|
||||
vs.strings = make([]string, len(values))
|
||||
for i := range vs.values {
|
||||
b := bytes.Buffer{}
|
||||
if !handleMethods(cs, &b, vs.values[i]) {
|
||||
vs.strings = nil
|
||||
break
|
||||
}
|
||||
vs.strings[i] = b.String()
|
||||
}
|
||||
}
|
||||
if vs.strings == nil && cs.SpewKeys {
|
||||
vs.strings = make([]string, len(values))
|
||||
for i := range vs.values {
|
||||
vs.strings[i] = Sprintf("%#v", vs.values[i].Interface())
|
||||
}
|
||||
}
|
||||
return vs
|
||||
}
|
||||
|
||||
// canSortSimply tests whether a reflect.Kind is a primitive that can be sorted
|
||||
// directly, or whether it should be considered for sorting by surrogate keys
|
||||
// (if the ConfigState allows it).
|
||||
func canSortSimply(kind reflect.Kind) bool {
|
||||
// This switch parallels valueSortLess, except for the default case.
|
||||
switch kind {
|
||||
case reflect.Bool:
|
||||
return true
|
||||
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
|
||||
return true
|
||||
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
|
||||
return true
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return true
|
||||
case reflect.String:
|
||||
return true
|
||||
case reflect.Uintptr:
|
||||
return true
|
||||
case reflect.Array:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Len returns the number of values in the slice. It is part of the
|
||||
// sort.Interface implementation.
|
||||
func (s *valuesSorter) Len() int {
|
||||
return len(s.values)
|
||||
}
|
||||
|
||||
// Swap swaps the values at the passed indices. It is part of the
|
||||
// sort.Interface implementation.
|
||||
func (s *valuesSorter) Swap(i, j int) {
|
||||
s.values[i], s.values[j] = s.values[j], s.values[i]
|
||||
if s.strings != nil {
|
||||
s.strings[i], s.strings[j] = s.strings[j], s.strings[i]
|
||||
}
|
||||
}
|
||||
|
||||
// valueSortLess returns whether the first value should sort before the second
|
||||
// value. It is used by valueSorter.Less as part of the sort.Interface
|
||||
// implementation.
|
||||
func valueSortLess(a, b reflect.Value) bool {
|
||||
switch a.Kind() {
|
||||
case reflect.Bool:
|
||||
return !a.Bool() && b.Bool()
|
||||
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
|
||||
return a.Int() < b.Int()
|
||||
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
|
||||
return a.Uint() < b.Uint()
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return a.Float() < b.Float()
|
||||
case reflect.String:
|
||||
return a.String() < b.String()
|
||||
case reflect.Uintptr:
|
||||
return a.Uint() < b.Uint()
|
||||
case reflect.Array:
|
||||
// Compare the contents of both arrays.
|
||||
l := a.Len()
|
||||
for i := 0; i < l; i++ {
|
||||
av := a.Index(i)
|
||||
bv := b.Index(i)
|
||||
if av.Interface() == bv.Interface() {
|
||||
continue
|
||||
}
|
||||
return valueSortLess(av, bv)
|
||||
}
|
||||
}
|
||||
return a.String() < b.String()
|
||||
}
|
||||
|
||||
// Less returns whether the value at index i should sort before the
|
||||
// value at index j. It is part of the sort.Interface implementation.
|
||||
func (s *valuesSorter) Less(i, j int) bool {
|
||||
if s.strings == nil {
|
||||
return valueSortLess(s.values[i], s.values[j])
|
||||
}
|
||||
return s.strings[i] < s.strings[j]
|
||||
}
|
||||
|
||||
// sortValues is a sort function that handles both native types and any type that
|
||||
// can be converted to error or Stringer. Other inputs are sorted according to
|
||||
// their Value.String() value to ensure display stability.
|
||||
func sortValues(values []reflect.Value, cs *ConfigState) {
|
||||
if len(values) == 0 {
|
||||
return
|
||||
}
|
||||
sort.Sort(newValuesSorter(values, cs))
|
||||
}
|
306
vendor/github.com/davecgh/go-spew/spew/config.go
generated
vendored
Normal file
306
vendor/github.com/davecgh/go-spew/spew/config.go
generated
vendored
Normal file
|
@ -0,0 +1,306 @@
|
|||
/*
|
||||
* Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
package spew
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
||||
// ConfigState houses the configuration options used by spew to format and
|
||||
// display values. There is a global instance, Config, that is used to control
|
||||
// all top-level Formatter and Dump functionality. Each ConfigState instance
|
||||
// provides methods equivalent to the top-level functions.
|
||||
//
|
||||
// The zero value for ConfigState provides no indentation. You would typically
|
||||
// want to set it to a space or a tab.
|
||||
//
|
||||
// Alternatively, you can use NewDefaultConfig to get a ConfigState instance
|
||||
// with default settings. See the documentation of NewDefaultConfig for default
|
||||
// values.
|
||||
type ConfigState struct {
|
||||
// Indent specifies the string to use for each indentation level. The
|
||||
// global config instance that all top-level functions use set this to a
|
||||
// single space by default. If you would like more indentation, you might
|
||||
// set this to a tab with "\t" or perhaps two spaces with " ".
|
||||
Indent string
|
||||
|
||||
// MaxDepth controls the maximum number of levels to descend into nested
|
||||
// data structures. The default, 0, means there is no limit.
|
||||
//
|
||||
// NOTE: Circular data structures are properly detected, so it is not
|
||||
// necessary to set this value unless you specifically want to limit deeply
|
||||
// nested data structures.
|
||||
MaxDepth int
|
||||
|
||||
// DisableMethods specifies whether or not error and Stringer interfaces are
|
||||
// invoked for types that implement them.
|
||||
DisableMethods bool
|
||||
|
||||
// DisablePointerMethods specifies whether or not to check for and invoke
|
||||
// error and Stringer interfaces on types which only accept a pointer
|
||||
// receiver when the current type is not a pointer.
|
||||
//
|
||||
// NOTE: This might be an unsafe action since calling one of these methods
|
||||
// with a pointer receiver could technically mutate the value, however,
|
||||
// in practice, types which choose to satisify an error or Stringer
|
||||
// interface with a pointer receiver should not be mutating their state
|
||||
// inside these interface methods. As a result, this option relies on
|
||||
// access to the unsafe package, so it will not have any effect when
|
||||
// running in environments without access to the unsafe package such as
|
||||
// Google App Engine or with the "safe" build tag specified.
|
||||
DisablePointerMethods bool
|
||||
|
||||
// DisablePointerAddresses specifies whether to disable the printing of
|
||||
// pointer addresses. This is useful when diffing data structures in tests.
|
||||
DisablePointerAddresses bool
|
||||
|
||||
// DisableCapacities specifies whether to disable the printing of capacities
|
||||
// for arrays, slices, maps and channels. This is useful when diffing
|
||||
// data structures in tests.
|
||||
DisableCapacities bool
|
||||
|
||||
// ContinueOnMethod specifies whether or not recursion should continue once
|
||||
// a custom error or Stringer interface is invoked. The default, false,
|
||||
// means it will print the results of invoking the custom error or Stringer
|
||||
// interface and return immediately instead of continuing to recurse into
|
||||
// the internals of the data type.
|
||||
//
|
||||
// NOTE: This flag does not have any effect if method invocation is disabled
|
||||
// via the DisableMethods or DisablePointerMethods options.
|
||||
ContinueOnMethod bool
|
||||
|
||||
// SortKeys specifies map keys should be sorted before being printed. Use
|
||||
// this to have a more deterministic, diffable output. Note that only
|
||||
// native types (bool, int, uint, floats, uintptr and string) and types
|
||||
// that support the error or Stringer interfaces (if methods are
|
||||
// enabled) are supported, with other types sorted according to the
|
||||
// reflect.Value.String() output which guarantees display stability.
|
||||
SortKeys bool
|
||||
|
||||
// SpewKeys specifies that, as a last resort attempt, map keys should
|
||||
// be spewed to strings and sorted by those strings. This is only
|
||||
// considered if SortKeys is true.
|
||||
SpewKeys bool
|
||||
}
|
||||
|
||||
// Config is the active configuration of the top-level functions.
|
||||
// The configuration can be changed by modifying the contents of spew.Config.
|
||||
var Config = ConfigState{Indent: " "}
|
||||
|
||||
// Errorf is a wrapper for fmt.Errorf that treats each argument as if it were
|
||||
// passed with a Formatter interface returned by c.NewFormatter. It returns
|
||||
// the formatted string as a value that satisfies error. See NewFormatter
|
||||
// for formatting details.
|
||||
//
|
||||
// This function is shorthand for the following syntax:
|
||||
//
|
||||
// fmt.Errorf(format, c.NewFormatter(a), c.NewFormatter(b))
|
||||
func (c *ConfigState) Errorf(format string, a ...interface{}) (err error) {
|
||||
return fmt.Errorf(format, c.convertArgs(a)...)
|
||||
}
|
||||
|
||||
// Fprint is a wrapper for fmt.Fprint that treats each argument as if it were
|
||||
// passed with a Formatter interface returned by c.NewFormatter. It returns
|
||||
// the number of bytes written and any write error encountered. See
|
||||
// NewFormatter for formatting details.
|
||||
//
|
||||
// This function is shorthand for the following syntax:
|
||||
//
|
||||
// fmt.Fprint(w, c.NewFormatter(a), c.NewFormatter(b))
|
||||
func (c *ConfigState) Fprint(w io.Writer, a ...interface{}) (n int, err error) {
|
||||
return fmt.Fprint(w, c.convertArgs(a)...)
|
||||
}
|
||||
|
||||
// Fprintf is a wrapper for fmt.Fprintf that treats each argument as if it were
|
||||
// passed with a Formatter interface returned by c.NewFormatter. It returns
|
||||
// the number of bytes written and any write error encountered. See
|
||||
// NewFormatter for formatting details.
|
||||
//
|
||||
// This function is shorthand for the following syntax:
|
||||
//
|
||||
// fmt.Fprintf(w, format, c.NewFormatter(a), c.NewFormatter(b))
|
||||
func (c *ConfigState) Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) {
|
||||
return fmt.Fprintf(w, format, c.convertArgs(a)...)
|
||||
}
|
||||
|
||||
// Fprintln is a wrapper for fmt.Fprintln that treats each argument as if it
|
||||
// passed with a Formatter interface returned by c.NewFormatter. See
|
||||
// NewFormatter for formatting details.
|
||||
//
|
||||
// This function is shorthand for the following syntax:
|
||||
//
|
||||
// fmt.Fprintln(w, c.NewFormatter(a), c.NewFormatter(b))
|
||||
func (c *ConfigState) Fprintln(w io.Writer, a ...interface{}) (n int, err error) {
|
||||
return fmt.Fprintln(w, c.convertArgs(a)...)
|
||||
}
|
||||
|
||||
// Print is a wrapper for fmt.Print that treats each argument as if it were
|
||||
// passed with a Formatter interface returned by c.NewFormatter. It returns
|
||||
// the number of bytes written and any write error encountered. See
|
||||
// NewFormatter for formatting details.
|
||||
//
|
||||
// This function is shorthand for the following syntax:
|
||||
//
|
||||
// fmt.Print(c.NewFormatter(a), c.NewFormatter(b))
|
||||
func (c *ConfigState) Print(a ...interface{}) (n int, err error) {
|
||||
return fmt.Print(c.convertArgs(a)...)
|
||||
}
|
||||
|
||||
// Printf is a wrapper for fmt.Printf that treats each argument as if it were
|
||||
// passed with a Formatter interface returned by c.NewFormatter. It returns
|
||||
// the number of bytes written and any write error encountered. See
|
||||
// NewFormatter for formatting details.
|
||||
//
|
||||
// This function is shorthand for the following syntax:
|
||||
//
|
||||
// fmt.Printf(format, c.NewFormatter(a), c.NewFormatter(b))
|
||||
func (c *ConfigState) Printf(format string, a ...interface{}) (n int, err error) {
|
||||
return fmt.Printf(format, c.convertArgs(a)...)
|
||||
}
|
||||
|
||||
// Println is a wrapper for fmt.Println that treats each argument as if it were
|
||||
// passed with a Formatter interface returned by c.NewFormatter. It returns
|
||||
// the number of bytes written and any write error encountered. See
|
||||
// NewFormatter for formatting details.
|
||||
//
|
||||
// This function is shorthand for the following syntax:
|
||||
//
|
||||
// fmt.Println(c.NewFormatter(a), c.NewFormatter(b))
|
||||
func (c *ConfigState) Println(a ...interface{}) (n int, err error) {
|
||||
return fmt.Println(c.convertArgs(a)...)
|
||||
}
|
||||
|
||||
// Sprint is a wrapper for fmt.Sprint that treats each argument as if it were
|
||||
// passed with a Formatter interface returned by c.NewFormatter. It returns
|
||||
// the resulting string. See NewFormatter for formatting details.
|
||||
//
|
||||
// This function is shorthand for the following syntax:
|
||||
//
|
||||
// fmt.Sprint(c.NewFormatter(a), c.NewFormatter(b))
|
||||
func (c *ConfigState) Sprint(a ...interface{}) string {
|
||||
return fmt.Sprint(c.convertArgs(a)...)
|
||||
}
|
||||
|
||||
// Sprintf is a wrapper for fmt.Sprintf that treats each argument as if it were
|
||||
// passed with a Formatter interface returned by c.NewFormatter. It returns
|
||||
// the resulting string. See NewFormatter for formatting details.
|
||||
//
|
||||
// This function is shorthand for the following syntax:
|
||||
//
|
||||
// fmt.Sprintf(format, c.NewFormatter(a), c.NewFormatter(b))
|
||||
func (c *ConfigState) Sprintf(format string, a ...interface{}) string {
|
||||
return fmt.Sprintf(format, c.convertArgs(a)...)
|
||||
}
|
||||
|
||||
// Sprintln is a wrapper for fmt.Sprintln that treats each argument as if it
|
||||
// were passed with a Formatter interface returned by c.NewFormatter. It
|
||||
// returns the resulting string. See NewFormatter for formatting details.
|
||||
//
|
||||
// This function is shorthand for the following syntax:
|
||||
//
|
||||
// fmt.Sprintln(c.NewFormatter(a), c.NewFormatter(b))
|
||||
func (c *ConfigState) Sprintln(a ...interface{}) string {
|
||||
return fmt.Sprintln(c.convertArgs(a)...)
|
||||
}
|
||||
|
||||
/*
|
||||
NewFormatter returns a custom formatter that satisfies the fmt.Formatter
|
||||
interface. As a result, it integrates cleanly with standard fmt package
|
||||
printing functions. The formatter is useful for inline printing of smaller data
|
||||
types similar to the standard %v format specifier.
|
||||
|
||||
The custom formatter only responds to the %v (most compact), %+v (adds pointer
|
||||
addresses), %#v (adds types), and %#+v (adds types and pointer addresses) verb
|
||||
combinations. Any other verbs such as %x and %q will be sent to the the
|
||||
standard fmt package for formatting. In addition, the custom formatter ignores
|
||||
the width and precision arguments (however they will still work on the format
|
||||
specifiers not handled by the custom formatter).
|
||||
|
||||
Typically this function shouldn't be called directly. It is much easier to make
|
||||
use of the custom formatter by calling one of the convenience functions such as
|
||||
c.Printf, c.Println, or c.Printf.
|
||||
*/
|
||||
func (c *ConfigState) NewFormatter(v interface{}) fmt.Formatter {
|
||||
return newFormatter(c, v)
|
||||
}
|
||||
|
||||
// Fdump formats and displays the passed arguments to io.Writer w. It formats
|
||||
// exactly the same as Dump.
|
||||
func (c *ConfigState) Fdump(w io.Writer, a ...interface{}) {
|
||||
fdump(c, w, a...)
|
||||
}
|
||||
|
||||
/*
|
||||
Dump displays the passed parameters to standard out with newlines, customizable
|
||||
indentation, and additional debug information such as complete types and all
|
||||
pointer addresses used to indirect to the final value. It provides the
|
||||
following features over the built-in printing facilities provided by the fmt
|
||||
package:
|
||||
|
||||
* Pointers are dereferenced and followed
|
||||
* Circular data structures are detected and handled properly
|
||||
* Custom Stringer/error interfaces are optionally invoked, including
|
||||
on unexported types
|
||||
* Custom types which only implement the Stringer/error interfaces via
|
||||
a pointer receiver are optionally invoked when passing non-pointer
|
||||
variables
|
||||
* Byte arrays and slices are dumped like the hexdump -C command which
|
||||
includes offsets, byte values in hex, and ASCII output
|
||||
|
||||
The configuration options are controlled by modifying the public members
|
||||
of c. See ConfigState for options documentation.
|
||||
|
||||
See Fdump if you would prefer dumping to an arbitrary io.Writer or Sdump to
|
||||
get the formatted result as a string.
|
||||
*/
|
||||
func (c *ConfigState) Dump(a ...interface{}) {
|
||||
fdump(c, os.Stdout, a...)
|
||||
}
|
||||
|
||||
// Sdump returns a string with the passed arguments formatted exactly the same
|
||||
// as Dump.
|
||||
func (c *ConfigState) Sdump(a ...interface{}) string {
|
||||
var buf bytes.Buffer
|
||||
fdump(c, &buf, a...)
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// convertArgs accepts a slice of arguments and returns a slice of the same
|
||||
// length with each argument converted to a spew Formatter interface using
|
||||
// the ConfigState associated with s.
|
||||
func (c *ConfigState) convertArgs(args []interface{}) (formatters []interface{}) {
|
||||
formatters = make([]interface{}, len(args))
|
||||
for index, arg := range args {
|
||||
formatters[index] = newFormatter(c, arg)
|
||||
}
|
||||
return formatters
|
||||
}
|
||||
|
||||
// NewDefaultConfig returns a ConfigState with the following default settings.
|
||||
//
|
||||
// Indent: " "
|
||||
// MaxDepth: 0
|
||||
// DisableMethods: false
|
||||
// DisablePointerMethods: false
|
||||
// ContinueOnMethod: false
|
||||
// SortKeys: false
|
||||
func NewDefaultConfig() *ConfigState {
|
||||
return &ConfigState{Indent: " "}
|
||||
}
|
211
vendor/github.com/davecgh/go-spew/spew/doc.go
generated
vendored
Normal file
211
vendor/github.com/davecgh/go-spew/spew/doc.go
generated
vendored
Normal file
|
@ -0,0 +1,211 @@
|
|||
/*
|
||||
* Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
Package spew implements a deep pretty printer for Go data structures to aid in
|
||||
debugging.
|
||||
|
||||
A quick overview of the additional features spew provides over the built-in
|
||||
printing facilities for Go data types are as follows:
|
||||
|
||||
* Pointers are dereferenced and followed
|
||||
* Circular data structures are detected and handled properly
|
||||
* Custom Stringer/error interfaces are optionally invoked, including
|
||||
on unexported types
|
||||
* Custom types which only implement the Stringer/error interfaces via
|
||||
a pointer receiver are optionally invoked when passing non-pointer
|
||||
variables
|
||||
* Byte arrays and slices are dumped like the hexdump -C command which
|
||||
includes offsets, byte values in hex, and ASCII output (only when using
|
||||
Dump style)
|
||||
|
||||
There are two different approaches spew allows for dumping Go data structures:
|
||||
|
||||
* Dump style which prints with newlines, customizable indentation,
|
||||
and additional debug information such as types and all pointer addresses
|
||||
used to indirect to the final value
|
||||
* A custom Formatter interface that integrates cleanly with the standard fmt
|
||||
package and replaces %v, %+v, %#v, and %#+v to provide inline printing
|
||||
similar to the default %v while providing the additional functionality
|
||||
outlined above and passing unsupported format verbs such as %x and %q
|
||||
along to fmt
|
||||
|
||||
Quick Start
|
||||
|
||||
This section demonstrates how to quickly get started with spew. See the
|
||||
sections below for further details on formatting and configuration options.
|
||||
|
||||
To dump a variable with full newlines, indentation, type, and pointer
|
||||
information use Dump, Fdump, or Sdump:
|
||||
spew.Dump(myVar1, myVar2, ...)
|
||||
spew.Fdump(someWriter, myVar1, myVar2, ...)
|
||||
str := spew.Sdump(myVar1, myVar2, ...)
|
||||
|
||||
Alternatively, if you would prefer to use format strings with a compacted inline
|
||||
printing style, use the convenience wrappers Printf, Fprintf, etc with
|
||||
%v (most compact), %+v (adds pointer addresses), %#v (adds types), or
|
||||
%#+v (adds types and pointer addresses):
|
||||
spew.Printf("myVar1: %v -- myVar2: %+v", myVar1, myVar2)
|
||||
spew.Printf("myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
|
||||
spew.Fprintf(someWriter, "myVar1: %v -- myVar2: %+v", myVar1, myVar2)
|
||||
spew.Fprintf(someWriter, "myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
|
||||
|
||||
Configuration Options
|
||||
|
||||
Configuration of spew is handled by fields in the ConfigState type. For
|
||||
convenience, all of the top-level functions use a global state available
|
||||
via the spew.Config global.
|
||||
|
||||
It is also possible to create a ConfigState instance that provides methods
|
||||
equivalent to the top-level functions. This allows concurrent configuration
|
||||
options. See the ConfigState documentation for more details.
|
||||
|
||||
The following configuration options are available:
|
||||
* Indent
|
||||
String to use for each indentation level for Dump functions.
|
||||
It is a single space by default. A popular alternative is "\t".
|
||||
|
||||
* MaxDepth
|
||||
Maximum number of levels to descend into nested data structures.
|
||||
There is no limit by default.
|
||||
|
||||
* DisableMethods
|
||||
Disables invocation of error and Stringer interface methods.
|
||||
Method invocation is enabled by default.
|
||||
|
||||
* DisablePointerMethods
|
||||
Disables invocation of error and Stringer interface methods on types
|
||||
which only accept pointer receivers from non-pointer variables.
|
||||
Pointer method invocation is enabled by default.
|
||||
|
||||
* DisablePointerAddresses
|
||||
DisablePointerAddresses specifies whether to disable the printing of
|
||||
pointer addresses. This is useful when diffing data structures in tests.
|
||||
|
||||
* DisableCapacities
|
||||
DisableCapacities specifies whether to disable the printing of
|
||||
capacities for arrays, slices, maps and channels. This is useful when
|
||||
diffing data structures in tests.
|
||||
|
||||
* ContinueOnMethod
|
||||
Enables recursion into types after invoking error and Stringer interface
|
||||
methods. Recursion after method invocation is disabled by default.
|
||||
|
||||
* SortKeys
|
||||
Specifies map keys should be sorted before being printed. Use
|
||||
this to have a more deterministic, diffable output. Note that
|
||||
only native types (bool, int, uint, floats, uintptr and string)
|
||||
and types which implement error or Stringer interfaces are
|
||||
supported with other types sorted according to the
|
||||
reflect.Value.String() output which guarantees display
|
||||
stability. Natural map order is used by default.
|
||||
|
||||
* SpewKeys
|
||||
Specifies that, as a last resort attempt, map keys should be
|
||||
spewed to strings and sorted by those strings. This is only
|
||||
considered if SortKeys is true.
|
||||
|
||||
Dump Usage
|
||||
|
||||
Simply call spew.Dump with a list of variables you want to dump:
|
||||
|
||||
spew.Dump(myVar1, myVar2, ...)
|
||||
|
||||
You may also call spew.Fdump if you would prefer to output to an arbitrary
|
||||
io.Writer. For example, to dump to standard error:
|
||||
|
||||
spew.Fdump(os.Stderr, myVar1, myVar2, ...)
|
||||
|
||||
A third option is to call spew.Sdump to get the formatted output as a string:
|
||||
|
||||
str := spew.Sdump(myVar1, myVar2, ...)
|
||||
|
||||
Sample Dump Output
|
||||
|
||||
See the Dump example for details on the setup of the types and variables being
|
||||
shown here.
|
||||
|
||||
(main.Foo) {
|
||||
unexportedField: (*main.Bar)(0xf84002e210)({
|
||||
flag: (main.Flag) flagTwo,
|
||||
data: (uintptr) <nil>
|
||||
}),
|
||||
ExportedField: (map[interface {}]interface {}) (len=1) {
|
||||
(string) (len=3) "one": (bool) true
|
||||
}
|
||||
}
|
||||
|
||||
Byte (and uint8) arrays and slices are displayed uniquely like the hexdump -C
|
||||
command as shown.
|
||||
([]uint8) (len=32 cap=32) {
|
||||
00000000 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 |............... |
|
||||
00000010 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 |!"#$%&'()*+,-./0|
|
||||
00000020 31 32 |12|
|
||||
}
|
||||
|
||||
Custom Formatter
|
||||
|
||||
Spew provides a custom formatter that implements the fmt.Formatter interface
|
||||
so that it integrates cleanly with standard fmt package printing functions. The
|
||||
formatter is useful for inline printing of smaller data types similar to the
|
||||
standard %v format specifier.
|
||||
|
||||
The custom formatter only responds to the %v (most compact), %+v (adds pointer
|
||||
addresses), %#v (adds types), or %#+v (adds types and pointer addresses) verb
|
||||
combinations. Any other verbs such as %x and %q will be sent to the the
|
||||
standard fmt package for formatting. In addition, the custom formatter ignores
|
||||
the width and precision arguments (however they will still work on the format
|
||||
specifiers not handled by the custom formatter).
|
||||
|
||||
Custom Formatter Usage
|
||||
|
||||
The simplest way to make use of the spew custom formatter is to call one of the
|
||||
convenience functions such as spew.Printf, spew.Println, or spew.Printf. The
|
||||
functions have syntax you are most likely already familiar with:
|
||||
|
||||
spew.Printf("myVar1: %v -- myVar2: %+v", myVar1, myVar2)
|
||||
spew.Printf("myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
|
||||
spew.Println(myVar, myVar2)
|
||||
spew.Fprintf(os.Stderr, "myVar1: %v -- myVar2: %+v", myVar1, myVar2)
|
||||
spew.Fprintf(os.Stderr, "myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
|
||||
|
||||
See the Index for the full list convenience functions.
|
||||
|
||||
Sample Formatter Output
|
||||
|
||||
Double pointer to a uint8:
|
||||
%v: <**>5
|
||||
%+v: <**>(0xf8400420d0->0xf8400420c8)5
|
||||
%#v: (**uint8)5
|
||||
%#+v: (**uint8)(0xf8400420d0->0xf8400420c8)5
|
||||
|
||||
Pointer to circular struct with a uint8 field and a pointer to itself:
|
||||
%v: <*>{1 <*><shown>}
|
||||
%+v: <*>(0xf84003e260){ui8:1 c:<*>(0xf84003e260)<shown>}
|
||||
%#v: (*main.circular){ui8:(uint8)1 c:(*main.circular)<shown>}
|
||||
%#+v: (*main.circular)(0xf84003e260){ui8:(uint8)1 c:(*main.circular)(0xf84003e260)<shown>}
|
||||
|
||||
See the Printf example for details on the setup of variables being shown
|
||||
here.
|
||||
|
||||
Errors
|
||||
|
||||
Since it is possible for custom Stringer/error interfaces to panic, spew
|
||||
detects them and handles them internally by printing the panic information
|
||||
inline with the output. Since spew is intended to provide deep pretty printing
|
||||
capabilities on structures, it intentionally does not return any errors.
|
||||
*/
|
||||
package spew
|
509
vendor/github.com/davecgh/go-spew/spew/dump.go
generated
vendored
Normal file
509
vendor/github.com/davecgh/go-spew/spew/dump.go
generated
vendored
Normal file
|
@ -0,0 +1,509 @@
|
|||
/*
|
||||
* Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
package spew
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
// uint8Type is a reflect.Type representing a uint8. It is used to
|
||||
// convert cgo types to uint8 slices for hexdumping.
|
||||
uint8Type = reflect.TypeOf(uint8(0))
|
||||
|
||||
// cCharRE is a regular expression that matches a cgo char.
|
||||
// It is used to detect character arrays to hexdump them.
|
||||
cCharRE = regexp.MustCompile(`^.*\._Ctype_char$`)
|
||||
|
||||
// cUnsignedCharRE is a regular expression that matches a cgo unsigned
|
||||
// char. It is used to detect unsigned character arrays to hexdump
|
||||
// them.
|
||||
cUnsignedCharRE = regexp.MustCompile(`^.*\._Ctype_unsignedchar$`)
|
||||
|
||||
// cUint8tCharRE is a regular expression that matches a cgo uint8_t.
|
||||
// It is used to detect uint8_t arrays to hexdump them.
|
||||
cUint8tCharRE = regexp.MustCompile(`^.*\._Ctype_uint8_t$`)
|
||||
)
|
||||
|
||||
// dumpState contains information about the state of a dump operation.
|
||||
type dumpState struct {
|
||||
w io.Writer
|
||||
depth int
|
||||
pointers map[uintptr]int
|
||||
ignoreNextType bool
|
||||
ignoreNextIndent bool
|
||||
cs *ConfigState
|
||||
}
|
||||
|
||||
// indent performs indentation according to the depth level and cs.Indent
|
||||
// option.
|
||||
func (d *dumpState) indent() {
|
||||
if d.ignoreNextIndent {
|
||||
d.ignoreNextIndent = false
|
||||
return
|
||||
}
|
||||
d.w.Write(bytes.Repeat([]byte(d.cs.Indent), d.depth))
|
||||
}
|
||||
|
||||
// unpackValue returns values inside of non-nil interfaces when possible.
|
||||
// This is useful for data types like structs, arrays, slices, and maps which
|
||||
// can contain varying types packed inside an interface.
|
||||
func (d *dumpState) unpackValue(v reflect.Value) reflect.Value {
|
||||
if v.Kind() == reflect.Interface && !v.IsNil() {
|
||||
v = v.Elem()
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// dumpPtr handles formatting of pointers by indirecting them as necessary.
|
||||
func (d *dumpState) dumpPtr(v reflect.Value) {
|
||||
// Remove pointers at or below the current depth from map used to detect
|
||||
// circular refs.
|
||||
for k, depth := range d.pointers {
|
||||
if depth >= d.depth {
|
||||
delete(d.pointers, k)
|
||||
}
|
||||
}
|
||||
|
||||
// Keep list of all dereferenced pointers to show later.
|
||||
pointerChain := make([]uintptr, 0)
|
||||
|
||||
// Figure out how many levels of indirection there are by dereferencing
|
||||
// pointers and unpacking interfaces down the chain while detecting circular
|
||||
// references.
|
||||
nilFound := false
|
||||
cycleFound := false
|
||||
indirects := 0
|
||||
ve := v
|
||||
for ve.Kind() == reflect.Ptr {
|
||||
if ve.IsNil() {
|
||||
nilFound = true
|
||||
break
|
||||
}
|
||||
indirects++
|
||||
addr := ve.Pointer()
|
||||
pointerChain = append(pointerChain, addr)
|
||||
if pd, ok := d.pointers[addr]; ok && pd < d.depth {
|
||||
cycleFound = true
|
||||
indirects--
|
||||
break
|
||||
}
|
||||
d.pointers[addr] = d.depth
|
||||
|
||||
ve = ve.Elem()
|
||||
if ve.Kind() == reflect.Interface {
|
||||
if ve.IsNil() {
|
||||
nilFound = true
|
||||
break
|
||||
}
|
||||
ve = ve.Elem()
|
||||
}
|
||||
}
|
||||
|
||||
// Display type information.
|
||||
d.w.Write(openParenBytes)
|
||||
d.w.Write(bytes.Repeat(asteriskBytes, indirects))
|
||||
d.w.Write([]byte(ve.Type().String()))
|
||||
d.w.Write(closeParenBytes)
|
||||
|
||||
// Display pointer information.
|
||||
if !d.cs.DisablePointerAddresses && len(pointerChain) > 0 {
|
||||
d.w.Write(openParenBytes)
|
||||
for i, addr := range pointerChain {
|
||||
if i > 0 {
|
||||
d.w.Write(pointerChainBytes)
|
||||
}
|
||||
printHexPtr(d.w, addr)
|
||||
}
|
||||
d.w.Write(closeParenBytes)
|
||||
}
|
||||
|
||||
// Display dereferenced value.
|
||||
d.w.Write(openParenBytes)
|
||||
switch {
|
||||
case nilFound:
|
||||
d.w.Write(nilAngleBytes)
|
||||
|
||||
case cycleFound:
|
||||
d.w.Write(circularBytes)
|
||||
|
||||
default:
|
||||
d.ignoreNextType = true
|
||||
d.dump(ve)
|
||||
}
|
||||
d.w.Write(closeParenBytes)
|
||||
}
|
||||
|
||||
// dumpSlice handles formatting of arrays and slices. Byte (uint8 under
|
||||
// reflection) arrays and slices are dumped in hexdump -C fashion.
|
||||
func (d *dumpState) dumpSlice(v reflect.Value) {
|
||||
// Determine whether this type should be hex dumped or not. Also,
|
||||
// for types which should be hexdumped, try to use the underlying data
|
||||
// first, then fall back to trying to convert them to a uint8 slice.
|
||||
var buf []uint8
|
||||
doConvert := false
|
||||
doHexDump := false
|
||||
numEntries := v.Len()
|
||||
if numEntries > 0 {
|
||||
vt := v.Index(0).Type()
|
||||
vts := vt.String()
|
||||
switch {
|
||||
// C types that need to be converted.
|
||||
case cCharRE.MatchString(vts):
|
||||
fallthrough
|
||||
case cUnsignedCharRE.MatchString(vts):
|
||||
fallthrough
|
||||
case cUint8tCharRE.MatchString(vts):
|
||||
doConvert = true
|
||||
|
||||
// Try to use existing uint8 slices and fall back to converting
|
||||
// and copying if that fails.
|
||||
case vt.Kind() == reflect.Uint8:
|
||||
// We need an addressable interface to convert the type
|
||||
// to a byte slice. However, the reflect package won't
|
||||
// give us an interface on certain things like
|
||||
// unexported struct fields in order to enforce
|
||||
// visibility rules. We use unsafe, when available, to
|
||||
// bypass these restrictions since this package does not
|
||||
// mutate the values.
|
||||
vs := v
|
||||
if !vs.CanInterface() || !vs.CanAddr() {
|
||||
vs = unsafeReflectValue(vs)
|
||||
}
|
||||
if !UnsafeDisabled {
|
||||
vs = vs.Slice(0, numEntries)
|
||||
|
||||
// Use the existing uint8 slice if it can be
|
||||
// type asserted.
|
||||
iface := vs.Interface()
|
||||
if slice, ok := iface.([]uint8); ok {
|
||||
buf = slice
|
||||
doHexDump = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// The underlying data needs to be converted if it can't
|
||||
// be type asserted to a uint8 slice.
|
||||
doConvert = true
|
||||
}
|
||||
|
||||
// Copy and convert the underlying type if needed.
|
||||
if doConvert && vt.ConvertibleTo(uint8Type) {
|
||||
// Convert and copy each element into a uint8 byte
|
||||
// slice.
|
||||
buf = make([]uint8, numEntries)
|
||||
for i := 0; i < numEntries; i++ {
|
||||
vv := v.Index(i)
|
||||
buf[i] = uint8(vv.Convert(uint8Type).Uint())
|
||||
}
|
||||
doHexDump = true
|
||||
}
|
||||
}
|
||||
|
||||
// Hexdump the entire slice as needed.
|
||||
if doHexDump {
|
||||
indent := strings.Repeat(d.cs.Indent, d.depth)
|
||||
str := indent + hex.Dump(buf)
|
||||
str = strings.Replace(str, "\n", "\n"+indent, -1)
|
||||
str = strings.TrimRight(str, d.cs.Indent)
|
||||
d.w.Write([]byte(str))
|
||||
return
|
||||
}
|
||||
|
||||
// Recursively call dump for each item.
|
||||
for i := 0; i < numEntries; i++ {
|
||||
d.dump(d.unpackValue(v.Index(i)))
|
||||
if i < (numEntries - 1) {
|
||||
d.w.Write(commaNewlineBytes)
|
||||
} else {
|
||||
d.w.Write(newlineBytes)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// dump is the main workhorse for dumping a value. It uses the passed reflect
|
||||
// value to figure out what kind of object we are dealing with and formats it
|
||||
// appropriately. It is a recursive function, however circular data structures
|
||||
// are detected and handled properly.
|
||||
func (d *dumpState) dump(v reflect.Value) {
|
||||
// Handle invalid reflect values immediately.
|
||||
kind := v.Kind()
|
||||
if kind == reflect.Invalid {
|
||||
d.w.Write(invalidAngleBytes)
|
||||
return
|
||||
}
|
||||
|
||||
// Handle pointers specially.
|
||||
if kind == reflect.Ptr {
|
||||
d.indent()
|
||||
d.dumpPtr(v)
|
||||
return
|
||||
}
|
||||
|
||||
// Print type information unless already handled elsewhere.
|
||||
if !d.ignoreNextType {
|
||||
d.indent()
|
||||
d.w.Write(openParenBytes)
|
||||
d.w.Write([]byte(v.Type().String()))
|
||||
d.w.Write(closeParenBytes)
|
||||
d.w.Write(spaceBytes)
|
||||
}
|
||||
d.ignoreNextType = false
|
||||
|
||||
// Display length and capacity if the built-in len and cap functions
|
||||
// work with the value's kind and the len/cap itself is non-zero.
|
||||
valueLen, valueCap := 0, 0
|
||||
switch v.Kind() {
|
||||
case reflect.Array, reflect.Slice, reflect.Chan:
|
||||
valueLen, valueCap = v.Len(), v.Cap()
|
||||
case reflect.Map, reflect.String:
|
||||
valueLen = v.Len()
|
||||
}
|
||||
if valueLen != 0 || !d.cs.DisableCapacities && valueCap != 0 {
|
||||
d.w.Write(openParenBytes)
|
||||
if valueLen != 0 {
|
||||
d.w.Write(lenEqualsBytes)
|
||||
printInt(d.w, int64(valueLen), 10)
|
||||
}
|
||||
if !d.cs.DisableCapacities && valueCap != 0 {
|
||||
if valueLen != 0 {
|
||||
d.w.Write(spaceBytes)
|
||||
}
|
||||
d.w.Write(capEqualsBytes)
|
||||
printInt(d.w, int64(valueCap), 10)
|
||||
}
|
||||
d.w.Write(closeParenBytes)
|
||||
d.w.Write(spaceBytes)
|
||||
}
|
||||
|
||||
// Call Stringer/error interfaces if they exist and the handle methods flag
|
||||
// is enabled
|
||||
if !d.cs.DisableMethods {
|
||||
if (kind != reflect.Invalid) && (kind != reflect.Interface) {
|
||||
if handled := handleMethods(d.cs, d.w, v); handled {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch kind {
|
||||
case reflect.Invalid:
|
||||
// Do nothing. We should never get here since invalid has already
|
||||
// been handled above.
|
||||
|
||||
case reflect.Bool:
|
||||
printBool(d.w, v.Bool())
|
||||
|
||||
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
|
||||
printInt(d.w, v.Int(), 10)
|
||||
|
||||
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
|
||||
printUint(d.w, v.Uint(), 10)
|
||||
|
||||
case reflect.Float32:
|
||||
printFloat(d.w, v.Float(), 32)
|
||||
|
||||
case reflect.Float64:
|
||||
printFloat(d.w, v.Float(), 64)
|
||||
|
||||
case reflect.Complex64:
|
||||
printComplex(d.w, v.Complex(), 32)
|
||||
|
||||
case reflect.Complex128:
|
||||
printComplex(d.w, v.Complex(), 64)
|
||||
|
||||
case reflect.Slice:
|
||||
if v.IsNil() {
|
||||
d.w.Write(nilAngleBytes)
|
||||
break
|
||||
}
|
||||
fallthrough
|
||||
|
||||
case reflect.Array:
|
||||
d.w.Write(openBraceNewlineBytes)
|
||||
d.depth++
|
||||
if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) {
|
||||
d.indent()
|
||||
d.w.Write(maxNewlineBytes)
|
||||
} else {
|
||||
d.dumpSlice(v)
|
||||
}
|
||||
d.depth--
|
||||
d.indent()
|
||||
d.w.Write(closeBraceBytes)
|
||||
|
||||
case reflect.String:
|
||||
d.w.Write([]byte(strconv.Quote(v.String())))
|
||||
|
||||
case reflect.Interface:
|
||||
// The only time we should get here is for nil interfaces due to
|
||||
// unpackValue calls.
|
||||
if v.IsNil() {
|
||||
d.w.Write(nilAngleBytes)
|
||||
}
|
||||
|
||||
case reflect.Ptr:
|
||||
// Do nothing. We should never get here since pointers have already
|
||||
// been handled above.
|
||||
|
||||
case reflect.Map:
|
||||
// nil maps should be indicated as different than empty maps
|
||||
if v.IsNil() {
|
||||
d.w.Write(nilAngleBytes)
|
||||
break
|
||||
}
|
||||
|
||||
d.w.Write(openBraceNewlineBytes)
|
||||
d.depth++
|
||||
if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) {
|
||||
d.indent()
|
||||
d.w.Write(maxNewlineBytes)
|
||||
} else {
|
||||
numEntries := v.Len()
|
||||
keys := v.MapKeys()
|
||||
if d.cs.SortKeys {
|
||||
sortValues(keys, d.cs)
|
||||
}
|
||||
for i, key := range keys {
|
||||
d.dump(d.unpackValue(key))
|
||||
d.w.Write(colonSpaceBytes)
|
||||
d.ignoreNextIndent = true
|
||||
d.dump(d.unpackValue(v.MapIndex(key)))
|
||||
if i < (numEntries - 1) {
|
||||
d.w.Write(commaNewlineBytes)
|
||||
} else {
|
||||
d.w.Write(newlineBytes)
|
||||
}
|
||||
}
|
||||
}
|
||||
d.depth--
|
||||
d.indent()
|
||||
d.w.Write(closeBraceBytes)
|
||||
|
||||
case reflect.Struct:
|
||||
d.w.Write(openBraceNewlineBytes)
|
||||
d.depth++
|
||||
if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) {
|
||||
d.indent()
|
||||
d.w.Write(maxNewlineBytes)
|
||||
} else {
|
||||
vt := v.Type()
|
||||
numFields := v.NumField()
|
||||
for i := 0; i < numFields; i++ {
|
||||
d.indent()
|
||||
vtf := vt.Field(i)
|
||||
d.w.Write([]byte(vtf.Name))
|
||||
d.w.Write(colonSpaceBytes)
|
||||
d.ignoreNextIndent = true
|
||||
d.dump(d.unpackValue(v.Field(i)))
|
||||
if i < (numFields - 1) {
|
||||
d.w.Write(commaNewlineBytes)
|
||||
} else {
|
||||
d.w.Write(newlineBytes)
|
||||
}
|
||||
}
|
||||
}
|
||||
d.depth--
|
||||
d.indent()
|
||||
d.w.Write(closeBraceBytes)
|
||||
|
||||
case reflect.Uintptr:
|
||||
printHexPtr(d.w, uintptr(v.Uint()))
|
||||
|
||||
case reflect.UnsafePointer, reflect.Chan, reflect.Func:
|
||||
printHexPtr(d.w, v.Pointer())
|
||||
|
||||
// There were not any other types at the time this code was written, but
|
||||
// fall back to letting the default fmt package handle it in case any new
|
||||
// types are added.
|
||||
default:
|
||||
if v.CanInterface() {
|
||||
fmt.Fprintf(d.w, "%v", v.Interface())
|
||||
} else {
|
||||
fmt.Fprintf(d.w, "%v", v.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// fdump is a helper function to consolidate the logic from the various public
|
||||
// methods which take varying writers and config states.
|
||||
func fdump(cs *ConfigState, w io.Writer, a ...interface{}) {
|
||||
for _, arg := range a {
|
||||
if arg == nil {
|
||||
w.Write(interfaceBytes)
|
||||
w.Write(spaceBytes)
|
||||
w.Write(nilAngleBytes)
|
||||
w.Write(newlineBytes)
|
||||
continue
|
||||
}
|
||||
|
||||
d := dumpState{w: w, cs: cs}
|
||||
d.pointers = make(map[uintptr]int)
|
||||
d.dump(reflect.ValueOf(arg))
|
||||
d.w.Write(newlineBytes)
|
||||
}
|
||||
}
|
||||
|
||||
// Fdump formats and displays the passed arguments to io.Writer w. It formats
|
||||
// exactly the same as Dump.
|
||||
func Fdump(w io.Writer, a ...interface{}) {
|
||||
fdump(&Config, w, a...)
|
||||
}
|
||||
|
||||
// Sdump returns a string with the passed arguments formatted exactly the same
|
||||
// as Dump.
|
||||
func Sdump(a ...interface{}) string {
|
||||
var buf bytes.Buffer
|
||||
fdump(&Config, &buf, a...)
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
/*
|
||||
Dump displays the passed parameters to standard out with newlines, customizable
|
||||
indentation, and additional debug information such as complete types and all
|
||||
pointer addresses used to indirect to the final value. It provides the
|
||||
following features over the built-in printing facilities provided by the fmt
|
||||
package:
|
||||
|
||||
* Pointers are dereferenced and followed
|
||||
* Circular data structures are detected and handled properly
|
||||
* Custom Stringer/error interfaces are optionally invoked, including
|
||||
on unexported types
|
||||
* Custom types which only implement the Stringer/error interfaces via
|
||||
a pointer receiver are optionally invoked when passing non-pointer
|
||||
variables
|
||||
* Byte arrays and slices are dumped like the hexdump -C command which
|
||||
includes offsets, byte values in hex, and ASCII output
|
||||
|
||||
The configuration options are controlled by an exported package global,
|
||||
spew.Config. See ConfigState for options documentation.
|
||||
|
||||
See Fdump if you would prefer dumping to an arbitrary io.Writer or Sdump to
|
||||
get the formatted result as a string.
|
||||
*/
|
||||
func Dump(a ...interface{}) {
|
||||
fdump(&Config, os.Stdout, a...)
|
||||
}
|
419
vendor/github.com/davecgh/go-spew/spew/format.go
generated
vendored
Normal file
419
vendor/github.com/davecgh/go-spew/spew/format.go
generated
vendored
Normal file
|
@ -0,0 +1,419 @@
|
|||
/*
|
||||
* Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
package spew
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// supportedFlags is a list of all the character flags supported by fmt package.
|
||||
const supportedFlags = "0-+# "
|
||||
|
||||
// formatState implements the fmt.Formatter interface and contains information
|
||||
// about the state of a formatting operation. The NewFormatter function can
|
||||
// be used to get a new Formatter which can be used directly as arguments
|
||||
// in standard fmt package printing calls.
|
||||
type formatState struct {
|
||||
value interface{}
|
||||
fs fmt.State
|
||||
depth int
|
||||
pointers map[uintptr]int
|
||||
ignoreNextType bool
|
||||
cs *ConfigState
|
||||
}
|
||||
|
||||
// buildDefaultFormat recreates the original format string without precision
|
||||
// and width information to pass in to fmt.Sprintf in the case of an
|
||||
// unrecognized type. Unless new types are added to the language, this
|
||||
// function won't ever be called.
|
||||
func (f *formatState) buildDefaultFormat() (format string) {
|
||||
buf := bytes.NewBuffer(percentBytes)
|
||||
|
||||
for _, flag := range supportedFlags {
|
||||
if f.fs.Flag(int(flag)) {
|
||||
buf.WriteRune(flag)
|
||||
}
|
||||
}
|
||||
|
||||
buf.WriteRune('v')
|
||||
|
||||
format = buf.String()
|
||||
return format
|
||||
}
|
||||
|
||||
// constructOrigFormat recreates the original format string including precision
|
||||
// and width information to pass along to the standard fmt package. This allows
|
||||
// automatic deferral of all format strings this package doesn't support.
|
||||
func (f *formatState) constructOrigFormat(verb rune) (format string) {
|
||||
buf := bytes.NewBuffer(percentBytes)
|
||||
|
||||
for _, flag := range supportedFlags {
|
||||
if f.fs.Flag(int(flag)) {
|
||||
buf.WriteRune(flag)
|
||||
}
|
||||
}
|
||||
|
||||
if width, ok := f.fs.Width(); ok {
|
||||
buf.WriteString(strconv.Itoa(width))
|
||||
}
|
||||
|
||||
if precision, ok := f.fs.Precision(); ok {
|
||||
buf.Write(precisionBytes)
|
||||
buf.WriteString(strconv.Itoa(precision))
|
||||
}
|
||||
|
||||
buf.WriteRune(verb)
|
||||
|
||||
format = buf.String()
|
||||
return format
|
||||
}
|
||||
|
||||
// unpackValue returns values inside of non-nil interfaces when possible and
|
||||
// ensures that types for values which have been unpacked from an interface
|
||||
// are displayed when the show types flag is also set.
|
||||
// This is useful for data types like structs, arrays, slices, and maps which
|
||||
// can contain varying types packed inside an interface.
|
||||
func (f *formatState) unpackValue(v reflect.Value) reflect.Value {
|
||||
if v.Kind() == reflect.Interface {
|
||||
f.ignoreNextType = false
|
||||
if !v.IsNil() {
|
||||
v = v.Elem()
|
||||
}
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// formatPtr handles formatting of pointers by indirecting them as necessary.
|
||||
func (f *formatState) formatPtr(v reflect.Value) {
|
||||
// Display nil if top level pointer is nil.
|
||||
showTypes := f.fs.Flag('#')
|
||||
if v.IsNil() && (!showTypes || f.ignoreNextType) {
|
||||
f.fs.Write(nilAngleBytes)
|
||||
return
|
||||
}
|
||||
|
||||
// Remove pointers at or below the current depth from map used to detect
|
||||
// circular refs.
|
||||
for k, depth := range f.pointers {
|
||||
if depth >= f.depth {
|
||||
delete(f.pointers, k)
|
||||
}
|
||||
}
|
||||
|
||||
// Keep list of all dereferenced pointers to possibly show later.
|
||||
pointerChain := make([]uintptr, 0)
|
||||
|
||||
// Figure out how many levels of indirection there are by derferencing
|
||||
// pointers and unpacking interfaces down the chain while detecting circular
|
||||
// references.
|
||||
nilFound := false
|
||||
cycleFound := false
|
||||
indirects := 0
|
||||
ve := v
|
||||
for ve.Kind() == reflect.Ptr {
|
||||
if ve.IsNil() {
|
||||
nilFound = true
|
||||
break
|
||||
}
|
||||
indirects++
|
||||
addr := ve.Pointer()
|
||||
pointerChain = append(pointerChain, addr)
|
||||
if pd, ok := f.pointers[addr]; ok && pd < f.depth {
|
||||
cycleFound = true
|
||||
indirects--
|
||||
break
|
||||
}
|
||||
f.pointers[addr] = f.depth
|
||||
|
||||
ve = ve.Elem()
|
||||
if ve.Kind() == reflect.Interface {
|
||||
if ve.IsNil() {
|
||||
nilFound = true
|
||||
break
|
||||
}
|
||||
ve = ve.Elem()
|
||||
}
|
||||
}
|
||||
|
||||
// Display type or indirection level depending on flags.
|
||||
if showTypes && !f.ignoreNextType {
|
||||
f.fs.Write(openParenBytes)
|
||||
f.fs.Write(bytes.Repeat(asteriskBytes, indirects))
|
||||
f.fs.Write([]byte(ve.Type().String()))
|
||||
f.fs.Write(closeParenBytes)
|
||||
} else {
|
||||
if nilFound || cycleFound {
|
||||
indirects += strings.Count(ve.Type().String(), "*")
|
||||
}
|
||||
f.fs.Write(openAngleBytes)
|
||||
f.fs.Write([]byte(strings.Repeat("*", indirects)))
|
||||
f.fs.Write(closeAngleBytes)
|
||||
}
|
||||
|
||||
// Display pointer information depending on flags.
|
||||
if f.fs.Flag('+') && (len(pointerChain) > 0) {
|
||||
f.fs.Write(openParenBytes)
|
||||
for i, addr := range pointerChain {
|
||||
if i > 0 {
|
||||
f.fs.Write(pointerChainBytes)
|
||||
}
|
||||
printHexPtr(f.fs, addr)
|
||||
}
|
||||
f.fs.Write(closeParenBytes)
|
||||
}
|
||||
|
||||
// Display dereferenced value.
|
||||
switch {
|
||||
case nilFound:
|
||||
f.fs.Write(nilAngleBytes)
|
||||
|
||||
case cycleFound:
|
||||
f.fs.Write(circularShortBytes)
|
||||
|
||||
default:
|
||||
f.ignoreNextType = true
|
||||
f.format(ve)
|
||||
}
|
||||
}
|
||||
|
||||
// format is the main workhorse for providing the Formatter interface. It
|
||||
// uses the passed reflect value to figure out what kind of object we are
|
||||
// dealing with and formats it appropriately. It is a recursive function,
|
||||
// however circular data structures are detected and handled properly.
|
||||
func (f *formatState) format(v reflect.Value) {
|
||||
// Handle invalid reflect values immediately.
|
||||
kind := v.Kind()
|
||||
if kind == reflect.Invalid {
|
||||
f.fs.Write(invalidAngleBytes)
|
||||
return
|
||||
}
|
||||
|
||||
// Handle pointers specially.
|
||||
if kind == reflect.Ptr {
|
||||
f.formatPtr(v)
|
||||
return
|
||||
}
|
||||
|
||||
// Print type information unless already handled elsewhere.
|
||||
if !f.ignoreNextType && f.fs.Flag('#') {
|
||||
f.fs.Write(openParenBytes)
|
||||
f.fs.Write([]byte(v.Type().String()))
|
||||
f.fs.Write(closeParenBytes)
|
||||
}
|
||||
f.ignoreNextType = false
|
||||
|
||||
// Call Stringer/error interfaces if they exist and the handle methods
|
||||
// flag is enabled.
|
||||
if !f.cs.DisableMethods {
|
||||
if (kind != reflect.Invalid) && (kind != reflect.Interface) {
|
||||
if handled := handleMethods(f.cs, f.fs, v); handled {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch kind {
|
||||
case reflect.Invalid:
|
||||
// Do nothing. We should never get here since invalid has already
|
||||
// been handled above.
|
||||
|
||||
case reflect.Bool:
|
||||
printBool(f.fs, v.Bool())
|
||||
|
||||
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
|
||||
printInt(f.fs, v.Int(), 10)
|
||||
|
||||
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
|
||||
printUint(f.fs, v.Uint(), 10)
|
||||
|
||||
case reflect.Float32:
|
||||
printFloat(f.fs, v.Float(), 32)
|
||||
|
||||
case reflect.Float64:
|
||||
printFloat(f.fs, v.Float(), 64)
|
||||
|
||||
case reflect.Complex64:
|
||||
printComplex(f.fs, v.Complex(), 32)
|
||||
|
||||
case reflect.Complex128:
|
||||
printComplex(f.fs, v.Complex(), 64)
|
||||
|
||||
case reflect.Slice:
|
||||
if v.IsNil() {
|
||||
f.fs.Write(nilAngleBytes)
|
||||
break
|
||||
}
|
||||
fallthrough
|
||||
|
||||
case reflect.Array:
|
||||
f.fs.Write(openBracketBytes)
|
||||
f.depth++
|
||||
if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) {
|
||||
f.fs.Write(maxShortBytes)
|
||||
} else {
|
||||
numEntries := v.Len()
|
||||
for i := 0; i < numEntries; i++ {
|
||||
if i > 0 {
|
||||
f.fs.Write(spaceBytes)
|
||||
}
|
||||
f.ignoreNextType = true
|
||||
f.format(f.unpackValue(v.Index(i)))
|
||||
}
|
||||
}
|
||||
f.depth--
|
||||
f.fs.Write(closeBracketBytes)
|
||||
|
||||
case reflect.String:
|
||||
f.fs.Write([]byte(v.String()))
|
||||
|
||||
case reflect.Interface:
|
||||
// The only time we should get here is for nil interfaces due to
|
||||
// unpackValue calls.
|
||||
if v.IsNil() {
|
||||
f.fs.Write(nilAngleBytes)
|
||||
}
|
||||
|
||||
case reflect.Ptr:
|
||||
// Do nothing. We should never get here since pointers have already
|
||||
// been handled above.
|
||||
|
||||
case reflect.Map:
|
||||
// nil maps should be indicated as different than empty maps
|
||||
if v.IsNil() {
|
||||
f.fs.Write(nilAngleBytes)
|
||||
break
|
||||
}
|
||||
|
||||
f.fs.Write(openMapBytes)
|
||||
f.depth++
|
||||
if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) {
|
||||
f.fs.Write(maxShortBytes)
|
||||
} else {
|
||||
keys := v.MapKeys()
|
||||
if f.cs.SortKeys {
|
||||
sortValues(keys, f.cs)
|
||||
}
|
||||
for i, key := range keys {
|
||||
if i > 0 {
|
||||
f.fs.Write(spaceBytes)
|
||||
}
|
||||
f.ignoreNextType = true
|
||||
f.format(f.unpackValue(key))
|
||||
f.fs.Write(colonBytes)
|
||||
f.ignoreNextType = true
|
||||
f.format(f.unpackValue(v.MapIndex(key)))
|
||||
}
|
||||
}
|
||||
f.depth--
|
||||
f.fs.Write(closeMapBytes)
|
||||
|
||||
case reflect.Struct:
|
||||
numFields := v.NumField()
|
||||
f.fs.Write(openBraceBytes)
|
||||
f.depth++
|
||||
if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) {
|
||||
f.fs.Write(maxShortBytes)
|
||||
} else {
|
||||
vt := v.Type()
|
||||
for i := 0; i < numFields; i++ {
|
||||
if i > 0 {
|
||||
f.fs.Write(spaceBytes)
|
||||
}
|
||||
vtf := vt.Field(i)
|
||||
if f.fs.Flag('+') || f.fs.Flag('#') {
|
||||
f.fs.Write([]byte(vtf.Name))
|
||||
f.fs.Write(colonBytes)
|
||||
}
|
||||
f.format(f.unpackValue(v.Field(i)))
|
||||
}
|
||||
}
|
||||
f.depth--
|
||||
f.fs.Write(closeBraceBytes)
|
||||
|
||||
case reflect.Uintptr:
|
||||
printHexPtr(f.fs, uintptr(v.Uint()))
|
||||
|
||||
case reflect.UnsafePointer, reflect.Chan, reflect.Func:
|
||||
printHexPtr(f.fs, v.Pointer())
|
||||
|
||||
// There were not any other types at the time this code was written, but
|
||||
// fall back to letting the default fmt package handle it if any get added.
|
||||
default:
|
||||
format := f.buildDefaultFormat()
|
||||
if v.CanInterface() {
|
||||
fmt.Fprintf(f.fs, format, v.Interface())
|
||||
} else {
|
||||
fmt.Fprintf(f.fs, format, v.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Format satisfies the fmt.Formatter interface. See NewFormatter for usage
|
||||
// details.
|
||||
func (f *formatState) Format(fs fmt.State, verb rune) {
|
||||
f.fs = fs
|
||||
|
||||
// Use standard formatting for verbs that are not v.
|
||||
if verb != 'v' {
|
||||
format := f.constructOrigFormat(verb)
|
||||
fmt.Fprintf(fs, format, f.value)
|
||||
return
|
||||
}
|
||||
|
||||
if f.value == nil {
|
||||
if fs.Flag('#') {
|
||||
fs.Write(interfaceBytes)
|
||||
}
|
||||
fs.Write(nilAngleBytes)
|
||||
return
|
||||
}
|
||||
|
||||
f.format(reflect.ValueOf(f.value))
|
||||
}
|
||||
|
||||
// newFormatter is a helper function to consolidate the logic from the various
|
||||
// public methods which take varying config states.
|
||||
func newFormatter(cs *ConfigState, v interface{}) fmt.Formatter {
|
||||
fs := &formatState{value: v, cs: cs}
|
||||
fs.pointers = make(map[uintptr]int)
|
||||
return fs
|
||||
}
|
||||
|
||||
/*
|
||||
NewFormatter returns a custom formatter that satisfies the fmt.Formatter
|
||||
interface. As a result, it integrates cleanly with standard fmt package
|
||||
printing functions. The formatter is useful for inline printing of smaller data
|
||||
types similar to the standard %v format specifier.
|
||||
|
||||
The custom formatter only responds to the %v (most compact), %+v (adds pointer
|
||||
addresses), %#v (adds types), or %#+v (adds types and pointer addresses) verb
|
||||
combinations. Any other verbs such as %x and %q will be sent to the the
|
||||
standard fmt package for formatting. In addition, the custom formatter ignores
|
||||
the width and precision arguments (however they will still work on the format
|
||||
specifiers not handled by the custom formatter).
|
||||
|
||||
Typically this function shouldn't be called directly. It is much easier to make
|
||||
use of the custom formatter by calling one of the convenience functions such as
|
||||
Printf, Println, or Fprintf.
|
||||
*/
|
||||
func NewFormatter(v interface{}) fmt.Formatter {
|
||||
return newFormatter(&Config, v)
|
||||
}
|
148
vendor/github.com/davecgh/go-spew/spew/spew.go
generated
vendored
Normal file
148
vendor/github.com/davecgh/go-spew/spew/spew.go
generated
vendored
Normal file
|
@ -0,0 +1,148 @@
|
|||
/*
|
||||
* Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
package spew
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
// Errorf is a wrapper for fmt.Errorf that treats each argument as if it were
|
||||
// passed with a default Formatter interface returned by NewFormatter. It
|
||||
// returns the formatted string as a value that satisfies error. See
|
||||
// NewFormatter for formatting details.
|
||||
//
|
||||
// This function is shorthand for the following syntax:
|
||||
//
|
||||
// fmt.Errorf(format, spew.NewFormatter(a), spew.NewFormatter(b))
|
||||
func Errorf(format string, a ...interface{}) (err error) {
|
||||
return fmt.Errorf(format, convertArgs(a)...)
|
||||
}
|
||||
|
||||
// Fprint is a wrapper for fmt.Fprint that treats each argument as if it were
|
||||
// passed with a default Formatter interface returned by NewFormatter. It
|
||||
// returns the number of bytes written and any write error encountered. See
|
||||
// NewFormatter for formatting details.
|
||||
//
|
||||
// This function is shorthand for the following syntax:
|
||||
//
|
||||
// fmt.Fprint(w, spew.NewFormatter(a), spew.NewFormatter(b))
|
||||
func Fprint(w io.Writer, a ...interface{}) (n int, err error) {
|
||||
return fmt.Fprint(w, convertArgs(a)...)
|
||||
}
|
||||
|
||||
// Fprintf is a wrapper for fmt.Fprintf that treats each argument as if it were
|
||||
// passed with a default Formatter interface returned by NewFormatter. It
|
||||
// returns the number of bytes written and any write error encountered. See
|
||||
// NewFormatter for formatting details.
|
||||
//
|
||||
// This function is shorthand for the following syntax:
|
||||
//
|
||||
// fmt.Fprintf(w, format, spew.NewFormatter(a), spew.NewFormatter(b))
|
||||
func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) {
|
||||
return fmt.Fprintf(w, format, convertArgs(a)...)
|
||||
}
|
||||
|
||||
// Fprintln is a wrapper for fmt.Fprintln that treats each argument as if it
|
||||
// passed with a default Formatter interface returned by NewFormatter. See
|
||||
// NewFormatter for formatting details.
|
||||
//
|
||||
// This function is shorthand for the following syntax:
|
||||
//
|
||||
// fmt.Fprintln(w, spew.NewFormatter(a), spew.NewFormatter(b))
|
||||
func Fprintln(w io.Writer, a ...interface{}) (n int, err error) {
|
||||
return fmt.Fprintln(w, convertArgs(a)...)
|
||||
}
|
||||
|
||||
// Print is a wrapper for fmt.Print that treats each argument as if it were
|
||||
// passed with a default Formatter interface returned by NewFormatter. It
|
||||
// returns the number of bytes written and any write error encountered. See
|
||||
// NewFormatter for formatting details.
|
||||
//
|
||||
// This function is shorthand for the following syntax:
|
||||
//
|
||||
// fmt.Print(spew.NewFormatter(a), spew.NewFormatter(b))
|
||||
func Print(a ...interface{}) (n int, err error) {
|
||||
return fmt.Print(convertArgs(a)...)
|
||||
}
|
||||
|
||||
// Printf is a wrapper for fmt.Printf that treats each argument as if it were
|
||||
// passed with a default Formatter interface returned by NewFormatter. It
|
||||
// returns the number of bytes written and any write error encountered. See
|
||||
// NewFormatter for formatting details.
|
||||
//
|
||||
// This function is shorthand for the following syntax:
|
||||
//
|
||||
// fmt.Printf(format, spew.NewFormatter(a), spew.NewFormatter(b))
|
||||
func Printf(format string, a ...interface{}) (n int, err error) {
|
||||
return fmt.Printf(format, convertArgs(a)...)
|
||||
}
|
||||
|
||||
// Println is a wrapper for fmt.Println that treats each argument as if it were
|
||||
// passed with a default Formatter interface returned by NewFormatter. It
|
||||
// returns the number of bytes written and any write error encountered. See
|
||||
// NewFormatter for formatting details.
|
||||
//
|
||||
// This function is shorthand for the following syntax:
|
||||
//
|
||||
// fmt.Println(spew.NewFormatter(a), spew.NewFormatter(b))
|
||||
func Println(a ...interface{}) (n int, err error) {
|
||||
return fmt.Println(convertArgs(a)...)
|
||||
}
|
||||
|
||||
// Sprint is a wrapper for fmt.Sprint that treats each argument as if it were
|
||||
// passed with a default Formatter interface returned by NewFormatter. It
|
||||
// returns the resulting string. See NewFormatter for formatting details.
|
||||
//
|
||||
// This function is shorthand for the following syntax:
|
||||
//
|
||||
// fmt.Sprint(spew.NewFormatter(a), spew.NewFormatter(b))
|
||||
func Sprint(a ...interface{}) string {
|
||||
return fmt.Sprint(convertArgs(a)...)
|
||||
}
|
||||
|
||||
// Sprintf is a wrapper for fmt.Sprintf that treats each argument as if it were
|
||||
// passed with a default Formatter interface returned by NewFormatter. It
|
||||
// returns the resulting string. See NewFormatter for formatting details.
|
||||
//
|
||||
// This function is shorthand for the following syntax:
|
||||
//
|
||||
// fmt.Sprintf(format, spew.NewFormatter(a), spew.NewFormatter(b))
|
||||
func Sprintf(format string, a ...interface{}) string {
|
||||
return fmt.Sprintf(format, convertArgs(a)...)
|
||||
}
|
||||
|
||||
// Sprintln is a wrapper for fmt.Sprintln that treats each argument as if it
|
||||
// were passed with a default Formatter interface returned by NewFormatter. It
|
||||
// returns the resulting string. See NewFormatter for formatting details.
|
||||
//
|
||||
// This function is shorthand for the following syntax:
|
||||
//
|
||||
// fmt.Sprintln(spew.NewFormatter(a), spew.NewFormatter(b))
|
||||
func Sprintln(a ...interface{}) string {
|
||||
return fmt.Sprintln(convertArgs(a)...)
|
||||
}
|
||||
|
||||
// convertArgs accepts a slice of arguments and returns a slice of the same
|
||||
// length with each argument converted to a default spew Formatter interface.
|
||||
func convertArgs(args []interface{}) (formatters []interface{}) {
|
||||
formatters = make([]interface{}, len(args))
|
||||
for index, arg := range args {
|
||||
formatters[index] = NewFormatter(arg)
|
||||
}
|
||||
return formatters
|
||||
}
|
24
vendor/github.com/pkg/errors/.gitignore
generated
vendored
Normal file
24
vendor/github.com/pkg/errors/.gitignore
generated
vendored
Normal file
|
@ -0,0 +1,24 @@
|
|||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
*.test
|
||||
*.prof
|
15
vendor/github.com/pkg/errors/.travis.yml
generated
vendored
Normal file
15
vendor/github.com/pkg/errors/.travis.yml
generated
vendored
Normal file
|
@ -0,0 +1,15 @@
|
|||
language: go
|
||||
go_import_path: github.com/pkg/errors
|
||||
go:
|
||||
- 1.4.x
|
||||
- 1.5.x
|
||||
- 1.6.x
|
||||
- 1.7.x
|
||||
- 1.8.x
|
||||
- 1.9.x
|
||||
- 1.10.x
|
||||
- 1.11.x
|
||||
- tip
|
||||
|
||||
script:
|
||||
- go test -v ./...
|
23
vendor/github.com/pkg/errors/LICENSE
generated
vendored
Normal file
23
vendor/github.com/pkg/errors/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,23 @@
|
|||
Copyright (c) 2015, Dave Cheney <dave@cheney.net>
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* 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.
|
||||
|
||||
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 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.
|
52
vendor/github.com/pkg/errors/README.md
generated
vendored
Normal file
52
vendor/github.com/pkg/errors/README.md
generated
vendored
Normal file
|
@ -0,0 +1,52 @@
|
|||
# errors [](https://travis-ci.org/pkg/errors) [](https://ci.appveyor.com/project/davecheney/errors/branch/master) [](http://godoc.org/github.com/pkg/errors) [](https://goreportcard.com/report/github.com/pkg/errors) [](https://sourcegraph.com/github.com/pkg/errors?badge)
|
||||
|
||||
Package errors provides simple error handling primitives.
|
||||
|
||||
`go get github.com/pkg/errors`
|
||||
|
||||
The traditional error handling idiom in Go is roughly akin to
|
||||
```go
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
```
|
||||
which applied recursively up the call stack results in error reports without context or debugging information. The errors package allows programmers to add context to the failure path in their code in a way that does not destroy the original value of the error.
|
||||
|
||||
## Adding context to an error
|
||||
|
||||
The errors.Wrap function returns a new error that adds context to the original error. For example
|
||||
```go
|
||||
_, err := ioutil.ReadAll(r)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "read failed")
|
||||
}
|
||||
```
|
||||
## Retrieving the cause of an error
|
||||
|
||||
Using `errors.Wrap` constructs a stack of errors, adding context to the preceding error. Depending on the nature of the error it may be necessary to reverse the operation of errors.Wrap to retrieve the original error for inspection. Any error value which implements this interface can be inspected by `errors.Cause`.
|
||||
```go
|
||||
type causer interface {
|
||||
Cause() error
|
||||
}
|
||||
```
|
||||
`errors.Cause` will recursively retrieve the topmost error which does not implement `causer`, which is assumed to be the original cause. For example:
|
||||
```go
|
||||
switch err := errors.Cause(err).(type) {
|
||||
case *MyError:
|
||||
// handle specifically
|
||||
default:
|
||||
// unknown error
|
||||
}
|
||||
```
|
||||
|
||||
[Read the package documentation for more information](https://godoc.org/github.com/pkg/errors).
|
||||
|
||||
## Contributing
|
||||
|
||||
We welcome pull requests, bug fixes and issue reports. With that said, the bar for adding new symbols to this package is intentionally set high.
|
||||
|
||||
Before proposing a change, please discuss your change by raising an issue.
|
||||
|
||||
## License
|
||||
|
||||
BSD-2-Clause
|
32
vendor/github.com/pkg/errors/appveyor.yml
generated
vendored
Normal file
32
vendor/github.com/pkg/errors/appveyor.yml
generated
vendored
Normal file
|
@ -0,0 +1,32 @@
|
|||
version: build-{build}.{branch}
|
||||
|
||||
clone_folder: C:\gopath\src\github.com\pkg\errors
|
||||
shallow_clone: true # for startup speed
|
||||
|
||||
environment:
|
||||
GOPATH: C:\gopath
|
||||
|
||||
platform:
|
||||
- x64
|
||||
|
||||
# http://www.appveyor.com/docs/installed-software
|
||||
install:
|
||||
# some helpful output for debugging builds
|
||||
- go version
|
||||
- go env
|
||||
# pre-installed MinGW at C:\MinGW is 32bit only
|
||||
# but MSYS2 at C:\msys64 has mingw64
|
||||
- set PATH=C:\msys64\mingw64\bin;%PATH%
|
||||
- gcc --version
|
||||
- g++ --version
|
||||
|
||||
build_script:
|
||||
- go install -v ./...
|
||||
|
||||
test_script:
|
||||
- set PATH=C:\gopath\bin;%PATH%
|
||||
- go test -v ./...
|
||||
|
||||
#artifacts:
|
||||
# - path: '%GOPATH%\bin\*.exe'
|
||||
deploy: off
|
282
vendor/github.com/pkg/errors/errors.go
generated
vendored
Normal file
282
vendor/github.com/pkg/errors/errors.go
generated
vendored
Normal file
|
@ -0,0 +1,282 @@
|
|||
// Package errors provides simple error handling primitives.
|
||||
//
|
||||
// The traditional error handling idiom in Go is roughly akin to
|
||||
//
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
//
|
||||
// which when applied recursively up the call stack results in error reports
|
||||
// without context or debugging information. The errors package allows
|
||||
// programmers to add context to the failure path in their code in a way
|
||||
// that does not destroy the original value of the error.
|
||||
//
|
||||
// Adding context to an error
|
||||
//
|
||||
// The errors.Wrap function returns a new error that adds context to the
|
||||
// original error by recording a stack trace at the point Wrap is called,
|
||||
// together with the supplied message. For example
|
||||
//
|
||||
// _, err := ioutil.ReadAll(r)
|
||||
// if err != nil {
|
||||
// return errors.Wrap(err, "read failed")
|
||||
// }
|
||||
//
|
||||
// If additional control is required, the errors.WithStack and
|
||||
// errors.WithMessage functions destructure errors.Wrap into its component
|
||||
// operations: annotating an error with a stack trace and with a message,
|
||||
// respectively.
|
||||
//
|
||||
// Retrieving the cause of an error
|
||||
//
|
||||
// Using errors.Wrap constructs a stack of errors, adding context to the
|
||||
// preceding error. Depending on the nature of the error it may be necessary
|
||||
// to reverse the operation of errors.Wrap to retrieve the original error
|
||||
// for inspection. Any error value which implements this interface
|
||||
//
|
||||
// type causer interface {
|
||||
// Cause() error
|
||||
// }
|
||||
//
|
||||
// can be inspected by errors.Cause. errors.Cause will recursively retrieve
|
||||
// the topmost error that does not implement causer, which is assumed to be
|
||||
// the original cause. For example:
|
||||
//
|
||||
// switch err := errors.Cause(err).(type) {
|
||||
// case *MyError:
|
||||
// // handle specifically
|
||||
// default:
|
||||
// // unknown error
|
||||
// }
|
||||
//
|
||||
// Although the causer interface is not exported by this package, it is
|
||||
// considered a part of its stable public interface.
|
||||
//
|
||||
// Formatted printing of errors
|
||||
//
|
||||
// All error values returned from this package implement fmt.Formatter and can
|
||||
// be formatted by the fmt package. The following verbs are supported:
|
||||
//
|
||||
// %s print the error. If the error has a Cause it will be
|
||||
// printed recursively.
|
||||
// %v see %s
|
||||
// %+v extended format. Each Frame of the error's StackTrace will
|
||||
// be printed in detail.
|
||||
//
|
||||
// Retrieving the stack trace of an error or wrapper
|
||||
//
|
||||
// New, Errorf, Wrap, and Wrapf record a stack trace at the point they are
|
||||
// invoked. This information can be retrieved with the following interface:
|
||||
//
|
||||
// type stackTracer interface {
|
||||
// StackTrace() errors.StackTrace
|
||||
// }
|
||||
//
|
||||
// The returned errors.StackTrace type is defined as
|
||||
//
|
||||
// type StackTrace []Frame
|
||||
//
|
||||
// The Frame type represents a call site in the stack trace. Frame supports
|
||||
// the fmt.Formatter interface that can be used for printing information about
|
||||
// the stack trace of this error. For example:
|
||||
//
|
||||
// if err, ok := err.(stackTracer); ok {
|
||||
// for _, f := range err.StackTrace() {
|
||||
// fmt.Printf("%+s:%d", f)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// Although the stackTracer interface is not exported by this package, it is
|
||||
// considered a part of its stable public interface.
|
||||
//
|
||||
// See the documentation for Frame.Format for more details.
|
||||
package errors
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
// New returns an error with the supplied message.
|
||||
// New also records the stack trace at the point it was called.
|
||||
func New(message string) error {
|
||||
return &fundamental{
|
||||
msg: message,
|
||||
stack: callers(),
|
||||
}
|
||||
}
|
||||
|
||||
// Errorf formats according to a format specifier and returns the string
|
||||
// as a value that satisfies error.
|
||||
// Errorf also records the stack trace at the point it was called.
|
||||
func Errorf(format string, args ...interface{}) error {
|
||||
return &fundamental{
|
||||
msg: fmt.Sprintf(format, args...),
|
||||
stack: callers(),
|
||||
}
|
||||
}
|
||||
|
||||
// fundamental is an error that has a message and a stack, but no caller.
|
||||
type fundamental struct {
|
||||
msg string
|
||||
*stack
|
||||
}
|
||||
|
||||
func (f *fundamental) Error() string { return f.msg }
|
||||
|
||||
func (f *fundamental) Format(s fmt.State, verb rune) {
|
||||
switch verb {
|
||||
case 'v':
|
||||
if s.Flag('+') {
|
||||
io.WriteString(s, f.msg)
|
||||
f.stack.Format(s, verb)
|
||||
return
|
||||
}
|
||||
fallthrough
|
||||
case 's':
|
||||
io.WriteString(s, f.msg)
|
||||
case 'q':
|
||||
fmt.Fprintf(s, "%q", f.msg)
|
||||
}
|
||||
}
|
||||
|
||||
// WithStack annotates err with a stack trace at the point WithStack was called.
|
||||
// If err is nil, WithStack returns nil.
|
||||
func WithStack(err error) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
return &withStack{
|
||||
err,
|
||||
callers(),
|
||||
}
|
||||
}
|
||||
|
||||
type withStack struct {
|
||||
error
|
||||
*stack
|
||||
}
|
||||
|
||||
func (w *withStack) Cause() error { return w.error }
|
||||
|
||||
func (w *withStack) Format(s fmt.State, verb rune) {
|
||||
switch verb {
|
||||
case 'v':
|
||||
if s.Flag('+') {
|
||||
fmt.Fprintf(s, "%+v", w.Cause())
|
||||
w.stack.Format(s, verb)
|
||||
return
|
||||
}
|
||||
fallthrough
|
||||
case 's':
|
||||
io.WriteString(s, w.Error())
|
||||
case 'q':
|
||||
fmt.Fprintf(s, "%q", w.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// Wrap returns an error annotating err with a stack trace
|
||||
// at the point Wrap is called, and the supplied message.
|
||||
// If err is nil, Wrap returns nil.
|
||||
func Wrap(err error, message string) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
err = &withMessage{
|
||||
cause: err,
|
||||
msg: message,
|
||||
}
|
||||
return &withStack{
|
||||
err,
|
||||
callers(),
|
||||
}
|
||||
}
|
||||
|
||||
// Wrapf returns an error annotating err with a stack trace
|
||||
// at the point Wrapf is called, and the format specifier.
|
||||
// If err is nil, Wrapf returns nil.
|
||||
func Wrapf(err error, format string, args ...interface{}) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
err = &withMessage{
|
||||
cause: err,
|
||||
msg: fmt.Sprintf(format, args...),
|
||||
}
|
||||
return &withStack{
|
||||
err,
|
||||
callers(),
|
||||
}
|
||||
}
|
||||
|
||||
// WithMessage annotates err with a new message.
|
||||
// If err is nil, WithMessage returns nil.
|
||||
func WithMessage(err error, message string) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
return &withMessage{
|
||||
cause: err,
|
||||
msg: message,
|
||||
}
|
||||
}
|
||||
|
||||
// WithMessagef annotates err with the format specifier.
|
||||
// If err is nil, WithMessagef returns nil.
|
||||
func WithMessagef(err error, format string, args ...interface{}) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
return &withMessage{
|
||||
cause: err,
|
||||
msg: fmt.Sprintf(format, args...),
|
||||
}
|
||||
}
|
||||
|
||||
type withMessage struct {
|
||||
cause error
|
||||
msg string
|
||||
}
|
||||
|
||||
func (w *withMessage) Error() string { return w.msg + ": " + w.cause.Error() }
|
||||
func (w *withMessage) Cause() error { return w.cause }
|
||||
|
||||
func (w *withMessage) Format(s fmt.State, verb rune) {
|
||||
switch verb {
|
||||
case 'v':
|
||||
if s.Flag('+') {
|
||||
fmt.Fprintf(s, "%+v\n", w.Cause())
|
||||
io.WriteString(s, w.msg)
|
||||
return
|
||||
}
|
||||
fallthrough
|
||||
case 's', 'q':
|
||||
io.WriteString(s, w.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// Cause returns the underlying cause of the error, if possible.
|
||||
// An error value has a cause if it implements the following
|
||||
// interface:
|
||||
//
|
||||
// type causer interface {
|
||||
// Cause() error
|
||||
// }
|
||||
//
|
||||
// If the error does not implement Cause, the original error will
|
||||
// be returned. If the error is nil, nil will be returned without further
|
||||
// investigation.
|
||||
func Cause(err error) error {
|
||||
type causer interface {
|
||||
Cause() error
|
||||
}
|
||||
|
||||
for err != nil {
|
||||
cause, ok := err.(causer)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
err = cause.Cause()
|
||||
}
|
||||
return err
|
||||
}
|
147
vendor/github.com/pkg/errors/stack.go
generated
vendored
Normal file
147
vendor/github.com/pkg/errors/stack.go
generated
vendored
Normal file
|
@ -0,0 +1,147 @@
|
|||
package errors
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"path"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Frame represents a program counter inside a stack frame.
|
||||
type Frame uintptr
|
||||
|
||||
// pc returns the program counter for this frame;
|
||||
// multiple frames may have the same PC value.
|
||||
func (f Frame) pc() uintptr { return uintptr(f) - 1 }
|
||||
|
||||
// file returns the full path to the file that contains the
|
||||
// function for this Frame's pc.
|
||||
func (f Frame) file() string {
|
||||
fn := runtime.FuncForPC(f.pc())
|
||||
if fn == nil {
|
||||
return "unknown"
|
||||
}
|
||||
file, _ := fn.FileLine(f.pc())
|
||||
return file
|
||||
}
|
||||
|
||||
// line returns the line number of source code of the
|
||||
// function for this Frame's pc.
|
||||
func (f Frame) line() int {
|
||||
fn := runtime.FuncForPC(f.pc())
|
||||
if fn == nil {
|
||||
return 0
|
||||
}
|
||||
_, line := fn.FileLine(f.pc())
|
||||
return line
|
||||
}
|
||||
|
||||
// Format formats the frame according to the fmt.Formatter interface.
|
||||
//
|
||||
// %s source file
|
||||
// %d source line
|
||||
// %n function name
|
||||
// %v equivalent to %s:%d
|
||||
//
|
||||
// Format accepts flags that alter the printing of some verbs, as follows:
|
||||
//
|
||||
// %+s function name and path of source file relative to the compile time
|
||||
// GOPATH separated by \n\t (<funcname>\n\t<path>)
|
||||
// %+v equivalent to %+s:%d
|
||||
func (f Frame) Format(s fmt.State, verb rune) {
|
||||
switch verb {
|
||||
case 's':
|
||||
switch {
|
||||
case s.Flag('+'):
|
||||
pc := f.pc()
|
||||
fn := runtime.FuncForPC(pc)
|
||||
if fn == nil {
|
||||
io.WriteString(s, "unknown")
|
||||
} else {
|
||||
file, _ := fn.FileLine(pc)
|
||||
fmt.Fprintf(s, "%s\n\t%s", fn.Name(), file)
|
||||
}
|
||||
default:
|
||||
io.WriteString(s, path.Base(f.file()))
|
||||
}
|
||||
case 'd':
|
||||
fmt.Fprintf(s, "%d", f.line())
|
||||
case 'n':
|
||||
name := runtime.FuncForPC(f.pc()).Name()
|
||||
io.WriteString(s, funcname(name))
|
||||
case 'v':
|
||||
f.Format(s, 's')
|
||||
io.WriteString(s, ":")
|
||||
f.Format(s, 'd')
|
||||
}
|
||||
}
|
||||
|
||||
// StackTrace is stack of Frames from innermost (newest) to outermost (oldest).
|
||||
type StackTrace []Frame
|
||||
|
||||
// Format formats the stack of Frames according to the fmt.Formatter interface.
|
||||
//
|
||||
// %s lists source files for each Frame in the stack
|
||||
// %v lists the source file and line number for each Frame in the stack
|
||||
//
|
||||
// Format accepts flags that alter the printing of some verbs, as follows:
|
||||
//
|
||||
// %+v Prints filename, function, and line number for each Frame in the stack.
|
||||
func (st StackTrace) Format(s fmt.State, verb rune) {
|
||||
switch verb {
|
||||
case 'v':
|
||||
switch {
|
||||
case s.Flag('+'):
|
||||
for _, f := range st {
|
||||
fmt.Fprintf(s, "\n%+v", f)
|
||||
}
|
||||
case s.Flag('#'):
|
||||
fmt.Fprintf(s, "%#v", []Frame(st))
|
||||
default:
|
||||
fmt.Fprintf(s, "%v", []Frame(st))
|
||||
}
|
||||
case 's':
|
||||
fmt.Fprintf(s, "%s", []Frame(st))
|
||||
}
|
||||
}
|
||||
|
||||
// stack represents a stack of program counters.
|
||||
type stack []uintptr
|
||||
|
||||
func (s *stack) Format(st fmt.State, verb rune) {
|
||||
switch verb {
|
||||
case 'v':
|
||||
switch {
|
||||
case st.Flag('+'):
|
||||
for _, pc := range *s {
|
||||
f := Frame(pc)
|
||||
fmt.Fprintf(st, "\n%+v", f)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *stack) StackTrace() StackTrace {
|
||||
f := make([]Frame, len(*s))
|
||||
for i := 0; i < len(f); i++ {
|
||||
f[i] = Frame((*s)[i])
|
||||
}
|
||||
return f
|
||||
}
|
||||
|
||||
func callers() *stack {
|
||||
const depth = 32
|
||||
var pcs [depth]uintptr
|
||||
n := runtime.Callers(3, pcs[:])
|
||||
var st stack = pcs[0:n]
|
||||
return &st
|
||||
}
|
||||
|
||||
// funcname removes the path prefix component of a function's name reported by func.Name().
|
||||
func funcname(name string) string {
|
||||
i := strings.LastIndex(name, "/")
|
||||
name = name[i+1:]
|
||||
i = strings.Index(name, ".")
|
||||
return name[i+1:]
|
||||
}
|
27
vendor/github.com/pmezard/go-difflib/LICENSE
generated
vendored
Normal file
27
vendor/github.com/pmezard/go-difflib/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,27 @@
|
|||
Copyright (c) 2013, Patrick Mezard
|
||||
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.
|
||||
The names of its contributors may not 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
|
||||
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.
|
772
vendor/github.com/pmezard/go-difflib/difflib/difflib.go
generated
vendored
Normal file
772
vendor/github.com/pmezard/go-difflib/difflib/difflib.go
generated
vendored
Normal file
|
@ -0,0 +1,772 @@
|
|||
// Package difflib is a partial port of Python difflib module.
|
||||
//
|
||||
// It provides tools to compare sequences of strings and generate textual diffs.
|
||||
//
|
||||
// The following class and functions have been ported:
|
||||
//
|
||||
// - SequenceMatcher
|
||||
//
|
||||
// - unified_diff
|
||||
//
|
||||
// - context_diff
|
||||
//
|
||||
// Getting unified diffs was the main goal of the port. Keep in mind this code
|
||||
// is mostly suitable to output text differences in a human friendly way, there
|
||||
// are no guarantees generated diffs are consumable by patch(1).
|
||||
package difflib
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func min(a, b int) int {
|
||||
if a < b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func max(a, b int) int {
|
||||
if a > b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func calculateRatio(matches, length int) float64 {
|
||||
if length > 0 {
|
||||
return 2.0 * float64(matches) / float64(length)
|
||||
}
|
||||
return 1.0
|
||||
}
|
||||
|
||||
type Match struct {
|
||||
A int
|
||||
B int
|
||||
Size int
|
||||
}
|
||||
|
||||
type OpCode struct {
|
||||
Tag byte
|
||||
I1 int
|
||||
I2 int
|
||||
J1 int
|
||||
J2 int
|
||||
}
|
||||
|
||||
// SequenceMatcher compares sequence of strings. The basic
|
||||
// algorithm predates, and is a little fancier than, an algorithm
|
||||
// published in the late 1980's by Ratcliff and Obershelp under the
|
||||
// hyperbolic name "gestalt pattern matching". The basic idea is to find
|
||||
// the longest contiguous matching subsequence that contains no "junk"
|
||||
// elements (R-O doesn't address junk). The same idea is then applied
|
||||
// recursively to the pieces of the sequences to the left and to the right
|
||||
// of the matching subsequence. This does not yield minimal edit
|
||||
// sequences, but does tend to yield matches that "look right" to people.
|
||||
//
|
||||
// SequenceMatcher tries to compute a "human-friendly diff" between two
|
||||
// sequences. Unlike e.g. UNIX(tm) diff, the fundamental notion is the
|
||||
// longest *contiguous* & junk-free matching subsequence. That's what
|
||||
// catches peoples' eyes. The Windows(tm) windiff has another interesting
|
||||
// notion, pairing up elements that appear uniquely in each sequence.
|
||||
// That, and the method here, appear to yield more intuitive difference
|
||||
// reports than does diff. This method appears to be the least vulnerable
|
||||
// to synching up on blocks of "junk lines", though (like blank lines in
|
||||
// ordinary text files, or maybe "<P>" lines in HTML files). That may be
|
||||
// because this is the only method of the 3 that has a *concept* of
|
||||
// "junk" <wink>.
|
||||
//
|
||||
// Timing: Basic R-O is cubic time worst case and quadratic time expected
|
||||
// case. SequenceMatcher is quadratic time for the worst case and has
|
||||
// expected-case behavior dependent in a complicated way on how many
|
||||
// elements the sequences have in common; best case time is linear.
|
||||
type SequenceMatcher struct {
|
||||
a []string
|
||||
b []string
|
||||
b2j map[string][]int
|
||||
IsJunk func(string) bool
|
||||
autoJunk bool
|
||||
bJunk map[string]struct{}
|
||||
matchingBlocks []Match
|
||||
fullBCount map[string]int
|
||||
bPopular map[string]struct{}
|
||||
opCodes []OpCode
|
||||
}
|
||||
|
||||
func NewMatcher(a, b []string) *SequenceMatcher {
|
||||
m := SequenceMatcher{autoJunk: true}
|
||||
m.SetSeqs(a, b)
|
||||
return &m
|
||||
}
|
||||
|
||||
func NewMatcherWithJunk(a, b []string, autoJunk bool,
|
||||
isJunk func(string) bool) *SequenceMatcher {
|
||||
|
||||
m := SequenceMatcher{IsJunk: isJunk, autoJunk: autoJunk}
|
||||
m.SetSeqs(a, b)
|
||||
return &m
|
||||
}
|
||||
|
||||
// Set two sequences to be compared.
|
||||
func (m *SequenceMatcher) SetSeqs(a, b []string) {
|
||||
m.SetSeq1(a)
|
||||
m.SetSeq2(b)
|
||||
}
|
||||
|
||||
// Set the first sequence to be compared. The second sequence to be compared is
|
||||
// not changed.
|
||||
//
|
||||
// SequenceMatcher computes and caches detailed information about the second
|
||||
// sequence, so if you want to compare one sequence S against many sequences,
|
||||
// use .SetSeq2(s) once and call .SetSeq1(x) repeatedly for each of the other
|
||||
// sequences.
|
||||
//
|
||||
// See also SetSeqs() and SetSeq2().
|
||||
func (m *SequenceMatcher) SetSeq1(a []string) {
|
||||
if &a == &m.a {
|
||||
return
|
||||
}
|
||||
m.a = a
|
||||
m.matchingBlocks = nil
|
||||
m.opCodes = nil
|
||||
}
|
||||
|
||||
// Set the second sequence to be compared. The first sequence to be compared is
|
||||
// not changed.
|
||||
func (m *SequenceMatcher) SetSeq2(b []string) {
|
||||
if &b == &m.b {
|
||||
return
|
||||
}
|
||||
m.b = b
|
||||
m.matchingBlocks = nil
|
||||
m.opCodes = nil
|
||||
m.fullBCount = nil
|
||||
m.chainB()
|
||||
}
|
||||
|
||||
func (m *SequenceMatcher) chainB() {
|
||||
// Populate line -> index mapping
|
||||
b2j := map[string][]int{}
|
||||
for i, s := range m.b {
|
||||
indices := b2j[s]
|
||||
indices = append(indices, i)
|
||||
b2j[s] = indices
|
||||
}
|
||||
|
||||
// Purge junk elements
|
||||
m.bJunk = map[string]struct{}{}
|
||||
if m.IsJunk != nil {
|
||||
junk := m.bJunk
|
||||
for s, _ := range b2j {
|
||||
if m.IsJunk(s) {
|
||||
junk[s] = struct{}{}
|
||||
}
|
||||
}
|
||||
for s, _ := range junk {
|
||||
delete(b2j, s)
|
||||
}
|
||||
}
|
||||
|
||||
// Purge remaining popular elements
|
||||
popular := map[string]struct{}{}
|
||||
n := len(m.b)
|
||||
if m.autoJunk && n >= 200 {
|
||||
ntest := n/100 + 1
|
||||
for s, indices := range b2j {
|
||||
if len(indices) > ntest {
|
||||
popular[s] = struct{}{}
|
||||
}
|
||||
}
|
||||
for s, _ := range popular {
|
||||
delete(b2j, s)
|
||||
}
|
||||
}
|
||||
m.bPopular = popular
|
||||
m.b2j = b2j
|
||||
}
|
||||
|
||||
func (m *SequenceMatcher) isBJunk(s string) bool {
|
||||
_, ok := m.bJunk[s]
|
||||
return ok
|
||||
}
|
||||
|
||||
// Find longest matching block in a[alo:ahi] and b[blo:bhi].
|
||||
//
|
||||
// If IsJunk is not defined:
|
||||
//
|
||||
// Return (i,j,k) such that a[i:i+k] is equal to b[j:j+k], where
|
||||
// alo <= i <= i+k <= ahi
|
||||
// blo <= j <= j+k <= bhi
|
||||
// and for all (i',j',k') meeting those conditions,
|
||||
// k >= k'
|
||||
// i <= i'
|
||||
// and if i == i', j <= j'
|
||||
//
|
||||
// In other words, of all maximal matching blocks, return one that
|
||||
// starts earliest in a, and of all those maximal matching blocks that
|
||||
// start earliest in a, return the one that starts earliest in b.
|
||||
//
|
||||
// If IsJunk is defined, first the longest matching block is
|
||||
// determined as above, but with the additional restriction that no
|
||||
// junk element appears in the block. Then that block is extended as
|
||||
// far as possible by matching (only) junk elements on both sides. So
|
||||
// the resulting block never matches on junk except as identical junk
|
||||
// happens to be adjacent to an "interesting" match.
|
||||
//
|
||||
// If no blocks match, return (alo, blo, 0).
|
||||
func (m *SequenceMatcher) findLongestMatch(alo, ahi, blo, bhi int) Match {
|
||||
// CAUTION: stripping common prefix or suffix would be incorrect.
|
||||
// E.g.,
|
||||
// ab
|
||||
// acab
|
||||
// Longest matching block is "ab", but if common prefix is
|
||||
// stripped, it's "a" (tied with "b"). UNIX(tm) diff does so
|
||||
// strip, so ends up claiming that ab is changed to acab by
|
||||
// inserting "ca" in the middle. That's minimal but unintuitive:
|
||||
// "it's obvious" that someone inserted "ac" at the front.
|
||||
// Windiff ends up at the same place as diff, but by pairing up
|
||||
// the unique 'b's and then matching the first two 'a's.
|
||||
besti, bestj, bestsize := alo, blo, 0
|
||||
|
||||
// find longest junk-free match
|
||||
// during an iteration of the loop, j2len[j] = length of longest
|
||||
// junk-free match ending with a[i-1] and b[j]
|
||||
j2len := map[int]int{}
|
||||
for i := alo; i != ahi; i++ {
|
||||
// look at all instances of a[i] in b; note that because
|
||||
// b2j has no junk keys, the loop is skipped if a[i] is junk
|
||||
newj2len := map[int]int{}
|
||||
for _, j := range m.b2j[m.a[i]] {
|
||||
// a[i] matches b[j]
|
||||
if j < blo {
|
||||
continue
|
||||
}
|
||||
if j >= bhi {
|
||||
break
|
||||
}
|
||||
k := j2len[j-1] + 1
|
||||
newj2len[j] = k
|
||||
if k > bestsize {
|
||||
besti, bestj, bestsize = i-k+1, j-k+1, k
|
||||
}
|
||||
}
|
||||
j2len = newj2len
|
||||
}
|
||||
|
||||
// Extend the best by non-junk elements on each end. In particular,
|
||||
// "popular" non-junk elements aren't in b2j, which greatly speeds
|
||||
// the inner loop above, but also means "the best" match so far
|
||||
// doesn't contain any junk *or* popular non-junk elements.
|
||||
for besti > alo && bestj > blo && !m.isBJunk(m.b[bestj-1]) &&
|
||||
m.a[besti-1] == m.b[bestj-1] {
|
||||
besti, bestj, bestsize = besti-1, bestj-1, bestsize+1
|
||||
}
|
||||
for besti+bestsize < ahi && bestj+bestsize < bhi &&
|
||||
!m.isBJunk(m.b[bestj+bestsize]) &&
|
||||
m.a[besti+bestsize] == m.b[bestj+bestsize] {
|
||||
bestsize += 1
|
||||
}
|
||||
|
||||
// Now that we have a wholly interesting match (albeit possibly
|
||||
// empty!), we may as well suck up the matching junk on each
|
||||
// side of it too. Can't think of a good reason not to, and it
|
||||
// saves post-processing the (possibly considerable) expense of
|
||||
// figuring out what to do with it. In the case of an empty
|
||||
// interesting match, this is clearly the right thing to do,
|
||||
// because no other kind of match is possible in the regions.
|
||||
for besti > alo && bestj > blo && m.isBJunk(m.b[bestj-1]) &&
|
||||
m.a[besti-1] == m.b[bestj-1] {
|
||||
besti, bestj, bestsize = besti-1, bestj-1, bestsize+1
|
||||
}
|
||||
for besti+bestsize < ahi && bestj+bestsize < bhi &&
|
||||
m.isBJunk(m.b[bestj+bestsize]) &&
|
||||
m.a[besti+bestsize] == m.b[bestj+bestsize] {
|
||||
bestsize += 1
|
||||
}
|
||||
|
||||
return Match{A: besti, B: bestj, Size: bestsize}
|
||||
}
|
||||
|
||||
// Return list of triples describing matching subsequences.
|
||||
//
|
||||
// Each triple is of the form (i, j, n), and means that
|
||||
// a[i:i+n] == b[j:j+n]. The triples are monotonically increasing in
|
||||
// i and in j. It's also guaranteed that if (i, j, n) and (i', j', n') are
|
||||
// adjacent triples in the list, and the second is not the last triple in the
|
||||
// list, then i+n != i' or j+n != j'. IOW, adjacent triples never describe
|
||||
// adjacent equal blocks.
|
||||
//
|
||||
// The last triple is a dummy, (len(a), len(b), 0), and is the only
|
||||
// triple with n==0.
|
||||
func (m *SequenceMatcher) GetMatchingBlocks() []Match {
|
||||
if m.matchingBlocks != nil {
|
||||
return m.matchingBlocks
|
||||
}
|
||||
|
||||
var matchBlocks func(alo, ahi, blo, bhi int, matched []Match) []Match
|
||||
matchBlocks = func(alo, ahi, blo, bhi int, matched []Match) []Match {
|
||||
match := m.findLongestMatch(alo, ahi, blo, bhi)
|
||||
i, j, k := match.A, match.B, match.Size
|
||||
if match.Size > 0 {
|
||||
if alo < i && blo < j {
|
||||
matched = matchBlocks(alo, i, blo, j, matched)
|
||||
}
|
||||
matched = append(matched, match)
|
||||
if i+k < ahi && j+k < bhi {
|
||||
matched = matchBlocks(i+k, ahi, j+k, bhi, matched)
|
||||
}
|
||||
}
|
||||
return matched
|
||||
}
|
||||
matched := matchBlocks(0, len(m.a), 0, len(m.b), nil)
|
||||
|
||||
// It's possible that we have adjacent equal blocks in the
|
||||
// matching_blocks list now.
|
||||
nonAdjacent := []Match{}
|
||||
i1, j1, k1 := 0, 0, 0
|
||||
for _, b := range matched {
|
||||
// Is this block adjacent to i1, j1, k1?
|
||||
i2, j2, k2 := b.A, b.B, b.Size
|
||||
if i1+k1 == i2 && j1+k1 == j2 {
|
||||
// Yes, so collapse them -- this just increases the length of
|
||||
// the first block by the length of the second, and the first
|
||||
// block so lengthened remains the block to compare against.
|
||||
k1 += k2
|
||||
} else {
|
||||
// Not adjacent. Remember the first block (k1==0 means it's
|
||||
// the dummy we started with), and make the second block the
|
||||
// new block to compare against.
|
||||
if k1 > 0 {
|
||||
nonAdjacent = append(nonAdjacent, Match{i1, j1, k1})
|
||||
}
|
||||
i1, j1, k1 = i2, j2, k2
|
||||
}
|
||||
}
|
||||
if k1 > 0 {
|
||||
nonAdjacent = append(nonAdjacent, Match{i1, j1, k1})
|
||||
}
|
||||
|
||||
nonAdjacent = append(nonAdjacent, Match{len(m.a), len(m.b), 0})
|
||||
m.matchingBlocks = nonAdjacent
|
||||
return m.matchingBlocks
|
||||
}
|
||||
|
||||
// Return list of 5-tuples describing how to turn a into b.
|
||||
//
|
||||
// Each tuple is of the form (tag, i1, i2, j1, j2). The first tuple
|
||||
// has i1 == j1 == 0, and remaining tuples have i1 == the i2 from the
|
||||
// tuple preceding it, and likewise for j1 == the previous j2.
|
||||
//
|
||||
// The tags are characters, with these meanings:
|
||||
//
|
||||
// 'r' (replace): a[i1:i2] should be replaced by b[j1:j2]
|
||||
//
|
||||
// 'd' (delete): a[i1:i2] should be deleted, j1==j2 in this case.
|
||||
//
|
||||
// 'i' (insert): b[j1:j2] should be inserted at a[i1:i1], i1==i2 in this case.
|
||||
//
|
||||
// 'e' (equal): a[i1:i2] == b[j1:j2]
|
||||
func (m *SequenceMatcher) GetOpCodes() []OpCode {
|
||||
if m.opCodes != nil {
|
||||
return m.opCodes
|
||||
}
|
||||
i, j := 0, 0
|
||||
matching := m.GetMatchingBlocks()
|
||||
opCodes := make([]OpCode, 0, len(matching))
|
||||
for _, m := range matching {
|
||||
// invariant: we've pumped out correct diffs to change
|
||||
// a[:i] into b[:j], and the next matching block is
|
||||
// a[ai:ai+size] == b[bj:bj+size]. So we need to pump
|
||||
// out a diff to change a[i:ai] into b[j:bj], pump out
|
||||
// the matching block, and move (i,j) beyond the match
|
||||
ai, bj, size := m.A, m.B, m.Size
|
||||
tag := byte(0)
|
||||
if i < ai && j < bj {
|
||||
tag = 'r'
|
||||
} else if i < ai {
|
||||
tag = 'd'
|
||||
} else if j < bj {
|
||||
tag = 'i'
|
||||
}
|
||||
if tag > 0 {
|
||||
opCodes = append(opCodes, OpCode{tag, i, ai, j, bj})
|
||||
}
|
||||
i, j = ai+size, bj+size
|
||||
// the list of matching blocks is terminated by a
|
||||
// sentinel with size 0
|
||||
if size > 0 {
|
||||
opCodes = append(opCodes, OpCode{'e', ai, i, bj, j})
|
||||
}
|
||||
}
|
||||
m.opCodes = opCodes
|
||||
return m.opCodes
|
||||
}
|
||||
|
||||
// Isolate change clusters by eliminating ranges with no changes.
|
||||
//
|
||||
// Return a generator of groups with up to n lines of context.
|
||||
// Each group is in the same format as returned by GetOpCodes().
|
||||
func (m *SequenceMatcher) GetGroupedOpCodes(n int) [][]OpCode {
|
||||
if n < 0 {
|
||||
n = 3
|
||||
}
|
||||
codes := m.GetOpCodes()
|
||||
if len(codes) == 0 {
|
||||
codes = []OpCode{OpCode{'e', 0, 1, 0, 1}}
|
||||
}
|
||||
// Fixup leading and trailing groups if they show no changes.
|
||||
if codes[0].Tag == 'e' {
|
||||
c := codes[0]
|
||||
i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2
|
||||
codes[0] = OpCode{c.Tag, max(i1, i2-n), i2, max(j1, j2-n), j2}
|
||||
}
|
||||
if codes[len(codes)-1].Tag == 'e' {
|
||||
c := codes[len(codes)-1]
|
||||
i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2
|
||||
codes[len(codes)-1] = OpCode{c.Tag, i1, min(i2, i1+n), j1, min(j2, j1+n)}
|
||||
}
|
||||
nn := n + n
|
||||
groups := [][]OpCode{}
|
||||
group := []OpCode{}
|
||||
for _, c := range codes {
|
||||
i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2
|
||||
// End the current group and start a new one whenever
|
||||
// there is a large range with no changes.
|
||||
if c.Tag == 'e' && i2-i1 > nn {
|
||||
group = append(group, OpCode{c.Tag, i1, min(i2, i1+n),
|
||||
j1, min(j2, j1+n)})
|
||||
groups = append(groups, group)
|
||||
group = []OpCode{}
|
||||
i1, j1 = max(i1, i2-n), max(j1, j2-n)
|
||||
}
|
||||
group = append(group, OpCode{c.Tag, i1, i2, j1, j2})
|
||||
}
|
||||
if len(group) > 0 && !(len(group) == 1 && group[0].Tag == 'e') {
|
||||
groups = append(groups, group)
|
||||
}
|
||||
return groups
|
||||
}
|
||||
|
||||
// Return a measure of the sequences' similarity (float in [0,1]).
|
||||
//
|
||||
// Where T is the total number of elements in both sequences, and
|
||||
// M is the number of matches, this is 2.0*M / T.
|
||||
// Note that this is 1 if the sequences are identical, and 0 if
|
||||
// they have nothing in common.
|
||||
//
|
||||
// .Ratio() is expensive to compute if you haven't already computed
|
||||
// .GetMatchingBlocks() or .GetOpCodes(), in which case you may
|
||||
// want to try .QuickRatio() or .RealQuickRation() first to get an
|
||||
// upper bound.
|
||||
func (m *SequenceMatcher) Ratio() float64 {
|
||||
matches := 0
|
||||
for _, m := range m.GetMatchingBlocks() {
|
||||
matches += m.Size
|
||||
}
|
||||
return calculateRatio(matches, len(m.a)+len(m.b))
|
||||
}
|
||||
|
||||
// Return an upper bound on ratio() relatively quickly.
|
||||
//
|
||||
// This isn't defined beyond that it is an upper bound on .Ratio(), and
|
||||
// is faster to compute.
|
||||
func (m *SequenceMatcher) QuickRatio() float64 {
|
||||
// viewing a and b as multisets, set matches to the cardinality
|
||||
// of their intersection; this counts the number of matches
|
||||
// without regard to order, so is clearly an upper bound
|
||||
if m.fullBCount == nil {
|
||||
m.fullBCount = map[string]int{}
|
||||
for _, s := range m.b {
|
||||
m.fullBCount[s] = m.fullBCount[s] + 1
|
||||
}
|
||||
}
|
||||
|
||||
// avail[x] is the number of times x appears in 'b' less the
|
||||
// number of times we've seen it in 'a' so far ... kinda
|
||||
avail := map[string]int{}
|
||||
matches := 0
|
||||
for _, s := range m.a {
|
||||
n, ok := avail[s]
|
||||
if !ok {
|
||||
n = m.fullBCount[s]
|
||||
}
|
||||
avail[s] = n - 1
|
||||
if n > 0 {
|
||||
matches += 1
|
||||
}
|
||||
}
|
||||
return calculateRatio(matches, len(m.a)+len(m.b))
|
||||
}
|
||||
|
||||
// Return an upper bound on ratio() very quickly.
|
||||
//
|
||||
// This isn't defined beyond that it is an upper bound on .Ratio(), and
|
||||
// is faster to compute than either .Ratio() or .QuickRatio().
|
||||
func (m *SequenceMatcher) RealQuickRatio() float64 {
|
||||
la, lb := len(m.a), len(m.b)
|
||||
return calculateRatio(min(la, lb), la+lb)
|
||||
}
|
||||
|
||||
// Convert range to the "ed" format
|
||||
func formatRangeUnified(start, stop int) string {
|
||||
// Per the diff spec at http://www.unix.org/single_unix_specification/
|
||||
beginning := start + 1 // lines start numbering with one
|
||||
length := stop - start
|
||||
if length == 1 {
|
||||
return fmt.Sprintf("%d", beginning)
|
||||
}
|
||||
if length == 0 {
|
||||
beginning -= 1 // empty ranges begin at line just before the range
|
||||
}
|
||||
return fmt.Sprintf("%d,%d", beginning, length)
|
||||
}
|
||||
|
||||
// Unified diff parameters
|
||||
type UnifiedDiff struct {
|
||||
A []string // First sequence lines
|
||||
FromFile string // First file name
|
||||
FromDate string // First file time
|
||||
B []string // Second sequence lines
|
||||
ToFile string // Second file name
|
||||
ToDate string // Second file time
|
||||
Eol string // Headers end of line, defaults to LF
|
||||
Context int // Number of context lines
|
||||
}
|
||||
|
||||
// Compare two sequences of lines; generate the delta as a unified diff.
|
||||
//
|
||||
// Unified diffs are a compact way of showing line changes and a few
|
||||
// lines of context. The number of context lines is set by 'n' which
|
||||
// defaults to three.
|
||||
//
|
||||
// By default, the diff control lines (those with ---, +++, or @@) are
|
||||
// created with a trailing newline. This is helpful so that inputs
|
||||
// created from file.readlines() result in diffs that are suitable for
|
||||
// file.writelines() since both the inputs and outputs have trailing
|
||||
// newlines.
|
||||
//
|
||||
// For inputs that do not have trailing newlines, set the lineterm
|
||||
// argument to "" so that the output will be uniformly newline free.
|
||||
//
|
||||
// The unidiff format normally has a header for filenames and modification
|
||||
// times. Any or all of these may be specified using strings for
|
||||
// 'fromfile', 'tofile', 'fromfiledate', and 'tofiledate'.
|
||||
// The modification times are normally expressed in the ISO 8601 format.
|
||||
func WriteUnifiedDiff(writer io.Writer, diff UnifiedDiff) error {
|
||||
buf := bufio.NewWriter(writer)
|
||||
defer buf.Flush()
|
||||
wf := func(format string, args ...interface{}) error {
|
||||
_, err := buf.WriteString(fmt.Sprintf(format, args...))
|
||||
return err
|
||||
}
|
||||
ws := func(s string) error {
|
||||
_, err := buf.WriteString(s)
|
||||
return err
|
||||
}
|
||||
|
||||
if len(diff.Eol) == 0 {
|
||||
diff.Eol = "\n"
|
||||
}
|
||||
|
||||
started := false
|
||||
m := NewMatcher(diff.A, diff.B)
|
||||
for _, g := range m.GetGroupedOpCodes(diff.Context) {
|
||||
if !started {
|
||||
started = true
|
||||
fromDate := ""
|
||||
if len(diff.FromDate) > 0 {
|
||||
fromDate = "\t" + diff.FromDate
|
||||
}
|
||||
toDate := ""
|
||||
if len(diff.ToDate) > 0 {
|
||||
toDate = "\t" + diff.ToDate
|
||||
}
|
||||
if diff.FromFile != "" || diff.ToFile != "" {
|
||||
err := wf("--- %s%s%s", diff.FromFile, fromDate, diff.Eol)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = wf("+++ %s%s%s", diff.ToFile, toDate, diff.Eol)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
first, last := g[0], g[len(g)-1]
|
||||
range1 := formatRangeUnified(first.I1, last.I2)
|
||||
range2 := formatRangeUnified(first.J1, last.J2)
|
||||
if err := wf("@@ -%s +%s @@%s", range1, range2, diff.Eol); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, c := range g {
|
||||
i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2
|
||||
if c.Tag == 'e' {
|
||||
for _, line := range diff.A[i1:i2] {
|
||||
if err := ws(" " + line); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
if c.Tag == 'r' || c.Tag == 'd' {
|
||||
for _, line := range diff.A[i1:i2] {
|
||||
if err := ws("-" + line); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
if c.Tag == 'r' || c.Tag == 'i' {
|
||||
for _, line := range diff.B[j1:j2] {
|
||||
if err := ws("+" + line); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Like WriteUnifiedDiff but returns the diff a string.
|
||||
func GetUnifiedDiffString(diff UnifiedDiff) (string, error) {
|
||||
w := &bytes.Buffer{}
|
||||
err := WriteUnifiedDiff(w, diff)
|
||||
return string(w.Bytes()), err
|
||||
}
|
||||
|
||||
// Convert range to the "ed" format.
|
||||
func formatRangeContext(start, stop int) string {
|
||||
// Per the diff spec at http://www.unix.org/single_unix_specification/
|
||||
beginning := start + 1 // lines start numbering with one
|
||||
length := stop - start
|
||||
if length == 0 {
|
||||
beginning -= 1 // empty ranges begin at line just before the range
|
||||
}
|
||||
if length <= 1 {
|
||||
return fmt.Sprintf("%d", beginning)
|
||||
}
|
||||
return fmt.Sprintf("%d,%d", beginning, beginning+length-1)
|
||||
}
|
||||
|
||||
type ContextDiff UnifiedDiff
|
||||
|
||||
// Compare two sequences of lines; generate the delta as a context diff.
|
||||
//
|
||||
// Context diffs are a compact way of showing line changes and a few
|
||||
// lines of context. The number of context lines is set by diff.Context
|
||||
// which defaults to three.
|
||||
//
|
||||
// By default, the diff control lines (those with *** or ---) are
|
||||
// created with a trailing newline.
|
||||
//
|
||||
// For inputs that do not have trailing newlines, set the diff.Eol
|
||||
// argument to "" so that the output will be uniformly newline free.
|
||||
//
|
||||
// The context diff format normally has a header for filenames and
|
||||
// modification times. Any or all of these may be specified using
|
||||
// strings for diff.FromFile, diff.ToFile, diff.FromDate, diff.ToDate.
|
||||
// The modification times are normally expressed in the ISO 8601 format.
|
||||
// If not specified, the strings default to blanks.
|
||||
func WriteContextDiff(writer io.Writer, diff ContextDiff) error {
|
||||
buf := bufio.NewWriter(writer)
|
||||
defer buf.Flush()
|
||||
var diffErr error
|
||||
wf := func(format string, args ...interface{}) {
|
||||
_, err := buf.WriteString(fmt.Sprintf(format, args...))
|
||||
if diffErr == nil && err != nil {
|
||||
diffErr = err
|
||||
}
|
||||
}
|
||||
ws := func(s string) {
|
||||
_, err := buf.WriteString(s)
|
||||
if diffErr == nil && err != nil {
|
||||
diffErr = err
|
||||
}
|
||||
}
|
||||
|
||||
if len(diff.Eol) == 0 {
|
||||
diff.Eol = "\n"
|
||||
}
|
||||
|
||||
prefix := map[byte]string{
|
||||
'i': "+ ",
|
||||
'd': "- ",
|
||||
'r': "! ",
|
||||
'e': " ",
|
||||
}
|
||||
|
||||
started := false
|
||||
m := NewMatcher(diff.A, diff.B)
|
||||
for _, g := range m.GetGroupedOpCodes(diff.Context) {
|
||||
if !started {
|
||||
started = true
|
||||
fromDate := ""
|
||||
if len(diff.FromDate) > 0 {
|
||||
fromDate = "\t" + diff.FromDate
|
||||
}
|
||||
toDate := ""
|
||||
if len(diff.ToDate) > 0 {
|
||||
toDate = "\t" + diff.ToDate
|
||||
}
|
||||
if diff.FromFile != "" || diff.ToFile != "" {
|
||||
wf("*** %s%s%s", diff.FromFile, fromDate, diff.Eol)
|
||||
wf("--- %s%s%s", diff.ToFile, toDate, diff.Eol)
|
||||
}
|
||||
}
|
||||
|
||||
first, last := g[0], g[len(g)-1]
|
||||
ws("***************" + diff.Eol)
|
||||
|
||||
range1 := formatRangeContext(first.I1, last.I2)
|
||||
wf("*** %s ****%s", range1, diff.Eol)
|
||||
for _, c := range g {
|
||||
if c.Tag == 'r' || c.Tag == 'd' {
|
||||
for _, cc := range g {
|
||||
if cc.Tag == 'i' {
|
||||
continue
|
||||
}
|
||||
for _, line := range diff.A[cc.I1:cc.I2] {
|
||||
ws(prefix[cc.Tag] + line)
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
range2 := formatRangeContext(first.J1, last.J2)
|
||||
wf("--- %s ----%s", range2, diff.Eol)
|
||||
for _, c := range g {
|
||||
if c.Tag == 'r' || c.Tag == 'i' {
|
||||
for _, cc := range g {
|
||||
if cc.Tag == 'd' {
|
||||
continue
|
||||
}
|
||||
for _, line := range diff.B[cc.J1:cc.J2] {
|
||||
ws(prefix[cc.Tag] + line)
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return diffErr
|
||||
}
|
||||
|
||||
// Like WriteContextDiff but returns the diff a string.
|
||||
func GetContextDiffString(diff ContextDiff) (string, error) {
|
||||
w := &bytes.Buffer{}
|
||||
err := WriteContextDiff(w, diff)
|
||||
return string(w.Bytes()), err
|
||||
}
|
||||
|
||||
// Split a string on "\n" while preserving them. The output can be used
|
||||
// as input for UnifiedDiff and ContextDiff structures.
|
||||
func SplitLines(s string) []string {
|
||||
lines := strings.SplitAfter(s, "\n")
|
||||
lines[len(lines)-1] += "\n"
|
||||
return lines
|
||||
}
|
333
vendor/github.com/powerman/check/.golangci.yml
generated
vendored
Normal file
333
vendor/github.com/powerman/check/.golangci.yml
generated
vendored
Normal file
|
@ -0,0 +1,333 @@
|
|||
# This file contains all available configuration options
|
||||
# with their default values.
|
||||
|
||||
# options for analysis running
|
||||
run:
|
||||
# default concurrency is a available CPU number
|
||||
# concurrency: 4
|
||||
|
||||
# timeout for analysis, e.g. 30s, 5m, default is 1m
|
||||
# timeout: 1m
|
||||
|
||||
# exit code when at least one issue was found, default is 1
|
||||
# issues-exit-code: 1
|
||||
|
||||
# include test files or not, default is true
|
||||
# tests: true
|
||||
|
||||
# list of build tags, all linters use it. Default is empty list.
|
||||
build-tags:
|
||||
- integration
|
||||
|
||||
# which dirs to skip: they won't be analyzed;
|
||||
# can use regexp here: generated.*, regexp is applied on full path;
|
||||
# default value is empty list, but next dirs are always skipped independently
|
||||
# from this option's value:
|
||||
# vendor$, third_party$, testdata$, examples$, Godeps$, builtin$
|
||||
# skip-dirs:
|
||||
# - src/external_libs
|
||||
# - autogenerated_by_my_lib
|
||||
|
||||
# which files to skip: they will be analyzed, but issues from them
|
||||
# won't be reported. Default value is empty list, but there is
|
||||
# no need to include all autogenerated files, we confidently recognize
|
||||
# autogenerated files. If it's not please let us know.
|
||||
# skip-files:
|
||||
# - "\\.\\w+\\.go$"
|
||||
|
||||
# by default isn't set. If set we pass it to "go list -mod={option}". From "go help modules":
|
||||
# If invoked with -mod=readonly, the go command is disallowed from the implicit
|
||||
# automatic updating of go.mod described above. Instead, it fails when any changes
|
||||
# to go.mod are needed. This setting is most useful to check that go.mod does
|
||||
# not need updates, such as in a continuous integration and testing system.
|
||||
# If invoked with -mod=vendor, the go command assumes that the vendor
|
||||
# directory holds the correct copies of dependencies and ignores
|
||||
# the dependency descriptions in go.mod.
|
||||
modules-download-mode: readonly
|
||||
|
||||
|
||||
# output configuration options
|
||||
output:
|
||||
# colored-line-number|line-number|json|tab|checkstyle|code-climate, default is "colored-line-number"
|
||||
# format: colored-line-number
|
||||
|
||||
# print lines of code with issue, default is true
|
||||
# print-issued-lines: true
|
||||
|
||||
# print linter name in the end of issue text, default is true
|
||||
# print-linter-name: true
|
||||
|
||||
|
||||
# all available settings of specific linters
|
||||
linters-settings:
|
||||
errcheck:
|
||||
# report about not checking of errors in type assetions: `a := b.(MyStruct)`;
|
||||
# default is false: such cases aren't reported by default.
|
||||
# check-type-assertions: false
|
||||
|
||||
# report about assignment of errors to blank identifier: `num, _ := strconv.Atoi(numStr)`;
|
||||
# default is false: such cases aren't reported by default.
|
||||
# check-blank: false
|
||||
|
||||
# [deprecated] comma-separated list of pairs of the form pkg:regex
|
||||
# the regex is used to ignore names within pkg. (default "fmt:.*").
|
||||
# see https://github.com/kisielk/errcheck#the-deprecated-method for details
|
||||
# ignore: fmt:.*,io/ioutil:^Read.*
|
||||
|
||||
# path to a file containing a list of functions to exclude from checking
|
||||
# see https://github.com/kisielk/errcheck#excluding-functions for details
|
||||
# exclude: .errcheck.excludes
|
||||
|
||||
funlen:
|
||||
lines: 60
|
||||
statements: 40
|
||||
|
||||
govet:
|
||||
# report about shadowed variables
|
||||
check-shadowing: true
|
||||
|
||||
# settings per analyzer
|
||||
settings:
|
||||
printf: # analyzer name, run `go tool vet help` to see all analyzers
|
||||
funcs: # run `go tool vet help printf` to see available settings for `printf` analyzer
|
||||
# - (github.com/powerman/structlog.Logger).Fatalf
|
||||
# - (github.com/powerman/structlog.Logger).Panicf
|
||||
# - (github.com/powerman/structlog.Logger).Printf
|
||||
|
||||
# enable or disable analyzers by name
|
||||
# enable:
|
||||
# - atomicalign
|
||||
enable-all: true
|
||||
# disable:
|
||||
# - shadow
|
||||
# disable-all: false
|
||||
golint:
|
||||
# minimal confidence for issues, default is 0.8
|
||||
# min-confidence: 0.8
|
||||
gofmt:
|
||||
# simplify code: gofmt with `-s` option, true by default
|
||||
# simplify: true
|
||||
goimports:
|
||||
# put imports beginning with prefix after 3rd-party packages;
|
||||
# it's a comma-separated list of prefixes
|
||||
# local-prefixes: github.com/powerman
|
||||
gocyclo:
|
||||
# minimal code complexity to report, 30 by default (but we recommend 10-20)
|
||||
min-complexity: 15
|
||||
gocognit:
|
||||
# minimal code complexity to report, 30 by default (but we recommend 10-20)
|
||||
min-complexity: 20
|
||||
maligned:
|
||||
# print struct with more effective memory layout or not, false by default
|
||||
suggest-new: true
|
||||
dupl:
|
||||
# tokens count to trigger issue, 150 by default
|
||||
threshold: 100
|
||||
goconst:
|
||||
# minimal length of string constant, 3 by default
|
||||
# min-len: 3
|
||||
# minimal occurrences count to trigger, 3 by default
|
||||
# min-occurrences: 3
|
||||
depguard:
|
||||
list-type: blacklist
|
||||
include-go-root: true
|
||||
packages:
|
||||
- log
|
||||
packages-with-error-messages:
|
||||
# specify an error message to output when a blacklisted package is used
|
||||
log: "logging is allowed only by github.com/powerman/structlog"
|
||||
misspell:
|
||||
# Correct spellings using locale preferences for US or UK.
|
||||
# Default is to use a neutral variety of English.
|
||||
# Setting locale to US will correct the British spelling of 'colour' to 'color'.
|
||||
# locale: US
|
||||
# ignore-words:
|
||||
# - someword
|
||||
lll:
|
||||
# max line length, lines longer will be reported. Default is 120.
|
||||
# '\t' is counted as 1 character by default, and can be changed with the tab-width option
|
||||
# line-length: 120
|
||||
# tab width in spaces. Default to 1.
|
||||
# tab-width: 1
|
||||
unused:
|
||||
# treat code as a program (not a library) and report unused exported identifiers; default is false.
|
||||
# XXX: if you enable this setting, unused will report a lot of false-positives in text editors:
|
||||
# if it's called for subdir of a project it can't find funcs usages. All text editor integrations
|
||||
# with golangci-lint call it on a directory with the changed file.
|
||||
# check-exported: false
|
||||
unparam:
|
||||
# Inspect exported functions, default is false. Set to true if no external program/library imports your code.
|
||||
# XXX: if you enable this setting, unparam will report a lot of false-positives in text editors:
|
||||
# if it's called for subdir of a project it can't find external interfaces. All text editor integrations
|
||||
# with golangci-lint call it on a directory with the changed file.
|
||||
# check-exported: false
|
||||
nakedret:
|
||||
# make an issue if func has more lines of code than this setting and it has naked returns; default is 30
|
||||
# max-func-lines: 30
|
||||
prealloc:
|
||||
# XXX: we don't recommend using this linter before doing performance profiling.
|
||||
# For most programs usage of prealloc will be a premature optimization.
|
||||
|
||||
# Report preallocation suggestions only on simple loops that have no returns/breaks/continues/gotos in them.
|
||||
# True by default.
|
||||
# simple: true
|
||||
# range-loops: true # Report preallocation suggestions on range loops, true by default
|
||||
# for-loops: false # Report preallocation suggestions on for loops, false by default
|
||||
gocritic:
|
||||
# Which checks should be enabled; can't be combined with 'disabled-checks';
|
||||
# See https://go-critic.github.io/overview#checks-overview
|
||||
# To check which checks are enabled run `GL_DEBUG=gocritic golangci-lint run`
|
||||
# By default list of stable checks is used.
|
||||
# enabled-checks:
|
||||
# - rangeValCopy
|
||||
|
||||
# Which checks should be disabled; can't be combined with 'enabled-checks'; default is empty
|
||||
disabled-checks:
|
||||
- hugeParam # premature optimization
|
||||
- paramTypeCombine # questionable
|
||||
- ptrToRefParam
|
||||
- typeUnparen # false positive
|
||||
- yodaStyleExpr # questionable
|
||||
|
||||
# Enable multiple checks by tags, run `GL_DEBUG=gocritic golangci-lint run` to see all tags and checks.
|
||||
# Empty list by default. See https://github.com/go-critic/go-critic#usage -> section "Tags".
|
||||
enabled-tags:
|
||||
- diagnostic
|
||||
- style
|
||||
- performance
|
||||
- experimental
|
||||
- opinionated
|
||||
|
||||
settings: # settings passed to gocritic
|
||||
captLocal: # must be valid enabled check name
|
||||
paramsOnly: true
|
||||
rangeValCopy:
|
||||
sizeThreshold: 32
|
||||
godox:
|
||||
# report any comments starting with keywords, this is useful for TODO or FIXME comments that
|
||||
# might be left in the code accidentally and should be resolved before merging
|
||||
keywords: # default keywords are TODO, BUG, and FIXME, these can be overwritten by this setting
|
||||
- BUG # marks issues that should be moved to issue tracker before merging
|
||||
- FIXME # marks issues that should be resolved before merging
|
||||
dogsled:
|
||||
# checks assignments with too many blank identifiers; default is 2
|
||||
max-blank-identifiers: 2
|
||||
|
||||
whitespace:
|
||||
multi-if: false # Enforces newlines (or comments) after every multi-line if statement
|
||||
multi-func: false # Enforces newlines (or comments) after every multi-line function signature
|
||||
wsl:
|
||||
# If true append is only allowed to be cuddled if appending value is
|
||||
# matching variables, fields or types on line above. Default is true.
|
||||
strict-append: true
|
||||
# Allow calls and assignments to be cuddled as long as the lines have any
|
||||
# matching variables, fields or types. Default is true.
|
||||
allow-assign-and-call: true
|
||||
# Allow multiline assignments to be cuddled. Default is true.
|
||||
allow-multiline-assign: true
|
||||
|
||||
linters:
|
||||
# enable:
|
||||
# - megacheck
|
||||
# - govet
|
||||
enable-all: true
|
||||
disable:
|
||||
- dogsled # questionable
|
||||
- dupl
|
||||
- gochecknoglobals
|
||||
- gochecknoinits
|
||||
- lll
|
||||
# - misspell
|
||||
- prealloc
|
||||
- wsl # questionable
|
||||
# disable-all: false
|
||||
# presets:
|
||||
# - bugs
|
||||
# - unused
|
||||
fast: false
|
||||
|
||||
|
||||
issues:
|
||||
# List of regexps of issue texts to exclude, empty list by default.
|
||||
# But independently from this option we use default exclude patterns,
|
||||
# it can be disabled by `exclude-use-default: false`. To list all
|
||||
# excluded by default patterns execute `golangci-lint run --help`
|
||||
exclude:
|
||||
- "declaration of \"(log|err|ctx)\" shadows"
|
||||
- "Potential file inclusion via variable" # [DEFAULT] gosec: False positive is triggered by 'src, err := ioutil.ReadFile(filename)'
|
||||
|
||||
# Excluding configuration per-path, per-linter, per-text and per-source
|
||||
exclude-rules:
|
||||
# Exclude some linters from running on tests files.
|
||||
- path: _test\.go|testing\.go
|
||||
linters:
|
||||
- bodyclose
|
||||
- dupl
|
||||
- errcheck
|
||||
- funlen
|
||||
- gochecknoglobals
|
||||
- gochecknoinits
|
||||
- gocyclo
|
||||
- gosec
|
||||
- maligned
|
||||
- scopelint
|
||||
|
||||
# Ease some gocritic warnings on test files.
|
||||
- path: _test\.go|testing\.go
|
||||
text: "(unnamedResult|exitAfterDefer|rangeValCopy|commentedOutCode)"
|
||||
linters:
|
||||
- gocritic
|
||||
|
||||
# Package def is designed to contain global constants.
|
||||
- path: internal/def/
|
||||
linters:
|
||||
- gochecknoglobals
|
||||
|
||||
# Commands are allowed to contain a lot of flags.
|
||||
- path: main.go
|
||||
text: Function '(init|main)'
|
||||
linters:
|
||||
- funlen
|
||||
|
||||
# Exclude known linters from partially hard-vendored code,
|
||||
# which is impossible to exclude via "nolint" comments.
|
||||
# - path: internal/hmac/
|
||||
# text: "weak cryptographic primitive"
|
||||
# linters:
|
||||
# - gosec
|
||||
|
||||
# Exclude some staticcheck messages
|
||||
# - linters:
|
||||
# - staticcheck
|
||||
# text: "SA9003:"
|
||||
|
||||
# Exclude lll issues for long lines with go:generate
|
||||
# - linters:
|
||||
# - lll
|
||||
# source: "^//go:generate "
|
||||
|
||||
# Independently from option `exclude` we use default exclude patterns,
|
||||
# it can be disabled by this option. To list all
|
||||
# excluded by default patterns execute `golangci-lint run --help`.
|
||||
# Default value for this option is true.
|
||||
exclude-use-default: false
|
||||
|
||||
# Maximum issues count per one linter. Set to 0 to disable. Default is 50.
|
||||
max-issues-per-linter: 0
|
||||
|
||||
# Maximum count of issues with the same text. Set to 0 to disable. Default is 3.
|
||||
max-same-issues: 0
|
||||
|
||||
# Show only new issues: if there are unstaged changes or untracked files,
|
||||
# only those changes are analyzed, else only changes in HEAD~ are analyzed.
|
||||
# It's a super-useful option for integration of golangci-lint into existing
|
||||
# large codebase. It's not practical to fix all existing issues at the moment
|
||||
# of integration: much better don't allow issues in new code.
|
||||
# Default is false.
|
||||
# new: false
|
||||
|
||||
# Show only new issues created after git revision `REV`
|
||||
# new-from-rev: REV
|
||||
|
||||
# Show only new issues created in git patch with set file path.
|
||||
# new-from-patch: path/to/patch/file
|
21
vendor/github.com/powerman/check/LICENSE
generated
vendored
Normal file
21
vendor/github.com/powerman/check/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2017 Alex Efros
|
||||
|
||||
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.
|
72
vendor/github.com/powerman/check/README.md
generated
vendored
Normal file
72
vendor/github.com/powerman/check/README.md
generated
vendored
Normal file
|
@ -0,0 +1,72 @@
|
|||
# check [](http://godoc.org/github.com/powerman/check) [](https://goreportcard.com/report/github.com/powerman/check) [](https://circleci.com/gh/powerman/check) [](https://coveralls.io/github/powerman/check?branch=master)
|
||||
|
||||
Helpers to complement Go [testing](https://golang.org/pkg/testing/)
|
||||
package.
|
||||
|
||||
Write tests with ease and fun!
|
||||
|
||||
This package is like
|
||||
[testify/assert](https://godoc.org/github.com/test-go/testify/assert)
|
||||
on steroids. :)
|
||||
|
||||
## Features
|
||||
|
||||
- Compelling output from failed tests:
|
||||
- Very easy-to-read dumps for expected and actual values.
|
||||
- Same text diff you loved in testify/assert.
|
||||
- Also visual diff in [GoConvey](http://goconvey.co/) web UI, if you
|
||||
use it (recommended).
|
||||
- Statistics with amount of passed/failed checks.
|
||||
- Colored output in terminal.
|
||||
- 100% compatible with testing package - check package just provide
|
||||
convenient wrappers for `*testing.T` methods and doesn't introduce new
|
||||
concepts like BDD, custom test suite or unusual execution flow.
|
||||
- All checks you may ever need! :)
|
||||
- Very easy to add your own check functions.
|
||||
- Concise, handy and consistent API, without dot-import!
|
||||
|
||||
## Quickstart
|
||||
|
||||
Just wrap each (including subtests) `*testing.T` using `check.T()` and write
|
||||
tests as usually with testing package. Call new methods provided by this
|
||||
package to have more clean/concise test code and cool dump/diff.
|
||||
|
||||
```go
|
||||
import "github.com/powerman/check"
|
||||
|
||||
func TestSomething(tt *testing.T) {
|
||||
t := check.T(tt)
|
||||
t.Equal(2, 2)
|
||||
t.Log("You can use new t just like usual *testing.T")
|
||||
t.Run("Subtests/Parallel example", func(tt *testing.T) {
|
||||
t := check.T(tt)
|
||||
t.Parallel()
|
||||
t.NotEqual(2, 3, "should not be 3!")
|
||||
obj, err := NewObj()
|
||||
if t.Nil(err) {
|
||||
t.Match(obj.field, `^\d+$`)
|
||||
}
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
To get optional statistics about executed checkers add:
|
||||
|
||||
```go
|
||||
func TestMain(m *testing.M) { check.TestMain(m) }
|
||||
```
|
||||
|
||||
When use goconvey tool, to get nice diff in web UI
|
||||
[add](https://github.com/smartystreets/goconvey/issues/513):
|
||||
|
||||
```go
|
||||
import _ "github.com/smartystreets/goconvey/convey"
|
||||
```
|
||||
|
||||
## Installation
|
||||
|
||||
Require [Go 1.9](https://golang.org/doc/go1.9#test-helper).
|
||||
|
||||
```
|
||||
go get github.com/powerman/check
|
||||
```
|
1274
vendor/github.com/powerman/check/check.go
generated
vendored
Normal file
1274
vendor/github.com/powerman/check/check.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
39
vendor/github.com/powerman/check/color.go
generated
vendored
Normal file
39
vendor/github.com/powerman/check/color.go
generated
vendored
Normal file
|
@ -0,0 +1,39 @@
|
|||
package check
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
ansiGreen = "\033[32m"
|
||||
ansiYellow = "\033[33m"
|
||||
ansiRed = "\033[31m"
|
||||
ansiReset = "\033[0m"
|
||||
)
|
||||
|
||||
func init() {
|
||||
if !wantColor() {
|
||||
ansiGreen, ansiYellow, ansiRed, ansiReset = "", "", "", ""
|
||||
}
|
||||
}
|
||||
|
||||
func wantColor() bool {
|
||||
return strings.Contains(os.Getenv("TERM"), "color") &&
|
||||
(isTerminal() || os.Getenv("GO_TEST_COLOR") != "")
|
||||
}
|
||||
|
||||
func colouredDiff(diff string) string {
|
||||
lines := strings.SplitAfter(diff, "\n")
|
||||
for i := range lines {
|
||||
switch {
|
||||
case strings.HasPrefix(lines[i], "--- "):
|
||||
case strings.HasPrefix(lines[i], "+++ "):
|
||||
case strings.HasPrefix(lines[i], "-"):
|
||||
lines[i] = ansiGreen + lines[i] + ansiReset
|
||||
case strings.HasPrefix(lines[i], "+"):
|
||||
lines[i] = ansiRed + lines[i] + ansiReset
|
||||
}
|
||||
}
|
||||
return strings.Join(lines, "")
|
||||
}
|
14
vendor/github.com/powerman/check/color_bsd.go
generated
vendored
Normal file
14
vendor/github.com/powerman/check/color_bsd.go
generated
vendored
Normal file
|
@ -0,0 +1,14 @@
|
|||
// +build darwin dragonfly freebsd netbsd openbsd
|
||||
|
||||
package check
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func isTerminal() bool {
|
||||
_, err := unix.IoctlGetTermios(int(os.Stdout.Fd()), unix.TIOCGETA)
|
||||
return err == nil
|
||||
}
|
14
vendor/github.com/powerman/check/color_linux.go
generated
vendored
Normal file
14
vendor/github.com/powerman/check/color_linux.go
generated
vendored
Normal file
|
@ -0,0 +1,14 @@
|
|||
// +build linux
|
||||
|
||||
package check
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func isTerminal() bool {
|
||||
_, err := unix.IoctlGetTermios(int(os.Stdout.Fd()), unix.TCGETS)
|
||||
return err == nil
|
||||
}
|
7
vendor/github.com/powerman/check/color_other.go
generated
vendored
Normal file
7
vendor/github.com/powerman/check/color_other.go
generated
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
// +build !linux,!darwin,!dragonfly,!freebsd,!netbsd,!openbsd,!windows
|
||||
|
||||
package check
|
||||
|
||||
func isTerminal() bool {
|
||||
return false
|
||||
}
|
14
vendor/github.com/powerman/check/color_windows.go
generated
vendored
Normal file
14
vendor/github.com/powerman/check/color_windows.go
generated
vendored
Normal file
|
@ -0,0 +1,14 @@
|
|||
// +build windows
|
||||
|
||||
package check
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func isTerminal() bool {
|
||||
var mode uint32
|
||||
err := syscall.GetConsoleMode(syscall.Handle(os.Stdout.Fd()), &mode)
|
||||
return err == nil
|
||||
}
|
150
vendor/github.com/powerman/check/doc.go
generated
vendored
Normal file
150
vendor/github.com/powerman/check/doc.go
generated
vendored
Normal file
|
@ -0,0 +1,150 @@
|
|||
// Package check provide helpers to complement Go testing package.
|
||||
//
|
||||
// Features
|
||||
//
|
||||
// This package is like testify/assert on steroids. :)
|
||||
//
|
||||
// - Compelling output from failed tests:
|
||||
// - Very easy-to-read dumps for expected and actual values.
|
||||
// - Same text diff you loved in testify/assert.
|
||||
// - Also visual diff in GoConvey web UI, if you use it (recommended).
|
||||
// - Statistics with amount of passed/failed checks.
|
||||
// - Colored output in terminal.
|
||||
// - 100% compatible with testing package - check package just provide
|
||||
// convenient wrappers for *testing.T methods and doesn't introduce
|
||||
// new concepts like BDD, custom test suite or unusual execution flow.
|
||||
// - All checks you may ever need! :)
|
||||
// - Very easy to add your own check functions.
|
||||
// - Concise, handy and consistent API, without dot-import!
|
||||
//
|
||||
// Quickstart
|
||||
//
|
||||
// Just wrap each (including subtests) *testing.T using check.T() and write
|
||||
// tests as usually with testing package. Call new methods provided by
|
||||
// this package to have more clean/concise test code and cool dump/diff.
|
||||
//
|
||||
// import "github.com/powerman/check"
|
||||
//
|
||||
// func TestSomething(tt *testing.T) {
|
||||
// t := check.T(tt)
|
||||
// t.Equal(2, 2)
|
||||
// t.Log("You can use new t just like usual *testing.T")
|
||||
// t.Run("Subtests/Parallel example", func(tt *testing.T) {
|
||||
// t := check.T(tt)
|
||||
// t.Parallel()
|
||||
// t.NotEqual(2, 3, "should not be 3!")
|
||||
// obj, err := NewObj()
|
||||
// if t.Nil(err) {
|
||||
// t.Match(obj.field, `^\d+$`)
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
//
|
||||
// To get optional statistics about executed checkers add:
|
||||
//
|
||||
// func TestMain(m *testing.M) { check.TestMain(m) }
|
||||
//
|
||||
// When use goconvey tool, to get nice diff in web UI add:
|
||||
//
|
||||
// import _ "github.com/smartystreets/goconvey/convey"
|
||||
//
|
||||
// Hints
|
||||
//
|
||||
// ★ How to check for errors:
|
||||
//
|
||||
// // If you just want nil:
|
||||
// t.Nil(err)
|
||||
// t.Err(err, nil)
|
||||
//
|
||||
// // Check for (absence of) concrete (possibly wrapped) error:
|
||||
// t.Err(err, io.EOF)
|
||||
// t.NotErr(err, io.EOF) // nil is not io.EOF, so it's ok too
|
||||
//
|
||||
// // When need to match by error's text:
|
||||
// t.Match(err, "file.*permission")
|
||||
//
|
||||
// // Use Equal ONLY when checking for same instance:
|
||||
// t.Equal(io.EOF, io.EOF) // this works
|
||||
// t.Equal(io.EOF, errors.New("EOF")) // this doesn't work!
|
||||
// t.Err(io.EOF, errors.New("EOF")) // this works
|
||||
// t.DeepEqual(io.EOF, errors.New("EOF")) // this works too
|
||||
//
|
||||
// ★ Each check returns bool, so you can easily skip problematic code:
|
||||
//
|
||||
// if t.Nil(err) {
|
||||
// t.Match(obj.field, `^\d+$`)
|
||||
// }
|
||||
//
|
||||
// ★ You can turn any check into assertion to stop test immediately:
|
||||
//
|
||||
// t.Must(t.Nil(err))
|
||||
//
|
||||
// ★ You can provide extra description to each check:
|
||||
//
|
||||
// t.Equal(got, want, "Just msg: will Print(), % isn't special")
|
||||
// t.Equal(got, want, "Msg with args: will Printf(): %v", extra)
|
||||
//
|
||||
// ★ There are short synonyms for checks implementing usual ==, !=, etc.:
|
||||
//
|
||||
// t.EQ(got, want) // same as t.Equal
|
||||
// t.NE(got, want) // same as t.NotEqual
|
||||
// t.LT(got, want) // same as t.Less
|
||||
// t.LE(got, want) // same as t.LessOrEqual
|
||||
// t.GT(got, want) // same as t.Greater
|
||||
// t.GE(got, want) // same as t.GreaterOrEqual
|
||||
//
|
||||
// ★ If you need custom check, which isn't available out-of-box - see
|
||||
// Should checker, it'll let you plug in your own checker with ease.
|
||||
//
|
||||
// ★ It will panic when called with arg of wrong type - because this
|
||||
// means bug in your test.
|
||||
//
|
||||
// ★ If you don't see colors in `go test` output it may happens because of
|
||||
// two reasons: either your $TERM doesn't contain substring "color" or
|
||||
// you're running `go test path/to/your/package`. To force colored output
|
||||
// in last case just set this environment variable:
|
||||
//
|
||||
// export GO_TEST_COLOR=1
|
||||
//
|
||||
// Contents
|
||||
//
|
||||
// There are few special functions (assertion, custom checkers, etc.).
|
||||
//
|
||||
// Must
|
||||
// Should
|
||||
// TODO
|
||||
//
|
||||
// Everything else are just trivial (mostly) checkers which works in
|
||||
// obvious way and accept values of any types which makes sense (and
|
||||
// panics on everything else).
|
||||
//
|
||||
// Nil NotNil
|
||||
// Zero NotZero
|
||||
// True False
|
||||
//
|
||||
// Equal NotEqual EQ NE
|
||||
// DeepEqual NotDeepEqual
|
||||
// Err NotErr
|
||||
// BytesEqual NotBytesEqual
|
||||
// JSONEqual
|
||||
//
|
||||
// Greater LessOrEqual GT LE
|
||||
// Less GreaterOrEqual LT GE
|
||||
// Between NotBetween
|
||||
// BetweenOrEqual NotBetweenOrEqual
|
||||
// InDelta NotInDelta
|
||||
// InSMAPE NotInSMAPE
|
||||
//
|
||||
// Len NotLen
|
||||
// Match NotMatch
|
||||
// HasPrefix NotHasPrefix
|
||||
// HasSuffix NotHasSuffix
|
||||
// HasKey NotHasKey
|
||||
// Contains NotContains
|
||||
//
|
||||
// HasType NotHasType
|
||||
// Implements NotImplements
|
||||
//
|
||||
// Panic NotPanic
|
||||
// PanicMatch PanicNotMatch
|
||||
package check
|
160
vendor/github.com/powerman/check/dump.go
generated
vendored
Normal file
160
vendor/github.com/powerman/check/dump.go
generated
vendored
Normal file
|
@ -0,0 +1,160 @@
|
|||
package check
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
"github.com/pmezard/go-difflib/difflib"
|
||||
)
|
||||
|
||||
var spewCfg = spew.ConfigState{
|
||||
Indent: " ",
|
||||
DisablePointerAddresses: true,
|
||||
DisableCapacities: true,
|
||||
SortKeys: true,
|
||||
SpewKeys: true,
|
||||
}
|
||||
|
||||
type dump struct {
|
||||
dump string
|
||||
indirectType reflect.Type
|
||||
}
|
||||
|
||||
// String returns dump of value given to newDump.
|
||||
func (v dump) String() string {
|
||||
return v.dump
|
||||
}
|
||||
|
||||
func (v dump) diff(expected dump) string {
|
||||
if v.indirectType != expected.indirectType {
|
||||
return ""
|
||||
}
|
||||
if !strings.ContainsRune(v.dump[:len(v.dump)-1], '\n') &&
|
||||
!strings.ContainsRune(expected.dump[:len(expected.dump)-1], '\n') {
|
||||
return ""
|
||||
}
|
||||
|
||||
diff, err := difflib.GetUnifiedDiffString(difflib.UnifiedDiff{
|
||||
A: difflib.SplitLines(expected.dump),
|
||||
B: difflib.SplitLines(v.dump),
|
||||
FromFile: "Expected",
|
||||
FromDate: "",
|
||||
ToFile: "Actual",
|
||||
ToDate: "",
|
||||
Context: 1,
|
||||
})
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return "Diff:\n" + diff
|
||||
}
|
||||
|
||||
// newDump prepare i dump using spew.Sdump in most cases and custom
|
||||
// improved dump for these cases:
|
||||
// - nil: remove "(interface{})" prefix
|
||||
// - byte: use 0xFF instead of decimal
|
||||
// - rune: use quoted char instead of number for valid runes
|
||||
// - string: use this instead of quoted single-line:
|
||||
// - valid utf8: don't quote ", show multiline strings on separate lines
|
||||
// - invalid utf8: use hexdump like for []byte
|
||||
// - []byte: same as string instead of hexdump for valid utf8
|
||||
// - []rune: use quoted char instead of number for valid runes in list
|
||||
// - json.RawMessage: indent, then same as string
|
||||
func newDump(i interface{}) (d dump) { // nolint:gocyclo,gocognit,funlen
|
||||
d.dump = spewCfg.Sdump(i)
|
||||
|
||||
if i == nil {
|
||||
d.dump = "<nil>\n"
|
||||
return d
|
||||
}
|
||||
|
||||
val := reflect.ValueOf(i)
|
||||
typ := reflect.TypeOf(i)
|
||||
kind := typ.Kind()
|
||||
if kind == reflect.Ptr {
|
||||
if val.IsNil() {
|
||||
return d
|
||||
}
|
||||
val = val.Elem()
|
||||
typ = typ.Elem()
|
||||
kind = typ.Kind()
|
||||
}
|
||||
d.indirectType = typ
|
||||
|
||||
switch {
|
||||
case typ == reflect.TypeOf(json.RawMessage(nil)):
|
||||
v := val.Bytes()
|
||||
var buf bytes.Buffer
|
||||
if json.Indent(&buf, v, "", " ") == nil {
|
||||
d.dump = fmt.Sprintf("(%T) (len=%d) '\n%s\n'\n", i, len(v), buf.String())
|
||||
}
|
||||
|
||||
case kind == reflect.Uint8:
|
||||
v := byte(val.Uint())
|
||||
d.dump = fmt.Sprintf("(%T) 0x%02X\n", i, v)
|
||||
|
||||
case kind == reflect.Int32:
|
||||
v := rune(val.Int())
|
||||
if utf8.ValidRune(v) {
|
||||
d.dump = fmt.Sprintf("(%T) %q\n", i, v)
|
||||
}
|
||||
|
||||
case kind == reflect.Slice && typ.Elem().Kind() == reflect.Int32:
|
||||
valid := true
|
||||
for k := 0; k < val.Len() && valid; k++ {
|
||||
valid = valid && utf8.ValidRune(rune(val.Index(k).Int()))
|
||||
}
|
||||
if valid {
|
||||
d.dump = fmt.Sprintf("(%T) %q\n", i, i)
|
||||
}
|
||||
|
||||
case kind == reflect.String:
|
||||
v := val.String()
|
||||
if utf8.ValidString(v) {
|
||||
d.dump = fmt.Sprintf("(%T) (len=%d) %s\n", i, len(v), quote(v))
|
||||
} else {
|
||||
d.dump = strings.Replace(spewCfg.Sdump([]byte(v)), "([]uint8)", fmt.Sprintf("(%T)", i), 1)
|
||||
}
|
||||
|
||||
case kind == reflect.Slice && typ.Elem().Kind() == reflect.Uint8:
|
||||
v := val.Bytes()
|
||||
if len(v) > 0 && utf8.Valid(v) || len(v) == 0 && !val.IsNil() {
|
||||
d.dump = fmt.Sprintf("(%T) (len=%d) %s\n", i, len(v), quote(string(v)))
|
||||
}
|
||||
}
|
||||
return d
|
||||
}
|
||||
|
||||
// quote like %#v, except keep \n and " unquoted for readability.
|
||||
func quote(s string) string {
|
||||
r := []rune(strconv.Quote(s))
|
||||
q := r[:0]
|
||||
var multiline, esc bool
|
||||
for _, c := range r[1 : len(r)-1] {
|
||||
if esc {
|
||||
esc = false
|
||||
switch c {
|
||||
case 'n':
|
||||
c = '\n'
|
||||
multiline = true
|
||||
case '"':
|
||||
default:
|
||||
q = append(q, '\\')
|
||||
}
|
||||
} else if c == '\\' {
|
||||
esc = true
|
||||
continue
|
||||
}
|
||||
q = append(q, c)
|
||||
}
|
||||
if multiline {
|
||||
return fmt.Sprintf("'\n%s\n'", string(q))
|
||||
}
|
||||
return fmt.Sprintf("'%s'", string(q))
|
||||
}
|
24
vendor/github.com/powerman/check/flags.go
generated
vendored
Normal file
24
vendor/github.com/powerman/check/flags.go
generated
vendored
Normal file
|
@ -0,0 +1,24 @@
|
|||
package check
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type peekFlags struct {
|
||||
sync.Once
|
||||
conveyJSON bool
|
||||
}
|
||||
|
||||
var flags peekFlags
|
||||
|
||||
func (p *peekFlags) detect() *peekFlags {
|
||||
flags.Do(func() {
|
||||
flag.Visit(func(f *flag.Flag) {
|
||||
if f.Name == "convey-json" {
|
||||
p.conveyJSON = f.Value.String() == "true"
|
||||
}
|
||||
})
|
||||
})
|
||||
return p
|
||||
}
|
13
vendor/github.com/powerman/check/go.mod
generated
vendored
Normal file
13
vendor/github.com/powerman/check/go.mod
generated
vendored
Normal file
|
@ -0,0 +1,13 @@
|
|||
module github.com/powerman/check
|
||||
|
||||
go 1.13
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.1
|
||||
github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c // indirect
|
||||
github.com/pkg/errors v0.8.1
|
||||
github.com/pmezard/go-difflib v1.0.0
|
||||
github.com/smartystreets/assertions v0.0.0-20190401211740-f487f9de1cd3 // indirect
|
||||
github.com/smartystreets/goconvey v1.6.4
|
||||
golang.org/x/sys v0.0.0-20191029155521-f43be2a4598c
|
||||
)
|
28
vendor/github.com/powerman/check/go.sum
generated
vendored
Normal file
28
vendor/github.com/powerman/check/go.sum
generated
vendored
Normal file
|
@ -0,0 +1,28 @@
|
|||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c h1:7lF+Vz0LqiRidnzC1Oq86fpX1q/iEv2KJdrCtttYjT4=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/assertions v0.0.0-20190401211740-f487f9de1cd3 h1:hBSHahWMEgzwRyS6dRpxY0XyjZsHyQ61s084wo5PJe0=
|
||||
github.com/smartystreets/assertions v0.0.0-20190401211740-f487f9de1cd3/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a h1:pa8hGb/2YqsZKovtsgrwcDH1RZhVbTKCjLp47XpqCDs=
|
||||
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
|
||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190526052359-791d8a0f4d09 h1:IlD35wZE03o2qJy2o37WIskL33b7PT6cHdGnE8bieZs=
|
||||
golang.org/x/sys v0.0.0-20190526052359-791d8a0f4d09/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191029155521-f43be2a4598c h1:S/FtSvpNLtFBgjTqcKsRpsa6aVsI6iztaz1bQd9BJwE=
|
||||
golang.org/x/sys v0.0.0-20191029155521-f43be2a4598c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
42
vendor/github.com/powerman/check/goconvey.go
generated
vendored
Normal file
42
vendor/github.com/powerman/check/goconvey.go
generated
vendored
Normal file
|
@ -0,0 +1,42 @@
|
|||
package check
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/smartystreets/goconvey/convey/reporting"
|
||||
)
|
||||
|
||||
var errNoGoConvey = errors.New("goconvey not detected")
|
||||
|
||||
func reportToGoConvey(actual, expected, failure string) error {
|
||||
if !flags.detect().conveyJSON {
|
||||
return errNoGoConvey
|
||||
}
|
||||
|
||||
testFile, testLine, funcLine := callerTestFileLines()
|
||||
report := reporting.ScopeResult{
|
||||
File: testFile,
|
||||
Line: funcLine,
|
||||
Assertions: []*reporting.AssertionResult{{
|
||||
File: testFile,
|
||||
Line: testLine,
|
||||
Expected: expected,
|
||||
Actual: actual,
|
||||
Failure: failure,
|
||||
}},
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
fmt.Fprintln(&buf, reporting.OpenJson)
|
||||
if err := json.NewEncoder(&buf).Encode(report); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintln(&buf, ",")
|
||||
fmt.Fprintln(&buf, reporting.CloseJson)
|
||||
_, err := buf.WriteTo(os.Stdout)
|
||||
return err
|
||||
}
|
103
vendor/github.com/powerman/check/stats.go
generated
vendored
Normal file
103
vendor/github.com/powerman/check/stats.go
generated
vendored
Normal file
|
@ -0,0 +1,103 @@
|
|||
package check
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type counter struct {
|
||||
name string
|
||||
value int
|
||||
force bool
|
||||
color string
|
||||
size int
|
||||
}
|
||||
|
||||
func (c counter) String() (s string) {
|
||||
if c.value != 0 || c.force {
|
||||
color := c.color
|
||||
if c.value == 0 {
|
||||
color = ansiReset
|
||||
}
|
||||
s = fmt.Sprintf("%s%*d %s%s", color, c.size, c.value, c.name, ansiReset)
|
||||
} else {
|
||||
s = strings.Repeat(" ", c.size+1+len(c.name))
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
type testStat struct {
|
||||
name string
|
||||
passed counter
|
||||
forged counter
|
||||
failed counter
|
||||
}
|
||||
|
||||
func newTestStat(desc string, force bool) *testStat {
|
||||
return &testStat{
|
||||
name: desc,
|
||||
passed: counter{force: force, name: "passed", color: ansiGreen},
|
||||
forged: counter{force: force, name: "todo", color: ansiYellow},
|
||||
failed: counter{force: force, name: "failed", color: ansiRed},
|
||||
}
|
||||
}
|
||||
|
||||
func (c testStat) String() string {
|
||||
return fmt.Sprintf("checks: %s %s %s\t%s", c.passed, c.forged, c.failed, c.name)
|
||||
}
|
||||
|
||||
var statsMu sync.Mutex
|
||||
var stats = map[*testing.T]*testStat{}
|
||||
|
||||
// Report output statistics about passed/failed checks.
|
||||
// It should be called from TestMain after m.Run(), for ex.:
|
||||
//
|
||||
// func TestMain(m *testing.M) {
|
||||
// code := m.Run()
|
||||
// check.Report()
|
||||
// os.Exit(code)
|
||||
// }
|
||||
//
|
||||
// If this is all you need - just use TestMain instead.
|
||||
func Report() {
|
||||
statsMu.Lock()
|
||||
defer statsMu.Unlock()
|
||||
|
||||
total := newTestStat("(total)", true)
|
||||
ts := make([]*testing.T, 0, len(stats))
|
||||
for t := range stats {
|
||||
ts = append(ts, t)
|
||||
total.passed.value += stats[t].passed.value
|
||||
total.forged.value += stats[t].forged.value
|
||||
total.failed.value += stats[t].failed.value
|
||||
}
|
||||
|
||||
total.passed.size = digits(total.passed.value)
|
||||
total.forged.size = digits(total.forged.value)
|
||||
total.failed.size = digits(total.failed.value)
|
||||
|
||||
if testing.Verbose() {
|
||||
sort.Slice(ts, func(a, b int) bool { return ts[a].Name() < ts[b].Name() })
|
||||
for _, t := range ts {
|
||||
stats[t].passed.size = total.passed.size
|
||||
stats[t].forged.size = total.forged.size
|
||||
stats[t].failed.size = total.failed.size
|
||||
fmt.Printf(" %s\n", stats[t])
|
||||
}
|
||||
}
|
||||
fmt.Printf(" %s\n", total)
|
||||
}
|
||||
|
||||
// TestMain provides same default implementation as used by testing
|
||||
// package with extra Report call to output statistics. Usage:
|
||||
//
|
||||
// func TestMain(m *testing.M) { check.TestMain(m) }
|
||||
func TestMain(m *testing.M) {
|
||||
code := m.Run()
|
||||
Report()
|
||||
os.Exit(code)
|
||||
}
|
64
vendor/github.com/powerman/check/util.go
generated
vendored
Normal file
64
vendor/github.com/powerman/check/util.go
generated
vendored
Normal file
|
@ -0,0 +1,64 @@
|
|||
package check
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func callerTestFileLines() (file string, line int, funcLine int) {
|
||||
pc, file, line, ok := runtime.Caller(0)
|
||||
myfile := file
|
||||
for stack := 1; ok && samePackage(myfile, file); stack++ {
|
||||
pc, file, line, ok = runtime.Caller(stack)
|
||||
}
|
||||
if f := runtime.FuncForPC(pc); f != nil {
|
||||
_, funcLine = f.FileLine(f.Entry())
|
||||
}
|
||||
return file, line, funcLine
|
||||
}
|
||||
|
||||
func samePackage(basefile, file string) bool {
|
||||
return filepath.Dir(basefile) == filepath.Dir(file) && !strings.HasSuffix(file, "_test.go")
|
||||
}
|
||||
|
||||
func callerFuncName(stack int) string {
|
||||
pc, _, _, _ := runtime.Caller(stack + 1)
|
||||
return strings.TrimPrefix(funcNameAt(pc), "(*C).")
|
||||
}
|
||||
|
||||
func funcName(f interface{}) string {
|
||||
return funcNameAt(reflect.ValueOf(f).Pointer())
|
||||
}
|
||||
|
||||
func funcNameAt(pc uintptr) string {
|
||||
name := "<unknown>"
|
||||
if f := runtime.FuncForPC(pc); f != nil {
|
||||
name = f.Name()
|
||||
if i := strings.LastIndex(name, "/"); i != -1 {
|
||||
name = name[i+1:]
|
||||
}
|
||||
if i := strings.Index(name, "."); i != -1 {
|
||||
name = name[i+1:]
|
||||
}
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
func format(msg ...interface{}) string {
|
||||
if len(msg) > 1 {
|
||||
return fmt.Sprintf(msg[0].(string), msg[1:]...)
|
||||
}
|
||||
return fmt.Sprint(msg...)
|
||||
}
|
||||
|
||||
// digits return amount of decimal digits in number.
|
||||
func digits(number int) int {
|
||||
if number == 0 {
|
||||
return 1
|
||||
}
|
||||
return int(math.Floor(math.Log10(float64(number)) + 1))
|
||||
}
|
23
vendor/github.com/smartystreets/goconvey/LICENSE.md
generated
vendored
Normal file
23
vendor/github.com/smartystreets/goconvey/LICENSE.md
generated
vendored
Normal file
|
@ -0,0 +1,23 @@
|
|||
Copyright (c) 2016 SmartyStreets, LLC
|
||||
|
||||
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.
|
||||
|
||||
NOTE: Various optional and subordinate components carry their own licensing
|
||||
requirements and restrictions. Use of those components is subject to the terms
|
||||
and conditions outlined the respective license of each component.
|
28
vendor/github.com/smartystreets/goconvey/convey/gotest/utils.go
generated
vendored
Normal file
28
vendor/github.com/smartystreets/goconvey/convey/gotest/utils.go
generated
vendored
Normal file
|
@ -0,0 +1,28 @@
|
|||
// Package gotest contains internal functionality. Although this package
|
||||
// contains one or more exported names it is not intended for public
|
||||
// consumption. See the examples package for how to use this project.
|
||||
package gotest
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func ResolveExternalCaller() (file string, line int, name string) {
|
||||
var caller_id uintptr
|
||||
callers := runtime.Callers(0, callStack)
|
||||
|
||||
for x := 0; x < callers; x++ {
|
||||
caller_id, file, line, _ = runtime.Caller(x)
|
||||
if strings.HasSuffix(file, "_test.go") || strings.HasSuffix(file, "_tests.go") {
|
||||
name = runtime.FuncForPC(caller_id).Name()
|
||||
return
|
||||
}
|
||||
}
|
||||
file, line, name = "<unknown file>", -1, "<unknown name>"
|
||||
return // panic?
|
||||
}
|
||||
|
||||
const maxStackDepth = 100 // This had better be enough...
|
||||
|
||||
var callStack []uintptr = make([]uintptr, maxStackDepth, maxStackDepth)
|
16
vendor/github.com/smartystreets/goconvey/convey/reporting/console.go
generated
vendored
Normal file
16
vendor/github.com/smartystreets/goconvey/convey/reporting/console.go
generated
vendored
Normal file
|
@ -0,0 +1,16 @@
|
|||
package reporting
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
type console struct{}
|
||||
|
||||
func (self *console) Write(p []byte) (n int, err error) {
|
||||
return fmt.Print(string(p))
|
||||
}
|
||||
|
||||
func NewConsole() io.Writer {
|
||||
return new(console)
|
||||
}
|
5
vendor/github.com/smartystreets/goconvey/convey/reporting/doc.go
generated
vendored
Normal file
5
vendor/github.com/smartystreets/goconvey/convey/reporting/doc.go
generated
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
// Package reporting contains internal functionality related
|
||||
// to console reporting and output. Although this package has
|
||||
// exported names is not intended for public consumption. See the
|
||||
// examples package for how to use this project.
|
||||
package reporting
|
40
vendor/github.com/smartystreets/goconvey/convey/reporting/dot.go
generated
vendored
Normal file
40
vendor/github.com/smartystreets/goconvey/convey/reporting/dot.go
generated
vendored
Normal file
|
@ -0,0 +1,40 @@
|
|||
package reporting
|
||||
|
||||
import "fmt"
|
||||
|
||||
type dot struct{ out *Printer }
|
||||
|
||||
func (self *dot) BeginStory(story *StoryReport) {}
|
||||
|
||||
func (self *dot) Enter(scope *ScopeReport) {}
|
||||
|
||||
func (self *dot) Report(report *AssertionResult) {
|
||||
if report.Error != nil {
|
||||
fmt.Print(redColor)
|
||||
self.out.Insert(dotError)
|
||||
} else if report.Failure != "" {
|
||||
fmt.Print(yellowColor)
|
||||
self.out.Insert(dotFailure)
|
||||
} else if report.Skipped {
|
||||
fmt.Print(yellowColor)
|
||||
self.out.Insert(dotSkip)
|
||||
} else {
|
||||
fmt.Print(greenColor)
|
||||
self.out.Insert(dotSuccess)
|
||||
}
|
||||
fmt.Print(resetColor)
|
||||
}
|
||||
|
||||
func (self *dot) Exit() {}
|
||||
|
||||
func (self *dot) EndStory() {}
|
||||
|
||||
func (self *dot) Write(content []byte) (written int, err error) {
|
||||
return len(content), nil // no-op
|
||||
}
|
||||
|
||||
func NewDotReporter(out *Printer) *dot {
|
||||
self := new(dot)
|
||||
self.out = out
|
||||
return self
|
||||
}
|
33
vendor/github.com/smartystreets/goconvey/convey/reporting/gotest.go
generated
vendored
Normal file
33
vendor/github.com/smartystreets/goconvey/convey/reporting/gotest.go
generated
vendored
Normal file
|
@ -0,0 +1,33 @@
|
|||
package reporting
|
||||
|
||||
type gotestReporter struct{ test T }
|
||||
|
||||
func (self *gotestReporter) BeginStory(story *StoryReport) {
|
||||
self.test = story.Test
|
||||
}
|
||||
|
||||
func (self *gotestReporter) Enter(scope *ScopeReport) {}
|
||||
|
||||
func (self *gotestReporter) Report(r *AssertionResult) {
|
||||
if !passed(r) {
|
||||
self.test.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func (self *gotestReporter) Exit() {}
|
||||
|
||||
func (self *gotestReporter) EndStory() {
|
||||
self.test = nil
|
||||
}
|
||||
|
||||
func (self *gotestReporter) Write(content []byte) (written int, err error) {
|
||||
return len(content), nil // no-op
|
||||
}
|
||||
|
||||
func NewGoTestReporter() *gotestReporter {
|
||||
return new(gotestReporter)
|
||||
}
|
||||
|
||||
func passed(r *AssertionResult) bool {
|
||||
return r.Error == nil && r.Failure == ""
|
||||
}
|
94
vendor/github.com/smartystreets/goconvey/convey/reporting/init.go
generated
vendored
Normal file
94
vendor/github.com/smartystreets/goconvey/convey/reporting/init.go
generated
vendored
Normal file
|
@ -0,0 +1,94 @@
|
|||
package reporting
|
||||
|
||||
import (
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func init() {
|
||||
if !isColorableTerminal() {
|
||||
monochrome()
|
||||
}
|
||||
|
||||
if runtime.GOOS == "windows" {
|
||||
success, failure, error_ = dotSuccess, dotFailure, dotError
|
||||
}
|
||||
}
|
||||
|
||||
func BuildJsonReporter() Reporter {
|
||||
out := NewPrinter(NewConsole())
|
||||
return NewReporters(
|
||||
NewGoTestReporter(),
|
||||
NewJsonReporter(out))
|
||||
}
|
||||
func BuildDotReporter() Reporter {
|
||||
out := NewPrinter(NewConsole())
|
||||
return NewReporters(
|
||||
NewGoTestReporter(),
|
||||
NewDotReporter(out),
|
||||
NewProblemReporter(out),
|
||||
consoleStatistics)
|
||||
}
|
||||
func BuildStoryReporter() Reporter {
|
||||
out := NewPrinter(NewConsole())
|
||||
return NewReporters(
|
||||
NewGoTestReporter(),
|
||||
NewStoryReporter(out),
|
||||
NewProblemReporter(out),
|
||||
consoleStatistics)
|
||||
}
|
||||
func BuildSilentReporter() Reporter {
|
||||
out := NewPrinter(NewConsole())
|
||||
return NewReporters(
|
||||
NewGoTestReporter(),
|
||||
NewSilentProblemReporter(out))
|
||||
}
|
||||
|
||||
var (
|
||||
newline = "\n"
|
||||
success = "✔"
|
||||
failure = "✘"
|
||||
error_ = "🔥"
|
||||
skip = "⚠"
|
||||
dotSuccess = "."
|
||||
dotFailure = "x"
|
||||
dotError = "E"
|
||||
dotSkip = "S"
|
||||
errorTemplate = "* %s \nLine %d: - %v \n%s\n"
|
||||
failureTemplate = "* %s \nLine %d:\n%s\n%s\n"
|
||||
)
|
||||
|
||||
var (
|
||||
greenColor = "\033[32m"
|
||||
yellowColor = "\033[33m"
|
||||
redColor = "\033[31m"
|
||||
resetColor = "\033[0m"
|
||||
)
|
||||
|
||||
var consoleStatistics = NewStatisticsReporter(NewPrinter(NewConsole()))
|
||||
|
||||
func SuppressConsoleStatistics() { consoleStatistics.Suppress() }
|
||||
func PrintConsoleStatistics() { consoleStatistics.PrintSummary() }
|
||||
|
||||
// QuietMode disables all console output symbols. This is only meant to be used
|
||||
// for tests that are internal to goconvey where the output is distracting or
|
||||
// otherwise not needed in the test output.
|
||||
func QuietMode() {
|
||||
success, failure, error_, skip, dotSuccess, dotFailure, dotError, dotSkip = "", "", "", "", "", "", "", ""
|
||||
}
|
||||
|
||||
func monochrome() {
|
||||
greenColor, yellowColor, redColor, resetColor = "", "", "", ""
|
||||
}
|
||||
|
||||
func isColorableTerminal() bool {
|
||||
return strings.Contains(os.Getenv("TERM"), "color")
|
||||
}
|
||||
|
||||
// This interface allows us to pass the *testing.T struct
|
||||
// throughout the internals of this tool without ever
|
||||
// having to import the "testing" package.
|
||||
type T interface {
|
||||
Fail()
|
||||
}
|
88
vendor/github.com/smartystreets/goconvey/convey/reporting/json.go
generated
vendored
Normal file
88
vendor/github.com/smartystreets/goconvey/convey/reporting/json.go
generated
vendored
Normal file
|
@ -0,0 +1,88 @@
|
|||
// TODO: under unit test
|
||||
|
||||
package reporting
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type JsonReporter struct {
|
||||
out *Printer
|
||||
currentKey []string
|
||||
current *ScopeResult
|
||||
index map[string]*ScopeResult
|
||||
scopes []*ScopeResult
|
||||
}
|
||||
|
||||
func (self *JsonReporter) depth() int { return len(self.currentKey) }
|
||||
|
||||
func (self *JsonReporter) BeginStory(story *StoryReport) {}
|
||||
|
||||
func (self *JsonReporter) Enter(scope *ScopeReport) {
|
||||
self.currentKey = append(self.currentKey, scope.Title)
|
||||
ID := strings.Join(self.currentKey, "|")
|
||||
if _, found := self.index[ID]; !found {
|
||||
next := newScopeResult(scope.Title, self.depth(), scope.File, scope.Line)
|
||||
self.scopes = append(self.scopes, next)
|
||||
self.index[ID] = next
|
||||
}
|
||||
self.current = self.index[ID]
|
||||
}
|
||||
|
||||
func (self *JsonReporter) Report(report *AssertionResult) {
|
||||
self.current.Assertions = append(self.current.Assertions, report)
|
||||
}
|
||||
|
||||
func (self *JsonReporter) Exit() {
|
||||
self.currentKey = self.currentKey[:len(self.currentKey)-1]
|
||||
}
|
||||
|
||||
func (self *JsonReporter) EndStory() {
|
||||
self.report()
|
||||
self.reset()
|
||||
}
|
||||
func (self *JsonReporter) report() {
|
||||
scopes := []string{}
|
||||
for _, scope := range self.scopes {
|
||||
serialized, err := json.Marshal(scope)
|
||||
if err != nil {
|
||||
self.out.Println(jsonMarshalFailure)
|
||||
panic(err)
|
||||
}
|
||||
var buffer bytes.Buffer
|
||||
json.Indent(&buffer, serialized, "", " ")
|
||||
scopes = append(scopes, buffer.String())
|
||||
}
|
||||
self.out.Print(fmt.Sprintf("%s\n%s,\n%s\n", OpenJson, strings.Join(scopes, ","), CloseJson))
|
||||
}
|
||||
func (self *JsonReporter) reset() {
|
||||
self.scopes = []*ScopeResult{}
|
||||
self.index = map[string]*ScopeResult{}
|
||||
self.currentKey = nil
|
||||
}
|
||||
|
||||
func (self *JsonReporter) Write(content []byte) (written int, err error) {
|
||||
self.current.Output += string(content)
|
||||
return len(content), nil
|
||||
}
|
||||
|
||||
func NewJsonReporter(out *Printer) *JsonReporter {
|
||||
self := new(JsonReporter)
|
||||
self.out = out
|
||||
self.reset()
|
||||
return self
|
||||
}
|
||||
|
||||
const OpenJson = ">->->OPEN-JSON->->->" // "⌦"
|
||||
const CloseJson = "<-<-<-CLOSE-JSON<-<-<" // "⌫"
|
||||
const jsonMarshalFailure = `
|
||||
|
||||
GOCONVEY_JSON_MARSHALL_FAILURE: There was an error when attempting to convert test results to JSON.
|
||||
Please file a bug report and reference the code that caused this failure if possible.
|
||||
|
||||
Here's the panic:
|
||||
|
||||
`
|
60
vendor/github.com/smartystreets/goconvey/convey/reporting/printer.go
generated
vendored
Normal file
60
vendor/github.com/smartystreets/goconvey/convey/reporting/printer.go
generated
vendored
Normal file
|
@ -0,0 +1,60 @@
|
|||
package reporting
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Printer struct {
|
||||
out io.Writer
|
||||
prefix string
|
||||
}
|
||||
|
||||
func (self *Printer) Println(message string, values ...interface{}) {
|
||||
formatted := self.format(message, values...) + newline
|
||||
self.out.Write([]byte(formatted))
|
||||
}
|
||||
|
||||
func (self *Printer) Print(message string, values ...interface{}) {
|
||||
formatted := self.format(message, values...)
|
||||
self.out.Write([]byte(formatted))
|
||||
}
|
||||
|
||||
func (self *Printer) Insert(text string) {
|
||||
self.out.Write([]byte(text))
|
||||
}
|
||||
|
||||
func (self *Printer) format(message string, values ...interface{}) string {
|
||||
var formatted string
|
||||
if len(values) == 0 {
|
||||
formatted = self.prefix + message
|
||||
} else {
|
||||
formatted = self.prefix + fmt_Sprintf(message, values...)
|
||||
}
|
||||
indented := strings.Replace(formatted, newline, newline+self.prefix, -1)
|
||||
return strings.TrimRight(indented, space)
|
||||
}
|
||||
|
||||
// Extracting fmt.Sprintf to a separate variable circumvents go vet, which, as of go 1.10 is run with go test.
|
||||
var fmt_Sprintf = fmt.Sprintf
|
||||
|
||||
func (self *Printer) Indent() {
|
||||
self.prefix += pad
|
||||
}
|
||||
|
||||
func (self *Printer) Dedent() {
|
||||
if len(self.prefix) >= padLength {
|
||||
self.prefix = self.prefix[:len(self.prefix)-padLength]
|
||||
}
|
||||
}
|
||||
|
||||
func NewPrinter(out io.Writer) *Printer {
|
||||
self := new(Printer)
|
||||
self.out = out
|
||||
return self
|
||||
}
|
||||
|
||||
const space = " "
|
||||
const pad = space + space
|
||||
const padLength = len(pad)
|
80
vendor/github.com/smartystreets/goconvey/convey/reporting/problems.go
generated
vendored
Normal file
80
vendor/github.com/smartystreets/goconvey/convey/reporting/problems.go
generated
vendored
Normal file
|
@ -0,0 +1,80 @@
|
|||
package reporting
|
||||
|
||||
import "fmt"
|
||||
|
||||
type problem struct {
|
||||
silent bool
|
||||
out *Printer
|
||||
errors []*AssertionResult
|
||||
failures []*AssertionResult
|
||||
}
|
||||
|
||||
func (self *problem) BeginStory(story *StoryReport) {}
|
||||
|
||||
func (self *problem) Enter(scope *ScopeReport) {}
|
||||
|
||||
func (self *problem) Report(report *AssertionResult) {
|
||||
if report.Error != nil {
|
||||
self.errors = append(self.errors, report)
|
||||
} else if report.Failure != "" {
|
||||
self.failures = append(self.failures, report)
|
||||
}
|
||||
}
|
||||
|
||||
func (self *problem) Exit() {}
|
||||
|
||||
func (self *problem) EndStory() {
|
||||
self.show(self.showErrors, redColor)
|
||||
self.show(self.showFailures, yellowColor)
|
||||
self.prepareForNextStory()
|
||||
}
|
||||
func (self *problem) show(display func(), color string) {
|
||||
if !self.silent {
|
||||
fmt.Print(color)
|
||||
}
|
||||
display()
|
||||
if !self.silent {
|
||||
fmt.Print(resetColor)
|
||||
}
|
||||
self.out.Dedent()
|
||||
}
|
||||
func (self *problem) showErrors() {
|
||||
for i, e := range self.errors {
|
||||
if i == 0 {
|
||||
self.out.Println("\nErrors:\n")
|
||||
self.out.Indent()
|
||||
}
|
||||
self.out.Println(errorTemplate, e.File, e.Line, e.Error, e.StackTrace)
|
||||
}
|
||||
}
|
||||
func (self *problem) showFailures() {
|
||||
for i, f := range self.failures {
|
||||
if i == 0 {
|
||||
self.out.Println("\nFailures:\n")
|
||||
self.out.Indent()
|
||||
}
|
||||
self.out.Println(failureTemplate, f.File, f.Line, f.Failure, f.StackTrace)
|
||||
}
|
||||
}
|
||||
|
||||
func (self *problem) Write(content []byte) (written int, err error) {
|
||||
return len(content), nil // no-op
|
||||
}
|
||||
|
||||
func NewProblemReporter(out *Printer) *problem {
|
||||
self := new(problem)
|
||||
self.out = out
|
||||
self.prepareForNextStory()
|
||||
return self
|
||||
}
|
||||
|
||||
func NewSilentProblemReporter(out *Printer) *problem {
|
||||
self := NewProblemReporter(out)
|
||||
self.silent = true
|
||||
return self
|
||||
}
|
||||
|
||||
func (self *problem) prepareForNextStory() {
|
||||
self.errors = []*AssertionResult{}
|
||||
self.failures = []*AssertionResult{}
|
||||
}
|
39
vendor/github.com/smartystreets/goconvey/convey/reporting/reporter.go
generated
vendored
Normal file
39
vendor/github.com/smartystreets/goconvey/convey/reporting/reporter.go
generated
vendored
Normal file
|
@ -0,0 +1,39 @@
|
|||
package reporting
|
||||
|
||||
import "io"
|
||||
|
||||
type Reporter interface {
|
||||
BeginStory(story *StoryReport)
|
||||
Enter(scope *ScopeReport)
|
||||
Report(r *AssertionResult)
|
||||
Exit()
|
||||
EndStory()
|
||||
io.Writer
|
||||
}
|
||||
|
||||
type reporters struct{ collection []Reporter }
|
||||
|
||||
func (self *reporters) BeginStory(s *StoryReport) { self.foreach(func(r Reporter) { r.BeginStory(s) }) }
|
||||
func (self *reporters) Enter(s *ScopeReport) { self.foreach(func(r Reporter) { r.Enter(s) }) }
|
||||
func (self *reporters) Report(a *AssertionResult) { self.foreach(func(r Reporter) { r.Report(a) }) }
|
||||
func (self *reporters) Exit() { self.foreach(func(r Reporter) { r.Exit() }) }
|
||||
func (self *reporters) EndStory() { self.foreach(func(r Reporter) { r.EndStory() }) }
|
||||
|
||||
func (self *reporters) Write(contents []byte) (written int, err error) {
|
||||
self.foreach(func(r Reporter) {
|
||||
written, err = r.Write(contents)
|
||||
})
|
||||
return written, err
|
||||
}
|
||||
|
||||
func (self *reporters) foreach(action func(Reporter)) {
|
||||
for _, r := range self.collection {
|
||||
action(r)
|
||||
}
|
||||
}
|
||||
|
||||
func NewReporters(collection ...Reporter) *reporters {
|
||||
self := new(reporters)
|
||||
self.collection = collection
|
||||
return self
|
||||
}
|
2
vendor/github.com/smartystreets/goconvey/convey/reporting/reporting.goconvey
generated
vendored
Normal file
2
vendor/github.com/smartystreets/goconvey/convey/reporting/reporting.goconvey
generated
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
#ignore
|
||||
-timeout=1s
|
179
vendor/github.com/smartystreets/goconvey/convey/reporting/reports.go
generated
vendored
Normal file
179
vendor/github.com/smartystreets/goconvey/convey/reporting/reports.go
generated
vendored
Normal file
|
@ -0,0 +1,179 @@
|
|||
package reporting
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/smartystreets/goconvey/convey/gotest"
|
||||
)
|
||||
|
||||
////////////////// ScopeReport ////////////////////
|
||||
|
||||
type ScopeReport struct {
|
||||
Title string
|
||||
File string
|
||||
Line int
|
||||
}
|
||||
|
||||
func NewScopeReport(title string) *ScopeReport {
|
||||
file, line, _ := gotest.ResolveExternalCaller()
|
||||
self := new(ScopeReport)
|
||||
self.Title = title
|
||||
self.File = file
|
||||
self.Line = line
|
||||
return self
|
||||
}
|
||||
|
||||
////////////////// ScopeResult ////////////////////
|
||||
|
||||
type ScopeResult struct {
|
||||
Title string
|
||||
File string
|
||||
Line int
|
||||
Depth int
|
||||
Assertions []*AssertionResult
|
||||
Output string
|
||||
}
|
||||
|
||||
func newScopeResult(title string, depth int, file string, line int) *ScopeResult {
|
||||
self := new(ScopeResult)
|
||||
self.Title = title
|
||||
self.Depth = depth
|
||||
self.File = file
|
||||
self.Line = line
|
||||
self.Assertions = []*AssertionResult{}
|
||||
return self
|
||||
}
|
||||
|
||||
/////////////////// StoryReport /////////////////////
|
||||
|
||||
type StoryReport struct {
|
||||
Test T
|
||||
Name string
|
||||
File string
|
||||
Line int
|
||||
}
|
||||
|
||||
func NewStoryReport(test T) *StoryReport {
|
||||
file, line, name := gotest.ResolveExternalCaller()
|
||||
name = removePackagePath(name)
|
||||
self := new(StoryReport)
|
||||
self.Test = test
|
||||
self.Name = name
|
||||
self.File = file
|
||||
self.Line = line
|
||||
return self
|
||||
}
|
||||
|
||||
// name comes in looking like "github.com/smartystreets/goconvey/examples.TestName".
|
||||
// We only want the stuff after the last '.', which is the name of the test function.
|
||||
func removePackagePath(name string) string {
|
||||
parts := strings.Split(name, ".")
|
||||
return parts[len(parts)-1]
|
||||
}
|
||||
|
||||
/////////////////// FailureView ////////////////////////
|
||||
|
||||
// This struct is also declared in github.com/smartystreets/assertions.
|
||||
// The json struct tags should be equal in both declarations.
|
||||
type FailureView struct {
|
||||
Message string `json:"Message"`
|
||||
Expected string `json:"Expected"`
|
||||
Actual string `json:"Actual"`
|
||||
}
|
||||
|
||||
////////////////////AssertionResult //////////////////////
|
||||
|
||||
type AssertionResult struct {
|
||||
File string
|
||||
Line int
|
||||
Expected string
|
||||
Actual string
|
||||
Failure string
|
||||
Error interface{}
|
||||
StackTrace string
|
||||
Skipped bool
|
||||
}
|
||||
|
||||
func NewFailureReport(failure string) *AssertionResult {
|
||||
report := new(AssertionResult)
|
||||
report.File, report.Line = caller()
|
||||
report.StackTrace = stackTrace()
|
||||
parseFailure(failure, report)
|
||||
return report
|
||||
}
|
||||
func parseFailure(failure string, report *AssertionResult) {
|
||||
view := new(FailureView)
|
||||
err := json.Unmarshal([]byte(failure), view)
|
||||
if err == nil {
|
||||
report.Failure = view.Message
|
||||
report.Expected = view.Expected
|
||||
report.Actual = view.Actual
|
||||
} else {
|
||||
report.Failure = failure
|
||||
}
|
||||
}
|
||||
func NewErrorReport(err interface{}) *AssertionResult {
|
||||
report := new(AssertionResult)
|
||||
report.File, report.Line = caller()
|
||||
report.StackTrace = fullStackTrace()
|
||||
report.Error = fmt.Sprintf("%v", err)
|
||||
return report
|
||||
}
|
||||
func NewSuccessReport() *AssertionResult {
|
||||
return new(AssertionResult)
|
||||
}
|
||||
func NewSkipReport() *AssertionResult {
|
||||
report := new(AssertionResult)
|
||||
report.File, report.Line = caller()
|
||||
report.StackTrace = fullStackTrace()
|
||||
report.Skipped = true
|
||||
return report
|
||||
}
|
||||
|
||||
func caller() (file string, line int) {
|
||||
file, line, _ = gotest.ResolveExternalCaller()
|
||||
return
|
||||
}
|
||||
|
||||
func stackTrace() string {
|
||||
buffer := make([]byte, 1024*64)
|
||||
n := runtime.Stack(buffer, false)
|
||||
return removeInternalEntries(string(buffer[:n]))
|
||||
}
|
||||
func fullStackTrace() string {
|
||||
buffer := make([]byte, 1024*64)
|
||||
n := runtime.Stack(buffer, true)
|
||||
return removeInternalEntries(string(buffer[:n]))
|
||||
}
|
||||
func removeInternalEntries(stack string) string {
|
||||
lines := strings.Split(stack, newline)
|
||||
filtered := []string{}
|
||||
for _, line := range lines {
|
||||
if !isExternal(line) {
|
||||
filtered = append(filtered, line)
|
||||
}
|
||||
}
|
||||
return strings.Join(filtered, newline)
|
||||
}
|
||||
func isExternal(line string) bool {
|
||||
for _, p := range internalPackages {
|
||||
if strings.Contains(line, p) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// NOTE: any new packages that host goconvey packages will need to be added here!
|
||||
// An alternative is to scan the goconvey directory and then exclude stuff like
|
||||
// the examples package but that's nasty too.
|
||||
var internalPackages = []string{
|
||||
"goconvey/assertions",
|
||||
"goconvey/convey",
|
||||
"goconvey/execution",
|
||||
"goconvey/gotest",
|
||||
"goconvey/reporting",
|
||||
}
|
108
vendor/github.com/smartystreets/goconvey/convey/reporting/statistics.go
generated
vendored
Normal file
108
vendor/github.com/smartystreets/goconvey/convey/reporting/statistics.go
generated
vendored
Normal file
|
@ -0,0 +1,108 @@
|
|||
package reporting
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
)
|
||||
|
||||
func (self *statistics) BeginStory(story *StoryReport) {}
|
||||
|
||||
func (self *statistics) Enter(scope *ScopeReport) {}
|
||||
|
||||
func (self *statistics) Report(report *AssertionResult) {
|
||||
self.Lock()
|
||||
defer self.Unlock()
|
||||
|
||||
if !self.failing && report.Failure != "" {
|
||||
self.failing = true
|
||||
}
|
||||
if !self.erroring && report.Error != nil {
|
||||
self.erroring = true
|
||||
}
|
||||
if report.Skipped {
|
||||
self.skipped += 1
|
||||
} else {
|
||||
self.total++
|
||||
}
|
||||
}
|
||||
|
||||
func (self *statistics) Exit() {}
|
||||
|
||||
func (self *statistics) EndStory() {
|
||||
self.Lock()
|
||||
defer self.Unlock()
|
||||
|
||||
if !self.suppressed {
|
||||
self.printSummaryLocked()
|
||||
}
|
||||
}
|
||||
|
||||
func (self *statistics) Suppress() {
|
||||
self.Lock()
|
||||
defer self.Unlock()
|
||||
self.suppressed = true
|
||||
}
|
||||
|
||||
func (self *statistics) PrintSummary() {
|
||||
self.Lock()
|
||||
defer self.Unlock()
|
||||
self.printSummaryLocked()
|
||||
}
|
||||
|
||||
func (self *statistics) printSummaryLocked() {
|
||||
self.reportAssertionsLocked()
|
||||
self.reportSkippedSectionsLocked()
|
||||
self.completeReportLocked()
|
||||
}
|
||||
func (self *statistics) reportAssertionsLocked() {
|
||||
self.decideColorLocked()
|
||||
self.out.Print("\n%d total %s", self.total, plural("assertion", self.total))
|
||||
}
|
||||
func (self *statistics) decideColorLocked() {
|
||||
if self.failing && !self.erroring {
|
||||
fmt.Print(yellowColor)
|
||||
} else if self.erroring {
|
||||
fmt.Print(redColor)
|
||||
} else {
|
||||
fmt.Print(greenColor)
|
||||
}
|
||||
}
|
||||
func (self *statistics) reportSkippedSectionsLocked() {
|
||||
if self.skipped > 0 {
|
||||
fmt.Print(yellowColor)
|
||||
self.out.Print(" (one or more sections skipped)")
|
||||
}
|
||||
}
|
||||
func (self *statistics) completeReportLocked() {
|
||||
fmt.Print(resetColor)
|
||||
self.out.Print("\n")
|
||||
self.out.Print("\n")
|
||||
}
|
||||
|
||||
func (self *statistics) Write(content []byte) (written int, err error) {
|
||||
return len(content), nil // no-op
|
||||
}
|
||||
|
||||
func NewStatisticsReporter(out *Printer) *statistics {
|
||||
self := statistics{}
|
||||
self.out = out
|
||||
return &self
|
||||
}
|
||||
|
||||
type statistics struct {
|
||||
sync.Mutex
|
||||
|
||||
out *Printer
|
||||
total int
|
||||
failing bool
|
||||
erroring bool
|
||||
skipped int
|
||||
suppressed bool
|
||||
}
|
||||
|
||||
func plural(word string, count int) string {
|
||||
if count == 1 {
|
||||
return word
|
||||
}
|
||||
return word + "s"
|
||||
}
|
73
vendor/github.com/smartystreets/goconvey/convey/reporting/story.go
generated
vendored
Normal file
73
vendor/github.com/smartystreets/goconvey/convey/reporting/story.go
generated
vendored
Normal file
|
@ -0,0 +1,73 @@
|
|||
// TODO: in order for this reporter to be completely honest
|
||||
// we need to retrofit to be more like the json reporter such that:
|
||||
// 1. it maintains ScopeResult collections, which count assertions
|
||||
// 2. it reports only after EndStory(), so that all tick marks
|
||||
// are placed near the appropriate title.
|
||||
// 3. Under unit test
|
||||
|
||||
package reporting
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type story struct {
|
||||
out *Printer
|
||||
titlesById map[string]string
|
||||
currentKey []string
|
||||
}
|
||||
|
||||
func (self *story) BeginStory(story *StoryReport) {}
|
||||
|
||||
func (self *story) Enter(scope *ScopeReport) {
|
||||
self.out.Indent()
|
||||
|
||||
self.currentKey = append(self.currentKey, scope.Title)
|
||||
ID := strings.Join(self.currentKey, "|")
|
||||
|
||||
if _, found := self.titlesById[ID]; !found {
|
||||
self.out.Println("")
|
||||
self.out.Print(scope.Title)
|
||||
self.out.Insert(" ")
|
||||
self.titlesById[ID] = scope.Title
|
||||
}
|
||||
}
|
||||
|
||||
func (self *story) Report(report *AssertionResult) {
|
||||
if report.Error != nil {
|
||||
fmt.Print(redColor)
|
||||
self.out.Insert(error_)
|
||||
} else if report.Failure != "" {
|
||||
fmt.Print(yellowColor)
|
||||
self.out.Insert(failure)
|
||||
} else if report.Skipped {
|
||||
fmt.Print(yellowColor)
|
||||
self.out.Insert(skip)
|
||||
} else {
|
||||
fmt.Print(greenColor)
|
||||
self.out.Insert(success)
|
||||
}
|
||||
fmt.Print(resetColor)
|
||||
}
|
||||
|
||||
func (self *story) Exit() {
|
||||
self.out.Dedent()
|
||||
self.currentKey = self.currentKey[:len(self.currentKey)-1]
|
||||
}
|
||||
|
||||
func (self *story) EndStory() {
|
||||
self.titlesById = make(map[string]string)
|
||||
self.out.Println("\n")
|
||||
}
|
||||
|
||||
func (self *story) Write(content []byte) (written int, err error) {
|
||||
return len(content), nil // no-op
|
||||
}
|
||||
|
||||
func NewStoryReporter(out *Printer) *story {
|
||||
self := new(story)
|
||||
self.out = out
|
||||
self.titlesById = make(map[string]string)
|
||||
return self
|
||||
}
|
11
vendor/modules.txt
vendored
11
vendor/modules.txt
vendored
|
@ -9,6 +9,8 @@ github.com/aead/poly1305
|
|||
# github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf
|
||||
github.com/coreos/go-systemd/activation
|
||||
github.com/coreos/go-systemd/daemon
|
||||
# github.com/davecgh/go-spew v1.1.1
|
||||
github.com/davecgh/go-spew/spew
|
||||
# github.com/dchest/safefile v0.0.0-20151022103144-855e8d98f185
|
||||
github.com/dchest/safefile
|
||||
# github.com/facebookgo/atomicfile v0.0.0-20151019160806-2de1f203e7d5
|
||||
|
@ -38,6 +40,15 @@ github.com/k-sone/critbitgo
|
|||
github.com/kardianos/service
|
||||
# github.com/miekg/dns v1.1.22
|
||||
github.com/miekg/dns
|
||||
# github.com/pkg/errors v0.8.1
|
||||
github.com/pkg/errors
|
||||
# github.com/pmezard/go-difflib v1.0.0
|
||||
github.com/pmezard/go-difflib/difflib
|
||||
# github.com/powerman/check v1.2.1
|
||||
github.com/powerman/check
|
||||
# github.com/smartystreets/goconvey v1.6.4
|
||||
github.com/smartystreets/goconvey/convey/gotest
|
||||
github.com/smartystreets/goconvey/convey/reporting
|
||||
# golang.org/x/crypto v0.0.0-20191029031824-8986dd9e96cf
|
||||
golang.org/x/crypto/curve25519
|
||||
golang.org/x/crypto/ed25519
|
||||
|
|
Loading…
Add table
Reference in a new issue