mirror of
https://github.com/Threnklyn/jira.git
synced 2026-05-19 04:33:28 +02:00
udpate to use latest dep, update figtree
This commit is contained in:
Generated
+16
-5
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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"
|
||||
}
|
||||
-1044
File diff suppressed because it is too large
Load Diff
-293
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -1,10 +0,0 @@
|
||||
language: go
|
||||
|
||||
go:
|
||||
# - 1.0
|
||||
# - 1.1
|
||||
- 1.2
|
||||
- 1.3
|
||||
- 1.4
|
||||
- 1.5
|
||||
- 1.6
|
||||
-245
@@ -1,245 +0,0 @@
|
||||
# genny - Generics for Go
|
||||
|
||||
[](https://travis-ci.org/cheekybits/genny) [](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
@@ -1,2 +0,0 @@
|
||||
// Package main is the command line tool for Genny.
|
||||
package main
|
||||
-160
@@ -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
@@ -1 +0,0 @@
|
||||
package main_test
|
||||
-1
@@ -1 +0,0 @@
|
||||
language: go
|
||||
-73
@@ -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
@@ -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
@@ -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
@@ -1,4 +0,0 @@
|
||||
[](https://travis-ci.org/coryb/figtree)
|
||||
[](https://godoc.org/github.com/coryb/figtree)
|
||||
|
||||
Figtree is a go library to recusively parse and merge yaml based config files.
|
||||
-113
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -2,7 +2,7 @@ package figtree
|
||||
|
||||
import "regexp"
|
||||
|
||||
type Option interface {
|
||||
type option interface {
|
||||
IsDefined() bool
|
||||
GetValue() interface{}
|
||||
SetValue(interface{}) error
|
||||
|
||||
-130
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -1 +0,0 @@
|
||||
language: go
|
||||
-5
@@ -1,5 +0,0 @@
|
||||
test:
|
||||
go get -t -v
|
||||
go test
|
||||
|
||||
.PHONY: test
|
||||
-4
@@ -1,4 +0,0 @@
|
||||
[](https://travis-ci.org/coryb/kingpeon)
|
||||
[](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
@@ -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
@@ -1,4 +0,0 @@
|
||||
package: github.com/coryb/kingpeon
|
||||
import:
|
||||
- package: gopkg.in/alecthomas/kingpin.v2
|
||||
version: ^2.2.5
|
||||
-208
@@ -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
@@ -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
@@ -1 +0,0 @@
|
||||
language: go
|
||||
-3
@@ -1,3 +0,0 @@
|
||||
test:
|
||||
go get -t -v
|
||||
go test
|
||||
-4
@@ -1,4 +0,0 @@
|
||||
[](https://travis-ci.org/coryb/oreo)
|
||||
[](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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -1,205 +0,0 @@
|
||||
go-spew
|
||||
=======
|
||||
|
||||
[]
|
||||
(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
|
||||
|
||||
[]
|
||||
(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
@@ -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
@@ -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)
|
||||
}
|
||||
-1042
File diff suppressed because it is too large
Load Diff
-99
@@ -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
@@ -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
@@ -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
|
||||
}
|
||||
-1558
File diff suppressed because it is too large
Load Diff
-87
@@ -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
@@ -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
@@ -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
@@ -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
@@ -1,3 +0,0 @@
|
||||
language: go
|
||||
go: 1.4
|
||||
|
||||
-58
@@ -1,58 +0,0 @@
|
||||
# CamelCase [](http://godoc.org/github.com/fatih/camelcase) [](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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -1,3 +0,0 @@
|
||||
guard 'gotest' do
|
||||
watch(%r{\.go$})
|
||||
end
|
||||
-98
@@ -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)
|
||||
|
||||
# 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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -1,48 +0,0 @@
|
||||
# go-colorable
|
||||
|
||||
[](http://godoc.org/github.com/mattn/go-colorable)
|
||||
[](https://travis-ci.org/mattn/go-colorable)
|
||||
[](https://coveralls.io/github/mattn/go-colorable?branch=master)
|
||||
[](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!
|
||||
|
||||

|
||||
|
||||
|
||||
## So Good!
|
||||
|
||||

|
||||
|
||||
## 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
@@ -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
@@ -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
@@ -1,50 +0,0 @@
|
||||
# go-isatty
|
||||
|
||||
[](http://godoc.org/github.com/mattn/go-isatty)
|
||||
[](https://travis-ci.org/mattn/go-isatty)
|
||||
[](https://coveralls.io/github/mattn/go-isatty?branch=master)
|
||||
[](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
@@ -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
@@ -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
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
Generated
-1
@@ -1 +0,0 @@
|
||||
*.test
|
||||
-121
@@ -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
@@ -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
@@ -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
@@ -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)
|
||||
}
|
||||
Generated
-24
@@ -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
@@ -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
@@ -1,52 +0,0 @@
|
||||
# errors [](https://travis-ci.org/pkg/errors) [](https://ci.appveyor.com/project/davecheney/errors/branch/master) [](http://godoc.org/github.com/pkg/errors) [](https://goreportcard.com/report/github.com/pkg/errors)
|
||||
|
||||
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
@@ -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
@@ -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
Reference in New Issue
Block a user