udpate to use latest dep, update figtree

This commit is contained in:
Cory Bennett
2018-07-28 15:49:57 -07:00
parent eead13aef1
commit 462ef1c485
254 changed files with 698 additions and 43003 deletions
Generated
+16 -5
View File
@@ -4,7 +4,10 @@
[[projects]]
branch = "master"
name = "github.com/alecthomas/template"
packages = [".","parse"]
packages = [
".",
"parse"
]
revision = "a0175ee3bccc567396460bf5acd36800cb10c49c"
[[projects]]
@@ -18,12 +21,13 @@
name = "github.com/cheekybits/genny"
packages = ["generic"]
revision = "9127e812e1e9e501ce899a18121d316ecb52e4ba"
source = "github.com/coryb/genny"
[[projects]]
branch = "master"
name = "github.com/coryb/figtree"
packages = ["."]
revision = "c7d8fbf1d7746b5864b8262fabffec813b5a43fa"
revision = "071d1ef303dfb7738166ba62aac71e5ee10ce218"
[[projects]]
branch = "master"
@@ -154,12 +158,19 @@
[[projects]]
branch = "master"
name = "golang.org/x/sys"
packages = ["unix","windows"]
packages = [
"unix",
"windows"
]
revision = "a5054c7c1385fd50d9394475365355a87a7873ec"
[[projects]]
name = "gopkg.in/AlecAivazis/survey.v1"
packages = [".","core","terminal"]
packages = [
".",
"core",
"terminal"
]
revision = "9d910423e24aa6d7c7950160658c295e0734c7e0"
version = "1.3.1"
@@ -184,6 +195,6 @@
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "50016720a1e2509a915e4465a53ffa957f977d2145e831b81d946ef87f7a8f48"
inputs-digest = "50ab53a36ae44f587b284a614020e177ef8445f8a879940cc69f4b7c935fad33"
solver-name = "gps-cdcl"
solver-version = 1
+14 -3
View File
@@ -20,40 +20,50 @@
# name = "github.com/x/y"
# version = "2.4.0"
[prune]
go-tests = true
unused-packages = true
non-go = true
[[constraint]]
name = "github.com/coryb/figtree"
branch = "master"
[[constraint]]
name = "github.com/coryb/kingpeon"
branch = "master"
[[constraint]]
name = "github.com/coryb/oreo"
branch = "master"
[[constraint]]
name = "github.com/jinzhu/copier"
branch = "master"
[[constraint]]
name = "github.com/kballard/go-shellquote"
branch = "master"
[[constraint]]
name = "github.com/mgutz/ansi"
branch = "master"
[[constraint]]
name = "github.com/pkg/browser"
branch = "master"
[[constraint]]
name = "github.com/pkg/errors"
version = "0.8.0"
[[constraint]]
name = "github.com/savaki/jq"
[[constraint]]
name = "github.com/tmc/keyring"
branch = "master"
[[constraint]]
name = "golang.org/x/crypto"
branch = "master"
[[constraint]]
name = "gopkg.in/AlecAivazis/survey.v1"
@@ -65,6 +75,7 @@
[[constraint]]
name = "gopkg.in/coryb/yaml.v2"
branch = "v2"
[[constraint]]
name = "gopkg.in/op/go-logging.v1"
-25
View File
@@ -1,25 +0,0 @@
# Go's `text/template` package with newline elision
This is a fork of Go 1.4's [text/template](http://golang.org/pkg/text/template/) package with one addition: a backslash immediately after a closing delimiter will delete all subsequent newlines until a non-newline.
eg.
```
{{if true}}\
hello
{{end}}\
```
Will result in:
```
hello\n
```
Rather than:
```
\n
hello\n
\n
```
-72
View File
@@ -1,72 +0,0 @@
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package template_test
import (
"log"
"os"
"github.com/alecthomas/template"
)
func ExampleTemplate() {
// Define a template.
const letter = `
Dear {{.Name}},
{{if .Attended}}
It was a pleasure to see you at the wedding.{{else}}
It is a shame you couldn't make it to the wedding.{{end}}
{{with .Gift}}Thank you for the lovely {{.}}.
{{end}}
Best wishes,
Josie
`
// Prepare some data to insert into the template.
type Recipient struct {
Name, Gift string
Attended bool
}
var recipients = []Recipient{
{"Aunt Mildred", "bone china tea set", true},
{"Uncle John", "moleskin pants", false},
{"Cousin Rodney", "", false},
}
// Create a new template and parse the letter into it.
t := template.Must(template.New("letter").Parse(letter))
// Execute the template for each recipient.
for _, r := range recipients {
err := t.Execute(os.Stdout, r)
if err != nil {
log.Println("executing template:", err)
}
}
// Output:
// Dear Aunt Mildred,
//
// It was a pleasure to see you at the wedding.
// Thank you for the lovely bone china tea set.
//
// Best wishes,
// Josie
//
// Dear Uncle John,
//
// It is a shame you couldn't make it to the wedding.
// Thank you for the lovely moleskin pants.
//
// Best wishes,
// Josie
//
// Dear Cousin Rodney,
//
// It is a shame you couldn't make it to the wedding.
//
// Best wishes,
// Josie
}
-183
View File
@@ -1,183 +0,0 @@
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package template_test
import (
"io"
"io/ioutil"
"log"
"os"
"path/filepath"
"github.com/alecthomas/template"
)
// templateFile defines the contents of a template to be stored in a file, for testing.
type templateFile struct {
name string
contents string
}
func createTestDir(files []templateFile) string {
dir, err := ioutil.TempDir("", "template")
if err != nil {
log.Fatal(err)
}
for _, file := range files {
f, err := os.Create(filepath.Join(dir, file.name))
if err != nil {
log.Fatal(err)
}
defer f.Close()
_, err = io.WriteString(f, file.contents)
if err != nil {
log.Fatal(err)
}
}
return dir
}
// Here we demonstrate loading a set of templates from a directory.
func ExampleTemplate_glob() {
// Here we create a temporary directory and populate it with our sample
// template definition files; usually the template files would already
// exist in some location known to the program.
dir := createTestDir([]templateFile{
// T0.tmpl is a plain template file that just invokes T1.
{"T0.tmpl", `T0 invokes T1: ({{template "T1"}})`},
// T1.tmpl defines a template, T1 that invokes T2.
{"T1.tmpl", `{{define "T1"}}T1 invokes T2: ({{template "T2"}}){{end}}`},
// T2.tmpl defines a template T2.
{"T2.tmpl", `{{define "T2"}}This is T2{{end}}`},
})
// Clean up after the test; another quirk of running as an example.
defer os.RemoveAll(dir)
// pattern is the glob pattern used to find all the template files.
pattern := filepath.Join(dir, "*.tmpl")
// Here starts the example proper.
// T0.tmpl is the first name matched, so it becomes the starting template,
// the value returned by ParseGlob.
tmpl := template.Must(template.ParseGlob(pattern))
err := tmpl.Execute(os.Stdout, nil)
if err != nil {
log.Fatalf("template execution: %s", err)
}
// Output:
// T0 invokes T1: (T1 invokes T2: (This is T2))
}
// This example demonstrates one way to share some templates
// and use them in different contexts. In this variant we add multiple driver
// templates by hand to an existing bundle of templates.
func ExampleTemplate_helpers() {
// Here we create a temporary directory and populate it with our sample
// template definition files; usually the template files would already
// exist in some location known to the program.
dir := createTestDir([]templateFile{
// T1.tmpl defines a template, T1 that invokes T2.
{"T1.tmpl", `{{define "T1"}}T1 invokes T2: ({{template "T2"}}){{end}}`},
// T2.tmpl defines a template T2.
{"T2.tmpl", `{{define "T2"}}This is T2{{end}}`},
})
// Clean up after the test; another quirk of running as an example.
defer os.RemoveAll(dir)
// pattern is the glob pattern used to find all the template files.
pattern := filepath.Join(dir, "*.tmpl")
// Here starts the example proper.
// Load the helpers.
templates := template.Must(template.ParseGlob(pattern))
// Add one driver template to the bunch; we do this with an explicit template definition.
_, err := templates.Parse("{{define `driver1`}}Driver 1 calls T1: ({{template `T1`}})\n{{end}}")
if err != nil {
log.Fatal("parsing driver1: ", err)
}
// Add another driver template.
_, err = templates.Parse("{{define `driver2`}}Driver 2 calls T2: ({{template `T2`}})\n{{end}}")
if err != nil {
log.Fatal("parsing driver2: ", err)
}
// We load all the templates before execution. This package does not require
// that behavior but html/template's escaping does, so it's a good habit.
err = templates.ExecuteTemplate(os.Stdout, "driver1", nil)
if err != nil {
log.Fatalf("driver1 execution: %s", err)
}
err = templates.ExecuteTemplate(os.Stdout, "driver2", nil)
if err != nil {
log.Fatalf("driver2 execution: %s", err)
}
// Output:
// Driver 1 calls T1: (T1 invokes T2: (This is T2))
// Driver 2 calls T2: (This is T2)
}
// This example demonstrates how to use one group of driver
// templates with distinct sets of helper templates.
func ExampleTemplate_share() {
// Here we create a temporary directory and populate it with our sample
// template definition files; usually the template files would already
// exist in some location known to the program.
dir := createTestDir([]templateFile{
// T0.tmpl is a plain template file that just invokes T1.
{"T0.tmpl", "T0 ({{.}} version) invokes T1: ({{template `T1`}})\n"},
// T1.tmpl defines a template, T1 that invokes T2. Note T2 is not defined
{"T1.tmpl", `{{define "T1"}}T1 invokes T2: ({{template "T2"}}){{end}}`},
})
// Clean up after the test; another quirk of running as an example.
defer os.RemoveAll(dir)
// pattern is the glob pattern used to find all the template files.
pattern := filepath.Join(dir, "*.tmpl")
// Here starts the example proper.
// Load the drivers.
drivers := template.Must(template.ParseGlob(pattern))
// We must define an implementation of the T2 template. First we clone
// the drivers, then add a definition of T2 to the template name space.
// 1. Clone the helper set to create a new name space from which to run them.
first, err := drivers.Clone()
if err != nil {
log.Fatal("cloning helpers: ", err)
}
// 2. Define T2, version A, and parse it.
_, err = first.Parse("{{define `T2`}}T2, version A{{end}}")
if err != nil {
log.Fatal("parsing T2: ", err)
}
// Now repeat the whole thing, using a different version of T2.
// 1. Clone the drivers.
second, err := drivers.Clone()
if err != nil {
log.Fatal("cloning drivers: ", err)
}
// 2. Define T2, version B, and parse it.
_, err = second.Parse("{{define `T2`}}T2, version B{{end}}")
if err != nil {
log.Fatal("parsing T2: ", err)
}
// Execute the templates in the reverse order to verify the
// first is unaffected by the second.
err = second.ExecuteTemplate(os.Stdout, "T0.tmpl", "second")
if err != nil {
log.Fatalf("second execution: %s", err)
}
err = first.ExecuteTemplate(os.Stdout, "T0.tmpl", "first")
if err != nil {
log.Fatalf("first: execution: %s", err)
}
// Output:
// T0 (second version) invokes T1: (T1 invokes T2: (T2, version B))
// T0 (first version) invokes T1: (T1 invokes T2: (T2, version A))
}
-55
View File
@@ -1,55 +0,0 @@
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package template_test
import (
"log"
"os"
"strings"
"github.com/alecthomas/template"
)
// This example demonstrates a custom function to process template text.
// It installs the strings.Title function and uses it to
// Make Title Text Look Good In Our Template's Output.
func ExampleTemplate_func() {
// First we create a FuncMap with which to register the function.
funcMap := template.FuncMap{
// The name "title" is what the function will be called in the template text.
"title": strings.Title,
}
// A simple template definition to test our function.
// We print the input text several ways:
// - the original
// - title-cased
// - title-cased and then printed with %q
// - printed with %q and then title-cased.
const templateText = `
Input: {{printf "%q" .}}
Output 0: {{title .}}
Output 1: {{title . | printf "%q"}}
Output 2: {{printf "%q" . | title}}
`
// Create a template, add the function map, and parse the text.
tmpl, err := template.New("titleTest").Funcs(funcMap).Parse(templateText)
if err != nil {
log.Fatalf("parsing: %s", err)
}
// Run the template to verify the output.
err = tmpl.Execute(os.Stdout, "the go programming language")
if err != nil {
log.Fatalf("execution: %s", err)
}
// Output:
// Input: "the go programming language"
// Output 0: The Go Programming Language
// Output 1: "The Go Programming Language"
// Output 2: "The Go Programming Language"
}
File diff suppressed because it is too large Load Diff
-293
View File
@@ -1,293 +0,0 @@
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package template
// Tests for mulitple-template parsing and execution.
import (
"bytes"
"fmt"
"strings"
"testing"
"github.com/alecthomas/template/parse"
)
const (
noError = true
hasError = false
)
type multiParseTest struct {
name string
input string
ok bool
names []string
results []string
}
var multiParseTests = []multiParseTest{
{"empty", "", noError,
nil,
nil},
{"one", `{{define "foo"}} FOO {{end}}`, noError,
[]string{"foo"},
[]string{" FOO "}},
{"two", `{{define "foo"}} FOO {{end}}{{define "bar"}} BAR {{end}}`, noError,
[]string{"foo", "bar"},
[]string{" FOO ", " BAR "}},
// errors
{"missing end", `{{define "foo"}} FOO `, hasError,
nil,
nil},
{"malformed name", `{{define "foo}} FOO `, hasError,
nil,
nil},
}
func TestMultiParse(t *testing.T) {
for _, test := range multiParseTests {
template, err := New("root").Parse(test.input)
switch {
case err == nil && !test.ok:
t.Errorf("%q: expected error; got none", test.name)
continue
case err != nil && test.ok:
t.Errorf("%q: unexpected error: %v", test.name, err)
continue
case err != nil && !test.ok:
// expected error, got one
if *debug {
fmt.Printf("%s: %s\n\t%s\n", test.name, test.input, err)
}
continue
}
if template == nil {
continue
}
if len(template.tmpl) != len(test.names)+1 { // +1 for root
t.Errorf("%s: wrong number of templates; wanted %d got %d", test.name, len(test.names), len(template.tmpl))
continue
}
for i, name := range test.names {
tmpl, ok := template.tmpl[name]
if !ok {
t.Errorf("%s: can't find template %q", test.name, name)
continue
}
result := tmpl.Root.String()
if result != test.results[i] {
t.Errorf("%s=(%q): got\n\t%v\nexpected\n\t%v", test.name, test.input, result, test.results[i])
}
}
}
}
var multiExecTests = []execTest{
{"empty", "", "", nil, true},
{"text", "some text", "some text", nil, true},
{"invoke x", `{{template "x" .SI}}`, "TEXT", tVal, true},
{"invoke x no args", `{{template "x"}}`, "TEXT", tVal, true},
{"invoke dot int", `{{template "dot" .I}}`, "17", tVal, true},
{"invoke dot []int", `{{template "dot" .SI}}`, "[3 4 5]", tVal, true},
{"invoke dotV", `{{template "dotV" .U}}`, "v", tVal, true},
{"invoke nested int", `{{template "nested" .I}}`, "17", tVal, true},
{"variable declared by template", `{{template "nested" $x:=.SI}},{{index $x 1}}`, "[3 4 5],4", tVal, true},
// User-defined function: test argument evaluator.
{"testFunc literal", `{{oneArg "joe"}}`, "oneArg=joe", tVal, true},
{"testFunc .", `{{oneArg .}}`, "oneArg=joe", "joe", true},
}
// These strings are also in testdata/*.
const multiText1 = `
{{define "x"}}TEXT{{end}}
{{define "dotV"}}{{.V}}{{end}}
`
const multiText2 = `
{{define "dot"}}{{.}}{{end}}
{{define "nested"}}{{template "dot" .}}{{end}}
`
func TestMultiExecute(t *testing.T) {
// Declare a couple of templates first.
template, err := New("root").Parse(multiText1)
if err != nil {
t.Fatalf("parse error for 1: %s", err)
}
_, err = template.Parse(multiText2)
if err != nil {
t.Fatalf("parse error for 2: %s", err)
}
testExecute(multiExecTests, template, t)
}
func TestParseFiles(t *testing.T) {
_, err := ParseFiles("DOES NOT EXIST")
if err == nil {
t.Error("expected error for non-existent file; got none")
}
template := New("root")
_, err = template.ParseFiles("testdata/file1.tmpl", "testdata/file2.tmpl")
if err != nil {
t.Fatalf("error parsing files: %v", err)
}
testExecute(multiExecTests, template, t)
}
func TestParseGlob(t *testing.T) {
_, err := ParseGlob("DOES NOT EXIST")
if err == nil {
t.Error("expected error for non-existent file; got none")
}
_, err = New("error").ParseGlob("[x")
if err == nil {
t.Error("expected error for bad pattern; got none")
}
template := New("root")
_, err = template.ParseGlob("testdata/file*.tmpl")
if err != nil {
t.Fatalf("error parsing files: %v", err)
}
testExecute(multiExecTests, template, t)
}
// In these tests, actual content (not just template definitions) comes from the parsed files.
var templateFileExecTests = []execTest{
{"test", `{{template "tmpl1.tmpl"}}{{template "tmpl2.tmpl"}}`, "template1\n\ny\ntemplate2\n\nx\n", 0, true},
}
func TestParseFilesWithData(t *testing.T) {
template, err := New("root").ParseFiles("testdata/tmpl1.tmpl", "testdata/tmpl2.tmpl")
if err != nil {
t.Fatalf("error parsing files: %v", err)
}
testExecute(templateFileExecTests, template, t)
}
func TestParseGlobWithData(t *testing.T) {
template, err := New("root").ParseGlob("testdata/tmpl*.tmpl")
if err != nil {
t.Fatalf("error parsing files: %v", err)
}
testExecute(templateFileExecTests, template, t)
}
const (
cloneText1 = `{{define "a"}}{{template "b"}}{{template "c"}}{{end}}`
cloneText2 = `{{define "b"}}b{{end}}`
cloneText3 = `{{define "c"}}root{{end}}`
cloneText4 = `{{define "c"}}clone{{end}}`
)
func TestClone(t *testing.T) {
// Create some templates and clone the root.
root, err := New("root").Parse(cloneText1)
if err != nil {
t.Fatal(err)
}
_, err = root.Parse(cloneText2)
if err != nil {
t.Fatal(err)
}
clone := Must(root.Clone())
// Add variants to both.
_, err = root.Parse(cloneText3)
if err != nil {
t.Fatal(err)
}
_, err = clone.Parse(cloneText4)
if err != nil {
t.Fatal(err)
}
// Verify that the clone is self-consistent.
for k, v := range clone.tmpl {
if k == clone.name && v.tmpl[k] != clone {
t.Error("clone does not contain root")
}
if v != v.tmpl[v.name] {
t.Errorf("clone does not contain self for %q", k)
}
}
// Execute root.
var b bytes.Buffer
err = root.ExecuteTemplate(&b, "a", 0)
if err != nil {
t.Fatal(err)
}
if b.String() != "broot" {
t.Errorf("expected %q got %q", "broot", b.String())
}
// Execute copy.
b.Reset()
err = clone.ExecuteTemplate(&b, "a", 0)
if err != nil {
t.Fatal(err)
}
if b.String() != "bclone" {
t.Errorf("expected %q got %q", "bclone", b.String())
}
}
func TestAddParseTree(t *testing.T) {
// Create some templates.
root, err := New("root").Parse(cloneText1)
if err != nil {
t.Fatal(err)
}
_, err = root.Parse(cloneText2)
if err != nil {
t.Fatal(err)
}
// Add a new parse tree.
tree, err := parse.Parse("cloneText3", cloneText3, "", "", nil, builtins)
if err != nil {
t.Fatal(err)
}
added, err := root.AddParseTree("c", tree["c"])
// Execute.
var b bytes.Buffer
err = added.ExecuteTemplate(&b, "a", 0)
if err != nil {
t.Fatal(err)
}
if b.String() != "broot" {
t.Errorf("expected %q got %q", "broot", b.String())
}
}
// Issue 7032
func TestAddParseTreeToUnparsedTemplate(t *testing.T) {
master := "{{define \"master\"}}{{end}}"
tmpl := New("master")
tree, err := parse.Parse("master", master, "", "", nil)
if err != nil {
t.Fatalf("unexpected parse err: %v", err)
}
masterTree := tree["master"]
tmpl.AddParseTree("master", masterTree) // used to panic
}
func TestRedefinition(t *testing.T) {
var tmpl *Template
var err error
if tmpl, err = New("tmpl1").Parse(`{{define "test"}}foo{{end}}`); err != nil {
t.Fatalf("parse 1: %v", err)
}
if _, err = tmpl.Parse(`{{define "test"}}bar{{end}}`); err == nil {
t.Fatal("expected error")
}
if !strings.Contains(err.Error(), "redefinition") {
t.Fatalf("expected redefinition error; got %v", err)
}
if _, err = tmpl.New("tmpl2").Parse(`{{define "test"}}bar{{end}}`); err == nil {
t.Fatal("expected error")
}
if !strings.Contains(err.Error(), "redefinition") {
t.Fatalf("expected redefinition error; got %v", err)
}
}
-468
View File
@@ -1,468 +0,0 @@
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package parse
import (
"fmt"
"testing"
)
// Make the types prettyprint.
var itemName = map[itemType]string{
itemError: "error",
itemBool: "bool",
itemChar: "char",
itemCharConstant: "charconst",
itemComplex: "complex",
itemColonEquals: ":=",
itemEOF: "EOF",
itemField: "field",
itemIdentifier: "identifier",
itemLeftDelim: "left delim",
itemLeftParen: "(",
itemNumber: "number",
itemPipe: "pipe",
itemRawString: "raw string",
itemRightDelim: "right delim",
itemElideNewline: "elide newline",
itemRightParen: ")",
itemSpace: "space",
itemString: "string",
itemVariable: "variable",
// keywords
itemDot: ".",
itemDefine: "define",
itemElse: "else",
itemIf: "if",
itemEnd: "end",
itemNil: "nil",
itemRange: "range",
itemTemplate: "template",
itemWith: "with",
}
func (i itemType) String() string {
s := itemName[i]
if s == "" {
return fmt.Sprintf("item%d", int(i))
}
return s
}
type lexTest struct {
name string
input string
items []item
}
var (
tEOF = item{itemEOF, 0, ""}
tFor = item{itemIdentifier, 0, "for"}
tLeft = item{itemLeftDelim, 0, "{{"}
tLpar = item{itemLeftParen, 0, "("}
tPipe = item{itemPipe, 0, "|"}
tQuote = item{itemString, 0, `"abc \n\t\" "`}
tRange = item{itemRange, 0, "range"}
tRight = item{itemRightDelim, 0, "}}"}
tElideNewline = item{itemElideNewline, 0, "\\"}
tRpar = item{itemRightParen, 0, ")"}
tSpace = item{itemSpace, 0, " "}
raw = "`" + `abc\n\t\" ` + "`"
tRawQuote = item{itemRawString, 0, raw}
)
var lexTests = []lexTest{
{"empty", "", []item{tEOF}},
{"spaces", " \t\n", []item{{itemText, 0, " \t\n"}, tEOF}},
{"text", `now is the time`, []item{{itemText, 0, "now is the time"}, tEOF}},
{"elide newline", "{{}}\\", []item{tLeft, tRight, tElideNewline, tEOF}},
{"text with comment", "hello-{{/* this is a comment */}}-world", []item{
{itemText, 0, "hello-"},
{itemText, 0, "-world"},
tEOF,
}},
{"punctuation", "{{,@% }}", []item{
tLeft,
{itemChar, 0, ","},
{itemChar, 0, "@"},
{itemChar, 0, "%"},
tSpace,
tRight,
tEOF,
}},
{"parens", "{{((3))}}", []item{
tLeft,
tLpar,
tLpar,
{itemNumber, 0, "3"},
tRpar,
tRpar,
tRight,
tEOF,
}},
{"empty action", `{{}}`, []item{tLeft, tRight, tEOF}},
{"for", `{{for}}`, []item{tLeft, tFor, tRight, tEOF}},
{"quote", `{{"abc \n\t\" "}}`, []item{tLeft, tQuote, tRight, tEOF}},
{"raw quote", "{{" + raw + "}}", []item{tLeft, tRawQuote, tRight, tEOF}},
{"numbers", "{{1 02 0x14 -7.2i 1e3 +1.2e-4 4.2i 1+2i}}", []item{
tLeft,
{itemNumber, 0, "1"},
tSpace,
{itemNumber, 0, "02"},
tSpace,
{itemNumber, 0, "0x14"},
tSpace,
{itemNumber, 0, "-7.2i"},
tSpace,
{itemNumber, 0, "1e3"},
tSpace,
{itemNumber, 0, "+1.2e-4"},
tSpace,
{itemNumber, 0, "4.2i"},
tSpace,
{itemComplex, 0, "1+2i"},
tRight,
tEOF,
}},
{"characters", `{{'a' '\n' '\'' '\\' '\u00FF' '\xFF' '本'}}`, []item{
tLeft,
{itemCharConstant, 0, `'a'`},
tSpace,
{itemCharConstant, 0, `'\n'`},
tSpace,
{itemCharConstant, 0, `'\''`},
tSpace,
{itemCharConstant, 0, `'\\'`},
tSpace,
{itemCharConstant, 0, `'\u00FF'`},
tSpace,
{itemCharConstant, 0, `'\xFF'`},
tSpace,
{itemCharConstant, 0, `'本'`},
tRight,
tEOF,
}},
{"bools", "{{true false}}", []item{
tLeft,
{itemBool, 0, "true"},
tSpace,
{itemBool, 0, "false"},
tRight,
tEOF,
}},
{"dot", "{{.}}", []item{
tLeft,
{itemDot, 0, "."},
tRight,
tEOF,
}},
{"nil", "{{nil}}", []item{
tLeft,
{itemNil, 0, "nil"},
tRight,
tEOF,
}},
{"dots", "{{.x . .2 .x.y.z}}", []item{
tLeft,
{itemField, 0, ".x"},
tSpace,
{itemDot, 0, "."},
tSpace,
{itemNumber, 0, ".2"},
tSpace,
{itemField, 0, ".x"},
{itemField, 0, ".y"},
{itemField, 0, ".z"},
tRight,
tEOF,
}},
{"keywords", "{{range if else end with}}", []item{
tLeft,
{itemRange, 0, "range"},
tSpace,
{itemIf, 0, "if"},
tSpace,
{itemElse, 0, "else"},
tSpace,
{itemEnd, 0, "end"},
tSpace,
{itemWith, 0, "with"},
tRight,
tEOF,
}},
{"variables", "{{$c := printf $ $hello $23 $ $var.Field .Method}}", []item{
tLeft,
{itemVariable, 0, "$c"},
tSpace,
{itemColonEquals, 0, ":="},
tSpace,
{itemIdentifier, 0, "printf"},
tSpace,
{itemVariable, 0, "$"},
tSpace,
{itemVariable, 0, "$hello"},
tSpace,
{itemVariable, 0, "$23"},
tSpace,
{itemVariable, 0, "$"},
tSpace,
{itemVariable, 0, "$var"},
{itemField, 0, ".Field"},
tSpace,
{itemField, 0, ".Method"},
tRight,
tEOF,
}},
{"variable invocation", "{{$x 23}}", []item{
tLeft,
{itemVariable, 0, "$x"},
tSpace,
{itemNumber, 0, "23"},
tRight,
tEOF,
}},
{"pipeline", `intro {{echo hi 1.2 |noargs|args 1 "hi"}} outro`, []item{
{itemText, 0, "intro "},
tLeft,
{itemIdentifier, 0, "echo"},
tSpace,
{itemIdentifier, 0, "hi"},
tSpace,
{itemNumber, 0, "1.2"},
tSpace,
tPipe,
{itemIdentifier, 0, "noargs"},
tPipe,
{itemIdentifier, 0, "args"},
tSpace,
{itemNumber, 0, "1"},
tSpace,
{itemString, 0, `"hi"`},
tRight,
{itemText, 0, " outro"},
tEOF,
}},
{"declaration", "{{$v := 3}}", []item{
tLeft,
{itemVariable, 0, "$v"},
tSpace,
{itemColonEquals, 0, ":="},
tSpace,
{itemNumber, 0, "3"},
tRight,
tEOF,
}},
{"2 declarations", "{{$v , $w := 3}}", []item{
tLeft,
{itemVariable, 0, "$v"},
tSpace,
{itemChar, 0, ","},
tSpace,
{itemVariable, 0, "$w"},
tSpace,
{itemColonEquals, 0, ":="},
tSpace,
{itemNumber, 0, "3"},
tRight,
tEOF,
}},
{"field of parenthesized expression", "{{(.X).Y}}", []item{
tLeft,
tLpar,
{itemField, 0, ".X"},
tRpar,
{itemField, 0, ".Y"},
tRight,
tEOF,
}},
// errors
{"badchar", "#{{\x01}}", []item{
{itemText, 0, "#"},
tLeft,
{itemError, 0, "unrecognized character in action: U+0001"},
}},
{"unclosed action", "{{\n}}", []item{
tLeft,
{itemError, 0, "unclosed action"},
}},
{"EOF in action", "{{range", []item{
tLeft,
tRange,
{itemError, 0, "unclosed action"},
}},
{"unclosed quote", "{{\"\n\"}}", []item{
tLeft,
{itemError, 0, "unterminated quoted string"},
}},
{"unclosed raw quote", "{{`xx\n`}}", []item{
tLeft,
{itemError, 0, "unterminated raw quoted string"},
}},
{"unclosed char constant", "{{'\n}}", []item{
tLeft,
{itemError, 0, "unterminated character constant"},
}},
{"bad number", "{{3k}}", []item{
tLeft,
{itemError, 0, `bad number syntax: "3k"`},
}},
{"unclosed paren", "{{(3}}", []item{
tLeft,
tLpar,
{itemNumber, 0, "3"},
{itemError, 0, `unclosed left paren`},
}},
{"extra right paren", "{{3)}}", []item{
tLeft,
{itemNumber, 0, "3"},
tRpar,
{itemError, 0, `unexpected right paren U+0029 ')'`},
}},
// Fixed bugs
// Many elements in an action blew the lookahead until
// we made lexInsideAction not loop.
{"long pipeline deadlock", "{{|||||}}", []item{
tLeft,
tPipe,
tPipe,
tPipe,
tPipe,
tPipe,
tRight,
tEOF,
}},
{"text with bad comment", "hello-{{/*/}}-world", []item{
{itemText, 0, "hello-"},
{itemError, 0, `unclosed comment`},
}},
{"text with comment close separted from delim", "hello-{{/* */ }}-world", []item{
{itemText, 0, "hello-"},
{itemError, 0, `comment ends before closing delimiter`},
}},
// This one is an error that we can't catch because it breaks templates with
// minimized JavaScript. Should have fixed it before Go 1.1.
{"unmatched right delimiter", "hello-{.}}-world", []item{
{itemText, 0, "hello-{.}}-world"},
tEOF,
}},
}
// collect gathers the emitted items into a slice.
func collect(t *lexTest, left, right string) (items []item) {
l := lex(t.name, t.input, left, right)
for {
item := l.nextItem()
items = append(items, item)
if item.typ == itemEOF || item.typ == itemError {
break
}
}
return
}
func equal(i1, i2 []item, checkPos bool) bool {
if len(i1) != len(i2) {
return false
}
for k := range i1 {
if i1[k].typ != i2[k].typ {
return false
}
if i1[k].val != i2[k].val {
return false
}
if checkPos && i1[k].pos != i2[k].pos {
return false
}
}
return true
}
func TestLex(t *testing.T) {
for _, test := range lexTests {
items := collect(&test, "", "")
if !equal(items, test.items, false) {
t.Errorf("%s: got\n\t%+v\nexpected\n\t%v", test.name, items, test.items)
}
}
}
// Some easy cases from above, but with delimiters $$ and @@
var lexDelimTests = []lexTest{
{"punctuation", "$$,@%{{}}@@", []item{
tLeftDelim,
{itemChar, 0, ","},
{itemChar, 0, "@"},
{itemChar, 0, "%"},
{itemChar, 0, "{"},
{itemChar, 0, "{"},
{itemChar, 0, "}"},
{itemChar, 0, "}"},
tRightDelim,
tEOF,
}},
{"empty action", `$$@@`, []item{tLeftDelim, tRightDelim, tEOF}},
{"for", `$$for@@`, []item{tLeftDelim, tFor, tRightDelim, tEOF}},
{"quote", `$$"abc \n\t\" "@@`, []item{tLeftDelim, tQuote, tRightDelim, tEOF}},
{"raw quote", "$$" + raw + "@@", []item{tLeftDelim, tRawQuote, tRightDelim, tEOF}},
}
var (
tLeftDelim = item{itemLeftDelim, 0, "$$"}
tRightDelim = item{itemRightDelim, 0, "@@"}
)
func TestDelims(t *testing.T) {
for _, test := range lexDelimTests {
items := collect(&test, "$$", "@@")
if !equal(items, test.items, false) {
t.Errorf("%s: got\n\t%v\nexpected\n\t%v", test.name, items, test.items)
}
}
}
var lexPosTests = []lexTest{
{"empty", "", []item{tEOF}},
{"punctuation", "{{,@%#}}", []item{
{itemLeftDelim, 0, "{{"},
{itemChar, 2, ","},
{itemChar, 3, "@"},
{itemChar, 4, "%"},
{itemChar, 5, "#"},
{itemRightDelim, 6, "}}"},
{itemEOF, 8, ""},
}},
{"sample", "0123{{hello}}xyz", []item{
{itemText, 0, "0123"},
{itemLeftDelim, 4, "{{"},
{itemIdentifier, 6, "hello"},
{itemRightDelim, 11, "}}"},
{itemText, 13, "xyz"},
{itemEOF, 16, ""},
}},
}
// The other tests don't check position, to make the test cases easier to construct.
// This one does.
func TestPos(t *testing.T) {
for _, test := range lexPosTests {
items := collect(&test, "", "")
if !equal(items, test.items, true) {
t.Errorf("%s: got\n\t%v\nexpected\n\t%v", test.name, items, test.items)
if len(items) == len(test.items) {
// Detailed print; avoid item.String() to expose the position value.
for i := range items {
if !equal(items[i:i+1], test.items[i:i+1], true) {
i1 := items[i]
i2 := test.items[i]
t.Errorf("\t#%d: got {%v %d %q} expected {%v %d %q}", i, i1.typ, i1.pos, i1.val, i2.typ, i2.pos, i2.val)
}
}
}
}
}
}
-426
View File
@@ -1,426 +0,0 @@
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package parse
import (
"flag"
"fmt"
"strings"
"testing"
)
var debug = flag.Bool("debug", false, "show the errors produced by the main tests")
type numberTest struct {
text string
isInt bool
isUint bool
isFloat bool
isComplex bool
int64
uint64
float64
complex128
}
var numberTests = []numberTest{
// basics
{"0", true, true, true, false, 0, 0, 0, 0},
{"-0", true, true, true, false, 0, 0, 0, 0}, // check that -0 is a uint.
{"73", true, true, true, false, 73, 73, 73, 0},
{"073", true, true, true, false, 073, 073, 073, 0},
{"0x73", true, true, true, false, 0x73, 0x73, 0x73, 0},
{"-73", true, false, true, false, -73, 0, -73, 0},
{"+73", true, false, true, false, 73, 0, 73, 0},
{"100", true, true, true, false, 100, 100, 100, 0},
{"1e9", true, true, true, false, 1e9, 1e9, 1e9, 0},
{"-1e9", true, false, true, false, -1e9, 0, -1e9, 0},
{"-1.2", false, false, true, false, 0, 0, -1.2, 0},
{"1e19", false, true, true, false, 0, 1e19, 1e19, 0},
{"-1e19", false, false, true, false, 0, 0, -1e19, 0},
{"4i", false, false, false, true, 0, 0, 0, 4i},
{"-1.2+4.2i", false, false, false, true, 0, 0, 0, -1.2 + 4.2i},
{"073i", false, false, false, true, 0, 0, 0, 73i}, // not octal!
// complex with 0 imaginary are float (and maybe integer)
{"0i", true, true, true, true, 0, 0, 0, 0},
{"-1.2+0i", false, false, true, true, 0, 0, -1.2, -1.2},
{"-12+0i", true, false, true, true, -12, 0, -12, -12},
{"13+0i", true, true, true, true, 13, 13, 13, 13},
// funny bases
{"0123", true, true, true, false, 0123, 0123, 0123, 0},
{"-0x0", true, true, true, false, 0, 0, 0, 0},
{"0xdeadbeef", true, true, true, false, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0},
// character constants
{`'a'`, true, true, true, false, 'a', 'a', 'a', 0},
{`'\n'`, true, true, true, false, '\n', '\n', '\n', 0},
{`'\\'`, true, true, true, false, '\\', '\\', '\\', 0},
{`'\''`, true, true, true, false, '\'', '\'', '\'', 0},
{`'\xFF'`, true, true, true, false, 0xFF, 0xFF, 0xFF, 0},
{`'パ'`, true, true, true, false, 0x30d1, 0x30d1, 0x30d1, 0},
{`'\u30d1'`, true, true, true, false, 0x30d1, 0x30d1, 0x30d1, 0},
{`'\U000030d1'`, true, true, true, false, 0x30d1, 0x30d1, 0x30d1, 0},
// some broken syntax
{text: "+-2"},
{text: "0x123."},
{text: "1e."},
{text: "0xi."},
{text: "1+2."},
{text: "'x"},
{text: "'xx'"},
// Issue 8622 - 0xe parsed as floating point. Very embarrassing.
{"0xef", true, true, true, false, 0xef, 0xef, 0xef, 0},
}
func TestNumberParse(t *testing.T) {
for _, test := range numberTests {
// If fmt.Sscan thinks it's complex, it's complex. We can't trust the output
// because imaginary comes out as a number.
var c complex128
typ := itemNumber
var tree *Tree
if test.text[0] == '\'' {
typ = itemCharConstant
} else {
_, err := fmt.Sscan(test.text, &c)
if err == nil {
typ = itemComplex
}
}
n, err := tree.newNumber(0, test.text, typ)
ok := test.isInt || test.isUint || test.isFloat || test.isComplex
if ok && err != nil {
t.Errorf("unexpected error for %q: %s", test.text, err)
continue
}
if !ok && err == nil {
t.Errorf("expected error for %q", test.text)
continue
}
if !ok {
if *debug {
fmt.Printf("%s\n\t%s\n", test.text, err)
}
continue
}
if n.IsComplex != test.isComplex {
t.Errorf("complex incorrect for %q; should be %t", test.text, test.isComplex)
}
if test.isInt {
if !n.IsInt {
t.Errorf("expected integer for %q", test.text)
}
if n.Int64 != test.int64 {
t.Errorf("int64 for %q should be %d Is %d", test.text, test.int64, n.Int64)
}
} else if n.IsInt {
t.Errorf("did not expect integer for %q", test.text)
}
if test.isUint {
if !n.IsUint {
t.Errorf("expected unsigned integer for %q", test.text)
}
if n.Uint64 != test.uint64 {
t.Errorf("uint64 for %q should be %d Is %d", test.text, test.uint64, n.Uint64)
}
} else if n.IsUint {
t.Errorf("did not expect unsigned integer for %q", test.text)
}
if test.isFloat {
if !n.IsFloat {
t.Errorf("expected float for %q", test.text)
}
if n.Float64 != test.float64 {
t.Errorf("float64 for %q should be %g Is %g", test.text, test.float64, n.Float64)
}
} else if n.IsFloat {
t.Errorf("did not expect float for %q", test.text)
}
if test.isComplex {
if !n.IsComplex {
t.Errorf("expected complex for %q", test.text)
}
if n.Complex128 != test.complex128 {
t.Errorf("complex128 for %q should be %g Is %g", test.text, test.complex128, n.Complex128)
}
} else if n.IsComplex {
t.Errorf("did not expect complex for %q", test.text)
}
}
}
type parseTest struct {
name string
input string
ok bool
result string // what the user would see in an error message.
}
const (
noError = true
hasError = false
)
var parseTests = []parseTest{
{"empty", "", noError,
``},
{"comment", "{{/*\n\n\n*/}}", noError,
``},
{"spaces", " \t\n", noError,
`" \t\n"`},
{"text", "some text", noError,
`"some text"`},
{"emptyAction", "{{}}", hasError,
`{{}}`},
{"field", "{{.X}}", noError,
`{{.X}}`},
{"simple command", "{{printf}}", noError,
`{{printf}}`},
{"$ invocation", "{{$}}", noError,
"{{$}}"},
{"variable invocation", "{{with $x := 3}}{{$x 23}}{{end}}", noError,
"{{with $x := 3}}{{$x 23}}{{end}}"},
{"variable with fields", "{{$.I}}", noError,
"{{$.I}}"},
{"multi-word command", "{{printf `%d` 23}}", noError,
"{{printf `%d` 23}}"},
{"pipeline", "{{.X|.Y}}", noError,
`{{.X | .Y}}`},
{"pipeline with decl", "{{$x := .X|.Y}}", noError,
`{{$x := .X | .Y}}`},
{"nested pipeline", "{{.X (.Y .Z) (.A | .B .C) (.E)}}", noError,
`{{.X (.Y .Z) (.A | .B .C) (.E)}}`},
{"field applied to parentheses", "{{(.Y .Z).Field}}", noError,
`{{(.Y .Z).Field}}`},
{"simple if", "{{if .X}}hello{{end}}", noError,
`{{if .X}}"hello"{{end}}`},
{"if with else", "{{if .X}}true{{else}}false{{end}}", noError,
`{{if .X}}"true"{{else}}"false"{{end}}`},
{"if with else if", "{{if .X}}true{{else if .Y}}false{{end}}", noError,
`{{if .X}}"true"{{else}}{{if .Y}}"false"{{end}}{{end}}`},
{"if else chain", "+{{if .X}}X{{else if .Y}}Y{{else if .Z}}Z{{end}}+", noError,
`"+"{{if .X}}"X"{{else}}{{if .Y}}"Y"{{else}}{{if .Z}}"Z"{{end}}{{end}}{{end}}"+"`},
{"simple range", "{{range .X}}hello{{end}}", noError,
`{{range .X}}"hello"{{end}}`},
{"chained field range", "{{range .X.Y.Z}}hello{{end}}", noError,
`{{range .X.Y.Z}}"hello"{{end}}`},
{"nested range", "{{range .X}}hello{{range .Y}}goodbye{{end}}{{end}}", noError,
`{{range .X}}"hello"{{range .Y}}"goodbye"{{end}}{{end}}`},
{"range with else", "{{range .X}}true{{else}}false{{end}}", noError,
`{{range .X}}"true"{{else}}"false"{{end}}`},
{"range over pipeline", "{{range .X|.M}}true{{else}}false{{end}}", noError,
`{{range .X | .M}}"true"{{else}}"false"{{end}}`},
{"range []int", "{{range .SI}}{{.}}{{end}}", noError,
`{{range .SI}}{{.}}{{end}}`},
{"range 1 var", "{{range $x := .SI}}{{.}}{{end}}", noError,
`{{range $x := .SI}}{{.}}{{end}}`},
{"range 2 vars", "{{range $x, $y := .SI}}{{.}}{{end}}", noError,
`{{range $x, $y := .SI}}{{.}}{{end}}`},
{"constants", "{{range .SI 1 -3.2i true false 'a' nil}}{{end}}", noError,
`{{range .SI 1 -3.2i true false 'a' nil}}{{end}}`},
{"template", "{{template `x`}}", noError,
`{{template "x"}}`},
{"template with arg", "{{template `x` .Y}}", noError,
`{{template "x" .Y}}`},
{"with", "{{with .X}}hello{{end}}", noError,
`{{with .X}}"hello"{{end}}`},
{"with with else", "{{with .X}}hello{{else}}goodbye{{end}}", noError,
`{{with .X}}"hello"{{else}}"goodbye"{{end}}`},
{"elide newline", "{{true}}\\\n ", noError,
`{{true}}" "`},
// Errors.
{"unclosed action", "hello{{range", hasError, ""},
{"unmatched end", "{{end}}", hasError, ""},
{"missing end", "hello{{range .x}}", hasError, ""},
{"missing end after else", "hello{{range .x}}{{else}}", hasError, ""},
{"undefined function", "hello{{undefined}}", hasError, ""},
{"undefined variable", "{{$x}}", hasError, ""},
{"variable undefined after end", "{{with $x := 4}}{{end}}{{$x}}", hasError, ""},
{"variable undefined in template", "{{template $v}}", hasError, ""},
{"declare with field", "{{with $x.Y := 4}}{{end}}", hasError, ""},
{"template with field ref", "{{template .X}}", hasError, ""},
{"template with var", "{{template $v}}", hasError, ""},
{"invalid punctuation", "{{printf 3, 4}}", hasError, ""},
{"multidecl outside range", "{{with $v, $u := 3}}{{end}}", hasError, ""},
{"too many decls in range", "{{range $u, $v, $w := 3}}{{end}}", hasError, ""},
{"dot applied to parentheses", "{{printf (printf .).}}", hasError, ""},
{"adjacent args", "{{printf 3`x`}}", hasError, ""},
{"adjacent args with .", "{{printf `x`.}}", hasError, ""},
{"extra end after if", "{{if .X}}a{{else if .Y}}b{{end}}{{end}}", hasError, ""},
{"invalid newline elision", "{{true}}\\{{true}}", hasError, ""},
// Equals (and other chars) do not assignments make (yet).
{"bug0a", "{{$x := 0}}{{$x}}", noError, "{{$x := 0}}{{$x}}"},
{"bug0b", "{{$x = 1}}{{$x}}", hasError, ""},
{"bug0c", "{{$x ! 2}}{{$x}}", hasError, ""},
{"bug0d", "{{$x % 3}}{{$x}}", hasError, ""},
// Check the parse fails for := rather than comma.
{"bug0e", "{{range $x := $y := 3}}{{end}}", hasError, ""},
// Another bug: variable read must ignore following punctuation.
{"bug1a", "{{$x:=.}}{{$x!2}}", hasError, ""}, // ! is just illegal here.
{"bug1b", "{{$x:=.}}{{$x+2}}", hasError, ""}, // $x+2 should not parse as ($x) (+2).
{"bug1c", "{{$x:=.}}{{$x +2}}", noError, "{{$x := .}}{{$x +2}}"}, // It's OK with a space.
}
var builtins = map[string]interface{}{
"printf": fmt.Sprintf,
}
func testParse(doCopy bool, t *testing.T) {
textFormat = "%q"
defer func() { textFormat = "%s" }()
for _, test := range parseTests {
tmpl, err := New(test.name).Parse(test.input, "", "", make(map[string]*Tree), builtins)
switch {
case err == nil && !test.ok:
t.Errorf("%q: expected error; got none", test.name)
continue
case err != nil && test.ok:
t.Errorf("%q: unexpected error: %v", test.name, err)
continue
case err != nil && !test.ok:
// expected error, got one
if *debug {
fmt.Printf("%s: %s\n\t%s\n", test.name, test.input, err)
}
continue
}
var result string
if doCopy {
result = tmpl.Root.Copy().String()
} else {
result = tmpl.Root.String()
}
if result != test.result {
t.Errorf("%s=(%q): got\n\t%v\nexpected\n\t%v", test.name, test.input, result, test.result)
}
}
}
func TestParse(t *testing.T) {
testParse(false, t)
}
// Same as TestParse, but we copy the node first
func TestParseCopy(t *testing.T) {
testParse(true, t)
}
type isEmptyTest struct {
name string
input string
empty bool
}
var isEmptyTests = []isEmptyTest{
{"empty", ``, true},
{"nonempty", `hello`, false},
{"spaces only", " \t\n \t\n", true},
{"definition", `{{define "x"}}something{{end}}`, true},
{"definitions and space", "{{define `x`}}something{{end}}\n\n{{define `y`}}something{{end}}\n\n", true},
{"definitions and text", "{{define `x`}}something{{end}}\nx\n{{define `y`}}something{{end}}\ny\n", false},
{"definition and action", "{{define `x`}}something{{end}}{{if 3}}foo{{end}}", false},
}
func TestIsEmpty(t *testing.T) {
if !IsEmptyTree(nil) {
t.Errorf("nil tree is not empty")
}
for _, test := range isEmptyTests {
tree, err := New("root").Parse(test.input, "", "", make(map[string]*Tree), nil)
if err != nil {
t.Errorf("%q: unexpected error: %v", test.name, err)
continue
}
if empty := IsEmptyTree(tree.Root); empty != test.empty {
t.Errorf("%q: expected %t got %t", test.name, test.empty, empty)
}
}
}
func TestErrorContextWithTreeCopy(t *testing.T) {
tree, err := New("root").Parse("{{if true}}{{end}}", "", "", make(map[string]*Tree), nil)
if err != nil {
t.Fatalf("unexpected tree parse failure: %v", err)
}
treeCopy := tree.Copy()
wantLocation, wantContext := tree.ErrorContext(tree.Root.Nodes[0])
gotLocation, gotContext := treeCopy.ErrorContext(treeCopy.Root.Nodes[0])
if wantLocation != gotLocation {
t.Errorf("wrong error location want %q got %q", wantLocation, gotLocation)
}
if wantContext != gotContext {
t.Errorf("wrong error location want %q got %q", wantContext, gotContext)
}
}
// All failures, and the result is a string that must appear in the error message.
var errorTests = []parseTest{
// Check line numbers are accurate.
{"unclosed1",
"line1\n{{",
hasError, `unclosed1:2: unexpected unclosed action in command`},
{"unclosed2",
"line1\n{{define `x`}}line2\n{{",
hasError, `unclosed2:3: unexpected unclosed action in command`},
// Specific errors.
{"function",
"{{foo}}",
hasError, `function "foo" not defined`},
{"comment",
"{{/*}}",
hasError, `unclosed comment`},
{"lparen",
"{{.X (1 2 3}}",
hasError, `unclosed left paren`},
{"rparen",
"{{.X 1 2 3)}}",
hasError, `unexpected ")"`},
{"space",
"{{`x`3}}",
hasError, `missing space?`},
{"idchar",
"{{a#}}",
hasError, `'#'`},
{"charconst",
"{{'a}}",
hasError, `unterminated character constant`},
{"stringconst",
`{{"a}}`,
hasError, `unterminated quoted string`},
{"rawstringconst",
"{{`a}}",
hasError, `unterminated raw quoted string`},
{"number",
"{{0xi}}",
hasError, `number syntax`},
{"multidefine",
"{{define `a`}}a{{end}}{{define `a`}}b{{end}}",
hasError, `multiple definition of template`},
{"eof",
"{{range .X}}",
hasError, `unexpected EOF`},
{"variable",
// Declare $x so it's defined, to avoid that error, and then check we don't parse a declaration.
"{{$x := 23}}{{with $x.y := 3}}{{$x 23}}{{end}}",
hasError, `unexpected ":="`},
{"multidecl",
"{{$a,$b,$c := 23}}",
hasError, `too many declarations`},
{"undefvar",
"{{$a}}",
hasError, `undefined variable`},
}
func TestErrors(t *testing.T) {
for _, test := range errorTests {
_, err := New(test.name).Parse(test.input, "", "", make(map[string]*Tree))
if err == nil {
t.Errorf("%q: expected error", test.name)
continue
}
if !strings.Contains(err.Error(), test.result) {
t.Errorf("%q: error %q does not contain %q", test.name, err, test.result)
}
}
}
-11
View File
@@ -1,11 +0,0 @@
# Units - Helpful unit multipliers and functions for Go
The goal of this package is to have functionality similar to the [time](http://golang.org/pkg/time/) package.
It allows for code like this:
```go
n, err := ParseBase2Bytes("1KB")
// n == 1024
n = units.Mebibyte * 512
```
-49
View File
@@ -1,49 +0,0 @@
package units
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestBase2BytesString(t *testing.T) {
assert.Equal(t, Base2Bytes(0).String(), "0B")
assert.Equal(t, Base2Bytes(1025).String(), "1KiB1B")
assert.Equal(t, Base2Bytes(1048577).String(), "1MiB1B")
}
func TestParseBase2Bytes(t *testing.T) {
n, err := ParseBase2Bytes("0B")
assert.NoError(t, err)
assert.Equal(t, 0, int(n))
n, err = ParseBase2Bytes("1KB")
assert.NoError(t, err)
assert.Equal(t, 1024, int(n))
n, err = ParseBase2Bytes("1MB1KB25B")
assert.NoError(t, err)
assert.Equal(t, 1049625, int(n))
n, err = ParseBase2Bytes("1.5MB")
assert.NoError(t, err)
assert.Equal(t, 1572864, int(n))
}
func TestMetricBytesString(t *testing.T) {
assert.Equal(t, MetricBytes(0).String(), "0B")
assert.Equal(t, MetricBytes(1001).String(), "1KB1B")
assert.Equal(t, MetricBytes(1001025).String(), "1MB1KB25B")
}
func TestParseMetricBytes(t *testing.T) {
n, err := ParseMetricBytes("0B")
assert.NoError(t, err)
assert.Equal(t, 0, int(n))
n, err = ParseMetricBytes("1KB1B")
assert.NoError(t, err)
assert.Equal(t, 1001, int(n))
n, err = ParseMetricBytes("1MB1KB25B")
assert.NoError(t, err)
assert.Equal(t, 1001025, int(n))
n, err = ParseMetricBytes("1.5MB")
assert.NoError(t, err)
assert.Equal(t, 1500000, int(n))
}
-26
View File
@@ -1,26 +0,0 @@
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so
# Folders
_obj
_test
# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out
*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*
_testmain.go
*.exe
*.test
*.prof
genny
-10
View File
@@ -1,10 +0,0 @@
language: go
go:
# - 1.0
# - 1.1
- 1.2
- 1.3
- 1.4
- 1.5
- 1.6
-245
View File
@@ -1,245 +0,0 @@
# genny - Generics for Go
[![Build Status](https://travis-ci.org/cheekybits/genny.svg?branch=master)](https://travis-ci.org/cheekybits/genny) [![GoDoc](https://godoc.org/github.com/cheekybits/genny/parse?status.png)](http://godoc.org/github.com/cheekybits/genny/parse)
Install:
```
go get github.com/cheekybits/genny
```
=====
(pron. Jenny) by Mat Ryer ([@matryer](https://twitter.com/matryer)) and Tyler Bunnell ([@TylerJBunnell](https://twitter.com/TylerJBunnell)).
Until the Go core team include support for [generics in Go](http://golang.org/doc/faq#generics), `genny` is a code-generation generics solution. It allows you write normal buildable and testable Go code which, when processed by the `genny gen` tool, will replace the generics with specific types.
* Generic code is valid Go code
* Generic code compiles and can be tested
* Use `stdin` and `stdout` or specify in and out files
* Supports Go 1.4's [go generate](http://tip.golang.org/doc/go1.4#gogenerate)
* Multiple specific types will generate every permutation
* Use `BUILTINS` and `NUMBERS` wildtype to generate specific code for all built-in (and number) Go types
* Function names and comments also get updated
## Library
We have started building a [library of common things](https://github.com/cheekybits/gennylib), and you can use `genny get` to generate the specific versions you need.
For example: `genny get maps/concurrentmap.go "KeyType=BUILTINS ValueType=BUILTINS"` will print out generated code for all types for a concurrent map. Any file in the library may be generated locally in this way using all the same options given to `genny gen`.
## Usage
```
genny [{flags}] gen "{types}"
gen - generates type specific code from generic code.
get <package/file> - fetch a generic template from the online library and gen it.
{types} - (optional) Command line flags (see below)
{types} - (required) Specific types for each generic type in the source
{types} format: {generic}={specific}[,another][ {generic2}={specific2}]
Examples:
Generic=Specific
Generic1=Specific1 Generic2=Specific2
Generic1=Specific1,Specific2 Generic2=Specific3,Specific4
Flags:
-in="": file to parse instead of stdin
-out="": file to save output to instead of stdout
-pkg="": package name for generated files
```
* Comma separated type lists will generate code for each type
### Flags
* `-in` - specify the input file (rather than using stdin)
* `-out` - specify the output file (rather than using stdout)
### go generate
To use Go 1.4's `go generate` capability, insert the following comment in your source code file:
```
//go:generate genny -in=$GOFILE -out=gen-$GOFILE gen "KeyType=string,int ValueType=string,int"
```
* Start the line with `//go:generate `
* Use the `-in` and `-out` flags to specify the files to work on
* Use the `genny` command as usual after the flags
Now, running `go generate` (in a shell) for the package will cause the generic versions of the files to be generated.
* The output file will be overwritten, so it's safe to call `go generate` many times
* Use `$GOFILE` to refer to the current file
* The `//go:generate` line will be removed from the output
To see a real example of how to use `genny` with `go generate`, look in the [example/go-generate directory](https://github.com/cheekybits/genny/tree/master/examples/go-generate).
## How it works
Define your generic types using the special `generic.Type` placeholder type:
```go
type KeyType generic.Type
type ValueType generic.Type
```
* You can use as many as you like
* Give them meaningful names
Then write the generic code referencing the types as your normally would:
```go
func SetValueTypeForKeyType(key KeyType, value ValueType) { /* ... */ }
```
* Generic type names will also be replaced in comments and function names (see Real example below)
Since `generic.Type` is a real Go type, your code will compile, and you can even write unit tests against your generic code.
#### Generating specific versions
Pass the file through the `genny gen` tool with the specific types as the argument:
```
cat generic.go | genny gen "KeyType=string ValueType=interface{}"
```
The output will be the complete Go source file with the generic types replaced with the types specified in the arguments.
## Real example
Given [this generic Go code](https://github.com/cheekybits/genny/tree/master/examples/queue) which compiles and is tested:
```go
package queue
import "github.com/cheekybits/genny/generic"
// NOTE: this is how easy it is to define a generic type
type Something generic.Type
// SomethingQueue is a queue of Somethings.
type SomethingQueue struct {
items []Something
}
func NewSomethingQueue() *SomethingQueue {
return &SomethingQueue{items: make([]Something, 0)}
}
func (q *SomethingQueue) Push(item Something) {
q.items = append(q.items, item)
}
func (q *SomethingQueue) Pop() Something {
item := q.items[0]
q.items = q.items[1:]
return item
}
```
When `genny gen` is invoked like this:
```
cat source.go | genny gen "Something=string"
```
It outputs:
```go
// This file was automatically generated by genny.
// Any changes will be lost if this file is regenerated.
// see https://github.com/cheekybits/genny
package queue
// StringQueue is a queue of Strings.
type StringQueue struct {
items []string
}
func NewStringQueue() *StringQueue {
return &StringQueue{items: make([]string, 0)}
}
func (q *StringQueue) Push(item string) {
q.items = append(q.items, item)
}
func (q *StringQueue) Pop() string {
item := q.items[0]
q.items = q.items[1:]
return item
}
```
To get a _something_ for every built-in Go type plus one of your own types, you could run:
```
cat source.go | genny gen "Something=BUILTINS,*MyType"
```
#### More examples
Check out the [test code files](https://github.com/cheekybits/genny/tree/master/parse/test) for more real examples.
## Writing test code
Once you have defined a generic type with some code worth testing:
```go
package slice
import (
"log"
"reflect"
"github.com/stretchr/gogen/generic"
)
type MyType generic.Type
func EnsureMyTypeSlice(objectOrSlice interface{}) []MyType {
log.Printf("%v", reflect.TypeOf(objectOrSlice))
switch obj := objectOrSlice.(type) {
case []MyType:
log.Println(" returning it untouched")
return obj
case MyType:
log.Println(" wrapping in slice")
return []MyType{obj}
default:
panic("ensure slice needs MyType or []MyType")
}
}
```
You can treat it like any normal Go type in your test code:
```go
func TestEnsureMyTypeSlice(t *testing.T) {
myType := new(MyType)
slice := EnsureMyTypeSlice(myType)
if assert.NotNil(t, slice) {
assert.Equal(t, slice[0], myType)
}
slice = EnsureMyTypeSlice(slice)
log.Printf("%#v", slice[0])
if assert.NotNil(t, slice) {
assert.Equal(t, slice[0], myType)
}
}
```
### Understanding what `generic.Type` is
Because `generic.Type` is an empty interface type (literally `interface{}`) every other type will be considered to be a `generic.Type` if you are switching on the type of an object. Of course, once the specific versions are generated, this issue goes away but it's worth knowing when you are writing your tests against generic code.
### Contributions
* See the [API documentation for the parse package](http://godoc.org/github.com/cheekybits/genny/parse)
* Please do TDD
* All input welcome
-2
View File
@@ -1,2 +0,0 @@
// Package main is the command line tool for Genny.
package main
-160
View File
@@ -1,160 +0,0 @@
package main
import (
"bytes"
"flag"
"fmt"
"io"
"io/ioutil"
"net/http"
"os"
"path"
"strings"
"github.com/cheekybits/genny/parse"
)
/*
source | genny gen [-in=""] [-out=""] [-pkg=""] "KeyType=string,int ValueType=string,int"
*/
const (
_ = iota
exitcodeInvalidArgs
exitcodeInvalidTypeSet
exitcodeStdinFailed
exitcodeGenFailed
exitcodeGetFailed
exitcodeSourceFileInvalid
exitcodeDestFileFailed
)
func main() {
var (
in = flag.String("in", "", "file to parse instead of stdin")
out = flag.String("out", "", "file to save output to instead of stdout")
pkgName = flag.String("pkg", "", "package name for generated files")
prefix = "https://github.com/metabition/gennylib/raw/master/"
)
flag.Parse()
args := flag.Args()
if len(args) < 2 {
usage()
os.Exit(exitcodeInvalidArgs)
}
if strings.ToLower(args[0]) != "gen" && strings.ToLower(args[0]) != "get" {
usage()
os.Exit(exitcodeInvalidArgs)
}
// parse the typesets
var setsArg = args[1]
if strings.ToLower(args[0]) == "get" {
setsArg = args[2]
}
typeSets, err := parse.TypeSet(setsArg)
if err != nil {
fatal(exitcodeInvalidTypeSet, err)
}
var outWriter io.Writer
if len(*out) > 0 {
err := os.MkdirAll(path.Dir(*out), 0755)
if err != nil {
fatal(exitcodeDestFileFailed, err)
}
outFile, err := os.Create(*out)
if err != nil {
fatal(exitcodeDestFileFailed, err)
}
defer outFile.Close()
outWriter = outFile
} else {
outWriter = os.Stdout
}
if strings.ToLower(args[0]) == "get" {
if len(args) != 3 {
fmt.Println("not enough arguments to get")
usage()
os.Exit(exitcodeInvalidArgs)
}
r, err := http.Get(prefix + args[1])
if err != nil {
fatal(exitcodeGetFailed, err)
}
b, err := ioutil.ReadAll(r.Body)
if err != nil {
fatal(exitcodeGetFailed, err)
}
r.Body.Close()
br := bytes.NewReader(b)
err = gen(*in, *pkgName, br, typeSets, outWriter)
} else if len(*in) > 0 {
var file *os.File
file, err = os.Open(*in)
if err != nil {
fatal(exitcodeSourceFileInvalid, err)
}
defer file.Close()
err = gen(*in, *pkgName, file, typeSets, outWriter)
} else {
var source []byte
source, err = ioutil.ReadAll(os.Stdin)
if err != nil {
fatal(exitcodeStdinFailed, err)
}
reader := bytes.NewReader(source)
err = gen("stdin", *pkgName, reader, typeSets, outWriter)
}
// do the work
if err != nil {
fatal(exitcodeGenFailed, err)
}
}
func usage() {
fmt.Fprintln(os.Stderr, `usage: genny [{flags}] gen "{types}"
gen - generates type specific code from generic code.
get <package/file> - fetch a generic template from the online library and gen it.
{flags} - (optional) Command line flags (see below)
{types} - (required) Specific types for each generic type in the source
{types} format: {generic}={specific}[,another][ {generic2}={specific2}]
Examples:
Generic=Specific
Generic1=Specific1 Generic2=Specific2
Generic1=Specific1,Specific2 Generic2=Specific3,Specific4
Flags:`)
flag.PrintDefaults()
}
func fatal(code int, a ...interface{}) {
fmt.Println(a...)
os.Exit(code)
}
// gen performs the generic generation.
func gen(filename, pkgName string, in io.ReadSeeker, typesets []map[string]string, out io.Writer) error {
var output []byte
var err error
output, err = parse.Generics(filename, pkgName, in, typesets)
if err != nil {
return err
}
out.Write(output)
return nil
}
-1
View File
@@ -1 +0,0 @@
package main_test
-1
View File
@@ -1 +0,0 @@
language: go
-73
View File
@@ -1,73 +0,0 @@
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
[[projects]]
branch = "master"
name = "github.com/alecthomas/template"
packages = [".","parse"]
revision = "a0175ee3bccc567396460bf5acd36800cb10c49c"
[[projects]]
branch = "master"
name = "github.com/alecthomas/units"
packages = ["."]
revision = "2efee857e7cfd4f3d0138cc3cbb1b4966962b93a"
[[projects]]
branch = "master"
name = "github.com/cheekybits/genny"
packages = ["generic"]
revision = "9127e812e1e9e501ce899a18121d316ecb52e4ba"
[[projects]]
name = "github.com/davecgh/go-spew"
packages = ["spew"]
revision = "6d212800a42e8ab5c146b8ace3490ee17e5225f9"
[[projects]]
branch = "master"
name = "github.com/fatih/camelcase"
packages = ["."]
revision = "f6a740d52f961c60348ebb109adde9f4635d7540"
[[projects]]
name = "github.com/pkg/errors"
packages = ["."]
revision = "645ef00459ed84a119197bfb8d8205042c6df63d"
version = "v0.8.0"
[[projects]]
name = "github.com/pmezard/go-difflib"
packages = ["difflib"]
revision = "d8ed2627bdf02c080bf22230dbb337003b7aba2d"
[[projects]]
name = "github.com/stretchr/testify"
packages = ["assert"]
revision = "69483b4bd14f5845b5a1e55bca19e954e827f1d0"
version = "v1.1.4"
[[projects]]
name = "gopkg.in/alecthomas/kingpin.v2"
packages = ["."]
revision = "7f0871f2e17818990e4eed73f9b5c2f429501228"
version = "v2.2.4"
[[projects]]
branch = "v2"
name = "gopkg.in/coryb/yaml.v2"
packages = ["."]
revision = "fb7cb9628c6e3bdd76c29fb91798d51a09832470"
[[projects]]
name = "gopkg.in/op/go-logging.v1"
packages = ["."]
revision = "b2cb9fa56473e98db8caba80237377e83fe44db5"
version = "v1"
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "1879d7c016fcc8e210ca7fdeed4b099f4e7fc931d34ff47a170222c60eea8aab"
solver-name = "gps-cdcl"
solver-version = 1
-47
View File
@@ -1,47 +0,0 @@
# Gopkg.toml example
#
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
# for detailed Gopkg.toml documentation.
#
# required = ["github.com/user/thing/cmd/thing"]
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
#
# [[constraint]]
# name = "github.com/user/project"
# version = "1.0.0"
#
# [[constraint]]
# name = "github.com/user/project2"
# branch = "dev"
# source = "github.com/myfork/project2"
#
# [[override]]
# name = "github.com/x/y"
# version = "2.4.0"
[[constraint]]
name = "github.com/cheekybits/genny"
[[constraint]]
name = "github.com/fatih/camelcase"
[[constraint]]
name = "github.com/pkg/errors"
version = "0.8.0"
[[constraint]]
name = "github.com/stretchr/testify"
version = "1.1.4"
[[constraint]]
name = "gopkg.in/alecthomas/kingpin.v2"
version = "2.2.4"
[[constraint]]
name = "gopkg.in/coryb/yaml.v2"
[[constraint]]
name = "gopkg.in/op/go-logging.v1"
version = "1.0.0"
-18
View File
@@ -1,18 +0,0 @@
GENERATOR_SRC = \
rawoption.go \
$(NULL)
GENERATED_SRC = $(GENERATOR_SRC:%.go=gen-%.go)
test: $(GENERATED_SRC)
go get -t -v
go get github.com/kr/pretty
go get gopkg.in/alecthomas/kingpin.v2
go test
gen-%.go: %.go
# use github.com/cheekybits/genny after https://github.com/cheekybits/genny/pull/42 is merged
go get github.com/coryb/genny
go generate
.PHONY: test
-4
View File
@@ -1,4 +0,0 @@
[![Build Status](https://travis-ci.org/coryb/figtree.svg?branch=master)](https://travis-ci.org/coryb/figtree)
[![GoDoc](https://godoc.org/github.com/coryb/figtree?status.png)](https://godoc.org/github.com/coryb/figtree)
Figtree is a go library to recusively parse and merge yaml based config files.
-113
View File
@@ -1,113 +0,0 @@
package figtree
import (
"os"
"sort"
"strings"
"testing"
"github.com/stretchr/testify/assert"
)
func TestOptionsEnv(t *testing.T) {
opts := TestOptions{}
os.Chdir("d1")
defer os.Chdir("..")
StringifyValue = true
defer func() {
StringifyValue = false
}()
os.Clearenv()
err := LoadAllConfigs("figtree.yml", &opts)
assert.Nil(t, err)
got := []string{}
for _, env := range os.Environ() {
if strings.HasPrefix(env, "FIGTREE_") {
got = append(got, env)
}
}
sort.StringSlice(got).Sort()
expected := []string{
"FIGTREE_ARRAY_1=[\"d1arr1val1\",\"d1arr1val2\",\"dupval\"]",
"FIGTREE_BOOL_1=true",
"FIGTREE_FLOAT_1=1.11",
"FIGTREE_INT_1=111",
"FIGTREE_MAP_1={\"dup\":\"d1dupval\",\"key0\":\"d1map1val0\",\"key1\":\"d1map1val1\"}",
"FIGTREE_STRING_1=d1str1val1",
}
assert.Equal(t, expected, got)
}
func TestOptionsNamedEnv(t *testing.T) {
opts := TestOptions{}
os.Chdir("d1")
defer os.Chdir("..")
StringifyValue = true
defer func() {
StringifyValue = false
}()
os.Clearenv()
fig := NewFigTree()
fig.EnvPrefix = "TEST"
err := fig.LoadAllConfigs("figtree.yml", &opts)
assert.Nil(t, err)
got := []string{}
for _, env := range os.Environ() {
if strings.HasPrefix(env, "FIGTREE_") || strings.HasPrefix(env, "TEST_") {
got = append(got, env)
}
}
sort.StringSlice(got).Sort()
expected := []string{
"TEST_ARRAY_1=[\"d1arr1val1\",\"d1arr1val2\",\"dupval\"]",
"TEST_BOOL_1=true",
"TEST_FLOAT_1=1.11",
"TEST_INT_1=111",
"TEST_MAP_1={\"dup\":\"d1dupval\",\"key0\":\"d1map1val0\",\"key1\":\"d1map1val1\"}",
"TEST_STRING_1=d1str1val1",
}
assert.Equal(t, expected, got)
}
func TestBuiltinEnv(t *testing.T) {
opts := TestBuiltin{}
os.Chdir("d1")
defer os.Chdir("..")
os.Clearenv()
err := LoadAllConfigs("figtree.yml", &opts)
assert.Nil(t, err)
got := []string{}
for _, env := range os.Environ() {
if strings.HasPrefix(env, "FIGTREE_") {
got = append(got, env)
}
}
sort.StringSlice(got).Sort()
expected := []string{
"FIGTREE_ARRAY_1=[\"d1arr1val1\",\"d1arr1val2\",\"dupval\"]",
"FIGTREE_BOOL_1=true",
"FIGTREE_FLOAT_1=1.11",
"FIGTREE_INT_1=111",
"FIGTREE_LEAVE_EMPTY=",
"FIGTREE_MAP_1={\"dup\":\"d1dupval\",\"key0\":\"d1map1val0\",\"key1\":\"d1map1val1\"}",
"FIGTREE_STRING_1=d1str1val1",
}
assert.Equal(t, expected, got)
}
-190
View File
@@ -1,190 +0,0 @@
package figtree
import (
"os"
"testing"
"github.com/stretchr/testify/assert"
)
func TestOptionsExecConfigD3(t *testing.T) {
opts := TestOptions{}
os.Chdir("d1/d2/d3")
defer os.Chdir("../../..")
arr1 := []StringOption{}
arr1 = append(arr1, StringOption{"exec.yml", true, "d3arr1val1"})
arr1 = append(arr1, StringOption{"exec.yml", true, "d3arr1val2"})
arr1 = append(arr1, StringOption{"../exec.yml", true, "d2arr1val1"})
arr1 = append(arr1, StringOption{"../exec.yml", true, "d2arr1val2"})
arr1 = append(arr1, StringOption{"../../exec.yml", true, "d1arr1val1"})
arr1 = append(arr1, StringOption{"../../exec.yml", true, "d1arr1val2"})
expected := TestOptions{
String1: StringOption{"exec.yml", true, "d3str1val1"},
LeaveEmpty: StringOption{},
Array1: arr1,
Map1: map[string]StringOption{
"key0": StringOption{"../../exec.yml", true, "d1map1val0"},
"key1": StringOption{"../exec.yml", true, "d2map1val1"},
"key2": StringOption{"exec.yml", true, "d3map1val2"},
"key3": StringOption{"exec.yml", true, "d3map1val3"},
},
Int1: IntOption{"exec.yml", true, 333},
Float1: Float32Option{"exec.yml", true, 3.33},
Bool1: BoolOption{"exec.yml", true, true},
}
err := LoadAllConfigs("exec.yml", &opts)
assert.Nil(t, err)
assert.Exactly(t, expected, opts)
}
func TestOptionsExecConfigD2(t *testing.T) {
opts := TestOptions{}
os.Chdir("d1/d2")
defer os.Chdir("../..")
arr1 := []StringOption{}
arr1 = append(arr1, StringOption{"exec.yml", true, "d2arr1val1"})
arr1 = append(arr1, StringOption{"exec.yml", true, "d2arr1val2"})
arr1 = append(arr1, StringOption{"../exec.yml", true, "d1arr1val1"})
arr1 = append(arr1, StringOption{"../exec.yml", true, "d1arr1val2"})
expected := TestOptions{
String1: StringOption{"exec.yml", true, "d2str1val1"},
LeaveEmpty: StringOption{},
Array1: arr1,
Map1: map[string]StringOption{
"key0": StringOption{"../exec.yml", true, "d1map1val0"},
"key1": StringOption{"exec.yml", true, "d2map1val1"},
"key2": StringOption{"exec.yml", true, "d2map1val2"},
},
Int1: IntOption{"exec.yml", true, 222},
Float1: Float32Option{"exec.yml", true, 2.22},
Bool1: BoolOption{"exec.yml", true, false},
}
err := LoadAllConfigs("exec.yml", &opts)
assert.Nil(t, err)
assert.Exactly(t, expected, opts)
}
func TestOptionsExecConfigD1(t *testing.T) {
opts := TestOptions{}
os.Chdir("d1")
defer os.Chdir("..")
arr1 := []StringOption{}
arr1 = append(arr1, StringOption{"exec.yml", true, "d1arr1val1"})
arr1 = append(arr1, StringOption{"exec.yml", true, "d1arr1val2"})
expected := TestOptions{
String1: StringOption{"exec.yml", true, "d1str1val1"},
LeaveEmpty: StringOption{},
Array1: arr1,
Map1: map[string]StringOption{
"key0": StringOption{"exec.yml", true, "d1map1val0"},
"key1": StringOption{"exec.yml", true, "d1map1val1"},
},
Int1: IntOption{"exec.yml", true, 111},
Float1: Float32Option{"exec.yml", true, 1.11},
Bool1: BoolOption{"exec.yml", true, true},
}
err := LoadAllConfigs("exec.yml", &opts)
assert.Nil(t, err)
assert.Exactly(t, expected, opts)
}
func TestBuiltinExecConfigD3(t *testing.T) {
opts := TestBuiltin{}
os.Chdir("d1/d2/d3")
defer os.Chdir("../../..")
arr1 := []string{}
arr1 = append(arr1, "d3arr1val1")
arr1 = append(arr1, "d3arr1val2")
arr1 = append(arr1, "d2arr1val1")
arr1 = append(arr1, "d2arr1val2")
arr1 = append(arr1, "d1arr1val1")
arr1 = append(arr1, "d1arr1val2")
expected := TestBuiltin{
String1: "d3str1val1",
LeaveEmpty: "",
Array1: arr1,
Map1: map[string]string{
"key0": "d1map1val0",
"key1": "d2map1val1",
"key2": "d3map1val2",
"key3": "d3map1val3",
},
Int1: 333,
Float1: 3.33,
Bool1: true,
}
err := LoadAllConfigs("exec.yml", &opts)
assert.Nil(t, err)
assert.Exactly(t, expected, opts)
}
func TestBuiltinExecConfigD2(t *testing.T) {
opts := TestBuiltin{}
os.Chdir("d1/d2")
defer os.Chdir("../..")
arr1 := []string{}
arr1 = append(arr1, "d2arr1val1")
arr1 = append(arr1, "d2arr1val2")
arr1 = append(arr1, "d1arr1val1")
arr1 = append(arr1, "d1arr1val2")
expected := TestBuiltin{
String1: "d2str1val1",
LeaveEmpty: "",
Array1: arr1,
Map1: map[string]string{
"key0": "d1map1val0",
"key1": "d2map1val1",
"key2": "d2map1val2",
},
Int1: 222,
Float1: 2.22,
// note this will be true from d1/exec.yml since the
// d1/d2/exec.yml set it to false which is a zero value
Bool1: true,
}
err := LoadAllConfigs("exec.yml", &opts)
assert.Nil(t, err)
assert.Exactly(t, expected, opts)
}
func TestBuiltinExecConfigD1(t *testing.T) {
opts := TestBuiltin{}
os.Chdir("d1")
defer os.Chdir("..")
arr1 := []string{}
arr1 = append(arr1, "d1arr1val1")
arr1 = append(arr1, "d1arr1val2")
expected := TestBuiltin{
String1: "d1str1val1",
LeaveEmpty: "",
Array1: arr1,
Map1: map[string]string{
"key0": "d1map1val0",
"key1": "d1map1val1",
},
Int1: 111,
Float1: 1.11,
Bool1: true,
}
err := LoadAllConfigs("exec.yml", &opts)
assert.Nil(t, err)
assert.Exactly(t, expected, opts)
}
+625 -129
View File
@@ -10,6 +10,8 @@ import (
"path"
"path/filepath"
"reflect"
"regexp"
"sort"
"strings"
"unicode"
@@ -17,69 +19,159 @@ import (
"github.com/pkg/errors"
yaml "gopkg.in/coryb/yaml.v2"
logging "gopkg.in/op/go-logging.v1"
)
var log = logging.MustGetLogger("figtree")
type FigTree struct {
ConfigDir string
Defaults interface{}
EnvPrefix string
stop bool
type Logger interface {
Debugf(format string, args ...interface{})
}
func NewFigTree() *FigTree {
return &FigTree{
EnvPrefix: "FIGTREE",
type nullLogger struct{}
func (*nullLogger) Debugf(string, ...interface{}) {}
var Log Logger = &nullLogger{}
func defaultApplyChangeSet(changeSet map[string]*string) error {
for k, v := range changeSet {
if v != nil {
os.Setenv(k, *v)
} else {
os.Unsetenv(k)
}
}
return nil
}
type Option func(*FigTree)
func WithHome(home string) Option {
return func(f *FigTree) {
f.home = home
}
}
func LoadAllConfigs(configFile string, options interface{}) error {
return NewFigTree().LoadAllConfigs(configFile, options)
func WithCwd(cwd string) Option {
return func(f *FigTree) {
f.workDir = cwd
}
}
func LoadConfig(configFile string, options interface{}) error {
return NewFigTree().LoadConfig(configFile, options)
func WithEnvPrefix(env string) Option {
return func(f *FigTree) {
f.envPrefix = env
}
}
func WithConfigDir(dir string) Option {
return func(f *FigTree) {
f.configDir = dir
}
}
type ChangeSetFunc func(map[string]*string) error
func WithApplyChangeSet(apply ChangeSetFunc) Option {
return func(f *FigTree) {
f.applyChangeSet = apply
}
}
type PreProcessor func([]byte) ([]byte, error)
func WithPreProcessor(pp PreProcessor) Option {
return func(f *FigTree) {
f.preProcessor = pp
}
}
type FigTree struct {
home string
workDir string
configDir string
envPrefix string
preProcessor PreProcessor
stop bool
applyChangeSet ChangeSetFunc
}
func NewFigTree(opts ...Option) *FigTree {
wd, _ := os.Getwd()
fig := &FigTree{
home: os.Getenv("HOME"),
workDir: wd,
envPrefix: "FIGTREE",
applyChangeSet: defaultApplyChangeSet,
}
for _, opt := range opts {
opt(fig)
}
return fig
}
func (f *FigTree) WithHome(home string) {
WithHome(home)(f)
}
func (f *FigTree) WithCwd(cwd string) {
WithCwd(cwd)(f)
}
func (f *FigTree) WithEnvPrefix(env string) {
WithEnvPrefix(env)(f)
}
func (f *FigTree) WithConfigDir(dir string) {
WithConfigDir(dir)(f)
}
func (f *FigTree) WithPreProcessor(pp PreProcessor) {
WithPreProcessor(pp)(f)
}
func (f *FigTree) WithApplyChangeSet(apply ChangeSetFunc) {
WithApplyChangeSet(apply)(f)
}
func (f *FigTree) WithIgnoreChangeSet() {
WithApplyChangeSet(func(_ map[string]*string) error {
return nil
})(f)
}
func (f *FigTree) Copy() *FigTree {
cp := *f
return &cp
}
func (f *FigTree) LoadAllConfigs(configFile string, options interface{}) error {
// reset from any previous config parsing runs
f.stop = false
if f.ConfigDir != "" {
configFile = path.Join(f.ConfigDir, configFile)
if f.configDir != "" {
configFile = path.Join(f.configDir, configFile)
}
paths := FindParentPaths(configFile)
paths := FindParentPaths(f.home, f.workDir, configFile)
paths = append([]string{fmt.Sprintf("/etc/%s", configFile)}, paths...)
// iterate paths in reverse
for i := len(paths) - 1; i >= 0; i-- {
file := paths[i]
err := f.LoadConfig(file, options)
if err != nil {
if err := f.LoadConfig(file, options); err != nil {
return err
}
if f.stop {
break
}
}
// apply defaults at the end to set any undefined fields
if f.Defaults != nil {
m := &merger{sourceFile: "default"}
m.mergeStructs(
reflect.ValueOf(options),
reflect.ValueOf(f.Defaults),
)
f.populateEnv(options)
}
return nil
}
func (f *FigTree) LoadConfigBytes(config []byte, source string, options interface{}) (err error) {
f.populateEnv(options)
func (f *FigTree) LoadConfigBytes(config []byte, source string, options interface{}) error {
if !reflect.ValueOf(options).IsValid() {
return fmt.Errorf("options argument [%#v] is not valid", options)
}
defer func(mapType, iface reflect.Type) {
yaml.DefaultMapType = mapType
@@ -89,7 +181,15 @@ func (f *FigTree) LoadConfigBytes(config []byte, source string, options interfac
yaml.DefaultMapType = reflect.TypeOf(map[string]interface{}{})
yaml.IfaceType = yaml.DefaultMapType.Elem()
m := &merger{sourceFile: source}
var err error
if f.preProcessor != nil {
config, err = f.preProcessor(config)
if err != nil {
return errors.Wrapf(err, "Failed to process config file: %s", source)
}
}
m := NewMerger(WithSourceFile(source))
type tmpOpts struct {
Config ConfigOptions
}
@@ -98,13 +198,13 @@ func (f *FigTree) LoadConfigBytes(config []byte, source string, options interfac
// look for config settings first
err = yaml.Unmarshal(config, m)
if err != nil {
return errors.Wrap(err, fmt.Sprintf("Unable to parse %s", source))
return errors.Wrapf(err, "Unable to parse %s", source)
}
// then parse document into requested struct
err = yaml.Unmarshal(config, tmp)
if err != nil {
return errors.Wrap(err, fmt.Sprintf("Unable to parse %s", source))
return errors.Wrapf(err, "Unable to parse %s", source)
}
m.setSource(reflect.ValueOf(tmp))
@@ -112,40 +212,35 @@ func (f *FigTree) LoadConfigBytes(config []byte, source string, options interfac
reflect.ValueOf(options),
reflect.ValueOf(tmp),
)
f.populateEnv(options)
changeSet := f.PopulateEnv(options)
if m.Config.Stop {
f.stop = true
return nil
return f.applyChangeSet(changeSet)
}
return nil
return f.applyChangeSet(changeSet)
}
func (f *FigTree) LoadConfig(file string, options interface{}) (err error) {
basePath, err := os.Getwd()
if err != nil {
return err
}
rel, err := filepath.Rel(basePath, file)
func (f *FigTree) LoadConfig(file string, options interface{}) error {
rel, err := filepath.Rel(f.workDir, file)
if err != nil {
rel = file
}
if stat, err := os.Stat(file); err == nil {
if stat.Mode()&0111 == 0 {
log.Debugf("Loading config %s", file)
Log.Debugf("Loading config %s", file)
if data, err := ioutil.ReadFile(file); err == nil {
return f.LoadConfigBytes(data, rel, options)
}
} else {
log.Debugf("Found Executable Config file: %s", file)
Log.Debugf("Found Executable Config file: %s", file)
// it is executable, so run it and try to parse the output
cmd := exec.Command(file)
stdout := bytes.NewBufferString("")
cmd.Stdout = stdout
cmd.Stderr = bytes.NewBufferString("")
if err := cmd.Run(); err != nil {
return errors.Wrap(err, fmt.Sprintf("%s is exectuable, but it failed to execute:\n%s", file, cmd.Stderr))
return errors.Wrapf(err, "%s is exectuable, but it failed to execute:\n%s", file, cmd.Stderr)
}
return f.LoadConfigBytes(stdout.Bytes(), rel, options)
}
@@ -153,17 +248,261 @@ func (f *FigTree) LoadConfig(file string, options interface{}) (err error) {
return nil
}
func FindParentPaths(homedir, cwd, fileName string) []string {
paths := make([]string, 0)
// special case if homedir is not in current path then check there anyway
if !strings.HasPrefix(cwd, homedir) {
file := path.Join(homedir, fileName)
if _, err := os.Stat(file); err == nil {
paths = append(paths, filepath.FromSlash(file))
}
}
var dir string
for _, part := range strings.Split(cwd, string(os.PathSeparator)) {
if part == "" && dir == "" {
dir = "/"
} else {
dir = path.Join(dir, part)
}
file := path.Join(dir, fileName)
if _, err := os.Stat(file); err == nil {
paths = append(paths, filepath.FromSlash(file))
}
}
return paths
}
func (f *FigTree) FindParentPaths(fileName string) []string {
return FindParentPaths(f.home, f.workDir, fileName)
}
var camelCaseWords = regexp.MustCompile("[0-9A-Za-z]+")
func camelCase(name string) string {
words := camelCaseWords.FindAllString(name, -1)
for i, word := range words {
words[i] = strings.Title(word)
}
return strings.Join(words, "")
}
type Merger struct {
sourceFile string
preserveMap map[string]struct{}
Config ConfigOptions `json:"config,omitempty" yaml:"config,omitempty"`
}
type MergeOption func(*Merger)
func WithSourceFile(source string) MergeOption {
return func(m *Merger) {
m.sourceFile = source
}
}
func PreserveMap(keys ...string) MergeOption {
return func(m *Merger) {
for _, key := range keys {
m.preserveMap[key] = struct{}{}
}
}
}
func NewMerger(options ...MergeOption) *Merger {
m := &Merger{
sourceFile: "merge",
preserveMap: make(map[string]struct{}),
}
for _, opt := range options {
opt(m)
}
return m
}
// Merge will attempt to merge the data from src into dst. They shoud be either both maps or both structs.
// The structs do not need to have the same structure, but any field name that exists in both
// structs will must be the same type.
func Merge(dst, src interface{}) {
m := NewMerger()
m.mergeStructs(reflect.ValueOf(dst), reflect.ValueOf(src))
}
// MakeMergeStruct will take multiple structs and return a pointer to a zero value for the
// anonymous struct that has all the public fields from all the structs merged into one struct.
// If there are multiple structs with the same field names, the first appearance of that name
// will be used.
func MakeMergeStruct(structs ...interface{}) interface{} {
m := NewMerger()
return m.MakeMergeStruct(structs...)
}
func (m *Merger) MakeMergeStruct(structs ...interface{}) interface{} {
values := []reflect.Value{}
for _, data := range structs {
values = append(values, reflect.ValueOf(data))
}
return m.makeMergeStruct(values...).Interface()
}
func inlineField(field reflect.StructField) bool {
if tag := field.Tag.Get("figtree"); tag != "" {
return strings.HasSuffix(tag, ",inline")
}
if tag := field.Tag.Get("yaml"); tag != "" {
return strings.HasSuffix(tag, ",inline")
}
return false
}
func (m *Merger) makeMergeStruct(values ...reflect.Value) reflect.Value {
foundFields := map[string]reflect.StructField{}
for i := 0; i < len(values); i++ {
v := values[i]
if v.Kind() == reflect.Ptr {
v = v.Elem()
}
typ := v.Type()
var field reflect.StructField
if typ.Kind() == reflect.Struct {
for i := 0; i < typ.NumField(); i++ {
field = typ.Field(i)
if field.PkgPath != "" {
// unexported field, skip
continue
}
if f, ok := foundFields[field.Name]; ok {
if f.Type.Kind() == reflect.Struct && field.Type.Kind() == reflect.Struct {
if fName, fieldName := f.Type.Name(), field.Type.Name(); fName == "" || fieldName == "" || fName != fieldName {
// we have 2 fields with the same name and they are both structs, so we need
// to merge the existing struct with the new one in case they are different
newval := m.makeMergeStruct(reflect.New(f.Type).Elem(), reflect.New(field.Type).Elem()).Elem()
f.Type = newval.Type()
foundFields[field.Name] = f
}
}
// field already found, skip
continue
}
if inlineField(field) {
values = append(values, v.Field(i))
continue
}
foundFields[field.Name] = field
}
} else if typ.Kind() == reflect.Map {
for _, key := range v.MapKeys() {
keyval := reflect.ValueOf(v.MapIndex(key).Interface())
if _, ok := m.preserveMap[key.String()]; !ok {
if keyval.Kind() == reflect.Ptr && keyval.Elem().Kind() == reflect.Map {
keyval = m.makeMergeStruct(keyval.Elem())
} else if keyval.Kind() == reflect.Map {
keyval = m.makeMergeStruct(keyval).Elem()
}
}
var t reflect.Type
if !keyval.IsValid() {
// this nonsense is to create a generic `interface{}` type. There is
// probably an easier to do this, but it eludes me at the moment.
var dummy interface{}
t = reflect.ValueOf(&dummy).Elem().Type()
} else {
t = reflect.ValueOf(keyval.Interface()).Type()
}
field = reflect.StructField{
Name: camelCase(key.String()),
Type: t,
Tag: reflect.StructTag(fmt.Sprintf(`json:"%s" yaml:"%s"`, key.String(), key.String())),
}
if f, ok := foundFields[field.Name]; ok {
if f.Type.Kind() == reflect.Struct && t.Kind() == reflect.Struct {
if fName, tName := f.Type.Name(), t.Name(); fName == "" || tName == "" || fName != tName {
// we have 2 fields with the same name and they are both structs, so we need
// to merge the existig struct with the new one in case they are different
newval := m.makeMergeStruct(reflect.New(f.Type).Elem(), reflect.New(t).Elem()).Elem()
f.Type = newval.Type()
foundFields[field.Name] = f
}
}
// field already found, skip
continue
}
foundFields[field.Name] = field
}
}
}
fields := []reflect.StructField{}
for _, value := range foundFields {
fields = append(fields, value)
}
sort.Slice(fields, func(i, j int) bool {
return fields[i].Name < fields[j].Name
})
newType := reflect.StructOf(fields)
return reflect.New(newType)
}
func (m *Merger) mapToStruct(src reflect.Value) reflect.Value {
if src.Kind() != reflect.Map {
return reflect.Value{}
}
dest := m.makeMergeStruct(src)
if dest.Kind() == reflect.Ptr {
dest = dest.Elem()
}
for _, key := range src.MapKeys() {
structFieldName := camelCase(key.String())
keyval := reflect.ValueOf(src.MapIndex(key).Interface())
// skip invalid (ie nil) key values
if !keyval.IsValid() {
continue
}
if keyval.Kind() == reflect.Ptr && keyval.Elem().Kind() == reflect.Map {
keyval = m.mapToStruct(keyval.Elem()).Addr()
m.mergeStructs(dest.FieldByName(structFieldName), reflect.ValueOf(keyval.Interface()))
} else if keyval.Kind() == reflect.Map {
keyval = m.mapToStruct(keyval)
m.mergeStructs(dest.FieldByName(structFieldName), reflect.ValueOf(keyval.Interface()))
} else {
dest.FieldByName(structFieldName).Set(reflect.ValueOf(keyval.Interface()))
}
}
return dest
}
func structToMap(src reflect.Value) reflect.Value {
if src.Kind() != reflect.Struct {
return reflect.Value{}
}
dest := reflect.ValueOf(map[string]interface{}{})
typ := src.Type()
for i := 0; i < typ.NumField(); i++ {
structField := typ.Field(i)
if structField.PkgPath != "" {
// skip private fields
continue
}
name := yamlFieldName(structField)
dest.SetMapIndex(reflect.ValueOf(name), src.Field(i))
}
return dest
}
type ConfigOptions struct {
Overwrite []string `json:"overwrite,omitempty" yaml:"overwrite,omitempty"`
Stop bool `json:"stop,omitempty" yaml:"stop,omitempty"`
// Merge bool `json:"merge,omitempty" yaml:"merge,omitempty"`
}
type merger struct {
sourceFile string
Config ConfigOptions `json:"config,omitempty" yaml:"config,omitempty"`
}
func yamlFieldName(sf reflect.StructField) string {
if tag, ok := sf.Tag.Lookup("yaml"); ok {
// with yaml:"foobar,omitempty"
@@ -171,10 +510,16 @@ func yamlFieldName(sf reflect.StructField) string {
parts := strings.Split(tag, ",")
return parts[0]
}
return sf.Name
// guess the field name from reversing camel case
// so "FooBar" becomes "foo-bar"
parts := camelcase.Split(sf.Name)
for i := range parts {
parts[i] = strings.ToLower(parts[i])
}
return strings.Join(parts, "-")
}
func (m *merger) mustOverwrite(name string) bool {
func (m *Merger) mustOverwrite(name string) bool {
for _, prop := range m.Config.Overwrite {
if name == prop {
return true
@@ -185,7 +530,7 @@ func (m *merger) mustOverwrite(name string) bool {
func isDefault(v reflect.Value) bool {
if v.CanAddr() {
if option, ok := v.Addr().Interface().(Option); ok {
if option, ok := v.Addr().Interface().(option); ok {
if option.GetSource() == "default" {
return true
}
@@ -194,7 +539,10 @@ func isDefault(v reflect.Value) bool {
return false
}
func isEmpty(v reflect.Value) bool {
func isZero(v reflect.Value) bool {
if !v.IsValid() {
return true
}
return reflect.DeepEqual(v.Interface(), reflect.Zero(v.Type()).Interface())
}
@@ -203,7 +551,7 @@ func isSame(v1, v2 reflect.Value) bool {
}
// recursively set the Source attribute of the Options
func (m *merger) setSource(v reflect.Value) {
func (m *Merger) setSource(v reflect.Value) {
if v.Kind() == reflect.Ptr {
v = v.Elem()
}
@@ -222,7 +570,7 @@ func (m *merger) setSource(v reflect.Value) {
}
case reflect.Struct:
if v.CanAddr() {
if option, ok := v.Addr().Interface().(Option); ok {
if option, ok := v.Addr().Interface().(option); ok {
if option.IsDefined() {
option.SetSource(m.sourceFile)
}
@@ -247,23 +595,117 @@ func (m *merger) setSource(v reflect.Value) {
}
}
func (m *merger) mergeStructs(ov, nv reflect.Value) {
if ov.Kind() == reflect.Ptr {
ov = ov.Elem()
func (m *Merger) assignValue(dest, src reflect.Value, overwrite bool) {
if src.Type().AssignableTo(dest.Type()) {
if (isZero(dest) || isDefault(dest) || overwrite) && !isZero(src) {
dest.Set(src)
return
}
return
}
if nv.Kind() == reflect.Ptr {
nv = nv.Elem()
if dest.CanAddr() {
if option, ok := dest.Addr().Interface().(option); ok {
destOptionValue := reflect.ValueOf(option.GetValue())
// map interface type to real-ish type:
src = reflect.ValueOf(src.Interface())
if !src.IsValid() {
Log.Debugf("assignValue: src isValid: %t", src.IsValid())
return
}
if src.Type().AssignableTo(destOptionValue.Type()) {
option.SetValue(src.Interface())
option.SetSource(m.sourceFile)
Log.Debugf("assignValue: assigned %#v to %#v", destOptionValue, src)
return
} else {
panic(fmt.Errorf("%s is not assinable to %s", src.Type(), destOptionValue.Type()))
}
}
}
if ov.Kind() == reflect.Map && nv.Kind() == reflect.Map {
// make copy so we can reliably Addr it to see if it fits the
// Option interface.
srcCopy := reflect.New(src.Type()).Elem()
srcCopy.Set(src)
if option, ok := srcCopy.Addr().Interface().(option); ok {
srcOptionValue := reflect.ValueOf(option.GetValue())
if srcOptionValue.Type().AssignableTo(dest.Type()) {
m.assignValue(dest, srcOptionValue, overwrite)
return
} else {
panic(fmt.Errorf("%s is not assinable to %s", srcOptionValue.Type(), dest.Type()))
}
}
}
func fromInterface(v reflect.Value) (reflect.Value, func()) {
if v.Kind() == reflect.Interface {
realV := reflect.ValueOf(v.Interface())
if !realV.IsValid() {
realV = reflect.New(v.Type()).Elem()
v.Set(realV)
return v, func() {}
}
tmp := reflect.New(realV.Type()).Elem()
tmp.Set(realV)
return tmp, func() {
v.Set(tmp)
}
}
return v, func() {}
}
func (m *Merger) mergeStructs(ov, nv reflect.Value) {
ov = reflect.Indirect(ov)
nv = reflect.Indirect(nv)
ov, restore := fromInterface(ov)
defer restore()
if nv.Kind() == reflect.Interface {
nv = reflect.ValueOf(nv.Interface())
}
if ov.Kind() == reflect.Map {
if nv.Kind() == reflect.Struct {
nv = structToMap(nv)
}
m.mergeMaps(ov, nv)
return
}
if ov.Kind() == reflect.Struct && nv.Kind() == reflect.Map {
nv = m.mapToStruct(nv)
}
if !ov.IsValid() || !nv.IsValid() {
Log.Debugf("Valid: ov:%v nv:%t", ov.IsValid(), nv.IsValid())
return
}
for i := 0; i < nv.NumField(); i++ {
ovStructField := ov.Type().Field(i)
nvField := nv.Field(i)
if nvField.Kind() == reflect.Interface {
nvField = reflect.ValueOf(nvField.Interface())
}
if !nvField.IsValid() {
continue
}
nvStructField := nv.Type().Field(i)
ovStructField, ok := ov.Type().FieldByName(nvStructField.Name)
if !ok {
if nvStructField.Anonymous {
// this is an embedded struct, and the destination does not contain
// the same embeded struct, so try to merge the embedded struct
// directly with the destination
m.mergeStructs(ov, nvField)
continue
}
// if original value does not have the same struct field
// then just skip this field.
continue
}
// PkgPath is empty for upper case (exported) field names.
if ovStructField.PkgPath != "" || nvStructField.PkgPath != "" {
// unexported field, skipping
@@ -271,95 +713,133 @@ func (m *merger) mergeStructs(ov, nv reflect.Value) {
}
fieldName := yamlFieldName(ovStructField)
if (isEmpty(ov.Field(i)) || isDefault(ov.Field(i)) || m.mustOverwrite(fieldName)) && !isEmpty(nv.Field(i)) && !isSame(ov.Field(i), nv.Field(i)) {
log.Debugf("Setting %s to %#v", nv.Type().Field(i).Name, nv.Field(i).Interface())
ov.Field(i).Set(nv.Field(i))
} else {
switch ov.Field(i).Kind() {
case reflect.Map:
if nv.Field(i).Len() > 0 {
log.Debugf("Merging: %v with %v", ov.Field(i), nv.Field(i))
m.mergeMaps(ov.Field(i), nv.Field(i))
}
case reflect.Slice:
if nv.Field(i).Len() > 0 {
log.Debugf("Merging: %v with %v", ov.Field(i), nv.Field(i))
if ov.Field(i).CanSet() {
if ov.Field(i).Len() == 0 {
ov.Field(i).Set(nv.Field(i))
} else {
log.Debugf("Merging: %v with %v", ov.Field(i), nv.Field(i))
ov.Field(i).Set(m.mergeArrays(ov.Field(i), nv.Field(i)))
}
}
ovField := ov.FieldByName(nvStructField.Name)
ovField, restore := fromInterface(ovField)
defer restore()
}
case reflect.Array:
if nv.Field(i).Len() > 0 {
log.Debugf("Merging: %v with %v", ov.Field(i), nv.Field(i))
ov.Field(i).Set(m.mergeArrays(ov.Field(i), nv.Field(i)))
}
case reflect.Struct:
// only merge structs if they are not an Option type:
if _, ok := ov.Field(i).Addr().Interface().(Option); !ok {
log.Debugf("Merging: %v with %v", ov.Field(i), nv.Field(i))
m.mergeStructs(ov.Field(i), nv.Field(i))
}
if (isZero(ovField) || isDefault(ovField) || m.mustOverwrite(fieldName)) && !isSame(ovField, nvField) {
Log.Debugf("Setting %s to %#v", nv.Type().Field(i).Name, nvField.Interface())
m.assignValue(ovField, nvField, m.mustOverwrite(fieldName))
}
switch ovField.Kind() {
case reflect.Map:
Log.Debugf("Merging Map: %#v with %#v", ovField, nvField)
m.mergeStructs(ovField, nvField)
case reflect.Slice:
if nvField.Len() > 0 {
Log.Debugf("Merging Slice: %#v with %#v", ovField, nvField)
ovField.Set(m.mergeArrays(ovField, nvField))
}
case reflect.Array:
if nvField.Len() > 0 {
Log.Debugf("Merging Array: %v with %v", ovField, nvField)
ovField.Set(m.mergeArrays(ovField, nvField))
}
case reflect.Struct:
// only merge structs if they are not an Option type:
if _, ok := ovField.Addr().Interface().(option); !ok {
Log.Debugf("Merging Struct: %v with %v", ovField, nvField)
m.mergeStructs(ovField, nvField)
}
}
}
}
func (m *merger) mergeMaps(ov, nv reflect.Value) {
func (m *Merger) mergeMaps(ov, nv reflect.Value) {
for _, key := range nv.MapKeys() {
if !ov.MapIndex(key).IsValid() {
log.Debugf("Setting %v to %#v", key.Interface(), nv.MapIndex(key).Interface())
ov.SetMapIndex(key, nv.MapIndex(key))
ovElem := reflect.New(ov.Type().Elem()).Elem()
m.assignValue(ovElem, nv.MapIndex(key), false)
if ov.IsNil() {
if !ov.CanSet() {
continue
}
ov.Set(reflect.MakeMap(ov.Type()))
}
Log.Debugf("Setting %v to %#v", key.Interface(), ovElem.Interface())
ov.SetMapIndex(key, ovElem)
} else {
ovi := reflect.ValueOf(ov.MapIndex(key).Interface())
nvi := reflect.ValueOf(nv.MapIndex(key).Interface())
if !nvi.IsValid() {
continue
}
switch ovi.Kind() {
case reflect.Map:
log.Debugf("Merging: %v with %v", ovi.Interface(), nvi.Interface())
m.mergeMaps(ovi, nvi)
Log.Debugf("Merging: %v with %v", ovi.Interface(), nvi.Interface())
m.mergeStructs(ovi, nvi)
case reflect.Slice:
log.Debugf("Merging: %v with %v", ovi.Interface(), nvi.Interface())
Log.Debugf("Merging: %v with %v", ovi.Interface(), nvi.Interface())
ov.SetMapIndex(key, m.mergeArrays(ovi, nvi))
case reflect.Array:
log.Debugf("Merging: %v with %v", ovi.Interface(), nvi.Interface())
Log.Debugf("Merging: %v with %v", ovi.Interface(), nvi.Interface())
ov.SetMapIndex(key, m.mergeArrays(ovi, nvi))
default:
if isZero(ovi) {
if !ovi.IsValid() || nvi.Type().AssignableTo(ovi.Type()) {
ov.SetMapIndex(key, nvi)
} else {
// to check for the Option interface we need the Addr of the value, but
// we cannot take the Addr of a map value, so we have to first copy
// it, meh not optimal
newVal := reflect.New(nvi.Type())
newVal.Elem().Set(nvi)
if nOption, ok := newVal.Interface().(option); ok {
ov.SetMapIndex(key, reflect.ValueOf(nOption.GetValue()))
continue
}
panic(fmt.Errorf("map value %T is not assignable to %T", nvi.Interface(), ovi.Interface()))
}
}
}
}
}
}
func (m *merger) mergeArrays(ov, nv reflect.Value) reflect.Value {
func (m *Merger) mergeArrays(ov, nv reflect.Value) reflect.Value {
var zero interface{}
Outer:
for ni := 0; ni < nv.Len(); ni++ {
niv := nv.Index(ni)
n := niv
if n.CanAddr() {
if nOption, ok := n.Addr().Interface().(option); ok {
if !nOption.IsDefined() {
continue
}
n = reflect.ValueOf(nOption.GetValue())
}
}
if reflect.DeepEqual(n.Interface(), zero) {
continue
}
for oi := 0; oi < ov.Len(); oi++ {
oiv := ov.Index(oi)
if oiv.CanAddr() && niv.CanAddr() {
if oOption, ok := oiv.Addr().Interface().(Option); ok {
if nOption, ok := niv.Addr().Interface().(Option); ok {
if reflect.DeepEqual(oOption.GetValue(), nOption.GetValue()) {
continue Outer
}
}
o := ov.Index(oi)
if o.CanAddr() {
if oOption, ok := o.Addr().Interface().(option); ok {
o = reflect.ValueOf(oOption.GetValue())
}
}
if reflect.DeepEqual(niv.Interface(), oiv.Interface()) {
if reflect.DeepEqual(n.Interface(), o.Interface()) {
continue Outer
}
}
log.Debugf("Appending %v to %v", niv.Interface(), ov)
ov = reflect.Append(ov, niv)
nvElem := reflect.New(ov.Type().Elem()).Elem()
m.assignValue(nvElem, niv, false)
Log.Debugf("Appending %v to %v", nvElem.Interface(), ov)
ov = reflect.Append(ov, nvElem)
}
return ov
}
func (f *FigTree) formatEnvName(name string) string {
name = fmt.Sprintf("%s_%s", f.EnvPrefix, strings.ToUpper(name))
name = fmt.Sprintf("%s_%s", f.envPrefix, strings.ToUpper(name))
return strings.Map(func(r rune) rune {
if unicode.IsDigit(r) || unicode.IsLetter(r) {
@@ -412,7 +892,9 @@ func (f *FigTree) formatEnvValue(value reflect.Value) (string, bool) {
return "", false
}
func (f *FigTree) populateEnv(data interface{}) {
func (f *FigTree) PopulateEnv(data interface{}) (changeSet map[string]*string) {
changeSet = make(map[string]*string)
options := reflect.ValueOf(data)
if options.Kind() == reflect.Ptr {
options = reflect.ValueOf(options.Elem().Interface())
@@ -435,7 +917,9 @@ func (f *FigTree) populateEnv(data interface{}) {
envName := f.formatEnvName(name)
val, ok := f.formatEnvValue(options.MapIndex(key))
if ok {
os.Setenv(envName, val)
changeSet[envName] = &val
} else {
changeSet[envName] = nil
}
}
}
@@ -448,28 +932,40 @@ func (f *FigTree) populateEnv(data interface{}) {
continue
}
name := strings.Join(camelcase.Split(structField.Name), "_")
envNames := []string{strings.Join(camelcase.Split(structField.Name), "_")}
if tag := structField.Tag.Get("figtree"); tag != "" {
if strings.HasSuffix(tag, ",inline") {
// if we have a tag like: `figtree:",inline"` then we
// want to the field as a top level member and not serialize
// the raw struct to json, so just recurse here
f.populateEnv(options.Field(i).Interface())
nestedEnvSet := f.PopulateEnv(options.Field(i).Interface())
for k, v := range nestedEnvSet {
changeSet[k] = v
}
continue
}
// next look for `figtree:"env,..."` to set the env name to that
parts := strings.Split(tag, ",")
if len(parts) > 0 {
name = parts[0]
// if the env name is "-" then we should not populate this data into the env
if parts[0] == "-" {
continue
}
envNames = strings.Split(parts[0], ";")
}
}
envName := f.formatEnvName(name)
val, ok := f.formatEnvValue(options.Field(i))
if ok {
os.Setenv(envName, val)
for _, name := range envNames {
envName := f.formatEnvName(name)
val, ok := f.formatEnvValue(options.Field(i))
if ok {
changeSet[envName] = &val
} else {
changeSet[envName] = nil
}
}
}
}
return changeSet
}
-282
View File
@@ -1,282 +0,0 @@
package figtree
import (
"os"
"testing"
logging "gopkg.in/op/go-logging.v1"
"github.com/stretchr/testify/assert"
)
func init() {
StringifyValue = false
logging.SetLevel(logging.NOTICE, "")
}
type TestOptions struct {
String1 StringOption `json:"str1,omitempty" yaml:"str1,omitempty"`
LeaveEmpty StringOption `json:"leave-empty,omitempty" yaml:"leave-empty,omitempty"`
Array1 ListStringOption `json:"arr1,omitempty" yaml:"arr1,omitempty"`
Map1 MapStringOption `json:"map1,omitempty" yaml:"map1,omitempty"`
Int1 IntOption `json:"int1,omitempty" yaml:"int1,omitempty"`
Float1 Float32Option `json:"float1,omitempty" yaml:"float1,omitempty"`
Bool1 BoolOption `json:"bool1,omitempty" yaml:"bool1,omitempty"`
}
type TestBuiltin struct {
String1 string `yaml:"str1,omitempty"`
LeaveEmpty string `yaml:"leave-empty,omitempty"`
Array1 []string `yaml:"arr1,omitempty"`
Map1 map[string]string `yaml:"map1,omitempty"`
Int1 int `yaml:"int1,omitempty"`
Float1 float32 `yaml:"float1,omitempty"`
Bool1 bool `yaml:"bool1,omitempty"`
}
func TestOptionsLoadConfigD3(t *testing.T) {
opts := TestOptions{}
os.Chdir("d1/d2/d3")
defer os.Chdir("../../..")
arr1 := []StringOption{}
arr1 = append(arr1, StringOption{"figtree.yml", true, "d3arr1val1"})
arr1 = append(arr1, StringOption{"figtree.yml", true, "d3arr1val2"})
arr1 = append(arr1, StringOption{"figtree.yml", true, "dupval"})
arr1 = append(arr1, StringOption{"../figtree.yml", true, "d2arr1val1"})
arr1 = append(arr1, StringOption{"../figtree.yml", true, "d2arr1val2"})
arr1 = append(arr1, StringOption{"../../figtree.yml", true, "d1arr1val1"})
arr1 = append(arr1, StringOption{"../../figtree.yml", true, "d1arr1val2"})
expected := TestOptions{
String1: StringOption{"figtree.yml", true, "d3str1val1"},
LeaveEmpty: StringOption{},
Array1: arr1,
Map1: map[string]StringOption{
"key0": StringOption{"../../figtree.yml", true, "d1map1val0"},
"key1": StringOption{"../figtree.yml", true, "d2map1val1"},
"key2": StringOption{"figtree.yml", true, "d3map1val2"},
"key3": StringOption{"figtree.yml", true, "d3map1val3"},
"dup": StringOption{"figtree.yml", true, "d3dupval"},
},
Int1: IntOption{"figtree.yml", true, 333},
Float1: Float32Option{"figtree.yml", true, 3.33},
Bool1: BoolOption{"figtree.yml", true, true},
}
err := LoadAllConfigs("figtree.yml", &opts)
assert.Nil(t, err)
assert.Exactly(t, expected, opts)
}
func TestOptionsLoadConfigD2(t *testing.T) {
opts := TestOptions{}
os.Chdir("d1/d2")
defer os.Chdir("../..")
arr1 := []StringOption{}
arr1 = append(arr1, StringOption{"figtree.yml", true, "d2arr1val1"})
arr1 = append(arr1, StringOption{"figtree.yml", true, "d2arr1val2"})
arr1 = append(arr1, StringOption{"figtree.yml", true, "dupval"})
arr1 = append(arr1, StringOption{"../figtree.yml", true, "d1arr1val1"})
arr1 = append(arr1, StringOption{"../figtree.yml", true, "d1arr1val2"})
expected := TestOptions{
String1: StringOption{"figtree.yml", true, "d2str1val1"},
LeaveEmpty: StringOption{},
Array1: arr1,
Map1: map[string]StringOption{
"key0": StringOption{"../figtree.yml", true, "d1map1val0"},
"key1": StringOption{"figtree.yml", true, "d2map1val1"},
"key2": StringOption{"figtree.yml", true, "d2map1val2"},
"dup": StringOption{"figtree.yml", true, "d2dupval"},
},
Int1: IntOption{"figtree.yml", true, 222},
Float1: Float32Option{"figtree.yml", true, 2.22},
Bool1: BoolOption{"figtree.yml", true, false},
}
err := LoadAllConfigs("figtree.yml", &opts)
assert.Nil(t, err)
assert.Exactly(t, expected, opts)
}
func TestOptionsLoadConfigD1(t *testing.T) {
opts := TestOptions{}
os.Chdir("d1")
defer os.Chdir("..")
arr1 := []StringOption{}
arr1 = append(arr1, StringOption{"figtree.yml", true, "d1arr1val1"})
arr1 = append(arr1, StringOption{"figtree.yml", true, "d1arr1val2"})
arr1 = append(arr1, StringOption{"figtree.yml", true, "dupval"})
expected := TestOptions{
String1: StringOption{"figtree.yml", true, "d1str1val1"},
LeaveEmpty: StringOption{},
Array1: arr1,
Map1: map[string]StringOption{
"key0": StringOption{"figtree.yml", true, "d1map1val0"},
"key1": StringOption{"figtree.yml", true, "d1map1val1"},
"dup": StringOption{"figtree.yml", true, "d1dupval"},
},
Int1: IntOption{"figtree.yml", true, 111},
Float1: Float32Option{"figtree.yml", true, 1.11},
Bool1: BoolOption{"figtree.yml", true, true},
}
err := LoadAllConfigs("figtree.yml", &opts)
assert.Nil(t, err)
assert.Exactly(t, expected, opts)
}
func TestOptionsCorrupt(t *testing.T) {
opts := TestOptions{}
os.Chdir("d1")
defer os.Chdir("..")
err := LoadAllConfigs("corrupt.yml", &opts)
assert.NotNil(t, err)
}
func TestBuiltinLoadConfigD3(t *testing.T) {
opts := TestBuiltin{}
os.Chdir("d1/d2/d3")
defer os.Chdir("../../..")
arr1 := []string{}
arr1 = append(arr1, "d3arr1val1")
arr1 = append(arr1, "d3arr1val2")
arr1 = append(arr1, "dupval")
arr1 = append(arr1, "d2arr1val1")
arr1 = append(arr1, "d2arr1val2")
arr1 = append(arr1, "d1arr1val1")
arr1 = append(arr1, "d1arr1val2")
expected := TestBuiltin{
String1: "d3str1val1",
LeaveEmpty: "",
Array1: arr1,
Map1: map[string]string{
"key0": "d1map1val0",
"key1": "d2map1val1",
"key2": "d3map1val2",
"key3": "d3map1val3",
"dup": "d3dupval",
},
Int1: 333,
Float1: 3.33,
Bool1: true,
}
err := LoadAllConfigs("figtree.yml", &opts)
assert.Nil(t, err)
assert.Exactly(t, expected, opts)
}
func TestBuiltinLoadConfigD2(t *testing.T) {
opts := TestBuiltin{}
os.Chdir("d1/d2")
defer os.Chdir("../..")
arr1 := []string{}
arr1 = append(arr1, "d2arr1val1")
arr1 = append(arr1, "d2arr1val2")
arr1 = append(arr1, "dupval")
arr1 = append(arr1, "d1arr1val1")
arr1 = append(arr1, "d1arr1val2")
expected := TestBuiltin{
String1: "d2str1val1",
LeaveEmpty: "",
Array1: arr1,
Map1: map[string]string{
"key0": "d1map1val0",
"key1": "d2map1val1",
"key2": "d2map1val2",
"dup": "d2dupval",
},
Int1: 222,
Float1: 2.22,
// note this will be true from d1/figtree.yml since the
// d1/d2/figtree.yml set it to false which is a zero value
Bool1: true,
}
err := LoadAllConfigs("figtree.yml", &opts)
assert.Nil(t, err)
assert.Exactly(t, expected, opts)
}
func TestBuiltinLoadConfigD1(t *testing.T) {
opts := TestBuiltin{}
os.Chdir("d1")
defer os.Chdir("..")
arr1 := []string{}
arr1 = append(arr1, "d1arr1val1")
arr1 = append(arr1, "d1arr1val2")
arr1 = append(arr1, "dupval")
expected := TestBuiltin{
String1: "d1str1val1",
LeaveEmpty: "",
Array1: arr1,
Map1: map[string]string{
"key0": "d1map1val0",
"key1": "d1map1val1",
"dup": "d1dupval",
},
Int1: 111,
Float1: 1.11,
Bool1: true,
}
err := LoadAllConfigs("figtree.yml", &opts)
assert.Nil(t, err)
assert.Exactly(t, expected, opts)
}
func TestBuiltinCorrupt(t *testing.T) {
opts := TestBuiltin{}
os.Chdir("d1")
defer os.Chdir("..")
err := LoadAllConfigs("corrupt.yml", &opts)
assert.NotNil(t, err)
}
func TestOptionsLoadConfigDefaults(t *testing.T) {
opts := TestOptions{
String1: NewStringOption("defaultVal1"),
LeaveEmpty: NewStringOption("emptyVal1"),
Int1: NewIntOption(999),
Float1: NewFloat32Option(9.99),
Bool1: NewBoolOption(false),
}
os.Chdir("d1")
defer os.Chdir("..")
arr1 := []StringOption{}
arr1 = append(arr1, StringOption{"figtree.yml", true, "d1arr1val1"})
arr1 = append(arr1, StringOption{"figtree.yml", true, "d1arr1val2"})
arr1 = append(arr1, StringOption{"figtree.yml", true, "dupval"})
expected := TestOptions{
String1: StringOption{"figtree.yml", true, "d1str1val1"},
LeaveEmpty: StringOption{"default", true, "emptyVal1"},
Array1: arr1,
Map1: map[string]StringOption{
"key0": StringOption{"figtree.yml", true, "d1map1val0"},
"key1": StringOption{"figtree.yml", true, "d1map1val1"},
"dup": StringOption{"figtree.yml", true, "d1dupval"},
},
Int1: IntOption{"figtree.yml", true, 111},
Float1: Float32Option{"figtree.yml", true, 1.11},
Bool1: BoolOption{"figtree.yml", true, true},
}
err := LoadAllConfigs("figtree.yml", &opts)
assert.Nil(t, err)
assert.Exactly(t, expected, opts)
}
+40
View File
@@ -74,6 +74,7 @@ func (o *BoolOption) UnmarshalYAML(unmarshal func(interface{}) error) error {
if err := unmarshal(&o.Value); err != nil {
return err
}
o.Source = "yaml"
o.Defined = true
return nil
}
@@ -82,6 +83,7 @@ func (o *BoolOption) UnmarshalJSON(b []byte) error {
if err := json.Unmarshal(b, &o.Value); err != nil {
return err
}
o.Source = "json"
o.Defined = true
return nil
}
@@ -305,6 +307,7 @@ func (o *ByteOption) UnmarshalYAML(unmarshal func(interface{}) error) error {
if err := unmarshal(&o.Value); err != nil {
return err
}
o.Source = "yaml"
o.Defined = true
return nil
}
@@ -313,6 +316,7 @@ func (o *ByteOption) UnmarshalJSON(b []byte) error {
if err := json.Unmarshal(b, &o.Value); err != nil {
return err
}
o.Source = "json"
o.Defined = true
return nil
}
@@ -536,6 +540,7 @@ func (o *Complex128Option) UnmarshalYAML(unmarshal func(interface{}) error) erro
if err := unmarshal(&o.Value); err != nil {
return err
}
o.Source = "yaml"
o.Defined = true
return nil
}
@@ -544,6 +549,7 @@ func (o *Complex128Option) UnmarshalJSON(b []byte) error {
if err := json.Unmarshal(b, &o.Value); err != nil {
return err
}
o.Source = "json"
o.Defined = true
return nil
}
@@ -767,6 +773,7 @@ func (o *Complex64Option) UnmarshalYAML(unmarshal func(interface{}) error) error
if err := unmarshal(&o.Value); err != nil {
return err
}
o.Source = "yaml"
o.Defined = true
return nil
}
@@ -775,6 +782,7 @@ func (o *Complex64Option) UnmarshalJSON(b []byte) error {
if err := json.Unmarshal(b, &o.Value); err != nil {
return err
}
o.Source = "json"
o.Defined = true
return nil
}
@@ -998,6 +1006,7 @@ func (o *ErrorOption) UnmarshalYAML(unmarshal func(interface{}) error) error {
if err := unmarshal(&o.Value); err != nil {
return err
}
o.Source = "yaml"
o.Defined = true
return nil
}
@@ -1006,6 +1015,7 @@ func (o *ErrorOption) UnmarshalJSON(b []byte) error {
if err := json.Unmarshal(b, &o.Value); err != nil {
return err
}
o.Source = "json"
o.Defined = true
return nil
}
@@ -1229,6 +1239,7 @@ func (o *Float32Option) UnmarshalYAML(unmarshal func(interface{}) error) error {
if err := unmarshal(&o.Value); err != nil {
return err
}
o.Source = "yaml"
o.Defined = true
return nil
}
@@ -1237,6 +1248,7 @@ func (o *Float32Option) UnmarshalJSON(b []byte) error {
if err := json.Unmarshal(b, &o.Value); err != nil {
return err
}
o.Source = "json"
o.Defined = true
return nil
}
@@ -1460,6 +1472,7 @@ func (o *Float64Option) UnmarshalYAML(unmarshal func(interface{}) error) error {
if err := unmarshal(&o.Value); err != nil {
return err
}
o.Source = "yaml"
o.Defined = true
return nil
}
@@ -1468,6 +1481,7 @@ func (o *Float64Option) UnmarshalJSON(b []byte) error {
if err := json.Unmarshal(b, &o.Value); err != nil {
return err
}
o.Source = "json"
o.Defined = true
return nil
}
@@ -1691,6 +1705,7 @@ func (o *IntOption) UnmarshalYAML(unmarshal func(interface{}) error) error {
if err := unmarshal(&o.Value); err != nil {
return err
}
o.Source = "yaml"
o.Defined = true
return nil
}
@@ -1699,6 +1714,7 @@ func (o *IntOption) UnmarshalJSON(b []byte) error {
if err := json.Unmarshal(b, &o.Value); err != nil {
return err
}
o.Source = "json"
o.Defined = true
return nil
}
@@ -1922,6 +1938,7 @@ func (o *Int16Option) UnmarshalYAML(unmarshal func(interface{}) error) error {
if err := unmarshal(&o.Value); err != nil {
return err
}
o.Source = "yaml"
o.Defined = true
return nil
}
@@ -1930,6 +1947,7 @@ func (o *Int16Option) UnmarshalJSON(b []byte) error {
if err := json.Unmarshal(b, &o.Value); err != nil {
return err
}
o.Source = "json"
o.Defined = true
return nil
}
@@ -2153,6 +2171,7 @@ func (o *Int32Option) UnmarshalYAML(unmarshal func(interface{}) error) error {
if err := unmarshal(&o.Value); err != nil {
return err
}
o.Source = "yaml"
o.Defined = true
return nil
}
@@ -2161,6 +2180,7 @@ func (o *Int32Option) UnmarshalJSON(b []byte) error {
if err := json.Unmarshal(b, &o.Value); err != nil {
return err
}
o.Source = "json"
o.Defined = true
return nil
}
@@ -2384,6 +2404,7 @@ func (o *Int64Option) UnmarshalYAML(unmarshal func(interface{}) error) error {
if err := unmarshal(&o.Value); err != nil {
return err
}
o.Source = "yaml"
o.Defined = true
return nil
}
@@ -2392,6 +2413,7 @@ func (o *Int64Option) UnmarshalJSON(b []byte) error {
if err := json.Unmarshal(b, &o.Value); err != nil {
return err
}
o.Source = "json"
o.Defined = true
return nil
}
@@ -2615,6 +2637,7 @@ func (o *Int8Option) UnmarshalYAML(unmarshal func(interface{}) error) error {
if err := unmarshal(&o.Value); err != nil {
return err
}
o.Source = "yaml"
o.Defined = true
return nil
}
@@ -2623,6 +2646,7 @@ func (o *Int8Option) UnmarshalJSON(b []byte) error {
if err := json.Unmarshal(b, &o.Value); err != nil {
return err
}
o.Source = "json"
o.Defined = true
return nil
}
@@ -2846,6 +2870,7 @@ func (o *RuneOption) UnmarshalYAML(unmarshal func(interface{}) error) error {
if err := unmarshal(&o.Value); err != nil {
return err
}
o.Source = "yaml"
o.Defined = true
return nil
}
@@ -2854,6 +2879,7 @@ func (o *RuneOption) UnmarshalJSON(b []byte) error {
if err := json.Unmarshal(b, &o.Value); err != nil {
return err
}
o.Source = "json"
o.Defined = true
return nil
}
@@ -3077,6 +3103,7 @@ func (o *StringOption) UnmarshalYAML(unmarshal func(interface{}) error) error {
if err := unmarshal(&o.Value); err != nil {
return err
}
o.Source = "yaml"
o.Defined = true
return nil
}
@@ -3085,6 +3112,7 @@ func (o *StringOption) UnmarshalJSON(b []byte) error {
if err := json.Unmarshal(b, &o.Value); err != nil {
return err
}
o.Source = "json"
o.Defined = true
return nil
}
@@ -3308,6 +3336,7 @@ func (o *UintOption) UnmarshalYAML(unmarshal func(interface{}) error) error {
if err := unmarshal(&o.Value); err != nil {
return err
}
o.Source = "yaml"
o.Defined = true
return nil
}
@@ -3316,6 +3345,7 @@ func (o *UintOption) UnmarshalJSON(b []byte) error {
if err := json.Unmarshal(b, &o.Value); err != nil {
return err
}
o.Source = "json"
o.Defined = true
return nil
}
@@ -3539,6 +3569,7 @@ func (o *Uint16Option) UnmarshalYAML(unmarshal func(interface{}) error) error {
if err := unmarshal(&o.Value); err != nil {
return err
}
o.Source = "yaml"
o.Defined = true
return nil
}
@@ -3547,6 +3578,7 @@ func (o *Uint16Option) UnmarshalJSON(b []byte) error {
if err := json.Unmarshal(b, &o.Value); err != nil {
return err
}
o.Source = "json"
o.Defined = true
return nil
}
@@ -3770,6 +3802,7 @@ func (o *Uint32Option) UnmarshalYAML(unmarshal func(interface{}) error) error {
if err := unmarshal(&o.Value); err != nil {
return err
}
o.Source = "yaml"
o.Defined = true
return nil
}
@@ -3778,6 +3811,7 @@ func (o *Uint32Option) UnmarshalJSON(b []byte) error {
if err := json.Unmarshal(b, &o.Value); err != nil {
return err
}
o.Source = "json"
o.Defined = true
return nil
}
@@ -4001,6 +4035,7 @@ func (o *Uint64Option) UnmarshalYAML(unmarshal func(interface{}) error) error {
if err := unmarshal(&o.Value); err != nil {
return err
}
o.Source = "yaml"
o.Defined = true
return nil
}
@@ -4009,6 +4044,7 @@ func (o *Uint64Option) UnmarshalJSON(b []byte) error {
if err := json.Unmarshal(b, &o.Value); err != nil {
return err
}
o.Source = "json"
o.Defined = true
return nil
}
@@ -4232,6 +4268,7 @@ func (o *Uint8Option) UnmarshalYAML(unmarshal func(interface{}) error) error {
if err := unmarshal(&o.Value); err != nil {
return err
}
o.Source = "yaml"
o.Defined = true
return nil
}
@@ -4240,6 +4277,7 @@ func (o *Uint8Option) UnmarshalJSON(b []byte) error {
if err := json.Unmarshal(b, &o.Value); err != nil {
return err
}
o.Source = "json"
o.Defined = true
return nil
}
@@ -4463,6 +4501,7 @@ func (o *UintptrOption) UnmarshalYAML(unmarshal func(interface{}) error) error {
if err := unmarshal(&o.Value); err != nil {
return err
}
o.Source = "yaml"
o.Defined = true
return nil
}
@@ -4471,6 +4510,7 @@ func (o *UintptrOption) UnmarshalJSON(b []byte) error {
if err := json.Unmarshal(b, &o.Value); err != nil {
return err
}
o.Source = "json"
o.Defined = true
return nil
}
-61
View File
@@ -1,61 +0,0 @@
package figtree
import (
"os"
"testing"
"github.com/stretchr/testify/assert"
kingpin "gopkg.in/alecthomas/kingpin.v2"
)
func TestCommandLine(t *testing.T) {
type CommandLineOptions struct {
Str1 StringOption `yaml:"str1,omitempty"`
Int1 IntOption `yaml:"int1,omitempty"`
Map1 MapStringOption `yaml:"map1,omitempty"`
Arr1 ListStringOption `yaml:"arr1,omitempty"`
}
opts := CommandLineOptions{}
os.Chdir("d1/d2/d3")
defer os.Chdir("../../..")
err := LoadAllConfigs("figtree.yml", &opts)
assert.Nil(t, err)
app := kingpin.New("test", "testing")
app.Flag("str1", "Str1").SetValue(&opts.Str1)
app.Flag("int1", "Int1").SetValue(&opts.Int1)
app.Flag("map1", "Map1").SetValue(&opts.Map1)
app.Flag("arr1", "Arr1").SetValue(&opts.Arr1)
_, err = app.Parse([]string{"--int1", "999", "--map1", "k1=v1", "--map1", "k2=v2", "--arr1", "v1", "--arr1", "v2"})
assert.Nil(t, err)
arr1 := ListStringOption{}
arr1 = append(arr1, StringOption{"figtree.yml", true, "d3arr1val1"})
arr1 = append(arr1, StringOption{"figtree.yml", true, "d3arr1val2"})
arr1 = append(arr1, StringOption{"figtree.yml", true, "dupval"})
arr1 = append(arr1, StringOption{"../figtree.yml", true, "d2arr1val1"})
arr1 = append(arr1, StringOption{"../figtree.yml", true, "d2arr1val2"})
arr1 = append(arr1, StringOption{"../../figtree.yml", true, "d1arr1val1"})
arr1 = append(arr1, StringOption{"../../figtree.yml", true, "d1arr1val2"})
arr1 = append(arr1, StringOption{"override", true, "v1"})
arr1 = append(arr1, StringOption{"override", true, "v2"})
expected := CommandLineOptions{
Str1: StringOption{"figtree.yml", true, "d3str1val1"},
Int1: IntOption{"override", true, 999},
Map1: map[string]StringOption{
"key0": StringOption{"../../figtree.yml", true, "d1map1val0"},
"key1": StringOption{"../figtree.yml", true, "d2map1val1"},
"key2": StringOption{"figtree.yml", true, "d3map1val2"},
"key3": StringOption{"figtree.yml", true, "d3map1val3"},
"dup": StringOption{"figtree.yml", true, "d3dupval"},
"k1": StringOption{"override", true, "v1"},
"k2": StringOption{"override", true, "v2"},
},
Arr1: arr1,
}
assert.Equal(t, expected, opts)
}
-67
View File
@@ -1,67 +0,0 @@
package figtree
import (
"encoding/json"
"os"
"testing"
yaml "gopkg.in/coryb/yaml.v2"
"github.com/stretchr/testify/assert"
)
func TestOptionsMarshalYAML(t *testing.T) {
opts := TestOptions{}
os.Chdir("d1/d2/d3")
defer os.Chdir("../../..")
err := LoadAllConfigs("figtree.yml", &opts)
assert.Nil(t, err)
StringifyValue = true
defer func() {
StringifyValue = false
}()
got, err := yaml.Marshal(&opts)
assert.Nil(t, err)
expected := `str1: d3str1val1
arr1:
- d3arr1val1
- d3arr1val2
- dupval
- d2arr1val1
- d2arr1val2
- d1arr1val1
- d1arr1val2
map1:
dup: d3dupval
key0: d1map1val0
key1: d2map1val1
key2: d3map1val2
key3: d3map1val3
int1: 333
float1: 3.33
bool1: true
`
assert.Equal(t, expected, string(got))
}
func TestOptionsMarshalJSON(t *testing.T) {
opts := TestOptions{}
os.Chdir("d1/d2/d3")
defer os.Chdir("../../..")
err := LoadAllConfigs("figtree.yml", &opts)
assert.Nil(t, err)
StringifyValue = true
defer func() {
StringifyValue = false
}()
got, err := json.Marshal(&opts)
assert.Nil(t, err)
// note that "leave-empty" is serialized even though "omitempty" tag is set
// this is because json always assumes structs are not empty and there
// is no interface to override this behavior
expected := `{"str1":"d3str1val1","leave-empty":"","arr1":["d3arr1val1","d3arr1val2","dupval","d2arr1val1","d2arr1val2","d1arr1val1","d1arr1val2"],"map1":{"dup":"d3dupval","key0":"d1map1val0","key1":"d2map1val1","key2":"d3map1val2","key3":"d3map1val3"},"int1":333,"float1":3.33,"bool1":true}`
assert.Equal(t, expected, string(got))
}
+1 -1
View File
@@ -2,7 +2,7 @@ package figtree
import "regexp"
type Option interface {
type option interface {
IsDefined() bool
GetValue() interface{}
SetValue(interface{}) error
-130
View File
@@ -1,130 +0,0 @@
package figtree
import (
"os"
"testing"
"github.com/stretchr/testify/assert"
)
func init() {
StringifyValue = false
}
func TestOptionsOverwriteConfigD3(t *testing.T) {
opts := TestOptions{}
os.Chdir("d1/d2/d3")
defer os.Chdir("../../..")
arr1 := []StringOption{}
arr1 = append(arr1, StringOption{"../overwrite.yml", true, "d2arr1val1"})
arr1 = append(arr1, StringOption{"../overwrite.yml", true, "d2arr1val2"})
arr1 = append(arr1, StringOption{"../../overwrite.yml", true, "d1arr1val1"})
arr1 = append(arr1, StringOption{"../../overwrite.yml", true, "d1arr1val2"})
expected := TestOptions{
String1: StringOption{"../overwrite.yml", true, "d2str1val1"},
LeaveEmpty: StringOption{},
Array1: arr1,
Map1: map[string]StringOption{
"key0": StringOption{"../../overwrite.yml", true, "d1map1val0"},
"key1": StringOption{"../../overwrite.yml", true, "d1map1val1"},
},
Int1: IntOption{"../../overwrite.yml", true, 111},
Float1: Float32Option{"../../overwrite.yml", true, 1.11},
Bool1: BoolOption{"../overwrite.yml", true, false},
}
err := LoadAllConfigs("overwrite.yml", &opts)
assert.Nil(t, err)
assert.Exactly(t, expected, opts)
}
func TestOptionsOverwriteConfigD2(t *testing.T) {
opts := TestOptions{}
os.Chdir("d1/d2")
defer os.Chdir("../..")
arr1 := []StringOption{}
arr1 = append(arr1, StringOption{"overwrite.yml", true, "d2arr1val1"})
arr1 = append(arr1, StringOption{"overwrite.yml", true, "d2arr1val2"})
arr1 = append(arr1, StringOption{"../overwrite.yml", true, "d1arr1val1"})
arr1 = append(arr1, StringOption{"../overwrite.yml", true, "d1arr1val2"})
expected := TestOptions{
String1: StringOption{"overwrite.yml", true, "d2str1val1"},
LeaveEmpty: StringOption{},
Array1: arr1,
Map1: map[string]StringOption{
"key0": StringOption{"../overwrite.yml", true, "d1map1val0"},
"key1": StringOption{"../overwrite.yml", true, "d1map1val1"},
},
Int1: IntOption{"../overwrite.yml", true, 111},
Float1: Float32Option{"../overwrite.yml", true, 1.11},
Bool1: BoolOption{"overwrite.yml", true, false},
}
err := LoadAllConfigs("overwrite.yml", &opts)
assert.Nil(t, err)
assert.Exactly(t, expected, opts)
}
func TestBuiltinOverwriteConfigD3(t *testing.T) {
opts := TestBuiltin{}
os.Chdir("d1/d2/d3")
defer os.Chdir("../../..")
arr1 := []string{}
arr1 = append(arr1, "d2arr1val1")
arr1 = append(arr1, "d2arr1val2")
arr1 = append(arr1, "d1arr1val1")
arr1 = append(arr1, "d1arr1val2")
expected := TestBuiltin{
String1: "d2str1val1",
LeaveEmpty: "",
Array1: arr1,
Map1: map[string]string{
"key0": "d1map1val0",
"key1": "d1map1val1",
},
Int1: 111,
Float1: 1.11,
Bool1: true,
}
err := LoadAllConfigs("overwrite.yml", &opts)
assert.Nil(t, err)
assert.Exactly(t, expected, opts)
}
func TestBuiltinOverwriteConfigD2(t *testing.T) {
opts := TestBuiltin{}
os.Chdir("d1/d2")
defer os.Chdir("../..")
arr1 := []string{}
arr1 = append(arr1, "d2arr1val1")
arr1 = append(arr1, "d2arr1val2")
arr1 = append(arr1, "d1arr1val1")
arr1 = append(arr1, "d1arr1val2")
expected := TestBuiltin{
String1: "d2str1val1",
LeaveEmpty: "",
Array1: arr1,
Map1: map[string]string{
"key0": "d1map1val0",
"key1": "d1map1val1",
},
Int1: 111,
Float1: 1.11,
// note this will be true from d1/overwrite.yml since the
// d1/d2/overwrite.yml set it to false which is a zero value
Bool1: true,
}
err := LoadAllConfigs("overwrite.yml", &opts)
assert.Nil(t, err)
assert.Exactly(t, expected, opts)
}
+2
View File
@@ -76,6 +76,7 @@ func (o *RawTypeOption) UnmarshalYAML(unmarshal func(interface{}) error) error {
if err := unmarshal(&o.Value); err != nil {
return err
}
o.Source = "yaml"
o.Defined = true
return nil
}
@@ -84,6 +85,7 @@ func (o *RawTypeOption) UnmarshalJSON(b []byte) error {
if err := json.Unmarshal(b, &o.Value); err != nil {
return err
}
o.Source = "json"
o.Defined = true
return nil
}
-74
View File
@@ -1,74 +0,0 @@
package figtree
import (
"encoding/json"
"testing"
yaml "gopkg.in/coryb/yaml.v2"
"github.com/stretchr/testify/assert"
)
func TestOptionInterface(t *testing.T) {
f := func(_ Option) bool {
return true
}
assert.True(t, f(&BoolOption{}))
assert.True(t, f(&ByteOption{}))
assert.True(t, f(&Complex128Option{}))
assert.True(t, f(&Complex64Option{}))
assert.True(t, f(&ErrorOption{}))
assert.True(t, f(&Float32Option{}))
assert.True(t, f(&Float64Option{}))
assert.True(t, f(&IntOption{}))
assert.True(t, f(&Int16Option{}))
assert.True(t, f(&Int32Option{}))
assert.True(t, f(&Int64Option{}))
assert.True(t, f(&Int8Option{}))
assert.True(t, f(&RuneOption{}))
assert.True(t, f(&StringOption{}))
assert.True(t, f(&UintOption{}))
assert.True(t, f(&Uint16Option{}))
assert.True(t, f(&Uint32Option{}))
assert.True(t, f(&Uint64Option{}))
assert.True(t, f(&Uint8Option{}))
assert.True(t, f(&UintptrOption{}))
}
func TestStringOptionYAML(t *testing.T) {
s := ""
err := yaml.Unmarshal([]byte(`""`), &s)
assert.Nil(t, err)
assert.Equal(t, s, "")
type testType struct {
String StringOption `yaml:"string,omitempty"`
}
tt := testType{}
err = yaml.Unmarshal([]byte(`string: ""`), &tt)
assert.Nil(t, err)
assert.Equal(t, tt.String, StringOption{Value: "", Defined: true})
tt = testType{}
err = yaml.Unmarshal([]byte(`string: "value"`), &tt)
assert.Nil(t, err)
assert.Equal(t, tt.String, StringOption{Value: "value", Defined: true})
}
func TestStringOptionJSON(t *testing.T) {
type testType struct {
String StringOption `json:"string,omitempty"`
}
tt := testType{}
err := json.Unmarshal([]byte(`{"string": ""}`), &tt)
assert.Nil(t, err)
assert.Equal(t, tt.String, StringOption{Value: "", Defined: true})
tt = testType{}
err = json.Unmarshal([]byte(`{"string": "value"}`), &tt)
assert.Nil(t, err)
assert.Equal(t, tt.String, StringOption{Value: "value", Defined: true})
}
-122
View File
@@ -1,122 +0,0 @@
package figtree
import (
"os"
"testing"
"github.com/stretchr/testify/assert"
)
func TestOptionsStopConfigD3(t *testing.T) {
opts := TestOptions{}
os.Chdir("d1/d2/d3")
defer os.Chdir("../../..")
arr1 := []StringOption{}
arr1 = append(arr1, StringOption{"stop.yml", true, "d3arr1val1"})
arr1 = append(arr1, StringOption{"stop.yml", true, "d3arr1val2"})
arr1 = append(arr1, StringOption{"../stop.yml", true, "d2arr1val1"})
arr1 = append(arr1, StringOption{"../stop.yml", true, "d2arr1val2"})
expected := TestOptions{
String1: StringOption{"stop.yml", true, "d3str1val1"},
LeaveEmpty: StringOption{},
Array1: arr1,
Map1: map[string]StringOption{
"key1": StringOption{"../stop.yml", true, "d2map1val1"},
"key2": StringOption{"stop.yml", true, "d3map1val2"},
"key3": StringOption{"stop.yml", true, "d3map1val3"},
},
Int1: IntOption{"stop.yml", true, 333},
Float1: Float32Option{"stop.yml", true, 3.33},
Bool1: BoolOption{"stop.yml", true, true},
}
err := LoadAllConfigs("stop.yml", &opts)
assert.Nil(t, err)
assert.Exactly(t, expected, opts)
}
func TestOptionsStopConfigD2(t *testing.T) {
opts := TestOptions{}
os.Chdir("d1/d2")
defer os.Chdir("../..")
arr1 := []StringOption{}
arr1 = append(arr1, StringOption{"stop.yml", true, "d2arr1val1"})
arr1 = append(arr1, StringOption{"stop.yml", true, "d2arr1val2"})
expected := TestOptions{
String1: StringOption{"stop.yml", true, "d2str1val1"},
LeaveEmpty: StringOption{},
Array1: arr1,
Map1: map[string]StringOption{
"key1": StringOption{"stop.yml", true, "d2map1val1"},
"key2": StringOption{"stop.yml", true, "d2map1val2"},
},
Int1: IntOption{"stop.yml", true, 222},
Float1: Float32Option{"stop.yml", true, 2.22},
Bool1: BoolOption{"stop.yml", true, false},
}
err := LoadAllConfigs("stop.yml", &opts)
assert.Nil(t, err)
assert.Exactly(t, expected, opts)
}
func TestBuiltinStopConfigD3(t *testing.T) {
opts := TestBuiltin{}
os.Chdir("d1/d2/d3")
defer os.Chdir("../../..")
arr1 := []string{}
arr1 = append(arr1, "d3arr1val1")
arr1 = append(arr1, "d3arr1val2")
arr1 = append(arr1, "d2arr1val1")
arr1 = append(arr1, "d2arr1val2")
expected := TestBuiltin{
String1: "d3str1val1",
LeaveEmpty: "",
Array1: arr1,
Map1: map[string]string{
"key1": "d2map1val1",
"key2": "d3map1val2",
"key3": "d3map1val3",
},
Int1: 333,
Float1: 3.33,
Bool1: true,
}
err := LoadAllConfigs("stop.yml", &opts)
assert.Nil(t, err)
assert.Exactly(t, expected, opts)
}
func TestBuiltinStopConfigD2(t *testing.T) {
opts := TestBuiltin{}
os.Chdir("d1/d2")
defer os.Chdir("../..")
arr1 := []string{}
arr1 = append(arr1, "d2arr1val1")
arr1 = append(arr1, "d2arr1val2")
expected := TestBuiltin{
String1: "d2str1val1",
LeaveEmpty: "",
Array1: arr1,
Map1: map[string]string{
"key1": "d2map1val1",
"key2": "d2map1val2",
},
Int1: 222,
Float1: 2.22,
Bool1: false,
}
err := LoadAllConfigs("stop.yml", &opts)
assert.Nil(t, err)
assert.Exactly(t, expected, opts)
}
-45
View File
@@ -1,45 +0,0 @@
package figtree
import (
"os"
"path"
"path/filepath"
"runtime"
"strings"
)
func homedir() string {
if runtime.GOOS == "windows" {
return os.Getenv("USERPROFILE")
}
return os.Getenv("HOME")
}
func FindParentPaths(fileName string) []string {
cwd, _ := os.Getwd()
paths := make([]string, 0)
// special case if homedir is not in current path then check there anyway
homedir := homedir()
if !strings.HasPrefix(cwd, homedir) {
file := path.Join(homedir, fileName)
if _, err := os.Stat(file); err == nil {
paths = append(paths, filepath.FromSlash(file))
}
}
var dir string
for _, part := range strings.Split(cwd, string(os.PathSeparator)) {
if part == "" && dir == "" {
dir = "/"
} else {
dir = path.Join(dir, part)
}
file := path.Join(dir, fileName)
if _, err := os.Stat(file); err == nil {
paths = append(paths, filepath.FromSlash(file))
}
}
return paths
}
-1
View File
@@ -1 +0,0 @@
language: go
-5
View File
@@ -1,5 +0,0 @@
test:
go get -t -v
go test
.PHONY: test
-4
View File
@@ -1,4 +0,0 @@
[![Build Status](https://travis-ci.org/coryb/kingpeon.svg?branch=master)](https://travis-ci.org/coryb/kingpeon)
[![GoDoc](https://godoc.org/github.com/coryb/kingpeon?status.png)](https://godoc.org/github.com/coryb/kingpeon)
Kingpeon is a Go library to generate [kingpin](https://godoc.org/gopkg.in/alecthomas/kingpin.v2) command usage from a configuration file. It is useful for creating allowing for user-defined aliases for Go command line tools.
-12
View File
@@ -1,12 +0,0 @@
hash: 69f929047be51886aecd667c9eedf56f84e06024391695c787f5d2a82238b185
updated: 2017-08-28T14:04:17.250162442-07:00
imports:
- name: github.com/alecthomas/template
version: a0175ee3bccc567396460bf5acd36800cb10c49c
subpackages:
- parse
- name: github.com/alecthomas/units
version: 2efee857e7cfd4f3d0138cc3cbb1b4966962b93a
- name: gopkg.in/alecthomas/kingpin.v2
version: 1087e65c9441605df944fb12c33f0fe7072d18ca
testImports: []
-4
View File
@@ -1,4 +0,0 @@
package: github.com/coryb/kingpeon
import:
- package: gopkg.in/alecthomas/kingpin.v2
version: ^2.2.5
-208
View File
@@ -1,208 +0,0 @@
package kingpeon
import (
"io/ioutil"
"testing"
"text/template"
"github.com/stretchr/testify/assert"
kingpin "gopkg.in/alecthomas/kingpin.v2"
yaml "gopkg.in/yaml.v2"
)
func TestRegisterDynamicCommands(t *testing.T) {
data := struct {
DynamicCommands []DynamicCommand `yaml:"dynamic-commands"`
}{}
config, err := ioutil.ReadFile("./sample.yml")
assert.Nil(t, err)
err = yaml.Unmarshal(config, &data)
assert.Nil(t, err)
tmpl := template.New("test")
app := kingpin.New("kingpeon", "Testing Aliases")
var expectedShell string
run := func(bin string, cmd []string, env []string) error {
assert.Equal(t, "/bin/sh", bin)
assert.Equal(t, []string{"sh", "-c", expectedShell}, cmd)
assert.NotEmpty(t, env)
return nil
}
err = RegisterDynamicCommandsWithRunner(run, app, data.DynamicCommands, tmpl)
assert.Nil(t, err)
expectedShell = "echo hello world"
_, err = app.Parse([]string{"echo"})
assert.Nil(t, err)
expectedShell = "echo -n hello world"
_, err = app.Parse([]string{"echo", "--no-newline"})
assert.Nil(t, err)
expectedShell = "echo hello test"
_, err = app.Parse([]string{"echo", "test", "--newline"})
assert.Nil(t, err)
expectedShell = "echo -n hello test"
_, err = app.Parse([]string{"echo", "test", "--no-newline"})
assert.Nil(t, err)
expectedShell = "echo true"
_, err = app.Parse([]string{"test", "bool", "arg", "true"})
assert.Nil(t, err)
expectedShell = "echo true"
_, err = app.Parse([]string{"test", "bool", "opt", "--BOOL"})
assert.Nil(t, err)
expectedShell = "echo 2"
_, err = app.Parse([]string{"test", "counter", "arg", "foo", "bar"})
assert.Nil(t, err)
expectedShell = "echo 2"
_, err = app.Parse([]string{"test", "counter", "opt", "--COUNTER", "--COUNTER"})
assert.Nil(t, err)
expectedShell = "echo foo"
_, err = app.Parse([]string{"test", "enum", "arg", "foo"})
assert.Nil(t, err)
expectedShell = "echo foo"
_, err = app.Parse([]string{"test", "enum", "opt", "--ENUM", "foo"})
assert.Nil(t, err)
_, err = app.Parse([]string{"test", "enum", "opt", "--ENUM", "bogus"})
assert.EqualError(t, err, "enum value must be one of foo,bar, got 'bogus'")
expectedShell = "echo 1.23"
_, err = app.Parse([]string{"test", "float32", "arg", "1.23"})
assert.Nil(t, err)
expectedShell = "echo 1.23"
_, err = app.Parse([]string{"test", "float32", "opt", "--FLOAT32", "1.23"})
assert.Nil(t, err)
expectedShell = "echo 1.23"
_, err = app.Parse([]string{"test", "float64", "arg", "1.23"})
assert.Nil(t, err)
expectedShell = "echo 1.23"
_, err = app.Parse([]string{"test", "float64", "opt", "--FLOAT64", "1.23"})
assert.Nil(t, err)
expectedShell = "echo 127"
_, err = app.Parse([]string{"test", "int8", "arg", "127"})
assert.Nil(t, err)
expectedShell = "echo 127"
_, err = app.Parse([]string{"test", "int8", "opt", "--INT8", "127"})
assert.Nil(t, err)
expectedShell = "echo 127"
_, err = app.Parse([]string{"test", "int8", "arg", "127"})
assert.Nil(t, err)
expectedShell = "echo 127"
_, err = app.Parse([]string{"test", "int8", "opt", "--INT8", "127"})
assert.Nil(t, err)
expectedShell = "echo 127"
_, err = app.Parse([]string{"test", "int16", "arg", "127"})
assert.Nil(t, err)
expectedShell = "echo 127"
_, err = app.Parse([]string{"test", "int16", "opt", "--INT16", "127"})
assert.Nil(t, err)
expectedShell = "echo 127"
_, err = app.Parse([]string{"test", "int32", "arg", "127"})
assert.Nil(t, err)
expectedShell = "echo 127"
_, err = app.Parse([]string{"test", "int32", "opt", "--INT32", "127"})
assert.Nil(t, err)
expectedShell = "echo 127"
_, err = app.Parse([]string{"test", "int64", "arg", "127"})
assert.Nil(t, err)
expectedShell = "echo 127"
_, err = app.Parse([]string{"test", "int64", "opt", "--INT64", "127"})
assert.Nil(t, err)
expectedShell = "echo 127"
_, err = app.Parse([]string{"test", "int", "arg", "127"})
assert.Nil(t, err)
expectedShell = "echo 127"
_, err = app.Parse([]string{"test", "int", "opt", "--INT", "127"})
assert.Nil(t, err)
expectedShell = "echo hello"
_, err = app.Parse([]string{"test", "string", "arg", "hello"})
assert.Nil(t, err)
expectedShell = "echo hello"
_, err = app.Parse([]string{"test", "string", "opt", "--STRING", "hello"})
assert.Nil(t, err)
expectedShell = "echo [abc: def][foo: bar]"
_, err = app.Parse([]string{"test", "stringmap", "arg", "foo=bar", "abc=def"})
assert.Nil(t, err)
expectedShell = "echo [abc: def][foo: bar]"
_, err = app.Parse([]string{"test", "stringmap", "opt", "--STRINGMAP", "foo=bar", "--STRINGMAP", "abc=def"})
assert.Nil(t, err)
expectedShell = "echo 255"
_, err = app.Parse([]string{"test", "uint8", "arg", "255"})
assert.Nil(t, err)
expectedShell = "echo 255"
_, err = app.Parse([]string{"test", "uint8", "opt", "--UINT8", "255"})
assert.Nil(t, err)
expectedShell = "echo 255"
_, err = app.Parse([]string{"test", "uint8", "arg", "255"})
assert.Nil(t, err)
expectedShell = "echo 255"
_, err = app.Parse([]string{"test", "uint8", "opt", "--UINT8", "255"})
assert.Nil(t, err)
expectedShell = "echo 255"
_, err = app.Parse([]string{"test", "uint16", "arg", "255"})
assert.Nil(t, err)
expectedShell = "echo 255"
_, err = app.Parse([]string{"test", "uint16", "opt", "--UINT16", "255"})
assert.Nil(t, err)
expectedShell = "echo 255"
_, err = app.Parse([]string{"test", "uint32", "arg", "255"})
assert.Nil(t, err)
expectedShell = "echo 255"
_, err = app.Parse([]string{"test", "uint32", "opt", "--UINT32", "255"})
assert.Nil(t, err)
expectedShell = "echo 255"
_, err = app.Parse([]string{"test", "uint64", "arg", "255"})
assert.Nil(t, err)
expectedShell = "echo 255"
_, err = app.Parse([]string{"test", "uint64", "opt", "--UINT64", "255"})
assert.Nil(t, err)
expectedShell = "echo 255"
_, err = app.Parse([]string{"test", "uint", "arg", "255"})
assert.Nil(t, err)
expectedShell = "echo 255"
_, err = app.Parse([]string{"test", "uint", "opt", "--UINT", "255"})
assert.Nil(t, err)
}
-289
View File
@@ -1,289 +0,0 @@
dynamic-commands:
- name: echo
help: echo stuff
script: |-
echo {{if not options.newline}}-n {{end}}hello {{args.WORD}}
args:
- name: WORD
default: world
options:
- name: newline
type: BOOL
default: true
- name: test bool arg
help: test bool arg
script: |-
echo {{args.BOOL}}
args:
- name: BOOL
type: BOOL
required: true
- name: test bool opt
help: test bool opt
script: |-
echo {{options.BOOL}}
options:
- name: BOOL
type: BOOL
required: true
- name: test counter arg
help: test counter arg
script: |-
echo {{args.COUNTER}}
args:
- name: COUNTER
type: COUNTER
required: true
- name: test counter opt
help: test counter opt
script: |-
echo {{options.COUNTER}}
options:
- name: COUNTER
type: COUNTER
required: true
- name: test enum arg
help: test enum arg
script: |-
echo {{args.ENUM}}
args:
- name: ENUM
type: ENUM
enum:
- foo
- bar
required: true
- name: test enum opt
help: test enum opt
script: |-
echo {{options.ENUM}}
options:
- name: ENUM
type: ENUM
enum:
- foo
- bar
required: true
- name: test float32 arg
help: test float32 arg
script: |-
echo {{args.FLOAT32}}
args:
- name: FLOAT32
type: FLOAT32
required: true
- name: test float32 opt
help: test float32 opt
script: |-
echo {{options.FLOAT32}}
options:
- name: FLOAT32
type: FLOAT32
required: true
- name: test float64 arg
help: test float64 arg
script: |-
echo {{args.FLOAT64}}
args:
- name: FLOAT64
type: FLOAT64
required: true
- name: test float64 opt
help: test float64 opt
script: |-
echo {{options.FLOAT64}}
options:
- name: FLOAT64
type: FLOAT64
required: true
- name: test int8 arg
help: test int8 arg
script: |-
echo {{args.INT8}}
args:
- name: INT8
type: INT8
required: true
- name: test int8 opt
help: test int8 opt
script: |-
echo {{options.INT8}}
options:
- name: INT8
type: INT8
required: true
- name: test int16 arg
help: test int16 arg
script: |-
echo {{args.INT16}}
args:
- name: INT16
type: INT16
required: true
- name: test int16 opt
help: test int16 opt
script: |-
echo {{options.INT16}}
options:
- name: INT16
type: INT16
required: true
- name: test int32 arg
help: test int32 arg
script: |-
echo {{args.INT32}}
args:
- name: INT32
type: INT32
required: true
- name: test int32 opt
help: test int32 opt
script: |-
echo {{options.INT32}}
options:
- name: INT32
type: INT32
required: true
- name: test int64 arg
help: test int64 arg
script: |-
echo {{args.INT64}}
args:
- name: INT64
type: INT64
required: true
- name: test int64 opt
help: test int64 opt
script: |-
echo {{options.INT64}}
options:
- name: INT64
type: INT64
required: true
- name: test int arg
help: test int arg
script: |-
echo {{args.INT}}
args:
- name: INT
type: INT
required: true
- name: test int opt
help: test int opt
script: |-
echo {{options.INT}}
options:
- name: INT
type: INT
required: true
- name: test string arg
help: test string arg
script: |-
echo {{args.STRING}}
args:
- name: STRING
required: true
- name: test string opt
help: test string opt
script: |-
echo {{options.STRING}}
options:
- name: STRING
required: true
- name: test stringmap arg
help: test stringmap arg
script: |-
echo {{range $key, $val := args.STRINGMAP}}[{{$key}}: {{$val}}]{{end}}
args:
- name: STRINGMAP
type: STRINGMAP
required: true
- name: test stringmap opt
help: test stringmap opt
script: |-
echo {{range $key, $val := options.STRINGMAP}}[{{$key}}: {{$val}}]{{end}}
options:
- name: STRINGMAP
type: STRINGMAP
required: true
- name: test uint8 arg
help: test uint8 arg
script: |-
echo {{args.UINT8}}
args:
- name: UINT8
type: UINT8
required: true
- name: test uint8 opt
help: test uint8 opt
script: |-
echo {{options.UINT8}}
options:
- name: UINT8
type: UINT8
required: true
- name: test uint16 arg
help: test uint16 arg
script: |-
echo {{args.UINT16}}
args:
- name: UINT16
type: UINT16
required: true
- name: test uint16 opt
help: test uint16 opt
script: |-
echo {{options.UINT16}}
options:
- name: UINT16
type: UINT16
required: true
- name: test uint32 arg
help: test uint32 arg
script: |-
echo {{args.UINT32}}
args:
- name: UINT32
type: UINT32
required: true
- name: test uint32 opt
help: test uint32 opt
script: |-
echo {{options.UINT32}}
options:
- name: UINT32
type: UINT32
required: true
- name: test uint64 arg
help: test uint64 arg
script: |-
echo {{args.UINT64}}
args:
- name: UINT64
type: UINT64
required: true
- name: test uint64 opt
help: test uint64 opt
script: |-
echo {{options.UINT64}}
options:
- name: UINT64
type: UINT64
required: true
- name: test uint arg
help: test uint arg
script: |-
echo {{args.UINT}}
args:
- name: UINT
type: UINT
required: true
- name: test uint opt
help: test uint opt
script: |-
echo {{options.UINT}}
options:
- name: UINT
type: UINT
required: true
-1
View File
@@ -1 +0,0 @@
language: go
-3
View File
@@ -1,3 +0,0 @@
test:
go get -t -v
go test
-4
View File
@@ -1,4 +0,0 @@
[![Build Status](https://travis-ci.org/coryb/oreo.svg?branch=master)](https://travis-ci.org/coryb/oreo)
[![GoDoc](https://godoc.org/github.com/coryb/oreo?status.png)](https://godoc.org/github.com/coryb/oreo)
Oreo is a simple wrapper build on top of [github.com/sethgrid/pester](http://github.com/sethgrid/pester) http client library. Oreo handles cookies and persist them to disk to be reused between client invocations.
-22
View File
@@ -1,22 +0,0 @@
hash: 36198c10af5880bbc485f4e52d35f5d97a2b3765e1b04a06a057a4496f5502b6
updated: 2017-06-20T14:10:57.557488284-07:00
imports:
- name: github.com/sethgrid/pester
version: 8053687f99650573b28fb75cddf3f295082704d7
- name: github.com/theckman/go-flock
version: 6de226b0d5f040ed85b88c82c381709b98277f3d
- name: gopkg.in/op/go-logging.v1
version: b2cb9fa56473e98db8caba80237377e83fe44db5
testImports:
- name: github.com/davecgh/go-spew
version: 6d212800a42e8ab5c146b8ace3490ee17e5225f9
subpackages:
- spew
- name: github.com/pmezard/go-difflib
version: d8ed2627bdf02c080bf22230dbb337003b7aba2d
subpackages:
- difflib
- name: github.com/stretchr/testify
version: 69483b4bd14f5845b5a1e55bca19e954e827f1d0
subpackages:
- assert
-6
View File
@@ -1,6 +0,0 @@
package: github.com/coryb/oreo
import:
- package: github.com/sethgrid/pester
- package: gopkg.in/op/go-logging.v1
version: ^1.0.0
- package: github.com/theckman/go-flock
-456
View File
@@ -1,456 +0,0 @@
package oreo
import (
"bytes"
"compress/gzip"
"fmt"
"io"
"io/ioutil"
"net"
"net/http"
"net/http/httptest"
"net/url"
"os"
"strings"
"testing"
"time"
"github.com/stretchr/testify/assert"
)
func init() {
TraceRequestBody = true
TraceResponseBody = true
}
func TestOreoGet(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "GET", r.Method)
fmt.Fprintf(w, "OK")
}))
defer ts.Close()
c := New()
resp, err := c.Get(ts.URL)
assert.Nil(t, err)
assert.NotNil(t, resp)
assert.Equal(t, 200, resp.StatusCode)
body, _ := ioutil.ReadAll(resp.Body)
assert.Equal(t, []byte("OK"), body)
}
func TestOreoHead(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "HEAD", r.Method)
fmt.Fprintf(w, "OK")
}))
defer ts.Close()
c := New()
resp, err := c.Head(ts.URL)
assert.Nil(t, err)
assert.NotNil(t, resp)
assert.Equal(t, 200, resp.StatusCode)
body, _ := ioutil.ReadAll(resp.Body)
assert.Equal(t, []byte(""), body)
assert.Equal(t, int64(2), resp.ContentLength)
}
func TestOreoPost(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "POST", r.Method)
body, _ := ioutil.ReadAll(r.Body)
assert.Equal(t, []byte("DATA"), body)
contentLength := r.Header["Content-Type"][0]
assert.Equal(t, "text/plain", contentLength)
fmt.Fprintf(w, "OK")
}))
defer ts.Close()
c := New()
resp, err := c.Post(ts.URL, "text/plain", strings.NewReader("DATA"))
assert.Nil(t, err)
assert.NotNil(t, resp)
assert.Equal(t, 200, resp.StatusCode)
body, _ := ioutil.ReadAll(resp.Body)
assert.Equal(t, []byte("OK"), body)
assert.Equal(t, int64(2), resp.ContentLength)
}
func TestOreoPostForm(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "POST", r.Method)
body, _ := ioutil.ReadAll(r.Body)
assert.Equal(t, []byte("key=value"), body)
contentLength := r.Header["Content-Type"][0]
assert.Equal(t, "application/x-www-form-urlencoded", contentLength)
fmt.Fprintf(w, "OK")
}))
defer ts.Close()
c := New()
data := url.Values{}
data.Add("key", "value")
resp, err := c.PostForm(ts.URL, data)
assert.Nil(t, err)
assert.NotNil(t, resp)
assert.Equal(t, 200, resp.StatusCode)
body, _ := ioutil.ReadAll(resp.Body)
assert.Equal(t, []byte("OK"), body)
assert.Equal(t, int64(2), resp.ContentLength)
}
func TestOreoPostJSON(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "POST", r.Method)
body, _ := ioutil.ReadAll(r.Body)
assert.Equal(t, []byte(`{"key":"value"}`), body)
contentLength := r.Header["Content-Type"][0]
assert.Equal(t, "application/json", contentLength)
fmt.Fprintf(w, "OK")
}))
defer ts.Close()
c := New()
resp, err := c.PostJSON(ts.URL, `{"key":"value"}`)
assert.Nil(t, err)
assert.NotNil(t, resp)
assert.Equal(t, 200, resp.StatusCode)
body, _ := ioutil.ReadAll(resp.Body)
assert.Equal(t, []byte("OK"), body)
assert.Equal(t, int64(2), resp.ContentLength)
}
func TestOreoPut(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "PUT", r.Method)
body, _ := ioutil.ReadAll(r.Body)
assert.Equal(t, []byte("DATA"), body)
contentLength := r.Header["Content-Type"][0]
assert.Equal(t, "text/plain", contentLength)
fmt.Fprintf(w, "OK")
}))
defer ts.Close()
c := New()
resp, err := c.Put(ts.URL, "text/plain", strings.NewReader("DATA"))
assert.Nil(t, err)
assert.NotNil(t, resp)
assert.Equal(t, 200, resp.StatusCode)
body, _ := ioutil.ReadAll(resp.Body)
assert.Equal(t, []byte("OK"), body)
assert.Equal(t, int64(2), resp.ContentLength)
}
func TestOreoPutJSON(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "PUT", r.Method)
body, _ := ioutil.ReadAll(r.Body)
assert.Equal(t, []byte(`{"key":"value"}`), body)
contentLength := r.Header["Content-Type"][0]
assert.Equal(t, "application/json", contentLength)
fmt.Fprintf(w, "OK")
}))
defer ts.Close()
c := New()
resp, err := c.PutJSON(ts.URL, `{"key":"value"}`)
assert.Nil(t, err)
assert.NotNil(t, resp)
assert.Equal(t, 200, resp.StatusCode)
body, _ := ioutil.ReadAll(resp.Body)
assert.Equal(t, []byte("OK"), body)
assert.Equal(t, int64(2), resp.ContentLength)
}
func TestOreoDelete(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "DELETE", r.Method)
fmt.Fprintf(w, "OK")
}))
defer ts.Close()
c := New()
resp, err := c.Delete(ts.URL)
assert.Nil(t, err)
assert.NotNil(t, resp)
assert.Equal(t, 200, resp.StatusCode)
body, _ := ioutil.ReadAll(resp.Body)
assert.Equal(t, []byte("OK"), body)
}
func TestOreoWithRetries(t *testing.T) {
attempts := 0
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
attempts++
http.Error(w, "error", http.StatusInternalServerError)
}))
defer ts.Close()
c := New().WithRetries(2)
resp, err := c.Get(ts.URL)
assert.Nil(t, err)
assert.Equal(t, 3, attempts)
assert.NotNil(t, resp)
assert.Equal(t, 500, resp.StatusCode)
}
func TestOreoWithTimeout(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
time.Sleep(2 * time.Second)
http.Error(w, "error", http.StatusInternalServerError)
}))
defer ts.Close()
c := New().WithTimeout(1 * time.Second).WithRetries(2)
start := time.Now().Unix()
resp, err := c.Get(ts.URL)
end := time.Now().Unix()
assert.Nil(t, resp)
assert.Error(t, err)
assert.True(t, end-start >= 2, "duration more than 2x timeout")
}
func TestOreoWithLinearTimeout(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
time.Sleep(2 * time.Second)
http.Error(w, "error", http.StatusInternalServerError)
}))
defer ts.Close()
c := New().WithTimeout(1 * time.Second).WithBackoff(LINEAR_BACKOFF).WithRetries(2)
start := time.Now().Unix()
resp, err := c.Get(ts.URL)
end := time.Now().Unix()
assert.Nil(t, resp)
assert.Error(t, err)
assert.True(t, end-start >= 3, "duration more than 1*timeout + 2*timeout")
}
func TestOreoWithCookieFile(t *testing.T) {
request := 0
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
request++
switch request {
case 1:
cookie := &http.Cookie{
Name: "key1",
Value: "val1",
}
http.SetCookie(w, cookie)
case 2:
cookie := r.Header["Cookie"][0]
assert.Equal(t, "key1=val1", cookie)
}
fmt.Fprintf(w, "OK")
}))
defer ts.Close()
tmpFile, err := ioutil.TempFile("", "oreo-cookies")
assert.Nil(t, err)
defer os.Remove(tmpFile.Name())
tmpFile.Close()
os.Remove(tmpFile.Name())
c := New().WithCookieFile(tmpFile.Name())
// first request will get a cookie set on response
resp, err := c.Get(ts.URL)
assert.NotNil(t, resp)
assert.Nil(t, err)
/// this request should automatically send cookie back to server
resp, err = c.Get(ts.URL)
assert.NotNil(t, resp)
assert.Nil(t, err)
}
func TestOreoWithTransport(t *testing.T) {
// set tcp connect timeout to 5s
var netTransport = &http.Transport{
Dial: (&net.Dialer{
Timeout: 5 * time.Second,
}).Dial,
}
c := New().WithTransport(netTransport).WithRetries(0)
// test against google dns servers, we will get a tcp connection
// failure (timeout due to firewall) to a non dns port on those hosts
start := time.Now().Unix()
resp, err := c.Get("http://8.8.8.8:9999")
end := time.Now().Unix()
assert.Nil(t, resp)
assert.Error(t, err)
lapse := end - start
msg := fmt.Sprintf("duration between 5-6s timeout, got: %d", lapse)
assert.True(t, lapse >= 5 && lapse <= 6, msg)
}
func TestOreoWithPostCallback(t *testing.T) {
requests := 0
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
requests++
_, ok := r.Header["Authorization"]
if ok {
fmt.Fprintf(w, "OK")
} else {
http.Error(w, "error", http.StatusUnauthorized)
}
}))
defer ts.Close()
var c *Client
called := 0
callback := func(req *http.Request, resp *http.Response) (*http.Response, error) {
called++
// if we get a 401 then add auth headers and try the request again
if resp.StatusCode == 401 {
req.SetBasicAuth("user", "pass")
return c.Do(req)
}
return resp, nil
}
c = New().WithPostCallback(callback)
resp, err := c.Get(ts.URL)
assert.NotNil(t, resp)
assert.Nil(t, err)
assert.Equal(t, 1, called)
assert.Equal(t, 2, requests)
}
func TestOreoWithPreCallback(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
_, ok := r.Header["Authorization"]
assert.True(t, ok)
fmt.Fprintf(w, "OK")
}))
defer ts.Close()
callback := func(req *http.Request) (*http.Request, error) {
req.SetBasicAuth("user", "pass")
return req, nil
}
c := New().WithPreCallback(callback)
resp, err := c.Get(ts.URL)
assert.NotNil(t, resp)
assert.Nil(t, err)
}
func TestOreoWithRedirect(t *testing.T) {
requests := 0
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
requests++
if requests == 1 {
http.Redirect(w, r, "/redirect", http.StatusMovedPermanently)
return
}
fmt.Fprintf(w, "OK")
}))
defer ts.Close()
c := New()
resp, err := c.Get(ts.URL)
assert.NotNil(t, resp)
assert.Nil(t, err)
assert.Equal(t, 2, requests)
}
func TestOreoWithNoRedirect(t *testing.T) {
requests := 0
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
requests++
if requests == 1 {
http.Redirect(w, r, "/redirect/", http.StatusMovedPermanently)
} else {
fmt.Fprintf(w, "OK")
}
}))
defer ts.Close()
c := New().WithCheckRedirect(NoRedirect)
resp, err := c.Get(ts.URL)
assert.NotNil(t, resp)
assert.Nil(t, err)
assert.Equal(t, 1, requests)
}
func TestOreoWithImmutability(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "OK")
}))
defer ts.Close()
result := ""
callback1 := func(req *http.Request) (*http.Request, error) {
result = "callback1"
return req, nil
}
callback2 := func(req *http.Request) (*http.Request, error) {
result = "callback2"
return req, nil
}
c1 := New().WithPreCallback(callback1)
c2 := c1.WithPreCallback(callback2)
resp, err := c1.Get(ts.URL)
assert.NotNil(t, resp)
assert.Nil(t, err)
assert.Equal(t, "callback1", result)
resp, err = c2.Get(ts.URL)
assert.NotNil(t, resp)
assert.Nil(t, err)
assert.Equal(t, "callback2", result)
resp, err = c1.Get(ts.URL)
assert.NotNil(t, resp)
assert.Nil(t, err)
assert.Equal(t, "callback1", result)
}
func TestOreoPostCompressed(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
assert.Equal(t, "POST", r.Method)
assert.Equal(t, "gzip", r.Header.Get("Content-Encoding"))
reader, err := gzip.NewReader(r.Body)
assert.Nil(t, err)
defer reader.Close()
buf := bytes.NewBufferString("")
_, err = io.Copy(buf, reader)
assert.Nil(t, err)
assert.Equal(t, []byte("DATA"), buf.Bytes())
contentLength := r.Header["Content-Type"][0]
assert.Equal(t, "text/plain", contentLength)
fmt.Fprintf(w, "OK")
}))
defer ts.Close()
c := New()
parsed, _ := url.Parse(ts.URL)
req := RequestBuilder(parsed).WithMethod("POST").WithContentType("text/plain").WithBody(strings.NewReader("DATA")).WithCompression().Build()
resp, err := c.Do(req)
assert.Nil(t, err)
assert.NotNil(t, resp)
assert.Equal(t, 200, resp.StatusCode)
body, _ := ioutil.ReadAll(resp.Body)
assert.Equal(t, []byte("OK"), body)
assert.Equal(t, int64(2), resp.ContentLength)
}
-22
View File
@@ -1,22 +0,0 @@
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so
# Folders
_obj
_test
# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out
*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*
_testmain.go
*.exe
-14
View File
@@ -1,14 +0,0 @@
language: go
go:
- 1.5.4
- 1.6.3
- 1.7
install:
- go get -v golang.org/x/tools/cmd/cover
script:
- go test -v -tags=safe ./spew
- go test -v -tags=testcgo ./spew -covermode=count -coverprofile=profile.cov
after_success:
- go get -v github.com/mattn/goveralls
- export PATH=$PATH:$HOME/gopath/bin
- goveralls -coverprofile=profile.cov -service=travis-ci
-205
View File
@@ -1,205 +0,0 @@
go-spew
=======
[![Build Status](https://img.shields.io/travis/davecgh/go-spew.svg)]
(https://travis-ci.org/davecgh/go-spew) [![ISC License]
(http://img.shields.io/badge/license-ISC-blue.svg)](http://copyfree.org) [![Coverage Status]
(https://img.shields.io/coveralls/davecgh/go-spew.svg)]
(https://coveralls.io/r/davecgh/go-spew?branch=master)
Go-spew implements a deep pretty printer for Go data structures to aid in
debugging. A comprehensive suite of tests with 100% test coverage is provided
to ensure proper functionality. See `test_coverage.txt` for the gocov coverage
report. Go-spew is licensed under the liberal ISC license, so it may be used in
open source or commercial projects.
If you're interested in reading about how this package came to life and some
of the challenges involved in providing a deep pretty printer, there is a blog
post about it
[here](https://web.archive.org/web/20160304013555/https://blog.cyphertite.com/go-spew-a-journey-into-dumping-go-data-structures/).
## Documentation
[![GoDoc](https://img.shields.io/badge/godoc-reference-blue.svg)]
(http://godoc.org/github.com/davecgh/go-spew/spew)
Full `go doc` style documentation for the project can be viewed online without
installing this package by using the excellent GoDoc site here:
http://godoc.org/github.com/davecgh/go-spew/spew
You can also view the documentation locally once the package is installed with
the `godoc` tool by running `godoc -http=":6060"` and pointing your browser to
http://localhost:6060/pkg/github.com/davecgh/go-spew/spew
## Installation
```bash
$ go get -u github.com/davecgh/go-spew/spew
```
## Quick Start
Add this import line to the file you're working in:
```Go
import "github.com/davecgh/go-spew/spew"
```
To dump a variable with full newlines, indentation, type, and pointer
information use Dump, Fdump, or Sdump:
```Go
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):
```Go
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)
```
## Debugging a Web Application Example
Here is an example of how you can use `spew.Sdump()` to help debug a web application. Please be sure to wrap your output using the `html.EscapeString()` function for safety reasons. You should also only use this debugging technique in a development environment, never in production.
```Go
package main
import (
"fmt"
"html"
"net/http"
"github.com/davecgh/go-spew/spew"
)
func handler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/html")
fmt.Fprintf(w, "Hi there, %s!", r.URL.Path[1:])
fmt.Fprintf(w, "<!--\n" + html.EscapeString(spew.Sdump(w)) + "\n-->")
}
func main() {
http.HandleFunc("/", handler)
http.ListenAndServe(":8080", nil)
}
```
## Sample Dump Output
```
(main.Foo) {
unexportedField: (*main.Bar)(0xf84002e210)({
flag: (main.Flag) flagTwo,
data: (uintptr) <nil>
}),
ExportedField: (map[interface {}]interface {}) {
(string) "one": (bool) true
}
}
([]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|
}
```
## 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>}
```
## 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.
```
* 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. This option
relies on access to the unsafe package, so it will not have any effect when
running in environments without access to the unsafe package such as Google
App Engine or with the "safe" build tag specified.
Pointer method invocation is enabled by default.
* DisablePointerAddresses
DisablePointerAddresses specifies whether to disable the printing of
pointer addresses. This is useful when diffing data structures in tests.
* DisableCapacities
DisableCapacities specifies whether to disable the printing of capacities
for arrays, slices, maps and channels. This is useful when diffing data
structures in tests.
* ContinueOnMethod
Enables recursion into types after invoking error and Stringer interface
methods. Recursion after method invocation is disabled by default.
* SortKeys
Specifies map keys should be sorted before being printed. Use
this to have a more deterministic, diffable output. Note that
only native types (bool, int, uint, floats, uintptr and string)
and types which implement error or Stringer interfaces are supported,
with other types sorted according to the reflect.Value.String() output
which guarantees display stability. Natural map order is used by
default.
* SpewKeys
SpewKeys specifies that, as a last resort attempt, map keys should be
spewed to strings and sorted by those strings. This is only considered
if SortKeys is true.
```
## Unsafe Package Dependency
This package relies on the unsafe package to perform some of the more advanced
features, however it also supports a "limited" mode which allows it to work in
environments where the unsafe package is not available. By default, it will
operate in this mode on Google App Engine and when compiled with GopherJS. The
"safe" build tag may also be specified to force the package to build without
using the unsafe package.
## License
Go-spew is licensed under the [copyfree](http://copyfree.org) ISC License.
-22
View File
@@ -1,22 +0,0 @@
#!/bin/sh
# This script uses gocov to generate a test coverage report.
# The gocov tool my be obtained with the following command:
# go get github.com/axw/gocov/gocov
#
# It will be installed to $GOPATH/bin, so ensure that location is in your $PATH.
# Check for gocov.
if ! type gocov >/dev/null 2>&1; then
echo >&2 "This script requires the gocov tool."
echo >&2 "You may obtain it with the following command:"
echo >&2 "go get github.com/axw/gocov/gocov"
exit 1
fi
# Only run the cgo tests if gcc is installed.
if type gcc >/dev/null 2>&1; then
(cd spew && gocov test -tags testcgo | gocov report)
else
(cd spew && gocov test | gocov report)
fi
-298
View File
@@ -1,298 +0,0 @@
/*
* Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
package spew_test
import (
"fmt"
"reflect"
"testing"
"github.com/davecgh/go-spew/spew"
)
// 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
}
type sortableStruct struct {
x int
}
func (ss sortableStruct) String() string {
return fmt.Sprintf("ss.%d", ss.x)
}
type unsortableStruct struct {
x int
}
type sortTestCase struct {
input []reflect.Value
expected []reflect.Value
}
func helpTestSortValues(tests []sortTestCase, cs *spew.ConfigState, t *testing.T) {
getInterfaces := func(values []reflect.Value) []interface{} {
interfaces := []interface{}{}
for _, v := range values {
interfaces = append(interfaces, v.Interface())
}
return interfaces
}
for _, test := range tests {
spew.SortValues(test.input, cs)
// 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)
}
}
}
// TestSortValues ensures the sort functionality for relect.Value based sorting
// works as intended.
func TestSortValues(t *testing.T) {
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 := []sortTestCase{
// 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},
},
// Array
{
[]reflect.Value{v([3]int{3, 2, 1}), v([3]int{1, 3, 2}), v([3]int{1, 2, 3})},
[]reflect.Value{v([3]int{1, 2, 3}), v([3]int{1, 3, 2}), v([3]int{3, 2, 1})},
},
// Uintptrs.
{
[]reflect.Value{v(uintptr(2)), v(uintptr(1)), v(uintptr(3))},
[]reflect.Value{v(uintptr(1)), v(uintptr(2)), v(uintptr(3))},
},
// SortableStructs.
{
// Note: not sorted - DisableMethods is set.
[]reflect.Value{v(sortableStruct{2}), v(sortableStruct{1}), v(sortableStruct{3})},
[]reflect.Value{v(sortableStruct{2}), v(sortableStruct{1}), v(sortableStruct{3})},
},
// UnsortableStructs.
{
// Note: not sorted - SpewKeys is false.
[]reflect.Value{v(unsortableStruct{2}), v(unsortableStruct{1}), v(unsortableStruct{3})},
[]reflect.Value{v(unsortableStruct{2}), v(unsortableStruct{1}), v(unsortableStruct{3})},
},
// Invalid.
{
[]reflect.Value{embedB, embedA, embedC},
[]reflect.Value{embedB, embedA, embedC},
},
}
cs := spew.ConfigState{DisableMethods: true, SpewKeys: false}
helpTestSortValues(tests, &cs, t)
}
// TestSortValuesWithMethods ensures the sort functionality for relect.Value
// based sorting works as intended when using string methods.
func TestSortValuesWithMethods(t *testing.T) {
v := reflect.ValueOf
a := v("a")
b := v("b")
c := v("c")
tests := []sortTestCase{
// Ints.
{
[]reflect.Value{v(2), v(1), v(3)},
[]reflect.Value{v(1), v(2), v(3)},
},
// Strings.
{
[]reflect.Value{b, a, c},
[]reflect.Value{a, b, c},
},
// SortableStructs.
{
[]reflect.Value{v(sortableStruct{2}), v(sortableStruct{1}), v(sortableStruct{3})},
[]reflect.Value{v(sortableStruct{1}), v(sortableStruct{2}), v(sortableStruct{3})},
},
// UnsortableStructs.
{
// Note: not sorted - SpewKeys is false.
[]reflect.Value{v(unsortableStruct{2}), v(unsortableStruct{1}), v(unsortableStruct{3})},
[]reflect.Value{v(unsortableStruct{2}), v(unsortableStruct{1}), v(unsortableStruct{3})},
},
}
cs := spew.ConfigState{DisableMethods: false, SpewKeys: false}
helpTestSortValues(tests, &cs, t)
}
// TestSortValuesWithSpew ensures the sort functionality for relect.Value
// based sorting works as intended when using spew to stringify keys.
func TestSortValuesWithSpew(t *testing.T) {
v := reflect.ValueOf
a := v("a")
b := v("b")
c := v("c")
tests := []sortTestCase{
// Ints.
{
[]reflect.Value{v(2), v(1), v(3)},
[]reflect.Value{v(1), v(2), v(3)},
},
// Strings.
{
[]reflect.Value{b, a, c},
[]reflect.Value{a, b, c},
},
// SortableStructs.
{
[]reflect.Value{v(sortableStruct{2}), v(sortableStruct{1}), v(sortableStruct{3})},
[]reflect.Value{v(sortableStruct{1}), v(sortableStruct{2}), v(sortableStruct{3})},
},
// UnsortableStructs.
{
[]reflect.Value{v(unsortableStruct{2}), v(unsortableStruct{1}), v(unsortableStruct{3})},
[]reflect.Value{v(unsortableStruct{1}), v(unsortableStruct{2}), v(unsortableStruct{3})},
},
}
cs := spew.ConfigState{DisableMethods: true, SpewKeys: true}
helpTestSortValues(tests, &cs, t)
}
File diff suppressed because it is too large Load Diff
-99
View File
@@ -1,99 +0,0 @@
// Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
//
// Permission to use, copy, modify, and distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
// 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, v2l, v2c := testdata.GetCgoCharArray()
v2Len := fmt.Sprintf("%d", v2l)
v2Cap := fmt.Sprintf("%d", v2c)
v2t := "[6]testdata._Ctype_char"
v2s := "(len=" + v2Len + " cap=" + v2Cap + ") " +
"{\n 00000000 74 65 73 74 32 00 " +
" |test2.|\n}"
addDumpTest(v2, "("+v2t+") "+v2s+"\n")
// C unsigned char array.
v3, v3l, v3c := testdata.GetCgoUnsignedCharArray()
v3Len := fmt.Sprintf("%d", v3l)
v3Cap := fmt.Sprintf("%d", v3c)
v3t := "[6]testdata._Ctype_unsignedchar"
v3t2 := "[6]testdata._Ctype_uchar"
v3s := "(len=" + v3Len + " cap=" + v3Cap + ") " +
"{\n 00000000 74 65 73 74 33 00 " +
" |test3.|\n}"
addDumpTest(v3, "("+v3t+") "+v3s+"\n", "("+v3t2+") "+v3s+"\n")
// C signed char array.
v4, v4l, v4c := testdata.GetCgoSignedCharArray()
v4Len := fmt.Sprintf("%d", v4l)
v4Cap := fmt.Sprintf("%d", v4c)
v4t := "[6]testdata._Ctype_schar"
v4t2 := "testdata._Ctype_schar"
v4s := "(len=" + v4Len + " cap=" + v4Cap + ") " +
"{\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, v5l, v5c := testdata.GetCgoUint8tArray()
v5Len := fmt.Sprintf("%d", v5l)
v5Cap := fmt.Sprintf("%d", v5c)
v5t := "[6]testdata._Ctype_uint8_t"
v5s := "(len=" + v5Len + " cap=" + v5Cap + ") " +
"{\n 00000000 74 65 73 74 35 00 " +
" |test5.|\n}"
addDumpTest(v5, "("+v5t+") "+v5s+"\n")
// C typedefed unsigned char array.
v6, v6l, v6c := testdata.GetCgoTypdefedUnsignedCharArray()
v6Len := fmt.Sprintf("%d", v6l)
v6Cap := fmt.Sprintf("%d", v6c)
v6t := "[6]testdata._Ctype_custom_uchar_t"
v6s := "(len=" + v6Len + " cap=" + v6Cap + ") " +
"{\n 00000000 74 65 73 74 36 00 " +
" |test6.|\n}"
addDumpTest(v6, "("+v6t+") "+v6s+"\n")
}
-26
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.
}
-226
View File
@@ -1,226 +0,0 @@
/*
* Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
package spew_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 {
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 {
data uintptr
}
type Foo struct {
unexportedField Bar
ExportedField map[interface{}]interface{}
}
*/
// Setup some sample data structures for the example.
bar := Bar{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) {
// data: (uintptr) <nil>
// },
// ExportedField: (map[interface {}]interface {}) (len=1) {
// (string) (len=3) "one": (bool) true
// }
// }
// (spew_test.Flag) Unknown flag (5)
// ([]uint8) (len=34 cap=34) {
// 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) (len=1) {
// (string) (len=3) "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{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) {
// data: (uintptr) <nil>
// },
// ExportedField: (map[interface {}]interface {}) (len=1) {
// (string) (len=3) "one": (bool) true
// }
// }
// (spew_test.Foo) {
// unexportedField: (spew_test.Bar) {
// data: (uintptr) <nil>
// },
// ExportedField: (map[interface {}]interface {}) (len=1) {
// (string) (len=3) "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
}
File diff suppressed because it is too large Load Diff
-87
View File
@@ -1,87 +0,0 @@
/*
* Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/*
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"
)
// 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)
}
}
// SortValues makes the internal sortValues function available to the test
// package.
func SortValues(values []reflect.Value, cs *ConfigState) {
sortValues(values, cs)
}
-102
View File
@@ -1,102 +0,0 @@
// Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
// Permission to use, copy, modify, and distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
// NOTE: Due to the following build constraints, this file will only be compiled
// when the code is not running on Google App Engine, compiled by GopherJS, and
// "-tags safe" is not added to the go build command line. The "disableunsafe"
// tag is deprecated and thus should not be used.
// +build !js,!appengine,!safe,!disableunsafe
/*
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"
)
// 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)
}
}
-320
View File
@@ -1,320 +0,0 @@
/*
* Copyright (c) 2013-2016 Dave Collins <dave@davec.name>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
package spew_test
import (
"bytes"
"fmt"
"io/ioutil"
"os"
"testing"
"github.com/davecgh/go-spew/spew"
)
// 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}
scsNoPtrAddr := &spew.ConfigState{DisablePointerAddresses: true}
scsNoCap := &spew.ConfigState{DisableCapacities: true}
// Variables for tests on types which implement Stringer interface with and
// without a pointer receiver.
ts := stringer("test")
tps := pstringer("test")
type ptrTester struct {
s *struct{}
}
tptr := &ptrTester{s: &struct{}{}}
// 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) (len=1 cap=1) {\n <max depth reached>\n },\n" +
" slice: ([]string) (len=1 cap=1) {\n <max depth reached>\n },\n" +
" m: (map[string]int) (len=1) {\n <max depth reached>\n }\n}\n"},
{scsContinue, fCSFprint, "", ts, "(stringer test) test"},
{scsContinue, fCSFdump, "", ts, "(spew_test.stringer) " +
"(len=4) (stringer test) \"test\"\n"},
{scsContinue, fCSFprint, "", te, "(error: 10) 10"},
{scsContinue, fCSFdump, "", te, "(spew_test.customError) " +
"(error: 10) 10\n"},
{scsNoPtrAddr, fCSFprint, "", tptr, "<*>{<*>{}}"},
{scsNoPtrAddr, fCSSdump, "", tptr, "(*spew_test.ptrTester)({\ns: (*struct {})({\n})\n})\n"},
{scsNoCap, fCSSdump, "", make([]string, 0, 10), "([]string) {\n}\n"},
{scsNoCap, fCSSdump, "", make([]string, 1, 10), "([]string) (len=1) {\n(string) \"\"\n}\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
}
}
}
-61
View File
@@ -1,61 +0,0 @@
github.com/davecgh/go-spew/spew/dump.go dumpState.dump 100.00% (88/88)
github.com/davecgh/go-spew/spew/format.go formatState.format 100.00% (82/82)
github.com/davecgh/go-spew/spew/format.go formatState.formatPtr 100.00% (52/52)
github.com/davecgh/go-spew/spew/dump.go dumpState.dumpPtr 100.00% (44/44)
github.com/davecgh/go-spew/spew/dump.go dumpState.dumpSlice 100.00% (39/39)
github.com/davecgh/go-spew/spew/common.go handleMethods 100.00% (30/30)
github.com/davecgh/go-spew/spew/common.go printHexPtr 100.00% (18/18)
github.com/davecgh/go-spew/spew/common.go unsafeReflectValue 100.00% (13/13)
github.com/davecgh/go-spew/spew/format.go formatState.constructOrigFormat 100.00% (12/12)
github.com/davecgh/go-spew/spew/dump.go fdump 100.00% (11/11)
github.com/davecgh/go-spew/spew/format.go formatState.Format 100.00% (11/11)
github.com/davecgh/go-spew/spew/common.go init 100.00% (10/10)
github.com/davecgh/go-spew/spew/common.go printComplex 100.00% (9/9)
github.com/davecgh/go-spew/spew/common.go valuesSorter.Less 100.00% (8/8)
github.com/davecgh/go-spew/spew/format.go formatState.buildDefaultFormat 100.00% (7/7)
github.com/davecgh/go-spew/spew/format.go formatState.unpackValue 100.00% (5/5)
github.com/davecgh/go-spew/spew/dump.go dumpState.indent 100.00% (4/4)
github.com/davecgh/go-spew/spew/common.go catchPanic 100.00% (4/4)
github.com/davecgh/go-spew/spew/config.go ConfigState.convertArgs 100.00% (4/4)
github.com/davecgh/go-spew/spew/spew.go convertArgs 100.00% (4/4)
github.com/davecgh/go-spew/spew/format.go newFormatter 100.00% (3/3)
github.com/davecgh/go-spew/spew/dump.go Sdump 100.00% (3/3)
github.com/davecgh/go-spew/spew/common.go printBool 100.00% (3/3)
github.com/davecgh/go-spew/spew/common.go sortValues 100.00% (3/3)
github.com/davecgh/go-spew/spew/config.go ConfigState.Sdump 100.00% (3/3)
github.com/davecgh/go-spew/spew/dump.go dumpState.unpackValue 100.00% (3/3)
github.com/davecgh/go-spew/spew/spew.go Printf 100.00% (1/1)
github.com/davecgh/go-spew/spew/spew.go Println 100.00% (1/1)
github.com/davecgh/go-spew/spew/spew.go Sprint 100.00% (1/1)
github.com/davecgh/go-spew/spew/spew.go Sprintf 100.00% (1/1)
github.com/davecgh/go-spew/spew/spew.go Sprintln 100.00% (1/1)
github.com/davecgh/go-spew/spew/common.go printFloat 100.00% (1/1)
github.com/davecgh/go-spew/spew/config.go NewDefaultConfig 100.00% (1/1)
github.com/davecgh/go-spew/spew/common.go printInt 100.00% (1/1)
github.com/davecgh/go-spew/spew/common.go printUint 100.00% (1/1)
github.com/davecgh/go-spew/spew/common.go valuesSorter.Len 100.00% (1/1)
github.com/davecgh/go-spew/spew/common.go valuesSorter.Swap 100.00% (1/1)
github.com/davecgh/go-spew/spew/config.go ConfigState.Errorf 100.00% (1/1)
github.com/davecgh/go-spew/spew/config.go ConfigState.Fprint 100.00% (1/1)
github.com/davecgh/go-spew/spew/config.go ConfigState.Fprintf 100.00% (1/1)
github.com/davecgh/go-spew/spew/config.go ConfigState.Fprintln 100.00% (1/1)
github.com/davecgh/go-spew/spew/config.go ConfigState.Print 100.00% (1/1)
github.com/davecgh/go-spew/spew/config.go ConfigState.Printf 100.00% (1/1)
github.com/davecgh/go-spew/spew/config.go ConfigState.Println 100.00% (1/1)
github.com/davecgh/go-spew/spew/config.go ConfigState.Sprint 100.00% (1/1)
github.com/davecgh/go-spew/spew/config.go ConfigState.Sprintf 100.00% (1/1)
github.com/davecgh/go-spew/spew/config.go ConfigState.Sprintln 100.00% (1/1)
github.com/davecgh/go-spew/spew/config.go ConfigState.NewFormatter 100.00% (1/1)
github.com/davecgh/go-spew/spew/config.go ConfigState.Fdump 100.00% (1/1)
github.com/davecgh/go-spew/spew/config.go ConfigState.Dump 100.00% (1/1)
github.com/davecgh/go-spew/spew/dump.go Fdump 100.00% (1/1)
github.com/davecgh/go-spew/spew/dump.go Dump 100.00% (1/1)
github.com/davecgh/go-spew/spew/spew.go Fprintln 100.00% (1/1)
github.com/davecgh/go-spew/spew/format.go NewFormatter 100.00% (1/1)
github.com/davecgh/go-spew/spew/spew.go Errorf 100.00% (1/1)
github.com/davecgh/go-spew/spew/spew.go Fprint 100.00% (1/1)
github.com/davecgh/go-spew/spew/spew.go Fprintf 100.00% (1/1)
github.com/davecgh/go-spew/spew/spew.go Print 100.00% (1/1)
github.com/davecgh/go-spew/spew ------------------------------- 100.00% (505/505)
-3
View File
@@ -1,3 +0,0 @@
language: go
go: 1.4
-58
View File
@@ -1,58 +0,0 @@
# CamelCase [![GoDoc](http://img.shields.io/badge/go-documentation-blue.svg?style=flat-square)](http://godoc.org/github.com/fatih/camelcase) [![Build Status](http://img.shields.io/travis/fatih/camelcase.svg?style=flat-square)](https://travis-ci.org/fatih/camelcase)
CamelCase is a Golang (Go) package to split the words of a camelcase type
string into a slice of words. It can be used to convert a camelcase word (lower
or upper case) into any type of word.
## Splitting rules:
1. If string is not valid UTF-8, return it without splitting as
single item array.
2. Assign all unicode characters into one of 4 sets: lower case
letters, upper case letters, numbers, and all other characters.
3. Iterate through characters of string, introducing splits
between adjacent characters that belong to different sets.
4. Iterate through array of split strings, and if a given string
is upper case:
* if subsequent string is lower case:
* move last character of upper case string to beginning of
lower case string
## Install
```bash
go get github.com/fatih/camelcase
```
## Usage and examples
```go
splitted := camelcase.Split("GolangPackage")
fmt.Println(splitted[0], splitted[1]) // prints: "Golang", "Package"
```
Both lower camel case and upper camel case are supported. For more info please
check: [http://en.wikipedia.org/wiki/CamelCase](http://en.wikipedia.org/wiki/CamelCase)
Below are some example cases:
```
"" => []
"lowercase" => ["lowercase"]
"Class" => ["Class"]
"MyClass" => ["My", "Class"]
"MyC" => ["My", "C"]
"HTML" => ["HTML"]
"PDFLoader" => ["PDF", "Loader"]
"AString" => ["A", "String"]
"SimpleXMLParser" => ["Simple", "XML", "Parser"]
"vimRPCPlugin" => ["vim", "RPC", "Plugin"]
"GL11Version" => ["GL", "11", "Version"]
"99Bottles" => ["99", "Bottles"]
"May5" => ["May", "5"]
"BFG9000" => ["BFG", "9000"]
"BöseÜberraschung" => ["Böse", "Überraschung"]
"Two spaces" => ["Two", " ", "spaces"]
"BadUTF8\xe2\xe2\xa1" => ["BadUTF8\xe2\xe2\xa1"]
```
-47
View File
@@ -1,47 +0,0 @@
package camelcase
import "fmt"
func ExampleSplit() {
for _, c := range []string{
"",
"lowercase",
"Class",
"MyClass",
"MyC",
"HTML",
"PDFLoader",
"AString",
"SimpleXMLParser",
"vimRPCPlugin",
"GL11Version",
"99Bottles",
"May5",
"BFG9000",
"BöseÜberraschung",
"Two spaces",
"BadUTF8\xe2\xe2\xa1",
} {
fmt.Printf("%#v => %#v\n", c, Split(c))
}
// Output:
// "" => []string{}
// "lowercase" => []string{"lowercase"}
// "Class" => []string{"Class"}
// "MyClass" => []string{"My", "Class"}
// "MyC" => []string{"My", "C"}
// "HTML" => []string{"HTML"}
// "PDFLoader" => []string{"PDF", "Loader"}
// "AString" => []string{"A", "String"}
// "SimpleXMLParser" => []string{"Simple", "XML", "Parser"}
// "vimRPCPlugin" => []string{"vim", "RPC", "Plugin"}
// "GL11Version" => []string{"GL", "11", "Version"}
// "99Bottles" => []string{"99", "Bottles"}
// "May5" => []string{"May", "5"}
// "BFG9000" => []string{"BFG", "9000"}
// "BöseÜberraschung" => []string{"Böse", "Überraschung"}
// "Two spaces" => []string{"Two", " ", "spaces"}
// "BadUTF8\xe2\xe2\xa1" => []string{"BadUTF8\xe2\xe2\xa1"}
}
-36
View File
@@ -1,36 +0,0 @@
go.dbus
-------
go.dbus is a simple library that implements native Go client bindings for the
D-Bus message bus system.
### Features
* Complete native implementation of the D-Bus message protocol
* Go-like API (channels for signals / asynchronous method calls, Goroutine-safe connections)
* Subpackages that help with the introspection / property interfaces
### Installation
This packages requires Go 1.1. If you installed it and set up your GOPATH, just run:
```
go get github.com/guelfey/go.dbus
```
If you want to use the subpackages, you can install them the same way.
### Usage
The complete package documentation and some simple examples are available at
[godoc.org](http://godoc.org/github.com/guelfey/go.dbus). Also, the
[_examples](https://github.com/guelfey/go.dbus/tree/master/_examples) directory
gives a short overview over the basic usage.
Please note that the API is considered unstable for now and may change without
further notice.
### License
go.dbus is available under the Simplified BSD License; see LICENSE for the full
text.
-199
View File
@@ -1,199 +0,0 @@
package dbus
import "testing"
func TestSessionBus(t *testing.T) {
_, err := SessionBus()
if err != nil {
t.Error(err)
}
}
func TestSystemBus(t *testing.T) {
_, err := SystemBus()
if err != nil {
t.Error(err)
}
}
func TestSend(t *testing.T) {
bus, err := SessionBus()
if err != nil {
t.Error(err)
}
ch := make(chan *Call, 1)
msg := &Message{
Type: TypeMethodCall,
Flags: 0,
Headers: map[HeaderField]Variant{
FieldDestination: MakeVariant(bus.Names()[0]),
FieldPath: MakeVariant(ObjectPath("/org/freedesktop/DBus")),
FieldInterface: MakeVariant("org.freedesktop.DBus.Peer"),
FieldMember: MakeVariant("Ping"),
},
}
call := bus.Send(msg, ch)
<-ch
if call.Err != nil {
t.Error(call.Err)
}
}
type server struct{}
func (server) Double(i int64) (int64, *Error) {
return 2 * i, nil
}
func BenchmarkCall(b *testing.B) {
b.StopTimer()
var s string
bus, err := SessionBus()
if err != nil {
b.Fatal(err)
}
name := bus.Names()[0]
obj := bus.BusObject()
b.StartTimer()
for i := 0; i < b.N; i++ {
err := obj.Call("org.freedesktop.DBus.GetNameOwner", 0, name).Store(&s)
if err != nil {
b.Fatal(err)
}
if s != name {
b.Errorf("got %s, wanted %s", s, name)
}
}
}
func BenchmarkCallAsync(b *testing.B) {
b.StopTimer()
bus, err := SessionBus()
if err != nil {
b.Fatal(err)
}
name := bus.Names()[0]
obj := bus.BusObject()
c := make(chan *Call, 50)
done := make(chan struct{})
go func() {
for i := 0; i < b.N; i++ {
v := <-c
if v.Err != nil {
b.Error(v.Err)
}
s := v.Body[0].(string)
if s != name {
b.Errorf("got %s, wanted %s", s, name)
}
}
close(done)
}()
b.StartTimer()
for i := 0; i < b.N; i++ {
obj.Go("org.freedesktop.DBus.GetNameOwner", 0, c, name)
}
<-done
}
func BenchmarkServe(b *testing.B) {
b.StopTimer()
srv, err := SessionBus()
if err != nil {
b.Fatal(err)
}
cli, err := SessionBusPrivate()
if err != nil {
b.Fatal(err)
}
if err = cli.Auth(nil); err != nil {
b.Fatal(err)
}
if err = cli.Hello(); err != nil {
b.Fatal(err)
}
benchmarkServe(b, srv, cli)
}
func BenchmarkServeAsync(b *testing.B) {
b.StopTimer()
srv, err := SessionBus()
if err != nil {
b.Fatal(err)
}
cli, err := SessionBusPrivate()
if err != nil {
b.Fatal(err)
}
if err = cli.Auth(nil); err != nil {
b.Fatal(err)
}
if err = cli.Hello(); err != nil {
b.Fatal(err)
}
benchmarkServeAsync(b, srv, cli)
}
func BenchmarkServeSameConn(b *testing.B) {
b.StopTimer()
bus, err := SessionBus()
if err != nil {
b.Fatal(err)
}
benchmarkServe(b, bus, bus)
}
func BenchmarkServeSameConnAsync(b *testing.B) {
b.StopTimer()
bus, err := SessionBus()
if err != nil {
b.Fatal(err)
}
benchmarkServeAsync(b, bus, bus)
}
func benchmarkServe(b *testing.B, srv, cli *Conn) {
var r int64
var err error
dest := srv.Names()[0]
srv.Export(server{}, "/org/guelfey/DBus/Test", "org.guelfey.DBus.Test")
obj := cli.Object(dest, "/org/guelfey/DBus/Test")
b.StartTimer()
for i := 0; i < b.N; i++ {
err = obj.Call("org.guelfey.DBus.Test.Double", 0, int64(i)).Store(&r)
if err != nil {
b.Fatal(err)
}
if r != 2*int64(i) {
b.Errorf("got %d, wanted %d", r, 2*int64(i))
}
}
}
func benchmarkServeAsync(b *testing.B, srv, cli *Conn) {
dest := srv.Names()[0]
srv.Export(server{}, "/org/guelfey/DBus/Test", "org.guelfey.DBus.Test")
obj := cli.Object(dest, "/org/guelfey/DBus/Test")
c := make(chan *Call, 50)
done := make(chan struct{})
go func() {
for i := 0; i < b.N; i++ {
v := <-c
if v.Err != nil {
b.Fatal(v.Err)
}
i, r := v.Args[0].(int64), v.Body[0].(int64)
if 2*i != r {
b.Errorf("got %d, wanted %d", r, 2*i)
}
}
close(done)
}()
b.StartTimer()
for i := 0; i < b.N; i++ {
obj.Go("org.guelfey.DBus.Test.Double", 0, c, int64(i))
}
<-done
}
-50
View File
@@ -1,50 +0,0 @@
package dbus
import "fmt"
func ExampleConn_Emit() {
conn, err := SessionBus()
if err != nil {
panic(err)
}
conn.Emit("/foo/bar", "foo.bar.Baz", uint32(0xDAEDBEEF))
}
func ExampleObject_Call() {
var list []string
conn, err := SessionBus()
if err != nil {
panic(err)
}
err = conn.BusObject().Call("org.freedesktop.DBus.ListNames", 0).Store(&list)
if err != nil {
panic(err)
}
for _, v := range list {
fmt.Println(v)
}
}
func ExampleObject_Go() {
conn, err := SessionBus()
if err != nil {
panic(err)
}
ch := make(chan *Call, 10)
conn.BusObject().Go("org.freedesktop.DBus.ListActivatableNames", 0, ch)
select {
case call := <-ch:
if call.Err != nil {
panic(err)
}
list := call.Body[0].([]string)
for _, v := range list {
fmt.Println(v)
}
// put some other cases here
}
}
-369
View File
@@ -1,369 +0,0 @@
package dbus
import (
"bytes"
"encoding/binary"
"io/ioutil"
"math"
"reflect"
"testing"
)
var protoTests = []struct {
vs []interface{}
bigEndian []byte
littleEndian []byte
}{
{
[]interface{}{int32(0)},
[]byte{0, 0, 0, 0},
[]byte{0, 0, 0, 0},
},
{
[]interface{}{true, false},
[]byte{0, 0, 0, 1, 0, 0, 0, 0},
[]byte{1, 0, 0, 0, 0, 0, 0, 0},
},
{
[]interface{}{byte(0), uint16(12), int16(32), uint32(43)},
[]byte{0, 0, 0, 12, 0, 32, 0, 0, 0, 0, 0, 43},
[]byte{0, 0, 12, 0, 32, 0, 0, 0, 43, 0, 0, 0},
},
{
[]interface{}{int64(-1), uint64(1<<64 - 1)},
bytes.Repeat([]byte{255}, 16),
bytes.Repeat([]byte{255}, 16),
},
{
[]interface{}{math.Inf(+1)},
[]byte{0x7f, 0xf0, 0, 0, 0, 0, 0, 0},
[]byte{0, 0, 0, 0, 0, 0, 0xf0, 0x7f},
},
{
[]interface{}{"foo"},
[]byte{0, 0, 0, 3, 'f', 'o', 'o', 0},
[]byte{3, 0, 0, 0, 'f', 'o', 'o', 0},
},
{
[]interface{}{Signature{"ai"}},
[]byte{2, 'a', 'i', 0},
[]byte{2, 'a', 'i', 0},
},
{
[]interface{}{[]int16{42, 256}},
[]byte{0, 0, 0, 4, 0, 42, 1, 0},
[]byte{4, 0, 0, 0, 42, 0, 0, 1},
},
{
[]interface{}{MakeVariant("foo")},
[]byte{1, 's', 0, 0, 0, 0, 0, 3, 'f', 'o', 'o', 0},
[]byte{1, 's', 0, 0, 3, 0, 0, 0, 'f', 'o', 'o', 0},
},
{
[]interface{}{MakeVariant(MakeVariant(Signature{"v"}))},
[]byte{1, 'v', 0, 1, 'g', 0, 1, 'v', 0},
[]byte{1, 'v', 0, 1, 'g', 0, 1, 'v', 0},
},
{
[]interface{}{map[int32]bool{42: true}},
[]byte{0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 42, 0, 0, 0, 1},
[]byte{8, 0, 0, 0, 0, 0, 0, 0, 42, 0, 0, 0, 1, 0, 0, 0},
},
{
[]interface{}{map[string]Variant{}, byte(42)},
[]byte{0, 0, 0, 0, 0, 0, 0, 0, 42},
[]byte{0, 0, 0, 0, 0, 0, 0, 0, 42},
},
{
[]interface{}{[]uint64{}, byte(42)},
[]byte{0, 0, 0, 0, 0, 0, 0, 0, 42},
[]byte{0, 0, 0, 0, 0, 0, 0, 0, 42},
},
}
func TestProto(t *testing.T) {
for i, v := range protoTests {
buf := new(bytes.Buffer)
bigEnc := newEncoder(buf, binary.BigEndian)
bigEnc.Encode(v.vs...)
marshalled := buf.Bytes()
if bytes.Compare(marshalled, v.bigEndian) != 0 {
t.Errorf("test %d (marshal be): got '%v', but expected '%v'\n", i+1, marshalled,
v.bigEndian)
}
buf.Reset()
litEnc := newEncoder(buf, binary.LittleEndian)
litEnc.Encode(v.vs...)
marshalled = buf.Bytes()
if bytes.Compare(marshalled, v.littleEndian) != 0 {
t.Errorf("test %d (marshal le): got '%v', but expected '%v'\n", i+1, marshalled,
v.littleEndian)
}
unmarshalled := reflect.MakeSlice(reflect.TypeOf(v.vs),
0, 0)
for i := range v.vs {
unmarshalled = reflect.Append(unmarshalled,
reflect.New(reflect.TypeOf(v.vs[i])))
}
bigDec := newDecoder(bytes.NewReader(v.bigEndian), binary.BigEndian)
vs, err := bigDec.Decode(SignatureOf(v.vs...))
if err != nil {
t.Errorf("test %d (unmarshal be): %s\n", i+1, err)
continue
}
if !reflect.DeepEqual(vs, v.vs) {
t.Errorf("test %d (unmarshal be): got %#v, but expected %#v\n", i+1, vs, v.vs)
}
litDec := newDecoder(bytes.NewReader(v.littleEndian), binary.LittleEndian)
vs, err = litDec.Decode(SignatureOf(v.vs...))
if err != nil {
t.Errorf("test %d (unmarshal le): %s\n", i+1, err)
continue
}
if !reflect.DeepEqual(vs, v.vs) {
t.Errorf("test %d (unmarshal le): got %#v, but expected %#v\n", i+1, vs, v.vs)
}
}
}
func TestProtoMap(t *testing.T) {
m := map[string]uint8{
"foo": 23,
"bar": 2,
}
var n map[string]uint8
buf := new(bytes.Buffer)
enc := newEncoder(buf, binary.LittleEndian)
enc.Encode(m)
dec := newDecoder(buf, binary.LittleEndian)
vs, err := dec.Decode(Signature{"a{sy}"})
if err != nil {
t.Fatal(err)
}
if err = Store(vs, &n); err != nil {
t.Fatal(err)
}
if len(n) != 2 || n["foo"] != 23 || n["bar"] != 2 {
t.Error("got", n)
}
}
func TestProtoVariantStruct(t *testing.T) {
var variant Variant
v := MakeVariant(struct {
A int32
B int16
}{1, 2})
buf := new(bytes.Buffer)
enc := newEncoder(buf, binary.LittleEndian)
enc.Encode(v)
dec := newDecoder(buf, binary.LittleEndian)
vs, err := dec.Decode(Signature{"v"})
if err != nil {
t.Fatal(err)
}
if err = Store(vs, &variant); err != nil {
t.Fatal(err)
}
sl := variant.Value().([]interface{})
v1, v2 := sl[0].(int32), sl[1].(int16)
if v1 != int32(1) {
t.Error("got", v1, "as first int")
}
if v2 != int16(2) {
t.Error("got", v2, "as second int")
}
}
func TestProtoStructTag(t *testing.T) {
type Bar struct {
A int32
B chan interface{} `dbus:"-"`
C int32
}
var bar1, bar2 Bar
bar1.A = 234
bar2.C = 345
buf := new(bytes.Buffer)
enc := newEncoder(buf, binary.LittleEndian)
enc.Encode(bar1)
dec := newDecoder(buf, binary.LittleEndian)
vs, err := dec.Decode(Signature{"(ii)"})
if err != nil {
t.Fatal(err)
}
if err = Store(vs, &bar2); err != nil {
t.Fatal(err)
}
if bar1 != bar2 {
t.Error("struct tag test: got", bar2)
}
}
func TestProtoStoreStruct(t *testing.T) {
var foo struct {
A int32
B string
c chan interface{}
D interface{} `dbus:"-"`
}
src := []interface{}{[]interface{}{int32(42), "foo"}}
err := Store(src, &foo)
if err != nil {
t.Fatal(err)
}
}
func TestProtoStoreNestedStruct(t *testing.T) {
var foo struct {
A int32
B struct {
C string
D float64
}
}
src := []interface{}{
[]interface{}{
int32(42),
[]interface{}{
"foo",
3.14,
},
},
}
err := Store(src, &foo)
if err != nil {
t.Fatal(err)
}
}
func TestMessage(t *testing.T) {
buf := new(bytes.Buffer)
message := new(Message)
message.Type = TypeMethodCall
message.serial = 32
message.Headers = map[HeaderField]Variant{
FieldPath: MakeVariant(ObjectPath("/org/foo/bar")),
FieldMember: MakeVariant("baz"),
}
message.Body = make([]interface{}, 0)
err := message.EncodeTo(buf, binary.LittleEndian)
if err != nil {
t.Error(err)
}
_, err = DecodeMessage(buf)
if err != nil {
t.Error(err)
}
}
func TestProtoStructInterfaces(t *testing.T) {
b := []byte{42}
vs, err := newDecoder(bytes.NewReader(b), binary.LittleEndian).Decode(Signature{"(y)"})
if err != nil {
t.Fatal(err)
}
if vs[0].([]interface{})[0].(byte) != 42 {
t.Errorf("wrongs results (got %v)", vs)
}
}
// ordinary org.freedesktop.DBus.Hello call
var smallMessage = &Message{
Type: TypeMethodCall,
serial: 1,
Headers: map[HeaderField]Variant{
FieldDestination: MakeVariant("org.freedesktop.DBus"),
FieldPath: MakeVariant(ObjectPath("/org/freedesktop/DBus")),
FieldInterface: MakeVariant("org.freedesktop.DBus"),
FieldMember: MakeVariant("Hello"),
},
}
// org.freedesktop.Notifications.Notify
var bigMessage = &Message{
Type: TypeMethodCall,
serial: 2,
Headers: map[HeaderField]Variant{
FieldDestination: MakeVariant("org.freedesktop.Notifications"),
FieldPath: MakeVariant(ObjectPath("/org/freedesktop/Notifications")),
FieldInterface: MakeVariant("org.freedesktop.Notifications"),
FieldMember: MakeVariant("Notify"),
FieldSignature: MakeVariant(Signature{"susssasa{sv}i"}),
},
Body: []interface{}{
"app_name",
uint32(0),
"dialog-information",
"Notification",
"This is the body of a notification",
[]string{"ok", "Ok"},
map[string]Variant{
"sound-name": MakeVariant("dialog-information"),
},
int32(-1),
},
}
func BenchmarkDecodeMessageSmall(b *testing.B) {
var err error
var rd *bytes.Reader
b.StopTimer()
buf := new(bytes.Buffer)
err = smallMessage.EncodeTo(buf, binary.LittleEndian)
if err != nil {
b.Fatal(err)
}
decoded := buf.Bytes()
b.StartTimer()
for i := 0; i < b.N; i++ {
rd = bytes.NewReader(decoded)
_, err = DecodeMessage(rd)
if err != nil {
b.Fatal(err)
}
}
}
func BenchmarkDecodeMessageBig(b *testing.B) {
var err error
var rd *bytes.Reader
b.StopTimer()
buf := new(bytes.Buffer)
err = bigMessage.EncodeTo(buf, binary.LittleEndian)
if err != nil {
b.Fatal(err)
}
decoded := buf.Bytes()
b.StartTimer()
for i := 0; i < b.N; i++ {
rd = bytes.NewReader(decoded)
_, err = DecodeMessage(rd)
if err != nil {
b.Fatal(err)
}
}
}
func BenchmarkEncodeMessageSmall(b *testing.B) {
var err error
for i := 0; i < b.N; i++ {
err = smallMessage.EncodeTo(ioutil.Discard, binary.LittleEndian)
if err != nil {
b.Fatal(err)
}
}
}
func BenchmarkEncodeMessageBig(b *testing.B) {
var err error
for i := 0; i < b.N; i++ {
err = bigMessage.EncodeTo(ioutil.Discard, binary.LittleEndian)
if err != nil {
b.Fatal(err)
}
}
}
-70
View File
@@ -1,70 +0,0 @@
package dbus
import (
"testing"
)
var sigTests = []struct {
vs []interface{}
sig Signature
}{
{
[]interface{}{new(int32)},
Signature{"i"},
},
{
[]interface{}{new(string)},
Signature{"s"},
},
{
[]interface{}{new(Signature)},
Signature{"g"},
},
{
[]interface{}{new([]int16)},
Signature{"an"},
},
{
[]interface{}{new(int16), new(uint32)},
Signature{"nu"},
},
{
[]interface{}{new(map[byte]Variant)},
Signature{"a{yv}"},
},
{
[]interface{}{new(Variant), new([]map[int32]string)},
Signature{"vaa{is}"},
},
}
func TestSig(t *testing.T) {
for i, v := range sigTests {
sig := SignatureOf(v.vs...)
if sig != v.sig {
t.Errorf("test %d: got %q, expected %q", i+1, sig.str, v.sig.str)
}
}
}
var getSigTest = []interface{}{
[]struct {
b byte
i int32
t uint64
s string
}{},
map[string]Variant{},
}
func BenchmarkGetSignatureSimple(b *testing.B) {
for i := 0; i < b.N; i++ {
SignatureOf("", int32(0))
}
}
func BenchmarkGetSignatureLong(b *testing.B) {
for i := 0; i < b.N; i++ {
SignatureOf(getSigTest...)
}
}
-49
View File
@@ -1,49 +0,0 @@
package dbus
import (
"os"
"testing"
)
const testString = `This is a test!
This text should be read from the file that is created by this test.`
type unixFDTest struct{}
func (t unixFDTest) Test(fd UnixFD) (string, *Error) {
var b [4096]byte
file := os.NewFile(uintptr(fd), "testfile")
defer file.Close()
n, err := file.Read(b[:])
if err != nil {
return "", &Error{"com.github.guelfey.test.Error", nil}
}
return string(b[:n]), nil
}
func TestUnixFDs(t *testing.T) {
conn, err := SessionBus()
if err != nil {
t.Fatal(err)
}
r, w, err := os.Pipe()
if err != nil {
t.Fatal(err)
}
defer w.Close()
if _, err := w.Write([]byte(testString)); err != nil {
t.Fatal(err)
}
name := conn.Names()[0]
test := unixFDTest{}
conn.Export(test, "/com/github/guelfey/test", "com.github.guelfey.test")
var s string
obj := conn.Object(name, "/com/github/guelfey/test")
err = obj.Call("com.github.guelfey.test.Test", 0, UnixFD(r.Fd())).Store(&s)
if err != nil {
t.Fatal(err)
}
if s != testString {
t.Fatal("got", s, "wanted", testString)
}
}
-78
View File
@@ -1,78 +0,0 @@
package dbus
import "reflect"
import "testing"
var variantFormatTests = []struct {
v interface{}
s string
}{
{int32(1), `1`},
{"foo", `"foo"`},
{ObjectPath("/org/foo"), `@o "/org/foo"`},
{Signature{"i"}, `@g "i"`},
{[]byte{}, `@ay []`},
{[]int32{1, 2}, `[1, 2]`},
{[]int64{1, 2}, `@ax [1, 2]`},
{[][]int32{{3, 4}, {5, 6}}, `[[3, 4], [5, 6]]`},
{[]Variant{MakeVariant(int32(1)), MakeVariant(1.0)}, `[<1>, <@d 1>]`},
{map[string]int32{"one": 1, "two": 2}, `{"one": 1, "two": 2}`},
{map[int32]ObjectPath{1: "/org/foo"}, `@a{io} {1: "/org/foo"}`},
{map[string]Variant{}, `@a{sv} {}`},
}
func TestFormatVariant(t *testing.T) {
for i, v := range variantFormatTests {
if s := MakeVariant(v.v).String(); s != v.s {
t.Errorf("test %d: got %q, wanted %q", i+1, s, v.s)
}
}
}
var variantParseTests = []struct {
s string
v interface{}
}{
{"1", int32(1)},
{"true", true},
{"false", false},
{"1.0", float64(1.0)},
{"0x10", int32(16)},
{"1e1", float64(10)},
{`"foo"`, "foo"},
{`"\a\b\f\n\r\t"`, "\x07\x08\x0c\n\r\t"},
{`"\u00e4\U0001f603"`, "\u00e4\U0001f603"},
{"[1]", []int32{1}},
{"[1, 2, 3]", []int32{1, 2, 3}},
{"@ai []", []int32{}},
{"[1, 5.0]", []float64{1, 5.0}},
{"[[1, 2], [3, 4.0]]", [][]float64{{1, 2}, {3, 4}}},
{`[@o "/org/foo", "/org/bar"]`, []ObjectPath{"/org/foo", "/org/bar"}},
{"<1>", MakeVariant(int32(1))},
{"[<1>, <2.0>]", []Variant{MakeVariant(int32(1)), MakeVariant(2.0)}},
{`[[], [""]]`, [][]string{{}, {""}}},
{`@a{ss} {}`, map[string]string{}},
{`{"foo": 1}`, map[string]int32{"foo": 1}},
{`[{}, {"foo": "bar"}]`, []map[string]string{{}, {"foo": "bar"}}},
{`{"a": <1>, "b": <"foo">}`,
map[string]Variant{"a": MakeVariant(int32(1)), "b": MakeVariant("foo")}},
{`b''`, []byte{0}},
{`b"abc"`, []byte{'a', 'b', 'c', 0}},
{`b"\x01\0002\a\b\f\n\r\t"`, []byte{1, 2, 0x7, 0x8, 0xc, '\n', '\r', '\t', 0}},
{`[[0], b""]`, [][]byte{{0}, {0}}},
{"int16 0", int16(0)},
{"byte 0", byte(0)},
}
func TestParseVariant(t *testing.T) {
for i, v := range variantParseTests {
nv, err := ParseVariant(v.s, Signature{})
if err != nil {
t.Errorf("test %d: parsing failed: %s", i+1, err)
continue
}
if !reflect.DeepEqual(nv.value, v.v) {
t.Errorf("test %d: got %q, wanted %q", i+1, nv, v.v)
}
}
}
-3
View File
@@ -1,3 +0,0 @@
guard 'gotest' do
watch(%r{\.go$})
end
-98
View File
@@ -1,98 +0,0 @@
# Copier
I am a copier, I copy everything from one to another
## Features
* Copy from field to field with same name
* Copy from method to field with same name
* Copy from field to method with same name
* Copy from slice to slice
* Copy from struct to slice
## Usage
```go
package main
import (
"fmt"
"github.com/jinzhu/copier"
)
type User struct {
Name string
Role string
Age int32
}
func (user *User) DoubleAge() int32 {
return 2 * user.Age
}
type Employee struct {
Name string
Age int32
DoubleAge int32
EmployeId int64
SuperRule string
}
func (employee *Employee) Role(role string) {
employee.SuperRule = "Super " + role
}
func main() {
var (
user = User{Name: "Jinzhu", Age: 18, Role: "Admin"}
users = []User{{Name: "Jinzhu", Age: 18, Role: "Admin"}, {Name: "jinzhu 2", Age: 30, Role: "Dev"}}
employee = Employee{}
employees = []Employee{}
)
copier.Copy(&employee, &user)
fmt.Printf("%#v \n", employee)
// Employee{
// Name: "Jinzhu", // Copy from field
// Age: 18, // Copy from field
// DoubleAge: 36, // Copy from method
// EmployeeId: 0, // Ignored
// SuperRule: "Super Admin", // Copy to method
// }
// Copy struct to slice
copier.Copy(&employees, &user)
fmt.Printf("%#v \n", employees)
// []Employee{
// {Name: "Jinzhu", Age: 18, DoubleAge: 36, EmployeId: 0, SuperRule: "Super Admin"}
// }
// Copy slice to slice
employees = []Employee{}
copier.Copy(&employees, &users)
fmt.Printf("%#v \n", employees)
// []Employee{
// {Name: "Jinzhu", Age: 18, DoubleAge: 36, EmployeId: 0, SuperRule: "Super Admin"},
// {Name: "jinzhu 2", Age: 30, DoubleAge: 60, EmployeId: 0, SuperRule: "Super Dev"},
// }
}
```
# Supporting the project
[![http://patreon.com/jinzhu](http://patreon_public_assets.s3.amazonaws.com/sized/becomeAPatronBanner.png)](http://patreon.com/jinzhu)
# Author
**jinzhu**
* <http://github.com/jinzhu>
* <wosmvp@gmail.com>
* <http://twitter.com/zhangjinzhu>
## License
Released under the [MIT License](https://github.com/jinzhu/copier/blob/master/License).
-45
View File
@@ -1,45 +0,0 @@
package copier_test
import (
"encoding/json"
"testing"
"github.com/jinzhu/copier"
)
func BenchmarkCopyStruct(b *testing.B) {
var fakeAge int32 = 12
user := User{Name: "Jinzhu", Nickname: "jinzhu", Age: 18, FakeAge: &fakeAge, Role: "Admin", Notes: []string{"hello world", "welcome"}, flags: []byte{'x'}}
for x := 0; x < b.N; x++ {
copier.Copy(&Employee{}, &user)
}
}
func BenchmarkNamaCopy(b *testing.B) {
var fakeAge int32 = 12
user := User{Name: "Jinzhu", Nickname: "jinzhu", Age: 18, FakeAge: &fakeAge, Role: "Admin", Notes: []string{"hello world", "welcome"}, flags: []byte{'x'}}
for x := 0; x < b.N; x++ {
employee := &Employee{
Name: user.Name,
Nickname: &user.Nickname,
Age: int64(user.Age),
FakeAge: int(*user.FakeAge),
DoubleAge: user.DoubleAge(),
Notes: user.Notes,
}
employee.Role(user.Role)
}
}
func BenchmarkJsonMarshalCopy(b *testing.B) {
var fakeAge int32 = 12
user := User{Name: "Jinzhu", Nickname: "jinzhu", Age: 18, FakeAge: &fakeAge, Role: "Admin", Notes: []string{"hello world", "welcome"}, flags: []byte{'x'}}
for x := 0; x < b.N; x++ {
data, _ := json.Marshal(user)
var employee Employee
json.Unmarshal(data, &employee)
employee.DoubleAge = user.DoubleAge()
employee.Role(user.Role)
}
}
-143
View File
@@ -1,143 +0,0 @@
package copier_test
import (
"testing"
"github.com/jinzhu/copier"
)
type TypeStruct1 struct {
Field1 string
Field2 string
Field3 TypeStruct2
Field4 *TypeStruct2
Field5 []*TypeStruct2
Field6 []TypeStruct2
Field7 []*TypeStruct2
Field8 []TypeStruct2
}
type TypeStruct2 struct {
Field1 int
Field2 string
}
type TypeStruct3 struct {
Field1 interface{}
Field2 string
Field3 TypeStruct4
Field4 *TypeStruct4
Field5 []*TypeStruct4
Field6 []*TypeStruct4
Field7 []TypeStruct4
Field8 []TypeStruct4
}
type TypeStruct4 struct {
field1 int
Field2 string
}
func (t *TypeStruct4) Field1(i int) {
t.field1 = i
}
func TestCopyDifferentFieldType(t *testing.T) {
ts := &TypeStruct1{
Field1: "str1",
Field2: "str2",
}
ts2 := &TypeStruct2{}
copier.Copy(ts2, ts)
if ts2.Field2 != ts.Field2 || ts2.Field1 != 0 {
t.Errorf("Should be able to copy from ts to ts2")
}
}
func TestCopyDifferentTypeMethod(t *testing.T) {
ts := &TypeStruct1{
Field1: "str1",
Field2: "str2",
}
ts4 := &TypeStruct4{}
copier.Copy(ts4, ts)
if ts4.Field2 != ts.Field2 || ts4.field1 != 0 {
t.Errorf("Should be able to copy from ts to ts4")
}
}
func TestAssignableType(t *testing.T) {
ts := &TypeStruct1{
Field1: "str1",
Field2: "str2",
Field3: TypeStruct2{
Field1: 666,
Field2: "str2",
},
Field4: &TypeStruct2{
Field1: 666,
Field2: "str2",
},
Field5: []*TypeStruct2{
{
Field1: 666,
Field2: "str2",
},
},
Field6: []TypeStruct2{
{
Field1: 666,
Field2: "str2",
},
},
Field7: []*TypeStruct2{
{
Field1: 666,
Field2: "str2",
},
},
}
ts3 := &TypeStruct3{}
copier.Copy(&ts3, &ts)
if v, ok := ts3.Field1.(string); !ok {
t.Error("Assign to interface{} type did not succeed")
} else if v != "str1" {
t.Error("String haven't been copied correctly")
}
if ts3.Field2 != ts.Field2 {
t.Errorf("Field2 should be copied")
}
checkType2WithType4(ts.Field3, ts3.Field3, t, "Field3")
checkType2WithType4(*ts.Field4, *ts3.Field4, t, "Field4")
for idx, f := range ts.Field5 {
checkType2WithType4(*f, *(ts3.Field5[idx]), t, "Field5")
}
for idx, f := range ts.Field6 {
checkType2WithType4(f, *(ts3.Field6[idx]), t, "Field6")
}
for idx, f := range ts.Field7 {
checkType2WithType4(*f, ts3.Field7[idx], t, "Field7")
}
for idx, f := range ts.Field8 {
checkType2WithType4(f, ts3.Field8[idx], t, "Field8")
}
}
func checkType2WithType4(t2 TypeStruct2, t4 TypeStruct4, t *testing.T, testCase string) {
if t2.Field1 != t4.field1 || t2.Field2 != t4.Field2 {
t.Errorf("%v: type struct 4 and type struct 2 is not equal", testCase)
}
}
-212
View File
@@ -1,212 +0,0 @@
package copier_test
import (
"reflect"
"testing"
"time"
"github.com/jinzhu/copier"
)
type User struct {
Name string
Nickname string
Role string
Age int32
FakeAge *int32
Notes []string
flags []byte
}
func (user User) DoubleAge() int32 {
return 2 * user.Age
}
type Employee struct {
Name string
Nickname *string
Age int64
FakeAge int
EmployeID int64
DoubleAge int32
SuperRule string
Notes []string
flags []byte
}
func (employee *Employee) Role(role string) {
employee.SuperRule = "Super " + role
}
func checkEmployee(employee Employee, user User, t *testing.T, testCase string) {
if employee.Name != user.Name {
t.Errorf("%v: Name haven't been copied correctly.", testCase)
}
if employee.Nickname == nil || *employee.Nickname != user.Nickname {
t.Errorf("%v: NickName haven't been copied correctly.", testCase)
}
if employee.Age != int64(user.Age) {
t.Errorf("%v: Age haven't been copied correctly.", testCase)
}
if user.FakeAge != nil && employee.FakeAge != int(*user.FakeAge) {
t.Errorf("%v: FakeAge haven't been copied correctly.", testCase)
}
if employee.DoubleAge != user.DoubleAge() {
t.Errorf("%v: Copy from method doesn't work", testCase)
}
if employee.SuperRule != "Super "+user.Role {
t.Errorf("%v: Copy to method doesn't work", testCase)
}
if !reflect.DeepEqual(employee.Notes, user.Notes) {
t.Errorf("%v: Copy from slice doen't work", testCase)
}
}
func TestCopyStruct(t *testing.T) {
var fakeAge int32 = 12
user := User{Name: "Jinzhu", Nickname: "jinzhu", Age: 18, FakeAge: &fakeAge, Role: "Admin", Notes: []string{"hello world", "welcome"}, flags: []byte{'x'}}
employee := Employee{}
if err := copier.Copy(employee, &user); err == nil {
t.Errorf("Copy to unaddressable value should get error")
}
copier.Copy(&employee, &user)
checkEmployee(employee, user, t, "Copy From Ptr To Ptr")
employee2 := Employee{}
copier.Copy(&employee2, user)
checkEmployee(employee2, user, t, "Copy From Struct To Ptr")
employee3 := Employee{}
ptrToUser := &user
copier.Copy(&employee3, &ptrToUser)
checkEmployee(employee3, user, t, "Copy From Double Ptr To Ptr")
employee4 := &Employee{}
copier.Copy(&employee4, user)
checkEmployee(*employee4, user, t, "Copy From Ptr To Double Ptr")
}
func TestCopyFromStructToSlice(t *testing.T) {
user := User{Name: "Jinzhu", Age: 18, Role: "Admin", Notes: []string{"hello world"}}
employees := []Employee{}
if err := copier.Copy(employees, &user); err != nil && len(employees) != 0 {
t.Errorf("Copy to unaddressable value should get error")
}
if copier.Copy(&employees, &user); len(employees) != 1 {
t.Errorf("Should only have one elem when copy struct to slice")
} else {
checkEmployee(employees[0], user, t, "Copy From Struct To Slice Ptr")
}
employees2 := &[]Employee{}
if copier.Copy(&employees2, user); len(*employees2) != 1 {
t.Errorf("Should only have one elem when copy struct to slice")
} else {
checkEmployee((*employees2)[0], user, t, "Copy From Struct To Double Slice Ptr")
}
employees3 := []*Employee{}
if copier.Copy(&employees3, user); len(employees3) != 1 {
t.Errorf("Should only have one elem when copy struct to slice")
} else {
checkEmployee(*(employees3[0]), user, t, "Copy From Struct To Ptr Slice Ptr")
}
employees4 := &[]*Employee{}
if copier.Copy(&employees4, user); len(*employees4) != 1 {
t.Errorf("Should only have one elem when copy struct to slice")
} else {
checkEmployee(*((*employees4)[0]), user, t, "Copy From Struct To Double Ptr Slice Ptr")
}
}
func TestCopyFromSliceToSlice(t *testing.T) {
users := []User{User{Name: "Jinzhu", Age: 18, Role: "Admin", Notes: []string{"hello world"}}, User{Name: "Jinzhu2", Age: 22, Role: "Dev", Notes: []string{"hello world", "hello"}}}
employees := []Employee{}
if copier.Copy(&employees, users); len(employees) != 2 {
t.Errorf("Should have two elems when copy slice to slice")
} else {
checkEmployee(employees[0], users[0], t, "Copy From Slice To Slice Ptr @ 1")
checkEmployee(employees[1], users[1], t, "Copy From Slice To Slice Ptr @ 2")
}
employees2 := &[]Employee{}
if copier.Copy(&employees2, &users); len(*employees2) != 2 {
t.Errorf("Should have two elems when copy slice to slice")
} else {
checkEmployee((*employees2)[0], users[0], t, "Copy From Slice Ptr To Double Slice Ptr @ 1")
checkEmployee((*employees2)[1], users[1], t, "Copy From Slice Ptr To Double Slice Ptr @ 2")
}
employees3 := []*Employee{}
if copier.Copy(&employees3, users); len(employees3) != 2 {
t.Errorf("Should have two elems when copy slice to slice")
} else {
checkEmployee(*(employees3[0]), users[0], t, "Copy From Slice To Ptr Slice Ptr @ 1")
checkEmployee(*(employees3[1]), users[1], t, "Copy From Slice To Ptr Slice Ptr @ 2")
}
employees4 := &[]*Employee{}
if copier.Copy(&employees4, users); len(*employees4) != 2 {
t.Errorf("Should have two elems when copy slice to slice")
} else {
checkEmployee(*((*employees4)[0]), users[0], t, "Copy From Slice Ptr To Double Ptr Slice Ptr @ 1")
checkEmployee(*((*employees4)[1]), users[1], t, "Copy From Slice Ptr To Double Ptr Slice Ptr @ 2")
}
}
func TestEmbedded(t *testing.T) {
type Base struct {
BaseField1 int
BaseField2 int
}
type Embed struct {
EmbedField1 int
EmbedField2 int
Base
}
base := Base{}
embeded := Embed{}
embeded.BaseField1 = 1
embeded.BaseField2 = 2
embeded.EmbedField1 = 3
embeded.EmbedField2 = 4
copier.Copy(&base, &embeded)
if base.BaseField1 != 1 {
t.Error("Embedded fields not copied")
}
}
type structSameName1 struct {
A string
B int64
C time.Time
}
type structSameName2 struct {
A string
B time.Time
C int64
}
func TestCopyFieldsWithSameNameButDifferentTypes(t *testing.T) {
obj1 := structSameName1{A: "123", B: 2, C: time.Now()}
obj2 := &structSameName2{}
err := copier.Copy(obj2, &obj1)
if err != nil {
t.Error("Should not raise error")
}
if obj2.A != obj1.A {
t.Errorf("Field A should be copied")
}
}
-36
View File
@@ -1,36 +0,0 @@
PACKAGE
package shellquote
import "github.com/kballard/go-shellquote"
Shellquote provides utilities for joining/splitting strings using sh's
word-splitting rules.
VARIABLES
var (
UnterminatedSingleQuoteError = errors.New("Unterminated single-quoted string")
UnterminatedDoubleQuoteError = errors.New("Unterminated double-quoted string")
UnterminatedEscapeError = errors.New("Unterminated backslash-escape")
)
FUNCTIONS
func Join(args ...string) string
Join quotes each argument and joins them with a space. If passed to
/bin/sh, the resulting string will be split back into the original
arguments.
func Split(input string) (words []string, err error)
Split splits a string according to /bin/sh's word-splitting rules. It
supports backslash-escapes, single-quotes, and double-quotes. Notably it
does not support the $'' style of quoting. It also doesn't attempt to
perform any other sort of expansion, including brace expansion, shell
expansion, or pathname expansion.
If the given input has an unterminated quoted string or ends in a
backslash-escape, one of UnterminatedSingleQuoteError,
UnterminatedDoubleQuoteError, or UnterminatedEscapeError is returned.
-29
View File
@@ -1,29 +0,0 @@
package shellquote
import (
"reflect"
"testing"
"testing/quick"
)
// this is called bothtest because it tests Split and Join together
func TestJoinSplit(t *testing.T) {
f := func(strs []string) bool {
// Join, then split, the input
combined := Join(strs...)
split, err := Split(combined)
if err != nil {
t.Logf("Error splitting %#v: %v", combined, err)
return false
}
if !reflect.DeepEqual(strs, split) {
t.Logf("Input %q did not match output %q", strs, split)
return false
}
return true
}
if err := quick.Check(f, nil); err != nil {
t.Error(err)
}
}
-31
View File
@@ -1,31 +0,0 @@
package shellquote
import (
"testing"
)
func TestSimpleJoin(t *testing.T) {
for _, elem := range simpleJoinTest {
output := Join(elem.input...)
if output != elem.output {
t.Errorf("Input %q, got %q, expected %q", elem.input, output, elem.output)
}
}
}
var simpleJoinTest = []struct {
input []string
output string
}{
{[]string{"test"}, "test"},
{[]string{"hello goodbye"}, "'hello goodbye'"},
{[]string{"hello", "goodbye"}, "hello goodbye"},
{[]string{"don't you know the dewey decimal system?"}, "'don'\\''t you know the dewey decimal system?'"},
{[]string{"don't", "you", "know", "the", "dewey", "decimal", "system?"}, "don\\'t you know the dewey decimal system\\?"},
{[]string{"~user", "u~ser", " ~user", "!~user"}, "\\~user u~ser ' ~user' \\!~user"},
{[]string{"foo*", "M{ovies,usic}", "ab[cd]", "%3"}, "foo\\* M\\{ovies,usic} ab\\[cd] %3"},
{[]string{"one", "", "three"}, "one '' three"},
{[]string{"some(parentheses)"}, "some\\(parentheses\\)"},
{[]string{"$some_ot~her_)spe!cial_*_characters"}, "\\$some_ot~her_\\)spe\\!cial_\\*_characters"},
{[]string{"' "}, "\\'' '"},
}
-53
View File
@@ -1,53 +0,0 @@
package shellquote
import (
"reflect"
"testing"
)
func TestSimpleSplit(t *testing.T) {
for _, elem := range simpleSplitTest {
output, err := Split(elem.input)
if err != nil {
t.Errorf("Input %q, got error %#v", elem.input, err)
} else if !reflect.DeepEqual(output, elem.output) {
t.Errorf("Input %q, got %q, expected %q", elem.input, output, elem.output)
}
}
}
func TestErrorSplit(t *testing.T) {
for _, elem := range errorSplitTest {
_, err := Split(elem.input)
if err != elem.error {
t.Errorf("Input %q, got error %#v, expected error %#v", elem.input, err, elem.error)
}
}
}
var simpleSplitTest = []struct {
input string
output []string
}{
{"hello", []string{"hello"}},
{"hello goodbye", []string{"hello", "goodbye"}},
{"hello goodbye", []string{"hello", "goodbye"}},
{"glob* test?", []string{"glob*", "test?"}},
{"don\\'t you know the dewey decimal system\\?", []string{"don't", "you", "know", "the", "dewey", "decimal", "system?"}},
{"'don'\\''t you know the dewey decimal system?'", []string{"don't you know the dewey decimal system?"}},
{"one '' two", []string{"one", "", "two"}},
{"text with\\\na backslash-escaped newline", []string{"text", "witha", "backslash-escaped", "newline"}},
{"text \"with\na\" quoted newline", []string{"text", "with\na", "quoted", "newline"}},
{"\"quoted\\d\\\\\\\" text with\\\na backslash-escaped newline\"", []string{"quoted\\d\\\" text witha backslash-escaped newline"}},
{"foo\"bar\"baz", []string{"foobarbaz"}},
}
var errorSplitTest = []struct {
input string
error error
}{
{"don't worry", UnterminatedSingleQuoteError},
{"'test'\\''ing", UnterminatedSingleQuoteError},
{"\"foo'bar", UnterminatedDoubleQuoteError},
{"foo\\", UnterminatedEscapeError},
}
-9
View File
@@ -1,9 +0,0 @@
language: go
go:
- tip
before_install:
- go get github.com/mattn/goveralls
- go get golang.org/x/tools/cmd/cover
script:
- $HOME/gopath/bin/goveralls -repotoken xnXqRGwgW3SXIguzxf90ZSK1GPYZPaGrw
-48
View File
@@ -1,48 +0,0 @@
# go-colorable
[![Godoc Reference](https://godoc.org/github.com/mattn/go-colorable?status.svg)](http://godoc.org/github.com/mattn/go-colorable)
[![Build Status](https://travis-ci.org/mattn/go-colorable.svg?branch=master)](https://travis-ci.org/mattn/go-colorable)
[![Coverage Status](https://coveralls.io/repos/github/mattn/go-colorable/badge.svg?branch=master)](https://coveralls.io/github/mattn/go-colorable?branch=master)
[![Go Report Card](https://goreportcard.com/badge/mattn/go-colorable)](https://goreportcard.com/report/mattn/go-colorable)
Colorable writer for windows.
For example, most of logger packages doesn't show colors on windows. (I know we can do it with ansicon. But I don't want.)
This package is possible to handle escape sequence for ansi color on windows.
## Too Bad!
![](https://raw.githubusercontent.com/mattn/go-colorable/gh-pages/bad.png)
## So Good!
![](https://raw.githubusercontent.com/mattn/go-colorable/gh-pages/good.png)
## Usage
```go
logrus.SetFormatter(&logrus.TextFormatter{ForceColors: true})
logrus.SetOutput(colorable.NewColorableStdout())
logrus.Info("succeeded")
logrus.Warn("not correct")
logrus.Error("something error")
logrus.Fatal("panic")
```
You can compile above code on non-windows OSs.
## Installation
```
$ go get github.com/mattn/go-colorable
```
# License
MIT
# Author
Yasuhiro Matsumoto (a.k.a mattn)
-83
View File
@@ -1,83 +0,0 @@
package colorable
import (
"bytes"
"os"
"runtime"
"testing"
)
// checkEncoding checks that colorable is output encoding agnostic as long as
// the encoding is a superset of ASCII. This implies that one byte not part of
// an ANSI sequence must give exactly one byte in output
func checkEncoding(t *testing.T, data []byte) {
// Send non-UTF8 data to colorable
b := bytes.NewBuffer(make([]byte, 0, 10))
if b.Len() != 0 {
t.FailNow()
}
// TODO move colorable wrapping outside the test
c := NewNonColorable(b)
c.Write(data)
if b.Len() != len(data) {
t.Fatalf("%d bytes expected, got %d", len(data), b.Len())
}
}
func TestEncoding(t *testing.T) {
checkEncoding(t, []byte{}) // Empty
checkEncoding(t, []byte(`abc`)) // "abc"
checkEncoding(t, []byte(`é`)) // "é" in UTF-8
checkEncoding(t, []byte{233}) // 'é' in Latin-1
}
func TestNonColorable(t *testing.T) {
var buf bytes.Buffer
want := "hello"
NewNonColorable(&buf).Write([]byte("\x1b[0m" + want + "\x1b[2J"))
got := buf.String()
if got != "hello" {
t.Fatalf("want %q but %q", want, got)
}
buf.Reset()
NewNonColorable(&buf).Write([]byte("\x1b["))
got = buf.String()
if got != "" {
t.Fatalf("want %q but %q", "", got)
}
}
func TestNonColorableNil(t *testing.T) {
paniced := false
func() {
defer func() {
recover()
paniced = true
}()
NewNonColorable(nil)
NewColorable(nil)
}()
if !paniced {
t.Fatalf("should panic")
}
}
func TestColorable(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skipf("skip this test on windows")
}
_, ok := NewColorableStdout().(*os.File)
if !ok {
t.Fatalf("should os.Stdout on UNIX")
}
_, ok = NewColorableStderr().(*os.File)
if !ok {
t.Fatalf("should os.Stdout on UNIX")
}
_, ok = NewColorable(os.Stdout).(*os.File)
if !ok {
t.Fatalf("should os.Stdout on UNIX")
}
}
-9
View File
@@ -1,9 +0,0 @@
language: go
go:
- tip
before_install:
- go get github.com/mattn/goveralls
- go get golang.org/x/tools/cmd/cover
script:
- $HOME/gopath/bin/goveralls -repotoken 3gHdORO5k5ziZcWMBxnd9LrMZaJs8m9x5
-50
View File
@@ -1,50 +0,0 @@
# go-isatty
[![Godoc Reference](https://godoc.org/github.com/mattn/go-isatty?status.svg)](http://godoc.org/github.com/mattn/go-isatty)
[![Build Status](https://travis-ci.org/mattn/go-isatty.svg?branch=master)](https://travis-ci.org/mattn/go-isatty)
[![Coverage Status](https://coveralls.io/repos/github/mattn/go-isatty/badge.svg?branch=master)](https://coveralls.io/github/mattn/go-isatty?branch=master)
[![Go Report Card](https://goreportcard.com/badge/mattn/go-isatty)](https://goreportcard.com/report/mattn/go-isatty)
isatty for golang
## Usage
```go
package main
import (
"fmt"
"github.com/mattn/go-isatty"
"os"
)
func main() {
if isatty.IsTerminal(os.Stdout.Fd()) {
fmt.Println("Is Terminal")
} else if isatty.IsCygwinTerminal(os.Stdout.Fd()) {
fmt.Println("Is Cygwin/MSYS2 Terminal")
} else {
fmt.Println("Is Not Terminal")
}
}
```
## Installation
```
$ go get github.com/mattn/go-isatty
```
## License
MIT
## Author
Yasuhiro Matsumoto (a.k.a mattn)
## Thanks
* k-takata: base idea for IsCygwinTerminal
https://github.com/k-takata/go-iscygpty
-18
View File
@@ -1,18 +0,0 @@
package isatty_test
import (
"fmt"
"os"
"github.com/mattn/go-isatty"
)
func Example() {
if isatty.IsTerminal(os.Stdout.Fd()) {
fmt.Println("Is Terminal")
} else if isatty.IsCygwinTerminal(os.Stdout.Fd()) {
fmt.Println("Is Cygwin/MSYS2 Terminal")
} else {
fmt.Println("Is Not Terminal")
}
}
-19
View File
@@ -1,19 +0,0 @@
// +build !windows
package isatty
import (
"os"
"testing"
)
func TestTerminal(t *testing.T) {
// test for non-panic
IsTerminal(os.Stdout.Fd())
}
func TestCygwinPipeName(t *testing.T) {
if IsCygwinTerminal(os.Stdout.Fd()) {
t.Fatal("should be false always")
}
}
-35
View File
@@ -1,35 +0,0 @@
// +build windows
package isatty
import (
"testing"
)
func TestCygwinPipeName(t *testing.T) {
tests := []struct {
name string
result bool
}{
{``, false},
{`\msys-`, false},
{`\cygwin-----`, false},
{`\msys-x-PTY5-pty1-from-master`, false},
{`\cygwin-x-PTY5-from-master`, false},
{`\cygwin-x-pty2-from-toaster`, false},
{`\cygwin--pty2-from-master`, false},
{`\\cygwin-x-pty2-from-master`, false},
{`\cygwin-x-pty2-from-master-`, true}, // for the feature
{`\cygwin-e022582115c10879-pty4-from-master`, true},
{`\msys-e022582115c10879-pty4-to-master`, true},
{`\cygwin-e022582115c10879-pty4-to-master`, true},
}
for _, test := range tests {
want := test.result
got := isCygwinPipeName(test.name)
if want != got {
t.Fatalf("isatty(%q): got %v, want %v:", test.name, got, want)
}
}
}
-1
View File
@@ -1 +0,0 @@
*.test
-121
View File
@@ -1,121 +0,0 @@
# ansi
Package ansi is a small, fast library to create ANSI colored strings and codes.
## Install
Get it
```sh
go get -u github.com/mgutz/ansi
```
## Example
```go
import "github.com/mgutz/ansi"
// colorize a string, SLOW
msg := ansi.Color("foo", "red+b:white")
// create a FAST closure function to avoid computation of ANSI code
phosphorize := ansi.ColorFunc("green+h:black")
msg = phosphorize("Bring back the 80s!")
msg2 := phospohorize("Look, I'm a CRT!")
// cache escape codes and build strings manually
lime := ansi.ColorCode("green+h:black")
reset := ansi.ColorCode("reset")
fmt.Println(lime, "Bring back the 80s!", reset)
```
Other examples
```go
Color(s, "red") // red
Color(s, "red+b") // red bold
Color(s, "red+B") // red blinking
Color(s, "red+u") // red underline
Color(s, "red+bh") // red bold bright
Color(s, "red:white") // red on white
Color(s, "red+b:white+h") // red bold on white bright
Color(s, "red+B:white+h") // red blink on white bright
Color(s, "off") // turn off ansi codes
```
To view color combinations, from project directory in terminal.
```sh
go test
```
## Style format
```go
"foregroundColor+attributes:backgroundColor+attributes"
```
Colors
* black
* red
* green
* yellow
* blue
* magenta
* cyan
* white
* 0...255 (256 colors)
Foreground Attributes
* B = Blink
* b = bold
* h = high intensity (bright)
* i = inverse
* s = strikethrough
* u = underline
Background Attributes
* h = high intensity (bright)
## Constants
* ansi.Reset
* ansi.DefaultBG
* ansi.DefaultFG
* ansi.Black
* ansi.Red
* ansi.Green
* ansi.Yellow
* ansi.Blue
* ansi.Magenta
* ansi.Cyan
* ansi.White
* ansi.LightBlack
* ansi.LightRed
* ansi.LightGreen
* ansi.LightYellow
* ansi.LightBlue
* ansi.LightMagenta
* ansi.LightCyan
* ansi.LightWhite
## References
Wikipedia ANSI escape codes [Colors](http://en.wikipedia.org/wiki/ANSI_escape_code#Colors)
General [tips and formatting](http://misc.flogisoft.com/bash/tip_colors_and_formatting)
What about support on Windows? Use [colorable by mattn](https://github.com/mattn/go-colorable).
Ansi and colorable are used by [logxi](https://github.com/mgutz/logxi) to support logging in
color on Windows.
## MIT License
Copyright (c) 2013 Mario Gutierrez mario@mgutz.com
See the file LICENSE for copying permission.
-52
View File
@@ -1,52 +0,0 @@
package ansi
import (
"strings"
"testing"
)
func TestPlain(t *testing.T) {
DisableColors(true)
PrintStyles()
}
func TestStyles(t *testing.T) {
DisableColors(false)
PrintStyles()
}
func TestDisableColors(t *testing.T) {
fn := ColorFunc("red")
buf := colorCode("off")
if buf.String() != "" {
t.Fail()
}
DisableColors(true)
if Black != "" {
t.Fail()
}
code := ColorCode("red")
if code != "" {
t.Fail()
}
s := fn("foo")
if s != "foo" {
t.Fail()
}
DisableColors(false)
if Black == "" {
t.Fail()
}
code = ColorCode("red")
if code == "" {
t.Fail()
}
// will have escape codes around it
index := strings.Index(fn("foo"), "foo")
if index <= 0 {
t.Fail()
}
}
-55
View File
@@ -1,55 +0,0 @@
# browser
import "github.com/pkg/browser"
Package browser provides helpers to open files, readers, and urls in a browser window.
The choice of which browser is started is entirely client dependant.
## Variables
``` go
var Stderr io.Writer = os.Stderr
```
Stderr is the io.Writer to which executed commands write standard error.
``` go
var Stdout io.Writer = os.Stdout
```
Stdout is the io.Writer to which executed commands write standard output.
## func OpenFile
``` go
func OpenFile(path string) error
```
OpenFile opens new browser window for the file path.
## func OpenReader
``` go
func OpenReader(r io.Reader) error
```
OpenReader consumes the contents of r and presents the
results in a new browser window.
## func OpenURL
``` go
func OpenURL(url string) error
```
OpenURL opens a new browser window pointing to url.
- - -
Generated by [godoc2md](http://godoc.org/github.com/davecheney/godoc2md)
-23
View File
@@ -1,23 +0,0 @@
package browser
import "strings"
func ExampleOpenFile() {
OpenFile("index.html")
}
func ExampleOpenReader() {
// https://github.com/rust-lang/rust/issues/13871
const quote = `There was a night when winds from unknown spaces
whirled us irresistibly into limitless vacum beyond all thought and entity.
Perceptions of the most maddeningly untransmissible sort thronged upon us;
perceptions of infinity which at the time convulsed us with joy, yet which
are now partly lost to my memory and partly incapable of presentation to others.`
r := strings.NewReader(quote)
OpenReader(r)
}
func ExampleOpenURL() {
const url = "http://golang.org/"
OpenURL(url)
}
-24
View File
@@ -1,24 +0,0 @@
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so
# Folders
_obj
_test
# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out
*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*
_testmain.go
*.exe
*.test
*.prof
-11
View File
@@ -1,11 +0,0 @@
language: go
go_import_path: github.com/pkg/errors
go:
- 1.4.3
- 1.5.4
- 1.6.2
- 1.7.1
- tip
script:
- go test -v ./...
-52
View File
@@ -1,52 +0,0 @@
# errors [![Travis-CI](https://travis-ci.org/pkg/errors.svg)](https://travis-ci.org/pkg/errors) [![AppVeyor](https://ci.appveyor.com/api/projects/status/b98mptawhudj53ep/branch/master?svg=true)](https://ci.appveyor.com/project/davecheney/errors/branch/master) [![GoDoc](https://godoc.org/github.com/pkg/errors?status.svg)](http://godoc.org/github.com/pkg/errors) [![Report card](https://goreportcard.com/badge/github.com/pkg/errors)](https://goreportcard.com/report/github.com/pkg/errors)
Package errors provides simple error handling primitives.
`go get github.com/pkg/errors`
The traditional error handling idiom in Go is roughly akin to
```go
if err != nil {
return err
}
```
which applied recursively up the call stack results in error reports without context or debugging information. The errors package allows programmers to add context to the failure path in their code in a way that does not destroy the original value of the error.
## Adding context to an error
The errors.Wrap function returns a new error that adds context to the original error. For example
```go
_, err := ioutil.ReadAll(r)
if err != nil {
return errors.Wrap(err, "read failed")
}
```
## Retrieving the cause of an error
Using `errors.Wrap` constructs a stack of errors, adding context to the preceding error. Depending on the nature of the error it may be necessary to reverse the operation of errors.Wrap to retrieve the original error for inspection. Any error value which implements this interface can be inspected by `errors.Cause`.
```go
type causer interface {
Cause() error
}
```
`errors.Cause` will recursively retrieve the topmost error which does not implement `causer`, which is assumed to be the original cause. For example:
```go
switch err := errors.Cause(err).(type) {
case *MyError:
// handle specifically
default:
// unknown error
}
```
[Read the package documentation for more information](https://godoc.org/github.com/pkg/errors).
## Contributing
We welcome pull requests, bug fixes and issue reports. With that said, the bar for adding new symbols to this package is intentionally set high.
Before proposing a change, please discuss your change by raising an issue.
## Licence
BSD-2-Clause
-32
View File
@@ -1,32 +0,0 @@
version: build-{build}.{branch}
clone_folder: C:\gopath\src\github.com\pkg\errors
shallow_clone: true # for startup speed
environment:
GOPATH: C:\gopath
platform:
- x64
# http://www.appveyor.com/docs/installed-software
install:
# some helpful output for debugging builds
- go version
- go env
# pre-installed MinGW at C:\MinGW is 32bit only
# but MSYS2 at C:\msys64 has mingw64
- set PATH=C:\msys64\mingw64\bin;%PATH%
- gcc --version
- g++ --version
build_script:
- go install -v ./...
test_script:
- set PATH=C:\gopath\bin;%PATH%
- go test -v ./...
#artifacts:
# - path: '%GOPATH%\bin\*.exe'
deploy: off
-59
View File
@@ -1,59 +0,0 @@
// +build go1.7
package errors
import (
"fmt"
"testing"
stderrors "errors"
)
func noErrors(at, depth int) error {
if at >= depth {
return stderrors.New("no error")
}
return noErrors(at+1, depth)
}
func yesErrors(at, depth int) error {
if at >= depth {
return New("ye error")
}
return yesErrors(at+1, depth)
}
func BenchmarkErrors(b *testing.B) {
var toperr error
type run struct {
stack int
std bool
}
runs := []run{
{10, false},
{10, true},
{100, false},
{100, true},
{1000, false},
{1000, true},
}
for _, r := range runs {
part := "pkg/errors"
if r.std {
part = "errors"
}
name := fmt.Sprintf("%s-stack-%d", part, r.stack)
b.Run(name, func(b *testing.B) {
var err error
f := yesErrors
if r.std {
f = noErrors
}
b.ReportAllocs()
for i := 0; i < b.N; i++ {
err = f(0, r.stack)
}
b.StopTimer()
toperr = err
})
}
}

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