refreshed godep since some PRs are still coming into master branch

This commit is contained in:
Brad Rydzewski 2014-09-23 23:51:29 -07:00
parent 9fcec0025d
commit 2523c82d33
394 changed files with 36704 additions and 31828 deletions

154
Godeps/Godeps.json generated
View file

@ -1,56 +1,52 @@
{
"ImportPath": "github.com/drone/drone",
"GoVersion": "go1.2.1",
"GoVersion": "go1.3.1",
"Packages": [
"./..."
],
"Deps": [
{
"ImportPath": "bitbucket.org/kardianos/osext",
"Comment": "null-9",
"Rev": "364fb577de68fb646c4cb39cc0e09c887ee16376"
"Comment": "null-13",
"Rev": "5d3ddcf53a508cc2f7404eaebf546ef2cb5cdb6e"
},
{
"ImportPath": "code.google.com/p/go.crypto/bcrypt",
"Comment": "null-192",
"Rev": "2990fc550b9ff92bc33c848f92c87a305ae62934"
"Comment": "null-191",
"Rev": "7aa593ce8ceaf0199504fff27455bbb60eedabc7"
},
{
"ImportPath": "code.google.com/p/go.crypto/blowfish",
"Comment": "null-192",
"Rev": "2990fc550b9ff92bc33c848f92c87a305ae62934"
"Comment": "null-191",
"Rev": "7aa593ce8ceaf0199504fff27455bbb60eedabc7"
},
{
"ImportPath": "code.google.com/p/go.crypto/ssh",
"Comment": "null-192",
"Rev": "2990fc550b9ff92bc33c848f92c87a305ae62934"
"Comment": "null-191",
"Rev": "7aa593ce8ceaf0199504fff27455bbb60eedabc7"
},
{
"ImportPath": "code.google.com/p/go.net/websocket",
"Comment": "null-117",
"Rev": "c17ad62118ea511e1051721b429779fa40bddc74"
"Comment": "null-144",
"Rev": "ad01a6fcc8a19d3a4478c836895ffe883bd2ceab"
},
{
"ImportPath": "code.google.com/p/go.text/transform",
"Comment": "null-82",
"Rev": "3a670b007ac92a10a5d9bcfc90307995a2d83d35"
"Comment": "null-78",
"Rev": "b381881d02615d8d58bba23bda3cb4ee20300630"
},
{
"ImportPath": "code.google.com/p/go.text/unicode/norm",
"Comment": "null-82",
"Rev": "3a670b007ac92a10a5d9bcfc90307995a2d83d35"
"Comment": "null-78",
"Rev": "b381881d02615d8d58bba23bda3cb4ee20300630"
},
{
"ImportPath": "code.google.com/p/gomock/gomock",
"Rev": "f465e13c1a71273ab234f1e40a327556b6518a27"
},
{
"ImportPath": "github.com/GeertJohan/go.incremental",
"Rev": "92fd0ce4a694213e8b3dfd2d39b16e51d26d0fbf"
"Rev": "e033c7513ca3d743bbb64df299bdec29e93fed03"
},
{
"ImportPath": "github.com/GeertJohan/go.rice",
"Rev": "387fd3d63f391b3155205bdc6cc089b0741273df"
"Rev": "a4d0b5624c673fef4b517f350272136ced6bb5b1"
},
{
"ImportPath": "github.com/andybons/hipchat",
@ -64,10 +60,6 @@
"ImportPath": "github.com/daaku/go.zipexe",
"Rev": "44882fc939f4c58d87a60de34796c6cfb9623269"
},
{
"ImportPath": "github.com/davecgh/go-spew/spew",
"Rev": "9ed19f9b0c9116d712e32dee78f1704cb9fc5b02"
},
{
"ImportPath": "github.com/dchest/authcookie",
"Comment": "weekly.2012-02-07-1-gfbdef6e",
@ -81,51 +73,96 @@
"ImportPath": "github.com/dchest/uniuri",
"Rev": "bc4af7603a3e0ce9d58009f82fca481555182e1c"
},
{
"ImportPath": "github.com/docker/docker/dockerversion",
"Comment": "v1.2.0-576-gf68f5fd",
"Rev": "f68f5fd521270ad9775fb0adfe7516f9e4855ba5"
},
{
"ImportPath": "github.com/docker/docker/pkg/ioutils",
"Comment": "v1.2.0-576-gf68f5fd",
"Rev": "f68f5fd521270ad9775fb0adfe7516f9e4855ba5"
},
{
"ImportPath": "github.com/docker/docker/pkg/log",
"Comment": "v1.2.0-576-gf68f5fd",
"Rev": "f68f5fd521270ad9775fb0adfe7516f9e4855ba5"
},
{
"ImportPath": "github.com/docker/docker/pkg/pools",
"Comment": "v1.2.0-576-gf68f5fd",
"Rev": "f68f5fd521270ad9775fb0adfe7516f9e4855ba5"
},
{
"ImportPath": "github.com/docker/docker/pkg/system",
"Comment": "v1.2.0-576-gf68f5fd",
"Rev": "f68f5fd521270ad9775fb0adfe7516f9e4855ba5"
},
{
"ImportPath": "github.com/docker/docker/pkg/term",
"Comment": "v1.2.0-576-gf68f5fd",
"Rev": "f68f5fd521270ad9775fb0adfe7516f9e4855ba5"
},
{
"ImportPath": "github.com/docker/docker/pkg/timeutils",
"Comment": "v1.2.0-576-gf68f5fd",
"Rev": "f68f5fd521270ad9775fb0adfe7516f9e4855ba5"
},
{
"ImportPath": "github.com/docker/docker/pkg/units",
"Comment": "v1.2.0-576-gf68f5fd",
"Rev": "f68f5fd521270ad9775fb0adfe7516f9e4855ba5"
},
{
"ImportPath": "github.com/docker/docker/utils",
"Comment": "v1.2.0-576-gf68f5fd",
"Rev": "f68f5fd521270ad9775fb0adfe7516f9e4855ba5"
},
{
"ImportPath": "github.com/docker/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar",
"Comment": "v1.2.0-576-gf68f5fd",
"Rev": "f68f5fd521270ad9775fb0adfe7516f9e4855ba5"
},
{
"ImportPath": "github.com/dotcloud/docker/archive",
"Comment": "v0.9.0-553-g36af293",
"Rev": "36af2936af2e55216456dbe62e7fcbeff9b630d5"
"Comment": "v1.2.0-576-gf68f5fd",
"Rev": "f68f5fd521270ad9775fb0adfe7516f9e4855ba5"
},
{
"ImportPath": "github.com/dotcloud/docker/dockerversion",
"Comment": "v0.9.0-553-g36af293",
"Rev": "36af2936af2e55216456dbe62e7fcbeff9b630d5"
"ImportPath": "github.com/dotcloud/docker/pkg/parsers",
"Comment": "v1.2.0-576-gf68f5fd",
"Rev": "f68f5fd521270ad9775fb0adfe7516f9e4855ba5"
},
{
"ImportPath": "github.com/dotcloud/docker/pkg/system",
"Comment": "v0.9.0-553-g36af293",
"Rev": "36af2936af2e55216456dbe62e7fcbeff9b630d5"
"ImportPath": "github.com/dotcloud/docker/pkg/stdcopy",
"Comment": "v1.2.0-576-gf68f5fd",
"Rev": "f68f5fd521270ad9775fb0adfe7516f9e4855ba5"
},
{
"ImportPath": "github.com/dotcloud/docker/pkg/term",
"Comment": "v0.9.0-553-g36af293",
"Rev": "36af2936af2e55216456dbe62e7fcbeff9b630d5"
"Comment": "v1.2.0-576-gf68f5fd",
"Rev": "f68f5fd521270ad9775fb0adfe7516f9e4855ba5"
},
{
"ImportPath": "github.com/dotcloud/docker/utils",
"Comment": "v0.9.0-553-g36af293",
"Rev": "36af2936af2e55216456dbe62e7fcbeff9b630d5"
},
{
"ImportPath": "github.com/dotcloud/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar",
"Comment": "v0.9.0-553-g36af293",
"Rev": "36af2936af2e55216456dbe62e7fcbeff9b630d5"
"Comment": "v1.2.0-576-gf68f5fd",
"Rev": "f68f5fd521270ad9775fb0adfe7516f9e4855ba5"
},
{
"ImportPath": "github.com/drone/go-bitbucket/bitbucket",
"Rev": "0c0cf4ece975efdfcf6daa78b03d4e84dd257da7"
"Rev": "2174d45d99216e5eed9669c4f9939a199bc212d1"
},
{
"ImportPath": "github.com/drone/go-bitbucket/oauth1",
"Rev": "0c0cf4ece975efdfcf6daa78b03d4e84dd257da7"
"Rev": "2174d45d99216e5eed9669c4f9939a199bc212d1"
},
{
"ImportPath": "github.com/drone/go-github/github",
"Rev": "1d31b1373d615a3cdef6eaa87d94e1601e6ee798"
"Rev": "ebc1b301cb7009a1e8fd265b491dd89980a0d12b"
},
{
"ImportPath": "github.com/drone/go-github/oauth2",
"Rev": "1d31b1373d615a3cdef6eaa87d94e1601e6ee798"
"Rev": "ebc1b301cb7009a1e8fd265b491dd89980a0d12b"
},
{
"ImportPath": "github.com/fluffle/goevent/event",
@ -148,22 +185,13 @@
"Comment": "go1",
"Rev": "9a7aa3606b82e2081a13a008ada88dfdb96c20fd"
},
{
"ImportPath": "github.com/jacobsa/oglematchers",
"Rev": "4fc24f97b5b74022c2a3f4ca7eed57ca29083d3e"
},
{
"ImportPath": "github.com/jessevdk/go-flags",
"Comment": "v1-154-gcee4db9",
"Rev": "cee4db96aec1f7382a0b09d8249c09a8bb8d160b"
},
{
"ImportPath": "github.com/mattn/go-sqlite3",
"Rev": "58c62dc30cda06e4a74e18f6489bb6c112fc6e5d"
"Rev": "fb0ae124843f77b46af49646f8316c71106ab758"
},
{
"ImportPath": "github.com/plouc/go-gitlab-client",
"Rev": "b0e1b9f3f7585d066d05341bbfdf6d0dd6930cae"
"Rev": "80398caaef592ef875851e74876d5b2d52dbcb43"
},
{
"ImportPath": "github.com/russross/meddler",
@ -171,17 +199,21 @@
},
{
"ImportPath": "github.com/smartystreets/goconvey/convey",
"Comment": "1.5.0-168-g2abea07",
"Rev": "2abea075ec076abf0572d29bdb28ae7da64cfb7a"
"Comment": "1.5.0-264-ge4cef38",
"Rev": "e4cef38b8924b3cff6d84a524e8fe938ad0bcbf2"
},
{
"ImportPath": "github.com/stvp/flowdock",
"Rev": "50362abeabebf40b0f56a326d62f17e61a59302b"
},
{
"ImportPath": "gopkg.in/v1/yaml",
"Rev": "cbe5fa8cb0a9cd6fdc7efe2581c8f91b6f20f949"
},
{
"ImportPath": "launchpad.net/goyaml",
"Comment": "51",
"Rev": "gustavo@niemeyer.net-20140305200416-7gh64vkcckre5mob"
"Comment": "50",
"Rev": "gustavo@niemeyer.net-20131114120802-abe042syx64z2m7s"
}
]
}

View file

@ -4,13 +4,17 @@
package osext
import "syscall"
import (
"syscall"
"os"
"strconv"
)
func executable() (string, error) {
f, err := Open("/proc/" + itoa(Getpid()) + "/text")
if err != nil {
return "", err
}
defer f.Close()
return syscall.Fd2path(int(f.Fd()))
f, err := os.Open("/proc/" + strconv.Itoa(os.Getpid()) + "/text")
if err != nil {
return "", err
}
defer f.Close()
return syscall.Fd2path(int(f.Fd()))
}

View file

@ -14,7 +14,7 @@ import (
"unsafe"
)
var startUpcwd, getwdError = os.Getwd()
var initCwd, initCwdErr = os.Getwd()
func executable() (string, error) {
var mib [4]int32
@ -26,20 +26,20 @@ func executable() (string, error) {
}
n := uintptr(0)
// get length
_, _, err := syscall.Syscall6(syscall.SYS___SYSCTL, uintptr(unsafe.Pointer(&mib[0])), 4, 0, uintptr(unsafe.Pointer(&n)), 0, 0)
if err != 0 {
return "", err
// Get length.
_, _, errNum := syscall.Syscall6(syscall.SYS___SYSCTL, uintptr(unsafe.Pointer(&mib[0])), 4, 0, uintptr(unsafe.Pointer(&n)), 0, 0)
if errNum != 0 {
return "", errNum
}
if n == 0 { // shouldn't happen
if n == 0 { // This shouldn't happen.
return "", nil
}
buf := make([]byte, n)
_, _, err = syscall.Syscall6(syscall.SYS___SYSCTL, uintptr(unsafe.Pointer(&mib[0])), 4, uintptr(unsafe.Pointer(&buf[0])), uintptr(unsafe.Pointer(&n)), 0, 0)
if err != 0 {
return "", err
_, _, errNum = syscall.Syscall6(syscall.SYS___SYSCTL, uintptr(unsafe.Pointer(&mib[0])), 4, uintptr(unsafe.Pointer(&buf[0])), uintptr(unsafe.Pointer(&n)), 0, 0)
if errNum != 0 {
return "", errNum
}
if n == 0 { // shouldn't happen
if n == 0 { // This shouldn't happen.
return "", nil
}
for i, v := range buf {
@ -48,35 +48,32 @@ func executable() (string, error) {
break
}
}
var strpath string
if buf[0] != '/' {
var e error
if strpath, e = getAbs(buf); e != nil {
return strpath, e
var err error
execPath := string(buf)
// execPath will not be empty due to above checks.
// Try to get the absolute path if the execPath is not rooted.
if execPath[0] != '/' {
execPath, err = getAbs(execPath)
if err != nil {
return execPath, err
}
} else {
strpath = string(buf)
}
// darwin KERN_PROCARGS may return the path to a symlink rather than the
// actual executable
// For darwin KERN_PROCARGS may return the path to a symlink rather than the
// actual executable.
if runtime.GOOS == "darwin" {
if strpath, err := filepath.EvalSymlinks(strpath); err != nil {
return strpath, err
if execPath, err = filepath.EvalSymlinks(execPath); err != nil {
return execPath, err
}
}
return strpath, nil
return execPath, nil
}
func getAbs(buf []byte) (string, error) {
if getwdError != nil {
return string(buf), getwdError
} else {
if buf[0] == '.' {
buf = buf[1:]
}
if startUpcwd[len(startUpcwd)-1] != '/' && buf[0] != '/' {
return startUpcwd + "/" + string(buf), nil
}
return startUpcwd + string(buf), nil
func getAbs(execPath string) (string, error) {
if initCwdErr != nil {
return execPath, initCwdErr
}
// The execPath may begin with a "../" or a "./" so clean it first.
// Join the two paths, trailing and starting slashes undetermined, so use
// the generic Join function.
return filepath.Join(initCwd, filepath.Clean(execPath)), nil
}

View file

@ -53,15 +53,6 @@ func TestBcryptingIsCorrect(t *testing.T) {
}
}
func TestVeryShortPasswords(t *testing.T) {
key := []byte("k")
salt := []byte("XajjQvNhvvRt5GSeFk1xFe")
_, err := bcrypt(key, 10, salt)
if err != nil {
t.Errorf("One byte key resulted in error: %s", err)
}
}
func TestTooLongPasswordsWork(t *testing.T) {
salt := []byte("XajjQvNhvvRt5GSeFk1xFe")
// One byte over the usual 56 byte limit that blowfish has

View file

@ -192,13 +192,19 @@ func TestCipherDecrypt(t *testing.T) {
}
func TestSaltedCipherKeyLength(t *testing.T) {
if _, err := NewSaltedCipher(nil, []byte{'a'}); err != KeySizeError(0) {
t.Errorf("NewSaltedCipher with short key, gave error %#v, expected %#v", err, KeySizeError(0))
var key []byte
for i := 0; i < 4; i++ {
_, err := NewSaltedCipher(key, []byte{'a'})
if err != KeySizeError(i) {
t.Errorf("NewSaltedCipher with short key, gave error %#v, expected %#v", err, KeySizeError(i))
}
key = append(key, 'a')
}
// A 57-byte key. One over the typical blowfish restriction.
key := []byte("012345678901234567890123456789012345678901234567890123456")
if _, err := NewSaltedCipher(key, []byte{'a'}); err != nil {
key = []byte("012345678901234567890123456789012345678901234567890123456")
_, err := NewSaltedCipher(key, []byte{'a'})
if err != nil {
t.Errorf("NewSaltedCipher with long key, gave error %#v", err)
}
}

View file

@ -26,10 +26,11 @@ func (k KeySizeError) Error() string {
}
// NewCipher creates and returns a Cipher.
// The key argument should be the Blowfish key, from 1 to 56 bytes.
// The key argument should be the Blowfish key, 4 to 56 bytes.
func NewCipher(key []byte) (*Cipher, error) {
var result Cipher
if k := len(key); k < 1 || k > 56 {
k := len(key)
if k < 4 || k > 56 {
return nil, KeySizeError(k)
}
initCipher(key, &result)
@ -43,7 +44,8 @@ func NewCipher(key []byte) (*Cipher, error) {
// bytes. Only the first 16 bytes of salt are used.
func NewSaltedCipher(key, salt []byte) (*Cipher, error) {
var result Cipher
if k := len(key); k < 1 {
k := len(key)
if k < 4 {
return nil, KeySizeError(k)
}
initCipher(key, &result)

View file

@ -129,7 +129,7 @@ type frameReaderFactory interface {
// frameWriter is an interface to write a WebSocket frame.
type frameWriter interface {
// Writer is to write playload of the frame.
// Writer is to write payload of the frame.
io.WriteCloser
}

View file

@ -61,6 +61,12 @@ func (cs callSet) FindMatch(receiver interface{}, method string, args []interfac
// Search through the unordered set of calls expected on a method on a
// receiver.
for _, call := range calls {
// A call should not normally still be here if exhausted,
// but it can happen if, for instance, .Times(0) was used.
// Pretend the call doesn't match.
if call.exhausted() {
continue
}
if call.matches(args) {
return call
}

View file

@ -86,6 +86,10 @@ func (ctrl *Controller) RecordCall(receiver interface{}, method string, args ...
for i, arg := range args {
if m, ok := arg.(Matcher); ok {
margs[i] = m
} else if arg == nil {
// Handle nil specially so that passing a nil interface value
// will match the typed nils of concrete args.
margs[i] = Nil()
} else {
margs[i] = Eq(arg)
}

View file

@ -382,3 +382,14 @@ func TestSetArgWithBadType(t *testing.T) {
})
ctrl.Call(s, "FooMethod", "1")
}
func TestTimes0(t *testing.T) {
rep, ctrl := createFixtures(t)
defer ctrl.Finish()
s := new(Subject)
ctrl.RecordCall(s, "FooMethod", "arg").Times(0)
rep.assertFatal(func() {
ctrl.Call(s, "FooMethod", "arg")
})
}

View file

@ -1,2 +0,0 @@
/gen/gen
/example/example

View file

@ -1,53 +0,0 @@
## go.incremental
[![Build Status](https://drone.io/github.com/GeertJohan/go.incremental/status.png)](https://drone.io/github.com/GeertJohan/go.incremental/latest)
Go package incremental provides typed incremental counters that are type-safe.
### Install
`go get github.com/GeertJohan/go.incremental`
### Usage example
This example is also located in the example subdirectory
```go
package main
import (
"fmt"
"github.com/GeertJohan/go.incremental"
"runtime"
)
func main() {
// use max cpu's
runtime.GOMAXPROCS(runtime.NumCPU())
// create new incremental.Int
i := &incremental.Int{}
// print some numbers
fmt.Println(i.Next()) // print 1
fmt.Println(i.Next()) // print 2
fmt.Println(i.Next()) // print 3
// create chan to check if goroutines are done
done := make(chan int)
// spawn 4 goroutines
for a := 0; a < 4; a++ {
// call goroutine with it's number (0-3)
go func(aa int) {
// print 10 incremental numbers
for b := 0; b < 10; b++ {
fmt.Printf("routine %d: %d\n", aa, i.Next())
}
// signal done
done <- aa
}(a)
}
// wait until all goroutines are done
for a := 0; a < 4; a++ {
fmt.Printf("goroutine %d done\n", <-done)
}
fmt.Println("all done")
}
```

View file

@ -1,5 +0,0 @@
// package incremental provides concurency-safe incremental numbers.
//
// This package was created by a simple piece of code located in the gen subdirectory. Please modify that command if you want to modify this package.
package incremental

View file

@ -1,42 +0,0 @@
package main
import (
"fmt"
"github.com/GeertJohan/go.incremental"
"runtime"
)
func main() {
// use max cpu's
runtime.GOMAXPROCS(runtime.NumCPU())
// create new incremental.Int
i := &incremental.Int{}
// print some numbers
fmt.Println(i.Next()) // print 1
fmt.Println(i.Next()) // print 2
fmt.Println(i.Next()) // print 3
// create chan to check if goroutines are done
done := make(chan int)
// spawn 4 goroutines
for a := 0; a < 4; a++ {
// call goroutine with it's number (0-3)
go func(aa int) {
// print 10 incremental numbers
for b := 0; b < 10; b++ {
fmt.Printf("routine %d: %d\n", aa, i.Next())
}
// signal done
done <- aa
}(a)
}
// wait until all goroutines are done
for a := 0; a < 4; a++ {
fmt.Printf("goroutine %d done\n", <-done)
}
fmt.Println("all done")
}

View file

@ -1,187 +0,0 @@
package main
import (
"log"
"os"
"strings"
"text/template"
)
var (
tmpl *template.Template
tmplTest *template.Template
)
var types = []string{
"int", "int8", "int16", "int32", "int64",
"uint", "uint8", "uint16", "uint32", "uint64",
}
func init() {
var err error
tmpl, err = template.New("tmpl").Parse(`package incremental
import (
"sync"
)
type {{.Upper}} struct {
increment {{.Lower}}
lock sync.Mutex
}
// Next returns with an integer that is exactly one higher as the previous call to Next() for this {{.Upper}}
func (i *{{.Upper}}) Next() {{.Lower}} {
i.lock.Lock()
defer i.lock.Unlock()
i.increment++
return i.increment
}
// Last returns the number ({{.Lower}}) that was returned by the most recent call to this instance's Next()
func (i *{{.Upper}}) Last() {{.Lower}} {
return i.increment
}
// Set changes the increment to given value, the succeeding call to Next() will return the given value+1
func (i *{{.Upper}}) Set(value {{.Lower}}) {
i.lock.Lock()
defer i.lock.Unlock()
i.increment = value
}
`)
tmplTest, err = template.New("tmplTest").Parse(`package incremental
import (
"testing"
)
type some{{.Upper}}Struct struct {
i {{.Upper}}
}
func Test{{.Upper}}Ptr(t *testing.T) {
i := &{{.Upper}}{}
num := i.Next()
if num != 1 {
t.Fatal("expected 1, got %d", num)
}
num = i.Next()
if num != 2 {
t.Fatal("expected 2, got %d", num)
}
num = i.Last()
if num != 2 {
t.Fatal("expected last to be 2, got %d", num)
}
i.Set(42)
num = i.Last()
if num != 42 {
t.Fatal("expected last to be 42, got %d", num)
}
num = i.Next()
if num != 43 {
t.Fatal("expected 43, got %d", num)
}
}
func Test{{.Upper}}AsField(t *testing.T) {
s := some{{.Upper}}Struct{}
num := s.i.Next()
if num != 1 {
t.Fatal("expected 1, got %d", num)
}
num = s.i.Next()
if num != 2 {
t.Fatal("expected 2, got %d", num)
}
num = s.i.Last()
if num != 2 {
t.Fatal("expected last to be 2, got %d", num)
}
useSome{{.Upper}}Struct(&s, t)
num = s.i.Last()
if num != 3 {
t.Fatal("expected last to be 3, got %d", num)
}
s.i.Set(42)
num = s.i.Last()
if num != 42 {
t.Fatal("expected last to be 42, got %d", num)
}
num = s.i.Next()
if num != 43 {
t.Fatal("expected 43, got %d", num)
}
}
func useSome{{.Upper}}Struct(s *some{{.Upper}}Struct, t *testing.T) {
num := s.i.Next()
if num != 3 {
t.Fatal("expected 3, got %d", num)
}
}
`)
if err != nil {
log.Fatal(err)
}
}
type data struct {
Upper string
Lower string
}
func main() {
// loop over integer types
for _, t := range types {
// create data with upper and lower names for the type
d := &data{
Upper: strings.ToUpper(t[0:1]) + t[1:],
Lower: t,
}
// create file for type
file, err := os.Create(t + ".go")
if err != nil {
log.Fatal(err)
}
defer file.Close()
// execute template, write directly to file
err = tmpl.Execute(file, d)
if err != nil {
log.Fatal(err)
}
// create file for test
file, err = os.Create(t + "_test.go")
if err != nil {
log.Fatal(err)
}
defer file.Close()
// execute template, write directly to file
err = tmplTest.Execute(file, d)
if err != nil {
log.Fatal(err)
}
}
// create doc.go
file, err := os.Create("doc.go")
if err != nil {
log.Fatal(err)
}
defer file.Close()
file.WriteString(`
// package incremental provides concurency-safe incremental numbers.
//
// This package was created by a simple piece of code located in the gen subdirectory. Please modify that command if you want to modify this package.
package incremental`)
}

View file

@ -1,30 +0,0 @@
package incremental
import (
"sync"
)
type Int struct {
increment int
lock sync.Mutex
}
// Next returns with an integer that is exactly one higher as the previous call to Next() for this Int
func (i *Int) Next() int {
i.lock.Lock()
defer i.lock.Unlock()
i.increment++
return i.increment
}
// Last returns the number (int) that was returned by the most recent call to this instance's Next()
func (i *Int) Last() int {
return i.increment
}
// Set changes the increment to given value, the succeeding call to Next() will return the given value+1
func (i *Int) Set(value int) {
i.lock.Lock()
defer i.lock.Unlock()
i.increment = value
}

View file

@ -1,30 +0,0 @@
package incremental
import (
"sync"
)
type Int16 struct {
increment int16
lock sync.Mutex
}
// Next returns with an integer that is exactly one higher as the previous call to Next() for this Int16
func (i *Int16) Next() int16 {
i.lock.Lock()
defer i.lock.Unlock()
i.increment++
return i.increment
}
// Last returns the number (int16) that was returned by the most recent call to this instance's Next()
func (i *Int16) Last() int16 {
return i.increment
}
// Set changes the increment to given value, the succeeding call to Next() will return the given value+1
func (i *Int16) Set(value int16) {
i.lock.Lock()
defer i.lock.Unlock()
i.increment = value
}

View file

@ -1,75 +0,0 @@
package incremental
import (
"testing"
)
type someInt16Struct struct {
i Int16
}
func TestInt16Ptr(t *testing.T) {
i := &Int16{}
num := i.Next()
if num != 1 {
t.Fatal("expected 1, got %d", num)
}
num = i.Next()
if num != 2 {
t.Fatal("expected 2, got %d", num)
}
num = i.Last()
if num != 2 {
t.Fatal("expected last to be 2, got %d", num)
}
i.Set(42)
num = i.Last()
if num != 42 {
t.Fatal("expected last to be 42, got %d", num)
}
num = i.Next()
if num != 43 {
t.Fatal("expected 43, got %d", num)
}
}
func TestInt16AsField(t *testing.T) {
s := someInt16Struct{}
num := s.i.Next()
if num != 1 {
t.Fatal("expected 1, got %d", num)
}
num = s.i.Next()
if num != 2 {
t.Fatal("expected 2, got %d", num)
}
num = s.i.Last()
if num != 2 {
t.Fatal("expected last to be 2, got %d", num)
}
useSomeInt16Struct(&s, t)
num = s.i.Last()
if num != 3 {
t.Fatal("expected last to be 3, got %d", num)
}
s.i.Set(42)
num = s.i.Last()
if num != 42 {
t.Fatal("expected last to be 42, got %d", num)
}
num = s.i.Next()
if num != 43 {
t.Fatal("expected 43, got %d", num)
}
}
func useSomeInt16Struct(s *someInt16Struct, t *testing.T) {
num := s.i.Next()
if num != 3 {
t.Fatal("expected 3, got %d", num)
}
}

View file

@ -1,30 +0,0 @@
package incremental
import (
"sync"
)
type Int32 struct {
increment int32
lock sync.Mutex
}
// Next returns with an integer that is exactly one higher as the previous call to Next() for this Int32
func (i *Int32) Next() int32 {
i.lock.Lock()
defer i.lock.Unlock()
i.increment++
return i.increment
}
// Last returns the number (int32) that was returned by the most recent call to this instance's Next()
func (i *Int32) Last() int32 {
return i.increment
}
// Set changes the increment to given value, the succeeding call to Next() will return the given value+1
func (i *Int32) Set(value int32) {
i.lock.Lock()
defer i.lock.Unlock()
i.increment = value
}

View file

@ -1,75 +0,0 @@
package incremental
import (
"testing"
)
type someInt32Struct struct {
i Int32
}
func TestInt32Ptr(t *testing.T) {
i := &Int32{}
num := i.Next()
if num != 1 {
t.Fatal("expected 1, got %d", num)
}
num = i.Next()
if num != 2 {
t.Fatal("expected 2, got %d", num)
}
num = i.Last()
if num != 2 {
t.Fatal("expected last to be 2, got %d", num)
}
i.Set(42)
num = i.Last()
if num != 42 {
t.Fatal("expected last to be 42, got %d", num)
}
num = i.Next()
if num != 43 {
t.Fatal("expected 43, got %d", num)
}
}
func TestInt32AsField(t *testing.T) {
s := someInt32Struct{}
num := s.i.Next()
if num != 1 {
t.Fatal("expected 1, got %d", num)
}
num = s.i.Next()
if num != 2 {
t.Fatal("expected 2, got %d", num)
}
num = s.i.Last()
if num != 2 {
t.Fatal("expected last to be 2, got %d", num)
}
useSomeInt32Struct(&s, t)
num = s.i.Last()
if num != 3 {
t.Fatal("expected last to be 3, got %d", num)
}
s.i.Set(42)
num = s.i.Last()
if num != 42 {
t.Fatal("expected last to be 42, got %d", num)
}
num = s.i.Next()
if num != 43 {
t.Fatal("expected 43, got %d", num)
}
}
func useSomeInt32Struct(s *someInt32Struct, t *testing.T) {
num := s.i.Next()
if num != 3 {
t.Fatal("expected 3, got %d", num)
}
}

View file

@ -1,30 +0,0 @@
package incremental
import (
"sync"
)
type Int64 struct {
increment int64
lock sync.Mutex
}
// Next returns with an integer that is exactly one higher as the previous call to Next() for this Int64
func (i *Int64) Next() int64 {
i.lock.Lock()
defer i.lock.Unlock()
i.increment++
return i.increment
}
// Last returns the number (int64) that was returned by the most recent call to this instance's Next()
func (i *Int64) Last() int64 {
return i.increment
}
// Set changes the increment to given value, the succeeding call to Next() will return the given value+1
func (i *Int64) Set(value int64) {
i.lock.Lock()
defer i.lock.Unlock()
i.increment = value
}

View file

@ -1,75 +0,0 @@
package incremental
import (
"testing"
)
type someInt64Struct struct {
i Int64
}
func TestInt64Ptr(t *testing.T) {
i := &Int64{}
num := i.Next()
if num != 1 {
t.Fatal("expected 1, got %d", num)
}
num = i.Next()
if num != 2 {
t.Fatal("expected 2, got %d", num)
}
num = i.Last()
if num != 2 {
t.Fatal("expected last to be 2, got %d", num)
}
i.Set(42)
num = i.Last()
if num != 42 {
t.Fatal("expected last to be 42, got %d", num)
}
num = i.Next()
if num != 43 {
t.Fatal("expected 43, got %d", num)
}
}
func TestInt64AsField(t *testing.T) {
s := someInt64Struct{}
num := s.i.Next()
if num != 1 {
t.Fatal("expected 1, got %d", num)
}
num = s.i.Next()
if num != 2 {
t.Fatal("expected 2, got %d", num)
}
num = s.i.Last()
if num != 2 {
t.Fatal("expected last to be 2, got %d", num)
}
useSomeInt64Struct(&s, t)
num = s.i.Last()
if num != 3 {
t.Fatal("expected last to be 3, got %d", num)
}
s.i.Set(42)
num = s.i.Last()
if num != 42 {
t.Fatal("expected last to be 42, got %d", num)
}
num = s.i.Next()
if num != 43 {
t.Fatal("expected 43, got %d", num)
}
}
func useSomeInt64Struct(s *someInt64Struct, t *testing.T) {
num := s.i.Next()
if num != 3 {
t.Fatal("expected 3, got %d", num)
}
}

View file

@ -1,30 +0,0 @@
package incremental
import (
"sync"
)
type Int8 struct {
increment int8
lock sync.Mutex
}
// Next returns with an integer that is exactly one higher as the previous call to Next() for this Int8
func (i *Int8) Next() int8 {
i.lock.Lock()
defer i.lock.Unlock()
i.increment++
return i.increment
}
// Last returns the number (int8) that was returned by the most recent call to this instance's Next()
func (i *Int8) Last() int8 {
return i.increment
}
// Set changes the increment to given value, the succeeding call to Next() will return the given value+1
func (i *Int8) Set(value int8) {
i.lock.Lock()
defer i.lock.Unlock()
i.increment = value
}

View file

@ -1,75 +0,0 @@
package incremental
import (
"testing"
)
type someInt8Struct struct {
i Int8
}
func TestInt8Ptr(t *testing.T) {
i := &Int8{}
num := i.Next()
if num != 1 {
t.Fatal("expected 1, got %d", num)
}
num = i.Next()
if num != 2 {
t.Fatal("expected 2, got %d", num)
}
num = i.Last()
if num != 2 {
t.Fatal("expected last to be 2, got %d", num)
}
i.Set(42)
num = i.Last()
if num != 42 {
t.Fatal("expected last to be 42, got %d", num)
}
num = i.Next()
if num != 43 {
t.Fatal("expected 43, got %d", num)
}
}
func TestInt8AsField(t *testing.T) {
s := someInt8Struct{}
num := s.i.Next()
if num != 1 {
t.Fatal("expected 1, got %d", num)
}
num = s.i.Next()
if num != 2 {
t.Fatal("expected 2, got %d", num)
}
num = s.i.Last()
if num != 2 {
t.Fatal("expected last to be 2, got %d", num)
}
useSomeInt8Struct(&s, t)
num = s.i.Last()
if num != 3 {
t.Fatal("expected last to be 3, got %d", num)
}
s.i.Set(42)
num = s.i.Last()
if num != 42 {
t.Fatal("expected last to be 42, got %d", num)
}
num = s.i.Next()
if num != 43 {
t.Fatal("expected 43, got %d", num)
}
}
func useSomeInt8Struct(s *someInt8Struct, t *testing.T) {
num := s.i.Next()
if num != 3 {
t.Fatal("expected 3, got %d", num)
}
}

View file

@ -1,75 +0,0 @@
package incremental
import (
"testing"
)
type someIntStruct struct {
i Int
}
func TestIntPtr(t *testing.T) {
i := &Int{}
num := i.Next()
if num != 1 {
t.Fatal("expected 1, got %d", num)
}
num = i.Next()
if num != 2 {
t.Fatal("expected 2, got %d", num)
}
num = i.Last()
if num != 2 {
t.Fatal("expected last to be 2, got %d", num)
}
i.Set(42)
num = i.Last()
if num != 42 {
t.Fatal("expected last to be 42, got %d", num)
}
num = i.Next()
if num != 43 {
t.Fatal("expected 43, got %d", num)
}
}
func TestIntAsField(t *testing.T) {
s := someIntStruct{}
num := s.i.Next()
if num != 1 {
t.Fatal("expected 1, got %d", num)
}
num = s.i.Next()
if num != 2 {
t.Fatal("expected 2, got %d", num)
}
num = s.i.Last()
if num != 2 {
t.Fatal("expected last to be 2, got %d", num)
}
useSomeIntStruct(&s, t)
num = s.i.Last()
if num != 3 {
t.Fatal("expected last to be 3, got %d", num)
}
s.i.Set(42)
num = s.i.Last()
if num != 42 {
t.Fatal("expected last to be 42, got %d", num)
}
num = s.i.Next()
if num != 43 {
t.Fatal("expected 43, got %d", num)
}
}
func useSomeIntStruct(s *someIntStruct, t *testing.T) {
num := s.i.Next()
if num != 3 {
t.Fatal("expected 3, got %d", num)
}
}

View file

@ -1,30 +0,0 @@
package incremental
import (
"sync"
)
type Uint struct {
increment uint
lock sync.Mutex
}
// Next returns with an integer that is exactly one higher as the previous call to Next() for this Uint
func (i *Uint) Next() uint {
i.lock.Lock()
defer i.lock.Unlock()
i.increment++
return i.increment
}
// Last returns the number (uint) that was returned by the most recent call to this instance's Next()
func (i *Uint) Last() uint {
return i.increment
}
// Set changes the increment to given value, the succeeding call to Next() will return the given value+1
func (i *Uint) Set(value uint) {
i.lock.Lock()
defer i.lock.Unlock()
i.increment = value
}

View file

@ -1,30 +0,0 @@
package incremental
import (
"sync"
)
type Uint16 struct {
increment uint16
lock sync.Mutex
}
// Next returns with an integer that is exactly one higher as the previous call to Next() for this Uint16
func (i *Uint16) Next() uint16 {
i.lock.Lock()
defer i.lock.Unlock()
i.increment++
return i.increment
}
// Last returns the number (uint16) that was returned by the most recent call to this instance's Next()
func (i *Uint16) Last() uint16 {
return i.increment
}
// Set changes the increment to given value, the succeeding call to Next() will return the given value+1
func (i *Uint16) Set(value uint16) {
i.lock.Lock()
defer i.lock.Unlock()
i.increment = value
}

View file

@ -1,75 +0,0 @@
package incremental
import (
"testing"
)
type someUint16Struct struct {
i Uint16
}
func TestUint16Ptr(t *testing.T) {
i := &Uint16{}
num := i.Next()
if num != 1 {
t.Fatal("expected 1, got %d", num)
}
num = i.Next()
if num != 2 {
t.Fatal("expected 2, got %d", num)
}
num = i.Last()
if num != 2 {
t.Fatal("expected last to be 2, got %d", num)
}
i.Set(42)
num = i.Last()
if num != 42 {
t.Fatal("expected last to be 42, got %d", num)
}
num = i.Next()
if num != 43 {
t.Fatal("expected 43, got %d", num)
}
}
func TestUint16AsField(t *testing.T) {
s := someUint16Struct{}
num := s.i.Next()
if num != 1 {
t.Fatal("expected 1, got %d", num)
}
num = s.i.Next()
if num != 2 {
t.Fatal("expected 2, got %d", num)
}
num = s.i.Last()
if num != 2 {
t.Fatal("expected last to be 2, got %d", num)
}
useSomeUint16Struct(&s, t)
num = s.i.Last()
if num != 3 {
t.Fatal("expected last to be 3, got %d", num)
}
s.i.Set(42)
num = s.i.Last()
if num != 42 {
t.Fatal("expected last to be 42, got %d", num)
}
num = s.i.Next()
if num != 43 {
t.Fatal("expected 43, got %d", num)
}
}
func useSomeUint16Struct(s *someUint16Struct, t *testing.T) {
num := s.i.Next()
if num != 3 {
t.Fatal("expected 3, got %d", num)
}
}

View file

@ -1,30 +0,0 @@
package incremental
import (
"sync"
)
type Uint32 struct {
increment uint32
lock sync.Mutex
}
// Next returns with an integer that is exactly one higher as the previous call to Next() for this Uint32
func (i *Uint32) Next() uint32 {
i.lock.Lock()
defer i.lock.Unlock()
i.increment++
return i.increment
}
// Last returns the number (uint32) that was returned by the most recent call to this instance's Next()
func (i *Uint32) Last() uint32 {
return i.increment
}
// Set changes the increment to given value, the succeeding call to Next() will return the given value+1
func (i *Uint32) Set(value uint32) {
i.lock.Lock()
defer i.lock.Unlock()
i.increment = value
}

View file

@ -1,75 +0,0 @@
package incremental
import (
"testing"
)
type someUint32Struct struct {
i Uint32
}
func TestUint32Ptr(t *testing.T) {
i := &Uint32{}
num := i.Next()
if num != 1 {
t.Fatal("expected 1, got %d", num)
}
num = i.Next()
if num != 2 {
t.Fatal("expected 2, got %d", num)
}
num = i.Last()
if num != 2 {
t.Fatal("expected last to be 2, got %d", num)
}
i.Set(42)
num = i.Last()
if num != 42 {
t.Fatal("expected last to be 42, got %d", num)
}
num = i.Next()
if num != 43 {
t.Fatal("expected 43, got %d", num)
}
}
func TestUint32AsField(t *testing.T) {
s := someUint32Struct{}
num := s.i.Next()
if num != 1 {
t.Fatal("expected 1, got %d", num)
}
num = s.i.Next()
if num != 2 {
t.Fatal("expected 2, got %d", num)
}
num = s.i.Last()
if num != 2 {
t.Fatal("expected last to be 2, got %d", num)
}
useSomeUint32Struct(&s, t)
num = s.i.Last()
if num != 3 {
t.Fatal("expected last to be 3, got %d", num)
}
s.i.Set(42)
num = s.i.Last()
if num != 42 {
t.Fatal("expected last to be 42, got %d", num)
}
num = s.i.Next()
if num != 43 {
t.Fatal("expected 43, got %d", num)
}
}
func useSomeUint32Struct(s *someUint32Struct, t *testing.T) {
num := s.i.Next()
if num != 3 {
t.Fatal("expected 3, got %d", num)
}
}

View file

@ -1,30 +0,0 @@
package incremental
import (
"sync"
)
type Uint64 struct {
increment uint64
lock sync.Mutex
}
// Next returns with an integer that is exactly one higher as the previous call to Next() for this Uint64
func (i *Uint64) Next() uint64 {
i.lock.Lock()
defer i.lock.Unlock()
i.increment++
return i.increment
}
// Last returns the number (uint64) that was returned by the most recent call to this instance's Next()
func (i *Uint64) Last() uint64 {
return i.increment
}
// Set changes the increment to given value, the succeeding call to Next() will return the given value+1
func (i *Uint64) Set(value uint64) {
i.lock.Lock()
defer i.lock.Unlock()
i.increment = value
}

View file

@ -1,75 +0,0 @@
package incremental
import (
"testing"
)
type someUint64Struct struct {
i Uint64
}
func TestUint64Ptr(t *testing.T) {
i := &Uint64{}
num := i.Next()
if num != 1 {
t.Fatal("expected 1, got %d", num)
}
num = i.Next()
if num != 2 {
t.Fatal("expected 2, got %d", num)
}
num = i.Last()
if num != 2 {
t.Fatal("expected last to be 2, got %d", num)
}
i.Set(42)
num = i.Last()
if num != 42 {
t.Fatal("expected last to be 42, got %d", num)
}
num = i.Next()
if num != 43 {
t.Fatal("expected 43, got %d", num)
}
}
func TestUint64AsField(t *testing.T) {
s := someUint64Struct{}
num := s.i.Next()
if num != 1 {
t.Fatal("expected 1, got %d", num)
}
num = s.i.Next()
if num != 2 {
t.Fatal("expected 2, got %d", num)
}
num = s.i.Last()
if num != 2 {
t.Fatal("expected last to be 2, got %d", num)
}
useSomeUint64Struct(&s, t)
num = s.i.Last()
if num != 3 {
t.Fatal("expected last to be 3, got %d", num)
}
s.i.Set(42)
num = s.i.Last()
if num != 42 {
t.Fatal("expected last to be 42, got %d", num)
}
num = s.i.Next()
if num != 43 {
t.Fatal("expected 43, got %d", num)
}
}
func useSomeUint64Struct(s *someUint64Struct, t *testing.T) {
num := s.i.Next()
if num != 3 {
t.Fatal("expected 3, got %d", num)
}
}

View file

@ -1,30 +0,0 @@
package incremental
import (
"sync"
)
type Uint8 struct {
increment uint8
lock sync.Mutex
}
// Next returns with an integer that is exactly one higher as the previous call to Next() for this Uint8
func (i *Uint8) Next() uint8 {
i.lock.Lock()
defer i.lock.Unlock()
i.increment++
return i.increment
}
// Last returns the number (uint8) that was returned by the most recent call to this instance's Next()
func (i *Uint8) Last() uint8 {
return i.increment
}
// Set changes the increment to given value, the succeeding call to Next() will return the given value+1
func (i *Uint8) Set(value uint8) {
i.lock.Lock()
defer i.lock.Unlock()
i.increment = value
}

View file

@ -1,75 +0,0 @@
package incremental
import (
"testing"
)
type someUint8Struct struct {
i Uint8
}
func TestUint8Ptr(t *testing.T) {
i := &Uint8{}
num := i.Next()
if num != 1 {
t.Fatal("expected 1, got %d", num)
}
num = i.Next()
if num != 2 {
t.Fatal("expected 2, got %d", num)
}
num = i.Last()
if num != 2 {
t.Fatal("expected last to be 2, got %d", num)
}
i.Set(42)
num = i.Last()
if num != 42 {
t.Fatal("expected last to be 42, got %d", num)
}
num = i.Next()
if num != 43 {
t.Fatal("expected 43, got %d", num)
}
}
func TestUint8AsField(t *testing.T) {
s := someUint8Struct{}
num := s.i.Next()
if num != 1 {
t.Fatal("expected 1, got %d", num)
}
num = s.i.Next()
if num != 2 {
t.Fatal("expected 2, got %d", num)
}
num = s.i.Last()
if num != 2 {
t.Fatal("expected last to be 2, got %d", num)
}
useSomeUint8Struct(&s, t)
num = s.i.Last()
if num != 3 {
t.Fatal("expected last to be 3, got %d", num)
}
s.i.Set(42)
num = s.i.Last()
if num != 42 {
t.Fatal("expected last to be 42, got %d", num)
}
num = s.i.Next()
if num != 43 {
t.Fatal("expected 43, got %d", num)
}
}
func useSomeUint8Struct(s *someUint8Struct, t *testing.T) {
num := s.i.Next()
if num != 3 {
t.Fatal("expected 3, got %d", num)
}
}

View file

@ -1,75 +0,0 @@
package incremental
import (
"testing"
)
type someUintStruct struct {
i Uint
}
func TestUintPtr(t *testing.T) {
i := &Uint{}
num := i.Next()
if num != 1 {
t.Fatal("expected 1, got %d", num)
}
num = i.Next()
if num != 2 {
t.Fatal("expected 2, got %d", num)
}
num = i.Last()
if num != 2 {
t.Fatal("expected last to be 2, got %d", num)
}
i.Set(42)
num = i.Last()
if num != 42 {
t.Fatal("expected last to be 42, got %d", num)
}
num = i.Next()
if num != 43 {
t.Fatal("expected 43, got %d", num)
}
}
func TestUintAsField(t *testing.T) {
s := someUintStruct{}
num := s.i.Next()
if num != 1 {
t.Fatal("expected 1, got %d", num)
}
num = s.i.Next()
if num != 2 {
t.Fatal("expected 2, got %d", num)
}
num = s.i.Last()
if num != 2 {
t.Fatal("expected last to be 2, got %d", num)
}
useSomeUintStruct(&s, t)
num = s.i.Last()
if num != 3 {
t.Fatal("expected last to be 3, got %d", num)
}
s.i.Set(42)
num = s.i.Last()
if num != 42 {
t.Fatal("expected last to be 42, got %d", num)
}
num = s.i.Next()
if num != 43 {
t.Fatal("expected 43, got %d", num)
}
}
func useSomeUintStruct(s *someUintStruct, t *testing.T) {
num := s.i.Next()
if num != 3 {
t.Fatal("expected 3, got %d", num)
}
}

View file

@ -4,4 +4,4 @@
/rice/rice.exe
*.rice-box.go
*.rice-single.go
*.rice-box.syso

View file

@ -1,11 +1,11 @@
## go.rice
go.rice is a [Go](http://golang.org) package that makes working with resources such as html,js,css,images and templates very easy. During development `go.rice` will load required files directly from disk. Upon deployment it is easy to add all resource files to a executable using the `rice` tool, without changing the source code for your package.
go.rice is a [Go](http://golang.org) package that makes working with resources such as html,js,css,images and templates very easy. During development `go.rice` will load required files directly from disk. Upon deployment it is easy to add all resource files to a executable using the `rice` tool, without changing the source code for your package. go.rice provides several methods to add resources to a binary.
### What does it do?
The first thing go.rice does is finding the correct absolute path for your resource files. Say you are executing go binary in your home directory, but your html files are located in `$GOPATH/src/webApplication/html-files`. `go.rice` will lookup the aboslute path for that directory. The only thing you have to do is include the resources using `rice.FindBox("html-files")`.
The first thing go.rice does is finding the correct absolute path for your resource files. Say you are executing go binary in your home directory, but your `html-files` are located in `$GOPATH/src/yourApplication/html-files`. `go.rice` will lookup the correct path for that directory (relative to the location of yourApplication). The only thing you have to do is include the resources using `rice.FindBox("html-files")`.
This only works when the source is available to the machine executing the binary. This is always the case when the binary was installed with `go get` or `go install`. It might happen that you wish to simply provide a binary, without source. The `rice` tool analyses source code and finds call's to `rice.FindBox(..)` and adds the required directories to the executable binary. There are several ways to add these resources. You can 'embed' by generating go source code, or append the resource to the executable.
This only works when the source is available to the machine executing the binary. Which is always the case when the binary was installed with `go get` or `go install`. It might occur that you wish to simply provide a binary, without source. The `rice` tool analyses source code and finds call's to `rice.FindBox(..)` and adds the required directories to the executable binary. There are several methods to add these resources. You can 'embed' by generating go source code, or append the resource to the executable as zip file. In both cases `go.rice` will detect the embedded or appended resources and load those, instead of looking up files from disk.
### Installation
@ -27,7 +27,7 @@ http.ListenAndServe(":8080", nil)
**Loading a template**
```go
// find/create a rice.Box
// find a rice.Box
templateBox, err := rice.FindBox("example-templates")
if err != nil {
log.Fatal(err)
@ -46,22 +46,45 @@ tmplMessage.Execute(os.Stdout, map[string]string{"Message": "Hello, world!"})
```
Never call `FindBox()` or `MustFindBox()` from an `init()` function, as the boxes might have not been loaded at that time.
### Tool usage
The `rice` tool lets you add the resources to a binary executable so the files are not loaded from the filesystem anymore. This creates a 'standalone' executable. There's several ways to add the resources to a binary, each has pro's and con's but all will work without changing your source code. `go.rice` will figure it all out for you.
The `rice` tool lets you add the resources to a binary executable so the files are not loaded from the filesystem anymore. This creates a 'standalone' executable. There are several ways to add the resources to a binary, each has pro's and con's but all will work without requiring changes to the way you load the resources.
**Embed resources in Go source**
#### embed-go
**Embed resources by generating Go source code**
This option is pre-build, it generates Go source files that are compiled into the binary.
This method must be executed before building. It generates Go source files that are compiled by the go compiler into the binary.
Run `rice embed` to generate Go source that contains all required resources. Afterwards run `go build` to create a standalone executable.
The downside with this option is that the generated go source files can become very large, which will slow down compilation and require lots of memory to compile.
**Append resources to executable**
Execute the following commands:
```
rice embed-go
go build
```
#### embed-syso
**Embed resources by generating a coff .syso file and some .go source code**
** This method is experimental and should not be used for production systems just yet **
This method must be executed before building. It generates a COFF .syso file and Go source file that are compiled by the go compiler into the binary.
Execute the following commands:
```
rice embed-syso
go build
```
#### append
**Append resources to executable as zip file**
_Does not work on windows (yet)_
This options is post-build, it appends the resources to the binary. It makes compilation a lot faster and can be used with large resource files.
This method changes an allready built executable. It appends the resources as zip file to the binary. It makes compilation a lot faster and can be used with large resource files.
Appending requires `zip` to be installed.
Downsides for appending are that it requires `zip` to be installed and does not provide a working Seek method.
Run the following commands to create a standalone executable.
```
@ -71,13 +94,13 @@ rice append --exec example
#### Help information
Run `rice -h` for information about all options.
You can run the -h option for each sub-command, e.g. `rice append -h`.
### Order of preference
When opening a new box, the rice pkg tries to locate it using the following order:
### Order of precedence
When opening a new box, the rice package tries to locate the resources in the following order:
- embedded in generated go source
- embedded with .a object files (not available yet)
- appended as zip
- 'live' from filesystem
@ -85,21 +108,16 @@ When opening a new box, the rice pkg tries to locate it using the following orde
### Licence
This project is licensed under a Simplified BSD license. Please read the [LICENSE file][license].
### TODO & Development
This package is not completed yet. Though it already provides working embedding, some important featuers are still missing.
- implement Readdir() correctly on virtualDir
- implement Seek() for zipFile
- implement embedding with .a object files
- automated testing with TravisCI or Drone **important**
- in-code TODO's
- find boxes in imported packages
Less important stuff:
- rice.FindSingle(..) that loads and embeds a single file as oposed to a complete directory. It should have methods .String(), .Bytes() and .File()
- The rice tool uses a simple regexp to find calls to `rice.FindBox(..)`, this should be changed to `go/ast` or maybe `go.tools/oracle`?
- idea, os/arch dependent embeds. rice checks if embedding file has _os_arch or build flags. If box is not requested by file without buildflags, then the buildflags are applied to the embed file.
- store meta information for appended (zip) files (mod time, etc)
### Package documentation

View file

@ -75,8 +75,10 @@ func init() {
if dirName == "." {
dirName = ""
}
if dir := box.Files[dirName]; dir != nil {
dir.children = append(dir.children, af)
if fileName != "" { // don't make box root dir a child of itself
if dir := box.Files[dirName]; dir != nil {
dir.children = append(dir.children, af)
}
}
}
}

View file

@ -29,7 +29,7 @@ func findBox(name string) (*Box, error) {
// no support for absolute paths since gopath can be different on different machines.
// therefore, required box must be located relative to package requiring it.
if filepath.IsAbs(name) {
return nil, errors.New("given name/path is aboslute")
return nil, errors.New("given name/path is absolute")
}
// find if box is embedded
@ -123,11 +123,6 @@ func (b *Box) Open(name string) (*File, error) {
fmt.Printf("Open(%s)\n", name)
}
// if b.IsAppended() {
// do stuff
// return ....
// }
if b.IsEmbedded() {
if Debug {
fmt.Println("Box is embedded")

View file

@ -1,3 +0,0 @@
package rice
//++ object embedding

View file

@ -1,25 +1,56 @@
// Package embedded defines embedded data types that are shared between the go.rice package and generated code.
// This package is seperated from go.rice to make go.rice's documentation cleaner.
package embedded
import (
"fmt"
"path/filepath"
"strings"
"time"
)
const (
EmbedTypeGo = 0
EmbedTypeSyso = 1
)
// EmbeddedBox defines an embedded box
type EmbeddedBox struct {
Name string // box name
Time time.Time // embed time
Files map[string]*EmbeddedFile // ALL embedded files by full path
Dirs map[string]*EmbeddedDir // ALL embedded dirs by full path
Name string // box name
Time time.Time // embed time
EmbedType int // kind of embedding
Files map[string]*EmbeddedFile // ALL embedded files by full path
Dirs map[string]*EmbeddedDir // ALL embedded dirs by full path
}
// EmbeddedSingle defines an embedded single
type EmbeddedSingle struct {
Name string // single name
Time time.Time // embed time
File *EmbeddedFile // embedded file
// Link creates the ChildDirs and ChildFiles links in all EmbeddedDir's
func (e *EmbeddedBox) Link() {
for path, ed := range e.Dirs {
fmt.Println(path)
ed.ChildDirs = make([]*EmbeddedDir, 0)
ed.ChildFiles = make([]*EmbeddedFile, 0)
}
for path, ed := range e.Dirs {
parentDirpath, _ := filepath.Split(path)
if strings.HasSuffix(parentDirpath, "/") {
parentDirpath = parentDirpath[:len(parentDirpath)-1]
}
parentDir := e.Dirs[parentDirpath]
if parentDir == nil {
panic("parentDir `" + parentDirpath + "` is missing in embedded box")
}
parentDir.ChildDirs = append(parentDir.ChildDirs, ed)
}
for path, ef := range e.Files {
dirpath, _ := filepath.Split(path)
if strings.HasSuffix(dirpath, "/") {
dirpath = dirpath[:len(dirpath)-1]
}
dir := e.Dirs[dirpath]
if dir == nil {
panic("dir `" + dirpath + "` is missing in embedded box")
}
dir.ChildFiles = append(dir.ChildFiles, ef)
}
}
// EmbeddedDir is instanced in the code generated by the rice tool and contains all necicary information about an embedded file

View file

@ -16,7 +16,7 @@ func main() {
if err != nil {
log.Fatalf("error opening rice.Box: %s\n", err)
}
spew.Dump(box)
// spew.Dump(box)
contentString, err := box.String("file.txt")
if err != nil {
@ -35,6 +35,16 @@ func main() {
log.Fatalf("could not open file: %s\n", err)
}
spew.Dump(file)
// debianFile, err := box.Open("debian-7.3.0-amd64-i386-netinst.iso")
// if err != nil {
// log.Fatalf("error opening file debian-7.3.0-amd64-i386-netinst.iso: %v", err)
// }
// info, err := debianFile.Stat()
// if err != nil {
// log.Fatalf("error doing stat for debian file: %v", err)
// }
// log.Printf("debian file was last modified at %v\n", info.ModTime())
// log.Printf("debian file is %d bytes large\n", info.Size())
// find/create a rice.Box
templateBox, err := rice.FindBox("example-templates")

View file

@ -59,10 +59,10 @@ func (f *File) Stat() (os.FileInfo, error) {
func (f *File) Readdir(count int) ([]os.FileInfo, error) {
if f.appendedF != nil {
if f.appendedF.dir {
fi := make([]os.FileInfo, len(f.appendedF.children))
fi := make([]os.FileInfo, 0, len(f.appendedF.children))
for _, childAppendedFile := range f.appendedF.children {
if childAppendedFile.dir {
fi = append(fi, f.appendedF.dirInfo)
fi = append(fi, childAppendedFile.dirInfo)
} else {
fi = append(fi, childAppendedFile.zipFile.FileInfo())
}

View file

@ -95,7 +95,13 @@ func operationAppend(pkg *build.Package) {
}
// create zipFileWriter
zipFileWriter, err := zipWriter.Create(zipFileName)
zipFileHeader, err := zip.FileInfoHeader(info)
if err != nil {
fmt.Printf("Error creating zip FileHeader: %v\n", err)
os.Exit(1)
}
zipFileHeader.Name = zipFileName
zipFileWriter, err := zipWriter.CreateHeader(zipFileHeader)
if err != nil {
fmt.Printf("Error creating file in tmp zip: %s\n", err)
os.Exit(1)

View file

@ -9,15 +9,24 @@ import (
)
func operationClean(pkg *build.Package) {
for _, filename := range pkg.GoFiles {
filepath.Walk(pkg.Dir, func(filename string, info os.FileInfo, err error) error {
if err != nil {
fmt.Printf("error walking pkg dir to clean files: %v\n", err)
os.Exit(1)
}
if info.IsDir() {
return nil
}
verbosef("checking file '%s'\n", filename)
if strings.HasSuffix(filename, ".rice-box.go") || strings.HasSuffix(filename, ".rice-single.go") {
err := os.Remove(filepath.Join(pkg.Dir, filename))
if strings.HasSuffix(filename, ".rice-box.go") ||
strings.HasSuffix(filename, ".rice-box.syso") {
err := os.Remove(filename)
if err != nil {
fmt.Printf("error removing file (%s): %s\n", filename, err)
os.Exit(-1)
}
verbosef("removed file '%s'\n", filename)
}
}
return nil
})
}

View file

@ -14,7 +14,7 @@ import (
"time"
)
func operationEmbed(pkg *build.Package) {
func operationEmbedGo(pkg *build.Package) {
boxMap := findBoxes(pkg)

View file

@ -0,0 +1,197 @@
package main
import (
"bytes"
"encoding/gob"
"fmt"
"github.com/GeertJohan/go.rice/embedded"
"github.com/akavel/rsrc/coff"
"go/build"
"io"
"io/ioutil"
"os"
"path/filepath"
"regexp"
"strings"
"text/template"
"time"
)
type sizedReader struct {
*bytes.Reader
}
func (s sizedReader) Size() int64 {
return int64(s.Len())
}
var tmplEmbeddedSysoHelper *template.Template
func init() {
var err error
tmplEmbeddedSysoHelper, err = template.New("embeddedSysoHelper").Parse(`package {{.Package}}
// ############# GENERATED CODE #####################
// ## This file was generated by the rice tool.
// ## Do not edit unless you know what you're doing.
// ##################################################
// extern char _bricebox_{{.Symname}}[], _ericebox_{{.Symname}};
// int get_{{.Symname}}_length() {
// return &_ericebox_{{.Symname}} - _bricebox_{{.Symname}};
// }
import "C"
import (
"bytes"
"encoding/gob"
"github.com/GeertJohan/go.rice/embedded"
"unsafe"
)
func init() {
ptr := unsafe.Pointer(&C._bricebox_{{.Symname}})
bts := C.GoBytes(ptr, C.get_{{.Symname}}_length())
embeddedBox := &embedded.EmbeddedBox{}
err := gob.NewDecoder(bytes.NewReader(bts)).Decode(embeddedBox)
if err != nil {
panic("error decoding embedded box: "+err.Error())
}
embeddedBox.Link()
embedded.RegisterEmbeddedBox(embeddedBox.Name, embeddedBox)
}`)
if err != nil {
panic("could not parse template embeddedSysoHelper: " + err.Error())
}
}
type embeddedSysoHelperData struct {
Package string
Symname string
}
func operationEmbedSyso(pkg *build.Package) {
regexpSynameReplacer := regexp.MustCompile(`[^a-z0-9_]`)
boxMap := findBoxes(pkg)
// notify user when no calls to rice.FindBox are made (is this an error and therefore os.Exit(1) ?
if len(boxMap) == 0 {
fmt.Println("no calls to rice.FindBox() found")
return
}
verbosef("\n")
for boxname := range boxMap {
// find path and filename for this box
boxPath := filepath.Join(pkg.Dir, boxname)
boxFilename := strings.Replace(boxname, "/", "-", -1)
boxFilename = strings.Replace(boxFilename, "..", "back", -1)
boxFilename = strings.Replace(boxFilename, ".", "-", -1)
// verbose info
verbosef("embedding box '%s'\n", boxname)
verbosef("\tto file %s\n", boxFilename)
// create box datastructure (used by template)
box := &embedded.EmbeddedBox{
Name: boxname,
Time: time.Now(),
EmbedType: embedded.EmbedTypeSyso,
Files: make(map[string]*embedded.EmbeddedFile),
Dirs: make(map[string]*embedded.EmbeddedDir),
}
// fill box datastructure with file data
filepath.Walk(boxPath, func(path string, info os.FileInfo, err error) error {
if err != nil {
fmt.Printf("error walking box: %s\n", err)
os.Exit(1)
}
filename := strings.TrimPrefix(path, boxPath)
filename = strings.Replace(filename, "\\", "/", -1)
filename = strings.TrimPrefix(filename, "/")
if info.IsDir() {
embeddedDir := &embedded.EmbeddedDir{
Filename: filename,
DirModTime: info.ModTime(),
}
verbosef("\tincludes dir: '%s'\n", embeddedDir.Filename)
box.Dirs[embeddedDir.Filename] = embeddedDir
// add tree entry (skip for root, it'll create a recursion)
if embeddedDir.Filename != "" {
pathParts := strings.Split(embeddedDir.Filename, "/")
parentDir := box.Dirs[strings.Join(pathParts[:len(pathParts)-1], "/")]
parentDir.ChildDirs = append(parentDir.ChildDirs, embeddedDir)
}
} else {
embeddedFile := &embedded.EmbeddedFile{
Filename: filename,
FileModTime: info.ModTime(),
Content: "",
}
verbosef("\tincludes file: '%s'\n", embeddedFile.Filename)
contentBytes, err := ioutil.ReadFile(path)
if err != nil {
fmt.Printf("error reading file content while walking box: %s\n", err)
os.Exit(1)
}
embeddedFile.Content = string(contentBytes)
box.Files[embeddedFile.Filename] = embeddedFile
}
return nil
})
// encode embedded box to gob file
boxGobBuf := &bytes.Buffer{}
err := gob.NewEncoder(boxGobBuf).Encode(box)
if err != nil {
fmt.Printf("error encoding box to gob: %v\n", err)
os.Exit(1)
}
verbosef("gob-encoded embeddedBox is %d bytes large\n", boxGobBuf.Len())
// write coff
symname := regexpSynameReplacer.ReplaceAllString(boxname, "_")
createCoffSyso(boxname, symname, "386", boxGobBuf.Bytes())
createCoffSyso(boxname, symname, "amd64", boxGobBuf.Bytes())
// write go
sysoHelperData := embeddedSysoHelperData{
Package: pkg.Name,
Symname: symname,
}
fileSysoHelper, err := os.Create(boxFilename + ".rice-box.go")
if err != nil {
fmt.Printf("error creating syso helper: %v\n", err)
os.Exit(1)
}
err = tmplEmbeddedSysoHelper.Execute(fileSysoHelper, sysoHelperData)
if err != nil {
fmt.Printf("error executing tmplEmbeddedSysoHelper: %v\n", err)
os.Exit(1)
}
}
}
func createCoffSyso(boxFilename string, symname string, arch string, data []byte) {
boxCoff := coff.NewRDATA()
switch arch {
case "386":
case "amd64":
boxCoff.FileHeader.Machine = 0x8664
default:
panic("invalid arch")
}
boxCoff.AddData("_bricebox_"+symname, sizedReader{bytes.NewReader(data)})
boxCoff.AddData("_ericebox_"+symname, io.NewSectionReader(strings.NewReader("\000\000"), 0, 2)) // TODO: why? copied from rsrc, which copied it from as-generated
boxCoff.Freeze()
err := writeCoff(boxCoff, boxFilename+"_"+arch+".rice-box.syso")
if err != nil {
fmt.Printf("error writing %s coff/.syso: %v\n", arch, err)
os.Exit(1)
}
}

View file

@ -2,11 +2,13 @@ package main
import (
"fmt"
"go/ast"
"go/build"
"io/ioutil"
"go/parser"
"go/token"
"os"
"path/filepath"
"regexp"
"strings"
)
func findBoxes(pkg *build.Package) map[string]bool {
@ -15,13 +17,6 @@ func findBoxes(pkg *build.Package) map[string]bool {
filenames = append(filenames, pkg.GoFiles...)
filenames = append(filenames, pkg.CgoFiles...)
// prepare regex to find calls to rice.FindBox(..)
regexpBox, err := regexp.Compile(`rice\.(?:Must)?FindBox\(["` + "`" + `]{1}([a-zA-Z0-9\\/\.\-_]+)["` + "`" + `]{1}\)`)
if err != nil {
fmt.Printf("error compiling rice.FindBox regexp: %s\n", err)
os.Exit(1)
}
// create map of boxes to embed
var boxMap = make(map[string]bool)
@ -29,29 +24,80 @@ func findBoxes(pkg *build.Package) map[string]bool {
for _, filename := range filenames {
// find full filepath
fullpath := filepath.Join(pkg.Dir, filename)
verbosef("scanning file %s\n", fullpath)
// open source file
file, err := os.Open(fullpath)
if err != nil {
fmt.Printf("error opening file '%s': %s\n", filename, err)
os.Exit(1)
if strings.HasSuffix(filename, "rice-box.go") {
// Ignore *.rice-box.go files
verbosef("skipping file %q\n", fullpath)
continue
}
defer file.Close()
verbosef("scanning file %q\n", fullpath)
// slurp source code
fileData, err := ioutil.ReadAll(file)
fset := token.NewFileSet()
f, err := parser.ParseFile(fset, fullpath, nil, 0)
if err != nil {
fmt.Printf("error reading file '%s': %s\n", filename, err)
fmt.Println(err)
os.Exit(1)
}
// find rice.FindBox(..) calls
matches := regexpBox.FindAllStringSubmatch(string(fileData), -1)
for _, match := range matches {
boxMap[match[1]] = true
verbosef("\tfound box '%s'\n", match[1])
var riceIsImported bool
ricePkgName := "rice"
for _, imp := range f.Imports {
if strings.HasSuffix(imp.Path.Value, "go.rice\"") {
if imp.Name != nil {
ricePkgName = imp.Name.Name
}
riceIsImported = true
break
}
}
if !riceIsImported {
// Rice wasn't imported, so we won't find a box.
continue
}
if ricePkgName == "_" {
// Rice pkg is unnamed, so we won't find a box.
continue
}
// Inspect AST, looking for calls to (Must)?FindBox.
// First parameter of the func must be a basic literal.
// Identifiers won't be resolved.
var nextIdentIsBoxFunc bool
var nextBasicLitParamIsBoxName bool
ast.Inspect(f, func(node ast.Node) bool {
if node == nil {
return false
}
switch x := node.(type) {
case *ast.Ident:
if nextIdentIsBoxFunc || ricePkgName == "." {
nextIdentIsBoxFunc = false
if x.Name == "FindBox" || x.Name == "MustFindBox" {
nextBasicLitParamIsBoxName = true
}
} else {
if x.Name == ricePkgName {
nextIdentIsBoxFunc = true
}
}
case *ast.BasicLit:
if nextBasicLitParamIsBoxName && x.Kind == token.STRING {
nextBasicLitParamIsBoxName = false
// trim "" or ``
name := x.Value[1 : len(x.Value)-1]
boxMap[name] = true
verbosef("\tfound box %q\n", name)
}
default:
if nextIdentIsBoxFunc {
nextIdentIsBoxFunc = false
}
if nextBasicLitParamIsBoxName {
nextBasicLitParamIsBoxName = false
}
}
return true
})
}
return boxMap

View file

@ -0,0 +1,265 @@
package main
import (
"fmt"
"go/build"
"io/ioutil"
"os"
"path/filepath"
"testing"
)
type sourceFile struct {
Name string
Contents []byte
}
func expectBoxes(expected []string, actual map[string]bool) error {
if len(expected) != len(actual) {
return fmt.Errorf("expected %v, got %v", expected, actual)
}
for _, box := range expected {
if _, ok := actual[box]; !ok {
return fmt.Errorf("expected %v, got %v", expected, actual)
}
}
return nil
}
func setUpTestPkg(pkgName string, files []sourceFile) (*build.Package, func(), error) {
temp, err := ioutil.TempDir("", "go.rice-test")
if err != nil {
return nil, func() {}, err
}
cleanup := func() {
os.RemoveAll(temp)
}
dir := filepath.Join(temp, pkgName)
if err := os.Mkdir(dir, 0770); err != nil {
return nil, cleanup, err
}
for _, f := range files {
if err := ioutil.WriteFile(filepath.Join(dir, f.Name), f.Contents, 0660); err != nil {
return nil, cleanup, err
}
}
pkg, err := build.ImportDir(dir, 0)
return pkg, cleanup, err
}
func TestFindOneBox(t *testing.T) {
pkg, cleanup, err := setUpTestPkg("foobar", []sourceFile{
{
"boxes.go",
[]byte(`package main
import (
"github.com/GeertJohan/go.rice"
)
func main() {
rice.MustFindBox("foo")
}
`),
},
})
defer cleanup()
if err != nil {
t.Error(err)
return
}
expectedBoxes := []string{"foo"}
boxMap := findBoxes(pkg)
if err := expectBoxes(expectedBoxes, boxMap); err != nil {
t.Error(err)
}
}
func TestFindMultipleBoxes(t *testing.T) {
pkg, cleanup, err := setUpTestPkg("foobar", []sourceFile{
{
"boxes.go",
[]byte(`package main
import (
"github.com/GeertJohan/go.rice"
)
func main() {
rice.MustFindBox("foo")
rice.MustFindBox("bar")
}
`),
},
})
defer cleanup()
if err != nil {
t.Error(err)
return
}
expectedBoxes := []string{"foo", "bar"}
boxMap := findBoxes(pkg)
if err := expectBoxes(expectedBoxes, boxMap); err != nil {
t.Error(err)
}
}
func TestNoBoxFoundIfRiceNotImported(t *testing.T) {
pkg, cleanup, err := setUpTestPkg("foobar", []sourceFile{
{
"boxes.go",
[]byte(`package main
type fakerice struct {}
func (fr fakerice) FindBox(s string) {
}
func main() {
rice := fakerice{}
rice.FindBox("foo")
}
`),
},
})
defer cleanup()
if err != nil {
t.Error(err)
return
}
boxMap := findBoxes(pkg)
if _, ok := boxMap["foo"]; ok {
t.Errorf("Unexpected box %q was found", "foo")
}
}
func TestUnrelatedBoxesAreNotFound(t *testing.T) {
pkg, cleanup, err := setUpTestPkg("foobar", []sourceFile{
{
"boxes.go",
[]byte(`package foobar
import (
_ "github.com/GeertJohan/go.rice"
)
type fakerice struct {}
func (fr fakerice) FindBox(s string) {
}
func FindBox(s string) {
}
func LoadBoxes() {
rice := fakerice{}
rice.FindBox("foo")
FindBox("bar")
}
`),
},
})
defer cleanup()
if err != nil {
t.Error(err)
return
}
boxMap := findBoxes(pkg)
for _, box := range []string{"foo", "bar"} {
if _, ok := boxMap[box]; ok {
t.Errorf("Unexpected box %q was found", box)
}
}
}
func TestMixGoodAndBadBoxes(t *testing.T) {
pkg, cleanup, err := setUpTestPkg("foobar", []sourceFile{
{
"boxes1.go",
[]byte(`package foobar
import (
_ "github.com/GeertJohan/go.rice"
)
type fakerice struct {}
func (fr fakerice) FindBox(s string) {
}
func FindBox(s string) {
}
func LoadBoxes1() {
rice := fakerice{}
rice.FindBox("foo")
FindBox("bar")
}
`),
},
{
"boxes2.go",
[]byte(`package foobar
import (
noodles "github.com/GeertJohan/go.rice"
)
func LoadBoxes2() {
FindBox("baz")
noodles.FindBox("veggies")
}
`),
},
{
"boxes3.go",
[]byte(`package foobar
import (
"github.com/GeertJohan/go.rice"
)
func LoadBoxes3() {
rice.FindBox("fish")
}
`),
},
{
"boxes4.go",
[]byte(`package foobar
import (
. "github.com/GeertJohan/go.rice"
)
func LoadBoxes3() {
MustFindBox("chicken")
}
`),
},
})
defer cleanup()
if err != nil {
t.Error(err)
return
}
boxMap := findBoxes(pkg)
for _, box := range []string{"foo", "bar", "baz"} {
if _, ok := boxMap[box]; ok {
t.Errorf("Unexpected box %q was found", box)
}
}
for _, box := range []string{"veggies", "fish", "chicken"} {
if _, ok := boxMap[box]; !ok {
t.Errorf("Expected box %q not found", box)
}
}
}

View file

@ -16,8 +16,9 @@ var flags struct {
Executable string `long:"exec" description:"Executable to append" required:"true"`
} `command:"append"`
Embed struct{} `command:"embed"`
Clean struct{} `command:"clean"`
EmbedGo struct{} `command:"embed-go" alias:"embed"`
EmbedSyso struct{} `command:"embed-syso"`
Clean struct{} `command:"clean"`
}
// flags parser

View file

@ -16,8 +16,11 @@ func main() {
// switch on the operation to perform
switch flagsParser.Active.Name {
case "embed":
operationEmbed(pkg)
case "embed", "embed-go":
operationEmbedGo(pkg)
case "embed-syso":
log.Println("WARNING: embedding .syso is expirimental..")
operationEmbedSyso(pkg)
case "append":
operationAppend(pkg)
case "clean":

View file

@ -75,13 +75,6 @@ type boxDataType struct {
Dirs map[string]*dirDataType
}
type singleDataType struct {
Package string
SingleName string
UnixNow int64
File *fileDataType
}
type fileDataType struct {
Identifier string
FileName string

View file

@ -0,0 +1,41 @@
package main
import (
"fmt"
"github.com/akavel/rsrc/binutil"
"github.com/akavel/rsrc/coff"
"os"
"reflect"
)
// copied from github.com/akavel/rsrc
// LICENSE: MIT
// Copyright 2013-2014 The rsrc Authors. (https://github.com/akavel/rsrc/blob/master/AUTHORS)
func writeCoff(coff *coff.Coff, fnameout string) error {
out, err := os.Create(fnameout)
if err != nil {
return err
}
defer out.Close()
w := binutil.Writer{W: out}
// write the resulting file to disk
binutil.Walk(coff, func(v reflect.Value, path string) error {
if binutil.Plain(v.Kind()) {
w.WriteLE(v.Interface())
return nil
}
vv, ok := v.Interface().(binutil.SizedReader)
if ok {
w.WriteFromSized(vv)
return binutil.WALK_SKIP
}
return nil
})
if w.Err != nil {
return fmt.Errorf("Error writing output file: %s", w.Err)
}
return nil
}

View file

@ -65,7 +65,7 @@ func (vf *virtualFile) readdir(count int) ([]os.FileInfo, error) {
Err: errors.New("bad file descriptor"),
}
}
//++ wont work for a file
//TODO: return proper error for a readdir() call on a file
return nil, ErrNotImplemented
}

View file

@ -1,344 +0,0 @@
/*
* Copyright (c) 2013 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"
"reflect"
"sort"
"strconv"
"unsafe"
)
// offsetPtr, offsetScalar, and offsetFlag are the offsets for the internal
// reflect.Value fields.
var offsetPtr, offsetScalar, offsetFlag uintptr
// reflectValueOld mirrors the struct layout of the reflect package Value type
// before golang commit ecccf07e7f9d.
var reflectValueOld struct {
typ unsafe.Pointer
val unsafe.Pointer
flag uintptr
}
// reflectValueNew mirrors the struct layout of the reflect package Value type
// after golang commit ecccf07e7f9d.
var reflectValueNew struct {
typ unsafe.Pointer
ptr unsafe.Pointer
scalar uintptr
flag uintptr
}
func init() {
// Older versions of reflect.Value stored small integers directly in the
// ptr field (which is named val in the older versions). Newer versions
// added a new field named scalar for this purpose which unfortuantely
// comes before the flag field. Further the new field is before the
// flag field, so the offset of the flag field is different as well.
// This code constructs a new reflect.Value from a known small integer
// and checks if the val field within it matches. When it matches, the
// old style reflect.Value is being used. Otherwise it's the new style.
v := 0xf00
vv := reflect.ValueOf(v)
upv := unsafe.Pointer(uintptr(unsafe.Pointer(&vv)) +
unsafe.Offsetof(reflectValueOld.val))
// Assume the old style by default.
offsetPtr = unsafe.Offsetof(reflectValueOld.val)
offsetScalar = 0
offsetFlag = unsafe.Offsetof(reflectValueOld.flag)
// Use the new style offsets if the ptr field doesn't match the value
// since it must be in the new scalar field.
if int(*(*uintptr)(upv)) != v {
offsetPtr = unsafe.Offsetof(reflectValueNew.ptr)
offsetScalar = unsafe.Offsetof(reflectValueNew.scalar)
offsetFlag = unsafe.Offsetof(reflectValueNew.flag)
}
}
// flagIndir indicates whether the value field of a reflect.Value is the actual
// data or a pointer to the data.
const flagIndir = 1 << 1
// 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) (rv reflect.Value) {
indirects := 1
vt := v.Type()
upv := unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + offsetPtr)
rvf := *(*uintptr)(unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + offsetFlag))
if rvf&flagIndir != 0 {
vt = reflect.PtrTo(v.Type())
indirects++
} else if offsetScalar != 0 {
upv = unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + offsetScalar)
}
pv := reflect.NewAt(vt, upv)
rv = pv
for i := 0; i < indirects; i++ {
rv = rv.Elem()
}
return rv
}
// 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("]")
)
// 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 to bypass these restrictions
// since this package does not mutate the values.
if !v.CanInterface() {
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.
var viface interface{}
if !cs.DisablePointerMethods {
if !v.CanAddr() {
v = unsafeReflectValue(v)
}
viface = v.Addr().Interface()
} else {
if v.CanAddr() {
v = v.Addr()
}
viface = v.Interface()
}
// Is it an error or Stringer?
switch iface := viface.(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 hexidecimal 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
}
// 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]
}
// 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 {
switch s.values[i].Kind() {
case reflect.Bool:
return !s.values[i].Bool() && s.values[j].Bool()
case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
return s.values[i].Int() < s.values[j].Int()
case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
return s.values[i].Uint() < s.values[j].Uint()
case reflect.Float32, reflect.Float64:
return s.values[i].Float() < s.values[j].Float()
case reflect.String:
return s.values[i].String() < s.values[j].String()
case reflect.Uintptr:
return s.values[i].Uint() < s.values[j].Uint()
}
return s.values[i].String() < s.values[j].String()
}
// sortValues is a generic sort function for native types: int, uint, bool,
// string and uintptr. Other inputs are sorted according to their
// Value.String() value to ensure display stability.
func sortValues(values []reflect.Value) {
if len(values) == 0 {
return
}
sort.Sort(&valuesSorter{values})
}

View file

@ -1,192 +0,0 @@
/*
* Copyright (c) 2013 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_test
import (
"fmt"
"github.com/davecgh/go-spew/spew"
"reflect"
"testing"
)
// custom type to test Stinger interface on non-pointer receiver.
type stringer string
// String implements the Stringer interface for testing invocation of custom
// stringers on types with non-pointer receivers.
func (s stringer) String() string {
return "stringer " + string(s)
}
// custom type to test Stinger interface on pointer receiver.
type pstringer string
// String implements the Stringer interface for testing invocation of custom
// stringers on types with only pointer receivers.
func (s *pstringer) String() string {
return "stringer " + string(*s)
}
// xref1 and xref2 are cross referencing structs for testing circular reference
// detection.
type xref1 struct {
ps2 *xref2
}
type xref2 struct {
ps1 *xref1
}
// indirCir1, indirCir2, and indirCir3 are used to generate an indirect circular
// reference for testing detection.
type indirCir1 struct {
ps2 *indirCir2
}
type indirCir2 struct {
ps3 *indirCir3
}
type indirCir3 struct {
ps1 *indirCir1
}
// embed is used to test embedded structures.
type embed struct {
a string
}
// embedwrap is used to test embedded structures.
type embedwrap struct {
*embed
e *embed
}
// panicer is used to intentionally cause a panic for testing spew properly
// handles them
type panicer int
func (p panicer) String() string {
panic("test panic")
}
// customError is used to test custom error interface invocation.
type customError int
func (e customError) Error() string {
return fmt.Sprintf("error: %d", int(e))
}
// stringizeWants converts a slice of wanted test output into a format suitable
// for a test error message.
func stringizeWants(wants []string) string {
s := ""
for i, want := range wants {
if i > 0 {
s += fmt.Sprintf("want%d: %s", i+1, want)
} else {
s += "want: " + want
}
}
return s
}
// testFailed returns whether or not a test failed by checking if the result
// of the test is in the slice of wanted strings.
func testFailed(result string, wants []string) bool {
for _, want := range wants {
if result == want {
return false
}
}
return true
}
// TestSortValues ensures the sort functionality for relect.Value based sorting
// works as intended.
func TestSortValues(t *testing.T) {
getInterfaces := func(values []reflect.Value) []interface{} {
interfaces := []interface{}{}
for _, v := range values {
interfaces = append(interfaces, v.Interface())
}
return interfaces
}
v := reflect.ValueOf
a := v("a")
b := v("b")
c := v("c")
embedA := v(embed{"a"})
embedB := v(embed{"b"})
embedC := v(embed{"c"})
tests := []struct {
input []reflect.Value
expected []reflect.Value
}{
// No values.
{
[]reflect.Value{},
[]reflect.Value{},
},
// Bools.
{
[]reflect.Value{v(false), v(true), v(false)},
[]reflect.Value{v(false), v(false), v(true)},
},
// Ints.
{
[]reflect.Value{v(2), v(1), v(3)},
[]reflect.Value{v(1), v(2), v(3)},
},
// Uints.
{
[]reflect.Value{v(uint8(2)), v(uint8(1)), v(uint8(3))},
[]reflect.Value{v(uint8(1)), v(uint8(2)), v(uint8(3))},
},
// Floats.
{
[]reflect.Value{v(2.0), v(1.0), v(3.0)},
[]reflect.Value{v(1.0), v(2.0), v(3.0)},
},
// Strings.
{
[]reflect.Value{b, a, c},
[]reflect.Value{a, b, c},
},
// Uintptrs.
{
[]reflect.Value{v(uintptr(2)), v(uintptr(1)), v(uintptr(3))},
[]reflect.Value{v(uintptr(1)), v(uintptr(2)), v(uintptr(3))},
},
// Invalid.
{
[]reflect.Value{embedB, embedA, embedC},
[]reflect.Value{embedB, embedA, embedC},
},
}
for _, test := range tests {
spew.SortValues(test.input)
// reflect.DeepEqual cannot really make sense of reflect.Value,
// probably because of all the pointer tricks. For instance,
// v(2.0) != v(2.0) on a 32-bits system. Turn them into interface{}
// instead.
input := getInterfaces(test.input)
expected := getInterfaces(test.expected)
if !reflect.DeepEqual(input, expected) {
t.Errorf("Sort mismatch:\n %v != %v", input, expected)
}
}
}

View file

@ -1,288 +0,0 @@
/*
* Copyright (c) 2013 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.
DisablePointerMethods 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) are supported
// with other types sorted according to the reflect.Value.String() output
// which guarantees display stability.
SortKeys 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: " "}
}

View file

@ -1,196 +0,0 @@
/*
* Copyright (c) 2013 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.
* 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)
are supported with other types sorted according to the
reflect.Value.String() output which guarantees display stability.
Natural map order is used by default.
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 {}) {
(string) "one": (bool) true
}
}
Byte (and uint8) arrays and slices are displayed uniquely like the hexdump -C
command as shown.
([]uint8) {
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

View file

@ -1,474 +0,0 @@
/*
* Copyright (c) 2013 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 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 == true:
d.w.Write(nilAngleBytes)
case cycleFound == true:
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 back
// into 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
// to bypass these restrictions since this package does not
// mutate the values.
vs := v
if !vs.CanInterface() || !vs.CanAddr() {
vs = unsafeReflectValue(vs)
}
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
// 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:
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)
}
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...)
}

View file

@ -1,912 +0,0 @@
/*
* Copyright (c) 2013 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.
*/
/*
Test Summary:
NOTE: For each test, a nil pointer, a single pointer and double pointer to the
base test element are also tested to ensure proper indirection across all types.
- Max int8, int16, int32, int64, int
- Max uint8, uint16, uint32, uint64, uint
- Boolean true and false
- Standard complex64 and complex128
- Array containing standard ints
- Array containing type with custom formatter on pointer receiver only
- Array containing interfaces
- Array containing bytes
- Slice containing standard float32 values
- Slice containing type with custom formatter on pointer receiver only
- Slice containing interfaces
- Slice containing bytes
- Nil slice
- Standard string
- Nil interface
- Sub-interface
- Map with string keys and int vals
- Map with custom formatter type on pointer receiver only keys and vals
- Map with interface keys and values
- Map with nil interface value
- Struct with primitives
- Struct that contains another struct
- Struct that contains custom type with Stringer pointer interface via both
exported and unexported fields
- Struct that contains embedded struct and field to same struct
- Uintptr to 0 (null pointer)
- Uintptr address of real variable
- Unsafe.Pointer to 0 (null pointer)
- Unsafe.Pointer to address of real variable
- Nil channel
- Standard int channel
- Function with no params and no returns
- Function with param and no returns
- Function with multiple params and multiple returns
- Struct that is circular through self referencing
- Structs that are circular through cross referencing
- Structs that are indirectly circular
- Type that panics in its Stringer interface
*/
package spew_test
import (
"bytes"
"fmt"
"github.com/davecgh/go-spew/spew"
"testing"
"unsafe"
)
// dumpTest is used to describe a test to be perfomed against the Dump method.
type dumpTest struct {
in interface{}
wants []string
}
// dumpTests houses all of the tests to be performed against the Dump method.
var dumpTests = make([]dumpTest, 0)
// addDumpTest is a helper method to append the passed input and desired result
// to dumpTests
func addDumpTest(in interface{}, wants ...string) {
test := dumpTest{in, wants}
dumpTests = append(dumpTests, test)
}
func addIntDumpTests() {
// Max int8.
v := int8(127)
nv := (*int8)(nil)
pv := &v
vAddr := fmt.Sprintf("%p", pv)
pvAddr := fmt.Sprintf("%p", &pv)
vt := "int8"
vs := "127"
addDumpTest(v, "("+vt+") "+vs+"\n")
addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs+")\n")
addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n")
addDumpTest(nv, "(*"+vt+")(<nil>)\n")
// Max int16.
v2 := int16(32767)
nv2 := (*int16)(nil)
pv2 := &v2
v2Addr := fmt.Sprintf("%p", pv2)
pv2Addr := fmt.Sprintf("%p", &pv2)
v2t := "int16"
v2s := "32767"
addDumpTest(v2, "("+v2t+") "+v2s+"\n")
addDumpTest(pv2, "(*"+v2t+")("+v2Addr+")("+v2s+")\n")
addDumpTest(&pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n")
addDumpTest(nv2, "(*"+v2t+")(<nil>)\n")
// Max int32.
v3 := int32(2147483647)
nv3 := (*int32)(nil)
pv3 := &v3
v3Addr := fmt.Sprintf("%p", pv3)
pv3Addr := fmt.Sprintf("%p", &pv3)
v3t := "int32"
v3s := "2147483647"
addDumpTest(v3, "("+v3t+") "+v3s+"\n")
addDumpTest(pv3, "(*"+v3t+")("+v3Addr+")("+v3s+")\n")
addDumpTest(&pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")("+v3s+")\n")
addDumpTest(nv3, "(*"+v3t+")(<nil>)\n")
// Max int64.
v4 := int64(9223372036854775807)
nv4 := (*int64)(nil)
pv4 := &v4
v4Addr := fmt.Sprintf("%p", pv4)
pv4Addr := fmt.Sprintf("%p", &pv4)
v4t := "int64"
v4s := "9223372036854775807"
addDumpTest(v4, "("+v4t+") "+v4s+"\n")
addDumpTest(pv4, "(*"+v4t+")("+v4Addr+")("+v4s+")\n")
addDumpTest(&pv4, "(**"+v4t+")("+pv4Addr+"->"+v4Addr+")("+v4s+")\n")
addDumpTest(nv4, "(*"+v4t+")(<nil>)\n")
// Max int.
v5 := int(2147483647)
nv5 := (*int)(nil)
pv5 := &v5
v5Addr := fmt.Sprintf("%p", pv5)
pv5Addr := fmt.Sprintf("%p", &pv5)
v5t := "int"
v5s := "2147483647"
addDumpTest(v5, "("+v5t+") "+v5s+"\n")
addDumpTest(pv5, "(*"+v5t+")("+v5Addr+")("+v5s+")\n")
addDumpTest(&pv5, "(**"+v5t+")("+pv5Addr+"->"+v5Addr+")("+v5s+")\n")
addDumpTest(nv5, "(*"+v5t+")(<nil>)\n")
}
func addUintDumpTests() {
// Max uint8.
v := uint8(255)
nv := (*uint8)(nil)
pv := &v
vAddr := fmt.Sprintf("%p", pv)
pvAddr := fmt.Sprintf("%p", &pv)
vt := "uint8"
vs := "255"
addDumpTest(v, "("+vt+") "+vs+"\n")
addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs+")\n")
addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n")
addDumpTest(nv, "(*"+vt+")(<nil>)\n")
// Max uint16.
v2 := uint16(65535)
nv2 := (*uint16)(nil)
pv2 := &v2
v2Addr := fmt.Sprintf("%p", pv2)
pv2Addr := fmt.Sprintf("%p", &pv2)
v2t := "uint16"
v2s := "65535"
addDumpTest(v2, "("+v2t+") "+v2s+"\n")
addDumpTest(pv2, "(*"+v2t+")("+v2Addr+")("+v2s+")\n")
addDumpTest(&pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n")
addDumpTest(nv2, "(*"+v2t+")(<nil>)\n")
// Max uint32.
v3 := uint32(4294967295)
nv3 := (*uint32)(nil)
pv3 := &v3
v3Addr := fmt.Sprintf("%p", pv3)
pv3Addr := fmt.Sprintf("%p", &pv3)
v3t := "uint32"
v3s := "4294967295"
addDumpTest(v3, "("+v3t+") "+v3s+"\n")
addDumpTest(pv3, "(*"+v3t+")("+v3Addr+")("+v3s+")\n")
addDumpTest(&pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")("+v3s+")\n")
addDumpTest(nv3, "(*"+v3t+")(<nil>)\n")
// Max uint64.
v4 := uint64(18446744073709551615)
nv4 := (*uint64)(nil)
pv4 := &v4
v4Addr := fmt.Sprintf("%p", pv4)
pv4Addr := fmt.Sprintf("%p", &pv4)
v4t := "uint64"
v4s := "18446744073709551615"
addDumpTest(v4, "("+v4t+") "+v4s+"\n")
addDumpTest(pv4, "(*"+v4t+")("+v4Addr+")("+v4s+")\n")
addDumpTest(&pv4, "(**"+v4t+")("+pv4Addr+"->"+v4Addr+")("+v4s+")\n")
addDumpTest(nv4, "(*"+v4t+")(<nil>)\n")
// Max uint.
v5 := uint(4294967295)
nv5 := (*uint)(nil)
pv5 := &v5
v5Addr := fmt.Sprintf("%p", pv5)
pv5Addr := fmt.Sprintf("%p", &pv5)
v5t := "uint"
v5s := "4294967295"
addDumpTest(v5, "("+v5t+") "+v5s+"\n")
addDumpTest(pv5, "(*"+v5t+")("+v5Addr+")("+v5s+")\n")
addDumpTest(&pv5, "(**"+v5t+")("+pv5Addr+"->"+v5Addr+")("+v5s+")\n")
addDumpTest(nv5, "(*"+v5t+")(<nil>)\n")
}
func addBoolDumpTests() {
// Boolean true.
v := bool(true)
nv := (*bool)(nil)
pv := &v
vAddr := fmt.Sprintf("%p", pv)
pvAddr := fmt.Sprintf("%p", &pv)
vt := "bool"
vs := "true"
addDumpTest(v, "("+vt+") "+vs+"\n")
addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs+")\n")
addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n")
addDumpTest(nv, "(*"+vt+")(<nil>)\n")
// Boolean false.
v2 := bool(false)
pv2 := &v2
v2Addr := fmt.Sprintf("%p", pv2)
pv2Addr := fmt.Sprintf("%p", &pv2)
v2t := "bool"
v2s := "false"
addDumpTest(v2, "("+v2t+") "+v2s+"\n")
addDumpTest(pv2, "(*"+v2t+")("+v2Addr+")("+v2s+")\n")
addDumpTest(&pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n")
}
func addFloatDumpTests() {
// Standard float32.
v := float32(3.1415)
nv := (*float32)(nil)
pv := &v
vAddr := fmt.Sprintf("%p", pv)
pvAddr := fmt.Sprintf("%p", &pv)
vt := "float32"
vs := "3.1415"
addDumpTest(v, "("+vt+") "+vs+"\n")
addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs+")\n")
addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n")
addDumpTest(nv, "(*"+vt+")(<nil>)\n")
// Standard float64.
v2 := float64(3.1415926)
nv2 := (*float64)(nil)
pv2 := &v2
v2Addr := fmt.Sprintf("%p", pv2)
pv2Addr := fmt.Sprintf("%p", &pv2)
v2t := "float64"
v2s := "3.1415926"
addDumpTest(v2, "("+v2t+") "+v2s+"\n")
addDumpTest(pv2, "(*"+v2t+")("+v2Addr+")("+v2s+")\n")
addDumpTest(&pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n")
addDumpTest(nv2, "(*"+v2t+")(<nil>)\n")
}
func addComplexDumpTests() {
// Standard complex64.
v := complex(float32(6), -2)
nv := (*complex64)(nil)
pv := &v
vAddr := fmt.Sprintf("%p", pv)
pvAddr := fmt.Sprintf("%p", &pv)
vt := "complex64"
vs := "(6-2i)"
addDumpTest(v, "("+vt+") "+vs+"\n")
addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs+")\n")
addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n")
addDumpTest(nv, "(*"+vt+")(<nil>)\n")
// Standard complex128.
v2 := complex(float64(-6), 2)
nv2 := (*complex128)(nil)
pv2 := &v2
v2Addr := fmt.Sprintf("%p", pv2)
pv2Addr := fmt.Sprintf("%p", &pv2)
v2t := "complex128"
v2s := "(-6+2i)"
addDumpTest(v2, "("+v2t+") "+v2s+"\n")
addDumpTest(pv2, "(*"+v2t+")("+v2Addr+")("+v2s+")\n")
addDumpTest(&pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n")
addDumpTest(nv2, "(*"+v2t+")(<nil>)\n")
}
func addArrayDumpTests() {
// Array containing standard ints.
v := [3]int{1, 2, 3}
nv := (*[3]int)(nil)
pv := &v
vAddr := fmt.Sprintf("%p", pv)
pvAddr := fmt.Sprintf("%p", &pv)
vt := "int"
vs := "{\n (" + vt + ") 1,\n (" + vt + ") 2,\n (" + vt + ") 3\n}"
addDumpTest(v, "([3]"+vt+") "+vs+"\n")
addDumpTest(pv, "(*[3]"+vt+")("+vAddr+")("+vs+")\n")
addDumpTest(&pv, "(**[3]"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n")
addDumpTest(nv, "(*[3]"+vt+")(<nil>)\n")
// Array containing type with custom formatter on pointer receiver only.
v2 := [3]pstringer{"1", "2", "3"}
nv2 := (*[3]pstringer)(nil)
pv2 := &v2
v2Addr := fmt.Sprintf("%p", pv2)
pv2Addr := fmt.Sprintf("%p", &pv2)
v2t := "spew_test.pstringer"
v2s := "{\n (" + v2t + ") stringer 1,\n (" + v2t + ") stringer 2,\n (" +
v2t + ") stringer 3\n}"
addDumpTest(v2, "([3]"+v2t+") "+v2s+"\n")
addDumpTest(pv2, "(*[3]"+v2t+")("+v2Addr+")("+v2s+")\n")
addDumpTest(&pv2, "(**[3]"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n")
addDumpTest(nv2, "(*[3]"+v2t+")(<nil>)\n")
// Array containing interfaces.
v3 := [3]interface{}{"one", int(2), uint(3)}
nv3 := (*[3]interface{})(nil)
pv3 := &v3
v3Addr := fmt.Sprintf("%p", pv3)
pv3Addr := fmt.Sprintf("%p", &pv3)
v3t := "[3]interface {}"
v3t2 := "string"
v3t3 := "int"
v3t4 := "uint"
v3s := "{\n (" + v3t2 + ") \"one\",\n (" + v3t3 + ") 2,\n (" + v3t4 +
") 3\n}"
addDumpTest(v3, "("+v3t+") "+v3s+"\n")
addDumpTest(pv3, "(*"+v3t+")("+v3Addr+")("+v3s+")\n")
addDumpTest(&pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")("+v3s+")\n")
addDumpTest(nv3, "(*"+v3t+")(<nil>)\n")
// Array containing bytes.
v4 := [34]byte{
0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
0x31, 0x32,
}
nv4 := (*[34]byte)(nil)
pv4 := &v4
v4Addr := fmt.Sprintf("%p", pv4)
pv4Addr := fmt.Sprintf("%p", &pv4)
v4t := "[34]uint8"
v4s := "{\n 00000000 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20" +
" |............... |\n" +
" 00000010 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30" +
" |!\"#$%&'()*+,-./0|\n" +
" 00000020 31 32 " +
" |12|\n}"
addDumpTest(v4, "("+v4t+") "+v4s+"\n")
addDumpTest(pv4, "(*"+v4t+")("+v4Addr+")("+v4s+")\n")
addDumpTest(&pv4, "(**"+v4t+")("+pv4Addr+"->"+v4Addr+")("+v4s+")\n")
addDumpTest(nv4, "(*"+v4t+")(<nil>)\n")
}
func addSliceDumpTests() {
// Slice containing standard float32 values.
v := []float32{3.14, 6.28, 12.56}
nv := (*[]float32)(nil)
pv := &v
vAddr := fmt.Sprintf("%p", pv)
pvAddr := fmt.Sprintf("%p", &pv)
vt := "float32"
vs := "{\n (" + vt + ") 3.14,\n (" + vt + ") 6.28,\n (" + vt + ") 12.56\n}"
addDumpTest(v, "([]"+vt+") "+vs+"\n")
addDumpTest(pv, "(*[]"+vt+")("+vAddr+")("+vs+")\n")
addDumpTest(&pv, "(**[]"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n")
addDumpTest(nv, "(*[]"+vt+")(<nil>)\n")
// Slice containing type with custom formatter on pointer receiver only.
v2 := []pstringer{"1", "2", "3"}
nv2 := (*[]pstringer)(nil)
pv2 := &v2
v2Addr := fmt.Sprintf("%p", pv2)
pv2Addr := fmt.Sprintf("%p", &pv2)
v2t := "spew_test.pstringer"
v2s := "{\n (" + v2t + ") stringer 1,\n (" + v2t + ") stringer 2,\n (" +
v2t + ") stringer 3\n}"
addDumpTest(v2, "([]"+v2t+") "+v2s+"\n")
addDumpTest(pv2, "(*[]"+v2t+")("+v2Addr+")("+v2s+")\n")
addDumpTest(&pv2, "(**[]"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n")
addDumpTest(nv2, "(*[]"+v2t+")(<nil>)\n")
// Slice containing interfaces.
v3 := []interface{}{"one", int(2), uint(3), nil}
nv3 := (*[]interface{})(nil)
pv3 := &v3
v3Addr := fmt.Sprintf("%p", pv3)
pv3Addr := fmt.Sprintf("%p", &pv3)
v3t := "[]interface {}"
v3t2 := "string"
v3t3 := "int"
v3t4 := "uint"
v3t5 := "interface {}"
v3s := "{\n (" + v3t2 + ") \"one\",\n (" + v3t3 + ") 2,\n (" + v3t4 +
") 3,\n (" + v3t5 + ") <nil>\n}"
addDumpTest(v3, "("+v3t+") "+v3s+"\n")
addDumpTest(pv3, "(*"+v3t+")("+v3Addr+")("+v3s+")\n")
addDumpTest(&pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")("+v3s+")\n")
addDumpTest(nv3, "(*"+v3t+")(<nil>)\n")
// Slice containing bytes.
v4 := []byte{
0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
0x31, 0x32,
}
nv4 := (*[]byte)(nil)
pv4 := &v4
v4Addr := fmt.Sprintf("%p", pv4)
pv4Addr := fmt.Sprintf("%p", &pv4)
v4t := "[]uint8"
v4s := "{\n 00000000 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20" +
" |............... |\n" +
" 00000010 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30" +
" |!\"#$%&'()*+,-./0|\n" +
" 00000020 31 32 " +
" |12|\n}"
addDumpTest(v4, "("+v4t+") "+v4s+"\n")
addDumpTest(pv4, "(*"+v4t+")("+v4Addr+")("+v4s+")\n")
addDumpTest(&pv4, "(**"+v4t+")("+pv4Addr+"->"+v4Addr+")("+v4s+")\n")
addDumpTest(nv4, "(*"+v4t+")(<nil>)\n")
// Nil slice.
v5 := []int(nil)
nv5 := (*[]int)(nil)
pv5 := &v5
v5Addr := fmt.Sprintf("%p", pv5)
pv5Addr := fmt.Sprintf("%p", &pv5)
v5t := "[]int"
v5s := "<nil>"
addDumpTest(v5, "("+v5t+") "+v5s+"\n")
addDumpTest(pv5, "(*"+v5t+")("+v5Addr+")("+v5s+")\n")
addDumpTest(&pv5, "(**"+v5t+")("+pv5Addr+"->"+v5Addr+")("+v5s+")\n")
addDumpTest(nv5, "(*"+v5t+")(<nil>)\n")
}
func addStringDumpTests() {
// Standard string.
v := "test"
nv := (*string)(nil)
pv := &v
vAddr := fmt.Sprintf("%p", pv)
pvAddr := fmt.Sprintf("%p", &pv)
vt := "string"
vs := "\"test\""
addDumpTest(v, "("+vt+") "+vs+"\n")
addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs+")\n")
addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n")
addDumpTest(nv, "(*"+vt+")(<nil>)\n")
}
func addInterfaceDumpTests() {
// Nil interface.
var v interface{}
nv := (*interface{})(nil)
pv := &v
vAddr := fmt.Sprintf("%p", pv)
pvAddr := fmt.Sprintf("%p", &pv)
vt := "interface {}"
vs := "<nil>"
addDumpTest(v, "("+vt+") "+vs+"\n")
addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs+")\n")
addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n")
addDumpTest(nv, "(*"+vt+")(<nil>)\n")
// Sub-interface.
v2 := interface{}(uint16(65535))
pv2 := &v2
v2Addr := fmt.Sprintf("%p", pv2)
pv2Addr := fmt.Sprintf("%p", &pv2)
v2t := "uint16"
v2s := "65535"
addDumpTest(v2, "("+v2t+") "+v2s+"\n")
addDumpTest(pv2, "(*"+v2t+")("+v2Addr+")("+v2s+")\n")
addDumpTest(&pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n")
}
func addMapDumpTests() {
// Map with string keys and int vals.
v := map[string]int{"one": 1, "two": 2}
nv := (*map[string]int)(nil)
pv := &v
vAddr := fmt.Sprintf("%p", pv)
pvAddr := fmt.Sprintf("%p", &pv)
vt := "map[string]int"
vt1 := "string"
vt2 := "int"
vs := "{\n (" + vt1 + ") \"one\": (" + vt2 + ") 1,\n (" + vt1 +
") \"two\": (" + vt2 + ") 2\n}"
vs2 := "{\n (" + vt1 + ") \"two\": (" + vt2 + ") 2,\n (" + vt1 +
") \"one\": (" + vt2 + ") 1\n}"
addDumpTest(v, "("+vt+") "+vs+"\n", "("+vt+") "+vs2+"\n")
addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs+")\n",
"(*"+vt+")("+vAddr+")("+vs2+")\n")
addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n",
"(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs2+")\n")
addDumpTest(nv, "(*"+vt+")(<nil>)\n")
// Map with custom formatter type on pointer receiver only keys and vals.
v2 := map[pstringer]pstringer{"one": "1"}
nv2 := (*map[pstringer]pstringer)(nil)
pv2 := &v2
v2Addr := fmt.Sprintf("%p", pv2)
pv2Addr := fmt.Sprintf("%p", &pv2)
v2t := "map[spew_test.pstringer]spew_test.pstringer"
v2t1 := "spew_test.pstringer"
v2t2 := "spew_test.pstringer"
v2s := "{\n (" + v2t1 + ") stringer one: (" + v2t2 + ") stringer 1\n}"
addDumpTest(v2, "("+v2t+") "+v2s+"\n")
addDumpTest(pv2, "(*"+v2t+")("+v2Addr+")("+v2s+")\n")
addDumpTest(&pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n")
addDumpTest(nv2, "(*"+v2t+")(<nil>)\n")
// Map with interface keys and values.
v3 := map[interface{}]interface{}{"one": 1}
nv3 := (*map[interface{}]interface{})(nil)
pv3 := &v3
v3Addr := fmt.Sprintf("%p", pv3)
pv3Addr := fmt.Sprintf("%p", &pv3)
v3t := "map[interface {}]interface {}"
v3t1 := "string"
v3t2 := "int"
v3s := "{\n (" + v3t1 + ") \"one\": (" + v3t2 + ") 1\n}"
addDumpTest(v3, "("+v3t+") "+v3s+"\n")
addDumpTest(pv3, "(*"+v3t+")("+v3Addr+")("+v3s+")\n")
addDumpTest(&pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")("+v3s+")\n")
addDumpTest(nv3, "(*"+v3t+")(<nil>)\n")
// Map with nil interface value.
v4 := map[string]interface{}{"nil": nil}
nv4 := (*map[string]interface{})(nil)
pv4 := &v4
v4Addr := fmt.Sprintf("%p", pv4)
pv4Addr := fmt.Sprintf("%p", &pv4)
v4t := "map[string]interface {}"
v4t1 := "string"
v4t2 := "interface {}"
v4s := "{\n (" + v4t1 + ") \"nil\": (" + v4t2 + ") <nil>\n}"
addDumpTest(v4, "("+v4t+") "+v4s+"\n")
addDumpTest(pv4, "(*"+v4t+")("+v4Addr+")("+v4s+")\n")
addDumpTest(&pv4, "(**"+v4t+")("+pv4Addr+"->"+v4Addr+")("+v4s+")\n")
addDumpTest(nv4, "(*"+v4t+")(<nil>)\n")
}
func addStructDumpTests() {
// Struct with primitives.
type s1 struct {
a int8
b uint8
}
v := s1{127, 255}
nv := (*s1)(nil)
pv := &v
vAddr := fmt.Sprintf("%p", pv)
pvAddr := fmt.Sprintf("%p", &pv)
vt := "spew_test.s1"
vt2 := "int8"
vt3 := "uint8"
vs := "{\n a: (" + vt2 + ") 127,\n b: (" + vt3 + ") 255\n}"
addDumpTest(v, "("+vt+") "+vs+"\n")
addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs+")\n")
addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n")
addDumpTest(nv, "(*"+vt+")(<nil>)\n")
// Struct that contains another struct.
type s2 struct {
s1 s1
b bool
}
v2 := s2{s1{127, 255}, true}
nv2 := (*s2)(nil)
pv2 := &v2
v2Addr := fmt.Sprintf("%p", pv2)
pv2Addr := fmt.Sprintf("%p", &pv2)
v2t := "spew_test.s2"
v2t2 := "spew_test.s1"
v2t3 := "int8"
v2t4 := "uint8"
v2t5 := "bool"
v2s := "{\n s1: (" + v2t2 + ") {\n a: (" + v2t3 + ") 127,\n b: (" +
v2t4 + ") 255\n },\n b: (" + v2t5 + ") true\n}"
addDumpTest(v2, "("+v2t+") "+v2s+"\n")
addDumpTest(pv2, "(*"+v2t+")("+v2Addr+")("+v2s+")\n")
addDumpTest(&pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n")
addDumpTest(nv2, "(*"+v2t+")(<nil>)\n")
// Struct that contains custom type with Stringer pointer interface via both
// exported and unexported fields.
type s3 struct {
s pstringer
S pstringer
}
v3 := s3{"test", "test2"}
nv3 := (*s3)(nil)
pv3 := &v3
v3Addr := fmt.Sprintf("%p", pv3)
pv3Addr := fmt.Sprintf("%p", &pv3)
v3t := "spew_test.s3"
v3t2 := "spew_test.pstringer"
v3s := "{\n s: (" + v3t2 + ") stringer test,\n S: (" + v3t2 +
") stringer test2\n}"
addDumpTest(v3, "("+v3t+") "+v3s+"\n")
addDumpTest(pv3, "(*"+v3t+")("+v3Addr+")("+v3s+")\n")
addDumpTest(&pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")("+v3s+")\n")
addDumpTest(nv3, "(*"+v3t+")(<nil>)\n")
// Struct that contains embedded struct and field to same struct.
e := embed{"embedstr"}
v4 := embedwrap{embed: &e, e: &e}
nv4 := (*embedwrap)(nil)
pv4 := &v4
eAddr := fmt.Sprintf("%p", &e)
v4Addr := fmt.Sprintf("%p", pv4)
pv4Addr := fmt.Sprintf("%p", &pv4)
v4t := "spew_test.embedwrap"
v4t2 := "spew_test.embed"
v4t3 := "string"
v4s := "{\n embed: (*" + v4t2 + ")(" + eAddr + ")({\n a: (" + v4t3 +
") \"embedstr\"\n }),\n e: (*" + v4t2 + ")(" + eAddr + ")({\n" +
" a: (" + v4t3 + ") \"embedstr\"\n })\n}"
addDumpTest(v4, "("+v4t+") "+v4s+"\n")
addDumpTest(pv4, "(*"+v4t+")("+v4Addr+")("+v4s+")\n")
addDumpTest(&pv4, "(**"+v4t+")("+pv4Addr+"->"+v4Addr+")("+v4s+")\n")
addDumpTest(nv4, "(*"+v4t+")(<nil>)\n")
}
func addUintptrDumpTests() {
// Null pointer.
v := uintptr(0)
pv := &v
vAddr := fmt.Sprintf("%p", pv)
pvAddr := fmt.Sprintf("%p", &pv)
vt := "uintptr"
vs := "<nil>"
addDumpTest(v, "("+vt+") "+vs+"\n")
addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs+")\n")
addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n")
// Address of real variable.
i := 1
v2 := uintptr(unsafe.Pointer(&i))
nv2 := (*uintptr)(nil)
pv2 := &v2
v2Addr := fmt.Sprintf("%p", pv2)
pv2Addr := fmt.Sprintf("%p", &pv2)
v2t := "uintptr"
v2s := fmt.Sprintf("%p", &i)
addDumpTest(v2, "("+v2t+") "+v2s+"\n")
addDumpTest(pv2, "(*"+v2t+")("+v2Addr+")("+v2s+")\n")
addDumpTest(&pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n")
addDumpTest(nv2, "(*"+v2t+")(<nil>)\n")
}
func addUnsafePointerDumpTests() {
// Null pointer.
v := unsafe.Pointer(uintptr(0))
nv := (*unsafe.Pointer)(nil)
pv := &v
vAddr := fmt.Sprintf("%p", pv)
pvAddr := fmt.Sprintf("%p", &pv)
vt := "unsafe.Pointer"
vs := "<nil>"
addDumpTest(v, "("+vt+") "+vs+"\n")
addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs+")\n")
addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n")
addDumpTest(nv, "(*"+vt+")(<nil>)\n")
// Address of real variable.
i := 1
v2 := unsafe.Pointer(&i)
pv2 := &v2
v2Addr := fmt.Sprintf("%p", pv2)
pv2Addr := fmt.Sprintf("%p", &pv2)
v2t := "unsafe.Pointer"
v2s := fmt.Sprintf("%p", &i)
addDumpTest(v2, "("+v2t+") "+v2s+"\n")
addDumpTest(pv2, "(*"+v2t+")("+v2Addr+")("+v2s+")\n")
addDumpTest(&pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n")
addDumpTest(nv, "(*"+vt+")(<nil>)\n")
}
func addChanDumpTests() {
// Nil channel.
var v chan int
pv := &v
nv := (*chan int)(nil)
vAddr := fmt.Sprintf("%p", pv)
pvAddr := fmt.Sprintf("%p", &pv)
vt := "chan int"
vs := "<nil>"
addDumpTest(v, "("+vt+") "+vs+"\n")
addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs+")\n")
addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n")
addDumpTest(nv, "(*"+vt+")(<nil>)\n")
// Real channel.
v2 := make(chan int)
pv2 := &v2
v2Addr := fmt.Sprintf("%p", pv2)
pv2Addr := fmt.Sprintf("%p", &pv2)
v2t := "chan int"
v2s := fmt.Sprintf("%p", v2)
addDumpTest(v2, "("+v2t+") "+v2s+"\n")
addDumpTest(pv2, "(*"+v2t+")("+v2Addr+")("+v2s+")\n")
addDumpTest(&pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n")
}
func addFuncDumpTests() {
// Function with no params and no returns.
v := addIntDumpTests
nv := (*func())(nil)
pv := &v
vAddr := fmt.Sprintf("%p", pv)
pvAddr := fmt.Sprintf("%p", &pv)
vt := "func()"
vs := fmt.Sprintf("%p", v)
addDumpTest(v, "("+vt+") "+vs+"\n")
addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs+")\n")
addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n")
addDumpTest(nv, "(*"+vt+")(<nil>)\n")
// Function with param and no returns.
v2 := TestDump
nv2 := (*func(*testing.T))(nil)
pv2 := &v2
v2Addr := fmt.Sprintf("%p", pv2)
pv2Addr := fmt.Sprintf("%p", &pv2)
v2t := "func(*testing.T)"
v2s := fmt.Sprintf("%p", v2)
addDumpTest(v2, "("+v2t+") "+v2s+"\n")
addDumpTest(pv2, "(*"+v2t+")("+v2Addr+")("+v2s+")\n")
addDumpTest(&pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n")
addDumpTest(nv2, "(*"+v2t+")(<nil>)\n")
// Function with multiple params and multiple returns.
var v3 = func(i int, s string) (b bool, err error) {
return true, nil
}
nv3 := (*func(int, string) (bool, error))(nil)
pv3 := &v3
v3Addr := fmt.Sprintf("%p", pv3)
pv3Addr := fmt.Sprintf("%p", &pv3)
v3t := "func(int, string) (bool, error)"
v3s := fmt.Sprintf("%p", v3)
addDumpTest(v3, "("+v3t+") "+v3s+"\n")
addDumpTest(pv3, "(*"+v3t+")("+v3Addr+")("+v3s+")\n")
addDumpTest(&pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")("+v3s+")\n")
addDumpTest(nv3, "(*"+v3t+")(<nil>)\n")
}
func addCircularDumpTests() {
// Struct that is circular through self referencing.
type circular struct {
c *circular
}
v := circular{nil}
v.c = &v
pv := &v
vAddr := fmt.Sprintf("%p", pv)
pvAddr := fmt.Sprintf("%p", &pv)
vt := "spew_test.circular"
vs := "{\n c: (*" + vt + ")(" + vAddr + ")({\n c: (*" + vt + ")(" +
vAddr + ")(<already shown>)\n })\n}"
vs2 := "{\n c: (*" + vt + ")(" + vAddr + ")(<already shown>)\n}"
addDumpTest(v, "("+vt+") "+vs+"\n")
addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs2+")\n")
addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs2+")\n")
// Structs that are circular through cross referencing.
v2 := xref1{nil}
ts2 := xref2{&v2}
v2.ps2 = &ts2
pv2 := &v2
ts2Addr := fmt.Sprintf("%p", &ts2)
v2Addr := fmt.Sprintf("%p", pv2)
pv2Addr := fmt.Sprintf("%p", &pv2)
v2t := "spew_test.xref1"
v2t2 := "spew_test.xref2"
v2s := "{\n ps2: (*" + v2t2 + ")(" + ts2Addr + ")({\n ps1: (*" + v2t +
")(" + v2Addr + ")({\n ps2: (*" + v2t2 + ")(" + ts2Addr +
")(<already shown>)\n })\n })\n}"
v2s2 := "{\n ps2: (*" + v2t2 + ")(" + ts2Addr + ")({\n ps1: (*" + v2t +
")(" + v2Addr + ")(<already shown>)\n })\n}"
addDumpTest(v2, "("+v2t+") "+v2s+"\n")
addDumpTest(pv2, "(*"+v2t+")("+v2Addr+")("+v2s2+")\n")
addDumpTest(&pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s2+")\n")
// Structs that are indirectly circular.
v3 := indirCir1{nil}
tic2 := indirCir2{nil}
tic3 := indirCir3{&v3}
tic2.ps3 = &tic3
v3.ps2 = &tic2
pv3 := &v3
tic2Addr := fmt.Sprintf("%p", &tic2)
tic3Addr := fmt.Sprintf("%p", &tic3)
v3Addr := fmt.Sprintf("%p", pv3)
pv3Addr := fmt.Sprintf("%p", &pv3)
v3t := "spew_test.indirCir1"
v3t2 := "spew_test.indirCir2"
v3t3 := "spew_test.indirCir3"
v3s := "{\n ps2: (*" + v3t2 + ")(" + tic2Addr + ")({\n ps3: (*" + v3t3 +
")(" + tic3Addr + ")({\n ps1: (*" + v3t + ")(" + v3Addr +
")({\n ps2: (*" + v3t2 + ")(" + tic2Addr +
")(<already shown>)\n })\n })\n })\n}"
v3s2 := "{\n ps2: (*" + v3t2 + ")(" + tic2Addr + ")({\n ps3: (*" + v3t3 +
")(" + tic3Addr + ")({\n ps1: (*" + v3t + ")(" + v3Addr +
")(<already shown>)\n })\n })\n}"
addDumpTest(v3, "("+v3t+") "+v3s+"\n")
addDumpTest(pv3, "(*"+v3t+")("+v3Addr+")("+v3s2+")\n")
addDumpTest(&pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")("+v3s2+")\n")
}
func addPanicDumpTests() {
// Type that panics in its Stringer interface.
v := panicer(127)
nv := (*panicer)(nil)
pv := &v
vAddr := fmt.Sprintf("%p", pv)
pvAddr := fmt.Sprintf("%p", &pv)
vt := "spew_test.panicer"
vs := "(PANIC=test panic)127"
addDumpTest(v, "("+vt+") "+vs+"\n")
addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs+")\n")
addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n")
addDumpTest(nv, "(*"+vt+")(<nil>)\n")
}
func addErrorDumpTests() {
// Type that has a custom Error interface.
v := customError(127)
nv := (*customError)(nil)
pv := &v
vAddr := fmt.Sprintf("%p", pv)
pvAddr := fmt.Sprintf("%p", &pv)
vt := "spew_test.customError"
vs := "error: 127"
addDumpTest(v, "("+vt+") "+vs+"\n")
addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs+")\n")
addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n")
addDumpTest(nv, "(*"+vt+")(<nil>)\n")
}
// TestDump executes all of the tests described by dumpTests.
func TestDump(t *testing.T) {
// Setup tests.
addIntDumpTests()
addUintDumpTests()
addBoolDumpTests()
addFloatDumpTests()
addComplexDumpTests()
addArrayDumpTests()
addSliceDumpTests()
addStringDumpTests()
addInterfaceDumpTests()
addMapDumpTests()
addStructDumpTests()
addUintptrDumpTests()
addUnsafePointerDumpTests()
addChanDumpTests()
addFuncDumpTests()
addCircularDumpTests()
addPanicDumpTests()
addErrorDumpTests()
addCgoDumpTests()
t.Logf("Running %d tests", len(dumpTests))
for i, test := range dumpTests {
buf := new(bytes.Buffer)
spew.Fdump(buf, test.in)
s := buf.String()
if testFailed(s, test.wants) {
t.Errorf("Dump #%d\n got: %s %s", i, s, stringizeWants(test.wants))
continue
}
}
}
func TestDumpSortedKeys(t *testing.T) {
cfg := spew.ConfigState{SortKeys: true}
s := cfg.Sdump(map[int]string{1: "1", 3: "3", 2: "2"})
expected := `(map[int]string) {
(int) 1: (string) "1",
(int) 2: (string) "2",
(int) 3: (string) "3"
}
`
if s != expected {
t.Errorf("Sorted keys mismatch:\n %v %v", s, expected)
}
}

View file

@ -1,82 +0,0 @@
// Copyright (c) 2013 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 both cgo is supported and "-tags testcgo" is added to the go test
// command line. This means the cgo tests are only added (and hence run) when
// specifially requested. This configuration is used because spew itself
// does not require cgo to run even though it does handle certain cgo types
// specially. Rather than forcing all clients to require cgo and an external
// C compiler just to run the tests, this scheme makes them optional.
// +build cgo,testcgo
package spew_test
import (
"fmt"
"github.com/davecgh/go-spew/spew/testdata"
)
func addCgoDumpTests() {
// C char pointer.
v := testdata.GetCgoCharPointer()
nv := testdata.GetCgoNullCharPointer()
pv := &v
vcAddr := fmt.Sprintf("%p", v)
vAddr := fmt.Sprintf("%p", pv)
pvAddr := fmt.Sprintf("%p", &pv)
vt := "*testdata._Ctype_char"
vs := "116"
addDumpTest(v, "("+vt+")("+vcAddr+")("+vs+")\n")
addDumpTest(pv, "(*"+vt+")("+vAddr+"->"+vcAddr+")("+vs+")\n")
addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+"->"+vcAddr+")("+vs+")\n")
addDumpTest(nv, "("+vt+")(<nil>)\n")
// C char array.
v2 := testdata.GetCgoCharArray()
v2t := "[6]testdata._Ctype_char"
v2s := "{\n 00000000 74 65 73 74 32 00 " +
" |test2.|\n}"
addDumpTest(v2, "("+v2t+") "+v2s+"\n")
// C unsigned char array.
v3 := testdata.GetCgoUnsignedCharArray()
v3t := "[6]testdata._Ctype_unsignedchar"
v3s := "{\n 00000000 74 65 73 74 33 00 " +
" |test3.|\n}"
addDumpTest(v3, "("+v3t+") "+v3s+"\n")
// C signed char array.
v4 := testdata.GetCgoSignedCharArray()
v4t := "[6]testdata._Ctype_schar"
v4t2 := "testdata._Ctype_schar"
v4s := "{\n (" + v4t2 + ") 116,\n (" + v4t2 + ") 101,\n (" + v4t2 +
") 115,\n (" + v4t2 + ") 116,\n (" + v4t2 + ") 52,\n (" + v4t2 +
") 0\n}"
addDumpTest(v4, "("+v4t+") "+v4s+"\n")
// C uint8_t array.
v5 := testdata.GetCgoUint8tArray()
v5t := "[6]testdata._Ctype_uint8_t"
v5s := "{\n 00000000 74 65 73 74 35 00 " +
" |test5.|\n}"
addDumpTest(v5, "("+v5t+") "+v5s+"\n")
// C typedefed unsigned char array.
v6 := testdata.GetCgoTypdefedUnsignedCharArray()
v6t := "[6]testdata._Ctype_custom_uchar_t"
v6s := "{\n 00000000 74 65 73 74 36 00 " +
" |test6.|\n}"
addDumpTest(v6, "("+v6t+") "+v6s+"\n")
}

View file

@ -1,26 +0,0 @@
// Copyright (c) 2013 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 either cgo is not supported or "-tags testcgo" is not added to the go
// test command line. This file intentionally does not setup any cgo tests in
// this scenario.
// +build !cgo !testcgo
package spew_test
func addCgoDumpTests() {
// Don't add any tests for cgo since this file is only compiled when
// there should not be any cgo tests.
}

View file

@ -1,230 +0,0 @@
/*
* Copyright (c) 2013 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_test
import (
"fmt"
"github.com/davecgh/go-spew/spew"
)
type Flag int
const (
flagOne Flag = iota
flagTwo
)
var flagStrings = map[Flag]string{
flagOne: "flagOne",
flagTwo: "flagTwo",
}
func (f Flag) String() string {
if s, ok := flagStrings[f]; ok {
return s
}
return fmt.Sprintf("Unknown flag (%d)", int(f))
}
type Bar struct {
flag Flag
data uintptr
}
type Foo struct {
unexportedField Bar
ExportedField map[interface{}]interface{}
}
// This example demonstrates how to use Dump to dump variables to stdout.
func ExampleDump() {
// The following package level declarations are assumed for this example:
/*
type Flag int
const (
flagOne Flag = iota
flagTwo
)
var flagStrings = map[Flag]string{
flagOne: "flagOne",
flagTwo: "flagTwo",
}
func (f Flag) String() string {
if s, ok := flagStrings[f]; ok {
return s
}
return fmt.Sprintf("Unknown flag (%d)", int(f))
}
type Bar struct {
flag Flag
data uintptr
}
type Foo struct {
unexportedField Bar
ExportedField map[interface{}]interface{}
}
*/
// Setup some sample data structures for the example.
bar := Bar{Flag(flagTwo), uintptr(0)}
s1 := Foo{bar, map[interface{}]interface{}{"one": true}}
f := Flag(5)
b := []byte{
0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
0x31, 0x32,
}
// Dump!
spew.Dump(s1, f, b)
// Output:
// (spew_test.Foo) {
// unexportedField: (spew_test.Bar) {
// flag: (spew_test.Flag) flagTwo,
// data: (uintptr) <nil>
// },
// ExportedField: (map[interface {}]interface {}) {
// (string) "one": (bool) true
// }
// }
// (spew_test.Flag) Unknown flag (5)
// ([]uint8) {
// 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|
// }
//
}
// This example demonstrates how to use Printf to display a variable with a
// format string and inline formatting.
func ExamplePrintf() {
// Create a double pointer to a uint 8.
ui8 := uint8(5)
pui8 := &ui8
ppui8 := &pui8
// Create a circular data type.
type circular struct {
ui8 uint8
c *circular
}
c := circular{ui8: 1}
c.c = &c
// Print!
spew.Printf("ppui8: %v\n", ppui8)
spew.Printf("circular: %v\n", c)
// Output:
// ppui8: <**>5
// circular: {1 <*>{1 <*><shown>}}
}
// This example demonstrates how to use a ConfigState.
func ExampleConfigState() {
// Modify the indent level of the ConfigState only. The global
// configuration is not modified.
scs := spew.ConfigState{Indent: "\t"}
// Output using the ConfigState instance.
v := map[string]int{"one": 1}
scs.Printf("v: %v\n", v)
scs.Dump(v)
// Output:
// v: map[one:1]
// (map[string]int) {
// (string) "one": (int) 1
// }
}
// This example demonstrates how to use ConfigState.Dump to dump variables to
// stdout
func ExampleConfigState_Dump() {
// See the top-level Dump example for details on the types used in this
// example.
// Create two ConfigState instances with different indentation.
scs := spew.ConfigState{Indent: "\t"}
scs2 := spew.ConfigState{Indent: " "}
// Setup some sample data structures for the example.
bar := Bar{Flag(flagTwo), uintptr(0)}
s1 := Foo{bar, map[interface{}]interface{}{"one": true}}
// Dump using the ConfigState instances.
scs.Dump(s1)
scs2.Dump(s1)
// Output:
// (spew_test.Foo) {
// unexportedField: (spew_test.Bar) {
// flag: (spew_test.Flag) flagTwo,
// data: (uintptr) <nil>
// },
// ExportedField: (map[interface {}]interface {}) {
// (string) "one": (bool) true
// }
// }
// (spew_test.Foo) {
// unexportedField: (spew_test.Bar) {
// flag: (spew_test.Flag) flagTwo,
// data: (uintptr) <nil>
// },
// ExportedField: (map[interface {}]interface {}) {
// (string) "one": (bool) true
// }
// }
//
}
// This example demonstrates how to use ConfigState.Printf to display a variable
// with a format string and inline formatting.
func ExampleConfigState_Printf() {
// See the top-level Dump example for details on the types used in this
// example.
// Create two ConfigState instances and modify the method handling of the
// first ConfigState only.
scs := spew.NewDefaultConfig()
scs2 := spew.NewDefaultConfig()
scs.DisableMethods = true
// Alternatively
// scs := spew.ConfigState{Indent: " ", DisableMethods: true}
// scs2 := spew.ConfigState{Indent: " "}
// This is of type Flag which implements a Stringer and has raw value 1.
f := flagTwo
// Dump using the ConfigState instances.
scs.Printf("f: %v\n", f)
scs2.Printf("f: %v\n", f)
// Output:
// f: 1
// f: flagTwo
}

View file

@ -1,413 +0,0 @@
/*
* Copyright (c) 2013 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 == true:
f.fs.Write(nilAngleBytes)
case cycleFound == true:
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:
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)
}
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)
}

File diff suppressed because it is too large Load diff

View file

@ -1,162 +0,0 @@
/*
* Copyright (c) 2013 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.
*/
/*
This test file is part of the spew package rather than than the spew_test
package because it needs access to internals to properly test certain cases
which are not possible via the public interface since they should never happen.
*/
package spew
import (
"bytes"
"reflect"
"testing"
"unsafe"
)
// dummyFmtState implements a fake fmt.State to use for testing invalid
// reflect.Value handling. This is necessary because the fmt package catches
// invalid values before invoking the formatter on them.
type dummyFmtState struct {
bytes.Buffer
}
func (dfs *dummyFmtState) Flag(f int) bool {
if f == int('+') {
return true
}
return false
}
func (dfs *dummyFmtState) Precision() (int, bool) {
return 0, false
}
func (dfs *dummyFmtState) Width() (int, bool) {
return 0, false
}
// TestInvalidReflectValue ensures the dump and formatter code handles an
// invalid reflect value properly. This needs access to internal state since it
// should never happen in real code and therefore can't be tested via the public
// API.
func TestInvalidReflectValue(t *testing.T) {
i := 1
// Dump invalid reflect value.
v := new(reflect.Value)
buf := new(bytes.Buffer)
d := dumpState{w: buf, cs: &Config}
d.dump(*v)
s := buf.String()
want := "<invalid>"
if s != want {
t.Errorf("InvalidReflectValue #%d\n got: %s want: %s", i, s, want)
}
i++
// Formatter invalid reflect value.
buf2 := new(dummyFmtState)
f := formatState{value: *v, cs: &Config, fs: buf2}
f.format(*v)
s = buf2.String()
want = "<invalid>"
if s != want {
t.Errorf("InvalidReflectValue #%d got: %s want: %s", i, s, want)
}
}
// flagRO, flagKindShift and flagKindWidth indicate various bit flags that the
// reflect package uses internally to track kind and state information.
const flagRO = 1 << 0
const flagKindShift = 4
const flagKindWidth = 5
// changeKind uses unsafe to intentionally change the kind of a reflect.Value to
// the maximum kind value which does not exist. This is needed to test the
// fallback code which punts to the standard fmt library for new types that
// might get added to the language.
func changeKind(v *reflect.Value, readOnly bool) {
rvf := (*uintptr)(unsafe.Pointer(uintptr(unsafe.Pointer(v)) + offsetFlag))
*rvf = *rvf | ((1<<flagKindWidth - 1) << flagKindShift)
if readOnly {
*rvf |= flagRO
} else {
*rvf &= ^uintptr(flagRO)
}
}
// TestAddedReflectValue tests functionaly of the dump and formatter code which
// falls back to the standard fmt library for new types that might get added to
// the language.
func TestAddedReflectValue(t *testing.T) {
i := 1
// Dump using a reflect.Value that is exported.
v := reflect.ValueOf(int8(5))
changeKind(&v, false)
buf := new(bytes.Buffer)
d := dumpState{w: buf, cs: &Config}
d.dump(v)
s := buf.String()
want := "(int8) 5"
if s != want {
t.Errorf("TestAddedReflectValue #%d\n got: %s want: %s", i, s, want)
}
i++
// Dump using a reflect.Value that is not exported.
changeKind(&v, true)
buf.Reset()
d.dump(v)
s = buf.String()
want = "(int8) <int8 Value>"
if s != want {
t.Errorf("TestAddedReflectValue #%d\n got: %s want: %s", i, s, want)
}
i++
// Formatter using a reflect.Value that is exported.
changeKind(&v, false)
buf2 := new(dummyFmtState)
f := formatState{value: v, cs: &Config, fs: buf2}
f.format(v)
s = buf2.String()
want = "5"
if s != want {
t.Errorf("TestAddedReflectValue #%d got: %s want: %s", i, s, want)
}
i++
// Formatter using a reflect.Value that is not exported.
changeKind(&v, true)
buf2.Reset()
f = formatState{value: v, cs: &Config, fs: buf2}
f.format(v)
s = buf2.String()
want = "<int8 Value>"
if s != want {
t.Errorf("TestAddedReflectValue #%d got: %s want: %s", i, s, want)
}
}
// SortValues makes the internal sortValues function available to the test
// package.
func SortValues(values []reflect.Value) {
sortValues(values)
}

View file

@ -1,148 +0,0 @@
/*
* Copyright (c) 2013 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
}

View file

@ -1,308 +0,0 @@
/*
* Copyright (c) 2013 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_test
import (
"bytes"
"fmt"
"github.com/davecgh/go-spew/spew"
"io/ioutil"
"os"
"testing"
)
// spewFunc is used to identify which public function of the spew package or
// ConfigState a test applies to.
type spewFunc int
const (
fCSFdump spewFunc = iota
fCSFprint
fCSFprintf
fCSFprintln
fCSPrint
fCSPrintln
fCSSdump
fCSSprint
fCSSprintf
fCSSprintln
fCSErrorf
fCSNewFormatter
fErrorf
fFprint
fFprintln
fPrint
fPrintln
fSdump
fSprint
fSprintf
fSprintln
)
// Map of spewFunc values to names for pretty printing.
var spewFuncStrings = map[spewFunc]string{
fCSFdump: "ConfigState.Fdump",
fCSFprint: "ConfigState.Fprint",
fCSFprintf: "ConfigState.Fprintf",
fCSFprintln: "ConfigState.Fprintln",
fCSSdump: "ConfigState.Sdump",
fCSPrint: "ConfigState.Print",
fCSPrintln: "ConfigState.Println",
fCSSprint: "ConfigState.Sprint",
fCSSprintf: "ConfigState.Sprintf",
fCSSprintln: "ConfigState.Sprintln",
fCSErrorf: "ConfigState.Errorf",
fCSNewFormatter: "ConfigState.NewFormatter",
fErrorf: "spew.Errorf",
fFprint: "spew.Fprint",
fFprintln: "spew.Fprintln",
fPrint: "spew.Print",
fPrintln: "spew.Println",
fSdump: "spew.Sdump",
fSprint: "spew.Sprint",
fSprintf: "spew.Sprintf",
fSprintln: "spew.Sprintln",
}
func (f spewFunc) String() string {
if s, ok := spewFuncStrings[f]; ok {
return s
}
return fmt.Sprintf("Unknown spewFunc (%d)", int(f))
}
// spewTest is used to describe a test to be performed against the public
// functions of the spew package or ConfigState.
type spewTest struct {
cs *spew.ConfigState
f spewFunc
format string
in interface{}
want string
}
// spewTests houses the tests to be performed against the public functions of
// the spew package and ConfigState.
//
// These tests are only intended to ensure the public functions are exercised
// and are intentionally not exhaustive of types. The exhaustive type
// tests are handled in the dump and format tests.
var spewTests []spewTest
// redirStdout is a helper function to return the standard output from f as a
// byte slice.
func redirStdout(f func()) ([]byte, error) {
tempFile, err := ioutil.TempFile("", "ss-test")
if err != nil {
return nil, err
}
fileName := tempFile.Name()
defer os.Remove(fileName) // Ignore error
origStdout := os.Stdout
os.Stdout = tempFile
f()
os.Stdout = origStdout
tempFile.Close()
return ioutil.ReadFile(fileName)
}
func initSpewTests() {
// Config states with various settings.
scsDefault := spew.NewDefaultConfig()
scsNoMethods := &spew.ConfigState{Indent: " ", DisableMethods: true}
scsNoPmethods := &spew.ConfigState{Indent: " ", DisablePointerMethods: true}
scsMaxDepth := &spew.ConfigState{Indent: " ", MaxDepth: 1}
scsContinue := &spew.ConfigState{Indent: " ", ContinueOnMethod: true}
// Variables for tests on types which implement Stringer interface with and
// without a pointer receiver.
ts := stringer("test")
tps := pstringer("test")
// depthTester is used to test max depth handling for structs, array, slices
// and maps.
type depthTester struct {
ic indirCir1
arr [1]string
slice []string
m map[string]int
}
dt := depthTester{indirCir1{nil}, [1]string{"arr"}, []string{"slice"},
map[string]int{"one": 1}}
// Variable for tests on types which implement error interface.
te := customError(10)
spewTests = []spewTest{
{scsDefault, fCSFdump, "", int8(127), "(int8) 127\n"},
{scsDefault, fCSFprint, "", int16(32767), "32767"},
{scsDefault, fCSFprintf, "%v", int32(2147483647), "2147483647"},
{scsDefault, fCSFprintln, "", int(2147483647), "2147483647\n"},
{scsDefault, fCSPrint, "", int64(9223372036854775807), "9223372036854775807"},
{scsDefault, fCSPrintln, "", uint8(255), "255\n"},
{scsDefault, fCSSdump, "", uint8(64), "(uint8) 64\n"},
{scsDefault, fCSSprint, "", complex(1, 2), "(1+2i)"},
{scsDefault, fCSSprintf, "%v", complex(float32(3), 4), "(3+4i)"},
{scsDefault, fCSSprintln, "", complex(float64(5), 6), "(5+6i)\n"},
{scsDefault, fCSErrorf, "%#v", uint16(65535), "(uint16)65535"},
{scsDefault, fCSNewFormatter, "%v", uint32(4294967295), "4294967295"},
{scsDefault, fErrorf, "%v", uint64(18446744073709551615), "18446744073709551615"},
{scsDefault, fFprint, "", float32(3.14), "3.14"},
{scsDefault, fFprintln, "", float64(6.28), "6.28\n"},
{scsDefault, fPrint, "", true, "true"},
{scsDefault, fPrintln, "", false, "false\n"},
{scsDefault, fSdump, "", complex(-10, -20), "(complex128) (-10-20i)\n"},
{scsDefault, fSprint, "", complex(-1, -2), "(-1-2i)"},
{scsDefault, fSprintf, "%v", complex(float32(-3), -4), "(-3-4i)"},
{scsDefault, fSprintln, "", complex(float64(-5), -6), "(-5-6i)\n"},
{scsNoMethods, fCSFprint, "", ts, "test"},
{scsNoMethods, fCSFprint, "", &ts, "<*>test"},
{scsNoMethods, fCSFprint, "", tps, "test"},
{scsNoMethods, fCSFprint, "", &tps, "<*>test"},
{scsNoPmethods, fCSFprint, "", ts, "stringer test"},
{scsNoPmethods, fCSFprint, "", &ts, "<*>stringer test"},
{scsNoPmethods, fCSFprint, "", tps, "test"},
{scsNoPmethods, fCSFprint, "", &tps, "<*>stringer test"},
{scsMaxDepth, fCSFprint, "", dt, "{{<max>} [<max>] [<max>] map[<max>]}"},
{scsMaxDepth, fCSFdump, "", dt, "(spew_test.depthTester) {\n" +
" ic: (spew_test.indirCir1) {\n <max depth reached>\n },\n" +
" arr: ([1]string) {\n <max depth reached>\n },\n" +
" slice: ([]string) {\n <max depth reached>\n },\n" +
" m: (map[string]int) {\n <max depth reached>\n }\n}\n"},
{scsContinue, fCSFprint, "", ts, "(stringer test) test"},
{scsContinue, fCSFdump, "", ts, "(spew_test.stringer) " +
"(stringer test) \"test\"\n"},
{scsContinue, fCSFprint, "", te, "(error: 10) 10"},
{scsContinue, fCSFdump, "", te, "(spew_test.customError) " +
"(error: 10) 10\n"},
}
}
// TestSpew executes all of the tests described by spewTests.
func TestSpew(t *testing.T) {
initSpewTests()
t.Logf("Running %d tests", len(spewTests))
for i, test := range spewTests {
buf := new(bytes.Buffer)
switch test.f {
case fCSFdump:
test.cs.Fdump(buf, test.in)
case fCSFprint:
test.cs.Fprint(buf, test.in)
case fCSFprintf:
test.cs.Fprintf(buf, test.format, test.in)
case fCSFprintln:
test.cs.Fprintln(buf, test.in)
case fCSPrint:
b, err := redirStdout(func() { test.cs.Print(test.in) })
if err != nil {
t.Errorf("%v #%d %v", test.f, i, err)
continue
}
buf.Write(b)
case fCSPrintln:
b, err := redirStdout(func() { test.cs.Println(test.in) })
if err != nil {
t.Errorf("%v #%d %v", test.f, i, err)
continue
}
buf.Write(b)
case fCSSdump:
str := test.cs.Sdump(test.in)
buf.WriteString(str)
case fCSSprint:
str := test.cs.Sprint(test.in)
buf.WriteString(str)
case fCSSprintf:
str := test.cs.Sprintf(test.format, test.in)
buf.WriteString(str)
case fCSSprintln:
str := test.cs.Sprintln(test.in)
buf.WriteString(str)
case fCSErrorf:
err := test.cs.Errorf(test.format, test.in)
buf.WriteString(err.Error())
case fCSNewFormatter:
fmt.Fprintf(buf, test.format, test.cs.NewFormatter(test.in))
case fErrorf:
err := spew.Errorf(test.format, test.in)
buf.WriteString(err.Error())
case fFprint:
spew.Fprint(buf, test.in)
case fFprintln:
spew.Fprintln(buf, test.in)
case fPrint:
b, err := redirStdout(func() { spew.Print(test.in) })
if err != nil {
t.Errorf("%v #%d %v", test.f, i, err)
continue
}
buf.Write(b)
case fPrintln:
b, err := redirStdout(func() { spew.Println(test.in) })
if err != nil {
t.Errorf("%v #%d %v", test.f, i, err)
continue
}
buf.Write(b)
case fSdump:
str := spew.Sdump(test.in)
buf.WriteString(str)
case fSprint:
str := spew.Sprint(test.in)
buf.WriteString(str)
case fSprintf:
str := spew.Sprintf(test.format, test.in)
buf.WriteString(str)
case fSprintln:
str := spew.Sprintln(test.in)
buf.WriteString(str)
default:
t.Errorf("%v #%d unrecognized function", test.f, i)
continue
}
s := buf.String()
if test.want != s {
t.Errorf("ConfigState #%d\n got: %s want: %s", i, s, test.want)
continue
}
}
}

View file

@ -1,81 +0,0 @@
// Copyright (c) 2013 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 both cgo is supported and "-tags testcgo" is added to the go test
// command line. This code should really only be in the dumpcgo_test.go file,
// but unfortunately Go will not allow cgo in test files, so this is a
// workaround to allow cgo types to be tested. This configuration is used
// because spew itself does not require cgo to run even though it does handle
// certain cgo types specially. Rather than forcing all clients to require cgo
// and an external C compiler just to run the tests, this scheme makes them
// optional.
// +build cgo,testcgo
package testdata
/*
#include <stdint.h>
typedef unsigned char custom_uchar_t;
char *ncp = 0;
char *cp = "test";
char ca[6] = {'t', 'e', 's', 't', '2', '\0'};
unsigned char uca[6] = {'t', 'e', 's', 't', '3', '\0'};
signed char sca[6] = {'t', 'e', 's', 't', '4', '\0'};
uint8_t ui8ta[6] = {'t', 'e', 's', 't', '5', '\0'};
custom_uchar_t tuca[6] = {'t', 'e', 's', 't', '6', '\0'};
*/
import "C"
// GetCgoNullCharPointer returns a null char pointer via cgo. This is only
// used for tests.
func GetCgoNullCharPointer() interface{} {
return C.ncp
}
// GetCgoCharPointer returns a char pointer via cgo. This is only used for
// tests.
func GetCgoCharPointer() interface{} {
return C.cp
}
// GetCgoCharArray returns a char array via cgo. This is only used for tests.
func GetCgoCharArray() interface{} {
return C.ca
}
// GetCgoUnsignedCharArray returns an unsigned char array via cgo. This is only
// used for tests.
func GetCgoUnsignedCharArray() interface{} {
return C.uca
}
// GetCgoSignedCharArray returns a signed char array via cgo. This is only used
// for tests.
func GetCgoSignedCharArray() interface{} {
return C.sca
}
// GetCgoUint8tArray returns a uint8_t array via cgo. This is only used for
// tests.
func GetCgoUint8tArray() interface{} {
return C.ui8ta
}
// GetCgoTypdefedUnsignedCharArray returns a typedefed unsigned char array via
// cgo. This is only used for tests.
func GetCgoTypdefedUnsignedCharArray() interface{} {
return C.tuca
}

View file

@ -0,0 +1,114 @@
package ioutils
import (
"bytes"
"io"
"sync"
)
type readCloserWrapper struct {
io.Reader
closer func() error
}
func (r *readCloserWrapper) Close() error {
return r.closer()
}
func NewReadCloserWrapper(r io.Reader, closer func() error) io.ReadCloser {
return &readCloserWrapper{
Reader: r,
closer: closer,
}
}
type readerErrWrapper struct {
reader io.Reader
closer func()
}
func (r *readerErrWrapper) Read(p []byte) (int, error) {
n, err := r.reader.Read(p)
if err != nil {
r.closer()
}
return n, err
}
func NewReaderErrWrapper(r io.Reader, closer func()) io.Reader {
return &readerErrWrapper{
reader: r,
closer: closer,
}
}
type bufReader struct {
sync.Mutex
buf *bytes.Buffer
reader io.Reader
err error
wait sync.Cond
drainBuf []byte
}
func NewBufReader(r io.Reader) *bufReader {
reader := &bufReader{
buf: &bytes.Buffer{},
drainBuf: make([]byte, 1024),
reader: r,
}
reader.wait.L = &reader.Mutex
go reader.drain()
return reader
}
func NewBufReaderWithDrainbufAndBuffer(r io.Reader, drainBuffer []byte, buffer *bytes.Buffer) *bufReader {
reader := &bufReader{
buf: buffer,
drainBuf: drainBuffer,
reader: r,
}
reader.wait.L = &reader.Mutex
go reader.drain()
return reader
}
func (r *bufReader) drain() {
for {
n, err := r.reader.Read(r.drainBuf)
r.Lock()
if err != nil {
r.err = err
} else {
r.buf.Write(r.drainBuf[0:n])
}
r.wait.Signal()
r.Unlock()
if err != nil {
break
}
}
}
func (r *bufReader) Read(p []byte) (n int, err error) {
r.Lock()
defer r.Unlock()
for {
n, err = r.buf.Read(p)
if n > 0 {
return n, err
}
if r.err != nil {
return 0, r.err
}
r.wait.Wait()
}
}
func (r *bufReader) Close() error {
closer, ok := r.reader.(io.ReadCloser)
if !ok {
return nil
}
return closer.Close()
}

View file

@ -0,0 +1,34 @@
package ioutils
import (
"bytes"
"io"
"io/ioutil"
"testing"
)
func TestBufReader(t *testing.T) {
reader, writer := io.Pipe()
bufreader := NewBufReader(reader)
// Write everything down to a Pipe
// Usually, a pipe should block but because of the buffered reader,
// the writes will go through
done := make(chan bool)
go func() {
writer.Write([]byte("hello world"))
writer.Close()
done <- true
}()
// Drain the reader *after* everything has been written, just to verify
// it is indeed buffering
<-done
output, err := ioutil.ReadAll(bufreader)
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(output, []byte("hello world")) {
t.Error(string(output))
}
}

View file

@ -0,0 +1,39 @@
package ioutils
import "io"
type NopWriter struct{}
func (*NopWriter) Write(buf []byte) (int, error) {
return len(buf), nil
}
type nopWriteCloser struct {
io.Writer
}
func (w *nopWriteCloser) Close() error { return nil }
func NopWriteCloser(w io.Writer) io.WriteCloser {
return &nopWriteCloser{w}
}
type NopFlusher struct{}
func (f *NopFlusher) Flush() {}
type writeCloserWrapper struct {
io.Writer
closer func() error
}
func (r *writeCloserWrapper) Close() error {
return r.closer()
}
func NewWriteCloserWrapper(r io.Writer, closer func() error) io.WriteCloser {
return &writeCloserWrapper{
Writer: r,
closer: closer,
}
}

View file

@ -0,0 +1,83 @@
package log
import (
"fmt"
"io"
"os"
"runtime"
"strings"
)
type priority int
const (
errorFormat = "[%s] %s:%d %s\n"
logFormat = "[%s] %s\n"
fatal priority = iota
error
info
debug
)
// A common interface to access the Fatal method of
// both testing.B and testing.T.
type Fataler interface {
Fatal(args ...interface{})
}
func (p priority) String() string {
switch p {
case fatal:
return "fatal"
case error:
return "error"
case info:
return "info"
case debug:
return "debug"
}
return ""
}
// Debug function, if the debug flag is set, then display. Do nothing otherwise
// If Docker is in damon mode, also send the debug info on the socket
func Debugf(format string, a ...interface{}) {
if os.Getenv("DEBUG") != "" {
logf(os.Stderr, debug, format, a...)
}
}
func Infof(format string, a ...interface{}) {
logf(os.Stdout, info, format, a...)
}
func Errorf(format string, a ...interface{}) {
logf(os.Stderr, error, format, a...)
}
func Fatalf(format string, a ...interface{}) {
logf(os.Stderr, fatal, format, a...)
os.Exit(1)
}
func logf(stream io.Writer, level priority, format string, a ...interface{}) {
var prefix string
if level <= error || level == debug {
// Retrieve the stack infos
_, file, line, ok := runtime.Caller(2)
if !ok {
file = "<unknown>"
line = -1
} else {
file = file[strings.LastIndex(file, "/")+1:]
}
prefix = fmt.Sprintf(errorFormat, level.String(), file, line, format)
} else {
prefix = fmt.Sprintf(logFormat, level.String(), format)
}
fmt.Fprintf(stream, prefix, a...)
}

View file

@ -0,0 +1,37 @@
package log
import (
"bytes"
"regexp"
"testing"
)
func TestLogFatalf(t *testing.T) {
var output *bytes.Buffer
tests := []struct {
Level priority
Format string
Values []interface{}
ExpectedPattern string
}{
{fatal, "%d + %d = %d", []interface{}{1, 1, 2}, "\\[fatal\\] testing.go:\\d+ 1 \\+ 1 = 2"},
{error, "%d + %d = %d", []interface{}{1, 1, 2}, "\\[error\\] testing.go:\\d+ 1 \\+ 1 = 2"},
{info, "%d + %d = %d", []interface{}{1, 1, 2}, "\\[info\\] 1 \\+ 1 = 2"},
{debug, "%d + %d = %d", []interface{}{1, 1, 2}, "\\[debug\\] testing.go:\\d+ 1 \\+ 1 = 2"},
}
for i, test := range tests {
output = &bytes.Buffer{}
logf(output, test.Level, test.Format, test.Values...)
expected := regexp.MustCompile(test.ExpectedPattern)
if !expected.MatchString(output.String()) {
t.Errorf("[%d] Log output does not match expected pattern:\n\tExpected: %s\n\tOutput: %s",
i,
expected.String(),
output.String())
}
}
}

View file

@ -0,0 +1,111 @@
// +build go1.3
// Package pools provides a collection of pools which provide various
// data types with buffers. These can be used to lower the number of
// memory allocations and reuse buffers.
//
// New pools should be added to this package to allow them to be
// shared across packages.
//
// Utility functions which operate on pools should be added to this
// package to allow them to be reused.
package pools
import (
"bufio"
"io"
"sync"
"github.com/docker/docker/pkg/ioutils"
)
var (
// Pool which returns bufio.Reader with a 32K buffer
BufioReader32KPool *BufioReaderPool
// Pool which returns bufio.Writer with a 32K buffer
BufioWriter32KPool *BufioWriterPool
)
const buffer32K = 32 * 1024
type BufioReaderPool struct {
pool sync.Pool
}
func init() {
BufioReader32KPool = newBufioReaderPoolWithSize(buffer32K)
BufioWriter32KPool = newBufioWriterPoolWithSize(buffer32K)
}
// newBufioReaderPoolWithSize is unexported because new pools should be
// added here to be shared where required.
func newBufioReaderPoolWithSize(size int) *BufioReaderPool {
pool := sync.Pool{
New: func() interface{} { return bufio.NewReaderSize(nil, size) },
}
return &BufioReaderPool{pool: pool}
}
// Get returns a bufio.Reader which reads from r. The buffer size is that of the pool.
func (bufPool *BufioReaderPool) Get(r io.Reader) *bufio.Reader {
buf := bufPool.pool.Get().(*bufio.Reader)
buf.Reset(r)
return buf
}
// Put puts the bufio.Reader back into the pool.
func (bufPool *BufioReaderPool) Put(b *bufio.Reader) {
b.Reset(nil)
bufPool.pool.Put(b)
}
// NewReadCloserWrapper returns a wrapper which puts the bufio.Reader back
// into the pool and closes the reader if it's an io.ReadCloser.
func (bufPool *BufioReaderPool) NewReadCloserWrapper(buf *bufio.Reader, r io.Reader) io.ReadCloser {
return ioutils.NewReadCloserWrapper(r, func() error {
if readCloser, ok := r.(io.ReadCloser); ok {
readCloser.Close()
}
bufPool.Put(buf)
return nil
})
}
type BufioWriterPool struct {
pool sync.Pool
}
// newBufioWriterPoolWithSize is unexported because new pools should be
// added here to be shared where required.
func newBufioWriterPoolWithSize(size int) *BufioWriterPool {
pool := sync.Pool{
New: func() interface{} { return bufio.NewWriterSize(nil, size) },
}
return &BufioWriterPool{pool: pool}
}
// Get returns a bufio.Writer which writes to w. The buffer size is that of the pool.
func (bufPool *BufioWriterPool) Get(w io.Writer) *bufio.Writer {
buf := bufPool.pool.Get().(*bufio.Writer)
buf.Reset(w)
return buf
}
// Put puts the bufio.Writer back into the pool.
func (bufPool *BufioWriterPool) Put(b *bufio.Writer) {
b.Reset(nil)
bufPool.pool.Put(b)
}
// NewWriteCloserWrapper returns a wrapper which puts the bufio.Writer back
// into the pool and closes the writer if it's an io.Writecloser.
func (bufPool *BufioWriterPool) NewWriteCloserWrapper(buf *bufio.Writer, w io.Writer) io.WriteCloser {
return ioutils.NewWriteCloserWrapper(w, func() error {
buf.Flush()
if writeCloser, ok := w.(io.WriteCloser); ok {
writeCloser.Close()
}
bufPool.Put(buf)
return nil
})
}

View file

@ -0,0 +1,73 @@
// +build !go1.3
package pools
import (
"bufio"
"io"
"github.com/docker/docker/pkg/ioutils"
)
var (
BufioReader32KPool *BufioReaderPool
BufioWriter32KPool *BufioWriterPool
)
const buffer32K = 32 * 1024
type BufioReaderPool struct {
size int
}
func init() {
BufioReader32KPool = newBufioReaderPoolWithSize(buffer32K)
BufioWriter32KPool = newBufioWriterPoolWithSize(buffer32K)
}
func newBufioReaderPoolWithSize(size int) *BufioReaderPool {
return &BufioReaderPool{size: size}
}
func (bufPool *BufioReaderPool) Get(r io.Reader) *bufio.Reader {
return bufio.NewReaderSize(r, bufPool.size)
}
func (bufPool *BufioReaderPool) Put(b *bufio.Reader) {
b.Reset(nil)
}
func (bufPool *BufioReaderPool) NewReadCloserWrapper(buf *bufio.Reader, r io.Reader) io.ReadCloser {
return ioutils.NewReadCloserWrapper(r, func() error {
if readCloser, ok := r.(io.ReadCloser); ok {
return readCloser.Close()
}
return nil
})
}
type BufioWriterPool struct {
size int
}
func newBufioWriterPoolWithSize(size int) *BufioWriterPool {
return &BufioWriterPool{size: size}
}
func (bufPool *BufioWriterPool) Get(w io.Writer) *bufio.Writer {
return bufio.NewWriterSize(w, bufPool.size)
}
func (bufPool *BufioWriterPool) Put(b *bufio.Writer) {
b.Reset(nil)
}
func (bufPool *BufioWriterPool) NewWriteCloserWrapper(buf *bufio.Writer, w io.Writer) io.WriteCloser {
return ioutils.NewWriteCloserWrapper(w, func() error {
buf.Flush()
if writeCloser, ok := w.(io.WriteCloser); ok {
return writeCloser.Close()
}
return nil
})
}

View file

@ -0,0 +1,2 @@
Michael Crosby <michael@crosbymichael.com> (@crosbymichael)
Victor Vieux <vieux@docker.com> (@vieux)

View file

@ -0,0 +1,11 @@
package system
import "syscall"
func LUtimesNano(path string, ts []syscall.Timespec) error {
return ErrNotSupportedPlatform
}
func UtimesNano(path string, ts []syscall.Timespec) error {
return syscall.UtimesNano(path, ts)
}

View file

@ -0,0 +1,24 @@
package system
import (
"syscall"
"unsafe"
)
func LUtimesNano(path string, ts []syscall.Timespec) error {
var _path *byte
_path, err := syscall.BytePtrFromString(path)
if err != nil {
return err
}
if _, _, err := syscall.Syscall(syscall.SYS_LUTIMES, uintptr(unsafe.Pointer(_path)), uintptr(unsafe.Pointer(&ts[0])), 0); err != 0 && err != syscall.ENOSYS {
return err
}
return nil
}
func UtimesNano(path string, ts []syscall.Timespec) error {
return syscall.UtimesNano(path, ts)
}

View file

@ -24,8 +24,5 @@ func LUtimesNano(path string, ts []syscall.Timespec) error {
}
func UtimesNano(path string, ts []syscall.Timespec) error {
if err := syscall.UtimesNano(path, ts); err != nil {
return err
}
return nil
return syscall.UtimesNano(path, ts)
}

View file

@ -0,0 +1,64 @@
package system
import (
"io/ioutil"
"os"
"path/filepath"
"syscall"
"testing"
)
func prepareFiles(t *testing.T) (string, string, string) {
dir, err := ioutil.TempDir("", "docker-system-test")
if err != nil {
t.Fatal(err)
}
file := filepath.Join(dir, "exist")
if err := ioutil.WriteFile(file, []byte("hello"), 0644); err != nil {
t.Fatal(err)
}
invalid := filepath.Join(dir, "doesnt-exist")
symlink := filepath.Join(dir, "symlink")
if err := os.Symlink(file, symlink); err != nil {
t.Fatal(err)
}
return file, invalid, symlink
}
func TestLUtimesNano(t *testing.T) {
file, invalid, symlink := prepareFiles(t)
before, err := os.Stat(file)
if err != nil {
t.Fatal(err)
}
ts := []syscall.Timespec{{0, 0}, {0, 0}}
if err := LUtimesNano(symlink, ts); err != nil {
t.Fatal(err)
}
symlinkInfo, err := os.Lstat(symlink)
if err != nil {
t.Fatal(err)
}
if before.ModTime().Unix() == symlinkInfo.ModTime().Unix() {
t.Fatal("The modification time of the symlink should be different")
}
fileInfo, err := os.Stat(file)
if err != nil {
t.Fatal(err)
}
if before.ModTime().Unix() != fileInfo.ModTime().Unix() {
t.Fatal("The modification time of the file should be same")
}
if err := LUtimesNano(invalid, ts); err == nil {
t.Fatal("Doesn't return an error on a non-existing file")
}
}

View file

@ -1,4 +1,4 @@
// +build !linux
// +build !linux,!freebsd,!darwin
package system

View file

@ -0,0 +1 @@
Solomon Hykes <solomon@docker.com> (@shykes)

View file

@ -0,0 +1,103 @@
package term
import (
"errors"
"os"
"os/signal"
"syscall"
"unsafe"
)
var (
ErrInvalidState = errors.New("Invalid terminal state")
)
type State struct {
termios Termios
}
type Winsize struct {
Height uint16
Width uint16
x uint16
y uint16
}
func GetWinsize(fd uintptr) (*Winsize, error) {
ws := &Winsize{}
_, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(syscall.TIOCGWINSZ), uintptr(unsafe.Pointer(ws)))
// Skipp errno = 0
if err == 0 {
return ws, nil
}
return ws, err
}
func SetWinsize(fd uintptr, ws *Winsize) error {
_, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(syscall.TIOCSWINSZ), uintptr(unsafe.Pointer(ws)))
// Skipp errno = 0
if err == 0 {
return nil
}
return err
}
// IsTerminal returns true if the given file descriptor is a terminal.
func IsTerminal(fd uintptr) bool {
var termios Termios
_, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(getTermios), uintptr(unsafe.Pointer(&termios)))
return err == 0
}
// Restore restores the terminal connected to the given file descriptor to a
// previous state.
func RestoreTerminal(fd uintptr, state *State) error {
if state == nil {
return ErrInvalidState
}
_, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(setTermios), uintptr(unsafe.Pointer(&state.termios)))
if err != 0 {
return err
}
return nil
}
func SaveState(fd uintptr) (*State, error) {
var oldState State
if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, getTermios, uintptr(unsafe.Pointer(&oldState.termios))); err != 0 {
return nil, err
}
return &oldState, nil
}
func DisableEcho(fd uintptr, state *State) error {
newState := state.termios
newState.Lflag &^= syscall.ECHO
if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, setTermios, uintptr(unsafe.Pointer(&newState))); err != 0 {
return err
}
handleInterrupt(fd, state)
return nil
}
func SetRawTerminal(fd uintptr) (*State, error) {
oldState, err := MakeRaw(fd)
if err != nil {
return nil, err
}
handleInterrupt(fd, oldState)
return oldState, err
}
func handleInterrupt(fd uintptr, state *State) {
sigchan := make(chan os.Signal, 1)
signal.Notify(sigchan, os.Interrupt)
go func() {
_ = <-sigchan
RestoreTerminal(fd, state)
os.Exit(0)
}()
}

View file

@ -0,0 +1,65 @@
package term
import (
"syscall"
"unsafe"
)
const (
getTermios = syscall.TIOCGETA
setTermios = syscall.TIOCSETA
IGNBRK = syscall.IGNBRK
PARMRK = syscall.PARMRK
INLCR = syscall.INLCR
IGNCR = syscall.IGNCR
ECHONL = syscall.ECHONL
CSIZE = syscall.CSIZE
ICRNL = syscall.ICRNL
ISTRIP = syscall.ISTRIP
PARENB = syscall.PARENB
ECHO = syscall.ECHO
ICANON = syscall.ICANON
ISIG = syscall.ISIG
IXON = syscall.IXON
BRKINT = syscall.BRKINT
INPCK = syscall.INPCK
OPOST = syscall.OPOST
CS8 = syscall.CS8
IEXTEN = syscall.IEXTEN
)
type Termios struct {
Iflag uint64
Oflag uint64
Cflag uint64
Lflag uint64
Cc [20]byte
Ispeed uint64
Ospeed uint64
}
// MakeRaw put the terminal connected to the given file descriptor into raw
// mode and returns the previous state of the terminal so that it can be
// restored.
func MakeRaw(fd uintptr) (*State, error) {
var oldState State
if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(getTermios), uintptr(unsafe.Pointer(&oldState.termios))); err != 0 {
return nil, err
}
newState := oldState.termios
newState.Iflag &^= (IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON)
newState.Oflag &^= OPOST
newState.Lflag &^= (ECHO | ECHONL | ICANON | ISIG | IEXTEN)
newState.Cflag &^= (CSIZE | PARENB)
newState.Cflag |= CS8
newState.Cc[syscall.VMIN] = 1
newState.Cc[syscall.VTIME] = 0
if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(setTermios), uintptr(unsafe.Pointer(&newState))); err != 0 {
return nil, err
}
return &oldState, nil
}

View file

@ -0,0 +1,65 @@
package term
import (
"syscall"
"unsafe"
)
const (
getTermios = syscall.TIOCGETA
setTermios = syscall.TIOCSETA
IGNBRK = syscall.IGNBRK
PARMRK = syscall.PARMRK
INLCR = syscall.INLCR
IGNCR = syscall.IGNCR
ECHONL = syscall.ECHONL
CSIZE = syscall.CSIZE
ICRNL = syscall.ICRNL
ISTRIP = syscall.ISTRIP
PARENB = syscall.PARENB
ECHO = syscall.ECHO
ICANON = syscall.ICANON
ISIG = syscall.ISIG
IXON = syscall.IXON
BRKINT = syscall.BRKINT
INPCK = syscall.INPCK
OPOST = syscall.OPOST
CS8 = syscall.CS8
IEXTEN = syscall.IEXTEN
)
type Termios struct {
Iflag uint32
Oflag uint32
Cflag uint32
Lflag uint32
Cc [20]byte
Ispeed uint32
Ospeed uint32
}
// MakeRaw put the terminal connected to the given file descriptor into raw
// mode and returns the previous state of the terminal so that it can be
// restored.
func MakeRaw(fd uintptr) (*State, error) {
var oldState State
if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(getTermios), uintptr(unsafe.Pointer(&oldState.termios))); err != 0 {
return nil, err
}
newState := oldState.termios
newState.Iflag &^= (IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON)
newState.Oflag &^= OPOST
newState.Lflag &^= (ECHO | ECHONL | ICANON | ISIG | IEXTEN)
newState.Cflag &^= (CSIZE | PARENB)
newState.Cflag |= CS8
newState.Cc[syscall.VMIN] = 1
newState.Cc[syscall.VTIME] = 0
if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, uintptr(setTermios), uintptr(unsafe.Pointer(&newState))); err != 0 {
return nil, err
}
return &oldState, nil
}

View file

@ -0,0 +1,44 @@
package term
import (
"syscall"
"unsafe"
)
const (
getTermios = syscall.TCGETS
setTermios = syscall.TCSETS
)
type Termios struct {
Iflag uint32
Oflag uint32
Cflag uint32
Lflag uint32
Cc [20]byte
Ispeed uint32
Ospeed uint32
}
// MakeRaw put the terminal connected to the given file descriptor into raw
// mode and returns the previous state of the terminal so that it can be
// restored.
func MakeRaw(fd uintptr) (*State, error) {
var oldState State
if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, getTermios, uintptr(unsafe.Pointer(&oldState.termios))); err != 0 {
return nil, err
}
newState := oldState.termios
newState.Iflag &^= (syscall.IGNBRK | syscall.BRKINT | syscall.PARMRK | syscall.ISTRIP | syscall.INLCR | syscall.IGNCR | syscall.ICRNL | syscall.IXON)
newState.Oflag &^= syscall.OPOST
newState.Lflag &^= (syscall.ECHO | syscall.ECHONL | syscall.ICANON | syscall.ISIG | syscall.IEXTEN)
newState.Cflag &^= (syscall.CSIZE | syscall.PARENB)
newState.Cflag |= syscall.CS8
if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, setTermios, uintptr(unsafe.Pointer(&newState))); err != 0 {
return nil, err
}
return &oldState, nil
}

View file

@ -0,0 +1 @@
Cristian Staretu <cristian.staretu@gmail.com> (@unclejack)

View file

@ -0,0 +1,23 @@
package timeutils
import (
"errors"
"time"
)
const (
// Define our own version of RFC339Nano because we want one
// that pads the nano seconds part with zeros to ensure
// the timestamps are aligned in the logs.
RFC3339NanoFixed = "2006-01-02T15:04:05.000000000Z07:00"
JSONFormat = `"` + time.RFC3339Nano + `"`
)
func FastMarshalJSON(t time.Time) (string, error) {
if y := t.Year(); y < 0 || y >= 10000 {
// RFC 3339 is clear that years are 4 digits exactly.
// See golang.org/issue/4556#c15 for more discussion.
return "", errors.New("Time.MarshalJSON: year outside of range [0,9999]")
}
return t.Format(JSONFormat), nil
}

View file

@ -0,0 +1,2 @@
Michael Crosby <michael@crosbymichael.com> (@crosbymichael)
Victor Vieux <vieux@docker.com> (@vieux)

View file

@ -0,0 +1,31 @@
package units
import (
"fmt"
"time"
)
// HumanDuration returns a human-readable approximation of a duration
// (eg. "About a minute", "4 hours ago", etc.)
func HumanDuration(d time.Duration) string {
if seconds := int(d.Seconds()); seconds < 1 {
return "Less than a second"
} else if seconds < 60 {
return fmt.Sprintf("%d seconds", seconds)
} else if minutes := int(d.Minutes()); minutes == 1 {
return "About a minute"
} else if minutes < 60 {
return fmt.Sprintf("%d minutes", minutes)
} else if hours := int(d.Hours()); hours == 1 {
return "About an hour"
} else if hours < 48 {
return fmt.Sprintf("%d hours", hours)
} else if hours < 24*7*2 {
return fmt.Sprintf("%d days", hours/24)
} else if hours < 24*30*3 {
return fmt.Sprintf("%d weeks", hours/24/7)
} else if hours < 24*365*2 {
return fmt.Sprintf("%d months", hours/24/30)
}
return fmt.Sprintf("%f years", d.Hours()/24/365)
}

View file

@ -0,0 +1,46 @@
package units
import (
"testing"
"time"
)
func TestHumanDuration(t *testing.T) {
// Useful duration abstractions
day := 24 * time.Hour
week := 7 * day
month := 30 * day
year := 365 * day
assertEquals(t, "Less than a second", HumanDuration(450*time.Millisecond))
assertEquals(t, "47 seconds", HumanDuration(47*time.Second))
assertEquals(t, "About a minute", HumanDuration(1*time.Minute))
assertEquals(t, "3 minutes", HumanDuration(3*time.Minute))
assertEquals(t, "35 minutes", HumanDuration(35*time.Minute))
assertEquals(t, "35 minutes", HumanDuration(35*time.Minute+40*time.Second))
assertEquals(t, "About an hour", HumanDuration(1*time.Hour))
assertEquals(t, "About an hour", HumanDuration(1*time.Hour+45*time.Minute))
assertEquals(t, "3 hours", HumanDuration(3*time.Hour))
assertEquals(t, "3 hours", HumanDuration(3*time.Hour+59*time.Minute))
assertEquals(t, "4 hours", HumanDuration(3*time.Hour+60*time.Minute))
assertEquals(t, "24 hours", HumanDuration(24*time.Hour))
assertEquals(t, "36 hours", HumanDuration(1*day+12*time.Hour))
assertEquals(t, "2 days", HumanDuration(2*day))
assertEquals(t, "7 days", HumanDuration(7*day))
assertEquals(t, "13 days", HumanDuration(13*day+5*time.Hour))
assertEquals(t, "2 weeks", HumanDuration(2*week))
assertEquals(t, "2 weeks", HumanDuration(2*week+4*day))
assertEquals(t, "3 weeks", HumanDuration(3*week))
assertEquals(t, "4 weeks", HumanDuration(4*week))
assertEquals(t, "4 weeks", HumanDuration(4*week+3*day))
assertEquals(t, "4 weeks", HumanDuration(1*month))
assertEquals(t, "6 weeks", HumanDuration(1*month+2*week))
assertEquals(t, "8 weeks", HumanDuration(2*month))
assertEquals(t, "3 months", HumanDuration(3*month+1*week))
assertEquals(t, "5 months", HumanDuration(5*month+2*week))
assertEquals(t, "13 months", HumanDuration(13*month))
assertEquals(t, "23 months", HumanDuration(23*month))
assertEquals(t, "24 months", HumanDuration(24*month))
assertEquals(t, "2.010959 years", HumanDuration(24*month+2*week))
assertEquals(t, "3.164384 years", HumanDuration(3*year+2*month))
}

View file

@ -0,0 +1,81 @@
package units
import (
"fmt"
"regexp"
"strconv"
"strings"
)
// See: http://en.wikipedia.org/wiki/Binary_prefix
const (
// Decimal
KB = 1000
MB = 1000 * KB
GB = 1000 * MB
TB = 1000 * GB
PB = 1000 * TB
// Binary
KiB = 1024
MiB = 1024 * KiB
GiB = 1024 * MiB
TiB = 1024 * GiB
PiB = 1024 * TiB
)
type unitMap map[string]int64
var (
decimalMap = unitMap{"k": KB, "m": MB, "g": GB, "t": TB, "p": PB}
binaryMap = unitMap{"k": KiB, "m": MiB, "g": GiB, "t": TiB, "p": PiB}
sizeRegex = regexp.MustCompile(`^(\d+)([kKmMgGtTpP])?[bB]?$`)
)
var unitAbbrs = [...]string{"B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"}
// HumanSize returns a human-readable approximation of a size
// using SI standard (eg. "44kB", "17MB")
func HumanSize(size int64) string {
i := 0
sizef := float64(size)
for sizef >= 1000.0 {
sizef = sizef / 1000.0
i++
}
return fmt.Sprintf("%.4g %s", sizef, unitAbbrs[i])
}
// FromHumanSize returns an integer from a human-readable specification of a
// size using SI standard (eg. "44kB", "17MB")
func FromHumanSize(size string) (int64, error) {
return parseSize(size, decimalMap)
}
// Parses a human-readable string representing an amount of RAM
// in bytes, kibibytes, mebibytes, gibibytes, or tebibytes and
// returns the number of bytes, or -1 if the string is unparseable.
// Units are case-insensitive, and the 'b' suffix is optional.
func RAMInBytes(size string) (int64, error) {
return parseSize(size, binaryMap)
}
// Parses the human-readable size string into the amount it represents
func parseSize(sizeStr string, uMap unitMap) (int64, error) {
matches := sizeRegex.FindStringSubmatch(sizeStr)
if len(matches) != 3 {
return -1, fmt.Errorf("Invalid size: '%s'", sizeStr)
}
size, err := strconv.ParseInt(matches[1], 10, 0)
if err != nil {
return -1, err
}
unitPrefix := strings.ToLower(matches[2])
if mul, ok := uMap[unitPrefix]; ok {
size *= mul
}
return size, nil
}

Some files were not shown because too many files have changed in this diff Show more