summaryrefslogtreecommitdiffstats
path: root/vendor/github.com
diff options
context:
space:
mode:
authorChristopher Speller <crspeller@gmail.com>2017-08-17 17:19:06 -0700
committerGitHub <noreply@github.com>2017-08-17 17:19:06 -0700
commit96eab1202717e073782ec399a4e0820cae15b1bb (patch)
tree011012982be971c7e9ef91466f026bc0956ac9a2 /vendor/github.com
parent2c895ee66eed626721135acfcc48254c6e3f3b29 (diff)
downloadchat-96eab1202717e073782ec399a4e0820cae15b1bb.tar.gz
chat-96eab1202717e073782ec399a4e0820cae15b1bb.tar.bz2
chat-96eab1202717e073782ec399a4e0820cae15b1bb.zip
Updating server dependancies. (#7246)
Diffstat (limited to 'vendor/github.com')
-rw-r--r--vendor/github.com/NYTimes/gziphandler/gzip.go91
-rw-r--r--vendor/github.com/NYTimes/gziphandler/gzip_test.go60
-rw-r--r--vendor/github.com/armon/go-metrics/circonus/circonus.go27
-rw-r--r--vendor/github.com/armon/go-metrics/circonus/circonus_test.go4
-rw-r--r--vendor/github.com/armon/go-metrics/datadog/dogstatsd.go73
-rw-r--r--vendor/github.com/armon/go-metrics/datadog/dogstatsd_test.go35
-rw-r--r--vendor/github.com/armon/go-metrics/inmem.go95
-rw-r--r--vendor/github.com/armon/go-metrics/inmem_endpoint.go118
-rw-r--r--vendor/github.com/armon/go-metrics/inmem_endpoint_test.go133
-rw-r--r--vendor/github.com/armon/go-metrics/inmem_signal.go33
-rw-r--r--vendor/github.com/armon/go-metrics/inmem_signal_test.go12
-rw-r--r--vendor/github.com/armon/go-metrics/inmem_test.go76
-rw-r--r--vendor/github.com/armon/go-metrics/metrics.go121
-rw-r--r--vendor/github.com/armon/go-metrics/metrics_test.go151
-rw-r--r--vendor/github.com/armon/go-metrics/prometheus/prometheus.go72
-rw-r--r--vendor/github.com/armon/go-metrics/sink.go32
-rw-r--r--vendor/github.com/armon/go-metrics/sink_test.go108
-rw-r--r--vendor/github.com/armon/go-metrics/start.go37
-rw-r--r--vendor/github.com/armon/go-metrics/start_test.go107
-rw-r--r--vendor/github.com/armon/go-metrics/statsd.go23
-rw-r--r--vendor/github.com/armon/go-metrics/statsd_test.go39
-rw-r--r--vendor/github.com/armon/go-metrics/statsite.go23
-rw-r--r--vendor/github.com/armon/go-metrics/statsite_test.go39
-rw-r--r--vendor/github.com/davecgh/go-spew/.gitignore22
-rw-r--r--vendor/github.com/davecgh/go-spew/.travis.yml14
-rw-r--r--vendor/github.com/davecgh/go-spew/LICENSE15
-rw-r--r--vendor/github.com/davecgh/go-spew/README.md194
-rw-r--r--vendor/github.com/davecgh/go-spew/cov_report.sh22
-rw-r--r--vendor/github.com/davecgh/go-spew/spew/bypass.go152
-rw-r--r--vendor/github.com/davecgh/go-spew/spew/bypasssafe.go38
-rw-r--r--vendor/github.com/davecgh/go-spew/spew/common.go341
-rw-r--r--vendor/github.com/davecgh/go-spew/spew/common_test.go298
-rw-r--r--vendor/github.com/davecgh/go-spew/spew/config.go297
-rw-r--r--vendor/github.com/davecgh/go-spew/spew/doc.go202
-rw-r--r--vendor/github.com/davecgh/go-spew/spew/dump.go509
-rw-r--r--vendor/github.com/davecgh/go-spew/spew/dump_test.go1042
-rw-r--r--vendor/github.com/davecgh/go-spew/spew/dumpcgo_test.go99
-rw-r--r--vendor/github.com/davecgh/go-spew/spew/dumpnocgo_test.go26
-rw-r--r--vendor/github.com/davecgh/go-spew/spew/example_test.go226
-rw-r--r--vendor/github.com/davecgh/go-spew/spew/format.go419
-rw-r--r--vendor/github.com/davecgh/go-spew/spew/format_test.go1558
-rw-r--r--vendor/github.com/davecgh/go-spew/spew/internal_test.go87
-rw-r--r--vendor/github.com/davecgh/go-spew/spew/internalunsafe_test.go102
-rw-r--r--vendor/github.com/davecgh/go-spew/spew/spew.go148
-rw-r--r--vendor/github.com/davecgh/go-spew/spew/spew_test.go309
-rw-r--r--vendor/github.com/davecgh/go-spew/spew/testdata/dumpcgo.go82
-rw-r--r--vendor/github.com/davecgh/go-spew/test_coverage.txt61
-rw-r--r--vendor/github.com/disintegration/imaging/adjust.go18
-rw-r--r--vendor/github.com/disintegration/imaging/effects.go4
-rw-r--r--vendor/github.com/disintegration/imaging/resize.go8
-rw-r--r--vendor/github.com/disintegration/imaging/tools.go8
-rw-r--r--vendor/github.com/disintegration/imaging/transform.go13
-rw-r--r--vendor/github.com/disintegration/imaging/transform_test.go164
-rw-r--r--vendor/github.com/go-ini/ini/.travis.yml9
-rw-r--r--vendor/github.com/go-ini/ini/ini.go63
-rw-r--r--vendor/github.com/go-redis/redis/.travis.yml8
-rw-r--r--vendor/github.com/go-redis/redis/README.md2
-rw-r--r--vendor/github.com/go-redis/redis/cluster.go321
-rw-r--r--vendor/github.com/go-redis/redis/cluster_test.go298
-rw-r--r--vendor/github.com/go-redis/redis/command.go20
-rw-r--r--vendor/github.com/go-redis/redis/commands.go32
-rw-r--r--vendor/github.com/go-redis/redis/commands_test.go36
-rw-r--r--vendor/github.com/go-redis/redis/example_test.go8
-rw-r--r--vendor/github.com/go-redis/redis/export_test.go7
-rw-r--r--vendor/github.com/go-redis/redis/internal/error.go (renamed from vendor/github.com/go-redis/redis/internal/errors.go)6
-rw-r--r--vendor/github.com/go-redis/redis/internal/internal.go13
-rw-r--r--vendor/github.com/go-redis/redis/internal/internal_test.go9
-rw-r--r--vendor/github.com/go-redis/redis/internal/pool/pool.go4
-rw-r--r--vendor/github.com/go-redis/redis/options.go15
-rw-r--r--vendor/github.com/go-redis/redis/pubsub.go54
-rw-r--r--vendor/github.com/go-redis/redis/pubsub_test.go27
-rw-r--r--vendor/github.com/go-redis/redis/redis.go66
-rw-r--r--vendor/github.com/go-redis/redis/ring.go4
-rw-r--r--vendor/github.com/go-redis/redis/sentinel.go24
-rw-r--r--vendor/github.com/go-redis/redis/universal.go6
-rw-r--r--vendor/github.com/golang/protobuf/jsonpb/jsonpb.go106
-rw-r--r--vendor/github.com/golang/protobuf/jsonpb/jsonpb_test.go54
-rw-r--r--vendor/github.com/golang/protobuf/protoc-gen-go/descriptor/Makefile1
-rw-r--r--vendor/github.com/golang/protobuf/protoc-gen-go/descriptor/descriptor.pb.go332
-rw-r--r--vendor/github.com/golang/protobuf/protoc-gen-go/descriptor/descriptor.proto837
-rw-r--r--vendor/github.com/golang/protobuf/protoc-gen-go/plugin/Makefile1
-rw-r--r--vendor/github.com/golang/protobuf/protoc-gen-go/plugin/plugin.proto166
-rw-r--r--vendor/github.com/golang/protobuf/ptypes/any/any.pb.go32
-rw-r--r--vendor/github.com/golang/protobuf/ptypes/duration/duration.pb.go34
-rw-r--r--vendor/github.com/golang/protobuf/ptypes/empty/empty.pb.go30
-rwxr-xr-xvendor/github.com/golang/protobuf/ptypes/regen.sh37
-rw-r--r--vendor/github.com/golang/protobuf/ptypes/struct/struct.pb.go60
-rw-r--r--vendor/github.com/golang/protobuf/ptypes/timestamp/timestamp.pb.go34
-rw-r--r--vendor/github.com/golang/protobuf/ptypes/wrappers/wrappers.pb.go44
-rw-r--r--vendor/github.com/hashicorp/go-immutable-radix/.gitignore24
-rw-r--r--vendor/github.com/hashicorp/go-immutable-radix/.travis.yml3
-rw-r--r--vendor/github.com/hashicorp/go-immutable-radix/LICENSE363
-rw-r--r--vendor/github.com/hashicorp/go-immutable-radix/README.md41
-rw-r--r--vendor/github.com/hashicorp/go-immutable-radix/edges.go21
-rw-r--r--vendor/github.com/hashicorp/go-immutable-radix/iradix.go657
-rw-r--r--vendor/github.com/hashicorp/go-immutable-radix/iradix_test.go1490
-rw-r--r--vendor/github.com/hashicorp/go-immutable-radix/iter.go91
-rw-r--r--vendor/github.com/hashicorp/go-immutable-radix/node.go292
-rw-r--r--vendor/github.com/hashicorp/go-immutable-radix/raw_iter.go78
-rw-r--r--vendor/github.com/hashicorp/memberlist/memberlist.go49
-rw-r--r--vendor/github.com/hashicorp/memberlist/memberlist_test.go4
-rw-r--r--vendor/github.com/hashicorp/memberlist/state.go4
-rw-r--r--vendor/github.com/lib/pq/array.go6
-rw-r--r--vendor/github.com/lib/pq/conn.go83
-rw-r--r--vendor/github.com/lib/pq/conn_test.go28
-rw-r--r--vendor/github.com/lib/pq/encode_test.go6
-rw-r--r--vendor/github.com/lib/pq/uuid_test.go2
-rw-r--r--vendor/github.com/mattermost/html2text/.gitignore24
-rw-r--r--vendor/github.com/mattermost/html2text/.travis.yml14
-rw-r--r--vendor/github.com/mattermost/html2text/LICENSE22
-rw-r--r--vendor/github.com/mattermost/html2text/README.md108
-rw-r--r--vendor/github.com/mattermost/html2text/html2text.go312
-rw-r--r--vendor/github.com/mattermost/html2text/html2text_test.go674
-rwxr-xr-xvendor/github.com/mattermost/html2text/testdata/utf8.html22
-rwxr-xr-xvendor/github.com/mattermost/html2text/testdata/utf8_with_bom.xhtml24
-rw-r--r--vendor/github.com/miekg/dns/scan.go3
-rw-r--r--vendor/github.com/miekg/dns/scan_test.go45
-rw-r--r--vendor/github.com/miekg/dns/tsig.go3
-rw-r--r--vendor/github.com/miekg/dns/tsig_test.go15
-rw-r--r--vendor/github.com/miekg/dns/types.go42
-rw-r--r--vendor/github.com/minio/minio-go/.travis.yml7
-rw-r--r--vendor/github.com/minio/minio-go/README.md2
-rw-r--r--vendor/github.com/minio/minio-go/api-compose-object.go2
-rw-r--r--vendor/github.com/minio/minio-go/api-get-object.go18
-rw-r--r--vendor/github.com/minio/minio-go/api-put-object-common.go23
-rw-r--r--vendor/github.com/minio/minio-go/api-put-object-encrypted.go2
-rw-r--r--vendor/github.com/minio/minio-go/api-put-object-multipart.go64
-rw-r--r--vendor/github.com/minio/minio-go/api-put-object.go121
-rw-r--r--vendor/github.com/minio/minio-go/api-stat.go10
-rw-r--r--vendor/github.com/minio/minio-go/api.go7
-rw-r--r--vendor/github.com/minio/minio-go/api_functional_v2_test.go1470
-rw-r--r--vendor/github.com/minio/minio-go/api_functional_v4_test.go2410
-rw-r--r--vendor/github.com/minio/minio-go/bucket-cache.go28
-rw-r--r--vendor/github.com/minio/minio-go/core_test.go154
-rw-r--r--vendor/github.com/minio/minio-go/docs/API.md22
-rw-r--r--vendor/github.com/minio/minio-go/examples/s3/composeobject.go21
-rw-r--r--vendor/github.com/minio/minio-go/examples/s3/copyobject.go5
-rw-r--r--vendor/github.com/minio/minio-go/examples/s3/putobject-progress.go4
-rw-r--r--vendor/github.com/minio/minio-go/examples/s3/putobject-s3-accelerate.go2
-rw-r--r--vendor/github.com/minio/minio-go/functional_tests.go4121
-rw-r--r--vendor/github.com/minio/minio-go/pkg/s3signer/request-signature-streaming.go2
-rw-r--r--vendor/github.com/minio/minio-go/pkg/s3signer/request-signature-v2.go4
-rw-r--r--vendor/github.com/minio/minio-go/pkg/s3signer/utils_test.go7
-rw-r--r--vendor/github.com/minio/minio-go/pkg/s3utils/utils.go5
-rw-r--r--vendor/github.com/minio/minio-go/pkg/s3utils/utils_test.go4
-rw-r--r--vendor/github.com/minio/minio-go/test-utils_test.go4
-rw-r--r--vendor/github.com/minio/minio-go/transport.go48
-rw-r--r--vendor/github.com/minio/minio-go/transport_1_5.go39
-rw-r--r--vendor/github.com/minio/minio-go/transport_1_6.go40
-rw-r--r--vendor/github.com/pelletier/go-toml/doc_test.go55
-rw-r--r--vendor/github.com/pelletier/go-toml/marshal_test.go19
-rw-r--r--vendor/github.com/pmezard/go-difflib/.travis.yml5
-rw-r--r--vendor/github.com/pmezard/go-difflib/LICENSE27
-rw-r--r--vendor/github.com/pmezard/go-difflib/README.md50
-rw-r--r--vendor/github.com/pmezard/go-difflib/difflib/difflib.go758
-rw-r--r--vendor/github.com/pmezard/go-difflib/difflib/difflib_test.go352
-rw-r--r--vendor/github.com/prometheus/common/log/log.go73
-rw-r--r--vendor/github.com/prometheus/common/log/log_test.go2
-rw-r--r--vendor/github.com/spf13/cobra/.travis.yml4
-rw-r--r--vendor/github.com/spf13/cobra/README.md30
-rw-r--r--vendor/github.com/spf13/cobra/args.go98
-rw-r--r--vendor/github.com/spf13/cobra/bash_completions_test.go2
-rw-r--r--vendor/github.com/spf13/cobra/cobra/cmd/add.go4
-rw-r--r--vendor/github.com/spf13/cobra/cobra/cmd/add_test.go15
-rw-r--r--vendor/github.com/spf13/cobra/cobra/cmd/golden_test.go4
-rw-r--r--vendor/github.com/spf13/cobra/cobra/cmd/init_test.go11
-rw-r--r--vendor/github.com/spf13/cobra/cobra/cmd/license_agpl.go3
-rw-r--r--vendor/github.com/spf13/cobra/cobra/cmd/license_apache_2.go3
-rw-r--r--vendor/github.com/spf13/cobra/cobra/cmd/license_bsd_clause_2.go3
-rw-r--r--vendor/github.com/spf13/cobra/cobra/cmd/license_bsd_clause_3.go3
-rw-r--r--vendor/github.com/spf13/cobra/cobra/cmd/license_gpl_2.go27
-rw-r--r--vendor/github.com/spf13/cobra/cobra/cmd/license_gpl_3.go3
-rw-r--r--vendor/github.com/spf13/cobra/cobra/cmd/license_lgpl.go3
-rw-r--r--vendor/github.com/spf13/cobra/cobra/cmd/license_mit.go2
-rw-r--r--vendor/github.com/spf13/cobra/cobra/cmd/licenses.go6
-rw-r--r--vendor/github.com/spf13/cobra/cobra/cmd/root.go4
-rw-r--r--vendor/github.com/spf13/cobra/cobra/cmd/testdata/main.go.golden1
-rw-r--r--vendor/github.com/spf13/cobra/cobra/cmd/testdata/root.go.golden1
-rw-r--r--vendor/github.com/spf13/cobra/cobra/cmd/testdata/test.go.golden1
-rw-r--r--vendor/github.com/spf13/cobra/cobra_test.go77
-rw-r--r--vendor/github.com/spf13/cobra/command.go51
-rw-r--r--vendor/github.com/spf13/cobra/zsh_completions.go114
-rw-r--r--vendor/github.com/spf13/cobra/zsh_completions_test.go88
-rw-r--r--vendor/github.com/spf13/viper/.travis.yml1
-rw-r--r--vendor/github.com/spf13/viper/README.md46
-rw-r--r--vendor/github.com/spf13/viper/flags_test.go1
-rw-r--r--vendor/github.com/spf13/viper/remote/remote.go17
-rw-r--r--vendor/github.com/spf13/viper/util_test.go1
-rw-r--r--vendor/github.com/spf13/viper/viper.go24
-rw-r--r--vendor/github.com/stretchr/objx/.gitignore22
-rw-r--r--vendor/github.com/stretchr/objx/LICENSE.md23
-rw-r--r--vendor/github.com/stretchr/objx/README.md3
-rw-r--r--vendor/github.com/stretchr/objx/accessors.go179
-rw-r--r--vendor/github.com/stretchr/objx/accessors_test.go145
-rw-r--r--vendor/github.com/stretchr/objx/codegen/array-access.txt14
-rw-r--r--vendor/github.com/stretchr/objx/codegen/index.html86
-rw-r--r--vendor/github.com/stretchr/objx/codegen/template.txt286
-rw-r--r--vendor/github.com/stretchr/objx/codegen/types_list.txt20
-rw-r--r--vendor/github.com/stretchr/objx/constants.go13
-rw-r--r--vendor/github.com/stretchr/objx/conversions.go117
-rw-r--r--vendor/github.com/stretchr/objx/conversions_test.go94
-rw-r--r--vendor/github.com/stretchr/objx/doc.go72
-rw-r--r--vendor/github.com/stretchr/objx/fixture_test.go98
-rw-r--r--vendor/github.com/stretchr/objx/map.go222
-rw-r--r--vendor/github.com/stretchr/objx/map_for_test.go10
-rw-r--r--vendor/github.com/stretchr/objx/map_test.go147
-rw-r--r--vendor/github.com/stretchr/objx/mutations.go81
-rw-r--r--vendor/github.com/stretchr/objx/mutations_test.go77
-rw-r--r--vendor/github.com/stretchr/objx/security.go14
-rw-r--r--vendor/github.com/stretchr/objx/security_test.go12
-rw-r--r--vendor/github.com/stretchr/objx/simple_example_test.go41
-rw-r--r--vendor/github.com/stretchr/objx/tests.go17
-rw-r--r--vendor/github.com/stretchr/objx/tests_test.go24
-rw-r--r--vendor/github.com/stretchr/objx/type_specific_codegen.go2881
-rw-r--r--vendor/github.com/stretchr/objx/type_specific_codegen_test.go2867
-rw-r--r--vendor/github.com/stretchr/objx/value.go13
-rw-r--r--vendor/github.com/stretchr/objx/value_test.go1
-rw-r--r--vendor/github.com/xenolf/lego/acme/dns_challenge.go2
-rw-r--r--vendor/github.com/xenolf/lego/cli.go1
-rw-r--r--vendor/github.com/xenolf/lego/providers/dns/dns_providers.go3
-rw-r--r--vendor/github.com/xenolf/lego/providers/dns/otc/mock.go152
-rw-r--r--vendor/github.com/xenolf/lego/providers/dns/otc/otc.go388
-rw-r--r--vendor/github.com/xenolf/lego/providers/dns/otc/otc_test.go112
223 files changed, 29004 insertions, 5205 deletions
diff --git a/vendor/github.com/NYTimes/gziphandler/gzip.go b/vendor/github.com/NYTimes/gziphandler/gzip.go
index ea6dba1e7..c47039fd3 100644
--- a/vendor/github.com/NYTimes/gziphandler/gzip.go
+++ b/vendor/github.com/NYTimes/gziphandler/gzip.go
@@ -80,6 +80,8 @@ type GzipResponseWriter struct {
minSize int // Specifed the minimum response size to gzip. If the response length is bigger than this value, it is compressed.
buf []byte // Holds the first part of the write before reaching the minSize or the end of the write.
+
+ contentTypes []string // Only compress if the response is one of these content-types. All are accepted if empty.
}
// Write appends data to the gzip writer.
@@ -100,8 +102,10 @@ func (w *GzipResponseWriter) Write(b []byte) (int, error) {
// On the first write, w.buf changes from nil to a valid slice
w.buf = append(w.buf, b...)
- // If the global writes are bigger than the minSize, compression is enable.
- if len(w.buf) >= w.minSize {
+ // If the global writes are bigger than the minSize and we're about to write
+ // a response containing a content type we want to handle, enable
+ // compression.
+ if len(w.buf) >= w.minSize && handleContentType(w.contentTypes, w) {
err := w.startGzip()
if err != nil {
return 0, err
@@ -230,14 +234,25 @@ func NewGzipLevelHandler(level int) (func(http.Handler) http.Handler, error) {
// NewGzipLevelAndMinSize behave as NewGzipLevelHandler except it let the caller
// specify the minimum size before compression.
func NewGzipLevelAndMinSize(level, minSize int) (func(http.Handler) http.Handler, error) {
- if level != gzip.DefaultCompression && (level < gzip.BestSpeed || level > gzip.BestCompression) {
- return nil, fmt.Errorf("invalid compression level requested: %d", level)
+ return GzipHandlerWithOpts(CompressionLevel(level), MinSize(minSize))
+}
+
+func GzipHandlerWithOpts(opts ...option) (func(http.Handler) http.Handler, error) {
+ c := &config{
+ level: gzip.DefaultCompression,
+ minSize: DefaultMinSize,
}
- if minSize < 0 {
- return nil, fmt.Errorf("minimum size must be more than zero")
+
+ for _, o := range opts {
+ o(c)
}
+
+ if err := c.validate(); err != nil {
+ return nil, err
+ }
+
return func(h http.Handler) http.Handler {
- index := poolIndex(level)
+ index := poolIndex(c.level)
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Add(vary, acceptEncoding)
@@ -246,7 +261,8 @@ func NewGzipLevelAndMinSize(level, minSize int) (func(http.Handler) http.Handler
gw := &GzipResponseWriter{
ResponseWriter: w,
index: index,
- minSize: minSize,
+ minSize: c.minSize,
+ contentTypes: c.contentTypes,
}
defer gw.Close()
@@ -258,6 +274,48 @@ func NewGzipLevelAndMinSize(level, minSize int) (func(http.Handler) http.Handler
}, nil
}
+// Used for functional configuration.
+type config struct {
+ minSize int
+ level int
+ contentTypes []string
+}
+
+func (c *config) validate() error {
+ if c.level != gzip.DefaultCompression && (c.level < gzip.BestSpeed || c.level > gzip.BestCompression) {
+ return fmt.Errorf("invalid compression level requested: %d", c.level)
+ }
+
+ if c.minSize < 0 {
+ return fmt.Errorf("minimum size must be more than zero")
+ }
+
+ return nil
+}
+
+type option func(c *config)
+
+func MinSize(size int) option {
+ return func(c *config) {
+ c.minSize = size
+ }
+}
+
+func CompressionLevel(level int) option {
+ return func(c *config) {
+ c.level = level
+ }
+}
+
+func ContentTypes(types []string) option {
+ return func(c *config) {
+ c.contentTypes = []string{}
+ for _, v := range types {
+ c.contentTypes = append(c.contentTypes, strings.ToLower(v))
+ }
+ }
+}
+
// GzipHandler wraps an HTTP handler, to transparently gzip the response body if
// the client supports it (via the Accept-Encoding header). This will compress at
// the default compression level.
@@ -273,6 +331,23 @@ func acceptsGzip(r *http.Request) bool {
return acceptedEncodings["gzip"] > 0.0
}
+// returns true if we've been configured to compress the specific content type.
+func handleContentType(contentTypes []string, w http.ResponseWriter) bool {
+ // If contentTypes is empty we handle all content types.
+ if len(contentTypes) == 0 {
+ return true
+ }
+
+ ct := strings.ToLower(w.Header().Get(contentType))
+ for _, c := range contentTypes {
+ if c == ct {
+ return true
+ }
+ }
+
+ return false
+}
+
// parseEncodings attempts to parse a list of codings, per RFC 2616, as might
// appear in an Accept-Encoding header. It returns a map of content-codings to
// quality values, and an error containing the errors encountered. It's probably
diff --git a/vendor/github.com/NYTimes/gziphandler/gzip_test.go b/vendor/github.com/NYTimes/gziphandler/gzip_test.go
index 0f6488cae..d5a7eb82b 100644
--- a/vendor/github.com/NYTimes/gziphandler/gzip_test.go
+++ b/vendor/github.com/NYTimes/gziphandler/gzip_test.go
@@ -319,6 +319,66 @@ func TestDontWriteWhenNotWrittenTo(t *testing.T) {
}
}
+var contentTypeTests = []struct {
+ name string
+ contentType string
+ acceptedContentTypes []string
+ expectedGzip bool
+}{
+ {
+ name: "Always gzip when content types are empty",
+ contentType: "",
+ acceptedContentTypes: []string{},
+ expectedGzip: true,
+ },
+ {
+ name: "Exact content-type match",
+ contentType: "application/json",
+ acceptedContentTypes: []string{"application/json"},
+ expectedGzip: true,
+ },
+ {
+ name: "Case insensitive content-type matching",
+ contentType: "Application/Json",
+ acceptedContentTypes: []string{"application/json"},
+ expectedGzip: true,
+ },
+ {
+ name: "Non-matching content-type",
+ contentType: "text/xml",
+ acceptedContentTypes: []string{"application/json"},
+ expectedGzip: false,
+ },
+}
+
+func TestContentTypes(t *testing.T) {
+ for _, tt := range contentTypeTests {
+ handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.WriteHeader(http.StatusOK)
+ w.Header().Set("Content-Type", tt.contentType)
+ io.WriteString(w, testBody)
+ })
+
+ wrapper, err := GzipHandlerWithOpts(ContentTypes(tt.acceptedContentTypes))
+ if !assert.Nil(t, err, "NewGzipHandlerWithOpts returned error", tt.name) {
+ continue
+ }
+
+ req, _ := http.NewRequest("GET", "/whatever", nil)
+ req.Header.Set("Accept-Encoding", "gzip")
+ resp := httptest.NewRecorder()
+ wrapper(handler).ServeHTTP(resp, req)
+ res := resp.Result()
+
+ assert.Equal(t, 200, res.StatusCode)
+ if tt.expectedGzip {
+ assert.Equal(t, "gzip", res.Header.Get("Content-Encoding"), tt.name)
+ } else {
+ assert.NotEqual(t, "gzip", res.Header.Get("Content-Encoding"), tt.name)
+ }
+ }
+}
+
// --------------------------------------------------------------------
func BenchmarkGzipHandler_S2k(b *testing.B) { benchmark(b, false, 2048) }
diff --git a/vendor/github.com/armon/go-metrics/circonus/circonus.go b/vendor/github.com/armon/go-metrics/circonus/circonus.go
index c6e3974b5..eb41b9945 100644
--- a/vendor/github.com/armon/go-metrics/circonus/circonus.go
+++ b/vendor/github.com/armon/go-metrics/circonus/circonus.go
@@ -5,6 +5,7 @@ package circonus
import (
"strings"
+ "github.com/armon/go-metrics"
cgm "github.com/circonus-labs/circonus-gometrics"
)
@@ -61,6 +62,12 @@ func (s *CirconusSink) SetGauge(key []string, val float32) {
s.metrics.SetGauge(flatKey, int64(val))
}
+// SetGaugeWithLabels sets value for a gauge metric with the given labels
+func (s *CirconusSink) SetGaugeWithLabels(key []string, val float32, labels []metrics.Label) {
+ flatKey := s.flattenKeyLabels(key, labels)
+ s.metrics.SetGauge(flatKey, int64(val))
+}
+
// EmitKey is not implemented in circonus
func (s *CirconusSink) EmitKey(key []string, val float32) {
// NOP
@@ -72,12 +79,24 @@ func (s *CirconusSink) IncrCounter(key []string, val float32) {
s.metrics.IncrementByValue(flatKey, uint64(val))
}
+// IncrCounterWithLabels increments a counter metric with the given labels
+func (s *CirconusSink) IncrCounterWithLabels(key []string, val float32, labels []metrics.Label) {
+ flatKey := s.flattenKeyLabels(key, labels)
+ s.metrics.IncrementByValue(flatKey, uint64(val))
+}
+
// AddSample adds a sample to a histogram metric
func (s *CirconusSink) AddSample(key []string, val float32) {
flatKey := s.flattenKey(key)
s.metrics.RecordValue(flatKey, float64(val))
}
+// AddSampleWithLabels adds a sample to a histogram metric with the given labels
+func (s *CirconusSink) AddSampleWithLabels(key []string, val float32, labels []metrics.Label) {
+ flatKey := s.flattenKeyLabels(key, labels)
+ s.metrics.RecordValue(flatKey, float64(val))
+}
+
// Flattens key to Circonus metric name
func (s *CirconusSink) flattenKey(parts []string) string {
joined := strings.Join(parts, "`")
@@ -90,3 +109,11 @@ func (s *CirconusSink) flattenKey(parts []string) string {
}
}, joined)
}
+
+// Flattens the key along with labels for formatting, removes spaces
+func (s *CirconusSink) flattenKeyLabels(parts []string, labels []metrics.Label) string {
+ for _, label := range labels {
+ parts = append(parts, label.Value)
+ }
+ return s.flattenKey(parts)
+}
diff --git a/vendor/github.com/armon/go-metrics/circonus/circonus_test.go b/vendor/github.com/armon/go-metrics/circonus/circonus_test.go
index 234a3cb89..4eb76e411 100644
--- a/vendor/github.com/armon/go-metrics/circonus/circonus_test.go
+++ b/vendor/github.com/armon/go-metrics/circonus/circonus_test.go
@@ -12,7 +12,7 @@ import (
func TestNewCirconusSink(t *testing.T) {
// test with invalid config (nil)
- expectedError := errors.New("Invalid check manager configuration (no API token AND no submission url).")
+ expectedError := errors.New("invalid check manager configuration (no API token AND no submission url)")
_, err := NewCirconusSink(nil)
if err == nil || err.Error() != expectedError.Error() {
t.Errorf("Expected an '%#v' error, got '%#v'", expectedError, err)
@@ -87,7 +87,7 @@ func TestSetGauge(t *testing.T) {
cs.Flush()
}()
- expect := "{\"foo`bar\":{\"_type\":\"n\",\"_value\":1}}"
+ expect := "{\"foo`bar\":{\"_type\":\"n\",\"_value\":\"1\"}}"
actual := <-q
if actual != expect {
diff --git a/vendor/github.com/armon/go-metrics/datadog/dogstatsd.go b/vendor/github.com/armon/go-metrics/datadog/dogstatsd.go
index aaba9fe0e..fe021d01c 100644
--- a/vendor/github.com/armon/go-metrics/datadog/dogstatsd.go
+++ b/vendor/github.com/armon/go-metrics/datadog/dogstatsd.go
@@ -5,6 +5,7 @@ import (
"strings"
"github.com/DataDog/datadog-go/statsd"
+ "github.com/armon/go-metrics"
)
// DogStatsdSink provides a MetricSink that can be used
@@ -45,46 +46,49 @@ func (s *DogStatsdSink) EnableHostNamePropagation() {
func (s *DogStatsdSink) flattenKey(parts []string) string {
joined := strings.Join(parts, ".")
- return strings.Map(func(r rune) rune {
- switch r {
- case ':':
- fallthrough
- case ' ':
- return '_'
- default:
- return r
- }
- }, joined)
+ return strings.Map(sanitize, joined)
+}
+
+func sanitize(r rune) rune {
+ switch r {
+ case ':':
+ fallthrough
+ case ' ':
+ return '_'
+ default:
+ return r
+ }
}
-func (s *DogStatsdSink) parseKey(key []string) ([]string, []string) {
+func (s *DogStatsdSink) parseKey(key []string) ([]string, []metrics.Label) {
// Since DogStatsd supports dimensionality via tags on metric keys, this sink's approach is to splice the hostname out of the key in favor of a `host` tag
// The `host` tag is either forced here, or set downstream by the DogStatsd server
- var tags []string
+ var labels []metrics.Label
hostName := s.hostName
- //Splice the hostname out of the key
+ // Splice the hostname out of the key
for i, el := range key {
if el == hostName {
key = append(key[:i], key[i+1:]...)
+ break
}
}
if s.propagateHostname {
- tags = append(tags, fmt.Sprintf("host:%s", hostName))
+ labels = append(labels, metrics.Label{"host", hostName})
}
- return key, tags
+ return key, labels
}
// Implementation of methods in the MetricSink interface
func (s *DogStatsdSink) SetGauge(key []string, val float32) {
- s.SetGaugeWithTags(key, val, []string{})
+ s.SetGaugeWithLabels(key, val, nil)
}
func (s *DogStatsdSink) IncrCounter(key []string, val float32) {
- s.IncrCounterWithTags(key, val, []string{})
+ s.IncrCounterWithLabels(key, val, nil)
}
// EmitKey is not implemented since DogStatsd does not provide a metric type that holds an
@@ -93,33 +97,44 @@ func (s *DogStatsdSink) EmitKey(key []string, val float32) {
}
func (s *DogStatsdSink) AddSample(key []string, val float32) {
- s.AddSampleWithTags(key, val, []string{})
+ s.AddSampleWithLabels(key, val, nil)
}
-// The following ...WithTags methods correspond to Datadog's Tag extension to Statsd.
+// The following ...WithLabels methods correspond to Datadog's Tag extension to Statsd.
// http://docs.datadoghq.com/guides/dogstatsd/#tags
-
-func (s *DogStatsdSink) SetGaugeWithTags(key []string, val float32, tags []string) {
- flatKey, tags := s.getFlatkeyAndCombinedTags(key, tags)
+func (s *DogStatsdSink) SetGaugeWithLabels(key []string, val float32, labels []metrics.Label) {
+ flatKey, tags := s.getFlatkeyAndCombinedLabels(key, labels)
rate := 1.0
s.client.Gauge(flatKey, float64(val), tags, rate)
}
-func (s *DogStatsdSink) IncrCounterWithTags(key []string, val float32, tags []string) {
- flatKey, tags := s.getFlatkeyAndCombinedTags(key, tags)
+func (s *DogStatsdSink) IncrCounterWithLabels(key []string, val float32, labels []metrics.Label) {
+ flatKey, tags := s.getFlatkeyAndCombinedLabels(key, labels)
rate := 1.0
s.client.Count(flatKey, int64(val), tags, rate)
}
-func (s *DogStatsdSink) AddSampleWithTags(key []string, val float32, tags []string) {
- flatKey, tags := s.getFlatkeyAndCombinedTags(key, tags)
+func (s *DogStatsdSink) AddSampleWithLabels(key []string, val float32, labels []metrics.Label) {
+ flatKey, tags := s.getFlatkeyAndCombinedLabels(key, labels)
rate := 1.0
s.client.TimeInMilliseconds(flatKey, float64(val), tags, rate)
}
-func (s *DogStatsdSink) getFlatkeyAndCombinedTags(key []string, tags []string) (flattenedKey string, combinedTags []string) {
- key, hostTags := s.parseKey(key)
+func (s *DogStatsdSink) getFlatkeyAndCombinedLabels(key []string, labels []metrics.Label) (string, []string) {
+ key, parsedLabels := s.parseKey(key)
flatKey := s.flattenKey(key)
- tags = append(tags, hostTags...)
+ labels = append(labels, parsedLabels...)
+
+ var tags []string
+ for _, label := range labels {
+ label.Name = strings.Map(sanitize, label.Name)
+ label.Value = strings.Map(sanitize, label.Value)
+ if label.Value != "" {
+ tags = append(tags, fmt.Sprintf("%s:%s", label.Name, label.Value))
+ } else {
+ tags = append(tags, label.Name)
+ }
+ }
+
return flatKey, tags
}
diff --git a/vendor/github.com/armon/go-metrics/datadog/dogstatsd_test.go b/vendor/github.com/armon/go-metrics/datadog/dogstatsd_test.go
index 0ec51e3f1..43b81ac7f 100644
--- a/vendor/github.com/armon/go-metrics/datadog/dogstatsd_test.go
+++ b/vendor/github.com/armon/go-metrics/datadog/dogstatsd_test.go
@@ -1,13 +1,14 @@
package datadog
import (
- "fmt"
"net"
"reflect"
"testing"
+
+ "github.com/armon/go-metrics"
)
-var EmptyTags []string
+var EmptyTags []metrics.Label
const (
DogStatsdAddr = "127.0.0.1:7254"
@@ -22,14 +23,14 @@ func MockGetHostname() string {
var ParseKeyTests = []struct {
KeyToParse []string
- Tags []string
+ Tags []metrics.Label
PropagateHostname bool
ExpectedKey []string
- ExpectedTags []string
+ ExpectedTags []metrics.Label
}{
{[]string{"a", MockGetHostname(), "b", "c"}, EmptyTags, HostnameDisabled, []string{"a", "b", "c"}, EmptyTags},
{[]string{"a", "b", "c"}, EmptyTags, HostnameDisabled, []string{"a", "b", "c"}, EmptyTags},
- {[]string{"a", "b", "c"}, EmptyTags, HostnameEnabled, []string{"a", "b", "c"}, []string{fmt.Sprintf("host:%s", MockGetHostname())}},
+ {[]string{"a", "b", "c"}, EmptyTags, HostnameEnabled, []string{"a", "b", "c"}, []metrics.Label{{"host", MockGetHostname()}}},
}
var FlattenKeyTests = []struct {
@@ -44,7 +45,7 @@ var MetricSinkTests = []struct {
Method string
Metric []string
Value interface{}
- Tags []string
+ Tags []metrics.Label
PropagateHostname bool
Expected string
}{
@@ -53,13 +54,15 @@ var MetricSinkTests = []struct {
{"AddSample", []string{"sample", "thing"}, float32(4), EmptyTags, HostnameDisabled, "sample.thing:4.000000|ms"},
{"IncrCounter", []string{"count", "me"}, float32(3), EmptyTags, HostnameDisabled, "count.me:3|c"},
- {"SetGauge", []string{"foo", "baz"}, float32(42), []string{"my_tag:my_value"}, HostnameDisabled, "foo.baz:42.000000|g|#my_tag:my_value"},
- {"SetGauge", []string{"foo", "bar"}, float32(42), []string{"my_tag:my_value", "other_tag:other_value"}, HostnameDisabled, "foo.bar:42.000000|g|#my_tag:my_value,other_tag:other_value"},
- {"SetGauge", []string{"foo", "bar"}, float32(42), []string{"my_tag:my_value", "other_tag:other_value"}, HostnameEnabled, "foo.bar:42.000000|g|#my_tag:my_value,other_tag:other_value,host:test_hostname"},
+ {"SetGauge", []string{"foo", "baz"}, float32(42), []metrics.Label{{"my_tag", ""}}, HostnameDisabled, "foo.baz:42.000000|g|#my_tag"},
+ {"SetGauge", []string{"foo", "baz"}, float32(42), []metrics.Label{{"my tag", "my_value"}}, HostnameDisabled, "foo.baz:42.000000|g|#my_tag:my_value"},
+ {"SetGauge", []string{"foo", "bar"}, float32(42), []metrics.Label{{"my_tag", "my_value"}, {"other_tag", "other_value"}}, HostnameDisabled, "foo.bar:42.000000|g|#my_tag:my_value,other_tag:other_value"},
+ {"SetGauge", []string{"foo", "bar"}, float32(42), []metrics.Label{{"my_tag", "my_value"}, {"other_tag", "other_value"}}, HostnameEnabled, "foo.bar:42.000000|g|#my_tag:my_value,other_tag:other_value,host:test_hostname"},
}
-func mockNewDogStatsdSink(addr string, tags []string, tagWithHostname bool) *DogStatsdSink {
+func mockNewDogStatsdSink(addr string, labels []metrics.Label, tagWithHostname bool) *DogStatsdSink {
dog, _ := NewDogStatsdSink(addr, MockGetHostname())
+ _, tags := dog.getFlatkeyAndCombinedLabels(nil, labels)
dog.SetTags(tags)
if tagWithHostname {
dog.EnableHostNamePropagation()
@@ -90,7 +93,7 @@ func TestParseKey(t *testing.T) {
}
if !reflect.DeepEqual(tags, tt.ExpectedTags) {
- t.Fatalf("Tag Parsing Failed for %v", tt.KeyToParse)
+ t.Fatalf("Tag Parsing Failed for %v, %v != %v", tt.KeyToParse, tags, tt.ExpectedTags)
}
}
}
@@ -124,17 +127,17 @@ func TestTaggableMetrics(t *testing.T) {
dog := mockNewDogStatsdSink(DogStatsdAddr, EmptyTags, HostnameDisabled)
- dog.AddSampleWithTags([]string{"sample", "thing"}, float32(4), []string{"tagkey:tagvalue"})
+ dog.AddSampleWithLabels([]string{"sample", "thing"}, float32(4), []metrics.Label{{"tagkey", "tagvalue"}})
assertServerMatchesExpected(t, server, buf, "sample.thing:4.000000|ms|#tagkey:tagvalue")
- dog.SetGaugeWithTags([]string{"sample", "thing"}, float32(4), []string{"tagkey:tagvalue"})
+ dog.SetGaugeWithLabels([]string{"sample", "thing"}, float32(4), []metrics.Label{{"tagkey", "tagvalue"}})
assertServerMatchesExpected(t, server, buf, "sample.thing:4.000000|g|#tagkey:tagvalue")
- dog.IncrCounterWithTags([]string{"sample", "thing"}, float32(4), []string{"tagkey:tagvalue"})
+ dog.IncrCounterWithLabels([]string{"sample", "thing"}, float32(4), []metrics.Label{{"tagkey", "tagvalue"}})
assertServerMatchesExpected(t, server, buf, "sample.thing:4|c|#tagkey:tagvalue")
- dog = mockNewDogStatsdSink(DogStatsdAddr, []string{"global"}, HostnameEnabled) // with hostname, global tags
- dog.IncrCounterWithTags([]string{"sample", "thing"}, float32(4), []string{"tagkey:tagvalue"})
+ dog = mockNewDogStatsdSink(DogStatsdAddr, []metrics.Label{{Name: "global"}}, HostnameEnabled) // with hostname, global tags
+ dog.IncrCounterWithLabels([]string{"sample", "thing"}, float32(4), []metrics.Label{{"tagkey", "tagvalue"}})
assertServerMatchesExpected(t, server, buf, "sample.thing:4|c|#global,tagkey:tagvalue,host:test_hostname")
}
diff --git a/vendor/github.com/armon/go-metrics/inmem.go b/vendor/github.com/armon/go-metrics/inmem.go
index ac46443be..cd1773042 100644
--- a/vendor/github.com/armon/go-metrics/inmem.go
+++ b/vendor/github.com/armon/go-metrics/inmem.go
@@ -1,6 +1,7 @@
package metrics
import (
+ "bytes"
"fmt"
"math"
"net/url"
@@ -39,7 +40,7 @@ type IntervalMetrics struct {
Interval time.Time
// Gauges maps the key to the last set value
- Gauges map[string]float32
+ Gauges map[string]GaugeValue
// Points maps the string to the list of emitted values
// from EmitKey
@@ -47,21 +48,21 @@ type IntervalMetrics struct {
// Counters maps the string key to a sum of the counter
// values
- Counters map[string]*AggregateSample
+ Counters map[string]SampledValue
// Samples maps the key to an AggregateSample,
// which has the rolled up view of a sample
- Samples map[string]*AggregateSample
+ Samples map[string]SampledValue
}
// NewIntervalMetrics creates a new IntervalMetrics for a given interval
func NewIntervalMetrics(intv time.Time) *IntervalMetrics {
return &IntervalMetrics{
Interval: intv,
- Gauges: make(map[string]float32),
+ Gauges: make(map[string]GaugeValue),
Points: make(map[string][]float32),
- Counters: make(map[string]*AggregateSample),
- Samples: make(map[string]*AggregateSample),
+ Counters: make(map[string]SampledValue),
+ Samples: make(map[string]SampledValue),
}
}
@@ -69,12 +70,12 @@ func NewIntervalMetrics(intv time.Time) *IntervalMetrics {
// about a sample
type AggregateSample struct {
Count int // The count of emitted pairs
- Rate float64 // The count of emitted pairs per time unit (usually 1 second)
+ Rate float64 `json:"-"` // The count of emitted pairs per time unit (usually 1 second)
Sum float64 // The sum of values
- SumSq float64 // The sum of squared values
+ SumSq float64 `json:"-"` // The sum of squared values
Min float64 // Minimum value
Max float64 // Maximum value
- LastUpdated time.Time // When value was last updated
+ LastUpdated time.Time `json:"-"` // When value was last updated
}
// Computes a Stddev of the values
@@ -154,12 +155,16 @@ func NewInmemSink(interval, retain time.Duration) *InmemSink {
}
func (i *InmemSink) SetGauge(key []string, val float32) {
- k := i.flattenKey(key)
+ i.SetGaugeWithLabels(key, val, nil)
+}
+
+func (i *InmemSink) SetGaugeWithLabels(key []string, val float32, labels []Label) {
+ k, name := i.flattenKeyLabels(key, labels)
intv := i.getInterval()
intv.Lock()
defer intv.Unlock()
- intv.Gauges[k] = val
+ intv.Gauges[k] = GaugeValue{Name: name, Value: val, Labels: labels}
}
func (i *InmemSink) EmitKey(key []string, val float32) {
@@ -173,30 +178,46 @@ func (i *InmemSink) EmitKey(key []string, val float32) {
}
func (i *InmemSink) IncrCounter(key []string, val float32) {
- k := i.flattenKey(key)
+ i.IncrCounterWithLabels(key, val, nil)
+}
+
+func (i *InmemSink) IncrCounterWithLabels(key []string, val float32, labels []Label) {
+ k, name := i.flattenKeyLabels(key, labels)
intv := i.getInterval()
intv.Lock()
defer intv.Unlock()
- agg := intv.Counters[k]
- if agg == nil {
- agg = &AggregateSample{}
+ agg, ok := intv.Counters[k]
+ if !ok {
+ agg = SampledValue{
+ Name: name,
+ AggregateSample: &AggregateSample{},
+ Labels: labels,
+ }
intv.Counters[k] = agg
}
agg.Ingest(float64(val), i.rateDenom)
}
func (i *InmemSink) AddSample(key []string, val float32) {
- k := i.flattenKey(key)
+ i.AddSampleWithLabels(key, val, nil)
+}
+
+func (i *InmemSink) AddSampleWithLabels(key []string, val float32, labels []Label) {
+ k, name := i.flattenKeyLabels(key, labels)
intv := i.getInterval()
intv.Lock()
defer intv.Unlock()
- agg := intv.Samples[k]
- if agg == nil {
- agg = &AggregateSample{}
+ agg, ok := intv.Samples[k]
+ if !ok {
+ agg = SampledValue{
+ Name: name,
+ AggregateSample: &AggregateSample{},
+ Labels: labels,
+ }
intv.Samples[k] = agg
}
agg.Ingest(float64(val), i.rateDenom)
@@ -261,6 +282,38 @@ func (i *InmemSink) getInterval() *IntervalMetrics {
// Flattens the key for formatting, removes spaces
func (i *InmemSink) flattenKey(parts []string) string {
- joined := strings.Join(parts, ".")
- return strings.Replace(joined, " ", "_", -1)
+ buf := &bytes.Buffer{}
+ replacer := strings.NewReplacer(" ", "_")
+
+ if len(parts) > 0 {
+ replacer.WriteString(buf, parts[0])
+ }
+ for _, part := range parts[1:] {
+ replacer.WriteString(buf, ".")
+ replacer.WriteString(buf, part)
+ }
+
+ return buf.String()
+}
+
+// Flattens the key for formatting along with its labels, removes spaces
+func (i *InmemSink) flattenKeyLabels(parts []string, labels []Label) (string, string) {
+ buf := &bytes.Buffer{}
+ replacer := strings.NewReplacer(" ", "_")
+
+ if len(parts) > 0 {
+ replacer.WriteString(buf, parts[0])
+ }
+ for _, part := range parts[1:] {
+ replacer.WriteString(buf, ".")
+ replacer.WriteString(buf, part)
+ }
+
+ key := buf.String()
+
+ for _, label := range labels {
+ replacer.WriteString(buf, fmt.Sprintf(";%s=%s", label.Name, label.Value))
+ }
+
+ return buf.String(), key
}
diff --git a/vendor/github.com/armon/go-metrics/inmem_endpoint.go b/vendor/github.com/armon/go-metrics/inmem_endpoint.go
new file mode 100644
index 000000000..504f1b374
--- /dev/null
+++ b/vendor/github.com/armon/go-metrics/inmem_endpoint.go
@@ -0,0 +1,118 @@
+package metrics
+
+import (
+ "fmt"
+ "net/http"
+ "sort"
+ "time"
+)
+
+// MetricsSummary holds a roll-up of metrics info for a given interval
+type MetricsSummary struct {
+ Timestamp string
+ Gauges []GaugeValue
+ Points []PointValue
+ Counters []SampledValue
+ Samples []SampledValue
+}
+
+type GaugeValue struct {
+ Name string
+ Hash string `json:"-"`
+ Value float32
+
+ Labels []Label `json:"-"`
+ DisplayLabels map[string]string `json:"Labels"`
+}
+
+type PointValue struct {
+ Name string
+ Points []float32
+}
+
+type SampledValue struct {
+ Name string
+ Hash string `json:"-"`
+ *AggregateSample
+ Mean float64
+ Stddev float64
+
+ Labels []Label `json:"-"`
+ DisplayLabels map[string]string `json:"Labels"`
+}
+
+// DisplayMetrics returns a summary of the metrics from the most recent finished interval.
+func (i *InmemSink) DisplayMetrics(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
+ data := i.Data()
+
+ var interval *IntervalMetrics
+ n := len(data)
+ switch {
+ case n == 0:
+ return nil, fmt.Errorf("no metric intervals have been initialized yet")
+ case n == 1:
+ // Show the current interval if it's all we have
+ interval = i.intervals[0]
+ default:
+ // Show the most recent finished interval if we have one
+ interval = i.intervals[n-2]
+ }
+
+ summary := MetricsSummary{
+ Timestamp: interval.Interval.Round(time.Second).UTC().String(),
+ Gauges: make([]GaugeValue, 0, len(interval.Gauges)),
+ Points: make([]PointValue, 0, len(interval.Points)),
+ }
+
+ // Format and sort the output of each metric type, so it gets displayed in a
+ // deterministic order.
+ for name, points := range interval.Points {
+ summary.Points = append(summary.Points, PointValue{name, points})
+ }
+ sort.Slice(summary.Points, func(i, j int) bool {
+ return summary.Points[i].Name < summary.Points[j].Name
+ })
+
+ for hash, value := range interval.Gauges {
+ value.Hash = hash
+ value.DisplayLabels = make(map[string]string)
+ for _, label := range value.Labels {
+ value.DisplayLabels[label.Name] = label.Value
+ }
+ value.Labels = nil
+
+ summary.Gauges = append(summary.Gauges, value)
+ }
+ sort.Slice(summary.Gauges, func(i, j int) bool {
+ return summary.Gauges[i].Hash < summary.Gauges[j].Hash
+ })
+
+ summary.Counters = formatSamples(interval.Counters)
+ summary.Samples = formatSamples(interval.Samples)
+
+ return summary, nil
+}
+
+func formatSamples(source map[string]SampledValue) []SampledValue {
+ output := make([]SampledValue, 0, len(source))
+ for hash, sample := range source {
+ displayLabels := make(map[string]string)
+ for _, label := range sample.Labels {
+ displayLabels[label.Name] = label.Value
+ }
+
+ output = append(output, SampledValue{
+ Name: sample.Name,
+ Hash: hash,
+ AggregateSample: sample.AggregateSample,
+ Mean: sample.AggregateSample.Mean(),
+ Stddev: sample.AggregateSample.Stddev(),
+ DisplayLabels: displayLabels,
+ })
+ }
+ sort.Slice(output, func(i, j int) bool {
+ return output[i].Hash < output[j].Hash
+ })
+
+ return output
+}
diff --git a/vendor/github.com/armon/go-metrics/inmem_endpoint_test.go b/vendor/github.com/armon/go-metrics/inmem_endpoint_test.go
new file mode 100644
index 000000000..326d56ada
--- /dev/null
+++ b/vendor/github.com/armon/go-metrics/inmem_endpoint_test.go
@@ -0,0 +1,133 @@
+package metrics
+
+import (
+ "testing"
+ "time"
+
+ "github.com/pascaldekloe/goe/verify"
+)
+
+func TestDisplayMetrics(t *testing.T) {
+ interval := 10 * time.Millisecond
+ inm := NewInmemSink(interval, 50*time.Millisecond)
+
+ // Add data points
+ inm.SetGauge([]string{"foo", "bar"}, 42)
+ inm.SetGaugeWithLabels([]string{"foo", "bar"}, 23, []Label{{"a", "b"}})
+ inm.EmitKey([]string{"foo", "bar"}, 42)
+ inm.IncrCounter([]string{"foo", "bar"}, 20)
+ inm.IncrCounter([]string{"foo", "bar"}, 22)
+ inm.IncrCounterWithLabels([]string{"foo", "bar"}, 20, []Label{{"a", "b"}})
+ inm.IncrCounterWithLabels([]string{"foo", "bar"}, 40, []Label{{"a", "b"}})
+ inm.AddSample([]string{"foo", "bar"}, 20)
+ inm.AddSample([]string{"foo", "bar"}, 24)
+ inm.AddSampleWithLabels([]string{"foo", "bar"}, 23, []Label{{"a", "b"}})
+ inm.AddSampleWithLabels([]string{"foo", "bar"}, 33, []Label{{"a", "b"}})
+
+ data := inm.Data()
+ if len(data) != 1 {
+ t.Fatalf("bad: %v", data)
+ }
+
+ expected := MetricsSummary{
+ Timestamp: data[0].Interval.Round(time.Second).UTC().String(),
+ Gauges: []GaugeValue{
+ {
+ Name: "foo.bar",
+ Hash: "foo.bar",
+ Value: float32(42),
+ DisplayLabels: map[string]string{},
+ },
+ {
+ Name: "foo.bar",
+ Hash: "foo.bar;a=b",
+ Value: float32(23),
+ DisplayLabels: map[string]string{"a": "b"},
+ },
+ },
+ Points: []PointValue{
+ {
+ Name: "foo.bar",
+ Points: []float32{42},
+ },
+ },
+ Counters: []SampledValue{
+ {
+ Name: "foo.bar",
+ Hash: "foo.bar",
+ AggregateSample: &AggregateSample{
+ Count: 2,
+ Min: 20,
+ Max: 22,
+ Sum: 42,
+ SumSq: 884,
+ Rate: 200,
+ },
+ Mean: 21,
+ Stddev: 1.4142135623730951,
+ },
+ {
+ Name: "foo.bar",
+ Hash: "foo.bar;a=b",
+ AggregateSample: &AggregateSample{
+ Count: 2,
+ Min: 20,
+ Max: 40,
+ Sum: 60,
+ SumSq: 2000,
+ Rate: 200,
+ },
+ Mean: 30,
+ Stddev: 14.142135623730951,
+ DisplayLabels: map[string]string{"a": "b"},
+ },
+ },
+ Samples: []SampledValue{
+ {
+ Name: "foo.bar",
+ Hash: "foo.bar",
+ AggregateSample: &AggregateSample{
+ Count: 2,
+ Min: 20,
+ Max: 24,
+ Sum: 44,
+ SumSq: 976,
+ Rate: 200,
+ },
+ Mean: 22,
+ Stddev: 2.8284271247461903,
+ },
+ {
+ Name: "foo.bar",
+ Hash: "foo.bar;a=b",
+ AggregateSample: &AggregateSample{
+ Count: 2,
+ Min: 23,
+ Max: 33,
+ Sum: 56,
+ SumSq: 1618,
+ Rate: 200,
+ },
+ Mean: 28,
+ Stddev: 7.0710678118654755,
+ DisplayLabels: map[string]string{"a": "b"},
+ },
+ },
+ }
+
+ raw, err := inm.DisplayMetrics(nil, nil)
+ if err != nil {
+ t.Fatalf("err: %v", err)
+ }
+ result := raw.(MetricsSummary)
+
+ // Ignore the LastUpdated field, we don't export that anyway
+ for i, got := range result.Counters {
+ expected.Counters[i].LastUpdated = got.LastUpdated
+ }
+ for i, got := range result.Samples {
+ expected.Samples[i].LastUpdated = got.LastUpdated
+ }
+
+ verify.Values(t, "all", result, expected)
+}
diff --git a/vendor/github.com/armon/go-metrics/inmem_signal.go b/vendor/github.com/armon/go-metrics/inmem_signal.go
index 95d08ee10..0937f4aed 100644
--- a/vendor/github.com/armon/go-metrics/inmem_signal.go
+++ b/vendor/github.com/armon/go-metrics/inmem_signal.go
@@ -6,6 +6,7 @@ import (
"io"
"os"
"os/signal"
+ "strings"
"sync"
"syscall"
)
@@ -75,22 +76,25 @@ func (i *InmemSignal) dumpStats() {
data := i.inm.Data()
// Skip the last period which is still being aggregated
- for i := 0; i < len(data)-1; i++ {
- intv := data[i]
+ for j := 0; j < len(data)-1; j++ {
+ intv := data[j]
intv.RLock()
- for name, val := range intv.Gauges {
- fmt.Fprintf(buf, "[%v][G] '%s': %0.3f\n", intv.Interval, name, val)
+ for _, val := range intv.Gauges {
+ name := i.flattenLabels(val.Name, val.Labels)
+ fmt.Fprintf(buf, "[%v][G] '%s': %0.3f\n", intv.Interval, name, val.Value)
}
for name, vals := range intv.Points {
for _, val := range vals {
fmt.Fprintf(buf, "[%v][P] '%s': %0.3f\n", intv.Interval, name, val)
}
}
- for name, agg := range intv.Counters {
- fmt.Fprintf(buf, "[%v][C] '%s': %s\n", intv.Interval, name, agg)
+ for _, agg := range intv.Counters {
+ name := i.flattenLabels(agg.Name, agg.Labels)
+ fmt.Fprintf(buf, "[%v][C] '%s': %s\n", intv.Interval, name, agg.AggregateSample)
}
- for name, agg := range intv.Samples {
- fmt.Fprintf(buf, "[%v][S] '%s': %s\n", intv.Interval, name, agg)
+ for _, agg := range intv.Samples {
+ name := i.flattenLabels(agg.Name, agg.Labels)
+ fmt.Fprintf(buf, "[%v][S] '%s': %s\n", intv.Interval, name, agg.AggregateSample)
}
intv.RUnlock()
}
@@ -98,3 +102,16 @@ func (i *InmemSignal) dumpStats() {
// Write out the bytes
i.w.Write(buf.Bytes())
}
+
+// Flattens the key for formatting along with its labels, removes spaces
+func (i *InmemSignal) flattenLabels(name string, labels []Label) string {
+ buf := bytes.NewBufferString(name)
+ replacer := strings.NewReplacer(" ", "_", ":", "_")
+
+ for _, label := range labels {
+ replacer.WriteString(buf, ".")
+ replacer.WriteString(buf, label.Value)
+ }
+
+ return buf.String()
+}
diff --git a/vendor/github.com/armon/go-metrics/inmem_signal_test.go b/vendor/github.com/armon/go-metrics/inmem_signal_test.go
index 9bbca5f25..c992d6bce 100644
--- a/vendor/github.com/armon/go-metrics/inmem_signal_test.go
+++ b/vendor/github.com/armon/go-metrics/inmem_signal_test.go
@@ -19,6 +19,9 @@ func TestInmemSignal(t *testing.T) {
inm.EmitKey([]string{"bar"}, 42)
inm.IncrCounter([]string{"baz"}, 42)
inm.AddSample([]string{"wow"}, 42)
+ inm.SetGaugeWithLabels([]string{"asdf"}, 42, []Label{{"a", "b"}})
+ inm.IncrCounterWithLabels([]string{"qwer"}, 42, []Label{{"a", "b"}})
+ inm.AddSampleWithLabels([]string{"zxcv"}, 42, []Label{{"a", "b"}})
// Wait for period to end
time.Sleep(15 * time.Millisecond)
@@ -43,4 +46,13 @@ func TestInmemSignal(t *testing.T) {
if !strings.Contains(out, "[S] 'wow': Count: 1 Sum: 42") {
t.Fatalf("bad: %v", out)
}
+ if !strings.Contains(out, "[G] 'asdf.b': 42") {
+ t.Fatalf("bad: %v", out)
+ }
+ if !strings.Contains(out, "[C] 'qwer.b': Count: 1 Sum: 42") {
+ t.Fatalf("bad: %v", out)
+ }
+ if !strings.Contains(out, "[S] 'zxcv.b': Count: 1 Sum: 42") {
+ t.Fatalf("bad: %v", out)
+ }
}
diff --git a/vendor/github.com/armon/go-metrics/inmem_test.go b/vendor/github.com/armon/go-metrics/inmem_test.go
index ed3b521da..8d30e7c30 100644
--- a/vendor/github.com/armon/go-metrics/inmem_test.go
+++ b/vendor/github.com/armon/go-metrics/inmem_test.go
@@ -18,11 +18,15 @@ func TestInmemSink(t *testing.T) {
// Add data points
inm.SetGauge([]string{"foo", "bar"}, 42)
+ inm.SetGaugeWithLabels([]string{"foo", "bar"}, 23, []Label{{"a", "b"}})
inm.EmitKey([]string{"foo", "bar"}, 42)
inm.IncrCounter([]string{"foo", "bar"}, 20)
inm.IncrCounter([]string{"foo", "bar"}, 22)
+ inm.IncrCounterWithLabels([]string{"foo", "bar"}, 20, []Label{{"a", "b"}})
+ inm.IncrCounterWithLabels([]string{"foo", "bar"}, 22, []Label{{"a", "b"}})
inm.AddSample([]string{"foo", "bar"}, 20)
inm.AddSample([]string{"foo", "bar"}, 22)
+ inm.AddSampleWithLabels([]string{"foo", "bar"}, 23, []Label{{"a", "b"}})
data = inm.Data()
if len(data) != 1 {
@@ -35,49 +39,57 @@ func TestInmemSink(t *testing.T) {
if time.Now().Sub(intvM.Interval) > 10*time.Millisecond {
t.Fatalf("interval too old")
}
- if intvM.Gauges["foo.bar"] != 42 {
+ if intvM.Gauges["foo.bar"].Value != 42 {
+ t.Fatalf("bad val: %v", intvM.Gauges)
+ }
+ if intvM.Gauges["foo.bar;a=b"].Value != 23 {
t.Fatalf("bad val: %v", intvM.Gauges)
}
if intvM.Points["foo.bar"][0] != 42 {
t.Fatalf("bad val: %v", intvM.Points)
}
- agg := intvM.Counters["foo.bar"]
- if agg.Count != 2 {
- t.Fatalf("bad val: %v", agg)
- }
- if agg.Rate != 200 {
- t.Fatalf("bad val: %v", agg.Rate)
- }
- if agg.Sum != 42 {
- t.Fatalf("bad val: %v", agg)
- }
- if agg.SumSq != 884 {
- t.Fatalf("bad val: %v", agg)
- }
- if agg.Min != 20 {
- t.Fatalf("bad val: %v", agg)
- }
- if agg.Max != 22 {
- t.Fatalf("bad val: %v", agg)
- }
- if agg.Mean() != 21 {
- t.Fatalf("bad val: %v", agg)
- }
- if agg.Stddev() != math.Sqrt(2) {
- t.Fatalf("bad val: %v", agg)
- }
+ for _, agg := range []SampledValue{intvM.Counters["foo.bar"], intvM.Counters["foo.bar;a=b"]} {
+ if agg.Count != 2 {
+ t.Fatalf("bad val: %v", agg)
+ }
+ if agg.Rate != 200 {
+ t.Fatalf("bad val: %v", agg.Rate)
+ }
+ if agg.Sum != 42 {
+ t.Fatalf("bad val: %v", agg)
+ }
+ if agg.SumSq != 884 {
+ t.Fatalf("bad val: %v", agg)
+ }
+ if agg.Min != 20 {
+ t.Fatalf("bad val: %v", agg)
+ }
+ if agg.Max != 22 {
+ t.Fatalf("bad val: %v", agg)
+ }
+ if agg.AggregateSample.Mean() != 21 {
+ t.Fatalf("bad val: %v", agg)
+ }
+ if agg.AggregateSample.Stddev() != math.Sqrt(2) {
+ t.Fatalf("bad val: %v", agg)
+ }
+
+ if agg.LastUpdated.IsZero() {
+ t.Fatalf("agg.LastUpdated is not set: %v", agg)
+ }
- if agg.LastUpdated.IsZero() {
- t.Fatalf("agg.LastUpdated is not set: %v", agg)
+ diff := time.Now().Sub(agg.LastUpdated).Seconds()
+ if diff > 1 {
+ t.Fatalf("time diff too great: %f", diff)
+ }
}
- diff := time.Now().Sub(agg.LastUpdated).Seconds()
- if diff > 1 {
- t.Fatalf("time diff too great: %f", diff)
+ if _, ok := intvM.Samples["foo.bar"]; !ok {
+ t.Fatalf("missing sample")
}
- if agg = intvM.Samples["foo.bar"]; agg == nil {
+ if _, ok := intvM.Samples["foo.bar;a=b"]; !ok {
t.Fatalf("missing sample")
}
diff --git a/vendor/github.com/armon/go-metrics/metrics.go b/vendor/github.com/armon/go-metrics/metrics.go
index b818e4182..d260bd4b2 100644
--- a/vendor/github.com/armon/go-metrics/metrics.go
+++ b/vendor/github.com/armon/go-metrics/metrics.go
@@ -2,20 +2,43 @@ package metrics
import (
"runtime"
+ "strings"
"time"
+
+ "github.com/hashicorp/go-immutable-radix"
)
+type Label struct {
+ Name string
+ Value string
+}
+
func (m *Metrics) SetGauge(key []string, val float32) {
- if m.HostName != "" && m.EnableHostname {
- key = insert(0, m.HostName, key)
+ m.SetGaugeWithLabels(key, val, nil)
+}
+
+func (m *Metrics) SetGaugeWithLabels(key []string, val float32, labels []Label) {
+ if m.HostName != "" {
+ if m.EnableHostnameLabel {
+ labels = append(labels, Label{"host", m.HostName})
+ } else if m.EnableHostname {
+ key = insert(0, m.HostName, key)
+ }
}
if m.EnableTypePrefix {
key = insert(0, "gauge", key)
}
if m.ServiceName != "" {
- key = insert(0, m.ServiceName, key)
+ if m.EnableServiceLabel {
+ labels = append(labels, Label{"service", m.ServiceName})
+ } else {
+ key = insert(0, m.ServiceName, key)
+ }
+ }
+ if !m.allowMetric(key) {
+ return
}
- m.sink.SetGauge(key, val)
+ m.sink.SetGaugeWithLabels(key, val, labels)
}
func (m *Metrics) EmitKey(key []string, val float32) {
@@ -25,40 +48,118 @@ func (m *Metrics) EmitKey(key []string, val float32) {
if m.ServiceName != "" {
key = insert(0, m.ServiceName, key)
}
+ if !m.allowMetric(key) {
+ return
+ }
m.sink.EmitKey(key, val)
}
func (m *Metrics) IncrCounter(key []string, val float32) {
+ m.IncrCounterWithLabels(key, val, nil)
+}
+
+func (m *Metrics) IncrCounterWithLabels(key []string, val float32, labels []Label) {
+ if m.HostName != "" && m.EnableHostnameLabel {
+ labels = append(labels, Label{"host", m.HostName})
+ }
if m.EnableTypePrefix {
key = insert(0, "counter", key)
}
if m.ServiceName != "" {
- key = insert(0, m.ServiceName, key)
+ if m.EnableServiceLabel {
+ labels = append(labels, Label{"service", m.ServiceName})
+ } else {
+ key = insert(0, m.ServiceName, key)
+ }
}
- m.sink.IncrCounter(key, val)
+ if !m.allowMetric(key) {
+ return
+ }
+ m.sink.IncrCounterWithLabels(key, val, labels)
}
func (m *Metrics) AddSample(key []string, val float32) {
+ m.AddSampleWithLabels(key, val, nil)
+}
+
+func (m *Metrics) AddSampleWithLabels(key []string, val float32, labels []Label) {
+ if m.HostName != "" && m.EnableHostnameLabel {
+ labels = append(labels, Label{"host", m.HostName})
+ }
if m.EnableTypePrefix {
key = insert(0, "sample", key)
}
if m.ServiceName != "" {
- key = insert(0, m.ServiceName, key)
+ if m.EnableServiceLabel {
+ labels = append(labels, Label{"service", m.ServiceName})
+ } else {
+ key = insert(0, m.ServiceName, key)
+ }
+ }
+ if !m.allowMetric(key) {
+ return
}
- m.sink.AddSample(key, val)
+ m.sink.AddSampleWithLabels(key, val, labels)
}
func (m *Metrics) MeasureSince(key []string, start time.Time) {
+ m.MeasureSinceWithLabels(key, start, nil)
+}
+
+func (m *Metrics) MeasureSinceWithLabels(key []string, start time.Time, labels []Label) {
+ if m.HostName != "" && m.EnableHostnameLabel {
+ labels = append(labels, Label{"host", m.HostName})
+ }
if m.EnableTypePrefix {
key = insert(0, "timer", key)
}
if m.ServiceName != "" {
- key = insert(0, m.ServiceName, key)
+ if m.EnableServiceLabel {
+ labels = append(labels, Label{"service", m.ServiceName})
+ } else {
+ key = insert(0, m.ServiceName, key)
+ }
+ }
+ if !m.allowMetric(key) {
+ return
}
now := time.Now()
elapsed := now.Sub(start)
msec := float32(elapsed.Nanoseconds()) / float32(m.TimerGranularity)
- m.sink.AddSample(key, msec)
+ m.sink.AddSampleWithLabels(key, msec, labels)
+}
+
+// UpdateFilter overwrites the existing filter with the given rules.
+func (m *Metrics) UpdateFilter(allow, block []string) {
+ m.filterLock.Lock()
+ defer m.filterLock.Unlock()
+
+ m.AllowedPrefixes = allow
+ m.BlockedPrefixes = block
+
+ m.filter = iradix.New()
+ for _, prefix := range m.AllowedPrefixes {
+ m.filter, _, _ = m.filter.Insert([]byte(prefix), true)
+ }
+ for _, prefix := range m.BlockedPrefixes {
+ m.filter, _, _ = m.filter.Insert([]byte(prefix), false)
+ }
+}
+
+// Returns whether the metric should be allowed based on configured prefix filters
+func (m *Metrics) allowMetric(key []string) bool {
+ m.filterLock.RLock()
+ defer m.filterLock.RUnlock()
+
+ if m.filter == nil || m.filter.Len() == 0 {
+ return m.Config.FilterDefault
+ }
+
+ _, allowed, ok := m.filter.Root().LongestPrefix([]byte(strings.Join(key, ".")))
+ if !ok {
+ return m.Config.FilterDefault
+ }
+ return allowed.(bool)
}
// Periodically collects runtime stats to publish
diff --git a/vendor/github.com/armon/go-metrics/metrics_test.go b/vendor/github.com/armon/go-metrics/metrics_test.go
index f5b2a4c79..8556a0019 100644
--- a/vendor/github.com/armon/go-metrics/metrics_test.go
+++ b/vendor/github.com/armon/go-metrics/metrics_test.go
@@ -9,7 +9,7 @@ import (
func mockMetric() (*MockSink, *Metrics) {
m := &MockSink{}
- met := &Metrics{sink: m}
+ met := &Metrics{Config: Config{FilterDefault: true}, sink: m}
return m, met
}
@@ -24,6 +24,19 @@ func TestMetrics_SetGauge(t *testing.T) {
}
m, met = mockMetric()
+ labels := []Label{{"a", "b"}}
+ met.SetGaugeWithLabels([]string{"key"}, float32(1), labels)
+ if m.keys[0][0] != "key" {
+ t.Fatalf("")
+ }
+ if m.vals[0] != 1 {
+ t.Fatalf("")
+ }
+ if !reflect.DeepEqual(m.labels[0], labels) {
+ t.Fatalf("")
+ }
+
+ m, met = mockMetric()
met.HostName = "test"
met.EnableHostname = true
met.SetGauge([]string{"key"}, float32(1))
@@ -97,6 +110,19 @@ func TestMetrics_IncrCounter(t *testing.T) {
}
m, met = mockMetric()
+ labels := []Label{{"a", "b"}}
+ met.IncrCounterWithLabels([]string{"key"}, float32(1), labels)
+ if m.keys[0][0] != "key" {
+ t.Fatalf("")
+ }
+ if m.vals[0] != 1 {
+ t.Fatalf("")
+ }
+ if !reflect.DeepEqual(m.labels[0], labels) {
+ t.Fatalf("")
+ }
+
+ m, met = mockMetric()
met.EnableTypePrefix = true
met.IncrCounter([]string{"key"}, float32(1))
if m.keys[0][0] != "counter" || m.keys[0][1] != "key" {
@@ -128,6 +154,19 @@ func TestMetrics_AddSample(t *testing.T) {
}
m, met = mockMetric()
+ labels := []Label{{"a", "b"}}
+ met.AddSampleWithLabels([]string{"key"}, float32(1), labels)
+ if m.keys[0][0] != "key" {
+ t.Fatalf("")
+ }
+ if m.vals[0] != 1 {
+ t.Fatalf("")
+ }
+ if !reflect.DeepEqual(m.labels[0], labels) {
+ t.Fatalf("")
+ }
+
+ m, met = mockMetric()
met.EnableTypePrefix = true
met.AddSample([]string{"key"}, float32(1))
if m.keys[0][0] != "sample" || m.keys[0][1] != "key" {
@@ -162,6 +201,20 @@ func TestMetrics_MeasureSince(t *testing.T) {
m, met = mockMetric()
met.TimerGranularity = time.Millisecond
+ labels := []Label{{"a", "b"}}
+ met.MeasureSinceWithLabels([]string{"key"}, n, labels)
+ if m.keys[0][0] != "key" {
+ t.Fatalf("")
+ }
+ if m.vals[0] > 0.1 {
+ t.Fatalf("")
+ }
+ if !reflect.DeepEqual(m.labels[0], labels) {
+ t.Fatalf("")
+ }
+
+ m, met = mockMetric()
+ met.TimerGranularity = time.Millisecond
met.EnableTypePrefix = true
met.MeasureSince([]string{"key"}, n)
if m.keys[0][0] != "timer" || m.keys[0][1] != "key" {
@@ -260,3 +313,99 @@ func TestInsert(t *testing.T) {
t.Fatalf("bad insert %v %v", exp, out)
}
}
+
+func TestMetrics_Filter_Blacklist(t *testing.T) {
+ m := &MockSink{}
+ conf := DefaultConfig("")
+ conf.AllowedPrefixes = []string{"service", "debug.thing"}
+ conf.BlockedPrefixes = []string{"debug"}
+ conf.EnableHostname = false
+ met, err := New(conf, m)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // Allowed by default
+ key := []string{"thing"}
+ met.SetGauge(key, 1)
+ if !reflect.DeepEqual(m.keys[0], key) {
+ t.Fatalf("key doesn't exist %v, %v", m.keys[0], key)
+ }
+ if m.vals[0] != 1 {
+ t.Fatalf("bad val: %v", m.vals[0])
+ }
+
+ // Allowed by filter
+ key = []string{"service", "thing"}
+ met.SetGauge(key, 2)
+ if !reflect.DeepEqual(m.keys[1], key) {
+ t.Fatalf("key doesn't exist")
+ }
+ if m.vals[1] != 2 {
+ t.Fatalf("bad val: %v", m.vals[1])
+ }
+
+ // Allowed by filter, subtree of a blocked entry
+ key = []string{"debug", "thing"}
+ met.SetGauge(key, 3)
+ if !reflect.DeepEqual(m.keys[2], key) {
+ t.Fatalf("key doesn't exist")
+ }
+ if m.vals[2] != 3 {
+ t.Fatalf("bad val: %v", m.vals[2])
+ }
+
+ // Blocked by filter
+ key = []string{"debug", "other-thing"}
+ met.SetGauge(key, 4)
+ if len(m.keys) != 3 {
+ t.Fatalf("key shouldn't exist")
+ }
+}
+
+func TestMetrics_Filter_Whitelist(t *testing.T) {
+ m := &MockSink{}
+ conf := DefaultConfig("")
+ conf.AllowedPrefixes = []string{"service", "debug.thing"}
+ conf.BlockedPrefixes = []string{"debug"}
+ conf.FilterDefault = false
+ conf.EnableHostname = false
+ met, err := New(conf, m)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // Blocked by default
+ key := []string{"thing"}
+ met.SetGauge(key, 1)
+ if len(m.keys) != 0 {
+ t.Fatalf("key should not exist")
+ }
+
+ // Allowed by filter
+ key = []string{"service", "thing"}
+ met.SetGauge(key, 2)
+ if !reflect.DeepEqual(m.keys[0], key) {
+ t.Fatalf("key doesn't exist")
+ }
+ if m.vals[0] != 2 {
+ t.Fatalf("bad val: %v", m.vals[0])
+ }
+
+ // Allowed by filter, subtree of a blocked entry
+ key = []string{"debug", "thing"}
+ met.SetGauge(key, 3)
+ if !reflect.DeepEqual(m.keys[1], key) {
+ t.Fatalf("key doesn't exist")
+ }
+ if m.vals[1] != 3 {
+ t.Fatalf("bad val: %v", m.vals[1])
+ }
+
+ // Blocked by filter
+ key = []string{"debug", "other-thing"}
+ met.SetGauge(key, 4)
+ if len(m.keys) != 2 {
+ t.Fatalf("key shouldn't exist")
+ }
+}
diff --git a/vendor/github.com/armon/go-metrics/prometheus/prometheus.go b/vendor/github.com/armon/go-metrics/prometheus/prometheus.go
index b26d27981..f31f2f9a5 100644
--- a/vendor/github.com/armon/go-metrics/prometheus/prometheus.go
+++ b/vendor/github.com/armon/go-metrics/prometheus/prometheus.go
@@ -2,10 +2,14 @@
package prometheus
import (
+ "fmt"
"strings"
"sync"
"time"
+ "regexp"
+
+ "github.com/armon/go-metrics"
"github.com/prometheus/client_golang/prometheus"
)
@@ -24,24 +28,42 @@ func NewPrometheusSink() (*PrometheusSink, error) {
}, nil
}
-func (p *PrometheusSink) flattenKey(parts []string) string {
- joined := strings.Join(parts, "_")
- joined = strings.Replace(joined, " ", "_", -1)
- joined = strings.Replace(joined, ".", "_", -1)
- joined = strings.Replace(joined, "-", "_", -1)
- joined = strings.Replace(joined, "=", "_", -1)
- return joined
+var forbiddenChars = regexp.MustCompile("[ .=\\-]")
+
+func (p *PrometheusSink) flattenKey(parts []string, labels []metrics.Label) (string, string) {
+ key := strings.Join(parts, "_")
+ key = forbiddenChars.ReplaceAllString(key, "_")
+
+ hash := key
+ for _, label := range labels {
+ hash += fmt.Sprintf(";%s=%s", label.Name, label.Value)
+ }
+
+ return key, hash
+}
+
+func prometheusLabels(labels []metrics.Label) prometheus.Labels {
+ l := make(prometheus.Labels)
+ for _, label := range labels {
+ l[label.Name] = label.Value
+ }
+ return l
}
func (p *PrometheusSink) SetGauge(parts []string, val float32) {
+ p.SetGaugeWithLabels(parts, val, nil)
+}
+
+func (p *PrometheusSink) SetGaugeWithLabels(parts []string, val float32, labels []metrics.Label) {
p.mu.Lock()
defer p.mu.Unlock()
- key := p.flattenKey(parts)
- g, ok := p.gauges[key]
+ key, hash := p.flattenKey(parts, labels)
+ g, ok := p.gauges[hash]
if !ok {
g = prometheus.NewGauge(prometheus.GaugeOpts{
- Name: key,
- Help: key,
+ Name: key,
+ Help: key,
+ ConstLabels: prometheusLabels(labels),
})
prometheus.MustRegister(g)
p.gauges[key] = g
@@ -50,15 +72,20 @@ func (p *PrometheusSink) SetGauge(parts []string, val float32) {
}
func (p *PrometheusSink) AddSample(parts []string, val float32) {
+ p.AddSampleWithLabels(parts, val, nil)
+}
+
+func (p *PrometheusSink) AddSampleWithLabels(parts []string, val float32, labels []metrics.Label) {
p.mu.Lock()
defer p.mu.Unlock()
- key := p.flattenKey(parts)
- g, ok := p.summaries[key]
+ key, hash := p.flattenKey(parts, labels)
+ g, ok := p.summaries[hash]
if !ok {
g = prometheus.NewSummary(prometheus.SummaryOpts{
- Name: key,
- Help: key,
- MaxAge: 10 * time.Second,
+ Name: key,
+ Help: key,
+ MaxAge: 10 * time.Second,
+ ConstLabels: prometheusLabels(labels),
})
prometheus.MustRegister(g)
p.summaries[key] = g
@@ -73,14 +100,19 @@ func (p *PrometheusSink) EmitKey(key []string, val float32) {
}
func (p *PrometheusSink) IncrCounter(parts []string, val float32) {
+ p.IncrCounterWithLabels(parts, val, nil)
+}
+
+func (p *PrometheusSink) IncrCounterWithLabels(parts []string, val float32, labels []metrics.Label) {
p.mu.Lock()
defer p.mu.Unlock()
- key := p.flattenKey(parts)
- g, ok := p.counters[key]
+ key, hash := p.flattenKey(parts, labels)
+ g, ok := p.counters[hash]
if !ok {
g = prometheus.NewCounter(prometheus.CounterOpts{
- Name: key,
- Help: key,
+ Name: key,
+ Help: key,
+ ConstLabels: prometheusLabels(labels),
})
prometheus.MustRegister(g)
p.counters[key] = g
diff --git a/vendor/github.com/armon/go-metrics/sink.go b/vendor/github.com/armon/go-metrics/sink.go
index 9f7e2f6a2..0b7d6e4be 100644
--- a/vendor/github.com/armon/go-metrics/sink.go
+++ b/vendor/github.com/armon/go-metrics/sink.go
@@ -10,31 +10,41 @@ import (
type MetricSink interface {
// A Gauge should retain the last value it is set to
SetGauge(key []string, val float32)
+ SetGaugeWithLabels(key []string, val float32, labels []Label)
// Should emit a Key/Value pair for each call
EmitKey(key []string, val float32)
// Counters should accumulate values
IncrCounter(key []string, val float32)
+ IncrCounterWithLabels(key []string, val float32, labels []Label)
// Samples are for timing information, where quantiles are used
AddSample(key []string, val float32)
+ AddSampleWithLabels(key []string, val float32, labels []Label)
}
// BlackholeSink is used to just blackhole messages
type BlackholeSink struct{}
-func (*BlackholeSink) SetGauge(key []string, val float32) {}
-func (*BlackholeSink) EmitKey(key []string, val float32) {}
-func (*BlackholeSink) IncrCounter(key []string, val float32) {}
-func (*BlackholeSink) AddSample(key []string, val float32) {}
+func (*BlackholeSink) SetGauge(key []string, val float32) {}
+func (*BlackholeSink) SetGaugeWithLabels(key []string, val float32, labels []Label) {}
+func (*BlackholeSink) EmitKey(key []string, val float32) {}
+func (*BlackholeSink) IncrCounter(key []string, val float32) {}
+func (*BlackholeSink) IncrCounterWithLabels(key []string, val float32, labels []Label) {}
+func (*BlackholeSink) AddSample(key []string, val float32) {}
+func (*BlackholeSink) AddSampleWithLabels(key []string, val float32, labels []Label) {}
// FanoutSink is used to sink to fanout values to multiple sinks
type FanoutSink []MetricSink
func (fh FanoutSink) SetGauge(key []string, val float32) {
+ fh.SetGaugeWithLabels(key, val, nil)
+}
+
+func (fh FanoutSink) SetGaugeWithLabels(key []string, val float32, labels []Label) {
for _, s := range fh {
- s.SetGauge(key, val)
+ s.SetGaugeWithLabels(key, val, labels)
}
}
@@ -45,14 +55,22 @@ func (fh FanoutSink) EmitKey(key []string, val float32) {
}
func (fh FanoutSink) IncrCounter(key []string, val float32) {
+ fh.IncrCounterWithLabels(key, val, nil)
+}
+
+func (fh FanoutSink) IncrCounterWithLabels(key []string, val float32, labels []Label) {
for _, s := range fh {
- s.IncrCounter(key, val)
+ s.IncrCounterWithLabels(key, val, labels)
}
}
func (fh FanoutSink) AddSample(key []string, val float32) {
+ fh.AddSampleWithLabels(key, val, nil)
+}
+
+func (fh FanoutSink) AddSampleWithLabels(key []string, val float32, labels []Label) {
for _, s := range fh {
- s.AddSample(key, val)
+ s.AddSampleWithLabels(key, val, labels)
}
}
diff --git a/vendor/github.com/armon/go-metrics/sink_test.go b/vendor/github.com/armon/go-metrics/sink_test.go
index 77c5c3278..714f99b81 100644
--- a/vendor/github.com/armon/go-metrics/sink_test.go
+++ b/vendor/github.com/armon/go-metrics/sink_test.go
@@ -7,25 +7,39 @@ import (
)
type MockSink struct {
- keys [][]string
- vals []float32
+ keys [][]string
+ vals []float32
+ labels [][]Label
}
func (m *MockSink) SetGauge(key []string, val float32) {
+ m.SetGaugeWithLabels(key, val, nil)
+}
+func (m *MockSink) SetGaugeWithLabels(key []string, val float32, labels []Label) {
m.keys = append(m.keys, key)
m.vals = append(m.vals, val)
+ m.labels = append(m.labels, labels)
}
func (m *MockSink) EmitKey(key []string, val float32) {
m.keys = append(m.keys, key)
m.vals = append(m.vals, val)
+ m.labels = append(m.labels, nil)
}
func (m *MockSink) IncrCounter(key []string, val float32) {
+ m.IncrCounterWithLabels(key, val, nil)
+}
+func (m *MockSink) IncrCounterWithLabels(key []string, val float32, labels []Label) {
m.keys = append(m.keys, key)
m.vals = append(m.vals, val)
+ m.labels = append(m.labels, labels)
}
func (m *MockSink) AddSample(key []string, val float32) {
+ m.AddSampleWithLabels(key, val, nil)
+}
+func (m *MockSink) AddSampleWithLabels(key []string, val float32, labels []Label) {
m.keys = append(m.keys, key)
m.vals = append(m.vals, val)
+ m.labels = append(m.labels, labels)
}
func TestFanoutSink_Gauge(t *testing.T) {
@@ -51,6 +65,36 @@ func TestFanoutSink_Gauge(t *testing.T) {
}
}
+func TestFanoutSink_Gauge_Labels(t *testing.T) {
+ m1 := &MockSink{}
+ m2 := &MockSink{}
+ fh := &FanoutSink{m1, m2}
+
+ k := []string{"test"}
+ v := float32(42.0)
+ l := []Label{{"a", "b"}}
+ fh.SetGaugeWithLabels(k, v, l)
+
+ if !reflect.DeepEqual(m1.keys[0], k) {
+ t.Fatalf("key not equal")
+ }
+ if !reflect.DeepEqual(m2.keys[0], k) {
+ t.Fatalf("key not equal")
+ }
+ if !reflect.DeepEqual(m1.vals[0], v) {
+ t.Fatalf("val not equal")
+ }
+ if !reflect.DeepEqual(m2.vals[0], v) {
+ t.Fatalf("val not equal")
+ }
+ if !reflect.DeepEqual(m1.labels[0], l) {
+ t.Fatalf("labels not equal")
+ }
+ if !reflect.DeepEqual(m2.labels[0], l) {
+ t.Fatalf("labels not equal")
+ }
+}
+
func TestFanoutSink_Key(t *testing.T) {
m1 := &MockSink{}
m2 := &MockSink{}
@@ -97,6 +141,36 @@ func TestFanoutSink_Counter(t *testing.T) {
}
}
+func TestFanoutSink_Counter_Labels(t *testing.T) {
+ m1 := &MockSink{}
+ m2 := &MockSink{}
+ fh := &FanoutSink{m1, m2}
+
+ k := []string{"test"}
+ v := float32(42.0)
+ l := []Label{{"a", "b"}}
+ fh.IncrCounterWithLabels(k, v, l)
+
+ if !reflect.DeepEqual(m1.keys[0], k) {
+ t.Fatalf("key not equal")
+ }
+ if !reflect.DeepEqual(m2.keys[0], k) {
+ t.Fatalf("key not equal")
+ }
+ if !reflect.DeepEqual(m1.vals[0], v) {
+ t.Fatalf("val not equal")
+ }
+ if !reflect.DeepEqual(m2.vals[0], v) {
+ t.Fatalf("val not equal")
+ }
+ if !reflect.DeepEqual(m1.labels[0], l) {
+ t.Fatalf("labels not equal")
+ }
+ if !reflect.DeepEqual(m2.labels[0], l) {
+ t.Fatalf("labels not equal")
+ }
+}
+
func TestFanoutSink_Sample(t *testing.T) {
m1 := &MockSink{}
m2 := &MockSink{}
@@ -120,6 +194,36 @@ func TestFanoutSink_Sample(t *testing.T) {
}
}
+func TestFanoutSink_Sample_Labels(t *testing.T) {
+ m1 := &MockSink{}
+ m2 := &MockSink{}
+ fh := &FanoutSink{m1, m2}
+
+ k := []string{"test"}
+ v := float32(42.0)
+ l := []Label{{"a", "b"}}
+ fh.AddSampleWithLabels(k, v, l)
+
+ if !reflect.DeepEqual(m1.keys[0], k) {
+ t.Fatalf("key not equal")
+ }
+ if !reflect.DeepEqual(m2.keys[0], k) {
+ t.Fatalf("key not equal")
+ }
+ if !reflect.DeepEqual(m1.vals[0], v) {
+ t.Fatalf("val not equal")
+ }
+ if !reflect.DeepEqual(m2.vals[0], v) {
+ t.Fatalf("val not equal")
+ }
+ if !reflect.DeepEqual(m1.labels[0], l) {
+ t.Fatalf("labels not equal")
+ }
+ if !reflect.DeepEqual(m2.labels[0], l) {
+ t.Fatalf("labels not equal")
+ }
+}
+
func TestNewMetricSinkFromURL(t *testing.T) {
for _, tc := range []struct {
desc string
diff --git a/vendor/github.com/armon/go-metrics/start.go b/vendor/github.com/armon/go-metrics/start.go
index 40c8d68c1..46f0c2eb2 100644
--- a/vendor/github.com/armon/go-metrics/start.go
+++ b/vendor/github.com/armon/go-metrics/start.go
@@ -2,8 +2,11 @@ package metrics
import (
"os"
+ "sync"
"sync/atomic"
"time"
+
+ "github.com/hashicorp/go-immutable-radix"
)
// Config is used to configure metrics settings
@@ -11,18 +14,26 @@ type Config struct {
ServiceName string // Prefixed with keys to seperate services
HostName string // Hostname to use. If not provided and EnableHostname, it will be os.Hostname
EnableHostname bool // Enable prefixing gauge values with hostname
+ EnableHostnameLabel bool // Enable adding hostname to labels
+ EnableServiceLabel bool // Enable adding service to labels
EnableRuntimeMetrics bool // Enables profiling of runtime metrics (GC, Goroutines, Memory)
EnableTypePrefix bool // Prefixes key with a type ("counter", "gauge", "timer")
TimerGranularity time.Duration // Granularity of timers.
ProfileInterval time.Duration // Interval to profile runtime metrics
+
+ AllowedPrefixes []string // A list of metric prefixes to allow, with '.' as the separator
+ BlockedPrefixes []string // A list of metric prefixes to block, with '.' as the separator
+ FilterDefault bool // Whether to allow metrics by default
}
// Metrics represents an instance of a metrics sink that can
// be used to emit
type Metrics struct {
Config
- lastNumGC uint32
- sink MetricSink
+ lastNumGC uint32
+ sink MetricSink
+ filter *iradix.Tree
+ filterLock sync.RWMutex
}
// Shared global metrics instance
@@ -43,6 +54,7 @@ func DefaultConfig(serviceName string) *Config {
EnableTypePrefix: false, // Disable type prefix
TimerGranularity: time.Millisecond, // Timers are in milliseconds
ProfileInterval: time.Second, // Poll runtime every second
+ FilterDefault: true, // Don't filter metrics by default
}
// Try to get the hostname
@@ -56,6 +68,7 @@ func New(conf *Config, sink MetricSink) (*Metrics, error) {
met := &Metrics{}
met.Config = *conf
met.sink = sink
+ met.UpdateFilter(conf.AllowedPrefixes, conf.BlockedPrefixes)
// Start the runtime collector
if conf.EnableRuntimeMetrics {
@@ -79,6 +92,10 @@ func SetGauge(key []string, val float32) {
globalMetrics.Load().(*Metrics).SetGauge(key, val)
}
+func SetGaugeWithLabels(key []string, val float32, labels []Label) {
+ globalMetrics.Load().(*Metrics).SetGaugeWithLabels(key, val, labels)
+}
+
func EmitKey(key []string, val float32) {
globalMetrics.Load().(*Metrics).EmitKey(key, val)
}
@@ -87,10 +104,26 @@ func IncrCounter(key []string, val float32) {
globalMetrics.Load().(*Metrics).IncrCounter(key, val)
}
+func IncrCounterWithLabels(key []string, val float32, labels []Label) {
+ globalMetrics.Load().(*Metrics).IncrCounterWithLabels(key, val, labels)
+}
+
func AddSample(key []string, val float32) {
globalMetrics.Load().(*Metrics).AddSample(key, val)
}
+func AddSampleWithLabels(key []string, val float32, labels []Label) {
+ globalMetrics.Load().(*Metrics).AddSampleWithLabels(key, val, labels)
+}
+
func MeasureSince(key []string, start time.Time) {
globalMetrics.Load().(*Metrics).MeasureSince(key, start)
}
+
+func MeasureSinceWithLabels(key []string, start time.Time, labels []Label) {
+ globalMetrics.Load().(*Metrics).MeasureSinceWithLabels(key, start, labels)
+}
+
+func UpdateFilter(allow, block []string) {
+ globalMetrics.Load().(*Metrics).UpdateFilter(allow, block)
+}
diff --git a/vendor/github.com/armon/go-metrics/start_test.go b/vendor/github.com/armon/go-metrics/start_test.go
index 96b73d956..f85a5bec3 100644
--- a/vendor/github.com/armon/go-metrics/start_test.go
+++ b/vendor/github.com/armon/go-metrics/start_test.go
@@ -30,6 +30,7 @@ func TestDefaultConfig(t *testing.T) {
t.Fatalf("bad interval")
}
}
+
func Test_GlobalMetrics(t *testing.T) {
var tests = []struct {
desc string
@@ -46,7 +47,7 @@ func Test_GlobalMetrics(t *testing.T) {
for _, tt := range tests {
t.Run(tt.desc, func(t *testing.T) {
s := &MockSink{}
- globalMetrics.Store(&Metrics{sink: s})
+ globalMetrics.Store(&Metrics{Config: Config{FilterDefault: true}, sink: s})
tt.fn(tt.key, tt.val)
if got, want := s.keys[0], tt.key; !reflect.DeepEqual(got, want) {
t.Fatalf("got key %s want %s", got, want)
@@ -58,9 +59,83 @@ func Test_GlobalMetrics(t *testing.T) {
}
}
+func Test_GlobalMetrics_Labels(t *testing.T) {
+ labels := []Label{{"a", "b"}}
+ var tests = []struct {
+ desc string
+ key []string
+ val float32
+ fn func([]string, float32, []Label)
+ labels []Label
+ }{
+ {"SetGaugeWithLabels", []string{"test"}, 42, SetGaugeWithLabels, labels},
+ {"IncrCounterWithLabels", []string{"test"}, 42, IncrCounterWithLabels, labels},
+ {"AddSampleWithLabels", []string{"test"}, 42, AddSampleWithLabels, labels},
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.desc, func(t *testing.T) {
+ s := &MockSink{}
+ globalMetrics.Store(&Metrics{Config: Config{FilterDefault: true}, sink: s})
+ tt.fn(tt.key, tt.val, tt.labels)
+ if got, want := s.keys[0], tt.key; !reflect.DeepEqual(got, want) {
+ t.Fatalf("got key %s want %s", got, want)
+ }
+ if got, want := s.vals[0], tt.val; !reflect.DeepEqual(got, want) {
+ t.Fatalf("got val %s want %s", got, want)
+ }
+ if got, want := s.labels[0], tt.labels; !reflect.DeepEqual(got, want) {
+ t.Fatalf("got val %s want %s", got, want)
+ }
+ })
+ }
+}
+
+func Test_GlobalMetrics_DefaultLabels(t *testing.T) {
+ config := Config{
+ HostName: "host1",
+ ServiceName: "redis",
+ EnableHostnameLabel: true,
+ EnableServiceLabel: true,
+ FilterDefault: true,
+ }
+ labels := []Label{
+ {"host", config.HostName},
+ {"service", config.ServiceName},
+ }
+ var tests = []struct {
+ desc string
+ key []string
+ val float32
+ fn func([]string, float32, []Label)
+ labels []Label
+ }{
+ {"SetGaugeWithLabels", []string{"test"}, 42, SetGaugeWithLabels, labels},
+ {"IncrCounterWithLabels", []string{"test"}, 42, IncrCounterWithLabels, labels},
+ {"AddSampleWithLabels", []string{"test"}, 42, AddSampleWithLabels, labels},
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.desc, func(t *testing.T) {
+ s := &MockSink{}
+ globalMetrics.Store(&Metrics{Config: config, sink: s})
+ tt.fn(tt.key, tt.val, nil)
+ if got, want := s.keys[0], tt.key; !reflect.DeepEqual(got, want) {
+ t.Fatalf("got key %s want %s", got, want)
+ }
+ if got, want := s.vals[0], tt.val; !reflect.DeepEqual(got, want) {
+ t.Fatalf("got val %s want %s", got, want)
+ }
+ if got, want := s.labels[0], tt.labels; !reflect.DeepEqual(got, want) {
+ t.Fatalf("got val %s want %s", got, want)
+ }
+ })
+ }
+}
+
func Test_GlobalMetrics_MeasureSince(t *testing.T) {
s := &MockSink{}
- m := &Metrics{sink: s, Config: Config{TimerGranularity: time.Millisecond}}
+ m := &Metrics{sink: s, Config: Config{TimerGranularity: time.Millisecond, FilterDefault: true}}
globalMetrics.Store(m)
k := []string{"test"}
@@ -73,6 +148,34 @@ func Test_GlobalMetrics_MeasureSince(t *testing.T) {
if s.vals[0] > 0.1 {
t.Fatalf("val too large %v", s.vals[0])
}
+
+ labels := []Label{{"a", "b"}}
+ MeasureSinceWithLabels(k, now, labels)
+ if got, want := s.keys[1], k; !reflect.DeepEqual(got, want) {
+ t.Fatalf("got key %s want %s", got, want)
+ }
+ if s.vals[1] > 0.1 {
+ t.Fatalf("val too large %v", s.vals[0])
+ }
+ if got, want := s.labels[1], labels; !reflect.DeepEqual(got, want) {
+ t.Fatalf("got val %s want %s", got, want)
+ }
+}
+
+func Test_GlobalMetrics_UpdateFilter(t *testing.T) {
+ globalMetrics.Store(&Metrics{Config: Config{
+ AllowedPrefixes: []string{"a"},
+ BlockedPrefixes: []string{"b"},
+ }})
+ UpdateFilter([]string{"c"}, []string{"d"})
+
+ m := globalMetrics.Load().(*Metrics)
+ if m.AllowedPrefixes[0] != "c" {
+ t.Fatalf("bad: %v", m.AllowedPrefixes)
+ }
+ if m.BlockedPrefixes[0] != "d" {
+ t.Fatalf("bad: %v", m.BlockedPrefixes)
+ }
}
// Benchmark_GlobalMetrics_Direct/direct-8 5000000 278 ns/op
diff --git a/vendor/github.com/armon/go-metrics/statsd.go b/vendor/github.com/armon/go-metrics/statsd.go
index 4241e880c..1bfffce46 100644
--- a/vendor/github.com/armon/go-metrics/statsd.go
+++ b/vendor/github.com/armon/go-metrics/statsd.go
@@ -50,6 +50,11 @@ func (s *StatsdSink) SetGauge(key []string, val float32) {
s.pushMetric(fmt.Sprintf("%s:%f|g\n", flatKey, val))
}
+func (s *StatsdSink) SetGaugeWithLabels(key []string, val float32, labels []Label) {
+ flatKey := s.flattenKeyLabels(key, labels)
+ s.pushMetric(fmt.Sprintf("%s:%f|g\n", flatKey, val))
+}
+
func (s *StatsdSink) EmitKey(key []string, val float32) {
flatKey := s.flattenKey(key)
s.pushMetric(fmt.Sprintf("%s:%f|kv\n", flatKey, val))
@@ -60,11 +65,21 @@ func (s *StatsdSink) IncrCounter(key []string, val float32) {
s.pushMetric(fmt.Sprintf("%s:%f|c\n", flatKey, val))
}
+func (s *StatsdSink) IncrCounterWithLabels(key []string, val float32, labels []Label) {
+ flatKey := s.flattenKeyLabels(key, labels)
+ s.pushMetric(fmt.Sprintf("%s:%f|c\n", flatKey, val))
+}
+
func (s *StatsdSink) AddSample(key []string, val float32) {
flatKey := s.flattenKey(key)
s.pushMetric(fmt.Sprintf("%s:%f|ms\n", flatKey, val))
}
+func (s *StatsdSink) AddSampleWithLabels(key []string, val float32, labels []Label) {
+ flatKey := s.flattenKeyLabels(key, labels)
+ s.pushMetric(fmt.Sprintf("%s:%f|ms\n", flatKey, val))
+}
+
// Flattens the key for formatting, removes spaces
func (s *StatsdSink) flattenKey(parts []string) string {
joined := strings.Join(parts, ".")
@@ -80,6 +95,14 @@ func (s *StatsdSink) flattenKey(parts []string) string {
}, joined)
}
+// Flattens the key along with labels for formatting, removes spaces
+func (s *StatsdSink) flattenKeyLabels(parts []string, labels []Label) string {
+ for _, label := range labels {
+ parts = append(parts, label.Value)
+ }
+ return s.flattenKey(parts)
+}
+
// Does a non-blocking push to the metrics queue
func (s *StatsdSink) pushMetric(m string) {
select {
diff --git a/vendor/github.com/armon/go-metrics/statsd_test.go b/vendor/github.com/armon/go-metrics/statsd_test.go
index 0602b213f..bdf36cc00 100644
--- a/vendor/github.com/armon/go-metrics/statsd_test.go
+++ b/vendor/github.com/armon/go-metrics/statsd_test.go
@@ -66,7 +66,7 @@ func TestStatsd_Conn(t *testing.T) {
if err != nil {
t.Fatalf("unexpected err %s", err)
}
- if line != "key.other:2.000000|kv\n" {
+ if line != "gauge_labels.val.label:2.000000|g\n" {
t.Fatalf("bad line %s", line)
}
@@ -74,7 +74,7 @@ func TestStatsd_Conn(t *testing.T) {
if err != nil {
t.Fatalf("unexpected err %s", err)
}
- if line != "counter.me:3.000000|c\n" {
+ if line != "key.other:3.000000|kv\n" {
t.Fatalf("bad line %s", line)
}
@@ -82,7 +82,31 @@ func TestStatsd_Conn(t *testing.T) {
if err != nil {
t.Fatalf("unexpected err %s", err)
}
- if line != "sample.slow_thingy:4.000000|ms\n" {
+ if line != "counter.me:4.000000|c\n" {
+ t.Fatalf("bad line %s", line)
+ }
+
+ line, err = reader.ReadString('\n')
+ if err != nil {
+ t.Fatalf("unexpected err %s", err)
+ }
+ if line != "counter_labels.me.label:5.000000|c\n" {
+ t.Fatalf("bad line %s", line)
+ }
+
+ line, err = reader.ReadString('\n')
+ if err != nil {
+ t.Fatalf("unexpected err %s", err)
+ }
+ if line != "sample.slow_thingy:6.000000|ms\n" {
+ t.Fatalf("bad line %s", line)
+ }
+
+ line, err = reader.ReadString('\n')
+ if err != nil {
+ t.Fatalf("unexpected err %s", err)
+ }
+ if line != "sample_labels.slow_thingy.label:7.000000|ms\n" {
t.Fatalf("bad line %s", line)
}
@@ -94,9 +118,12 @@ func TestStatsd_Conn(t *testing.T) {
}
s.SetGauge([]string{"gauge", "val"}, float32(1))
- s.EmitKey([]string{"key", "other"}, float32(2))
- s.IncrCounter([]string{"counter", "me"}, float32(3))
- s.AddSample([]string{"sample", "slow thingy"}, float32(4))
+ s.SetGaugeWithLabels([]string{"gauge_labels", "val"}, float32(2), []Label{{"a", "label"}})
+ s.EmitKey([]string{"key", "other"}, float32(3))
+ s.IncrCounter([]string{"counter", "me"}, float32(4))
+ s.IncrCounterWithLabels([]string{"counter_labels", "me"}, float32(5), []Label{{"a", "label"}})
+ s.AddSample([]string{"sample", "slow thingy"}, float32(6))
+ s.AddSampleWithLabels([]string{"sample_labels", "slow thingy"}, float32(7), []Label{{"a", "label"}})
select {
case <-done:
diff --git a/vendor/github.com/armon/go-metrics/statsite.go b/vendor/github.com/armon/go-metrics/statsite.go
index 572fe0571..6c0d284d2 100644
--- a/vendor/github.com/armon/go-metrics/statsite.go
+++ b/vendor/github.com/armon/go-metrics/statsite.go
@@ -50,6 +50,11 @@ func (s *StatsiteSink) SetGauge(key []string, val float32) {
s.pushMetric(fmt.Sprintf("%s:%f|g\n", flatKey, val))
}
+func (s *StatsiteSink) SetGaugeWithLabels(key []string, val float32, labels []Label) {
+ flatKey := s.flattenKeyLabels(key, labels)
+ s.pushMetric(fmt.Sprintf("%s:%f|g\n", flatKey, val))
+}
+
func (s *StatsiteSink) EmitKey(key []string, val float32) {
flatKey := s.flattenKey(key)
s.pushMetric(fmt.Sprintf("%s:%f|kv\n", flatKey, val))
@@ -60,11 +65,21 @@ func (s *StatsiteSink) IncrCounter(key []string, val float32) {
s.pushMetric(fmt.Sprintf("%s:%f|c\n", flatKey, val))
}
+func (s *StatsiteSink) IncrCounterWithLabels(key []string, val float32, labels []Label) {
+ flatKey := s.flattenKeyLabels(key, labels)
+ s.pushMetric(fmt.Sprintf("%s:%f|c\n", flatKey, val))
+}
+
func (s *StatsiteSink) AddSample(key []string, val float32) {
flatKey := s.flattenKey(key)
s.pushMetric(fmt.Sprintf("%s:%f|ms\n", flatKey, val))
}
+func (s *StatsiteSink) AddSampleWithLabels(key []string, val float32, labels []Label) {
+ flatKey := s.flattenKeyLabels(key, labels)
+ s.pushMetric(fmt.Sprintf("%s:%f|ms\n", flatKey, val))
+}
+
// Flattens the key for formatting, removes spaces
func (s *StatsiteSink) flattenKey(parts []string) string {
joined := strings.Join(parts, ".")
@@ -80,6 +95,14 @@ func (s *StatsiteSink) flattenKey(parts []string) string {
}, joined)
}
+// Flattens the key along with labels for formatting, removes spaces
+func (s *StatsiteSink) flattenKeyLabels(parts []string, labels []Label) string {
+ for _, label := range labels {
+ parts = append(parts, label.Value)
+ }
+ return s.flattenKey(parts)
+}
+
// Does a non-blocking push to the metrics queue
func (s *StatsiteSink) pushMetric(m string) {
select {
diff --git a/vendor/github.com/armon/go-metrics/statsite_test.go b/vendor/github.com/armon/go-metrics/statsite_test.go
index 704474f43..92687889b 100644
--- a/vendor/github.com/armon/go-metrics/statsite_test.go
+++ b/vendor/github.com/armon/go-metrics/statsite_test.go
@@ -61,7 +61,7 @@ func TestStatsite_Conn(t *testing.T) {
if err != nil {
t.Fatalf("unexpected err %s", err)
}
- if line != "key.other:2.000000|kv\n" {
+ if line != "gauge_labels.val.label:2.000000|g\n" {
t.Fatalf("bad line %s", line)
}
@@ -69,7 +69,7 @@ func TestStatsite_Conn(t *testing.T) {
if err != nil {
t.Fatalf("unexpected err %s", err)
}
- if line != "counter.me:3.000000|c\n" {
+ if line != "key.other:3.000000|kv\n" {
t.Fatalf("bad line %s", line)
}
@@ -77,7 +77,31 @@ func TestStatsite_Conn(t *testing.T) {
if err != nil {
t.Fatalf("unexpected err %s", err)
}
- if line != "sample.slow_thingy:4.000000|ms\n" {
+ if line != "counter.me:4.000000|c\n" {
+ t.Fatalf("bad line %s", line)
+ }
+
+ line, err = reader.ReadString('\n')
+ if err != nil {
+ t.Fatalf("unexpected err %s", err)
+ }
+ if line != "counter_labels.me.label:5.000000|c\n" {
+ t.Fatalf("bad line %s", line)
+ }
+
+ line, err = reader.ReadString('\n')
+ if err != nil {
+ t.Fatalf("unexpected err %s", err)
+ }
+ if line != "sample.slow_thingy:6.000000|ms\n" {
+ t.Fatalf("bad line %s", line)
+ }
+
+ line, err = reader.ReadString('\n')
+ if err != nil {
+ t.Fatalf("unexpected err %s", err)
+ }
+ if line != "sample_labels.slow_thingy.label:7.000000|ms\n" {
t.Fatalf("bad line %s", line)
}
@@ -90,9 +114,12 @@ func TestStatsite_Conn(t *testing.T) {
}
s.SetGauge([]string{"gauge", "val"}, float32(1))
- s.EmitKey([]string{"key", "other"}, float32(2))
- s.IncrCounter([]string{"counter", "me"}, float32(3))
- s.AddSample([]string{"sample", "slow thingy"}, float32(4))
+ s.SetGaugeWithLabels([]string{"gauge_labels", "val"}, float32(2), []Label{{"a", "label"}})
+ s.EmitKey([]string{"key", "other"}, float32(3))
+ s.IncrCounter([]string{"counter", "me"}, float32(4))
+ s.IncrCounterWithLabels([]string{"counter_labels", "me"}, float32(5), []Label{{"a", "label"}})
+ s.AddSample([]string{"sample", "slow thingy"}, float32(6))
+ s.AddSampleWithLabels([]string{"sample_labels", "slow thingy"}, float32(7), []Label{{"a", "label"}})
select {
case <-done:
diff --git a/vendor/github.com/davecgh/go-spew/.gitignore b/vendor/github.com/davecgh/go-spew/.gitignore
new file mode 100644
index 000000000..00268614f
--- /dev/null
+++ b/vendor/github.com/davecgh/go-spew/.gitignore
@@ -0,0 +1,22 @@
+# 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
diff --git a/vendor/github.com/davecgh/go-spew/.travis.yml b/vendor/github.com/davecgh/go-spew/.travis.yml
new file mode 100644
index 000000000..984e0736e
--- /dev/null
+++ b/vendor/github.com/davecgh/go-spew/.travis.yml
@@ -0,0 +1,14 @@
+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
diff --git a/vendor/github.com/davecgh/go-spew/LICENSE b/vendor/github.com/davecgh/go-spew/LICENSE
new file mode 100644
index 000000000..bb6733231
--- /dev/null
+++ b/vendor/github.com/davecgh/go-spew/LICENSE
@@ -0,0 +1,15 @@
+ISC License
+
+Copyright (c) 2012-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.
diff --git a/vendor/github.com/davecgh/go-spew/README.md b/vendor/github.com/davecgh/go-spew/README.md
new file mode 100644
index 000000000..556170ae6
--- /dev/null
+++ b/vendor/github.com/davecgh/go-spew/README.md
@@ -0,0 +1,194 @@
+go-spew
+=======
+
+[![Build Status](https://travis-ci.org/davecgh/go-spew.png?branch=master)]
+(https://travis-ci.org/davecgh/go-spew) [![Coverage Status]
+(https://coveralls.io/repos/davecgh/go-spew/badge.png?branch=master)]
+(https://coveralls.io/r/davecgh/go-spew?branch=master)
+
+Go-spew implements a deep pretty printer for Go data structures to aid in
+debugging. A comprehensive suite of tests with 100% test coverage is provided
+to ensure proper functionality. See `test_coverage.txt` for the gocov coverage
+report. Go-spew is licensed under the liberal ISC license, so it may be used in
+open source or commercial projects.
+
+If you're interested in reading about how this package came to life and some
+of the challenges involved in providing a deep pretty printer, there is a blog
+post about it
+[here](https://web.archive.org/web/20160304013555/https://blog.cyphertite.com/go-spew-a-journey-into-dumping-go-data-structures/).
+
+## Documentation
+
+[![GoDoc](https://godoc.org/github.com/davecgh/go-spew/spew?status.png)]
+(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.
+
+* 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 liberal ISC License.
diff --git a/vendor/github.com/davecgh/go-spew/cov_report.sh b/vendor/github.com/davecgh/go-spew/cov_report.sh
new file mode 100644
index 000000000..9579497e4
--- /dev/null
+++ b/vendor/github.com/davecgh/go-spew/cov_report.sh
@@ -0,0 +1,22 @@
+#!/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
diff --git a/vendor/github.com/davecgh/go-spew/spew/bypass.go b/vendor/github.com/davecgh/go-spew/spew/bypass.go
new file mode 100644
index 000000000..d42a0bc4a
--- /dev/null
+++ b/vendor/github.com/davecgh/go-spew/spew/bypass.go
@@ -0,0 +1,152 @@
+// Copyright (c) 2015 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
+
+package spew
+
+import (
+ "reflect"
+ "unsafe"
+)
+
+const (
+ // UnsafeDisabled is a build-time constant which specifies whether or
+ // not access to the unsafe package is available.
+ UnsafeDisabled = false
+
+ // ptrSize is the size of a pointer on the current arch.
+ ptrSize = unsafe.Sizeof((*byte)(nil))
+)
+
+var (
+ // offsetPtr, offsetScalar, and offsetFlag are the offsets for the
+ // internal reflect.Value fields. These values are valid before golang
+ // commit ecccf07e7f9d which changed the format. The are also valid
+ // after commit 82f48826c6c7 which changed the format again to mirror
+ // the original format. Code in the init function updates these offsets
+ // as necessary.
+ offsetPtr = uintptr(ptrSize)
+ offsetScalar = uintptr(0)
+ offsetFlag = uintptr(ptrSize * 2)
+
+ // flagKindWidth and flagKindShift indicate various bits that the
+ // reflect package uses internally to track kind information.
+ //
+ // flagRO indicates whether or not the value field of a reflect.Value is
+ // read-only.
+ //
+ // flagIndir indicates whether the value field of a reflect.Value is
+ // the actual data or a pointer to the data.
+ //
+ // These values are valid before golang commit 90a7c3c86944 which
+ // changed their positions. Code in the init function updates these
+ // flags as necessary.
+ flagKindWidth = uintptr(5)
+ flagKindShift = uintptr(flagKindWidth - 1)
+ flagRO = uintptr(1 << 0)
+ flagIndir = uintptr(1 << 1)
+)
+
+func init() {
+ // Older versions of reflect.Value stored small integers directly in the
+ // ptr field (which is named val in the older versions). Versions
+ // between commits ecccf07e7f9d and 82f48826c6c7 added a new field named
+ // scalar for this purpose which unfortunately came before the flag
+ // field, so the offset of the flag field is different for those
+ // versions.
+ //
+ // This code constructs a new reflect.Value from a known small integer
+ // and checks if the size of the reflect.Value struct indicates it has
+ // the scalar field. When it does, the offsets are updated accordingly.
+ vv := reflect.ValueOf(0xf00)
+ if unsafe.Sizeof(vv) == (ptrSize * 4) {
+ offsetScalar = ptrSize * 2
+ offsetFlag = ptrSize * 3
+ }
+
+ // Commit 90a7c3c86944 changed the flag positions such that the low
+ // order bits are the kind. This code extracts the kind from the flags
+ // field and ensures it's the correct type. When it's not, the flag
+ // order has been changed to the newer format, so the flags are updated
+ // accordingly.
+ upf := unsafe.Pointer(uintptr(unsafe.Pointer(&vv)) + offsetFlag)
+ upfv := *(*uintptr)(upf)
+ flagKindMask := uintptr((1<<flagKindWidth - 1) << flagKindShift)
+ if (upfv&flagKindMask)>>flagKindShift != uintptr(reflect.Int) {
+ flagKindShift = 0
+ flagRO = 1 << 5
+ flagIndir = 1 << 6
+
+ // Commit adf9b30e5594 modified the flags to separate the
+ // flagRO flag into two bits which specifies whether or not the
+ // field is embedded. This causes flagIndir to move over a bit
+ // and means that flagRO is the combination of either of the
+ // original flagRO bit and the new bit.
+ //
+ // This code detects the change by extracting what used to be
+ // the indirect bit to ensure it's set. When it's not, the flag
+ // order has been changed to the newer format, so the flags are
+ // updated accordingly.
+ if upfv&flagIndir == 0 {
+ flagRO = 3 << 5
+ flagIndir = 1 << 7
+ }
+ }
+}
+
+// unsafeReflectValue converts the passed reflect.Value into a one that bypasses
+// the typical safety restrictions preventing access to unaddressable and
+// unexported data. It works by digging the raw pointer to the underlying
+// value out of the protected value and generating a new unprotected (unsafe)
+// reflect.Value to it.
+//
+// This allows us to check for implementations of the Stringer and error
+// interfaces to be used for pretty printing ordinarily unaddressable and
+// inaccessible values such as unexported struct fields.
+func unsafeReflectValue(v reflect.Value) (rv reflect.Value) {
+ indirects := 1
+ vt := v.Type()
+ upv := unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + offsetPtr)
+ rvf := *(*uintptr)(unsafe.Pointer(uintptr(unsafe.Pointer(&v)) + offsetFlag))
+ if rvf&flagIndir != 0 {
+ vt = reflect.PtrTo(v.Type())
+ indirects++
+ } else if offsetScalar != 0 {
+ // The value is in the scalar field when it's not one of the
+ // reference types.
+ switch vt.Kind() {
+ case reflect.Uintptr:
+ case reflect.Chan:
+ case reflect.Func:
+ case reflect.Map:
+ case reflect.Ptr:
+ case reflect.UnsafePointer:
+ default:
+ upv = unsafe.Pointer(uintptr(unsafe.Pointer(&v)) +
+ offsetScalar)
+ }
+ }
+
+ pv := reflect.NewAt(vt, upv)
+ rv = pv
+ for i := 0; i < indirects; i++ {
+ rv = rv.Elem()
+ }
+ return rv
+}
diff --git a/vendor/github.com/davecgh/go-spew/spew/bypasssafe.go b/vendor/github.com/davecgh/go-spew/spew/bypasssafe.go
new file mode 100644
index 000000000..e47a4e795
--- /dev/null
+++ b/vendor/github.com/davecgh/go-spew/spew/bypasssafe.go
@@ -0,0 +1,38 @@
+// Copyright (c) 2015 Dave Collins <dave@davec.name>
+//
+// Permission to use, copy, modify, and distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+// NOTE: Due to the following build constraints, this file will only be compiled
+// when the code is running on Google App Engine, compiled by GopherJS, or
+// "-tags safe" is added to the go build command line. The "disableunsafe"
+// tag is deprecated and thus should not be used.
+// +build js appengine safe disableunsafe
+
+package spew
+
+import "reflect"
+
+const (
+ // UnsafeDisabled is a build-time constant which specifies whether or
+ // not access to the unsafe package is available.
+ UnsafeDisabled = true
+)
+
+// unsafeReflectValue typically converts the passed reflect.Value into a one
+// that bypasses the typical safety restrictions preventing access to
+// unaddressable and unexported data. However, doing this relies on access to
+// the unsafe package. This is a stub version which simply returns the passed
+// reflect.Value when the unsafe package is not available.
+func unsafeReflectValue(v reflect.Value) reflect.Value {
+ return v
+}
diff --git a/vendor/github.com/davecgh/go-spew/spew/common.go b/vendor/github.com/davecgh/go-spew/spew/common.go
new file mode 100644
index 000000000..14f02dc15
--- /dev/null
+++ b/vendor/github.com/davecgh/go-spew/spew/common.go
@@ -0,0 +1,341 @@
+/*
+ * Copyright (c) 2013 Dave Collins <dave@davec.name>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+package spew
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "reflect"
+ "sort"
+ "strconv"
+)
+
+// Some constants in the form of bytes to avoid string overhead. This mirrors
+// the technique used in the fmt package.
+var (
+ panicBytes = []byte("(PANIC=")
+ plusBytes = []byte("+")
+ iBytes = []byte("i")
+ trueBytes = []byte("true")
+ falseBytes = []byte("false")
+ interfaceBytes = []byte("(interface {})")
+ commaNewlineBytes = []byte(",\n")
+ newlineBytes = []byte("\n")
+ openBraceBytes = []byte("{")
+ openBraceNewlineBytes = []byte("{\n")
+ closeBraceBytes = []byte("}")
+ asteriskBytes = []byte("*")
+ colonBytes = []byte(":")
+ colonSpaceBytes = []byte(": ")
+ openParenBytes = []byte("(")
+ closeParenBytes = []byte(")")
+ spaceBytes = []byte(" ")
+ pointerChainBytes = []byte("->")
+ nilAngleBytes = []byte("<nil>")
+ maxNewlineBytes = []byte("<max depth reached>\n")
+ maxShortBytes = []byte("<max>")
+ circularBytes = []byte("<already shown>")
+ circularShortBytes = []byte("<shown>")
+ invalidAngleBytes = []byte("<invalid>")
+ openBracketBytes = []byte("[")
+ closeBracketBytes = []byte("]")
+ percentBytes = []byte("%")
+ precisionBytes = []byte(".")
+ openAngleBytes = []byte("<")
+ closeAngleBytes = []byte(">")
+ openMapBytes = []byte("map[")
+ closeMapBytes = []byte("]")
+ lenEqualsBytes = []byte("len=")
+ capEqualsBytes = []byte("cap=")
+)
+
+// hexDigits is used to map a decimal value to a hex digit.
+var hexDigits = "0123456789abcdef"
+
+// catchPanic handles any panics that might occur during the handleMethods
+// calls.
+func catchPanic(w io.Writer, v reflect.Value) {
+ if err := recover(); err != nil {
+ w.Write(panicBytes)
+ fmt.Fprintf(w, "%v", err)
+ w.Write(closeParenBytes)
+ }
+}
+
+// handleMethods attempts to call the Error and String methods on the underlying
+// type the passed reflect.Value represents and outputes the result to Writer w.
+//
+// It handles panics in any called methods by catching and displaying the error
+// as the formatted value.
+func handleMethods(cs *ConfigState, w io.Writer, v reflect.Value) (handled bool) {
+ // We need an interface to check if the type implements the error or
+ // Stringer interface. However, the reflect package won't give us an
+ // interface on certain things like unexported struct fields in order
+ // to enforce visibility rules. We use unsafe, when it's available,
+ // to bypass these restrictions since this package does not mutate the
+ // values.
+ if !v.CanInterface() {
+ if UnsafeDisabled {
+ return false
+ }
+
+ v = unsafeReflectValue(v)
+ }
+
+ // Choose whether or not to do error and Stringer interface lookups against
+ // the base type or a pointer to the base type depending on settings.
+ // Technically calling one of these methods with a pointer receiver can
+ // mutate the value, however, types which choose to satisify an error or
+ // Stringer interface with a pointer receiver should not be mutating their
+ // state inside these interface methods.
+ if !cs.DisablePointerMethods && !UnsafeDisabled && !v.CanAddr() {
+ v = unsafeReflectValue(v)
+ }
+ if v.CanAddr() {
+ v = v.Addr()
+ }
+
+ // Is it an error or Stringer?
+ switch iface := v.Interface().(type) {
+ case error:
+ defer catchPanic(w, v)
+ if cs.ContinueOnMethod {
+ w.Write(openParenBytes)
+ w.Write([]byte(iface.Error()))
+ w.Write(closeParenBytes)
+ w.Write(spaceBytes)
+ return false
+ }
+
+ w.Write([]byte(iface.Error()))
+ return true
+
+ case fmt.Stringer:
+ defer catchPanic(w, v)
+ if cs.ContinueOnMethod {
+ w.Write(openParenBytes)
+ w.Write([]byte(iface.String()))
+ w.Write(closeParenBytes)
+ w.Write(spaceBytes)
+ return false
+ }
+ w.Write([]byte(iface.String()))
+ return true
+ }
+ return false
+}
+
+// printBool outputs a boolean value as true or false to Writer w.
+func printBool(w io.Writer, val bool) {
+ if val {
+ w.Write(trueBytes)
+ } else {
+ w.Write(falseBytes)
+ }
+}
+
+// printInt outputs a signed integer value to Writer w.
+func printInt(w io.Writer, val int64, base int) {
+ w.Write([]byte(strconv.FormatInt(val, base)))
+}
+
+// printUint outputs an unsigned integer value to Writer w.
+func printUint(w io.Writer, val uint64, base int) {
+ w.Write([]byte(strconv.FormatUint(val, base)))
+}
+
+// printFloat outputs a floating point value using the specified precision,
+// which is expected to be 32 or 64bit, to Writer w.
+func printFloat(w io.Writer, val float64, precision int) {
+ w.Write([]byte(strconv.FormatFloat(val, 'g', -1, precision)))
+}
+
+// printComplex outputs a complex value using the specified float precision
+// for the real and imaginary parts to Writer w.
+func printComplex(w io.Writer, c complex128, floatPrecision int) {
+ r := real(c)
+ w.Write(openParenBytes)
+ w.Write([]byte(strconv.FormatFloat(r, 'g', -1, floatPrecision)))
+ i := imag(c)
+ if i >= 0 {
+ w.Write(plusBytes)
+ }
+ w.Write([]byte(strconv.FormatFloat(i, 'g', -1, floatPrecision)))
+ w.Write(iBytes)
+ w.Write(closeParenBytes)
+}
+
+// printHexPtr outputs a uintptr formatted as hexidecimal with a leading '0x'
+// prefix to Writer w.
+func printHexPtr(w io.Writer, p uintptr) {
+ // Null pointer.
+ num := uint64(p)
+ if num == 0 {
+ w.Write(nilAngleBytes)
+ return
+ }
+
+ // Max uint64 is 16 bytes in hex + 2 bytes for '0x' prefix
+ buf := make([]byte, 18)
+
+ // It's simpler to construct the hex string right to left.
+ base := uint64(16)
+ i := len(buf) - 1
+ for num >= base {
+ buf[i] = hexDigits[num%base]
+ num /= base
+ i--
+ }
+ buf[i] = hexDigits[num]
+
+ // Add '0x' prefix.
+ i--
+ buf[i] = 'x'
+ i--
+ buf[i] = '0'
+
+ // Strip unused leading bytes.
+ buf = buf[i:]
+ w.Write(buf)
+}
+
+// valuesSorter implements sort.Interface to allow a slice of reflect.Value
+// elements to be sorted.
+type valuesSorter struct {
+ values []reflect.Value
+ strings []string // either nil or same len and values
+ cs *ConfigState
+}
+
+// newValuesSorter initializes a valuesSorter instance, which holds a set of
+// surrogate keys on which the data should be sorted. It uses flags in
+// ConfigState to decide if and how to populate those surrogate keys.
+func newValuesSorter(values []reflect.Value, cs *ConfigState) sort.Interface {
+ vs := &valuesSorter{values: values, cs: cs}
+ if canSortSimply(vs.values[0].Kind()) {
+ return vs
+ }
+ if !cs.DisableMethods {
+ vs.strings = make([]string, len(values))
+ for i := range vs.values {
+ b := bytes.Buffer{}
+ if !handleMethods(cs, &b, vs.values[i]) {
+ vs.strings = nil
+ break
+ }
+ vs.strings[i] = b.String()
+ }
+ }
+ if vs.strings == nil && cs.SpewKeys {
+ vs.strings = make([]string, len(values))
+ for i := range vs.values {
+ vs.strings[i] = Sprintf("%#v", vs.values[i].Interface())
+ }
+ }
+ return vs
+}
+
+// canSortSimply tests whether a reflect.Kind is a primitive that can be sorted
+// directly, or whether it should be considered for sorting by surrogate keys
+// (if the ConfigState allows it).
+func canSortSimply(kind reflect.Kind) bool {
+ // This switch parallels valueSortLess, except for the default case.
+ switch kind {
+ case reflect.Bool:
+ return true
+ case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
+ return true
+ case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
+ return true
+ case reflect.Float32, reflect.Float64:
+ return true
+ case reflect.String:
+ return true
+ case reflect.Uintptr:
+ return true
+ case reflect.Array:
+ return true
+ }
+ return false
+}
+
+// Len returns the number of values in the slice. It is part of the
+// sort.Interface implementation.
+func (s *valuesSorter) Len() int {
+ return len(s.values)
+}
+
+// Swap swaps the values at the passed indices. It is part of the
+// sort.Interface implementation.
+func (s *valuesSorter) Swap(i, j int) {
+ s.values[i], s.values[j] = s.values[j], s.values[i]
+ if s.strings != nil {
+ s.strings[i], s.strings[j] = s.strings[j], s.strings[i]
+ }
+}
+
+// valueSortLess returns whether the first value should sort before the second
+// value. It is used by valueSorter.Less as part of the sort.Interface
+// implementation.
+func valueSortLess(a, b reflect.Value) bool {
+ switch a.Kind() {
+ case reflect.Bool:
+ return !a.Bool() && b.Bool()
+ case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
+ return a.Int() < b.Int()
+ case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
+ return a.Uint() < b.Uint()
+ case reflect.Float32, reflect.Float64:
+ return a.Float() < b.Float()
+ case reflect.String:
+ return a.String() < b.String()
+ case reflect.Uintptr:
+ return a.Uint() < b.Uint()
+ case reflect.Array:
+ // Compare the contents of both arrays.
+ l := a.Len()
+ for i := 0; i < l; i++ {
+ av := a.Index(i)
+ bv := b.Index(i)
+ if av.Interface() == bv.Interface() {
+ continue
+ }
+ return valueSortLess(av, bv)
+ }
+ }
+ return a.String() < b.String()
+}
+
+// Less returns whether the value at index i should sort before the
+// value at index j. It is part of the sort.Interface implementation.
+func (s *valuesSorter) Less(i, j int) bool {
+ if s.strings == nil {
+ return valueSortLess(s.values[i], s.values[j])
+ }
+ return s.strings[i] < s.strings[j]
+}
+
+// sortValues is a sort function that handles both native types and any type that
+// can be converted to error or Stringer. Other inputs are sorted according to
+// their Value.String() value to ensure display stability.
+func sortValues(values []reflect.Value, cs *ConfigState) {
+ if len(values) == 0 {
+ return
+ }
+ sort.Sort(newValuesSorter(values, cs))
+}
diff --git a/vendor/github.com/davecgh/go-spew/spew/common_test.go b/vendor/github.com/davecgh/go-spew/spew/common_test.go
new file mode 100644
index 000000000..39b7525b3
--- /dev/null
+++ b/vendor/github.com/davecgh/go-spew/spew/common_test.go
@@ -0,0 +1,298 @@
+/*
+ * Copyright (c) 2013 Dave Collins <dave@davec.name>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+package spew_test
+
+import (
+ "fmt"
+ "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)
+}
diff --git a/vendor/github.com/davecgh/go-spew/spew/config.go b/vendor/github.com/davecgh/go-spew/spew/config.go
new file mode 100644
index 000000000..555282723
--- /dev/null
+++ b/vendor/github.com/davecgh/go-spew/spew/config.go
@@ -0,0 +1,297 @@
+/*
+ * Copyright (c) 2013 Dave Collins <dave@davec.name>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+package spew
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "os"
+)
+
+// ConfigState houses the configuration options used by spew to format and
+// display values. There is a global instance, Config, that is used to control
+// all top-level Formatter and Dump functionality. Each ConfigState instance
+// provides methods equivalent to the top-level functions.
+//
+// The zero value for ConfigState provides no indentation. You would typically
+// want to set it to a space or a tab.
+//
+// Alternatively, you can use NewDefaultConfig to get a ConfigState instance
+// with default settings. See the documentation of NewDefaultConfig for default
+// values.
+type ConfigState struct {
+ // Indent specifies the string to use for each indentation level. The
+ // global config instance that all top-level functions use set this to a
+ // single space by default. If you would like more indentation, you might
+ // set this to a tab with "\t" or perhaps two spaces with " ".
+ Indent string
+
+ // MaxDepth controls the maximum number of levels to descend into nested
+ // data structures. The default, 0, means there is no limit.
+ //
+ // NOTE: Circular data structures are properly detected, so it is not
+ // necessary to set this value unless you specifically want to limit deeply
+ // nested data structures.
+ MaxDepth int
+
+ // DisableMethods specifies whether or not error and Stringer interfaces are
+ // invoked for types that implement them.
+ DisableMethods bool
+
+ // DisablePointerMethods specifies whether or not to check for and invoke
+ // error and Stringer interfaces on types which only accept a pointer
+ // receiver when the current type is not a pointer.
+ //
+ // NOTE: This might be an unsafe action since calling one of these methods
+ // with a pointer receiver could technically mutate the value, however,
+ // in practice, types which choose to satisify an error or Stringer
+ // interface with a pointer receiver should not be mutating their state
+ // inside these interface methods. As a result, this option relies on
+ // access to the unsafe package, so it will not have any effect when
+ // running in environments without access to the unsafe package such as
+ // Google App Engine or with the "safe" build tag specified.
+ DisablePointerMethods bool
+
+ // ContinueOnMethod specifies whether or not recursion should continue once
+ // a custom error or Stringer interface is invoked. The default, false,
+ // means it will print the results of invoking the custom error or Stringer
+ // interface and return immediately instead of continuing to recurse into
+ // the internals of the data type.
+ //
+ // NOTE: This flag does not have any effect if method invocation is disabled
+ // via the DisableMethods or DisablePointerMethods options.
+ ContinueOnMethod bool
+
+ // SortKeys specifies map keys should be sorted before being printed. Use
+ // this to have a more deterministic, diffable output. Note that only
+ // native types (bool, int, uint, floats, uintptr and string) and types
+ // that support the error or Stringer interfaces (if methods are
+ // enabled) are supported, with other types sorted according to the
+ // reflect.Value.String() output which guarantees display stability.
+ SortKeys bool
+
+ // SpewKeys specifies that, as a last resort attempt, map keys should
+ // be spewed to strings and sorted by those strings. This is only
+ // considered if SortKeys is true.
+ SpewKeys bool
+}
+
+// Config is the active configuration of the top-level functions.
+// The configuration can be changed by modifying the contents of spew.Config.
+var Config = ConfigState{Indent: " "}
+
+// Errorf is a wrapper for fmt.Errorf that treats each argument as if it were
+// passed with a Formatter interface returned by c.NewFormatter. It returns
+// the formatted string as a value that satisfies error. See NewFormatter
+// for formatting details.
+//
+// This function is shorthand for the following syntax:
+//
+// fmt.Errorf(format, c.NewFormatter(a), c.NewFormatter(b))
+func (c *ConfigState) Errorf(format string, a ...interface{}) (err error) {
+ return fmt.Errorf(format, c.convertArgs(a)...)
+}
+
+// Fprint is a wrapper for fmt.Fprint that treats each argument as if it were
+// passed with a Formatter interface returned by c.NewFormatter. It returns
+// the number of bytes written and any write error encountered. See
+// NewFormatter for formatting details.
+//
+// This function is shorthand for the following syntax:
+//
+// fmt.Fprint(w, c.NewFormatter(a), c.NewFormatter(b))
+func (c *ConfigState) Fprint(w io.Writer, a ...interface{}) (n int, err error) {
+ return fmt.Fprint(w, c.convertArgs(a)...)
+}
+
+// Fprintf is a wrapper for fmt.Fprintf that treats each argument as if it were
+// passed with a Formatter interface returned by c.NewFormatter. It returns
+// the number of bytes written and any write error encountered. See
+// NewFormatter for formatting details.
+//
+// This function is shorthand for the following syntax:
+//
+// fmt.Fprintf(w, format, c.NewFormatter(a), c.NewFormatter(b))
+func (c *ConfigState) Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) {
+ return fmt.Fprintf(w, format, c.convertArgs(a)...)
+}
+
+// Fprintln is a wrapper for fmt.Fprintln that treats each argument as if it
+// passed with a Formatter interface returned by c.NewFormatter. See
+// NewFormatter for formatting details.
+//
+// This function is shorthand for the following syntax:
+//
+// fmt.Fprintln(w, c.NewFormatter(a), c.NewFormatter(b))
+func (c *ConfigState) Fprintln(w io.Writer, a ...interface{}) (n int, err error) {
+ return fmt.Fprintln(w, c.convertArgs(a)...)
+}
+
+// Print is a wrapper for fmt.Print that treats each argument as if it were
+// passed with a Formatter interface returned by c.NewFormatter. It returns
+// the number of bytes written and any write error encountered. See
+// NewFormatter for formatting details.
+//
+// This function is shorthand for the following syntax:
+//
+// fmt.Print(c.NewFormatter(a), c.NewFormatter(b))
+func (c *ConfigState) Print(a ...interface{}) (n int, err error) {
+ return fmt.Print(c.convertArgs(a)...)
+}
+
+// Printf is a wrapper for fmt.Printf that treats each argument as if it were
+// passed with a Formatter interface returned by c.NewFormatter. It returns
+// the number of bytes written and any write error encountered. See
+// NewFormatter for formatting details.
+//
+// This function is shorthand for the following syntax:
+//
+// fmt.Printf(format, c.NewFormatter(a), c.NewFormatter(b))
+func (c *ConfigState) Printf(format string, a ...interface{}) (n int, err error) {
+ return fmt.Printf(format, c.convertArgs(a)...)
+}
+
+// Println is a wrapper for fmt.Println that treats each argument as if it were
+// passed with a Formatter interface returned by c.NewFormatter. It returns
+// the number of bytes written and any write error encountered. See
+// NewFormatter for formatting details.
+//
+// This function is shorthand for the following syntax:
+//
+// fmt.Println(c.NewFormatter(a), c.NewFormatter(b))
+func (c *ConfigState) Println(a ...interface{}) (n int, err error) {
+ return fmt.Println(c.convertArgs(a)...)
+}
+
+// Sprint is a wrapper for fmt.Sprint that treats each argument as if it were
+// passed with a Formatter interface returned by c.NewFormatter. It returns
+// the resulting string. See NewFormatter for formatting details.
+//
+// This function is shorthand for the following syntax:
+//
+// fmt.Sprint(c.NewFormatter(a), c.NewFormatter(b))
+func (c *ConfigState) Sprint(a ...interface{}) string {
+ return fmt.Sprint(c.convertArgs(a)...)
+}
+
+// Sprintf is a wrapper for fmt.Sprintf that treats each argument as if it were
+// passed with a Formatter interface returned by c.NewFormatter. It returns
+// the resulting string. See NewFormatter for formatting details.
+//
+// This function is shorthand for the following syntax:
+//
+// fmt.Sprintf(format, c.NewFormatter(a), c.NewFormatter(b))
+func (c *ConfigState) Sprintf(format string, a ...interface{}) string {
+ return fmt.Sprintf(format, c.convertArgs(a)...)
+}
+
+// Sprintln is a wrapper for fmt.Sprintln that treats each argument as if it
+// were passed with a Formatter interface returned by c.NewFormatter. It
+// returns the resulting string. See NewFormatter for formatting details.
+//
+// This function is shorthand for the following syntax:
+//
+// fmt.Sprintln(c.NewFormatter(a), c.NewFormatter(b))
+func (c *ConfigState) Sprintln(a ...interface{}) string {
+ return fmt.Sprintln(c.convertArgs(a)...)
+}
+
+/*
+NewFormatter returns a custom formatter that satisfies the fmt.Formatter
+interface. As a result, it integrates cleanly with standard fmt package
+printing functions. The formatter is useful for inline printing of smaller data
+types similar to the standard %v format specifier.
+
+The custom formatter only responds to the %v (most compact), %+v (adds pointer
+addresses), %#v (adds types), and %#+v (adds types and pointer addresses) verb
+combinations. Any other verbs such as %x and %q will be sent to the the
+standard fmt package for formatting. In addition, the custom formatter ignores
+the width and precision arguments (however they will still work on the format
+specifiers not handled by the custom formatter).
+
+Typically this function shouldn't be called directly. It is much easier to make
+use of the custom formatter by calling one of the convenience functions such as
+c.Printf, c.Println, or c.Printf.
+*/
+func (c *ConfigState) NewFormatter(v interface{}) fmt.Formatter {
+ return newFormatter(c, v)
+}
+
+// Fdump formats and displays the passed arguments to io.Writer w. It formats
+// exactly the same as Dump.
+func (c *ConfigState) Fdump(w io.Writer, a ...interface{}) {
+ fdump(c, w, a...)
+}
+
+/*
+Dump displays the passed parameters to standard out with newlines, customizable
+indentation, and additional debug information such as complete types and all
+pointer addresses used to indirect to the final value. It provides the
+following features over the built-in printing facilities provided by the fmt
+package:
+
+ * Pointers are dereferenced and followed
+ * Circular data structures are detected and handled properly
+ * Custom Stringer/error interfaces are optionally invoked, including
+ on unexported types
+ * Custom types which only implement the Stringer/error interfaces via
+ a pointer receiver are optionally invoked when passing non-pointer
+ variables
+ * Byte arrays and slices are dumped like the hexdump -C command which
+ includes offsets, byte values in hex, and ASCII output
+
+The configuration options are controlled by modifying the public members
+of c. See ConfigState for options documentation.
+
+See Fdump if you would prefer dumping to an arbitrary io.Writer or Sdump to
+get the formatted result as a string.
+*/
+func (c *ConfigState) Dump(a ...interface{}) {
+ fdump(c, os.Stdout, a...)
+}
+
+// Sdump returns a string with the passed arguments formatted exactly the same
+// as Dump.
+func (c *ConfigState) Sdump(a ...interface{}) string {
+ var buf bytes.Buffer
+ fdump(c, &buf, a...)
+ return buf.String()
+}
+
+// convertArgs accepts a slice of arguments and returns a slice of the same
+// length with each argument converted to a spew Formatter interface using
+// the ConfigState associated with s.
+func (c *ConfigState) convertArgs(args []interface{}) (formatters []interface{}) {
+ formatters = make([]interface{}, len(args))
+ for index, arg := range args {
+ formatters[index] = newFormatter(c, arg)
+ }
+ return formatters
+}
+
+// NewDefaultConfig returns a ConfigState with the following default settings.
+//
+// Indent: " "
+// MaxDepth: 0
+// DisableMethods: false
+// DisablePointerMethods: false
+// ContinueOnMethod: false
+// SortKeys: false
+func NewDefaultConfig() *ConfigState {
+ return &ConfigState{Indent: " "}
+}
diff --git a/vendor/github.com/davecgh/go-spew/spew/doc.go b/vendor/github.com/davecgh/go-spew/spew/doc.go
new file mode 100644
index 000000000..5be0c4060
--- /dev/null
+++ b/vendor/github.com/davecgh/go-spew/spew/doc.go
@@ -0,0 +1,202 @@
+/*
+ * Copyright (c) 2013 Dave Collins <dave@davec.name>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+Package spew implements a deep pretty printer for Go data structures to aid in
+debugging.
+
+A quick overview of the additional features spew provides over the built-in
+printing facilities for Go data types are as follows:
+
+ * Pointers are dereferenced and followed
+ * Circular data structures are detected and handled properly
+ * Custom Stringer/error interfaces are optionally invoked, including
+ on unexported types
+ * Custom types which only implement the Stringer/error interfaces via
+ a pointer receiver are optionally invoked when passing non-pointer
+ variables
+ * Byte arrays and slices are dumped like the hexdump -C command which
+ includes offsets, byte values in hex, and ASCII output (only when using
+ Dump style)
+
+There are two different approaches spew allows for dumping Go data structures:
+
+ * Dump style which prints with newlines, customizable indentation,
+ and additional debug information such as types and all pointer addresses
+ used to indirect to the final value
+ * A custom Formatter interface that integrates cleanly with the standard fmt
+ package and replaces %v, %+v, %#v, and %#+v to provide inline printing
+ similar to the default %v while providing the additional functionality
+ outlined above and passing unsupported format verbs such as %x and %q
+ along to fmt
+
+Quick Start
+
+This section demonstrates how to quickly get started with spew. See the
+sections below for further details on formatting and configuration options.
+
+To dump a variable with full newlines, indentation, type, and pointer
+information use Dump, Fdump, or Sdump:
+ spew.Dump(myVar1, myVar2, ...)
+ spew.Fdump(someWriter, myVar1, myVar2, ...)
+ str := spew.Sdump(myVar1, myVar2, ...)
+
+Alternatively, if you would prefer to use format strings with a compacted inline
+printing style, use the convenience wrappers Printf, Fprintf, etc with
+%v (most compact), %+v (adds pointer addresses), %#v (adds types), or
+%#+v (adds types and pointer addresses):
+ spew.Printf("myVar1: %v -- myVar2: %+v", myVar1, myVar2)
+ spew.Printf("myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
+ spew.Fprintf(someWriter, "myVar1: %v -- myVar2: %+v", myVar1, myVar2)
+ spew.Fprintf(someWriter, "myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
+
+Configuration Options
+
+Configuration of spew is handled by fields in the ConfigState type. For
+convenience, all of the top-level functions use a global state available
+via the spew.Config global.
+
+It is also possible to create a ConfigState instance that provides methods
+equivalent to the top-level functions. This allows concurrent configuration
+options. See the ConfigState documentation for more details.
+
+The following configuration options are available:
+ * Indent
+ String to use for each indentation level for Dump functions.
+ It is a single space by default. A popular alternative is "\t".
+
+ * MaxDepth
+ Maximum number of levels to descend into nested data structures.
+ There is no limit by default.
+
+ * DisableMethods
+ Disables invocation of error and Stringer interface methods.
+ Method invocation is enabled by default.
+
+ * DisablePointerMethods
+ Disables invocation of error and Stringer interface methods on types
+ which only accept pointer receivers from non-pointer variables.
+ Pointer method invocation is enabled by default.
+
+ * ContinueOnMethod
+ Enables recursion into types after invoking error and Stringer interface
+ methods. Recursion after method invocation is disabled by default.
+
+ * SortKeys
+ Specifies map keys should be sorted before being printed. Use
+ this to have a more deterministic, diffable output. Note that
+ only native types (bool, int, uint, floats, uintptr and string)
+ and types which implement error or Stringer interfaces are
+ supported with other types sorted according to the
+ reflect.Value.String() output which guarantees display
+ stability. Natural map order is used by default.
+
+ * SpewKeys
+ Specifies that, as a last resort attempt, map keys should be
+ spewed to strings and sorted by those strings. This is only
+ considered if SortKeys is true.
+
+Dump Usage
+
+Simply call spew.Dump with a list of variables you want to dump:
+
+ spew.Dump(myVar1, myVar2, ...)
+
+You may also call spew.Fdump if you would prefer to output to an arbitrary
+io.Writer. For example, to dump to standard error:
+
+ spew.Fdump(os.Stderr, myVar1, myVar2, ...)
+
+A third option is to call spew.Sdump to get the formatted output as a string:
+
+ str := spew.Sdump(myVar1, myVar2, ...)
+
+Sample Dump Output
+
+See the Dump example for details on the setup of the types and variables being
+shown here.
+
+ (main.Foo) {
+ unexportedField: (*main.Bar)(0xf84002e210)({
+ flag: (main.Flag) flagTwo,
+ data: (uintptr) <nil>
+ }),
+ ExportedField: (map[interface {}]interface {}) (len=1) {
+ (string) (len=3) "one": (bool) true
+ }
+ }
+
+Byte (and uint8) arrays and slices are displayed uniquely like the hexdump -C
+command as shown.
+ ([]uint8) (len=32 cap=32) {
+ 00000000 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 |............... |
+ 00000010 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 |!"#$%&'()*+,-./0|
+ 00000020 31 32 |12|
+ }
+
+Custom Formatter
+
+Spew provides a custom formatter that implements the fmt.Formatter interface
+so that it integrates cleanly with standard fmt package printing functions. The
+formatter is useful for inline printing of smaller data types similar to the
+standard %v format specifier.
+
+The custom formatter only responds to the %v (most compact), %+v (adds pointer
+addresses), %#v (adds types), or %#+v (adds types and pointer addresses) verb
+combinations. Any other verbs such as %x and %q will be sent to the the
+standard fmt package for formatting. In addition, the custom formatter ignores
+the width and precision arguments (however they will still work on the format
+specifiers not handled by the custom formatter).
+
+Custom Formatter Usage
+
+The simplest way to make use of the spew custom formatter is to call one of the
+convenience functions such as spew.Printf, spew.Println, or spew.Printf. The
+functions have syntax you are most likely already familiar with:
+
+ spew.Printf("myVar1: %v -- myVar2: %+v", myVar1, myVar2)
+ spew.Printf("myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
+ spew.Println(myVar, myVar2)
+ spew.Fprintf(os.Stderr, "myVar1: %v -- myVar2: %+v", myVar1, myVar2)
+ spew.Fprintf(os.Stderr, "myVar3: %#v -- myVar4: %#+v", myVar3, myVar4)
+
+See the Index for the full list convenience functions.
+
+Sample Formatter Output
+
+Double pointer to a uint8:
+ %v: <**>5
+ %+v: <**>(0xf8400420d0->0xf8400420c8)5
+ %#v: (**uint8)5
+ %#+v: (**uint8)(0xf8400420d0->0xf8400420c8)5
+
+Pointer to circular struct with a uint8 field and a pointer to itself:
+ %v: <*>{1 <*><shown>}
+ %+v: <*>(0xf84003e260){ui8:1 c:<*>(0xf84003e260)<shown>}
+ %#v: (*main.circular){ui8:(uint8)1 c:(*main.circular)<shown>}
+ %#+v: (*main.circular)(0xf84003e260){ui8:(uint8)1 c:(*main.circular)(0xf84003e260)<shown>}
+
+See the Printf example for details on the setup of variables being shown
+here.
+
+Errors
+
+Since it is possible for custom Stringer/error interfaces to panic, spew
+detects them and handles them internally by printing the panic information
+inline with the output. Since spew is intended to provide deep pretty printing
+capabilities on structures, it intentionally does not return any errors.
+*/
+package spew
diff --git a/vendor/github.com/davecgh/go-spew/spew/dump.go b/vendor/github.com/davecgh/go-spew/spew/dump.go
new file mode 100644
index 000000000..a0ff95e27
--- /dev/null
+++ b/vendor/github.com/davecgh/go-spew/spew/dump.go
@@ -0,0 +1,509 @@
+/*
+ * Copyright (c) 2013 Dave Collins <dave@davec.name>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+package spew
+
+import (
+ "bytes"
+ "encoding/hex"
+ "fmt"
+ "io"
+ "os"
+ "reflect"
+ "regexp"
+ "strconv"
+ "strings"
+)
+
+var (
+ // uint8Type is a reflect.Type representing a uint8. It is used to
+ // convert cgo types to uint8 slices for hexdumping.
+ uint8Type = reflect.TypeOf(uint8(0))
+
+ // cCharRE is a regular expression that matches a cgo char.
+ // It is used to detect character arrays to hexdump them.
+ cCharRE = regexp.MustCompile("^.*\\._Ctype_char$")
+
+ // cUnsignedCharRE is a regular expression that matches a cgo unsigned
+ // char. It is used to detect unsigned character arrays to hexdump
+ // them.
+ cUnsignedCharRE = regexp.MustCompile("^.*\\._Ctype_unsignedchar$")
+
+ // cUint8tCharRE is a regular expression that matches a cgo uint8_t.
+ // It is used to detect uint8_t arrays to hexdump them.
+ cUint8tCharRE = regexp.MustCompile("^.*\\._Ctype_uint8_t$")
+)
+
+// dumpState contains information about the state of a dump operation.
+type dumpState struct {
+ w io.Writer
+ depth int
+ pointers map[uintptr]int
+ ignoreNextType bool
+ ignoreNextIndent bool
+ cs *ConfigState
+}
+
+// indent performs indentation according to the depth level and cs.Indent
+// option.
+func (d *dumpState) indent() {
+ if d.ignoreNextIndent {
+ d.ignoreNextIndent = false
+ return
+ }
+ d.w.Write(bytes.Repeat([]byte(d.cs.Indent), d.depth))
+}
+
+// unpackValue returns values inside of non-nil interfaces when possible.
+// This is useful for data types like structs, arrays, slices, and maps which
+// can contain varying types packed inside an interface.
+func (d *dumpState) unpackValue(v reflect.Value) reflect.Value {
+ if v.Kind() == reflect.Interface && !v.IsNil() {
+ v = v.Elem()
+ }
+ return v
+}
+
+// dumpPtr handles formatting of pointers by indirecting them as necessary.
+func (d *dumpState) dumpPtr(v reflect.Value) {
+ // Remove pointers at or below the current depth from map used to detect
+ // circular refs.
+ for k, depth := range d.pointers {
+ if depth >= d.depth {
+ delete(d.pointers, k)
+ }
+ }
+
+ // Keep list of all dereferenced pointers to show later.
+ pointerChain := make([]uintptr, 0)
+
+ // Figure out how many levels of indirection there are by dereferencing
+ // pointers and unpacking interfaces down the chain while detecting circular
+ // references.
+ nilFound := false
+ cycleFound := false
+ indirects := 0
+ ve := v
+ for ve.Kind() == reflect.Ptr {
+ if ve.IsNil() {
+ nilFound = true
+ break
+ }
+ indirects++
+ addr := ve.Pointer()
+ pointerChain = append(pointerChain, addr)
+ if pd, ok := d.pointers[addr]; ok && pd < d.depth {
+ cycleFound = true
+ indirects--
+ break
+ }
+ d.pointers[addr] = d.depth
+
+ ve = ve.Elem()
+ if ve.Kind() == reflect.Interface {
+ if ve.IsNil() {
+ nilFound = true
+ break
+ }
+ ve = ve.Elem()
+ }
+ }
+
+ // Display type information.
+ d.w.Write(openParenBytes)
+ d.w.Write(bytes.Repeat(asteriskBytes, indirects))
+ d.w.Write([]byte(ve.Type().String()))
+ d.w.Write(closeParenBytes)
+
+ // Display pointer information.
+ if len(pointerChain) > 0 {
+ d.w.Write(openParenBytes)
+ for i, addr := range pointerChain {
+ if i > 0 {
+ d.w.Write(pointerChainBytes)
+ }
+ printHexPtr(d.w, addr)
+ }
+ d.w.Write(closeParenBytes)
+ }
+
+ // Display dereferenced value.
+ d.w.Write(openParenBytes)
+ switch {
+ case nilFound == true:
+ d.w.Write(nilAngleBytes)
+
+ case cycleFound == true:
+ d.w.Write(circularBytes)
+
+ default:
+ d.ignoreNextType = true
+ d.dump(ve)
+ }
+ d.w.Write(closeParenBytes)
+}
+
+// dumpSlice handles formatting of arrays and slices. Byte (uint8 under
+// reflection) arrays and slices are dumped in hexdump -C fashion.
+func (d *dumpState) dumpSlice(v reflect.Value) {
+ // Determine whether this type should be hex dumped or not. Also,
+ // for types which should be hexdumped, try to use the underlying data
+ // first, then fall back to trying to convert them to a uint8 slice.
+ var buf []uint8
+ doConvert := false
+ doHexDump := false
+ numEntries := v.Len()
+ if numEntries > 0 {
+ vt := v.Index(0).Type()
+ vts := vt.String()
+ switch {
+ // C types that need to be converted.
+ case cCharRE.MatchString(vts):
+ fallthrough
+ case cUnsignedCharRE.MatchString(vts):
+ fallthrough
+ case cUint8tCharRE.MatchString(vts):
+ doConvert = true
+
+ // Try to use existing uint8 slices and fall back to converting
+ // and copying if that fails.
+ case vt.Kind() == reflect.Uint8:
+ // We need an addressable interface to convert the type
+ // to a byte slice. However, the reflect package won't
+ // give us an interface on certain things like
+ // unexported struct fields in order to enforce
+ // visibility rules. We use unsafe, when available, to
+ // bypass these restrictions since this package does not
+ // mutate the values.
+ vs := v
+ if !vs.CanInterface() || !vs.CanAddr() {
+ vs = unsafeReflectValue(vs)
+ }
+ if !UnsafeDisabled {
+ vs = vs.Slice(0, numEntries)
+
+ // Use the existing uint8 slice if it can be
+ // type asserted.
+ iface := vs.Interface()
+ if slice, ok := iface.([]uint8); ok {
+ buf = slice
+ doHexDump = true
+ break
+ }
+ }
+
+ // The underlying data needs to be converted if it can't
+ // be type asserted to a uint8 slice.
+ doConvert = true
+ }
+
+ // Copy and convert the underlying type if needed.
+ if doConvert && vt.ConvertibleTo(uint8Type) {
+ // Convert and copy each element into a uint8 byte
+ // slice.
+ buf = make([]uint8, numEntries)
+ for i := 0; i < numEntries; i++ {
+ vv := v.Index(i)
+ buf[i] = uint8(vv.Convert(uint8Type).Uint())
+ }
+ doHexDump = true
+ }
+ }
+
+ // Hexdump the entire slice as needed.
+ if doHexDump {
+ indent := strings.Repeat(d.cs.Indent, d.depth)
+ str := indent + hex.Dump(buf)
+ str = strings.Replace(str, "\n", "\n"+indent, -1)
+ str = strings.TrimRight(str, d.cs.Indent)
+ d.w.Write([]byte(str))
+ return
+ }
+
+ // Recursively call dump for each item.
+ for i := 0; i < numEntries; i++ {
+ d.dump(d.unpackValue(v.Index(i)))
+ if i < (numEntries - 1) {
+ d.w.Write(commaNewlineBytes)
+ } else {
+ d.w.Write(newlineBytes)
+ }
+ }
+}
+
+// dump is the main workhorse for dumping a value. It uses the passed reflect
+// value to figure out what kind of object we are dealing with and formats it
+// appropriately. It is a recursive function, however circular data structures
+// are detected and handled properly.
+func (d *dumpState) dump(v reflect.Value) {
+ // Handle invalid reflect values immediately.
+ kind := v.Kind()
+ if kind == reflect.Invalid {
+ d.w.Write(invalidAngleBytes)
+ return
+ }
+
+ // Handle pointers specially.
+ if kind == reflect.Ptr {
+ d.indent()
+ d.dumpPtr(v)
+ return
+ }
+
+ // Print type information unless already handled elsewhere.
+ if !d.ignoreNextType {
+ d.indent()
+ d.w.Write(openParenBytes)
+ d.w.Write([]byte(v.Type().String()))
+ d.w.Write(closeParenBytes)
+ d.w.Write(spaceBytes)
+ }
+ d.ignoreNextType = false
+
+ // Display length and capacity if the built-in len and cap functions
+ // work with the value's kind and the len/cap itself is non-zero.
+ valueLen, valueCap := 0, 0
+ switch v.Kind() {
+ case reflect.Array, reflect.Slice, reflect.Chan:
+ valueLen, valueCap = v.Len(), v.Cap()
+ case reflect.Map, reflect.String:
+ valueLen = v.Len()
+ }
+ if valueLen != 0 || valueCap != 0 {
+ d.w.Write(openParenBytes)
+ if valueLen != 0 {
+ d.w.Write(lenEqualsBytes)
+ printInt(d.w, int64(valueLen), 10)
+ }
+ if valueCap != 0 {
+ if valueLen != 0 {
+ d.w.Write(spaceBytes)
+ }
+ d.w.Write(capEqualsBytes)
+ printInt(d.w, int64(valueCap), 10)
+ }
+ d.w.Write(closeParenBytes)
+ d.w.Write(spaceBytes)
+ }
+
+ // Call Stringer/error interfaces if they exist and the handle methods flag
+ // is enabled
+ if !d.cs.DisableMethods {
+ if (kind != reflect.Invalid) && (kind != reflect.Interface) {
+ if handled := handleMethods(d.cs, d.w, v); handled {
+ return
+ }
+ }
+ }
+
+ switch kind {
+ case reflect.Invalid:
+ // Do nothing. We should never get here since invalid has already
+ // been handled above.
+
+ case reflect.Bool:
+ printBool(d.w, v.Bool())
+
+ case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
+ printInt(d.w, v.Int(), 10)
+
+ case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
+ printUint(d.w, v.Uint(), 10)
+
+ case reflect.Float32:
+ printFloat(d.w, v.Float(), 32)
+
+ case reflect.Float64:
+ printFloat(d.w, v.Float(), 64)
+
+ case reflect.Complex64:
+ printComplex(d.w, v.Complex(), 32)
+
+ case reflect.Complex128:
+ printComplex(d.w, v.Complex(), 64)
+
+ case reflect.Slice:
+ if v.IsNil() {
+ d.w.Write(nilAngleBytes)
+ break
+ }
+ fallthrough
+
+ case reflect.Array:
+ d.w.Write(openBraceNewlineBytes)
+ d.depth++
+ if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) {
+ d.indent()
+ d.w.Write(maxNewlineBytes)
+ } else {
+ d.dumpSlice(v)
+ }
+ d.depth--
+ d.indent()
+ d.w.Write(closeBraceBytes)
+
+ case reflect.String:
+ d.w.Write([]byte(strconv.Quote(v.String())))
+
+ case reflect.Interface:
+ // The only time we should get here is for nil interfaces due to
+ // unpackValue calls.
+ if v.IsNil() {
+ d.w.Write(nilAngleBytes)
+ }
+
+ case reflect.Ptr:
+ // Do nothing. We should never get here since pointers have already
+ // been handled above.
+
+ case reflect.Map:
+ // nil maps should be indicated as different than empty maps
+ if v.IsNil() {
+ d.w.Write(nilAngleBytes)
+ break
+ }
+
+ d.w.Write(openBraceNewlineBytes)
+ d.depth++
+ if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) {
+ d.indent()
+ d.w.Write(maxNewlineBytes)
+ } else {
+ numEntries := v.Len()
+ keys := v.MapKeys()
+ if d.cs.SortKeys {
+ sortValues(keys, d.cs)
+ }
+ for i, key := range keys {
+ d.dump(d.unpackValue(key))
+ d.w.Write(colonSpaceBytes)
+ d.ignoreNextIndent = true
+ d.dump(d.unpackValue(v.MapIndex(key)))
+ if i < (numEntries - 1) {
+ d.w.Write(commaNewlineBytes)
+ } else {
+ d.w.Write(newlineBytes)
+ }
+ }
+ }
+ d.depth--
+ d.indent()
+ d.w.Write(closeBraceBytes)
+
+ case reflect.Struct:
+ d.w.Write(openBraceNewlineBytes)
+ d.depth++
+ if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) {
+ d.indent()
+ d.w.Write(maxNewlineBytes)
+ } else {
+ vt := v.Type()
+ numFields := v.NumField()
+ for i := 0; i < numFields; i++ {
+ d.indent()
+ vtf := vt.Field(i)
+ d.w.Write([]byte(vtf.Name))
+ d.w.Write(colonSpaceBytes)
+ d.ignoreNextIndent = true
+ d.dump(d.unpackValue(v.Field(i)))
+ if i < (numFields - 1) {
+ d.w.Write(commaNewlineBytes)
+ } else {
+ d.w.Write(newlineBytes)
+ }
+ }
+ }
+ d.depth--
+ d.indent()
+ d.w.Write(closeBraceBytes)
+
+ case reflect.Uintptr:
+ printHexPtr(d.w, uintptr(v.Uint()))
+
+ case reflect.UnsafePointer, reflect.Chan, reflect.Func:
+ printHexPtr(d.w, v.Pointer())
+
+ // There were not any other types at the time this code was written, but
+ // fall back to letting the default fmt package handle it in case any new
+ // types are added.
+ default:
+ if v.CanInterface() {
+ fmt.Fprintf(d.w, "%v", v.Interface())
+ } else {
+ fmt.Fprintf(d.w, "%v", v.String())
+ }
+ }
+}
+
+// fdump is a helper function to consolidate the logic from the various public
+// methods which take varying writers and config states.
+func fdump(cs *ConfigState, w io.Writer, a ...interface{}) {
+ for _, arg := range a {
+ if arg == nil {
+ w.Write(interfaceBytes)
+ w.Write(spaceBytes)
+ w.Write(nilAngleBytes)
+ w.Write(newlineBytes)
+ continue
+ }
+
+ d := dumpState{w: w, cs: cs}
+ d.pointers = make(map[uintptr]int)
+ d.dump(reflect.ValueOf(arg))
+ d.w.Write(newlineBytes)
+ }
+}
+
+// Fdump formats and displays the passed arguments to io.Writer w. It formats
+// exactly the same as Dump.
+func Fdump(w io.Writer, a ...interface{}) {
+ fdump(&Config, w, a...)
+}
+
+// Sdump returns a string with the passed arguments formatted exactly the same
+// as Dump.
+func Sdump(a ...interface{}) string {
+ var buf bytes.Buffer
+ fdump(&Config, &buf, a...)
+ return buf.String()
+}
+
+/*
+Dump displays the passed parameters to standard out with newlines, customizable
+indentation, and additional debug information such as complete types and all
+pointer addresses used to indirect to the final value. It provides the
+following features over the built-in printing facilities provided by the fmt
+package:
+
+ * Pointers are dereferenced and followed
+ * Circular data structures are detected and handled properly
+ * Custom Stringer/error interfaces are optionally invoked, including
+ on unexported types
+ * Custom types which only implement the Stringer/error interfaces via
+ a pointer receiver are optionally invoked when passing non-pointer
+ variables
+ * Byte arrays and slices are dumped like the hexdump -C command which
+ includes offsets, byte values in hex, and ASCII output
+
+The configuration options are controlled by an exported package global,
+spew.Config. See ConfigState for options documentation.
+
+See Fdump if you would prefer dumping to an arbitrary io.Writer or Sdump to
+get the formatted result as a string.
+*/
+func Dump(a ...interface{}) {
+ fdump(&Config, os.Stdout, a...)
+}
diff --git a/vendor/github.com/davecgh/go-spew/spew/dump_test.go b/vendor/github.com/davecgh/go-spew/spew/dump_test.go
new file mode 100644
index 000000000..2b320401d
--- /dev/null
+++ b/vendor/github.com/davecgh/go-spew/spew/dump_test.go
@@ -0,0 +1,1042 @@
+/*
+ * Copyright (c) 2013 Dave Collins <dave@davec.name>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+Test Summary:
+NOTE: For each test, a nil pointer, a single pointer and double pointer to the
+base test element are also tested to ensure proper indirection across all types.
+
+- Max int8, int16, int32, int64, int
+- Max uint8, uint16, uint32, uint64, uint
+- Boolean true and false
+- Standard complex64 and complex128
+- Array containing standard ints
+- Array containing type with custom formatter on pointer receiver only
+- Array containing interfaces
+- Array containing bytes
+- Slice containing standard float32 values
+- Slice containing type with custom formatter on pointer receiver only
+- Slice containing interfaces
+- Slice containing bytes
+- Nil slice
+- Standard string
+- Nil interface
+- Sub-interface
+- Map with string keys and int vals
+- Map with custom formatter type on pointer receiver only keys and vals
+- Map with interface keys and values
+- Map with nil interface value
+- Struct with primitives
+- Struct that contains another struct
+- Struct that contains custom type with Stringer pointer interface via both
+ exported and unexported fields
+- Struct that contains embedded struct and field to same struct
+- Uintptr to 0 (null pointer)
+- Uintptr address of real variable
+- Unsafe.Pointer to 0 (null pointer)
+- Unsafe.Pointer to address of real variable
+- Nil channel
+- Standard int channel
+- Function with no params and no returns
+- Function with param and no returns
+- Function with multiple params and multiple returns
+- Struct that is circular through self referencing
+- Structs that are circular through cross referencing
+- Structs that are indirectly circular
+- Type that panics in its Stringer interface
+*/
+
+package spew_test
+
+import (
+ "bytes"
+ "fmt"
+ "testing"
+ "unsafe"
+
+ "github.com/davecgh/go-spew/spew"
+)
+
+// dumpTest is used to describe a test to be perfomed against the Dump method.
+type dumpTest struct {
+ in interface{}
+ wants []string
+}
+
+// dumpTests houses all of the tests to be performed against the Dump method.
+var dumpTests = make([]dumpTest, 0)
+
+// addDumpTest is a helper method to append the passed input and desired result
+// to dumpTests
+func addDumpTest(in interface{}, wants ...string) {
+ test := dumpTest{in, wants}
+ dumpTests = append(dumpTests, test)
+}
+
+func addIntDumpTests() {
+ // Max int8.
+ v := int8(127)
+ nv := (*int8)(nil)
+ pv := &v
+ vAddr := fmt.Sprintf("%p", pv)
+ pvAddr := fmt.Sprintf("%p", &pv)
+ vt := "int8"
+ vs := "127"
+ addDumpTest(v, "("+vt+") "+vs+"\n")
+ addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs+")\n")
+ addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n")
+ addDumpTest(nv, "(*"+vt+")(<nil>)\n")
+
+ // Max int16.
+ v2 := int16(32767)
+ nv2 := (*int16)(nil)
+ pv2 := &v2
+ v2Addr := fmt.Sprintf("%p", pv2)
+ pv2Addr := fmt.Sprintf("%p", &pv2)
+ v2t := "int16"
+ v2s := "32767"
+ addDumpTest(v2, "("+v2t+") "+v2s+"\n")
+ addDumpTest(pv2, "(*"+v2t+")("+v2Addr+")("+v2s+")\n")
+ addDumpTest(&pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n")
+ addDumpTest(nv2, "(*"+v2t+")(<nil>)\n")
+
+ // Max int32.
+ v3 := int32(2147483647)
+ nv3 := (*int32)(nil)
+ pv3 := &v3
+ v3Addr := fmt.Sprintf("%p", pv3)
+ pv3Addr := fmt.Sprintf("%p", &pv3)
+ v3t := "int32"
+ v3s := "2147483647"
+ addDumpTest(v3, "("+v3t+") "+v3s+"\n")
+ addDumpTest(pv3, "(*"+v3t+")("+v3Addr+")("+v3s+")\n")
+ addDumpTest(&pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")("+v3s+")\n")
+ addDumpTest(nv3, "(*"+v3t+")(<nil>)\n")
+
+ // Max int64.
+ v4 := int64(9223372036854775807)
+ nv4 := (*int64)(nil)
+ pv4 := &v4
+ v4Addr := fmt.Sprintf("%p", pv4)
+ pv4Addr := fmt.Sprintf("%p", &pv4)
+ v4t := "int64"
+ v4s := "9223372036854775807"
+ addDumpTest(v4, "("+v4t+") "+v4s+"\n")
+ addDumpTest(pv4, "(*"+v4t+")("+v4Addr+")("+v4s+")\n")
+ addDumpTest(&pv4, "(**"+v4t+")("+pv4Addr+"->"+v4Addr+")("+v4s+")\n")
+ addDumpTest(nv4, "(*"+v4t+")(<nil>)\n")
+
+ // Max int.
+ v5 := int(2147483647)
+ nv5 := (*int)(nil)
+ pv5 := &v5
+ v5Addr := fmt.Sprintf("%p", pv5)
+ pv5Addr := fmt.Sprintf("%p", &pv5)
+ v5t := "int"
+ v5s := "2147483647"
+ addDumpTest(v5, "("+v5t+") "+v5s+"\n")
+ addDumpTest(pv5, "(*"+v5t+")("+v5Addr+")("+v5s+")\n")
+ addDumpTest(&pv5, "(**"+v5t+")("+pv5Addr+"->"+v5Addr+")("+v5s+")\n")
+ addDumpTest(nv5, "(*"+v5t+")(<nil>)\n")
+}
+
+func addUintDumpTests() {
+ // Max uint8.
+ v := uint8(255)
+ nv := (*uint8)(nil)
+ pv := &v
+ vAddr := fmt.Sprintf("%p", pv)
+ pvAddr := fmt.Sprintf("%p", &pv)
+ vt := "uint8"
+ vs := "255"
+ addDumpTest(v, "("+vt+") "+vs+"\n")
+ addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs+")\n")
+ addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n")
+ addDumpTest(nv, "(*"+vt+")(<nil>)\n")
+
+ // Max uint16.
+ v2 := uint16(65535)
+ nv2 := (*uint16)(nil)
+ pv2 := &v2
+ v2Addr := fmt.Sprintf("%p", pv2)
+ pv2Addr := fmt.Sprintf("%p", &pv2)
+ v2t := "uint16"
+ v2s := "65535"
+ addDumpTest(v2, "("+v2t+") "+v2s+"\n")
+ addDumpTest(pv2, "(*"+v2t+")("+v2Addr+")("+v2s+")\n")
+ addDumpTest(&pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n")
+ addDumpTest(nv2, "(*"+v2t+")(<nil>)\n")
+
+ // Max uint32.
+ v3 := uint32(4294967295)
+ nv3 := (*uint32)(nil)
+ pv3 := &v3
+ v3Addr := fmt.Sprintf("%p", pv3)
+ pv3Addr := fmt.Sprintf("%p", &pv3)
+ v3t := "uint32"
+ v3s := "4294967295"
+ addDumpTest(v3, "("+v3t+") "+v3s+"\n")
+ addDumpTest(pv3, "(*"+v3t+")("+v3Addr+")("+v3s+")\n")
+ addDumpTest(&pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")("+v3s+")\n")
+ addDumpTest(nv3, "(*"+v3t+")(<nil>)\n")
+
+ // Max uint64.
+ v4 := uint64(18446744073709551615)
+ nv4 := (*uint64)(nil)
+ pv4 := &v4
+ v4Addr := fmt.Sprintf("%p", pv4)
+ pv4Addr := fmt.Sprintf("%p", &pv4)
+ v4t := "uint64"
+ v4s := "18446744073709551615"
+ addDumpTest(v4, "("+v4t+") "+v4s+"\n")
+ addDumpTest(pv4, "(*"+v4t+")("+v4Addr+")("+v4s+")\n")
+ addDumpTest(&pv4, "(**"+v4t+")("+pv4Addr+"->"+v4Addr+")("+v4s+")\n")
+ addDumpTest(nv4, "(*"+v4t+")(<nil>)\n")
+
+ // Max uint.
+ v5 := uint(4294967295)
+ nv5 := (*uint)(nil)
+ pv5 := &v5
+ v5Addr := fmt.Sprintf("%p", pv5)
+ pv5Addr := fmt.Sprintf("%p", &pv5)
+ v5t := "uint"
+ v5s := "4294967295"
+ addDumpTest(v5, "("+v5t+") "+v5s+"\n")
+ addDumpTest(pv5, "(*"+v5t+")("+v5Addr+")("+v5s+")\n")
+ addDumpTest(&pv5, "(**"+v5t+")("+pv5Addr+"->"+v5Addr+")("+v5s+")\n")
+ addDumpTest(nv5, "(*"+v5t+")(<nil>)\n")
+}
+
+func addBoolDumpTests() {
+ // Boolean true.
+ v := bool(true)
+ nv := (*bool)(nil)
+ pv := &v
+ vAddr := fmt.Sprintf("%p", pv)
+ pvAddr := fmt.Sprintf("%p", &pv)
+ vt := "bool"
+ vs := "true"
+ addDumpTest(v, "("+vt+") "+vs+"\n")
+ addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs+")\n")
+ addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n")
+ addDumpTest(nv, "(*"+vt+")(<nil>)\n")
+
+ // Boolean false.
+ v2 := bool(false)
+ pv2 := &v2
+ v2Addr := fmt.Sprintf("%p", pv2)
+ pv2Addr := fmt.Sprintf("%p", &pv2)
+ v2t := "bool"
+ v2s := "false"
+ addDumpTest(v2, "("+v2t+") "+v2s+"\n")
+ addDumpTest(pv2, "(*"+v2t+")("+v2Addr+")("+v2s+")\n")
+ addDumpTest(&pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n")
+}
+
+func addFloatDumpTests() {
+ // Standard float32.
+ v := float32(3.1415)
+ nv := (*float32)(nil)
+ pv := &v
+ vAddr := fmt.Sprintf("%p", pv)
+ pvAddr := fmt.Sprintf("%p", &pv)
+ vt := "float32"
+ vs := "3.1415"
+ addDumpTest(v, "("+vt+") "+vs+"\n")
+ addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs+")\n")
+ addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n")
+ addDumpTest(nv, "(*"+vt+")(<nil>)\n")
+
+ // Standard float64.
+ v2 := float64(3.1415926)
+ nv2 := (*float64)(nil)
+ pv2 := &v2
+ v2Addr := fmt.Sprintf("%p", pv2)
+ pv2Addr := fmt.Sprintf("%p", &pv2)
+ v2t := "float64"
+ v2s := "3.1415926"
+ addDumpTest(v2, "("+v2t+") "+v2s+"\n")
+ addDumpTest(pv2, "(*"+v2t+")("+v2Addr+")("+v2s+")\n")
+ addDumpTest(&pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n")
+ addDumpTest(nv2, "(*"+v2t+")(<nil>)\n")
+}
+
+func addComplexDumpTests() {
+ // Standard complex64.
+ v := complex(float32(6), -2)
+ nv := (*complex64)(nil)
+ pv := &v
+ vAddr := fmt.Sprintf("%p", pv)
+ pvAddr := fmt.Sprintf("%p", &pv)
+ vt := "complex64"
+ vs := "(6-2i)"
+ addDumpTest(v, "("+vt+") "+vs+"\n")
+ addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs+")\n")
+ addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n")
+ addDumpTest(nv, "(*"+vt+")(<nil>)\n")
+
+ // Standard complex128.
+ v2 := complex(float64(-6), 2)
+ nv2 := (*complex128)(nil)
+ pv2 := &v2
+ v2Addr := fmt.Sprintf("%p", pv2)
+ pv2Addr := fmt.Sprintf("%p", &pv2)
+ v2t := "complex128"
+ v2s := "(-6+2i)"
+ addDumpTest(v2, "("+v2t+") "+v2s+"\n")
+ addDumpTest(pv2, "(*"+v2t+")("+v2Addr+")("+v2s+")\n")
+ addDumpTest(&pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n")
+ addDumpTest(nv2, "(*"+v2t+")(<nil>)\n")
+}
+
+func addArrayDumpTests() {
+ // Array containing standard ints.
+ v := [3]int{1, 2, 3}
+ vLen := fmt.Sprintf("%d", len(v))
+ vCap := fmt.Sprintf("%d", cap(v))
+ nv := (*[3]int)(nil)
+ pv := &v
+ vAddr := fmt.Sprintf("%p", pv)
+ pvAddr := fmt.Sprintf("%p", &pv)
+ vt := "int"
+ vs := "(len=" + vLen + " cap=" + vCap + ") {\n (" + vt + ") 1,\n (" +
+ vt + ") 2,\n (" + vt + ") 3\n}"
+ addDumpTest(v, "([3]"+vt+") "+vs+"\n")
+ addDumpTest(pv, "(*[3]"+vt+")("+vAddr+")("+vs+")\n")
+ addDumpTest(&pv, "(**[3]"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n")
+ addDumpTest(nv, "(*[3]"+vt+")(<nil>)\n")
+
+ // Array containing type with custom formatter on pointer receiver only.
+ v2i0 := pstringer("1")
+ v2i1 := pstringer("2")
+ v2i2 := pstringer("3")
+ v2 := [3]pstringer{v2i0, v2i1, v2i2}
+ v2i0Len := fmt.Sprintf("%d", len(v2i0))
+ v2i1Len := fmt.Sprintf("%d", len(v2i1))
+ v2i2Len := fmt.Sprintf("%d", len(v2i2))
+ v2Len := fmt.Sprintf("%d", len(v2))
+ v2Cap := fmt.Sprintf("%d", cap(v2))
+ nv2 := (*[3]pstringer)(nil)
+ pv2 := &v2
+ v2Addr := fmt.Sprintf("%p", pv2)
+ pv2Addr := fmt.Sprintf("%p", &pv2)
+ v2t := "spew_test.pstringer"
+ v2sp := "(len=" + v2Len + " cap=" + v2Cap + ") {\n (" + v2t +
+ ") (len=" + v2i0Len + ") stringer 1,\n (" + v2t +
+ ") (len=" + v2i1Len + ") stringer 2,\n (" + v2t +
+ ") (len=" + v2i2Len + ") " + "stringer 3\n}"
+ v2s := v2sp
+ if spew.UnsafeDisabled {
+ v2s = "(len=" + v2Len + " cap=" + v2Cap + ") {\n (" + v2t +
+ ") (len=" + v2i0Len + ") \"1\",\n (" + v2t + ") (len=" +
+ v2i1Len + ") \"2\",\n (" + v2t + ") (len=" + v2i2Len +
+ ") " + "\"3\"\n}"
+ }
+ addDumpTest(v2, "([3]"+v2t+") "+v2s+"\n")
+ addDumpTest(pv2, "(*[3]"+v2t+")("+v2Addr+")("+v2sp+")\n")
+ addDumpTest(&pv2, "(**[3]"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2sp+")\n")
+ addDumpTest(nv2, "(*[3]"+v2t+")(<nil>)\n")
+
+ // Array containing interfaces.
+ v3i0 := "one"
+ v3 := [3]interface{}{v3i0, int(2), uint(3)}
+ v3i0Len := fmt.Sprintf("%d", len(v3i0))
+ v3Len := fmt.Sprintf("%d", len(v3))
+ v3Cap := fmt.Sprintf("%d", cap(v3))
+ nv3 := (*[3]interface{})(nil)
+ pv3 := &v3
+ v3Addr := fmt.Sprintf("%p", pv3)
+ pv3Addr := fmt.Sprintf("%p", &pv3)
+ v3t := "[3]interface {}"
+ v3t2 := "string"
+ v3t3 := "int"
+ v3t4 := "uint"
+ v3s := "(len=" + v3Len + " cap=" + v3Cap + ") {\n (" + v3t2 + ") " +
+ "(len=" + v3i0Len + ") \"one\",\n (" + v3t3 + ") 2,\n (" +
+ v3t4 + ") 3\n}"
+ addDumpTest(v3, "("+v3t+") "+v3s+"\n")
+ addDumpTest(pv3, "(*"+v3t+")("+v3Addr+")("+v3s+")\n")
+ addDumpTest(&pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")("+v3s+")\n")
+ addDumpTest(nv3, "(*"+v3t+")(<nil>)\n")
+
+ // Array containing bytes.
+ v4 := [34]byte{
+ 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
+ 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
+ 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
+ 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
+ 0x31, 0x32,
+ }
+ v4Len := fmt.Sprintf("%d", len(v4))
+ v4Cap := fmt.Sprintf("%d", cap(v4))
+ nv4 := (*[34]byte)(nil)
+ pv4 := &v4
+ v4Addr := fmt.Sprintf("%p", pv4)
+ pv4Addr := fmt.Sprintf("%p", &pv4)
+ v4t := "[34]uint8"
+ v4s := "(len=" + v4Len + " cap=" + v4Cap + ") " +
+ "{\n 00000000 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20" +
+ " |............... |\n" +
+ " 00000010 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30" +
+ " |!\"#$%&'()*+,-./0|\n" +
+ " 00000020 31 32 " +
+ " |12|\n}"
+ addDumpTest(v4, "("+v4t+") "+v4s+"\n")
+ addDumpTest(pv4, "(*"+v4t+")("+v4Addr+")("+v4s+")\n")
+ addDumpTest(&pv4, "(**"+v4t+")("+pv4Addr+"->"+v4Addr+")("+v4s+")\n")
+ addDumpTest(nv4, "(*"+v4t+")(<nil>)\n")
+}
+
+func addSliceDumpTests() {
+ // Slice containing standard float32 values.
+ v := []float32{3.14, 6.28, 12.56}
+ vLen := fmt.Sprintf("%d", len(v))
+ vCap := fmt.Sprintf("%d", cap(v))
+ nv := (*[]float32)(nil)
+ pv := &v
+ vAddr := fmt.Sprintf("%p", pv)
+ pvAddr := fmt.Sprintf("%p", &pv)
+ vt := "float32"
+ vs := "(len=" + vLen + " cap=" + vCap + ") {\n (" + vt + ") 3.14,\n (" +
+ vt + ") 6.28,\n (" + vt + ") 12.56\n}"
+ addDumpTest(v, "([]"+vt+") "+vs+"\n")
+ addDumpTest(pv, "(*[]"+vt+")("+vAddr+")("+vs+")\n")
+ addDumpTest(&pv, "(**[]"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n")
+ addDumpTest(nv, "(*[]"+vt+")(<nil>)\n")
+
+ // Slice containing type with custom formatter on pointer receiver only.
+ v2i0 := pstringer("1")
+ v2i1 := pstringer("2")
+ v2i2 := pstringer("3")
+ v2 := []pstringer{v2i0, v2i1, v2i2}
+ v2i0Len := fmt.Sprintf("%d", len(v2i0))
+ v2i1Len := fmt.Sprintf("%d", len(v2i1))
+ v2i2Len := fmt.Sprintf("%d", len(v2i2))
+ v2Len := fmt.Sprintf("%d", len(v2))
+ v2Cap := fmt.Sprintf("%d", cap(v2))
+ nv2 := (*[]pstringer)(nil)
+ pv2 := &v2
+ v2Addr := fmt.Sprintf("%p", pv2)
+ pv2Addr := fmt.Sprintf("%p", &pv2)
+ v2t := "spew_test.pstringer"
+ v2s := "(len=" + v2Len + " cap=" + v2Cap + ") {\n (" + v2t + ") (len=" +
+ v2i0Len + ") stringer 1,\n (" + v2t + ") (len=" + v2i1Len +
+ ") stringer 2,\n (" + v2t + ") (len=" + v2i2Len + ") " +
+ "stringer 3\n}"
+ addDumpTest(v2, "([]"+v2t+") "+v2s+"\n")
+ addDumpTest(pv2, "(*[]"+v2t+")("+v2Addr+")("+v2s+")\n")
+ addDumpTest(&pv2, "(**[]"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n")
+ addDumpTest(nv2, "(*[]"+v2t+")(<nil>)\n")
+
+ // Slice containing interfaces.
+ v3i0 := "one"
+ v3 := []interface{}{v3i0, int(2), uint(3), nil}
+ v3i0Len := fmt.Sprintf("%d", len(v3i0))
+ v3Len := fmt.Sprintf("%d", len(v3))
+ v3Cap := fmt.Sprintf("%d", cap(v3))
+ nv3 := (*[]interface{})(nil)
+ pv3 := &v3
+ v3Addr := fmt.Sprintf("%p", pv3)
+ pv3Addr := fmt.Sprintf("%p", &pv3)
+ v3t := "[]interface {}"
+ v3t2 := "string"
+ v3t3 := "int"
+ v3t4 := "uint"
+ v3t5 := "interface {}"
+ v3s := "(len=" + v3Len + " cap=" + v3Cap + ") {\n (" + v3t2 + ") " +
+ "(len=" + v3i0Len + ") \"one\",\n (" + v3t3 + ") 2,\n (" +
+ v3t4 + ") 3,\n (" + v3t5 + ") <nil>\n}"
+ addDumpTest(v3, "("+v3t+") "+v3s+"\n")
+ addDumpTest(pv3, "(*"+v3t+")("+v3Addr+")("+v3s+")\n")
+ addDumpTest(&pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")("+v3s+")\n")
+ addDumpTest(nv3, "(*"+v3t+")(<nil>)\n")
+
+ // Slice containing bytes.
+ v4 := []byte{
+ 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
+ 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
+ 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
+ 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
+ 0x31, 0x32,
+ }
+ v4Len := fmt.Sprintf("%d", len(v4))
+ v4Cap := fmt.Sprintf("%d", cap(v4))
+ nv4 := (*[]byte)(nil)
+ pv4 := &v4
+ v4Addr := fmt.Sprintf("%p", pv4)
+ pv4Addr := fmt.Sprintf("%p", &pv4)
+ v4t := "[]uint8"
+ v4s := "(len=" + v4Len + " cap=" + v4Cap + ") " +
+ "{\n 00000000 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20" +
+ " |............... |\n" +
+ " 00000010 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30" +
+ " |!\"#$%&'()*+,-./0|\n" +
+ " 00000020 31 32 " +
+ " |12|\n}"
+ addDumpTest(v4, "("+v4t+") "+v4s+"\n")
+ addDumpTest(pv4, "(*"+v4t+")("+v4Addr+")("+v4s+")\n")
+ addDumpTest(&pv4, "(**"+v4t+")("+pv4Addr+"->"+v4Addr+")("+v4s+")\n")
+ addDumpTest(nv4, "(*"+v4t+")(<nil>)\n")
+
+ // Nil slice.
+ v5 := []int(nil)
+ nv5 := (*[]int)(nil)
+ pv5 := &v5
+ v5Addr := fmt.Sprintf("%p", pv5)
+ pv5Addr := fmt.Sprintf("%p", &pv5)
+ v5t := "[]int"
+ v5s := "<nil>"
+ addDumpTest(v5, "("+v5t+") "+v5s+"\n")
+ addDumpTest(pv5, "(*"+v5t+")("+v5Addr+")("+v5s+")\n")
+ addDumpTest(&pv5, "(**"+v5t+")("+pv5Addr+"->"+v5Addr+")("+v5s+")\n")
+ addDumpTest(nv5, "(*"+v5t+")(<nil>)\n")
+}
+
+func addStringDumpTests() {
+ // Standard string.
+ v := "test"
+ vLen := fmt.Sprintf("%d", len(v))
+ nv := (*string)(nil)
+ pv := &v
+ vAddr := fmt.Sprintf("%p", pv)
+ pvAddr := fmt.Sprintf("%p", &pv)
+ vt := "string"
+ vs := "(len=" + vLen + ") \"test\""
+ addDumpTest(v, "("+vt+") "+vs+"\n")
+ addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs+")\n")
+ addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n")
+ addDumpTest(nv, "(*"+vt+")(<nil>)\n")
+}
+
+func addInterfaceDumpTests() {
+ // Nil interface.
+ var v interface{}
+ nv := (*interface{})(nil)
+ pv := &v
+ vAddr := fmt.Sprintf("%p", pv)
+ pvAddr := fmt.Sprintf("%p", &pv)
+ vt := "interface {}"
+ vs := "<nil>"
+ addDumpTest(v, "("+vt+") "+vs+"\n")
+ addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs+")\n")
+ addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n")
+ addDumpTest(nv, "(*"+vt+")(<nil>)\n")
+
+ // Sub-interface.
+ v2 := interface{}(uint16(65535))
+ pv2 := &v2
+ v2Addr := fmt.Sprintf("%p", pv2)
+ pv2Addr := fmt.Sprintf("%p", &pv2)
+ v2t := "uint16"
+ v2s := "65535"
+ addDumpTest(v2, "("+v2t+") "+v2s+"\n")
+ addDumpTest(pv2, "(*"+v2t+")("+v2Addr+")("+v2s+")\n")
+ addDumpTest(&pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n")
+}
+
+func addMapDumpTests() {
+ // Map with string keys and int vals.
+ k := "one"
+ kk := "two"
+ m := map[string]int{k: 1, kk: 2}
+ klen := fmt.Sprintf("%d", len(k)) // not kLen to shut golint up
+ kkLen := fmt.Sprintf("%d", len(kk))
+ mLen := fmt.Sprintf("%d", len(m))
+ nilMap := map[string]int(nil)
+ nm := (*map[string]int)(nil)
+ pm := &m
+ mAddr := fmt.Sprintf("%p", pm)
+ pmAddr := fmt.Sprintf("%p", &pm)
+ mt := "map[string]int"
+ mt1 := "string"
+ mt2 := "int"
+ ms := "(len=" + mLen + ") {\n (" + mt1 + ") (len=" + klen + ") " +
+ "\"one\": (" + mt2 + ") 1,\n (" + mt1 + ") (len=" + kkLen +
+ ") \"two\": (" + mt2 + ") 2\n}"
+ ms2 := "(len=" + mLen + ") {\n (" + mt1 + ") (len=" + kkLen + ") " +
+ "\"two\": (" + mt2 + ") 2,\n (" + mt1 + ") (len=" + klen +
+ ") \"one\": (" + mt2 + ") 1\n}"
+ addDumpTest(m, "("+mt+") "+ms+"\n", "("+mt+") "+ms2+"\n")
+ addDumpTest(pm, "(*"+mt+")("+mAddr+")("+ms+")\n",
+ "(*"+mt+")("+mAddr+")("+ms2+")\n")
+ addDumpTest(&pm, "(**"+mt+")("+pmAddr+"->"+mAddr+")("+ms+")\n",
+ "(**"+mt+")("+pmAddr+"->"+mAddr+")("+ms2+")\n")
+ addDumpTest(nm, "(*"+mt+")(<nil>)\n")
+ addDumpTest(nilMap, "("+mt+") <nil>\n")
+
+ // Map with custom formatter type on pointer receiver only keys and vals.
+ k2 := pstringer("one")
+ v2 := pstringer("1")
+ m2 := map[pstringer]pstringer{k2: v2}
+ k2Len := fmt.Sprintf("%d", len(k2))
+ v2Len := fmt.Sprintf("%d", len(v2))
+ m2Len := fmt.Sprintf("%d", len(m2))
+ nilMap2 := map[pstringer]pstringer(nil)
+ nm2 := (*map[pstringer]pstringer)(nil)
+ pm2 := &m2
+ m2Addr := fmt.Sprintf("%p", pm2)
+ pm2Addr := fmt.Sprintf("%p", &pm2)
+ m2t := "map[spew_test.pstringer]spew_test.pstringer"
+ m2t1 := "spew_test.pstringer"
+ m2t2 := "spew_test.pstringer"
+ m2s := "(len=" + m2Len + ") {\n (" + m2t1 + ") (len=" + k2Len + ") " +
+ "stringer one: (" + m2t2 + ") (len=" + v2Len + ") stringer 1\n}"
+ if spew.UnsafeDisabled {
+ m2s = "(len=" + m2Len + ") {\n (" + m2t1 + ") (len=" + k2Len +
+ ") " + "\"one\": (" + m2t2 + ") (len=" + v2Len +
+ ") \"1\"\n}"
+ }
+ addDumpTest(m2, "("+m2t+") "+m2s+"\n")
+ addDumpTest(pm2, "(*"+m2t+")("+m2Addr+")("+m2s+")\n")
+ addDumpTest(&pm2, "(**"+m2t+")("+pm2Addr+"->"+m2Addr+")("+m2s+")\n")
+ addDumpTest(nm2, "(*"+m2t+")(<nil>)\n")
+ addDumpTest(nilMap2, "("+m2t+") <nil>\n")
+
+ // Map with interface keys and values.
+ k3 := "one"
+ k3Len := fmt.Sprintf("%d", len(k3))
+ m3 := map[interface{}]interface{}{k3: 1}
+ m3Len := fmt.Sprintf("%d", len(m3))
+ nilMap3 := map[interface{}]interface{}(nil)
+ nm3 := (*map[interface{}]interface{})(nil)
+ pm3 := &m3
+ m3Addr := fmt.Sprintf("%p", pm3)
+ pm3Addr := fmt.Sprintf("%p", &pm3)
+ m3t := "map[interface {}]interface {}"
+ m3t1 := "string"
+ m3t2 := "int"
+ m3s := "(len=" + m3Len + ") {\n (" + m3t1 + ") (len=" + k3Len + ") " +
+ "\"one\": (" + m3t2 + ") 1\n}"
+ addDumpTest(m3, "("+m3t+") "+m3s+"\n")
+ addDumpTest(pm3, "(*"+m3t+")("+m3Addr+")("+m3s+")\n")
+ addDumpTest(&pm3, "(**"+m3t+")("+pm3Addr+"->"+m3Addr+")("+m3s+")\n")
+ addDumpTest(nm3, "(*"+m3t+")(<nil>)\n")
+ addDumpTest(nilMap3, "("+m3t+") <nil>\n")
+
+ // Map with nil interface value.
+ k4 := "nil"
+ k4Len := fmt.Sprintf("%d", len(k4))
+ m4 := map[string]interface{}{k4: nil}
+ m4Len := fmt.Sprintf("%d", len(m4))
+ nilMap4 := map[string]interface{}(nil)
+ nm4 := (*map[string]interface{})(nil)
+ pm4 := &m4
+ m4Addr := fmt.Sprintf("%p", pm4)
+ pm4Addr := fmt.Sprintf("%p", &pm4)
+ m4t := "map[string]interface {}"
+ m4t1 := "string"
+ m4t2 := "interface {}"
+ m4s := "(len=" + m4Len + ") {\n (" + m4t1 + ") (len=" + k4Len + ")" +
+ " \"nil\": (" + m4t2 + ") <nil>\n}"
+ addDumpTest(m4, "("+m4t+") "+m4s+"\n")
+ addDumpTest(pm4, "(*"+m4t+")("+m4Addr+")("+m4s+")\n")
+ addDumpTest(&pm4, "(**"+m4t+")("+pm4Addr+"->"+m4Addr+")("+m4s+")\n")
+ addDumpTest(nm4, "(*"+m4t+")(<nil>)\n")
+ addDumpTest(nilMap4, "("+m4t+") <nil>\n")
+}
+
+func addStructDumpTests() {
+ // Struct with primitives.
+ type s1 struct {
+ a int8
+ b uint8
+ }
+ v := s1{127, 255}
+ nv := (*s1)(nil)
+ pv := &v
+ vAddr := fmt.Sprintf("%p", pv)
+ pvAddr := fmt.Sprintf("%p", &pv)
+ vt := "spew_test.s1"
+ vt2 := "int8"
+ vt3 := "uint8"
+ vs := "{\n a: (" + vt2 + ") 127,\n b: (" + vt3 + ") 255\n}"
+ addDumpTest(v, "("+vt+") "+vs+"\n")
+ addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs+")\n")
+ addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n")
+ addDumpTest(nv, "(*"+vt+")(<nil>)\n")
+
+ // Struct that contains another struct.
+ type s2 struct {
+ s1 s1
+ b bool
+ }
+ v2 := s2{s1{127, 255}, true}
+ nv2 := (*s2)(nil)
+ pv2 := &v2
+ v2Addr := fmt.Sprintf("%p", pv2)
+ pv2Addr := fmt.Sprintf("%p", &pv2)
+ v2t := "spew_test.s2"
+ v2t2 := "spew_test.s1"
+ v2t3 := "int8"
+ v2t4 := "uint8"
+ v2t5 := "bool"
+ v2s := "{\n s1: (" + v2t2 + ") {\n a: (" + v2t3 + ") 127,\n b: (" +
+ v2t4 + ") 255\n },\n b: (" + v2t5 + ") true\n}"
+ addDumpTest(v2, "("+v2t+") "+v2s+"\n")
+ addDumpTest(pv2, "(*"+v2t+")("+v2Addr+")("+v2s+")\n")
+ addDumpTest(&pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n")
+ addDumpTest(nv2, "(*"+v2t+")(<nil>)\n")
+
+ // Struct that contains custom type with Stringer pointer interface via both
+ // exported and unexported fields.
+ type s3 struct {
+ s pstringer
+ S pstringer
+ }
+ v3 := s3{"test", "test2"}
+ nv3 := (*s3)(nil)
+ pv3 := &v3
+ v3Addr := fmt.Sprintf("%p", pv3)
+ pv3Addr := fmt.Sprintf("%p", &pv3)
+ v3t := "spew_test.s3"
+ v3t2 := "spew_test.pstringer"
+ v3s := "{\n s: (" + v3t2 + ") (len=4) stringer test,\n S: (" + v3t2 +
+ ") (len=5) stringer test2\n}"
+ v3sp := v3s
+ if spew.UnsafeDisabled {
+ v3s = "{\n s: (" + v3t2 + ") (len=4) \"test\",\n S: (" +
+ v3t2 + ") (len=5) \"test2\"\n}"
+ v3sp = "{\n s: (" + v3t2 + ") (len=4) \"test\",\n S: (" +
+ v3t2 + ") (len=5) stringer test2\n}"
+ }
+ addDumpTest(v3, "("+v3t+") "+v3s+"\n")
+ addDumpTest(pv3, "(*"+v3t+")("+v3Addr+")("+v3sp+")\n")
+ addDumpTest(&pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")("+v3sp+")\n")
+ addDumpTest(nv3, "(*"+v3t+")(<nil>)\n")
+
+ // Struct that contains embedded struct and field to same struct.
+ e := embed{"embedstr"}
+ eLen := fmt.Sprintf("%d", len("embedstr"))
+ v4 := embedwrap{embed: &e, e: &e}
+ nv4 := (*embedwrap)(nil)
+ pv4 := &v4
+ eAddr := fmt.Sprintf("%p", &e)
+ v4Addr := fmt.Sprintf("%p", pv4)
+ pv4Addr := fmt.Sprintf("%p", &pv4)
+ v4t := "spew_test.embedwrap"
+ v4t2 := "spew_test.embed"
+ v4t3 := "string"
+ v4s := "{\n embed: (*" + v4t2 + ")(" + eAddr + ")({\n a: (" + v4t3 +
+ ") (len=" + eLen + ") \"embedstr\"\n }),\n e: (*" + v4t2 +
+ ")(" + eAddr + ")({\n a: (" + v4t3 + ") (len=" + eLen + ")" +
+ " \"embedstr\"\n })\n}"
+ addDumpTest(v4, "("+v4t+") "+v4s+"\n")
+ addDumpTest(pv4, "(*"+v4t+")("+v4Addr+")("+v4s+")\n")
+ addDumpTest(&pv4, "(**"+v4t+")("+pv4Addr+"->"+v4Addr+")("+v4s+")\n")
+ addDumpTest(nv4, "(*"+v4t+")(<nil>)\n")
+}
+
+func addUintptrDumpTests() {
+ // Null pointer.
+ v := uintptr(0)
+ pv := &v
+ vAddr := fmt.Sprintf("%p", pv)
+ pvAddr := fmt.Sprintf("%p", &pv)
+ vt := "uintptr"
+ vs := "<nil>"
+ addDumpTest(v, "("+vt+") "+vs+"\n")
+ addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs+")\n")
+ addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n")
+
+ // Address of real variable.
+ i := 1
+ v2 := uintptr(unsafe.Pointer(&i))
+ nv2 := (*uintptr)(nil)
+ pv2 := &v2
+ v2Addr := fmt.Sprintf("%p", pv2)
+ pv2Addr := fmt.Sprintf("%p", &pv2)
+ v2t := "uintptr"
+ v2s := fmt.Sprintf("%p", &i)
+ addDumpTest(v2, "("+v2t+") "+v2s+"\n")
+ addDumpTest(pv2, "(*"+v2t+")("+v2Addr+")("+v2s+")\n")
+ addDumpTest(&pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n")
+ addDumpTest(nv2, "(*"+v2t+")(<nil>)\n")
+}
+
+func addUnsafePointerDumpTests() {
+ // Null pointer.
+ v := unsafe.Pointer(uintptr(0))
+ nv := (*unsafe.Pointer)(nil)
+ pv := &v
+ vAddr := fmt.Sprintf("%p", pv)
+ pvAddr := fmt.Sprintf("%p", &pv)
+ vt := "unsafe.Pointer"
+ vs := "<nil>"
+ addDumpTest(v, "("+vt+") "+vs+"\n")
+ addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs+")\n")
+ addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n")
+ addDumpTest(nv, "(*"+vt+")(<nil>)\n")
+
+ // Address of real variable.
+ i := 1
+ v2 := unsafe.Pointer(&i)
+ pv2 := &v2
+ v2Addr := fmt.Sprintf("%p", pv2)
+ pv2Addr := fmt.Sprintf("%p", &pv2)
+ v2t := "unsafe.Pointer"
+ v2s := fmt.Sprintf("%p", &i)
+ addDumpTest(v2, "("+v2t+") "+v2s+"\n")
+ addDumpTest(pv2, "(*"+v2t+")("+v2Addr+")("+v2s+")\n")
+ addDumpTest(&pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n")
+ addDumpTest(nv, "(*"+vt+")(<nil>)\n")
+}
+
+func addChanDumpTests() {
+ // Nil channel.
+ var v chan int
+ pv := &v
+ nv := (*chan int)(nil)
+ vAddr := fmt.Sprintf("%p", pv)
+ pvAddr := fmt.Sprintf("%p", &pv)
+ vt := "chan int"
+ vs := "<nil>"
+ addDumpTest(v, "("+vt+") "+vs+"\n")
+ addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs+")\n")
+ addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n")
+ addDumpTest(nv, "(*"+vt+")(<nil>)\n")
+
+ // Real channel.
+ v2 := make(chan int)
+ pv2 := &v2
+ v2Addr := fmt.Sprintf("%p", pv2)
+ pv2Addr := fmt.Sprintf("%p", &pv2)
+ v2t := "chan int"
+ v2s := fmt.Sprintf("%p", v2)
+ addDumpTest(v2, "("+v2t+") "+v2s+"\n")
+ addDumpTest(pv2, "(*"+v2t+")("+v2Addr+")("+v2s+")\n")
+ addDumpTest(&pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n")
+}
+
+func addFuncDumpTests() {
+ // Function with no params and no returns.
+ v := addIntDumpTests
+ nv := (*func())(nil)
+ pv := &v
+ vAddr := fmt.Sprintf("%p", pv)
+ pvAddr := fmt.Sprintf("%p", &pv)
+ vt := "func()"
+ vs := fmt.Sprintf("%p", v)
+ addDumpTest(v, "("+vt+") "+vs+"\n")
+ addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs+")\n")
+ addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n")
+ addDumpTest(nv, "(*"+vt+")(<nil>)\n")
+
+ // Function with param and no returns.
+ v2 := TestDump
+ nv2 := (*func(*testing.T))(nil)
+ pv2 := &v2
+ v2Addr := fmt.Sprintf("%p", pv2)
+ pv2Addr := fmt.Sprintf("%p", &pv2)
+ v2t := "func(*testing.T)"
+ v2s := fmt.Sprintf("%p", v2)
+ addDumpTest(v2, "("+v2t+") "+v2s+"\n")
+ addDumpTest(pv2, "(*"+v2t+")("+v2Addr+")("+v2s+")\n")
+ addDumpTest(&pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s+")\n")
+ addDumpTest(nv2, "(*"+v2t+")(<nil>)\n")
+
+ // Function with multiple params and multiple returns.
+ var v3 = func(i int, s string) (b bool, err error) {
+ return true, nil
+ }
+ nv3 := (*func(int, string) (bool, error))(nil)
+ pv3 := &v3
+ v3Addr := fmt.Sprintf("%p", pv3)
+ pv3Addr := fmt.Sprintf("%p", &pv3)
+ v3t := "func(int, string) (bool, error)"
+ v3s := fmt.Sprintf("%p", v3)
+ addDumpTest(v3, "("+v3t+") "+v3s+"\n")
+ addDumpTest(pv3, "(*"+v3t+")("+v3Addr+")("+v3s+")\n")
+ addDumpTest(&pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")("+v3s+")\n")
+ addDumpTest(nv3, "(*"+v3t+")(<nil>)\n")
+}
+
+func addCircularDumpTests() {
+ // Struct that is circular through self referencing.
+ type circular struct {
+ c *circular
+ }
+ v := circular{nil}
+ v.c = &v
+ pv := &v
+ vAddr := fmt.Sprintf("%p", pv)
+ pvAddr := fmt.Sprintf("%p", &pv)
+ vt := "spew_test.circular"
+ vs := "{\n c: (*" + vt + ")(" + vAddr + ")({\n c: (*" + vt + ")(" +
+ vAddr + ")(<already shown>)\n })\n}"
+ vs2 := "{\n c: (*" + vt + ")(" + vAddr + ")(<already shown>)\n}"
+ addDumpTest(v, "("+vt+") "+vs+"\n")
+ addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs2+")\n")
+ addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs2+")\n")
+
+ // Structs that are circular through cross referencing.
+ v2 := xref1{nil}
+ ts2 := xref2{&v2}
+ v2.ps2 = &ts2
+ pv2 := &v2
+ ts2Addr := fmt.Sprintf("%p", &ts2)
+ v2Addr := fmt.Sprintf("%p", pv2)
+ pv2Addr := fmt.Sprintf("%p", &pv2)
+ v2t := "spew_test.xref1"
+ v2t2 := "spew_test.xref2"
+ v2s := "{\n ps2: (*" + v2t2 + ")(" + ts2Addr + ")({\n ps1: (*" + v2t +
+ ")(" + v2Addr + ")({\n ps2: (*" + v2t2 + ")(" + ts2Addr +
+ ")(<already shown>)\n })\n })\n}"
+ v2s2 := "{\n ps2: (*" + v2t2 + ")(" + ts2Addr + ")({\n ps1: (*" + v2t +
+ ")(" + v2Addr + ")(<already shown>)\n })\n}"
+ addDumpTest(v2, "("+v2t+") "+v2s+"\n")
+ addDumpTest(pv2, "(*"+v2t+")("+v2Addr+")("+v2s2+")\n")
+ addDumpTest(&pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")("+v2s2+")\n")
+
+ // Structs that are indirectly circular.
+ v3 := indirCir1{nil}
+ tic2 := indirCir2{nil}
+ tic3 := indirCir3{&v3}
+ tic2.ps3 = &tic3
+ v3.ps2 = &tic2
+ pv3 := &v3
+ tic2Addr := fmt.Sprintf("%p", &tic2)
+ tic3Addr := fmt.Sprintf("%p", &tic3)
+ v3Addr := fmt.Sprintf("%p", pv3)
+ pv3Addr := fmt.Sprintf("%p", &pv3)
+ v3t := "spew_test.indirCir1"
+ v3t2 := "spew_test.indirCir2"
+ v3t3 := "spew_test.indirCir3"
+ v3s := "{\n ps2: (*" + v3t2 + ")(" + tic2Addr + ")({\n ps3: (*" + v3t3 +
+ ")(" + tic3Addr + ")({\n ps1: (*" + v3t + ")(" + v3Addr +
+ ")({\n ps2: (*" + v3t2 + ")(" + tic2Addr +
+ ")(<already shown>)\n })\n })\n })\n}"
+ v3s2 := "{\n ps2: (*" + v3t2 + ")(" + tic2Addr + ")({\n ps3: (*" + v3t3 +
+ ")(" + tic3Addr + ")({\n ps1: (*" + v3t + ")(" + v3Addr +
+ ")(<already shown>)\n })\n })\n}"
+ addDumpTest(v3, "("+v3t+") "+v3s+"\n")
+ addDumpTest(pv3, "(*"+v3t+")("+v3Addr+")("+v3s2+")\n")
+ addDumpTest(&pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")("+v3s2+")\n")
+}
+
+func addPanicDumpTests() {
+ // Type that panics in its Stringer interface.
+ v := panicer(127)
+ nv := (*panicer)(nil)
+ pv := &v
+ vAddr := fmt.Sprintf("%p", pv)
+ pvAddr := fmt.Sprintf("%p", &pv)
+ vt := "spew_test.panicer"
+ vs := "(PANIC=test panic)127"
+ addDumpTest(v, "("+vt+") "+vs+"\n")
+ addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs+")\n")
+ addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n")
+ addDumpTest(nv, "(*"+vt+")(<nil>)\n")
+}
+
+func addErrorDumpTests() {
+ // Type that has a custom Error interface.
+ v := customError(127)
+ nv := (*customError)(nil)
+ pv := &v
+ vAddr := fmt.Sprintf("%p", pv)
+ pvAddr := fmt.Sprintf("%p", &pv)
+ vt := "spew_test.customError"
+ vs := "error: 127"
+ addDumpTest(v, "("+vt+") "+vs+"\n")
+ addDumpTest(pv, "(*"+vt+")("+vAddr+")("+vs+")\n")
+ addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")("+vs+")\n")
+ addDumpTest(nv, "(*"+vt+")(<nil>)\n")
+}
+
+// TestDump executes all of the tests described by dumpTests.
+func TestDump(t *testing.T) {
+ // Setup tests.
+ addIntDumpTests()
+ addUintDumpTests()
+ addBoolDumpTests()
+ addFloatDumpTests()
+ addComplexDumpTests()
+ addArrayDumpTests()
+ addSliceDumpTests()
+ addStringDumpTests()
+ addInterfaceDumpTests()
+ addMapDumpTests()
+ addStructDumpTests()
+ addUintptrDumpTests()
+ addUnsafePointerDumpTests()
+ addChanDumpTests()
+ addFuncDumpTests()
+ addCircularDumpTests()
+ addPanicDumpTests()
+ addErrorDumpTests()
+ addCgoDumpTests()
+
+ t.Logf("Running %d tests", len(dumpTests))
+ for i, test := range dumpTests {
+ buf := new(bytes.Buffer)
+ spew.Fdump(buf, test.in)
+ s := buf.String()
+ if testFailed(s, test.wants) {
+ t.Errorf("Dump #%d\n got: %s %s", i, s, stringizeWants(test.wants))
+ continue
+ }
+ }
+}
+
+func TestDumpSortedKeys(t *testing.T) {
+ cfg := spew.ConfigState{SortKeys: true}
+ s := cfg.Sdump(map[int]string{1: "1", 3: "3", 2: "2"})
+ expected := "(map[int]string) (len=3) {\n(int) 1: (string) (len=1) " +
+ "\"1\",\n(int) 2: (string) (len=1) \"2\",\n(int) 3: (string) " +
+ "(len=1) \"3\"\n" +
+ "}\n"
+ if s != expected {
+ t.Errorf("Sorted keys mismatch:\n %v %v", s, expected)
+ }
+
+ s = cfg.Sdump(map[stringer]int{"1": 1, "3": 3, "2": 2})
+ expected = "(map[spew_test.stringer]int) (len=3) {\n" +
+ "(spew_test.stringer) (len=1) stringer 1: (int) 1,\n" +
+ "(spew_test.stringer) (len=1) stringer 2: (int) 2,\n" +
+ "(spew_test.stringer) (len=1) stringer 3: (int) 3\n" +
+ "}\n"
+ if s != expected {
+ t.Errorf("Sorted keys mismatch:\n %v %v", s, expected)
+ }
+
+ s = cfg.Sdump(map[pstringer]int{pstringer("1"): 1, pstringer("3"): 3, pstringer("2"): 2})
+ expected = "(map[spew_test.pstringer]int) (len=3) {\n" +
+ "(spew_test.pstringer) (len=1) stringer 1: (int) 1,\n" +
+ "(spew_test.pstringer) (len=1) stringer 2: (int) 2,\n" +
+ "(spew_test.pstringer) (len=1) stringer 3: (int) 3\n" +
+ "}\n"
+ if spew.UnsafeDisabled {
+ expected = "(map[spew_test.pstringer]int) (len=3) {\n" +
+ "(spew_test.pstringer) (len=1) \"1\": (int) 1,\n" +
+ "(spew_test.pstringer) (len=1) \"2\": (int) 2,\n" +
+ "(spew_test.pstringer) (len=1) \"3\": (int) 3\n" +
+ "}\n"
+ }
+ if s != expected {
+ t.Errorf("Sorted keys mismatch:\n %v %v", s, expected)
+ }
+
+ s = cfg.Sdump(map[customError]int{customError(1): 1, customError(3): 3, customError(2): 2})
+ expected = "(map[spew_test.customError]int) (len=3) {\n" +
+ "(spew_test.customError) error: 1: (int) 1,\n" +
+ "(spew_test.customError) error: 2: (int) 2,\n" +
+ "(spew_test.customError) error: 3: (int) 3\n" +
+ "}\n"
+ if s != expected {
+ t.Errorf("Sorted keys mismatch:\n %v %v", s, expected)
+ }
+
+}
diff --git a/vendor/github.com/davecgh/go-spew/spew/dumpcgo_test.go b/vendor/github.com/davecgh/go-spew/spew/dumpcgo_test.go
new file mode 100644
index 000000000..ed3e3c31a
--- /dev/null
+++ b/vendor/github.com/davecgh/go-spew/spew/dumpcgo_test.go
@@ -0,0 +1,99 @@
+// Copyright (c) 2013 Dave Collins <dave@davec.name>
+//
+// Permission to use, copy, modify, and distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+// NOTE: Due to the following build constraints, this file will only be compiled
+// when both cgo is supported and "-tags testcgo" is added to the go test
+// command line. This means the cgo tests are only added (and hence run) when
+// specifially requested. This configuration is used because spew itself
+// does not require cgo to run even though it does handle certain cgo types
+// specially. Rather than forcing all clients to require cgo and an external
+// C compiler just to run the tests, this scheme makes them optional.
+// +build cgo,testcgo
+
+package spew_test
+
+import (
+ "fmt"
+
+ "github.com/davecgh/go-spew/spew/testdata"
+)
+
+func addCgoDumpTests() {
+ // C char pointer.
+ v := testdata.GetCgoCharPointer()
+ nv := testdata.GetCgoNullCharPointer()
+ pv := &v
+ vcAddr := fmt.Sprintf("%p", v)
+ vAddr := fmt.Sprintf("%p", pv)
+ pvAddr := fmt.Sprintf("%p", &pv)
+ vt := "*testdata._Ctype_char"
+ vs := "116"
+ addDumpTest(v, "("+vt+")("+vcAddr+")("+vs+")\n")
+ addDumpTest(pv, "(*"+vt+")("+vAddr+"->"+vcAddr+")("+vs+")\n")
+ addDumpTest(&pv, "(**"+vt+")("+pvAddr+"->"+vAddr+"->"+vcAddr+")("+vs+")\n")
+ addDumpTest(nv, "("+vt+")(<nil>)\n")
+
+ // C char array.
+ v2, 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")
+}
diff --git a/vendor/github.com/davecgh/go-spew/spew/dumpnocgo_test.go b/vendor/github.com/davecgh/go-spew/spew/dumpnocgo_test.go
new file mode 100644
index 000000000..52a0971fb
--- /dev/null
+++ b/vendor/github.com/davecgh/go-spew/spew/dumpnocgo_test.go
@@ -0,0 +1,26 @@
+// 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.
+}
diff --git a/vendor/github.com/davecgh/go-spew/spew/example_test.go b/vendor/github.com/davecgh/go-spew/spew/example_test.go
new file mode 100644
index 000000000..de6c4e309
--- /dev/null
+++ b/vendor/github.com/davecgh/go-spew/spew/example_test.go
@@ -0,0 +1,226 @@
+/*
+ * Copyright (c) 2013 Dave Collins <dave@davec.name>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+package spew_test
+
+import (
+ "fmt"
+
+ "github.com/davecgh/go-spew/spew"
+)
+
+type Flag int
+
+const (
+ flagOne Flag = iota
+ flagTwo
+)
+
+var flagStrings = map[Flag]string{
+ flagOne: "flagOne",
+ flagTwo: "flagTwo",
+}
+
+func (f Flag) String() string {
+ if s, ok := flagStrings[f]; ok {
+ return s
+ }
+ return fmt.Sprintf("Unknown flag (%d)", int(f))
+}
+
+type Bar struct {
+ 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
+}
diff --git a/vendor/github.com/davecgh/go-spew/spew/format.go b/vendor/github.com/davecgh/go-spew/spew/format.go
new file mode 100644
index 000000000..ecf3b80e2
--- /dev/null
+++ b/vendor/github.com/davecgh/go-spew/spew/format.go
@@ -0,0 +1,419 @@
+/*
+ * Copyright (c) 2013 Dave Collins <dave@davec.name>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+package spew
+
+import (
+ "bytes"
+ "fmt"
+ "reflect"
+ "strconv"
+ "strings"
+)
+
+// supportedFlags is a list of all the character flags supported by fmt package.
+const supportedFlags = "0-+# "
+
+// formatState implements the fmt.Formatter interface and contains information
+// about the state of a formatting operation. The NewFormatter function can
+// be used to get a new Formatter which can be used directly as arguments
+// in standard fmt package printing calls.
+type formatState struct {
+ value interface{}
+ fs fmt.State
+ depth int
+ pointers map[uintptr]int
+ ignoreNextType bool
+ cs *ConfigState
+}
+
+// buildDefaultFormat recreates the original format string without precision
+// and width information to pass in to fmt.Sprintf in the case of an
+// unrecognized type. Unless new types are added to the language, this
+// function won't ever be called.
+func (f *formatState) buildDefaultFormat() (format string) {
+ buf := bytes.NewBuffer(percentBytes)
+
+ for _, flag := range supportedFlags {
+ if f.fs.Flag(int(flag)) {
+ buf.WriteRune(flag)
+ }
+ }
+
+ buf.WriteRune('v')
+
+ format = buf.String()
+ return format
+}
+
+// constructOrigFormat recreates the original format string including precision
+// and width information to pass along to the standard fmt package. This allows
+// automatic deferral of all format strings this package doesn't support.
+func (f *formatState) constructOrigFormat(verb rune) (format string) {
+ buf := bytes.NewBuffer(percentBytes)
+
+ for _, flag := range supportedFlags {
+ if f.fs.Flag(int(flag)) {
+ buf.WriteRune(flag)
+ }
+ }
+
+ if width, ok := f.fs.Width(); ok {
+ buf.WriteString(strconv.Itoa(width))
+ }
+
+ if precision, ok := f.fs.Precision(); ok {
+ buf.Write(precisionBytes)
+ buf.WriteString(strconv.Itoa(precision))
+ }
+
+ buf.WriteRune(verb)
+
+ format = buf.String()
+ return format
+}
+
+// unpackValue returns values inside of non-nil interfaces when possible and
+// ensures that types for values which have been unpacked from an interface
+// are displayed when the show types flag is also set.
+// This is useful for data types like structs, arrays, slices, and maps which
+// can contain varying types packed inside an interface.
+func (f *formatState) unpackValue(v reflect.Value) reflect.Value {
+ if v.Kind() == reflect.Interface {
+ f.ignoreNextType = false
+ if !v.IsNil() {
+ v = v.Elem()
+ }
+ }
+ return v
+}
+
+// formatPtr handles formatting of pointers by indirecting them as necessary.
+func (f *formatState) formatPtr(v reflect.Value) {
+ // Display nil if top level pointer is nil.
+ showTypes := f.fs.Flag('#')
+ if v.IsNil() && (!showTypes || f.ignoreNextType) {
+ f.fs.Write(nilAngleBytes)
+ return
+ }
+
+ // Remove pointers at or below the current depth from map used to detect
+ // circular refs.
+ for k, depth := range f.pointers {
+ if depth >= f.depth {
+ delete(f.pointers, k)
+ }
+ }
+
+ // Keep list of all dereferenced pointers to possibly show later.
+ pointerChain := make([]uintptr, 0)
+
+ // Figure out how many levels of indirection there are by derferencing
+ // pointers and unpacking interfaces down the chain while detecting circular
+ // references.
+ nilFound := false
+ cycleFound := false
+ indirects := 0
+ ve := v
+ for ve.Kind() == reflect.Ptr {
+ if ve.IsNil() {
+ nilFound = true
+ break
+ }
+ indirects++
+ addr := ve.Pointer()
+ pointerChain = append(pointerChain, addr)
+ if pd, ok := f.pointers[addr]; ok && pd < f.depth {
+ cycleFound = true
+ indirects--
+ break
+ }
+ f.pointers[addr] = f.depth
+
+ ve = ve.Elem()
+ if ve.Kind() == reflect.Interface {
+ if ve.IsNil() {
+ nilFound = true
+ break
+ }
+ ve = ve.Elem()
+ }
+ }
+
+ // Display type or indirection level depending on flags.
+ if showTypes && !f.ignoreNextType {
+ f.fs.Write(openParenBytes)
+ f.fs.Write(bytes.Repeat(asteriskBytes, indirects))
+ f.fs.Write([]byte(ve.Type().String()))
+ f.fs.Write(closeParenBytes)
+ } else {
+ if nilFound || cycleFound {
+ indirects += strings.Count(ve.Type().String(), "*")
+ }
+ f.fs.Write(openAngleBytes)
+ f.fs.Write([]byte(strings.Repeat("*", indirects)))
+ f.fs.Write(closeAngleBytes)
+ }
+
+ // Display pointer information depending on flags.
+ if f.fs.Flag('+') && (len(pointerChain) > 0) {
+ f.fs.Write(openParenBytes)
+ for i, addr := range pointerChain {
+ if i > 0 {
+ f.fs.Write(pointerChainBytes)
+ }
+ printHexPtr(f.fs, addr)
+ }
+ f.fs.Write(closeParenBytes)
+ }
+
+ // Display dereferenced value.
+ switch {
+ case nilFound == true:
+ f.fs.Write(nilAngleBytes)
+
+ case cycleFound == true:
+ f.fs.Write(circularShortBytes)
+
+ default:
+ f.ignoreNextType = true
+ f.format(ve)
+ }
+}
+
+// format is the main workhorse for providing the Formatter interface. It
+// uses the passed reflect value to figure out what kind of object we are
+// dealing with and formats it appropriately. It is a recursive function,
+// however circular data structures are detected and handled properly.
+func (f *formatState) format(v reflect.Value) {
+ // Handle invalid reflect values immediately.
+ kind := v.Kind()
+ if kind == reflect.Invalid {
+ f.fs.Write(invalidAngleBytes)
+ return
+ }
+
+ // Handle pointers specially.
+ if kind == reflect.Ptr {
+ f.formatPtr(v)
+ return
+ }
+
+ // Print type information unless already handled elsewhere.
+ if !f.ignoreNextType && f.fs.Flag('#') {
+ f.fs.Write(openParenBytes)
+ f.fs.Write([]byte(v.Type().String()))
+ f.fs.Write(closeParenBytes)
+ }
+ f.ignoreNextType = false
+
+ // Call Stringer/error interfaces if they exist and the handle methods
+ // flag is enabled.
+ if !f.cs.DisableMethods {
+ if (kind != reflect.Invalid) && (kind != reflect.Interface) {
+ if handled := handleMethods(f.cs, f.fs, v); handled {
+ return
+ }
+ }
+ }
+
+ switch kind {
+ case reflect.Invalid:
+ // Do nothing. We should never get here since invalid has already
+ // been handled above.
+
+ case reflect.Bool:
+ printBool(f.fs, v.Bool())
+
+ case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
+ printInt(f.fs, v.Int(), 10)
+
+ case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
+ printUint(f.fs, v.Uint(), 10)
+
+ case reflect.Float32:
+ printFloat(f.fs, v.Float(), 32)
+
+ case reflect.Float64:
+ printFloat(f.fs, v.Float(), 64)
+
+ case reflect.Complex64:
+ printComplex(f.fs, v.Complex(), 32)
+
+ case reflect.Complex128:
+ printComplex(f.fs, v.Complex(), 64)
+
+ case reflect.Slice:
+ if v.IsNil() {
+ f.fs.Write(nilAngleBytes)
+ break
+ }
+ fallthrough
+
+ case reflect.Array:
+ f.fs.Write(openBracketBytes)
+ f.depth++
+ if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) {
+ f.fs.Write(maxShortBytes)
+ } else {
+ numEntries := v.Len()
+ for i := 0; i < numEntries; i++ {
+ if i > 0 {
+ f.fs.Write(spaceBytes)
+ }
+ f.ignoreNextType = true
+ f.format(f.unpackValue(v.Index(i)))
+ }
+ }
+ f.depth--
+ f.fs.Write(closeBracketBytes)
+
+ case reflect.String:
+ f.fs.Write([]byte(v.String()))
+
+ case reflect.Interface:
+ // The only time we should get here is for nil interfaces due to
+ // unpackValue calls.
+ if v.IsNil() {
+ f.fs.Write(nilAngleBytes)
+ }
+
+ case reflect.Ptr:
+ // Do nothing. We should never get here since pointers have already
+ // been handled above.
+
+ case reflect.Map:
+ // nil maps should be indicated as different than empty maps
+ if v.IsNil() {
+ f.fs.Write(nilAngleBytes)
+ break
+ }
+
+ f.fs.Write(openMapBytes)
+ f.depth++
+ if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) {
+ f.fs.Write(maxShortBytes)
+ } else {
+ keys := v.MapKeys()
+ if f.cs.SortKeys {
+ sortValues(keys, f.cs)
+ }
+ for i, key := range keys {
+ if i > 0 {
+ f.fs.Write(spaceBytes)
+ }
+ f.ignoreNextType = true
+ f.format(f.unpackValue(key))
+ f.fs.Write(colonBytes)
+ f.ignoreNextType = true
+ f.format(f.unpackValue(v.MapIndex(key)))
+ }
+ }
+ f.depth--
+ f.fs.Write(closeMapBytes)
+
+ case reflect.Struct:
+ numFields := v.NumField()
+ f.fs.Write(openBraceBytes)
+ f.depth++
+ if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) {
+ f.fs.Write(maxShortBytes)
+ } else {
+ vt := v.Type()
+ for i := 0; i < numFields; i++ {
+ if i > 0 {
+ f.fs.Write(spaceBytes)
+ }
+ vtf := vt.Field(i)
+ if f.fs.Flag('+') || f.fs.Flag('#') {
+ f.fs.Write([]byte(vtf.Name))
+ f.fs.Write(colonBytes)
+ }
+ f.format(f.unpackValue(v.Field(i)))
+ }
+ }
+ f.depth--
+ f.fs.Write(closeBraceBytes)
+
+ case reflect.Uintptr:
+ printHexPtr(f.fs, uintptr(v.Uint()))
+
+ case reflect.UnsafePointer, reflect.Chan, reflect.Func:
+ printHexPtr(f.fs, v.Pointer())
+
+ // There were not any other types at the time this code was written, but
+ // fall back to letting the default fmt package handle it if any get added.
+ default:
+ format := f.buildDefaultFormat()
+ if v.CanInterface() {
+ fmt.Fprintf(f.fs, format, v.Interface())
+ } else {
+ fmt.Fprintf(f.fs, format, v.String())
+ }
+ }
+}
+
+// Format satisfies the fmt.Formatter interface. See NewFormatter for usage
+// details.
+func (f *formatState) Format(fs fmt.State, verb rune) {
+ f.fs = fs
+
+ // Use standard formatting for verbs that are not v.
+ if verb != 'v' {
+ format := f.constructOrigFormat(verb)
+ fmt.Fprintf(fs, format, f.value)
+ return
+ }
+
+ if f.value == nil {
+ if fs.Flag('#') {
+ fs.Write(interfaceBytes)
+ }
+ fs.Write(nilAngleBytes)
+ return
+ }
+
+ f.format(reflect.ValueOf(f.value))
+}
+
+// newFormatter is a helper function to consolidate the logic from the various
+// public methods which take varying config states.
+func newFormatter(cs *ConfigState, v interface{}) fmt.Formatter {
+ fs := &formatState{value: v, cs: cs}
+ fs.pointers = make(map[uintptr]int)
+ return fs
+}
+
+/*
+NewFormatter returns a custom formatter that satisfies the fmt.Formatter
+interface. As a result, it integrates cleanly with standard fmt package
+printing functions. The formatter is useful for inline printing of smaller data
+types similar to the standard %v format specifier.
+
+The custom formatter only responds to the %v (most compact), %+v (adds pointer
+addresses), %#v (adds types), or %#+v (adds types and pointer addresses) verb
+combinations. Any other verbs such as %x and %q will be sent to the the
+standard fmt package for formatting. In addition, the custom formatter ignores
+the width and precision arguments (however they will still work on the format
+specifiers not handled by the custom formatter).
+
+Typically this function shouldn't be called directly. It is much easier to make
+use of the custom formatter by calling one of the convenience functions such as
+Printf, Println, or Fprintf.
+*/
+func NewFormatter(v interface{}) fmt.Formatter {
+ return newFormatter(&Config, v)
+}
diff --git a/vendor/github.com/davecgh/go-spew/spew/format_test.go b/vendor/github.com/davecgh/go-spew/spew/format_test.go
new file mode 100644
index 000000000..b664b3f13
--- /dev/null
+++ b/vendor/github.com/davecgh/go-spew/spew/format_test.go
@@ -0,0 +1,1558 @@
+/*
+ * Copyright (c) 2013 Dave Collins <dave@davec.name>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+Test Summary:
+NOTE: For each test, a nil pointer, a single pointer and double pointer to the
+base test element are also tested to ensure proper indirection across all types.
+
+- Max int8, int16, int32, int64, int
+- Max uint8, uint16, uint32, uint64, uint
+- Boolean true and false
+- Standard complex64 and complex128
+- Array containing standard ints
+- Array containing type with custom formatter on pointer receiver only
+- Array containing interfaces
+- Slice containing standard float32 values
+- Slice containing type with custom formatter on pointer receiver only
+- Slice containing interfaces
+- Nil slice
+- Standard string
+- Nil interface
+- Sub-interface
+- Map with string keys and int vals
+- Map with custom formatter type on pointer receiver only keys and vals
+- Map with interface keys and values
+- Map with nil interface value
+- Struct with primitives
+- Struct that contains another struct
+- Struct that contains custom type with Stringer pointer interface via both
+ exported and unexported fields
+- Struct that contains embedded struct and field to same struct
+- Uintptr to 0 (null pointer)
+- Uintptr address of real variable
+- Unsafe.Pointer to 0 (null pointer)
+- Unsafe.Pointer to address of real variable
+- Nil channel
+- Standard int channel
+- Function with no params and no returns
+- Function with param and no returns
+- Function with multiple params and multiple returns
+- Struct that is circular through self referencing
+- Structs that are circular through cross referencing
+- Structs that are indirectly circular
+- Type that panics in its Stringer interface
+- Type that has a custom Error interface
+- %x passthrough with uint
+- %#x passthrough with uint
+- %f passthrough with precision
+- %f passthrough with width and precision
+- %d passthrough with width
+- %q passthrough with string
+*/
+
+package spew_test
+
+import (
+ "bytes"
+ "fmt"
+ "testing"
+ "unsafe"
+
+ "github.com/davecgh/go-spew/spew"
+)
+
+// formatterTest is used to describe a test to be perfomed against NewFormatter.
+type formatterTest struct {
+ format string
+ in interface{}
+ wants []string
+}
+
+// formatterTests houses all of the tests to be performed against NewFormatter.
+var formatterTests = make([]formatterTest, 0)
+
+// addFormatterTest is a helper method to append the passed input and desired
+// result to formatterTests.
+func addFormatterTest(format string, in interface{}, wants ...string) {
+ test := formatterTest{format, in, wants}
+ formatterTests = append(formatterTests, test)
+}
+
+func addIntFormatterTests() {
+ // Max int8.
+ v := int8(127)
+ nv := (*int8)(nil)
+ pv := &v
+ vAddr := fmt.Sprintf("%p", pv)
+ pvAddr := fmt.Sprintf("%p", &pv)
+ vt := "int8"
+ vs := "127"
+ addFormatterTest("%v", v, vs)
+ addFormatterTest("%v", pv, "<*>"+vs)
+ addFormatterTest("%v", &pv, "<**>"+vs)
+ addFormatterTest("%v", nv, "<nil>")
+ addFormatterTest("%+v", v, vs)
+ addFormatterTest("%+v", pv, "<*>("+vAddr+")"+vs)
+ addFormatterTest("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs)
+ addFormatterTest("%+v", nv, "<nil>")
+ addFormatterTest("%#v", v, "("+vt+")"+vs)
+ addFormatterTest("%#v", pv, "(*"+vt+")"+vs)
+ addFormatterTest("%#v", &pv, "(**"+vt+")"+vs)
+ addFormatterTest("%#v", nv, "(*"+vt+")"+"<nil>")
+ addFormatterTest("%#+v", v, "("+vt+")"+vs)
+ addFormatterTest("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs)
+ addFormatterTest("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs)
+ addFormatterTest("%#+v", nv, "(*"+vt+")"+"<nil>")
+
+ // Max int16.
+ v2 := int16(32767)
+ nv2 := (*int16)(nil)
+ pv2 := &v2
+ v2Addr := fmt.Sprintf("%p", pv2)
+ pv2Addr := fmt.Sprintf("%p", &pv2)
+ v2t := "int16"
+ v2s := "32767"
+ addFormatterTest("%v", v2, v2s)
+ addFormatterTest("%v", pv2, "<*>"+v2s)
+ addFormatterTest("%v", &pv2, "<**>"+v2s)
+ addFormatterTest("%v", nv2, "<nil>")
+ addFormatterTest("%+v", v2, v2s)
+ addFormatterTest("%+v", pv2, "<*>("+v2Addr+")"+v2s)
+ addFormatterTest("%+v", &pv2, "<**>("+pv2Addr+"->"+v2Addr+")"+v2s)
+ addFormatterTest("%+v", nv2, "<nil>")
+ addFormatterTest("%#v", v2, "("+v2t+")"+v2s)
+ addFormatterTest("%#v", pv2, "(*"+v2t+")"+v2s)
+ addFormatterTest("%#v", &pv2, "(**"+v2t+")"+v2s)
+ addFormatterTest("%#v", nv2, "(*"+v2t+")"+"<nil>")
+ addFormatterTest("%#+v", v2, "("+v2t+")"+v2s)
+ addFormatterTest("%#+v", pv2, "(*"+v2t+")("+v2Addr+")"+v2s)
+ addFormatterTest("%#+v", &pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")"+v2s)
+ addFormatterTest("%#+v", nv2, "(*"+v2t+")"+"<nil>")
+
+ // Max int32.
+ v3 := int32(2147483647)
+ nv3 := (*int32)(nil)
+ pv3 := &v3
+ v3Addr := fmt.Sprintf("%p", pv3)
+ pv3Addr := fmt.Sprintf("%p", &pv3)
+ v3t := "int32"
+ v3s := "2147483647"
+ addFormatterTest("%v", v3, v3s)
+ addFormatterTest("%v", pv3, "<*>"+v3s)
+ addFormatterTest("%v", &pv3, "<**>"+v3s)
+ addFormatterTest("%v", nv3, "<nil>")
+ addFormatterTest("%+v", v3, v3s)
+ addFormatterTest("%+v", pv3, "<*>("+v3Addr+")"+v3s)
+ addFormatterTest("%+v", &pv3, "<**>("+pv3Addr+"->"+v3Addr+")"+v3s)
+ addFormatterTest("%+v", nv3, "<nil>")
+ addFormatterTest("%#v", v3, "("+v3t+")"+v3s)
+ addFormatterTest("%#v", pv3, "(*"+v3t+")"+v3s)
+ addFormatterTest("%#v", &pv3, "(**"+v3t+")"+v3s)
+ addFormatterTest("%#v", nv3, "(*"+v3t+")"+"<nil>")
+ addFormatterTest("%#+v", v3, "("+v3t+")"+v3s)
+ addFormatterTest("%#+v", pv3, "(*"+v3t+")("+v3Addr+")"+v3s)
+ addFormatterTest("%#+v", &pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")"+v3s)
+ addFormatterTest("%#v", nv3, "(*"+v3t+")"+"<nil>")
+
+ // Max int64.
+ v4 := int64(9223372036854775807)
+ nv4 := (*int64)(nil)
+ pv4 := &v4
+ v4Addr := fmt.Sprintf("%p", pv4)
+ pv4Addr := fmt.Sprintf("%p", &pv4)
+ v4t := "int64"
+ v4s := "9223372036854775807"
+ addFormatterTest("%v", v4, v4s)
+ addFormatterTest("%v", pv4, "<*>"+v4s)
+ addFormatterTest("%v", &pv4, "<**>"+v4s)
+ addFormatterTest("%v", nv4, "<nil>")
+ addFormatterTest("%+v", v4, v4s)
+ addFormatterTest("%+v", pv4, "<*>("+v4Addr+")"+v4s)
+ addFormatterTest("%+v", &pv4, "<**>("+pv4Addr+"->"+v4Addr+")"+v4s)
+ addFormatterTest("%+v", nv4, "<nil>")
+ addFormatterTest("%#v", v4, "("+v4t+")"+v4s)
+ addFormatterTest("%#v", pv4, "(*"+v4t+")"+v4s)
+ addFormatterTest("%#v", &pv4, "(**"+v4t+")"+v4s)
+ addFormatterTest("%#v", nv4, "(*"+v4t+")"+"<nil>")
+ addFormatterTest("%#+v", v4, "("+v4t+")"+v4s)
+ addFormatterTest("%#+v", pv4, "(*"+v4t+")("+v4Addr+")"+v4s)
+ addFormatterTest("%#+v", &pv4, "(**"+v4t+")("+pv4Addr+"->"+v4Addr+")"+v4s)
+ addFormatterTest("%#+v", nv4, "(*"+v4t+")"+"<nil>")
+
+ // Max int.
+ v5 := int(2147483647)
+ nv5 := (*int)(nil)
+ pv5 := &v5
+ v5Addr := fmt.Sprintf("%p", pv5)
+ pv5Addr := fmt.Sprintf("%p", &pv5)
+ v5t := "int"
+ v5s := "2147483647"
+ addFormatterTest("%v", v5, v5s)
+ addFormatterTest("%v", pv5, "<*>"+v5s)
+ addFormatterTest("%v", &pv5, "<**>"+v5s)
+ addFormatterTest("%v", nv5, "<nil>")
+ addFormatterTest("%+v", v5, v5s)
+ addFormatterTest("%+v", pv5, "<*>("+v5Addr+")"+v5s)
+ addFormatterTest("%+v", &pv5, "<**>("+pv5Addr+"->"+v5Addr+")"+v5s)
+ addFormatterTest("%+v", nv5, "<nil>")
+ addFormatterTest("%#v", v5, "("+v5t+")"+v5s)
+ addFormatterTest("%#v", pv5, "(*"+v5t+")"+v5s)
+ addFormatterTest("%#v", &pv5, "(**"+v5t+")"+v5s)
+ addFormatterTest("%#v", nv5, "(*"+v5t+")"+"<nil>")
+ addFormatterTest("%#+v", v5, "("+v5t+")"+v5s)
+ addFormatterTest("%#+v", pv5, "(*"+v5t+")("+v5Addr+")"+v5s)
+ addFormatterTest("%#+v", &pv5, "(**"+v5t+")("+pv5Addr+"->"+v5Addr+")"+v5s)
+ addFormatterTest("%#+v", nv5, "(*"+v5t+")"+"<nil>")
+}
+
+func addUintFormatterTests() {
+ // Max uint8.
+ v := uint8(255)
+ nv := (*uint8)(nil)
+ pv := &v
+ vAddr := fmt.Sprintf("%p", pv)
+ pvAddr := fmt.Sprintf("%p", &pv)
+ vt := "uint8"
+ vs := "255"
+ addFormatterTest("%v", v, vs)
+ addFormatterTest("%v", pv, "<*>"+vs)
+ addFormatterTest("%v", &pv, "<**>"+vs)
+ addFormatterTest("%v", nv, "<nil>")
+ addFormatterTest("%+v", v, vs)
+ addFormatterTest("%+v", pv, "<*>("+vAddr+")"+vs)
+ addFormatterTest("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs)
+ addFormatterTest("%+v", nv, "<nil>")
+ addFormatterTest("%#v", v, "("+vt+")"+vs)
+ addFormatterTest("%#v", pv, "(*"+vt+")"+vs)
+ addFormatterTest("%#v", &pv, "(**"+vt+")"+vs)
+ addFormatterTest("%#v", nv, "(*"+vt+")"+"<nil>")
+ addFormatterTest("%#+v", v, "("+vt+")"+vs)
+ addFormatterTest("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs)
+ addFormatterTest("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs)
+ addFormatterTest("%#+v", nv, "(*"+vt+")"+"<nil>")
+
+ // Max uint16.
+ v2 := uint16(65535)
+ nv2 := (*uint16)(nil)
+ pv2 := &v2
+ v2Addr := fmt.Sprintf("%p", pv2)
+ pv2Addr := fmt.Sprintf("%p", &pv2)
+ v2t := "uint16"
+ v2s := "65535"
+ addFormatterTest("%v", v2, v2s)
+ addFormatterTest("%v", pv2, "<*>"+v2s)
+ addFormatterTest("%v", &pv2, "<**>"+v2s)
+ addFormatterTest("%v", nv2, "<nil>")
+ addFormatterTest("%+v", v2, v2s)
+ addFormatterTest("%+v", pv2, "<*>("+v2Addr+")"+v2s)
+ addFormatterTest("%+v", &pv2, "<**>("+pv2Addr+"->"+v2Addr+")"+v2s)
+ addFormatterTest("%+v", nv2, "<nil>")
+ addFormatterTest("%#v", v2, "("+v2t+")"+v2s)
+ addFormatterTest("%#v", pv2, "(*"+v2t+")"+v2s)
+ addFormatterTest("%#v", &pv2, "(**"+v2t+")"+v2s)
+ addFormatterTest("%#v", nv2, "(*"+v2t+")"+"<nil>")
+ addFormatterTest("%#+v", v2, "("+v2t+")"+v2s)
+ addFormatterTest("%#+v", pv2, "(*"+v2t+")("+v2Addr+")"+v2s)
+ addFormatterTest("%#+v", &pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")"+v2s)
+ addFormatterTest("%#+v", nv2, "(*"+v2t+")"+"<nil>")
+
+ // Max uint32.
+ v3 := uint32(4294967295)
+ nv3 := (*uint32)(nil)
+ pv3 := &v3
+ v3Addr := fmt.Sprintf("%p", pv3)
+ pv3Addr := fmt.Sprintf("%p", &pv3)
+ v3t := "uint32"
+ v3s := "4294967295"
+ addFormatterTest("%v", v3, v3s)
+ addFormatterTest("%v", pv3, "<*>"+v3s)
+ addFormatterTest("%v", &pv3, "<**>"+v3s)
+ addFormatterTest("%v", nv3, "<nil>")
+ addFormatterTest("%+v", v3, v3s)
+ addFormatterTest("%+v", pv3, "<*>("+v3Addr+")"+v3s)
+ addFormatterTest("%+v", &pv3, "<**>("+pv3Addr+"->"+v3Addr+")"+v3s)
+ addFormatterTest("%+v", nv3, "<nil>")
+ addFormatterTest("%#v", v3, "("+v3t+")"+v3s)
+ addFormatterTest("%#v", pv3, "(*"+v3t+")"+v3s)
+ addFormatterTest("%#v", &pv3, "(**"+v3t+")"+v3s)
+ addFormatterTest("%#v", nv3, "(*"+v3t+")"+"<nil>")
+ addFormatterTest("%#+v", v3, "("+v3t+")"+v3s)
+ addFormatterTest("%#+v", pv3, "(*"+v3t+")("+v3Addr+")"+v3s)
+ addFormatterTest("%#+v", &pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")"+v3s)
+ addFormatterTest("%#v", nv3, "(*"+v3t+")"+"<nil>")
+
+ // Max uint64.
+ v4 := uint64(18446744073709551615)
+ nv4 := (*uint64)(nil)
+ pv4 := &v4
+ v4Addr := fmt.Sprintf("%p", pv4)
+ pv4Addr := fmt.Sprintf("%p", &pv4)
+ v4t := "uint64"
+ v4s := "18446744073709551615"
+ addFormatterTest("%v", v4, v4s)
+ addFormatterTest("%v", pv4, "<*>"+v4s)
+ addFormatterTest("%v", &pv4, "<**>"+v4s)
+ addFormatterTest("%v", nv4, "<nil>")
+ addFormatterTest("%+v", v4, v4s)
+ addFormatterTest("%+v", pv4, "<*>("+v4Addr+")"+v4s)
+ addFormatterTest("%+v", &pv4, "<**>("+pv4Addr+"->"+v4Addr+")"+v4s)
+ addFormatterTest("%+v", nv4, "<nil>")
+ addFormatterTest("%#v", v4, "("+v4t+")"+v4s)
+ addFormatterTest("%#v", pv4, "(*"+v4t+")"+v4s)
+ addFormatterTest("%#v", &pv4, "(**"+v4t+")"+v4s)
+ addFormatterTest("%#v", nv4, "(*"+v4t+")"+"<nil>")
+ addFormatterTest("%#+v", v4, "("+v4t+")"+v4s)
+ addFormatterTest("%#+v", pv4, "(*"+v4t+")("+v4Addr+")"+v4s)
+ addFormatterTest("%#+v", &pv4, "(**"+v4t+")("+pv4Addr+"->"+v4Addr+")"+v4s)
+ addFormatterTest("%#+v", nv4, "(*"+v4t+")"+"<nil>")
+
+ // Max uint.
+ v5 := uint(4294967295)
+ nv5 := (*uint)(nil)
+ pv5 := &v5
+ v5Addr := fmt.Sprintf("%p", pv5)
+ pv5Addr := fmt.Sprintf("%p", &pv5)
+ v5t := "uint"
+ v5s := "4294967295"
+ addFormatterTest("%v", v5, v5s)
+ addFormatterTest("%v", pv5, "<*>"+v5s)
+ addFormatterTest("%v", &pv5, "<**>"+v5s)
+ addFormatterTest("%v", nv5, "<nil>")
+ addFormatterTest("%+v", v5, v5s)
+ addFormatterTest("%+v", pv5, "<*>("+v5Addr+")"+v5s)
+ addFormatterTest("%+v", &pv5, "<**>("+pv5Addr+"->"+v5Addr+")"+v5s)
+ addFormatterTest("%+v", nv5, "<nil>")
+ addFormatterTest("%#v", v5, "("+v5t+")"+v5s)
+ addFormatterTest("%#v", pv5, "(*"+v5t+")"+v5s)
+ addFormatterTest("%#v", &pv5, "(**"+v5t+")"+v5s)
+ addFormatterTest("%#v", nv5, "(*"+v5t+")"+"<nil>")
+ addFormatterTest("%#+v", v5, "("+v5t+")"+v5s)
+ addFormatterTest("%#+v", pv5, "(*"+v5t+")("+v5Addr+")"+v5s)
+ addFormatterTest("%#+v", &pv5, "(**"+v5t+")("+pv5Addr+"->"+v5Addr+")"+v5s)
+ addFormatterTest("%#v", nv5, "(*"+v5t+")"+"<nil>")
+}
+
+func addBoolFormatterTests() {
+ // Boolean true.
+ v := bool(true)
+ nv := (*bool)(nil)
+ pv := &v
+ vAddr := fmt.Sprintf("%p", pv)
+ pvAddr := fmt.Sprintf("%p", &pv)
+ vt := "bool"
+ vs := "true"
+ addFormatterTest("%v", v, vs)
+ addFormatterTest("%v", pv, "<*>"+vs)
+ addFormatterTest("%v", &pv, "<**>"+vs)
+ addFormatterTest("%v", nv, "<nil>")
+ addFormatterTest("%+v", v, vs)
+ addFormatterTest("%+v", pv, "<*>("+vAddr+")"+vs)
+ addFormatterTest("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs)
+ addFormatterTest("%+v", nv, "<nil>")
+ addFormatterTest("%#v", v, "("+vt+")"+vs)
+ addFormatterTest("%#v", pv, "(*"+vt+")"+vs)
+ addFormatterTest("%#v", &pv, "(**"+vt+")"+vs)
+ addFormatterTest("%#v", nv, "(*"+vt+")"+"<nil>")
+ addFormatterTest("%#+v", v, "("+vt+")"+vs)
+ addFormatterTest("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs)
+ addFormatterTest("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs)
+ addFormatterTest("%#+v", nv, "(*"+vt+")"+"<nil>")
+
+ // Boolean false.
+ v2 := bool(false)
+ pv2 := &v2
+ v2Addr := fmt.Sprintf("%p", pv2)
+ pv2Addr := fmt.Sprintf("%p", &pv2)
+ v2t := "bool"
+ v2s := "false"
+ addFormatterTest("%v", v2, v2s)
+ addFormatterTest("%v", pv2, "<*>"+v2s)
+ addFormatterTest("%v", &pv2, "<**>"+v2s)
+ addFormatterTest("%+v", v2, v2s)
+ addFormatterTest("%+v", pv2, "<*>("+v2Addr+")"+v2s)
+ addFormatterTest("%+v", &pv2, "<**>("+pv2Addr+"->"+v2Addr+")"+v2s)
+ addFormatterTest("%#v", v2, "("+v2t+")"+v2s)
+ addFormatterTest("%#v", pv2, "(*"+v2t+")"+v2s)
+ addFormatterTest("%#v", &pv2, "(**"+v2t+")"+v2s)
+ addFormatterTest("%#+v", v2, "("+v2t+")"+v2s)
+ addFormatterTest("%#+v", pv2, "(*"+v2t+")("+v2Addr+")"+v2s)
+ addFormatterTest("%#+v", &pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")"+v2s)
+}
+
+func addFloatFormatterTests() {
+ // Standard float32.
+ v := float32(3.1415)
+ nv := (*float32)(nil)
+ pv := &v
+ vAddr := fmt.Sprintf("%p", pv)
+ pvAddr := fmt.Sprintf("%p", &pv)
+ vt := "float32"
+ vs := "3.1415"
+ addFormatterTest("%v", v, vs)
+ addFormatterTest("%v", pv, "<*>"+vs)
+ addFormatterTest("%v", &pv, "<**>"+vs)
+ addFormatterTest("%v", nv, "<nil>")
+ addFormatterTest("%+v", v, vs)
+ addFormatterTest("%+v", pv, "<*>("+vAddr+")"+vs)
+ addFormatterTest("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs)
+ addFormatterTest("%+v", nv, "<nil>")
+ addFormatterTest("%#v", v, "("+vt+")"+vs)
+ addFormatterTest("%#v", pv, "(*"+vt+")"+vs)
+ addFormatterTest("%#v", &pv, "(**"+vt+")"+vs)
+ addFormatterTest("%#v", nv, "(*"+vt+")"+"<nil>")
+ addFormatterTest("%#+v", v, "("+vt+")"+vs)
+ addFormatterTest("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs)
+ addFormatterTest("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs)
+ addFormatterTest("%#+v", nv, "(*"+vt+")"+"<nil>")
+
+ // Standard float64.
+ v2 := float64(3.1415926)
+ nv2 := (*float64)(nil)
+ pv2 := &v2
+ v2Addr := fmt.Sprintf("%p", pv2)
+ pv2Addr := fmt.Sprintf("%p", &pv2)
+ v2t := "float64"
+ v2s := "3.1415926"
+ addFormatterTest("%v", v2, v2s)
+ addFormatterTest("%v", pv2, "<*>"+v2s)
+ addFormatterTest("%v", &pv2, "<**>"+v2s)
+ addFormatterTest("%+v", nv2, "<nil>")
+ addFormatterTest("%+v", v2, v2s)
+ addFormatterTest("%+v", pv2, "<*>("+v2Addr+")"+v2s)
+ addFormatterTest("%+v", &pv2, "<**>("+pv2Addr+"->"+v2Addr+")"+v2s)
+ addFormatterTest("%+v", nv2, "<nil>")
+ addFormatterTest("%#v", v2, "("+v2t+")"+v2s)
+ addFormatterTest("%#v", pv2, "(*"+v2t+")"+v2s)
+ addFormatterTest("%#v", &pv2, "(**"+v2t+")"+v2s)
+ addFormatterTest("%#v", nv2, "(*"+v2t+")"+"<nil>")
+ addFormatterTest("%#+v", v2, "("+v2t+")"+v2s)
+ addFormatterTest("%#+v", pv2, "(*"+v2t+")("+v2Addr+")"+v2s)
+ addFormatterTest("%#+v", &pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")"+v2s)
+ addFormatterTest("%#+v", nv2, "(*"+v2t+")"+"<nil>")
+}
+
+func addComplexFormatterTests() {
+ // Standard complex64.
+ v := complex(float32(6), -2)
+ nv := (*complex64)(nil)
+ pv := &v
+ vAddr := fmt.Sprintf("%p", pv)
+ pvAddr := fmt.Sprintf("%p", &pv)
+ vt := "complex64"
+ vs := "(6-2i)"
+ addFormatterTest("%v", v, vs)
+ addFormatterTest("%v", pv, "<*>"+vs)
+ addFormatterTest("%v", &pv, "<**>"+vs)
+ addFormatterTest("%+v", nv, "<nil>")
+ addFormatterTest("%+v", v, vs)
+ addFormatterTest("%+v", pv, "<*>("+vAddr+")"+vs)
+ addFormatterTest("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs)
+ addFormatterTest("%+v", nv, "<nil>")
+ addFormatterTest("%#v", v, "("+vt+")"+vs)
+ addFormatterTest("%#v", pv, "(*"+vt+")"+vs)
+ addFormatterTest("%#v", &pv, "(**"+vt+")"+vs)
+ addFormatterTest("%#v", nv, "(*"+vt+")"+"<nil>")
+ addFormatterTest("%#+v", v, "("+vt+")"+vs)
+ addFormatterTest("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs)
+ addFormatterTest("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs)
+ addFormatterTest("%#+v", nv, "(*"+vt+")"+"<nil>")
+
+ // Standard complex128.
+ v2 := complex(float64(-6), 2)
+ nv2 := (*complex128)(nil)
+ pv2 := &v2
+ v2Addr := fmt.Sprintf("%p", pv2)
+ pv2Addr := fmt.Sprintf("%p", &pv2)
+ v2t := "complex128"
+ v2s := "(-6+2i)"
+ addFormatterTest("%v", v2, v2s)
+ addFormatterTest("%v", pv2, "<*>"+v2s)
+ addFormatterTest("%v", &pv2, "<**>"+v2s)
+ addFormatterTest("%+v", nv2, "<nil>")
+ addFormatterTest("%+v", v2, v2s)
+ addFormatterTest("%+v", pv2, "<*>("+v2Addr+")"+v2s)
+ addFormatterTest("%+v", &pv2, "<**>("+pv2Addr+"->"+v2Addr+")"+v2s)
+ addFormatterTest("%+v", nv2, "<nil>")
+ addFormatterTest("%#v", v2, "("+v2t+")"+v2s)
+ addFormatterTest("%#v", pv2, "(*"+v2t+")"+v2s)
+ addFormatterTest("%#v", &pv2, "(**"+v2t+")"+v2s)
+ addFormatterTest("%#v", nv2, "(*"+v2t+")"+"<nil>")
+ addFormatterTest("%#+v", v2, "("+v2t+")"+v2s)
+ addFormatterTest("%#+v", pv2, "(*"+v2t+")("+v2Addr+")"+v2s)
+ addFormatterTest("%#+v", &pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")"+v2s)
+ addFormatterTest("%#+v", nv2, "(*"+v2t+")"+"<nil>")
+}
+
+func addArrayFormatterTests() {
+ // Array containing standard ints.
+ v := [3]int{1, 2, 3}
+ nv := (*[3]int)(nil)
+ pv := &v
+ vAddr := fmt.Sprintf("%p", pv)
+ pvAddr := fmt.Sprintf("%p", &pv)
+ vt := "[3]int"
+ vs := "[1 2 3]"
+ addFormatterTest("%v", v, vs)
+ addFormatterTest("%v", pv, "<*>"+vs)
+ addFormatterTest("%v", &pv, "<**>"+vs)
+ addFormatterTest("%+v", nv, "<nil>")
+ addFormatterTest("%+v", v, vs)
+ addFormatterTest("%+v", pv, "<*>("+vAddr+")"+vs)
+ addFormatterTest("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs)
+ addFormatterTest("%+v", nv, "<nil>")
+ addFormatterTest("%#v", v, "("+vt+")"+vs)
+ addFormatterTest("%#v", pv, "(*"+vt+")"+vs)
+ addFormatterTest("%#v", &pv, "(**"+vt+")"+vs)
+ addFormatterTest("%#v", nv, "(*"+vt+")"+"<nil>")
+ addFormatterTest("%#+v", v, "("+vt+")"+vs)
+ addFormatterTest("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs)
+ addFormatterTest("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs)
+ addFormatterTest("%#+v", nv, "(*"+vt+")"+"<nil>")
+
+ // Array containing type with custom formatter on pointer receiver only.
+ v2 := [3]pstringer{"1", "2", "3"}
+ nv2 := (*[3]pstringer)(nil)
+ pv2 := &v2
+ v2Addr := fmt.Sprintf("%p", pv2)
+ pv2Addr := fmt.Sprintf("%p", &pv2)
+ v2t := "[3]spew_test.pstringer"
+ v2sp := "[stringer 1 stringer 2 stringer 3]"
+ v2s := v2sp
+ if spew.UnsafeDisabled {
+ v2s = "[1 2 3]"
+ }
+ addFormatterTest("%v", v2, v2s)
+ addFormatterTest("%v", pv2, "<*>"+v2sp)
+ addFormatterTest("%v", &pv2, "<**>"+v2sp)
+ addFormatterTest("%+v", nv2, "<nil>")
+ addFormatterTest("%+v", v2, v2s)
+ addFormatterTest("%+v", pv2, "<*>("+v2Addr+")"+v2sp)
+ addFormatterTest("%+v", &pv2, "<**>("+pv2Addr+"->"+v2Addr+")"+v2sp)
+ addFormatterTest("%+v", nv2, "<nil>")
+ addFormatterTest("%#v", v2, "("+v2t+")"+v2s)
+ addFormatterTest("%#v", pv2, "(*"+v2t+")"+v2sp)
+ addFormatterTest("%#v", &pv2, "(**"+v2t+")"+v2sp)
+ addFormatterTest("%#v", nv2, "(*"+v2t+")"+"<nil>")
+ addFormatterTest("%#+v", v2, "("+v2t+")"+v2s)
+ addFormatterTest("%#+v", pv2, "(*"+v2t+")("+v2Addr+")"+v2sp)
+ addFormatterTest("%#+v", &pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")"+v2sp)
+ addFormatterTest("%#+v", nv2, "(*"+v2t+")"+"<nil>")
+
+ // Array containing interfaces.
+ v3 := [3]interface{}{"one", int(2), uint(3)}
+ nv3 := (*[3]interface{})(nil)
+ pv3 := &v3
+ v3Addr := fmt.Sprintf("%p", pv3)
+ pv3Addr := fmt.Sprintf("%p", &pv3)
+ v3t := "[3]interface {}"
+ v3t2 := "string"
+ v3t3 := "int"
+ v3t4 := "uint"
+ v3s := "[one 2 3]"
+ v3s2 := "[(" + v3t2 + ")one (" + v3t3 + ")2 (" + v3t4 + ")3]"
+ addFormatterTest("%v", v3, v3s)
+ addFormatterTest("%v", pv3, "<*>"+v3s)
+ addFormatterTest("%v", &pv3, "<**>"+v3s)
+ addFormatterTest("%+v", nv3, "<nil>")
+ addFormatterTest("%+v", v3, v3s)
+ addFormatterTest("%+v", pv3, "<*>("+v3Addr+")"+v3s)
+ addFormatterTest("%+v", &pv3, "<**>("+pv3Addr+"->"+v3Addr+")"+v3s)
+ addFormatterTest("%+v", nv3, "<nil>")
+ addFormatterTest("%#v", v3, "("+v3t+")"+v3s2)
+ addFormatterTest("%#v", pv3, "(*"+v3t+")"+v3s2)
+ addFormatterTest("%#v", &pv3, "(**"+v3t+")"+v3s2)
+ addFormatterTest("%#v", nv3, "(*"+v3t+")"+"<nil>")
+ addFormatterTest("%#+v", v3, "("+v3t+")"+v3s2)
+ addFormatterTest("%#+v", pv3, "(*"+v3t+")("+v3Addr+")"+v3s2)
+ addFormatterTest("%#+v", &pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")"+v3s2)
+ addFormatterTest("%#+v", nv3, "(*"+v3t+")"+"<nil>")
+}
+
+func addSliceFormatterTests() {
+ // Slice containing standard float32 values.
+ v := []float32{3.14, 6.28, 12.56}
+ nv := (*[]float32)(nil)
+ pv := &v
+ vAddr := fmt.Sprintf("%p", pv)
+ pvAddr := fmt.Sprintf("%p", &pv)
+ vt := "[]float32"
+ vs := "[3.14 6.28 12.56]"
+ addFormatterTest("%v", v, vs)
+ addFormatterTest("%v", pv, "<*>"+vs)
+ addFormatterTest("%v", &pv, "<**>"+vs)
+ addFormatterTest("%+v", nv, "<nil>")
+ addFormatterTest("%+v", v, vs)
+ addFormatterTest("%+v", pv, "<*>("+vAddr+")"+vs)
+ addFormatterTest("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs)
+ addFormatterTest("%+v", nv, "<nil>")
+ addFormatterTest("%#v", v, "("+vt+")"+vs)
+ addFormatterTest("%#v", pv, "(*"+vt+")"+vs)
+ addFormatterTest("%#v", &pv, "(**"+vt+")"+vs)
+ addFormatterTest("%#v", nv, "(*"+vt+")"+"<nil>")
+ addFormatterTest("%#+v", v, "("+vt+")"+vs)
+ addFormatterTest("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs)
+ addFormatterTest("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs)
+ addFormatterTest("%#+v", nv, "(*"+vt+")"+"<nil>")
+
+ // Slice containing type with custom formatter on pointer receiver only.
+ v2 := []pstringer{"1", "2", "3"}
+ nv2 := (*[]pstringer)(nil)
+ pv2 := &v2
+ v2Addr := fmt.Sprintf("%p", pv2)
+ pv2Addr := fmt.Sprintf("%p", &pv2)
+ v2t := "[]spew_test.pstringer"
+ v2s := "[stringer 1 stringer 2 stringer 3]"
+ addFormatterTest("%v", v2, v2s)
+ addFormatterTest("%v", pv2, "<*>"+v2s)
+ addFormatterTest("%v", &pv2, "<**>"+v2s)
+ addFormatterTest("%+v", nv2, "<nil>")
+ addFormatterTest("%+v", v2, v2s)
+ addFormatterTest("%+v", pv2, "<*>("+v2Addr+")"+v2s)
+ addFormatterTest("%+v", &pv2, "<**>("+pv2Addr+"->"+v2Addr+")"+v2s)
+ addFormatterTest("%+v", nv2, "<nil>")
+ addFormatterTest("%#v", v2, "("+v2t+")"+v2s)
+ addFormatterTest("%#v", pv2, "(*"+v2t+")"+v2s)
+ addFormatterTest("%#v", &pv2, "(**"+v2t+")"+v2s)
+ addFormatterTest("%#v", nv2, "(*"+v2t+")"+"<nil>")
+ addFormatterTest("%#+v", v2, "("+v2t+")"+v2s)
+ addFormatterTest("%#+v", pv2, "(*"+v2t+")("+v2Addr+")"+v2s)
+ addFormatterTest("%#+v", &pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")"+v2s)
+ addFormatterTest("%#+v", nv2, "(*"+v2t+")"+"<nil>")
+
+ // Slice containing interfaces.
+ v3 := []interface{}{"one", int(2), uint(3), nil}
+ nv3 := (*[]interface{})(nil)
+ pv3 := &v3
+ v3Addr := fmt.Sprintf("%p", pv3)
+ pv3Addr := fmt.Sprintf("%p", &pv3)
+ v3t := "[]interface {}"
+ v3t2 := "string"
+ v3t3 := "int"
+ v3t4 := "uint"
+ v3t5 := "interface {}"
+ v3s := "[one 2 3 <nil>]"
+ v3s2 := "[(" + v3t2 + ")one (" + v3t3 + ")2 (" + v3t4 + ")3 (" + v3t5 +
+ ")<nil>]"
+ addFormatterTest("%v", v3, v3s)
+ addFormatterTest("%v", pv3, "<*>"+v3s)
+ addFormatterTest("%v", &pv3, "<**>"+v3s)
+ addFormatterTest("%+v", nv3, "<nil>")
+ addFormatterTest("%+v", v3, v3s)
+ addFormatterTest("%+v", pv3, "<*>("+v3Addr+")"+v3s)
+ addFormatterTest("%+v", &pv3, "<**>("+pv3Addr+"->"+v3Addr+")"+v3s)
+ addFormatterTest("%+v", nv3, "<nil>")
+ addFormatterTest("%#v", v3, "("+v3t+")"+v3s2)
+ addFormatterTest("%#v", pv3, "(*"+v3t+")"+v3s2)
+ addFormatterTest("%#v", &pv3, "(**"+v3t+")"+v3s2)
+ addFormatterTest("%#v", nv3, "(*"+v3t+")"+"<nil>")
+ addFormatterTest("%#+v", v3, "("+v3t+")"+v3s2)
+ addFormatterTest("%#+v", pv3, "(*"+v3t+")("+v3Addr+")"+v3s2)
+ addFormatterTest("%#+v", &pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")"+v3s2)
+ addFormatterTest("%#+v", nv3, "(*"+v3t+")"+"<nil>")
+
+ // Nil slice.
+ var v4 []int
+ nv4 := (*[]int)(nil)
+ pv4 := &v4
+ v4Addr := fmt.Sprintf("%p", pv4)
+ pv4Addr := fmt.Sprintf("%p", &pv4)
+ v4t := "[]int"
+ v4s := "<nil>"
+ addFormatterTest("%v", v4, v4s)
+ addFormatterTest("%v", pv4, "<*>"+v4s)
+ addFormatterTest("%v", &pv4, "<**>"+v4s)
+ addFormatterTest("%+v", nv4, "<nil>")
+ addFormatterTest("%+v", v4, v4s)
+ addFormatterTest("%+v", pv4, "<*>("+v4Addr+")"+v4s)
+ addFormatterTest("%+v", &pv4, "<**>("+pv4Addr+"->"+v4Addr+")"+v4s)
+ addFormatterTest("%+v", nv4, "<nil>")
+ addFormatterTest("%#v", v4, "("+v4t+")"+v4s)
+ addFormatterTest("%#v", pv4, "(*"+v4t+")"+v4s)
+ addFormatterTest("%#v", &pv4, "(**"+v4t+")"+v4s)
+ addFormatterTest("%#v", nv4, "(*"+v4t+")"+"<nil>")
+ addFormatterTest("%#+v", v4, "("+v4t+")"+v4s)
+ addFormatterTest("%#+v", pv4, "(*"+v4t+")("+v4Addr+")"+v4s)
+ addFormatterTest("%#+v", &pv4, "(**"+v4t+")("+pv4Addr+"->"+v4Addr+")"+v4s)
+ addFormatterTest("%#+v", nv4, "(*"+v4t+")"+"<nil>")
+}
+
+func addStringFormatterTests() {
+ // Standard string.
+ v := "test"
+ nv := (*string)(nil)
+ pv := &v
+ vAddr := fmt.Sprintf("%p", pv)
+ pvAddr := fmt.Sprintf("%p", &pv)
+ vt := "string"
+ vs := "test"
+ addFormatterTest("%v", v, vs)
+ addFormatterTest("%v", pv, "<*>"+vs)
+ addFormatterTest("%v", &pv, "<**>"+vs)
+ addFormatterTest("%+v", nv, "<nil>")
+ addFormatterTest("%+v", v, vs)
+ addFormatterTest("%+v", pv, "<*>("+vAddr+")"+vs)
+ addFormatterTest("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs)
+ addFormatterTest("%+v", nv, "<nil>")
+ addFormatterTest("%#v", v, "("+vt+")"+vs)
+ addFormatterTest("%#v", pv, "(*"+vt+")"+vs)
+ addFormatterTest("%#v", &pv, "(**"+vt+")"+vs)
+ addFormatterTest("%#v", nv, "(*"+vt+")"+"<nil>")
+ addFormatterTest("%#+v", v, "("+vt+")"+vs)
+ addFormatterTest("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs)
+ addFormatterTest("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs)
+ addFormatterTest("%#+v", nv, "(*"+vt+")"+"<nil>")
+}
+
+func addInterfaceFormatterTests() {
+ // Nil interface.
+ var v interface{}
+ nv := (*interface{})(nil)
+ pv := &v
+ vAddr := fmt.Sprintf("%p", pv)
+ pvAddr := fmt.Sprintf("%p", &pv)
+ vt := "interface {}"
+ vs := "<nil>"
+ addFormatterTest("%v", v, vs)
+ addFormatterTest("%v", pv, "<*>"+vs)
+ addFormatterTest("%v", &pv, "<**>"+vs)
+ addFormatterTest("%+v", nv, "<nil>")
+ addFormatterTest("%+v", v, vs)
+ addFormatterTest("%+v", pv, "<*>("+vAddr+")"+vs)
+ addFormatterTest("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs)
+ addFormatterTest("%+v", nv, "<nil>")
+ addFormatterTest("%#v", v, "("+vt+")"+vs)
+ addFormatterTest("%#v", pv, "(*"+vt+")"+vs)
+ addFormatterTest("%#v", &pv, "(**"+vt+")"+vs)
+ addFormatterTest("%#v", nv, "(*"+vt+")"+"<nil>")
+ addFormatterTest("%#+v", v, "("+vt+")"+vs)
+ addFormatterTest("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs)
+ addFormatterTest("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs)
+ addFormatterTest("%#+v", nv, "(*"+vt+")"+"<nil>")
+
+ // Sub-interface.
+ v2 := interface{}(uint16(65535))
+ pv2 := &v2
+ v2Addr := fmt.Sprintf("%p", pv2)
+ pv2Addr := fmt.Sprintf("%p", &pv2)
+ v2t := "uint16"
+ v2s := "65535"
+ addFormatterTest("%v", v2, v2s)
+ addFormatterTest("%v", pv2, "<*>"+v2s)
+ addFormatterTest("%v", &pv2, "<**>"+v2s)
+ addFormatterTest("%+v", v2, v2s)
+ addFormatterTest("%+v", pv2, "<*>("+v2Addr+")"+v2s)
+ addFormatterTest("%+v", &pv2, "<**>("+pv2Addr+"->"+v2Addr+")"+v2s)
+ addFormatterTest("%#v", v2, "("+v2t+")"+v2s)
+ addFormatterTest("%#v", pv2, "(*"+v2t+")"+v2s)
+ addFormatterTest("%#v", &pv2, "(**"+v2t+")"+v2s)
+ addFormatterTest("%#+v", v2, "("+v2t+")"+v2s)
+ addFormatterTest("%#+v", pv2, "(*"+v2t+")("+v2Addr+")"+v2s)
+ addFormatterTest("%#+v", &pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")"+v2s)
+}
+
+func addMapFormatterTests() {
+ // Map with string keys and int vals.
+ v := map[string]int{"one": 1, "two": 2}
+ nilMap := map[string]int(nil)
+ nv := (*map[string]int)(nil)
+ pv := &v
+ vAddr := fmt.Sprintf("%p", pv)
+ pvAddr := fmt.Sprintf("%p", &pv)
+ vt := "map[string]int"
+ vs := "map[one:1 two:2]"
+ vs2 := "map[two:2 one:1]"
+ addFormatterTest("%v", v, vs, vs2)
+ addFormatterTest("%v", pv, "<*>"+vs, "<*>"+vs2)
+ addFormatterTest("%v", &pv, "<**>"+vs, "<**>"+vs2)
+ addFormatterTest("%+v", nilMap, "<nil>")
+ addFormatterTest("%+v", nv, "<nil>")
+ addFormatterTest("%+v", v, vs, vs2)
+ addFormatterTest("%+v", pv, "<*>("+vAddr+")"+vs, "<*>("+vAddr+")"+vs2)
+ addFormatterTest("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs,
+ "<**>("+pvAddr+"->"+vAddr+")"+vs2)
+ addFormatterTest("%+v", nilMap, "<nil>")
+ addFormatterTest("%+v", nv, "<nil>")
+ addFormatterTest("%#v", v, "("+vt+")"+vs, "("+vt+")"+vs2)
+ addFormatterTest("%#v", pv, "(*"+vt+")"+vs, "(*"+vt+")"+vs2)
+ addFormatterTest("%#v", &pv, "(**"+vt+")"+vs, "(**"+vt+")"+vs2)
+ addFormatterTest("%#v", nilMap, "("+vt+")"+"<nil>")
+ addFormatterTest("%#v", nv, "(*"+vt+")"+"<nil>")
+ addFormatterTest("%#+v", v, "("+vt+")"+vs, "("+vt+")"+vs2)
+ addFormatterTest("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs,
+ "(*"+vt+")("+vAddr+")"+vs2)
+ addFormatterTest("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs,
+ "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs2)
+ addFormatterTest("%#+v", nilMap, "("+vt+")"+"<nil>")
+ addFormatterTest("%#+v", nv, "(*"+vt+")"+"<nil>")
+
+ // Map with custom formatter type on pointer receiver only keys and vals.
+ v2 := map[pstringer]pstringer{"one": "1"}
+ nv2 := (*map[pstringer]pstringer)(nil)
+ pv2 := &v2
+ v2Addr := fmt.Sprintf("%p", pv2)
+ pv2Addr := fmt.Sprintf("%p", &pv2)
+ v2t := "map[spew_test.pstringer]spew_test.pstringer"
+ v2s := "map[stringer one:stringer 1]"
+ if spew.UnsafeDisabled {
+ v2s = "map[one:1]"
+ }
+ addFormatterTest("%v", v2, v2s)
+ addFormatterTest("%v", pv2, "<*>"+v2s)
+ addFormatterTest("%v", &pv2, "<**>"+v2s)
+ addFormatterTest("%+v", nv2, "<nil>")
+ addFormatterTest("%+v", v2, v2s)
+ addFormatterTest("%+v", pv2, "<*>("+v2Addr+")"+v2s)
+ addFormatterTest("%+v", &pv2, "<**>("+pv2Addr+"->"+v2Addr+")"+v2s)
+ addFormatterTest("%+v", nv2, "<nil>")
+ addFormatterTest("%#v", v2, "("+v2t+")"+v2s)
+ addFormatterTest("%#v", pv2, "(*"+v2t+")"+v2s)
+ addFormatterTest("%#v", &pv2, "(**"+v2t+")"+v2s)
+ addFormatterTest("%#v", nv2, "(*"+v2t+")"+"<nil>")
+ addFormatterTest("%#+v", v2, "("+v2t+")"+v2s)
+ addFormatterTest("%#+v", pv2, "(*"+v2t+")("+v2Addr+")"+v2s)
+ addFormatterTest("%#+v", &pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")"+v2s)
+ addFormatterTest("%#+v", nv2, "(*"+v2t+")"+"<nil>")
+
+ // Map with interface keys and values.
+ v3 := map[interface{}]interface{}{"one": 1}
+ nv3 := (*map[interface{}]interface{})(nil)
+ pv3 := &v3
+ v3Addr := fmt.Sprintf("%p", pv3)
+ pv3Addr := fmt.Sprintf("%p", &pv3)
+ v3t := "map[interface {}]interface {}"
+ v3t1 := "string"
+ v3t2 := "int"
+ v3s := "map[one:1]"
+ v3s2 := "map[(" + v3t1 + ")one:(" + v3t2 + ")1]"
+ addFormatterTest("%v", v3, v3s)
+ addFormatterTest("%v", pv3, "<*>"+v3s)
+ addFormatterTest("%v", &pv3, "<**>"+v3s)
+ addFormatterTest("%+v", nv3, "<nil>")
+ addFormatterTest("%+v", v3, v3s)
+ addFormatterTest("%+v", pv3, "<*>("+v3Addr+")"+v3s)
+ addFormatterTest("%+v", &pv3, "<**>("+pv3Addr+"->"+v3Addr+")"+v3s)
+ addFormatterTest("%+v", nv3, "<nil>")
+ addFormatterTest("%#v", v3, "("+v3t+")"+v3s2)
+ addFormatterTest("%#v", pv3, "(*"+v3t+")"+v3s2)
+ addFormatterTest("%#v", &pv3, "(**"+v3t+")"+v3s2)
+ addFormatterTest("%#v", nv3, "(*"+v3t+")"+"<nil>")
+ addFormatterTest("%#+v", v3, "("+v3t+")"+v3s2)
+ addFormatterTest("%#+v", pv3, "(*"+v3t+")("+v3Addr+")"+v3s2)
+ addFormatterTest("%#+v", &pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")"+v3s2)
+ addFormatterTest("%#+v", nv3, "(*"+v3t+")"+"<nil>")
+
+ // Map with nil interface value
+ v4 := map[string]interface{}{"nil": nil}
+ nv4 := (*map[string]interface{})(nil)
+ pv4 := &v4
+ v4Addr := fmt.Sprintf("%p", pv4)
+ pv4Addr := fmt.Sprintf("%p", &pv4)
+ v4t := "map[string]interface {}"
+ v4t1 := "interface {}"
+ v4s := "map[nil:<nil>]"
+ v4s2 := "map[nil:(" + v4t1 + ")<nil>]"
+ addFormatterTest("%v", v4, v4s)
+ addFormatterTest("%v", pv4, "<*>"+v4s)
+ addFormatterTest("%v", &pv4, "<**>"+v4s)
+ addFormatterTest("%+v", nv4, "<nil>")
+ addFormatterTest("%+v", v4, v4s)
+ addFormatterTest("%+v", pv4, "<*>("+v4Addr+")"+v4s)
+ addFormatterTest("%+v", &pv4, "<**>("+pv4Addr+"->"+v4Addr+")"+v4s)
+ addFormatterTest("%+v", nv4, "<nil>")
+ addFormatterTest("%#v", v4, "("+v4t+")"+v4s2)
+ addFormatterTest("%#v", pv4, "(*"+v4t+")"+v4s2)
+ addFormatterTest("%#v", &pv4, "(**"+v4t+")"+v4s2)
+ addFormatterTest("%#v", nv4, "(*"+v4t+")"+"<nil>")
+ addFormatterTest("%#+v", v4, "("+v4t+")"+v4s2)
+ addFormatterTest("%#+v", pv4, "(*"+v4t+")("+v4Addr+")"+v4s2)
+ addFormatterTest("%#+v", &pv4, "(**"+v4t+")("+pv4Addr+"->"+v4Addr+")"+v4s2)
+ addFormatterTest("%#+v", nv4, "(*"+v4t+")"+"<nil>")
+}
+
+func addStructFormatterTests() {
+ // Struct with primitives.
+ type s1 struct {
+ a int8
+ b uint8
+ }
+ v := s1{127, 255}
+ nv := (*s1)(nil)
+ pv := &v
+ vAddr := fmt.Sprintf("%p", pv)
+ pvAddr := fmt.Sprintf("%p", &pv)
+ vt := "spew_test.s1"
+ vt2 := "int8"
+ vt3 := "uint8"
+ vs := "{127 255}"
+ vs2 := "{a:127 b:255}"
+ vs3 := "{a:(" + vt2 + ")127 b:(" + vt3 + ")255}"
+ addFormatterTest("%v", v, vs)
+ addFormatterTest("%v", pv, "<*>"+vs)
+ addFormatterTest("%v", &pv, "<**>"+vs)
+ addFormatterTest("%+v", nv, "<nil>")
+ addFormatterTest("%+v", v, vs2)
+ addFormatterTest("%+v", pv, "<*>("+vAddr+")"+vs2)
+ addFormatterTest("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs2)
+ addFormatterTest("%+v", nv, "<nil>")
+ addFormatterTest("%#v", v, "("+vt+")"+vs3)
+ addFormatterTest("%#v", pv, "(*"+vt+")"+vs3)
+ addFormatterTest("%#v", &pv, "(**"+vt+")"+vs3)
+ addFormatterTest("%#v", nv, "(*"+vt+")"+"<nil>")
+ addFormatterTest("%#+v", v, "("+vt+")"+vs3)
+ addFormatterTest("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs3)
+ addFormatterTest("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs3)
+ addFormatterTest("%#+v", nv, "(*"+vt+")"+"<nil>")
+
+ // Struct that contains another struct.
+ type s2 struct {
+ s1 s1
+ b bool
+ }
+ v2 := s2{s1{127, 255}, true}
+ nv2 := (*s2)(nil)
+ pv2 := &v2
+ v2Addr := fmt.Sprintf("%p", pv2)
+ pv2Addr := fmt.Sprintf("%p", &pv2)
+ v2t := "spew_test.s2"
+ v2t2 := "spew_test.s1"
+ v2t3 := "int8"
+ v2t4 := "uint8"
+ v2t5 := "bool"
+ v2s := "{{127 255} true}"
+ v2s2 := "{s1:{a:127 b:255} b:true}"
+ v2s3 := "{s1:(" + v2t2 + "){a:(" + v2t3 + ")127 b:(" + v2t4 + ")255} b:(" +
+ v2t5 + ")true}"
+ addFormatterTest("%v", v2, v2s)
+ addFormatterTest("%v", pv2, "<*>"+v2s)
+ addFormatterTest("%v", &pv2, "<**>"+v2s)
+ addFormatterTest("%+v", nv2, "<nil>")
+ addFormatterTest("%+v", v2, v2s2)
+ addFormatterTest("%+v", pv2, "<*>("+v2Addr+")"+v2s2)
+ addFormatterTest("%+v", &pv2, "<**>("+pv2Addr+"->"+v2Addr+")"+v2s2)
+ addFormatterTest("%+v", nv2, "<nil>")
+ addFormatterTest("%#v", v2, "("+v2t+")"+v2s3)
+ addFormatterTest("%#v", pv2, "(*"+v2t+")"+v2s3)
+ addFormatterTest("%#v", &pv2, "(**"+v2t+")"+v2s3)
+ addFormatterTest("%#v", nv2, "(*"+v2t+")"+"<nil>")
+ addFormatterTest("%#+v", v2, "("+v2t+")"+v2s3)
+ addFormatterTest("%#+v", pv2, "(*"+v2t+")("+v2Addr+")"+v2s3)
+ addFormatterTest("%#+v", &pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")"+v2s3)
+ addFormatterTest("%#+v", nv2, "(*"+v2t+")"+"<nil>")
+
+ // Struct that contains custom type with Stringer pointer interface via both
+ // exported and unexported fields.
+ type s3 struct {
+ s pstringer
+ S pstringer
+ }
+ v3 := s3{"test", "test2"}
+ nv3 := (*s3)(nil)
+ pv3 := &v3
+ v3Addr := fmt.Sprintf("%p", pv3)
+ pv3Addr := fmt.Sprintf("%p", &pv3)
+ v3t := "spew_test.s3"
+ v3t2 := "spew_test.pstringer"
+ v3s := "{stringer test stringer test2}"
+ v3sp := v3s
+ v3s2 := "{s:stringer test S:stringer test2}"
+ v3s2p := v3s2
+ v3s3 := "{s:(" + v3t2 + ")stringer test S:(" + v3t2 + ")stringer test2}"
+ v3s3p := v3s3
+ if spew.UnsafeDisabled {
+ v3s = "{test test2}"
+ v3sp = "{test stringer test2}"
+ v3s2 = "{s:test S:test2}"
+ v3s2p = "{s:test S:stringer test2}"
+ v3s3 = "{s:(" + v3t2 + ")test S:(" + v3t2 + ")test2}"
+ v3s3p = "{s:(" + v3t2 + ")test S:(" + v3t2 + ")stringer test2}"
+ }
+ addFormatterTest("%v", v3, v3s)
+ addFormatterTest("%v", pv3, "<*>"+v3sp)
+ addFormatterTest("%v", &pv3, "<**>"+v3sp)
+ addFormatterTest("%+v", nv3, "<nil>")
+ addFormatterTest("%+v", v3, v3s2)
+ addFormatterTest("%+v", pv3, "<*>("+v3Addr+")"+v3s2p)
+ addFormatterTest("%+v", &pv3, "<**>("+pv3Addr+"->"+v3Addr+")"+v3s2p)
+ addFormatterTest("%+v", nv3, "<nil>")
+ addFormatterTest("%#v", v3, "("+v3t+")"+v3s3)
+ addFormatterTest("%#v", pv3, "(*"+v3t+")"+v3s3p)
+ addFormatterTest("%#v", &pv3, "(**"+v3t+")"+v3s3p)
+ addFormatterTest("%#v", nv3, "(*"+v3t+")"+"<nil>")
+ addFormatterTest("%#+v", v3, "("+v3t+")"+v3s3)
+ addFormatterTest("%#+v", pv3, "(*"+v3t+")("+v3Addr+")"+v3s3p)
+ addFormatterTest("%#+v", &pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")"+v3s3p)
+ addFormatterTest("%#+v", nv3, "(*"+v3t+")"+"<nil>")
+
+ // Struct that contains embedded struct and field to same struct.
+ e := embed{"embedstr"}
+ v4 := embedwrap{embed: &e, e: &e}
+ nv4 := (*embedwrap)(nil)
+ pv4 := &v4
+ eAddr := fmt.Sprintf("%p", &e)
+ v4Addr := fmt.Sprintf("%p", pv4)
+ pv4Addr := fmt.Sprintf("%p", &pv4)
+ v4t := "spew_test.embedwrap"
+ v4t2 := "spew_test.embed"
+ v4t3 := "string"
+ v4s := "{<*>{embedstr} <*>{embedstr}}"
+ v4s2 := "{embed:<*>(" + eAddr + "){a:embedstr} e:<*>(" + eAddr +
+ "){a:embedstr}}"
+ v4s3 := "{embed:(*" + v4t2 + "){a:(" + v4t3 + ")embedstr} e:(*" + v4t2 +
+ "){a:(" + v4t3 + ")embedstr}}"
+ v4s4 := "{embed:(*" + v4t2 + ")(" + eAddr + "){a:(" + v4t3 +
+ ")embedstr} e:(*" + v4t2 + ")(" + eAddr + "){a:(" + v4t3 + ")embedstr}}"
+ addFormatterTest("%v", v4, v4s)
+ addFormatterTest("%v", pv4, "<*>"+v4s)
+ addFormatterTest("%v", &pv4, "<**>"+v4s)
+ addFormatterTest("%+v", nv4, "<nil>")
+ addFormatterTest("%+v", v4, v4s2)
+ addFormatterTest("%+v", pv4, "<*>("+v4Addr+")"+v4s2)
+ addFormatterTest("%+v", &pv4, "<**>("+pv4Addr+"->"+v4Addr+")"+v4s2)
+ addFormatterTest("%+v", nv4, "<nil>")
+ addFormatterTest("%#v", v4, "("+v4t+")"+v4s3)
+ addFormatterTest("%#v", pv4, "(*"+v4t+")"+v4s3)
+ addFormatterTest("%#v", &pv4, "(**"+v4t+")"+v4s3)
+ addFormatterTest("%#v", nv4, "(*"+v4t+")"+"<nil>")
+ addFormatterTest("%#+v", v4, "("+v4t+")"+v4s4)
+ addFormatterTest("%#+v", pv4, "(*"+v4t+")("+v4Addr+")"+v4s4)
+ addFormatterTest("%#+v", &pv4, "(**"+v4t+")("+pv4Addr+"->"+v4Addr+")"+v4s4)
+ addFormatterTest("%#+v", nv4, "(*"+v4t+")"+"<nil>")
+}
+
+func addUintptrFormatterTests() {
+ // Null pointer.
+ v := uintptr(0)
+ nv := (*uintptr)(nil)
+ pv := &v
+ vAddr := fmt.Sprintf("%p", pv)
+ pvAddr := fmt.Sprintf("%p", &pv)
+ vt := "uintptr"
+ vs := "<nil>"
+ addFormatterTest("%v", v, vs)
+ addFormatterTest("%v", pv, "<*>"+vs)
+ addFormatterTest("%v", &pv, "<**>"+vs)
+ addFormatterTest("%+v", nv, "<nil>")
+ addFormatterTest("%+v", v, vs)
+ addFormatterTest("%+v", pv, "<*>("+vAddr+")"+vs)
+ addFormatterTest("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs)
+ addFormatterTest("%+v", nv, "<nil>")
+ addFormatterTest("%#v", v, "("+vt+")"+vs)
+ addFormatterTest("%#v", pv, "(*"+vt+")"+vs)
+ addFormatterTest("%#v", &pv, "(**"+vt+")"+vs)
+ addFormatterTest("%#v", nv, "(*"+vt+")"+"<nil>")
+ addFormatterTest("%#+v", v, "("+vt+")"+vs)
+ addFormatterTest("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs)
+ addFormatterTest("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs)
+ addFormatterTest("%#+v", nv, "(*"+vt+")"+"<nil>")
+
+ // Address of real variable.
+ i := 1
+ v2 := uintptr(unsafe.Pointer(&i))
+ pv2 := &v2
+ v2Addr := fmt.Sprintf("%p", pv2)
+ pv2Addr := fmt.Sprintf("%p", &pv2)
+ v2t := "uintptr"
+ v2s := fmt.Sprintf("%p", &i)
+ addFormatterTest("%v", v2, v2s)
+ addFormatterTest("%v", pv2, "<*>"+v2s)
+ addFormatterTest("%v", &pv2, "<**>"+v2s)
+ addFormatterTest("%+v", v2, v2s)
+ addFormatterTest("%+v", pv2, "<*>("+v2Addr+")"+v2s)
+ addFormatterTest("%+v", &pv2, "<**>("+pv2Addr+"->"+v2Addr+")"+v2s)
+ addFormatterTest("%#v", v2, "("+v2t+")"+v2s)
+ addFormatterTest("%#v", pv2, "(*"+v2t+")"+v2s)
+ addFormatterTest("%#v", &pv2, "(**"+v2t+")"+v2s)
+ addFormatterTest("%#+v", v2, "("+v2t+")"+v2s)
+ addFormatterTest("%#+v", pv2, "(*"+v2t+")("+v2Addr+")"+v2s)
+ addFormatterTest("%#+v", &pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")"+v2s)
+}
+
+func addUnsafePointerFormatterTests() {
+ // Null pointer.
+ v := unsafe.Pointer(uintptr(0))
+ nv := (*unsafe.Pointer)(nil)
+ pv := &v
+ vAddr := fmt.Sprintf("%p", pv)
+ pvAddr := fmt.Sprintf("%p", &pv)
+ vt := "unsafe.Pointer"
+ vs := "<nil>"
+ addFormatterTest("%v", v, vs)
+ addFormatterTest("%v", pv, "<*>"+vs)
+ addFormatterTest("%v", &pv, "<**>"+vs)
+ addFormatterTest("%+v", nv, "<nil>")
+ addFormatterTest("%+v", v, vs)
+ addFormatterTest("%+v", pv, "<*>("+vAddr+")"+vs)
+ addFormatterTest("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs)
+ addFormatterTest("%+v", nv, "<nil>")
+ addFormatterTest("%#v", v, "("+vt+")"+vs)
+ addFormatterTest("%#v", pv, "(*"+vt+")"+vs)
+ addFormatterTest("%#v", &pv, "(**"+vt+")"+vs)
+ addFormatterTest("%#v", nv, "(*"+vt+")"+"<nil>")
+ addFormatterTest("%#+v", v, "("+vt+")"+vs)
+ addFormatterTest("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs)
+ addFormatterTest("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs)
+ addFormatterTest("%#+v", nv, "(*"+vt+")"+"<nil>")
+
+ // Address of real variable.
+ i := 1
+ v2 := unsafe.Pointer(&i)
+ pv2 := &v2
+ v2Addr := fmt.Sprintf("%p", pv2)
+ pv2Addr := fmt.Sprintf("%p", &pv2)
+ v2t := "unsafe.Pointer"
+ v2s := fmt.Sprintf("%p", &i)
+ addFormatterTest("%v", v2, v2s)
+ addFormatterTest("%v", pv2, "<*>"+v2s)
+ addFormatterTest("%v", &pv2, "<**>"+v2s)
+ addFormatterTest("%+v", v2, v2s)
+ addFormatterTest("%+v", pv2, "<*>("+v2Addr+")"+v2s)
+ addFormatterTest("%+v", &pv2, "<**>("+pv2Addr+"->"+v2Addr+")"+v2s)
+ addFormatterTest("%#v", v2, "("+v2t+")"+v2s)
+ addFormatterTest("%#v", pv2, "(*"+v2t+")"+v2s)
+ addFormatterTest("%#v", &pv2, "(**"+v2t+")"+v2s)
+ addFormatterTest("%#+v", v2, "("+v2t+")"+v2s)
+ addFormatterTest("%#+v", pv2, "(*"+v2t+")("+v2Addr+")"+v2s)
+ addFormatterTest("%#+v", &pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")"+v2s)
+}
+
+func addChanFormatterTests() {
+ // Nil channel.
+ var v chan int
+ pv := &v
+ nv := (*chan int)(nil)
+ vAddr := fmt.Sprintf("%p", pv)
+ pvAddr := fmt.Sprintf("%p", &pv)
+ vt := "chan int"
+ vs := "<nil>"
+ addFormatterTest("%v", v, vs)
+ addFormatterTest("%v", pv, "<*>"+vs)
+ addFormatterTest("%v", &pv, "<**>"+vs)
+ addFormatterTest("%+v", nv, "<nil>")
+ addFormatterTest("%+v", v, vs)
+ addFormatterTest("%+v", pv, "<*>("+vAddr+")"+vs)
+ addFormatterTest("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs)
+ addFormatterTest("%+v", nv, "<nil>")
+ addFormatterTest("%#v", v, "("+vt+")"+vs)
+ addFormatterTest("%#v", pv, "(*"+vt+")"+vs)
+ addFormatterTest("%#v", &pv, "(**"+vt+")"+vs)
+ addFormatterTest("%#v", nv, "(*"+vt+")"+"<nil>")
+ addFormatterTest("%#+v", v, "("+vt+")"+vs)
+ addFormatterTest("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs)
+ addFormatterTest("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs)
+ addFormatterTest("%#+v", nv, "(*"+vt+")"+"<nil>")
+
+ // Real channel.
+ v2 := make(chan int)
+ pv2 := &v2
+ v2Addr := fmt.Sprintf("%p", pv2)
+ pv2Addr := fmt.Sprintf("%p", &pv2)
+ v2t := "chan int"
+ v2s := fmt.Sprintf("%p", v2)
+ addFormatterTest("%v", v2, v2s)
+ addFormatterTest("%v", pv2, "<*>"+v2s)
+ addFormatterTest("%v", &pv2, "<**>"+v2s)
+ addFormatterTest("%+v", v2, v2s)
+ addFormatterTest("%+v", pv2, "<*>("+v2Addr+")"+v2s)
+ addFormatterTest("%+v", &pv2, "<**>("+pv2Addr+"->"+v2Addr+")"+v2s)
+ addFormatterTest("%#v", v2, "("+v2t+")"+v2s)
+ addFormatterTest("%#v", pv2, "(*"+v2t+")"+v2s)
+ addFormatterTest("%#v", &pv2, "(**"+v2t+")"+v2s)
+ addFormatterTest("%#+v", v2, "("+v2t+")"+v2s)
+ addFormatterTest("%#+v", pv2, "(*"+v2t+")("+v2Addr+")"+v2s)
+ addFormatterTest("%#+v", &pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")"+v2s)
+}
+
+func addFuncFormatterTests() {
+ // Function with no params and no returns.
+ v := addIntFormatterTests
+ nv := (*func())(nil)
+ pv := &v
+ vAddr := fmt.Sprintf("%p", pv)
+ pvAddr := fmt.Sprintf("%p", &pv)
+ vt := "func()"
+ vs := fmt.Sprintf("%p", v)
+ addFormatterTest("%v", v, vs)
+ addFormatterTest("%v", pv, "<*>"+vs)
+ addFormatterTest("%v", &pv, "<**>"+vs)
+ addFormatterTest("%+v", nv, "<nil>")
+ addFormatterTest("%+v", v, vs)
+ addFormatterTest("%+v", pv, "<*>("+vAddr+")"+vs)
+ addFormatterTest("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs)
+ addFormatterTest("%+v", nv, "<nil>")
+ addFormatterTest("%#v", v, "("+vt+")"+vs)
+ addFormatterTest("%#v", pv, "(*"+vt+")"+vs)
+ addFormatterTest("%#v", &pv, "(**"+vt+")"+vs)
+ addFormatterTest("%#v", nv, "(*"+vt+")"+"<nil>")
+ addFormatterTest("%#+v", v, "("+vt+")"+vs)
+ addFormatterTest("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs)
+ addFormatterTest("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs)
+ addFormatterTest("%#+v", nv, "(*"+vt+")"+"<nil>")
+
+ // Function with param and no returns.
+ v2 := TestFormatter
+ nv2 := (*func(*testing.T))(nil)
+ pv2 := &v2
+ v2Addr := fmt.Sprintf("%p", pv2)
+ pv2Addr := fmt.Sprintf("%p", &pv2)
+ v2t := "func(*testing.T)"
+ v2s := fmt.Sprintf("%p", v2)
+ addFormatterTest("%v", v2, v2s)
+ addFormatterTest("%v", pv2, "<*>"+v2s)
+ addFormatterTest("%v", &pv2, "<**>"+v2s)
+ addFormatterTest("%+v", nv2, "<nil>")
+ addFormatterTest("%+v", v2, v2s)
+ addFormatterTest("%+v", pv2, "<*>("+v2Addr+")"+v2s)
+ addFormatterTest("%+v", &pv2, "<**>("+pv2Addr+"->"+v2Addr+")"+v2s)
+ addFormatterTest("%+v", nv2, "<nil>")
+ addFormatterTest("%#v", v2, "("+v2t+")"+v2s)
+ addFormatterTest("%#v", pv2, "(*"+v2t+")"+v2s)
+ addFormatterTest("%#v", &pv2, "(**"+v2t+")"+v2s)
+ addFormatterTest("%#v", nv2, "(*"+v2t+")"+"<nil>")
+ addFormatterTest("%#+v", v2, "("+v2t+")"+v2s)
+ addFormatterTest("%#+v", pv2, "(*"+v2t+")("+v2Addr+")"+v2s)
+ addFormatterTest("%#+v", &pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")"+v2s)
+ addFormatterTest("%#+v", nv2, "(*"+v2t+")"+"<nil>")
+
+ // Function with multiple params and multiple returns.
+ var v3 = func(i int, s string) (b bool, err error) {
+ return true, nil
+ }
+ nv3 := (*func(int, string) (bool, error))(nil)
+ pv3 := &v3
+ v3Addr := fmt.Sprintf("%p", pv3)
+ pv3Addr := fmt.Sprintf("%p", &pv3)
+ v3t := "func(int, string) (bool, error)"
+ v3s := fmt.Sprintf("%p", v3)
+ addFormatterTest("%v", v3, v3s)
+ addFormatterTest("%v", pv3, "<*>"+v3s)
+ addFormatterTest("%v", &pv3, "<**>"+v3s)
+ addFormatterTest("%+v", nv3, "<nil>")
+ addFormatterTest("%+v", v3, v3s)
+ addFormatterTest("%+v", pv3, "<*>("+v3Addr+")"+v3s)
+ addFormatterTest("%+v", &pv3, "<**>("+pv3Addr+"->"+v3Addr+")"+v3s)
+ addFormatterTest("%+v", nv3, "<nil>")
+ addFormatterTest("%#v", v3, "("+v3t+")"+v3s)
+ addFormatterTest("%#v", pv3, "(*"+v3t+")"+v3s)
+ addFormatterTest("%#v", &pv3, "(**"+v3t+")"+v3s)
+ addFormatterTest("%#v", nv3, "(*"+v3t+")"+"<nil>")
+ addFormatterTest("%#+v", v3, "("+v3t+")"+v3s)
+ addFormatterTest("%#+v", pv3, "(*"+v3t+")("+v3Addr+")"+v3s)
+ addFormatterTest("%#+v", &pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")"+v3s)
+ addFormatterTest("%#+v", nv3, "(*"+v3t+")"+"<nil>")
+}
+
+func addCircularFormatterTests() {
+ // Struct that is circular through self referencing.
+ type circular struct {
+ c *circular
+ }
+ v := circular{nil}
+ v.c = &v
+ pv := &v
+ vAddr := fmt.Sprintf("%p", pv)
+ pvAddr := fmt.Sprintf("%p", &pv)
+ vt := "spew_test.circular"
+ vs := "{<*>{<*><shown>}}"
+ vs2 := "{<*><shown>}"
+ vs3 := "{c:<*>(" + vAddr + "){c:<*>(" + vAddr + ")<shown>}}"
+ vs4 := "{c:<*>(" + vAddr + ")<shown>}"
+ vs5 := "{c:(*" + vt + "){c:(*" + vt + ")<shown>}}"
+ vs6 := "{c:(*" + vt + ")<shown>}"
+ vs7 := "{c:(*" + vt + ")(" + vAddr + "){c:(*" + vt + ")(" + vAddr +
+ ")<shown>}}"
+ vs8 := "{c:(*" + vt + ")(" + vAddr + ")<shown>}"
+ addFormatterTest("%v", v, vs)
+ addFormatterTest("%v", pv, "<*>"+vs2)
+ addFormatterTest("%v", &pv, "<**>"+vs2)
+ addFormatterTest("%+v", v, vs3)
+ addFormatterTest("%+v", pv, "<*>("+vAddr+")"+vs4)
+ addFormatterTest("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs4)
+ addFormatterTest("%#v", v, "("+vt+")"+vs5)
+ addFormatterTest("%#v", pv, "(*"+vt+")"+vs6)
+ addFormatterTest("%#v", &pv, "(**"+vt+")"+vs6)
+ addFormatterTest("%#+v", v, "("+vt+")"+vs7)
+ addFormatterTest("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs8)
+ addFormatterTest("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs8)
+
+ // Structs that are circular through cross referencing.
+ v2 := xref1{nil}
+ ts2 := xref2{&v2}
+ v2.ps2 = &ts2
+ pv2 := &v2
+ ts2Addr := fmt.Sprintf("%p", &ts2)
+ v2Addr := fmt.Sprintf("%p", pv2)
+ pv2Addr := fmt.Sprintf("%p", &pv2)
+ v2t := "spew_test.xref1"
+ v2t2 := "spew_test.xref2"
+ v2s := "{<*>{<*>{<*><shown>}}}"
+ v2s2 := "{<*>{<*><shown>}}"
+ v2s3 := "{ps2:<*>(" + ts2Addr + "){ps1:<*>(" + v2Addr + "){ps2:<*>(" +
+ ts2Addr + ")<shown>}}}"
+ v2s4 := "{ps2:<*>(" + ts2Addr + "){ps1:<*>(" + v2Addr + ")<shown>}}"
+ v2s5 := "{ps2:(*" + v2t2 + "){ps1:(*" + v2t + "){ps2:(*" + v2t2 +
+ ")<shown>}}}"
+ v2s6 := "{ps2:(*" + v2t2 + "){ps1:(*" + v2t + ")<shown>}}"
+ v2s7 := "{ps2:(*" + v2t2 + ")(" + ts2Addr + "){ps1:(*" + v2t +
+ ")(" + v2Addr + "){ps2:(*" + v2t2 + ")(" + ts2Addr +
+ ")<shown>}}}"
+ v2s8 := "{ps2:(*" + v2t2 + ")(" + ts2Addr + "){ps1:(*" + v2t +
+ ")(" + v2Addr + ")<shown>}}"
+ addFormatterTest("%v", v2, v2s)
+ addFormatterTest("%v", pv2, "<*>"+v2s2)
+ addFormatterTest("%v", &pv2, "<**>"+v2s2)
+ addFormatterTest("%+v", v2, v2s3)
+ addFormatterTest("%+v", pv2, "<*>("+v2Addr+")"+v2s4)
+ addFormatterTest("%+v", &pv2, "<**>("+pv2Addr+"->"+v2Addr+")"+v2s4)
+ addFormatterTest("%#v", v2, "("+v2t+")"+v2s5)
+ addFormatterTest("%#v", pv2, "(*"+v2t+")"+v2s6)
+ addFormatterTest("%#v", &pv2, "(**"+v2t+")"+v2s6)
+ addFormatterTest("%#+v", v2, "("+v2t+")"+v2s7)
+ addFormatterTest("%#+v", pv2, "(*"+v2t+")("+v2Addr+")"+v2s8)
+ addFormatterTest("%#+v", &pv2, "(**"+v2t+")("+pv2Addr+"->"+v2Addr+")"+v2s8)
+
+ // Structs that are indirectly circular.
+ v3 := indirCir1{nil}
+ tic2 := indirCir2{nil}
+ tic3 := indirCir3{&v3}
+ tic2.ps3 = &tic3
+ v3.ps2 = &tic2
+ pv3 := &v3
+ tic2Addr := fmt.Sprintf("%p", &tic2)
+ tic3Addr := fmt.Sprintf("%p", &tic3)
+ v3Addr := fmt.Sprintf("%p", pv3)
+ pv3Addr := fmt.Sprintf("%p", &pv3)
+ v3t := "spew_test.indirCir1"
+ v3t2 := "spew_test.indirCir2"
+ v3t3 := "spew_test.indirCir3"
+ v3s := "{<*>{<*>{<*>{<*><shown>}}}}"
+ v3s2 := "{<*>{<*>{<*><shown>}}}"
+ v3s3 := "{ps2:<*>(" + tic2Addr + "){ps3:<*>(" + tic3Addr + "){ps1:<*>(" +
+ v3Addr + "){ps2:<*>(" + tic2Addr + ")<shown>}}}}"
+ v3s4 := "{ps2:<*>(" + tic2Addr + "){ps3:<*>(" + tic3Addr + "){ps1:<*>(" +
+ v3Addr + ")<shown>}}}"
+ v3s5 := "{ps2:(*" + v3t2 + "){ps3:(*" + v3t3 + "){ps1:(*" + v3t +
+ "){ps2:(*" + v3t2 + ")<shown>}}}}"
+ v3s6 := "{ps2:(*" + v3t2 + "){ps3:(*" + v3t3 + "){ps1:(*" + v3t +
+ ")<shown>}}}"
+ v3s7 := "{ps2:(*" + v3t2 + ")(" + tic2Addr + "){ps3:(*" + v3t3 + ")(" +
+ tic3Addr + "){ps1:(*" + v3t + ")(" + v3Addr + "){ps2:(*" + v3t2 +
+ ")(" + tic2Addr + ")<shown>}}}}"
+ v3s8 := "{ps2:(*" + v3t2 + ")(" + tic2Addr + "){ps3:(*" + v3t3 + ")(" +
+ tic3Addr + "){ps1:(*" + v3t + ")(" + v3Addr + ")<shown>}}}"
+ addFormatterTest("%v", v3, v3s)
+ addFormatterTest("%v", pv3, "<*>"+v3s2)
+ addFormatterTest("%v", &pv3, "<**>"+v3s2)
+ addFormatterTest("%+v", v3, v3s3)
+ addFormatterTest("%+v", pv3, "<*>("+v3Addr+")"+v3s4)
+ addFormatterTest("%+v", &pv3, "<**>("+pv3Addr+"->"+v3Addr+")"+v3s4)
+ addFormatterTest("%#v", v3, "("+v3t+")"+v3s5)
+ addFormatterTest("%#v", pv3, "(*"+v3t+")"+v3s6)
+ addFormatterTest("%#v", &pv3, "(**"+v3t+")"+v3s6)
+ addFormatterTest("%#+v", v3, "("+v3t+")"+v3s7)
+ addFormatterTest("%#+v", pv3, "(*"+v3t+")("+v3Addr+")"+v3s8)
+ addFormatterTest("%#+v", &pv3, "(**"+v3t+")("+pv3Addr+"->"+v3Addr+")"+v3s8)
+}
+
+func addPanicFormatterTests() {
+ // Type that panics in its Stringer interface.
+ v := panicer(127)
+ nv := (*panicer)(nil)
+ pv := &v
+ vAddr := fmt.Sprintf("%p", pv)
+ pvAddr := fmt.Sprintf("%p", &pv)
+ vt := "spew_test.panicer"
+ vs := "(PANIC=test panic)127"
+ addFormatterTest("%v", v, vs)
+ addFormatterTest("%v", pv, "<*>"+vs)
+ addFormatterTest("%v", &pv, "<**>"+vs)
+ addFormatterTest("%v", nv, "<nil>")
+ addFormatterTest("%+v", v, vs)
+ addFormatterTest("%+v", pv, "<*>("+vAddr+")"+vs)
+ addFormatterTest("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs)
+ addFormatterTest("%+v", nv, "<nil>")
+ addFormatterTest("%#v", v, "("+vt+")"+vs)
+ addFormatterTest("%#v", pv, "(*"+vt+")"+vs)
+ addFormatterTest("%#v", &pv, "(**"+vt+")"+vs)
+ addFormatterTest("%#v", nv, "(*"+vt+")"+"<nil>")
+ addFormatterTest("%#+v", v, "("+vt+")"+vs)
+ addFormatterTest("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs)
+ addFormatterTest("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs)
+ addFormatterTest("%#+v", nv, "(*"+vt+")"+"<nil>")
+}
+
+func addErrorFormatterTests() {
+ // Type that has a custom Error interface.
+ v := customError(127)
+ nv := (*customError)(nil)
+ pv := &v
+ vAddr := fmt.Sprintf("%p", pv)
+ pvAddr := fmt.Sprintf("%p", &pv)
+ vt := "spew_test.customError"
+ vs := "error: 127"
+ addFormatterTest("%v", v, vs)
+ addFormatterTest("%v", pv, "<*>"+vs)
+ addFormatterTest("%v", &pv, "<**>"+vs)
+ addFormatterTest("%v", nv, "<nil>")
+ addFormatterTest("%+v", v, vs)
+ addFormatterTest("%+v", pv, "<*>("+vAddr+")"+vs)
+ addFormatterTest("%+v", &pv, "<**>("+pvAddr+"->"+vAddr+")"+vs)
+ addFormatterTest("%+v", nv, "<nil>")
+ addFormatterTest("%#v", v, "("+vt+")"+vs)
+ addFormatterTest("%#v", pv, "(*"+vt+")"+vs)
+ addFormatterTest("%#v", &pv, "(**"+vt+")"+vs)
+ addFormatterTest("%#v", nv, "(*"+vt+")"+"<nil>")
+ addFormatterTest("%#+v", v, "("+vt+")"+vs)
+ addFormatterTest("%#+v", pv, "(*"+vt+")("+vAddr+")"+vs)
+ addFormatterTest("%#+v", &pv, "(**"+vt+")("+pvAddr+"->"+vAddr+")"+vs)
+ addFormatterTest("%#+v", nv, "(*"+vt+")"+"<nil>")
+}
+
+func addPassthroughFormatterTests() {
+ // %x passthrough with uint.
+ v := uint(4294967295)
+ pv := &v
+ vAddr := fmt.Sprintf("%x", pv)
+ pvAddr := fmt.Sprintf("%x", &pv)
+ vs := "ffffffff"
+ addFormatterTest("%x", v, vs)
+ addFormatterTest("%x", pv, vAddr)
+ addFormatterTest("%x", &pv, pvAddr)
+
+ // %#x passthrough with uint.
+ v2 := int(2147483647)
+ pv2 := &v2
+ v2Addr := fmt.Sprintf("%#x", pv2)
+ pv2Addr := fmt.Sprintf("%#x", &pv2)
+ v2s := "0x7fffffff"
+ addFormatterTest("%#x", v2, v2s)
+ addFormatterTest("%#x", pv2, v2Addr)
+ addFormatterTest("%#x", &pv2, pv2Addr)
+
+ // %f passthrough with precision.
+ addFormatterTest("%.2f", 3.1415, "3.14")
+ addFormatterTest("%.3f", 3.1415, "3.142")
+ addFormatterTest("%.4f", 3.1415, "3.1415")
+
+ // %f passthrough with width and precision.
+ addFormatterTest("%5.2f", 3.1415, " 3.14")
+ addFormatterTest("%6.3f", 3.1415, " 3.142")
+ addFormatterTest("%7.4f", 3.1415, " 3.1415")
+
+ // %d passthrough with width.
+ addFormatterTest("%3d", 127, "127")
+ addFormatterTest("%4d", 127, " 127")
+ addFormatterTest("%5d", 127, " 127")
+
+ // %q passthrough with string.
+ addFormatterTest("%q", "test", "\"test\"")
+}
+
+// TestFormatter executes all of the tests described by formatterTests.
+func TestFormatter(t *testing.T) {
+ // Setup tests.
+ addIntFormatterTests()
+ addUintFormatterTests()
+ addBoolFormatterTests()
+ addFloatFormatterTests()
+ addComplexFormatterTests()
+ addArrayFormatterTests()
+ addSliceFormatterTests()
+ addStringFormatterTests()
+ addInterfaceFormatterTests()
+ addMapFormatterTests()
+ addStructFormatterTests()
+ addUintptrFormatterTests()
+ addUnsafePointerFormatterTests()
+ addChanFormatterTests()
+ addFuncFormatterTests()
+ addCircularFormatterTests()
+ addPanicFormatterTests()
+ addErrorFormatterTests()
+ addPassthroughFormatterTests()
+
+ t.Logf("Running %d tests", len(formatterTests))
+ for i, test := range formatterTests {
+ buf := new(bytes.Buffer)
+ spew.Fprintf(buf, test.format, test.in)
+ s := buf.String()
+ if testFailed(s, test.wants) {
+ t.Errorf("Formatter #%d format: %s got: %s %s", i, test.format, s,
+ stringizeWants(test.wants))
+ continue
+ }
+ }
+}
+
+type testStruct struct {
+ x int
+}
+
+func (ts testStruct) String() string {
+ return fmt.Sprintf("ts.%d", ts.x)
+}
+
+type testStructP struct {
+ x int
+}
+
+func (ts *testStructP) String() string {
+ return fmt.Sprintf("ts.%d", ts.x)
+}
+
+func TestPrintSortedKeys(t *testing.T) {
+ cfg := spew.ConfigState{SortKeys: true}
+ s := cfg.Sprint(map[int]string{1: "1", 3: "3", 2: "2"})
+ expected := "map[1:1 2:2 3:3]"
+ if s != expected {
+ t.Errorf("Sorted keys mismatch 1:\n %v %v", s, expected)
+ }
+
+ s = cfg.Sprint(map[stringer]int{"1": 1, "3": 3, "2": 2})
+ expected = "map[stringer 1:1 stringer 2:2 stringer 3:3]"
+ if s != expected {
+ t.Errorf("Sorted keys mismatch 2:\n %v %v", s, expected)
+ }
+
+ s = cfg.Sprint(map[pstringer]int{pstringer("1"): 1, pstringer("3"): 3, pstringer("2"): 2})
+ expected = "map[stringer 1:1 stringer 2:2 stringer 3:3]"
+ if spew.UnsafeDisabled {
+ expected = "map[1:1 2:2 3:3]"
+ }
+ if s != expected {
+ t.Errorf("Sorted keys mismatch 3:\n %v %v", s, expected)
+ }
+
+ s = cfg.Sprint(map[testStruct]int{testStruct{1}: 1, testStruct{3}: 3, testStruct{2}: 2})
+ expected = "map[ts.1:1 ts.2:2 ts.3:3]"
+ if s != expected {
+ t.Errorf("Sorted keys mismatch 4:\n %v %v", s, expected)
+ }
+
+ if !spew.UnsafeDisabled {
+ s = cfg.Sprint(map[testStructP]int{testStructP{1}: 1, testStructP{3}: 3, testStructP{2}: 2})
+ expected = "map[ts.1:1 ts.2:2 ts.3:3]"
+ if s != expected {
+ t.Errorf("Sorted keys mismatch 5:\n %v %v", s, expected)
+ }
+ }
+
+ s = cfg.Sprint(map[customError]int{customError(1): 1, customError(3): 3, customError(2): 2})
+ expected = "map[error: 1:1 error: 2:2 error: 3:3]"
+ if s != expected {
+ t.Errorf("Sorted keys mismatch 6:\n %v %v", s, expected)
+ }
+}
diff --git a/vendor/github.com/davecgh/go-spew/spew/internal_test.go b/vendor/github.com/davecgh/go-spew/spew/internal_test.go
new file mode 100644
index 000000000..1069ee21c
--- /dev/null
+++ b/vendor/github.com/davecgh/go-spew/spew/internal_test.go
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2013 Dave Collins <dave@davec.name>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+This test file is part of the spew package rather than than the spew_test
+package because it needs access to internals to properly test certain cases
+which are not possible via the public interface since they should never happen.
+*/
+
+package spew
+
+import (
+ "bytes"
+ "reflect"
+ "testing"
+)
+
+// 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)
+}
diff --git a/vendor/github.com/davecgh/go-spew/spew/internalunsafe_test.go b/vendor/github.com/davecgh/go-spew/spew/internalunsafe_test.go
new file mode 100644
index 000000000..863b62cf5
--- /dev/null
+++ b/vendor/github.com/davecgh/go-spew/spew/internalunsafe_test.go
@@ -0,0 +1,102 @@
+// Copyright (c) 2013-2015 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)
+ }
+}
diff --git a/vendor/github.com/davecgh/go-spew/spew/spew.go b/vendor/github.com/davecgh/go-spew/spew/spew.go
new file mode 100644
index 000000000..d8233f542
--- /dev/null
+++ b/vendor/github.com/davecgh/go-spew/spew/spew.go
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) 2013 Dave Collins <dave@davec.name>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+package spew
+
+import (
+ "fmt"
+ "io"
+)
+
+// Errorf is a wrapper for fmt.Errorf that treats each argument as if it were
+// passed with a default Formatter interface returned by NewFormatter. It
+// returns the formatted string as a value that satisfies error. See
+// NewFormatter for formatting details.
+//
+// This function is shorthand for the following syntax:
+//
+// fmt.Errorf(format, spew.NewFormatter(a), spew.NewFormatter(b))
+func Errorf(format string, a ...interface{}) (err error) {
+ return fmt.Errorf(format, convertArgs(a)...)
+}
+
+// Fprint is a wrapper for fmt.Fprint that treats each argument as if it were
+// passed with a default Formatter interface returned by NewFormatter. It
+// returns the number of bytes written and any write error encountered. See
+// NewFormatter for formatting details.
+//
+// This function is shorthand for the following syntax:
+//
+// fmt.Fprint(w, spew.NewFormatter(a), spew.NewFormatter(b))
+func Fprint(w io.Writer, a ...interface{}) (n int, err error) {
+ return fmt.Fprint(w, convertArgs(a)...)
+}
+
+// Fprintf is a wrapper for fmt.Fprintf that treats each argument as if it were
+// passed with a default Formatter interface returned by NewFormatter. It
+// returns the number of bytes written and any write error encountered. See
+// NewFormatter for formatting details.
+//
+// This function is shorthand for the following syntax:
+//
+// fmt.Fprintf(w, format, spew.NewFormatter(a), spew.NewFormatter(b))
+func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) {
+ return fmt.Fprintf(w, format, convertArgs(a)...)
+}
+
+// Fprintln is a wrapper for fmt.Fprintln that treats each argument as if it
+// passed with a default Formatter interface returned by NewFormatter. See
+// NewFormatter for formatting details.
+//
+// This function is shorthand for the following syntax:
+//
+// fmt.Fprintln(w, spew.NewFormatter(a), spew.NewFormatter(b))
+func Fprintln(w io.Writer, a ...interface{}) (n int, err error) {
+ return fmt.Fprintln(w, convertArgs(a)...)
+}
+
+// Print is a wrapper for fmt.Print that treats each argument as if it were
+// passed with a default Formatter interface returned by NewFormatter. It
+// returns the number of bytes written and any write error encountered. See
+// NewFormatter for formatting details.
+//
+// This function is shorthand for the following syntax:
+//
+// fmt.Print(spew.NewFormatter(a), spew.NewFormatter(b))
+func Print(a ...interface{}) (n int, err error) {
+ return fmt.Print(convertArgs(a)...)
+}
+
+// Printf is a wrapper for fmt.Printf that treats each argument as if it were
+// passed with a default Formatter interface returned by NewFormatter. It
+// returns the number of bytes written and any write error encountered. See
+// NewFormatter for formatting details.
+//
+// This function is shorthand for the following syntax:
+//
+// fmt.Printf(format, spew.NewFormatter(a), spew.NewFormatter(b))
+func Printf(format string, a ...interface{}) (n int, err error) {
+ return fmt.Printf(format, convertArgs(a)...)
+}
+
+// Println is a wrapper for fmt.Println that treats each argument as if it were
+// passed with a default Formatter interface returned by NewFormatter. It
+// returns the number of bytes written and any write error encountered. See
+// NewFormatter for formatting details.
+//
+// This function is shorthand for the following syntax:
+//
+// fmt.Println(spew.NewFormatter(a), spew.NewFormatter(b))
+func Println(a ...interface{}) (n int, err error) {
+ return fmt.Println(convertArgs(a)...)
+}
+
+// Sprint is a wrapper for fmt.Sprint that treats each argument as if it were
+// passed with a default Formatter interface returned by NewFormatter. It
+// returns the resulting string. See NewFormatter for formatting details.
+//
+// This function is shorthand for the following syntax:
+//
+// fmt.Sprint(spew.NewFormatter(a), spew.NewFormatter(b))
+func Sprint(a ...interface{}) string {
+ return fmt.Sprint(convertArgs(a)...)
+}
+
+// Sprintf is a wrapper for fmt.Sprintf that treats each argument as if it were
+// passed with a default Formatter interface returned by NewFormatter. It
+// returns the resulting string. See NewFormatter for formatting details.
+//
+// This function is shorthand for the following syntax:
+//
+// fmt.Sprintf(format, spew.NewFormatter(a), spew.NewFormatter(b))
+func Sprintf(format string, a ...interface{}) string {
+ return fmt.Sprintf(format, convertArgs(a)...)
+}
+
+// Sprintln is a wrapper for fmt.Sprintln that treats each argument as if it
+// were passed with a default Formatter interface returned by NewFormatter. It
+// returns the resulting string. See NewFormatter for formatting details.
+//
+// This function is shorthand for the following syntax:
+//
+// fmt.Sprintln(spew.NewFormatter(a), spew.NewFormatter(b))
+func Sprintln(a ...interface{}) string {
+ return fmt.Sprintln(convertArgs(a)...)
+}
+
+// convertArgs accepts a slice of arguments and returns a slice of the same
+// length with each argument converted to a default spew Formatter interface.
+func convertArgs(args []interface{}) (formatters []interface{}) {
+ formatters = make([]interface{}, len(args))
+ for index, arg := range args {
+ formatters[index] = NewFormatter(arg)
+ }
+ return formatters
+}
diff --git a/vendor/github.com/davecgh/go-spew/spew/spew_test.go b/vendor/github.com/davecgh/go-spew/spew/spew_test.go
new file mode 100644
index 000000000..dbbc08567
--- /dev/null
+++ b/vendor/github.com/davecgh/go-spew/spew/spew_test.go
@@ -0,0 +1,309 @@
+/*
+ * Copyright (c) 2013 Dave Collins <dave@davec.name>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+package spew_test
+
+import (
+ "bytes"
+ "fmt"
+ "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}
+
+ // Variables for tests on types which implement Stringer interface with and
+ // without a pointer receiver.
+ ts := stringer("test")
+ tps := pstringer("test")
+
+ // depthTester is used to test max depth handling for structs, array, slices
+ // and maps.
+ type depthTester struct {
+ ic indirCir1
+ arr [1]string
+ slice []string
+ m map[string]int
+ }
+ dt := depthTester{indirCir1{nil}, [1]string{"arr"}, []string{"slice"},
+ map[string]int{"one": 1}}
+
+ // Variable for tests on types which implement error interface.
+ te := customError(10)
+
+ spewTests = []spewTest{
+ {scsDefault, fCSFdump, "", int8(127), "(int8) 127\n"},
+ {scsDefault, fCSFprint, "", int16(32767), "32767"},
+ {scsDefault, fCSFprintf, "%v", int32(2147483647), "2147483647"},
+ {scsDefault, fCSFprintln, "", int(2147483647), "2147483647\n"},
+ {scsDefault, fCSPrint, "", int64(9223372036854775807), "9223372036854775807"},
+ {scsDefault, fCSPrintln, "", uint8(255), "255\n"},
+ {scsDefault, fCSSdump, "", uint8(64), "(uint8) 64\n"},
+ {scsDefault, fCSSprint, "", complex(1, 2), "(1+2i)"},
+ {scsDefault, fCSSprintf, "%v", complex(float32(3), 4), "(3+4i)"},
+ {scsDefault, fCSSprintln, "", complex(float64(5), 6), "(5+6i)\n"},
+ {scsDefault, fCSErrorf, "%#v", uint16(65535), "(uint16)65535"},
+ {scsDefault, fCSNewFormatter, "%v", uint32(4294967295), "4294967295"},
+ {scsDefault, fErrorf, "%v", uint64(18446744073709551615), "18446744073709551615"},
+ {scsDefault, fFprint, "", float32(3.14), "3.14"},
+ {scsDefault, fFprintln, "", float64(6.28), "6.28\n"},
+ {scsDefault, fPrint, "", true, "true"},
+ {scsDefault, fPrintln, "", false, "false\n"},
+ {scsDefault, fSdump, "", complex(-10, -20), "(complex128) (-10-20i)\n"},
+ {scsDefault, fSprint, "", complex(-1, -2), "(-1-2i)"},
+ {scsDefault, fSprintf, "%v", complex(float32(-3), -4), "(-3-4i)"},
+ {scsDefault, fSprintln, "", complex(float64(-5), -6), "(-5-6i)\n"},
+ {scsNoMethods, fCSFprint, "", ts, "test"},
+ {scsNoMethods, fCSFprint, "", &ts, "<*>test"},
+ {scsNoMethods, fCSFprint, "", tps, "test"},
+ {scsNoMethods, fCSFprint, "", &tps, "<*>test"},
+ {scsNoPmethods, fCSFprint, "", ts, "stringer test"},
+ {scsNoPmethods, fCSFprint, "", &ts, "<*>stringer test"},
+ {scsNoPmethods, fCSFprint, "", tps, "test"},
+ {scsNoPmethods, fCSFprint, "", &tps, "<*>stringer test"},
+ {scsMaxDepth, fCSFprint, "", dt, "{{<max>} [<max>] [<max>] map[<max>]}"},
+ {scsMaxDepth, fCSFdump, "", dt, "(spew_test.depthTester) {\n" +
+ " ic: (spew_test.indirCir1) {\n <max depth reached>\n },\n" +
+ " arr: ([1]string) (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"},
+ }
+}
+
+// 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
+ }
+ }
+}
diff --git a/vendor/github.com/davecgh/go-spew/spew/testdata/dumpcgo.go b/vendor/github.com/davecgh/go-spew/spew/testdata/dumpcgo.go
new file mode 100644
index 000000000..5c87dd456
--- /dev/null
+++ b/vendor/github.com/davecgh/go-spew/spew/testdata/dumpcgo.go
@@ -0,0 +1,82 @@
+// Copyright (c) 2013 Dave Collins <dave@davec.name>
+//
+// Permission to use, copy, modify, and distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+// NOTE: Due to the following build constraints, this file will only be compiled
+// when both cgo is supported and "-tags testcgo" is added to the go test
+// command line. This code should really only be in the dumpcgo_test.go file,
+// but unfortunately Go will not allow cgo in test files, so this is a
+// workaround to allow cgo types to be tested. This configuration is used
+// because spew itself does not require cgo to run even though it does handle
+// certain cgo types specially. Rather than forcing all clients to require cgo
+// and an external C compiler just to run the tests, this scheme makes them
+// optional.
+// +build cgo,testcgo
+
+package testdata
+
+/*
+#include <stdint.h>
+typedef unsigned char custom_uchar_t;
+
+char *ncp = 0;
+char *cp = "test";
+char ca[6] = {'t', 'e', 's', 't', '2', '\0'};
+unsigned char uca[6] = {'t', 'e', 's', 't', '3', '\0'};
+signed char sca[6] = {'t', 'e', 's', 't', '4', '\0'};
+uint8_t ui8ta[6] = {'t', 'e', 's', 't', '5', '\0'};
+custom_uchar_t tuca[6] = {'t', 'e', 's', 't', '6', '\0'};
+*/
+import "C"
+
+// GetCgoNullCharPointer returns a null char pointer via cgo. This is only
+// used for tests.
+func GetCgoNullCharPointer() interface{} {
+ return C.ncp
+}
+
+// GetCgoCharPointer returns a char pointer via cgo. This is only used for
+// tests.
+func GetCgoCharPointer() interface{} {
+ return C.cp
+}
+
+// GetCgoCharArray returns a char array via cgo and the array's len and cap.
+// This is only used for tests.
+func GetCgoCharArray() (interface{}, int, int) {
+ return C.ca, len(C.ca), cap(C.ca)
+}
+
+// GetCgoUnsignedCharArray returns an unsigned char array via cgo and the
+// array's len and cap. This is only used for tests.
+func GetCgoUnsignedCharArray() (interface{}, int, int) {
+ return C.uca, len(C.uca), cap(C.uca)
+}
+
+// GetCgoSignedCharArray returns a signed char array via cgo and the array's len
+// and cap. This is only used for tests.
+func GetCgoSignedCharArray() (interface{}, int, int) {
+ return C.sca, len(C.sca), cap(C.sca)
+}
+
+// GetCgoUint8tArray returns a uint8_t array via cgo and the array's len and
+// cap. This is only used for tests.
+func GetCgoUint8tArray() (interface{}, int, int) {
+ return C.ui8ta, len(C.ui8ta), cap(C.ui8ta)
+}
+
+// GetCgoTypdefedUnsignedCharArray returns a typedefed unsigned char array via
+// cgo and the array's len and cap. This is only used for tests.
+func GetCgoTypdefedUnsignedCharArray() (interface{}, int, int) {
+ return C.tuca, len(C.tuca), cap(C.tuca)
+}
diff --git a/vendor/github.com/davecgh/go-spew/test_coverage.txt b/vendor/github.com/davecgh/go-spew/test_coverage.txt
new file mode 100644
index 000000000..2cd087a2a
--- /dev/null
+++ b/vendor/github.com/davecgh/go-spew/test_coverage.txt
@@ -0,0 +1,61 @@
+
+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)
+
diff --git a/vendor/github.com/disintegration/imaging/adjust.go b/vendor/github.com/disintegration/imaging/adjust.go
index 9b1b83a4f..daee893cc 100644
--- a/vendor/github.com/disintegration/imaging/adjust.go
+++ b/vendor/github.com/disintegration/imaging/adjust.go
@@ -10,17 +10,17 @@ import (
//
// Example:
//
-// dstImage = imaging.AdjustFunc(
-// srcImage,
-// func(c color.NRGBA) color.NRGBA {
-// // shift the red channel by 16
+// dstImage = imaging.AdjustFunc(
+// srcImage,
+// func(c color.NRGBA) color.NRGBA {
+// // shift the red channel by 16
// r := int(c.R) + 16
// if r > 255 {
-// r = 255
-// }
-// return color.NRGBA{uint8(r), c.G, c.B, c.A}
-// }
-// )
+// r = 255
+// }
+// return color.NRGBA{uint8(r), c.G, c.B, c.A}
+// }
+// )
//
func AdjustFunc(img image.Image, fn func(c color.NRGBA) color.NRGBA) *image.NRGBA {
src := toNRGBA(img)
diff --git a/vendor/github.com/disintegration/imaging/effects.go b/vendor/github.com/disintegration/imaging/effects.go
index 25c940381..f358a48ea 100644
--- a/vendor/github.com/disintegration/imaging/effects.go
+++ b/vendor/github.com/disintegration/imaging/effects.go
@@ -14,7 +14,7 @@ func gaussianBlurKernel(x, sigma float64) float64 {
//
// Usage example:
//
-// dstImage := imaging.Blur(srcImage, 3.5)
+// dstImage := imaging.Blur(srcImage, 3.5)
//
func Blur(img image.Image, sigma float64) *image.NRGBA {
if sigma <= 0 {
@@ -138,7 +138,7 @@ func blurVertical(src *image.NRGBA, kernel []float64) *image.NRGBA {
//
// Usage example:
//
-// dstImage := imaging.Sharpen(srcImage, 3.5)
+// dstImage := imaging.Sharpen(srcImage, 3.5)
//
func Sharpen(img image.Image, sigma float64) *image.NRGBA {
if sigma <= 0 {
diff --git a/vendor/github.com/disintegration/imaging/resize.go b/vendor/github.com/disintegration/imaging/resize.go
index 937f63fe9..1659bbce8 100644
--- a/vendor/github.com/disintegration/imaging/resize.go
+++ b/vendor/github.com/disintegration/imaging/resize.go
@@ -59,7 +59,7 @@ func precomputeWeights(dstSize, srcSize int, filter ResampleFilter) [][]indexWei
//
// Usage example:
//
-// dstImage := imaging.Resize(srcImage, 800, 600, imaging.Lanczos)
+// dstImage := imaging.Resize(srcImage, 800, 600, imaging.Lanczos)
//
func Resize(img image.Image, width, height int, filter ResampleFilter) *image.NRGBA {
dstW, dstH := width, height
@@ -239,7 +239,7 @@ func resizeNearest(src *image.NRGBA, width, height int) *image.NRGBA {
//
// Usage example:
//
-// dstImage := imaging.Fit(srcImage, 800, 600, imaging.Lanczos)
+// dstImage := imaging.Fit(srcImage, 800, 600, imaging.Lanczos)
//
func Fit(img image.Image, width, height int, filter ResampleFilter) *image.NRGBA {
maxW, maxH := width, height
@@ -284,7 +284,7 @@ func Fit(img image.Image, width, height int, filter ResampleFilter) *image.NRGBA
//
// Usage example:
//
-// dstImage := imaging.Fill(srcImage, 800, 600, imaging.Center, imaging.Lanczos)
+// dstImage := imaging.Fill(srcImage, 800, 600, imaging.Center, imaging.Lanczos)
//
func Fill(img image.Image, width, height int, anchor Anchor, filter ResampleFilter) *image.NRGBA {
minW, minH := width, height
@@ -326,7 +326,7 @@ func Fill(img image.Image, width, height int, anchor Anchor, filter ResampleFilt
//
// Usage example:
//
-// dstImage := imaging.Thumbnail(srcImage, 100, 100, imaging.Lanczos)
+// dstImage := imaging.Thumbnail(srcImage, 100, 100, imaging.Lanczos)
//
func Thumbnail(img image.Image, width, height int, filter ResampleFilter) *image.NRGBA {
return Fill(img, width, height, Center, filter)
diff --git a/vendor/github.com/disintegration/imaging/tools.go b/vendor/github.com/disintegration/imaging/tools.go
index 2c6d68eea..e57225c58 100644
--- a/vendor/github.com/disintegration/imaging/tools.go
+++ b/vendor/github.com/disintegration/imaging/tools.go
@@ -136,11 +136,11 @@ func PasteCenter(background, img image.Image) *image.NRGBA {
//
// Usage examples:
//
-// // draw the sprite over the background at position (50, 50)
-// dstImage := imaging.Overlay(backgroundImage, spriteImage, image.Pt(50, 50), 1.0)
+// // draw the sprite over the background at position (50, 50)
+// dstImage := imaging.Overlay(backgroundImage, spriteImage, image.Pt(50, 50), 1.0)
//
-// // blend two opaque images of the same size
-// dstImage := imaging.Overlay(imageOne, imageTwo, image.Pt(0, 0), 0.5)
+// // blend two opaque images of the same size
+// dstImage := imaging.Overlay(imageOne, imageTwo, image.Pt(0, 0), 0.5)
//
func Overlay(background, img image.Image, pos image.Point, opacity float64) *image.NRGBA {
opacity = math.Min(math.Max(opacity, 0.0), 1.0) // check: 0.0 <= opacity <= 1.0
diff --git a/vendor/github.com/disintegration/imaging/transform.go b/vendor/github.com/disintegration/imaging/transform.go
index 30410c278..43668dfd7 100644
--- a/vendor/github.com/disintegration/imaging/transform.go
+++ b/vendor/github.com/disintegration/imaging/transform.go
@@ -206,6 +206,19 @@ func Rotate270(img image.Image) *image.NRGBA {
// The angle parameter is the rotation angle in degrees.
// The bgColor parameter specifies the color of the uncovered zone after the rotation.
func Rotate(img image.Image, angle float64, bgColor color.Color) *image.NRGBA {
+ angle = angle - math.Floor(angle/360)*360
+
+ switch angle {
+ case 0:
+ return Clone(img)
+ case 90:
+ return Rotate90(img)
+ case 180:
+ return Rotate180(img)
+ case 270:
+ return Rotate270(img)
+ }
+
src := toNRGBA(img)
srcW := src.Bounds().Max.X
srcH := src.Bounds().Max.Y
diff --git a/vendor/github.com/disintegration/imaging/transform_test.go b/vendor/github.com/disintegration/imaging/transform_test.go
index 3772225a3..447dd1ea0 100644
--- a/vendor/github.com/disintegration/imaging/transform_test.go
+++ b/vendor/github.com/disintegration/imaging/transform_test.go
@@ -406,6 +406,170 @@ func TestRotate(t *testing.T) {
},
},
},
+ {
+ "Rotate -360*10",
+ &image.NRGBA{
+ Rect: image.Rect(-1, -1, 0, 1),
+ Stride: 1 * 4,
+ Pix: []uint8{
+ 0x00, 0xff, 0x00, 0xff,
+ 0xff, 0x00, 0x00, 0xff,
+ },
+ },
+ -360 * 10,
+ color.Black,
+ &image.NRGBA{
+ Rect: image.Rect(0, 0, 1, 2),
+ Stride: 1 * 4,
+ Pix: []uint8{
+ 0x00, 0xff, 0x00, 0xff,
+ 0xff, 0x00, 0x00, 0xff,
+ },
+ },
+ },
+ {
+ "Rotate -360*10 + 90",
+ &image.NRGBA{
+ Rect: image.Rect(-1, -1, 0, 1),
+ Stride: 1 * 4,
+ Pix: []uint8{
+ 0xff, 0x00, 0x00, 0xff,
+ 0x00, 0xff, 0x00, 0xff,
+ },
+ },
+ -360*10 + 90,
+ color.Black,
+ &image.NRGBA{
+ Rect: image.Rect(0, 0, 2, 1),
+ Stride: 2 * 4,
+ Pix: []uint8{
+ 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff,
+ },
+ },
+ },
+ {
+ "Rotate -360*10 + 180",
+ &image.NRGBA{
+ Rect: image.Rect(-1, -1, 0, 1),
+ Stride: 1 * 4,
+ Pix: []uint8{
+ 0xff, 0x00, 0x00, 0xff,
+ 0x00, 0xff, 0x00, 0xff,
+ },
+ },
+ -360*10 + 180,
+ color.Black,
+ &image.NRGBA{
+ Rect: image.Rect(0, 0, 1, 2),
+ Stride: 1 * 4,
+ Pix: []uint8{
+ 0x00, 0xff, 0x00, 0xff,
+ 0xff, 0x00, 0x00, 0xff,
+ },
+ },
+ },
+ {
+ "Rotate -360*10 + 270",
+ &image.NRGBA{
+ Rect: image.Rect(-1, -1, 0, 1),
+ Stride: 1 * 4,
+ Pix: []uint8{
+ 0xff, 0x00, 0x00, 0xff,
+ 0x00, 0xff, 0x00, 0xff,
+ },
+ },
+ -360*10 + 270,
+ color.Black,
+ &image.NRGBA{
+ Rect: image.Rect(0, 0, 2, 1),
+ Stride: 2 * 4,
+ Pix: []uint8{
+ 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff,
+ },
+ },
+ },
+ {
+ "Rotate 360*10",
+ &image.NRGBA{
+ Rect: image.Rect(-1, -1, 0, 1),
+ Stride: 1 * 4,
+ Pix: []uint8{
+ 0x00, 0xff, 0x00, 0xff,
+ 0xff, 0x00, 0x00, 0xff,
+ },
+ },
+ 360 * 10,
+ color.Black,
+ &image.NRGBA{
+ Rect: image.Rect(0, 0, 1, 2),
+ Stride: 1 * 4,
+ Pix: []uint8{
+ 0x00, 0xff, 0x00, 0xff,
+ 0xff, 0x00, 0x00, 0xff,
+ },
+ },
+ },
+ {
+ "Rotate 360*10 + 90",
+ &image.NRGBA{
+ Rect: image.Rect(-1, -1, 0, 1),
+ Stride: 1 * 4,
+ Pix: []uint8{
+ 0xff, 0x00, 0x00, 0xff,
+ 0x00, 0xff, 0x00, 0xff,
+ },
+ },
+ 360*10 + 90,
+ color.Black,
+ &image.NRGBA{
+ Rect: image.Rect(0, 0, 2, 1),
+ Stride: 2 * 4,
+ Pix: []uint8{
+ 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff,
+ },
+ },
+ },
+ {
+ "Rotate 360*10 + 180",
+ &image.NRGBA{
+ Rect: image.Rect(-1, -1, 0, 1),
+ Stride: 1 * 4,
+ Pix: []uint8{
+ 0xff, 0x00, 0x00, 0xff,
+ 0x00, 0xff, 0x00, 0xff,
+ },
+ },
+ 360*10 + 180,
+ color.Black,
+ &image.NRGBA{
+ Rect: image.Rect(0, 0, 1, 2),
+ Stride: 1 * 4,
+ Pix: []uint8{
+ 0x00, 0xff, 0x00, 0xff,
+ 0xff, 0x00, 0x00, 0xff,
+ },
+ },
+ },
+ {
+ "Rotate 360*10 + 270",
+ &image.NRGBA{
+ Rect: image.Rect(-1, -1, 0, 1),
+ Stride: 1 * 4,
+ Pix: []uint8{
+ 0xff, 0x00, 0x00, 0xff,
+ 0x00, 0xff, 0x00, 0xff,
+ },
+ },
+ 360*10 + 270,
+ color.Black,
+ &image.NRGBA{
+ Rect: image.Rect(0, 0, 2, 1),
+ Stride: 2 * 4,
+ Pix: []uint8{
+ 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff,
+ },
+ },
+ },
}
for _, test := range testCases {
got := Rotate(test.src, test.angle, test.bg)
diff --git a/vendor/github.com/go-ini/ini/.travis.yml b/vendor/github.com/go-ini/ini/.travis.yml
index 9a41f64df..d9d1b8ffa 100644
--- a/vendor/github.com/go-ini/ini/.travis.yml
+++ b/vendor/github.com/go-ini/ini/.travis.yml
@@ -1,11 +1,10 @@
sudo: false
language: go
go:
- - 1.4
- - 1.5
- - 1.6
- - 1.7
- - 1.8
+ - 1.5.x
+ - 1.6.x
+ - 1.7.x
+ - 1.8.x
- master
script:
diff --git a/vendor/github.com/go-ini/ini/ini.go b/vendor/github.com/go-ini/ini/ini.go
index f8827ddd2..7f3c4d1ed 100644
--- a/vendor/github.com/go-ini/ini/ini.go
+++ b/vendor/github.com/go-ini/ini/ini.go
@@ -24,10 +24,8 @@ import (
"os"
"regexp"
"runtime"
- "strconv"
"strings"
"sync"
- "time"
)
const (
@@ -37,7 +35,7 @@ const (
// Maximum allowed depth when recursively substituing variable names.
_DEPTH_VALUES = 99
- _VERSION = "1.28.1"
+ _VERSION = "1.28.2"
)
// Version returns current package version literal.
@@ -398,10 +396,7 @@ func (f *File) Append(source interface{}, others ...interface{}) error {
return f.Reload()
}
-// WriteToIndent writes content into io.Writer with given indention.
-// If PrettyFormat has been set to be true,
-// it will align "=" sign with spaces under each section.
-func (f *File) WriteToIndent(w io.Writer, indent string) (n int64, err error) {
+func (f *File) writeToBuffer(indent string) (*bytes.Buffer, error) {
equalSign := "="
if PrettyFormat {
equalSign = " = "
@@ -415,14 +410,14 @@ func (f *File) WriteToIndent(w io.Writer, indent string) (n int64, err error) {
if sec.Comment[0] != '#' && sec.Comment[0] != ';' {
sec.Comment = "; " + sec.Comment
}
- if _, err = buf.WriteString(sec.Comment + LineBreak); err != nil {
- return 0, err
+ if _, err := buf.WriteString(sec.Comment + LineBreak); err != nil {
+ return nil, err
}
}
if i > 0 || DefaultHeader {
- if _, err = buf.WriteString("[" + sname + "]" + LineBreak); err != nil {
- return 0, err
+ if _, err := buf.WriteString("[" + sname + "]" + LineBreak); err != nil {
+ return nil, err
}
} else {
// Write nothing if default section is empty
@@ -432,8 +427,8 @@ func (f *File) WriteToIndent(w io.Writer, indent string) (n int64, err error) {
}
if sec.isRawSection {
- if _, err = buf.WriteString(sec.rawBody); err != nil {
- return 0, err
+ if _, err := buf.WriteString(sec.rawBody); err != nil {
+ return nil, err
}
continue
}
@@ -469,8 +464,8 @@ func (f *File) WriteToIndent(w io.Writer, indent string) (n int64, err error) {
if key.Comment[0] != '#' && key.Comment[0] != ';' {
key.Comment = "; " + key.Comment
}
- if _, err = buf.WriteString(key.Comment + LineBreak); err != nil {
- return 0, err
+ if _, err := buf.WriteString(key.Comment + LineBreak); err != nil {
+ return nil, err
}
}
@@ -488,8 +483,8 @@ func (f *File) WriteToIndent(w io.Writer, indent string) (n int64, err error) {
}
for _, val := range key.ValueWithShadows() {
- if _, err = buf.WriteString(kname); err != nil {
- return 0, err
+ if _, err := buf.WriteString(kname); err != nil {
+ return nil, err
}
if key.isBooleanType {
@@ -510,20 +505,31 @@ func (f *File) WriteToIndent(w io.Writer, indent string) (n int64, err error) {
} else if !f.options.IgnoreInlineComment && strings.ContainsAny(val, "#;") {
val = "`" + val + "`"
}
- if _, err = buf.WriteString(equalSign + val + LineBreak); err != nil {
- return 0, err
+ if _, err := buf.WriteString(equalSign + val + LineBreak); err != nil {
+ return nil, err
}
}
}
if PrettySection {
// Put a line between sections
- if _, err = buf.WriteString(LineBreak); err != nil {
- return 0, err
+ if _, err := buf.WriteString(LineBreak); err != nil {
+ return nil, err
}
}
}
+ return buf, nil
+}
+
+// WriteToIndent writes content into io.Writer with given indention.
+// If PrettyFormat has been set to be true,
+// it will align "=" sign with spaces under each section.
+func (f *File) WriteToIndent(w io.Writer, indent string) (int64, error) {
+ buf, err := f.writeToBuffer(indent)
+ if err != nil {
+ return 0, err
+ }
return buf.WriteTo(w)
}
@@ -536,23 +542,12 @@ func (f *File) WriteTo(w io.Writer) (int64, error) {
func (f *File) SaveToIndent(filename, indent string) error {
// Note: Because we are truncating with os.Create,
// so it's safer to save to a temporary file location and rename afte done.
- tmpPath := filename + "." + strconv.Itoa(time.Now().Nanosecond()) + ".tmp"
- defer os.Remove(tmpPath)
-
- fw, err := os.Create(tmpPath)
+ buf, err := f.writeToBuffer(indent)
if err != nil {
return err
}
- if _, err = f.WriteToIndent(fw, indent); err != nil {
- fw.Close()
- return err
- }
- fw.Close()
-
- // Remove old file and rename the new one.
- os.Remove(filename)
- return os.Rename(tmpPath, filename)
+ return ioutil.WriteFile(filename, buf.Bytes(), 0666)
}
// SaveTo writes content to file system.
diff --git a/vendor/github.com/go-redis/redis/.travis.yml b/vendor/github.com/go-redis/redis/.travis.yml
index f8e0d652e..f4666c593 100644
--- a/vendor/github.com/go-redis/redis/.travis.yml
+++ b/vendor/github.com/go-redis/redis/.travis.yml
@@ -5,14 +5,14 @@ services:
- redis-server
go:
- - 1.4
- - 1.7
- - 1.8
+ - 1.4.x
+ - 1.7.x
+ - 1.8.x
- tip
matrix:
allow_failures:
- - go: 1.4
+ - go: 1.4.x
- go: tip
install:
diff --git a/vendor/github.com/go-redis/redis/README.md b/vendor/github.com/go-redis/redis/README.md
index f3c61795e..fd036496d 100644
--- a/vendor/github.com/go-redis/redis/README.md
+++ b/vendor/github.com/go-redis/redis/README.md
@@ -102,7 +102,7 @@ Some corner cases:
vals, err := client.ZInterStore("out", redis.ZStore{Weights: []int64{2, 3}}, "zset1", "zset2").Result()
EVAL "return {KEYS[1],ARGV[1]}" 1 "key" "hello"
- vals, err := client.Eval("return {KEYS[1],ARGV[1]}", []string{"key"}, []string{"hello"}).Result()
+ vals, err := client.Eval("return {KEYS[1],ARGV[1]}", []string{"key"}, "hello").Result()
## Benchmark
diff --git a/vendor/github.com/go-redis/redis/cluster.go b/vendor/github.com/go-redis/redis/cluster.go
index f758b01b9..647a25be3 100644
--- a/vendor/github.com/go-redis/redis/cluster.go
+++ b/vendor/github.com/go-redis/redis/cluster.go
@@ -28,18 +28,19 @@ type ClusterOptions struct {
// Default is 16.
MaxRedirects int
- // Enables read queries for a connection to a Redis Cluster slave node.
+ // Enables read-only commands on slave nodes.
ReadOnly bool
-
- // Enables routing read-only queries to the closest master or slave node.
+ // Allows routing read-only commands to the closest master or slave node.
RouteByLatency bool
// Following options are copied from Options struct.
OnConnect func(*Conn) error
- MaxRetries int
- Password string
+ MaxRetries int
+ MinRetryBackoff time.Duration
+ MaxRetryBackoff time.Duration
+ Password string
DialTimeout time.Duration
ReadTimeout time.Duration
@@ -62,6 +63,19 @@ func (opt *ClusterOptions) init() {
if opt.RouteByLatency {
opt.ReadOnly = true
}
+
+ switch opt.MinRetryBackoff {
+ case -1:
+ opt.MinRetryBackoff = 0
+ case 0:
+ opt.MinRetryBackoff = 8 * time.Millisecond
+ }
+ switch opt.MaxRetryBackoff {
+ case -1:
+ opt.MaxRetryBackoff = 0
+ case 0:
+ opt.MaxRetryBackoff = 512 * time.Millisecond
+ }
}
func (opt *ClusterOptions) clientOptions() *Options {
@@ -70,9 +84,11 @@ func (opt *ClusterOptions) clientOptions() *Options {
return &Options{
OnConnect: opt.OnConnect,
- MaxRetries: opt.MaxRetries,
- Password: opt.Password,
- ReadOnly: opt.ReadOnly,
+ MaxRetries: opt.MaxRetries,
+ MinRetryBackoff: opt.MinRetryBackoff,
+ MaxRetryBackoff: opt.MaxRetryBackoff,
+ Password: opt.Password,
+ readOnly: opt.ReadOnly,
DialTimeout: opt.DialTimeout,
ReadTimeout: opt.ReadTimeout,
@@ -91,7 +107,9 @@ func (opt *ClusterOptions) clientOptions() *Options {
type clusterNode struct {
Client *Client
Latency time.Duration
- loading time.Time
+
+ loading time.Time
+ generation uint32
}
func newClusterNode(clOpt *ClusterOptions, addr string) *clusterNode {
@@ -122,6 +140,17 @@ func (n *clusterNode) Loading() bool {
return !n.loading.IsZero() && time.Since(n.loading) < time.Minute
}
+func (n *clusterNode) Generation() uint32 {
+ return n.generation
+}
+
+func (n *clusterNode) SetGeneration(gen uint32) {
+ if gen < n.generation {
+ panic("gen < n.generation")
+ }
+ n.generation = gen
+}
+
//------------------------------------------------------------------------------
type clusterNodes struct {
@@ -131,6 +160,8 @@ type clusterNodes struct {
addrs []string
nodes map[string]*clusterNode
closed bool
+
+ generation uint32
}
func newClusterNodes(opt *ClusterOptions) *clusterNodes {
@@ -161,6 +192,39 @@ func (c *clusterNodes) Close() error {
return firstErr
}
+func (c *clusterNodes) NextGeneration() uint32 {
+ c.generation++
+ return c.generation
+}
+
+// GC removes unused nodes.
+func (c *clusterNodes) GC(generation uint32) error {
+ var collected []*clusterNode
+ c.mu.Lock()
+ for i := 0; i < len(c.addrs); {
+ addr := c.addrs[i]
+ node := c.nodes[addr]
+ if node.Generation() >= generation {
+ i++
+ continue
+ }
+
+ c.addrs = append(c.addrs[:i], c.addrs[i+1:]...)
+ delete(c.nodes, addr)
+ collected = append(collected, node)
+ }
+ c.mu.Unlock()
+
+ var firstErr error
+ for _, node := range collected {
+ if err := node.Client.Close(); err != nil && firstErr == nil {
+ firstErr = err
+ }
+ }
+
+ return firstErr
+}
+
func (c *clusterNodes) All() ([]*clusterNode, error) {
c.mu.RLock()
defer c.mu.RUnlock()
@@ -176,7 +240,7 @@ func (c *clusterNodes) All() ([]*clusterNode, error) {
return nodes, nil
}
-func (c *clusterNodes) Get(addr string) (*clusterNode, error) {
+func (c *clusterNodes) GetOrCreate(addr string) (*clusterNode, error) {
var node *clusterNode
var ok bool
@@ -223,7 +287,7 @@ func (c *clusterNodes) Random() (*clusterNode, error) {
var nodeErr error
for i := 0; i <= c.opt.MaxRedirects; i++ {
n := rand.Intn(len(addrs))
- node, err := c.Get(addrs[n])
+ node, err := c.GetOrCreate(addrs[n])
if err != nil {
return nil, err
}
@@ -239,30 +303,45 @@ func (c *clusterNodes) Random() (*clusterNode, error) {
//------------------------------------------------------------------------------
type clusterState struct {
- nodes *clusterNodes
+ nodes *clusterNodes
+ masters []*clusterNode
+ slaves []*clusterNode
+
slots [][]*clusterNode
+
+ generation uint32
}
func newClusterState(nodes *clusterNodes, slots []ClusterSlot, origin string) (*clusterState, error) {
c := clusterState{
- nodes: nodes,
+ nodes: nodes,
+ generation: nodes.NextGeneration(),
+
slots: make([][]*clusterNode, hashtag.SlotNumber),
}
isLoopbackOrigin := isLoopbackAddr(origin)
for _, slot := range slots {
var nodes []*clusterNode
- for _, slotNode := range slot.Nodes {
+ for i, slotNode := range slot.Nodes {
addr := slotNode.Addr
if !isLoopbackOrigin && isLoopbackAddr(addr) {
addr = origin
}
- node, err := c.nodes.Get(addr)
+ node, err := c.nodes.GetOrCreate(addr)
if err != nil {
return nil, err
}
+
+ node.SetGeneration(c.generation)
nodes = append(nodes, node)
+
+ if i == 0 {
+ c.masters = appendNode(c.masters, node)
+ } else {
+ c.slaves = appendNode(c.slaves, node)
+ }
}
for i := slot.Start; i <= slot.End; i++ {
@@ -327,7 +406,7 @@ func (c *clusterState) slotClosestNode(slot int) (*clusterNode, error) {
}
func (c *clusterState) slotNodes(slot int) []*clusterNode {
- if slot < len(c.slots) {
+ if slot >= 0 && slot < len(c.slots) {
return c.slots[slot]
}
return nil
@@ -348,7 +427,7 @@ type ClusterClient struct {
cmdsInfoOnce internal.Once
cmdsInfo map[string]*CommandInfo
- // Reports where slots reloading is in progress.
+ // Reports whether slots reloading is in progress.
reloading uint32
}
@@ -365,12 +444,12 @@ func NewClusterClient(opt *ClusterOptions) *ClusterClient {
// Add initial nodes.
for _, addr := range opt.Addrs {
- _, _ = c.nodes.Get(addr)
+ _, _ = c.nodes.GetOrCreate(addr)
}
// Preload cluster slots.
for i := 0; i < 10; i++ {
- state, err := c.reloadSlots()
+ state, err := c.reloadState()
if err == nil {
c._state.Store(state)
break
@@ -394,7 +473,7 @@ func (c *ClusterClient) state() *clusterState {
if v != nil {
return v.(*clusterState)
}
- c.lazyReloadSlots()
+ c.lazyReloadState()
return nil
}
@@ -476,6 +555,10 @@ func (c *ClusterClient) Process(cmd Cmder) error {
var ask bool
for attempt := 0; attempt <= c.opt.MaxRedirects; attempt++ {
+ if attempt > 0 {
+ time.Sleep(node.Client.retryBackoff(attempt))
+ }
+
if ask {
pipe := node.Client.Pipeline()
pipe.Process(NewCmd("ASKING"))
@@ -487,19 +570,20 @@ func (c *ClusterClient) Process(cmd Cmder) error {
err = node.Client.Process(cmd)
}
- // If there is no (real) error - we are done.
+ // If there is no error - we are done.
if err == nil {
return nil
}
// If slave is loading - read from master.
if c.opt.ReadOnly && internal.IsLoadingError(err) {
+ // TODO: race
node.loading = time.Now()
continue
}
// On network errors try random node.
- if internal.IsRetryableError(err) {
+ if internal.IsRetryableError(err) || internal.IsClusterDownError(err) {
node, err = c.nodes.Random()
if err != nil {
cmd.setErr(err)
@@ -516,11 +600,11 @@ func (c *ClusterClient) Process(cmd Cmder) error {
if state != nil && slot >= 0 {
master, _ := state.slotMasterNode(slot)
if moved && (master == nil || master.Client.getAddr() != addr) {
- c.lazyReloadSlots()
+ c.lazyReloadState()
}
}
- node, err = c.nodes.Get(addr)
+ node, err = c.nodes.GetOrCreate(addr)
if err != nil {
cmd.setErr(err)
return err
@@ -535,17 +619,17 @@ func (c *ClusterClient) Process(cmd Cmder) error {
return cmd.Err()
}
-// ForEachNode concurrently calls the fn on each ever known node in the cluster.
+// ForEachMaster concurrently calls the fn on each master node in the cluster.
// It returns the first error if any.
-func (c *ClusterClient) ForEachNode(fn func(client *Client) error) error {
- nodes, err := c.nodes.All()
- if err != nil {
- return err
+func (c *ClusterClient) ForEachMaster(fn func(client *Client) error) error {
+ state := c.state()
+ if state == nil {
+ return errNilClusterState
}
var wg sync.WaitGroup
errCh := make(chan error, 1)
- for _, node := range nodes {
+ for _, master := range state.masters {
wg.Add(1)
go func(node *clusterNode) {
defer wg.Done()
@@ -556,7 +640,7 @@ func (c *ClusterClient) ForEachNode(fn func(client *Client) error) error {
default:
}
}
- }(node)
+ }(master)
}
wg.Wait()
@@ -568,28 +652,17 @@ func (c *ClusterClient) ForEachNode(fn func(client *Client) error) error {
}
}
-// ForEachMaster concurrently calls the fn on each master node in the cluster.
+// ForEachSlave concurrently calls the fn on each slave node in the cluster.
// It returns the first error if any.
-func (c *ClusterClient) ForEachMaster(fn func(client *Client) error) error {
+func (c *ClusterClient) ForEachSlave(fn func(client *Client) error) error {
state := c.state()
if state == nil {
return errNilClusterState
}
var wg sync.WaitGroup
- visited := make(map[*clusterNode]struct{})
errCh := make(chan error, 1)
- for _, nodes := range state.slots {
- if len(nodes) == 0 {
- continue
- }
-
- master := nodes[0]
- if _, ok := visited[master]; ok {
- continue
- }
- visited[master] = struct{}{}
-
+ for _, slave := range state.slaves {
wg.Add(1)
go func(node *clusterNode) {
defer wg.Done()
@@ -600,7 +673,7 @@ func (c *ClusterClient) ForEachMaster(fn func(client *Client) error) error {
default:
}
}
- }(master)
+ }(slave)
}
wg.Wait()
@@ -612,16 +685,64 @@ func (c *ClusterClient) ForEachMaster(fn func(client *Client) error) error {
}
}
+// ForEachNode concurrently calls the fn on each known node in the cluster.
+// It returns the first error if any.
+func (c *ClusterClient) ForEachNode(fn func(client *Client) error) error {
+ state := c.state()
+ if state == nil {
+ return errNilClusterState
+ }
+
+ var wg sync.WaitGroup
+ errCh := make(chan error, 1)
+ worker := func(node *clusterNode) {
+ defer wg.Done()
+ err := fn(node.Client)
+ if err != nil {
+ select {
+ case errCh <- err:
+ default:
+ }
+ }
+ }
+
+ for _, node := range state.masters {
+ wg.Add(1)
+ go worker(node)
+ }
+ for _, node := range state.slaves {
+ wg.Add(1)
+ go worker(node)
+ }
+
+ wg.Wait()
+ select {
+ case err := <-errCh:
+ return err
+ default:
+ return nil
+ }
+}
+
// PoolStats returns accumulated connection pool stats.
func (c *ClusterClient) PoolStats() *PoolStats {
var acc PoolStats
- nodes, err := c.nodes.All()
- if err != nil {
+ state := c.state()
+ if state == nil {
return &acc
}
- for _, node := range nodes {
+ for _, node := range state.masters {
+ s := node.Client.connPool.Stats()
+ acc.Requests += s.Requests
+ acc.Hits += s.Hits
+ acc.Timeouts += s.Timeouts
+ acc.TotalConns += s.TotalConns
+ acc.FreeConns += s.FreeConns
+ }
+
+ for _, node := range state.slaves {
s := node.Client.connPool.Stats()
acc.Requests += s.Requests
acc.Hits += s.Hits
@@ -629,33 +750,42 @@ func (c *ClusterClient) PoolStats() *PoolStats {
acc.TotalConns += s.TotalConns
acc.FreeConns += s.FreeConns
}
+
return &acc
}
-func (c *ClusterClient) lazyReloadSlots() {
+func (c *ClusterClient) lazyReloadState() {
if !atomic.CompareAndSwapUint32(&c.reloading, 0, 1) {
return
}
go func() {
- for i := 0; i < 1000; i++ {
- state, err := c.reloadSlots()
+ defer atomic.StoreUint32(&c.reloading, 0)
+
+ var state *clusterState
+ for {
+ var err error
+ state, err = c.reloadState()
if err == pool.ErrClosed {
- break
+ return
}
- if err == nil {
- c._state.Store(state)
- break
+
+ if err != nil {
+ time.Sleep(time.Millisecond)
+ continue
}
- time.Sleep(time.Millisecond)
+
+ c._state.Store(state)
+ break
}
time.Sleep(3 * time.Second)
- atomic.StoreUint32(&c.reloading, 0)
+ c.nodes.GC(state.generation)
}()
}
-func (c *ClusterClient) reloadSlots() (*clusterState, error) {
+// Not thread-safe.
+func (c *ClusterClient) reloadState() (*clusterState, error) {
node, err := c.nodes.Random()
if err != nil {
return nil, err
@@ -720,14 +850,14 @@ func (c *ClusterClient) pipelineExec(cmds []Cmder) error {
failedCmds := make(map[*clusterNode][]Cmder)
for node, cmds := range cmdsMap {
- cn, _, err := node.Client.conn()
+ cn, _, err := node.Client.getConn()
if err != nil {
setCmdsErr(cmds, err)
continue
}
err = c.pipelineProcessCmds(cn, cmds, failedCmds)
- node.Client.putConn(cn, err)
+ node.Client.releaseConn(cn, err)
}
if len(failedCmds) == 0 {
@@ -799,9 +929,9 @@ func (c *ClusterClient) pipelineReadCmds(
func (c *ClusterClient) checkMovedErr(cmd Cmder, failedCmds map[*clusterNode][]Cmder) error {
moved, ask, addr := internal.IsMovedError(cmd.Err())
if moved {
- c.lazyReloadSlots()
+ c.lazyReloadState()
- node, err := c.nodes.Get(addr)
+ node, err := c.nodes.GetOrCreate(addr)
if err != nil {
return err
}
@@ -809,7 +939,7 @@ func (c *ClusterClient) checkMovedErr(cmd Cmder, failedCmds map[*clusterNode][]C
failedCmds[node] = append(failedCmds[node], cmd)
}
if ask {
- node, err := c.nodes.Get(addr)
+ node, err := c.nodes.GetOrCreate(addr)
if err != nil {
return err
}
@@ -855,14 +985,14 @@ func (c *ClusterClient) txPipelineExec(cmds []Cmder) error {
failedCmds := make(map[*clusterNode][]Cmder)
for node, cmds := range cmdsMap {
- cn, _, err := node.Client.conn()
+ cn, _, err := node.Client.getConn()
if err != nil {
setCmdsErr(cmds, err)
continue
}
err = c.txPipelineProcessCmds(node, cn, cmds, failedCmds)
- node.Client.putConn(cn, err)
+ node.Client.releaseConn(cn, err)
}
if len(failedCmds) == 0 {
@@ -966,6 +1096,56 @@ func (c *ClusterClient) txPipelineReadQueued(
return firstErr
}
+func (c *ClusterClient) pubSub(channels []string) *PubSub {
+ opt := c.opt.clientOptions()
+
+ var node *clusterNode
+ return &PubSub{
+ opt: opt,
+
+ newConn: func(channels []string) (*pool.Conn, error) {
+ if node == nil {
+ var slot int
+ if len(channels) > 0 {
+ slot = hashtag.Slot(channels[0])
+ } else {
+ slot = -1
+ }
+
+ masterNode, err := c.state().slotMasterNode(slot)
+ if err != nil {
+ return nil, err
+ }
+ node = masterNode
+ }
+ return node.Client.newConn()
+ },
+ closeConn: func(cn *pool.Conn) error {
+ return node.Client.connPool.CloseConn(cn)
+ },
+ }
+}
+
+// Subscribe subscribes the client to the specified channels.
+// Channels can be omitted to create empty subscription.
+func (c *ClusterClient) Subscribe(channels ...string) *PubSub {
+ pubsub := c.pubSub(channels)
+ if len(channels) > 0 {
+ _ = pubsub.Subscribe(channels...)
+ }
+ return pubsub
+}
+
+// PSubscribe subscribes the client to the given patterns.
+// Patterns can be omitted to create empty subscription.
+func (c *ClusterClient) PSubscribe(channels ...string) *PubSub {
+ pubsub := c.pubSub(channels)
+ if len(channels) > 0 {
+ _ = pubsub.PSubscribe(channels...)
+ }
+ return pubsub
+}
+
func isLoopbackAddr(addr string) bool {
host, _, err := net.SplitHostPort(addr)
if err != nil {
@@ -979,3 +1159,12 @@ func isLoopbackAddr(addr string) bool {
return ip.IsLoopback()
}
+
+func appendNode(nodes []*clusterNode, node *clusterNode) []*clusterNode {
+ for _, n := range nodes {
+ if n == node {
+ return nodes
+ }
+ }
+ return append(nodes, node)
+}
diff --git a/vendor/github.com/go-redis/redis/cluster_test.go b/vendor/github.com/go-redis/redis/cluster_test.go
index 3a69255a4..324bd1ce1 100644
--- a/vendor/github.com/go-redis/redis/cluster_test.go
+++ b/vendor/github.com/go-redis/redis/cluster_test.go
@@ -75,7 +75,7 @@ func startCluster(scenario *clusterScenario) error {
scenario.nodeIds[pos] = info[:40]
}
- // Meet cluster nodes
+ // Meet cluster nodes.
for _, client := range scenario.clients {
err := client.ClusterMeet("127.0.0.1", scenario.ports[0]).Err()
if err != nil {
@@ -83,7 +83,7 @@ func startCluster(scenario *clusterScenario) error {
}
}
- // Bootstrap masters
+ // Bootstrap masters.
slots := []int{0, 5000, 10000, 16384}
for pos, master := range scenario.masters() {
err := master.ClusterAddSlotsRange(slots[pos], slots[pos+1]-1).Err()
@@ -92,7 +92,7 @@ func startCluster(scenario *clusterScenario) error {
}
}
- // Bootstrap slaves
+ // Bootstrap slaves.
for idx, slave := range scenario.slaves() {
masterId := scenario.nodeIds[idx]
@@ -115,7 +115,7 @@ func startCluster(scenario *clusterScenario) error {
}
}
- // Wait until all nodes have consistent info
+ // Wait until all nodes have consistent info.
for _, client := range scenario.clients {
err := eventually(func() error {
res, err := client.ClusterSlots().Result()
@@ -189,62 +189,6 @@ var _ = Describe("ClusterClient", func() {
var client *redis.ClusterClient
assertClusterClient := func() {
- It("should CLUSTER SLOTS", func() {
- res, err := client.ClusterSlots().Result()
- Expect(err).NotTo(HaveOccurred())
- Expect(res).To(HaveLen(3))
-
- wanted := []redis.ClusterSlot{
- {0, 4999, []redis.ClusterNode{{"", "127.0.0.1:8220"}, {"", "127.0.0.1:8223"}}},
- {5000, 9999, []redis.ClusterNode{{"", "127.0.0.1:8221"}, {"", "127.0.0.1:8224"}}},
- {10000, 16383, []redis.ClusterNode{{"", "127.0.0.1:8222"}, {"", "127.0.0.1:8225"}}},
- }
- Expect(assertSlotsEqual(res, wanted)).NotTo(HaveOccurred())
- })
-
- It("should CLUSTER NODES", func() {
- res, err := client.ClusterNodes().Result()
- Expect(err).NotTo(HaveOccurred())
- Expect(len(res)).To(BeNumerically(">", 400))
- })
-
- It("should CLUSTER INFO", func() {
- res, err := client.ClusterInfo().Result()
- Expect(err).NotTo(HaveOccurred())
- Expect(res).To(ContainSubstring("cluster_known_nodes:6"))
- })
-
- It("should CLUSTER KEYSLOT", func() {
- hashSlot, err := client.ClusterKeySlot("somekey").Result()
- Expect(err).NotTo(HaveOccurred())
- Expect(hashSlot).To(Equal(int64(hashtag.Slot("somekey"))))
- })
-
- It("should CLUSTER COUNT-FAILURE-REPORTS", func() {
- n, err := client.ClusterCountFailureReports(cluster.nodeIds[0]).Result()
- Expect(err).NotTo(HaveOccurred())
- Expect(n).To(Equal(int64(0)))
- })
-
- It("should CLUSTER COUNTKEYSINSLOT", func() {
- n, err := client.ClusterCountKeysInSlot(10).Result()
- Expect(err).NotTo(HaveOccurred())
- Expect(n).To(Equal(int64(0)))
- })
-
- It("should CLUSTER SAVECONFIG", func() {
- res, err := client.ClusterSaveConfig().Result()
- Expect(err).NotTo(HaveOccurred())
- Expect(res).To(Equal("OK"))
- })
-
- It("should CLUSTER SLAVES", func() {
- nodesList, err := client.ClusterSlaves(cluster.nodeIds[0]).Result()
- Expect(err).NotTo(HaveOccurred())
- Expect(nodesList).Should(ContainElement(ContainSubstring("slave")))
- Expect(nodesList).Should(HaveLen(1))
- })
-
It("should GET/SET/DEL", func() {
val, err := client.Get("A").Result()
Expect(err).To(Equal(redis.Nil))
@@ -254,55 +198,24 @@ var _ = Describe("ClusterClient", func() {
Expect(err).NotTo(HaveOccurred())
Expect(val).To(Equal("OK"))
- val, err = client.Get("A").Result()
- Expect(err).NotTo(HaveOccurred())
- Expect(val).To(Equal("VALUE"))
+ Eventually(func() string {
+ return client.Get("A").Val()
+ }).Should(Equal("VALUE"))
cnt, err := client.Del("A").Result()
Expect(err).NotTo(HaveOccurred())
Expect(cnt).To(Equal(int64(1)))
})
- It("returns pool stats", func() {
- Expect(client.PoolStats()).To(BeAssignableToTypeOf(&redis.PoolStats{}))
- })
-
- It("removes idle connections", func() {
- stats := client.PoolStats()
- Expect(stats.TotalConns).NotTo(BeZero())
- Expect(stats.FreeConns).NotTo(BeZero())
-
- time.Sleep(2 * time.Second)
-
- stats = client.PoolStats()
- Expect(stats.TotalConns).To(BeZero())
- Expect(stats.FreeConns).To(BeZero())
- })
-
It("follows redirects", func() {
Expect(client.Set("A", "VALUE", 0).Err()).NotTo(HaveOccurred())
slot := hashtag.Slot("A")
- Expect(client.SwapSlotNodes(slot)).To(Equal([]string{"127.0.0.1:8224", "127.0.0.1:8221"}))
+ client.SwapSlotNodes(slot)
- val, err := client.Get("A").Result()
- Expect(err).NotTo(HaveOccurred())
- Expect(val).To(Equal("VALUE"))
- })
-
- It("returns an error when there are no attempts left", func() {
- opt := redisClusterOptions()
- opt.MaxRedirects = -1
- client := cluster.clusterClient(opt)
-
- slot := hashtag.Slot("A")
- Expect(client.SwapSlotNodes(slot)).To(Equal([]string{"127.0.0.1:8224", "127.0.0.1:8221"}))
-
- err := client.Get("A").Err()
- Expect(err).To(HaveOccurred())
- Expect(err.Error()).To(ContainSubstring("MOVED"))
-
- Expect(client.Close()).NotTo(HaveOccurred())
+ Eventually(func() string {
+ return client.Get("A").Val()
+ }).Should(Equal("VALUE"))
})
It("distributes keys", func() {
@@ -311,9 +224,14 @@ var _ = Describe("ClusterClient", func() {
Expect(err).NotTo(HaveOccurred())
}
- wanted := []string{"keys=31", "keys=29", "keys=40"}
- for i, master := range cluster.masters() {
- Expect(master.Info().Val()).To(ContainSubstring(wanted[i]))
+ for _, master := range cluster.masters() {
+ Eventually(func() string {
+ return master.Info("keyspace").Val()
+ }, 5*time.Second).Should(Or(
+ ContainSubstring("keys=31"),
+ ContainSubstring("keys=29"),
+ ContainSubstring("keys=40"),
+ ))
}
})
@@ -330,9 +248,14 @@ var _ = Describe("ClusterClient", func() {
Expect(err).NotTo(HaveOccurred())
}
- wanted := []string{"keys=31", "keys=29", "keys=40"}
- for i, master := range cluster.masters() {
- Expect(master.Info().Val()).To(ContainSubstring(wanted[i]))
+ for _, master := range cluster.masters() {
+ Eventually(func() string {
+ return master.Info("keyspace").Val()
+ }, 5*time.Second).Should(Or(
+ ContainSubstring("keys=31"),
+ ContainSubstring("keys=29"),
+ ContainSubstring("keys=40"),
+ ))
}
})
@@ -419,7 +342,8 @@ var _ = Describe("ClusterClient", func() {
Expect(get.Val()).To(Equal(key + "_value"))
ttl := cmds[(i*2)+1].(*redis.DurationCmd)
- Expect(ttl.Val()).To(BeNumerically("~", time.Duration(i+1)*time.Hour, time.Second))
+ dur := time.Duration(i+1) * time.Hour
+ Expect(ttl.Val()).To(BeNumerically("~", dur, 5*time.Second))
}
})
@@ -447,7 +371,7 @@ var _ = Describe("ClusterClient", func() {
})
}
- Describe("Pipeline", func() {
+ Describe("with Pipeline", func() {
BeforeEach(func() {
pipe = client.Pipeline().(*redis.Pipeline)
})
@@ -459,7 +383,7 @@ var _ = Describe("ClusterClient", func() {
assertPipeline()
})
- Describe("TxPipeline", func() {
+ Describe("with TxPipeline", func() {
BeforeEach(func() {
pipe = client.TxPipeline().(*redis.Pipeline)
})
@@ -472,6 +396,76 @@ var _ = Describe("ClusterClient", func() {
})
})
+ It("supports PubSub", func() {
+ pubsub := client.Subscribe("mychannel")
+ defer pubsub.Close()
+
+ Eventually(func() error {
+ _, err := client.Publish("mychannel", "hello").Result()
+ if err != nil {
+ return err
+ }
+
+ msg, err := pubsub.ReceiveTimeout(time.Second)
+ if err != nil {
+ return err
+ }
+
+ _, ok := msg.(*redis.Message)
+ if !ok {
+ return fmt.Errorf("got %T, wanted *redis.Message", msg)
+ }
+
+ return nil
+ }, 30*time.Second).ShouldNot(HaveOccurred())
+ })
+ }
+
+ Describe("ClusterClient", func() {
+ BeforeEach(func() {
+ opt = redisClusterOptions()
+ client = cluster.clusterClient(opt)
+
+ _ = client.ForEachMaster(func(master *redis.Client) error {
+ return master.FlushDB().Err()
+ })
+ })
+
+ AfterEach(func() {
+ Expect(client.Close()).NotTo(HaveOccurred())
+ })
+
+ It("returns pool stats", func() {
+ Expect(client.PoolStats()).To(BeAssignableToTypeOf(&redis.PoolStats{}))
+ })
+
+ It("removes idle connections", func() {
+ stats := client.PoolStats()
+ Expect(stats.TotalConns).NotTo(BeZero())
+ Expect(stats.FreeConns).NotTo(BeZero())
+
+ time.Sleep(2 * time.Second)
+
+ stats = client.PoolStats()
+ Expect(stats.TotalConns).To(BeZero())
+ Expect(stats.FreeConns).To(BeZero())
+ })
+
+ It("returns an error when there are no attempts left", func() {
+ opt := redisClusterOptions()
+ opt.MaxRedirects = -1
+ client := cluster.clusterClient(opt)
+
+ slot := hashtag.Slot("A")
+ client.SwapSlotNodes(slot)
+
+ err := client.Get("A").Err()
+ Expect(err).To(HaveOccurred())
+ Expect(err.Error()).To(ContainSubstring("MOVED"))
+
+ Expect(client.Close()).NotTo(HaveOccurred())
+ })
+
It("calls fn for every master node", func() {
for i := 0; i < 10; i++ {
Expect(client.Set(strconv.Itoa(i), "", 0).Err()).NotTo(HaveOccurred())
@@ -483,14 +477,72 @@ var _ = Describe("ClusterClient", func() {
Expect(err).NotTo(HaveOccurred())
for _, client := range cluster.masters() {
- keys, err := client.Keys("*").Result()
+ size, err := client.DBSize().Result()
Expect(err).NotTo(HaveOccurred())
- Expect(keys).To(HaveLen(0))
+ Expect(size).To(Equal(int64(0)))
}
})
- }
- Describe("default ClusterClient", func() {
+ It("should CLUSTER SLOTS", func() {
+ res, err := client.ClusterSlots().Result()
+ Expect(err).NotTo(HaveOccurred())
+ Expect(res).To(HaveLen(3))
+
+ wanted := []redis.ClusterSlot{
+ {0, 4999, []redis.ClusterNode{{"", "127.0.0.1:8220"}, {"", "127.0.0.1:8223"}}},
+ {5000, 9999, []redis.ClusterNode{{"", "127.0.0.1:8221"}, {"", "127.0.0.1:8224"}}},
+ {10000, 16383, []redis.ClusterNode{{"", "127.0.0.1:8222"}, {"", "127.0.0.1:8225"}}},
+ }
+ Expect(assertSlotsEqual(res, wanted)).NotTo(HaveOccurred())
+ })
+
+ It("should CLUSTER NODES", func() {
+ res, err := client.ClusterNodes().Result()
+ Expect(err).NotTo(HaveOccurred())
+ Expect(len(res)).To(BeNumerically(">", 400))
+ })
+
+ It("should CLUSTER INFO", func() {
+ res, err := client.ClusterInfo().Result()
+ Expect(err).NotTo(HaveOccurred())
+ Expect(res).To(ContainSubstring("cluster_known_nodes:6"))
+ })
+
+ It("should CLUSTER KEYSLOT", func() {
+ hashSlot, err := client.ClusterKeySlot("somekey").Result()
+ Expect(err).NotTo(HaveOccurred())
+ Expect(hashSlot).To(Equal(int64(hashtag.Slot("somekey"))))
+ })
+
+ It("should CLUSTER COUNT-FAILURE-REPORTS", func() {
+ n, err := client.ClusterCountFailureReports(cluster.nodeIds[0]).Result()
+ Expect(err).NotTo(HaveOccurred())
+ Expect(n).To(Equal(int64(0)))
+ })
+
+ It("should CLUSTER COUNTKEYSINSLOT", func() {
+ n, err := client.ClusterCountKeysInSlot(10).Result()
+ Expect(err).NotTo(HaveOccurred())
+ Expect(n).To(Equal(int64(0)))
+ })
+
+ It("should CLUSTER SAVECONFIG", func() {
+ res, err := client.ClusterSaveConfig().Result()
+ Expect(err).NotTo(HaveOccurred())
+ Expect(res).To(Equal("OK"))
+ })
+
+ It("should CLUSTER SLAVES", func() {
+ nodesList, err := client.ClusterSlaves(cluster.nodeIds[0]).Result()
+ Expect(err).NotTo(HaveOccurred())
+ Expect(nodesList).Should(ContainElement(ContainSubstring("slave")))
+ Expect(nodesList).Should(HaveLen(1))
+ })
+
+ assertClusterClient()
+ })
+
+ Describe("ClusterClient failover", func() {
BeforeEach(func() {
opt = redisClusterOptions()
client = cluster.clusterClient(opt)
@@ -498,6 +550,13 @@ var _ = Describe("ClusterClient", func() {
_ = client.ForEachMaster(func(master *redis.Client) error {
return master.FlushDB().Err()
})
+
+ _ = client.ForEachSlave(func(slave *redis.Client) error {
+ Eventually(func() int64 {
+ return client.DBSize().Val()
+ }, 30*time.Second).Should(Equal(int64(0)))
+ return slave.ClusterFailover().Err()
+ })
})
AfterEach(func() {
@@ -645,14 +704,14 @@ var _ = Describe("ClusterClient timeout", func() {
testTimeout()
})
- Context("network timeout", func() {
+ Context("ClientPause timeout", func() {
const pause = time.Second
BeforeEach(func() {
opt := redisClusterOptions()
- opt.ReadTimeout = 100 * time.Millisecond
- opt.WriteTimeout = 100 * time.Millisecond
- opt.MaxRedirects = 1
+ opt.ReadTimeout = pause / 10
+ opt.WriteTimeout = pause / 10
+ opt.MaxRedirects = -1
client = cluster.clusterClient(opt)
err := client.ForEachNode(func(client *redis.Client) error {
@@ -662,11 +721,12 @@ var _ = Describe("ClusterClient timeout", func() {
})
AfterEach(func() {
- Eventually(func() error {
- return client.ForEachNode(func(client *redis.Client) error {
+ client.ForEachNode(func(client *redis.Client) error {
+ Eventually(func() error {
return client.Ping().Err()
- })
- }, 2*pause).ShouldNot(HaveOccurred())
+ }, 2*pause).ShouldNot(HaveOccurred())
+ return nil
+ })
})
testTimeout()
diff --git a/vendor/github.com/go-redis/redis/command.go b/vendor/github.com/go-redis/redis/command.go
index 361661adf..0e5b2016e 100644
--- a/vendor/github.com/go-redis/redis/command.go
+++ b/vendor/github.com/go-redis/redis/command.go
@@ -799,7 +799,9 @@ type GeoRadiusQuery struct {
WithGeoHash bool
Count int
// Can be ASC or DESC. Default is no sort order.
- Sort string
+ Sort string
+ Store string
+ StoreDist string
}
type GeoLocationCmd struct {
@@ -817,20 +819,28 @@ func NewGeoLocationCmd(q *GeoRadiusQuery, args ...interface{}) *GeoLocationCmd {
args = append(args, "km")
}
if q.WithCoord {
- args = append(args, "WITHCOORD")
+ args = append(args, "withcoord")
}
if q.WithDist {
- args = append(args, "WITHDIST")
+ args = append(args, "withdist")
}
if q.WithGeoHash {
- args = append(args, "WITHHASH")
+ args = append(args, "withhash")
}
if q.Count > 0 {
- args = append(args, "COUNT", q.Count)
+ args = append(args, "count", q.Count)
}
if q.Sort != "" {
args = append(args, q.Sort)
}
+ if q.Store != "" {
+ args = append(args, "store")
+ args = append(args, q.Store)
+ }
+ if q.StoreDist != "" {
+ args = append(args, "storedist")
+ args = append(args, q.StoreDist)
+ }
return &GeoLocationCmd{
baseCmd: baseCmd{_args: args},
q: q,
diff --git a/vendor/github.com/go-redis/redis/commands.go b/vendor/github.com/go-redis/redis/commands.go
index 4ea78777c..83b3824f8 100644
--- a/vendor/github.com/go-redis/redis/commands.go
+++ b/vendor/github.com/go-redis/redis/commands.go
@@ -159,6 +159,7 @@ type Cmdable interface {
ZIncrXX(key string, member Z) *FloatCmd
ZCard(key string) *IntCmd
ZCount(key, min, max string) *IntCmd
+ ZLexCount(key, min, max string) *IntCmd
ZIncrBy(key string, increment float64, member string) *FloatCmd
ZInterStore(destination string, store ZStore, keys ...string) *IntCmd
ZRange(key string, start, stop int64) *StringSliceCmd
@@ -190,7 +191,7 @@ type Cmdable interface {
ConfigGet(parameter string) *SliceCmd
ConfigResetStat() *StatusCmd
ConfigSet(parameter, value string) *StatusCmd
- DbSize() *IntCmd
+ DBSize() *IntCmd
FlushAll() *StatusCmd
FlushAllAsync() *StatusCmd
FlushDB() *StatusCmd
@@ -234,7 +235,9 @@ type Cmdable interface {
GeoAdd(key string, geoLocation ...*GeoLocation) *IntCmd
GeoPos(key string, members ...string) *GeoPosCmd
GeoRadius(key string, longitude, latitude float64, query *GeoRadiusQuery) *GeoLocationCmd
+ GeoRadiusRO(key string, longitude, latitude float64, query *GeoRadiusQuery) *GeoLocationCmd
GeoRadiusByMember(key, member string, query *GeoRadiusQuery) *GeoLocationCmd
+ GeoRadiusByMemberRO(key, member string, query *GeoRadiusQuery) *GeoLocationCmd
GeoDist(key string, member1, member2, unit string) *FloatCmd
GeoHash(key string, members ...string) *StringSliceCmd
Command() *CommandsInfoCmd
@@ -1350,6 +1353,12 @@ func (c *cmdable) ZCount(key, min, max string) *IntCmd {
return cmd
}
+func (c *cmdable) ZLexCount(key, min, max string) *IntCmd {
+ cmd := NewIntCmd("zlexcount", key, min, max)
+ c.process(cmd)
+ return cmd
+}
+
func (c *cmdable) ZIncrBy(key string, increment float64, member string) *FloatCmd {
cmd := NewFloatCmd("zincrby", key, increment, member)
c.process(cmd)
@@ -1675,7 +1684,12 @@ func (c *cmdable) ConfigSet(parameter, value string) *StatusCmd {
return cmd
}
+// Deperecated. Use DBSize instead.
func (c *cmdable) DbSize() *IntCmd {
+ return c.DBSize()
+}
+
+func (c *cmdable) DBSize() *IntCmd {
cmd := NewIntCmd("dbsize")
c.process(cmd)
return cmd
@@ -1695,9 +1709,7 @@ func (c *cmdable) FlushAllAsync() *StatusCmd {
// Deprecated. Use FlushDB instead.
func (c *cmdable) FlushDb() *StatusCmd {
- cmd := NewStatusCmd("flushdb")
- c.process(cmd)
- return cmd
+ return c.FlushDB()
}
func (c *cmdable) FlushDB() *StatusCmd {
@@ -2061,12 +2073,24 @@ func (c *cmdable) GeoRadius(key string, longitude, latitude float64, query *GeoR
return cmd
}
+func (c *cmdable) GeoRadiusRO(key string, longitude, latitude float64, query *GeoRadiusQuery) *GeoLocationCmd {
+ cmd := NewGeoLocationCmd(query, "georadius_ro", key, longitude, latitude)
+ c.process(cmd)
+ return cmd
+}
+
func (c *cmdable) GeoRadiusByMember(key, member string, query *GeoRadiusQuery) *GeoLocationCmd {
cmd := NewGeoLocationCmd(query, "georadiusbymember", key, member)
c.process(cmd)
return cmd
}
+func (c *cmdable) GeoRadiusByMemberRO(key, member string, query *GeoRadiusQuery) *GeoLocationCmd {
+ cmd := NewGeoLocationCmd(query, "georadiusbymember_ro", key, member)
+ c.process(cmd)
+ return cmd
+}
+
func (c *cmdable) GeoDist(key string, member1, member2, unit string) *FloatCmd {
if unit == "" {
unit = "km"
diff --git a/vendor/github.com/go-redis/redis/commands_test.go b/vendor/github.com/go-redis/redis/commands_test.go
index e8cdb205e..4298cba68 100644
--- a/vendor/github.com/go-redis/redis/commands_test.go
+++ b/vendor/github.com/go-redis/redis/commands_test.go
@@ -139,10 +139,10 @@ var _ = Describe("Commands", func() {
Expect(configSet.Val()).To(Equal("OK"))
})
- It("should DbSize", func() {
- dbSize := client.DbSize()
- Expect(dbSize.Err()).NotTo(HaveOccurred())
- Expect(dbSize.Val()).To(Equal(int64(0)))
+ It("should DBSize", func() {
+ size, err := client.DBSize().Result()
+ Expect(err).NotTo(HaveOccurred())
+ Expect(size).To(Equal(int64(0)))
})
It("should Info", func() {
@@ -2176,20 +2176,24 @@ var _ = Describe("Commands", func() {
})
It("should ZCount", func() {
- zAdd := client.ZAdd("zset", redis.Z{1, "one"})
- Expect(zAdd.Err()).NotTo(HaveOccurred())
- zAdd = client.ZAdd("zset", redis.Z{2, "two"})
- Expect(zAdd.Err()).NotTo(HaveOccurred())
- zAdd = client.ZAdd("zset", redis.Z{3, "three"})
- Expect(zAdd.Err()).NotTo(HaveOccurred())
+ err := client.ZAdd("zset", redis.Z{1, "one"}).Err()
+ Expect(err).NotTo(HaveOccurred())
+ err = client.ZAdd("zset", redis.Z{2, "two"}).Err()
+ Expect(err).NotTo(HaveOccurred())
+ err = client.ZAdd("zset", redis.Z{3, "three"}).Err()
+ Expect(err).NotTo(HaveOccurred())
- zCount := client.ZCount("zset", "-inf", "+inf")
- Expect(zCount.Err()).NotTo(HaveOccurred())
- Expect(zCount.Val()).To(Equal(int64(3)))
+ count, err := client.ZCount("zset", "-inf", "+inf").Result()
+ Expect(err).NotTo(HaveOccurred())
+ Expect(count).To(Equal(int64(3)))
- zCount = client.ZCount("zset", "(1", "3")
- Expect(zCount.Err()).NotTo(HaveOccurred())
- Expect(zCount.Val()).To(Equal(int64(2)))
+ count, err = client.ZCount("zset", "(1", "3").Result()
+ Expect(err).NotTo(HaveOccurred())
+ Expect(count).To(Equal(int64(2)))
+
+ count, err = client.ZLexCount("zset", "-", "+").Result()
+ Expect(err).NotTo(HaveOccurred())
+ Expect(count).To(Equal(int64(3)))
})
It("should ZIncrBy", func() {
diff --git a/vendor/github.com/go-redis/redis/example_test.go b/vendor/github.com/go-redis/redis/example_test.go
index 319ea0ca2..7e04cd487 100644
--- a/vendor/github.com/go-redis/redis/example_test.go
+++ b/vendor/github.com/go-redis/redis/example_test.go
@@ -122,13 +122,13 @@ func ExampleClient_Set() {
}
func ExampleClient_Incr() {
- if err := client.Incr("counter").Err(); err != nil {
+ result, err := client.Incr("counter").Result()
+ if err != nil {
panic(err)
}
- n, err := client.Get("counter").Int64()
- fmt.Println(n, err)
- // Output: 1 <nil>
+ fmt.Println(result)
+ // Output: 1
}
func ExampleClient_BLPop() {
diff --git a/vendor/github.com/go-redis/redis/export_test.go b/vendor/github.com/go-redis/redis/export_test.go
index b88e41be9..3b7965d79 100644
--- a/vendor/github.com/go-redis/redis/export_test.go
+++ b/vendor/github.com/go-redis/redis/export_test.go
@@ -28,8 +28,9 @@ func (c *ClusterClient) SlotAddrs(slot int) []string {
}
// SwapSlot swaps a slot's master/slave address for testing MOVED redirects.
-func (c *ClusterClient) SwapSlotNodes(slot int) []string {
+func (c *ClusterClient) SwapSlotNodes(slot int) {
nodes := c.state().slots[slot]
- nodes[0], nodes[1] = nodes[1], nodes[0]
- return c.SlotAddrs(slot)
+ if len(nodes) == 2 {
+ nodes[0], nodes[1] = nodes[1], nodes[0]
+ }
}
diff --git a/vendor/github.com/go-redis/redis/internal/errors.go b/vendor/github.com/go-redis/redis/internal/error.go
index c93e00818..90f6503a1 100644
--- a/vendor/github.com/go-redis/redis/internal/errors.go
+++ b/vendor/github.com/go-redis/redis/internal/error.go
@@ -67,9 +67,9 @@ func IsMovedError(err error) (moved bool, ask bool, addr string) {
}
func IsLoadingError(err error) bool {
- return strings.HasPrefix(err.Error(), "LOADING")
+ return strings.HasPrefix(err.Error(), "LOADING ")
}
-func IsExecAbortError(err error) bool {
- return strings.HasPrefix(err.Error(), "EXECABORT")
+func IsClusterDownError(err error) bool {
+ return strings.HasPrefix(err.Error(), "CLUSTERDOWN ")
}
diff --git a/vendor/github.com/go-redis/redis/internal/internal.go b/vendor/github.com/go-redis/redis/internal/internal.go
index fb4efa5f0..ad3fc3c9f 100644
--- a/vendor/github.com/go-redis/redis/internal/internal.go
+++ b/vendor/github.com/go-redis/redis/internal/internal.go
@@ -5,19 +5,20 @@ import (
"time"
)
-const retryBackoff = 8 * time.Millisecond
-
// Retry backoff with jitter sleep to prevent overloaded conditions during intervals
// https://www.awsarchitectureblog.com/2015/03/backoff.html
-func RetryBackoff(retry int, maxRetryBackoff time.Duration) time.Duration {
+func RetryBackoff(retry int, minBackoff, maxBackoff time.Duration) time.Duration {
if retry < 0 {
retry = 0
}
- backoff := retryBackoff << uint(retry)
- if backoff > maxRetryBackoff {
- backoff = maxRetryBackoff
+ backoff := minBackoff << uint(retry)
+ if backoff > maxBackoff || backoff < minBackoff {
+ backoff = maxBackoff
}
+ if backoff == 0 {
+ return 0
+ }
return time.Duration(rand.Int63n(int64(backoff)))
}
diff --git a/vendor/github.com/go-redis/redis/internal/internal_test.go b/vendor/github.com/go-redis/redis/internal/internal_test.go
index 5c7000e1e..56ff611e1 100644
--- a/vendor/github.com/go-redis/redis/internal/internal_test.go
+++ b/vendor/github.com/go-redis/redis/internal/internal_test.go
@@ -2,15 +2,16 @@ package internal
import (
"testing"
- . "github.com/onsi/gomega"
"time"
+
+ . "github.com/onsi/gomega"
)
func TestRetryBackoff(t *testing.T) {
RegisterTestingT(t)
-
- for i := -1; i<= 8; i++ {
- backoff := RetryBackoff(i, 512*time.Millisecond)
+
+ for i := -1; i <= 16; i++ {
+ backoff := RetryBackoff(i, time.Millisecond, 512*time.Millisecond)
Expect(backoff >= 0).To(BeTrue())
Expect(backoff <= 512*time.Millisecond).To(BeTrue())
}
diff --git a/vendor/github.com/go-redis/redis/internal/pool/pool.go b/vendor/github.com/go-redis/redis/internal/pool/pool.go
index a4e650847..25e78aa3c 100644
--- a/vendor/github.com/go-redis/redis/internal/pool/pool.go
+++ b/vendor/github.com/go-redis/redis/internal/pool/pool.go
@@ -119,6 +119,10 @@ func (p *ConnPool) NewConn() (*Conn, error) {
func (p *ConnPool) tryDial() {
for {
+ if p.closed() {
+ return
+ }
+
conn, err := p.opt.Dialer()
if err != nil {
p.setLastDialError(err)
diff --git a/vendor/github.com/go-redis/redis/options.go b/vendor/github.com/go-redis/redis/options.go
index cd6fa981a..dea045453 100644
--- a/vendor/github.com/go-redis/redis/options.go
+++ b/vendor/github.com/go-redis/redis/options.go
@@ -37,9 +37,11 @@ type Options struct {
// Maximum number of retries before giving up.
// Default is to not retry failed commands.
MaxRetries int
-
+ // Minimum backoff between each retry.
+ // Default is 8 milliseconds; -1 disables backoff.
+ MinRetryBackoff time.Duration
// Maximum backoff between each retry.
- // Default is 512 seconds; -1 disables backoff.
+ // Default is 512 milliseconds; -1 disables backoff.
MaxRetryBackoff time.Duration
// Dial timeout for establishing new connections.
@@ -71,7 +73,7 @@ type Options struct {
IdleCheckFrequency time.Duration
// Enables read only queries on slave nodes.
- ReadOnly bool
+ readOnly bool
// TLS Config to use. When set TLS will be negotiated.
TLSConfig *tls.Config
@@ -118,6 +120,13 @@ func (opt *Options) init() {
if opt.IdleCheckFrequency == 0 {
opt.IdleCheckFrequency = time.Minute
}
+
+ switch opt.MinRetryBackoff {
+ case -1:
+ opt.MinRetryBackoff = 0
+ case 0:
+ opt.MinRetryBackoff = 8 * time.Millisecond
+ }
switch opt.MaxRetryBackoff {
case -1:
opt.MaxRetryBackoff = 0
diff --git a/vendor/github.com/go-redis/redis/pubsub.go b/vendor/github.com/go-redis/redis/pubsub.go
index 4872b4e88..4a5c65f57 100644
--- a/vendor/github.com/go-redis/redis/pubsub.go
+++ b/vendor/github.com/go-redis/redis/pubsub.go
@@ -17,7 +17,10 @@ import (
// PubSub automatically resubscribes to the channels and patterns
// when Redis becomes unavailable.
type PubSub struct {
- base baseClient
+ opt *Options
+
+ newConn func([]string) (*pool.Conn, error)
+ closeConn func(*pool.Conn) error
mu sync.Mutex
cn *pool.Conn
@@ -30,12 +33,12 @@ type PubSub struct {
func (c *PubSub) conn() (*pool.Conn, error) {
c.mu.Lock()
- cn, err := c._conn()
+ cn, err := c._conn(nil)
c.mu.Unlock()
return cn, err
}
-func (c *PubSub) _conn() (*pool.Conn, error) {
+func (c *PubSub) _conn(channels []string) (*pool.Conn, error) {
if c.closed {
return nil, pool.ErrClosed
}
@@ -44,20 +47,13 @@ func (c *PubSub) _conn() (*pool.Conn, error) {
return c.cn, nil
}
- cn, err := c.base.connPool.NewConn()
+ cn, err := c.newConn(channels)
if err != nil {
return nil, err
}
- if !cn.Inited {
- if err := c.base.initConn(cn); err != nil {
- _ = c.base.connPool.CloseConn(cn)
- return nil, err
- }
- }
-
if err := c.resubscribe(cn); err != nil {
- _ = c.base.connPool.CloseConn(cn)
+ _ = c.closeConn(cn)
return nil, err
}
@@ -88,24 +84,24 @@ func (c *PubSub) _subscribe(cn *pool.Conn, redisCmd string, channels ...string)
}
cmd := NewSliceCmd(args...)
- cn.SetWriteTimeout(c.base.opt.WriteTimeout)
+ cn.SetWriteTimeout(c.opt.WriteTimeout)
return writeCmd(cn, cmd)
}
-func (c *PubSub) putConn(cn *pool.Conn, err error) {
- if !internal.IsBadConn(err, true) {
- return
- }
-
+func (c *PubSub) releaseConn(cn *pool.Conn, err error) {
c.mu.Lock()
- if c.cn == cn {
- _ = c.closeConn()
- }
+ c._releaseConn(cn, err)
c.mu.Unlock()
}
-func (c *PubSub) closeConn() error {
- err := c.base.connPool.CloseConn(c.cn)
+func (c *PubSub) _releaseConn(cn *pool.Conn, err error) {
+ if internal.IsBadConn(err, true) && c.cn == cn {
+ _ = c.closeTheCn()
+ }
+}
+
+func (c *PubSub) closeTheCn() error {
+ err := c.closeConn(c.cn)
c.cn = nil
return err
}
@@ -120,7 +116,7 @@ func (c *PubSub) Close() error {
c.closed = true
if c.cn != nil {
- return c.closeConn()
+ return c.closeTheCn()
}
return nil
}
@@ -166,13 +162,13 @@ func (c *PubSub) PUnsubscribe(patterns ...string) error {
}
func (c *PubSub) subscribe(redisCmd string, channels ...string) error {
- cn, err := c._conn()
+ cn, err := c._conn(channels)
if err != nil {
return err
}
err = c._subscribe(cn, redisCmd, channels...)
- c.putConn(cn, err)
+ c._releaseConn(cn, err)
return err
}
@@ -188,9 +184,9 @@ func (c *PubSub) Ping(payload ...string) error {
return err
}
- cn.SetWriteTimeout(c.base.opt.WriteTimeout)
+ cn.SetWriteTimeout(c.opt.WriteTimeout)
err = writeCmd(cn, cmd)
- c.putConn(cn, err)
+ c.releaseConn(cn, err)
return err
}
@@ -283,7 +279,7 @@ func (c *PubSub) ReceiveTimeout(timeout time.Duration) (interface{}, error) {
cn.SetReadTimeout(timeout)
err = c.cmd.readReply(cn)
- c.putConn(cn, err)
+ c.releaseConn(cn, err)
if err != nil {
return nil, err
}
diff --git a/vendor/github.com/go-redis/redis/pubsub_test.go b/vendor/github.com/go-redis/redis/pubsub_test.go
index e8589f461..1d9dfcb99 100644
--- a/vendor/github.com/go-redis/redis/pubsub_test.go
+++ b/vendor/github.com/go-redis/redis/pubsub_test.go
@@ -159,9 +159,9 @@ var _ = Describe("PubSub", func() {
{
msgi, err := pubsub.ReceiveTimeout(time.Second)
Expect(err).NotTo(HaveOccurred())
- subscr := msgi.(*redis.Message)
- Expect(subscr.Channel).To(Equal("mychannel"))
- Expect(subscr.Payload).To(Equal("hello"))
+ msg := msgi.(*redis.Message)
+ Expect(msg.Channel).To(Equal("mychannel"))
+ Expect(msg.Payload).To(Equal("hello"))
}
{
@@ -294,6 +294,22 @@ var _ = Describe("PubSub", func() {
Expect(stats.Hits).To(Equal(uint32(1)))
})
+ It("returns an error when subscribe fails", func() {
+ pubsub := client.Subscribe()
+ defer pubsub.Close()
+
+ pubsub.SetNetConn(&badConn{
+ readErr: io.EOF,
+ writeErr: io.EOF,
+ })
+
+ err := pubsub.Subscribe("mychannel")
+ Expect(err).To(MatchError("EOF"))
+
+ err = pubsub.Subscribe("mychannel")
+ Expect(err).NotTo(HaveOccurred())
+ })
+
expectReceiveMessageOnError := func(pubsub *redis.PubSub) {
pubsub.SetNetConn(&badConn{
readErr: io.EOF,
@@ -384,8 +400,11 @@ var _ = Describe("PubSub", func() {
pubsub := client.Subscribe()
defer pubsub.Close()
+ var wg sync.WaitGroup
+ wg.Add(1)
go func() {
defer GinkgoRecover()
+ defer wg.Done()
time.Sleep(2 * timeout)
@@ -402,5 +421,7 @@ var _ = Describe("PubSub", func() {
Expect(err).NotTo(HaveOccurred())
Expect(msg.Channel).To(Equal("mychannel"))
Expect(msg.Payload).To(Equal("hello"))
+
+ wg.Wait()
})
})
diff --git a/vendor/github.com/go-redis/redis/redis.go b/vendor/github.com/go-redis/redis/redis.go
index 9812daf66..b18973cdb 100644
--- a/vendor/github.com/go-redis/redis/redis.go
+++ b/vendor/github.com/go-redis/redis/redis.go
@@ -21,7 +21,23 @@ func (c *baseClient) String() string {
return fmt.Sprintf("Redis<%s db:%d>", c.getAddr(), c.opt.DB)
}
-func (c *baseClient) conn() (*pool.Conn, bool, error) {
+func (c *baseClient) newConn() (*pool.Conn, error) {
+ cn, err := c.connPool.NewConn()
+ if err != nil {
+ return nil, err
+ }
+
+ if !cn.Inited {
+ if err := c.initConn(cn); err != nil {
+ _ = c.connPool.CloseConn(cn)
+ return nil, err
+ }
+ }
+
+ return cn, nil
+}
+
+func (c *baseClient) getConn() (*pool.Conn, bool, error) {
cn, isNew, err := c.connPool.Get()
if err != nil {
return nil, false, err
@@ -37,7 +53,7 @@ func (c *baseClient) conn() (*pool.Conn, bool, error) {
return cn, isNew, nil
}
-func (c *baseClient) putConn(cn *pool.Conn, err error) bool {
+func (c *baseClient) releaseConn(cn *pool.Conn, err error) bool {
if internal.IsBadConn(err, false) {
_ = c.connPool.Remove(cn)
return false
@@ -52,7 +68,7 @@ func (c *baseClient) initConn(cn *pool.Conn) error {
if c.opt.Password == "" &&
c.opt.DB == 0 &&
- !c.opt.ReadOnly &&
+ !c.opt.readOnly &&
c.opt.OnConnect == nil {
return nil
}
@@ -75,7 +91,7 @@ func (c *baseClient) initConn(cn *pool.Conn) error {
pipe.Select(c.opt.DB)
}
- if c.opt.ReadOnly {
+ if c.opt.readOnly {
pipe.ReadOnly()
}
@@ -91,13 +107,6 @@ func (c *baseClient) initConn(cn *pool.Conn) error {
return nil
}
-func (c *baseClient) Process(cmd Cmder) error {
- if c.process != nil {
- return c.process(cmd)
- }
- return c.defaultProcess(cmd)
-}
-
// WrapProcess replaces the process func. It takes a function createWrapper
// which is supplied by the user. createWrapper takes the old process func as
// an input and returns the new wrapper process func. createWrapper should
@@ -106,13 +115,20 @@ func (c *baseClient) WrapProcess(fn func(oldProcess func(cmd Cmder) error) func(
c.process = fn(c.defaultProcess)
}
+func (c *baseClient) Process(cmd Cmder) error {
+ if c.process != nil {
+ return c.process(cmd)
+ }
+ return c.defaultProcess(cmd)
+}
+
func (c *baseClient) defaultProcess(cmd Cmder) error {
- for i := 0; i <= c.opt.MaxRetries; i++ {
- if i > 0 {
- time.Sleep(internal.RetryBackoff(i, c.opt.MaxRetryBackoff))
+ for attempt := 0; attempt <= c.opt.MaxRetries; attempt++ {
+ if attempt > 0 {
+ time.Sleep(c.retryBackoff(attempt))
}
- cn, _, err := c.conn()
+ cn, _, err := c.getConn()
if err != nil {
cmd.setErr(err)
if internal.IsRetryableError(err) {
@@ -123,7 +139,7 @@ func (c *baseClient) defaultProcess(cmd Cmder) error {
cn.SetWriteTimeout(c.opt.WriteTimeout)
if err := writeCmd(cn, cmd); err != nil {
- c.putConn(cn, err)
+ c.releaseConn(cn, err)
cmd.setErr(err)
if internal.IsRetryableError(err) {
continue
@@ -133,7 +149,7 @@ func (c *baseClient) defaultProcess(cmd Cmder) error {
cn.SetReadTimeout(c.cmdTimeout(cmd))
err = cmd.readReply(cn)
- c.putConn(cn, err)
+ c.releaseConn(cn, err)
if err != nil && internal.IsRetryableError(err) {
continue
}
@@ -144,6 +160,10 @@ func (c *baseClient) defaultProcess(cmd Cmder) error {
return cmd.Err()
}
+func (c *baseClient) retryBackoff(attempt int) time.Duration {
+ return internal.RetryBackoff(attempt, c.opt.MinRetryBackoff, c.opt.MaxRetryBackoff)
+}
+
func (c *baseClient) cmdTimeout(cmd Cmder) time.Duration {
if timeout := cmd.readTimeout(); timeout != nil {
return *timeout
@@ -179,14 +199,14 @@ func (c *baseClient) pipelineExecer(p pipelineProcessor) pipelineExecer {
return func(cmds []Cmder) error {
var firstErr error
for i := 0; i <= c.opt.MaxRetries; i++ {
- cn, _, err := c.conn()
+ cn, _, err := c.getConn()
if err != nil {
setCmdsErr(cmds, err)
return err
}
canRetry, err := p(cn, cmds)
- c.putConn(cn, err)
+ c.releaseConn(cn, err)
if err == nil {
return nil
}
@@ -375,10 +395,12 @@ func (c *Client) TxPipeline() Pipeliner {
func (c *Client) pubSub() *PubSub {
return &PubSub{
- base: baseClient{
- opt: c.opt,
- connPool: c.connPool,
+ opt: c.opt,
+
+ newConn: func(channels []string) (*pool.Conn, error) {
+ return c.newConn()
},
+ closeConn: c.connPool.CloseConn,
}
}
diff --git a/vendor/github.com/go-redis/redis/ring.go b/vendor/github.com/go-redis/redis/ring.go
index be9251096..72d52bf75 100644
--- a/vendor/github.com/go-redis/redis/ring.go
+++ b/vendor/github.com/go-redis/redis/ring.go
@@ -423,7 +423,7 @@ func (c *Ring) pipelineExec(cmds []Cmder) (firstErr error) {
continue
}
- cn, _, err := shard.Client.conn()
+ cn, _, err := shard.Client.getConn()
if err != nil {
setCmdsErr(cmds, err)
if firstErr == nil {
@@ -433,7 +433,7 @@ func (c *Ring) pipelineExec(cmds []Cmder) (firstErr error) {
}
canRetry, err := shard.Client.pipelineProcessCmds(cn, cmds)
- shard.Client.putConn(cn, err)
+ shard.Client.releaseConn(cn, err)
if err == nil {
continue
}
diff --git a/vendor/github.com/go-redis/redis/sentinel.go b/vendor/github.com/go-redis/redis/sentinel.go
index ed6e7ffb3..3bfdb4a3f 100644
--- a/vendor/github.com/go-redis/redis/sentinel.go
+++ b/vendor/github.com/go-redis/redis/sentinel.go
@@ -112,10 +112,12 @@ func newSentinel(opt *Options) *sentinelClient {
func (c *sentinelClient) PubSub() *PubSub {
return &PubSub{
- base: baseClient{
- opt: c.opt,
- connPool: c.connPool,
+ opt: c.opt,
+
+ newConn: func(channels []string) (*pool.Conn, error) {
+ return c.newConn()
},
+ closeConn: c.connPool.CloseConn,
}
}
@@ -149,14 +151,6 @@ func (d *sentinelFailover) Close() error {
return d.resetSentinel()
}
-func (d *sentinelFailover) dial() (net.Conn, error) {
- addr, err := d.MasterAddr()
- if err != nil {
- return nil, err
- }
- return net.DialTimeout("tcp", addr, d.opt.DialTimeout)
-}
-
func (d *sentinelFailover) Pool() *pool.ConnPool {
d.poolOnce.Do(func() {
d.opt.Dialer = d.dial
@@ -165,6 +159,14 @@ func (d *sentinelFailover) Pool() *pool.ConnPool {
return d.pool
}
+func (d *sentinelFailover) dial() (net.Conn, error) {
+ addr, err := d.MasterAddr()
+ if err != nil {
+ return nil, err
+ }
+ return net.DialTimeout("tcp", addr, d.opt.DialTimeout)
+}
+
func (d *sentinelFailover) MasterAddr() (string, error) {
d.mu.Lock()
defer d.mu.Unlock()
diff --git a/vendor/github.com/go-redis/redis/universal.go b/vendor/github.com/go-redis/redis/universal.go
index 02ed51abd..4aa579fa4 100644
--- a/vendor/github.com/go-redis/redis/universal.go
+++ b/vendor/github.com/go-redis/redis/universal.go
@@ -17,12 +17,11 @@ type UniversalOptions struct {
// Only single-node and failover clients.
DB int
+ // Only cluster clients.
+
// Enables read only queries on slave nodes.
- // Only cluster and single-node clients.
ReadOnly bool
- // Only cluster clients.
-
MaxRedirects int
RouteByLatency bool
@@ -93,7 +92,6 @@ func (o *UniversalOptions) simple() *Options {
return &Options{
Addr: addr,
DB: o.DB,
- ReadOnly: o.ReadOnly,
MaxRetries: o.MaxRetries,
Password: o.Password,
diff --git a/vendor/github.com/golang/protobuf/jsonpb/jsonpb.go b/vendor/github.com/golang/protobuf/jsonpb/jsonpb.go
index 412ba1ce4..29bca020f 100644
--- a/vendor/github.com/golang/protobuf/jsonpb/jsonpb.go
+++ b/vendor/github.com/golang/protobuf/jsonpb/jsonpb.go
@@ -639,7 +639,13 @@ func (u *Unmarshaler) unmarshalValue(target reflect.Value, inputValue json.RawMe
// Allocate memory for pointer fields.
if targetType.Kind() == reflect.Ptr {
+ // If input value is "null" and target is a pointer type, then the field should be treated as not set
+ // UNLESS the target is structpb.Value, in which case it should be set to structpb.NullValue.
+ if string(inputValue) == "null" && targetType != reflect.TypeOf(&stpb.Value{}) {
+ return nil
+ }
target.Set(reflect.New(targetType.Elem()))
+
return u.unmarshalValue(target.Elem(), inputValue, prop)
}
@@ -647,15 +653,11 @@ func (u *Unmarshaler) unmarshalValue(target reflect.Value, inputValue json.RawMe
return jsu.UnmarshalJSONPB(u, []byte(inputValue))
}
- // Handle well-known types.
+ // Handle well-known types that are not pointers.
if w, ok := target.Addr().Interface().(wkt); ok {
switch w.XXX_WellKnownType() {
case "DoubleValue", "FloatValue", "Int64Value", "UInt64Value",
"Int32Value", "UInt32Value", "BoolValue", "StringValue", "BytesValue":
- // "Wrappers use the same representation in JSON
- // as the wrapped primitive type, except that null is allowed."
- // encoding/json will turn JSON `null` into Go `nil`,
- // so we don't have to do any extra work.
return u.unmarshalValue(target.Field(0), inputValue, prop)
case "Any":
// Use json.RawMessage pointer type instead of value to support pre-1.8 version.
@@ -716,21 +718,16 @@ func (u *Unmarshaler) unmarshalValue(target reflect.Value, inputValue json.RawMe
return nil
case "Duration":
- ivStr := string(inputValue)
- if ivStr == "null" {
- target.Field(0).SetInt(0)
- target.Field(1).SetInt(0)
- return nil
- }
-
- unq, err := strconv.Unquote(ivStr)
+ unq, err := strconv.Unquote(string(inputValue))
if err != nil {
return err
}
+
d, err := time.ParseDuration(unq)
if err != nil {
return fmt.Errorf("bad Duration: %v", err)
}
+
ns := d.Nanoseconds()
s := ns / 1e9
ns %= 1e9
@@ -738,33 +735,25 @@ func (u *Unmarshaler) unmarshalValue(target reflect.Value, inputValue json.RawMe
target.Field(1).SetInt(ns)
return nil
case "Timestamp":
- ivStr := string(inputValue)
- if ivStr == "null" {
- target.Field(0).SetInt(0)
- target.Field(1).SetInt(0)
- return nil
- }
-
- unq, err := strconv.Unquote(ivStr)
+ unq, err := strconv.Unquote(string(inputValue))
if err != nil {
return err
}
+
t, err := time.Parse(time.RFC3339Nano, unq)
if err != nil {
return fmt.Errorf("bad Timestamp: %v", err)
}
+
target.Field(0).SetInt(t.Unix())
target.Field(1).SetInt(int64(t.Nanosecond()))
return nil
case "Struct":
- if string(inputValue) == "null" {
- // Interpret a null struct as empty.
- return nil
- }
var m map[string]json.RawMessage
if err := json.Unmarshal(inputValue, &m); err != nil {
return fmt.Errorf("bad StructValue: %v", err)
}
+
target.Field(0).Set(reflect.ValueOf(map[string]*stpb.Value{}))
for k, jv := range m {
pv := &stpb.Value{}
@@ -775,14 +764,11 @@ func (u *Unmarshaler) unmarshalValue(target reflect.Value, inputValue json.RawMe
}
return nil
case "ListValue":
- if string(inputValue) == "null" {
- // Interpret a null ListValue as empty.
- return nil
- }
var s []json.RawMessage
if err := json.Unmarshal(inputValue, &s); err != nil {
return fmt.Errorf("bad ListValue: %v", err)
}
+
target.Field(0).Set(reflect.ValueOf(make([]*stpb.Value, len(s), len(s))))
for i, sv := range s {
if err := u.unmarshalValue(target.Field(0).Index(i), sv, prop); err != nil {
@@ -933,11 +919,13 @@ func (u *Unmarshaler) unmarshalValue(target reflect.Value, inputValue json.RawMe
if err := json.Unmarshal(inputValue, &slc); err != nil {
return err
}
- len := len(slc)
- target.Set(reflect.MakeSlice(targetType, len, len))
- for i := 0; i < len; i++ {
- if err := u.unmarshalValue(target.Index(i), slc[i], prop); err != nil {
- return err
+ if slc != nil {
+ l := len(slc)
+ target.Set(reflect.MakeSlice(targetType, l, l))
+ for i := 0; i < l; i++ {
+ if err := u.unmarshalValue(target.Index(i), slc[i], prop); err != nil {
+ return err
+ }
}
}
return nil
@@ -949,33 +937,35 @@ func (u *Unmarshaler) unmarshalValue(target reflect.Value, inputValue json.RawMe
if err := json.Unmarshal(inputValue, &mp); err != nil {
return err
}
- target.Set(reflect.MakeMap(targetType))
- var keyprop, valprop *proto.Properties
- if prop != nil {
- // These could still be nil if the protobuf metadata is broken somehow.
- // TODO: This won't work because the fields are unexported.
- // We should probably just reparse them.
- //keyprop, valprop = prop.mkeyprop, prop.mvalprop
- }
- for ks, raw := range mp {
- // Unmarshal map key. The core json library already decoded the key into a
- // string, so we handle that specially. Other types were quoted post-serialization.
- var k reflect.Value
- if targetType.Key().Kind() == reflect.String {
- k = reflect.ValueOf(ks)
- } else {
- k = reflect.New(targetType.Key()).Elem()
- if err := u.unmarshalValue(k, json.RawMessage(ks), keyprop); err != nil {
- return err
- }
+ if mp != nil {
+ target.Set(reflect.MakeMap(targetType))
+ var keyprop, valprop *proto.Properties
+ if prop != nil {
+ // These could still be nil if the protobuf metadata is broken somehow.
+ // TODO: This won't work because the fields are unexported.
+ // We should probably just reparse them.
+ //keyprop, valprop = prop.mkeyprop, prop.mvalprop
}
+ for ks, raw := range mp {
+ // Unmarshal map key. The core json library already decoded the key into a
+ // string, so we handle that specially. Other types were quoted post-serialization.
+ var k reflect.Value
+ if targetType.Key().Kind() == reflect.String {
+ k = reflect.ValueOf(ks)
+ } else {
+ k = reflect.New(targetType.Key()).Elem()
+ if err := u.unmarshalValue(k, json.RawMessage(ks), keyprop); err != nil {
+ return err
+ }
+ }
- // Unmarshal map value.
- v := reflect.New(targetType.Elem()).Elem()
- if err := u.unmarshalValue(v, raw, valprop); err != nil {
- return err
+ // Unmarshal map value.
+ v := reflect.New(targetType.Elem()).Elem()
+ if err := u.unmarshalValue(v, raw, valprop); err != nil {
+ return err
+ }
+ target.SetMapIndex(k, v)
}
- target.SetMapIndex(k, v)
}
return nil
}
diff --git a/vendor/github.com/golang/protobuf/jsonpb/jsonpb_test.go b/vendor/github.com/golang/protobuf/jsonpb/jsonpb_test.go
index da93163e6..254caa6c4 100644
--- a/vendor/github.com/golang/protobuf/jsonpb/jsonpb_test.go
+++ b/vendor/github.com/golang/protobuf/jsonpb/jsonpb_test.go
@@ -379,9 +379,9 @@ var marshalingTests = []struct {
&pb.Mappy{Strry: map[string]string{`"one"`: "two", "three": "four"}},
`{"strry":{"\"one\"":"two","three":"four"}}`},
{"map<int32, Object>", marshaler,
- &pb.Mappy{Objjy: map[int32]*pb.Simple3{1: &pb.Simple3{Dub: 1}}}, `{"objjy":{"1":{"dub":1}}}`},
+ &pb.Mappy{Objjy: map[int32]*pb.Simple3{1: {Dub: 1}}}, `{"objjy":{"1":{"dub":1}}}`},
{"map<int32, Object>", marshalerAllOptions,
- &pb.Mappy{Objjy: map[int32]*pb.Simple3{1: &pb.Simple3{Dub: 1}}}, objjyPrettyJSON},
+ &pb.Mappy{Objjy: map[int32]*pb.Simple3{1: {Dub: 1}}}, objjyPrettyJSON},
{"map<int64, string>", marshaler, &pb.Mappy{Buggy: map[int64]string{1234: "yup"}},
`{"buggy":{"1234":"yup"}}`},
{"map<bool, bool>", marshaler, &pb.Mappy{Booly: map[bool]bool{false: true}}, `{"booly":{"false":true}}`},
@@ -395,7 +395,7 @@ var marshalingTests = []struct {
{"proto2 map<int64, string>", marshaler, &pb.Maps{MInt64Str: map[int64]string{213: "cat"}},
`{"mInt64Str":{"213":"cat"}}`},
{"proto2 map<bool, Object>", marshaler,
- &pb.Maps{MBoolSimple: map[bool]*pb.Simple{true: &pb.Simple{OInt32: proto.Int32(1)}}},
+ &pb.Maps{MBoolSimple: map[bool]*pb.Simple{true: {OInt32: proto.Int32(1)}}},
`{"mBoolSimple":{"true":{"oInt32":1}}}`},
{"oneof, not set", marshaler, &pb.MsgWithOneof{}, `{}`},
{"oneof, set", marshaler, &pb.MsgWithOneof{Union: &pb.MsgWithOneof_Title{"Grand Poobah"}}, `{"title":"Grand Poobah"}`},
@@ -486,7 +486,7 @@ func TestMarshalAnyJSONPBMarshaler(t *testing.T) {
}
// after custom marshaling, it's round-tripped through JSON decoding/encoding already,
// so the keys are sorted, whitespace is compacted, and "@type" key has been added
- expected := `{"@type":"type.googleapis.com/` + dynamicMessageName +`","baz":[0,1,2,3],"foo":"bar"}`
+ expected := `{"@type":"type.googleapis.com/` + dynamicMessageName + `","baz":[0,1,2,3],"foo":"bar"}`
if str != expected {
t.Errorf("marshalling JSON produced incorrect output: got %s, wanted %s", str, expected)
}
@@ -535,7 +535,7 @@ var unmarshalingTests = []struct {
{"-Inf", Unmarshaler{}, `{"oDouble":"-Infinity"}`, &pb.Simple{ODouble: proto.Float64(math.Inf(-1))}},
{"map<int64, int32>", Unmarshaler{}, `{"nummy":{"1":2,"3":4}}`, &pb.Mappy{Nummy: map[int64]int32{1: 2, 3: 4}}},
{"map<string, string>", Unmarshaler{}, `{"strry":{"\"one\"":"two","three":"four"}}`, &pb.Mappy{Strry: map[string]string{`"one"`: "two", "three": "four"}}},
- {"map<int32, Object>", Unmarshaler{}, `{"objjy":{"1":{"dub":1}}}`, &pb.Mappy{Objjy: map[int32]*pb.Simple3{1: &pb.Simple3{Dub: 1}}}},
+ {"map<int32, Object>", Unmarshaler{}, `{"objjy":{"1":{"dub":1}}}`, &pb.Mappy{Objjy: map[int32]*pb.Simple3{1: {Dub: 1}}}},
{"proto2 extension", Unmarshaler{}, realNumberJSON, realNumber},
{"Any with message", Unmarshaler{}, anySimpleJSON, anySimple},
{"Any with message and indent", Unmarshaler{}, anySimplePrettyJSON, anySimple},
@@ -553,12 +553,12 @@ var unmarshalingTests = []struct {
{"camelName input", Unmarshaler{}, `{"oBool":true}`, &pb.Simple{OBool: proto.Bool(true)}},
{"Duration", Unmarshaler{}, `{"dur":"3.000s"}`, &pb.KnownTypes{Dur: &durpb.Duration{Seconds: 3}}},
- {"null Duration", Unmarshaler{}, `{"dur":null}`, &pb.KnownTypes{Dur: &durpb.Duration{Seconds: 0}}},
+ {"null Duration", Unmarshaler{}, `{"dur":null}`, &pb.KnownTypes{Dur: nil}},
{"Timestamp", Unmarshaler{}, `{"ts":"2014-05-13T16:53:20.021Z"}`, &pb.KnownTypes{Ts: &tspb.Timestamp{Seconds: 14e8, Nanos: 21e6}}},
{"PreEpochTimestamp", Unmarshaler{}, `{"ts":"1969-12-31T23:59:58.999999995Z"}`, &pb.KnownTypes{Ts: &tspb.Timestamp{Seconds: -2, Nanos: 999999995}}},
{"ZeroTimeTimestamp", Unmarshaler{}, `{"ts":"0001-01-01T00:00:00Z"}`, &pb.KnownTypes{Ts: &tspb.Timestamp{Seconds: -62135596800, Nanos: 0}}},
- {"null Timestamp", Unmarshaler{}, `{"ts":null}`, &pb.KnownTypes{Ts: &tspb.Timestamp{Seconds: 0, Nanos: 0}}},
- {"null Struct", Unmarshaler{}, `{"st": null}`, &pb.KnownTypes{St: &stpb.Struct{}}},
+ {"null Timestamp", Unmarshaler{}, `{"ts":null}`, &pb.KnownTypes{Ts: nil}},
+ {"null Struct", Unmarshaler{}, `{"st": null}`, &pb.KnownTypes{St: nil}},
{"empty Struct", Unmarshaler{}, `{"st": {}}`, &pb.KnownTypes{St: &stpb.Struct{}}},
{"basic Struct", Unmarshaler{}, `{"st": {"a": "x", "b": null, "c": 3, "d": true}}`, &pb.KnownTypes{St: &stpb.Struct{Fields: map[string]*stpb.Value{
"a": {Kind: &stpb.Value_StringValue{"x"}},
@@ -575,7 +575,7 @@ var unmarshalingTests = []struct {
}}}},
}}}},
}}}},
- {"null ListValue", Unmarshaler{}, `{"lv": null}`, &pb.KnownTypes{Lv: &stpb.ListValue{}}},
+ {"null ListValue", Unmarshaler{}, `{"lv": null}`, &pb.KnownTypes{Lv: nil}},
{"empty ListValue", Unmarshaler{}, `{"lv": []}`, &pb.KnownTypes{Lv: &stpb.ListValue{}}},
{"basic ListValue", Unmarshaler{}, `{"lv": ["x", null, 3, true]}`, &pb.KnownTypes{Lv: &stpb.ListValue{Values: []*stpb.Value{
{Kind: &stpb.Value_StringValue{"x"}},
@@ -612,8 +612,17 @@ var unmarshalingTests = []struct {
{"BoolValue", Unmarshaler{}, `{"bool":true}`, &pb.KnownTypes{Bool: &wpb.BoolValue{Value: true}}},
{"StringValue", Unmarshaler{}, `{"str":"plush"}`, &pb.KnownTypes{Str: &wpb.StringValue{Value: "plush"}}},
{"BytesValue", Unmarshaler{}, `{"bytes":"d293"}`, &pb.KnownTypes{Bytes: &wpb.BytesValue{Value: []byte("wow")}}},
- // `null` is also a permissible value. Let's just test one.
- {"null DoubleValue", Unmarshaler{}, `{"dbl":null}`, &pb.KnownTypes{Dbl: &wpb.DoubleValue{}}},
+
+ // Ensure that `null` as a value ends up with a nil pointer instead of a [type]Value struct.
+ {"null DoubleValue", Unmarshaler{}, `{"dbl":null}`, &pb.KnownTypes{Dbl: nil}},
+ {"null FloatValue", Unmarshaler{}, `{"flt":null}`, &pb.KnownTypes{Flt: nil}},
+ {"null Int64Value", Unmarshaler{}, `{"i64":null}`, &pb.KnownTypes{I64: nil}},
+ {"null UInt64Value", Unmarshaler{}, `{"u64":null}`, &pb.KnownTypes{U64: nil}},
+ {"null Int32Value", Unmarshaler{}, `{"i32":null}`, &pb.KnownTypes{I32: nil}},
+ {"null UInt32Value", Unmarshaler{}, `{"u32":null}`, &pb.KnownTypes{U32: nil}},
+ {"null BoolValue", Unmarshaler{}, `{"bool":null}`, &pb.KnownTypes{Bool: nil}},
+ {"null StringValue", Unmarshaler{}, `{"str":null}`, &pb.KnownTypes{Str: nil}},
+ {"null BytesValue", Unmarshaler{}, `{"bytes":null}`, &pb.KnownTypes{Bytes: nil}},
}
func TestUnmarshaling(t *testing.T) {
@@ -636,6 +645,26 @@ func TestUnmarshaling(t *testing.T) {
}
}
+func TestUnmarshalNullArray(t *testing.T) {
+ var repeats pb.Repeats
+ if err := UnmarshalString(`{"rBool":null}`, &repeats); err != nil {
+ t.Fatal(err)
+ }
+ if !reflect.DeepEqual(repeats, pb.Repeats{}) {
+ t.Errorf("got non-nil fields in [%#v]", repeats)
+ }
+}
+
+func TestUnmarshalNullObject(t *testing.T) {
+ var maps pb.Maps
+ if err := UnmarshalString(`{"mInt64Str":null}`, &maps); err != nil {
+ t.Fatal(err)
+ }
+ if !reflect.DeepEqual(maps, pb.Maps{}) {
+ t.Errorf("got non-nil fields in [%#v]", maps)
+ }
+}
+
func TestUnmarshalNext(t *testing.T) {
// We only need to check against a few, not all of them.
tests := unmarshalingTests[:5]
@@ -727,6 +756,7 @@ func TestUnmarshalAnyJSONPBUnmarshaler(t *testing.T) {
const (
dynamicMessageName = "google.protobuf.jsonpb.testing.dynamicMessage"
)
+
func init() {
// we register the custom type below so that we can use it in Any types
proto.RegisterType((*dynamicMessage)(nil), dynamicMessageName)
@@ -756,4 +786,4 @@ func (m *dynamicMessage) MarshalJSONPB(jm *Marshaler) ([]byte, error) {
func (m *dynamicMessage) UnmarshalJSONPB(jum *Unmarshaler, js []byte) error {
m.rawJson = string(js)
return nil
-} \ No newline at end of file
+}
diff --git a/vendor/github.com/golang/protobuf/protoc-gen-go/descriptor/Makefile b/vendor/github.com/golang/protobuf/protoc-gen-go/descriptor/Makefile
index 41a2d04d0..f706871a6 100644
--- a/vendor/github.com/golang/protobuf/protoc-gen-go/descriptor/Makefile
+++ b/vendor/github.com/golang/protobuf/protoc-gen-go/descriptor/Makefile
@@ -33,4 +33,5 @@
# at src/google/protobuf/descriptor.proto
regenerate:
@echo WARNING! THIS RULE IS PROBABLY NOT RIGHT FOR YOUR INSTALLATION
+ cp $(HOME)/src/protobuf/include/google/protobuf/descriptor.proto .
protoc --go_out=../../../../.. -I$(HOME)/src/protobuf/include $(HOME)/src/protobuf/include/google/protobuf/descriptor.proto
diff --git a/vendor/github.com/golang/protobuf/protoc-gen-go/descriptor/descriptor.pb.go b/vendor/github.com/golang/protobuf/protoc-gen-go/descriptor/descriptor.pb.go
index 63cf2c80a..1d92cb272 100644
--- a/vendor/github.com/golang/protobuf/protoc-gen-go/descriptor/descriptor.pb.go
+++ b/vendor/github.com/golang/protobuf/protoc-gen-go/descriptor/descriptor.pb.go
@@ -974,6 +974,7 @@ type FileOptions struct {
CcGenericServices *bool `protobuf:"varint,16,opt,name=cc_generic_services,json=ccGenericServices,def=0" json:"cc_generic_services,omitempty"`
JavaGenericServices *bool `protobuf:"varint,17,opt,name=java_generic_services,json=javaGenericServices,def=0" json:"java_generic_services,omitempty"`
PyGenericServices *bool `protobuf:"varint,18,opt,name=py_generic_services,json=pyGenericServices,def=0" json:"py_generic_services,omitempty"`
+ PhpGenericServices *bool `protobuf:"varint,19,opt,name=php_generic_services,json=phpGenericServices,def=0" json:"php_generic_services,omitempty"`
// Is this file deprecated?
// Depending on the target platform, this can emit Deprecated annotations
// for everything in the file, or it will be completely ignored; in the very
@@ -995,6 +996,10 @@ type FileOptions struct {
// Sets the php class prefix which is prepended to all php generated classes
// from this .proto. Default is empty.
PhpClassPrefix *string `protobuf:"bytes,40,opt,name=php_class_prefix,json=phpClassPrefix" json:"php_class_prefix,omitempty"`
+ // Use this option to change the namespace of php generated classes. Default
+ // is empty. When this option is empty, the package name will be used for
+ // determining the namespace.
+ PhpNamespace *string `protobuf:"bytes,41,opt,name=php_namespace,json=phpNamespace" json:"php_namespace,omitempty"`
// The parser stores options it doesn't recognize here. See above.
UninterpretedOption []*UninterpretedOption `protobuf:"bytes,999,rep,name=uninterpreted_option,json=uninterpretedOption" json:"uninterpreted_option,omitempty"`
proto.XXX_InternalExtensions `json:"-"`
@@ -1020,6 +1025,7 @@ const Default_FileOptions_OptimizeFor FileOptions_OptimizeMode = FileOptions_SPE
const Default_FileOptions_CcGenericServices bool = false
const Default_FileOptions_JavaGenericServices bool = false
const Default_FileOptions_PyGenericServices bool = false
+const Default_FileOptions_PhpGenericServices bool = false
const Default_FileOptions_Deprecated bool = false
const Default_FileOptions_CcEnableArenas bool = false
@@ -1093,6 +1099,13 @@ func (m *FileOptions) GetPyGenericServices() bool {
return Default_FileOptions_PyGenericServices
}
+func (m *FileOptions) GetPhpGenericServices() bool {
+ if m != nil && m.PhpGenericServices != nil {
+ return *m.PhpGenericServices
+ }
+ return Default_FileOptions_PhpGenericServices
+}
+
func (m *FileOptions) GetDeprecated() bool {
if m != nil && m.Deprecated != nil {
return *m.Deprecated
@@ -1135,6 +1148,13 @@ func (m *FileOptions) GetPhpClassPrefix() string {
return ""
}
+func (m *FileOptions) GetPhpNamespace() string {
+ if m != nil && m.PhpNamespace != nil {
+ return *m.PhpNamespace
+ }
+ return ""
+}
+
func (m *FileOptions) GetUninterpretedOption() []*UninterpretedOption {
if m != nil {
return m.UninterpretedOption
@@ -1994,159 +2014,161 @@ func init() {
func init() { proto.RegisterFile("google/protobuf/descriptor.proto", fileDescriptor0) }
var fileDescriptor0 = []byte{
- // 2460 bytes of a gzipped FileDescriptorProto
- 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x59, 0x5b, 0x6f, 0xdb, 0xc8,
- 0x15, 0x5e, 0x5d, 0x2d, 0x1d, 0xc9, 0xf2, 0x78, 0xec, 0x4d, 0x18, 0xef, 0x25, 0x8e, 0xf6, 0x12,
- 0x6f, 0xd2, 0xc8, 0x0b, 0xe7, 0xb2, 0x59, 0xa7, 0x48, 0x21, 0x4b, 0x8c, 0x57, 0xa9, 0x2c, 0xa9,
- 0x94, 0xdc, 0x4d, 0xf6, 0x85, 0x18, 0x93, 0x23, 0x99, 0x09, 0x45, 0x72, 0x49, 0x2a, 0x89, 0xf7,
- 0x29, 0x40, 0x9f, 0x0a, 0xf4, 0x07, 0x14, 0x45, 0xd1, 0x87, 0x7d, 0x59, 0xa0, 0x3f, 0xa0, 0xcf,
- 0xfd, 0x05, 0x05, 0xf6, 0xb9, 0x2f, 0x45, 0x51, 0xa0, 0xfd, 0x07, 0x7d, 0x2d, 0x66, 0x86, 0xa4,
- 0x48, 0x5d, 0x12, 0x77, 0x81, 0xec, 0x3e, 0xd9, 0x73, 0xce, 0x77, 0x0e, 0xcf, 0x9c, 0xf9, 0x66,
- 0xce, 0x99, 0x11, 0x6c, 0x8f, 0x6c, 0x7b, 0x64, 0xd2, 0x5d, 0xc7, 0xb5, 0x7d, 0xfb, 0x64, 0x32,
- 0xdc, 0xd5, 0xa9, 0xa7, 0xb9, 0x86, 0xe3, 0xdb, 0x6e, 0x8d, 0xcb, 0xf0, 0x9a, 0x40, 0xd4, 0x42,
- 0x44, 0xf5, 0x08, 0xd6, 0x1f, 0x18, 0x26, 0x6d, 0x46, 0xc0, 0x3e, 0xf5, 0xf1, 0x5d, 0xc8, 0x0e,
- 0x0d, 0x93, 0x4a, 0xa9, 0xed, 0xcc, 0x4e, 0x69, 0xef, 0xc3, 0xda, 0x8c, 0x51, 0x2d, 0x69, 0xd1,
- 0x63, 0x62, 0x85, 0x5b, 0x54, 0xff, 0x95, 0x85, 0x8d, 0x05, 0x5a, 0x8c, 0x21, 0x6b, 0x91, 0x31,
- 0xf3, 0x98, 0xda, 0x29, 0x2a, 0xfc, 0x7f, 0x2c, 0xc1, 0x8a, 0x43, 0xb4, 0xa7, 0x64, 0x44, 0xa5,
- 0x34, 0x17, 0x87, 0x43, 0xfc, 0x3e, 0x80, 0x4e, 0x1d, 0x6a, 0xe9, 0xd4, 0xd2, 0xce, 0xa4, 0xcc,
- 0x76, 0x66, 0xa7, 0xa8, 0xc4, 0x24, 0xf8, 0x3a, 0xac, 0x3b, 0x93, 0x13, 0xd3, 0xd0, 0xd4, 0x18,
- 0x0c, 0xb6, 0x33, 0x3b, 0x39, 0x05, 0x09, 0x45, 0x73, 0x0a, 0xbe, 0x0a, 0x6b, 0xcf, 0x29, 0x79,
- 0x1a, 0x87, 0x96, 0x38, 0xb4, 0xc2, 0xc4, 0x31, 0x60, 0x03, 0xca, 0x63, 0xea, 0x79, 0x64, 0x44,
- 0x55, 0xff, 0xcc, 0xa1, 0x52, 0x96, 0xcf, 0x7e, 0x7b, 0x6e, 0xf6, 0xb3, 0x33, 0x2f, 0x05, 0x56,
- 0x83, 0x33, 0x87, 0xe2, 0x3a, 0x14, 0xa9, 0x35, 0x19, 0x0b, 0x0f, 0xb9, 0x25, 0xf9, 0x93, 0xad,
- 0xc9, 0x78, 0xd6, 0x4b, 0x81, 0x99, 0x05, 0x2e, 0x56, 0x3c, 0xea, 0x3e, 0x33, 0x34, 0x2a, 0xe5,
- 0xb9, 0x83, 0xab, 0x73, 0x0e, 0xfa, 0x42, 0x3f, 0xeb, 0x23, 0xb4, 0xc3, 0x0d, 0x28, 0xd2, 0x17,
- 0x3e, 0xb5, 0x3c, 0xc3, 0xb6, 0xa4, 0x15, 0xee, 0xe4, 0xa3, 0x05, 0xab, 0x48, 0x4d, 0x7d, 0xd6,
- 0xc5, 0xd4, 0x0e, 0xdf, 0x81, 0x15, 0xdb, 0xf1, 0x0d, 0xdb, 0xf2, 0xa4, 0xc2, 0x76, 0x6a, 0xa7,
- 0xb4, 0xf7, 0xee, 0x42, 0x22, 0x74, 0x05, 0x46, 0x09, 0xc1, 0xb8, 0x05, 0xc8, 0xb3, 0x27, 0xae,
- 0x46, 0x55, 0xcd, 0xd6, 0xa9, 0x6a, 0x58, 0x43, 0x5b, 0x2a, 0x72, 0x07, 0x97, 0xe7, 0x27, 0xc2,
- 0x81, 0x0d, 0x5b, 0xa7, 0x2d, 0x6b, 0x68, 0x2b, 0x15, 0x2f, 0x31, 0xc6, 0x17, 0x20, 0xef, 0x9d,
- 0x59, 0x3e, 0x79, 0x21, 0x95, 0x39, 0x43, 0x82, 0x51, 0xf5, 0xbf, 0x39, 0x58, 0x3b, 0x0f, 0xc5,
- 0xee, 0x41, 0x6e, 0xc8, 0x66, 0x29, 0xa5, 0xff, 0x9f, 0x1c, 0x08, 0x9b, 0x64, 0x12, 0xf3, 0x3f,
- 0x30, 0x89, 0x75, 0x28, 0x59, 0xd4, 0xf3, 0xa9, 0x2e, 0x18, 0x91, 0x39, 0x27, 0xa7, 0x40, 0x18,
- 0xcd, 0x53, 0x2a, 0xfb, 0x83, 0x28, 0xf5, 0x08, 0xd6, 0xa2, 0x90, 0x54, 0x97, 0x58, 0xa3, 0x90,
- 0x9b, 0xbb, 0xaf, 0x8b, 0xa4, 0x26, 0x87, 0x76, 0x0a, 0x33, 0x53, 0x2a, 0x34, 0x31, 0xc6, 0x4d,
- 0x00, 0xdb, 0xa2, 0xf6, 0x50, 0xd5, 0xa9, 0x66, 0x4a, 0x85, 0x25, 0x59, 0xea, 0x32, 0xc8, 0x5c,
- 0x96, 0x6c, 0x21, 0xd5, 0x4c, 0xfc, 0xf9, 0x94, 0x6a, 0x2b, 0x4b, 0x98, 0x72, 0x24, 0x36, 0xd9,
- 0x1c, 0xdb, 0x8e, 0xa1, 0xe2, 0x52, 0xc6, 0x7b, 0xaa, 0x07, 0x33, 0x2b, 0xf2, 0x20, 0x6a, 0xaf,
- 0x9d, 0x99, 0x12, 0x98, 0x89, 0x89, 0xad, 0xba, 0xf1, 0x21, 0xfe, 0x00, 0x22, 0x81, 0xca, 0x69,
- 0x05, 0xfc, 0x14, 0x2a, 0x87, 0xc2, 0x0e, 0x19, 0xd3, 0xad, 0xbb, 0x50, 0x49, 0xa6, 0x07, 0x6f,
- 0x42, 0xce, 0xf3, 0x89, 0xeb, 0x73, 0x16, 0xe6, 0x14, 0x31, 0xc0, 0x08, 0x32, 0xd4, 0xd2, 0xf9,
- 0x29, 0x97, 0x53, 0xd8, 0xbf, 0x5b, 0x9f, 0xc1, 0x6a, 0xe2, 0xf3, 0xe7, 0x35, 0xac, 0xfe, 0x3e,
- 0x0f, 0x9b, 0x8b, 0x38, 0xb7, 0x90, 0xfe, 0x17, 0x20, 0x6f, 0x4d, 0xc6, 0x27, 0xd4, 0x95, 0x32,
- 0xdc, 0x43, 0x30, 0xc2, 0x75, 0xc8, 0x99, 0xe4, 0x84, 0x9a, 0x52, 0x76, 0x3b, 0xb5, 0x53, 0xd9,
- 0xbb, 0x7e, 0x2e, 0x56, 0xd7, 0xda, 0xcc, 0x44, 0x11, 0x96, 0xf8, 0x3e, 0x64, 0x83, 0x23, 0x8e,
- 0x79, 0xb8, 0x76, 0x3e, 0x0f, 0x8c, 0x8b, 0x0a, 0xb7, 0xc3, 0xef, 0x40, 0x91, 0xfd, 0x15, 0xb9,
- 0xcd, 0xf3, 0x98, 0x0b, 0x4c, 0xc0, 0xf2, 0x8a, 0xb7, 0xa0, 0xc0, 0x69, 0xa6, 0xd3, 0xb0, 0x34,
- 0x44, 0x63, 0xb6, 0x30, 0x3a, 0x1d, 0x92, 0x89, 0xe9, 0xab, 0xcf, 0x88, 0x39, 0xa1, 0x9c, 0x30,
- 0x45, 0xa5, 0x1c, 0x08, 0x7f, 0xcd, 0x64, 0xf8, 0x32, 0x94, 0x04, 0x2b, 0x0d, 0x4b, 0xa7, 0x2f,
- 0xf8, 0xe9, 0x93, 0x53, 0x04, 0x51, 0x5b, 0x4c, 0xc2, 0x3e, 0xff, 0xc4, 0xb3, 0xad, 0x70, 0x69,
- 0xf9, 0x27, 0x98, 0x80, 0x7f, 0xfe, 0xb3, 0xd9, 0x83, 0xef, 0xbd, 0xc5, 0xd3, 0x9b, 0xe5, 0x62,
- 0xf5, 0x2f, 0x69, 0xc8, 0xf2, 0xfd, 0xb6, 0x06, 0xa5, 0xc1, 0xe3, 0x9e, 0xac, 0x36, 0xbb, 0xc7,
- 0x07, 0x6d, 0x19, 0xa5, 0x70, 0x05, 0x80, 0x0b, 0x1e, 0xb4, 0xbb, 0xf5, 0x01, 0x4a, 0x47, 0xe3,
- 0x56, 0x67, 0x70, 0xe7, 0x16, 0xca, 0x44, 0x06, 0xc7, 0x42, 0x90, 0x8d, 0x03, 0x6e, 0xee, 0xa1,
- 0x1c, 0x46, 0x50, 0x16, 0x0e, 0x5a, 0x8f, 0xe4, 0xe6, 0x9d, 0x5b, 0x28, 0x9f, 0x94, 0xdc, 0xdc,
- 0x43, 0x2b, 0x78, 0x15, 0x8a, 0x5c, 0x72, 0xd0, 0xed, 0xb6, 0x51, 0x21, 0xf2, 0xd9, 0x1f, 0x28,
- 0xad, 0xce, 0x21, 0x2a, 0x46, 0x3e, 0x0f, 0x95, 0xee, 0x71, 0x0f, 0x41, 0xe4, 0xe1, 0x48, 0xee,
- 0xf7, 0xeb, 0x87, 0x32, 0x2a, 0x45, 0x88, 0x83, 0xc7, 0x03, 0xb9, 0x8f, 0xca, 0x89, 0xb0, 0x6e,
- 0xee, 0xa1, 0xd5, 0xe8, 0x13, 0x72, 0xe7, 0xf8, 0x08, 0x55, 0xf0, 0x3a, 0xac, 0x8a, 0x4f, 0x84,
- 0x41, 0xac, 0xcd, 0x88, 0xee, 0xdc, 0x42, 0x68, 0x1a, 0x88, 0xf0, 0xb2, 0x9e, 0x10, 0xdc, 0xb9,
- 0x85, 0x70, 0xb5, 0x01, 0x39, 0xce, 0x2e, 0x8c, 0xa1, 0xd2, 0xae, 0x1f, 0xc8, 0x6d, 0xb5, 0xdb,
- 0x1b, 0xb4, 0xba, 0x9d, 0x7a, 0x1b, 0xa5, 0xa6, 0x32, 0x45, 0xfe, 0xd5, 0x71, 0x4b, 0x91, 0x9b,
- 0x28, 0x1d, 0x97, 0xf5, 0xe4, 0xfa, 0x40, 0x6e, 0xa2, 0x4c, 0x55, 0x83, 0xcd, 0x45, 0xe7, 0xcc,
- 0xc2, 0x9d, 0x11, 0x5b, 0xe2, 0xf4, 0x92, 0x25, 0xe6, 0xbe, 0xe6, 0x96, 0xf8, 0xdb, 0x14, 0x6c,
- 0x2c, 0x38, 0x6b, 0x17, 0x7e, 0xe4, 0x17, 0x90, 0x13, 0x14, 0x15, 0xd5, 0xe7, 0x93, 0x85, 0x87,
- 0x36, 0x27, 0xec, 0x5c, 0x05, 0xe2, 0x76, 0xf1, 0x0a, 0x9c, 0x59, 0x52, 0x81, 0x99, 0x8b, 0xb9,
- 0x20, 0x7f, 0x93, 0x02, 0x69, 0x99, 0xef, 0xd7, 0x1c, 0x14, 0xe9, 0xc4, 0x41, 0x71, 0x6f, 0x36,
- 0x80, 0x2b, 0xcb, 0xe7, 0x30, 0x17, 0xc5, 0x77, 0x29, 0xb8, 0xb0, 0xb8, 0x51, 0x59, 0x18, 0xc3,
- 0x7d, 0xc8, 0x8f, 0xa9, 0x7f, 0x6a, 0x87, 0xc5, 0xfa, 0xe3, 0x05, 0x25, 0x80, 0xa9, 0x67, 0x73,
- 0x15, 0x58, 0xc5, 0x6b, 0x48, 0x66, 0x59, 0xb7, 0x21, 0xa2, 0x99, 0x8b, 0xf4, 0xb7, 0x69, 0x78,
- 0x7b, 0xa1, 0xf3, 0x85, 0x81, 0xbe, 0x07, 0x60, 0x58, 0xce, 0xc4, 0x17, 0x05, 0x59, 0x9c, 0x4f,
- 0x45, 0x2e, 0xe1, 0x7b, 0x9f, 0x9d, 0x3d, 0x13, 0x3f, 0xd2, 0x67, 0xb8, 0x1e, 0x84, 0x88, 0x03,
- 0xee, 0x4e, 0x03, 0xcd, 0xf2, 0x40, 0xdf, 0x5f, 0x32, 0xd3, 0xb9, 0x5a, 0xf7, 0x29, 0x20, 0xcd,
- 0x34, 0xa8, 0xe5, 0xab, 0x9e, 0xef, 0x52, 0x32, 0x36, 0xac, 0x11, 0x3f, 0x80, 0x0b, 0xfb, 0xb9,
- 0x21, 0x31, 0x3d, 0xaa, 0xac, 0x09, 0x75, 0x3f, 0xd4, 0x32, 0x0b, 0x5e, 0x65, 0xdc, 0x98, 0x45,
- 0x3e, 0x61, 0x21, 0xd4, 0x91, 0x45, 0xf5, 0xef, 0x2b, 0x50, 0x8a, 0xb5, 0x75, 0xf8, 0x0a, 0x94,
- 0x9f, 0x90, 0x67, 0x44, 0x0d, 0x5b, 0x75, 0x91, 0x89, 0x12, 0x93, 0xf5, 0x82, 0x76, 0xfd, 0x53,
- 0xd8, 0xe4, 0x10, 0x7b, 0xe2, 0x53, 0x57, 0xd5, 0x4c, 0xe2, 0x79, 0x3c, 0x69, 0x05, 0x0e, 0xc5,
- 0x4c, 0xd7, 0x65, 0xaa, 0x46, 0xa8, 0xc1, 0xb7, 0x61, 0x83, 0x5b, 0x8c, 0x27, 0xa6, 0x6f, 0x38,
- 0x26, 0x55, 0xd9, 0xe5, 0xc1, 0xe3, 0x07, 0x71, 0x14, 0xd9, 0x3a, 0x43, 0x1c, 0x05, 0x00, 0x16,
- 0x91, 0x87, 0x9b, 0xf0, 0x1e, 0x37, 0x1b, 0x51, 0x8b, 0xba, 0xc4, 0xa7, 0x2a, 0xfd, 0x7a, 0x42,
- 0x4c, 0x4f, 0x25, 0x96, 0xae, 0x9e, 0x12, 0xef, 0x54, 0xda, 0x64, 0x0e, 0x0e, 0xd2, 0x52, 0x4a,
- 0xb9, 0xc4, 0x80, 0x87, 0x01, 0x4e, 0xe6, 0xb0, 0xba, 0xa5, 0x7f, 0x41, 0xbc, 0x53, 0xbc, 0x0f,
- 0x17, 0xb8, 0x17, 0xcf, 0x77, 0x0d, 0x6b, 0xa4, 0x6a, 0xa7, 0x54, 0x7b, 0xaa, 0x4e, 0xfc, 0xe1,
- 0x5d, 0xe9, 0x9d, 0xf8, 0xf7, 0x79, 0x84, 0x7d, 0x8e, 0x69, 0x30, 0xc8, 0xb1, 0x3f, 0xbc, 0x8b,
- 0xfb, 0x50, 0x66, 0x8b, 0x31, 0x36, 0xbe, 0xa1, 0xea, 0xd0, 0x76, 0x79, 0x65, 0xa9, 0x2c, 0xd8,
- 0xd9, 0xb1, 0x0c, 0xd6, 0xba, 0x81, 0xc1, 0x91, 0xad, 0xd3, 0xfd, 0x5c, 0xbf, 0x27, 0xcb, 0x4d,
- 0xa5, 0x14, 0x7a, 0x79, 0x60, 0xbb, 0x8c, 0x50, 0x23, 0x3b, 0x4a, 0x70, 0x49, 0x10, 0x6a, 0x64,
- 0x87, 0xe9, 0xbd, 0x0d, 0x1b, 0x9a, 0x26, 0xe6, 0x6c, 0x68, 0x6a, 0xd0, 0xe2, 0x7b, 0x12, 0x4a,
- 0x24, 0x4b, 0xd3, 0x0e, 0x05, 0x20, 0xe0, 0xb8, 0x87, 0x3f, 0x87, 0xb7, 0xa7, 0xc9, 0x8a, 0x1b,
- 0xae, 0xcf, 0xcd, 0x72, 0xd6, 0xf4, 0x36, 0x6c, 0x38, 0x67, 0xf3, 0x86, 0x38, 0xf1, 0x45, 0xe7,
- 0x6c, 0xd6, 0xec, 0x23, 0x7e, 0x6d, 0x73, 0xa9, 0x46, 0x7c, 0xaa, 0x4b, 0x17, 0xe3, 0xe8, 0x98,
- 0x02, 0xef, 0x02, 0xd2, 0x34, 0x95, 0x5a, 0xe4, 0xc4, 0xa4, 0x2a, 0x71, 0xa9, 0x45, 0x3c, 0xe9,
- 0x72, 0x1c, 0x5c, 0xd1, 0x34, 0x99, 0x6b, 0xeb, 0x5c, 0x89, 0xaf, 0xc1, 0xba, 0x7d, 0xf2, 0x44,
- 0x13, 0xcc, 0x52, 0x1d, 0x97, 0x0e, 0x8d, 0x17, 0xd2, 0x87, 0x3c, 0x4d, 0x6b, 0x4c, 0xc1, 0x79,
- 0xd5, 0xe3, 0x62, 0xfc, 0x09, 0x20, 0xcd, 0x3b, 0x25, 0xae, 0xc3, 0x4b, 0xbb, 0xe7, 0x10, 0x8d,
- 0x4a, 0x1f, 0x09, 0xa8, 0x90, 0x77, 0x42, 0x31, 0x63, 0xb6, 0xf7, 0xdc, 0x18, 0xfa, 0xa1, 0xc7,
- 0xab, 0x82, 0xd9, 0x5c, 0x16, 0x78, 0xdb, 0x01, 0xe4, 0x9c, 0x3a, 0xc9, 0x0f, 0xef, 0x70, 0x58,
- 0xc5, 0x39, 0x75, 0xe2, 0xdf, 0x7d, 0x04, 0x9b, 0x13, 0xcb, 0xb0, 0x7c, 0xea, 0x3a, 0x2e, 0x65,
- 0xed, 0xbe, 0xd8, 0xb3, 0xd2, 0xbf, 0x57, 0x96, 0x34, 0xec, 0xc7, 0x71, 0xb4, 0xa0, 0x8a, 0xb2,
- 0x31, 0x99, 0x17, 0x56, 0xf7, 0xa1, 0x1c, 0x67, 0x10, 0x2e, 0x82, 0xe0, 0x10, 0x4a, 0xb1, 0x6a,
- 0xdc, 0xe8, 0x36, 0x59, 0x1d, 0xfd, 0x4a, 0x46, 0x69, 0x56, 0xcf, 0xdb, 0xad, 0x81, 0xac, 0x2a,
- 0xc7, 0x9d, 0x41, 0xeb, 0x48, 0x46, 0x99, 0x6b, 0xc5, 0xc2, 0x7f, 0x56, 0xd0, 0xcb, 0x97, 0x2f,
- 0x5f, 0xa6, 0x1f, 0x66, 0x0b, 0x1f, 0xa3, 0xab, 0xd5, 0xef, 0xd3, 0x50, 0x49, 0x76, 0xd2, 0xf8,
- 0xe7, 0x70, 0x31, 0xbc, 0xf6, 0x7a, 0xd4, 0x57, 0x9f, 0x1b, 0x2e, 0xa7, 0xf6, 0x98, 0x88, 0x5e,
- 0x34, 0x5a, 0x95, 0xcd, 0x00, 0xd5, 0xa7, 0xfe, 0x97, 0x86, 0xcb, 0x88, 0x3b, 0x26, 0x3e, 0x6e,
- 0xc3, 0x65, 0xcb, 0x56, 0x3d, 0x9f, 0x58, 0x3a, 0x71, 0x75, 0x75, 0xfa, 0xe0, 0xa0, 0x12, 0x4d,
- 0xa3, 0x9e, 0x67, 0x8b, 0x92, 0x12, 0x79, 0x79, 0xd7, 0xb2, 0xfb, 0x01, 0x78, 0x7a, 0xd6, 0xd6,
- 0x03, 0xe8, 0x0c, 0x83, 0x32, 0xcb, 0x18, 0xf4, 0x0e, 0x14, 0xc7, 0xc4, 0x51, 0xa9, 0xe5, 0xbb,
- 0x67, 0xbc, 0xff, 0x2b, 0x28, 0x85, 0x31, 0x71, 0x64, 0x36, 0x7e, 0x73, 0x2b, 0x91, 0xcc, 0x66,
- 0x01, 0x15, 0x1f, 0x66, 0x0b, 0x45, 0x04, 0xd5, 0x7f, 0x66, 0xa0, 0x1c, 0xef, 0x07, 0x59, 0x7b,
- 0xad, 0xf1, 0xb3, 0x3f, 0xc5, 0x4f, 0x87, 0x0f, 0x5e, 0xd9, 0x3d, 0xd6, 0x1a, 0xac, 0x28, 0xec,
- 0xe7, 0x45, 0x97, 0xa6, 0x08, 0x4b, 0x56, 0x90, 0xd9, 0x79, 0x40, 0x45, 0xef, 0x5f, 0x50, 0x82,
- 0x11, 0x3e, 0x84, 0xfc, 0x13, 0x8f, 0xfb, 0xce, 0x73, 0xdf, 0x1f, 0xbe, 0xda, 0xf7, 0xc3, 0x3e,
- 0x77, 0x5e, 0x7c, 0xd8, 0x57, 0x3b, 0x5d, 0xe5, 0xa8, 0xde, 0x56, 0x02, 0x73, 0x7c, 0x09, 0xb2,
- 0x26, 0xf9, 0xe6, 0x2c, 0x59, 0x3e, 0xb8, 0xe8, 0xbc, 0x8b, 0x70, 0x09, 0xb2, 0xcf, 0x29, 0x79,
- 0x9a, 0x3c, 0xb4, 0xb9, 0xe8, 0x0d, 0x6e, 0x86, 0x5d, 0xc8, 0xf1, 0x7c, 0x61, 0x80, 0x20, 0x63,
- 0xe8, 0x2d, 0x5c, 0x80, 0x6c, 0xa3, 0xab, 0xb0, 0x0d, 0x81, 0xa0, 0x2c, 0xa4, 0x6a, 0xaf, 0x25,
- 0x37, 0x64, 0x94, 0xae, 0xde, 0x86, 0xbc, 0x48, 0x02, 0xdb, 0x2c, 0x51, 0x1a, 0xd0, 0x5b, 0xc1,
- 0x30, 0xf0, 0x91, 0x0a, 0xb5, 0xc7, 0x47, 0x07, 0xb2, 0x82, 0xd2, 0xc9, 0xa5, 0xce, 0xa2, 0x5c,
- 0xd5, 0x83, 0x72, 0xbc, 0x21, 0xfc, 0x51, 0x58, 0x56, 0xfd, 0x6b, 0x0a, 0x4a, 0xb1, 0x06, 0x8f,
- 0xb5, 0x16, 0xc4, 0x34, 0xed, 0xe7, 0x2a, 0x31, 0x0d, 0xe2, 0x05, 0xd4, 0x00, 0x2e, 0xaa, 0x33,
- 0xc9, 0x79, 0x97, 0xee, 0x47, 0xda, 0x22, 0x39, 0x94, 0xaf, 0xfe, 0x29, 0x05, 0x68, 0xb6, 0x45,
- 0x9c, 0x09, 0x33, 0xf5, 0x53, 0x86, 0x59, 0xfd, 0x63, 0x0a, 0x2a, 0xc9, 0xbe, 0x70, 0x26, 0xbc,
- 0x2b, 0x3f, 0x69, 0x78, 0xff, 0x48, 0xc3, 0x6a, 0xa2, 0x1b, 0x3c, 0x6f, 0x74, 0x5f, 0xc3, 0xba,
- 0xa1, 0xd3, 0xb1, 0x63, 0xfb, 0xd4, 0xd2, 0xce, 0x54, 0x93, 0x3e, 0xa3, 0xa6, 0x54, 0xe5, 0x87,
- 0xc6, 0xee, 0xab, 0xfb, 0xcd, 0x5a, 0x6b, 0x6a, 0xd7, 0x66, 0x66, 0xfb, 0x1b, 0xad, 0xa6, 0x7c,
- 0xd4, 0xeb, 0x0e, 0xe4, 0x4e, 0xe3, 0xb1, 0x7a, 0xdc, 0xf9, 0x65, 0xa7, 0xfb, 0x65, 0x47, 0x41,
- 0xc6, 0x0c, 0xec, 0x0d, 0x6e, 0xfb, 0x1e, 0xa0, 0xd9, 0xa0, 0xf0, 0x45, 0x58, 0x14, 0x16, 0x7a,
- 0x0b, 0x6f, 0xc0, 0x5a, 0xa7, 0xab, 0xf6, 0x5b, 0x4d, 0x59, 0x95, 0x1f, 0x3c, 0x90, 0x1b, 0x83,
- 0xbe, 0xb8, 0x80, 0x47, 0xe8, 0x41, 0x62, 0x83, 0x57, 0xff, 0x90, 0x81, 0x8d, 0x05, 0x91, 0xe0,
- 0x7a, 0xd0, 0xfb, 0x8b, 0xeb, 0xc8, 0x8d, 0xf3, 0x44, 0x5f, 0x63, 0xdd, 0x45, 0x8f, 0xb8, 0x7e,
- 0x70, 0x55, 0xf8, 0x04, 0x58, 0x96, 0x2c, 0xdf, 0x18, 0x1a, 0xd4, 0x0d, 0xde, 0x2b, 0xc4, 0x85,
- 0x60, 0x6d, 0x2a, 0x17, 0x4f, 0x16, 0x3f, 0x03, 0xec, 0xd8, 0x9e, 0xe1, 0x1b, 0xcf, 0xa8, 0x6a,
- 0x58, 0xe1, 0xe3, 0x06, 0xbb, 0x20, 0x64, 0x15, 0x14, 0x6a, 0x5a, 0x96, 0x1f, 0xa1, 0x2d, 0x3a,
- 0x22, 0x33, 0x68, 0x76, 0x98, 0x67, 0x14, 0x14, 0x6a, 0x22, 0xf4, 0x15, 0x28, 0xeb, 0xf6, 0x84,
- 0xb5, 0x5b, 0x02, 0xc7, 0x6a, 0x47, 0x4a, 0x29, 0x09, 0x59, 0x04, 0x09, 0xfa, 0xe1, 0xe9, 0xab,
- 0x4a, 0x59, 0x29, 0x09, 0x99, 0x80, 0x5c, 0x85, 0x35, 0x32, 0x1a, 0xb9, 0xcc, 0x79, 0xe8, 0x48,
- 0x74, 0xf8, 0x95, 0x48, 0xcc, 0x81, 0x5b, 0x0f, 0xa1, 0x10, 0xe6, 0x81, 0x95, 0x6a, 0x96, 0x09,
- 0xd5, 0x11, 0x6f, 0x5b, 0xe9, 0x9d, 0xa2, 0x52, 0xb0, 0x42, 0xe5, 0x15, 0x28, 0x1b, 0x9e, 0x3a,
- 0x7d, 0x64, 0x4d, 0x6f, 0xa7, 0x77, 0x0a, 0x4a, 0xc9, 0xf0, 0xa2, 0x57, 0xb5, 0xea, 0x77, 0x69,
- 0xa8, 0x24, 0x1f, 0x89, 0x71, 0x13, 0x0a, 0xa6, 0xad, 0x11, 0x4e, 0x2d, 0xf1, 0x0b, 0xc5, 0xce,
- 0x6b, 0xde, 0x95, 0x6b, 0xed, 0x00, 0xaf, 0x44, 0x96, 0x5b, 0x7f, 0x4b, 0x41, 0x21, 0x14, 0xe3,
- 0x0b, 0x90, 0x75, 0x88, 0x7f, 0xca, 0xdd, 0xe5, 0x0e, 0xd2, 0x28, 0xa5, 0xf0, 0x31, 0x93, 0x7b,
- 0x0e, 0xb1, 0x38, 0x05, 0x02, 0x39, 0x1b, 0xb3, 0x75, 0x35, 0x29, 0xd1, 0xf9, 0xf5, 0xc1, 0x1e,
- 0x8f, 0xa9, 0xe5, 0x7b, 0xe1, 0xba, 0x06, 0xf2, 0x46, 0x20, 0xc6, 0xd7, 0x61, 0xdd, 0x77, 0x89,
- 0x61, 0x26, 0xb0, 0x59, 0x8e, 0x45, 0xa1, 0x22, 0x02, 0xef, 0xc3, 0xa5, 0xd0, 0xaf, 0x4e, 0x7d,
- 0xa2, 0x9d, 0x52, 0x7d, 0x6a, 0x94, 0xe7, 0x2f, 0x90, 0x17, 0x03, 0x40, 0x33, 0xd0, 0x87, 0xb6,
- 0xd5, 0xef, 0x53, 0xb0, 0x1e, 0x5e, 0x78, 0xf4, 0x28, 0x59, 0x47, 0x00, 0xc4, 0xb2, 0x6c, 0x3f,
- 0x9e, 0xae, 0x79, 0x2a, 0xcf, 0xd9, 0xd5, 0xea, 0x91, 0x91, 0x12, 0x73, 0xb0, 0x35, 0x06, 0x98,
- 0x6a, 0x96, 0xa6, 0xed, 0x32, 0x94, 0x82, 0x5f, 0x00, 0xf8, 0xcf, 0x48, 0xe2, 0x8a, 0x0c, 0x42,
- 0xc4, 0x6e, 0x46, 0x78, 0x13, 0x72, 0x27, 0x74, 0x64, 0x58, 0xc1, 0xbb, 0xa4, 0x18, 0x84, 0xaf,
- 0x9d, 0xd9, 0xe8, 0xb5, 0xf3, 0xe0, 0x77, 0x29, 0xd8, 0xd0, 0xec, 0xf1, 0x6c, 0xbc, 0x07, 0x68,
- 0xe6, 0x9e, 0xee, 0x7d, 0x91, 0xfa, 0xea, 0xfe, 0xc8, 0xf0, 0x4f, 0x27, 0x27, 0x35, 0xcd, 0x1e,
- 0xef, 0x8e, 0x6c, 0x93, 0x58, 0xa3, 0xe9, 0xef, 0x60, 0xfc, 0x1f, 0xed, 0xc6, 0x88, 0x5a, 0x37,
- 0x46, 0x76, 0xec, 0x57, 0xb1, 0x7b, 0xd3, 0x7f, 0xbf, 0x4d, 0x67, 0x0e, 0x7b, 0x07, 0x7f, 0x4e,
- 0x6f, 0x1d, 0x8a, 0x6f, 0xf5, 0xc2, 0xdc, 0x28, 0x74, 0x68, 0x52, 0x8d, 0xcd, 0xf7, 0x7f, 0x01,
- 0x00, 0x00, 0xff, 0xff, 0x8e, 0x54, 0xe7, 0xef, 0x60, 0x1b, 0x00, 0x00,
+ // 2490 bytes of a gzipped FileDescriptorProto
+ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x59, 0xdd, 0x8e, 0xdb, 0xc6,
+ 0x15, 0x8e, 0x7e, 0x57, 0x3a, 0xd2, 0x6a, 0x67, 0x67, 0x37, 0x36, 0xbd, 0xf9, 0xf1, 0x5a, 0xf9,
+ 0xf1, 0x3a, 0x69, 0xb4, 0xc1, 0xc6, 0x76, 0x9c, 0x4d, 0xe1, 0x42, 0x2b, 0xd1, 0x1b, 0xb9, 0x5a,
+ 0x49, 0xa5, 0xb4, 0x8d, 0x9d, 0x1b, 0x62, 0x96, 0x1c, 0x49, 0xb4, 0x29, 0x92, 0x21, 0x29, 0xdb,
+ 0x9b, 0x2b, 0x03, 0xbd, 0x2a, 0xd0, 0x07, 0x28, 0x8a, 0xa2, 0x17, 0xb9, 0x09, 0xd0, 0x07, 0x28,
+ 0xd0, 0xbb, 0x3e, 0x41, 0x81, 0xbc, 0x41, 0x51, 0x14, 0x68, 0xdf, 0xa0, 0xb7, 0xc5, 0xcc, 0x90,
+ 0x14, 0xa9, 0x1f, 0x7b, 0x1b, 0xc0, 0xc9, 0x95, 0x34, 0xdf, 0xf9, 0xce, 0x99, 0x33, 0x67, 0xce,
+ 0xcc, 0x9c, 0x19, 0xc2, 0xee, 0xc8, 0xb6, 0x47, 0x26, 0xdd, 0x77, 0x5c, 0xdb, 0xb7, 0xcf, 0xa6,
+ 0xc3, 0x7d, 0x9d, 0x7a, 0x9a, 0x6b, 0x38, 0xbe, 0xed, 0xd6, 0x38, 0x86, 0x37, 0x04, 0xa3, 0x16,
+ 0x32, 0xaa, 0x27, 0xb0, 0x79, 0xcf, 0x30, 0x69, 0x33, 0x22, 0xf6, 0xa9, 0x8f, 0xef, 0x40, 0x76,
+ 0x68, 0x98, 0x54, 0x4a, 0xed, 0x66, 0xf6, 0x4a, 0x07, 0xef, 0xd6, 0xe6, 0x94, 0x6a, 0x49, 0x8d,
+ 0x1e, 0x83, 0x15, 0xae, 0x51, 0xfd, 0x57, 0x16, 0xb6, 0x96, 0x48, 0x31, 0x86, 0xac, 0x45, 0x26,
+ 0xcc, 0x62, 0x6a, 0xaf, 0xa8, 0xf0, 0xff, 0x58, 0x82, 0x35, 0x87, 0x68, 0x8f, 0xc9, 0x88, 0x4a,
+ 0x69, 0x0e, 0x87, 0x4d, 0xfc, 0x36, 0x80, 0x4e, 0x1d, 0x6a, 0xe9, 0xd4, 0xd2, 0xce, 0xa5, 0xcc,
+ 0x6e, 0x66, 0xaf, 0xa8, 0xc4, 0x10, 0xfc, 0x21, 0x6c, 0x3a, 0xd3, 0x33, 0xd3, 0xd0, 0xd4, 0x18,
+ 0x0d, 0x76, 0x33, 0x7b, 0x39, 0x05, 0x09, 0x41, 0x73, 0x46, 0xbe, 0x0e, 0x1b, 0x4f, 0x29, 0x79,
+ 0x1c, 0xa7, 0x96, 0x38, 0xb5, 0xc2, 0xe0, 0x18, 0xb1, 0x01, 0xe5, 0x09, 0xf5, 0x3c, 0x32, 0xa2,
+ 0xaa, 0x7f, 0xee, 0x50, 0x29, 0xcb, 0x47, 0xbf, 0xbb, 0x30, 0xfa, 0xf9, 0x91, 0x97, 0x02, 0xad,
+ 0xc1, 0xb9, 0x43, 0x71, 0x1d, 0x8a, 0xd4, 0x9a, 0x4e, 0x84, 0x85, 0xdc, 0x8a, 0xf8, 0xc9, 0xd6,
+ 0x74, 0x32, 0x6f, 0xa5, 0xc0, 0xd4, 0x02, 0x13, 0x6b, 0x1e, 0x75, 0x9f, 0x18, 0x1a, 0x95, 0xf2,
+ 0xdc, 0xc0, 0xf5, 0x05, 0x03, 0x7d, 0x21, 0x9f, 0xb7, 0x11, 0xea, 0xe1, 0x06, 0x14, 0xe9, 0x33,
+ 0x9f, 0x5a, 0x9e, 0x61, 0x5b, 0xd2, 0x1a, 0x37, 0xf2, 0xde, 0x92, 0x59, 0xa4, 0xa6, 0x3e, 0x6f,
+ 0x62, 0xa6, 0x87, 0x6f, 0xc3, 0x9a, 0xed, 0xf8, 0x86, 0x6d, 0x79, 0x52, 0x61, 0x37, 0xb5, 0x57,
+ 0x3a, 0x78, 0x73, 0x69, 0x22, 0x74, 0x05, 0x47, 0x09, 0xc9, 0xb8, 0x05, 0xc8, 0xb3, 0xa7, 0xae,
+ 0x46, 0x55, 0xcd, 0xd6, 0xa9, 0x6a, 0x58, 0x43, 0x5b, 0x2a, 0x72, 0x03, 0x57, 0x17, 0x07, 0xc2,
+ 0x89, 0x0d, 0x5b, 0xa7, 0x2d, 0x6b, 0x68, 0x2b, 0x15, 0x2f, 0xd1, 0xc6, 0x97, 0x20, 0xef, 0x9d,
+ 0x5b, 0x3e, 0x79, 0x26, 0x95, 0x79, 0x86, 0x04, 0xad, 0xea, 0x7f, 0x73, 0xb0, 0x71, 0x91, 0x14,
+ 0xfb, 0x1c, 0x72, 0x43, 0x36, 0x4a, 0x29, 0xfd, 0xff, 0xc4, 0x40, 0xe8, 0x24, 0x83, 0x98, 0xff,
+ 0x81, 0x41, 0xac, 0x43, 0xc9, 0xa2, 0x9e, 0x4f, 0x75, 0x91, 0x11, 0x99, 0x0b, 0xe6, 0x14, 0x08,
+ 0xa5, 0xc5, 0x94, 0xca, 0xfe, 0xa0, 0x94, 0x7a, 0x00, 0x1b, 0x91, 0x4b, 0xaa, 0x4b, 0xac, 0x51,
+ 0x98, 0x9b, 0xfb, 0x2f, 0xf3, 0xa4, 0x26, 0x87, 0x7a, 0x0a, 0x53, 0x53, 0x2a, 0x34, 0xd1, 0xc6,
+ 0x4d, 0x00, 0xdb, 0xa2, 0xf6, 0x50, 0xd5, 0xa9, 0x66, 0x4a, 0x85, 0x15, 0x51, 0xea, 0x32, 0xca,
+ 0x42, 0x94, 0x6c, 0x81, 0x6a, 0x26, 0xfe, 0x6c, 0x96, 0x6a, 0x6b, 0x2b, 0x32, 0xe5, 0x44, 0x2c,
+ 0xb2, 0x85, 0x6c, 0x3b, 0x85, 0x8a, 0x4b, 0x59, 0xde, 0x53, 0x3d, 0x18, 0x59, 0x91, 0x3b, 0x51,
+ 0x7b, 0xe9, 0xc8, 0x94, 0x40, 0x4d, 0x0c, 0x6c, 0xdd, 0x8d, 0x37, 0xf1, 0x3b, 0x10, 0x01, 0x2a,
+ 0x4f, 0x2b, 0xe0, 0xbb, 0x50, 0x39, 0x04, 0x3b, 0x64, 0x42, 0x77, 0xee, 0x40, 0x25, 0x19, 0x1e,
+ 0xbc, 0x0d, 0x39, 0xcf, 0x27, 0xae, 0xcf, 0xb3, 0x30, 0xa7, 0x88, 0x06, 0x46, 0x90, 0xa1, 0x96,
+ 0xce, 0x77, 0xb9, 0x9c, 0xc2, 0xfe, 0xee, 0x7c, 0x0a, 0xeb, 0x89, 0xee, 0x2f, 0xaa, 0x58, 0xfd,
+ 0x7d, 0x1e, 0xb6, 0x97, 0xe5, 0xdc, 0xd2, 0xf4, 0xbf, 0x04, 0x79, 0x6b, 0x3a, 0x39, 0xa3, 0xae,
+ 0x94, 0xe1, 0x16, 0x82, 0x16, 0xae, 0x43, 0xce, 0x24, 0x67, 0xd4, 0x94, 0xb2, 0xbb, 0xa9, 0xbd,
+ 0xca, 0xc1, 0x87, 0x17, 0xca, 0xea, 0x5a, 0x9b, 0xa9, 0x28, 0x42, 0x13, 0xdf, 0x85, 0x6c, 0xb0,
+ 0xc5, 0x31, 0x0b, 0x1f, 0x5c, 0xcc, 0x02, 0xcb, 0x45, 0x85, 0xeb, 0xe1, 0x37, 0xa0, 0xc8, 0x7e,
+ 0x45, 0x6c, 0xf3, 0xdc, 0xe7, 0x02, 0x03, 0x58, 0x5c, 0xf1, 0x0e, 0x14, 0x78, 0x9a, 0xe9, 0x34,
+ 0x3c, 0x1a, 0xa2, 0x36, 0x9b, 0x18, 0x9d, 0x0e, 0xc9, 0xd4, 0xf4, 0xd5, 0x27, 0xc4, 0x9c, 0x52,
+ 0x9e, 0x30, 0x45, 0xa5, 0x1c, 0x80, 0xbf, 0x66, 0x18, 0xbe, 0x0a, 0x25, 0x91, 0x95, 0x86, 0xa5,
+ 0xd3, 0x67, 0x7c, 0xf7, 0xc9, 0x29, 0x22, 0x51, 0x5b, 0x0c, 0x61, 0xdd, 0x3f, 0xf2, 0x6c, 0x2b,
+ 0x9c, 0x5a, 0xde, 0x05, 0x03, 0x78, 0xf7, 0x9f, 0xce, 0x6f, 0x7c, 0x6f, 0x2d, 0x1f, 0xde, 0x7c,
+ 0x2e, 0x56, 0xff, 0x92, 0x86, 0x2c, 0x5f, 0x6f, 0x1b, 0x50, 0x1a, 0x3c, 0xec, 0xc9, 0x6a, 0xb3,
+ 0x7b, 0x7a, 0xd4, 0x96, 0x51, 0x0a, 0x57, 0x00, 0x38, 0x70, 0xaf, 0xdd, 0xad, 0x0f, 0x50, 0x3a,
+ 0x6a, 0xb7, 0x3a, 0x83, 0xdb, 0x37, 0x51, 0x26, 0x52, 0x38, 0x15, 0x40, 0x36, 0x4e, 0xf8, 0xe4,
+ 0x00, 0xe5, 0x30, 0x82, 0xb2, 0x30, 0xd0, 0x7a, 0x20, 0x37, 0x6f, 0xdf, 0x44, 0xf9, 0x24, 0xf2,
+ 0xc9, 0x01, 0x5a, 0xc3, 0xeb, 0x50, 0xe4, 0xc8, 0x51, 0xb7, 0xdb, 0x46, 0x85, 0xc8, 0x66, 0x7f,
+ 0xa0, 0xb4, 0x3a, 0xc7, 0xa8, 0x18, 0xd9, 0x3c, 0x56, 0xba, 0xa7, 0x3d, 0x04, 0x91, 0x85, 0x13,
+ 0xb9, 0xdf, 0xaf, 0x1f, 0xcb, 0xa8, 0x14, 0x31, 0x8e, 0x1e, 0x0e, 0xe4, 0x3e, 0x2a, 0x27, 0xdc,
+ 0xfa, 0xe4, 0x00, 0xad, 0x47, 0x5d, 0xc8, 0x9d, 0xd3, 0x13, 0x54, 0xc1, 0x9b, 0xb0, 0x2e, 0xba,
+ 0x08, 0x9d, 0xd8, 0x98, 0x83, 0x6e, 0xdf, 0x44, 0x68, 0xe6, 0x88, 0xb0, 0xb2, 0x99, 0x00, 0x6e,
+ 0xdf, 0x44, 0xb8, 0xda, 0x80, 0x1c, 0xcf, 0x2e, 0x8c, 0xa1, 0xd2, 0xae, 0x1f, 0xc9, 0x6d, 0xb5,
+ 0xdb, 0x1b, 0xb4, 0xba, 0x9d, 0x7a, 0x1b, 0xa5, 0x66, 0x98, 0x22, 0xff, 0xea, 0xb4, 0xa5, 0xc8,
+ 0x4d, 0x94, 0x8e, 0x63, 0x3d, 0xb9, 0x3e, 0x90, 0x9b, 0x28, 0x53, 0xd5, 0x60, 0x7b, 0xd9, 0x3e,
+ 0xb3, 0x74, 0x65, 0xc4, 0xa6, 0x38, 0xbd, 0x62, 0x8a, 0xb9, 0xad, 0x85, 0x29, 0xfe, 0x36, 0x05,
+ 0x5b, 0x4b, 0xf6, 0xda, 0xa5, 0x9d, 0xfc, 0x02, 0x72, 0x22, 0x45, 0xc5, 0xe9, 0x73, 0x63, 0xe9,
+ 0xa6, 0xcd, 0x13, 0x76, 0xe1, 0x04, 0xe2, 0x7a, 0xf1, 0x13, 0x38, 0xb3, 0xe2, 0x04, 0x66, 0x26,
+ 0x16, 0x9c, 0xfc, 0x4d, 0x0a, 0xa4, 0x55, 0xb6, 0x5f, 0xb2, 0x51, 0xa4, 0x13, 0x1b, 0xc5, 0xe7,
+ 0xf3, 0x0e, 0x5c, 0x5b, 0x3d, 0x86, 0x05, 0x2f, 0xbe, 0x4b, 0xc1, 0xa5, 0xe5, 0x85, 0xca, 0x52,
+ 0x1f, 0xee, 0x42, 0x7e, 0x42, 0xfd, 0xb1, 0x1d, 0x1e, 0xd6, 0xef, 0x2f, 0x39, 0x02, 0x98, 0x78,
+ 0x3e, 0x56, 0x81, 0x56, 0xfc, 0x0c, 0xc9, 0xac, 0xaa, 0x36, 0x84, 0x37, 0x0b, 0x9e, 0xfe, 0x36,
+ 0x0d, 0xaf, 0x2f, 0x35, 0xbe, 0xd4, 0xd1, 0xb7, 0x00, 0x0c, 0xcb, 0x99, 0xfa, 0xe2, 0x40, 0x16,
+ 0xfb, 0x53, 0x91, 0x23, 0x7c, 0xed, 0xb3, 0xbd, 0x67, 0xea, 0x47, 0xf2, 0x0c, 0x97, 0x83, 0x80,
+ 0x38, 0xe1, 0xce, 0xcc, 0xd1, 0x2c, 0x77, 0xf4, 0xed, 0x15, 0x23, 0x5d, 0x38, 0xeb, 0x3e, 0x06,
+ 0xa4, 0x99, 0x06, 0xb5, 0x7c, 0xd5, 0xf3, 0x5d, 0x4a, 0x26, 0x86, 0x35, 0xe2, 0x1b, 0x70, 0xe1,
+ 0x30, 0x37, 0x24, 0xa6, 0x47, 0x95, 0x0d, 0x21, 0xee, 0x87, 0x52, 0xa6, 0xc1, 0x4f, 0x19, 0x37,
+ 0xa6, 0x91, 0x4f, 0x68, 0x08, 0x71, 0xa4, 0x51, 0xfd, 0x6b, 0x01, 0x4a, 0xb1, 0xb2, 0x0e, 0x5f,
+ 0x83, 0xf2, 0x23, 0xf2, 0x84, 0xa8, 0x61, 0xa9, 0x2e, 0x22, 0x51, 0x62, 0x58, 0x2f, 0x28, 0xd7,
+ 0x3f, 0x86, 0x6d, 0x4e, 0xb1, 0xa7, 0x3e, 0x75, 0x55, 0xcd, 0x24, 0x9e, 0xc7, 0x83, 0x56, 0xe0,
+ 0x54, 0xcc, 0x64, 0x5d, 0x26, 0x6a, 0x84, 0x12, 0x7c, 0x0b, 0xb6, 0xb8, 0xc6, 0x64, 0x6a, 0xfa,
+ 0x86, 0x63, 0x52, 0x95, 0x5d, 0x1e, 0x3c, 0xbe, 0x11, 0x47, 0x9e, 0x6d, 0x32, 0xc6, 0x49, 0x40,
+ 0x60, 0x1e, 0x79, 0xb8, 0x09, 0x6f, 0x71, 0xb5, 0x11, 0xb5, 0xa8, 0x4b, 0x7c, 0xaa, 0xd2, 0xaf,
+ 0xa7, 0xc4, 0xf4, 0x54, 0x62, 0xe9, 0xea, 0x98, 0x78, 0x63, 0x69, 0x9b, 0x19, 0x38, 0x4a, 0x4b,
+ 0x29, 0xe5, 0x0a, 0x23, 0x1e, 0x07, 0x3c, 0x99, 0xd3, 0xea, 0x96, 0xfe, 0x05, 0xf1, 0xc6, 0xf8,
+ 0x10, 0x2e, 0x71, 0x2b, 0x9e, 0xef, 0x1a, 0xd6, 0x48, 0xd5, 0xc6, 0x54, 0x7b, 0xac, 0x4e, 0xfd,
+ 0xe1, 0x1d, 0xe9, 0x8d, 0x78, 0xff, 0xdc, 0xc3, 0x3e, 0xe7, 0x34, 0x18, 0xe5, 0xd4, 0x1f, 0xde,
+ 0xc1, 0x7d, 0x28, 0xb3, 0xc9, 0x98, 0x18, 0xdf, 0x50, 0x75, 0x68, 0xbb, 0xfc, 0x64, 0xa9, 0x2c,
+ 0x59, 0xd9, 0xb1, 0x08, 0xd6, 0xba, 0x81, 0xc2, 0x89, 0xad, 0xd3, 0xc3, 0x5c, 0xbf, 0x27, 0xcb,
+ 0x4d, 0xa5, 0x14, 0x5a, 0xb9, 0x67, 0xbb, 0x2c, 0xa1, 0x46, 0x76, 0x14, 0xe0, 0x92, 0x48, 0xa8,
+ 0x91, 0x1d, 0x86, 0xf7, 0x16, 0x6c, 0x69, 0x9a, 0x18, 0xb3, 0xa1, 0xa9, 0x41, 0x89, 0xef, 0x49,
+ 0x28, 0x11, 0x2c, 0x4d, 0x3b, 0x16, 0x84, 0x20, 0xc7, 0x3d, 0xfc, 0x19, 0xbc, 0x3e, 0x0b, 0x56,
+ 0x5c, 0x71, 0x73, 0x61, 0x94, 0xf3, 0xaa, 0xb7, 0x60, 0xcb, 0x39, 0x5f, 0x54, 0xc4, 0x89, 0x1e,
+ 0x9d, 0xf3, 0x79, 0xb5, 0x4f, 0x61, 0xdb, 0x19, 0x3b, 0x8b, 0x7a, 0x5b, 0x71, 0x3d, 0xec, 0x8c,
+ 0x9d, 0x79, 0xc5, 0xf7, 0xf8, 0x7d, 0xcf, 0xa5, 0x1a, 0xf1, 0xa9, 0x2e, 0x5d, 0x8e, 0xd3, 0x63,
+ 0x02, 0xbc, 0x0f, 0x48, 0xd3, 0x54, 0x6a, 0x91, 0x33, 0x93, 0xaa, 0xc4, 0xa5, 0x16, 0xf1, 0xa4,
+ 0xab, 0x71, 0x72, 0x45, 0xd3, 0x64, 0x2e, 0xad, 0x73, 0x21, 0xfe, 0x00, 0x36, 0xed, 0xb3, 0x47,
+ 0x9a, 0x48, 0x49, 0xd5, 0x71, 0xe9, 0xd0, 0x78, 0x26, 0xbd, 0xcb, 0xe3, 0xbb, 0xc1, 0x04, 0x3c,
+ 0x21, 0x7b, 0x1c, 0xc6, 0x37, 0x00, 0x69, 0xde, 0x98, 0xb8, 0x0e, 0xaf, 0x09, 0x3c, 0x87, 0x68,
+ 0x54, 0x7a, 0x4f, 0x50, 0x05, 0xde, 0x09, 0x61, 0xb6, 0x24, 0xbc, 0xa7, 0xc6, 0xd0, 0x0f, 0x2d,
+ 0x5e, 0x17, 0x4b, 0x82, 0x63, 0x81, 0xb5, 0x3d, 0x40, 0x2c, 0x14, 0x89, 0x8e, 0xf7, 0x38, 0xad,
+ 0xe2, 0x8c, 0x9d, 0x78, 0xbf, 0xef, 0xc0, 0x3a, 0x63, 0xce, 0x3a, 0xbd, 0x21, 0xea, 0x19, 0x67,
+ 0x1c, 0xeb, 0xf1, 0x01, 0x6c, 0x4f, 0x2d, 0xc3, 0xf2, 0xa9, 0xeb, 0xb8, 0x94, 0x5d, 0x26, 0xc4,
+ 0x8e, 0x20, 0xfd, 0x7b, 0x6d, 0xc5, 0x75, 0xe0, 0x34, 0xce, 0x16, 0x89, 0xa8, 0x6c, 0x4d, 0x17,
+ 0xc1, 0xea, 0x21, 0x94, 0xe3, 0xf9, 0x89, 0x8b, 0x20, 0x32, 0x14, 0xa5, 0xd8, 0x59, 0xdf, 0xe8,
+ 0x36, 0xd9, 0x29, 0xfd, 0x95, 0x8c, 0xd2, 0xac, 0x5a, 0x68, 0xb7, 0x06, 0xb2, 0xaa, 0x9c, 0x76,
+ 0x06, 0xad, 0x13, 0x19, 0x65, 0x3e, 0x28, 0x16, 0xfe, 0xb3, 0x86, 0x9e, 0x3f, 0x7f, 0xfe, 0x3c,
+ 0x7d, 0x3f, 0x5b, 0x78, 0x1f, 0x5d, 0xaf, 0x7e, 0x9f, 0x86, 0x4a, 0xb2, 0x4e, 0xc7, 0x3f, 0x87,
+ 0xcb, 0xe1, 0xa5, 0xda, 0xa3, 0xbe, 0xfa, 0xd4, 0x70, 0xf9, 0xc2, 0x99, 0x10, 0x51, 0xe9, 0x46,
+ 0x53, 0xb7, 0x1d, 0xb0, 0xfa, 0xd4, 0xff, 0xd2, 0x70, 0xd9, 0xb2, 0x98, 0x10, 0x1f, 0xb7, 0xe1,
+ 0xaa, 0x65, 0xab, 0x9e, 0x4f, 0x2c, 0x9d, 0xb8, 0xba, 0x3a, 0x7b, 0xce, 0x50, 0x89, 0xa6, 0x51,
+ 0xcf, 0xb3, 0xc5, 0x81, 0x15, 0x59, 0x79, 0xd3, 0xb2, 0xfb, 0x01, 0x79, 0xb6, 0x93, 0xd7, 0x03,
+ 0xea, 0x5c, 0x9a, 0x65, 0x56, 0xa5, 0xd9, 0x1b, 0x50, 0x9c, 0x10, 0x47, 0xa5, 0x96, 0xef, 0x9e,
+ 0xf3, 0xea, 0xb2, 0xa0, 0x14, 0x26, 0xc4, 0x91, 0x59, 0xfb, 0xd5, 0xcd, 0x44, 0x32, 0x9a, 0x05,
+ 0x54, 0xbc, 0x9f, 0x2d, 0x14, 0x11, 0x54, 0xff, 0x99, 0x81, 0x72, 0xbc, 0xda, 0x64, 0xc5, 0xbb,
+ 0xc6, 0x4f, 0x96, 0x14, 0xdf, 0x7b, 0xde, 0x79, 0x61, 0x6d, 0x5a, 0x6b, 0xb0, 0x23, 0xe7, 0x30,
+ 0x2f, 0x6a, 0x40, 0x45, 0x68, 0xb2, 0xe3, 0x9e, 0xed, 0x36, 0x54, 0xdc, 0x2c, 0x0a, 0x4a, 0xd0,
+ 0xc2, 0xc7, 0x90, 0x7f, 0xe4, 0x71, 0xdb, 0x79, 0x6e, 0xfb, 0xdd, 0x17, 0xdb, 0xbe, 0xdf, 0xe7,
+ 0xc6, 0x8b, 0xf7, 0xfb, 0x6a, 0xa7, 0xab, 0x9c, 0xd4, 0xdb, 0x4a, 0xa0, 0x8e, 0xaf, 0x40, 0xd6,
+ 0x24, 0xdf, 0x9c, 0x27, 0x0f, 0x27, 0x0e, 0x5d, 0x74, 0x12, 0xae, 0x40, 0xf6, 0x29, 0x25, 0x8f,
+ 0x93, 0x47, 0x02, 0x87, 0x5e, 0xe1, 0x62, 0xd8, 0x87, 0x1c, 0x8f, 0x17, 0x06, 0x08, 0x22, 0x86,
+ 0x5e, 0xc3, 0x05, 0xc8, 0x36, 0xba, 0x0a, 0x5b, 0x10, 0x08, 0xca, 0x02, 0x55, 0x7b, 0x2d, 0xb9,
+ 0x21, 0xa3, 0x74, 0xf5, 0x16, 0xe4, 0x45, 0x10, 0xd8, 0x62, 0x89, 0xc2, 0x80, 0x5e, 0x0b, 0x9a,
+ 0x81, 0x8d, 0x54, 0x28, 0x3d, 0x3d, 0x39, 0x92, 0x15, 0x94, 0x4e, 0x4e, 0x75, 0x16, 0xe5, 0xaa,
+ 0x1e, 0x94, 0xe3, 0xe5, 0xe6, 0x8f, 0x92, 0x65, 0xd5, 0xbf, 0xa5, 0xa0, 0x14, 0x2b, 0x1f, 0x59,
+ 0xe1, 0x42, 0x4c, 0xd3, 0x7e, 0xaa, 0x12, 0xd3, 0x20, 0x5e, 0x90, 0x1a, 0xc0, 0xa1, 0x3a, 0x43,
+ 0x2e, 0x3a, 0x75, 0x3f, 0xd2, 0x12, 0xc9, 0xa1, 0x7c, 0xf5, 0x4f, 0x29, 0x40, 0xf3, 0x05, 0xe8,
+ 0x9c, 0x9b, 0xa9, 0x9f, 0xd2, 0xcd, 0xea, 0x1f, 0x53, 0x50, 0x49, 0x56, 0x9d, 0x73, 0xee, 0x5d,
+ 0xfb, 0x49, 0xdd, 0xfb, 0x47, 0x1a, 0xd6, 0x13, 0xb5, 0xe6, 0x45, 0xbd, 0xfb, 0x1a, 0x36, 0x0d,
+ 0x9d, 0x4e, 0x1c, 0xdb, 0xa7, 0x96, 0x76, 0xae, 0x9a, 0xf4, 0x09, 0x35, 0xa5, 0x2a, 0xdf, 0x34,
+ 0xf6, 0x5f, 0x5c, 0xcd, 0xd6, 0x5a, 0x33, 0xbd, 0x36, 0x53, 0x3b, 0xdc, 0x6a, 0x35, 0xe5, 0x93,
+ 0x5e, 0x77, 0x20, 0x77, 0x1a, 0x0f, 0xd5, 0xd3, 0xce, 0x2f, 0x3b, 0xdd, 0x2f, 0x3b, 0x0a, 0x32,
+ 0xe6, 0x68, 0xaf, 0x70, 0xd9, 0xf7, 0x00, 0xcd, 0x3b, 0x85, 0x2f, 0xc3, 0x32, 0xb7, 0xd0, 0x6b,
+ 0x78, 0x0b, 0x36, 0x3a, 0x5d, 0xb5, 0xdf, 0x6a, 0xca, 0xaa, 0x7c, 0xef, 0x9e, 0xdc, 0x18, 0xf4,
+ 0xc5, 0xf5, 0x3e, 0x62, 0x0f, 0x12, 0x0b, 0xbc, 0xfa, 0x87, 0x0c, 0x6c, 0x2d, 0xf1, 0x04, 0xd7,
+ 0x83, 0x9b, 0x85, 0xb8, 0xec, 0x7c, 0x74, 0x11, 0xef, 0x6b, 0xac, 0x20, 0xe8, 0x11, 0xd7, 0x0f,
+ 0x2e, 0x22, 0x37, 0x80, 0x45, 0xc9, 0xf2, 0x8d, 0xa1, 0x41, 0xdd, 0xe0, 0x35, 0x44, 0x5c, 0x37,
+ 0x36, 0x66, 0xb8, 0x78, 0x10, 0xf9, 0x19, 0x60, 0xc7, 0xf6, 0x0c, 0xdf, 0x78, 0x42, 0x55, 0xc3,
+ 0x0a, 0x9f, 0x4e, 0xd8, 0xf5, 0x23, 0xab, 0xa0, 0x50, 0xd2, 0xb2, 0xfc, 0x88, 0x6d, 0xd1, 0x11,
+ 0x99, 0x63, 0xb3, 0xcd, 0x3c, 0xa3, 0xa0, 0x50, 0x12, 0xb1, 0xaf, 0x41, 0x59, 0xb7, 0xa7, 0xac,
+ 0x26, 0x13, 0x3c, 0x76, 0x76, 0xa4, 0x94, 0x92, 0xc0, 0x22, 0x4a, 0x50, 0x6d, 0xcf, 0xde, 0x6c,
+ 0xca, 0x4a, 0x49, 0x60, 0x82, 0x72, 0x1d, 0x36, 0xc8, 0x68, 0xe4, 0x32, 0xe3, 0xa1, 0x21, 0x71,
+ 0x7f, 0xa8, 0x44, 0x30, 0x27, 0xee, 0xdc, 0x87, 0x42, 0x18, 0x07, 0x76, 0x54, 0xb3, 0x48, 0xa8,
+ 0x8e, 0x78, 0x39, 0x4b, 0xef, 0x15, 0x95, 0x82, 0x15, 0x0a, 0xaf, 0x41, 0xd9, 0xf0, 0xd4, 0xd9,
+ 0x13, 0x6e, 0x7a, 0x37, 0xbd, 0x57, 0x50, 0x4a, 0x86, 0x17, 0xbd, 0xd9, 0x55, 0xbf, 0x4b, 0x43,
+ 0x25, 0xf9, 0x04, 0x8d, 0x9b, 0x50, 0x30, 0x6d, 0x8d, 0xf0, 0xd4, 0x12, 0xdf, 0x3f, 0xf6, 0x5e,
+ 0xf2, 0x6a, 0x5d, 0x6b, 0x07, 0x7c, 0x25, 0xd2, 0xdc, 0xf9, 0x7b, 0x0a, 0x0a, 0x21, 0x8c, 0x2f,
+ 0x41, 0xd6, 0x21, 0xfe, 0x98, 0x9b, 0xcb, 0x1d, 0xa5, 0x51, 0x4a, 0xe1, 0x6d, 0x86, 0x7b, 0x0e,
+ 0xb1, 0x78, 0x0a, 0x04, 0x38, 0x6b, 0xb3, 0x79, 0x35, 0x29, 0xd1, 0xf9, 0xe5, 0xc4, 0x9e, 0x4c,
+ 0xa8, 0xe5, 0x7b, 0xe1, 0xbc, 0x06, 0x78, 0x23, 0x80, 0xf1, 0x87, 0xb0, 0xe9, 0xbb, 0xc4, 0x30,
+ 0x13, 0xdc, 0x2c, 0xe7, 0xa2, 0x50, 0x10, 0x91, 0x0f, 0xe1, 0x4a, 0x68, 0x57, 0xa7, 0x3e, 0xd1,
+ 0xc6, 0x54, 0x9f, 0x29, 0xe5, 0xf9, 0xfb, 0xe6, 0xe5, 0x80, 0xd0, 0x0c, 0xe4, 0xa1, 0x6e, 0xf5,
+ 0xfb, 0x14, 0x6c, 0x86, 0xd7, 0x29, 0x3d, 0x0a, 0xd6, 0x09, 0x00, 0xb1, 0x2c, 0xdb, 0x8f, 0x87,
+ 0x6b, 0x31, 0x95, 0x17, 0xf4, 0x6a, 0xf5, 0x48, 0x49, 0x89, 0x19, 0xd8, 0x99, 0x00, 0xcc, 0x24,
+ 0x2b, 0xc3, 0x76, 0x15, 0x4a, 0xc1, 0xf7, 0x05, 0xfe, 0x91, 0x4a, 0x5c, 0xc0, 0x41, 0x40, 0xec,
+ 0xde, 0x85, 0xb7, 0x21, 0x77, 0x46, 0x47, 0x86, 0x15, 0xbc, 0x7a, 0x8a, 0x46, 0xf8, 0x96, 0x9a,
+ 0x8d, 0xde, 0x52, 0x8f, 0x7e, 0x97, 0x82, 0x2d, 0xcd, 0x9e, 0xcc, 0xfb, 0x7b, 0x84, 0xe6, 0x5e,
+ 0x01, 0xbc, 0x2f, 0x52, 0x5f, 0xdd, 0x1d, 0x19, 0xfe, 0x78, 0x7a, 0x56, 0xd3, 0xec, 0xc9, 0xfe,
+ 0xc8, 0x36, 0x89, 0x35, 0x9a, 0x7d, 0x65, 0xe3, 0x7f, 0xb4, 0x8f, 0x46, 0xd4, 0xfa, 0x68, 0x64,
+ 0xc7, 0xbe, 0xb9, 0x7d, 0x3e, 0xfb, 0xfb, 0x6d, 0x3a, 0x73, 0xdc, 0x3b, 0xfa, 0x73, 0x7a, 0xe7,
+ 0x58, 0xf4, 0xd5, 0x0b, 0x63, 0xa3, 0xd0, 0xa1, 0x49, 0x35, 0x36, 0xde, 0xff, 0x05, 0x00, 0x00,
+ 0xff, 0xff, 0xa2, 0xc3, 0x4e, 0x18, 0xbe, 0x1b, 0x00, 0x00,
}
diff --git a/vendor/github.com/golang/protobuf/protoc-gen-go/descriptor/descriptor.proto b/vendor/github.com/golang/protobuf/protoc-gen-go/descriptor/descriptor.proto
new file mode 100644
index 000000000..70b82a4dc
--- /dev/null
+++ b/vendor/github.com/golang/protobuf/protoc-gen-go/descriptor/descriptor.proto
@@ -0,0 +1,837 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Author: kenton@google.com (Kenton Varda)
+// Based on original Protocol Buffers design by
+// Sanjay Ghemawat, Jeff Dean, and others.
+//
+// The messages in this file describe the definitions found in .proto files.
+// A valid .proto file can be translated directly to a FileDescriptorProto
+// without any other information (e.g. without reading its imports).
+
+
+syntax = "proto2";
+
+package google.protobuf;
+option go_package = "github.com/golang/protobuf/protoc-gen-go/descriptor;descriptor";
+option java_package = "com.google.protobuf";
+option java_outer_classname = "DescriptorProtos";
+option csharp_namespace = "Google.Protobuf.Reflection";
+option objc_class_prefix = "GPB";
+
+// descriptor.proto must be optimized for speed because reflection-based
+// algorithms don't work during bootstrapping.
+option optimize_for = SPEED;
+
+// The protocol compiler can output a FileDescriptorSet containing the .proto
+// files it parses.
+message FileDescriptorSet {
+ repeated FileDescriptorProto file = 1;
+}
+
+// Describes a complete .proto file.
+message FileDescriptorProto {
+ optional string name = 1; // file name, relative to root of source tree
+ optional string package = 2; // e.g. "foo", "foo.bar", etc.
+
+ // Names of files imported by this file.
+ repeated string dependency = 3;
+ // Indexes of the public imported files in the dependency list above.
+ repeated int32 public_dependency = 10;
+ // Indexes of the weak imported files in the dependency list.
+ // For Google-internal migration only. Do not use.
+ repeated int32 weak_dependency = 11;
+
+ // All top-level definitions in this file.
+ repeated DescriptorProto message_type = 4;
+ repeated EnumDescriptorProto enum_type = 5;
+ repeated ServiceDescriptorProto service = 6;
+ repeated FieldDescriptorProto extension = 7;
+
+ optional FileOptions options = 8;
+
+ // This field contains optional information about the original source code.
+ // You may safely remove this entire field without harming runtime
+ // functionality of the descriptors -- the information is needed only by
+ // development tools.
+ optional SourceCodeInfo source_code_info = 9;
+
+ // The syntax of the proto file.
+ // The supported values are "proto2" and "proto3".
+ optional string syntax = 12;
+}
+
+// Describes a message type.
+message DescriptorProto {
+ optional string name = 1;
+
+ repeated FieldDescriptorProto field = 2;
+ repeated FieldDescriptorProto extension = 6;
+
+ repeated DescriptorProto nested_type = 3;
+ repeated EnumDescriptorProto enum_type = 4;
+
+ message ExtensionRange {
+ optional int32 start = 1;
+ optional int32 end = 2;
+ }
+ repeated ExtensionRange extension_range = 5;
+
+ repeated OneofDescriptorProto oneof_decl = 8;
+
+ optional MessageOptions options = 7;
+
+ // Range of reserved tag numbers. Reserved tag numbers may not be used by
+ // fields or extension ranges in the same message. Reserved ranges may
+ // not overlap.
+ message ReservedRange {
+ optional int32 start = 1; // Inclusive.
+ optional int32 end = 2; // Exclusive.
+ }
+ repeated ReservedRange reserved_range = 9;
+ // Reserved field names, which may not be used by fields in the same message.
+ // A given name may only be reserved once.
+ repeated string reserved_name = 10;
+}
+
+// Describes a field within a message.
+message FieldDescriptorProto {
+ enum Type {
+ // 0 is reserved for errors.
+ // Order is weird for historical reasons.
+ TYPE_DOUBLE = 1;
+ TYPE_FLOAT = 2;
+ // Not ZigZag encoded. Negative numbers take 10 bytes. Use TYPE_SINT64 if
+ // negative values are likely.
+ TYPE_INT64 = 3;
+ TYPE_UINT64 = 4;
+ // Not ZigZag encoded. Negative numbers take 10 bytes. Use TYPE_SINT32 if
+ // negative values are likely.
+ TYPE_INT32 = 5;
+ TYPE_FIXED64 = 6;
+ TYPE_FIXED32 = 7;
+ TYPE_BOOL = 8;
+ TYPE_STRING = 9;
+ // Tag-delimited aggregate.
+ // Group type is deprecated and not supported in proto3. However, Proto3
+ // implementations should still be able to parse the group wire format and
+ // treat group fields as unknown fields.
+ TYPE_GROUP = 10;
+ TYPE_MESSAGE = 11; // Length-delimited aggregate.
+
+ // New in version 2.
+ TYPE_BYTES = 12;
+ TYPE_UINT32 = 13;
+ TYPE_ENUM = 14;
+ TYPE_SFIXED32 = 15;
+ TYPE_SFIXED64 = 16;
+ TYPE_SINT32 = 17; // Uses ZigZag encoding.
+ TYPE_SINT64 = 18; // Uses ZigZag encoding.
+ };
+
+ enum Label {
+ // 0 is reserved for errors
+ LABEL_OPTIONAL = 1;
+ LABEL_REQUIRED = 2;
+ LABEL_REPEATED = 3;
+ };
+
+ optional string name = 1;
+ optional int32 number = 3;
+ optional Label label = 4;
+
+ // If type_name is set, this need not be set. If both this and type_name
+ // are set, this must be one of TYPE_ENUM, TYPE_MESSAGE or TYPE_GROUP.
+ optional Type type = 5;
+
+ // For message and enum types, this is the name of the type. If the name
+ // starts with a '.', it is fully-qualified. Otherwise, C++-like scoping
+ // rules are used to find the type (i.e. first the nested types within this
+ // message are searched, then within the parent, on up to the root
+ // namespace).
+ optional string type_name = 6;
+
+ // For extensions, this is the name of the type being extended. It is
+ // resolved in the same manner as type_name.
+ optional string extendee = 2;
+
+ // For numeric types, contains the original text representation of the value.
+ // For booleans, "true" or "false".
+ // For strings, contains the default text contents (not escaped in any way).
+ // For bytes, contains the C escaped value. All bytes >= 128 are escaped.
+ // TODO(kenton): Base-64 encode?
+ optional string default_value = 7;
+
+ // If set, gives the index of a oneof in the containing type's oneof_decl
+ // list. This field is a member of that oneof.
+ optional int32 oneof_index = 9;
+
+ // JSON name of this field. The value is set by protocol compiler. If the
+ // user has set a "json_name" option on this field, that option's value
+ // will be used. Otherwise, it's deduced from the field's name by converting
+ // it to camelCase.
+ optional string json_name = 10;
+
+ optional FieldOptions options = 8;
+}
+
+// Describes a oneof.
+message OneofDescriptorProto {
+ optional string name = 1;
+ optional OneofOptions options = 2;
+}
+
+// Describes an enum type.
+message EnumDescriptorProto {
+ optional string name = 1;
+
+ repeated EnumValueDescriptorProto value = 2;
+
+ optional EnumOptions options = 3;
+}
+
+// Describes a value within an enum.
+message EnumValueDescriptorProto {
+ optional string name = 1;
+ optional int32 number = 2;
+
+ optional EnumValueOptions options = 3;
+}
+
+// Describes a service.
+message ServiceDescriptorProto {
+ optional string name = 1;
+ repeated MethodDescriptorProto method = 2;
+
+ optional ServiceOptions options = 3;
+}
+
+// Describes a method of a service.
+message MethodDescriptorProto {
+ optional string name = 1;
+
+ // Input and output type names. These are resolved in the same way as
+ // FieldDescriptorProto.type_name, but must refer to a message type.
+ optional string input_type = 2;
+ optional string output_type = 3;
+
+ optional MethodOptions options = 4;
+
+ // Identifies if client streams multiple client messages
+ optional bool client_streaming = 5 [default=false];
+ // Identifies if server streams multiple server messages
+ optional bool server_streaming = 6 [default=false];
+}
+
+
+// ===================================================================
+// Options
+
+// Each of the definitions above may have "options" attached. These are
+// just annotations which may cause code to be generated slightly differently
+// or may contain hints for code that manipulates protocol messages.
+//
+// Clients may define custom options as extensions of the *Options messages.
+// These extensions may not yet be known at parsing time, so the parser cannot
+// store the values in them. Instead it stores them in a field in the *Options
+// message called uninterpreted_option. This field must have the same name
+// across all *Options messages. We then use this field to populate the
+// extensions when we build a descriptor, at which point all protos have been
+// parsed and so all extensions are known.
+//
+// Extension numbers for custom options may be chosen as follows:
+// * For options which will only be used within a single application or
+// organization, or for experimental options, use field numbers 50000
+// through 99999. It is up to you to ensure that you do not use the
+// same number for multiple options.
+// * For options which will be published and used publicly by multiple
+// independent entities, e-mail protobuf-global-extension-registry@google.com
+// to reserve extension numbers. Simply provide your project name (e.g.
+// Objective-C plugin) and your project website (if available) -- there's no
+// need to explain how you intend to use them. Usually you only need one
+// extension number. You can declare multiple options with only one extension
+// number by putting them in a sub-message. See the Custom Options section of
+// the docs for examples:
+// https://developers.google.com/protocol-buffers/docs/proto#options
+// If this turns out to be popular, a web service will be set up
+// to automatically assign option numbers.
+
+
+message FileOptions {
+
+ // Sets the Java package where classes generated from this .proto will be
+ // placed. By default, the proto package is used, but this is often
+ // inappropriate because proto packages do not normally start with backwards
+ // domain names.
+ optional string java_package = 1;
+
+
+ // If set, all the classes from the .proto file are wrapped in a single
+ // outer class with the given name. This applies to both Proto1
+ // (equivalent to the old "--one_java_file" option) and Proto2 (where
+ // a .proto always translates to a single class, but you may want to
+ // explicitly choose the class name).
+ optional string java_outer_classname = 8;
+
+ // If set true, then the Java code generator will generate a separate .java
+ // file for each top-level message, enum, and service defined in the .proto
+ // file. Thus, these types will *not* be nested inside the outer class
+ // named by java_outer_classname. However, the outer class will still be
+ // generated to contain the file's getDescriptor() method as well as any
+ // top-level extensions defined in the file.
+ optional bool java_multiple_files = 10 [default=false];
+
+ // This option does nothing.
+ optional bool java_generate_equals_and_hash = 20 [deprecated=true];
+
+ // If set true, then the Java2 code generator will generate code that
+ // throws an exception whenever an attempt is made to assign a non-UTF-8
+ // byte sequence to a string field.
+ // Message reflection will do the same.
+ // However, an extension field still accepts non-UTF-8 byte sequences.
+ // This option has no effect on when used with the lite runtime.
+ optional bool java_string_check_utf8 = 27 [default=false];
+
+
+ // Generated classes can be optimized for speed or code size.
+ enum OptimizeMode {
+ SPEED = 1; // Generate complete code for parsing, serialization,
+ // etc.
+ CODE_SIZE = 2; // Use ReflectionOps to implement these methods.
+ LITE_RUNTIME = 3; // Generate code using MessageLite and the lite runtime.
+ }
+ optional OptimizeMode optimize_for = 9 [default=SPEED];
+
+ // Sets the Go package where structs generated from this .proto will be
+ // placed. If omitted, the Go package will be derived from the following:
+ // - The basename of the package import path, if provided.
+ // - Otherwise, the package statement in the .proto file, if present.
+ // - Otherwise, the basename of the .proto file, without extension.
+ optional string go_package = 11;
+
+
+
+ // Should generic services be generated in each language? "Generic" services
+ // are not specific to any particular RPC system. They are generated by the
+ // main code generators in each language (without additional plugins).
+ // Generic services were the only kind of service generation supported by
+ // early versions of google.protobuf.
+ //
+ // Generic services are now considered deprecated in favor of using plugins
+ // that generate code specific to your particular RPC system. Therefore,
+ // these default to false. Old code which depends on generic services should
+ // explicitly set them to true.
+ optional bool cc_generic_services = 16 [default=false];
+ optional bool java_generic_services = 17 [default=false];
+ optional bool py_generic_services = 18 [default=false];
+ optional bool php_generic_services = 19 [default=false];
+
+ // Is this file deprecated?
+ // Depending on the target platform, this can emit Deprecated annotations
+ // for everything in the file, or it will be completely ignored; in the very
+ // least, this is a formalization for deprecating files.
+ optional bool deprecated = 23 [default=false];
+
+ // Enables the use of arenas for the proto messages in this file. This applies
+ // only to generated classes for C++.
+ optional bool cc_enable_arenas = 31 [default=false];
+
+
+ // Sets the objective c class prefix which is prepended to all objective c
+ // generated classes from this .proto. There is no default.
+ optional string objc_class_prefix = 36;
+
+ // Namespace for generated classes; defaults to the package.
+ optional string csharp_namespace = 37;
+
+ // By default Swift generators will take the proto package and CamelCase it
+ // replacing '.' with underscore and use that to prefix the types/symbols
+ // defined. When this options is provided, they will use this value instead
+ // to prefix the types/symbols defined.
+ optional string swift_prefix = 39;
+
+ // Sets the php class prefix which is prepended to all php generated classes
+ // from this .proto. Default is empty.
+ optional string php_class_prefix = 40;
+
+ // Use this option to change the namespace of php generated classes. Default
+ // is empty. When this option is empty, the package name will be used for
+ // determining the namespace.
+ optional string php_namespace = 41;
+
+ // The parser stores options it doesn't recognize here. See above.
+ repeated UninterpretedOption uninterpreted_option = 999;
+
+ // Clients can define custom options in extensions of this message. See above.
+ extensions 1000 to max;
+
+ reserved 38;
+}
+
+message MessageOptions {
+ // Set true to use the old proto1 MessageSet wire format for extensions.
+ // This is provided for backwards-compatibility with the MessageSet wire
+ // format. You should not use this for any other reason: It's less
+ // efficient, has fewer features, and is more complicated.
+ //
+ // The message must be defined exactly as follows:
+ // message Foo {
+ // option message_set_wire_format = true;
+ // extensions 4 to max;
+ // }
+ // Note that the message cannot have any defined fields; MessageSets only
+ // have extensions.
+ //
+ // All extensions of your type must be singular messages; e.g. they cannot
+ // be int32s, enums, or repeated messages.
+ //
+ // Because this is an option, the above two restrictions are not enforced by
+ // the protocol compiler.
+ optional bool message_set_wire_format = 1 [default=false];
+
+ // Disables the generation of the standard "descriptor()" accessor, which can
+ // conflict with a field of the same name. This is meant to make migration
+ // from proto1 easier; new code should avoid fields named "descriptor".
+ optional bool no_standard_descriptor_accessor = 2 [default=false];
+
+ // Is this message deprecated?
+ // Depending on the target platform, this can emit Deprecated annotations
+ // for the message, or it will be completely ignored; in the very least,
+ // this is a formalization for deprecating messages.
+ optional bool deprecated = 3 [default=false];
+
+ // Whether the message is an automatically generated map entry type for the
+ // maps field.
+ //
+ // For maps fields:
+ // map<KeyType, ValueType> map_field = 1;
+ // The parsed descriptor looks like:
+ // message MapFieldEntry {
+ // option map_entry = true;
+ // optional KeyType key = 1;
+ // optional ValueType value = 2;
+ // }
+ // repeated MapFieldEntry map_field = 1;
+ //
+ // Implementations may choose not to generate the map_entry=true message, but
+ // use a native map in the target language to hold the keys and values.
+ // The reflection APIs in such implementions still need to work as
+ // if the field is a repeated message field.
+ //
+ // NOTE: Do not set the option in .proto files. Always use the maps syntax
+ // instead. The option should only be implicitly set by the proto compiler
+ // parser.
+ optional bool map_entry = 7;
+
+ reserved 8; // javalite_serializable
+ reserved 9; // javanano_as_lite
+
+ // The parser stores options it doesn't recognize here. See above.
+ repeated UninterpretedOption uninterpreted_option = 999;
+
+ // Clients can define custom options in extensions of this message. See above.
+ extensions 1000 to max;
+}
+
+message FieldOptions {
+ // The ctype option instructs the C++ code generator to use a different
+ // representation of the field than it normally would. See the specific
+ // options below. This option is not yet implemented in the open source
+ // release -- sorry, we'll try to include it in a future version!
+ optional CType ctype = 1 [default = STRING];
+ enum CType {
+ // Default mode.
+ STRING = 0;
+
+ CORD = 1;
+
+ STRING_PIECE = 2;
+ }
+ // The packed option can be enabled for repeated primitive fields to enable
+ // a more efficient representation on the wire. Rather than repeatedly
+ // writing the tag and type for each element, the entire array is encoded as
+ // a single length-delimited blob. In proto3, only explicit setting it to
+ // false will avoid using packed encoding.
+ optional bool packed = 2;
+
+ // The jstype option determines the JavaScript type used for values of the
+ // field. The option is permitted only for 64 bit integral and fixed types
+ // (int64, uint64, sint64, fixed64, sfixed64). By default these types are
+ // represented as JavaScript strings. This avoids loss of precision that can
+ // happen when a large value is converted to a floating point JavaScript
+ // numbers. Specifying JS_NUMBER for the jstype causes the generated
+ // JavaScript code to use the JavaScript "number" type instead of strings.
+ // This option is an enum to permit additional types to be added,
+ // e.g. goog.math.Integer.
+ optional JSType jstype = 6 [default = JS_NORMAL];
+ enum JSType {
+ // Use the default type.
+ JS_NORMAL = 0;
+
+ // Use JavaScript strings.
+ JS_STRING = 1;
+
+ // Use JavaScript numbers.
+ JS_NUMBER = 2;
+ }
+
+ // Should this field be parsed lazily? Lazy applies only to message-type
+ // fields. It means that when the outer message is initially parsed, the
+ // inner message's contents will not be parsed but instead stored in encoded
+ // form. The inner message will actually be parsed when it is first accessed.
+ //
+ // This is only a hint. Implementations are free to choose whether to use
+ // eager or lazy parsing regardless of the value of this option. However,
+ // setting this option true suggests that the protocol author believes that
+ // using lazy parsing on this field is worth the additional bookkeeping
+ // overhead typically needed to implement it.
+ //
+ // This option does not affect the public interface of any generated code;
+ // all method signatures remain the same. Furthermore, thread-safety of the
+ // interface is not affected by this option; const methods remain safe to
+ // call from multiple threads concurrently, while non-const methods continue
+ // to require exclusive access.
+ //
+ //
+ // Note that implementations may choose not to check required fields within
+ // a lazy sub-message. That is, calling IsInitialized() on the outer message
+ // may return true even if the inner message has missing required fields.
+ // This is necessary because otherwise the inner message would have to be
+ // parsed in order to perform the check, defeating the purpose of lazy
+ // parsing. An implementation which chooses not to check required fields
+ // must be consistent about it. That is, for any particular sub-message, the
+ // implementation must either *always* check its required fields, or *never*
+ // check its required fields, regardless of whether or not the message has
+ // been parsed.
+ optional bool lazy = 5 [default=false];
+
+ // Is this field deprecated?
+ // Depending on the target platform, this can emit Deprecated annotations
+ // for accessors, or it will be completely ignored; in the very least, this
+ // is a formalization for deprecating fields.
+ optional bool deprecated = 3 [default=false];
+
+ // For Google-internal migration only. Do not use.
+ optional bool weak = 10 [default=false];
+
+
+ // The parser stores options it doesn't recognize here. See above.
+ repeated UninterpretedOption uninterpreted_option = 999;
+
+ // Clients can define custom options in extensions of this message. See above.
+ extensions 1000 to max;
+
+ reserved 4; // removed jtype
+}
+
+message OneofOptions {
+ // The parser stores options it doesn't recognize here. See above.
+ repeated UninterpretedOption uninterpreted_option = 999;
+
+ // Clients can define custom options in extensions of this message. See above.
+ extensions 1000 to max;
+}
+
+message EnumOptions {
+
+ // Set this option to true to allow mapping different tag names to the same
+ // value.
+ optional bool allow_alias = 2;
+
+ // Is this enum deprecated?
+ // Depending on the target platform, this can emit Deprecated annotations
+ // for the enum, or it will be completely ignored; in the very least, this
+ // is a formalization for deprecating enums.
+ optional bool deprecated = 3 [default=false];
+
+ reserved 5; // javanano_as_lite
+
+ // The parser stores options it doesn't recognize here. See above.
+ repeated UninterpretedOption uninterpreted_option = 999;
+
+ // Clients can define custom options in extensions of this message. See above.
+ extensions 1000 to max;
+}
+
+message EnumValueOptions {
+ // Is this enum value deprecated?
+ // Depending on the target platform, this can emit Deprecated annotations
+ // for the enum value, or it will be completely ignored; in the very least,
+ // this is a formalization for deprecating enum values.
+ optional bool deprecated = 1 [default=false];
+
+ // The parser stores options it doesn't recognize here. See above.
+ repeated UninterpretedOption uninterpreted_option = 999;
+
+ // Clients can define custom options in extensions of this message. See above.
+ extensions 1000 to max;
+}
+
+message ServiceOptions {
+
+ // Note: Field numbers 1 through 32 are reserved for Google's internal RPC
+ // framework. We apologize for hoarding these numbers to ourselves, but
+ // we were already using them long before we decided to release Protocol
+ // Buffers.
+
+ // Is this service deprecated?
+ // Depending on the target platform, this can emit Deprecated annotations
+ // for the service, or it will be completely ignored; in the very least,
+ // this is a formalization for deprecating services.
+ optional bool deprecated = 33 [default=false];
+
+ // The parser stores options it doesn't recognize here. See above.
+ repeated UninterpretedOption uninterpreted_option = 999;
+
+ // Clients can define custom options in extensions of this message. See above.
+ extensions 1000 to max;
+}
+
+message MethodOptions {
+
+ // Note: Field numbers 1 through 32 are reserved for Google's internal RPC
+ // framework. We apologize for hoarding these numbers to ourselves, but
+ // we were already using them long before we decided to release Protocol
+ // Buffers.
+
+ // Is this method deprecated?
+ // Depending on the target platform, this can emit Deprecated annotations
+ // for the method, or it will be completely ignored; in the very least,
+ // this is a formalization for deprecating methods.
+ optional bool deprecated = 33 [default=false];
+
+ // Is this method side-effect-free (or safe in HTTP parlance), or idempotent,
+ // or neither? HTTP based RPC implementation may choose GET verb for safe
+ // methods, and PUT verb for idempotent methods instead of the default POST.
+ enum IdempotencyLevel {
+ IDEMPOTENCY_UNKNOWN = 0;
+ NO_SIDE_EFFECTS = 1; // implies idempotent
+ IDEMPOTENT = 2; // idempotent, but may have side effects
+ }
+ optional IdempotencyLevel idempotency_level =
+ 34 [default=IDEMPOTENCY_UNKNOWN];
+
+ // The parser stores options it doesn't recognize here. See above.
+ repeated UninterpretedOption uninterpreted_option = 999;
+
+ // Clients can define custom options in extensions of this message. See above.
+ extensions 1000 to max;
+}
+
+
+// A message representing a option the parser does not recognize. This only
+// appears in options protos created by the compiler::Parser class.
+// DescriptorPool resolves these when building Descriptor objects. Therefore,
+// options protos in descriptor objects (e.g. returned by Descriptor::options(),
+// or produced by Descriptor::CopyTo()) will never have UninterpretedOptions
+// in them.
+message UninterpretedOption {
+ // The name of the uninterpreted option. Each string represents a segment in
+ // a dot-separated name. is_extension is true iff a segment represents an
+ // extension (denoted with parentheses in options specs in .proto files).
+ // E.g.,{ ["foo", false], ["bar.baz", true], ["qux", false] } represents
+ // "foo.(bar.baz).qux".
+ message NamePart {
+ required string name_part = 1;
+ required bool is_extension = 2;
+ }
+ repeated NamePart name = 2;
+
+ // The value of the uninterpreted option, in whatever type the tokenizer
+ // identified it as during parsing. Exactly one of these should be set.
+ optional string identifier_value = 3;
+ optional uint64 positive_int_value = 4;
+ optional int64 negative_int_value = 5;
+ optional double double_value = 6;
+ optional bytes string_value = 7;
+ optional string aggregate_value = 8;
+}
+
+// ===================================================================
+// Optional source code info
+
+// Encapsulates information about the original source file from which a
+// FileDescriptorProto was generated.
+message SourceCodeInfo {
+ // A Location identifies a piece of source code in a .proto file which
+ // corresponds to a particular definition. This information is intended
+ // to be useful to IDEs, code indexers, documentation generators, and similar
+ // tools.
+ //
+ // For example, say we have a file like:
+ // message Foo {
+ // optional string foo = 1;
+ // }
+ // Let's look at just the field definition:
+ // optional string foo = 1;
+ // ^ ^^ ^^ ^ ^^^
+ // a bc de f ghi
+ // We have the following locations:
+ // span path represents
+ // [a,i) [ 4, 0, 2, 0 ] The whole field definition.
+ // [a,b) [ 4, 0, 2, 0, 4 ] The label (optional).
+ // [c,d) [ 4, 0, 2, 0, 5 ] The type (string).
+ // [e,f) [ 4, 0, 2, 0, 1 ] The name (foo).
+ // [g,h) [ 4, 0, 2, 0, 3 ] The number (1).
+ //
+ // Notes:
+ // - A location may refer to a repeated field itself (i.e. not to any
+ // particular index within it). This is used whenever a set of elements are
+ // logically enclosed in a single code segment. For example, an entire
+ // extend block (possibly containing multiple extension definitions) will
+ // have an outer location whose path refers to the "extensions" repeated
+ // field without an index.
+ // - Multiple locations may have the same path. This happens when a single
+ // logical declaration is spread out across multiple places. The most
+ // obvious example is the "extend" block again -- there may be multiple
+ // extend blocks in the same scope, each of which will have the same path.
+ // - A location's span is not always a subset of its parent's span. For
+ // example, the "extendee" of an extension declaration appears at the
+ // beginning of the "extend" block and is shared by all extensions within
+ // the block.
+ // - Just because a location's span is a subset of some other location's span
+ // does not mean that it is a descendent. For example, a "group" defines
+ // both a type and a field in a single declaration. Thus, the locations
+ // corresponding to the type and field and their components will overlap.
+ // - Code which tries to interpret locations should probably be designed to
+ // ignore those that it doesn't understand, as more types of locations could
+ // be recorded in the future.
+ repeated Location location = 1;
+ message Location {
+ // Identifies which part of the FileDescriptorProto was defined at this
+ // location.
+ //
+ // Each element is a field number or an index. They form a path from
+ // the root FileDescriptorProto to the place where the definition. For
+ // example, this path:
+ // [ 4, 3, 2, 7, 1 ]
+ // refers to:
+ // file.message_type(3) // 4, 3
+ // .field(7) // 2, 7
+ // .name() // 1
+ // This is because FileDescriptorProto.message_type has field number 4:
+ // repeated DescriptorProto message_type = 4;
+ // and DescriptorProto.field has field number 2:
+ // repeated FieldDescriptorProto field = 2;
+ // and FieldDescriptorProto.name has field number 1:
+ // optional string name = 1;
+ //
+ // Thus, the above path gives the location of a field name. If we removed
+ // the last element:
+ // [ 4, 3, 2, 7 ]
+ // this path refers to the whole field declaration (from the beginning
+ // of the label to the terminating semicolon).
+ repeated int32 path = 1 [packed=true];
+
+ // Always has exactly three or four elements: start line, start column,
+ // end line (optional, otherwise assumed same as start line), end column.
+ // These are packed into a single field for efficiency. Note that line
+ // and column numbers are zero-based -- typically you will want to add
+ // 1 to each before displaying to a user.
+ repeated int32 span = 2 [packed=true];
+
+ // If this SourceCodeInfo represents a complete declaration, these are any
+ // comments appearing before and after the declaration which appear to be
+ // attached to the declaration.
+ //
+ // A series of line comments appearing on consecutive lines, with no other
+ // tokens appearing on those lines, will be treated as a single comment.
+ //
+ // leading_detached_comments will keep paragraphs of comments that appear
+ // before (but not connected to) the current element. Each paragraph,
+ // separated by empty lines, will be one comment element in the repeated
+ // field.
+ //
+ // Only the comment content is provided; comment markers (e.g. //) are
+ // stripped out. For block comments, leading whitespace and an asterisk
+ // will be stripped from the beginning of each line other than the first.
+ // Newlines are included in the output.
+ //
+ // Examples:
+ //
+ // optional int32 foo = 1; // Comment attached to foo.
+ // // Comment attached to bar.
+ // optional int32 bar = 2;
+ //
+ // optional string baz = 3;
+ // // Comment attached to baz.
+ // // Another line attached to baz.
+ //
+ // // Comment attached to qux.
+ // //
+ // // Another line attached to qux.
+ // optional double qux = 4;
+ //
+ // // Detached comment for corge. This is not leading or trailing comments
+ // // to qux or corge because there are blank lines separating it from
+ // // both.
+ //
+ // // Detached comment for corge paragraph 2.
+ //
+ // optional string corge = 5;
+ // /* Block comment attached
+ // * to corge. Leading asterisks
+ // * will be removed. */
+ // /* Block comment attached to
+ // * grault. */
+ // optional int32 grault = 6;
+ //
+ // // ignored detached comments.
+ optional string leading_comments = 3;
+ optional string trailing_comments = 4;
+ repeated string leading_detached_comments = 6;
+ }
+}
+
+// Describes the relationship between generated code and its original source
+// file. A GeneratedCodeInfo message is associated with only one generated
+// source file, but may contain references to different source .proto files.
+message GeneratedCodeInfo {
+ // An Annotation connects some span of text in generated code to an element
+ // of its generating .proto file.
+ repeated Annotation annotation = 1;
+ message Annotation {
+ // Identifies the element in the original source .proto file. This field
+ // is formatted the same as SourceCodeInfo.Location.path.
+ repeated int32 path = 1 [packed=true];
+
+ // Identifies the filesystem path to the original source .proto.
+ optional string source_file = 2;
+
+ // Identifies the starting offset in bytes in the generated code
+ // that relates to the identified object.
+ optional int32 begin = 3;
+
+ // Identifies the ending offset in bytes in the generated code that
+ // relates to the identified offset. The end offset should be one past
+ // the last relevant byte (so the length of the text = end - begin).
+ optional int32 end = 4;
+ }
+}
diff --git a/vendor/github.com/golang/protobuf/protoc-gen-go/plugin/Makefile b/vendor/github.com/golang/protobuf/protoc-gen-go/plugin/Makefile
index 4095623e0..bc0463d57 100644
--- a/vendor/github.com/golang/protobuf/protoc-gen-go/plugin/Makefile
+++ b/vendor/github.com/golang/protobuf/protoc-gen-go/plugin/Makefile
@@ -34,6 +34,7 @@
# Also we need to fix an import.
regenerate:
@echo WARNING! THIS RULE IS PROBABLY NOT RIGHT FOR YOUR INSTALLATION
+ cp $(HOME)/src/protobuf/include/google/protobuf/compiler/plugin.proto .
protoc --go_out=Mgoogle/protobuf/descriptor.proto=github.com/golang/protobuf/protoc-gen-go/descriptor:../../../../.. \
-I$(HOME)/src/protobuf/include $(HOME)/src/protobuf/include/google/protobuf/compiler/plugin.proto
diff --git a/vendor/github.com/golang/protobuf/protoc-gen-go/plugin/plugin.proto b/vendor/github.com/golang/protobuf/protoc-gen-go/plugin/plugin.proto
new file mode 100644
index 000000000..f04dc73c8
--- /dev/null
+++ b/vendor/github.com/golang/protobuf/protoc-gen-go/plugin/plugin.proto
@@ -0,0 +1,166 @@
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Author: kenton@google.com (Kenton Varda)
+//
+// WARNING: The plugin interface is currently EXPERIMENTAL and is subject to
+// change.
+//
+// protoc (aka the Protocol Compiler) can be extended via plugins. A plugin is
+// just a program that reads a CodeGeneratorRequest from stdin and writes a
+// CodeGeneratorResponse to stdout.
+//
+// Plugins written using C++ can use google/protobuf/compiler/plugin.h instead
+// of dealing with the raw protocol defined here.
+//
+// A plugin executable needs only to be placed somewhere in the path. The
+// plugin should be named "protoc-gen-$NAME", and will then be used when the
+// flag "--${NAME}_out" is passed to protoc.
+
+syntax = "proto2";
+package google.protobuf.compiler;
+option java_package = "com.google.protobuf.compiler";
+option java_outer_classname = "PluginProtos";
+
+option go_package = "github.com/golang/protobuf/protoc-gen-go/plugin;plugin_go";
+
+import "google/protobuf/descriptor.proto";
+
+// The version number of protocol compiler.
+message Version {
+ optional int32 major = 1;
+ optional int32 minor = 2;
+ optional int32 patch = 3;
+ // A suffix for alpha, beta or rc release, e.g., "alpha-1", "rc2". It should
+ // be empty for mainline stable releases.
+ optional string suffix = 4;
+}
+
+// An encoded CodeGeneratorRequest is written to the plugin's stdin.
+message CodeGeneratorRequest {
+ // The .proto files that were explicitly listed on the command-line. The
+ // code generator should generate code only for these files. Each file's
+ // descriptor will be included in proto_file, below.
+ repeated string file_to_generate = 1;
+
+ // The generator parameter passed on the command-line.
+ optional string parameter = 2;
+
+ // FileDescriptorProtos for all files in files_to_generate and everything
+ // they import. The files will appear in topological order, so each file
+ // appears before any file that imports it.
+ //
+ // protoc guarantees that all proto_files will be written after
+ // the fields above, even though this is not technically guaranteed by the
+ // protobuf wire format. This theoretically could allow a plugin to stream
+ // in the FileDescriptorProtos and handle them one by one rather than read
+ // the entire set into memory at once. However, as of this writing, this
+ // is not similarly optimized on protoc's end -- it will store all fields in
+ // memory at once before sending them to the plugin.
+ //
+ // Type names of fields and extensions in the FileDescriptorProto are always
+ // fully qualified.
+ repeated FileDescriptorProto proto_file = 15;
+
+ // The version number of protocol compiler.
+ optional Version compiler_version = 3;
+}
+
+// The plugin writes an encoded CodeGeneratorResponse to stdout.
+message CodeGeneratorResponse {
+ // Error message. If non-empty, code generation failed. The plugin process
+ // should exit with status code zero even if it reports an error in this way.
+ //
+ // This should be used to indicate errors in .proto files which prevent the
+ // code generator from generating correct code. Errors which indicate a
+ // problem in protoc itself -- such as the input CodeGeneratorRequest being
+ // unparseable -- should be reported by writing a message to stderr and
+ // exiting with a non-zero status code.
+ optional string error = 1;
+
+ // Represents a single generated file.
+ message File {
+ // The file name, relative to the output directory. The name must not
+ // contain "." or ".." components and must be relative, not be absolute (so,
+ // the file cannot lie outside the output directory). "/" must be used as
+ // the path separator, not "\".
+ //
+ // If the name is omitted, the content will be appended to the previous
+ // file. This allows the generator to break large files into small chunks,
+ // and allows the generated text to be streamed back to protoc so that large
+ // files need not reside completely in memory at one time. Note that as of
+ // this writing protoc does not optimize for this -- it will read the entire
+ // CodeGeneratorResponse before writing files to disk.
+ optional string name = 1;
+
+ // If non-empty, indicates that the named file should already exist, and the
+ // content here is to be inserted into that file at a defined insertion
+ // point. This feature allows a code generator to extend the output
+ // produced by another code generator. The original generator may provide
+ // insertion points by placing special annotations in the file that look
+ // like:
+ // @@protoc_insertion_point(NAME)
+ // The annotation can have arbitrary text before and after it on the line,
+ // which allows it to be placed in a comment. NAME should be replaced with
+ // an identifier naming the point -- this is what other generators will use
+ // as the insertion_point. Code inserted at this point will be placed
+ // immediately above the line containing the insertion point (thus multiple
+ // insertions to the same point will come out in the order they were added).
+ // The double-@ is intended to make it unlikely that the generated code
+ // could contain things that look like insertion points by accident.
+ //
+ // For example, the C++ code generator places the following line in the
+ // .pb.h files that it generates:
+ // // @@protoc_insertion_point(namespace_scope)
+ // This line appears within the scope of the file's package namespace, but
+ // outside of any particular class. Another plugin can then specify the
+ // insertion_point "namespace_scope" to generate additional classes or
+ // other declarations that should be placed in this scope.
+ //
+ // Note that if the line containing the insertion point begins with
+ // whitespace, the same whitespace will be added to every line of the
+ // inserted text. This is useful for languages like Python, where
+ // indentation matters. In these languages, the insertion point comment
+ // should be indented the same amount as any inserted code will need to be
+ // in order to work correctly in that context.
+ //
+ // The code generator that generates the initial file and the one which
+ // inserts into it must both run as part of a single invocation of protoc.
+ // Code generators are executed in the order in which they appear on the
+ // command line.
+ //
+ // If |insertion_point| is present, |name| must also be present.
+ optional string insertion_point = 2;
+
+ // The file contents.
+ optional string content = 15;
+ }
+ repeated File file = 15;
+}
diff --git a/vendor/github.com/golang/protobuf/ptypes/any/any.pb.go b/vendor/github.com/golang/protobuf/ptypes/any/any.pb.go
index 1fbaa44ca..6c9a6cf74 100644
--- a/vendor/github.com/golang/protobuf/ptypes/any/any.pb.go
+++ b/vendor/github.com/golang/protobuf/ptypes/any/any.pb.go
@@ -1,11 +1,11 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
-// source: github.com/golang/protobuf/ptypes/any/any.proto
+// source: google/protobuf/any.proto
/*
Package any is a generated protocol buffer package.
It is generated from these files:
- github.com/golang/protobuf/ptypes/any/any.proto
+ google/protobuf/any.proto
It has these top-level messages:
Any
@@ -149,20 +149,20 @@ func init() {
proto.RegisterType((*Any)(nil), "google.protobuf.Any")
}
-func init() { proto.RegisterFile("github.com/golang/protobuf/ptypes/any/any.proto", fileDescriptor0) }
+func init() { proto.RegisterFile("google/protobuf/any.proto", fileDescriptor0) }
var fileDescriptor0 = []byte{
- // 184 bytes of a gzipped FileDescriptorProto
- 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xd2, 0x4f, 0xcf, 0x2c, 0xc9,
- 0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xcf, 0xcf, 0x49, 0xcc, 0x4b, 0xd7, 0x2f, 0x28,
- 0xca, 0x2f, 0xc9, 0x4f, 0x2a, 0x4d, 0xd3, 0x2f, 0x28, 0xa9, 0x2c, 0x48, 0x2d, 0xd6, 0x4f, 0xcc,
- 0xab, 0x04, 0x61, 0x3d, 0xb0, 0xb8, 0x10, 0x7f, 0x7a, 0x7e, 0x7e, 0x7a, 0x4e, 0xaa, 0x1e, 0x4c,
- 0x95, 0x92, 0x19, 0x17, 0xb3, 0x63, 0x5e, 0xa5, 0x90, 0x24, 0x17, 0x07, 0x48, 0x79, 0x7c, 0x69,
- 0x51, 0x8e, 0x04, 0xa3, 0x02, 0xa3, 0x06, 0x67, 0x10, 0x3b, 0x88, 0x1f, 0x5a, 0x94, 0x23, 0x24,
- 0xc2, 0xc5, 0x5a, 0x96, 0x98, 0x53, 0x9a, 0x2a, 0xc1, 0xa4, 0xc0, 0xa8, 0xc1, 0x13, 0x04, 0xe1,
- 0x38, 0xe5, 0x73, 0x09, 0x27, 0xe7, 0xe7, 0xea, 0xa1, 0x19, 0xe7, 0xc4, 0xe1, 0x98, 0x57, 0x19,
- 0x00, 0xe2, 0x04, 0x30, 0x46, 0xa9, 0x12, 0xe5, 0xb8, 0x45, 0x4c, 0xcc, 0xee, 0x01, 0x4e, 0xab,
- 0x98, 0xe4, 0xdc, 0x21, 0x46, 0x05, 0x40, 0x95, 0xe8, 0x85, 0xa7, 0xe6, 0xe4, 0x78, 0xe7, 0xe5,
- 0x97, 0xe7, 0x85, 0x80, 0x94, 0x26, 0xb1, 0x81, 0xf5, 0x1a, 0x03, 0x02, 0x00, 0x00, 0xff, 0xff,
- 0x45, 0x1f, 0x1a, 0xf2, 0xf3, 0x00, 0x00, 0x00,
+ // 185 bytes of a gzipped FileDescriptorProto
+ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x4c, 0xcf, 0xcf, 0x4f,
+ 0xcf, 0x49, 0xd5, 0x2f, 0x28, 0xca, 0x2f, 0xc9, 0x4f, 0x2a, 0x4d, 0xd3, 0x4f, 0xcc, 0xab, 0xd4,
+ 0x03, 0x73, 0x84, 0xf8, 0x21, 0x52, 0x7a, 0x30, 0x29, 0x25, 0x33, 0x2e, 0x66, 0xc7, 0xbc, 0x4a,
+ 0x21, 0x49, 0x2e, 0x8e, 0x92, 0xca, 0x82, 0xd4, 0xf8, 0xd2, 0xa2, 0x1c, 0x09, 0x46, 0x05, 0x46,
+ 0x0d, 0xce, 0x20, 0x76, 0x10, 0x3f, 0xb4, 0x28, 0x47, 0x48, 0x84, 0x8b, 0xb5, 0x2c, 0x31, 0xa7,
+ 0x34, 0x55, 0x82, 0x49, 0x81, 0x51, 0x83, 0x27, 0x08, 0xc2, 0x71, 0xca, 0xe7, 0x12, 0x4e, 0xce,
+ 0xcf, 0xd5, 0x43, 0x33, 0xce, 0x89, 0xc3, 0x31, 0xaf, 0x32, 0x00, 0xc4, 0x09, 0x60, 0x8c, 0x52,
+ 0x4d, 0xcf, 0x2c, 0xc9, 0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xcf, 0xcf, 0x49, 0xcc,
+ 0x4b, 0x47, 0xb8, 0xa8, 0x00, 0x64, 0x7a, 0x31, 0xc8, 0x61, 0x8b, 0x98, 0x98, 0xdd, 0x03, 0x9c,
+ 0x56, 0x31, 0xc9, 0xb9, 0x43, 0x8c, 0x0a, 0x80, 0x2a, 0xd1, 0x0b, 0x4f, 0xcd, 0xc9, 0xf1, 0xce,
+ 0xcb, 0x2f, 0xcf, 0x0b, 0x01, 0x29, 0x4d, 0x62, 0x03, 0xeb, 0x35, 0x06, 0x04, 0x00, 0x00, 0xff,
+ 0xff, 0x13, 0xf8, 0xe8, 0x42, 0xdd, 0x00, 0x00, 0x00,
}
diff --git a/vendor/github.com/golang/protobuf/ptypes/duration/duration.pb.go b/vendor/github.com/golang/protobuf/ptypes/duration/duration.pb.go
index fe3350bed..b2410a098 100644
--- a/vendor/github.com/golang/protobuf/ptypes/duration/duration.pb.go
+++ b/vendor/github.com/golang/protobuf/ptypes/duration/duration.pb.go
@@ -1,11 +1,11 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
-// source: github.com/golang/protobuf/ptypes/duration/duration.proto
+// source: google/protobuf/duration.proto
/*
Package duration is a generated protocol buffer package.
It is generated from these files:
- github.com/golang/protobuf/ptypes/duration/duration.proto
+ google/protobuf/duration.proto
It has these top-level messages:
Duration
@@ -125,22 +125,20 @@ func init() {
proto.RegisterType((*Duration)(nil), "google.protobuf.Duration")
}
-func init() {
- proto.RegisterFile("github.com/golang/protobuf/ptypes/duration/duration.proto", fileDescriptor0)
-}
+func init() { proto.RegisterFile("google/protobuf/duration.proto", fileDescriptor0) }
var fileDescriptor0 = []byte{
- // 189 bytes of a gzipped FileDescriptorProto
- 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xb2, 0x4c, 0xcf, 0x2c, 0xc9,
- 0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xcf, 0xcf, 0x49, 0xcc, 0x4b, 0xd7, 0x2f, 0x28,
- 0xca, 0x2f, 0xc9, 0x4f, 0x2a, 0x4d, 0xd3, 0x2f, 0x28, 0xa9, 0x2c, 0x48, 0x2d, 0xd6, 0x4f, 0x29,
- 0x2d, 0x4a, 0x2c, 0xc9, 0xcc, 0xcf, 0x83, 0x33, 0xf4, 0xc0, 0x2a, 0x84, 0xf8, 0xd3, 0xf3, 0xf3,
- 0xd3, 0x73, 0x52, 0xf5, 0x60, 0xea, 0x95, 0xac, 0xb8, 0x38, 0x5c, 0xa0, 0x4a, 0x84, 0x24, 0xb8,
- 0xd8, 0x8b, 0x53, 0x93, 0xf3, 0xf3, 0x52, 0x8a, 0x25, 0x18, 0x15, 0x18, 0x35, 0x98, 0x83, 0x60,
- 0x5c, 0x21, 0x11, 0x2e, 0xd6, 0xbc, 0xc4, 0xbc, 0xfc, 0x62, 0x09, 0x26, 0x05, 0x46, 0x0d, 0xd6,
- 0x20, 0x08, 0xc7, 0xa9, 0x86, 0x4b, 0x38, 0x39, 0x3f, 0x57, 0x0f, 0xcd, 0x48, 0x27, 0x5e, 0x98,
- 0x81, 0x01, 0x20, 0x91, 0x00, 0xc6, 0x28, 0x2d, 0xe2, 0xdd, 0xfb, 0x83, 0x91, 0x71, 0x11, 0x13,
- 0xb3, 0x7b, 0x80, 0xd3, 0x2a, 0x26, 0x39, 0x77, 0x88, 0xb9, 0x01, 0x50, 0xa5, 0x7a, 0xe1, 0xa9,
- 0x39, 0x39, 0xde, 0x79, 0xf9, 0xe5, 0x79, 0x21, 0x20, 0x2d, 0x49, 0x6c, 0x60, 0x33, 0x8c, 0x01,
- 0x01, 0x00, 0x00, 0xff, 0xff, 0x45, 0x5a, 0x81, 0x3d, 0x0e, 0x01, 0x00, 0x00,
+ // 190 bytes of a gzipped FileDescriptorProto
+ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x4b, 0xcf, 0xcf, 0x4f,
+ 0xcf, 0x49, 0xd5, 0x2f, 0x28, 0xca, 0x2f, 0xc9, 0x4f, 0x2a, 0x4d, 0xd3, 0x4f, 0x29, 0x2d, 0x4a,
+ 0x2c, 0xc9, 0xcc, 0xcf, 0xd3, 0x03, 0x8b, 0x08, 0xf1, 0x43, 0xe4, 0xf5, 0x60, 0xf2, 0x4a, 0x56,
+ 0x5c, 0x1c, 0x2e, 0x50, 0x25, 0x42, 0x12, 0x5c, 0xec, 0xc5, 0xa9, 0xc9, 0xf9, 0x79, 0x29, 0xc5,
+ 0x12, 0x8c, 0x0a, 0x8c, 0x1a, 0xcc, 0x41, 0x30, 0xae, 0x90, 0x08, 0x17, 0x6b, 0x5e, 0x62, 0x5e,
+ 0x7e, 0xb1, 0x04, 0x93, 0x02, 0xa3, 0x06, 0x6b, 0x10, 0x84, 0xe3, 0x54, 0xc3, 0x25, 0x9c, 0x9c,
+ 0x9f, 0xab, 0x87, 0x66, 0xa4, 0x13, 0x2f, 0xcc, 0xc0, 0x00, 0x90, 0x48, 0x00, 0x63, 0x94, 0x56,
+ 0x7a, 0x66, 0x49, 0x46, 0x69, 0x92, 0x5e, 0x72, 0x7e, 0xae, 0x7e, 0x7a, 0x7e, 0x4e, 0x62, 0x5e,
+ 0x3a, 0xc2, 0x7d, 0x05, 0x25, 0x95, 0x05, 0xa9, 0xc5, 0x70, 0x67, 0xfe, 0x60, 0x64, 0x5c, 0xc4,
+ 0xc4, 0xec, 0x1e, 0xe0, 0xb4, 0x8a, 0x49, 0xce, 0x1d, 0x62, 0x6e, 0x00, 0x54, 0xa9, 0x5e, 0x78,
+ 0x6a, 0x4e, 0x8e, 0x77, 0x5e, 0x7e, 0x79, 0x5e, 0x08, 0x48, 0x4b, 0x12, 0x1b, 0xd8, 0x0c, 0x63,
+ 0x40, 0x00, 0x00, 0x00, 0xff, 0xff, 0xdc, 0x84, 0x30, 0xff, 0xf3, 0x00, 0x00, 0x00,
}
diff --git a/vendor/github.com/golang/protobuf/ptypes/empty/empty.pb.go b/vendor/github.com/golang/protobuf/ptypes/empty/empty.pb.go
index ae1594144..e877b72c3 100644
--- a/vendor/github.com/golang/protobuf/ptypes/empty/empty.pb.go
+++ b/vendor/github.com/golang/protobuf/ptypes/empty/empty.pb.go
@@ -1,11 +1,11 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
-// source: github.com/golang/protobuf/ptypes/empty/empty.proto
+// source: google/protobuf/empty.proto
/*
Package empty is a generated protocol buffer package.
It is generated from these files:
- github.com/golang/protobuf/ptypes/empty/empty.proto
+ google/protobuf/empty.proto
It has these top-level messages:
Empty
@@ -49,20 +49,18 @@ func init() {
proto.RegisterType((*Empty)(nil), "google.protobuf.Empty")
}
-func init() {
- proto.RegisterFile("github.com/golang/protobuf/ptypes/empty/empty.proto", fileDescriptor0)
-}
+func init() { proto.RegisterFile("google/protobuf/empty.proto", fileDescriptor0) }
var fileDescriptor0 = []byte{
- // 147 bytes of a gzipped FileDescriptorProto
- 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x32, 0x4e, 0xcf, 0x2c, 0xc9,
- 0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xcf, 0xcf, 0x49, 0xcc, 0x4b, 0xd7, 0x2f, 0x28,
- 0xca, 0x2f, 0xc9, 0x4f, 0x2a, 0x4d, 0xd3, 0x2f, 0x28, 0xa9, 0x2c, 0x48, 0x2d, 0xd6, 0x4f, 0xcd,
- 0x2d, 0x28, 0xa9, 0x84, 0x90, 0x7a, 0x60, 0x39, 0x21, 0xfe, 0xf4, 0xfc, 0xfc, 0xf4, 0x9c, 0x54,
- 0x3d, 0x98, 0x4a, 0x25, 0x76, 0x2e, 0x56, 0x57, 0x90, 0xbc, 0x53, 0x19, 0x97, 0x70, 0x72, 0x7e,
- 0xae, 0x1e, 0x9a, 0xbc, 0x13, 0x17, 0x58, 0x36, 0x00, 0xc4, 0x0d, 0x60, 0x8c, 0x52, 0x27, 0xd2,
- 0xce, 0x1f, 0x8c, 0x8c, 0x8b, 0x98, 0x98, 0xdd, 0x03, 0x9c, 0x56, 0x31, 0xc9, 0xb9, 0x43, 0x4c,
- 0x0c, 0x80, 0xaa, 0xd3, 0x0b, 0x4f, 0xcd, 0xc9, 0xf1, 0xce, 0xcb, 0x2f, 0xcf, 0x0b, 0x01, 0xa9,
- 0x4f, 0x62, 0x03, 0x1b, 0x60, 0x0c, 0x08, 0x00, 0x00, 0xff, 0xff, 0x6e, 0x8e, 0x0a, 0x06, 0xcf,
- 0x00, 0x00, 0x00,
+ // 148 bytes of a gzipped FileDescriptorProto
+ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x4e, 0xcf, 0xcf, 0x4f,
+ 0xcf, 0x49, 0xd5, 0x2f, 0x28, 0xca, 0x2f, 0xc9, 0x4f, 0x2a, 0x4d, 0xd3, 0x4f, 0xcd, 0x2d, 0x28,
+ 0xa9, 0xd4, 0x03, 0x73, 0x85, 0xf8, 0x21, 0x92, 0x7a, 0x30, 0x49, 0x25, 0x76, 0x2e, 0x56, 0x57,
+ 0x90, 0xbc, 0x53, 0x19, 0x97, 0x70, 0x72, 0x7e, 0xae, 0x1e, 0x9a, 0xbc, 0x13, 0x17, 0x58, 0x36,
+ 0x00, 0xc4, 0x0d, 0x60, 0x8c, 0x52, 0x4f, 0xcf, 0x2c, 0xc9, 0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf,
+ 0xd5, 0x4f, 0xcf, 0xcf, 0x49, 0xcc, 0x4b, 0x47, 0x58, 0x53, 0x50, 0x52, 0x59, 0x90, 0x5a, 0x0c,
+ 0xb1, 0xed, 0x07, 0x23, 0xe3, 0x22, 0x26, 0x66, 0xf7, 0x00, 0xa7, 0x55, 0x4c, 0x72, 0xee, 0x10,
+ 0x13, 0x03, 0xa0, 0xea, 0xf4, 0xc2, 0x53, 0x73, 0x72, 0xbc, 0xf3, 0xf2, 0xcb, 0xf3, 0x42, 0x40,
+ 0xea, 0x93, 0xd8, 0xc0, 0x06, 0x18, 0x03, 0x02, 0x00, 0x00, 0xff, 0xff, 0x64, 0xd4, 0xb3, 0xa6,
+ 0xb7, 0x00, 0x00, 0x00,
}
diff --git a/vendor/github.com/golang/protobuf/ptypes/regen.sh b/vendor/github.com/golang/protobuf/ptypes/regen.sh
index 2a5b4e8bd..b50a9414a 100755
--- a/vendor/github.com/golang/protobuf/ptypes/regen.sh
+++ b/vendor/github.com/golang/protobuf/ptypes/regen.sh
@@ -8,14 +8,7 @@
PKG=github.com/golang/protobuf/ptypes
UPSTREAM=https://github.com/google/protobuf
UPSTREAM_SUBDIR=src/google/protobuf
-PROTO_FILES='
- any.proto
- duration.proto
- empty.proto
- struct.proto
- timestamp.proto
- wrappers.proto
-'
+PROTO_FILES=(any duration empty struct timestamp wrappers)
function die() {
echo 1>&2 $*
@@ -36,31 +29,15 @@ pkgdir=$(go list -f '{{.Dir}}' $PKG)
echo 1>&2 $pkgdir
base=$(echo $pkgdir | sed "s,/$PKG\$,,")
echo 1>&2 "base: $base"
-cd $base
+cd "$base"
echo 1>&2 "fetching latest protos... "
git clone -q $UPSTREAM $tmpdir
-# Pass 1: build mapping from upstream filename to our filename.
-declare -A filename_map
-for f in $(cd $PKG && find * -name '*.proto'); do
- echo -n 1>&2 "looking for latest version of $f... "
- up=$(cd $tmpdir/$UPSTREAM_SUBDIR && find * -name $(basename $f) | grep -v /testdata/)
- echo 1>&2 $up
- if [ $(echo $up | wc -w) != "1" ]; then
- die "not exactly one match"
- fi
- filename_map[$up]=$f
-done
-# Pass 2: copy files
-for up in "${!filename_map[@]}"; do
- f=${filename_map[$up]}
- shortname=$(basename $f | sed 's,\.proto$,,')
- cp $tmpdir/$UPSTREAM_SUBDIR/$up $PKG/$f
-done
-# Run protoc once per package.
-for dir in $(find $PKG -name '*.proto' | xargs dirname | sort | uniq); do
- echo 1>&2 "* $dir"
- protoc --go_out=. $dir/*.proto
+for file in ${PROTO_FILES[@]}; do
+ echo 1>&2 "* $file"
+ protoc --go_out=. -I$tmpdir/src $tmpdir/src/google/protobuf/$file.proto || die
+ cp $tmpdir/src/google/protobuf/$file.proto $PKG/$file
done
+
echo 1>&2 "All OK"
diff --git a/vendor/github.com/golang/protobuf/ptypes/struct/struct.pb.go b/vendor/github.com/golang/protobuf/ptypes/struct/struct.pb.go
index 35a8ec599..4cfe60818 100644
--- a/vendor/github.com/golang/protobuf/ptypes/struct/struct.pb.go
+++ b/vendor/github.com/golang/protobuf/ptypes/struct/struct.pb.go
@@ -1,11 +1,11 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
-// source: github.com/golang/protobuf/ptypes/struct/struct.proto
+// source: google/protobuf/struct.proto
/*
Package structpb is a generated protocol buffer package.
It is generated from these files:
- github.com/golang/protobuf/ptypes/struct/struct.proto
+ google/protobuf/struct.proto
It has these top-level messages:
Struct
@@ -346,37 +346,35 @@ func init() {
proto.RegisterEnum("google.protobuf.NullValue", NullValue_name, NullValue_value)
}
-func init() {
- proto.RegisterFile("github.com/golang/protobuf/ptypes/struct/struct.proto", fileDescriptor0)
-}
+func init() { proto.RegisterFile("google/protobuf/struct.proto", fileDescriptor0) }
var fileDescriptor0 = []byte{
// 417 bytes of a gzipped FileDescriptorProto
- 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x92, 0x41, 0x8b, 0xd3, 0x40,
- 0x14, 0x80, 0x3b, 0xc9, 0x36, 0x98, 0x17, 0x59, 0x97, 0x11, 0xb4, 0xac, 0xa0, 0xa1, 0x7b, 0x09,
- 0x22, 0x09, 0x56, 0x04, 0x31, 0x5e, 0x0c, 0xac, 0xbb, 0x60, 0x58, 0x62, 0x74, 0x57, 0xf0, 0x52,
- 0x9a, 0x34, 0x8d, 0xa1, 0xd3, 0x99, 0x90, 0xcc, 0x28, 0x3d, 0xfa, 0x2f, 0x3c, 0x7b, 0xf4, 0xe8,
- 0xaf, 0xf3, 0x28, 0x33, 0x93, 0x44, 0x69, 0x29, 0x78, 0x9a, 0xbe, 0x37, 0xdf, 0xfb, 0xe6, 0xbd,
- 0xd7, 0xc0, 0xf3, 0xb2, 0xe2, 0x9f, 0x45, 0xe6, 0xe7, 0x6c, 0x13, 0x94, 0x8c, 0x2c, 0x68, 0x19,
- 0xd4, 0x0d, 0xe3, 0x2c, 0x13, 0xab, 0xa0, 0xe6, 0xdb, 0xba, 0x68, 0x83, 0x96, 0x37, 0x22, 0xe7,
- 0xdd, 0xe1, 0xab, 0x5b, 0x7c, 0xa7, 0x64, 0xac, 0x24, 0x85, 0xdf, 0xb3, 0xd3, 0xef, 0x08, 0xac,
- 0xf7, 0x8a, 0xc0, 0x21, 0x58, 0xab, 0xaa, 0x20, 0xcb, 0x76, 0x82, 0x5c, 0xd3, 0x73, 0x66, 0x67,
- 0xfe, 0x0e, 0xec, 0x6b, 0xd0, 0x7f, 0xa3, 0xa8, 0x73, 0xca, 0x9b, 0x6d, 0xda, 0x95, 0x9c, 0xbe,
- 0x03, 0xe7, 0x9f, 0x34, 0x3e, 0x01, 0x73, 0x5d, 0x6c, 0x27, 0xc8, 0x45, 0x9e, 0x9d, 0xca, 0x9f,
- 0xf8, 0x09, 0x8c, 0xbf, 0x2c, 0x88, 0x28, 0x26, 0x86, 0x8b, 0x3c, 0x67, 0x76, 0x6f, 0x4f, 0x7e,
- 0x23, 0x6f, 0x53, 0x0d, 0xbd, 0x34, 0x5e, 0xa0, 0xe9, 0x2f, 0x03, 0xc6, 0x2a, 0x89, 0x43, 0x00,
- 0x2a, 0x08, 0x99, 0x6b, 0x81, 0x94, 0x1e, 0xcf, 0x4e, 0xf7, 0x04, 0x57, 0x82, 0x10, 0xc5, 0x5f,
- 0x8e, 0x52, 0x9b, 0xf6, 0x01, 0x3e, 0x83, 0xdb, 0x54, 0x6c, 0xb2, 0xa2, 0x99, 0xff, 0x7d, 0x1f,
- 0x5d, 0x8e, 0x52, 0x47, 0x67, 0x07, 0xa8, 0xe5, 0x4d, 0x45, 0xcb, 0x0e, 0x32, 0x65, 0xe3, 0x12,
- 0xd2, 0x59, 0x0d, 0x3d, 0x02, 0xc8, 0x18, 0xeb, 0xdb, 0x38, 0x72, 0x91, 0x77, 0x4b, 0x3e, 0x25,
- 0x73, 0x1a, 0x78, 0xa5, 0x2c, 0x22, 0xe7, 0x1d, 0x32, 0x56, 0xa3, 0xde, 0x3f, 0xb0, 0xc7, 0x4e,
- 0x2f, 0x72, 0x3e, 0x4c, 0x49, 0xaa, 0xb6, 0xaf, 0xb5, 0x54, 0xed, 0xfe, 0x94, 0x71, 0xd5, 0xf2,
- 0x61, 0x4a, 0xd2, 0x07, 0x91, 0x05, 0x47, 0xeb, 0x8a, 0x2e, 0xa7, 0x21, 0xd8, 0x03, 0x81, 0x7d,
- 0xb0, 0x94, 0xac, 0xff, 0x47, 0x0f, 0x2d, 0xbd, 0xa3, 0x1e, 0x3f, 0x00, 0x7b, 0x58, 0x22, 0x3e,
- 0x06, 0xb8, 0xba, 0x8e, 0xe3, 0xf9, 0xcd, 0xeb, 0xf8, 0xfa, 0xfc, 0x64, 0x14, 0x7d, 0x43, 0x70,
- 0x37, 0x67, 0x9b, 0x5d, 0x45, 0xe4, 0xe8, 0x69, 0x12, 0x19, 0x27, 0xe8, 0xd3, 0xd3, 0xff, 0xfd,
- 0x30, 0x43, 0x7d, 0xd4, 0xd9, 0x6f, 0x84, 0x7e, 0x18, 0xe6, 0x45, 0x12, 0xfd, 0x34, 0x1e, 0x5e,
- 0x68, 0x79, 0xd2, 0xf7, 0xf7, 0xb1, 0x20, 0xe4, 0x2d, 0x65, 0x5f, 0xe9, 0x07, 0x59, 0x99, 0x59,
- 0x4a, 0xf5, 0xec, 0x4f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x9b, 0x6e, 0x5d, 0x3c, 0xfe, 0x02, 0x00,
+ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x92, 0x41, 0x8b, 0xd3, 0x40,
+ 0x14, 0xc7, 0x3b, 0xc9, 0x36, 0x98, 0x17, 0x59, 0x97, 0x11, 0xb4, 0xac, 0xa2, 0xa1, 0x7b, 0x09,
+ 0x22, 0x29, 0xd6, 0x8b, 0x18, 0x2f, 0x06, 0xd6, 0x5d, 0x30, 0x2c, 0x31, 0xba, 0x15, 0xbc, 0x94,
+ 0x26, 0x4d, 0x63, 0xe8, 0x74, 0x26, 0x24, 0x33, 0x4a, 0x8f, 0x7e, 0x0b, 0xcf, 0x1e, 0x3d, 0xfa,
+ 0xe9, 0x3c, 0xca, 0xcc, 0x24, 0xa9, 0xb4, 0xf4, 0x94, 0xbc, 0xf7, 0x7e, 0xef, 0x3f, 0xef, 0xff,
+ 0x66, 0xe0, 0x71, 0xc1, 0x58, 0x41, 0xf2, 0x49, 0x55, 0x33, 0xce, 0x52, 0xb1, 0x9a, 0x34, 0xbc,
+ 0x16, 0x19, 0xf7, 0x55, 0x8c, 0xef, 0xe9, 0xaa, 0xdf, 0x55, 0xc7, 0x3f, 0x11, 0x58, 0x1f, 0x15,
+ 0x81, 0x03, 0xb0, 0x56, 0x65, 0x4e, 0x96, 0xcd, 0x08, 0xb9, 0xa6, 0xe7, 0x4c, 0x2f, 0xfc, 0x3d,
+ 0xd8, 0xd7, 0xa0, 0xff, 0x4e, 0x51, 0x97, 0x94, 0xd7, 0xdb, 0xa4, 0x6d, 0x39, 0xff, 0x00, 0xce,
+ 0x7f, 0x69, 0x7c, 0x06, 0xe6, 0x3a, 0xdf, 0x8e, 0x90, 0x8b, 0x3c, 0x3b, 0x91, 0xbf, 0xf8, 0x39,
+ 0x0c, 0xbf, 0x2d, 0x88, 0xc8, 0x47, 0x86, 0x8b, 0x3c, 0x67, 0xfa, 0xe0, 0x40, 0x7c, 0x26, 0xab,
+ 0x89, 0x86, 0x5e, 0x1b, 0xaf, 0xd0, 0xf8, 0x8f, 0x01, 0x43, 0x95, 0xc4, 0x01, 0x00, 0x15, 0x84,
+ 0xcc, 0xb5, 0x80, 0x14, 0x3d, 0x9d, 0x9e, 0x1f, 0x08, 0xdc, 0x08, 0x42, 0x14, 0x7f, 0x3d, 0x48,
+ 0x6c, 0xda, 0x05, 0xf8, 0x02, 0xee, 0x52, 0xb1, 0x49, 0xf3, 0x7a, 0xbe, 0x3b, 0x1f, 0x5d, 0x0f,
+ 0x12, 0x47, 0x67, 0x7b, 0xa8, 0xe1, 0x75, 0x49, 0x8b, 0x16, 0x32, 0xe5, 0xe0, 0x12, 0xd2, 0x59,
+ 0x0d, 0x3d, 0x05, 0x48, 0x19, 0xeb, 0xc6, 0x38, 0x71, 0x91, 0x77, 0x47, 0x1e, 0x25, 0x73, 0x1a,
+ 0x78, 0xa3, 0x54, 0x44, 0xc6, 0x5b, 0x64, 0xa8, 0xac, 0x3e, 0x3c, 0xb2, 0xc7, 0x56, 0x5e, 0x64,
+ 0xbc, 0x77, 0x49, 0xca, 0xa6, 0xeb, 0xb5, 0x54, 0xef, 0xa1, 0xcb, 0xa8, 0x6c, 0x78, 0xef, 0x92,
+ 0x74, 0x41, 0x68, 0xc1, 0xc9, 0xba, 0xa4, 0xcb, 0x71, 0x00, 0x76, 0x4f, 0x60, 0x1f, 0x2c, 0x25,
+ 0xd6, 0xdd, 0xe8, 0xb1, 0xa5, 0xb7, 0xd4, 0xb3, 0x47, 0x60, 0xf7, 0x4b, 0xc4, 0xa7, 0x00, 0x37,
+ 0xb7, 0x51, 0x34, 0x9f, 0xbd, 0x8d, 0x6e, 0x2f, 0xcf, 0x06, 0xe1, 0x0f, 0x04, 0xf7, 0x33, 0xb6,
+ 0xd9, 0x97, 0x08, 0x1d, 0xed, 0x26, 0x96, 0x71, 0x8c, 0xbe, 0xbc, 0x28, 0x4a, 0xfe, 0x55, 0xa4,
+ 0x7e, 0xc6, 0x36, 0x93, 0x82, 0x91, 0x05, 0x2d, 0x76, 0x4f, 0xb1, 0xe2, 0xdb, 0x2a, 0x6f, 0xda,
+ 0x17, 0x19, 0xe8, 0x4f, 0x95, 0xfe, 0x45, 0xe8, 0x97, 0x61, 0x5e, 0xc5, 0xe1, 0x6f, 0xe3, 0xc9,
+ 0x95, 0x16, 0x8f, 0xbb, 0xf9, 0x3e, 0xe7, 0x84, 0xbc, 0xa7, 0xec, 0x3b, 0xfd, 0x24, 0x3b, 0x53,
+ 0x4b, 0x49, 0xbd, 0xfc, 0x17, 0x00, 0x00, 0xff, 0xff, 0xe8, 0x1b, 0x59, 0xf8, 0xe5, 0x02, 0x00,
0x00,
}
diff --git a/vendor/github.com/golang/protobuf/ptypes/timestamp/timestamp.pb.go b/vendor/github.com/golang/protobuf/ptypes/timestamp/timestamp.pb.go
index 3b76261ec..e23e4a25d 100644
--- a/vendor/github.com/golang/protobuf/ptypes/timestamp/timestamp.pb.go
+++ b/vendor/github.com/golang/protobuf/ptypes/timestamp/timestamp.pb.go
@@ -1,11 +1,11 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
-// source: github.com/golang/protobuf/ptypes/timestamp/timestamp.proto
+// source: google/protobuf/timestamp.proto
/*
Package timestamp is a generated protocol buffer package.
It is generated from these files:
- github.com/golang/protobuf/ptypes/timestamp/timestamp.proto
+ google/protobuf/timestamp.proto
It has these top-level messages:
Timestamp
@@ -141,22 +141,20 @@ func init() {
proto.RegisterType((*Timestamp)(nil), "google.protobuf.Timestamp")
}
-func init() {
- proto.RegisterFile("github.com/golang/protobuf/ptypes/timestamp/timestamp.proto", fileDescriptor0)
-}
+func init() { proto.RegisterFile("google/protobuf/timestamp.proto", fileDescriptor0) }
var fileDescriptor0 = []byte{
- // 190 bytes of a gzipped FileDescriptorProto
- 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xb2, 0x4e, 0xcf, 0x2c, 0xc9,
- 0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xcf, 0xcf, 0x49, 0xcc, 0x4b, 0xd7, 0x2f, 0x28,
- 0xca, 0x2f, 0xc9, 0x4f, 0x2a, 0x4d, 0xd3, 0x2f, 0x28, 0xa9, 0x2c, 0x48, 0x2d, 0xd6, 0x2f, 0xc9,
- 0xcc, 0x4d, 0x2d, 0x2e, 0x49, 0xcc, 0x2d, 0x40, 0xb0, 0xf4, 0xc0, 0x6a, 0x84, 0xf8, 0xd3, 0xf3,
- 0xf3, 0xd3, 0x73, 0x52, 0xf5, 0x60, 0x3a, 0x94, 0xac, 0xb9, 0x38, 0x43, 0x60, 0x6a, 0x84, 0x24,
- 0xb8, 0xd8, 0x8b, 0x53, 0x93, 0xf3, 0xf3, 0x52, 0x8a, 0x25, 0x18, 0x15, 0x18, 0x35, 0x98, 0x83,
- 0x60, 0x5c, 0x21, 0x11, 0x2e, 0xd6, 0xbc, 0xc4, 0xbc, 0xfc, 0x62, 0x09, 0x26, 0x05, 0x46, 0x0d,
- 0xd6, 0x20, 0x08, 0xc7, 0xa9, 0x8e, 0x4b, 0x38, 0x39, 0x3f, 0x57, 0x0f, 0xcd, 0x4c, 0x27, 0x3e,
- 0xb8, 0x89, 0x01, 0x20, 0xa1, 0x00, 0xc6, 0x28, 0x6d, 0x12, 0xdc, 0xfc, 0x83, 0x91, 0x71, 0x11,
- 0x13, 0xb3, 0x7b, 0x80, 0xd3, 0x2a, 0x26, 0x39, 0x77, 0x88, 0xc9, 0x01, 0x50, 0xb5, 0x7a, 0xe1,
- 0xa9, 0x39, 0x39, 0xde, 0x79, 0xf9, 0xe5, 0x79, 0x21, 0x20, 0x3d, 0x49, 0x6c, 0x60, 0x43, 0x8c,
- 0x01, 0x01, 0x00, 0x00, 0xff, 0xff, 0x6b, 0x59, 0x0a, 0x4d, 0x13, 0x01, 0x00, 0x00,
+ // 191 bytes of a gzipped FileDescriptorProto
+ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x4f, 0xcf, 0xcf, 0x4f,
+ 0xcf, 0x49, 0xd5, 0x2f, 0x28, 0xca, 0x2f, 0xc9, 0x4f, 0x2a, 0x4d, 0xd3, 0x2f, 0xc9, 0xcc, 0x4d,
+ 0x2d, 0x2e, 0x49, 0xcc, 0x2d, 0xd0, 0x03, 0x0b, 0x09, 0xf1, 0x43, 0x14, 0xe8, 0xc1, 0x14, 0x28,
+ 0x59, 0x73, 0x71, 0x86, 0xc0, 0xd4, 0x08, 0x49, 0x70, 0xb1, 0x17, 0xa7, 0x26, 0xe7, 0xe7, 0xa5,
+ 0x14, 0x4b, 0x30, 0x2a, 0x30, 0x6a, 0x30, 0x07, 0xc1, 0xb8, 0x42, 0x22, 0x5c, 0xac, 0x79, 0x89,
+ 0x79, 0xf9, 0xc5, 0x12, 0x4c, 0x0a, 0x8c, 0x1a, 0xac, 0x41, 0x10, 0x8e, 0x53, 0x1d, 0x97, 0x70,
+ 0x72, 0x7e, 0xae, 0x1e, 0x9a, 0x99, 0x4e, 0x7c, 0x70, 0x13, 0x03, 0x40, 0x42, 0x01, 0x8c, 0x51,
+ 0xda, 0xe9, 0x99, 0x25, 0x19, 0xa5, 0x49, 0x7a, 0xc9, 0xf9, 0xb9, 0xfa, 0xe9, 0xf9, 0x39, 0x89,
+ 0x79, 0xe9, 0x08, 0x27, 0x16, 0x94, 0x54, 0x16, 0xa4, 0x16, 0x23, 0x5c, 0xfa, 0x83, 0x91, 0x71,
+ 0x11, 0x13, 0xb3, 0x7b, 0x80, 0xd3, 0x2a, 0x26, 0x39, 0x77, 0x88, 0xc9, 0x01, 0x50, 0xb5, 0x7a,
+ 0xe1, 0xa9, 0x39, 0x39, 0xde, 0x79, 0xf9, 0xe5, 0x79, 0x21, 0x20, 0x3d, 0x49, 0x6c, 0x60, 0x43,
+ 0x8c, 0x01, 0x01, 0x00, 0x00, 0xff, 0xff, 0xbc, 0x77, 0x4a, 0x07, 0xf7, 0x00, 0x00, 0x00,
}
diff --git a/vendor/github.com/golang/protobuf/ptypes/wrappers/wrappers.pb.go b/vendor/github.com/golang/protobuf/ptypes/wrappers/wrappers.pb.go
index 1328bd296..0ed59bf19 100644
--- a/vendor/github.com/golang/protobuf/ptypes/wrappers/wrappers.pb.go
+++ b/vendor/github.com/golang/protobuf/ptypes/wrappers/wrappers.pb.go
@@ -1,11 +1,11 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
-// source: github.com/golang/protobuf/ptypes/wrappers/wrappers.proto
+// source: google/protobuf/wrappers.proto
/*
Package wrappers is a generated protocol buffer package.
It is generated from these files:
- github.com/golang/protobuf/ptypes/wrappers/wrappers.proto
+ google/protobuf/wrappers.proto
It has these top-level messages:
DoubleValue
@@ -236,27 +236,25 @@ func init() {
proto.RegisterType((*BytesValue)(nil), "google.protobuf.BytesValue")
}
-func init() {
- proto.RegisterFile("github.com/golang/protobuf/ptypes/wrappers/wrappers.proto", fileDescriptor0)
-}
+func init() { proto.RegisterFile("google/protobuf/wrappers.proto", fileDescriptor0) }
var fileDescriptor0 = []byte{
- // 257 bytes of a gzipped FileDescriptorProto
- 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xb2, 0x4c, 0xcf, 0x2c, 0xc9,
- 0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xcf, 0xcf, 0x49, 0xcc, 0x4b, 0xd7, 0x2f, 0x28,
- 0xca, 0x2f, 0xc9, 0x4f, 0x2a, 0x4d, 0xd3, 0x2f, 0x28, 0xa9, 0x2c, 0x48, 0x2d, 0xd6, 0x2f, 0x2f,
- 0x4a, 0x2c, 0x28, 0x48, 0x2d, 0x42, 0x30, 0xf4, 0xc0, 0x2a, 0x84, 0xf8, 0xd3, 0xf3, 0xf3, 0xd3,
- 0x73, 0x52, 0xf5, 0x60, 0xea, 0x95, 0x94, 0xb9, 0xb8, 0x5d, 0xf2, 0x4b, 0x93, 0x72, 0x52, 0xc3,
- 0x12, 0x73, 0x4a, 0x53, 0x85, 0x44, 0xb8, 0x58, 0xcb, 0x40, 0x0c, 0x09, 0x46, 0x05, 0x46, 0x0d,
- 0xc6, 0x20, 0x08, 0x47, 0x49, 0x89, 0x8b, 0xcb, 0x2d, 0x27, 0x3f, 0xb1, 0x04, 0x8b, 0x1a, 0x26,
- 0x24, 0x35, 0x9e, 0x79, 0x25, 0x66, 0x26, 0x58, 0xd4, 0x30, 0xc3, 0xd4, 0x28, 0x73, 0x71, 0x87,
- 0xe2, 0x52, 0xc4, 0x82, 0x6a, 0x90, 0xb1, 0x11, 0x16, 0x35, 0xac, 0x68, 0x06, 0x61, 0x55, 0xc4,
- 0x0b, 0x53, 0xa4, 0xc8, 0xc5, 0xe9, 0x94, 0x9f, 0x9f, 0x83, 0x45, 0x09, 0x07, 0x92, 0x39, 0xc1,
- 0x25, 0x45, 0x99, 0x79, 0xe9, 0x58, 0x14, 0x71, 0x22, 0x39, 0xc8, 0xa9, 0xb2, 0x24, 0xb5, 0x18,
- 0x8b, 0x1a, 0x1e, 0xa8, 0x1a, 0xa7, 0x1a, 0x2e, 0xe1, 0xe4, 0xfc, 0x5c, 0x3d, 0xb4, 0xd0, 0x75,
- 0xe2, 0x0d, 0x87, 0x06, 0x7f, 0x00, 0x48, 0x24, 0x80, 0x31, 0x4a, 0x8b, 0xf8, 0xa8, 0xfb, 0xc1,
- 0xc8, 0xb8, 0x88, 0x89, 0xd9, 0x3d, 0xc0, 0x69, 0x15, 0x93, 0x9c, 0x3b, 0xc4, 0xdc, 0x00, 0xa8,
- 0x52, 0xbd, 0xf0, 0xd4, 0x9c, 0x1c, 0xef, 0xbc, 0xfc, 0xf2, 0xbc, 0x10, 0x90, 0x96, 0x24, 0x36,
- 0xb0, 0x19, 0xc6, 0x80, 0x00, 0x00, 0x00, 0xff, 0xff, 0xee, 0x36, 0x8d, 0xd8, 0x19, 0x02, 0x00,
- 0x00,
+ // 259 bytes of a gzipped FileDescriptorProto
+ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x4b, 0xcf, 0xcf, 0x4f,
+ 0xcf, 0x49, 0xd5, 0x2f, 0x28, 0xca, 0x2f, 0xc9, 0x4f, 0x2a, 0x4d, 0xd3, 0x2f, 0x2f, 0x4a, 0x2c,
+ 0x28, 0x48, 0x2d, 0x2a, 0xd6, 0x03, 0x8b, 0x08, 0xf1, 0x43, 0xe4, 0xf5, 0x60, 0xf2, 0x4a, 0xca,
+ 0x5c, 0xdc, 0x2e, 0xf9, 0xa5, 0x49, 0x39, 0xa9, 0x61, 0x89, 0x39, 0xa5, 0xa9, 0x42, 0x22, 0x5c,
+ 0xac, 0x65, 0x20, 0x86, 0x04, 0xa3, 0x02, 0xa3, 0x06, 0x63, 0x10, 0x84, 0xa3, 0xa4, 0xc4, 0xc5,
+ 0xe5, 0x96, 0x93, 0x9f, 0x58, 0x82, 0x45, 0x0d, 0x13, 0x92, 0x1a, 0xcf, 0xbc, 0x12, 0x33, 0x13,
+ 0x2c, 0x6a, 0x98, 0x61, 0x6a, 0x94, 0xb9, 0xb8, 0x43, 0x71, 0x29, 0x62, 0x41, 0x35, 0xc8, 0xd8,
+ 0x08, 0x8b, 0x1a, 0x56, 0x34, 0x83, 0xb0, 0x2a, 0xe2, 0x85, 0x29, 0x52, 0xe4, 0xe2, 0x74, 0xca,
+ 0xcf, 0xcf, 0xc1, 0xa2, 0x84, 0x03, 0xc9, 0x9c, 0xe0, 0x92, 0xa2, 0xcc, 0xbc, 0x74, 0x2c, 0x8a,
+ 0x38, 0x91, 0x1c, 0xe4, 0x54, 0x59, 0x92, 0x5a, 0x8c, 0x45, 0x0d, 0x0f, 0x54, 0x8d, 0x53, 0x0d,
+ 0x97, 0x70, 0x72, 0x7e, 0xae, 0x1e, 0x5a, 0xe8, 0x3a, 0xf1, 0x86, 0x43, 0x83, 0x3f, 0x00, 0x24,
+ 0x12, 0xc0, 0x18, 0xa5, 0x95, 0x9e, 0x59, 0x92, 0x51, 0x9a, 0xa4, 0x97, 0x9c, 0x9f, 0xab, 0x9f,
+ 0x9e, 0x9f, 0x93, 0x98, 0x97, 0x8e, 0x88, 0xaa, 0x82, 0x92, 0xca, 0x82, 0xd4, 0x62, 0x78, 0x8c,
+ 0xfd, 0x60, 0x64, 0x5c, 0xc4, 0xc4, 0xec, 0x1e, 0xe0, 0xb4, 0x8a, 0x49, 0xce, 0x1d, 0x62, 0x6e,
+ 0x00, 0x54, 0xa9, 0x5e, 0x78, 0x6a, 0x4e, 0x8e, 0x77, 0x5e, 0x7e, 0x79, 0x5e, 0x08, 0x48, 0x4b,
+ 0x12, 0x1b, 0xd8, 0x0c, 0x63, 0x40, 0x00, 0x00, 0x00, 0xff, 0xff, 0x19, 0x6c, 0xb9, 0xb8, 0xfe,
+ 0x01, 0x00, 0x00,
}
diff --git a/vendor/github.com/hashicorp/go-immutable-radix/.gitignore b/vendor/github.com/hashicorp/go-immutable-radix/.gitignore
new file mode 100644
index 000000000..daf913b1b
--- /dev/null
+++ b/vendor/github.com/hashicorp/go-immutable-radix/.gitignore
@@ -0,0 +1,24 @@
+# Compiled Object files, Static and Dynamic libs (Shared Objects)
+*.o
+*.a
+*.so
+
+# Folders
+_obj
+_test
+
+# Architecture specific extensions/prefixes
+*.[568vq]
+[568vq].out
+
+*.cgo1.go
+*.cgo2.c
+_cgo_defun.c
+_cgo_gotypes.go
+_cgo_export.*
+
+_testmain.go
+
+*.exe
+*.test
+*.prof
diff --git a/vendor/github.com/hashicorp/go-immutable-radix/.travis.yml b/vendor/github.com/hashicorp/go-immutable-radix/.travis.yml
new file mode 100644
index 000000000..1a0bbea6c
--- /dev/null
+++ b/vendor/github.com/hashicorp/go-immutable-radix/.travis.yml
@@ -0,0 +1,3 @@
+language: go
+go:
+ - tip
diff --git a/vendor/github.com/hashicorp/go-immutable-radix/LICENSE b/vendor/github.com/hashicorp/go-immutable-radix/LICENSE
new file mode 100644
index 000000000..e87a115e4
--- /dev/null
+++ b/vendor/github.com/hashicorp/go-immutable-radix/LICENSE
@@ -0,0 +1,363 @@
+Mozilla Public License, version 2.0
+
+1. Definitions
+
+1.1. "Contributor"
+
+ means each individual or legal entity that creates, contributes to the
+ creation of, or owns Covered Software.
+
+1.2. "Contributor Version"
+
+ means the combination of the Contributions of others (if any) used by a
+ Contributor and that particular Contributor's Contribution.
+
+1.3. "Contribution"
+
+ means Covered Software of a particular Contributor.
+
+1.4. "Covered Software"
+
+ means Source Code Form to which the initial Contributor has attached the
+ notice in Exhibit A, the Executable Form of such Source Code Form, and
+ Modifications of such Source Code Form, in each case including portions
+ thereof.
+
+1.5. "Incompatible With Secondary Licenses"
+ means
+
+ a. that the initial Contributor has attached the notice described in
+ Exhibit B to the Covered Software; or
+
+ b. that the Covered Software was made available under the terms of
+ version 1.1 or earlier of the License, but not also under the terms of
+ a Secondary License.
+
+1.6. "Executable Form"
+
+ means any form of the work other than Source Code Form.
+
+1.7. "Larger Work"
+
+ means a work that combines Covered Software with other material, in a
+ separate file or files, that is not Covered Software.
+
+1.8. "License"
+
+ means this document.
+
+1.9. "Licensable"
+
+ means having the right to grant, to the maximum extent possible, whether
+ at the time of the initial grant or subsequently, any and all of the
+ rights conveyed by this License.
+
+1.10. "Modifications"
+
+ means any of the following:
+
+ a. any file in Source Code Form that results from an addition to,
+ deletion from, or modification of the contents of Covered Software; or
+
+ b. any new file in Source Code Form that contains any Covered Software.
+
+1.11. "Patent Claims" of a Contributor
+
+ means any patent claim(s), including without limitation, method,
+ process, and apparatus claims, in any patent Licensable by such
+ Contributor that would be infringed, but for the grant of the License,
+ by the making, using, selling, offering for sale, having made, import,
+ or transfer of either its Contributions or its Contributor Version.
+
+1.12. "Secondary License"
+
+ means either the GNU General Public License, Version 2.0, the GNU Lesser
+ General Public License, Version 2.1, the GNU Affero General Public
+ License, Version 3.0, or any later versions of those licenses.
+
+1.13. "Source Code Form"
+
+ means the form of the work preferred for making modifications.
+
+1.14. "You" (or "Your")
+
+ means an individual or a legal entity exercising rights under this
+ License. For legal entities, "You" includes any entity that controls, is
+ controlled by, or is under common control with You. For purposes of this
+ definition, "control" means (a) the power, direct or indirect, to cause
+ the direction or management of such entity, whether by contract or
+ otherwise, or (b) ownership of more than fifty percent (50%) of the
+ outstanding shares or beneficial ownership of such entity.
+
+
+2. License Grants and Conditions
+
+2.1. Grants
+
+ Each Contributor hereby grants You a world-wide, royalty-free,
+ non-exclusive license:
+
+ a. under intellectual property rights (other than patent or trademark)
+ Licensable by such Contributor to use, reproduce, make available,
+ modify, display, perform, distribute, and otherwise exploit its
+ Contributions, either on an unmodified basis, with Modifications, or
+ as part of a Larger Work; and
+
+ b. under Patent Claims of such Contributor to make, use, sell, offer for
+ sale, have made, import, and otherwise transfer either its
+ Contributions or its Contributor Version.
+
+2.2. Effective Date
+
+ The licenses granted in Section 2.1 with respect to any Contribution
+ become effective for each Contribution on the date the Contributor first
+ distributes such Contribution.
+
+2.3. Limitations on Grant Scope
+
+ The licenses granted in this Section 2 are the only rights granted under
+ this License. No additional rights or licenses will be implied from the
+ distribution or licensing of Covered Software under this License.
+ Notwithstanding Section 2.1(b) above, no patent license is granted by a
+ Contributor:
+
+ a. for any code that a Contributor has removed from Covered Software; or
+
+ b. for infringements caused by: (i) Your and any other third party's
+ modifications of Covered Software, or (ii) the combination of its
+ Contributions with other software (except as part of its Contributor
+ Version); or
+
+ c. under Patent Claims infringed by Covered Software in the absence of
+ its Contributions.
+
+ This License does not grant any rights in the trademarks, service marks,
+ or logos of any Contributor (except as may be necessary to comply with
+ the notice requirements in Section 3.4).
+
+2.4. Subsequent Licenses
+
+ No Contributor makes additional grants as a result of Your choice to
+ distribute the Covered Software under a subsequent version of this
+ License (see Section 10.2) or under the terms of a Secondary License (if
+ permitted under the terms of Section 3.3).
+
+2.5. Representation
+
+ Each Contributor represents that the Contributor believes its
+ Contributions are its original creation(s) or it has sufficient rights to
+ grant the rights to its Contributions conveyed by this License.
+
+2.6. Fair Use
+
+ This License is not intended to limit any rights You have under
+ applicable copyright doctrines of fair use, fair dealing, or other
+ equivalents.
+
+2.7. Conditions
+
+ Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in
+ Section 2.1.
+
+
+3. Responsibilities
+
+3.1. Distribution of Source Form
+
+ All distribution of Covered Software in Source Code Form, including any
+ Modifications that You create or to which You contribute, must be under
+ the terms of this License. You must inform recipients that the Source
+ Code Form of the Covered Software is governed by the terms of this
+ License, and how they can obtain a copy of this License. You may not
+ attempt to alter or restrict the recipients' rights in the Source Code
+ Form.
+
+3.2. Distribution of Executable Form
+
+ If You distribute Covered Software in Executable Form then:
+
+ a. such Covered Software must also be made available in Source Code Form,
+ as described in Section 3.1, and You must inform recipients of the
+ Executable Form how they can obtain a copy of such Source Code Form by
+ reasonable means in a timely manner, at a charge no more than the cost
+ of distribution to the recipient; and
+
+ b. You may distribute such Executable Form under the terms of this
+ License, or sublicense it under different terms, provided that the
+ license for the Executable Form does not attempt to limit or alter the
+ recipients' rights in the Source Code Form under this License.
+
+3.3. Distribution of a Larger Work
+
+ You may create and distribute a Larger Work under terms of Your choice,
+ provided that You also comply with the requirements of this License for
+ the Covered Software. If the Larger Work is a combination of Covered
+ Software with a work governed by one or more Secondary Licenses, and the
+ Covered Software is not Incompatible With Secondary Licenses, this
+ License permits You to additionally distribute such Covered Software
+ under the terms of such Secondary License(s), so that the recipient of
+ the Larger Work may, at their option, further distribute the Covered
+ Software under the terms of either this License or such Secondary
+ License(s).
+
+3.4. Notices
+
+ You may not remove or alter the substance of any license notices
+ (including copyright notices, patent notices, disclaimers of warranty, or
+ limitations of liability) contained within the Source Code Form of the
+ Covered Software, except that You may alter any license notices to the
+ extent required to remedy known factual inaccuracies.
+
+3.5. Application of Additional Terms
+
+ You may choose to offer, and to charge a fee for, warranty, support,
+ indemnity or liability obligations to one or more recipients of Covered
+ Software. However, You may do so only on Your own behalf, and not on
+ behalf of any Contributor. You must make it absolutely clear that any
+ such warranty, support, indemnity, or liability obligation is offered by
+ You alone, and You hereby agree to indemnify every Contributor for any
+ liability incurred by such Contributor as a result of warranty, support,
+ indemnity or liability terms You offer. You may include additional
+ disclaimers of warranty and limitations of liability specific to any
+ jurisdiction.
+
+4. Inability to Comply Due to Statute or Regulation
+
+ If it is impossible for You to comply with any of the terms of this License
+ with respect to some or all of the Covered Software due to statute,
+ judicial order, or regulation then You must: (a) comply with the terms of
+ this License to the maximum extent possible; and (b) describe the
+ limitations and the code they affect. Such description must be placed in a
+ text file included with all distributions of the Covered Software under
+ this License. Except to the extent prohibited by statute or regulation,
+ such description must be sufficiently detailed for a recipient of ordinary
+ skill to be able to understand it.
+
+5. Termination
+
+5.1. The rights granted under this License will terminate automatically if You
+ fail to comply with any of its terms. However, if You become compliant,
+ then the rights granted under this License from a particular Contributor
+ are reinstated (a) provisionally, unless and until such Contributor
+ explicitly and finally terminates Your grants, and (b) on an ongoing
+ basis, if such Contributor fails to notify You of the non-compliance by
+ some reasonable means prior to 60 days after You have come back into
+ compliance. Moreover, Your grants from a particular Contributor are
+ reinstated on an ongoing basis if such Contributor notifies You of the
+ non-compliance by some reasonable means, this is the first time You have
+ received notice of non-compliance with this License from such
+ Contributor, and You become compliant prior to 30 days after Your receipt
+ of the notice.
+
+5.2. If You initiate litigation against any entity by asserting a patent
+ infringement claim (excluding declaratory judgment actions,
+ counter-claims, and cross-claims) alleging that a Contributor Version
+ directly or indirectly infringes any patent, then the rights granted to
+ You by any and all Contributors for the Covered Software under Section
+ 2.1 of this License shall terminate.
+
+5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user
+ license agreements (excluding distributors and resellers) which have been
+ validly granted by You or Your distributors under this License prior to
+ termination shall survive termination.
+
+6. Disclaimer of Warranty
+
+ Covered Software is provided under this License on an "as is" basis,
+ without warranty of any kind, either expressed, implied, or statutory,
+ including, without limitation, warranties that the Covered Software is free
+ of defects, merchantable, fit for a particular purpose or non-infringing.
+ The entire risk as to the quality and performance of the Covered Software
+ is with You. Should any Covered Software prove defective in any respect,
+ You (not any Contributor) assume the cost of any necessary servicing,
+ repair, or correction. This disclaimer of warranty constitutes an essential
+ part of this License. No use of any Covered Software is authorized under
+ this License except under this disclaimer.
+
+7. Limitation of Liability
+
+ Under no circumstances and under no legal theory, whether tort (including
+ negligence), contract, or otherwise, shall any Contributor, or anyone who
+ distributes Covered Software as permitted above, be liable to You for any
+ direct, indirect, special, incidental, or consequential damages of any
+ character including, without limitation, damages for lost profits, loss of
+ goodwill, work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses, even if such party shall have been
+ informed of the possibility of such damages. This limitation of liability
+ shall not apply to liability for death or personal injury resulting from
+ such party's negligence to the extent applicable law prohibits such
+ limitation. Some jurisdictions do not allow the exclusion or limitation of
+ incidental or consequential damages, so this exclusion and limitation may
+ not apply to You.
+
+8. Litigation
+
+ Any litigation relating to this License may be brought only in the courts
+ of a jurisdiction where the defendant maintains its principal place of
+ business and such litigation shall be governed by laws of that
+ jurisdiction, without reference to its conflict-of-law provisions. Nothing
+ in this Section shall prevent a party's ability to bring cross-claims or
+ counter-claims.
+
+9. Miscellaneous
+
+ This License represents the complete agreement concerning the subject
+ matter hereof. If any provision of this License is held to be
+ unenforceable, such provision shall be reformed only to the extent
+ necessary to make it enforceable. Any law or regulation which provides that
+ the language of a contract shall be construed against the drafter shall not
+ be used to construe this License against a Contributor.
+
+
+10. Versions of the License
+
+10.1. New Versions
+
+ Mozilla Foundation is the license steward. Except as provided in Section
+ 10.3, no one other than the license steward has the right to modify or
+ publish new versions of this License. Each version will be given a
+ distinguishing version number.
+
+10.2. Effect of New Versions
+
+ You may distribute the Covered Software under the terms of the version
+ of the License under which You originally received the Covered Software,
+ or under the terms of any subsequent version published by the license
+ steward.
+
+10.3. Modified Versions
+
+ If you create software not governed by this License, and you want to
+ create a new license for such software, you may create and use a
+ modified version of this License if you rename the license and remove
+ any references to the name of the license steward (except to note that
+ such modified license differs from this License).
+
+10.4. Distributing Source Code Form that is Incompatible With Secondary
+ Licenses If You choose to distribute Source Code Form that is
+ Incompatible With Secondary Licenses under the terms of this version of
+ the License, the notice described in Exhibit B of this License must be
+ attached.
+
+Exhibit A - Source Code Form License Notice
+
+ This Source Code Form is subject to the
+ terms of the Mozilla Public License, v.
+ 2.0. If a copy of the MPL was not
+ distributed with this file, You can
+ obtain one at
+ http://mozilla.org/MPL/2.0/.
+
+If it is not possible or desirable to put the notice in a particular file,
+then You may include the notice in a location (such as a LICENSE file in a
+relevant directory) where a recipient would be likely to look for such a
+notice.
+
+You may add additional accurate notices of copyright ownership.
+
+Exhibit B - "Incompatible With Secondary Licenses" Notice
+
+ This Source Code Form is "Incompatible
+ With Secondary Licenses", as defined by
+ the Mozilla Public License, v. 2.0.
+
diff --git a/vendor/github.com/hashicorp/go-immutable-radix/README.md b/vendor/github.com/hashicorp/go-immutable-radix/README.md
new file mode 100644
index 000000000..8910fcc03
--- /dev/null
+++ b/vendor/github.com/hashicorp/go-immutable-radix/README.md
@@ -0,0 +1,41 @@
+go-immutable-radix [![Build Status](https://travis-ci.org/hashicorp/go-immutable-radix.png)](https://travis-ci.org/hashicorp/go-immutable-radix)
+=========
+
+Provides the `iradix` package that implements an immutable [radix tree](http://en.wikipedia.org/wiki/Radix_tree).
+The package only provides a single `Tree` implementation, optimized for sparse nodes.
+
+As a radix tree, it provides the following:
+ * O(k) operations. In many cases, this can be faster than a hash table since
+ the hash function is an O(k) operation, and hash tables have very poor cache locality.
+ * Minimum / Maximum value lookups
+ * Ordered iteration
+
+A tree supports using a transaction to batch multiple updates (insert, delete)
+in a more efficient manner than performing each operation one at a time.
+
+For a mutable variant, see [go-radix](https://github.com/armon/go-radix).
+
+Documentation
+=============
+
+The full documentation is available on [Godoc](http://godoc.org/github.com/hashicorp/go-immutable-radix).
+
+Example
+=======
+
+Below is a simple example of usage
+
+```go
+// Create a tree
+r := iradix.New()
+r, _, _ = r.Insert([]byte("foo"), 1)
+r, _, _ = r.Insert([]byte("bar"), 2)
+r, _, _ = r.Insert([]byte("foobar"), 2)
+
+// Find the longest prefix match
+m, _, _ := r.Root().LongestPrefix([]byte("foozip"))
+if string(m) != "foo" {
+ panic("should be foo")
+}
+```
+
diff --git a/vendor/github.com/hashicorp/go-immutable-radix/edges.go b/vendor/github.com/hashicorp/go-immutable-radix/edges.go
new file mode 100644
index 000000000..a63674775
--- /dev/null
+++ b/vendor/github.com/hashicorp/go-immutable-radix/edges.go
@@ -0,0 +1,21 @@
+package iradix
+
+import "sort"
+
+type edges []edge
+
+func (e edges) Len() int {
+ return len(e)
+}
+
+func (e edges) Less(i, j int) bool {
+ return e[i].label < e[j].label
+}
+
+func (e edges) Swap(i, j int) {
+ e[i], e[j] = e[j], e[i]
+}
+
+func (e edges) Sort() {
+ sort.Sort(e)
+}
diff --git a/vendor/github.com/hashicorp/go-immutable-radix/iradix.go b/vendor/github.com/hashicorp/go-immutable-radix/iradix.go
new file mode 100644
index 000000000..c7172c406
--- /dev/null
+++ b/vendor/github.com/hashicorp/go-immutable-radix/iradix.go
@@ -0,0 +1,657 @@
+package iradix
+
+import (
+ "bytes"
+ "strings"
+
+ "github.com/hashicorp/golang-lru/simplelru"
+)
+
+const (
+ // defaultModifiedCache is the default size of the modified node
+ // cache used per transaction. This is used to cache the updates
+ // to the nodes near the root, while the leaves do not need to be
+ // cached. This is important for very large transactions to prevent
+ // the modified cache from growing to be enormous. This is also used
+ // to set the max size of the mutation notify maps since those should
+ // also be bounded in a similar way.
+ defaultModifiedCache = 8192
+)
+
+// Tree implements an immutable radix tree. This can be treated as a
+// Dictionary abstract data type. The main advantage over a standard
+// hash map is prefix-based lookups and ordered iteration. The immutability
+// means that it is safe to concurrently read from a Tree without any
+// coordination.
+type Tree struct {
+ root *Node
+ size int
+}
+
+// New returns an empty Tree
+func New() *Tree {
+ t := &Tree{
+ root: &Node{
+ mutateCh: make(chan struct{}),
+ },
+ }
+ return t
+}
+
+// Len is used to return the number of elements in the tree
+func (t *Tree) Len() int {
+ return t.size
+}
+
+// Txn is a transaction on the tree. This transaction is applied
+// atomically and returns a new tree when committed. A transaction
+// is not thread safe, and should only be used by a single goroutine.
+type Txn struct {
+ // root is the modified root for the transaction.
+ root *Node
+
+ // snap is a snapshot of the root node for use if we have to run the
+ // slow notify algorithm.
+ snap *Node
+
+ // size tracks the size of the tree as it is modified during the
+ // transaction.
+ size int
+
+ // writable is a cache of writable nodes that have been created during
+ // the course of the transaction. This allows us to re-use the same
+ // nodes for further writes and avoid unnecessary copies of nodes that
+ // have never been exposed outside the transaction. This will only hold
+ // up to defaultModifiedCache number of entries.
+ writable *simplelru.LRU
+
+ // trackChannels is used to hold channels that need to be notified to
+ // signal mutation of the tree. This will only hold up to
+ // defaultModifiedCache number of entries, after which we will set the
+ // trackOverflow flag, which will cause us to use a more expensive
+ // algorithm to perform the notifications. Mutation tracking is only
+ // performed if trackMutate is true.
+ trackChannels map[chan struct{}]struct{}
+ trackOverflow bool
+ trackMutate bool
+}
+
+// Txn starts a new transaction that can be used to mutate the tree
+func (t *Tree) Txn() *Txn {
+ txn := &Txn{
+ root: t.root,
+ snap: t.root,
+ size: t.size,
+ }
+ return txn
+}
+
+// TrackMutate can be used to toggle if mutations are tracked. If this is enabled
+// then notifications will be issued for affected internal nodes and leaves when
+// the transaction is committed.
+func (t *Txn) TrackMutate(track bool) {
+ t.trackMutate = track
+}
+
+// trackChannel safely attempts to track the given mutation channel, setting the
+// overflow flag if we can no longer track any more. This limits the amount of
+// state that will accumulate during a transaction and we have a slower algorithm
+// to switch to if we overflow.
+func (t *Txn) trackChannel(ch chan struct{}) {
+ // In overflow, make sure we don't store any more objects.
+ if t.trackOverflow {
+ return
+ }
+
+ // If this would overflow the state we reject it and set the flag (since
+ // we aren't tracking everything that's required any longer).
+ if len(t.trackChannels) >= defaultModifiedCache {
+ // Mark that we are in the overflow state
+ t.trackOverflow = true
+
+ // Clear the map so that the channels can be garbage collected. It is
+ // safe to do this since we have already overflowed and will be using
+ // the slow notify algorithm.
+ t.trackChannels = nil
+ return
+ }
+
+ // Create the map on the fly when we need it.
+ if t.trackChannels == nil {
+ t.trackChannels = make(map[chan struct{}]struct{})
+ }
+
+ // Otherwise we are good to track it.
+ t.trackChannels[ch] = struct{}{}
+}
+
+// writeNode returns a node to be modified, if the current node has already been
+// modified during the course of the transaction, it is used in-place. Set
+// forLeafUpdate to true if you are getting a write node to update the leaf,
+// which will set leaf mutation tracking appropriately as well.
+func (t *Txn) writeNode(n *Node, forLeafUpdate bool) *Node {
+ // Ensure the writable set exists.
+ if t.writable == nil {
+ lru, err := simplelru.NewLRU(defaultModifiedCache, nil)
+ if err != nil {
+ panic(err)
+ }
+ t.writable = lru
+ }
+
+ // If this node has already been modified, we can continue to use it
+ // during this transaction. We know that we don't need to track it for
+ // a node update since the node is writable, but if this is for a leaf
+ // update we track it, in case the initial write to this node didn't
+ // update the leaf.
+ if _, ok := t.writable.Get(n); ok {
+ if t.trackMutate && forLeafUpdate && n.leaf != nil {
+ t.trackChannel(n.leaf.mutateCh)
+ }
+ return n
+ }
+
+ // Mark this node as being mutated.
+ if t.trackMutate {
+ t.trackChannel(n.mutateCh)
+ }
+
+ // Mark its leaf as being mutated, if appropriate.
+ if t.trackMutate && forLeafUpdate && n.leaf != nil {
+ t.trackChannel(n.leaf.mutateCh)
+ }
+
+ // Copy the existing node. If you have set forLeafUpdate it will be
+ // safe to replace this leaf with another after you get your node for
+ // writing. You MUST replace it, because the channel associated with
+ // this leaf will be closed when this transaction is committed.
+ nc := &Node{
+ mutateCh: make(chan struct{}),
+ leaf: n.leaf,
+ }
+ if n.prefix != nil {
+ nc.prefix = make([]byte, len(n.prefix))
+ copy(nc.prefix, n.prefix)
+ }
+ if len(n.edges) != 0 {
+ nc.edges = make([]edge, len(n.edges))
+ copy(nc.edges, n.edges)
+ }
+
+ // Mark this node as writable.
+ t.writable.Add(nc, nil)
+ return nc
+}
+
+// Visit all the nodes in the tree under n, and add their mutateChannels to the transaction
+// Returns the size of the subtree visited
+func (t *Txn) trackChannelsAndCount(n *Node) int {
+ // Count only leaf nodes
+ leaves := 0
+ if n.leaf != nil {
+ leaves = 1
+ }
+ // Mark this node as being mutated.
+ if t.trackMutate {
+ t.trackChannel(n.mutateCh)
+ }
+
+ // Mark its leaf as being mutated, if appropriate.
+ if t.trackMutate && n.leaf != nil {
+ t.trackChannel(n.leaf.mutateCh)
+ }
+
+ // Recurse on the children
+ for _, e := range n.edges {
+ leaves += t.trackChannelsAndCount(e.node)
+ }
+ return leaves
+}
+
+// mergeChild is called to collapse the given node with its child. This is only
+// called when the given node is not a leaf and has a single edge.
+func (t *Txn) mergeChild(n *Node) {
+ // Mark the child node as being mutated since we are about to abandon
+ // it. We don't need to mark the leaf since we are retaining it if it
+ // is there.
+ e := n.edges[0]
+ child := e.node
+ if t.trackMutate {
+ t.trackChannel(child.mutateCh)
+ }
+
+ // Merge the nodes.
+ n.prefix = concat(n.prefix, child.prefix)
+ n.leaf = child.leaf
+ if len(child.edges) != 0 {
+ n.edges = make([]edge, len(child.edges))
+ copy(n.edges, child.edges)
+ } else {
+ n.edges = nil
+ }
+}
+
+// insert does a recursive insertion
+func (t *Txn) insert(n *Node, k, search []byte, v interface{}) (*Node, interface{}, bool) {
+ // Handle key exhaustion
+ if len(search) == 0 {
+ var oldVal interface{}
+ didUpdate := false
+ if n.isLeaf() {
+ oldVal = n.leaf.val
+ didUpdate = true
+ }
+
+ nc := t.writeNode(n, true)
+ nc.leaf = &leafNode{
+ mutateCh: make(chan struct{}),
+ key: k,
+ val: v,
+ }
+ return nc, oldVal, didUpdate
+ }
+
+ // Look for the edge
+ idx, child := n.getEdge(search[0])
+
+ // No edge, create one
+ if child == nil {
+ e := edge{
+ label: search[0],
+ node: &Node{
+ mutateCh: make(chan struct{}),
+ leaf: &leafNode{
+ mutateCh: make(chan struct{}),
+ key: k,
+ val: v,
+ },
+ prefix: search,
+ },
+ }
+ nc := t.writeNode(n, false)
+ nc.addEdge(e)
+ return nc, nil, false
+ }
+
+ // Determine longest prefix of the search key on match
+ commonPrefix := longestPrefix(search, child.prefix)
+ if commonPrefix == len(child.prefix) {
+ search = search[commonPrefix:]
+ newChild, oldVal, didUpdate := t.insert(child, k, search, v)
+ if newChild != nil {
+ nc := t.writeNode(n, false)
+ nc.edges[idx].node = newChild
+ return nc, oldVal, didUpdate
+ }
+ return nil, oldVal, didUpdate
+ }
+
+ // Split the node
+ nc := t.writeNode(n, false)
+ splitNode := &Node{
+ mutateCh: make(chan struct{}),
+ prefix: search[:commonPrefix],
+ }
+ nc.replaceEdge(edge{
+ label: search[0],
+ node: splitNode,
+ })
+
+ // Restore the existing child node
+ modChild := t.writeNode(child, false)
+ splitNode.addEdge(edge{
+ label: modChild.prefix[commonPrefix],
+ node: modChild,
+ })
+ modChild.prefix = modChild.prefix[commonPrefix:]
+
+ // Create a new leaf node
+ leaf := &leafNode{
+ mutateCh: make(chan struct{}),
+ key: k,
+ val: v,
+ }
+
+ // If the new key is a subset, add to to this node
+ search = search[commonPrefix:]
+ if len(search) == 0 {
+ splitNode.leaf = leaf
+ return nc, nil, false
+ }
+
+ // Create a new edge for the node
+ splitNode.addEdge(edge{
+ label: search[0],
+ node: &Node{
+ mutateCh: make(chan struct{}),
+ leaf: leaf,
+ prefix: search,
+ },
+ })
+ return nc, nil, false
+}
+
+// delete does a recursive deletion
+func (t *Txn) delete(parent, n *Node, search []byte) (*Node, *leafNode) {
+ // Check for key exhaustion
+ if len(search) == 0 {
+ if !n.isLeaf() {
+ return nil, nil
+ }
+
+ // Remove the leaf node
+ nc := t.writeNode(n, true)
+ nc.leaf = nil
+
+ // Check if this node should be merged
+ if n != t.root && len(nc.edges) == 1 {
+ t.mergeChild(nc)
+ }
+ return nc, n.leaf
+ }
+
+ // Look for an edge
+ label := search[0]
+ idx, child := n.getEdge(label)
+ if child == nil || !bytes.HasPrefix(search, child.prefix) {
+ return nil, nil
+ }
+
+ // Consume the search prefix
+ search = search[len(child.prefix):]
+ newChild, leaf := t.delete(n, child, search)
+ if newChild == nil {
+ return nil, nil
+ }
+
+ // Copy this node. WATCH OUT - it's safe to pass "false" here because we
+ // will only ADD a leaf via nc.mergeChild() if there isn't one due to
+ // the !nc.isLeaf() check in the logic just below. This is pretty subtle,
+ // so be careful if you change any of the logic here.
+ nc := t.writeNode(n, false)
+
+ // Delete the edge if the node has no edges
+ if newChild.leaf == nil && len(newChild.edges) == 0 {
+ nc.delEdge(label)
+ if n != t.root && len(nc.edges) == 1 && !nc.isLeaf() {
+ t.mergeChild(nc)
+ }
+ } else {
+ nc.edges[idx].node = newChild
+ }
+ return nc, leaf
+}
+
+// delete does a recursive deletion
+func (t *Txn) deletePrefix(parent, n *Node, search []byte) (*Node, int) {
+ // Check for key exhaustion
+ if len(search) == 0 {
+ nc := t.writeNode(n, true)
+ if n.isLeaf() {
+ nc.leaf = nil
+ }
+ nc.edges = nil
+ return nc, t.trackChannelsAndCount(n)
+ }
+
+ // Look for an edge
+ label := search[0]
+ idx, child := n.getEdge(label)
+ // We make sure that either the child node's prefix starts with the search term, or the search term starts with the child node's prefix
+ // Need to do both so that we can delete prefixes that don't correspond to any node in the tree
+ if child == nil || (!bytes.HasPrefix(child.prefix, search) && !bytes.HasPrefix(search, child.prefix)) {
+ return nil, 0
+ }
+
+ // Consume the search prefix
+ if len(child.prefix) > len(search) {
+ search = []byte("")
+ } else {
+ search = search[len(child.prefix):]
+ }
+ newChild, numDeletions := t.deletePrefix(n, child, search)
+ if newChild == nil {
+ return nil, 0
+ }
+ // Copy this node. WATCH OUT - it's safe to pass "false" here because we
+ // will only ADD a leaf via nc.mergeChild() if there isn't one due to
+ // the !nc.isLeaf() check in the logic just below. This is pretty subtle,
+ // so be careful if you change any of the logic here.
+
+ nc := t.writeNode(n, false)
+
+ // Delete the edge if the node has no edges
+ if newChild.leaf == nil && len(newChild.edges) == 0 {
+ nc.delEdge(label)
+ if n != t.root && len(nc.edges) == 1 && !nc.isLeaf() {
+ t.mergeChild(nc)
+ }
+ } else {
+ nc.edges[idx].node = newChild
+ }
+ return nc, numDeletions
+}
+
+// Insert is used to add or update a given key. The return provides
+// the previous value and a bool indicating if any was set.
+func (t *Txn) Insert(k []byte, v interface{}) (interface{}, bool) {
+ newRoot, oldVal, didUpdate := t.insert(t.root, k, k, v)
+ if newRoot != nil {
+ t.root = newRoot
+ }
+ if !didUpdate {
+ t.size++
+ }
+ return oldVal, didUpdate
+}
+
+// Delete is used to delete a given key. Returns the old value if any,
+// and a bool indicating if the key was set.
+func (t *Txn) Delete(k []byte) (interface{}, bool) {
+ newRoot, leaf := t.delete(nil, t.root, k)
+ if newRoot != nil {
+ t.root = newRoot
+ }
+ if leaf != nil {
+ t.size--
+ return leaf.val, true
+ }
+ return nil, false
+}
+
+// DeletePrefix is used to delete an entire subtree that matches the prefix
+// This will delete all nodes under that prefix
+func (t *Txn) DeletePrefix(prefix []byte) bool {
+ newRoot, numDeletions := t.deletePrefix(nil, t.root, prefix)
+ if newRoot != nil {
+ t.root = newRoot
+ t.size = t.size - numDeletions
+ return true
+ }
+ return false
+
+}
+
+// Root returns the current root of the radix tree within this
+// transaction. The root is not safe across insert and delete operations,
+// but can be used to read the current state during a transaction.
+func (t *Txn) Root() *Node {
+ return t.root
+}
+
+// Get is used to lookup a specific key, returning
+// the value and if it was found
+func (t *Txn) Get(k []byte) (interface{}, bool) {
+ return t.root.Get(k)
+}
+
+// GetWatch is used to lookup a specific key, returning
+// the watch channel, value and if it was found
+func (t *Txn) GetWatch(k []byte) (<-chan struct{}, interface{}, bool) {
+ return t.root.GetWatch(k)
+}
+
+// Commit is used to finalize the transaction and return a new tree. If mutation
+// tracking is turned on then notifications will also be issued.
+func (t *Txn) Commit() *Tree {
+ nt := t.CommitOnly()
+ if t.trackMutate {
+ t.Notify()
+ }
+ return nt
+}
+
+// CommitOnly is used to finalize the transaction and return a new tree, but
+// does not issue any notifications until Notify is called.
+func (t *Txn) CommitOnly() *Tree {
+ nt := &Tree{t.root, t.size}
+ t.writable = nil
+ return nt
+}
+
+// slowNotify does a complete comparison of the before and after trees in order
+// to trigger notifications. This doesn't require any additional state but it
+// is very expensive to compute.
+func (t *Txn) slowNotify() {
+ snapIter := t.snap.rawIterator()
+ rootIter := t.root.rawIterator()
+ for snapIter.Front() != nil || rootIter.Front() != nil {
+ // If we've exhausted the nodes in the old snapshot, we know
+ // there's nothing remaining to notify.
+ if snapIter.Front() == nil {
+ return
+ }
+ snapElem := snapIter.Front()
+
+ // If we've exhausted the nodes in the new root, we know we need
+ // to invalidate everything that remains in the old snapshot. We
+ // know from the loop condition there's something in the old
+ // snapshot.
+ if rootIter.Front() == nil {
+ close(snapElem.mutateCh)
+ if snapElem.isLeaf() {
+ close(snapElem.leaf.mutateCh)
+ }
+ snapIter.Next()
+ continue
+ }
+
+ // Do one string compare so we can check the various conditions
+ // below without repeating the compare.
+ cmp := strings.Compare(snapIter.Path(), rootIter.Path())
+
+ // If the snapshot is behind the root, then we must have deleted
+ // this node during the transaction.
+ if cmp < 0 {
+ close(snapElem.mutateCh)
+ if snapElem.isLeaf() {
+ close(snapElem.leaf.mutateCh)
+ }
+ snapIter.Next()
+ continue
+ }
+
+ // If the snapshot is ahead of the root, then we must have added
+ // this node during the transaction.
+ if cmp > 0 {
+ rootIter.Next()
+ continue
+ }
+
+ // If we have the same path, then we need to see if we mutated a
+ // node and possibly the leaf.
+ rootElem := rootIter.Front()
+ if snapElem != rootElem {
+ close(snapElem.mutateCh)
+ if snapElem.leaf != nil && (snapElem.leaf != rootElem.leaf) {
+ close(snapElem.leaf.mutateCh)
+ }
+ }
+ snapIter.Next()
+ rootIter.Next()
+ }
+}
+
+// Notify is used along with TrackMutate to trigger notifications. This must
+// only be done once a transaction is committed via CommitOnly, and it is called
+// automatically by Commit.
+func (t *Txn) Notify() {
+ if !t.trackMutate {
+ return
+ }
+
+ // If we've overflowed the tracking state we can't use it in any way and
+ // need to do a full tree compare.
+ if t.trackOverflow {
+ t.slowNotify()
+ } else {
+ for ch := range t.trackChannels {
+ close(ch)
+ }
+ }
+
+ // Clean up the tracking state so that a re-notify is safe (will trigger
+ // the else clause above which will be a no-op).
+ t.trackChannels = nil
+ t.trackOverflow = false
+}
+
+// Insert is used to add or update a given key. The return provides
+// the new tree, previous value and a bool indicating if any was set.
+func (t *Tree) Insert(k []byte, v interface{}) (*Tree, interface{}, bool) {
+ txn := t.Txn()
+ old, ok := txn.Insert(k, v)
+ return txn.Commit(), old, ok
+}
+
+// Delete is used to delete a given key. Returns the new tree,
+// old value if any, and a bool indicating if the key was set.
+func (t *Tree) Delete(k []byte) (*Tree, interface{}, bool) {
+ txn := t.Txn()
+ old, ok := txn.Delete(k)
+ return txn.Commit(), old, ok
+}
+
+// DeletePrefix is used to delete all nodes starting with a given prefix. Returns the new tree,
+// and a bool indicating if the prefix matched any nodes
+func (t *Tree) DeletePrefix(k []byte) (*Tree, bool) {
+ txn := t.Txn()
+ ok := txn.DeletePrefix(k)
+ return txn.Commit(), ok
+}
+
+// Root returns the root node of the tree which can be used for richer
+// query operations.
+func (t *Tree) Root() *Node {
+ return t.root
+}
+
+// Get is used to lookup a specific key, returning
+// the value and if it was found
+func (t *Tree) Get(k []byte) (interface{}, bool) {
+ return t.root.Get(k)
+}
+
+// longestPrefix finds the length of the shared prefix
+// of two strings
+func longestPrefix(k1, k2 []byte) int {
+ max := len(k1)
+ if l := len(k2); l < max {
+ max = l
+ }
+ var i int
+ for i = 0; i < max; i++ {
+ if k1[i] != k2[i] {
+ break
+ }
+ }
+ return i
+}
+
+// concat two byte slices, returning a third new copy
+func concat(a, b []byte) []byte {
+ c := make([]byte, len(a)+len(b))
+ copy(c, a)
+ copy(c[len(a):], b)
+ return c
+}
diff --git a/vendor/github.com/hashicorp/go-immutable-radix/iradix_test.go b/vendor/github.com/hashicorp/go-immutable-radix/iradix_test.go
new file mode 100644
index 000000000..94d7578a3
--- /dev/null
+++ b/vendor/github.com/hashicorp/go-immutable-radix/iradix_test.go
@@ -0,0 +1,1490 @@
+package iradix
+
+import (
+ "fmt"
+ "reflect"
+ "sort"
+ "testing"
+
+ "github.com/hashicorp/uuid"
+)
+
+func CopyTree(t *Tree) *Tree {
+ nt := &Tree{
+ root: CopyNode(t.root),
+ size: t.size,
+ }
+ return nt
+}
+
+func CopyNode(n *Node) *Node {
+ nn := &Node{}
+ if n.mutateCh != nil {
+ nn.mutateCh = n.mutateCh
+ }
+ if n.prefix != nil {
+ nn.prefix = make([]byte, len(n.prefix))
+ copy(nn.prefix, n.prefix)
+ }
+ if n.leaf != nil {
+ nn.leaf = CopyLeaf(n.leaf)
+ }
+ if len(n.edges) != 0 {
+ nn.edges = make([]edge, len(n.edges))
+ for idx, edge := range n.edges {
+ nn.edges[idx].label = edge.label
+ nn.edges[idx].node = CopyNode(edge.node)
+ }
+ }
+ return nn
+}
+
+func CopyLeaf(l *leafNode) *leafNode {
+ ll := &leafNode{
+ mutateCh: l.mutateCh,
+ key: l.key,
+ val: l.val,
+ }
+ return ll
+}
+
+func TestRadix_HugeTxn(t *testing.T) {
+ r := New()
+
+ // Insert way more nodes than the cache can fit
+ txn1 := r.Txn()
+ var expect []string
+ for i := 0; i < defaultModifiedCache*100; i++ {
+ gen := uuid.GenerateUUID()
+ txn1.Insert([]byte(gen), i)
+ expect = append(expect, gen)
+ }
+ r = txn1.Commit()
+ sort.Strings(expect)
+
+ // Collect the output, should be sorted
+ var out []string
+ fn := func(k []byte, v interface{}) bool {
+ out = append(out, string(k))
+ return false
+ }
+ r.Root().Walk(fn)
+
+ // Verify the match
+ if len(out) != len(expect) {
+ t.Fatalf("length mis-match: %d vs %d", len(out), len(expect))
+ }
+ for i := 0; i < len(out); i++ {
+ if out[i] != expect[i] {
+ t.Fatalf("mis-match: %v %v", out[i], expect[i])
+ }
+ }
+}
+
+func TestRadix(t *testing.T) {
+ var min, max string
+ inp := make(map[string]interface{})
+ for i := 0; i < 1000; i++ {
+ gen := uuid.GenerateUUID()
+ inp[gen] = i
+ if gen < min || i == 0 {
+ min = gen
+ }
+ if gen > max || i == 0 {
+ max = gen
+ }
+ }
+
+ r := New()
+ rCopy := CopyTree(r)
+ for k, v := range inp {
+ newR, _, _ := r.Insert([]byte(k), v)
+ if !reflect.DeepEqual(r, rCopy) {
+ t.Errorf("r: %#v rc: %#v", r, rCopy)
+ t.Errorf("r: %#v rc: %#v", r.root, rCopy.root)
+ t.Fatalf("structure modified %d", newR.Len())
+ }
+ r = newR
+ rCopy = CopyTree(r)
+ }
+
+ if r.Len() != len(inp) {
+ t.Fatalf("bad length: %v %v", r.Len(), len(inp))
+ }
+
+ for k, v := range inp {
+ out, ok := r.Get([]byte(k))
+ if !ok {
+ t.Fatalf("missing key: %v", k)
+ }
+ if out != v {
+ t.Fatalf("value mis-match: %v %v", out, v)
+ }
+ }
+
+ // Check min and max
+ outMin, _, _ := r.Root().Minimum()
+ if string(outMin) != min {
+ t.Fatalf("bad minimum: %v %v", outMin, min)
+ }
+ outMax, _, _ := r.Root().Maximum()
+ if string(outMax) != max {
+ t.Fatalf("bad maximum: %v %v", outMax, max)
+ }
+
+ // Copy the full tree before delete
+ orig := r
+ origCopy := CopyTree(r)
+
+ for k, v := range inp {
+ tree, out, ok := r.Delete([]byte(k))
+ r = tree
+ if !ok {
+ t.Fatalf("missing key: %v", k)
+ }
+ if out != v {
+ t.Fatalf("value mis-match: %v %v", out, v)
+ }
+ }
+ if r.Len() != 0 {
+ t.Fatalf("bad length: %v", r.Len())
+ }
+
+ if !reflect.DeepEqual(orig, origCopy) {
+ t.Fatalf("structure modified")
+ }
+}
+
+func TestRoot(t *testing.T) {
+ r := New()
+ r, _, ok := r.Delete(nil)
+ if ok {
+ t.Fatalf("bad")
+ }
+ r, _, ok = r.Insert(nil, true)
+ if ok {
+ t.Fatalf("bad")
+ }
+ val, ok := r.Get(nil)
+ if !ok || val != true {
+ t.Fatalf("bad: %v %#v", val)
+ }
+ r, val, ok = r.Delete(nil)
+ if !ok || val != true {
+ t.Fatalf("bad: %v", val)
+ }
+}
+
+func TestInsert_UpdateFeedback(t *testing.T) {
+ r := New()
+ txn1 := r.Txn()
+
+ for i := 0; i < 10; i++ {
+ var old interface{}
+ var didUpdate bool
+ old, didUpdate = txn1.Insert([]byte("helloworld"), i)
+ if i == 0 {
+ if old != nil || didUpdate {
+ t.Fatalf("bad: %d %v %v", i, old, didUpdate)
+ }
+ } else {
+ if old == nil || old.(int) != i-1 || !didUpdate {
+ t.Fatalf("bad: %d %v %v", i, old, didUpdate)
+ }
+ }
+ }
+}
+
+func TestDelete(t *testing.T) {
+ r := New()
+ s := []string{"", "A", "AB"}
+
+ for _, ss := range s {
+ r, _, _ = r.Insert([]byte(ss), true)
+ }
+ var ok bool
+ for _, ss := range s {
+ r, _, ok = r.Delete([]byte(ss))
+ if !ok {
+ t.Fatalf("bad %q", ss)
+ }
+ }
+}
+
+func TestDeletePrefix(t *testing.T) {
+
+ type exp struct {
+ desc string
+ treeNodes []string
+ prefix string
+ expectedOut []string
+ }
+
+ //various test cases where DeletePrefix should succeed
+ cases := []exp{
+ {
+ "prefix not a node in tree",
+ []string{
+ "",
+ "test/test1",
+ "test/test2",
+ "test/test3",
+ "R",
+ "RA"},
+ "test",
+ []string{
+ "",
+ "R",
+ "RA",
+ },
+ },
+ {
+ "prefix matches a node in tree",
+ []string{
+ "",
+ "test",
+ "test/test1",
+ "test/test2",
+ "test/test3",
+ "test/testAAA",
+ "R",
+ "RA",
+ },
+ "test",
+ []string{
+ "",
+ "R",
+ "RA",
+ },
+ },
+ {
+ "longer prefix, but prefix is not a node in tree",
+ []string{
+ "",
+ "test/test1",
+ "test/test2",
+ "test/test3",
+ "test/testAAA",
+ "R",
+ "RA",
+ },
+ "test/test",
+ []string{
+ "",
+ "R",
+ "RA",
+ },
+ },
+ {
+ "prefix only matches one node",
+ []string{
+ "",
+ "AB",
+ "ABC",
+ "AR",
+ "R",
+ "RA",
+ },
+ "AR",
+ []string{
+ "",
+ "AB",
+ "ABC",
+ "R",
+ "RA",
+ },
+ },
+ }
+
+ for _, testCase := range cases {
+ t.Run(testCase.desc, func(t *testing.T) {
+ r := New()
+ for _, ss := range testCase.treeNodes {
+ r, _, _ = r.Insert([]byte(ss), true)
+ }
+ if got, want := r.Len(), len(testCase.treeNodes); got != want {
+ t.Fatalf("Unexpected tree length after insert, got %d want %d ", got, want)
+ }
+ r, ok := r.DeletePrefix([]byte(testCase.prefix))
+ if !ok {
+ t.Fatalf("DeletePrefix should have returned true for tree %v, deleting prefix %v", testCase.treeNodes, testCase.prefix)
+ }
+ if got, want := r.Len(), len(testCase.expectedOut); got != want {
+ t.Fatalf("Bad tree length, got %d want %d tree %v, deleting prefix %v ", got, want, testCase.treeNodes, testCase.prefix)
+ }
+
+ verifyTree(t, testCase.expectedOut, r)
+ //Delete a non-existant node
+ r, ok = r.DeletePrefix([]byte("CCCCC"))
+ if ok {
+ t.Fatalf("Expected DeletePrefix to return false ")
+ }
+ verifyTree(t, testCase.expectedOut, r)
+ })
+ }
+}
+
+func TestTrackMutate_DeletePrefix(t *testing.T) {
+
+ r := New()
+
+ keys := []string{
+ "foo",
+ "foo/bar/baz",
+ "foo/baz/bar",
+ "foo/zip/zap",
+ "bazbaz",
+ "zipzap",
+ }
+ for _, k := range keys {
+ r, _, _ = r.Insert([]byte(k), nil)
+ }
+ if r.Len() != len(keys) {
+ t.Fatalf("bad len: %v %v", r.Len(), len(keys))
+ }
+
+ rootWatch, _, _ := r.Root().GetWatch(nil)
+ if rootWatch == nil {
+ t.Fatalf("Should have returned a watch")
+ }
+
+ nodeWatch1, _, _ := r.Root().GetWatch([]byte("foo/bar/baz"))
+ if nodeWatch1 == nil {
+ t.Fatalf("Should have returned a watch")
+ }
+
+ nodeWatch2, _, _ := r.Root().GetWatch([]byte("foo/baz/bar"))
+ if nodeWatch2 == nil {
+ t.Fatalf("Should have returned a watch")
+ }
+
+ nodeWatch3, _, _ := r.Root().GetWatch([]byte("foo/zip/zap"))
+ if nodeWatch3 == nil {
+ t.Fatalf("Should have returned a watch")
+ }
+
+ unknownNodeWatch, _, _ := r.Root().GetWatch([]byte("bazbaz"))
+ if unknownNodeWatch == nil {
+ t.Fatalf("Should have returned a watch")
+ }
+
+ // Verify that deleting prefixes triggers the right set of watches
+ txn := r.Txn()
+ txn.TrackMutate(true)
+ ok := txn.DeletePrefix([]byte("foo"))
+ if !ok {
+ t.Fatalf("Expected delete prefix to return true")
+ }
+ if hasAnyClosedMutateCh(r) {
+ t.Fatalf("Transaction was not committed, no channel should have been closed")
+ }
+
+ txn.Commit()
+
+ // Verify that all the leaf nodes we set up watches for above get triggered from the delete prefix call
+ select {
+ case <-rootWatch:
+ default:
+ t.Fatalf("root watch was not triggered")
+ }
+ select {
+ case <-nodeWatch1:
+ default:
+ t.Fatalf("node watch was not triggered")
+ }
+ select {
+ case <-nodeWatch2:
+ default:
+ t.Fatalf("node watch was not triggered")
+ }
+ select {
+ case <-nodeWatch3:
+ default:
+ t.Fatalf("node watch was not triggered")
+ }
+ select {
+ case <-unknownNodeWatch:
+ t.Fatalf("Unrelated node watch was triggered during a prefix delete")
+ default:
+ }
+
+}
+
+func verifyTree(t *testing.T, expected []string, r *Tree) {
+ root := r.Root()
+ out := []string{}
+ fn := func(k []byte, v interface{}) bool {
+ out = append(out, string(k))
+ return false
+ }
+ root.Walk(fn)
+
+ if !reflect.DeepEqual(expected, out) {
+ t.Fatalf("Unexpected contents of tree after delete prefix: expected %v, but got %v", expected, out)
+ }
+}
+
+func TestLongestPrefix(t *testing.T) {
+ r := New()
+
+ keys := []string{
+ "",
+ "foo",
+ "foobar",
+ "foobarbaz",
+ "foobarbazzip",
+ "foozip",
+ }
+ for _, k := range keys {
+ r, _, _ = r.Insert([]byte(k), nil)
+ }
+ if r.Len() != len(keys) {
+ t.Fatalf("bad len: %v %v", r.Len(), len(keys))
+ }
+
+ type exp struct {
+ inp string
+ out string
+ }
+ cases := []exp{
+ {"a", ""},
+ {"abc", ""},
+ {"fo", ""},
+ {"foo", "foo"},
+ {"foob", "foo"},
+ {"foobar", "foobar"},
+ {"foobarba", "foobar"},
+ {"foobarbaz", "foobarbaz"},
+ {"foobarbazzi", "foobarbaz"},
+ {"foobarbazzip", "foobarbazzip"},
+ {"foozi", "foo"},
+ {"foozip", "foozip"},
+ {"foozipzap", "foozip"},
+ }
+ root := r.Root()
+ for _, test := range cases {
+ m, _, ok := root.LongestPrefix([]byte(test.inp))
+ if !ok {
+ t.Fatalf("no match: %v", test)
+ }
+ if string(m) != test.out {
+ t.Fatalf("mis-match: %v %v", m, test)
+ }
+ }
+}
+
+func TestWalkPrefix(t *testing.T) {
+ r := New()
+
+ keys := []string{
+ "foobar",
+ "foo/bar/baz",
+ "foo/baz/bar",
+ "foo/zip/zap",
+ "zipzap",
+ }
+ for _, k := range keys {
+ r, _, _ = r.Insert([]byte(k), nil)
+ }
+ if r.Len() != len(keys) {
+ t.Fatalf("bad len: %v %v", r.Len(), len(keys))
+ }
+
+ type exp struct {
+ inp string
+ out []string
+ }
+ cases := []exp{
+ exp{
+ "f",
+ []string{"foobar", "foo/bar/baz", "foo/baz/bar", "foo/zip/zap"},
+ },
+ exp{
+ "foo",
+ []string{"foobar", "foo/bar/baz", "foo/baz/bar", "foo/zip/zap"},
+ },
+ exp{
+ "foob",
+ []string{"foobar"},
+ },
+ exp{
+ "foo/",
+ []string{"foo/bar/baz", "foo/baz/bar", "foo/zip/zap"},
+ },
+ exp{
+ "foo/b",
+ []string{"foo/bar/baz", "foo/baz/bar"},
+ },
+ exp{
+ "foo/ba",
+ []string{"foo/bar/baz", "foo/baz/bar"},
+ },
+ exp{
+ "foo/bar",
+ []string{"foo/bar/baz"},
+ },
+ exp{
+ "foo/bar/baz",
+ []string{"foo/bar/baz"},
+ },
+ exp{
+ "foo/bar/bazoo",
+ []string{},
+ },
+ exp{
+ "z",
+ []string{"zipzap"},
+ },
+ }
+
+ root := r.Root()
+ for _, test := range cases {
+ out := []string{}
+ fn := func(k []byte, v interface{}) bool {
+ out = append(out, string(k))
+ return false
+ }
+ root.WalkPrefix([]byte(test.inp), fn)
+ sort.Strings(out)
+ sort.Strings(test.out)
+ if !reflect.DeepEqual(out, test.out) {
+ t.Fatalf("mis-match: %v %v", out, test.out)
+ }
+ }
+}
+
+func TestWalkPath(t *testing.T) {
+ r := New()
+
+ keys := []string{
+ "foo",
+ "foo/bar",
+ "foo/bar/baz",
+ "foo/baz/bar",
+ "foo/zip/zap",
+ "zipzap",
+ }
+ for _, k := range keys {
+ r, _, _ = r.Insert([]byte(k), nil)
+ }
+ if r.Len() != len(keys) {
+ t.Fatalf("bad len: %v %v", r.Len(), len(keys))
+ }
+
+ type exp struct {
+ inp string
+ out []string
+ }
+ cases := []exp{
+ exp{
+ "f",
+ []string{},
+ },
+ exp{
+ "foo",
+ []string{"foo"},
+ },
+ exp{
+ "foo/",
+ []string{"foo"},
+ },
+ exp{
+ "foo/ba",
+ []string{"foo"},
+ },
+ exp{
+ "foo/bar",
+ []string{"foo", "foo/bar"},
+ },
+ exp{
+ "foo/bar/baz",
+ []string{"foo", "foo/bar", "foo/bar/baz"},
+ },
+ exp{
+ "foo/bar/bazoo",
+ []string{"foo", "foo/bar", "foo/bar/baz"},
+ },
+ exp{
+ "z",
+ []string{},
+ },
+ }
+
+ root := r.Root()
+ for _, test := range cases {
+ out := []string{}
+ fn := func(k []byte, v interface{}) bool {
+ out = append(out, string(k))
+ return false
+ }
+ root.WalkPath([]byte(test.inp), fn)
+ sort.Strings(out)
+ sort.Strings(test.out)
+ if !reflect.DeepEqual(out, test.out) {
+ t.Fatalf("mis-match: %v %v", out, test.out)
+ }
+ }
+}
+
+func TestIteratePrefix(t *testing.T) {
+ r := New()
+
+ keys := []string{
+ "foo/bar/baz",
+ "foo/baz/bar",
+ "foo/zip/zap",
+ "foobar",
+ "zipzap",
+ }
+ for _, k := range keys {
+ r, _, _ = r.Insert([]byte(k), nil)
+ }
+ if r.Len() != len(keys) {
+ t.Fatalf("bad len: %v %v", r.Len(), len(keys))
+ }
+
+ type exp struct {
+ inp string
+ out []string
+ }
+ cases := []exp{
+ exp{
+ "",
+ keys,
+ },
+ exp{
+ "f",
+ []string{
+ "foo/bar/baz",
+ "foo/baz/bar",
+ "foo/zip/zap",
+ "foobar",
+ },
+ },
+ exp{
+ "foo",
+ []string{
+ "foo/bar/baz",
+ "foo/baz/bar",
+ "foo/zip/zap",
+ "foobar",
+ },
+ },
+ exp{
+ "foob",
+ []string{"foobar"},
+ },
+ exp{
+ "foo/",
+ []string{"foo/bar/baz", "foo/baz/bar", "foo/zip/zap"},
+ },
+ exp{
+ "foo/b",
+ []string{"foo/bar/baz", "foo/baz/bar"},
+ },
+ exp{
+ "foo/ba",
+ []string{"foo/bar/baz", "foo/baz/bar"},
+ },
+ exp{
+ "foo/bar",
+ []string{"foo/bar/baz"},
+ },
+ exp{
+ "foo/bar/baz",
+ []string{"foo/bar/baz"},
+ },
+ exp{
+ "foo/bar/bazoo",
+ []string{},
+ },
+ exp{
+ "z",
+ []string{"zipzap"},
+ },
+ }
+
+ root := r.Root()
+ for idx, test := range cases {
+ iter := root.Iterator()
+ if test.inp != "" {
+ iter.SeekPrefix([]byte(test.inp))
+ }
+
+ // Consume all the keys
+ out := []string{}
+ for {
+ key, _, ok := iter.Next()
+ if !ok {
+ break
+ }
+ out = append(out, string(key))
+ }
+ if !reflect.DeepEqual(out, test.out) {
+ t.Fatalf("mis-match: %d %v %v", idx, out, test.out)
+ }
+ }
+}
+
+func TestMergeChildNilEdges(t *testing.T) {
+ r := New()
+ r, _, _ = r.Insert([]byte("foobar"), 42)
+ r, _, _ = r.Insert([]byte("foozip"), 43)
+ r, _, _ = r.Delete([]byte("foobar"))
+
+ root := r.Root()
+ out := []string{}
+ fn := func(k []byte, v interface{}) bool {
+ out = append(out, string(k))
+ return false
+ }
+ root.Walk(fn)
+
+ expect := []string{"foozip"}
+ sort.Strings(out)
+ sort.Strings(expect)
+ if !reflect.DeepEqual(out, expect) {
+ t.Fatalf("mis-match: %v %v", out, expect)
+ }
+}
+
+func TestMergeChildVisibility(t *testing.T) {
+ r := New()
+ r, _, _ = r.Insert([]byte("foobar"), 42)
+ r, _, _ = r.Insert([]byte("foobaz"), 43)
+ r, _, _ = r.Insert([]byte("foozip"), 10)
+
+ txn1 := r.Txn()
+ txn2 := r.Txn()
+
+ // Ensure we get the expected value foobar and foobaz
+ if val, ok := txn1.Get([]byte("foobar")); !ok || val != 42 {
+ t.Fatalf("bad: %v", val)
+ }
+ if val, ok := txn1.Get([]byte("foobaz")); !ok || val != 43 {
+ t.Fatalf("bad: %v", val)
+ }
+ if val, ok := txn2.Get([]byte("foobar")); !ok || val != 42 {
+ t.Fatalf("bad: %v", val)
+ }
+ if val, ok := txn2.Get([]byte("foobaz")); !ok || val != 43 {
+ t.Fatalf("bad: %v", val)
+ }
+
+ // Delete of foozip will cause a merge child between the
+ // "foo" and "ba" nodes.
+ if val, ok := txn2.Delete([]byte("foozip")); !ok || val != 10 {
+ t.Fatalf("bad: %v", val)
+ }
+
+ // Insert of "foobaz" will update the slice of the "fooba" node
+ // in-place to point to the new "foobaz" node. This in-place update
+ // will cause the visibility of the update to leak into txn1 (prior
+ // to the fix).
+ if val, ok := txn2.Insert([]byte("foobaz"), 44); !ok || val != 43 {
+ t.Fatalf("bad: %v", val)
+ }
+
+ // Ensure we get the expected value foobar and foobaz
+ if val, ok := txn1.Get([]byte("foobar")); !ok || val != 42 {
+ t.Fatalf("bad: %v", val)
+ }
+ if val, ok := txn1.Get([]byte("foobaz")); !ok || val != 43 {
+ t.Fatalf("bad: %v", val)
+ }
+ if val, ok := txn2.Get([]byte("foobar")); !ok || val != 42 {
+ t.Fatalf("bad: %v", val)
+ }
+ if val, ok := txn2.Get([]byte("foobaz")); !ok || val != 44 {
+ t.Fatalf("bad: %v", val)
+ }
+
+ // Commit txn2
+ r = txn2.Commit()
+
+ // Ensure we get the expected value foobar and foobaz
+ if val, ok := txn1.Get([]byte("foobar")); !ok || val != 42 {
+ t.Fatalf("bad: %v", val)
+ }
+ if val, ok := txn1.Get([]byte("foobaz")); !ok || val != 43 {
+ t.Fatalf("bad: %v", val)
+ }
+ if val, ok := r.Get([]byte("foobar")); !ok || val != 42 {
+ t.Fatalf("bad: %v", val)
+ }
+ if val, ok := r.Get([]byte("foobaz")); !ok || val != 44 {
+ t.Fatalf("bad: %v", val)
+ }
+}
+
+// isClosed returns true if the given channel is closed.
+func isClosed(ch chan struct{}) bool {
+ select {
+ case <-ch:
+ return true
+ default:
+ return false
+ }
+}
+
+// hasAnyClosedMutateCh scans the given tree and returns true if there are any
+// closed mutate channels on any nodes or leaves.
+func hasAnyClosedMutateCh(r *Tree) bool {
+ for iter := r.root.rawIterator(); iter.Front() != nil; iter.Next() {
+ n := iter.Front()
+ if isClosed(n.mutateCh) {
+ return true
+ }
+ if n.isLeaf() && isClosed(n.leaf.mutateCh) {
+ return true
+ }
+ }
+ return false
+}
+
+func TestTrackMutate_SeekPrefixWatch(t *testing.T) {
+ for i := 0; i < 3; i++ {
+ r := New()
+
+ keys := []string{
+ "foo/bar/baz",
+ "foo/baz/bar",
+ "foo/zip/zap",
+ "foobar",
+ "zipzap",
+ }
+ for _, k := range keys {
+ r, _, _ = r.Insert([]byte(k), nil)
+ }
+ if r.Len() != len(keys) {
+ t.Fatalf("bad len: %v %v", r.Len(), len(keys))
+ }
+
+ iter := r.Root().Iterator()
+ rootWatch := iter.SeekPrefixWatch([]byte("nope"))
+
+ iter = r.Root().Iterator()
+ parentWatch := iter.SeekPrefixWatch([]byte("foo"))
+
+ iter = r.Root().Iterator()
+ leafWatch := iter.SeekPrefixWatch([]byte("foobar"))
+
+ iter = r.Root().Iterator()
+ missingWatch := iter.SeekPrefixWatch([]byte("foobarbaz"))
+
+ iter = r.Root().Iterator()
+ otherWatch := iter.SeekPrefixWatch([]byte("foo/b"))
+
+ // Write to a sub-child should trigger the leaf!
+ txn := r.Txn()
+ txn.TrackMutate(true)
+ txn.Insert([]byte("foobarbaz"), nil)
+ switch i {
+ case 0:
+ r = txn.Commit()
+ case 1:
+ r = txn.CommitOnly()
+ txn.Notify()
+ default:
+ r = txn.CommitOnly()
+ txn.slowNotify()
+ }
+ if hasAnyClosedMutateCh(r) {
+ t.Fatalf("bad")
+ }
+
+ // Verify root and parent triggered, and leaf affected
+ select {
+ case <-rootWatch:
+ default:
+ t.Fatalf("bad")
+ }
+ select {
+ case <-parentWatch:
+ default:
+ t.Fatalf("bad")
+ }
+ select {
+ case <-leafWatch:
+ default:
+ t.Fatalf("bad")
+ }
+ select {
+ case <-missingWatch:
+ default:
+ t.Fatalf("bad")
+ }
+ select {
+ case <-otherWatch:
+ t.Fatalf("bad")
+ default:
+ }
+
+ iter = r.Root().Iterator()
+ rootWatch = iter.SeekPrefixWatch([]byte("nope"))
+
+ iter = r.Root().Iterator()
+ parentWatch = iter.SeekPrefixWatch([]byte("foo"))
+
+ iter = r.Root().Iterator()
+ leafWatch = iter.SeekPrefixWatch([]byte("foobar"))
+
+ iter = r.Root().Iterator()
+ missingWatch = iter.SeekPrefixWatch([]byte("foobarbaz"))
+
+ // Delete to a sub-child should trigger the leaf!
+ txn = r.Txn()
+ txn.TrackMutate(true)
+ txn.Delete([]byte("foobarbaz"))
+ switch i {
+ case 0:
+ r = txn.Commit()
+ case 1:
+ r = txn.CommitOnly()
+ txn.Notify()
+ default:
+ r = txn.CommitOnly()
+ txn.slowNotify()
+ }
+ if hasAnyClosedMutateCh(r) {
+ t.Fatalf("bad")
+ }
+
+ // Verify root and parent triggered, and leaf affected
+ select {
+ case <-rootWatch:
+ default:
+ t.Fatalf("bad")
+ }
+ select {
+ case <-parentWatch:
+ default:
+ t.Fatalf("bad")
+ }
+ select {
+ case <-leafWatch:
+ default:
+ t.Fatalf("bad")
+ }
+ select {
+ case <-missingWatch:
+ default:
+ t.Fatalf("bad")
+ }
+ select {
+ case <-otherWatch:
+ t.Fatalf("bad")
+ default:
+ }
+ }
+}
+
+func TestTrackMutate_GetWatch(t *testing.T) {
+ for i := 0; i < 3; i++ {
+ r := New()
+
+ keys := []string{
+ "foo/bar/baz",
+ "foo/baz/bar",
+ "foo/zip/zap",
+ "foobar",
+ "zipzap",
+ }
+ for _, k := range keys {
+ r, _, _ = r.Insert([]byte(k), nil)
+ }
+ if r.Len() != len(keys) {
+ t.Fatalf("bad len: %v %v", r.Len(), len(keys))
+ }
+
+ rootWatch, _, ok := r.Root().GetWatch(nil)
+ if rootWatch == nil {
+ t.Fatalf("bad")
+ }
+
+ parentWatch, _, ok := r.Root().GetWatch([]byte("foo"))
+ if parentWatch == nil {
+ t.Fatalf("bad")
+ }
+
+ leafWatch, _, ok := r.Root().GetWatch([]byte("foobar"))
+ if !ok {
+ t.Fatalf("should be found")
+ }
+ if leafWatch == nil {
+ t.Fatalf("bad")
+ }
+
+ otherWatch, _, ok := r.Root().GetWatch([]byte("foo/b"))
+ if otherWatch == nil {
+ t.Fatalf("bad")
+ }
+
+ // Write to a sub-child should not trigger the leaf!
+ txn := r.Txn()
+ txn.TrackMutate(true)
+ txn.Insert([]byte("foobarbaz"), nil)
+ switch i {
+ case 0:
+ r = txn.Commit()
+ case 1:
+ r = txn.CommitOnly()
+ txn.Notify()
+ default:
+ r = txn.CommitOnly()
+ txn.slowNotify()
+ }
+ if hasAnyClosedMutateCh(r) {
+ t.Fatalf("bad")
+ }
+
+ // Verify root and parent triggered, not leaf affected
+ select {
+ case <-rootWatch:
+ default:
+ t.Fatalf("bad")
+ }
+ select {
+ case <-parentWatch:
+ default:
+ t.Fatalf("bad")
+ }
+ select {
+ case <-leafWatch:
+ t.Fatalf("bad")
+ default:
+ }
+ select {
+ case <-otherWatch:
+ t.Fatalf("bad")
+ default:
+ }
+
+ // Setup new watchers
+ rootWatch, _, ok = r.Root().GetWatch(nil)
+ if rootWatch == nil {
+ t.Fatalf("bad")
+ }
+
+ parentWatch, _, ok = r.Root().GetWatch([]byte("foo"))
+ if parentWatch == nil {
+ t.Fatalf("bad")
+ }
+
+ // Write to a exactly leaf should trigger the leaf!
+ txn = r.Txn()
+ txn.TrackMutate(true)
+ txn.Insert([]byte("foobar"), nil)
+ switch i {
+ case 0:
+ r = txn.Commit()
+ case 1:
+ r = txn.CommitOnly()
+ txn.Notify()
+ default:
+ r = txn.CommitOnly()
+ txn.slowNotify()
+ }
+ if hasAnyClosedMutateCh(r) {
+ t.Fatalf("bad")
+ }
+
+ select {
+ case <-rootWatch:
+ default:
+ t.Fatalf("bad")
+ }
+ select {
+ case <-parentWatch:
+ default:
+ t.Fatalf("bad")
+ }
+ select {
+ case <-leafWatch:
+ default:
+ t.Fatalf("bad")
+ }
+ select {
+ case <-otherWatch:
+ t.Fatalf("bad")
+ default:
+ }
+
+ // Setup all the watchers again
+ rootWatch, _, ok = r.Root().GetWatch(nil)
+ if rootWatch == nil {
+ t.Fatalf("bad")
+ }
+
+ parentWatch, _, ok = r.Root().GetWatch([]byte("foo"))
+ if parentWatch == nil {
+ t.Fatalf("bad")
+ }
+
+ leafWatch, _, ok = r.Root().GetWatch([]byte("foobar"))
+ if !ok {
+ t.Fatalf("should be found")
+ }
+ if leafWatch == nil {
+ t.Fatalf("bad")
+ }
+
+ // Delete to a sub-child should not trigger the leaf!
+ txn = r.Txn()
+ txn.TrackMutate(true)
+ txn.Delete([]byte("foobarbaz"))
+ switch i {
+ case 0:
+ r = txn.Commit()
+ case 1:
+ r = txn.CommitOnly()
+ txn.Notify()
+ default:
+ r = txn.CommitOnly()
+ txn.slowNotify()
+ }
+ if hasAnyClosedMutateCh(r) {
+ t.Fatalf("bad")
+ }
+
+ // Verify root and parent triggered, not leaf affected
+ select {
+ case <-rootWatch:
+ default:
+ t.Fatalf("bad")
+ }
+ select {
+ case <-parentWatch:
+ default:
+ t.Fatalf("bad")
+ }
+ select {
+ case <-leafWatch:
+ t.Fatalf("bad")
+ default:
+ }
+ select {
+ case <-otherWatch:
+ t.Fatalf("bad")
+ default:
+ }
+
+ // Setup new watchers
+ rootWatch, _, ok = r.Root().GetWatch(nil)
+ if rootWatch == nil {
+ t.Fatalf("bad")
+ }
+
+ parentWatch, _, ok = r.Root().GetWatch([]byte("foo"))
+ if parentWatch == nil {
+ t.Fatalf("bad")
+ }
+
+ // Write to a exactly leaf should trigger the leaf!
+ txn = r.Txn()
+ txn.TrackMutate(true)
+ txn.Delete([]byte("foobar"))
+ switch i {
+ case 0:
+ r = txn.Commit()
+ case 1:
+ r = txn.CommitOnly()
+ txn.Notify()
+ default:
+ r = txn.CommitOnly()
+ txn.slowNotify()
+ }
+ if hasAnyClosedMutateCh(r) {
+ t.Fatalf("bad")
+ }
+
+ select {
+ case <-rootWatch:
+ default:
+ t.Fatalf("bad")
+ }
+ select {
+ case <-parentWatch:
+ default:
+ t.Fatalf("bad")
+ }
+ select {
+ case <-leafWatch:
+ default:
+ t.Fatalf("bad")
+ }
+ select {
+ case <-otherWatch:
+ t.Fatalf("bad")
+ default:
+ }
+ }
+}
+
+func TestTrackMutate_HugeTxn(t *testing.T) {
+ r := New()
+
+ keys := []string{
+ "foo/bar/baz",
+ "foo/baz/bar",
+ "foo/zip/zap",
+ "foobar",
+ "nochange",
+ }
+ for i := 0; i < defaultModifiedCache; i++ {
+ key := fmt.Sprintf("aaa%d", i)
+ r, _, _ = r.Insert([]byte(key), nil)
+ }
+ for _, k := range keys {
+ r, _, _ = r.Insert([]byte(k), nil)
+ }
+ for i := 0; i < defaultModifiedCache; i++ {
+ key := fmt.Sprintf("zzz%d", i)
+ r, _, _ = r.Insert([]byte(key), nil)
+ }
+ if r.Len() != len(keys)+2*defaultModifiedCache {
+ t.Fatalf("bad len: %v %v", r.Len(), len(keys))
+ }
+
+ rootWatch, _, ok := r.Root().GetWatch(nil)
+ if rootWatch == nil {
+ t.Fatalf("bad")
+ }
+
+ parentWatch, _, ok := r.Root().GetWatch([]byte("foo"))
+ if parentWatch == nil {
+ t.Fatalf("bad")
+ }
+
+ leafWatch, _, ok := r.Root().GetWatch([]byte("foobar"))
+ if !ok {
+ t.Fatalf("should be found")
+ }
+ if leafWatch == nil {
+ t.Fatalf("bad")
+ }
+
+ nopeWatch, _, ok := r.Root().GetWatch([]byte("nochange"))
+ if !ok {
+ t.Fatalf("should be found")
+ }
+ if nopeWatch == nil {
+ t.Fatalf("bad")
+ }
+
+ beforeWatch, _, ok := r.Root().GetWatch([]byte("aaa123"))
+ if beforeWatch == nil {
+ t.Fatalf("bad")
+ }
+
+ afterWatch, _, ok := r.Root().GetWatch([]byte("zzz123"))
+ if afterWatch == nil {
+ t.Fatalf("bad")
+ }
+
+ // Start the transaction.
+ txn := r.Txn()
+ txn.TrackMutate(true)
+
+ // Add new nodes on both sides of the tree and delete enough nodes to
+ // overflow the tracking.
+ txn.Insert([]byte("aaa"), nil)
+ for i := 0; i < defaultModifiedCache; i++ {
+ key := fmt.Sprintf("aaa%d", i)
+ txn.Delete([]byte(key))
+ }
+ for i := 0; i < defaultModifiedCache; i++ {
+ key := fmt.Sprintf("zzz%d", i)
+ txn.Delete([]byte(key))
+ }
+ txn.Insert([]byte("zzz"), nil)
+
+ // Hit the leaf, and add a child so we make multiple mutations to the
+ // same node.
+ txn.Insert([]byte("foobar"), nil)
+ txn.Insert([]byte("foobarbaz"), nil)
+
+ // Commit and make sure we overflowed but didn't take on extra stuff.
+ r = txn.CommitOnly()
+ if !txn.trackOverflow || txn.trackChannels != nil {
+ t.Fatalf("bad")
+ }
+
+ // Now do the trigger.
+ txn.Notify()
+
+ // Make sure no closed channels escaped the transaction.
+ if hasAnyClosedMutateCh(r) {
+ t.Fatalf("bad")
+ }
+
+ // Verify the watches fired as expected.
+ select {
+ case <-rootWatch:
+ default:
+ t.Fatalf("bad")
+ }
+ select {
+ case <-parentWatch:
+ default:
+ t.Fatalf("bad")
+ }
+ select {
+ case <-leafWatch:
+ default:
+ t.Fatalf("bad")
+ }
+ select {
+ case <-nopeWatch:
+ t.Fatalf("bad")
+ default:
+ }
+ select {
+ case <-beforeWatch:
+ default:
+ t.Fatalf("bad")
+ }
+ select {
+ case <-afterWatch:
+ default:
+ t.Fatalf("bad")
+ }
+}
+
+func TestTrackMutate_mergeChild(t *testing.T) {
+ // This case does a delete of the "acb" leaf, which causes the "aca"
+ // leaf to get merged with the old "ac" node:
+ //
+ // [root] [root]
+ // |a |a
+ // [node] [node]
+ // b/ \c b/ \c
+ // (ab) [node] (ab) (aca)
+ // a/ \b
+ // (aca) (acb)
+ //
+ for i := 0; i < 3; i++ {
+ r := New()
+ r, _, _ = r.Insert([]byte("ab"), nil)
+ r, _, _ = r.Insert([]byte("aca"), nil)
+ r, _, _ = r.Insert([]byte("acb"), nil)
+ snapIter := r.root.rawIterator()
+
+ // Run through all notification methods as there were bugs in
+ // both that affected these operations. The slowNotify path
+ // would detect copied but otherwise identical leaves as changed
+ // and wrongly close channels. The normal path would fail to
+ // notify on a child node that had been merged.
+ txn := r.Txn()
+ txn.TrackMutate(true)
+ txn.Delete([]byte("acb"))
+ switch i {
+ case 0:
+ r = txn.Commit()
+ case 1:
+ r = txn.CommitOnly()
+ txn.Notify()
+ default:
+ r = txn.CommitOnly()
+ txn.slowNotify()
+ }
+ if hasAnyClosedMutateCh(r) {
+ t.Fatalf("bad")
+ }
+
+ // Run through the old tree and make sure the exact channels we
+ // expected were closed.
+ for ; snapIter.Front() != nil; snapIter.Next() {
+ n := snapIter.Front()
+ path := snapIter.Path()
+ switch path {
+ case "", "a", "ac": // parent nodes all change
+ if !isClosed(n.mutateCh) || n.leaf != nil {
+ t.Fatalf("bad")
+ }
+ case "ab": // unrelated node / leaf sees no change
+ if isClosed(n.mutateCh) || isClosed(n.leaf.mutateCh) {
+ t.Fatalf("bad")
+ }
+ case "aca": // this node gets merged, but the leaf doesn't change
+ if !isClosed(n.mutateCh) || isClosed(n.leaf.mutateCh) {
+ t.Fatalf("bad")
+ }
+ case "acb": // this node / leaf gets deleted
+ if !isClosed(n.mutateCh) || !isClosed(n.leaf.mutateCh) {
+ t.Fatalf("bad")
+ }
+ default:
+ t.Fatalf("bad: %s", path)
+ }
+ }
+ }
+}
+
+func TestTrackMutate_cachedNodeChange(t *testing.T) {
+ // This case does a delete of the "acb" leaf, which causes the "aca"
+ // leaf to get merged with the old "ac" node:
+ //
+ // [root] [root]
+ // |a |a
+ // [node] [node]
+ // b/ \c b/ \c
+ // (ab) [node] (ab) (aca*) <- this leaf gets modified
+ // a/ \b post-merge
+ // (aca) (acb)
+ //
+ // Then it makes a modification to the "aca" leaf on a node that will
+ // be in the cache, so this makes sure that the leaf watch fires.
+ for i := 0; i < 3; i++ {
+ r := New()
+ r, _, _ = r.Insert([]byte("ab"), nil)
+ r, _, _ = r.Insert([]byte("aca"), nil)
+ r, _, _ = r.Insert([]byte("acb"), nil)
+ snapIter := r.root.rawIterator()
+
+ txn := r.Txn()
+ txn.TrackMutate(true)
+ txn.Delete([]byte("acb"))
+ txn.Insert([]byte("aca"), nil)
+ switch i {
+ case 0:
+ r = txn.Commit()
+ case 1:
+ r = txn.CommitOnly()
+ txn.Notify()
+ default:
+ r = txn.CommitOnly()
+ txn.slowNotify()
+ }
+ if hasAnyClosedMutateCh(r) {
+ t.Fatalf("bad")
+ }
+
+ // Run through the old tree and make sure the exact channels we
+ // expected were closed.
+ for ; snapIter.Front() != nil; snapIter.Next() {
+ n := snapIter.Front()
+ path := snapIter.Path()
+ switch path {
+ case "", "a", "ac": // parent nodes all change
+ if !isClosed(n.mutateCh) || n.leaf != nil {
+ t.Fatalf("bad")
+ }
+ case "ab": // unrelated node / leaf sees no change
+ if isClosed(n.mutateCh) || isClosed(n.leaf.mutateCh) {
+ t.Fatalf("bad")
+ }
+ case "aca": // merge changes the node, then we update the leaf
+ if !isClosed(n.mutateCh) || !isClosed(n.leaf.mutateCh) {
+ t.Fatalf("bad")
+ }
+ case "acb": // this node / leaf gets deleted
+ if !isClosed(n.mutateCh) || !isClosed(n.leaf.mutateCh) {
+ t.Fatalf("bad")
+ }
+ default:
+ t.Fatalf("bad: %s", path)
+ }
+ }
+ }
+}
diff --git a/vendor/github.com/hashicorp/go-immutable-radix/iter.go b/vendor/github.com/hashicorp/go-immutable-radix/iter.go
new file mode 100644
index 000000000..9815e0253
--- /dev/null
+++ b/vendor/github.com/hashicorp/go-immutable-radix/iter.go
@@ -0,0 +1,91 @@
+package iradix
+
+import "bytes"
+
+// Iterator is used to iterate over a set of nodes
+// in pre-order
+type Iterator struct {
+ node *Node
+ stack []edges
+}
+
+// SeekPrefixWatch is used to seek the iterator to a given prefix
+// and returns the watch channel of the finest granularity
+func (i *Iterator) SeekPrefixWatch(prefix []byte) (watch <-chan struct{}) {
+ // Wipe the stack
+ i.stack = nil
+ n := i.node
+ watch = n.mutateCh
+ search := prefix
+ for {
+ // Check for key exhaution
+ if len(search) == 0 {
+ i.node = n
+ return
+ }
+
+ // Look for an edge
+ _, n = n.getEdge(search[0])
+ if n == nil {
+ i.node = nil
+ return
+ }
+
+ // Update to the finest granularity as the search makes progress
+ watch = n.mutateCh
+
+ // Consume the search prefix
+ if bytes.HasPrefix(search, n.prefix) {
+ search = search[len(n.prefix):]
+
+ } else if bytes.HasPrefix(n.prefix, search) {
+ i.node = n
+ return
+ } else {
+ i.node = nil
+ return
+ }
+ }
+}
+
+// SeekPrefix is used to seek the iterator to a given prefix
+func (i *Iterator) SeekPrefix(prefix []byte) {
+ i.SeekPrefixWatch(prefix)
+}
+
+// Next returns the next node in order
+func (i *Iterator) Next() ([]byte, interface{}, bool) {
+ // Initialize our stack if needed
+ if i.stack == nil && i.node != nil {
+ i.stack = []edges{
+ edges{
+ edge{node: i.node},
+ },
+ }
+ }
+
+ for len(i.stack) > 0 {
+ // Inspect the last element of the stack
+ n := len(i.stack)
+ last := i.stack[n-1]
+ elem := last[0].node
+
+ // Update the stack
+ if len(last) > 1 {
+ i.stack[n-1] = last[1:]
+ } else {
+ i.stack = i.stack[:n-1]
+ }
+
+ // Push the edges onto the frontier
+ if len(elem.edges) > 0 {
+ i.stack = append(i.stack, elem.edges)
+ }
+
+ // Return the leaf values if any
+ if elem.leaf != nil {
+ return elem.leaf.key, elem.leaf.val, true
+ }
+ }
+ return nil, nil, false
+}
diff --git a/vendor/github.com/hashicorp/go-immutable-radix/node.go b/vendor/github.com/hashicorp/go-immutable-radix/node.go
new file mode 100644
index 000000000..7a065e7a0
--- /dev/null
+++ b/vendor/github.com/hashicorp/go-immutable-radix/node.go
@@ -0,0 +1,292 @@
+package iradix
+
+import (
+ "bytes"
+ "sort"
+)
+
+// WalkFn is used when walking the tree. Takes a
+// key and value, returning if iteration should
+// be terminated.
+type WalkFn func(k []byte, v interface{}) bool
+
+// leafNode is used to represent a value
+type leafNode struct {
+ mutateCh chan struct{}
+ key []byte
+ val interface{}
+}
+
+// edge is used to represent an edge node
+type edge struct {
+ label byte
+ node *Node
+}
+
+// Node is an immutable node in the radix tree
+type Node struct {
+ // mutateCh is closed if this node is modified
+ mutateCh chan struct{}
+
+ // leaf is used to store possible leaf
+ leaf *leafNode
+
+ // prefix is the common prefix we ignore
+ prefix []byte
+
+ // Edges should be stored in-order for iteration.
+ // We avoid a fully materialized slice to save memory,
+ // since in most cases we expect to be sparse
+ edges edges
+}
+
+func (n *Node) isLeaf() bool {
+ return n.leaf != nil
+}
+
+func (n *Node) addEdge(e edge) {
+ num := len(n.edges)
+ idx := sort.Search(num, func(i int) bool {
+ return n.edges[i].label >= e.label
+ })
+ n.edges = append(n.edges, e)
+ if idx != num {
+ copy(n.edges[idx+1:], n.edges[idx:num])
+ n.edges[idx] = e
+ }
+}
+
+func (n *Node) replaceEdge(e edge) {
+ num := len(n.edges)
+ idx := sort.Search(num, func(i int) bool {
+ return n.edges[i].label >= e.label
+ })
+ if idx < num && n.edges[idx].label == e.label {
+ n.edges[idx].node = e.node
+ return
+ }
+ panic("replacing missing edge")
+}
+
+func (n *Node) getEdge(label byte) (int, *Node) {
+ num := len(n.edges)
+ idx := sort.Search(num, func(i int) bool {
+ return n.edges[i].label >= label
+ })
+ if idx < num && n.edges[idx].label == label {
+ return idx, n.edges[idx].node
+ }
+ return -1, nil
+}
+
+func (n *Node) delEdge(label byte) {
+ num := len(n.edges)
+ idx := sort.Search(num, func(i int) bool {
+ return n.edges[i].label >= label
+ })
+ if idx < num && n.edges[idx].label == label {
+ copy(n.edges[idx:], n.edges[idx+1:])
+ n.edges[len(n.edges)-1] = edge{}
+ n.edges = n.edges[:len(n.edges)-1]
+ }
+}
+
+func (n *Node) GetWatch(k []byte) (<-chan struct{}, interface{}, bool) {
+ search := k
+ watch := n.mutateCh
+ for {
+ // Check for key exhaustion
+ if len(search) == 0 {
+ if n.isLeaf() {
+ return n.leaf.mutateCh, n.leaf.val, true
+ }
+ break
+ }
+
+ // Look for an edge
+ _, n = n.getEdge(search[0])
+ if n == nil {
+ break
+ }
+
+ // Update to the finest granularity as the search makes progress
+ watch = n.mutateCh
+
+ // Consume the search prefix
+ if bytes.HasPrefix(search, n.prefix) {
+ search = search[len(n.prefix):]
+ } else {
+ break
+ }
+ }
+ return watch, nil, false
+}
+
+func (n *Node) Get(k []byte) (interface{}, bool) {
+ _, val, ok := n.GetWatch(k)
+ return val, ok
+}
+
+// LongestPrefix is like Get, but instead of an
+// exact match, it will return the longest prefix match.
+func (n *Node) LongestPrefix(k []byte) ([]byte, interface{}, bool) {
+ var last *leafNode
+ search := k
+ for {
+ // Look for a leaf node
+ if n.isLeaf() {
+ last = n.leaf
+ }
+
+ // Check for key exhaution
+ if len(search) == 0 {
+ break
+ }
+
+ // Look for an edge
+ _, n = n.getEdge(search[0])
+ if n == nil {
+ break
+ }
+
+ // Consume the search prefix
+ if bytes.HasPrefix(search, n.prefix) {
+ search = search[len(n.prefix):]
+ } else {
+ break
+ }
+ }
+ if last != nil {
+ return last.key, last.val, true
+ }
+ return nil, nil, false
+}
+
+// Minimum is used to return the minimum value in the tree
+func (n *Node) Minimum() ([]byte, interface{}, bool) {
+ for {
+ if n.isLeaf() {
+ return n.leaf.key, n.leaf.val, true
+ }
+ if len(n.edges) > 0 {
+ n = n.edges[0].node
+ } else {
+ break
+ }
+ }
+ return nil, nil, false
+}
+
+// Maximum is used to return the maximum value in the tree
+func (n *Node) Maximum() ([]byte, interface{}, bool) {
+ for {
+ if num := len(n.edges); num > 0 {
+ n = n.edges[num-1].node
+ continue
+ }
+ if n.isLeaf() {
+ return n.leaf.key, n.leaf.val, true
+ } else {
+ break
+ }
+ }
+ return nil, nil, false
+}
+
+// Iterator is used to return an iterator at
+// the given node to walk the tree
+func (n *Node) Iterator() *Iterator {
+ return &Iterator{node: n}
+}
+
+// rawIterator is used to return a raw iterator at the given node to walk the
+// tree.
+func (n *Node) rawIterator() *rawIterator {
+ iter := &rawIterator{node: n}
+ iter.Next()
+ return iter
+}
+
+// Walk is used to walk the tree
+func (n *Node) Walk(fn WalkFn) {
+ recursiveWalk(n, fn)
+}
+
+// WalkPrefix is used to walk the tree under a prefix
+func (n *Node) WalkPrefix(prefix []byte, fn WalkFn) {
+ search := prefix
+ for {
+ // Check for key exhaution
+ if len(search) == 0 {
+ recursiveWalk(n, fn)
+ return
+ }
+
+ // Look for an edge
+ _, n = n.getEdge(search[0])
+ if n == nil {
+ break
+ }
+
+ // Consume the search prefix
+ if bytes.HasPrefix(search, n.prefix) {
+ search = search[len(n.prefix):]
+
+ } else if bytes.HasPrefix(n.prefix, search) {
+ // Child may be under our search prefix
+ recursiveWalk(n, fn)
+ return
+ } else {
+ break
+ }
+ }
+}
+
+// WalkPath is used to walk the tree, but only visiting nodes
+// from the root down to a given leaf. Where WalkPrefix walks
+// all the entries *under* the given prefix, this walks the
+// entries *above* the given prefix.
+func (n *Node) WalkPath(path []byte, fn WalkFn) {
+ search := path
+ for {
+ // Visit the leaf values if any
+ if n.leaf != nil && fn(n.leaf.key, n.leaf.val) {
+ return
+ }
+
+ // Check for key exhaution
+ if len(search) == 0 {
+ return
+ }
+
+ // Look for an edge
+ _, n = n.getEdge(search[0])
+ if n == nil {
+ return
+ }
+
+ // Consume the search prefix
+ if bytes.HasPrefix(search, n.prefix) {
+ search = search[len(n.prefix):]
+ } else {
+ break
+ }
+ }
+}
+
+// recursiveWalk is used to do a pre-order walk of a node
+// recursively. Returns true if the walk should be aborted
+func recursiveWalk(n *Node, fn WalkFn) bool {
+ // Visit the leaf values if any
+ if n.leaf != nil && fn(n.leaf.key, n.leaf.val) {
+ return true
+ }
+
+ // Recurse on the children
+ for _, e := range n.edges {
+ if recursiveWalk(e.node, fn) {
+ return true
+ }
+ }
+ return false
+}
diff --git a/vendor/github.com/hashicorp/go-immutable-radix/raw_iter.go b/vendor/github.com/hashicorp/go-immutable-radix/raw_iter.go
new file mode 100644
index 000000000..04814c132
--- /dev/null
+++ b/vendor/github.com/hashicorp/go-immutable-radix/raw_iter.go
@@ -0,0 +1,78 @@
+package iradix
+
+// rawIterator visits each of the nodes in the tree, even the ones that are not
+// leaves. It keeps track of the effective path (what a leaf at a given node
+// would be called), which is useful for comparing trees.
+type rawIterator struct {
+ // node is the starting node in the tree for the iterator.
+ node *Node
+
+ // stack keeps track of edges in the frontier.
+ stack []rawStackEntry
+
+ // pos is the current position of the iterator.
+ pos *Node
+
+ // path is the effective path of the current iterator position,
+ // regardless of whether the current node is a leaf.
+ path string
+}
+
+// rawStackEntry is used to keep track of the cumulative common path as well as
+// its associated edges in the frontier.
+type rawStackEntry struct {
+ path string
+ edges edges
+}
+
+// Front returns the current node that has been iterated to.
+func (i *rawIterator) Front() *Node {
+ return i.pos
+}
+
+// Path returns the effective path of the current node, even if it's not actually
+// a leaf.
+func (i *rawIterator) Path() string {
+ return i.path
+}
+
+// Next advances the iterator to the next node.
+func (i *rawIterator) Next() {
+ // Initialize our stack if needed.
+ if i.stack == nil && i.node != nil {
+ i.stack = []rawStackEntry{
+ rawStackEntry{
+ edges: edges{
+ edge{node: i.node},
+ },
+ },
+ }
+ }
+
+ for len(i.stack) > 0 {
+ // Inspect the last element of the stack.
+ n := len(i.stack)
+ last := i.stack[n-1]
+ elem := last.edges[0].node
+
+ // Update the stack.
+ if len(last.edges) > 1 {
+ i.stack[n-1].edges = last.edges[1:]
+ } else {
+ i.stack = i.stack[:n-1]
+ }
+
+ // Push the edges onto the frontier.
+ if len(elem.edges) > 0 {
+ path := last.path + string(elem.prefix)
+ i.stack = append(i.stack, rawStackEntry{path, elem.edges})
+ }
+
+ i.pos = elem
+ i.path = last.path + string(elem.prefix)
+ return
+ }
+
+ i.pos = nil
+ i.path = ""
+}
diff --git a/vendor/github.com/hashicorp/memberlist/memberlist.go b/vendor/github.com/hashicorp/memberlist/memberlist.go
index 9ea195cfc..d5c175e5b 100644
--- a/vendor/github.com/hashicorp/memberlist/memberlist.go
+++ b/vendor/github.com/hashicorp/memberlist/memberlist.go
@@ -22,9 +22,10 @@ import (
"strconv"
"strings"
"sync"
+ "sync/atomic"
"time"
- "github.com/hashicorp/go-multierror"
+ multierror "github.com/hashicorp/go-multierror"
sockaddr "github.com/hashicorp/go-sockaddr"
"github.com/miekg/dns"
)
@@ -35,11 +36,14 @@ type Memberlist struct {
numNodes uint32 // Number of known nodes (estimate)
config *Config
- shutdown bool
+ shutdown int32 // Used as an atomic boolean value
shutdownCh chan struct{}
- leave bool
+ leave int32 // Used as an atomic boolean value
leaveBroadcast chan struct{}
+ shutdownLock sync.Mutex // Serializes calls to Shutdown
+ leaveLock sync.Mutex // Serializes calls to Leave
+
transport Transport
handoff chan msgHandoff
@@ -116,19 +120,19 @@ func newMemberlist(conf *Config) (*Memberlist, error) {
// See comment below for details about the retry in here.
makeNetRetry := func(limit int) (*NetTransport, error) {
+ var err error
for try := 0; try < limit; try++ {
- nt, err := NewNetTransport(nc)
- if err == nil {
+ var nt *NetTransport
+ if nt, err = NewNetTransport(nc); err == nil {
return nt, nil
}
-
if strings.Contains(err.Error(), "address already in use") {
logger.Printf("[DEBUG] Got bind error: %v", err)
continue
}
}
- return nil, fmt.Errorf("ran out of tries to obtain an address")
+ return nil, fmt.Errorf("failed to obtain an address: %v", err)
}
// The dynamic bind port operation is inherently racy because
@@ -554,18 +558,17 @@ func (m *Memberlist) NumMembers() (alive int) {
// This method is safe to call multiple times, but must not be called
// after the cluster is already shut down.
func (m *Memberlist) Leave(timeout time.Duration) error {
- m.nodeLock.Lock()
- // We can't defer m.nodeLock.Unlock() because m.deadNode will also try to
- // acquire a lock so we need to Unlock before that.
+ m.leaveLock.Lock()
+ defer m.leaveLock.Unlock()
- if m.shutdown {
- m.nodeLock.Unlock()
+ if m.hasShutdown() {
panic("leave after shutdown")
}
- if !m.leave {
- m.leave = true
+ if !m.hasLeft() {
+ atomic.StoreInt32(&m.leave, 1)
+ m.nodeLock.Lock()
state, ok := m.nodeMap[m.config.Name]
m.nodeLock.Unlock()
if !ok {
@@ -591,8 +594,6 @@ func (m *Memberlist) Leave(timeout time.Duration) error {
return fmt.Errorf("timeout waiting for leave broadcast")
}
}
- } else {
- m.nodeLock.Unlock()
}
return nil
@@ -634,10 +635,10 @@ func (m *Memberlist) ProtocolVersion() uint8 {
//
// This method is safe to call multiple times.
func (m *Memberlist) Shutdown() error {
- m.nodeLock.Lock()
- defer m.nodeLock.Unlock()
+ m.shutdownLock.Lock()
+ defer m.shutdownLock.Unlock()
- if m.shutdown {
+ if m.hasShutdown() {
return nil
}
@@ -647,8 +648,16 @@ func (m *Memberlist) Shutdown() error {
m.transport.Shutdown()
// Now tear down everything else.
- m.shutdown = true
+ atomic.StoreInt32(&m.shutdown, 1)
close(m.shutdownCh)
m.deschedule()
return nil
}
+
+func (m *Memberlist) hasShutdown() bool {
+ return atomic.LoadInt32(&m.shutdown) == 1
+}
+
+func (m *Memberlist) hasLeft() bool {
+ return atomic.LoadInt32(&m.leave) == 1
+}
diff --git a/vendor/github.com/hashicorp/memberlist/memberlist_test.go b/vendor/github.com/hashicorp/memberlist/memberlist_test.go
index 964112dfd..ecda7fb55 100644
--- a/vendor/github.com/hashicorp/memberlist/memberlist_test.go
+++ b/vendor/github.com/hashicorp/memberlist/memberlist_test.go
@@ -424,11 +424,11 @@ func TestMemberList_ResolveAddr_TCP_First(t *testing.T) {
}
port := uint16(m.config.BindPort)
expected := []ipPort{
- ipPort{net.ParseIP("127.0.0.1"), port},
+ ipPort{net.ParseIP("127.0.0.1").To4(), port},
ipPort{net.ParseIP("2001:db8:a0b:12f0::1"), port},
}
if !reflect.DeepEqual(ips, expected) {
- t.Fatalf("bad: %#v", ips)
+ t.Fatalf("bad: %#v expected: %#v", ips, expected)
}
}
}
diff --git a/vendor/github.com/hashicorp/memberlist/state.go b/vendor/github.com/hashicorp/memberlist/state.go
index 8513361b1..29fe5f1cf 100644
--- a/vendor/github.com/hashicorp/memberlist/state.go
+++ b/vendor/github.com/hashicorp/memberlist/state.go
@@ -835,7 +835,7 @@ func (m *Memberlist) aliveNode(a *alive, notify chan struct{}, bootstrap bool) {
// in-queue to be processed but blocked by the locks above. If we let
// that aliveMsg process, it'll cause us to re-join the cluster. This
// ensures that we don't.
- if m.leave && a.Node == m.config.Name {
+ if m.hasLeft() && a.Node == m.config.Name {
return
}
@@ -1111,7 +1111,7 @@ func (m *Memberlist) deadNode(d *dead) {
// Check if this is us
if state.Name == m.config.Name {
// If we are not leaving we need to refute
- if !m.leave {
+ if !m.hasLeft() {
m.refute(state, d.Incarnation)
m.logger.Printf("[WARN] memberlist: Refuting a dead message (from: %s)", d.From)
return // Do not mark ourself dead
diff --git a/vendor/github.com/lib/pq/array.go b/vendor/github.com/lib/pq/array.go
index e7b2145d6..e4933e227 100644
--- a/vendor/github.com/lib/pq/array.go
+++ b/vendor/github.com/lib/pq/array.go
@@ -13,7 +13,7 @@ import (
var typeByteSlice = reflect.TypeOf([]byte{})
var typeDriverValuer = reflect.TypeOf((*driver.Valuer)(nil)).Elem()
-var typeSqlScanner = reflect.TypeOf((*sql.Scanner)(nil)).Elem()
+var typeSQLScanner = reflect.TypeOf((*sql.Scanner)(nil)).Elem()
// Array returns the optimal driver.Valuer and sql.Scanner for an array or
// slice of any dimension.
@@ -278,7 +278,7 @@ func (GenericArray) evaluateDestination(rt reflect.Type) (reflect.Type, func([]b
// TODO calculate the assign function for other types
// TODO repeat this section on the element type of arrays or slices (multidimensional)
{
- if reflect.PtrTo(rt).Implements(typeSqlScanner) {
+ if reflect.PtrTo(rt).Implements(typeSQLScanner) {
// dest is always addressable because it is an element of a slice.
assign = func(src []byte, dest reflect.Value) (err error) {
ss := dest.Addr().Interface().(sql.Scanner)
@@ -587,7 +587,7 @@ func appendArrayElement(b []byte, rv reflect.Value) ([]byte, string, error) {
}
}
- var del string = ","
+ var del = ","
var err error
var iv interface{} = rv.Interface()
diff --git a/vendor/github.com/lib/pq/conn.go b/vendor/github.com/lib/pq/conn.go
index 3747ffcaf..1725ab0d3 100644
--- a/vendor/github.com/lib/pq/conn.go
+++ b/vendor/github.com/lib/pq/conn.go
@@ -27,12 +27,12 @@ var (
ErrNotSupported = errors.New("pq: Unsupported command")
ErrInFailedTransaction = errors.New("pq: Could not complete operation in a failed transaction")
ErrSSLNotSupported = errors.New("pq: SSL is not enabled on the server")
- ErrSSLKeyHasWorldPermissions = errors.New("pq: Private key file has group or world access. Permissions should be u=rw (0600) or less.")
- ErrCouldNotDetectUsername = errors.New("pq: Could not detect default username. Please provide one explicitly.")
+ ErrSSLKeyHasWorldPermissions = errors.New("pq: Private key file has group or world access. Permissions should be u=rw (0600) or less")
+ ErrCouldNotDetectUsername = errors.New("pq: Could not detect default username. Please provide one explicitly")
errUnexpectedReady = errors.New("unexpected ReadyForQuery")
errNoRowsAffected = errors.New("no RowsAffected available after the empty statement")
- errNoLastInsertId = errors.New("no LastInsertId available after the empty statement")
+ errNoLastInsertID = errors.New("no LastInsertId available after the empty statement")
)
type Driver struct{}
@@ -131,7 +131,7 @@ type conn struct {
}
// Handle driver-side settings in parsed connection string.
-func (c *conn) handleDriverSettings(o values) (err error) {
+func (cn *conn) handleDriverSettings(o values) (err error) {
boolSetting := func(key string, val *bool) error {
if value, ok := o[key]; ok {
if value == "yes" {
@@ -145,18 +145,18 @@ func (c *conn) handleDriverSettings(o values) (err error) {
return nil
}
- err = boolSetting("disable_prepared_binary_result", &c.disablePreparedBinaryResult)
+ err = boolSetting("disable_prepared_binary_result", &cn.disablePreparedBinaryResult)
if err != nil {
return err
}
- err = boolSetting("binary_parameters", &c.binaryParameters)
+ err = boolSetting("binary_parameters", &cn.binaryParameters)
if err != nil {
return err
}
return nil
}
-func (c *conn) handlePgpass(o values) {
+func (cn *conn) handlePgpass(o values) {
// if a password was supplied, do not process .pgpass
if _, ok := o["password"]; ok {
return
@@ -229,10 +229,10 @@ func (c *conn) handlePgpass(o values) {
}
}
-func (c *conn) writeBuf(b byte) *writeBuf {
- c.scratch[0] = b
+func (cn *conn) writeBuf(b byte) *writeBuf {
+ cn.scratch[0] = b
return &writeBuf{
- buf: c.scratch[:5],
+ buf: cn.scratch[:5],
pos: 1,
}
}
@@ -310,9 +310,8 @@ func DialOpen(d Dialer, name string) (_ driver.Conn, err error) {
u, err := userCurrent()
if err != nil {
return nil, err
- } else {
- o["user"] = u
}
+ o["user"] = u
}
cn := &conn{
@@ -698,7 +697,7 @@ var emptyRows noRows
var _ driver.Result = noRows{}
func (noRows) LastInsertId() (int64, error) {
- return 0, errNoLastInsertId
+ return 0, errNoLastInsertID
}
func (noRows) RowsAffected() (int64, error) {
@@ -840,16 +839,15 @@ func (cn *conn) query(query string, args []driver.Value) (_ *rows, err error) {
rows.colNames, rows.colFmts, rows.colTyps = cn.readPortalDescribeResponse()
cn.postExecuteWorkaround()
return rows, nil
- } else {
- st := cn.prepareTo(query, "")
- st.exec(args)
- return &rows{
- cn: cn,
- colNames: st.colNames,
- colTyps: st.colTyps,
- colFmts: st.colFmts,
- }, nil
}
+ st := cn.prepareTo(query, "")
+ st.exec(args)
+ return &rows{
+ cn: cn,
+ colNames: st.colNames,
+ colTyps: st.colTyps,
+ colFmts: st.colFmts,
+ }, nil
}
// Implement the optional "Execer" interface for one-shot queries
@@ -876,17 +874,16 @@ func (cn *conn) Exec(query string, args []driver.Value) (res driver.Result, err
cn.postExecuteWorkaround()
res, _, err = cn.readExecuteResponse("Execute")
return res, err
- } else {
- // Use the unnamed statement to defer planning until bind
- // time, or else value-based selectivity estimates cannot be
- // used.
- st := cn.prepareTo(query, "")
- r, err := st.Exec(args)
- if err != nil {
- panic(err)
- }
- return r, err
}
+ // Use the unnamed statement to defer planning until bind
+ // time, or else value-based selectivity estimates cannot be
+ // used.
+ st := cn.prepareTo(query, "")
+ r, err := st.Exec(args)
+ if err != nil {
+ panic(err)
+ }
+ return r, err
}
func (cn *conn) send(m *writeBuf) {
@@ -1147,10 +1144,10 @@ const formatText format = 0
const formatBinary format = 1
// One result-column format code with the value 1 (i.e. all binary).
-var colFmtDataAllBinary []byte = []byte{0, 1, 0, 1}
+var colFmtDataAllBinary = []byte{0, 1, 0, 1}
// No result-column format codes (i.e. all text).
-var colFmtDataAllText []byte = []byte{0, 0}
+var colFmtDataAllText = []byte{0, 0}
type stmt struct {
cn *conn
@@ -1515,7 +1512,7 @@ func (cn *conn) sendBinaryModeQuery(query string, args []driver.Value) {
cn.send(b)
}
-func (c *conn) processParameterStatus(r *readBuf) {
+func (cn *conn) processParameterStatus(r *readBuf) {
var err error
param := r.string()
@@ -1526,13 +1523,13 @@ func (c *conn) processParameterStatus(r *readBuf) {
var minor int
_, err = fmt.Sscanf(r.string(), "%d.%d.%d", &major1, &major2, &minor)
if err == nil {
- c.parameterStatus.serverVersion = major1*10000 + major2*100 + minor
+ cn.parameterStatus.serverVersion = major1*10000 + major2*100 + minor
}
case "TimeZone":
- c.parameterStatus.currentLocation, err = time.LoadLocation(r.string())
+ cn.parameterStatus.currentLocation, err = time.LoadLocation(r.string())
if err != nil {
- c.parameterStatus.currentLocation = nil
+ cn.parameterStatus.currentLocation = nil
}
default:
@@ -1540,8 +1537,8 @@ func (c *conn) processParameterStatus(r *readBuf) {
}
}
-func (c *conn) processReadyForQuery(r *readBuf) {
- c.txnStatus = transactionStatus(r.byte())
+func (cn *conn) processReadyForQuery(r *readBuf) {
+ cn.txnStatus = transactionStatus(r.byte())
}
func (cn *conn) readReadyForQuery() {
@@ -1556,9 +1553,9 @@ func (cn *conn) readReadyForQuery() {
}
}
-func (c *conn) processBackendKeyData(r *readBuf) {
- c.processID = r.int32()
- c.secretKey = r.int32()
+func (cn *conn) processBackendKeyData(r *readBuf) {
+ cn.processID = r.int32()
+ cn.secretKey = r.int32()
}
func (cn *conn) readParseResponse() {
diff --git a/vendor/github.com/lib/pq/conn_test.go b/vendor/github.com/lib/pq/conn_test.go
index dfc827201..d3c01f343 100644
--- a/vendor/github.com/lib/pq/conn_test.go
+++ b/vendor/github.com/lib/pq/conn_test.go
@@ -136,7 +136,7 @@ func TestOpenURL(t *testing.T) {
testURL("postgresql://")
}
-const pgpass_file = "/tmp/pqgotest_pgpass"
+const pgpassFile = "/tmp/pqgotest_pgpass"
func TestPgpass(t *testing.T) {
if os.Getenv("TRAVIS") != "true" {
@@ -172,10 +172,10 @@ func TestPgpass(t *testing.T) {
txn.Rollback()
}
testAssert("", "ok", "missing .pgpass, unexpected error %#v")
- os.Setenv("PGPASSFILE", pgpass_file)
+ os.Setenv("PGPASSFILE", pgpassFile)
testAssert("host=/tmp", "fail", ", unexpected error %#v")
- os.Remove(pgpass_file)
- pgpass, err := os.OpenFile(pgpass_file, os.O_RDWR|os.O_CREATE, 0644)
+ os.Remove(pgpassFile)
+ pgpass, err := os.OpenFile(pgpassFile, os.O_RDWR|os.O_CREATE, 0644)
if err != nil {
t.Fatalf("Unexpected error writing pgpass file %#v", err)
}
@@ -213,7 +213,7 @@ localhost:*:*:*:pass_C
// wrong permissions for the pgpass file means it should be ignored
assertPassword(values{"host": "example.com", "user": "foo"}, "")
// fix the permissions and check if it has taken effect
- os.Chmod(pgpass_file, 0600)
+ os.Chmod(pgpassFile, 0600)
assertPassword(values{"host": "server", "dbname": "some_db", "user": "some_user"}, "pass_A")
assertPassword(values{"host": "example.com", "user": "foo"}, "pass_fallback")
assertPassword(values{"host": "example.com", "dbname": "some_db", "user": "some_user"}, "pass_B")
@@ -221,7 +221,7 @@ localhost:*:*:*:pass_C
assertPassword(values{"host": "", "user": "some_user"}, "pass_C")
assertPassword(values{"host": "/tmp", "user": "some_user"}, "pass_C")
// cleanup
- os.Remove(pgpass_file)
+ os.Remove(pgpassFile)
os.Setenv("PGPASSFILE", "")
}
@@ -393,8 +393,8 @@ func TestEmptyQuery(t *testing.T) {
if _, err := res.RowsAffected(); err != errNoRowsAffected {
t.Fatalf("expected %s, got %v", errNoRowsAffected, err)
}
- if _, err := res.LastInsertId(); err != errNoLastInsertId {
- t.Fatalf("expected %s, got %v", errNoLastInsertId, err)
+ if _, err := res.LastInsertId(); err != errNoLastInsertID {
+ t.Fatalf("expected %s, got %v", errNoLastInsertID, err)
}
rows, err := db.Query("")
if err != nil {
@@ -425,8 +425,8 @@ func TestEmptyQuery(t *testing.T) {
if _, err := res.RowsAffected(); err != errNoRowsAffected {
t.Fatalf("expected %s, got %v", errNoRowsAffected, err)
}
- if _, err := res.LastInsertId(); err != errNoLastInsertId {
- t.Fatalf("expected %s, got %v", errNoLastInsertId, err)
+ if _, err := res.LastInsertId(); err != errNoLastInsertID {
+ t.Fatalf("expected %s, got %v", errNoLastInsertID, err)
}
rows, err = stmt.Query()
if err != nil {
@@ -1053,16 +1053,16 @@ func TestIssue282(t *testing.T) {
db := openTestConn(t)
defer db.Close()
- var search_path string
+ var searchPath string
err := db.QueryRow(`
SET LOCAL search_path TO pg_catalog;
SET LOCAL search_path TO pg_catalog;
- SHOW search_path`).Scan(&search_path)
+ SHOW search_path`).Scan(&searchPath)
if err != nil {
t.Fatal(err)
}
- if search_path != "pg_catalog" {
- t.Fatalf("unexpected search_path %s", search_path)
+ if searchPath != "pg_catalog" {
+ t.Fatalf("unexpected search_path %s", searchPath)
}
}
diff --git a/vendor/github.com/lib/pq/encode_test.go b/vendor/github.com/lib/pq/encode_test.go
index b1531ec29..3a0f7286e 100644
--- a/vendor/github.com/lib/pq/encode_test.go
+++ b/vendor/github.com/lib/pq/encode_test.go
@@ -370,17 +370,17 @@ func TestInfinityTimestamp(t *testing.T) {
t.Errorf("Scanning -infinity, expected time %q, got %q", y1500, resultT.String())
}
- y_1500 := time.Date(-1500, time.January, 1, 0, 0, 0, 0, time.UTC)
+ ym1500 := time.Date(-1500, time.January, 1, 0, 0, 0, 0, time.UTC)
y11500 := time.Date(11500, time.January, 1, 0, 0, 0, 0, time.UTC)
var s string
- err = db.QueryRow("SELECT $1::timestamp::text", y_1500).Scan(&s)
+ err = db.QueryRow("SELECT $1::timestamp::text", ym1500).Scan(&s)
if err != nil {
t.Errorf("Encoding -infinity, expected no error, got %q", err)
}
if s != "-infinity" {
t.Errorf("Encoding -infinity, expected %q, got %q", "-infinity", s)
}
- err = db.QueryRow("SELECT $1::timestamptz::text", y_1500).Scan(&s)
+ err = db.QueryRow("SELECT $1::timestamptz::text", ym1500).Scan(&s)
if err != nil {
t.Errorf("Encoding -infinity, expected no error, got %q", err)
}
diff --git a/vendor/github.com/lib/pq/uuid_test.go b/vendor/github.com/lib/pq/uuid_test.go
index 9df4a79b0..8ecee2fde 100644
--- a/vendor/github.com/lib/pq/uuid_test.go
+++ b/vendor/github.com/lib/pq/uuid_test.go
@@ -33,7 +33,7 @@ func TestDecodeUUIDBackend(t *testing.T) {
db := openTestConn(t)
defer db.Close()
- var s string = "a0ecc91d-a13f-4fe4-9fce-7e09777cc70a"
+ var s = "a0ecc91d-a13f-4fe4-9fce-7e09777cc70a"
var scanned interface{}
err := db.QueryRow(`SELECT $1::uuid`, s).Scan(&scanned)
diff --git a/vendor/github.com/mattermost/html2text/.gitignore b/vendor/github.com/mattermost/html2text/.gitignore
new file mode 100644
index 000000000..daf913b1b
--- /dev/null
+++ b/vendor/github.com/mattermost/html2text/.gitignore
@@ -0,0 +1,24 @@
+# Compiled Object files, Static and Dynamic libs (Shared Objects)
+*.o
+*.a
+*.so
+
+# Folders
+_obj
+_test
+
+# Architecture specific extensions/prefixes
+*.[568vq]
+[568vq].out
+
+*.cgo1.go
+*.cgo2.c
+_cgo_defun.c
+_cgo_gotypes.go
+_cgo_export.*
+
+_testmain.go
+
+*.exe
+*.test
+*.prof
diff --git a/vendor/github.com/mattermost/html2text/.travis.yml b/vendor/github.com/mattermost/html2text/.travis.yml
new file mode 100644
index 000000000..6c7f48efd
--- /dev/null
+++ b/vendor/github.com/mattermost/html2text/.travis.yml
@@ -0,0 +1,14 @@
+language: go
+go:
+ - tip
+ - 1.8
+ - 1.7
+ - 1.6
+ - 1.5
+ - 1.4
+ - 1.3
+ - 1.2
+notifications:
+ email:
+ on_success: change
+ on_failure: always
diff --git a/vendor/github.com/mattermost/html2text/LICENSE b/vendor/github.com/mattermost/html2text/LICENSE
new file mode 100644
index 000000000..24dc4abec
--- /dev/null
+++ b/vendor/github.com/mattermost/html2text/LICENSE
@@ -0,0 +1,22 @@
+The MIT License (MIT)
+
+Copyright (c) 2015 Jay Taylor
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
diff --git a/vendor/github.com/mattermost/html2text/README.md b/vendor/github.com/mattermost/html2text/README.md
new file mode 100644
index 000000000..57abf3ff3
--- /dev/null
+++ b/vendor/github.com/mattermost/html2text/README.md
@@ -0,0 +1,108 @@
+# html2text
+
+[![Documentation](https://godoc.org/github.com/cpanato/html2text?status.svg)](https://godoc.org/github.com/cpanato/html2text)
+[![Build Status](https://travis-ci.org/cpanato/html2text.svg?branch=master)](https://travis-ci.org/cpanato/html2text)
+[![Report Card](https://goreportcard.com/badge/github.com/jaytaylor/html2text)](https://goreportcard.com/report/github.com/cpanato/html2text)
+
+### Initial information
+ This project was forked from [github.com/jaytaylor/html2text](https://github.com/jaytaylor/html2text) in order to use another clean bom library due the original one has no license.
+
+
+### Converts HTML into text
+
+
+## Introduction
+
+Ensure your emails are readable by all!
+
+Turns HTML into raw text, useful for sending fancy HTML emails with a equivalently nicely formatted TXT document as a fallback (e.g. for people who don't allow HTML emails or have other display issues).
+
+html2text is a simple golang package for rendering HTML into plaintext.
+
+There are still lots of improvements to be had, but FWIW this has worked fine for my [basic] HTML-2-text needs.
+
+It requires go 1.x or newer ;)
+
+
+## Download the package
+
+```bash
+go get github.com/cpanato/html2text
+```
+
+## Example usage
+
+```go
+package main
+
+import (
+ "fmt"
+
+ "github.com/cpanato/html2text"
+)
+
+func main() {
+ inputHtml := `
+ <html>
+ <head>
+ <title>My Mega Service</title>
+ <link rel=\"stylesheet\" href=\"main.css\">
+ <style type=\"text/css\">body { color: #fff; }</style>
+ </head>
+
+ <body>
+ <div class="logo">
+ <a href="http://mymegaservice.com/"><img src="/logo-image.jpg" alt="Mega Service"/></a>
+ </div>
+
+ <h1>Welcome to your new account on my service!</h1>
+
+ <p>
+ Here is some more information:
+
+ <ul>
+ <li>Link 1: <a href="https://example.com">Example.com</a></li>
+ <li>Link 2: <a href="https://example2.com">Example2.com</a></li>
+ <li>Something else</li>
+ </ul>
+ </p>
+ </body>
+ </html>
+ `
+
+ text, err := html2text.FromString(inputHtml)
+ if err != nil {
+ panic(err)
+ }
+ fmt.Println(text)
+}
+```
+
+Output:
+```
+Mega Service ( http://mymegaservice.com/ )
+
+******************************************
+Welcome to your new account on my service!
+******************************************
+
+Here is some more information:
+
+* Link 1: Example.com ( https://example.com )
+* Link 2: Example2.com ( https://example2.com )
+* Something else
+```
+
+
+## Unit-tests
+
+Running the unit-tests is straightforward and standard:
+
+```bash
+go test
+```
+
+
+# License
+
+Permissive MIT license.
diff --git a/vendor/github.com/mattermost/html2text/html2text.go b/vendor/github.com/mattermost/html2text/html2text.go
new file mode 100644
index 000000000..61774e8a0
--- /dev/null
+++ b/vendor/github.com/mattermost/html2text/html2text.go
@@ -0,0 +1,312 @@
+package html2text
+
+import (
+ "bytes"
+ "io"
+ "io/ioutil"
+ "regexp"
+ "strings"
+ "unicode"
+
+ "github.com/dimchansky/utfbom"
+
+ "golang.org/x/net/html"
+ "golang.org/x/net/html/atom"
+)
+
+var (
+ spacingRe = regexp.MustCompile(`[ \r\n\t]+`)
+ newlineRe = regexp.MustCompile(`\n\n+`)
+)
+
+type textifyTraverseCtx struct {
+ Buf bytes.Buffer
+
+ prefix string
+ blockquoteLevel int
+ lineLength int
+ endsWithSpace bool
+ endsWithNewline bool
+ justClosedDiv bool
+}
+
+func (ctx *textifyTraverseCtx) traverse(node *html.Node) error {
+ switch node.Type {
+ default:
+ return ctx.traverseChildren(node)
+
+ case html.TextNode:
+ data := strings.Trim(spacingRe.ReplaceAllString(node.Data, " "), " ")
+ return ctx.emit(data)
+
+ case html.ElementNode:
+ return ctx.handleElementNode(node)
+ }
+}
+
+func (ctx *textifyTraverseCtx) handleElementNode(node *html.Node) error {
+ ctx.justClosedDiv = false
+ switch node.DataAtom {
+ case atom.Br:
+ return ctx.emit("\n")
+
+ case atom.H1, atom.H2, atom.H3:
+ subCtx := textifyTraverseCtx{}
+ if err := subCtx.traverseChildren(node); err != nil {
+ return err
+ }
+
+ str := subCtx.Buf.String()
+ dividerLen := 0
+ for _, line := range strings.Split(str, "\n") {
+ if lineLen := len([]rune(line)); lineLen-1 > dividerLen {
+ dividerLen = lineLen - 1
+ }
+ }
+ divider := ""
+ if node.DataAtom == atom.H1 {
+ divider = strings.Repeat("*", dividerLen)
+ } else {
+ divider = strings.Repeat("-", dividerLen)
+ }
+
+ if node.DataAtom == atom.H3 {
+ return ctx.emit("\n\n" + str + "\n" + divider + "\n\n")
+ }
+ return ctx.emit("\n\n" + divider + "\n" + str + "\n" + divider + "\n\n")
+
+ case atom.Blockquote:
+ ctx.blockquoteLevel++
+ ctx.prefix = strings.Repeat(">", ctx.blockquoteLevel) + " "
+ if err := ctx.emit("\n"); err != nil {
+ return err
+ }
+ if ctx.blockquoteLevel == 1 {
+ if err := ctx.emit("\n"); err != nil {
+ return err
+ }
+ }
+ if err := ctx.traverseChildren(node); err != nil {
+ return err
+ }
+ ctx.blockquoteLevel--
+ ctx.prefix = strings.Repeat(">", ctx.blockquoteLevel)
+ if ctx.blockquoteLevel > 0 {
+ ctx.prefix += " "
+ }
+ return ctx.emit("\n\n")
+
+ case atom.Div:
+ if ctx.lineLength > 0 {
+ if err := ctx.emit("\n"); err != nil {
+ return err
+ }
+ }
+ if err := ctx.traverseChildren(node); err != nil {
+ return err
+ }
+ var err error
+ if ctx.justClosedDiv == false {
+ err = ctx.emit("\n")
+ }
+ ctx.justClosedDiv = true
+ return err
+
+ case atom.Li:
+ if err := ctx.emit("* "); err != nil {
+ return err
+ }
+
+ if err := ctx.traverseChildren(node); err != nil {
+ return err
+ }
+
+ return ctx.emit("\n")
+
+ case atom.B, atom.Strong:
+ subCtx := textifyTraverseCtx{}
+ subCtx.endsWithSpace = true
+ if err := subCtx.traverseChildren(node); err != nil {
+ return err
+ }
+ str := subCtx.Buf.String()
+ return ctx.emit("*" + str + "*")
+
+ case atom.A:
+ // If image is the only child, take its alt text as the link text
+ if img := node.FirstChild; img != nil && node.LastChild == img && img.DataAtom == atom.Img {
+ if altText := getAttrVal(img, "alt"); altText != "" {
+ ctx.emit(altText)
+ }
+ } else if err := ctx.traverseChildren(node); err != nil {
+ return err
+ }
+
+ hrefLink := ""
+ if attrVal := getAttrVal(node, "href"); attrVal != "" {
+ attrVal = ctx.normalizeHrefLink(attrVal)
+ if attrVal != "" {
+ hrefLink = "( " + attrVal + " )"
+ }
+ }
+
+ return ctx.emit(hrefLink)
+
+ case atom.P, atom.Ul, atom.Table:
+ if err := ctx.emit("\n\n"); err != nil {
+ return err
+ }
+
+ if err := ctx.traverseChildren(node); err != nil {
+ return err
+ }
+
+ return ctx.emit("\n\n")
+
+ case atom.Tr:
+ if err := ctx.traverseChildren(node); err != nil {
+ return err
+ }
+
+ return ctx.emit("\n")
+
+ case atom.Style, atom.Script, atom.Head:
+ // Ignore the subtree
+ return nil
+
+ default:
+ return ctx.traverseChildren(node)
+ }
+}
+func (ctx *textifyTraverseCtx) traverseChildren(node *html.Node) error {
+ for c := node.FirstChild; c != nil; c = c.NextSibling {
+ if err := ctx.traverse(c); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+func (ctx *textifyTraverseCtx) emit(data string) error {
+ if len(data) == 0 {
+ return nil
+ }
+ lines := ctx.breakLongLines(data)
+ var err error
+ for _, line := range lines {
+ runes := []rune(line)
+ startsWithSpace := unicode.IsSpace(runes[0])
+ if !startsWithSpace && !ctx.endsWithSpace {
+ ctx.Buf.WriteByte(' ')
+ ctx.lineLength++
+ }
+ ctx.endsWithSpace = unicode.IsSpace(runes[len(runes)-1])
+ for _, c := range line {
+ _, err = ctx.Buf.WriteString(string(c))
+ if err != nil {
+ return err
+ }
+ ctx.lineLength++
+ if c == '\n' {
+ ctx.lineLength = 0
+ if ctx.prefix != "" {
+ _, err = ctx.Buf.WriteString(ctx.prefix)
+ if err != nil {
+ return err
+ }
+ }
+ }
+ }
+ }
+ return nil
+}
+
+func (ctx *textifyTraverseCtx) breakLongLines(data string) []string {
+ // only break lines when we are in blockquotes
+ if ctx.blockquoteLevel == 0 {
+ return []string{data}
+ }
+ var ret []string
+ runes := []rune(data)
+ l := len(runes)
+ existing := ctx.lineLength
+ if existing >= 74 {
+ ret = append(ret, "\n")
+ existing = 0
+ }
+ for l+existing > 74 {
+ i := 74 - existing
+ for i >= 0 && !unicode.IsSpace(runes[i]) {
+ i--
+ }
+ if i == -1 {
+ // no spaces, so go the other way
+ i = 74 - existing
+ for i < l && !unicode.IsSpace(runes[i]) {
+ i++
+ }
+ }
+ ret = append(ret, string(runes[:i])+"\n")
+ for i < l && unicode.IsSpace(runes[i]) {
+ i++
+ }
+ runes = runes[i:]
+ l = len(runes)
+ existing = 0
+ }
+ if len(runes) > 0 {
+ ret = append(ret, string(runes))
+ }
+ return ret
+}
+
+func (ctx *textifyTraverseCtx) normalizeHrefLink(link string) string {
+ link = strings.TrimSpace(link)
+ link = strings.TrimPrefix(link, "mailto:")
+ return link
+}
+
+func getAttrVal(node *html.Node, attrName string) string {
+ for _, attr := range node.Attr {
+ if attr.Key == attrName {
+ return attr.Val
+ }
+ }
+
+ return ""
+}
+
+func FromHtmlNode(doc *html.Node) (string, error) {
+ ctx := textifyTraverseCtx{
+ Buf: bytes.Buffer{},
+ }
+ if err := ctx.traverse(doc); err != nil {
+ return "", err
+ }
+
+ text := strings.TrimSpace(newlineRe.ReplaceAllString(
+ strings.Replace(ctx.Buf.String(), "\n ", "\n", -1), "\n\n"))
+ return text, nil
+
+}
+
+func FromReader(reader io.Reader) (string, error) {
+ bs, err := ioutil.ReadAll(reader)
+ newReader, _ := utfbom.Skip(bytes.NewReader(bs))
+
+ doc, err := html.Parse(newReader)
+ if err != nil {
+ return "", err
+ }
+ return FromHtmlNode(doc)
+}
+
+func FromString(input string) (string, error) {
+ bs := utfbom.SkipOnly(bytes.NewReader([]byte(input)))
+ text, err := FromReader(bs)
+ if err != nil {
+ return "", err
+ }
+ return text, nil
+}
diff --git a/vendor/github.com/mattermost/html2text/html2text_test.go b/vendor/github.com/mattermost/html2text/html2text_test.go
new file mode 100644
index 000000000..b30d68ac9
--- /dev/null
+++ b/vendor/github.com/mattermost/html2text/html2text_test.go
@@ -0,0 +1,674 @@
+package html2text
+
+import (
+ "bytes"
+ "fmt"
+ "io/ioutil"
+ "path"
+ "regexp"
+ "strings"
+ "testing"
+)
+
+const (
+ destPath = "testdata"
+)
+
+func TestParseUTF8(t *testing.T) {
+ htmlFiles := []struct {
+ file string
+ keywordShouldNotExist string
+ keywordShouldExist string
+ }{
+ {
+ "utf8.html",
+ "学习之道:美国公认学习第一书title",
+ "次世界冠军赛上,我几近疯狂",
+ },
+ {
+ "utf8_with_bom.xhtml",
+ "1892年波兰文版序言title",
+ "种新的波兰文本已成为必要",
+ },
+ }
+
+ for _, htmlFile := range htmlFiles {
+ bs, err := ioutil.ReadFile(path.Join(destPath, htmlFile.file))
+ if err != nil {
+ t.Fatal(err)
+ }
+ text, err := FromReader(bytes.NewReader(bs))
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !strings.Contains(text, htmlFile.keywordShouldExist) {
+ t.Fatalf("keyword %s should exists in file %s", htmlFile.keywordShouldExist, htmlFile.file)
+ }
+ if strings.Contains(text, htmlFile.keywordShouldNotExist) {
+ t.Fatalf("keyword %s should not exists in file %s", htmlFile.keywordShouldNotExist, htmlFile.file)
+ }
+ }
+}
+
+func TestStrippingWhitespace(t *testing.T) {
+ testCases := []struct {
+ input string
+ output string
+ }{
+ {
+ "test text",
+ "test text",
+ },
+ {
+ " \ttext\ntext\n",
+ "text text",
+ },
+ {
+ " \na \n\t \n \n a \t",
+ "a a",
+ },
+ {
+ "test text",
+ "test text",
+ },
+ {
+ "test&nbsp;&nbsp;&nbsp; text&nbsp;",
+ "test    text",
+ },
+ }
+
+ for _, testCase := range testCases {
+ assertString(t, testCase.input, testCase.output)
+ }
+}
+
+func TestParagraphsAndBreaks(t *testing.T) {
+ testCases := []struct {
+ input string
+ output string
+ }{
+ {
+ "Test text",
+ "Test text",
+ },
+ {
+ "Test text<br>",
+ "Test text",
+ },
+ {
+ "Test text<br>Test",
+ "Test text\nTest",
+ },
+ {
+ "<p>Test text</p>",
+ "Test text",
+ },
+ {
+ "<p>Test text</p><p>Test text</p>",
+ "Test text\n\nTest text",
+ },
+ {
+ "\n<p>Test text</p>\n\n\n\t<p>Test text</p>\n",
+ "Test text\n\nTest text",
+ },
+ {
+ "\n<p>Test text<br/>Test text</p>\n",
+ "Test text\nTest text",
+ },
+ {
+ "\n<p>Test text<br> \tTest text<br></p>\n",
+ "Test text\nTest text",
+ },
+ {
+ "Test text<br><BR />Test text",
+ "Test text\n\nTest text",
+ },
+ }
+
+ for _, testCase := range testCases {
+ assertString(t, testCase.input, testCase.output)
+ }
+}
+
+func TestTables(t *testing.T) {
+ testCases := []struct {
+ input string
+ output string
+ }{
+ {
+ "<table><tr><td></td><td></td></tr></table>",
+ "",
+ },
+ {
+ "<table><tr><td>cell1</td><td>cell2</td></tr></table>",
+ "cell1 cell2",
+ },
+ {
+ "<table><tr><td>row1</td></tr><tr><td>row2</td></tr></table>",
+ "row1\nrow2",
+ },
+ {
+ `<table>
+ <tr><td>cell1-1</td><td>cell1-2</td></tr>
+ <tr><td>cell2-1</td><td>cell2-2</td></tr>
+ </table>`,
+ "cell1-1 cell1-2\ncell2-1 cell2-2",
+ },
+ {
+ "_<table><tr><td>cell</td></tr></table>_",
+ "_\n\ncell\n\n_",
+ },
+ }
+
+ for _, testCase := range testCases {
+ assertString(t, testCase.input, testCase.output)
+ }
+}
+
+func TestStrippingLists(t *testing.T) {
+ testCases := []struct {
+ input string
+ output string
+ }{
+ {
+ "<ul></ul>",
+ "",
+ },
+ {
+ "<ul><li>item</li></ul>_",
+ "* item\n\n_",
+ },
+ {
+ "<li class='123'>item 1</li> <li>item 2</li>\n_",
+ "* item 1\n* item 2\n_",
+ },
+ {
+ "<li>item 1</li> \t\n <li>item 2</li> <li> item 3</li>\n_",
+ "* item 1\n* item 2\n* item 3\n_",
+ },
+ }
+
+ for _, testCase := range testCases {
+ assertString(t, testCase.input, testCase.output)
+ }
+}
+
+func TestLinks(t *testing.T) {
+ testCases := []struct {
+ input string
+ output string
+ }{
+ {
+ `<a></a>`,
+ ``,
+ },
+ {
+ `<a href=""></a>`,
+ ``,
+ },
+ {
+ `<a href="http://example.com/"></a>`,
+ `( http://example.com/ )`,
+ },
+ {
+ `<a href="">Link</a>`,
+ `Link`,
+ },
+ {
+ `<a href="http://example.com/">Link</a>`,
+ `Link ( http://example.com/ )`,
+ },
+ {
+ `<a href="http://example.com/"><span class="a">Link</span></a>`,
+ `Link ( http://example.com/ )`,
+ },
+ {
+ "<a href='http://example.com/'>\n\t<span class='a'>Link</span>\n\t</a>",
+ `Link ( http://example.com/ )`,
+ },
+ {
+ "<a href='mailto:contact@example.org'>Contact Us</a>",
+ `Contact Us ( contact@example.org )`,
+ },
+ {
+ "<a href=\"http://example.com:80/~user?aaa=bb&amp;c=d,e,f#foo\">Link</a>",
+ `Link ( http://example.com:80/~user?aaa=bb&c=d,e,f#foo )`,
+ },
+ {
+ "<a title='title' href=\"http://example.com/\">Link</a>",
+ `Link ( http://example.com/ )`,
+ },
+ {
+ "<a href=\" http://example.com/ \"> Link </a>",
+ `Link ( http://example.com/ )`,
+ },
+ {
+ "<a href=\"http://example.com/a/\">Link A</a> <a href=\"http://example.com/b/\">Link B</a>",
+ `Link A ( http://example.com/a/ ) Link B ( http://example.com/b/ )`,
+ },
+ {
+ "<a href=\"%%LINK%%\">Link</a>",
+ `Link ( %%LINK%% )`,
+ },
+ {
+ "<a href=\"[LINK]\">Link</a>",
+ `Link ( [LINK] )`,
+ },
+ {
+ "<a href=\"{LINK}\">Link</a>",
+ `Link ( {LINK} )`,
+ },
+ {
+ "<a href=\"[[!unsubscribe]]\">Link</a>",
+ `Link ( [[!unsubscribe]] )`,
+ },
+ {
+ "<p>This is <a href=\"http://www.google.com\" >link1</a> and <a href=\"http://www.google.com\" >link2 </a> is next.</p>",
+ `This is link1 ( http://www.google.com ) and link2 ( http://www.google.com ) is next.`,
+ },
+ }
+
+ for _, testCase := range testCases {
+ assertString(t, testCase.input, testCase.output)
+ }
+}
+
+func TestImageAltTags(t *testing.T) {
+ testCases := []struct {
+ input string
+ output string
+ }{
+ {
+ `<img />`,
+ ``,
+ },
+ {
+ `<img src="http://example.ru/hello.jpg" />`,
+ ``,
+ },
+ {
+ `<img alt="Example"/>`,
+ ``,
+ },
+ {
+ `<img src="http://example.ru/hello.jpg" alt="Example"/>`,
+ ``,
+ },
+ // Images do matter if they are in a link
+ {
+ `<a href="http://example.com/"><img src="http://example.ru/hello.jpg" alt="Example"/></a>`,
+ `Example ( http://example.com/ )`,
+ },
+ {
+ `<a href="http://example.com/"><img src="http://example.ru/hello.jpg" alt="Example"></a>`,
+ `Example ( http://example.com/ )`,
+ },
+ {
+ `<a href='http://example.com/'><img src='http://example.ru/hello.jpg' alt='Example'/></a>`,
+ `Example ( http://example.com/ )`,
+ },
+ {
+ `<a href='http://example.com/'><img src='http://example.ru/hello.jpg' alt='Example'></a>`,
+ `Example ( http://example.com/ )`,
+ },
+ }
+
+ for _, testCase := range testCases {
+ assertString(t, testCase.input, testCase.output)
+ }
+}
+
+func TestHeadings(t *testing.T) {
+ testCases := []struct {
+ input string
+ output string
+ }{
+ {
+ "<h1>Test</h1>",
+ "****\nTest\n****",
+ },
+ {
+ "\t<h1>\nTest</h1> ",
+ "****\nTest\n****",
+ },
+ {
+ "\t<h1>\nTest line 1<br>Test 2</h1> ",
+ "***********\nTest line 1\nTest 2\n***********",
+ },
+ {
+ "<h1>Test</h1> <h1>Test</h1>",
+ "****\nTest\n****\n\n****\nTest\n****",
+ },
+ {
+ "<h2>Test</h2>",
+ "----\nTest\n----",
+ },
+ {
+ "<h1><a href='http://example.com/'>Test</a></h1>",
+ "****************************\nTest ( http://example.com/ )\n****************************",
+ },
+ {
+ "<h3> <span class='a'>Test </span></h3>",
+ "Test\n----",
+ },
+ }
+
+ for _, testCase := range testCases {
+ assertString(t, testCase.input, testCase.output)
+ }
+
+}
+
+func TestBold(t *testing.T) {
+ testCases := []struct {
+ input string
+ output string
+ }{
+ {
+ "<b>Test</b>",
+ "*Test*",
+ },
+ {
+ "\t<b>Test</b> ",
+ "*Test*",
+ },
+ {
+ "\t<b>Test line 1<br>Test 2</b> ",
+ "*Test line 1\nTest 2*",
+ },
+ {
+ "<b>Test</b> <b>Test</b>",
+ "*Test* *Test*",
+ },
+ }
+
+ for _, testCase := range testCases {
+ assertString(t, testCase.input, testCase.output)
+ }
+
+}
+
+func TestDiv(t *testing.T) {
+ testCases := []struct {
+ input string
+ output string
+ }{
+ {
+ "<div>Test</div>",
+ "Test",
+ },
+ {
+ "\t<div>Test</div> ",
+ "Test",
+ },
+ {
+ "<div>Test line 1<div>Test 2</div></div>",
+ "Test line 1\nTest 2",
+ },
+ {
+ "Test 1<div>Test 2</div> <div>Test 3</div>Test 4",
+ "Test 1\nTest 2\nTest 3\nTest 4",
+ },
+ }
+
+ for _, testCase := range testCases {
+ assertString(t, testCase.input, testCase.output)
+ }
+
+}
+
+func TestBlockquotes(t *testing.T) {
+ testCases := []struct {
+ input string
+ output string
+ }{
+ {
+ "<div>level 0<blockquote>level 1<br><blockquote>level 2</blockquote>level 1</blockquote><div>level 0</div></div>",
+ "level 0\n> \n> level 1\n> \n>> level 2\n> \n> level 1\n\nlevel 0",
+ },
+ {
+ "<blockquote>Test</blockquote>Test",
+ "> \n> Test\n\nTest",
+ },
+ {
+ "\t<blockquote> \nTest<br></blockquote> ",
+ "> \n> Test\n>",
+ },
+ {
+ "\t<blockquote> \nTest line 1<br>Test 2</blockquote> ",
+ "> \n> Test line 1\n> Test 2",
+ },
+ {
+ "<blockquote>Test</blockquote> <blockquote>Test</blockquote> Other Test",
+ "> \n> Test\n\n> \n> Test\n\nOther Test",
+ },
+ {
+ "<blockquote>Lorem ipsum Commodo id consectetur pariatur ea occaecat minim aliqua ad sit consequat quis ex commodo Duis incididunt eu mollit consectetur fugiat voluptate dolore in pariatur in commodo occaecat Ut occaecat velit esse labore aute quis commodo non sit dolore officia Excepteur cillum amet cupidatat culpa velit labore ullamco dolore mollit elit in aliqua dolor irure do</blockquote>",
+ "> \n> Lorem ipsum Commodo id consectetur pariatur ea occaecat minim aliqua ad\n> sit consequat quis ex commodo Duis incididunt eu mollit consectetur fugiat\n> voluptate dolore in pariatur in commodo occaecat Ut occaecat velit esse\n> labore aute quis commodo non sit dolore officia Excepteur cillum amet\n> cupidatat culpa velit labore ullamco dolore mollit elit in aliqua dolor\n> irure do",
+ },
+ {
+ "<blockquote>Lorem<b>ipsum</b><b>Commodo</b><b>id</b><b>consectetur</b><b>pariatur</b><b>ea</b><b>occaecat</b><b>minim</b><b>aliqua</b><b>ad</b><b>sit</b><b>consequat</b><b>quis</b><b>ex</b><b>commodo</b><b>Duis</b><b>incididunt</b><b>eu</b><b>mollit</b><b>consectetur</b><b>fugiat</b><b>voluptate</b><b>dolore</b><b>in</b><b>pariatur</b><b>in</b><b>commodo</b><b>occaecat</b><b>Ut</b><b>occaecat</b><b>velit</b><b>esse</b><b>labore</b><b>aute</b><b>quis</b><b>commodo</b><b>non</b><b>sit</b><b>dolore</b><b>officia</b><b>Excepteur</b><b>cillum</b><b>amet</b><b>cupidatat</b><b>culpa</b><b>velit</b><b>labore</b><b>ullamco</b><b>dolore</b><b>mollit</b><b>elit</b><b>in</b><b>aliqua</b><b>dolor</b><b>irure</b><b>do</b></blockquote>",
+ "> \n> Lorem *ipsum* *Commodo* *id* *consectetur* *pariatur* *ea* *occaecat* *minim*\n> *aliqua* *ad* *sit* *consequat* *quis* *ex* *commodo* *Duis* *incididunt* *eu*\n> *mollit* *consectetur* *fugiat* *voluptate* *dolore* *in* *pariatur* *in* *commodo*\n> *occaecat* *Ut* *occaecat* *velit* *esse* *labore* *aute* *quis* *commodo*\n> *non* *sit* *dolore* *officia* *Excepteur* *cillum* *amet* *cupidatat* *culpa*\n> *velit* *labore* *ullamco* *dolore* *mollit* *elit* *in* *aliqua* *dolor* *irure*\n> *do*",
+ },
+ }
+
+ for _, testCase := range testCases {
+ assertString(t, testCase.input, testCase.output)
+ }
+
+}
+
+func TestIgnoreStylesScriptsHead(t *testing.T) {
+ testCases := []struct {
+ input string
+ output string
+ }{
+ {
+ "<style>Test</style>",
+ "",
+ },
+ {
+ "<style type=\"text/css\">body { color: #fff; }</style>",
+ "",
+ },
+ {
+ "<link rel=\"stylesheet\" href=\"main.css\">",
+ "",
+ },
+ {
+ "<script>Test</script>",
+ "",
+ },
+ {
+ "<script src=\"main.js\"></script>",
+ "",
+ },
+ {
+ "<script type=\"text/javascript\" src=\"main.js\"></script>",
+ "",
+ },
+ {
+ "<script type=\"text/javascript\">Test</script>",
+ "",
+ },
+ {
+ "<script type=\"text/ng-template\" id=\"template.html\"><a href=\"http://google.com\">Google</a></script>",
+ "",
+ },
+ {
+ "<script type=\"bla-bla-bla\" id=\"template.html\">Test</script>",
+ "",
+ },
+ {
+ `<html><head><title>Title</title></head><body></body></html>`,
+ "",
+ },
+ }
+
+ for _, testCase := range testCases {
+ assertString(t, testCase.input, testCase.output)
+ }
+}
+
+func TestText(t *testing.T) {
+ testCases := []struct {
+ input string
+ expr string
+ }{
+ {
+ `<li>
+ <a href="/new" data-ga-click="Header, create new repository, icon:repo"><span class="octicon octicon-repo"></span> New repository</a>
+ </li>`,
+ `\* New repository \( /new \)`,
+ },
+ {
+ `hi
+
+ <br>
+
+ hello <a href="https://google.com">google</a>
+ <br><br>
+ test<p>List:</p>
+
+ <ul>
+ <li><a href="foo">Foo</a></li>
+ <li><a href="http://www.microshwhat.com/bar/soapy">Barsoap</a></li>
+ <li>Baz</li>
+ </ul>
+`,
+ `hi
+hello google \( https://google.com \)
+
+test
+
+List:
+
+\* Foo \( foo \)
+\* Barsoap \( http://www.microshwhat.com/bar/soapy \)
+\* Baz`,
+ },
+ // Malformed input html.
+ {
+ `hi
+
+ hello <a href="https://google.com">google</a>
+
+ test<p>List:</p>
+
+ <ul>
+ <li><a href="foo">Foo</a>
+ <li><a href="/
+ bar/baz">Bar</a>
+ <li>Baz</li>
+ </ul>
+ `,
+ `hi hello google \( https://google.com \) test
+
+List:
+
+\* Foo \( foo \)
+\* Bar \( /\n[ \t]+bar/baz \)
+\* Baz`,
+ },
+ }
+
+ for _, testCase := range testCases {
+ assertRegexp(t, testCase.input, testCase.expr)
+ }
+}
+
+type StringMatcher interface {
+ MatchString(string) bool
+ String() string
+}
+
+type RegexpStringMatcher string
+
+func (m RegexpStringMatcher) MatchString(str string) bool {
+ return regexp.MustCompile(string(m)).MatchString(str)
+}
+func (m RegexpStringMatcher) String() string {
+ return string(m)
+}
+
+type ExactStringMatcher string
+
+func (m ExactStringMatcher) MatchString(str string) bool {
+ return string(m) == str
+}
+func (m ExactStringMatcher) String() string {
+ return string(m)
+}
+
+func assertRegexp(t *testing.T, input string, outputRE string) {
+ assertPlaintext(t, input, RegexpStringMatcher(outputRE))
+}
+
+func assertString(t *testing.T, input string, output string) {
+ assertPlaintext(t, input, ExactStringMatcher(output))
+}
+
+func assertPlaintext(t *testing.T, input string, matcher StringMatcher) {
+ text, err := FromString(input)
+ if err != nil {
+ t.Error(err)
+ }
+ if !matcher.MatchString(text) {
+ t.Errorf("Input did not match expression\n"+
+ "Input:\n>>>>\n%s\n<<<<\n\n"+
+ "Output:\n>>>>\n%s\n<<<<\n\n"+
+ "Expected output:\n>>>>\n%s\n<<<<\n\n",
+ input, text, matcher.String())
+ } else {
+ t.Logf("input:\n\n%s\n\n\n\noutput:\n\n%s\n", input, text)
+ }
+}
+
+func Example() {
+ inputHtml := `
+ <html>
+ <head>
+ <title>My Mega Service</title>
+ <link rel=\"stylesheet\" href=\"main.css\">
+ <style type=\"text/css\">body { color: #fff; }</style>
+ </head>
+
+ <body>
+ <div class="logo">
+ <a href="http://mymegaservice.com/"><img src="/logo-image.jpg" alt="Mega Service"/></a>
+ </div>
+
+ <h1>Welcome to your new account on my service!</h1>
+
+ <p>
+ Here is some more information:
+
+ <ul>
+ <li>Link 1: <a href="https://example.com">Example.com</a></li>
+ <li>Link 2: <a href="https://example2.com">Example2.com</a></li>
+ <li>Something else</li>
+ </ul>
+ </p>
+ </body>
+ </html>
+ `
+
+ text, err := FromString(inputHtml)
+ if err != nil {
+ panic(err)
+ }
+ fmt.Println(text)
+
+ // Output:
+ // Mega Service ( http://mymegaservice.com/ )
+ //
+ // ******************************************
+ // Welcome to your new account on my service!
+ // ******************************************
+ //
+ // Here is some more information:
+ //
+ // * Link 1: Example.com ( https://example.com )
+ // * Link 2: Example2.com ( https://example2.com )
+ // * Something else
+}
diff --git a/vendor/github.com/mattermost/html2text/testdata/utf8.html b/vendor/github.com/mattermost/html2text/testdata/utf8.html
new file mode 100755
index 000000000..53d401ce9
--- /dev/null
+++ b/vendor/github.com/mattermost/html2text/testdata/utf8.html
@@ -0,0 +1,22 @@
+<?xml version='1.0' encoding='utf-8'?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+ <title>学习之道:美国公认学习第一书title</title>
+ <link href="stylesheet.css" rel="stylesheet" type="text/css" />
+ <link href="page_styles.css" rel="stylesheet" type="text/css" />
+</head>
+
+<body class="calibre">
+ <p id="filepos9452" class="calibre_"><span class="calibre6"><span class="bold">写在前面的话</span></span>
+ </p>
+ <p class="calibre_12">在台湾的那次世界冠军赛上,我几近疯狂,直至两年后的今天,我仍沉浸在这次的经历中。这是我生平第一次如此深入地审视我自己,甚至是第一次尝试审视自己。这个过程令人很是兴奋,同时也有点感觉怪异。我重新认识了自我,看到了自己的另外一面,自己从未发觉的另外一面。为了生存,为了取胜,我成了一名角斗士,彻头彻尾,简单纯粹。我并没有意识到这一角色早已在我的心中生根发芽,呼之欲出。也许,他的出现已是不可避免。</p>
+ <p class="calibre_7">而我这全新的一面,与我一直熟识的那个乔希,那个曾经害怕黑暗的孩子,那个象棋手,那个狂热于雨水、反复诵读杰克·克鲁亚克作品的年轻人之间,又有什么样的联系呢?这些都是我正在努力弄清楚的问题。</p>
+ <p class="calibre_7">自台湾赛事之后,我急切非常,一心想要回到训练中去,摆脱自己已经达到巅峰的想法。在过去的两年中,我已经重新开始。这是一个新的起点。前方的路还很长,有待进一步的探索。</p>
+ <p class="calibre_7">这本书的创作耗费了相当多的时间和精力。在成长的过程中,我在我的小房间里从未想过等待我的会是这样的战斗。在创作中,我的思想逐渐成熟;爱恋从分崩离析,到失而复得,世界冠军头衔从失之交臂,到囊中取物。如果说在我人生的第一个二十九年中,我学到了什么,那就是,我们永远无法预测结局,无论是重要的比赛、冒险,还是轰轰烈烈的爱情。我们唯一可以肯定的只有,出乎意料。不管我们做了多么万全的准备,在生活的真实场景中,我们总是会处于陌生的境地。我们也许会无法冷静,失去理智,感觉似乎整个世界都在针对我们。在这个时候,我们所要做的是要付出加倍的努力,要表现得比预想得更好。我认为,关键在于准备好随机应变,准备好在所能想象的高压下发挥出创造力。</p>
+ <p class="calibre_7">读者朋友们,我非常希望你们在读过这本书后,可以得到启发,甚至会得到触动,从而能够根据各自的天赋与特长,去实现自己的梦想。这就是我写作此书的目的。我在字里行间所传达的理念曾经使我受益匪浅,我很希望它们可以为大家提供一个基本的框架和方向。如果我的方法言之有理,那么就请接受它,琢磨它,并加之自己的见解。忘记我的那些数字。真正的掌握需要通过自己发现一些最能够引起共鸣的信息,并将其彻底地融合进来,直至成为一体,这样我们才能随心所欲地驾驭它。</p>
+ <div class="mbp_pagebreak" id="calibre_pb_4"></div>
+</body>
+
+</html> \ No newline at end of file
diff --git a/vendor/github.com/mattermost/html2text/testdata/utf8_with_bom.xhtml b/vendor/github.com/mattermost/html2text/testdata/utf8_with_bom.xhtml
new file mode 100755
index 000000000..68f0ee707
--- /dev/null
+++ b/vendor/github.com/mattermost/html2text/testdata/utf8_with_bom.xhtml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="zh-CN">
+
+<head>
+ <meta http-equiv="Content-Type" content="application/xhtml+xml; charset=utf-8" />
+ <title>1892年波兰文版序言title</title>
+ <link rel="stylesheet" href="css/stylesheet.css" type="text/css" />
+</head>
+
+<body>
+ <div id="page30" />
+ <h2 id="CHP2-6">1892年波兰文版序言<a id="wzyy_18_30" href="#wz_18_30"><sup>[18]</sup></a></h2>
+ <p>出版共产主义宣言的一种新的波兰文本已成为必要,这一事实,引起了许多感想。</p>
+ <p>首先值得注意的是,近来宣言在一定程度上已成为欧洲大陆大工业发展的一种尺度。一个国家的大工业越发展,该国工人中想认清自己作为工人阶级在有产阶级面前所处地位的要求就越增加,他们中间的社会主义运动也越扩大,因而对宣言的需求也越增长。这样,根据宣言用某国文字销行的份数,不仅能够相当确切地断定该国工人运动的状况,而且还能够相当确切地断定该国大工业发展的程度。</p>
+ <p>因此,波兰文的新版本标志着波兰工业的决定性进步。从十年前发表的上一个版本以来确实有了这种进步,对此丝毫不容置疑。俄国的波兰,会议的波兰<a id="wzyy_19_30" href="#wz_19_30"><sup>[19]</sup></a>,成了俄罗斯帝国巨大的工业区。俄国大工业是零星分散的,一部分在芬兰湾沿岸,一部分在中央区(莫斯科和弗拉基米尔),第三部分在黑海和亚速海沿岸,还有另一些散布在别处;而波兰工业则紧缩于相对狭小的地区,享受到由这种积聚引起的长处与短处。这种长处是竞争着的俄罗斯工厂主所承认的,他们要求实行保护关税以对付波兰,尽管他们渴望使波兰人俄罗斯化。这种短处,对波兰工厂主与俄罗斯政府来说,表现在社会主义思想在波兰工人中间的迅速传播和对宣言需求的增长。</p>
+ <p>但是,波兰工业的迅速发展——它超过了俄国工业——本身<a id="page31" />是波兰人民的坚强生命力的一个新证明,是波兰人民临近的民族复兴的一个新保证。而一个独立强盛的波兰的复兴,不只是一件同波兰人有关、而且是同我们大家有关的事情。只有当每个民族在自己内部完全自主时,欧洲各民族间真诚的国际合作才是可能的。1848年革命在无产阶级旗帜下,使无产阶级的战士最终只作了资产阶级的工作,这次革命通过自己遗嘱的执行者路易·波拿巴和俾斯麦也实现了意大利、德国和匈牙利的独立。然而波兰,它从1792年以来为革命做的比所有这三个国家总共做的还要多,而当它1863年失败于强大十倍的俄军的时候,人们却把它抛弃不顾了。贵族既未能保持住、也未能重新争得波兰的独立;今天波兰的独立对资产阶级至少是无所谓的。然而波兰的独立对于欧洲各民族和谐的合作是必需的。这种独立只有年轻的波兰无产阶级才能争得,而且在它的手中会很好地保持住。因为欧洲所有其余的工人都象波兰工人自己一样也需要波兰的独立。</p>
+ <p>弗·恩格斯</p>
+ <p>1892年2月10日于伦敦</p>
+ <div id="page74" />
+ <div><a id="wz_18_30" href="#wzyy_18_30">[18]</a> 恩格斯用德文为《宣言》新的波兰文本写了这篇序言。1892年由波兰社会主义者在伦敦办的《黎明》杂志社出版。序言寄出后,恩格斯写信给门德尔森(1892年2月11日),信中说,他很愿意学会波兰文,并且深入研究波兰工人运动的发展,以便能够为《宣言》的下一版写一篇更详细的序言。——第20页</div>
+ <div><a id="wz_19_30" href="#wzyy_19_30">[19]</a> 指维也纳会议的波兰,即根据1814—1815年维也纳会议的决定,以波兰王国的正式名义割给俄国的那部分波兰土地。——第20页</div>
+</body>
+
+</html> \ No newline at end of file
diff --git a/vendor/github.com/miekg/dns/scan.go b/vendor/github.com/miekg/dns/scan.go
index 8d4773c3e..5f7f64423 100644
--- a/vendor/github.com/miekg/dns/scan.go
+++ b/vendor/github.com/miekg/dns/scan.go
@@ -278,8 +278,7 @@ func parseZone(r io.Reader, origin, f string, t chan *Token, include int) {
return
}
neworigin := origin // There may be optionally a new origin set after the filename, if not use current one
- l := <-c
- switch l.value {
+ switch l := <-c; l.value {
case zBlank:
l := <-c
if l.value == zString {
diff --git a/vendor/github.com/miekg/dns/scan_test.go b/vendor/github.com/miekg/dns/scan_test.go
new file mode 100644
index 000000000..b31c4c779
--- /dev/null
+++ b/vendor/github.com/miekg/dns/scan_test.go
@@ -0,0 +1,45 @@
+package dns
+
+import (
+ "io/ioutil"
+ "os"
+ "strings"
+ "testing"
+)
+
+func TestParseZoneInclude(t *testing.T) {
+
+ tmpfile, err := ioutil.TempFile("", "dns")
+ if err != nil {
+ t.Fatalf("could not create tmpfile for test: %s", err)
+ }
+
+ if _, err := tmpfile.WriteString("foo\tIN\tA\t127.0.0.1"); err != nil {
+ t.Fatalf("unable to write content to tmpfile %q: %s", tmpfile.Name(), err)
+ }
+ if err := tmpfile.Close(); err != nil {
+ t.Fatalf("could not close tmpfile %q: %s", tmpfile.Name(), err)
+ }
+
+ zone := "$INCLUDE " + tmpfile.Name()
+
+ tok := ParseZone(strings.NewReader(zone), "", "")
+ for x := range tok {
+ if x.Error != nil {
+ t.Fatalf("expected no error, but got %s", x.Error)
+ }
+ }
+
+ os.Remove(tmpfile.Name())
+
+ tok = ParseZone(strings.NewReader(zone), "", "")
+ for x := range tok {
+ if x.Error == nil {
+ t.Fatalf("expected first token to contain an error but it didn't")
+ }
+ if !strings.Contains(x.Error.Error(), "failed to open") ||
+ !strings.Contains(x.Error.Error(), tmpfile.Name()) {
+ t.Fatalf(`expected error to contain: "failed to open" and %q but got: %s`, tmpfile.Name(), x.Error)
+ }
+ }
+}
diff --git a/vendor/github.com/miekg/dns/tsig.go b/vendor/github.com/miekg/dns/tsig.go
index 24013096b..4837b4ab1 100644
--- a/vendor/github.com/miekg/dns/tsig.go
+++ b/vendor/github.com/miekg/dns/tsig.go
@@ -208,6 +208,9 @@ func tsigBuffer(msgbuf []byte, rr *TSIG, requestMAC string, timersOnly bool) []b
rr.Fudge = 300 // Standard (RFC) default.
}
+ // Replace message ID in header with original ID from TSIG
+ binary.BigEndian.PutUint16(msgbuf[0:2], rr.OrigId)
+
if requestMAC != "" {
m := new(macWireFmt)
m.MACSize = uint16(len(requestMAC) / 2)
diff --git a/vendor/github.com/miekg/dns/tsig_test.go b/vendor/github.com/miekg/dns/tsig_test.go
index 48b9988b6..4bc52733c 100644
--- a/vendor/github.com/miekg/dns/tsig_test.go
+++ b/vendor/github.com/miekg/dns/tsig_test.go
@@ -1,6 +1,7 @@
package dns
import (
+ "encoding/binary"
"testing"
"time"
)
@@ -22,6 +23,20 @@ func TestTsig(t *testing.T) {
if err != nil {
t.Fatal(err)
}
+
+ // TSIG accounts for ID substitution. This means if the message ID is
+ // changed by a forwarder, we should still be able to verify the TSIG.
+ m = newTsig(HmacMD5)
+ buf, _, err = TsigGenerate(m, "pRZgBrBvI4NAHZYhxmhs/Q==", "", false)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ binary.BigEndian.PutUint16(buf[0:2], uint16(42))
+ err = TsigVerify(buf, "pRZgBrBvI4NAHZYhxmhs/Q==", "", false)
+ if err != nil {
+ t.Fatal(err)
+ }
}
func TestTsigCase(t *testing.T) {
diff --git a/vendor/github.com/miekg/dns/types.go b/vendor/github.com/miekg/dns/types.go
index 53da4755c..57f065bc8 100644
--- a/vendor/github.com/miekg/dns/types.go
+++ b/vendor/github.com/miekg/dns/types.go
@@ -115,27 +115,27 @@ const (
ClassNONE = 254
ClassANY = 255
- // Message Response Codes.
- RcodeSuccess = 0
- RcodeFormatError = 1
- RcodeServerFailure = 2
- RcodeNameError = 3
- RcodeNotImplemented = 4
- RcodeRefused = 5
- RcodeYXDomain = 6
- RcodeYXRrset = 7
- RcodeNXRrset = 8
- RcodeNotAuth = 9
- RcodeNotZone = 10
- RcodeBadSig = 16 // TSIG
- RcodeBadVers = 16 // EDNS0
- RcodeBadKey = 17
- RcodeBadTime = 18
- RcodeBadMode = 19 // TKEY
- RcodeBadName = 20
- RcodeBadAlg = 21
- RcodeBadTrunc = 22 // TSIG
- RcodeBadCookie = 23 // DNS Cookies
+ // Message Response Codes, see https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml
+ RcodeSuccess = 0 // NoError - No Error [DNS]
+ RcodeFormatError = 1 // FormErr - Format Error [DNS]
+ RcodeServerFailure = 2 // ServFail - Server Failure [DNS]
+ RcodeNameError = 3 // NXDomain - Non-Existent Domain [DNS]
+ RcodeNotImplemented = 4 // NotImp - Not Implemented [DNS]
+ RcodeRefused = 5 // Refused - Query Refused [DNS]
+ RcodeYXDomain = 6 // YXDomain - Name Exists when it should not [DNS Update]
+ RcodeYXRrset = 7 // YXRRSet - RR Set Exists when it should not [DNS Update]
+ RcodeNXRrset = 8 // NXRRSet - RR Set that should exist does not [DNS Update]
+ RcodeNotAuth = 9 // NotAuth - Server Not Authoritative for zone [DNS Update]
+ RcodeNotZone = 10 // NotZone - Name not contained in zone [DNS Update/TSIG]
+ RcodeBadSig = 16 // BADSIG - TSIG Signature Failure [TSIG]
+ RcodeBadVers = 16 // BADVERS - Bad OPT Version [EDNS0]
+ RcodeBadKey = 17 // BADKEY - Key not recognized [TSIG]
+ RcodeBadTime = 18 // BADTIME - Signature out of time window [TSIG]
+ RcodeBadMode = 19 // BADMODE - Bad TKEY Mode [TKEY]
+ RcodeBadName = 20 // BADNAME - Duplicate key name [TKEY]
+ RcodeBadAlg = 21 // BADALG - Algorithm not supported [TKEY]
+ RcodeBadTrunc = 22 // BADTRUNC - Bad Truncation [TSIG]
+ RcodeBadCookie = 23 // BADCOOKIE - Bad/missing Server Cookie [DNS Cookies]
// Message Opcodes. There is no 3.
OpcodeQuery = 0
diff --git a/vendor/github.com/minio/minio-go/.travis.yml b/vendor/github.com/minio/minio-go/.travis.yml
index 066cbe883..9805f7ec8 100644
--- a/vendor/github.com/minio/minio-go/.travis.yml
+++ b/vendor/github.com/minio/minio-go/.travis.yml
@@ -16,5 +16,10 @@ go:
script:
- diff -au <(gofmt -d .) <(printf "")
+- go get -u github.com/cheggaaa/pb/...
+- go get -u github.com/sirupsen/logrus/...
- go vet ./...
-- go test -short -race -v ./...
+- SERVER_ENDPOINT=play.minio.io:9000 ACCESS_KEY=Q3AM3UQ867SPQQA43P2F SECRET_KEY=zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG ENABLE_HTTPS=1 go test -race -v ./...
+- SERVER_ENDPOINT=play.minio.io:9000 ACCESS_KEY=Q3AM3UQ867SPQQA43P2F SECRET_KEY=zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG ENABLE_HTTPS=1 go run functional_tests.go
+- mkdir /tmp/examples \
+ && for i in $(echo examples/s3/*); do go build -o /tmp/examples/$(basename ${i:0:-3}) ${i}; done
diff --git a/vendor/github.com/minio/minio-go/README.md b/vendor/github.com/minio/minio-go/README.md
index 4a91dc9c8..8f3c69668 100644
--- a/vendor/github.com/minio/minio-go/README.md
+++ b/vendor/github.com/minio/minio-go/README.md
@@ -54,7 +54,7 @@ func main() {
log.Fatalln(err)
}
- log.Println("%v", minioClient) // minioClient is now setup
+ log.Printf("%#v\n", minioClient) // minioClient is now setup
```
## Quick Start Example - File Uploader
diff --git a/vendor/github.com/minio/minio-go/api-compose-object.go b/vendor/github.com/minio/minio-go/api-compose-object.go
index 6baf09e84..4fa88b818 100644
--- a/vendor/github.com/minio/minio-go/api-compose-object.go
+++ b/vendor/github.com/minio/minio-go/api-compose-object.go
@@ -309,7 +309,7 @@ func (c Client) uploadPartCopy(bucket, object, uploadID string, partNumber int,
// server-side copying operations.
func (c Client) ComposeObject(dst DestinationInfo, srcs []SourceInfo) error {
if len(srcs) < 1 || len(srcs) > maxPartsCount {
- return ErrInvalidArgument("There must be as least one and upto 10000 source objects.")
+ return ErrInvalidArgument("There must be as least one and up to 10000 source objects.")
}
srcSizes := make([]int64, len(srcs))
diff --git a/vendor/github.com/minio/minio-go/api-get-object.go b/vendor/github.com/minio/minio-go/api-get-object.go
index 1078d2f98..9bd784ffa 100644
--- a/vendor/github.com/minio/minio-go/api-get-object.go
+++ b/vendor/github.com/minio/minio-go/api-get-object.go
@@ -679,12 +679,18 @@ func (c Client) getObject(bucketName, objectName string, reqHeaders RequestHeade
if contentType == "" {
contentType = "application/octet-stream"
}
- var objectStat ObjectInfo
- objectStat.ETag = md5sum
- objectStat.Key = objectName
- objectStat.Size = resp.ContentLength
- objectStat.LastModified = date
- objectStat.ContentType = contentType
+
+ objectStat := ObjectInfo{
+ ETag: md5sum,
+ Key: objectName,
+ Size: resp.ContentLength,
+ LastModified: date,
+ ContentType: contentType,
+ // Extract only the relevant header keys describing the object.
+ // following function filters out a list of standard set of keys
+ // which are not part of object metadata.
+ Metadata: extractObjMetadata(resp.Header),
+ }
// do not close body here, caller will close
return resp.Body, objectStat, nil
diff --git a/vendor/github.com/minio/minio-go/api-put-object-common.go b/vendor/github.com/minio/minio-go/api-put-object-common.go
index 833f1fe8f..0158bc1d8 100644
--- a/vendor/github.com/minio/minio-go/api-put-object-common.go
+++ b/vendor/github.com/minio/minio-go/api-put-object-common.go
@@ -17,7 +17,6 @@
package minio
import (
- "hash"
"io"
"math"
"os"
@@ -76,28 +75,6 @@ func optimalPartInfo(objectSize int64) (totalPartsCount int, partSize int64, las
return totalPartsCount, partSize, lastPartSize, nil
}
-// hashCopyN - Calculates chosen hashes up to partSize amount of bytes.
-func hashCopyN(hashAlgorithms map[string]hash.Hash, hashSums map[string][]byte, writer io.Writer, reader io.Reader, partSize int64) (size int64, err error) {
- hashWriter := writer
- for _, v := range hashAlgorithms {
- hashWriter = io.MultiWriter(hashWriter, v)
- }
-
- // Copies to input at writer.
- size, err = io.CopyN(hashWriter, reader, partSize)
- if err != nil {
- // If not EOF return error right here.
- if err != io.EOF {
- return 0, err
- }
- }
-
- for k, v := range hashAlgorithms {
- hashSums[k] = v.Sum(nil)
- }
- return size, err
-}
-
// getUploadID - fetch upload id if already present for an object name
// or initiate a new request to fetch a new upload id.
func (c Client) newUploadID(bucketName, objectName string, metaData map[string][]string) (uploadID string, err error) {
diff --git a/vendor/github.com/minio/minio-go/api-put-object-encrypted.go b/vendor/github.com/minio/minio-go/api-put-object-encrypted.go
index 141b3e91c..534a21ecf 100644
--- a/vendor/github.com/minio/minio-go/api-put-object-encrypted.go
+++ b/vendor/github.com/minio/minio-go/api-put-object-encrypted.go
@@ -42,5 +42,5 @@ func (c Client) PutEncryptedObject(bucketName, objectName string, reader io.Read
metadata[amzHeaderKey] = []string{encryptMaterials.GetKey()}
metadata[amzHeaderMatDesc] = []string{encryptMaterials.GetDesc()}
- return c.putObjectMultipart(bucketName, objectName, encryptMaterials, -1, metadata, progress)
+ return c.putObjectMultipartStreamNoLength(bucketName, objectName, encryptMaterials, metadata, progress)
}
diff --git a/vendor/github.com/minio/minio-go/api-put-object-multipart.go b/vendor/github.com/minio/minio-go/api-put-object-multipart.go
index 1938378f8..6e0015acc 100644
--- a/vendor/github.com/minio/minio-go/api-put-object-multipart.go
+++ b/vendor/github.com/minio/minio-go/api-put-object-multipart.go
@@ -27,13 +27,14 @@ import (
"sort"
"strconv"
"strings"
+ "sync"
"github.com/minio/minio-go/pkg/s3utils"
)
func (c Client) putObjectMultipart(bucketName, objectName string, reader io.Reader, size int64,
metadata map[string][]string, progress io.Reader) (n int64, err error) {
- n, err = c.putObjectMultipartNoStream(bucketName, objectName, reader, size, metadata, progress)
+ n, err = c.putObjectMultipartNoStream(bucketName, objectName, reader, metadata, progress)
if err != nil {
errResp := ToErrorResponse(err)
// Verify if multipart functionality is not available, if not
@@ -50,8 +51,17 @@ func (c Client) putObjectMultipart(bucketName, objectName string, reader io.Read
return n, err
}
-func (c Client) putObjectMultipartNoStream(bucketName, objectName string, reader io.Reader, size int64,
- metadata map[string][]string, progress io.Reader) (n int64, err error) {
+// Pool to manage re-usable memory for upload objects
+// with streams with unknown size.
+var bufPool = sync.Pool{
+ New: func() interface{} {
+ _, partSize, _, _ := optimalPartInfo(-1)
+ b := make([]byte, partSize)
+ return &b
+ },
+}
+
+func (c Client) putObjectMultipartNoStream(bucketName, objectName string, reader io.Reader, metadata map[string][]string, progress io.Reader) (n int64, err error) {
// Input validation.
if err = s3utils.CheckValidBucketName(bucketName); err != nil {
return 0, err
@@ -68,7 +78,7 @@ func (c Client) putObjectMultipartNoStream(bucketName, objectName string, reader
var complMultipartUpload completeMultipartUpload
// Calculate the optimal parts info for a given size.
- totalPartsCount, partSize, _, err := optimalPartInfo(size)
+ totalPartsCount, _, _, err := optimalPartInfo(-1)
if err != nil {
return 0, err
}
@@ -88,9 +98,6 @@ func (c Client) putObjectMultipartNoStream(bucketName, objectName string, reader
// Part number always starts with '1'.
partNumber := 1
- // Initialize a temporary buffer.
- tmpBuffer := new(bytes.Buffer)
-
// Initialize parts uploaded map.
partsInfo := make(map[int]ObjectPart)
@@ -100,53 +107,54 @@ func (c Client) putObjectMultipartNoStream(bucketName, objectName string, reader
// HTTPS connection.
hashAlgos, hashSums := c.hashMaterials()
- // Calculates hash sums while copying partSize bytes into tmpBuffer.
- prtSize, rErr := hashCopyN(hashAlgos, hashSums, tmpBuffer, reader, partSize)
- if rErr != nil && rErr != io.EOF {
+ bufp := bufPool.Get().(*[]byte)
+ length, rErr := io.ReadFull(reader, *bufp)
+ if rErr == io.EOF {
+ break
+ }
+ if rErr != nil && rErr != io.ErrUnexpectedEOF {
+ bufPool.Put(bufp)
return 0, rErr
}
- var reader io.Reader
+ // Calculates hash sums while copying partSize bytes into cw.
+ for k, v := range hashAlgos {
+ v.Write((*bufp)[:length])
+ hashSums[k] = v.Sum(nil)
+ }
+
// Update progress reader appropriately to the latest offset
// as we read from the source.
- reader = newHook(tmpBuffer, progress)
+ rd := newHook(bytes.NewReader((*bufp)[:length]), progress)
// Proceed to upload the part.
var objPart ObjectPart
- objPart, err = c.uploadPart(bucketName, objectName, uploadID, reader, partNumber,
- hashSums["md5"], hashSums["sha256"], prtSize, metadata)
+ objPart, err = c.uploadPart(bucketName, objectName, uploadID, rd, partNumber,
+ hashSums["md5"], hashSums["sha256"], int64(length), metadata)
if err != nil {
- // Reset the temporary buffer upon any error.
- tmpBuffer.Reset()
+ bufPool.Put(bufp)
return totalUploadedSize, err
}
// Save successfully uploaded part metadata.
partsInfo[partNumber] = objPart
- // Reset the temporary buffer.
- tmpBuffer.Reset()
-
// Save successfully uploaded size.
- totalUploadedSize += prtSize
+ totalUploadedSize += int64(length)
// Increment part number.
partNumber++
+ // Put back data into bufpool.
+ bufPool.Put(bufp)
+
// For unknown size, Read EOF we break away.
// We do not have to upload till totalPartsCount.
- if size < 0 && rErr == io.EOF {
+ if rErr == io.EOF {
break
}
}
- // Verify if we uploaded all the data.
- if size > 0 {
- if totalUploadedSize != size {
- return totalUploadedSize, ErrUnexpectedEOF(totalUploadedSize, size, bucketName, objectName)
- }
- }
-
// Loop over total uploaded parts to save them in
// Parts array before completing the multipart request.
for i := 1; i < partNumber; i++ {
diff --git a/vendor/github.com/minio/minio-go/api-put-object.go b/vendor/github.com/minio/minio-go/api-put-object.go
index 2ea498789..f4107132e 100644
--- a/vendor/github.com/minio/minio-go/api-put-object.go
+++ b/vendor/github.com/minio/minio-go/api-put-object.go
@@ -17,13 +17,15 @@
package minio
import (
+ "bytes"
+ "fmt"
"io"
"os"
"reflect"
"runtime"
+ "sort"
"strings"
- "github.com/minio/minio-go/pkg/credentials"
"github.com/minio/minio-go/pkg/s3utils"
)
@@ -178,6 +180,7 @@ func (c Client) PutObjectWithProgress(bucketName, objectName string, reader io.R
if err != nil {
return 0, err
}
+
return c.putObjectCommon(bucketName, objectName, reader, size, metadata, progress)
}
@@ -194,21 +197,16 @@ func (c Client) putObjectCommon(bucketName, objectName string, reader io.Reader,
}
if c.overrideSignerType.IsV2() {
- if size > 0 && size < minPartSize {
+ if size >= 0 && size < minPartSize {
return c.putObjectNoChecksum(bucketName, objectName, reader, size, metadata, progress)
}
return c.putObjectMultipart(bucketName, objectName, reader, size, metadata, progress)
}
- // If size cannot be found on a stream, it is not possible
- // to upload using streaming signature, fall back to multipart.
if size < 0 {
- return c.putObjectMultipart(bucketName, objectName, reader, size, metadata, progress)
+ return c.putObjectMultipartStreamNoLength(bucketName, objectName, reader, metadata, progress)
}
- // Set streaming signature.
- c.overrideSignerType = credentials.SignatureV4Streaming
-
if size < minPartSize {
return c.putObjectNoChecksum(bucketName, objectName, reader, size, metadata, progress)
}
@@ -216,3 +214,110 @@ func (c Client) putObjectCommon(bucketName, objectName string, reader io.Reader,
// For all sizes greater than 64MiB do multipart.
return c.putObjectMultipartStream(bucketName, objectName, reader, size, metadata, progress)
}
+
+func (c Client) putObjectMultipartStreamNoLength(bucketName, objectName string, reader io.Reader, metadata map[string][]string,
+ progress io.Reader) (n int64, err error) {
+ // Input validation.
+ if err = s3utils.CheckValidBucketName(bucketName); err != nil {
+ return 0, err
+ }
+ if err = s3utils.CheckValidObjectName(objectName); err != nil {
+ return 0, err
+ }
+
+ // Total data read and written to server. should be equal to
+ // 'size' at the end of the call.
+ var totalUploadedSize int64
+
+ // Complete multipart upload.
+ var complMultipartUpload completeMultipartUpload
+
+ // Calculate the optimal parts info for a given size.
+ totalPartsCount, _, _, err := optimalPartInfo(-1)
+ if err != nil {
+ return 0, err
+ }
+
+ // Initiate a new multipart upload.
+ uploadID, err := c.newUploadID(bucketName, objectName, metadata)
+ if err != nil {
+ return 0, err
+ }
+
+ defer func() {
+ if err != nil {
+ c.abortMultipartUpload(bucketName, objectName, uploadID)
+ }
+ }()
+
+ // Part number always starts with '1'.
+ partNumber := 1
+
+ // Initialize parts uploaded map.
+ partsInfo := make(map[int]ObjectPart)
+
+ for partNumber <= totalPartsCount {
+ bufp := bufPool.Get().(*[]byte)
+ length, rErr := io.ReadFull(reader, *bufp)
+ if rErr == io.EOF {
+ break
+ }
+ if rErr != nil && rErr != io.ErrUnexpectedEOF {
+ bufPool.Put(bufp)
+ return 0, rErr
+ }
+
+ // Update progress reader appropriately to the latest offset
+ // as we read from the source.
+ rd := newHook(bytes.NewReader((*bufp)[:length]), progress)
+
+ // Proceed to upload the part.
+ var objPart ObjectPart
+ objPart, err = c.uploadPart(bucketName, objectName, uploadID, rd, partNumber,
+ nil, nil, int64(length), metadata)
+ if err != nil {
+ bufPool.Put(bufp)
+ return totalUploadedSize, err
+ }
+
+ // Save successfully uploaded part metadata.
+ partsInfo[partNumber] = objPart
+
+ // Save successfully uploaded size.
+ totalUploadedSize += int64(length)
+
+ // Increment part number.
+ partNumber++
+
+ // Put back data into bufpool.
+ bufPool.Put(bufp)
+
+ // For unknown size, Read EOF we break away.
+ // We do not have to upload till totalPartsCount.
+ if rErr == io.EOF {
+ break
+ }
+ }
+
+ // Loop over total uploaded parts to save them in
+ // Parts array before completing the multipart request.
+ for i := 1; i < partNumber; i++ {
+ part, ok := partsInfo[i]
+ if !ok {
+ return 0, ErrInvalidArgument(fmt.Sprintf("Missing part number %d", i))
+ }
+ complMultipartUpload.Parts = append(complMultipartUpload.Parts, CompletePart{
+ ETag: part.ETag,
+ PartNumber: part.PartNumber,
+ })
+ }
+
+ // Sort all completed parts.
+ sort.Sort(completedParts(complMultipartUpload.Parts))
+ if _, err = c.completeMultipartUpload(bucketName, objectName, uploadID, complMultipartUpload); err != nil {
+ return totalUploadedSize, err
+ }
+
+ // Return final size.
+ return totalUploadedSize, nil
+}
diff --git a/vendor/github.com/minio/minio-go/api-stat.go b/vendor/github.com/minio/minio-go/api-stat.go
index 4b530327b..5f06bfc9e 100644
--- a/vendor/github.com/minio/minio-go/api-stat.go
+++ b/vendor/github.com/minio/minio-go/api-stat.go
@@ -167,11 +167,6 @@ func (c Client) statObject(bucketName, objectName string, reqHeaders RequestHead
contentType = "application/octet-stream"
}
- // Extract only the relevant header keys describing the object.
- // following function filters out a list of standard set of keys
- // which are not part of object metadata.
- metadata := extractObjMetadata(resp.Header)
-
// Save object metadata info.
return ObjectInfo{
ETag: md5sum,
@@ -179,6 +174,9 @@ func (c Client) statObject(bucketName, objectName string, reqHeaders RequestHead
Size: size,
LastModified: date,
ContentType: contentType,
- Metadata: metadata,
+ // Extract only the relevant header keys describing the object.
+ // following function filters out a list of standard set of keys
+ // which are not part of object metadata.
+ Metadata: extractObjMetadata(resp.Header),
}, nil
}
diff --git a/vendor/github.com/minio/minio-go/api.go b/vendor/github.com/minio/minio-go/api.go
index 6fe508aa1..811f89a3f 100644
--- a/vendor/github.com/minio/minio-go/api.go
+++ b/vendor/github.com/minio/minio-go/api.go
@@ -211,7 +211,7 @@ func privateNew(endpoint string, creds *credentials.Credentials, secure bool, re
// Instantiate http client and bucket location cache.
clnt.httpClient = &http.Client{
- Transport: http.DefaultTransport,
+ Transport: defaultMinioTransport,
CheckRedirect: redirectHeaders,
}
@@ -700,7 +700,10 @@ func (c Client) newRequest(method string, metadata requestMetadata) (req *http.R
case signerType.IsV2():
// Add signature version '2' authorization header.
req = s3signer.SignV2(*req, accessKeyID, secretAccessKey)
- case signerType.IsStreamingV4() && method == "PUT":
+ case metadata.objectName != "" && method == "PUT" && metadata.customHeader.Get("X-Amz-Copy-Source") == "" && !c.secure:
+ // Streaming signature is used by default for a PUT object request. Additionally we also
+ // look if the initialized client is secure, if yes then we don't need to perform
+ // streaming signature.
req = s3signer.StreamingSignV4(req, accessKeyID,
secretAccessKey, sessionToken, location, metadata.contentLength, time.Now().UTC())
default:
diff --git a/vendor/github.com/minio/minio-go/api_functional_v2_test.go b/vendor/github.com/minio/minio-go/api_functional_v2_test.go
deleted file mode 100644
index e81596ecf..000000000
--- a/vendor/github.com/minio/minio-go/api_functional_v2_test.go
+++ /dev/null
@@ -1,1470 +0,0 @@
-/*
- * Minio Go Library for Amazon S3 Compatible Cloud Storage (C) 2015 Minio, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package minio
-
-import (
- "bytes"
- "errors"
- "io"
- "io/ioutil"
- "log"
- "math/rand"
- "net/http"
- "net/url"
- "os"
- "reflect"
- "strings"
- "testing"
- "time"
-
- "github.com/minio/minio-go/pkg/policy"
-)
-
-// Tests bucket re-create errors.
-func TestMakeBucketErrorV2(t *testing.T) {
- if testing.Short() {
- t.Skip("skipping functional tests for short runs")
- }
- if os.Getenv(serverEndpoint) != "s3.amazonaws.com" {
- t.Skip("skipping region functional tests for non s3 runs")
- }
-
- // Seed random based on current time.
- rand.Seed(time.Now().Unix())
-
- // Instantiate new minio client object.
- c, err := NewV2(
- os.Getenv(serverEndpoint),
- os.Getenv(accessKey),
- os.Getenv(secretKey),
- mustParseBool(os.Getenv(enableSecurity)),
- )
- if err != nil {
- t.Fatal("Error:", err)
- }
-
- // Enable tracing, write to stderr.
- // c.TraceOn(os.Stderr)
-
- // Set user agent.
- c.SetAppInfo("Minio-go-FunctionalTest", "0.1.0")
-
- // Generate a new random bucket name.
- bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test")
-
- // Make a new bucket in 'eu-west-1'.
- if err = c.MakeBucket(bucketName, "eu-west-1"); err != nil {
- t.Fatal("Error:", err, bucketName)
- }
- if err = c.MakeBucket(bucketName, "eu-west-1"); err == nil {
- t.Fatal("Error: make bucket should should fail for", bucketName)
- }
- // Verify valid error response from server.
- if ToErrorResponse(err).Code != "BucketAlreadyExists" &&
- ToErrorResponse(err).Code != "BucketAlreadyOwnedByYou" {
- t.Fatal("Error: Invalid error returned by server", err)
- }
- if err = c.RemoveBucket(bucketName); err != nil {
- t.Fatal("Error:", err, bucketName)
- }
-}
-
-// Test get object reader to not throw error on being closed twice.
-func TestGetObjectClosedTwiceV2(t *testing.T) {
- if testing.Short() {
- t.Skip("skipping functional tests for short runs")
- }
-
- // Seed random based on current time.
- rand.Seed(time.Now().Unix())
-
- // Instantiate new minio client object.
- c, err := NewV2(
- os.Getenv(serverEndpoint),
- os.Getenv(accessKey),
- os.Getenv(secretKey),
- mustParseBool(os.Getenv(enableSecurity)),
- )
- if err != nil {
- t.Fatal("Error:", err)
- }
-
- // Enable tracing, write to stderr.
- // c.TraceOn(os.Stderr)
-
- // Set user agent.
- c.SetAppInfo("Minio-go-FunctionalTest", "0.1.0")
-
- // Generate a new random bucket name.
- bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test")
-
- // Make a new bucket.
- err = c.MakeBucket(bucketName, "us-east-1")
- if err != nil {
- t.Fatal("Error:", err, bucketName)
- }
-
- // Generate data more than 32K.
- buf := bytes.Repeat([]byte("h"), rand.Intn(1<<20)+32*1024)
-
- // Save the data
- objectName := randString(60, rand.NewSource(time.Now().UnixNano()), "")
- n, err := c.PutObject(bucketName, objectName, bytes.NewReader(buf), "binary/octet-stream")
- if err != nil {
- t.Fatal("Error:", err, bucketName, objectName)
- }
-
- if n != int64(len(buf)) {
- t.Fatalf("Error: number of bytes does not match, want %v, got %v\n", len(buf), n)
- }
-
- // Read the data back
- r, err := c.GetObject(bucketName, objectName)
- if err != nil {
- t.Fatal("Error:", err, bucketName, objectName)
- }
-
- st, err := r.Stat()
- if err != nil {
- t.Fatal("Error:", err, bucketName, objectName)
- }
- if st.Size != int64(len(buf)) {
- t.Fatalf("Error: number of bytes in stat does not match, want %v, got %v\n",
- len(buf), st.Size)
- }
- if err := r.Close(); err != nil {
- t.Fatal("Error:", err)
- }
- if err := r.Close(); err == nil {
- t.Fatal("Error: object is already closed, should return error")
- }
-
- err = c.RemoveObject(bucketName, objectName)
- if err != nil {
- t.Fatal("Error: ", err)
- }
- err = c.RemoveBucket(bucketName)
- if err != nil {
- t.Fatal("Error:", err)
- }
-}
-
-// Tests removing partially uploaded objects.
-func TestRemovePartiallyUploadedV2(t *testing.T) {
- if testing.Short() {
- t.Skip("skipping function tests for short runs")
- }
-
- // Seed random based on current time.
- rand.Seed(time.Now().Unix())
-
- // Instantiate new minio client object.
- c, err := NewV2(
- os.Getenv(serverEndpoint),
- os.Getenv(accessKey),
- os.Getenv(secretKey),
- mustParseBool(os.Getenv(enableSecurity)),
- )
- if err != nil {
- t.Fatal("Error:", err)
- }
-
- // Set user agent.
- c.SetAppInfo("Minio-go-FunctionalTest", "0.1.0")
-
- // Enable tracing, write to stdout.
- // c.TraceOn(os.Stderr)
-
- // Generate a new random bucket name.
- bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test")
-
- // make a new bucket.
- err = c.MakeBucket(bucketName, "us-east-1")
- if err != nil {
- t.Fatal("Error:", err, bucketName)
- }
-
- r := bytes.NewReader(bytes.Repeat([]byte("a"), 128*1024))
-
- reader, writer := io.Pipe()
- go func() {
- i := 0
- for i < 25 {
- _, cerr := io.CopyN(writer, r, 128*1024)
- if cerr != nil {
- t.Fatal("Error:", cerr, bucketName)
- }
- i++
- r.Seek(0, 0)
- }
- writer.CloseWithError(errors.New("proactively closed to be verified later"))
- }()
-
- objectName := bucketName + "-resumable"
- _, err = c.PutObject(bucketName, objectName, reader, "application/octet-stream")
- if err == nil {
- t.Fatal("Error: PutObject should fail.")
- }
- if err.Error() != "proactively closed to be verified later" {
- t.Fatal("Error:", err)
- }
- err = c.RemoveIncompleteUpload(bucketName, objectName)
- if err != nil {
- t.Fatal("Error:", err)
- }
- err = c.RemoveBucket(bucketName)
- if err != nil {
- t.Fatal("Error:", err)
- }
-}
-
-// Tests FPutObject hidden contentType setting
-func TestFPutObjectV2(t *testing.T) {
- if testing.Short() {
- t.Skip("skipping functional tests for short runs")
- }
-
- // Seed random based on current time.
- rand.Seed(time.Now().Unix())
-
- // Instantiate new minio client object.
- c, err := NewV2(
- os.Getenv(serverEndpoint),
- os.Getenv(accessKey),
- os.Getenv(secretKey),
- mustParseBool(os.Getenv(enableSecurity)),
- )
- if err != nil {
- t.Fatal("Error:", err)
- }
-
- // Enable tracing, write to stderr.
- // c.TraceOn(os.Stderr)
-
- // Set user agent.
- c.SetAppInfo("Minio-go-FunctionalTest", "0.1.0")
-
- // Generate a new random bucket name.
- bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test")
-
- // Make a new bucket.
- err = c.MakeBucket(bucketName, "us-east-1")
- if err != nil {
- t.Fatal("Error:", err, bucketName)
- }
-
- // Make a temp file with 11*1024*1024 bytes of data.
- file, err := ioutil.TempFile(os.TempDir(), "FPutObjectTest")
- if err != nil {
- t.Fatal("Error:", err)
- }
-
- r := bytes.NewReader(bytes.Repeat([]byte("b"), 11*1024*1024))
- n, err := io.CopyN(file, r, 11*1024*1024)
- if err != nil {
- t.Fatal("Error:", err)
- }
- if n != int64(11*1024*1024) {
- t.Fatalf("Error: number of bytes does not match, want %v, got %v\n", 11*1024*1024, n)
- }
-
- // Close the file pro-actively for windows.
- err = file.Close()
- if err != nil {
- t.Fatal("Error:", err)
- }
-
- // Set base object name
- objectName := bucketName + "FPutObject"
-
- // Perform standard FPutObject with contentType provided (Expecting application/octet-stream)
- n, err = c.FPutObject(bucketName, objectName+"-standard", file.Name(), "application/octet-stream")
- if err != nil {
- t.Fatal("Error:", err)
- }
- if n != int64(11*1024*1024) {
- t.Fatalf("Error: number of bytes does not match, want %v, got %v\n", 11*1024*1024, n)
- }
-
- // Perform FPutObject with no contentType provided (Expecting application/octet-stream)
- n, err = c.FPutObject(bucketName, objectName+"-Octet", file.Name(), "")
- if err != nil {
- t.Fatal("Error:", err)
- }
- if n != int64(11*1024*1024) {
- t.Fatalf("Error: number of bytes does not match, want %v, got %v\n", 11*1024*1024, n)
- }
-
- // Add extension to temp file name
- fileName := file.Name()
- err = os.Rename(file.Name(), fileName+".gtar")
- if err != nil {
- t.Fatal("Error:", err)
- }
-
- // Perform FPutObject with no contentType provided (Expecting application/x-gtar)
- n, err = c.FPutObject(bucketName, objectName+"-GTar", fileName+".gtar", "")
- if err != nil {
- t.Fatal("Error:", err)
- }
- if n != int64(11*1024*1024) {
- t.Fatalf("Error: number of bytes does not match, want %v, got %v\n", 11*1024*1024, n)
- }
-
- // Check headers
- rStandard, err := c.StatObject(bucketName, objectName+"-standard")
- if err != nil {
- t.Fatal("Error:", err, bucketName, objectName+"-standard")
- }
- if rStandard.ContentType != "application/octet-stream" {
- t.Fatalf("Error: Content-Type headers mismatched, want %v, got %v\n",
- "application/octet-stream", rStandard.ContentType)
- }
-
- rOctet, err := c.StatObject(bucketName, objectName+"-Octet")
- if err != nil {
- t.Fatal("Error:", err, bucketName, objectName+"-Octet")
- }
- if rOctet.ContentType != "application/octet-stream" {
- t.Fatalf("Error: Content-Type headers mismatched, want %v, got %v\n",
- "application/octet-stream", rStandard.ContentType)
- }
-
- rGTar, err := c.StatObject(bucketName, objectName+"-GTar")
- if err != nil {
- t.Fatal("Error:", err, bucketName, objectName+"-GTar")
- }
- if rGTar.ContentType != "application/x-gtar" {
- t.Fatalf("Error: Content-Type headers mismatched, want %v, got %v\n",
- "application/x-gtar", rStandard.ContentType)
- }
-
- // Remove all objects and bucket and temp file
- err = c.RemoveObject(bucketName, objectName+"-standard")
- if err != nil {
- t.Fatal("Error: ", err)
- }
-
- err = c.RemoveObject(bucketName, objectName+"-Octet")
- if err != nil {
- t.Fatal("Error: ", err)
- }
-
- err = c.RemoveObject(bucketName, objectName+"-GTar")
- if err != nil {
- t.Fatal("Error: ", err)
- }
-
- err = c.RemoveBucket(bucketName)
- if err != nil {
- t.Fatal("Error:", err)
- }
-
- err = os.Remove(fileName + ".gtar")
- if err != nil {
- t.Fatal("Error:", err)
- }
-
-}
-
-// Tests various bucket supported formats.
-func TestMakeBucketRegionsV2(t *testing.T) {
- if testing.Short() {
- t.Skip("skipping functional tests for short runs")
- }
- if os.Getenv(serverEndpoint) != "s3.amazonaws.com" {
- t.Skip("skipping region functional tests for non s3 runs")
- }
-
- // Seed random based on current time.
- rand.Seed(time.Now().Unix())
-
- // Instantiate new minio client object.
- c, err := NewV2(
- os.Getenv(serverEndpoint),
- os.Getenv(accessKey),
- os.Getenv(secretKey),
- mustParseBool(os.Getenv(enableSecurity)),
- )
- if err != nil {
- t.Fatal("Error:", err)
- }
-
- // Enable tracing, write to stderr.
- // c.TraceOn(os.Stderr)
-
- // Set user agent.
- c.SetAppInfo("Minio-go-FunctionalTest", "0.1.0")
-
- // Generate a new random bucket name.
- bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test")
-
- // Make a new bucket in 'eu-central-1'.
- if err = c.MakeBucket(bucketName, "eu-west-1"); err != nil {
- t.Fatal("Error:", err, bucketName)
- }
-
- if err = c.RemoveBucket(bucketName); err != nil {
- t.Fatal("Error:", err, bucketName)
- }
-
- // Make a new bucket with '.' in its name, in 'us-west-2'. This
- // request is internally staged into a path style instead of
- // virtual host style.
- if err = c.MakeBucket(bucketName+".withperiod", "us-west-2"); err != nil {
- t.Fatal("Error:", err, bucketName+".withperiod")
- }
-
- // Remove the newly created bucket.
- if err = c.RemoveBucket(bucketName + ".withperiod"); err != nil {
- t.Fatal("Error:", err, bucketName+".withperiod")
- }
-}
-
-// Tests get object ReaderSeeker interface methods.
-func TestGetObjectReadSeekFunctionalV2(t *testing.T) {
- if testing.Short() {
- t.Skip("skipping functional tests for short runs")
- }
-
- // Seed random based on current time.
- rand.Seed(time.Now().Unix())
-
- // Instantiate new minio client object.
- c, err := NewV2(
- os.Getenv(serverEndpoint),
- os.Getenv(accessKey),
- os.Getenv(secretKey),
- mustParseBool(os.Getenv(enableSecurity)),
- )
- if err != nil {
- t.Fatal("Error:", err)
- }
-
- // Enable tracing, write to stderr.
- // c.TraceOn(os.Stderr)
-
- // Set user agent.
- c.SetAppInfo("Minio-go-FunctionalTest", "0.1.0")
-
- // Generate a new random bucket name.
- bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test")
-
- // Make a new bucket.
- err = c.MakeBucket(bucketName, "us-east-1")
- if err != nil {
- t.Fatal("Error:", err, bucketName)
- }
-
- // Generate data more than 32K.
- buf := bytes.Repeat([]byte("2"), rand.Intn(1<<20)+32*1024)
-
- // Save the data.
- objectName := randString(60, rand.NewSource(time.Now().UnixNano()), "")
- n, err := c.PutObject(bucketName, objectName, bytes.NewReader(buf), "binary/octet-stream")
- if err != nil {
- t.Fatal("Error:", err, bucketName, objectName)
- }
-
- if n != int64(len(buf)) {
- t.Fatalf("Error: number of bytes does not match, want %v, got %v\n", len(buf), n)
- }
-
- // Read the data back
- r, err := c.GetObject(bucketName, objectName)
- if err != nil {
- t.Fatal("Error:", err, bucketName, objectName)
- }
-
- st, err := r.Stat()
- if err != nil {
- t.Fatal("Error:", err, bucketName, objectName)
- }
- if st.Size != int64(len(buf)) {
- t.Fatalf("Error: number of bytes in stat does not match, want %v, got %v\n",
- len(buf), st.Size)
- }
-
- offset := int64(2048)
- n, err = r.Seek(offset, 0)
- if err != nil {
- t.Fatal("Error:", err, offset)
- }
- if n != offset {
- t.Fatalf("Error: number of bytes seeked does not match, want %v, got %v\n",
- offset, n)
- }
- n, err = r.Seek(0, 1)
- if err != nil {
- t.Fatal("Error:", err)
- }
- if n != offset {
- t.Fatalf("Error: number of current seek does not match, want %v, got %v\n",
- offset, n)
- }
- _, err = r.Seek(offset, 2)
- if err == nil {
- t.Fatal("Error: seek on positive offset for whence '2' should error out")
- }
- n, err = r.Seek(-offset, 2)
- if err != nil {
- t.Fatal("Error:", err)
- }
- if n != st.Size-offset {
- t.Fatalf("Error: number of bytes seeked back does not match, want %d, got %v\n", st.Size-offset, n)
- }
-
- var buffer1 bytes.Buffer
- if _, err = io.CopyN(&buffer1, r, st.Size); err != nil {
- if err != io.EOF {
- t.Fatal("Error:", err)
- }
- }
- if !bytes.Equal(buf[len(buf)-int(offset):], buffer1.Bytes()) {
- t.Fatal("Error: Incorrect read bytes v/s original buffer.")
- }
-
- // Seek again and read again.
- n, err = r.Seek(offset-1, 0)
- if err != nil {
- t.Fatal("Error:", err)
- }
- if n != (offset - 1) {
- t.Fatalf("Error: number of bytes seeked back does not match, want %v, got %v\n", offset-1, n)
- }
-
- var buffer2 bytes.Buffer
- if _, err = io.CopyN(&buffer2, r, st.Size); err != nil {
- if err != io.EOF {
- t.Fatal("Error:", err)
- }
- }
- // Verify now lesser bytes.
- if !bytes.Equal(buf[2047:], buffer2.Bytes()) {
- t.Fatal("Error: Incorrect read bytes v/s original buffer.")
- }
-
- err = c.RemoveObject(bucketName, objectName)
- if err != nil {
- t.Fatal("Error: ", err)
- }
- err = c.RemoveBucket(bucketName)
- if err != nil {
- t.Fatal("Error:", err)
- }
-}
-
-// Tests get object ReaderAt interface methods.
-func TestGetObjectReadAtFunctionalV2(t *testing.T) {
- if testing.Short() {
- t.Skip("skipping functional tests for the short runs")
- }
-
- // Seed random based on current time.
- rand.Seed(time.Now().Unix())
-
- // Instantiate new minio client object.
- c, err := NewV2(
- os.Getenv(serverEndpoint),
- os.Getenv(accessKey),
- os.Getenv(secretKey),
- mustParseBool(os.Getenv(enableSecurity)),
- )
- if err != nil {
- t.Fatal("Error:", err)
- }
-
- // Enable tracing, write to stderr.
- // c.TraceOn(os.Stderr)
-
- // Set user agent.
- c.SetAppInfo("Minio-go-FunctionalTest", "0.1.0")
-
- // Generate a new random bucket name.
- bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test")
-
- // Make a new bucket.
- err = c.MakeBucket(bucketName, "us-east-1")
- if err != nil {
- t.Fatal("Error:", err, bucketName)
- }
-
- // Generate data more than 32K
- buf := bytes.Repeat([]byte("8"), rand.Intn(1<<20)+32*1024)
-
- // Save the data
- objectName := randString(60, rand.NewSource(time.Now().UnixNano()), "")
- n, err := c.PutObject(bucketName, objectName, bytes.NewReader(buf), "binary/octet-stream")
- if err != nil {
- t.Fatal("Error:", err, bucketName, objectName)
- }
-
- if n != int64(len(buf)) {
- t.Fatalf("Error: number of bytes does not match, want %v, got %v\n", len(buf), n)
- }
-
- // Read the data back
- r, err := c.GetObject(bucketName, objectName)
- if err != nil {
- t.Fatal("Error:", err, bucketName, objectName)
- }
-
- st, err := r.Stat()
- if err != nil {
- t.Fatal("Error:", err, bucketName, objectName)
- }
- if st.Size != int64(len(buf)) {
- t.Fatalf("Error: number of bytes in stat does not match, want %v, got %v\n",
- len(buf), st.Size)
- }
-
- offset := int64(2048)
-
- // Read directly
- buf2 := make([]byte, 512)
- buf3 := make([]byte, 512)
- buf4 := make([]byte, 512)
-
- m, err := r.ReadAt(buf2, offset)
- if err != nil {
- t.Fatal("Error:", err, st.Size, len(buf2), offset)
- }
- if m != len(buf2) {
- t.Fatalf("Error: ReadAt read shorter bytes before reaching EOF, want %v, got %v\n", m, len(buf2))
- }
- if !bytes.Equal(buf2, buf[offset:offset+512]) {
- t.Fatal("Error: Incorrect read between two ReadAt from same offset.")
- }
- offset += 512
- m, err = r.ReadAt(buf3, offset)
- if err != nil {
- t.Fatal("Error:", err, st.Size, len(buf3), offset)
- }
- if m != len(buf3) {
- t.Fatalf("Error: ReadAt read shorter bytes before reaching EOF, want %v, got %v\n", m, len(buf3))
- }
- if !bytes.Equal(buf3, buf[offset:offset+512]) {
- t.Fatal("Error: Incorrect read between two ReadAt from same offset.")
- }
- offset += 512
- m, err = r.ReadAt(buf4, offset)
- if err != nil {
- t.Fatal("Error:", err, st.Size, len(buf4), offset)
- }
- if m != len(buf4) {
- t.Fatalf("Error: ReadAt read shorter bytes before reaching EOF, want %v, got %v\n", m, len(buf4))
- }
- if !bytes.Equal(buf4, buf[offset:offset+512]) {
- t.Fatal("Error: Incorrect read between two ReadAt from same offset.")
- }
-
- buf5 := make([]byte, n)
- // Read the whole object.
- m, err = r.ReadAt(buf5, 0)
- if err != nil {
- if err != io.EOF {
- t.Fatal("Error:", err, len(buf5))
- }
- }
- if m != len(buf5) {
- t.Fatalf("Error: ReadAt read shorter bytes before reaching EOF, want %v, got %v\n", m, len(buf5))
- }
- if !bytes.Equal(buf, buf5) {
- t.Fatal("Error: Incorrect data read in GetObject, than what was previously upoaded.")
- }
-
- buf6 := make([]byte, n+1)
- // Read the whole object and beyond.
- _, err = r.ReadAt(buf6, 0)
- if err != nil {
- if err != io.EOF {
- t.Fatal("Error:", err, len(buf6))
- }
- }
- err = c.RemoveObject(bucketName, objectName)
- if err != nil {
- t.Fatal("Error: ", err)
- }
- err = c.RemoveBucket(bucketName)
- if err != nil {
- t.Fatal("Error:", err)
- }
-}
-
-// Tests copy object
-func TestCopyObjectV2(t *testing.T) {
- if testing.Short() {
- t.Skip("Skipping functional tests for short runs")
- }
- // Seed random based on current time.
- rand.Seed(time.Now().Unix())
-
- // Instantiate new minio client object
- c, err := NewV2(
- os.Getenv(serverEndpoint),
- os.Getenv(accessKey),
- os.Getenv(secretKey),
- mustParseBool(os.Getenv(enableSecurity)),
- )
- if err != nil {
- t.Fatal("Error:", err)
- }
-
- // Enable tracing, write to stderr.
- // c.TraceOn(os.Stderr)
-
- // Set user agent.
- c.SetAppInfo("Minio-go-FunctionalTest", "0.1.0")
-
- // Generate a new random bucket name.
- bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test")
-
- // Make a new bucket in 'us-east-1' (source bucket).
- err = c.MakeBucket(bucketName, "us-east-1")
- if err != nil {
- t.Fatal("Error:", err, bucketName)
- }
-
- // Make a new bucket in 'us-east-1' (destination bucket).
- err = c.MakeBucket(bucketName+"-copy", "us-east-1")
- if err != nil {
- t.Fatal("Error:", err, bucketName+"-copy")
- }
-
- // Generate data more than 32K
- buf := bytes.Repeat([]byte("9"), rand.Intn(1<<20)+32*1024)
-
- // Save the data
- objectName := randString(60, rand.NewSource(time.Now().UnixNano()), "")
- n, err := c.PutObject(bucketName, objectName, bytes.NewReader(buf), "binary/octet-stream")
- if err != nil {
- t.Fatal("Error:", err, bucketName, objectName)
- }
-
- if n != int64(len(buf)) {
- t.Fatalf("Error: number of bytes does not match want %v, got %v",
- len(buf), n)
- }
-
- dst, err := NewDestinationInfo(bucketName+"-copy", objectName+"-copy", nil, nil)
- if err != nil {
- t.Fatal(err)
- }
-
- src := NewSourceInfo(bucketName, objectName, nil)
- err = src.SetModifiedSinceCond(time.Date(2014, time.April, 0, 0, 0, 0, 0, time.UTC))
- if err != nil {
- t.Fatal("Error:", err)
- }
-
- // Perform the Copy
- err = c.CopyObject(dst, src)
- if err != nil {
- t.Fatal("Error:", err, bucketName+"-copy", objectName+"-copy")
- }
-
- // Source object
- reader, err := c.GetObject(bucketName, objectName)
- if err != nil {
- t.Fatal("Error:", err)
- }
- // Destination object
- readerCopy, err := c.GetObject(bucketName+"-copy", objectName+"-copy")
- if err != nil {
- t.Fatal("Error:", err)
- }
- // Check the various fields of source object against destination object.
- objInfo, err := reader.Stat()
- if err != nil {
- t.Fatal("Error:", err)
- }
- objInfoCopy, err := readerCopy.Stat()
- if err != nil {
- t.Fatal("Error:", err)
- }
- if objInfo.Size != objInfoCopy.Size {
- t.Fatalf("Error: number of bytes does not match, want %v, got %v\n",
- objInfo.Size, objInfoCopy.Size)
- }
-
- // Remove all objects and buckets
- err = c.RemoveObject(bucketName, objectName)
- if err != nil {
- t.Fatal("Error:", err)
- }
-
- err = c.RemoveObject(bucketName+"-copy", objectName+"-copy")
- if err != nil {
- t.Fatal("Error:", err)
- }
-
- err = c.RemoveBucket(bucketName)
- if err != nil {
- t.Fatal("Error:", err)
- }
-
- err = c.RemoveBucket(bucketName + "-copy")
- if err != nil {
- t.Fatal("Error:", err)
- }
-}
-
-// Tests comprehensive list of all methods.
-func TestFunctionalV2(t *testing.T) {
- if testing.Short() {
- t.Skip("skipping functional tests for the short runs")
- }
-
- // Seed random based on current time.
- rand.Seed(time.Now().Unix())
-
- c, err := NewV2(
- os.Getenv(serverEndpoint),
- os.Getenv(accessKey),
- os.Getenv(secretKey),
- mustParseBool(os.Getenv(enableSecurity)),
- )
- if err != nil {
- t.Fatal("Error:", err)
- }
-
- // Enable to debug
- // c.TraceOn(os.Stderr)
-
- // Set user agent.
- c.SetAppInfo("Minio-go-FunctionalTest", "0.1.0")
-
- // Generate a new random bucket name.
- bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test")
-
- // Make a new bucket.
- err = c.MakeBucket(bucketName, "us-east-1")
- if err != nil {
- t.Fatal("Error:", err, bucketName)
- }
-
- // Generate a random file name.
- fileName := randString(60, rand.NewSource(time.Now().UnixNano()), "")
- file, err := os.Create(fileName)
- if err != nil {
- t.Fatal("Error:", err)
- }
- for i := 0; i < 3; i++ {
- buf := make([]byte, rand.Intn(1<<19))
- _, err = file.Write(buf)
- if err != nil {
- t.Fatal("Error:", err)
- }
- }
- file.Close()
-
- // Verify if bucket exits and you have access.
- var exists bool
- exists, err = c.BucketExists(bucketName)
- if err != nil {
- t.Fatal("Error:", err, bucketName)
- }
- if !exists {
- t.Fatal("Error: could not find ", bucketName)
- }
-
- // Make the bucket 'public read/write'.
- err = c.SetBucketPolicy(bucketName, "", policy.BucketPolicyReadWrite)
- if err != nil {
- t.Fatal("Error:", err)
- }
-
- // List all buckets.
- buckets, err := c.ListBuckets()
- if len(buckets) == 0 {
- t.Fatal("Error: list buckets cannot be empty", buckets)
- }
- if err != nil {
- t.Fatal("Error:", err)
- }
-
- // Verify if previously created bucket is listed in list buckets.
- bucketFound := false
- for _, bucket := range buckets {
- if bucket.Name == bucketName {
- bucketFound = true
- }
- }
-
- // If bucket not found error out.
- if !bucketFound {
- t.Fatal("Error: bucket ", bucketName, "not found")
- }
-
- objectName := bucketName + "unique"
-
- // Generate data
- buf := bytes.Repeat([]byte("n"), rand.Intn(1<<19))
-
- n, err := c.PutObject(bucketName, objectName, bytes.NewReader(buf), "")
- if err != nil {
- t.Fatal("Error: ", err)
- }
- if n != int64(len(buf)) {
- t.Fatal("Error: bad length ", n, len(buf))
- }
-
- n, err = c.PutObject(bucketName, objectName+"-nolength", bytes.NewReader(buf), "binary/octet-stream")
- if err != nil {
- t.Fatal("Error:", err, bucketName, objectName+"-nolength")
- }
-
- if n != int64(len(buf)) {
- t.Fatalf("Error: number of bytes does not match, want %v, got %v\n", len(buf), n)
- }
-
- // Instantiate a done channel to close all listing.
- doneCh := make(chan struct{})
- defer close(doneCh)
-
- objFound := false
- isRecursive := true // Recursive is true.
- for obj := range c.ListObjects(bucketName, objectName, isRecursive, doneCh) {
- if obj.Key == objectName {
- objFound = true
- break
- }
- }
- if !objFound {
- t.Fatal("Error: object " + objectName + " not found.")
- }
-
- objFound = false
- isRecursive = true // Recursive is true.
- for obj := range c.ListObjects(bucketName, objectName, isRecursive, doneCh) {
- if obj.Key == objectName {
- objFound = true
- break
- }
- }
- if !objFound {
- t.Fatal("Error: object " + objectName + " not found.")
- }
-
- incompObjNotFound := true
- for objIncompl := range c.ListIncompleteUploads(bucketName, objectName, isRecursive, doneCh) {
- if objIncompl.Key != "" {
- incompObjNotFound = false
- break
- }
- }
- if !incompObjNotFound {
- t.Fatal("Error: unexpected dangling incomplete upload found.")
- }
-
- newReader, err := c.GetObject(bucketName, objectName)
- if err != nil {
- t.Fatal("Error: ", err)
- }
-
- newReadBytes, err := ioutil.ReadAll(newReader)
- if err != nil {
- t.Fatal("Error: ", err)
- }
-
- if !bytes.Equal(newReadBytes, buf) {
- t.Fatal("Error: bytes mismatch.")
- }
-
- err = c.FGetObject(bucketName, objectName, fileName+"-f")
- if err != nil {
- t.Fatal("Error: ", err)
- }
-
- // Generate presigned GET object url.
- presignedGetURL, err := c.PresignedGetObject(bucketName, objectName, 3600*time.Second, nil)
- if err != nil {
- t.Fatal("Error: ", err)
- }
- // Verify if presigned url works.
- resp, err := http.Get(presignedGetURL.String())
- if err != nil {
- t.Fatal("Error: ", err)
- }
- if resp.StatusCode != http.StatusOK {
- t.Fatal("Error: ", resp.Status)
- }
- newPresignedBytes, err := ioutil.ReadAll(resp.Body)
- if err != nil {
- t.Fatal("Error: ", err)
- }
- if !bytes.Equal(newPresignedBytes, buf) {
- t.Fatal("Error: bytes mismatch.")
- }
-
- // Set request parameters.
- reqParams := make(url.Values)
- reqParams.Set("response-content-disposition", "attachment; filename=\"test.txt\"")
- // Generate presigned GET object url.
- presignedGetURL, err = c.PresignedGetObject(bucketName, objectName, 3600*time.Second, reqParams)
- if err != nil {
- t.Fatal("Error: ", err)
- }
- // Verify if presigned url works.
- resp, err = http.Get(presignedGetURL.String())
- if err != nil {
- t.Fatal("Error: ", err)
- }
- if resp.StatusCode != http.StatusOK {
- t.Fatal("Error: ", resp.Status)
- }
- newPresignedBytes, err = ioutil.ReadAll(resp.Body)
- if err != nil {
- t.Fatal("Error: ", err)
- }
- if !bytes.Equal(newPresignedBytes, buf) {
- t.Fatal("Error: bytes mismatch for presigned GET url.")
- }
- // Verify content disposition.
- if resp.Header.Get("Content-Disposition") != "attachment; filename=\"test.txt\"" {
- t.Fatalf("Error: wrong Content-Disposition received %s", resp.Header.Get("Content-Disposition"))
- }
-
- presignedPutURL, err := c.PresignedPutObject(bucketName, objectName+"-presigned", 3600*time.Second)
- if err != nil {
- t.Fatal("Error: ", err)
- }
- // Generate data more than 32K
- buf = bytes.Repeat([]byte("1"), rand.Intn(1<<20)+32*1024)
-
- req, err := http.NewRequest("PUT", presignedPutURL.String(), bytes.NewReader(buf))
- if err != nil {
- t.Fatal("Error: ", err)
- }
- httpClient := &http.Client{
- // Setting a sensible time out of 30secs to wait for response
- // headers. Request is pro-actively cancelled after 30secs
- // with no response.
- Timeout: 30 * time.Second,
- Transport: http.DefaultTransport,
- }
- resp, err = httpClient.Do(req)
- if err != nil {
- t.Fatal("Error: ", err)
- }
-
- newReader, err = c.GetObject(bucketName, objectName+"-presigned")
- if err != nil {
- t.Fatal("Error: ", err)
- }
-
- newReadBytes, err = ioutil.ReadAll(newReader)
- if err != nil {
- t.Fatal("Error: ", err)
- }
-
- if !bytes.Equal(newReadBytes, buf) {
- t.Fatal("Error: bytes mismatch.")
- }
-
- err = c.RemoveObject(bucketName, objectName)
- if err != nil {
- t.Fatal("Error: ", err)
- }
- err = c.RemoveObject(bucketName, objectName+"-f")
- if err != nil {
- t.Fatal("Error: ", err)
- }
- err = c.RemoveObject(bucketName, objectName+"-nolength")
- if err != nil {
- t.Fatal("Error: ", err)
- }
- err = c.RemoveObject(bucketName, objectName+"-presigned")
- if err != nil {
- t.Fatal("Error: ", err)
- }
- err = c.RemoveBucket(bucketName)
- if err != nil {
- t.Fatal("Error:", err)
- }
- err = c.RemoveBucket(bucketName)
- if err == nil {
- t.Fatal("Error:")
- }
- if err.Error() != "The specified bucket does not exist" {
- t.Fatal("Error: ", err)
- }
- if err = os.Remove(fileName); err != nil {
- t.Fatal("Error: ", err)
- }
- if err = os.Remove(fileName + "-f"); err != nil {
- t.Fatal("Error: ", err)
- }
-}
-
-func testComposeObjectErrorCases(c *Client, t *testing.T) {
- // Generate a new random bucket name.
- bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test")
-
- // Make a new bucket in 'us-east-1' (source bucket).
- err := c.MakeBucket(bucketName, "us-east-1")
- if err != nil {
- t.Fatal("Error:", err, bucketName)
- }
-
- // Test that more than 10K source objects cannot be
- // concatenated.
- srcArr := [10001]SourceInfo{}
- srcSlice := srcArr[:]
- dst, err := NewDestinationInfo(bucketName, "object", nil, nil)
- if err != nil {
- t.Fatal(err)
- }
-
- if err := c.ComposeObject(dst, srcSlice); err == nil {
- t.Fatal("Error was expected.")
- } else if err.Error() != "There must be as least one and upto 10000 source objects." {
- t.Fatal("Got unexpected error: ", err)
- }
-
- // Create a source with invalid offset spec and check that
- // error is returned:
- // 1. Create the source object.
- const badSrcSize = 5 * 1024 * 1024
- buf := bytes.Repeat([]byte("1"), badSrcSize)
- _, err = c.PutObject(bucketName, "badObject", bytes.NewReader(buf), "")
- if err != nil {
- t.Fatal("Error:", err)
- }
- // 2. Set invalid range spec on the object (going beyond
- // object size)
- badSrc := NewSourceInfo(bucketName, "badObject", nil)
- err = badSrc.SetRange(1, badSrcSize)
- if err != nil {
- t.Fatal("Error:", err)
- }
- // 3. ComposeObject call should fail.
- if err := c.ComposeObject(dst, []SourceInfo{badSrc}); err == nil {
- t.Fatal("Error was expected.")
- } else if !strings.Contains(err.Error(), "has invalid segment-to-copy") {
- t.Fatal("Got unexpected error: ", err)
- }
-}
-
-// Test expected error cases
-func TestComposeObjectErrorCasesV2(t *testing.T) {
- if testing.Short() {
- t.Skip("skipping functional tests for the short runs")
- }
-
- // Instantiate new minio client object
- c, err := NewV2(
- os.Getenv(serverEndpoint),
- os.Getenv(accessKey),
- os.Getenv(secretKey),
- mustParseBool(os.Getenv(enableSecurity)),
- )
- if err != nil {
- t.Fatal("Error:", err)
- }
-
- testComposeObjectErrorCases(c, t)
-}
-
-func testComposeMultipleSources(c *Client, t *testing.T) {
- // Generate a new random bucket name.
- bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test")
- // Make a new bucket in 'us-east-1' (source bucket).
- err := c.MakeBucket(bucketName, "us-east-1")
- if err != nil {
- t.Fatal("Error:", err, bucketName)
- }
-
- // Upload a small source object
- const srcSize = 1024 * 1024 * 5
- buf := bytes.Repeat([]byte("1"), srcSize)
- _, err = c.PutObject(bucketName, "srcObject", bytes.NewReader(buf), "binary/octet-stream")
- if err != nil {
- t.Fatal("Error:", err)
- }
-
- // We will append 10 copies of the object.
- srcs := []SourceInfo{}
- for i := 0; i < 10; i++ {
- srcs = append(srcs, NewSourceInfo(bucketName, "srcObject", nil))
- }
- // make the last part very small
- err = srcs[9].SetRange(0, 0)
- if err != nil {
- t.Fatal("unexpected error:", err)
- }
-
- dst, err := NewDestinationInfo(bucketName, "dstObject", nil, nil)
- if err != nil {
- t.Fatal(err)
- }
- err = c.ComposeObject(dst, srcs)
- if err != nil {
- t.Fatal("Error:", err)
- }
-
- objProps, err := c.StatObject(bucketName, "dstObject")
- if err != nil {
- t.Fatal("Error:", err)
- }
-
- if objProps.Size != 9*srcSize+1 {
- t.Fatal("Size mismatched! Expected:", 10000*srcSize, "but got:", objProps.Size)
- }
-}
-
-// Test concatenating multiple objects objects
-func TestCompose10KSourcesV2(t *testing.T) {
- if testing.Short() {
- t.Skip("skipping functional tests for the short runs")
- }
-
- // Instantiate new minio client object
- c, err := NewV2(
- os.Getenv(serverEndpoint),
- os.Getenv(accessKey),
- os.Getenv(secretKey),
- mustParseBool(os.Getenv(enableSecurity)),
- )
- if err != nil {
- t.Fatal("Error:", err)
- }
-
- testComposeMultipleSources(c, t)
-}
-
-func testEncryptedCopyObject(c *Client, t *testing.T) {
- // Generate a new random bucket name.
- bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test")
- // Make a new bucket in 'us-east-1' (source bucket).
- err := c.MakeBucket(bucketName, "us-east-1")
- if err != nil {
- t.Fatal("Error:", err, bucketName)
- }
-
- key1 := NewSSEInfo([]byte("32byteslongsecretkeymustbegiven1"), "AES256")
- key2 := NewSSEInfo([]byte("32byteslongsecretkeymustbegiven2"), "AES256")
-
- // 1. create an sse-c encrypted object to copy by uploading
- const srcSize = 1024 * 1024
- buf := bytes.Repeat([]byte("abcde"), srcSize) // gives a buffer of 5MiB
- metadata := make(map[string][]string)
- for k, v := range key1.GetSSEHeaders() {
- metadata[k] = append(metadata[k], v)
- }
- _, err = c.PutObjectWithSize(bucketName, "srcObject", bytes.NewReader(buf), int64(len(buf)), metadata, nil)
- if err != nil {
- t.Fatal("PutObjectWithSize Error:", err)
- }
-
- // 2. copy object and change encryption key
- src := NewSourceInfo(bucketName, "srcObject", &key1)
- dst, err := NewDestinationInfo(bucketName, "dstObject", &key2, nil)
- if err != nil {
- t.Fatal("Error:", err)
- }
-
- err = c.CopyObject(dst, src)
- if err != nil {
- t.Fatal("CopyObject Error:", err)
- }
-
- // 3. get copied object and check if content is equal
- reqH := NewGetReqHeaders()
- for k, v := range key2.GetSSEHeaders() {
- reqH.Set(k, v)
- }
- coreClient := Core{c}
- reader, _, err := coreClient.GetObject(bucketName, "dstObject", reqH)
- if err != nil {
- t.Fatal("GetObject Error:", err)
- }
- defer reader.Close()
-
- decBytes, err := ioutil.ReadAll(reader)
- if err != nil {
- log.Fatalln(err)
- }
- if !bytes.Equal(decBytes, buf) {
- log.Fatal("downloaded object mismatched for encrypted object")
- }
-}
-
-// Test encrypted copy object
-func TestEncryptedCopyObjectV2(t *testing.T) {
- if testing.Short() {
- t.Skip("skipping functional tests for the short runs")
- }
-
- // Instantiate new minio client object
- c, err := NewV2(
- os.Getenv(serverEndpoint),
- os.Getenv(accessKey),
- os.Getenv(secretKey),
- mustParseBool(os.Getenv(enableSecurity)),
- )
- if err != nil {
- t.Fatal("Error:", err)
- }
-
- testEncryptedCopyObject(c, t)
-}
-
-func testUserMetadataCopying(c *Client, t *testing.T) {
- // Generate a new random bucket name.
- bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test")
- // Make a new bucket in 'us-east-1' (source bucket).
- err := c.MakeBucket(bucketName, "us-east-1")
- if err != nil {
- t.Fatal("Error:", err, bucketName)
- }
-
- fetchMeta := func(object string) (h http.Header) {
- objInfo, err := c.StatObject(bucketName, object)
- if err != nil {
- t.Fatal("Metadata fetch error:", err)
- }
- h = make(http.Header)
- for k, vs := range objInfo.Metadata {
- if strings.HasPrefix(strings.ToLower(k), "x-amz-meta-") {
- for _, v := range vs {
- h.Add(k, v)
- }
- }
- }
- return h
- }
-
- // 1. create a client encrypted object to copy by uploading
- const srcSize = 1024 * 1024
- buf := bytes.Repeat([]byte("abcde"), srcSize) // gives a buffer of 5MiB
- metadata := make(http.Header)
- metadata.Set("x-amz-meta-myheader", "myvalue")
- _, err = c.PutObjectWithMetadata(bucketName, "srcObject",
- bytes.NewReader(buf), metadata, nil)
- if err != nil {
- t.Fatal("Put Error:", err)
- }
- if !reflect.DeepEqual(metadata, fetchMeta("srcObject")) {
- t.Fatal("Unequal metadata")
- }
-
- // 2. create source
- src := NewSourceInfo(bucketName, "srcObject", nil)
- // 2.1 create destination with metadata set
- dst1, err := NewDestinationInfo(bucketName, "dstObject-1", nil, map[string]string{"notmyheader": "notmyvalue"})
- if err != nil {
- t.Fatal("Error:", err)
- }
-
- // 3. Check that copying to an object with metadata set resets
- // the headers on the copy.
- err = c.CopyObject(dst1, src)
- if err != nil {
- t.Fatal("Error:", err)
- }
-
- expectedHeaders := make(http.Header)
- expectedHeaders.Set("x-amz-meta-notmyheader", "notmyvalue")
- if !reflect.DeepEqual(expectedHeaders, fetchMeta("dstObject-1")) {
- t.Fatal("Unequal metadata")
- }
-
- // 4. create destination with no metadata set and same source
- dst2, err := NewDestinationInfo(bucketName, "dstObject-2", nil, nil)
- if err != nil {
- t.Fatal("Error:", err)
-
- }
- src = NewSourceInfo(bucketName, "srcObject", nil)
-
- // 5. Check that copying to an object with no metadata set,
- // copies metadata.
- err = c.CopyObject(dst2, src)
- if err != nil {
- t.Fatal("Error:", err)
- }
-
- expectedHeaders = metadata
- if !reflect.DeepEqual(expectedHeaders, fetchMeta("dstObject-2")) {
- t.Fatal("Unequal metadata")
- }
-
- // 6. Compose a pair of sources.
- srcs := []SourceInfo{
- NewSourceInfo(bucketName, "srcObject", nil),
- NewSourceInfo(bucketName, "srcObject", nil),
- }
- dst3, err := NewDestinationInfo(bucketName, "dstObject-3", nil, nil)
- if err != nil {
- t.Fatal("Error:", err)
-
- }
-
- err = c.ComposeObject(dst3, srcs)
- if err != nil {
- t.Fatal("Error:", err)
- }
-
- // Check that no headers are copied in this case
- if !reflect.DeepEqual(make(http.Header), fetchMeta("dstObject-3")) {
- t.Fatal("Unequal metadata")
- }
-
- // 7. Compose a pair of sources with dest user metadata set.
- srcs = []SourceInfo{
- NewSourceInfo(bucketName, "srcObject", nil),
- NewSourceInfo(bucketName, "srcObject", nil),
- }
- dst4, err := NewDestinationInfo(bucketName, "dstObject-4", nil, map[string]string{"notmyheader": "notmyvalue"})
- if err != nil {
- t.Fatal("Error:", err)
-
- }
-
- err = c.ComposeObject(dst4, srcs)
- if err != nil {
- t.Fatal("Error:", err)
- }
-
- // Check that no headers are copied in this case
- expectedHeaders = make(http.Header)
- expectedHeaders.Set("x-amz-meta-notmyheader", "notmyvalue")
- if !reflect.DeepEqual(expectedHeaders, fetchMeta("dstObject-4")) {
- t.Fatal("Unequal metadata")
- }
-}
-
-func TestUserMetadataCopyingV2(t *testing.T) {
- if testing.Short() {
- t.Skip("skipping functional tests for the short runs")
- }
-
- // Instantiate new minio client object
- c, err := NewV2(
- os.Getenv(serverEndpoint),
- os.Getenv(accessKey),
- os.Getenv(secretKey),
- mustParseBool(os.Getenv(enableSecurity)),
- )
- if err != nil {
- t.Fatal("Error:", err)
- }
-
- // c.TraceOn(os.Stderr)
- testUserMetadataCopying(c, t)
-}
diff --git a/vendor/github.com/minio/minio-go/api_functional_v4_test.go b/vendor/github.com/minio/minio-go/api_functional_v4_test.go
deleted file mode 100644
index e9593ddaf..000000000
--- a/vendor/github.com/minio/minio-go/api_functional_v4_test.go
+++ /dev/null
@@ -1,2410 +0,0 @@
-/*
- * Minio Go Library for Amazon S3 Compatible Cloud Storage (C) 2015 Minio, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package minio
-
-import (
- "bytes"
- "encoding/hex"
- "errors"
- "fmt"
- "io"
- "io/ioutil"
- "math/rand"
- "net/http"
- "net/url"
- "os"
- "strconv"
- "strings"
- "testing"
- "time"
-
- "github.com/minio/minio-go/pkg/encrypt"
- "github.com/minio/minio-go/pkg/policy"
-)
-
-const letterBytes = "abcdefghijklmnopqrstuvwxyz01234569"
-const (
- letterIdxBits = 6 // 6 bits to represent a letter index
- letterIdxMask = 1<<letterIdxBits - 1 // All 1-bits, as many as letterIdxBits
- letterIdxMax = 63 / letterIdxBits // # of letter indices fitting in 63 bits
-)
-
-// randString generates random names and prepends them with a known prefix.
-func randString(n int, src rand.Source, prefix string) string {
- b := make([]byte, n)
- // A rand.Int63() generates 63 random bits, enough for letterIdxMax letters!
- for i, cache, remain := n-1, src.Int63(), letterIdxMax; i >= 0; {
- if remain == 0 {
- cache, remain = src.Int63(), letterIdxMax
- }
- if idx := int(cache & letterIdxMask); idx < len(letterBytes) {
- b[i] = letterBytes[idx]
- i--
- }
- cache >>= letterIdxBits
- remain--
- }
- return prefix + string(b[0:30-len(prefix)])
-}
-
-// Tests bucket re-create errors.
-func TestMakeBucketError(t *testing.T) {
- if testing.Short() {
- t.Skip("skipping functional tests for short runs")
- }
- if os.Getenv(serverEndpoint) != "s3.amazonaws.com" {
- t.Skip("skipping region functional tests for non s3 runs")
- }
-
- // Seed random based on current time.
- rand.Seed(time.Now().Unix())
-
- // Instantiate new minio client object.
- c, err := New(
- os.Getenv(serverEndpoint),
- os.Getenv(accessKey),
- os.Getenv(secretKey),
- mustParseBool(os.Getenv(enableSecurity)),
- )
- if err != nil {
- t.Fatal("Error:", err)
- }
-
- // Enable tracing, write to stderr.
- // c.TraceOn(os.Stderr)
-
- // Set user agent.
- c.SetAppInfo("Minio-go-FunctionalTest", "0.1.0")
-
- // Generate a new random bucket name.
- bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test")
-
- // Make a new bucket in 'eu-central-1'.
- if err = c.MakeBucket(bucketName, "eu-central-1"); err != nil {
- t.Fatal("Error:", err, bucketName)
- }
- if err = c.MakeBucket(bucketName, "eu-central-1"); err == nil {
- t.Fatal("Error: make bucket should should fail for", bucketName)
- }
- // Verify valid error response from server.
- if ToErrorResponse(err).Code != "BucketAlreadyExists" &&
- ToErrorResponse(err).Code != "BucketAlreadyOwnedByYou" {
- t.Fatal("Error: Invalid error returned by server", err)
- }
- if err = c.RemoveBucket(bucketName); err != nil {
- t.Fatal("Error:", err, bucketName)
- }
- if err = c.MakeBucket(bucketName+"..-1", "eu-central-1"); err == nil {
- t.Fatal("Error:", err, bucketName+"..-1")
- }
- // Verify valid error response.
- if err != nil && err.Error() != "Bucket name contains invalid characters" {
- t.Fatal("Error: Invalid error returned by server", err)
- }
- if err = c.MakeBucket(bucketName+"AAA-1", "eu-central-1"); err == nil {
- t.Fatal("Error:", err, bucketName+"..-1")
- }
- // Verify valid error response.
- if err != nil && err.Error() != "Bucket name contains invalid characters" {
- t.Fatal("Error: Invalid error returned by server", err)
- }
-}
-
-// Tests various bucket supported formats.
-func TestMakeBucketRegions(t *testing.T) {
- if testing.Short() {
- t.Skip("skipping functional tests for short runs")
- }
- if os.Getenv(serverEndpoint) != "s3.amazonaws.com" {
- t.Skip("skipping region functional tests for non s3 runs")
- }
-
- // Seed random based on current time.
- rand.Seed(time.Now().Unix())
-
- // Instantiate new minio client object.
- c, err := New(
- os.Getenv(serverEndpoint),
- os.Getenv(accessKey),
- os.Getenv(secretKey),
- mustParseBool(os.Getenv(enableSecurity)),
- )
- if err != nil {
- t.Fatal("Error:", err)
- }
-
- // Enable tracing, write to stderr.
- // c.TraceOn(os.Stderr)
-
- // Set user agent.
- c.SetAppInfo("Minio-go-FunctionalTest", "0.1.0")
-
- // Generate a new random bucket name.
- bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test")
-
- // Make a new bucket in 'eu-central-1'.
- if err = c.MakeBucket(bucketName, "eu-central-1"); err != nil {
- t.Fatal("Error:", err, bucketName)
- }
-
- if err = c.RemoveBucket(bucketName); err != nil {
- t.Fatal("Error:", err, bucketName)
- }
-
- // Make a new bucket with '.' in its name, in 'us-west-2'. This
- // request is internally staged into a path style instead of
- // virtual host style.
- if err = c.MakeBucket(bucketName+".withperiod", "us-west-2"); err != nil {
- t.Fatal("Error:", err, bucketName+".withperiod")
- }
-
- // Remove the newly created bucket.
- if err = c.RemoveBucket(bucketName + ".withperiod"); err != nil {
- t.Fatal("Error:", err, bucketName+".withperiod")
- }
-}
-
-// Test PutObject using a large data to trigger multipart readat
-func TestPutObjectReadAt(t *testing.T) {
- if testing.Short() {
- t.Skip("skipping functional tests for short runs")
- }
-
- // Seed random based on current time.
- rand.Seed(time.Now().Unix())
-
- // Instantiate new minio client object.
- c, err := New(
- os.Getenv(serverEndpoint),
- os.Getenv(accessKey),
- os.Getenv(secretKey),
- mustParseBool(os.Getenv(enableSecurity)),
- )
- if err != nil {
- t.Fatal("Error:", err)
- }
-
- // Enable tracing, write to stderr.
- // c.TraceOn(os.Stderr)
-
- // Set user agent.
- c.SetAppInfo("Minio-go-FunctionalTest", "0.1.0")
-
- // Generate a new random bucket name.
- bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test")
-
- // Make a new bucket.
- err = c.MakeBucket(bucketName, "us-east-1")
- if err != nil {
- t.Fatal("Error:", err, bucketName)
- }
-
- // Generate data using 4 parts so that all 3 'workers' are utilized and a part is leftover.
- // Use different data for each part for multipart tests to ensure part order at the end.
- var buf []byte
- for i := 0; i < 4; i++ {
- buf = append(buf, bytes.Repeat([]byte(string('a'+i)), minPartSize)...)
- }
-
- // Save the data
- objectName := randString(60, rand.NewSource(time.Now().UnixNano()), "")
- // Object content type
- objectContentType := "binary/octet-stream"
-
- n, err := c.PutObject(bucketName, objectName, bytes.NewReader(buf), objectContentType)
- if err != nil {
- t.Fatal("Error:", err, bucketName, objectName)
- }
-
- if n != int64(len(buf)) {
- t.Fatalf("Error: number of bytes does not match, want %v, got %v\n", len(buf), n)
- }
-
- // Read the data back
- r, err := c.GetObject(bucketName, objectName)
- if err != nil {
- t.Fatal("Error:", err, bucketName, objectName)
- }
-
- st, err := r.Stat()
- if err != nil {
- t.Fatal("Error:", err, bucketName, objectName)
- }
- if st.Size != int64(len(buf)) {
- t.Fatalf("Error: number of bytes in stat does not match, want %v, got %v\n",
- len(buf), st.Size)
- }
- if st.ContentType != objectContentType {
- t.Fatalf("Error: Content types don't match, expected: %+v, found: %+v\n", objectContentType, st.ContentType)
- }
- if err := r.Close(); err != nil {
- t.Fatal("Error:", err)
- }
- if err := r.Close(); err == nil {
- t.Fatal("Error: object is already closed, should return error")
- }
-
- err = c.RemoveObject(bucketName, objectName)
- if err != nil {
- t.Fatal("Error: ", err)
- }
- err = c.RemoveBucket(bucketName)
- if err != nil {
- t.Fatal("Error:", err)
- }
-}
-
-// Test PutObject using a large data to trigger multipart readat
-func TestPutObjectWithMetadata(t *testing.T) {
- if testing.Short() {
- t.Skip("skipping functional tests for short runs")
- }
-
- // Seed random based on current time.
- rand.Seed(time.Now().Unix())
-
- // Instantiate new minio client object.
- c, err := New(
- os.Getenv(serverEndpoint),
- os.Getenv(accessKey),
- os.Getenv(secretKey),
- mustParseBool(os.Getenv(enableSecurity)),
- )
- if err != nil {
- t.Fatal("Error:", err)
- }
-
- // Enable tracing, write to stderr.
- // c.TraceOn(os.Stderr)
-
- // Set user agent.
- c.SetAppInfo("Minio-go-FunctionalTest", "0.1.0")
-
- // Generate a new random bucket name.
- bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test")
-
- // Make a new bucket.
- err = c.MakeBucket(bucketName, "us-east-1")
- if err != nil {
- t.Fatal("Error:", err, bucketName)
- }
-
- // Generate data using 2 parts
- // Use different data in each part for multipart tests to ensure part order at the end.
- var buf []byte
- for i := 0; i < 2; i++ {
- buf = append(buf, bytes.Repeat([]byte(string('a'+i)), minPartSize)...)
- }
-
- // Save the data
- objectName := randString(60, rand.NewSource(time.Now().UnixNano()), "")
-
- // Object custom metadata
- customContentType := "custom/contenttype"
-
- n, err := c.PutObjectWithMetadata(bucketName, objectName, bytes.NewReader(buf), map[string][]string{
- "Content-Type": {customContentType},
- }, nil)
- if err != nil {
- t.Fatal("Error:", err, bucketName, objectName)
- }
-
- if n != int64(len(buf)) {
- t.Fatalf("Error: number of bytes does not match, want %v, got %v\n", len(buf), n)
- }
-
- // Read the data back
- r, err := c.GetObject(bucketName, objectName)
- if err != nil {
- t.Fatal("Error:", err, bucketName, objectName)
- }
-
- st, err := r.Stat()
- if err != nil {
- t.Fatal("Error:", err, bucketName, objectName)
- }
- if st.Size != int64(len(buf)) {
- t.Fatalf("Error: number of bytes in stat does not match, want %v, got %v\n",
- len(buf), st.Size)
- }
- if st.ContentType != customContentType {
- t.Fatalf("Error: Expected and found content types do not match, want %v, got %v\n",
- customContentType, st.ContentType)
- }
- if err := r.Close(); err != nil {
- t.Fatal("Error:", err)
- }
- if err := r.Close(); err == nil {
- t.Fatal("Error: object is already closed, should return error")
- }
-
- err = c.RemoveObject(bucketName, objectName)
- if err != nil {
- t.Fatal("Error: ", err)
- }
- err = c.RemoveBucket(bucketName)
- if err != nil {
- t.Fatal("Error:", err)
- }
-}
-
-// Test put object with streaming signature.
-func TestPutObjectStreaming(t *testing.T) {
- if testing.Short() {
- t.Skip("skipping function tests for short runs")
- }
-
- // Seed random based on current time.
- rand.Seed(time.Now().Unix())
-
- // Instantiate new minio client object.
- c, err := NewV4(
- os.Getenv(serverEndpoint),
- os.Getenv(accessKey),
- os.Getenv(secretKey),
- mustParseBool(os.Getenv(enableSecurity)),
- )
- if err != nil {
- t.Fatal("Error:", err)
- }
-
- // Enable tracing, write to stderr.
- // c.TraceOn(os.Stderr)
-
- // Set user agent.
- c.SetAppInfo("Minio-go-FunctionalTest", "0.1.0")
-
- // Generate a new random bucket name.
- bucketName := randString(60, rand.NewSource(time.Now().UnixNano()),
- "minio-go-test")
-
- // Make a new bucket.
- err = c.MakeBucket(bucketName, "us-east-1")
- if err != nil {
- t.Fatal("Error:", err, bucketName)
- }
-
- // Upload an object.
- sizes := []int64{0, 64*1024 - 1, 64 * 1024}
- objectName := "test-object"
- for i, size := range sizes {
- data := bytes.Repeat([]byte("a"), int(size))
- n, err := c.PutObjectStreaming(bucketName, objectName, bytes.NewReader(data))
- if err != nil {
- t.Fatalf("Test %d Error: %v %s %s", i+1, err, bucketName, objectName)
- }
-
- if n != size {
- t.Errorf("Test %d Expected upload object size %d but got %d", i+1, size, n)
- }
- }
-
- // Remove the object.
- err = c.RemoveObject(bucketName, objectName)
- if err != nil {
- t.Fatal("Error:", err)
- }
-
- // Remove the bucket.
- err = c.RemoveBucket(bucketName)
- if err != nil {
- t.Fatal("Error:", err)
- }
-}
-
-// Test listing no partially uploaded objects upon putObject error.
-func TestListNoPartiallyUploadedObjects(t *testing.T) {
- if testing.Short() {
- t.Skip("skipping function tests for short runs")
- }
-
- // Seed random based on current time.
- rand.Seed(time.Now().Unix())
-
- // Instantiate new minio client object.
- c, err := New(
- os.Getenv(serverEndpoint),
- os.Getenv(accessKey),
- os.Getenv(secretKey),
- mustParseBool(os.Getenv(enableSecurity)),
- )
- if err != nil {
- t.Fatal("Error:", err)
- }
-
- // Set user agent.
- c.SetAppInfo("Minio-go-FunctionalTest", "0.1.0")
-
- // Enable tracing, write to stdout.
- // c.TraceOn(os.Stderr)
-
- // Generate a new random bucket name.
- bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test")
-
- // Make a new bucket.
- err = c.MakeBucket(bucketName, "us-east-1")
- if err != nil {
- t.Fatal("Error:", err, bucketName)
- }
-
- r := bytes.NewReader(bytes.Repeat([]byte("0"), minPartSize*2))
-
- reader, writer := io.Pipe()
- go func() {
- i := 0
- for i < 25 {
- _, cerr := io.CopyN(writer, r, (minPartSize*2)/25)
- if cerr != nil {
- t.Fatal("Error:", cerr, bucketName)
- }
- i++
- r.Seek(0, 0)
- }
- writer.CloseWithError(errors.New("proactively closed to be verified later"))
- }()
-
- objectName := bucketName + "-resumable"
- _, err = c.PutObject(bucketName, objectName, reader, "application/octet-stream")
- if err == nil {
- t.Fatal("Error: PutObject should fail.")
- }
- if !strings.Contains(err.Error(), "proactively closed to be verified later") {
- t.Fatal("Error:", err)
- }
-
- doneCh := make(chan struct{})
- defer close(doneCh)
-
- isRecursive := true
- multiPartObjectCh := c.ListIncompleteUploads(bucketName, objectName, isRecursive, doneCh)
-
- var activeUploads bool
- for multiPartObject := range multiPartObjectCh {
- if multiPartObject.Err != nil {
- t.Fatalf("Error: Error when listing incomplete upload")
- }
- activeUploads = true
- }
- if activeUploads {
- t.Errorf("There should be no active uploads in progress upon error for %s/%s", bucketName, objectName)
- }
-
- err = c.RemoveBucket(bucketName)
- if err != nil {
- t.Fatal("Error:", err)
- }
-}
-
-// Test get object seeker from the end, using whence set to '2'.
-func TestGetOjectSeekEnd(t *testing.T) {
- if testing.Short() {
- t.Skip("skipping functional tests for short runs")
- }
-
- // Seed random based on current time.
- rand.Seed(time.Now().Unix())
-
- // Instantiate new minio client object.
- c, err := New(
- os.Getenv(serverEndpoint),
- os.Getenv(accessKey),
- os.Getenv(secretKey),
- mustParseBool(os.Getenv(enableSecurity)),
- )
- if err != nil {
- t.Fatal("Error:", err)
- }
-
- // Enable tracing, write to stderr.
- // c.TraceOn(os.Stderr)
-
- // Set user agent.
- c.SetAppInfo("Minio-go-FunctionalTest", "0.1.0")
-
- // Generate a new random bucket name.
- bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test")
-
- // Make a new bucket.
- err = c.MakeBucket(bucketName, "us-east-1")
- if err != nil {
- t.Fatal("Error:", err, bucketName)
- }
-
- // Generate data more than 32K
- buf := bytes.Repeat([]byte("1"), rand.Intn(1<<20)+32*1024)
- // Save the data
- objectName := randString(60, rand.NewSource(time.Now().UnixNano()), "")
- n, err := c.PutObject(bucketName, objectName, bytes.NewReader(buf), "binary/octet-stream")
- if err != nil {
- t.Fatal("Error:", err, bucketName, objectName)
- }
-
- if n != int64(len(buf)) {
- t.Fatalf("Error: number of bytes does not match, want %v, got %v\n", len(buf), n)
- }
-
- // Read the data back
- r, err := c.GetObject(bucketName, objectName)
- if err != nil {
- t.Fatal("Error:", err, bucketName, objectName)
- }
-
- st, err := r.Stat()
- if err != nil {
- t.Fatal("Error:", err, bucketName, objectName)
- }
- if st.Size != int64(len(buf)) {
- t.Fatalf("Error: number of bytes in stat does not match, want %v, got %v\n",
- len(buf), st.Size)
- }
-
- pos, err := r.Seek(-100, 2)
- if err != nil {
- t.Fatal("Error:", err, bucketName, objectName)
- }
- if pos != st.Size-100 {
- t.Fatalf("Expected %d, got %d instead", pos, st.Size-100)
- }
- buf2 := make([]byte, 100)
- m, err := io.ReadFull(r, buf2)
- if err != nil {
- t.Fatal("Error: reading through io.ReadFull", err, bucketName, objectName)
- }
- if m != len(buf2) {
- t.Fatalf("Expected %d bytes, got %d", len(buf2), m)
- }
- hexBuf1 := fmt.Sprintf("%02x", buf[len(buf)-100:])
- hexBuf2 := fmt.Sprintf("%02x", buf2[:m])
- if hexBuf1 != hexBuf2 {
- t.Fatalf("Expected %s, got %s instead", hexBuf1, hexBuf2)
- }
- pos, err = r.Seek(-100, 2)
- if err != nil {
- t.Fatal("Error:", err, bucketName, objectName)
- }
- if pos != st.Size-100 {
- t.Fatalf("Expected %d, got %d instead", pos, st.Size-100)
- }
- if err = r.Close(); err != nil {
- t.Fatal("Error:", err, bucketName, objectName)
- }
-}
-
-// Test get object reader to not throw error on being closed twice.
-func TestGetObjectClosedTwice(t *testing.T) {
- if testing.Short() {
- t.Skip("skipping functional tests for short runs")
- }
-
- // Seed random based on current time.
- rand.Seed(time.Now().Unix())
-
- // Instantiate new minio client object.
- c, err := New(
- os.Getenv(serverEndpoint),
- os.Getenv(accessKey),
- os.Getenv(secretKey),
- mustParseBool(os.Getenv(enableSecurity)),
- )
- if err != nil {
- t.Fatal("Error:", err)
- }
-
- // Enable tracing, write to stderr.
- // c.TraceOn(os.Stderr)
-
- // Set user agent.
- c.SetAppInfo("Minio-go-FunctionalTest", "0.1.0")
-
- // Generate a new random bucket name.
- bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test")
-
- // Make a new bucket.
- err = c.MakeBucket(bucketName, "us-east-1")
- if err != nil {
- t.Fatal("Error:", err, bucketName)
- }
-
- // Generate data more than 32K
- buf := bytes.Repeat([]byte("1"), rand.Intn(1<<20)+32*1024)
-
- // Save the data
- objectName := randString(60, rand.NewSource(time.Now().UnixNano()), "")
- n, err := c.PutObject(bucketName, objectName, bytes.NewReader(buf), "binary/octet-stream")
- if err != nil {
- t.Fatal("Error:", err, bucketName, objectName)
- }
-
- if n != int64(len(buf)) {
- t.Fatalf("Error: number of bytes does not match, want %v, got %v\n", len(buf), n)
- }
-
- // Read the data back
- r, err := c.GetObject(bucketName, objectName)
- if err != nil {
- t.Fatal("Error:", err, bucketName, objectName)
- }
-
- st, err := r.Stat()
- if err != nil {
- t.Fatal("Error:", err, bucketName, objectName)
- }
- if st.Size != int64(len(buf)) {
- t.Fatalf("Error: number of bytes in stat does not match, want %v, got %v\n",
- len(buf), st.Size)
- }
- if err := r.Close(); err != nil {
- t.Fatal("Error:", err)
- }
- if err := r.Close(); err == nil {
- t.Fatal("Error: object is already closed, should return error")
- }
-
- err = c.RemoveObject(bucketName, objectName)
- if err != nil {
- t.Fatal("Error: ", err)
- }
- err = c.RemoveBucket(bucketName)
- if err != nil {
- t.Fatal("Error:", err)
- }
-}
-
-// Test removing multiple objects with Remove API
-func TestRemoveMultipleObjects(t *testing.T) {
- if testing.Short() {
- t.Skip("skipping function tests for short runs")
- }
-
- // Seed random based on current time.
- rand.Seed(time.Now().Unix())
-
- // Instantiate new minio client object.
- c, err := New(
- os.Getenv(serverEndpoint),
- os.Getenv(accessKey),
- os.Getenv(secretKey),
- mustParseBool(os.Getenv(enableSecurity)),
- )
-
- if err != nil {
- t.Fatal("Error:", err)
- }
-
- // Set user agent.
- c.SetAppInfo("Minio-go-FunctionalTest", "0.1.0")
-
- // Enable tracing, write to stdout.
- // c.TraceOn(os.Stderr)
-
- // Generate a new random bucket name.
- bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test")
-
- // Make a new bucket.
- err = c.MakeBucket(bucketName, "us-east-1")
- if err != nil {
- t.Fatal("Error:", err, bucketName)
- }
-
- r := bytes.NewReader(bytes.Repeat([]byte("a"), 8))
-
- // Multi remove of 1100 objects
- nrObjects := 1100
-
- objectsCh := make(chan string)
-
- go func() {
- defer close(objectsCh)
- // Upload objects and send them to objectsCh
- for i := 0; i < nrObjects; i++ {
- objectName := "sample" + strconv.Itoa(i) + ".txt"
- _, err = c.PutObject(bucketName, objectName, r, "application/octet-stream")
- if err != nil {
- t.Error("Error: PutObject shouldn't fail.", err)
- continue
- }
- objectsCh <- objectName
- }
- }()
-
- // Call RemoveObjects API
- errorCh := c.RemoveObjects(bucketName, objectsCh)
-
- // Check if errorCh doesn't receive any error
- select {
- case r, more := <-errorCh:
- if more {
- t.Fatalf("Unexpected error, objName(%v) err(%v)", r.ObjectName, r.Err)
- }
- }
-
- // Clean the bucket created by the test
- err = c.RemoveBucket(bucketName)
- if err != nil {
- t.Fatal("Error:", err)
- }
-}
-
-// Tests FPutObject of a big file to trigger multipart
-func TestFPutObjectMultipart(t *testing.T) {
- if testing.Short() {
- t.Skip("skipping functional tests for short runs")
- }
-
- // Seed random based on current time.
- rand.Seed(time.Now().Unix())
-
- // Instantiate new minio client object.
- c, err := New(
- os.Getenv(serverEndpoint),
- os.Getenv(accessKey),
- os.Getenv(secretKey),
- mustParseBool(os.Getenv(enableSecurity)),
- )
- if err != nil {
- t.Fatal("Error:", err)
- }
-
- // Enable tracing, write to stderr.
- // c.TraceOn(os.Stderr)
-
- // Set user agent.
- c.SetAppInfo("Minio-go-FunctionalTest", "0.1.0")
-
- // Generate a new random bucket name.
- bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test")
-
- // Make a new bucket.
- err = c.MakeBucket(bucketName, "us-east-1")
- if err != nil {
- t.Fatal("Error:", err, bucketName)
- }
-
- // Make a temp file with minPartSize*4 bytes of data.
- file, err := ioutil.TempFile(os.TempDir(), "FPutObjectTest")
- if err != nil {
- t.Fatal("Error:", err)
- }
-
- // Upload 4 parts to utilize all 3 'workers' in multipart and still have a part to upload.
- var buffer []byte
- for i := 0; i < 4; i++ {
- buffer = append(buffer, bytes.Repeat([]byte(string('a'+i)), minPartSize)...)
- }
-
- size, err := file.Write(buffer)
- if err != nil {
- t.Fatal("Error:", err)
- }
- if size != minPartSize*4 {
- t.Fatalf("Error: number of bytes does not match, want %v, got %v\n", minPartSize*4, size)
- }
-
- // Close the file pro-actively for windows.
- err = file.Close()
- if err != nil {
- t.Fatal("Error:", err)
- }
-
- // Set base object name
- objectName := bucketName + "FPutObject"
- objectContentType := "testapplication/octet-stream"
-
- // Perform standard FPutObject with contentType provided (Expecting application/octet-stream)
- n, err := c.FPutObject(bucketName, objectName+"-standard", file.Name(), objectContentType)
- if err != nil {
- t.Fatal("Error:", err)
- }
- if n != int64(minPartSize*4) {
- t.Fatalf("Error: number of bytes does not match, want %v, got %v\n", minPartSize*4, n)
- }
-
- r, err := c.GetObject(bucketName, objectName+"-standard")
- if err != nil {
- t.Fatalf("Unexpected error: %v\n", err)
- }
- objInfo, err := r.Stat()
- if err != nil {
- t.Fatalf("Unexpected error: %v\n", err)
- }
- if objInfo.Size != minPartSize*4 {
- t.Fatalf("Error: number of bytes does not match, want %v, got %v\n", minPartSize*4, n)
- }
- if objInfo.ContentType != objectContentType {
- t.Fatalf("Error: Content types don't match, want %v, got %v\n", objectContentType, objInfo.ContentType)
- }
-
- // Remove all objects and bucket and temp file
- err = c.RemoveObject(bucketName, objectName+"-standard")
- if err != nil {
- t.Fatal("Error: ", err)
- }
-
- err = c.RemoveBucket(bucketName)
- if err != nil {
- t.Fatal("Error:", err)
- }
-}
-
-// Tests FPutObject hidden contentType setting
-func TestFPutObject(t *testing.T) {
- if testing.Short() {
- t.Skip("skipping functional tests for short runs")
- }
-
- // Seed random based on current time.
- rand.Seed(time.Now().Unix())
-
- // Instantiate new minio client object.
- c, err := New(
- os.Getenv(serverEndpoint),
- os.Getenv(accessKey),
- os.Getenv(secretKey),
- mustParseBool(os.Getenv(enableSecurity)),
- )
- if err != nil {
- t.Fatal("Error:", err)
- }
-
- // Enable tracing, write to stderr.
- // c.TraceOn(os.Stderr)
-
- // Set user agent.
- c.SetAppInfo("Minio-go-FunctionalTest", "0.1.0")
-
- // Generate a new random bucket name.
- bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test")
-
- // Make a new bucket.
- err = c.MakeBucket(bucketName, "us-east-1")
- if err != nil {
- t.Fatal("Error:", err, bucketName)
- }
-
- // Make a temp file with minPartSize*4 bytes of data.
- file, err := ioutil.TempFile(os.TempDir(), "FPutObjectTest")
- if err != nil {
- t.Fatal("Error:", err)
- }
-
- // Upload 4 parts worth of data to use all 3 of multiparts 'workers' and have an extra part.
- // Use different data in part for multipart tests to check parts are uploaded in correct order.
- var buffer []byte
- for i := 0; i < 4; i++ {
- buffer = append(buffer, bytes.Repeat([]byte(string('a'+i)), minPartSize)...)
- }
-
- // Write the data to the file.
- size, err := file.Write(buffer)
- if err != nil {
- t.Fatal("Error:", err)
- }
- if size != minPartSize*4 {
- t.Fatalf("Error: number of bytes does not match, want %v, got %v\n", minPartSize*4, size)
- }
-
- // Close the file pro-actively for windows.
- err = file.Close()
- if err != nil {
- t.Fatal("Error:", err)
- }
-
- // Set base object name
- objectName := bucketName + "FPutObject"
-
- // Perform standard FPutObject with contentType provided (Expecting application/octet-stream)
- n, err := c.FPutObject(bucketName, objectName+"-standard", file.Name(), "application/octet-stream")
- if err != nil {
- t.Fatal("Error:", err)
- }
- if n != int64(minPartSize*4) {
- t.Fatalf("Error: number of bytes does not match, want %v, got %v\n", minPartSize*4, n)
- }
-
- // Perform FPutObject with no contentType provided (Expecting application/octet-stream)
- n, err = c.FPutObject(bucketName, objectName+"-Octet", file.Name(), "")
- if err != nil {
- t.Fatal("Error:", err)
- }
- if n != int64(minPartSize*4) {
- t.Fatalf("Error: number of bytes does not match, want %v, got %v\n", minPartSize*4, n)
- }
-
- // Add extension to temp file name
- fileName := file.Name()
- err = os.Rename(file.Name(), fileName+".gtar")
- if err != nil {
- t.Fatal("Error:", err)
- }
-
- // Perform FPutObject with no contentType provided (Expecting application/x-gtar)
- n, err = c.FPutObject(bucketName, objectName+"-GTar", fileName+".gtar", "")
- if err != nil {
- t.Fatal("Error:", err)
- }
- if n != int64(minPartSize*4) {
- t.Fatalf("Error: number of bytes does not match, want %v, got %v\n", minPartSize*4, n)
- }
-
- // Check headers
- rStandard, err := c.StatObject(bucketName, objectName+"-standard")
- if err != nil {
- t.Fatal("Error:", err, bucketName, objectName+"-standard")
- }
- if rStandard.ContentType != "application/octet-stream" {
- t.Fatalf("Error: Content-Type headers mismatched, want %v, got %v\n",
- "application/octet-stream", rStandard.ContentType)
- }
-
- rOctet, err := c.StatObject(bucketName, objectName+"-Octet")
- if err != nil {
- t.Fatal("Error:", err, bucketName, objectName+"-Octet")
- }
- if rOctet.ContentType != "application/octet-stream" {
- t.Fatalf("Error: Content-Type headers mismatched, want %v, got %v\n",
- "application/octet-stream", rStandard.ContentType)
- }
-
- rGTar, err := c.StatObject(bucketName, objectName+"-GTar")
- if err != nil {
- t.Fatal("Error:", err, bucketName, objectName+"-GTar")
- }
- if rGTar.ContentType != "application/x-gtar" {
- t.Fatalf("Error: Content-Type headers mismatched, want %v, got %v\n",
- "application/x-gtar", rStandard.ContentType)
- }
-
- // Remove all objects and bucket and temp file
- err = c.RemoveObject(bucketName, objectName+"-standard")
- if err != nil {
- t.Fatal("Error: ", err)
- }
-
- err = c.RemoveObject(bucketName, objectName+"-Octet")
- if err != nil {
- t.Fatal("Error: ", err)
- }
-
- err = c.RemoveObject(bucketName, objectName+"-GTar")
- if err != nil {
- t.Fatal("Error: ", err)
- }
-
- err = c.RemoveBucket(bucketName)
- if err != nil {
- t.Fatal("Error:", err)
- }
-
- err = os.Remove(fileName + ".gtar")
- if err != nil {
- t.Fatal("Error:", err)
- }
-
-}
-
-// Tests get object ReaderSeeker interface methods.
-func TestGetObjectReadSeekFunctional(t *testing.T) {
- if testing.Short() {
- t.Skip("skipping functional tests for short runs")
- }
-
- // Seed random based on current time.
- rand.Seed(time.Now().Unix())
-
- // Instantiate new minio client object.
- c, err := New(
- os.Getenv(serverEndpoint),
- os.Getenv(accessKey),
- os.Getenv(secretKey),
- mustParseBool(os.Getenv(enableSecurity)),
- )
- if err != nil {
- t.Fatal("Error:", err)
- }
-
- // Enable tracing, write to stderr.
- // c.TraceOn(os.Stderr)
-
- // Set user agent.
- c.SetAppInfo("Minio-go-FunctionalTest", "0.1.0")
-
- // Generate a new random bucket name.
- bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test")
-
- // Make a new bucket.
- err = c.MakeBucket(bucketName, "us-east-1")
- if err != nil {
- t.Fatal("Error:", err, bucketName)
- }
-
- // Generate data more than 32K
- buf := bytes.Repeat([]byte("2"), rand.Intn(1<<20)+32*1024)
- bufSize := len(buf)
-
- // Save the data
- objectName := randString(60, rand.NewSource(time.Now().UnixNano()), "")
- n, err := c.PutObject(bucketName, objectName, bytes.NewReader(buf), "binary/octet-stream")
- if err != nil {
- t.Fatal("Error:", err, bucketName, objectName)
- }
-
- if n != int64(bufSize) {
- t.Fatalf("Error: number of bytes does not match, want %v, got %v\n", len(buf), n)
- }
-
- defer func() {
- err = c.RemoveObject(bucketName, objectName)
- if err != nil {
- t.Fatal("Error: ", err)
- }
- err = c.RemoveBucket(bucketName)
- if err != nil {
- t.Fatal("Error:", err)
- }
- }()
-
- // Read the data back
- r, err := c.GetObject(bucketName, objectName)
- if err != nil {
- t.Fatal("Error:", err, bucketName, objectName)
- }
-
- st, err := r.Stat()
- if err != nil {
- t.Fatal("Error:", err, bucketName, objectName)
- }
- if st.Size != int64(bufSize) {
- t.Fatalf("Error: number of bytes in stat does not match, want %v, got %v\n",
- len(buf), st.Size)
- }
-
- // This following function helps us to compare data from the reader after seek
- // with the data from the original buffer
- cmpData := func(r io.Reader, start, end int) {
- if end-start == 0 {
- return
- }
- buffer := bytes.NewBuffer([]byte{})
- if _, err := io.CopyN(buffer, r, int64(bufSize)); err != nil {
- if err != io.EOF {
- t.Fatal("Error:", err)
- }
- }
- if !bytes.Equal(buf[start:end], buffer.Bytes()) {
- t.Fatal("Error: Incorrect read bytes v/s original buffer.")
- }
- }
-
- // Generic seek error for errors other than io.EOF
- seekErr := errors.New("seek error")
-
- testCases := []struct {
- offset int64
- whence int
- pos int64
- err error
- shouldCmp bool
- start int
- end int
- }{
- // Start from offset 0, fetch data and compare
- {0, 0, 0, nil, true, 0, 0},
- // Start from offset 2048, fetch data and compare
- {2048, 0, 2048, nil, true, 2048, bufSize},
- // Start from offset larger than possible
- {int64(bufSize) + 1024, 0, 0, seekErr, false, 0, 0},
- // Move to offset 0 without comparing
- {0, 0, 0, nil, false, 0, 0},
- // Move one step forward and compare
- {1, 1, 1, nil, true, 1, bufSize},
- // Move larger than possible
- {int64(bufSize), 1, 0, seekErr, false, 0, 0},
- // Provide negative offset with CUR_SEEK
- {int64(-1), 1, 0, seekErr, false, 0, 0},
- // Test with whence SEEK_END and with positive offset
- {1024, 2, int64(bufSize) - 1024, io.EOF, true, 0, 0},
- // Test with whence SEEK_END and with negative offset
- {-1024, 2, int64(bufSize) - 1024, nil, true, bufSize - 1024, bufSize},
- // Test with whence SEEK_END and with large negative offset
- {-int64(bufSize) * 2, 2, 0, seekErr, true, 0, 0},
- }
-
- for i, testCase := range testCases {
- // Perform seek operation
- n, err := r.Seek(testCase.offset, testCase.whence)
- // We expect an error
- if testCase.err == seekErr && err == nil {
- t.Fatalf("Test %d, unexpected err value: expected: %v, found: %v", i+1, testCase.err, err)
- }
- // We expect a specific error
- if testCase.err != seekErr && testCase.err != err {
- t.Fatalf("Test %d, unexpected err value: expected: %v, found: %v", i+1, testCase.err, err)
- }
- // If we expect an error go to the next loop
- if testCase.err != nil {
- continue
- }
- // Check the returned seek pos
- if n != testCase.pos {
- t.Fatalf("Test %d, error: number of bytes seeked does not match, want %v, got %v\n", i+1,
- testCase.pos, n)
- }
- // Compare only if shouldCmp is activated
- if testCase.shouldCmp {
- cmpData(r, testCase.start, testCase.end)
- }
- }
-}
-
-// Tests get object ReaderAt interface methods.
-func TestGetObjectReadAtFunctional(t *testing.T) {
- if testing.Short() {
- t.Skip("skipping functional tests for the short runs")
- }
-
- // Seed random based on current time.
- rand.Seed(time.Now().Unix())
-
- // Instantiate new minio client object.
- c, err := New(
- os.Getenv(serverEndpoint),
- os.Getenv(accessKey),
- os.Getenv(secretKey),
- mustParseBool(os.Getenv(enableSecurity)),
- )
- if err != nil {
- t.Fatal("Error:", err)
- }
-
- // Enable tracing, write to stderr.
- // c.TraceOn(os.Stderr)
-
- // Set user agent.
- c.SetAppInfo("Minio-go-FunctionalTest", "0.1.0")
-
- // Generate a new random bucket name.
- bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test")
-
- // Make a new bucket.
- err = c.MakeBucket(bucketName, "us-east-1")
- if err != nil {
- t.Fatal("Error:", err, bucketName)
- }
-
- // Generate data more than 32K
- buf := bytes.Repeat([]byte("3"), rand.Intn(1<<20)+32*1024)
-
- // Save the data
- objectName := randString(60, rand.NewSource(time.Now().UnixNano()), "")
- n, err := c.PutObject(bucketName, objectName, bytes.NewReader(buf), "binary/octet-stream")
- if err != nil {
- t.Fatal("Error:", err, bucketName, objectName)
- }
-
- if n != int64(len(buf)) {
- t.Fatalf("Error: number of bytes does not match, want %v, got %v\n", len(buf), n)
- }
-
- // read the data back
- r, err := c.GetObject(bucketName, objectName)
- if err != nil {
- t.Fatal("Error:", err, bucketName, objectName)
- }
- offset := int64(2048)
-
- // read directly
- buf1 := make([]byte, 512)
- buf2 := make([]byte, 512)
- buf3 := make([]byte, 512)
- buf4 := make([]byte, 512)
-
- // Test readAt before stat is called.
- m, err := r.ReadAt(buf1, offset)
- if err != nil {
- t.Fatal("Error:", err, len(buf1), offset)
- }
- if m != len(buf1) {
- t.Fatalf("Error: ReadAt read shorter bytes before reaching EOF, want %v, got %v\n", m, len(buf1))
- }
- if !bytes.Equal(buf1, buf[offset:offset+512]) {
- t.Fatal("Error: Incorrect read between two ReadAt from same offset.")
- }
- offset += 512
-
- st, err := r.Stat()
- if err != nil {
- t.Fatal("Error:", err, bucketName, objectName)
- }
- if st.Size != int64(len(buf)) {
- t.Fatalf("Error: number of bytes in stat does not match, want %v, got %v\n",
- len(buf), st.Size)
- }
-
- m, err = r.ReadAt(buf2, offset)
- if err != nil {
- t.Fatal("Error:", err, st.Size, len(buf2), offset)
- }
- if m != len(buf2) {
- t.Fatalf("Error: ReadAt read shorter bytes before reaching EOF, want %v, got %v\n", m, len(buf2))
- }
- if !bytes.Equal(buf2, buf[offset:offset+512]) {
- t.Fatal("Error: Incorrect read between two ReadAt from same offset.")
- }
- offset += 512
- m, err = r.ReadAt(buf3, offset)
- if err != nil {
- t.Fatal("Error:", err, st.Size, len(buf3), offset)
- }
- if m != len(buf3) {
- t.Fatalf("Error: ReadAt read shorter bytes before reaching EOF, want %v, got %v\n", m, len(buf3))
- }
- if !bytes.Equal(buf3, buf[offset:offset+512]) {
- t.Fatal("Error: Incorrect read between two ReadAt from same offset.")
- }
- offset += 512
- m, err = r.ReadAt(buf4, offset)
- if err != nil {
- t.Fatal("Error:", err, st.Size, len(buf4), offset)
- }
- if m != len(buf4) {
- t.Fatalf("Error: ReadAt read shorter bytes before reaching EOF, want %v, got %v\n", m, len(buf4))
- }
- if !bytes.Equal(buf4, buf[offset:offset+512]) {
- t.Fatal("Error: Incorrect read between two ReadAt from same offset.")
- }
-
- buf5 := make([]byte, n)
- // Read the whole object.
- m, err = r.ReadAt(buf5, 0)
- if err != nil {
- if err != io.EOF {
- t.Fatal("Error:", err, len(buf5))
- }
- }
- if m != len(buf5) {
- t.Fatalf("Error: ReadAt read shorter bytes before reaching EOF, want %v, got %v\n", m, len(buf5))
- }
- if !bytes.Equal(buf, buf5) {
- t.Fatal("Error: Incorrect data read in GetObject, than what was previously upoaded.")
- }
-
- buf6 := make([]byte, n+1)
- // Read the whole object and beyond.
- _, err = r.ReadAt(buf6, 0)
- if err != nil {
- if err != io.EOF {
- t.Fatal("Error:", err, len(buf6))
- }
- }
- err = c.RemoveObject(bucketName, objectName)
- if err != nil {
- t.Fatal("Error: ", err)
- }
- err = c.RemoveBucket(bucketName)
- if err != nil {
- t.Fatal("Error:", err)
- }
-}
-
-// Test Presigned Post Policy
-func TestPresignedPostPolicy(t *testing.T) {
- if testing.Short() {
- t.Skip("Skipping functional tests for short runs")
- }
- // Seed random based on current time.
- rand.Seed(time.Now().Unix())
-
- // Instantiate new minio client object
- c, err := NewV4(
- os.Getenv(serverEndpoint),
- os.Getenv(accessKey),
- os.Getenv(secretKey),
- mustParseBool(os.Getenv(enableSecurity)),
- )
- if err != nil {
- t.Fatal("Error:", err)
- }
-
- // Enable tracing, write to stderr.
- // c.TraceOn(os.Stderr)
-
- // Set user agent.
- c.SetAppInfo("Minio-go-FunctionalTest", "0.1.0")
-
- // Generate a new random bucket name.
- bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test")
-
- // Make a new bucket in 'us-east-1' (source bucket).
- err = c.MakeBucket(bucketName, "us-east-1")
- if err != nil {
- t.Fatal("Error:", err, bucketName)
- }
-
- // Generate data more than 32K
- buf := bytes.Repeat([]byte("4"), rand.Intn(1<<20)+32*1024)
-
- // Save the data
- objectName := randString(60, rand.NewSource(time.Now().UnixNano()), "")
- n, err := c.PutObject(bucketName, objectName, bytes.NewReader(buf), "binary/octet-stream")
- if err != nil {
- t.Fatal("Error:", err, bucketName, objectName)
- }
-
- if n != int64(len(buf)) {
- t.Fatalf("Error: number of bytes does not match want %v, got %v",
- len(buf), n)
- }
-
- policy := NewPostPolicy()
-
- if err := policy.SetBucket(""); err == nil {
- t.Fatalf("Error: %s", err)
- }
- if err := policy.SetKey(""); err == nil {
- t.Fatalf("Error: %s", err)
- }
- if err := policy.SetKeyStartsWith(""); err == nil {
- t.Fatalf("Error: %s", err)
- }
- if err := policy.SetExpires(time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC)); err == nil {
- t.Fatalf("Error: %s", err)
- }
- if err := policy.SetContentType(""); err == nil {
- t.Fatalf("Error: %s", err)
- }
- if err := policy.SetContentLengthRange(1024*1024, 1024); err == nil {
- t.Fatalf("Error: %s", err)
- }
-
- policy.SetBucket(bucketName)
- policy.SetKey(objectName)
- policy.SetExpires(time.Now().UTC().AddDate(0, 0, 10)) // expires in 10 days
- policy.SetContentType("image/png")
- policy.SetContentLengthRange(1024, 1024*1024)
-
- _, _, err = c.PresignedPostPolicy(policy)
- if err != nil {
- t.Fatal("Error:", err)
- }
-
- policy = NewPostPolicy()
-
- // Remove all objects and buckets
- err = c.RemoveObject(bucketName, objectName)
- if err != nil {
- t.Fatal("Error:", err)
- }
-
- err = c.RemoveBucket(bucketName)
- if err != nil {
- t.Fatal("Error:", err)
- }
-}
-
-// Tests copy object
-func TestCopyObject(t *testing.T) {
- if testing.Short() {
- t.Skip("Skipping functional tests for short runs")
- }
- // Seed random based on current time.
- rand.Seed(time.Now().Unix())
-
- // Instantiate new minio client object
- c, err := NewV4(
- os.Getenv(serverEndpoint),
- os.Getenv(accessKey),
- os.Getenv(secretKey),
- mustParseBool(os.Getenv(enableSecurity)),
- )
- if err != nil {
- t.Fatal("Error:", err)
- }
-
- // Enable tracing, write to stderr.
- // c.TraceOn(os.Stderr)
-
- // Set user agent.
- c.SetAppInfo("Minio-go-FunctionalTest", "0.1.0")
-
- // Generate a new random bucket name.
- bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test")
-
- // Make a new bucket in 'us-east-1' (source bucket).
- err = c.MakeBucket(bucketName, "us-east-1")
- if err != nil {
- t.Fatal("Error:", err, bucketName)
- }
-
- // Make a new bucket in 'us-east-1' (destination bucket).
- err = c.MakeBucket(bucketName+"-copy", "us-east-1")
- if err != nil {
- t.Fatal("Error:", err, bucketName+"-copy")
- }
-
- // Generate data more than 32K
- buf := bytes.Repeat([]byte("5"), rand.Intn(1<<20)+32*1024)
-
- // Save the data
- objectName := randString(60, rand.NewSource(time.Now().UnixNano()), "")
- n, err := c.PutObject(bucketName, objectName, bytes.NewReader(buf), "binary/octet-stream")
- if err != nil {
- t.Fatal("Error:", err, bucketName, objectName)
- }
-
- if n != int64(len(buf)) {
- t.Fatalf("Error: number of bytes does not match want %v, got %v",
- len(buf), n)
- }
-
- r, err := c.GetObject(bucketName, objectName)
- if err != nil {
- t.Fatal("Error:", err)
- }
- // Check the various fields of source object against destination object.
- objInfo, err := r.Stat()
- if err != nil {
- t.Fatal("Error:", err)
- }
-
- // Copy Source
- src := NewSourceInfo(bucketName, objectName, nil)
-
- // Set copy conditions.
-
- // All invalid conditions first.
- err = src.SetModifiedSinceCond(time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC))
- if err == nil {
- t.Fatal("Error:", err)
- }
- err = src.SetUnmodifiedSinceCond(time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC))
- if err == nil {
- t.Fatal("Error:", err)
- }
- err = src.SetMatchETagCond("")
- if err == nil {
- t.Fatal("Error:", err)
- }
- err = src.SetMatchETagExceptCond("")
- if err == nil {
- t.Fatal("Error:", err)
- }
-
- err = src.SetModifiedSinceCond(time.Date(2014, time.April, 0, 0, 0, 0, 0, time.UTC))
- if err != nil {
- t.Fatal("Error:", err)
- }
- err = src.SetMatchETagCond(objInfo.ETag)
- if err != nil {
- t.Fatal("Error:", err)
- }
-
- dst, err := NewDestinationInfo(bucketName+"-copy", objectName+"-copy", nil, nil)
- if err != nil {
- t.Fatal(err)
- }
-
- // Perform the Copy
- err = c.CopyObject(dst, src)
- if err != nil {
- t.Fatal("Error:", err, bucketName+"-copy", objectName+"-copy")
- }
-
- // Source object
- reader, err := c.GetObject(bucketName, objectName)
- if err != nil {
- t.Fatal("Error:", err)
- }
- // Destination object
- readerCopy, err := c.GetObject(bucketName+"-copy", objectName+"-copy")
- if err != nil {
- t.Fatal("Error:", err)
- }
- // Check the various fields of source object against destination object.
- objInfo, err = reader.Stat()
- if err != nil {
- t.Fatal("Error:", err)
- }
- objInfoCopy, err := readerCopy.Stat()
- if err != nil {
- t.Fatal("Error:", err)
- }
- if objInfo.Size != objInfoCopy.Size {
- t.Fatalf("Error: number of bytes does not match, want %v, got %v\n",
- objInfo.Size, objInfoCopy.Size)
- }
-
- // CopyObject again but with wrong conditions
- src = NewSourceInfo(bucketName, objectName, nil)
- err = src.SetUnmodifiedSinceCond(time.Date(2014, time.April, 0, 0, 0, 0, 0, time.UTC))
- if err != nil {
- t.Fatal("Error:", err)
- }
- err = src.SetMatchETagExceptCond(objInfo.ETag)
- if err != nil {
- t.Fatal("Error:", err)
- }
-
- // Perform the Copy which should fail
- err = c.CopyObject(dst, src)
- if err == nil {
- t.Fatal("Error:", err, bucketName+"-copy", objectName+"-copy should fail")
- }
-
- // Remove all objects and buckets
- err = c.RemoveObject(bucketName, objectName)
- if err != nil {
- t.Fatal("Error:", err)
- }
-
- err = c.RemoveObject(bucketName+"-copy", objectName+"-copy")
- if err != nil {
- t.Fatal("Error:", err)
- }
-
- err = c.RemoveBucket(bucketName)
- if err != nil {
- t.Fatal("Error:", err)
- }
-
- err = c.RemoveBucket(bucketName + "-copy")
- if err != nil {
- t.Fatal("Error:", err)
- }
-}
-
-// TestEncryptionPutGet tests client side encryption
-func TestEncryptionPutGet(t *testing.T) {
- if testing.Short() {
- t.Skip("skipping functional tests for the short runs")
- }
-
- // Seed random based on current time.
- rand.Seed(time.Now().Unix())
-
- // Instantiate new minio client object.
- c, err := New(
- os.Getenv(serverEndpoint),
- os.Getenv(accessKey),
- os.Getenv(secretKey),
- mustParseBool(os.Getenv(enableSecurity)),
- )
- if err != nil {
- t.Fatal("Error:", err)
- }
-
- // Enable tracing, write to stderr.
- // c.TraceOn(os.Stderr)
-
- // Set user agent.
- c.SetAppInfo("Minio-go-FunctionalTest", "0.1.0")
-
- // Generate a new random bucket name.
- bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test")
-
- // Make a new bucket.
- err = c.MakeBucket(bucketName, "us-east-1")
- if err != nil {
- t.Fatal("Error:", err, bucketName)
- }
-
- // Generate a symmetric key
- symKey := encrypt.NewSymmetricKey([]byte("my-secret-key-00"))
-
- // Generate an assymmetric key from predefine public and private certificates
- privateKey, err := hex.DecodeString(
- "30820277020100300d06092a864886f70d0101010500048202613082025d" +
- "0201000281810087b42ea73243a3576dc4c0b6fa245d339582dfdbddc20c" +
- "bb8ab666385034d997210c54ba79275c51162a1221c3fb1a4c7c61131ca6" +
- "5563b319d83474ef5e803fbfa7e52b889e1893b02586b724250de7ac6351" +
- "cc0b7c638c980acec0a07020a78eed7eaa471eca4b92071394e061346c06" +
- "15ccce2f465dee2080a89e43f29b5702030100010281801dd5770c3af8b3" +
- "c85cd18cacad81a11bde1acfac3eac92b00866e142301fee565365aa9af4" +
- "57baebf8bb7711054d071319a51dd6869aef3848ce477a0dc5f0dbc0c336" +
- "5814b24c820491ae2bb3c707229a654427e03307fec683e6b27856688f08" +
- "bdaa88054c5eeeb773793ff7543ee0fb0e2ad716856f2777f809ef7e6fa4" +
- "41024100ca6b1edf89e8a8f93cce4b98c76c6990a09eb0d32ad9d3d04fbf" +
- "0b026fa935c44f0a1c05dd96df192143b7bda8b110ec8ace28927181fd8c" +
- "d2f17330b9b63535024100aba0260afb41489451baaeba423bee39bcbd1e" +
- "f63dd44ee2d466d2453e683bf46d019a8baead3a2c7fca987988eb4d565e" +
- "27d6be34605953f5034e4faeec9bdb0241009db2cb00b8be8c36710aff96" +
- "6d77a6dec86419baca9d9e09a2b761ea69f7d82db2ae5b9aae4246599bb2" +
- "d849684d5ab40e8802cfe4a2b358ad56f2b939561d2902404e0ead9ecafd" +
- "bb33f22414fa13cbcc22a86bdf9c212ce1a01af894e3f76952f36d6c904c" +
- "bd6a7e0de52550c9ddf31f1e8bfe5495f79e66a25fca5c20b3af5b870241" +
- "0083456232aa58a8c45e5b110494599bda8dbe6a094683a0539ddd24e19d" +
- "47684263bbe285ad953d725942d670b8f290d50c0bca3d1dc9688569f1d5" +
- "9945cb5c7d")
-
- if err != nil {
- t.Fatal(err)
- }
-
- publicKey, err := hex.DecodeString("30819f300d06092a864886f70d010101050003818d003081890281810087" +
- "b42ea73243a3576dc4c0b6fa245d339582dfdbddc20cbb8ab666385034d9" +
- "97210c54ba79275c51162a1221c3fb1a4c7c61131ca65563b319d83474ef" +
- "5e803fbfa7e52b889e1893b02586b724250de7ac6351cc0b7c638c980ace" +
- "c0a07020a78eed7eaa471eca4b92071394e061346c0615ccce2f465dee20" +
- "80a89e43f29b570203010001")
- if err != nil {
- t.Fatal(err)
- }
-
- // Generate an asymmetric key
- asymKey, err := encrypt.NewAsymmetricKey(privateKey, publicKey)
- if err != nil {
- t.Fatal(err)
- }
-
- // Object custom metadata
- customContentType := "custom/contenttype"
-
- testCases := []struct {
- buf []byte
- encKey encrypt.Key
- }{
- {encKey: symKey, buf: bytes.Repeat([]byte("F"), 0)},
- {encKey: symKey, buf: bytes.Repeat([]byte("F"), 1)},
- {encKey: symKey, buf: bytes.Repeat([]byte("F"), 15)},
- {encKey: symKey, buf: bytes.Repeat([]byte("F"), 16)},
- {encKey: symKey, buf: bytes.Repeat([]byte("F"), 17)},
- {encKey: symKey, buf: bytes.Repeat([]byte("F"), 31)},
- {encKey: symKey, buf: bytes.Repeat([]byte("F"), 32)},
- {encKey: symKey, buf: bytes.Repeat([]byte("F"), 33)},
- {encKey: symKey, buf: bytes.Repeat([]byte("F"), 1024)},
- {encKey: symKey, buf: bytes.Repeat([]byte("F"), 1024*2)},
- {encKey: symKey, buf: bytes.Repeat([]byte("F"), 1024*1024)},
-
- {encKey: asymKey, buf: bytes.Repeat([]byte("F"), 0)},
- {encKey: asymKey, buf: bytes.Repeat([]byte("F"), 1)},
- {encKey: asymKey, buf: bytes.Repeat([]byte("F"), 16)},
- {encKey: asymKey, buf: bytes.Repeat([]byte("F"), 32)},
- {encKey: asymKey, buf: bytes.Repeat([]byte("F"), 1024)},
- {encKey: asymKey, buf: bytes.Repeat([]byte("F"), 1024*1024)},
- }
-
- for i, testCase := range testCases {
- // Generate a random object name
- objectName := randString(60, rand.NewSource(time.Now().UnixNano()), "")
-
- // Secured object
- cbcMaterials, err := encrypt.NewCBCSecureMaterials(testCase.encKey)
- if err != nil {
- t.Fatal(err)
- }
-
- // Put encrypted data
- _, err = c.PutEncryptedObject(bucketName, objectName, bytes.NewReader(testCase.buf), cbcMaterials, map[string][]string{"Content-Type": {customContentType}}, nil)
- if err != nil {
- t.Fatalf("Test %d, error: %v %v %v", i+1, err, bucketName, objectName)
- }
-
- // Read the data back
- r, err := c.GetEncryptedObject(bucketName, objectName, cbcMaterials)
- if err != nil {
- t.Fatalf("Test %d, error: %v %v %v", i+1, err, bucketName, objectName)
- }
- defer r.Close()
-
- // Compare the sent object with the received one
- recvBuffer := bytes.NewBuffer([]byte{})
- if _, err = io.Copy(recvBuffer, r); err != nil {
- t.Fatalf("Test %d, error: %v", i+1, err)
- }
- if recvBuffer.Len() != len(testCase.buf) {
- t.Fatalf("Test %d, error: number of bytes of received object does not match, want %v, got %v\n",
- i+1, len(testCase.buf), recvBuffer.Len())
- }
- if !bytes.Equal(testCase.buf, recvBuffer.Bytes()) {
- t.Fatalf("Test %d, error: Encrypted sent is not equal to decrypted, want `%x`, go `%x`", i+1, testCase.buf, recvBuffer.Bytes())
- }
-
- // Remove test object
- err = c.RemoveObject(bucketName, objectName)
- if err != nil {
- t.Fatalf("Test %d, error: %v", i+1, err)
- }
-
- }
-
- // Remove test bucket
- err = c.RemoveBucket(bucketName)
- if err != nil {
- t.Fatal("Error:", err)
- }
-
-}
-
-func TestBucketNotification(t *testing.T) {
- if testing.Short() {
- t.Skip("skipping functional tests for the short runs")
- }
- if os.Getenv("NOTIFY_BUCKET") == "" ||
- os.Getenv("NOTIFY_SERVICE") == "" ||
- os.Getenv("NOTIFY_REGION") == "" ||
- os.Getenv("NOTIFY_ACCOUNTID") == "" ||
- os.Getenv("NOTIFY_RESOURCE") == "" {
- t.Skip("skipping notification test if not configured")
- }
-
- // Seed random based on current time.
- rand.Seed(time.Now().Unix())
-
- c, err := New(
- os.Getenv(serverEndpoint),
- os.Getenv(accessKey),
- os.Getenv(secretKey),
- mustParseBool(os.Getenv(enableSecurity)),
- )
- if err != nil {
- t.Fatal("Error:", err)
- }
-
- // Enable to debug
- // c.TraceOn(os.Stderr)
-
- // Set user agent.
- c.SetAppInfo("Minio-go-FunctionalTest", "0.1.0")
-
- bucketName := os.Getenv("NOTIFY_BUCKET")
-
- topicArn := NewArn("aws", os.Getenv("NOTIFY_SERVICE"), os.Getenv("NOTIFY_REGION"), os.Getenv("NOTIFY_ACCOUNTID"), os.Getenv("NOTIFY_RESOURCE"))
- queueArn := NewArn("aws", "dummy-service", "dummy-region", "dummy-accountid", "dummy-resource")
-
- topicConfig := NewNotificationConfig(topicArn)
- topicConfig.AddEvents(ObjectCreatedAll, ObjectRemovedAll)
- topicConfig.AddFilterSuffix("jpg")
-
- queueConfig := NewNotificationConfig(queueArn)
- queueConfig.AddEvents(ObjectCreatedAll)
- queueConfig.AddFilterPrefix("photos/")
-
- bNotification := BucketNotification{}
- bNotification.AddTopic(topicConfig)
-
- // Add the same topicConfig again, should have no effect
- // because it is duplicated
- bNotification.AddTopic(topicConfig)
- if len(bNotification.TopicConfigs) != 1 {
- t.Fatal("Error: duplicated entry added")
- }
-
- // Add and remove a queue config
- bNotification.AddQueue(queueConfig)
- bNotification.RemoveQueueByArn(queueArn)
-
- err = c.SetBucketNotification(bucketName, bNotification)
- if err != nil {
- t.Fatal("Error: ", err)
- }
-
- bNotification, err = c.GetBucketNotification(bucketName)
- if err != nil {
- t.Fatal("Error: ", err)
- }
-
- if len(bNotification.TopicConfigs) != 1 {
- t.Fatal("Error: Topic config is empty")
- }
-
- if bNotification.TopicConfigs[0].Filter.S3Key.FilterRules[0].Value != "jpg" {
- t.Fatal("Error: cannot get the suffix")
- }
-
- err = c.RemoveAllBucketNotification(bucketName)
- if err != nil {
- t.Fatal("Error: cannot delete bucket notification")
- }
-}
-
-// Tests comprehensive list of all methods.
-func TestFunctional(t *testing.T) {
- if testing.Short() {
- t.Skip("skipping functional tests for the short runs")
- }
-
- // Seed random based on current time.
- rand.Seed(time.Now().Unix())
-
- c, err := New(
- os.Getenv(serverEndpoint),
- os.Getenv(accessKey),
- os.Getenv(secretKey),
- mustParseBool(os.Getenv(enableSecurity)),
- )
- if err != nil {
- t.Fatal("Error:", err)
- }
-
- // Enable to debug
- // c.TraceOn(os.Stderr)
-
- // Set user agent.
- c.SetAppInfo("Minio-go-FunctionalTest", "0.1.0")
-
- // Generate a new random bucket name.
- bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test")
-
- // Make a new bucket.
- err = c.MakeBucket(bucketName, "us-east-1")
- if err != nil {
- t.Fatal("Error:", err, bucketName)
- }
-
- // Generate a random file name.
- fileName := randString(60, rand.NewSource(time.Now().UnixNano()), "")
- file, err := os.Create(fileName)
- if err != nil {
- t.Fatal("Error:", err)
- }
- for i := 0; i < 3; i++ {
- buf := make([]byte, rand.Intn(1<<19))
- _, err = file.Write(buf)
- if err != nil {
- t.Fatal("Error:", err)
- }
- }
- file.Close()
-
- // Verify if bucket exits and you have access.
- var exists bool
- exists, err = c.BucketExists(bucketName)
- if err != nil {
- t.Fatal("Error:", err, bucketName)
- }
- if !exists {
- t.Fatal("Error: could not find ", bucketName)
- }
-
- // Asserting the default bucket policy.
- policyAccess, err := c.GetBucketPolicy(bucketName, "")
- if err != nil {
- t.Fatal("Error:", err)
- }
- if policyAccess != "none" {
- t.Fatalf("Default bucket policy incorrect")
- }
- // Set the bucket policy to 'public readonly'.
- err = c.SetBucketPolicy(bucketName, "", policy.BucketPolicyReadOnly)
- if err != nil {
- t.Fatal("Error:", err)
- }
- // should return policy `readonly`.
- policyAccess, err = c.GetBucketPolicy(bucketName, "")
- if err != nil {
- t.Fatal("Error:", err)
- }
- if policyAccess != "readonly" {
- t.Fatalf("Expected bucket policy to be readonly")
- }
-
- // Make the bucket 'public writeonly'.
- err = c.SetBucketPolicy(bucketName, "", policy.BucketPolicyWriteOnly)
- if err != nil {
- t.Fatal("Error:", err)
- }
- // should return policy `writeonly`.
- policyAccess, err = c.GetBucketPolicy(bucketName, "")
- if err != nil {
- t.Fatal("Error:", err)
- }
- if policyAccess != "writeonly" {
- t.Fatalf("Expected bucket policy to be writeonly")
- }
- // Make the bucket 'public read/write'.
- err = c.SetBucketPolicy(bucketName, "", policy.BucketPolicyReadWrite)
- if err != nil {
- t.Fatal("Error:", err)
- }
- // should return policy `readwrite`.
- policyAccess, err = c.GetBucketPolicy(bucketName, "")
- if err != nil {
- t.Fatal("Error:", err)
- }
- if policyAccess != "readwrite" {
- t.Fatalf("Expected bucket policy to be readwrite")
- }
- // List all buckets.
- buckets, err := c.ListBuckets()
- if len(buckets) == 0 {
- t.Fatal("Error: list buckets cannot be empty", buckets)
- }
- if err != nil {
- t.Fatal("Error:", err)
- }
-
- // Verify if previously created bucket is listed in list buckets.
- bucketFound := false
- for _, bucket := range buckets {
- if bucket.Name == bucketName {
- bucketFound = true
- }
- }
-
- // If bucket not found error out.
- if !bucketFound {
- t.Fatal("Error: bucket ", bucketName, "not found")
- }
-
- objectName := bucketName + "unique"
-
- // Generate data
- buf := bytes.Repeat([]byte("f"), 1<<19)
-
- n, err := c.PutObject(bucketName, objectName, bytes.NewReader(buf), "")
- if err != nil {
- t.Fatal("Error: ", err)
- }
- if n != int64(len(buf)) {
- t.Fatal("Error: bad length ", n, len(buf))
- }
-
- n, err = c.PutObject(bucketName, objectName+"-nolength", bytes.NewReader(buf), "binary/octet-stream")
- if err != nil {
- t.Fatal("Error:", err, bucketName, objectName+"-nolength")
- }
-
- if n != int64(len(buf)) {
- t.Fatalf("Error: number of bytes does not match, want %v, got %v\n", len(buf), n)
- }
-
- // Instantiate a done channel to close all listing.
- doneCh := make(chan struct{})
- defer close(doneCh)
-
- objFound := false
- isRecursive := true // Recursive is true.
- for obj := range c.ListObjects(bucketName, objectName, isRecursive, doneCh) {
- if obj.Key == objectName {
- objFound = true
- break
- }
- }
- if !objFound {
- t.Fatal("Error: object " + objectName + " not found.")
- }
-
- objFound = false
- isRecursive = true // Recursive is true.
- for obj := range c.ListObjectsV2(bucketName, objectName, isRecursive, doneCh) {
- if obj.Key == objectName {
- objFound = true
- break
- }
- }
- if !objFound {
- t.Fatal("Error: object " + objectName + " not found.")
- }
-
- incompObjNotFound := true
- for objIncompl := range c.ListIncompleteUploads(bucketName, objectName, isRecursive, doneCh) {
- if objIncompl.Key != "" {
- incompObjNotFound = false
- break
- }
- }
- if !incompObjNotFound {
- t.Fatal("Error: unexpected dangling incomplete upload found.")
- }
-
- newReader, err := c.GetObject(bucketName, objectName)
- if err != nil {
- t.Fatal("Error: ", err)
- }
-
- newReadBytes, err := ioutil.ReadAll(newReader)
- if err != nil {
- t.Fatal("Error: ", err)
- }
-
- if !bytes.Equal(newReadBytes, buf) {
- t.Fatal("Error: bytes mismatch.")
- }
-
- err = c.FGetObject(bucketName, objectName, fileName+"-f")
- if err != nil {
- t.Fatal("Error: ", err)
- }
-
- // Generate presigned GET object url.
- presignedGetURL, err := c.PresignedGetObject(bucketName, objectName, 3600*time.Second, nil)
- if err != nil {
- t.Fatal("Error: ", err)
- }
-
- // Verify if presigned url works.
- resp, err := http.Get(presignedGetURL.String())
- if err != nil {
- t.Fatal("Error: ", err)
- }
- if resp.StatusCode != http.StatusOK {
- t.Fatal("Error: ", resp.Status)
- }
- newPresignedBytes, err := ioutil.ReadAll(resp.Body)
- if err != nil {
- t.Fatal("Error: ", err)
- }
- if !bytes.Equal(newPresignedBytes, buf) {
- t.Fatal("Error: bytes mismatch.")
- }
-
- // Set request parameters.
- reqParams := make(url.Values)
- reqParams.Set("response-content-disposition", "attachment; filename=\"test.txt\"")
- presignedGetURL, err = c.PresignedGetObject(bucketName, objectName, 3600*time.Second, reqParams)
- if err != nil {
- t.Fatal("Error: ", err)
- }
- // Verify if presigned url works.
- resp, err = http.Get(presignedGetURL.String())
- if err != nil {
- t.Fatal("Error: ", err)
- }
- if resp.StatusCode != http.StatusOK {
- t.Fatal("Error: ", resp.Status)
- }
- newPresignedBytes, err = ioutil.ReadAll(resp.Body)
- if err != nil {
- t.Fatal("Error: ", err)
- }
- if !bytes.Equal(newPresignedBytes, buf) {
- t.Fatal("Error: bytes mismatch for presigned GET URL.")
- }
- if resp.Header.Get("Content-Disposition") != "attachment; filename=\"test.txt\"" {
- t.Fatalf("Error: wrong Content-Disposition received %s", resp.Header.Get("Content-Disposition"))
- }
-
- presignedPutURL, err := c.PresignedPutObject(bucketName, objectName+"-presigned", 3600*time.Second)
- if err != nil {
- t.Fatal("Error: ", err)
- }
-
- buf = bytes.Repeat([]byte("g"), 1<<19)
-
- req, err := http.NewRequest("PUT", presignedPutURL.String(), bytes.NewReader(buf))
- if err != nil {
- t.Fatal("Error: ", err)
- }
- httpClient := &http.Client{
- // Setting a sensible time out of 30secs to wait for response
- // headers. Request is pro-actively cancelled after 30secs
- // with no response.
- Timeout: 30 * time.Second,
- Transport: http.DefaultTransport,
- }
- resp, err = httpClient.Do(req)
- if err != nil {
- t.Fatal("Error: ", err)
- }
-
- newReader, err = c.GetObject(bucketName, objectName+"-presigned")
- if err != nil {
- t.Fatal("Error: ", err)
- }
-
- newReadBytes, err = ioutil.ReadAll(newReader)
- if err != nil {
- t.Fatal("Error: ", err)
- }
-
- if !bytes.Equal(newReadBytes, buf) {
- t.Fatal("Error: bytes mismatch.")
- }
-
- err = c.RemoveObject(bucketName, objectName)
- if err != nil {
- t.Fatal("Error: ", err)
- }
- err = c.RemoveObject(bucketName, objectName+"-f")
- if err != nil {
- t.Fatal("Error: ", err)
- }
- err = c.RemoveObject(bucketName, objectName+"-nolength")
- if err != nil {
- t.Fatal("Error: ", err)
- }
- err = c.RemoveObject(bucketName, objectName+"-presigned")
- if err != nil {
- t.Fatal("Error: ", err)
- }
- err = c.RemoveBucket(bucketName)
- if err != nil {
- t.Fatal("Error:", err)
- }
- err = c.RemoveBucket(bucketName)
- if err == nil {
- t.Fatal("Error:")
- }
- if err.Error() != "The specified bucket does not exist" {
- t.Fatal("Error: ", err)
- }
- if err = os.Remove(fileName); err != nil {
- t.Fatal("Error: ", err)
- }
- if err = os.Remove(fileName + "-f"); err != nil {
- t.Fatal("Error: ", err)
- }
-}
-
-// Test for validating GetObject Reader* methods functioning when the
-// object is modified in the object store.
-func TestGetObjectObjectModified(t *testing.T) {
- if testing.Short() {
- t.Skip("skipping functional tests for the short runs")
- }
-
- // Instantiate new minio client object.
- c, err := NewV4(
- os.Getenv(serverEndpoint),
- os.Getenv(accessKey),
- os.Getenv(secretKey),
- mustParseBool(os.Getenv(enableSecurity)),
- )
- if err != nil {
- t.Fatal("Error:", err)
- }
-
- // Enable tracing, write to stderr.
- // c.TraceOn(os.Stderr)
-
- // Set user agent.
- c.SetAppInfo("Minio-go-FunctionalTest", "0.1.0")
-
- // Make a new bucket.
- bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test")
- err = c.MakeBucket(bucketName, "us-east-1")
- if err != nil {
- t.Fatal("Error:", err, bucketName)
- }
- defer c.RemoveBucket(bucketName)
-
- // Upload an object.
- objectName := "myobject"
- content := "helloworld"
- _, err = c.PutObject(bucketName, objectName, strings.NewReader(content), "application/text")
- if err != nil {
- t.Fatalf("Failed to upload %s/%s: %v", bucketName, objectName, err)
- }
-
- defer c.RemoveObject(bucketName, objectName)
-
- reader, err := c.GetObject(bucketName, objectName)
- if err != nil {
- t.Fatalf("Failed to get object %s/%s: %v", bucketName, objectName, err)
- }
- defer reader.Close()
-
- // Read a few bytes of the object.
- b := make([]byte, 5)
- n, err := reader.ReadAt(b, 0)
- if err != nil {
- t.Fatalf("Failed to read object %s/%s at an offset: %v", bucketName, objectName, err)
- }
-
- // Upload different contents to the same object while object is being read.
- newContent := "goodbyeworld"
- _, err = c.PutObject(bucketName, objectName, strings.NewReader(newContent), "application/text")
- if err != nil {
- t.Fatalf("Failed to upload %s/%s: %v", bucketName, objectName, err)
- }
-
- // Confirm that a Stat() call in between doesn't change the Object's cached etag.
- _, err = reader.Stat()
- if err.Error() != s3ErrorResponseMap["PreconditionFailed"] {
- t.Errorf("Expected Stat to fail with error %s but received %s", s3ErrorResponseMap["PreconditionFailed"], err.Error())
- }
-
- // Read again only to find object contents have been modified since last read.
- _, err = reader.ReadAt(b, int64(n))
- if err.Error() != s3ErrorResponseMap["PreconditionFailed"] {
- t.Errorf("Expected ReadAt to fail with error %s but received %s", s3ErrorResponseMap["PreconditionFailed"], err.Error())
- }
-}
-
-// Test validates putObject to upload a file seeked at a given offset.
-func TestPutObjectUploadSeekedObject(t *testing.T) {
- if testing.Short() {
- t.Skip("skipping functional tests for the short runs")
- }
-
- // Instantiate new minio client object.
- c, err := NewV4(
- os.Getenv(serverEndpoint),
- os.Getenv(accessKey),
- os.Getenv(secretKey),
- mustParseBool(os.Getenv(enableSecurity)),
- )
- if err != nil {
- t.Fatal("Error:", err)
- }
-
- // Enable tracing, write to stderr.
- // c.TraceOn(os.Stderr)
-
- // Set user agent.
- c.SetAppInfo("Minio-go-FunctionalTest", "0.1.0")
-
- // Make a new bucket.
- bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test")
- err = c.MakeBucket(bucketName, "us-east-1")
- if err != nil {
- t.Fatal("Error:", err, bucketName)
- }
- defer c.RemoveBucket(bucketName)
-
- tempfile, err := ioutil.TempFile("", "minio-go-upload-test-")
- if err != nil {
- t.Fatal("Error:", err)
- }
-
- var length = 120000
- data := bytes.Repeat([]byte("1"), length)
-
- if _, err = tempfile.Write(data); err != nil {
- t.Fatal("Error:", err)
- }
-
- objectName := fmt.Sprintf("test-file-%v", rand.Uint32())
-
- offset := length / 2
- if _, err := tempfile.Seek(int64(offset), 0); err != nil {
- t.Fatal("Error:", err)
- }
-
- n, err := c.PutObject(bucketName, objectName, tempfile, "binary/octet-stream")
- if err != nil {
- t.Fatal("Error:", err)
- }
- if n != int64(length-offset) {
- t.Fatalf("Invalid length returned, want %v, got %v", int64(length-offset), n)
- }
- tempfile.Close()
- if err = os.Remove(tempfile.Name()); err != nil {
- t.Fatal("Error:", err)
- }
-
- length = int(n)
-
- obj, err := c.GetObject(bucketName, objectName)
- if err != nil {
- t.Fatal("Error:", err)
- }
-
- n, err = obj.Seek(int64(offset), 0)
- if err != nil {
- t.Fatal("Error:", err)
- }
- if n != int64(offset) {
- t.Fatalf("Invalid offset returned, want %v, got %v", int64(offset), n)
- }
-
- n, err = c.PutObject(bucketName, objectName+"getobject", obj, "binary/octet-stream")
- if err != nil {
- t.Fatal("Error:", err)
- }
- if n != int64(length-offset) {
- t.Fatalf("Invalid length returned, want %v, got %v", int64(length-offset), n)
- }
-
- if err = c.RemoveObject(bucketName, objectName); err != nil {
- t.Fatal("Error:", err)
- }
-
- if err = c.RemoveObject(bucketName, objectName+"getobject"); err != nil {
- t.Fatal("Error:", err)
- }
-}
-
-// Test expected error cases
-func TestComposeObjectErrorCases(t *testing.T) {
- if testing.Short() {
- t.Skip("skipping functional tests for the short runs")
- }
-
- // Instantiate new minio client object
- c, err := NewV4(
- os.Getenv(serverEndpoint),
- os.Getenv(accessKey),
- os.Getenv(secretKey),
- mustParseBool(os.Getenv(enableSecurity)),
- )
- if err != nil {
- t.Fatal("Error:", err)
- }
-
- testComposeObjectErrorCases(c, t)
-}
-
-// Test concatenating 10K objects
-func TestCompose10KSources(t *testing.T) {
- if testing.Short() {
- t.Skip("skipping functional tests for the short runs")
- }
-
- // Instantiate new minio client object
- c, err := NewV4(
- os.Getenv(serverEndpoint),
- os.Getenv(accessKey),
- os.Getenv(secretKey),
- mustParseBool(os.Getenv(enableSecurity)),
- )
- if err != nil {
- t.Fatal("Error:", err)
- }
-
- testComposeMultipleSources(c, t)
-}
-
-// Test encrypted copy object
-func TestEncryptedCopyObject(t *testing.T) {
- if testing.Short() {
- t.Skip("skipping functional tests for the short runs")
- }
-
- // Instantiate new minio client object
- c, err := NewV4(
- os.Getenv(serverEndpoint),
- os.Getenv(accessKey),
- os.Getenv(secretKey),
- mustParseBool(os.Getenv(enableSecurity)),
- )
- if err != nil {
- t.Fatal("Error:", err)
- }
-
- // c.TraceOn(os.Stderr)
- testEncryptedCopyObject(c, t)
-}
-
-func TestUserMetadataCopying(t *testing.T) {
- if testing.Short() {
- t.Skip("skipping functional tests for the short runs")
- }
-
- // Instantiate new minio client object
- c, err := NewV4(
- os.Getenv(serverEndpoint),
- os.Getenv(accessKey),
- os.Getenv(secretKey),
- mustParseBool(os.Getenv(enableSecurity)),
- )
- if err != nil {
- t.Fatal("Error:", err)
- }
-
- // c.TraceOn(os.Stderr)
- testUserMetadataCopying(c, t)
-}
diff --git a/vendor/github.com/minio/minio-go/bucket-cache.go b/vendor/github.com/minio/minio-go/bucket-cache.go
index 6d2a40f78..748fd01ee 100644
--- a/vendor/github.com/minio/minio-go/bucket-cache.go
+++ b/vendor/github.com/minio/minio-go/bucket-cache.go
@@ -213,20 +213,24 @@ func (c Client) getBucketLocationRequest(bucketName string) (*http.Request, erro
signerType = credentials.SignatureAnonymous
}
- // Set sha256 sum for signature calculation only with signature version '4'.
- switch {
- case signerType.IsV4():
- var contentSha256 string
- if c.secure {
- contentSha256 = unsignedPayload
- } else {
- contentSha256 = hex.EncodeToString(sum256([]byte{}))
- }
- req.Header.Set("X-Amz-Content-Sha256", contentSha256)
- req = s3signer.SignV4(*req, accessKeyID, secretAccessKey, sessionToken, "us-east-1")
- case signerType.IsV2():
+ if signerType.IsAnonymous() {
+ return req, nil
+ }
+
+ if signerType.IsV2() {
req = s3signer.SignV2(*req, accessKeyID, secretAccessKey)
+ return req, nil
+ }
+
+ // Set sha256 sum for signature calculation only with signature version '4'.
+ var contentSha256 string
+ if c.secure {
+ contentSha256 = unsignedPayload
+ } else {
+ contentSha256 = hex.EncodeToString(sum256([]byte{}))
}
+ req.Header.Set("X-Amz-Content-Sha256", contentSha256)
+ req = s3signer.SignV4(*req, accessKeyID, secretAccessKey, sessionToken, "us-east-1")
return req, nil
}
diff --git a/vendor/github.com/minio/minio-go/core_test.go b/vendor/github.com/minio/minio-go/core_test.go
index 81e1cd5bf..8cadc251b 100644
--- a/vendor/github.com/minio/minio-go/core_test.go
+++ b/vendor/github.com/minio/minio-go/core_test.go
@@ -18,14 +18,15 @@ package minio
import (
"bytes"
- "crypto/md5"
-
"io"
- "math/rand"
+ "log"
"os"
"reflect"
"testing"
"time"
+
+ "crypto/md5"
+ "math/rand"
)
const (
@@ -35,6 +36,33 @@ const (
enableSecurity = "ENABLE_HTTPS"
)
+// Minimum part size
+const MinPartSize = 1024 * 1024 * 64
+const letterBytes = "abcdefghijklmnopqrstuvwxyz01234569"
+const (
+ letterIdxBits = 6 // 6 bits to represent a letter index
+ letterIdxMask = 1<<letterIdxBits - 1 // All 1-bits, as many as letterIdxBits
+ letterIdxMax = 63 / letterIdxBits // # of letter indices fitting in 63 bits
+)
+
+// randString generates random names and prepends them with a known prefix.
+func randString(n int, src rand.Source, prefix string) string {
+ b := make([]byte, n)
+ // A rand.Int63() generates 63 random bits, enough for letterIdxMax letters!
+ for i, cache, remain := n-1, src.Int63(), letterIdxMax; i >= 0; {
+ if remain == 0 {
+ cache, remain = src.Int63(), letterIdxMax
+ }
+ if idx := int(cache & letterIdxMask); idx < len(letterBytes) {
+ b[i] = letterBytes[idx]
+ i--
+ }
+ cache >>= letterIdxBits
+ remain--
+ }
+ return prefix + string(b[0:30-len(prefix)])
+}
+
// Tests for Core GetObject() function.
func TestGetObjectCore(t *testing.T) {
if testing.Short() {
@@ -209,6 +237,76 @@ func TestGetObjectCore(t *testing.T) {
}
}
+// Tests GetObject to return Content-Encoding properly set
+// and overrides any auto decoding.
+func TestGetObjectContentEncoding(t *testing.T) {
+ if testing.Short() {
+ t.Skip("skipping functional tests for the short runs")
+ }
+
+ // Seed random based on current time.
+ rand.Seed(time.Now().Unix())
+
+ // Instantiate new minio core client object.
+ c, err := NewCore(
+ os.Getenv(serverEndpoint),
+ os.Getenv(accessKey),
+ os.Getenv(secretKey),
+ mustParseBool(os.Getenv(enableSecurity)),
+ )
+ if err != nil {
+ t.Fatal("Error:", err)
+ }
+
+ // Enable tracing, write to stderr.
+ // c.TraceOn(os.Stderr)
+
+ // Set user agent.
+ c.SetAppInfo("Minio-go-FunctionalTest", "0.1.0")
+
+ // Generate a new random bucket name.
+ bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test")
+
+ // Make a new bucket.
+ err = c.MakeBucket(bucketName, "us-east-1")
+ if err != nil {
+ t.Fatal("Error:", err, bucketName)
+ }
+
+ // Generate data more than 32K
+ buf := bytes.Repeat([]byte("3"), rand.Intn(1<<20)+32*1024)
+ m := make(map[string][]string)
+ m["Content-Encoding"] = []string{"gzip"}
+
+ // Save the data
+ objectName := randString(60, rand.NewSource(time.Now().UnixNano()), "")
+ n, err := c.Client.PutObjectWithMetadata(bucketName, objectName, bytes.NewReader(buf), m, nil)
+ if err != nil {
+ t.Fatal("Error:", err, bucketName, objectName)
+ }
+
+ if n != int64(len(buf)) {
+ t.Fatalf("Error: number of bytes does not match, want %v, got %v\n", len(buf), n)
+ }
+
+ reqHeaders := NewGetReqHeaders()
+ rwc, objInfo, err := c.GetObject(bucketName, objectName, reqHeaders)
+ if err != nil {
+ t.Fatalf("Error: %v", err)
+ }
+ rwc.Close()
+ if objInfo.Size <= 0 {
+ t.Fatalf("Unexpected size of the object %v, expected %v", objInfo.Size, n)
+ }
+ value, ok := objInfo.Metadata["Content-Encoding"]
+ if !ok {
+ t.Fatalf("Expected Content-Encoding metadata to be set.")
+ }
+ if value[0] != "gzip" {
+ t.Fatalf("Unexpected content-encoding found, want gzip, got %v", value)
+ }
+}
+
// Tests get bucket policy core API.
func TestGetBucketPolicy(t *testing.T) {
if testing.Short() {
@@ -321,11 +419,6 @@ func TestCorePutObject(t *testing.T) {
t.Fatal("Error expected: nil, got: ", err)
}
- objInfo, err = c.PutObject(bucketName, objectName, int64(len(buf)), bytes.NewReader(buf), nil, sum256(nil), metadata)
- if err == nil {
- t.Fatal("Error expected: nil, got: ", err)
- }
-
objInfo, err = c.PutObject(bucketName, objectName, int64(len(buf)), bytes.NewReader(buf), nil, nil, metadata)
if err != nil {
t.Fatal("Error:", err, bucketName, objectName)
@@ -373,3 +466,48 @@ func TestCorePutObject(t *testing.T) {
t.Fatal("Error:", err)
}
}
+
+func TestCoreGetObjectMetadata(t *testing.T) {
+ if testing.Short() {
+ t.Skip("skipping functional tests for the short runs")
+ }
+
+ core, err := NewCore(
+ os.Getenv(serverEndpoint),
+ os.Getenv(accessKey),
+ os.Getenv(secretKey),
+ mustParseBool(os.Getenv(enableSecurity)))
+ if err != nil {
+ log.Fatalln(err)
+ }
+
+ // Generate a new random bucket name.
+ bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test")
+
+ // Make a new bucket.
+ err = core.MakeBucket(bucketName, "us-east-1")
+ if err != nil {
+ t.Fatal("Error:", err, bucketName)
+ }
+
+ metadata := map[string][]string{
+ "X-Amz-Meta-Key-1": {"Val-1"},
+ }
+
+ _, err = core.PutObject(bucketName, "my-objectname", 5,
+ bytes.NewReader([]byte("hello")), nil, nil, metadata)
+ if err != nil {
+ log.Fatalln(err)
+ }
+
+ reader, objInfo, err := core.GetObject(bucketName, "my-objectname",
+ RequestHeaders{})
+ if err != nil {
+ log.Fatalln(err)
+ }
+ defer reader.Close()
+
+ if objInfo.Metadata.Get("X-Amz-Meta-Key-1") != "Val-1" {
+ log.Fatalln("Expected metadata to be available but wasn't")
+ }
+}
diff --git a/vendor/github.com/minio/minio-go/docs/API.md b/vendor/github.com/minio/minio-go/docs/API.md
index e0d0a11e6..902a975d9 100644
--- a/vendor/github.com/minio/minio-go/docs/API.md
+++ b/vendor/github.com/minio/minio-go/docs/API.md
@@ -531,9 +531,13 @@ __Example__
src := minio.NewSourceInfo("my-sourcebucketname", "my-sourceobjectname", nil)
// Destination object
-dst := minio.NewDestinationInfo("my-bucketname", "my-objectname", nil, nil)
+dst, err := minio.NewDestinationInfo("my-bucketname", "my-objectname", nil, nil)
+if err != nil {
+ fmt.Println(err)
+ return
+}
-/ Copy object call
+// Copy object call
err = s3Client.CopyObject(dst, src)
if err != nil {
fmt.Println(err)
@@ -562,9 +566,13 @@ src.SetUnmodifiedSinceCond(time.Date(2014, time.April, 23, 0, 0, 0, 0, time.UTC)
src.SetRange(0, 1024*1024-1)
// Destination object
-dst := minio.NewDestinationInfo("my-bucketname", "my-objectname", nil, nil)
+dst, err := minio.NewDestinationInfo("my-bucketname", "my-objectname", nil, nil)
+if err != nil {
+ fmt.Println(err)
+ return
+}
-/ Copy object call
+// Copy object call
err = s3Client.CopyObject(dst, src)
if err != nil {
fmt.Println(err)
@@ -648,7 +656,7 @@ src := NewSourceInfo("bucket", "object", decKey)
```
<a name="NewDestinationInfo"></a>
-### NewDestinationInfo(bucket, object string, encryptSSEC *SSEInfo, userMeta map[string]string) DestinationInfo
+### NewDestinationInfo(bucket, object string, encryptSSEC *SSEInfo, userMeta map[string]string) (DestinationInfo, error)
Construct a `DestinationInfo` object that can be used as the destination object for server-side copying operations like `CopyObject` and `ComposeObject`.
@@ -665,11 +673,11 @@ __Example__
``` go
// No encryption parameter.
-src := NewDestinationInfo("bucket", "object", nil, nil)
+dst, err := NewDestinationInfo("bucket", "object", nil, nil)
// With encryption parameter.
encKey := NewSSEKey([]byte{1,2,3}, "")
-src := NewDecryptionInfo("bucket", "object", encKey, nil)
+dst, err := NewDecryptionInfo("bucket", "object", encKey, nil)
```
diff --git a/vendor/github.com/minio/minio-go/examples/s3/composeobject.go b/vendor/github.com/minio/minio-go/examples/s3/composeobject.go
index 555d98bc3..8aec6c158 100644
--- a/vendor/github.com/minio/minio-go/examples/s3/composeobject.go
+++ b/vendor/github.com/minio/minio-go/examples/s3/composeobject.go
@@ -47,14 +47,14 @@ func main() {
// Source objects to concatenate. We also specify decryption
// key for each
- src1 := minio.NewSourceInfo("bucket1", "object1", decKey)
- src1.SetMatchETag("31624deb84149d2f8ef9c385918b653a")
+ src1 := minio.NewSourceInfo("bucket1", "object1", &decKey)
+ src1.SetMatchETagCond("31624deb84149d2f8ef9c385918b653a")
- src2 := minio.NewSourceInfo("bucket2", "object2", decKey)
- src2.SetMatchETag("f8ef9c385918b653a31624deb84149d2")
+ src2 := minio.NewSourceInfo("bucket2", "object2", &decKey)
+ src2.SetMatchETagCond("f8ef9c385918b653a31624deb84149d2")
- src3 := minio.NewSourceInfo("bucket3", "object3", decKey)
- src3.SetMatchETag("5918b653a31624deb84149d2f8ef9c38")
+ src3 := minio.NewSourceInfo("bucket3", "object3", &decKey)
+ src3.SetMatchETagCond("5918b653a31624deb84149d2f8ef9c38")
// Create slice of sources.
srcs := []minio.SourceInfo{src1, src2, src3}
@@ -63,11 +63,14 @@ func main() {
encKey := minio.NewSSEInfo([]byte{8, 9, 0}, "")
// Create destination info
- dst := minio.NewDestinationInfo("bucket", "object", encKey)
+ dst, err := minio.NewDestinationInfo("bucket", "object", &encKey, nil)
+ if err != nil {
+ log.Fatalln(err)
+ }
+
err = s3Client.ComposeObject(dst, srcs)
if err != nil {
- log.Println(err)
- return
+ log.Fatalln(err)
}
log.Println("Composed object successfully.")
diff --git a/vendor/github.com/minio/minio-go/examples/s3/copyobject.go b/vendor/github.com/minio/minio-go/examples/s3/copyobject.go
index 0de865555..c1d92d73a 100644
--- a/vendor/github.com/minio/minio-go/examples/s3/copyobject.go
+++ b/vendor/github.com/minio/minio-go/examples/s3/copyobject.go
@@ -60,7 +60,10 @@ func main() {
// src.SetMatchETagExceptCond("31624deb84149d2f8ef9c385918b653a")
// Destination object
- dst := minio.NewDestinationInfo("my-bucketname", "my-objectname", nil)
+ dst, err := minio.NewDestinationInfo("my-bucketname", "my-objectname", nil, nil)
+ if err != nil {
+ log.Fatalln(err)
+ }
// Initiate copy object.
err = s3Client.CopyObject(dst, src)
diff --git a/vendor/github.com/minio/minio-go/examples/s3/putobject-progress.go b/vendor/github.com/minio/minio-go/examples/s3/putobject-progress.go
index 1179fd787..26e77b9e6 100644
--- a/vendor/github.com/minio/minio-go/examples/s3/putobject-progress.go
+++ b/vendor/github.com/minio/minio-go/examples/s3/putobject-progress.go
@@ -55,7 +55,9 @@ func main() {
progress := pb.New64(objectInfo.Size)
progress.Start()
- n, err := s3Client.PutObjectWithProgress("my-bucketname", "my-objectname-progress", reader, "application/octet-stream", progress)
+ n, err := s3Client.PutObjectWithProgress("my-bucketname", "my-objectname-progress", reader, map[string][]string{
+ "Content-Type": []string{"application/octet-stream"},
+ }, progress)
if err != nil {
log.Fatalln(err)
}
diff --git a/vendor/github.com/minio/minio-go/examples/s3/putobject-s3-accelerate.go b/vendor/github.com/minio/minio-go/examples/s3/putobject-s3-accelerate.go
index e47976f2e..a26415c7a 100644
--- a/vendor/github.com/minio/minio-go/examples/s3/putobject-s3-accelerate.go
+++ b/vendor/github.com/minio/minio-go/examples/s3/putobject-s3-accelerate.go
@@ -40,7 +40,7 @@ func main() {
}
// Enable S3 transfer accelerate endpoint.
- s3Client.S3TransferAccelerate("s3-accelerate.amazonaws.com")
+ s3Client.SetS3TransferAccelerate("s3-accelerate.amazonaws.com")
object, err := os.Open("my-testfile")
if err != nil {
diff --git a/vendor/github.com/minio/minio-go/functional_tests.go b/vendor/github.com/minio/minio-go/functional_tests.go
new file mode 100644
index 000000000..3d16da4fe
--- /dev/null
+++ b/vendor/github.com/minio/minio-go/functional_tests.go
@@ -0,0 +1,4121 @@
+// +build ignore
+
+/*
+ * Minio Go Library for Amazon S3 Compatible Cloud Storage (C) 2015 Minio, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package main
+
+import (
+ "bytes"
+ "encoding/hex"
+ "errors"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "math/rand"
+ "net/http"
+ "net/url"
+ "os"
+ "path"
+ "reflect"
+ "runtime"
+ "strconv"
+ "strings"
+ "time"
+
+ minio "github.com/minio/minio-go"
+ log "github.com/sirupsen/logrus"
+ logrus "github.com/sirupsen/logrus"
+
+ "github.com/minio/minio-go/pkg/encrypt"
+ "github.com/minio/minio-go/pkg/policy"
+)
+
+// MinPartSize ... Minimum part size
+const MinPartSize = 1024 * 1024 * 64
+const letterBytes = "abcdefghijklmnopqrstuvwxyz01234569"
+const (
+ letterIdxBits = 6 // 6 bits to represent a letter index
+ letterIdxMask = 1<<letterIdxBits - 1 // All 1-bits, as many as letterIdxBits
+ letterIdxMax = 63 / letterIdxBits // # of letter indices fitting in 63 bits
+)
+const (
+ serverEndpoint = "SERVER_ENDPOINT"
+ accessKey = "ACCESS_KEY"
+ secretKey = "SECRET_KEY"
+ enableHTTPS = "ENABLE_HTTPS"
+)
+
+func init() {
+ // If server endpoint is not set, all tests default to
+ // using https://play.minio.io:9000
+ if os.Getenv(serverEndpoint) == "" {
+ os.Setenv(serverEndpoint, "play.minio.io:9000")
+ os.Setenv(accessKey, "Q3AM3UQ867SPQQA43P2F")
+ os.Setenv(secretKey, "zuf+tfteSlswRu7BJ86wekitnifILbZam1KYY3TG")
+ os.Setenv(enableHTTPS, "1")
+ }
+}
+
+func getDataDir() (dir string) {
+ dir = os.Getenv("MINT_DATA_DIR")
+ if dir == "" {
+ dir = "/mint/data"
+ }
+ return
+}
+
+func getFilePath(filename string) (filepath string) {
+ if getDataDir() != "" {
+ filepath = getDataDir() + "/" + filename
+ }
+ return
+}
+
+// read data from file if it exists or optionally create a buffer of particular size
+func getDataBuffer(fileName string, size int) (buf []byte) {
+ if _, err := os.Stat(getFilePath(fileName)); os.IsNotExist(err) {
+ buf = bytes.Repeat([]byte(string('a')), size)
+ return
+ }
+ buf, _ = ioutil.ReadFile(getFilePath(fileName))
+ return
+}
+
+// randString generates random names and prepends them with a known prefix.
+func randString(n int, src rand.Source, prefix string) string {
+ b := make([]byte, n)
+ // A rand.Int63() generates 63 random bits, enough for letterIdxMax letters!
+ for i, cache, remain := n-1, src.Int63(), letterIdxMax; i >= 0; {
+ if remain == 0 {
+ cache, remain = src.Int63(), letterIdxMax
+ }
+ if idx := int(cache & letterIdxMask); idx < len(letterBytes) {
+ b[i] = letterBytes[idx]
+ i--
+ }
+ cache >>= letterIdxBits
+ remain--
+ }
+ return prefix + string(b[0:30-len(prefix)])
+}
+
+func isQuickMode() bool {
+ return os.Getenv("MODE") == "quick"
+}
+
+// Tests bucket re-create errors.
+func testMakeBucketError() {
+ logger().Info()
+
+ if os.Getenv(serverEndpoint) != "s3.amazonaws.com" {
+ logger().Info("skipping region functional tests for non s3 runs")
+ return
+ }
+
+ // Seed random based on current time.
+ rand.Seed(time.Now().Unix())
+
+ // Instantiate new minio client object.
+ c, err := minio.New(
+ os.Getenv(serverEndpoint),
+ os.Getenv(accessKey),
+ os.Getenv(secretKey),
+ mustParseBool(os.Getenv(enableHTTPS)),
+ )
+ if err != nil {
+ logger().Fatalf("Error: %s", err)
+ }
+
+ // Enable tracing, write to stderr.
+ // c.TraceOn(os.Stderr)
+
+ // Set user agent.
+ c.SetAppInfo("Minio-go-FunctionalTest", "0.1.0")
+
+ // Generate a new random bucket name.
+ bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test")
+
+ // Make a new bucket in 'eu-central-1'.
+ if err = c.MakeBucket(bucketName, "eu-central-1"); err != nil {
+ logger().Fatal("Error:", err, bucketName)
+ }
+ if err = c.MakeBucket(bucketName, "eu-central-1"); err == nil {
+ logger().Fatal("Error: make bucket should should fail for", bucketName)
+ }
+ // Verify valid error response from server.
+ if minio.ToErrorResponse(err).Code != "BucketAlreadyExists" &&
+ minio.ToErrorResponse(err).Code != "BucketAlreadyOwnedByYou" {
+ logger().Fatal("Error: Invalid error returned by server", err)
+ }
+ if err = c.RemoveBucket(bucketName); err != nil {
+ logger().Fatal("Error:", err, bucketName)
+ }
+}
+
+// Tests various bucket supported formats.
+func testMakeBucketRegions() {
+ logger().Info()
+
+ if os.Getenv(serverEndpoint) != "s3.amazonaws.com" {
+ logger().Info("skipping region functional tests for non s3 runs")
+ return
+ }
+
+ // Seed random based on current time.
+ rand.Seed(time.Now().Unix())
+
+ // Instantiate new minio client object.
+ c, err := minio.New(
+ os.Getenv(serverEndpoint),
+ os.Getenv(accessKey),
+ os.Getenv(secretKey),
+ mustParseBool(os.Getenv(enableHTTPS)),
+ )
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+
+ // Enable tracing, write to stderr.
+ // c.TraceOn(os.Stderr)
+
+ // Set user agent.
+ c.SetAppInfo("Minio-go-FunctionalTest", "0.1.0")
+
+ // Generate a new random bucket name.
+ bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test")
+
+ // Make a new bucket in 'eu-central-1'.
+ if err = c.MakeBucket(bucketName, "eu-central-1"); err != nil {
+ logger().Fatal("Error:", err, bucketName)
+ }
+
+ if err = c.RemoveBucket(bucketName); err != nil {
+ logger().Fatal("Error:", err, bucketName)
+ }
+
+ // Make a new bucket with '.' in its name, in 'us-west-2'. This
+ // request is internally staged into a path style instead of
+ // virtual host style.
+ if err = c.MakeBucket(bucketName+".withperiod", "us-west-2"); err != nil {
+ logger().Fatal("Error:", err, bucketName+".withperiod")
+ }
+
+ // Remove the newly created bucket.
+ if err = c.RemoveBucket(bucketName + ".withperiod"); err != nil {
+ logger().Fatal("Error:", err, bucketName+".withperiod")
+ }
+}
+
+// Test PutObject using a large data to trigger multipart readat
+func testPutObjectReadAt() {
+ logger().Info()
+
+ // Seed random based on current time.
+ rand.Seed(time.Now().Unix())
+
+ // Instantiate new minio client object.
+ c, err := minio.New(
+ os.Getenv(serverEndpoint),
+ os.Getenv(accessKey),
+ os.Getenv(secretKey),
+ mustParseBool(os.Getenv(enableHTTPS)),
+ )
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+
+ // Enable tracing, write to stderr.
+ // c.TraceOn(os.Stderr)
+
+ // Set user agent.
+ c.SetAppInfo("Minio-go-FunctionalTest", "0.1.0")
+
+ // Generate a new random bucket name.
+ bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test")
+
+ // Make a new bucket.
+ err = c.MakeBucket(bucketName, "us-east-1")
+ if err != nil {
+ logger().Fatal("Error:", err, bucketName)
+ }
+
+ // Generate data using 4 parts so that all 3 'workers' are utilized and a part is leftover.
+ // Use different data for each part for multipart tests to ensure part order at the end.
+ var buf = getDataBuffer("datafile-65-MB", MinPartSize)
+
+ // Save the data
+ objectName := randString(60, rand.NewSource(time.Now().UnixNano()), "")
+ // Object content type
+ objectContentType := "binary/octet-stream"
+
+ n, err := c.PutObject(bucketName, objectName, bytes.NewReader(buf), objectContentType)
+ if err != nil {
+ logger().Fatal("Error:", err, bucketName, objectName)
+ }
+
+ if n != int64(len(buf)) {
+ logger().Fatalf("Error: number of bytes does not match, want %v, got %v\n", len(buf), n)
+ }
+
+ // Read the data back
+ r, err := c.GetObject(bucketName, objectName)
+ if err != nil {
+ logger().Fatal("Error:", err, bucketName, objectName)
+ }
+
+ st, err := r.Stat()
+ if err != nil {
+ logger().Fatal("Error:", err, bucketName, objectName)
+ }
+ if st.Size != int64(len(buf)) {
+ logger().Fatalf("Error: number of bytes in stat does not match, want %v, got %v\n",
+ len(buf), st.Size)
+ }
+ if st.ContentType != objectContentType {
+ logger().Fatalf("Error: Content types don't match, expected: %+v, found: %+v\n", objectContentType, st.ContentType)
+ }
+ if err := r.Close(); err != nil {
+ logger().Fatal("Error:", err)
+ }
+ if err := r.Close(); err == nil {
+ logger().Fatal("Error: object is already closed, should return error")
+ }
+
+ err = c.RemoveObject(bucketName, objectName)
+ if err != nil {
+ logger().Fatal("Error: ", err)
+ }
+ err = c.RemoveBucket(bucketName)
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+}
+
+// Test PutObject using a large data to trigger multipart readat
+func testPutObjectWithMetadata() {
+ logger().Info()
+ if isQuickMode() {
+ logger().Info("skipping functional tests for short runs")
+ return
+ }
+
+ // Seed random based on current time.
+ rand.Seed(time.Now().Unix())
+
+ // Instantiate new minio client object.
+ c, err := minio.New(
+ os.Getenv(serverEndpoint),
+ os.Getenv(accessKey),
+ os.Getenv(secretKey),
+ mustParseBool(os.Getenv(enableHTTPS)),
+ )
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+
+ // Enable tracing, write to stderr.
+ // c.TraceOn(os.Stderr)
+
+ // Set user agent.
+ c.SetAppInfo("Minio-go-FunctionalTest", "0.1.0")
+
+ // Generate a new random bucket name.
+ bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test")
+
+ // Make a new bucket.
+ err = c.MakeBucket(bucketName, "us-east-1")
+ if err != nil {
+ logger().Fatal("Error:", err, bucketName)
+ }
+
+ // Generate data using 2 parts
+ // Use different data in each part for multipart tests to ensure part order at the end.
+ var buf = getDataBuffer("datafile-65-MB", MinPartSize)
+
+ // Save the data
+ objectName := randString(60, rand.NewSource(time.Now().UnixNano()), "")
+
+ // Object custom metadata
+ customContentType := "custom/contenttype"
+
+ n, err := c.PutObjectWithMetadata(bucketName, objectName, bytes.NewReader(buf), map[string][]string{
+ "Content-Type": {customContentType},
+ }, nil)
+ if err != nil {
+ logger().Fatal("Error:", err, bucketName, objectName)
+ }
+
+ if n != int64(len(buf)) {
+ logger().Fatalf("Error: number of bytes does not match, want %v, got %v\n", len(buf), n)
+ }
+
+ // Read the data back
+ r, err := c.GetObject(bucketName, objectName)
+ if err != nil {
+ logger().Fatal("Error:", err, bucketName, objectName)
+ }
+
+ st, err := r.Stat()
+ if err != nil {
+ logger().Fatal("Error:", err, bucketName, objectName)
+ }
+ if st.Size != int64(len(buf)) {
+ logger().Fatalf("Error: number of bytes in stat does not match, want %v, got %v\n",
+ len(buf), st.Size)
+ }
+ if st.ContentType != customContentType {
+ logger().Fatalf("Error: Expected and found content types do not match, want %v, got %v\n",
+ customContentType, st.ContentType)
+ }
+ if err := r.Close(); err != nil {
+ logger().Fatal("Error:", err)
+ }
+ if err := r.Close(); err == nil {
+ logger().Fatal("Error: object is already closed, should return error")
+ }
+
+ err = c.RemoveObject(bucketName, objectName)
+ if err != nil {
+ logger().Fatal("Error: ", err)
+ }
+ err = c.RemoveBucket(bucketName)
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+}
+
+// Test put object with streaming signature.
+func testPutObjectStreaming() {
+ logger().Info()
+
+ // Seed random based on current time.
+ rand.Seed(time.Now().Unix())
+
+ // Instantiate new minio client object.
+ c, err := minio.NewV4(
+ os.Getenv(serverEndpoint),
+ os.Getenv(accessKey),
+ os.Getenv(secretKey),
+ mustParseBool(os.Getenv(enableHTTPS)),
+ )
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+
+ // Enable tracing, write to stderr.
+ // c.TraceOn(os.Stderr)
+
+ // Set user agent.
+ c.SetAppInfo("Minio-go-FunctionalTest", "0.1.0")
+
+ // Generate a new random bucket name.
+ bucketName := randString(60, rand.NewSource(time.Now().UnixNano()),
+ "minio-go-test")
+
+ // Make a new bucket.
+ err = c.MakeBucket(bucketName, "us-east-1")
+ if err != nil {
+ logger().Fatal("Error:", err, bucketName)
+ }
+
+ // Upload an object.
+ sizes := []int64{0, 64*1024 - 1, 64 * 1024}
+ objectName := "test-object"
+ for i, size := range sizes {
+ data := bytes.Repeat([]byte("a"), int(size))
+ n, err := c.PutObjectStreaming(bucketName, objectName, bytes.NewReader(data))
+ if err != nil {
+ logger().Fatalf("Test %d Error: %v %s %s", i+1, err, bucketName, objectName)
+ }
+
+ if n != size {
+ log.Error(fmt.Errorf("Test %d Expected upload object size %d but got %d", i+1, size, n))
+ }
+ }
+
+ // Remove the object.
+ err = c.RemoveObject(bucketName, objectName)
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+
+ // Remove the bucket.
+ err = c.RemoveBucket(bucketName)
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+}
+
+// Test listing partially uploaded objects.
+func testListPartiallyUploaded() {
+ logger().Info()
+
+ // Seed random based on current time.
+ rand.Seed(time.Now().Unix())
+
+ // Instantiate new minio client object.
+ c, err := minio.New(
+ os.Getenv(serverEndpoint),
+ os.Getenv(accessKey),
+ os.Getenv(secretKey),
+ mustParseBool(os.Getenv(enableHTTPS)),
+ )
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+
+ // Set user agent.
+ c.SetAppInfo("Minio-go-FunctionalTest", "0.1.0")
+
+ // Enable tracing, write to stdout.
+ // c.TraceOn(os.Stderr)
+
+ // Generate a new random bucket name.
+ bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test")
+
+ // Make a new bucket.
+ err = c.MakeBucket(bucketName, "us-east-1")
+ if err != nil {
+ logger().Fatal("Error:", err, bucketName)
+ }
+
+ r := bytes.NewReader(bytes.Repeat([]byte("0"), MinPartSize*2))
+
+ reader, writer := io.Pipe()
+ go func() {
+ i := 0
+ for i < 25 {
+ _, cerr := io.CopyN(writer, r, (MinPartSize*2)/25)
+ if cerr != nil {
+ logger().Fatal("Error:", cerr, bucketName)
+ }
+ i++
+ r.Seek(0, 0)
+ }
+ writer.CloseWithError(errors.New("proactively closed to be verified later"))
+ }()
+
+ objectName := bucketName + "-resumable"
+ _, err = c.PutObject(bucketName, objectName, reader, "application/octet-stream")
+ if err == nil {
+ logger().Fatal("Error: PutObject should fail.")
+ }
+ if !strings.Contains(err.Error(), "proactively closed to be verified later") {
+ logger().Fatal("Error:", err)
+ }
+
+ doneCh := make(chan struct{})
+ defer close(doneCh)
+ isRecursive := true
+ multiPartObjectCh := c.ListIncompleteUploads(bucketName, objectName, isRecursive, doneCh)
+ for multiPartObject := range multiPartObjectCh {
+ if multiPartObject.Err != nil {
+ logger().Fatalf("Error: Error when listing incomplete upload")
+ }
+ }
+
+ err = c.RemoveBucket(bucketName)
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+}
+
+// Test get object seeker from the end, using whence set to '2'.
+func testGetObjectSeekEnd() {
+ logger().Info()
+
+ // Seed random based on current time.
+ rand.Seed(time.Now().Unix())
+
+ // Instantiate new minio client object.
+ c, err := minio.New(
+ os.Getenv(serverEndpoint),
+ os.Getenv(accessKey),
+ os.Getenv(secretKey),
+ mustParseBool(os.Getenv(enableHTTPS)),
+ )
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+
+ // Enable tracing, write to stderr.
+ // c.TraceOn(os.Stderr)
+
+ // Set user agent.
+ c.SetAppInfo("Minio-go-FunctionalTest", "0.1.0")
+
+ // Generate a new random bucket name.
+ bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test")
+
+ // Make a new bucket.
+ err = c.MakeBucket(bucketName, "us-east-1")
+ if err != nil {
+ logger().Fatal("Error:", err, bucketName)
+ }
+ var buf = getDataBuffer("datafile-33-kB", rand.Intn(1<<20)+32*1024)
+
+ // Save the data
+ objectName := randString(60, rand.NewSource(time.Now().UnixNano()), "")
+ n, err := c.PutObject(bucketName, objectName, bytes.NewReader(buf), "binary/octet-stream")
+ if err != nil {
+ logger().Fatal("Error:", err, bucketName, objectName)
+ }
+
+ if n != int64(len(buf)) {
+ logger().Fatalf("Error: number of bytes does not match, want %v, got %v\n", len(buf), n)
+ }
+
+ // Read the data back
+ r, err := c.GetObject(bucketName, objectName)
+ if err != nil {
+ logger().Fatal("Error:", err, bucketName, objectName)
+ }
+
+ st, err := r.Stat()
+ if err != nil {
+ logger().Fatal("Error:", err, bucketName, objectName)
+ }
+ if st.Size != int64(len(buf)) {
+ logger().Fatalf("Error: number of bytes in stat does not match, want %v, got %v\n",
+ len(buf), st.Size)
+ }
+
+ pos, err := r.Seek(-100, 2)
+ if err != nil {
+ logger().Fatal("Error:", err, bucketName, objectName)
+ }
+ if pos != st.Size-100 {
+ logger().Fatalf("Expected %d, got %d instead", pos, st.Size-100)
+ }
+ buf2 := make([]byte, 100)
+ m, err := io.ReadFull(r, buf2)
+ if err != nil {
+ logger().Fatal("Error: reading through io.ReadFull", err, bucketName, objectName)
+ }
+ if m != len(buf2) {
+ logger().Fatalf("Expected %d bytes, got %d", len(buf2), m)
+ }
+ hexBuf1 := fmt.Sprintf("%02x", buf[len(buf)-100:])
+ hexBuf2 := fmt.Sprintf("%02x", buf2[:m])
+ if hexBuf1 != hexBuf2 {
+ logger().Fatalf("Expected %s, got %s instead", hexBuf1, hexBuf2)
+ }
+ pos, err = r.Seek(-100, 2)
+ if err != nil {
+ logger().Fatal("Error:", err, bucketName, objectName)
+ }
+ if pos != st.Size-100 {
+ logger().Fatalf("Expected %d, got %d instead", pos, st.Size-100)
+ }
+ if err = r.Close(); err != nil {
+ logger().Fatal("Error:", err, bucketName, objectName)
+ }
+}
+
+// Test get object reader to not throw error on being closed twice.
+func testGetObjectClosedTwice() {
+ logger().Info()
+
+ // Seed random based on current time.
+ rand.Seed(time.Now().Unix())
+
+ // Instantiate new minio client object.
+ c, err := minio.New(
+ os.Getenv(serverEndpoint),
+ os.Getenv(accessKey),
+ os.Getenv(secretKey),
+ mustParseBool(os.Getenv(enableHTTPS)),
+ )
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+
+ // Enable tracing, write to stderr.
+ // c.TraceOn(os.Stderr)
+
+ // Set user agent.
+ c.SetAppInfo("Minio-go-FunctionalTest", "0.1.0")
+
+ // Generate a new random bucket name.
+ bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test")
+
+ // Make a new bucket.
+ err = c.MakeBucket(bucketName, "us-east-1")
+ if err != nil {
+ logger().Fatal("Error:", err, bucketName)
+ }
+
+ // Generate data more than 32K
+ var buf = getDataBuffer("datafile-33-kB", rand.Intn(1<<20)+32*1024)
+ // Save the data
+ objectName := randString(60, rand.NewSource(time.Now().UnixNano()), "")
+ n, err := c.PutObject(bucketName, objectName, bytes.NewReader(buf), "binary/octet-stream")
+ if err != nil {
+ logger().Fatal("Error:", err, bucketName, objectName)
+ }
+
+ if n != int64(len(buf)) {
+ logger().Fatalf("Error: number of bytes does not match, want %v, got %v\n", len(buf), n)
+ }
+
+ // Read the data back
+ r, err := c.GetObject(bucketName, objectName)
+ if err != nil {
+ logger().Fatal("Error:", err, bucketName, objectName)
+ }
+
+ st, err := r.Stat()
+ if err != nil {
+ logger().Fatal("Error:", err, bucketName, objectName)
+ }
+ if st.Size != int64(len(buf)) {
+ logger().Fatalf("Error: number of bytes in stat does not match, want %v, got %v\n",
+ len(buf), st.Size)
+ }
+ if err := r.Close(); err != nil {
+ logger().Fatal("Error:", err)
+ }
+ if err := r.Close(); err == nil {
+ logger().Fatal("Error: object is already closed, should return error")
+ }
+
+ err = c.RemoveObject(bucketName, objectName)
+ if err != nil {
+ logger().Fatal("Error: ", err)
+ }
+ err = c.RemoveBucket(bucketName)
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+}
+
+// Test removing multiple objects with Remove API
+func testRemoveMultipleObjects() {
+ logger().Info()
+ // Seed random based on current time.
+ rand.Seed(time.Now().Unix())
+
+ // Instantiate new minio client object.
+ c, err := minio.New(
+ os.Getenv(serverEndpoint),
+ os.Getenv(accessKey),
+ os.Getenv(secretKey),
+ mustParseBool(os.Getenv(enableHTTPS)),
+ )
+
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+
+ // Set user agent.
+ c.SetAppInfo("Minio-go-FunctionalTest", "0.1.0")
+
+ // Enable tracing, write to stdout.
+ // c.TraceOn(os.Stderr)
+
+ // Generate a new random bucket name.
+ bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test")
+
+ // Make a new bucket.
+ err = c.MakeBucket(bucketName, "us-east-1")
+ if err != nil {
+ logger().Fatal("Error:", err, bucketName)
+ }
+
+ r := bytes.NewReader(bytes.Repeat([]byte("a"), 8))
+
+ // Multi remove of 1100 objects
+ nrObjects := 1100
+
+ objectsCh := make(chan string)
+
+ go func() {
+ defer close(objectsCh)
+ // Upload objects and send them to objectsCh
+ for i := 0; i < nrObjects; i++ {
+ objectName := "sample" + strconv.Itoa(i) + ".txt"
+ _, err = c.PutObject(bucketName, objectName, r, "application/octet-stream")
+ if err != nil {
+ log.Error("Error: PutObject shouldn't fail.", err)
+ continue
+ }
+ objectsCh <- objectName
+ }
+ }()
+
+ // Call RemoveObjects API
+ errorCh := c.RemoveObjects(bucketName, objectsCh)
+
+ // Check if errorCh doesn't receive any error
+ select {
+ case r, more := <-errorCh:
+ if more {
+ logger().Fatalf("Unexpected error, objName(%v) err(%v)", r.ObjectName, r.Err)
+ }
+ }
+
+ // Clean the bucket created by the test
+ err = c.RemoveBucket(bucketName)
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+}
+
+// Tests removing partially uploaded objects.
+func testRemovePartiallyUploaded() {
+ logger().Info()
+
+ // Seed random based on current time.
+ rand.Seed(time.Now().Unix())
+
+ // Instantiate new minio client object.
+ c, err := minio.New(
+ os.Getenv(serverEndpoint),
+ os.Getenv(accessKey),
+ os.Getenv(secretKey),
+ mustParseBool(os.Getenv(enableHTTPS)),
+ )
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+
+ // Set user agent.
+ c.SetAppInfo("Minio-go-FunctionalTest", "0.1.0")
+
+ // Enable tracing, write to stdout.
+ // c.TraceOn(os.Stderr)
+
+ // Generate a new random bucket name.
+ bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test")
+
+ // Make a new bucket.
+ err = c.MakeBucket(bucketName, "us-east-1")
+ if err != nil {
+ logger().Fatal("Error:", err, bucketName)
+ }
+
+ r := bytes.NewReader(bytes.Repeat([]byte("a"), 128*1024))
+
+ reader, writer := io.Pipe()
+ go func() {
+ i := 0
+ for i < 25 {
+ _, cerr := io.CopyN(writer, r, 128*1024)
+ if cerr != nil {
+ logger().Fatal("Error:", cerr, bucketName)
+ }
+ i++
+ r.Seek(0, 0)
+ }
+ writer.CloseWithError(errors.New("proactively closed to be verified later"))
+ }()
+
+ objectName := bucketName + "-resumable"
+ _, err = c.PutObject(bucketName, objectName, reader, "application/octet-stream")
+ if err == nil {
+ logger().Fatal("Error: PutObject should fail.")
+ }
+ if !strings.Contains(err.Error(), "proactively closed to be verified later") {
+ logger().Fatal("Error:", err)
+ }
+ err = c.RemoveIncompleteUpload(bucketName, objectName)
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+ err = c.RemoveBucket(bucketName)
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+}
+
+// Tests FPutObject of a big file to trigger multipart
+func testFPutObjectMultipart() {
+ logger().Info()
+
+ // Seed random based on current time.
+ rand.Seed(time.Now().Unix())
+
+ // Instantiate new minio client object.
+ c, err := minio.New(
+ os.Getenv(serverEndpoint),
+ os.Getenv(accessKey),
+ os.Getenv(secretKey),
+ mustParseBool(os.Getenv(enableHTTPS)),
+ )
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+
+ // Enable tracing, write to stderr.
+ // c.TraceOn(os.Stderr)
+
+ // Set user agent.
+ c.SetAppInfo("Minio-go-FunctionalTest", "0.1.0")
+
+ // Generate a new random bucket name.
+ bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test")
+
+ // Make a new bucket.
+ err = c.MakeBucket(bucketName, "us-east-1")
+ if err != nil {
+ logger().Fatal("Error:", err, bucketName)
+ }
+
+ // Upload 4 parts to utilize all 3 'workers' in multipart and still have a part to upload.
+ var fileName = getFilePath("datafile-65-MB")
+ if os.Getenv("MINT_DATA_DIR") == "" {
+ // Make a temp file with minPartSize bytes of data.
+ file, err := ioutil.TempFile(os.TempDir(), "FPutObjectTest")
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+
+ // Upload 4 parts to utilize all 3 'workers' in multipart and still have a part to upload.
+ var buffer = bytes.Repeat([]byte(string('a')), MinPartSize)
+ if _, err := file.Write(buffer); err != nil {
+ logger().Fatal("Error:", err)
+ }
+ // Close the file pro-actively for windows.
+ err = file.Close()
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+ fileName = file.Name()
+ }
+ totalSize := MinPartSize * 1
+ // Set base object name
+ objectName := bucketName + "FPutObject"
+ objectContentType := "testapplication/octet-stream"
+
+ // Perform standard FPutObject with contentType provided (Expecting application/octet-stream)
+ n, err := c.FPutObject(bucketName, objectName+"-standard", fileName, objectContentType)
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+ if n != int64(totalSize) {
+ logger().Fatalf("Error: number of bytes does not match, want %v, got %v\n", totalSize, n)
+ }
+
+ r, err := c.GetObject(bucketName, objectName+"-standard")
+ if err != nil {
+ logger().Fatalf("Unexpected error: %v\n", err)
+ }
+ objInfo, err := r.Stat()
+ if err != nil {
+ logger().Fatalf("Unexpected error: %v\n", err)
+ }
+ if objInfo.Size != int64(totalSize) {
+ logger().Fatalf("Error: number of bytes does not match, want %v, got %v\n", totalSize, n)
+ }
+ if objInfo.ContentType != objectContentType {
+ logger().Fatalf("Error: Content types don't match, want %v, got %v\n", objectContentType, objInfo.ContentType)
+ }
+
+ // Remove all objects and bucket and temp file
+ err = c.RemoveObject(bucketName, objectName+"-standard")
+ if err != nil {
+ logger().Fatal("Error: ", err)
+ }
+
+ err = c.RemoveBucket(bucketName)
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+}
+
+// Tests FPutObject hidden contentType setting
+func testFPutObject() {
+ logger().Info()
+
+ // Seed random based on current time.
+ rand.Seed(time.Now().Unix())
+
+ // Instantiate new minio client object.
+ c, err := minio.New(
+ os.Getenv(serverEndpoint),
+ os.Getenv(accessKey),
+ os.Getenv(secretKey),
+ mustParseBool(os.Getenv(enableHTTPS)),
+ )
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+
+ // Enable tracing, write to stderr.
+ // c.TraceOn(os.Stderr)
+
+ // Set user agent.
+ c.SetAppInfo("Minio-go-FunctionalTest", "0.1.0")
+
+ // Generate a new random bucket name.
+ bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test")
+
+ // Make a new bucket.
+ err = c.MakeBucket(bucketName, "us-east-1")
+ if err != nil {
+ logger().Fatal("Error:", err, bucketName)
+ }
+
+ // Upload 3 parts worth of data to use all 3 of multiparts 'workers' and have an extra part.
+ // Use different data in part for multipart tests to check parts are uploaded in correct order.
+ var fName = getFilePath("datafile-65-MB")
+ if os.Getenv("MINT_DATA_DIR") == "" {
+ // Make a temp file with minPartSize bytes of data.
+ file, err := ioutil.TempFile(os.TempDir(), "FPutObjectTest")
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+
+ // Upload 4 parts to utilize all 3 'workers' in multipart and still have a part to upload.
+ var buffer = bytes.Repeat([]byte(string('a')), MinPartSize)
+ if _, err = file.Write(buffer); err != nil {
+ logger().Fatal("Error:", err)
+ }
+ // Close the file pro-actively for windows.
+ err = file.Close()
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+ fName = file.Name()
+ }
+ var totalSize = MinPartSize * 1
+
+ // Set base object name
+ objectName := bucketName + "FPutObject"
+
+ // Perform standard FPutObject with contentType provided (Expecting application/octet-stream)
+ n, err := c.FPutObject(bucketName, objectName+"-standard", fName, "application/octet-stream")
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+ if n != int64(totalSize) {
+ logger().Fatalf("Error: number of bytes does not match, want %v, got %v\n", totalSize, n)
+ }
+
+ // Perform FPutObject with no contentType provided (Expecting application/octet-stream)
+ n, err = c.FPutObject(bucketName, objectName+"-Octet", fName, "")
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+ if n != int64(totalSize) {
+ logger().Fatalf("Error: number of bytes does not match, want %v, got %v\n", totalSize, n)
+ }
+ srcFile, err := os.Open(fName)
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+ defer srcFile.Close()
+ // Add extension to temp file name
+ tmpFile, err := os.Create(fName + ".gtar")
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+ defer tmpFile.Close()
+ _, err = io.Copy(tmpFile, srcFile)
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+
+ // Perform FPutObject with no contentType provided (Expecting application/x-gtar)
+ n, err = c.FPutObject(bucketName, objectName+"-GTar", fName+".gtar", "")
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+ if n != int64(totalSize) {
+ logger().Fatalf("Error: number of bytes does not match, want %v, got %v\n", totalSize, n)
+ }
+
+ // Check headers
+ rStandard, err := c.StatObject(bucketName, objectName+"-standard")
+ if err != nil {
+ logger().Fatal("Error:", err, bucketName, objectName+"-standard")
+ }
+ if rStandard.ContentType != "application/octet-stream" {
+ logger().Fatalf("Error: Content-Type headers mismatched, want %v, got %v\n",
+ "application/octet-stream", rStandard.ContentType)
+ }
+
+ rOctet, err := c.StatObject(bucketName, objectName+"-Octet")
+ if err != nil {
+ logger().Fatal("Error:", err, bucketName, objectName+"-Octet")
+ }
+ if rOctet.ContentType != "application/octet-stream" {
+ logger().Fatalf("Error: Content-Type headers mismatched, want %v, got %v\n",
+ "application/octet-stream", rStandard.ContentType)
+ }
+
+ rGTar, err := c.StatObject(bucketName, objectName+"-GTar")
+ if err != nil {
+ logger().Fatal("Error:", err, bucketName, objectName+"-GTar")
+ }
+ if rGTar.ContentType != "application/x-gtar" {
+ logger().Fatalf("Error: Content-Type headers mismatched, want %v, got %v\n",
+ "application/x-gtar", rStandard.ContentType)
+ }
+
+ // Remove all objects and bucket and temp file
+ err = c.RemoveObject(bucketName, objectName+"-standard")
+ if err != nil {
+ logger().Fatal("Error: ", err)
+ }
+
+ err = c.RemoveObject(bucketName, objectName+"-Octet")
+ if err != nil {
+ logger().Fatal("Error: ", err)
+ }
+
+ err = c.RemoveObject(bucketName, objectName+"-GTar")
+ if err != nil {
+ logger().Fatal("Error: ", err)
+ }
+
+ err = c.RemoveBucket(bucketName)
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+
+ err = os.Remove(fName + ".gtar")
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+
+}
+
+// Tests get object ReaderSeeker interface methods.
+func testGetObjectReadSeekFunctional() {
+ logger().Info()
+
+ // Seed random based on current time.
+ rand.Seed(time.Now().Unix())
+
+ // Instantiate new minio client object.
+ c, err := minio.New(
+ os.Getenv(serverEndpoint),
+ os.Getenv(accessKey),
+ os.Getenv(secretKey),
+ mustParseBool(os.Getenv(enableHTTPS)),
+ )
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+
+ // Enable tracing, write to stderr.
+ // c.TraceOn(os.Stderr)
+
+ // Set user agent.
+ c.SetAppInfo("Minio-go-FunctionalTest", "0.1.0")
+
+ // Generate a new random bucket name.
+ bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test")
+
+ // Make a new bucket.
+ err = c.MakeBucket(bucketName, "us-east-1")
+ if err != nil {
+ logger().Fatal("Error:", err, bucketName)
+ }
+
+ // Generate data more than 32K
+ var buf = getDataBuffer("datafile-33-kB", rand.Intn(1<<20)+32*1024)
+
+ bufSize := len(buf)
+
+ // Save the data
+ objectName := randString(60, rand.NewSource(time.Now().UnixNano()), "")
+ n, err := c.PutObject(bucketName, objectName, bytes.NewReader(buf), "binary/octet-stream")
+ if err != nil {
+ logger().Fatal("Error:", err, bucketName, objectName)
+ }
+
+ if n != int64(bufSize) {
+ logger().Fatalf("Error: number of bytes does not match, want %v, got %v\n", len(buf), n)
+ }
+
+ defer func() {
+ err = c.RemoveObject(bucketName, objectName)
+ if err != nil {
+ logger().Fatal("Error: ", err)
+ }
+ err = c.RemoveBucket(bucketName)
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+ }()
+
+ // Read the data back
+ r, err := c.GetObject(bucketName, objectName)
+ if err != nil {
+ logger().Fatal("Error:", err, bucketName, objectName)
+ }
+
+ st, err := r.Stat()
+ if err != nil {
+ logger().Fatal("Error:", err, bucketName, objectName)
+ }
+ if st.Size != int64(bufSize) {
+ logger().Fatalf("Error: number of bytes in stat does not match, want %v, got %v\n",
+ len(buf), st.Size)
+ }
+
+ // This following function helps us to compare data from the reader after seek
+ // with the data from the original buffer
+ cmpData := func(r io.Reader, start, end int) {
+ if end-start == 0 {
+ return
+ }
+ buffer := bytes.NewBuffer([]byte{})
+ if _, err := io.CopyN(buffer, r, int64(bufSize)); err != nil {
+ if err != io.EOF {
+ logger().Fatal("Error:", err)
+ }
+ }
+ if !bytes.Equal(buf[start:end], buffer.Bytes()) {
+ logger().Fatal("Error: Incorrect read bytes v/s original buffer.")
+ }
+ }
+
+ // Generic seek error for errors other than io.EOF
+ seekErr := errors.New("seek error")
+
+ testCases := []struct {
+ offset int64
+ whence int
+ pos int64
+ err error
+ shouldCmp bool
+ start int
+ end int
+ }{
+ // Start from offset 0, fetch data and compare
+ {0, 0, 0, nil, true, 0, 0},
+ // Start from offset 2048, fetch data and compare
+ {2048, 0, 2048, nil, true, 2048, bufSize},
+ // Start from offset larger than possible
+ {int64(bufSize) + 1024, 0, 0, seekErr, false, 0, 0},
+ // Move to offset 0 without comparing
+ {0, 0, 0, nil, false, 0, 0},
+ // Move one step forward and compare
+ {1, 1, 1, nil, true, 1, bufSize},
+ // Move larger than possible
+ {int64(bufSize), 1, 0, seekErr, false, 0, 0},
+ // Provide negative offset with CUR_SEEK
+ {int64(-1), 1, 0, seekErr, false, 0, 0},
+ // Test with whence SEEK_END and with positive offset
+ {1024, 2, int64(bufSize) - 1024, io.EOF, true, 0, 0},
+ // Test with whence SEEK_END and with negative offset
+ {-1024, 2, int64(bufSize) - 1024, nil, true, bufSize - 1024, bufSize},
+ // Test with whence SEEK_END and with large negative offset
+ {-int64(bufSize) * 2, 2, 0, seekErr, true, 0, 0},
+ }
+
+ for i, testCase := range testCases {
+ // Perform seek operation
+ n, err := r.Seek(testCase.offset, testCase.whence)
+ // We expect an error
+ if testCase.err == seekErr && err == nil {
+ logger().Fatalf("Test %d, unexpected err value: expected: %v, found: %v", i+1, testCase.err, err)
+ }
+ // We expect a specific error
+ if testCase.err != seekErr && testCase.err != err {
+ logger().Fatalf("Test %d, unexpected err value: expected: %v, found: %v", i+1, testCase.err, err)
+ }
+ // If we expect an error go to the next loop
+ if testCase.err != nil {
+ continue
+ }
+ // Check the returned seek pos
+ if n != testCase.pos {
+ logger().Fatalf("Test %d, error: number of bytes seeked does not match, want %v, got %v\n", i+1,
+ testCase.pos, n)
+ }
+ // Compare only if shouldCmp is activated
+ if testCase.shouldCmp {
+ cmpData(r, testCase.start, testCase.end)
+ }
+ }
+}
+
+// Tests get object ReaderAt interface methods.
+func testGetObjectReadAtFunctional() {
+ logger().Info()
+
+ // Seed random based on current time.
+ rand.Seed(time.Now().Unix())
+
+ // Instantiate new minio client object.
+ c, err := minio.New(
+ os.Getenv(serverEndpoint),
+ os.Getenv(accessKey),
+ os.Getenv(secretKey),
+ mustParseBool(os.Getenv(enableHTTPS)),
+ )
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+
+ // Enable tracing, write to stderr.
+ // c.TraceOn(os.Stderr)
+
+ // Set user agent.
+ c.SetAppInfo("Minio-go-FunctionalTest", "0.1.0")
+
+ // Generate a new random bucket name.
+ bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test")
+
+ // Make a new bucket.
+ err = c.MakeBucket(bucketName, "us-east-1")
+ if err != nil {
+ logger().Fatal("Error:", err, bucketName)
+ }
+
+ // Generate data more than 32K
+ var buf = getDataBuffer("datafile-33-kB", rand.Intn(1<<20)+32*1024)
+
+ // Save the data
+ objectName := randString(60, rand.NewSource(time.Now().UnixNano()), "")
+ n, err := c.PutObject(bucketName, objectName, bytes.NewReader(buf), "binary/octet-stream")
+ if err != nil {
+ logger().Fatal("Error:", err, bucketName, objectName)
+ }
+
+ if n != int64(len(buf)) {
+ logger().Fatalf("Error: number of bytes does not match, want %v, got %v\n", len(buf), n)
+ }
+
+ // read the data back
+ r, err := c.GetObject(bucketName, objectName)
+ if err != nil {
+ logger().Fatal("Error:", err, bucketName, objectName)
+ }
+ offset := int64(2048)
+
+ // read directly
+ buf1 := make([]byte, 512)
+ buf2 := make([]byte, 512)
+ buf3 := make([]byte, 512)
+ buf4 := make([]byte, 512)
+
+ // Test readAt before stat is called.
+ m, err := r.ReadAt(buf1, offset)
+ if err != nil {
+ logger().Fatal("Error:", err, len(buf1), offset)
+ }
+ if m != len(buf1) {
+ logger().Fatalf("Error: ReadAt read shorter bytes before reaching EOF, want %v, got %v\n", m, len(buf1))
+ }
+ if !bytes.Equal(buf1, buf[offset:offset+512]) {
+ logger().Fatal("Error: Incorrect read between two ReadAt from same offset.")
+ }
+ offset += 512
+
+ st, err := r.Stat()
+ if err != nil {
+ logger().Fatal("Error:", err, bucketName, objectName)
+ }
+ if st.Size != int64(len(buf)) {
+ logger().Fatalf("Error: number of bytes in stat does not match, want %v, got %v\n",
+ len(buf), st.Size)
+ }
+
+ m, err = r.ReadAt(buf2, offset)
+ if err != nil {
+ logger().Fatal("Error:", err, st.Size, len(buf2), offset)
+ }
+ if m != len(buf2) {
+ logger().Fatalf("Error: ReadAt read shorter bytes before reaching EOF, want %v, got %v\n", m, len(buf2))
+ }
+ if !bytes.Equal(buf2, buf[offset:offset+512]) {
+ logger().Fatal("Error: Incorrect read between two ReadAt from same offset.")
+ }
+ offset += 512
+ m, err = r.ReadAt(buf3, offset)
+ if err != nil {
+ logger().Fatal("Error:", err, st.Size, len(buf3), offset)
+ }
+ if m != len(buf3) {
+ logger().Fatalf("Error: ReadAt read shorter bytes before reaching EOF, want %v, got %v\n", m, len(buf3))
+ }
+ if !bytes.Equal(buf3, buf[offset:offset+512]) {
+ logger().Fatal("Error: Incorrect read between two ReadAt from same offset.")
+ }
+ offset += 512
+ m, err = r.ReadAt(buf4, offset)
+ if err != nil {
+ logger().Fatal("Error:", err, st.Size, len(buf4), offset)
+ }
+ if m != len(buf4) {
+ logger().Fatalf("Error: ReadAt read shorter bytes before reaching EOF, want %v, got %v\n", m, len(buf4))
+ }
+ if !bytes.Equal(buf4, buf[offset:offset+512]) {
+ logger().Fatal("Error: Incorrect read between two ReadAt from same offset.")
+ }
+
+ buf5 := make([]byte, n)
+ // Read the whole object.
+ m, err = r.ReadAt(buf5, 0)
+ if err != nil {
+ if err != io.EOF {
+ logger().Fatal("Error:", err, len(buf5))
+ }
+ }
+ if m != len(buf5) {
+ logger().Fatalf("Error: ReadAt read shorter bytes before reaching EOF, want %v, got %v\n", m, len(buf5))
+ }
+ if !bytes.Equal(buf, buf5) {
+ logger().Fatal("Error: Incorrect data read in GetObject, than what was previously upoaded.")
+ }
+
+ buf6 := make([]byte, n+1)
+ // Read the whole object and beyond.
+ _, err = r.ReadAt(buf6, 0)
+ if err != nil {
+ if err != io.EOF {
+ logger().Fatal("Error:", err, len(buf6))
+ }
+ }
+ err = c.RemoveObject(bucketName, objectName)
+ if err != nil {
+ logger().Fatal("Error: ", err)
+ }
+ err = c.RemoveBucket(bucketName)
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+}
+
+// Test Presigned Post Policy
+func testPresignedPostPolicy() {
+ logger().Info()
+
+ // Seed random based on current time.
+ rand.Seed(time.Now().Unix())
+
+ // Instantiate new minio client object
+ c, err := minio.NewV4(
+ os.Getenv(serverEndpoint),
+ os.Getenv(accessKey),
+ os.Getenv(secretKey),
+ mustParseBool(os.Getenv(enableHTTPS)),
+ )
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+
+ // Enable tracing, write to stderr.
+ // c.TraceOn(os.Stderr)
+
+ // Set user agent.
+ c.SetAppInfo("Minio-go-FunctionalTest", "0.1.0")
+
+ // Generate a new random bucket name.
+ bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test")
+
+ // Make a new bucket in 'us-east-1' (source bucket).
+ err = c.MakeBucket(bucketName, "us-east-1")
+ if err != nil {
+ logger().Fatal("Error:", err, bucketName)
+ }
+
+ // Generate data more than 32K
+ var buf = getDataBuffer("datafile-33-kB", rand.Intn(1<<20)+32*1024)
+
+ // Save the data
+ objectName := randString(60, rand.NewSource(time.Now().UnixNano()), "")
+ n, err := c.PutObject(bucketName, objectName, bytes.NewReader(buf), "binary/octet-stream")
+ if err != nil {
+ logger().Fatal("Error:", err, bucketName, objectName)
+ }
+
+ if n != int64(len(buf)) {
+ logger().Fatalf("Error: number of bytes does not match want %v, got %v",
+ len(buf), n)
+ }
+
+ policy := minio.NewPostPolicy()
+
+ if err := policy.SetBucket(""); err == nil {
+ logger().Fatalf("Error: %s", err)
+ }
+ if err := policy.SetKey(""); err == nil {
+ logger().Fatalf("Error: %s", err)
+ }
+ if err := policy.SetKeyStartsWith(""); err == nil {
+ logger().Fatalf("Error: %s", err)
+ }
+ if err := policy.SetExpires(time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC)); err == nil {
+ logger().Fatalf("Error: %s", err)
+ }
+ if err := policy.SetContentType(""); err == nil {
+ logger().Fatalf("Error: %s", err)
+ }
+ if err := policy.SetContentLengthRange(1024*1024, 1024); err == nil {
+ logger().Fatalf("Error: %s", err)
+ }
+
+ policy.SetBucket(bucketName)
+ policy.SetKey(objectName)
+ policy.SetExpires(time.Now().UTC().AddDate(0, 0, 10)) // expires in 10 days
+ policy.SetContentType("image/png")
+ policy.SetContentLengthRange(1024, 1024*1024)
+
+ _, _, err = c.PresignedPostPolicy(policy)
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+
+ policy = minio.NewPostPolicy()
+
+ // Remove all objects and buckets
+ err = c.RemoveObject(bucketName, objectName)
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+
+ err = c.RemoveBucket(bucketName)
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+}
+
+// Tests copy object
+func testCopyObject() {
+ logger().Info()
+ // Seed random based on current time.
+ rand.Seed(time.Now().Unix())
+
+ // Instantiate new minio client object
+ c, err := minio.NewV4(
+ os.Getenv(serverEndpoint),
+ os.Getenv(accessKey),
+ os.Getenv(secretKey),
+ mustParseBool(os.Getenv(enableHTTPS)),
+ )
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+
+ // Enable tracing, write to stderr.
+ // c.TraceOn(os.Stderr)
+
+ // Set user agent.
+ c.SetAppInfo("Minio-go-FunctionalTest", "0.1.0")
+
+ // Generate a new random bucket name.
+ bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test")
+
+ // Make a new bucket in 'us-east-1' (source bucket).
+ err = c.MakeBucket(bucketName, "us-east-1")
+ if err != nil {
+ logger().Fatal("Error:", err, bucketName)
+ }
+
+ // Make a new bucket in 'us-east-1' (destination bucket).
+ err = c.MakeBucket(bucketName+"-copy", "us-east-1")
+ if err != nil {
+ logger().Fatal("Error:", err, bucketName+"-copy")
+ }
+
+ // Generate data more than 32K
+ buf := bytes.Repeat([]byte("5"), rand.Intn(1<<20)+32*1024)
+
+ // Save the data
+ objectName := randString(60, rand.NewSource(time.Now().UnixNano()), "")
+ n, err := c.PutObject(bucketName, objectName, bytes.NewReader(buf), "binary/octet-stream")
+ if err != nil {
+ logger().Fatal("Error:", err, bucketName, objectName)
+ }
+
+ if n != int64(len(buf)) {
+ logger().Fatalf("Error: number of bytes does not match want %v, got %v",
+ len(buf), n)
+ }
+
+ r, err := c.GetObject(bucketName, objectName)
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+ // Check the various fields of source object against destination object.
+ objInfo, err := r.Stat()
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+
+ // Copy Source
+ src := minio.NewSourceInfo(bucketName, objectName, nil)
+
+ // Set copy conditions.
+
+ // All invalid conditions first.
+ err = src.SetModifiedSinceCond(time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC))
+ if err == nil {
+ logger().Fatal("Error:", err)
+ }
+ err = src.SetUnmodifiedSinceCond(time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC))
+ if err == nil {
+ logger().Fatal("Error:", err)
+ }
+ err = src.SetMatchETagCond("")
+ if err == nil {
+ logger().Fatal("Error:", err)
+ }
+ err = src.SetMatchETagExceptCond("")
+ if err == nil {
+ logger().Fatal("Error:", err)
+ }
+
+ err = src.SetModifiedSinceCond(time.Date(2014, time.April, 0, 0, 0, 0, 0, time.UTC))
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+ err = src.SetMatchETagCond(objInfo.ETag)
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+
+ dst, err := minio.NewDestinationInfo(bucketName+"-copy", objectName+"-copy", nil, nil)
+ if err != nil {
+ logger().Fatal(err)
+ }
+
+ // Perform the Copy
+ err = c.CopyObject(dst, src)
+ if err != nil {
+ logger().Fatal("Error:", err, bucketName+"-copy", objectName+"-copy")
+ }
+
+ // Source object
+ reader, err := c.GetObject(bucketName, objectName)
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+ // Destination object
+ readerCopy, err := c.GetObject(bucketName+"-copy", objectName+"-copy")
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+ // Check the various fields of source object against destination object.
+ objInfo, err = reader.Stat()
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+ objInfoCopy, err := readerCopy.Stat()
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+ if objInfo.Size != objInfoCopy.Size {
+ logger().Fatalf("Error: number of bytes does not match, want %v, got %v\n",
+ objInfo.Size, objInfoCopy.Size)
+ }
+
+ // CopyObject again but with wrong conditions
+ src = minio.NewSourceInfo(bucketName, objectName, nil)
+ err = src.SetUnmodifiedSinceCond(time.Date(2014, time.April, 0, 0, 0, 0, 0, time.UTC))
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+ err = src.SetMatchETagExceptCond(objInfo.ETag)
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+
+ // Perform the Copy which should fail
+ err = c.CopyObject(dst, src)
+ if err == nil {
+ logger().Fatal("Error:", err, bucketName+"-copy", objectName+"-copy should fail")
+ }
+
+ // Remove all objects and buckets
+ err = c.RemoveObject(bucketName, objectName)
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+
+ err = c.RemoveObject(bucketName+"-copy", objectName+"-copy")
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+
+ err = c.RemoveBucket(bucketName)
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+
+ err = c.RemoveBucket(bucketName + "-copy")
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+}
+
+// TestEncryptionPutGet tests client side encryption
+func testEncryptionPutGet() {
+ logger().Info()
+
+ // Seed random based on current time.
+ rand.Seed(time.Now().Unix())
+
+ // Instantiate new minio client object.
+ c, err := minio.New(
+ os.Getenv(serverEndpoint),
+ os.Getenv(accessKey),
+ os.Getenv(secretKey),
+ mustParseBool(os.Getenv(enableHTTPS)),
+ )
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+
+ // Enable tracing, write to stderr.
+ // c.TraceOn(os.Stderr)
+
+ // Set user agent.
+ c.SetAppInfo("Minio-go-FunctionalTest", "0.1.0")
+
+ // Generate a new random bucket name.
+ bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test")
+
+ // Make a new bucket.
+ err = c.MakeBucket(bucketName, "us-east-1")
+ if err != nil {
+ logger().Fatal("Error:", err, bucketName)
+ }
+
+ // Generate a symmetric key
+ symKey := encrypt.NewSymmetricKey([]byte("my-secret-key-00"))
+
+ // Generate an assymmetric key from predefine public and private certificates
+ privateKey, err := hex.DecodeString(
+ "30820277020100300d06092a864886f70d0101010500048202613082025d" +
+ "0201000281810087b42ea73243a3576dc4c0b6fa245d339582dfdbddc20c" +
+ "bb8ab666385034d997210c54ba79275c51162a1221c3fb1a4c7c61131ca6" +
+ "5563b319d83474ef5e803fbfa7e52b889e1893b02586b724250de7ac6351" +
+ "cc0b7c638c980acec0a07020a78eed7eaa471eca4b92071394e061346c06" +
+ "15ccce2f465dee2080a89e43f29b5702030100010281801dd5770c3af8b3" +
+ "c85cd18cacad81a11bde1acfac3eac92b00866e142301fee565365aa9af4" +
+ "57baebf8bb7711054d071319a51dd6869aef3848ce477a0dc5f0dbc0c336" +
+ "5814b24c820491ae2bb3c707229a654427e03307fec683e6b27856688f08" +
+ "bdaa88054c5eeeb773793ff7543ee0fb0e2ad716856f2777f809ef7e6fa4" +
+ "41024100ca6b1edf89e8a8f93cce4b98c76c6990a09eb0d32ad9d3d04fbf" +
+ "0b026fa935c44f0a1c05dd96df192143b7bda8b110ec8ace28927181fd8c" +
+ "d2f17330b9b63535024100aba0260afb41489451baaeba423bee39bcbd1e" +
+ "f63dd44ee2d466d2453e683bf46d019a8baead3a2c7fca987988eb4d565e" +
+ "27d6be34605953f5034e4faeec9bdb0241009db2cb00b8be8c36710aff96" +
+ "6d77a6dec86419baca9d9e09a2b761ea69f7d82db2ae5b9aae4246599bb2" +
+ "d849684d5ab40e8802cfe4a2b358ad56f2b939561d2902404e0ead9ecafd" +
+ "bb33f22414fa13cbcc22a86bdf9c212ce1a01af894e3f76952f36d6c904c" +
+ "bd6a7e0de52550c9ddf31f1e8bfe5495f79e66a25fca5c20b3af5b870241" +
+ "0083456232aa58a8c45e5b110494599bda8dbe6a094683a0539ddd24e19d" +
+ "47684263bbe285ad953d725942d670b8f290d50c0bca3d1dc9688569f1d5" +
+ "9945cb5c7d")
+
+ if err != nil {
+ logger().Fatal(err)
+ }
+
+ publicKey, err := hex.DecodeString("30819f300d06092a864886f70d010101050003818d003081890281810087" +
+ "b42ea73243a3576dc4c0b6fa245d339582dfdbddc20cbb8ab666385034d9" +
+ "97210c54ba79275c51162a1221c3fb1a4c7c61131ca65563b319d83474ef" +
+ "5e803fbfa7e52b889e1893b02586b724250de7ac6351cc0b7c638c980ace" +
+ "c0a07020a78eed7eaa471eca4b92071394e061346c0615ccce2f465dee20" +
+ "80a89e43f29b570203010001")
+ if err != nil {
+ logger().Fatal(err)
+ }
+
+ // Generate an asymmetric key
+ asymKey, err := encrypt.NewAsymmetricKey(privateKey, publicKey)
+ if err != nil {
+ logger().Fatal(err)
+ }
+
+ // Object custom metadata
+ customContentType := "custom/contenttype"
+
+ testCases := []struct {
+ buf []byte
+ encKey encrypt.Key
+ }{
+ {encKey: symKey, buf: bytes.Repeat([]byte("F"), 0)},
+ {encKey: symKey, buf: bytes.Repeat([]byte("F"), 1)},
+ {encKey: symKey, buf: bytes.Repeat([]byte("F"), 15)},
+ {encKey: symKey, buf: bytes.Repeat([]byte("F"), 16)},
+ {encKey: symKey, buf: bytes.Repeat([]byte("F"), 17)},
+ {encKey: symKey, buf: bytes.Repeat([]byte("F"), 31)},
+ {encKey: symKey, buf: bytes.Repeat([]byte("F"), 32)},
+ {encKey: symKey, buf: bytes.Repeat([]byte("F"), 33)},
+ {encKey: symKey, buf: bytes.Repeat([]byte("F"), 1024)},
+ {encKey: symKey, buf: bytes.Repeat([]byte("F"), 1024*2)},
+ {encKey: symKey, buf: bytes.Repeat([]byte("F"), 1024*1024)},
+
+ {encKey: asymKey, buf: bytes.Repeat([]byte("F"), 0)},
+ {encKey: asymKey, buf: bytes.Repeat([]byte("F"), 1)},
+ {encKey: asymKey, buf: bytes.Repeat([]byte("F"), 16)},
+ {encKey: asymKey, buf: bytes.Repeat([]byte("F"), 32)},
+ {encKey: asymKey, buf: bytes.Repeat([]byte("F"), 1024)},
+ {encKey: asymKey, buf: bytes.Repeat([]byte("F"), 1024*1024)},
+ }
+
+ for i, testCase := range testCases {
+ // Generate a random object name
+ objectName := randString(60, rand.NewSource(time.Now().UnixNano()), "")
+
+ // Secured object
+ cbcMaterials, err := encrypt.NewCBCSecureMaterials(testCase.encKey)
+ if err != nil {
+ logger().Fatal(err)
+ }
+
+ // Put encrypted data
+ _, err = c.PutEncryptedObject(bucketName, objectName, bytes.NewReader(testCase.buf), cbcMaterials, map[string][]string{"Content-Type": {customContentType}}, nil)
+ if err != nil {
+ logger().Fatalf("Test %d, error: %v %v %v", i+1, err, bucketName, objectName)
+ }
+
+ // Read the data back
+ r, err := c.GetEncryptedObject(bucketName, objectName, cbcMaterials)
+ if err != nil {
+ logger().Fatalf("Test %d, error: %v %v %v", i+1, err, bucketName, objectName)
+ }
+ defer r.Close()
+
+ // Compare the sent object with the received one
+ recvBuffer := bytes.NewBuffer([]byte{})
+ if _, err = io.Copy(recvBuffer, r); err != nil {
+ logger().Fatalf("Test %d, error: %v", i+1, err)
+ }
+ if recvBuffer.Len() != len(testCase.buf) {
+ logger().Fatalf("Test %d, error: number of bytes of received object does not match, want %v, got %v\n",
+ i+1, len(testCase.buf), recvBuffer.Len())
+ }
+ if !bytes.Equal(testCase.buf, recvBuffer.Bytes()) {
+ logger().Fatalf("Test %d, error: Encrypted sent is not equal to decrypted, want `%x`, go `%x`", i+1, testCase.buf, recvBuffer.Bytes())
+ }
+
+ // Remove test object
+ err = c.RemoveObject(bucketName, objectName)
+ if err != nil {
+ logger().Fatalf("Test %d, error: %v", i+1, err)
+ }
+
+ }
+
+ // Remove test bucket
+ err = c.RemoveBucket(bucketName)
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+
+}
+
+func testBucketNotification() {
+ logger().Info()
+
+ if os.Getenv("NOTIFY_BUCKET") == "" ||
+ os.Getenv("NOTIFY_SERVICE") == "" ||
+ os.Getenv("NOTIFY_REGION") == "" ||
+ os.Getenv("NOTIFY_ACCOUNTID") == "" ||
+ os.Getenv("NOTIFY_RESOURCE") == "" {
+ logger().Info("skipping notification test if not configured")
+ return
+ }
+
+ // Seed random based on current time.
+ rand.Seed(time.Now().Unix())
+
+ c, err := minio.New(
+ os.Getenv(serverEndpoint),
+ os.Getenv(accessKey),
+ os.Getenv(secretKey),
+ mustParseBool(os.Getenv(enableHTTPS)),
+ )
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+
+ // Enable to debug
+ // c.TraceOn(os.Stderr)
+
+ // Set user agent.
+ c.SetAppInfo("Minio-go-FunctionalTest", "0.1.0")
+
+ bucketName := os.Getenv("NOTIFY_BUCKET")
+
+ topicArn := minio.NewArn("aws", os.Getenv("NOTIFY_SERVICE"), os.Getenv("NOTIFY_REGION"), os.Getenv("NOTIFY_ACCOUNTID"), os.Getenv("NOTIFY_RESOURCE"))
+ queueArn := minio.NewArn("aws", "dummy-service", "dummy-region", "dummy-accountid", "dummy-resource")
+
+ topicConfig := minio.NewNotificationConfig(topicArn)
+ topicConfig.AddEvents(minio.ObjectCreatedAll, minio.ObjectRemovedAll)
+ topicConfig.AddFilterSuffix("jpg")
+
+ queueConfig := minio.NewNotificationConfig(queueArn)
+ queueConfig.AddEvents(minio.ObjectCreatedAll)
+ queueConfig.AddFilterPrefix("photos/")
+
+ bNotification := minio.BucketNotification{}
+ bNotification.AddTopic(topicConfig)
+
+ // Add the same topicConfig again, should have no effect
+ // because it is duplicated
+ bNotification.AddTopic(topicConfig)
+ if len(bNotification.TopicConfigs) != 1 {
+ logger().Fatal("Error: duplicated entry added")
+ }
+
+ // Add and remove a queue config
+ bNotification.AddQueue(queueConfig)
+ bNotification.RemoveQueueByArn(queueArn)
+
+ err = c.SetBucketNotification(bucketName, bNotification)
+ if err != nil {
+ logger().Fatal("Error: ", err)
+ }
+
+ bNotification, err = c.GetBucketNotification(bucketName)
+ if err != nil {
+ logger().Fatal("Error: ", err)
+ }
+
+ if len(bNotification.TopicConfigs) != 1 {
+ logger().Fatal("Error: Topic config is empty")
+ }
+
+ if bNotification.TopicConfigs[0].Filter.S3Key.FilterRules[0].Value != "jpg" {
+ logger().Fatal("Error: cannot get the suffix")
+ }
+
+ err = c.RemoveAllBucketNotification(bucketName)
+ if err != nil {
+ logger().Fatal("Error: cannot delete bucket notification")
+ }
+}
+
+// Tests comprehensive list of all methods.
+func testFunctional() {
+ logger().Info()
+
+ // Seed random based on current time.
+ rand.Seed(time.Now().Unix())
+
+ c, err := minio.New(
+ os.Getenv(serverEndpoint),
+ os.Getenv(accessKey),
+ os.Getenv(secretKey),
+ mustParseBool(os.Getenv(enableHTTPS)),
+ )
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+
+ // Enable to debug
+ // c.TraceOn(os.Stderr)
+
+ // Set user agent.
+ c.SetAppInfo("Minio-go-FunctionalTest", "0.1.0")
+
+ // Generate a new random bucket name.
+ bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test")
+
+ // Make a new bucket.
+ err = c.MakeBucket(bucketName, "us-east-1")
+ if err != nil {
+ logger().Fatal("Error:", err, bucketName)
+ }
+
+ // Generate a random file name.
+ fileName := randString(60, rand.NewSource(time.Now().UnixNano()), "")
+ file, err := os.Create(fileName)
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+ for i := 0; i < 3; i++ {
+ buf := make([]byte, rand.Intn(1<<19))
+ _, err = file.Write(buf)
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+ }
+ file.Close()
+
+ // Verify if bucket exits and you have access.
+ var exists bool
+ exists, err = c.BucketExists(bucketName)
+ if err != nil {
+ logger().Fatal("Error:", err, bucketName)
+ }
+ if !exists {
+ logger().Fatal("Error: could not find ", bucketName)
+ }
+
+ // Asserting the default bucket policy.
+ policyAccess, err := c.GetBucketPolicy(bucketName, "")
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+ if policyAccess != "none" {
+ logger().Fatalf("Default bucket policy incorrect")
+ }
+ // Set the bucket policy to 'public readonly'.
+ err = c.SetBucketPolicy(bucketName, "", policy.BucketPolicyReadOnly)
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+ // should return policy `readonly`.
+ policyAccess, err = c.GetBucketPolicy(bucketName, "")
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+ if policyAccess != "readonly" {
+ logger().Fatalf("Expected bucket policy to be readonly")
+ }
+
+ // Make the bucket 'public writeonly'.
+ err = c.SetBucketPolicy(bucketName, "", policy.BucketPolicyWriteOnly)
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+ // should return policy `writeonly`.
+ policyAccess, err = c.GetBucketPolicy(bucketName, "")
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+ if policyAccess != "writeonly" {
+ logger().Fatalf("Expected bucket policy to be writeonly")
+ }
+ // Make the bucket 'public read/write'.
+ err = c.SetBucketPolicy(bucketName, "", policy.BucketPolicyReadWrite)
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+ // should return policy `readwrite`.
+ policyAccess, err = c.GetBucketPolicy(bucketName, "")
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+ if policyAccess != "readwrite" {
+ logger().Fatalf("Expected bucket policy to be readwrite")
+ }
+ // List all buckets.
+ buckets, err := c.ListBuckets()
+ if len(buckets) == 0 {
+ logger().Fatal("Error: list buckets cannot be empty", buckets)
+ }
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+
+ // Verify if previously created bucket is listed in list buckets.
+ bucketFound := false
+ for _, bucket := range buckets {
+ if bucket.Name == bucketName {
+ bucketFound = true
+ }
+ }
+
+ // If bucket not found error out.
+ if !bucketFound {
+ logger().Fatal("Error: bucket ", bucketName, "not found")
+ }
+
+ objectName := bucketName + "unique"
+
+ // Generate data
+ buf := bytes.Repeat([]byte("f"), 1<<19)
+
+ n, err := c.PutObject(bucketName, objectName, bytes.NewReader(buf), "")
+ if err != nil {
+ logger().Fatal("Error: ", err)
+ }
+ if n != int64(len(buf)) {
+ logger().Fatal("Error: bad length ", n, len(buf))
+ }
+
+ n, err = c.PutObject(bucketName, objectName+"-nolength", bytes.NewReader(buf), "binary/octet-stream")
+ if err != nil {
+ logger().Fatal("Error:", err, bucketName, objectName+"-nolength")
+ }
+
+ if n != int64(len(buf)) {
+ logger().Fatalf("Error: number of bytes does not match, want %v, got %v\n", len(buf), n)
+ }
+
+ // Instantiate a done channel to close all listing.
+ doneCh := make(chan struct{})
+ defer close(doneCh)
+
+ objFound := false
+ isRecursive := true // Recursive is true.
+ for obj := range c.ListObjects(bucketName, objectName, isRecursive, doneCh) {
+ if obj.Key == objectName {
+ objFound = true
+ break
+ }
+ }
+ if !objFound {
+ logger().Fatal("Error: object " + objectName + " not found.")
+ }
+
+ objFound = false
+ isRecursive = true // Recursive is true.
+ for obj := range c.ListObjectsV2(bucketName, objectName, isRecursive, doneCh) {
+ if obj.Key == objectName {
+ objFound = true
+ break
+ }
+ }
+ if !objFound {
+ logger().Fatal("Error: object " + objectName + " not found.")
+ }
+
+ incompObjNotFound := true
+ for objIncompl := range c.ListIncompleteUploads(bucketName, objectName, isRecursive, doneCh) {
+ if objIncompl.Key != "" {
+ incompObjNotFound = false
+ break
+ }
+ }
+ if !incompObjNotFound {
+ logger().Fatal("Error: unexpected dangling incomplete upload found.")
+ }
+
+ newReader, err := c.GetObject(bucketName, objectName)
+ if err != nil {
+ logger().Fatal("Error: ", err)
+ }
+
+ newReadBytes, err := ioutil.ReadAll(newReader)
+ if err != nil {
+ logger().Fatal("Error: ", err)
+ }
+
+ if !bytes.Equal(newReadBytes, buf) {
+ logger().Fatal("Error: bytes mismatch.")
+ }
+
+ err = c.FGetObject(bucketName, objectName, fileName+"-f")
+ if err != nil {
+ logger().Fatal("Error: ", err)
+ }
+
+ // Generate presigned GET object url.
+ presignedGetURL, err := c.PresignedGetObject(bucketName, objectName, 3600*time.Second, nil)
+ if err != nil {
+ logger().Fatal("Error: ", err)
+ }
+
+ // Verify if presigned url works.
+ resp, err := http.Get(presignedGetURL.String())
+ if err != nil {
+ logger().Fatal("Error: ", err)
+ }
+ if resp.StatusCode != http.StatusOK {
+ logger().Fatal("Error: ", resp.Status)
+ }
+ newPresignedBytes, err := ioutil.ReadAll(resp.Body)
+ if err != nil {
+ logger().Fatal("Error: ", err)
+ }
+ if !bytes.Equal(newPresignedBytes, buf) {
+ logger().Fatal("Error: bytes mismatch.")
+ }
+
+ // Set request parameters.
+ reqParams := make(url.Values)
+ reqParams.Set("response-content-disposition", "attachment; filename=\"test.txt\"")
+ presignedGetURL, err = c.PresignedGetObject(bucketName, objectName, 3600*time.Second, reqParams)
+ if err != nil {
+ logger().Fatal("Error: ", err)
+ }
+ // Verify if presigned url works.
+ resp, err = http.Get(presignedGetURL.String())
+ if err != nil {
+ logger().Fatal("Error: ", err)
+ }
+ if resp.StatusCode != http.StatusOK {
+ logger().Fatal("Error: ", resp.Status)
+ }
+ newPresignedBytes, err = ioutil.ReadAll(resp.Body)
+ if err != nil {
+ logger().Fatal("Error: ", err)
+ }
+ if !bytes.Equal(newPresignedBytes, buf) {
+ logger().Fatal("Error: bytes mismatch for presigned GET URL.")
+ }
+ if resp.Header.Get("Content-Disposition") != "attachment; filename=\"test.txt\"" {
+ logger().Fatalf("Error: wrong Content-Disposition received %s", resp.Header.Get("Content-Disposition"))
+ }
+
+ presignedPutURL, err := c.PresignedPutObject(bucketName, objectName+"-presigned", 3600*time.Second)
+ if err != nil {
+ logger().Fatal("Error: ", err)
+ }
+
+ buf = bytes.Repeat([]byte("g"), 1<<19)
+
+ req, err := http.NewRequest("PUT", presignedPutURL.String(), bytes.NewReader(buf))
+ if err != nil {
+ logger().Fatal("Error: ", err)
+ }
+ httpClient := &http.Client{
+ // Setting a sensible time out of 30secs to wait for response
+ // headers. Request is pro-actively cancelled after 30secs
+ // with no response.
+ Timeout: 30 * time.Second,
+ Transport: http.DefaultTransport,
+ }
+ resp, err = httpClient.Do(req)
+ if err != nil {
+ logger().Fatal("Error: ", err)
+ }
+
+ newReader, err = c.GetObject(bucketName, objectName+"-presigned")
+ if err != nil {
+ logger().Fatal("Error: ", err)
+ }
+
+ newReadBytes, err = ioutil.ReadAll(newReader)
+ if err != nil {
+ logger().Fatal("Error: ", err)
+ }
+
+ if !bytes.Equal(newReadBytes, buf) {
+ logger().Fatal("Error: bytes mismatch.")
+ }
+
+ err = c.RemoveObject(bucketName, objectName)
+ if err != nil {
+ logger().Fatal("Error: ", err)
+ }
+ err = c.RemoveObject(bucketName, objectName+"-f")
+ if err != nil {
+ logger().Fatal("Error: ", err)
+ }
+ err = c.RemoveObject(bucketName, objectName+"-nolength")
+ if err != nil {
+ logger().Fatal("Error: ", err)
+ }
+ err = c.RemoveObject(bucketName, objectName+"-presigned")
+ if err != nil {
+ logger().Fatal("Error: ", err)
+ }
+ err = c.RemoveBucket(bucketName)
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+ err = c.RemoveBucket(bucketName)
+ if err == nil {
+ logger().Fatal("Error:")
+ }
+ if err.Error() != "The specified bucket does not exist" {
+ logger().Fatal("Error: ", err)
+ }
+ if err = os.Remove(fileName); err != nil {
+ logger().Fatal("Error: ", err)
+ }
+ if err = os.Remove(fileName + "-f"); err != nil {
+ logger().Fatal("Error: ", err)
+ }
+}
+
+// Test for validating GetObject Reader* methods functioning when the
+// object is modified in the object store.
+func testGetObjectObjectModified() {
+ logger().Info()
+
+ // Instantiate new minio client object.
+ c, err := minio.NewV4(
+ os.Getenv(serverEndpoint),
+ os.Getenv(accessKey),
+ os.Getenv(secretKey),
+ mustParseBool(os.Getenv(enableHTTPS)),
+ )
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+
+ // Enable tracing, write to stderr.
+ // c.TraceOn(os.Stderr)
+
+ // Set user agent.
+ c.SetAppInfo("Minio-go-FunctionalTest", "0.1.0")
+
+ // Make a new bucket.
+ bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test")
+ err = c.MakeBucket(bucketName, "us-east-1")
+ if err != nil {
+ logger().Fatal("Error:", err, bucketName)
+ }
+ defer c.RemoveBucket(bucketName)
+
+ // Upload an object.
+ objectName := "myobject"
+ content := "helloworld"
+ _, err = c.PutObject(bucketName, objectName, strings.NewReader(content), "application/text")
+ if err != nil {
+ logger().Fatalf("Failed to upload %s/%s: %v", bucketName, objectName, err)
+ }
+
+ defer c.RemoveObject(bucketName, objectName)
+
+ reader, err := c.GetObject(bucketName, objectName)
+ if err != nil {
+ logger().Fatalf("Failed to get object %s/%s: %v", bucketName, objectName, err)
+ }
+ defer reader.Close()
+
+ // Read a few bytes of the object.
+ b := make([]byte, 5)
+ n, err := reader.ReadAt(b, 0)
+ if err != nil {
+ logger().Fatalf("Failed to read object %s/%s at an offset: %v", bucketName, objectName, err)
+ }
+
+ // Upload different contents to the same object while object is being read.
+ newContent := "goodbyeworld"
+ _, err = c.PutObject(bucketName, objectName, strings.NewReader(newContent), "application/text")
+ if err != nil {
+ logger().Fatalf("Failed to upload %s/%s: %v", bucketName, objectName, err)
+ }
+
+ // Confirm that a Stat() call in between doesn't change the Object's cached etag.
+ _, err = reader.Stat()
+ if err.Error() != "At least one of the pre-conditions you specified did not hold" {
+ log.Error(fmt.Errorf("Expected Stat to fail with error %s but received %s", "At least one of the pre-conditions you specified did not hold", err.Error()))
+ }
+
+ // Read again only to find object contents have been modified since last read.
+ _, err = reader.ReadAt(b, int64(n))
+ if err.Error() != "At least one of the pre-conditions you specified did not hold" {
+ log.Error(fmt.Errorf("Expected ReadAt to fail with error %s but received %s", "At least one of the pre-conditions you specified did not hold", err.Error()))
+ }
+}
+
+// Test validates putObject to upload a file seeked at a given offset.
+func testPutObjectUploadSeekedObject() {
+ logger().Info()
+
+ // Instantiate new minio client object.
+ c, err := minio.NewV4(
+ os.Getenv(serverEndpoint),
+ os.Getenv(accessKey),
+ os.Getenv(secretKey),
+ mustParseBool(os.Getenv(enableHTTPS)),
+ )
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+
+ // Enable tracing, write to stderr.
+ // c.TraceOn(os.Stderr)
+
+ // Set user agent.
+ c.SetAppInfo("Minio-go-FunctionalTest", "0.1.0")
+
+ // Make a new bucket.
+ bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test")
+ err = c.MakeBucket(bucketName, "us-east-1")
+ if err != nil {
+ logger().Fatal("Error:", err, bucketName)
+ }
+ defer c.RemoveBucket(bucketName)
+
+ tempfile, err := ioutil.TempFile("", "minio-go-upload-test-")
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+
+ var data []byte
+ if fileName := getFilePath("datafile-100-kB"); fileName != "" {
+ data, _ = ioutil.ReadFile(fileName)
+ } else {
+ // Generate data more than 32K
+ data = bytes.Repeat([]byte("1"), 120000)
+ }
+ var length = len(data)
+ if _, err = tempfile.Write(data); err != nil {
+ logger().Fatal("Error:", err)
+ }
+
+ objectName := fmt.Sprintf("test-file-%v", rand.Uint32())
+
+ offset := length / 2
+ if _, err := tempfile.Seek(int64(offset), 0); err != nil {
+ logger().Fatal("Error:", err)
+ }
+
+ n, err := c.PutObject(bucketName, objectName, tempfile, "binary/octet-stream")
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+ if n != int64(length-offset) {
+ logger().Fatalf("Invalid length returned, want %v, got %v", int64(length-offset), n)
+ }
+ tempfile.Close()
+ if err = os.Remove(tempfile.Name()); err != nil {
+ logger().Fatal("Error:", err)
+ }
+
+ length = int(n)
+
+ obj, err := c.GetObject(bucketName, objectName)
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+
+ n, err = obj.Seek(int64(offset), 0)
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+ if n != int64(offset) {
+ logger().Fatalf("Invalid offset returned, want %v, got %v", int64(offset), n)
+ }
+
+ n, err = c.PutObject(bucketName, objectName+"getobject", obj, "binary/octet-stream")
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+ if n != int64(length-offset) {
+ logger().Fatalf("Invalid length returned, want %v, got %v", int64(length-offset), n)
+ }
+
+ if err = c.RemoveObject(bucketName, objectName); err != nil {
+ logger().Fatal("Error:", err)
+ }
+
+ if err = c.RemoveObject(bucketName, objectName+"getobject"); err != nil {
+ logger().Fatal("Error:", err)
+ }
+}
+
+// Tests bucket re-create errors.
+func testMakeBucketErrorV2() {
+ logger().Info()
+ if os.Getenv(serverEndpoint) != "s3.amazonaws.com" {
+ logger().Info("skipping region functional tests for non s3 runs")
+ return
+ }
+
+ // Seed random based on current time.
+ rand.Seed(time.Now().Unix())
+
+ // Instantiate new minio client object.
+ c, err := minio.NewV2(
+ os.Getenv(serverEndpoint),
+ os.Getenv(accessKey),
+ os.Getenv(secretKey),
+ mustParseBool(os.Getenv(enableHTTPS)),
+ )
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+
+ // Enable tracing, write to stderr.
+ // c.TraceOn(os.Stderr)
+
+ // Set user agent.
+ c.SetAppInfo("Minio-go-FunctionalTest", "0.1.0")
+
+ // Generate a new random bucket name.
+ bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test")
+
+ // Make a new bucket in 'eu-west-1'.
+ if err = c.MakeBucket(bucketName, "eu-west-1"); err != nil {
+ logger().Fatal("Error:", err, bucketName)
+ }
+ if err = c.MakeBucket(bucketName, "eu-west-1"); err == nil {
+ logger().Fatal("Error: make bucket should should fail for", bucketName)
+ }
+ // Verify valid error response from server.
+ if minio.ToErrorResponse(err).Code != "BucketAlreadyExists" &&
+ minio.ToErrorResponse(err).Code != "BucketAlreadyOwnedByYou" {
+ logger().Fatal("Error: Invalid error returned by server", err)
+ }
+ if err = c.RemoveBucket(bucketName); err != nil {
+ logger().Fatal("Error:", err, bucketName)
+ }
+}
+
+// Test get object reader to not throw error on being closed twice.
+func testGetObjectClosedTwiceV2() {
+ logger().Info()
+
+ // Seed random based on current time.
+ rand.Seed(time.Now().Unix())
+
+ // Instantiate new minio client object.
+ c, err := minio.NewV2(
+ os.Getenv(serverEndpoint),
+ os.Getenv(accessKey),
+ os.Getenv(secretKey),
+ mustParseBool(os.Getenv(enableHTTPS)),
+ )
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+
+ // Enable tracing, write to stderr.
+ // c.TraceOn(os.Stderr)
+
+ // Set user agent.
+ c.SetAppInfo("Minio-go-FunctionalTest", "0.1.0")
+
+ // Generate a new random bucket name.
+ bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test")
+
+ // Make a new bucket.
+ err = c.MakeBucket(bucketName, "us-east-1")
+ if err != nil {
+ logger().Fatal("Error:", err, bucketName)
+ }
+
+ // Generate data more than 32K.
+ var buf = getDataBuffer("datafile-33-kB", rand.Intn(1<<20)+32*1024)
+
+ // Save the data
+ objectName := randString(60, rand.NewSource(time.Now().UnixNano()), "")
+ n, err := c.PutObject(bucketName, objectName, bytes.NewReader(buf), "binary/octet-stream")
+ if err != nil {
+ logger().Fatal("Error:", err, bucketName, objectName)
+ }
+
+ if n != int64(len(buf)) {
+ logger().Fatalf("Error: number of bytes does not match, want %v, got %v\n", len(buf), n)
+ }
+
+ // Read the data back
+ r, err := c.GetObject(bucketName, objectName)
+ if err != nil {
+ logger().Fatal("Error:", err, bucketName, objectName)
+ }
+
+ st, err := r.Stat()
+ if err != nil {
+ logger().Fatal("Error:", err, bucketName, objectName)
+ }
+ if st.Size != int64(len(buf)) {
+ logger().Fatalf("Error: number of bytes in stat does not match, want %v, got %v\n",
+ len(buf), st.Size)
+ }
+ if err := r.Close(); err != nil {
+ logger().Fatal("Error:", err)
+ }
+ if err := r.Close(); err == nil {
+ logger().Fatal("Error: object is already closed, should return error")
+ }
+
+ err = c.RemoveObject(bucketName, objectName)
+ if err != nil {
+ logger().Fatal("Error: ", err)
+ }
+ err = c.RemoveBucket(bucketName)
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+}
+
+// Tests removing partially uploaded objects.
+func testRemovePartiallyUploadedV2() {
+ logger().Info()
+
+ // Seed random based on current time.
+ rand.Seed(time.Now().Unix())
+
+ // Instantiate new minio client object.
+ c, err := minio.NewV2(
+ os.Getenv(serverEndpoint),
+ os.Getenv(accessKey),
+ os.Getenv(secretKey),
+ mustParseBool(os.Getenv(enableHTTPS)),
+ )
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+
+ // Set user agent.
+ c.SetAppInfo("Minio-go-FunctionalTest", "0.1.0")
+
+ // Enable tracing, write to stdout.
+ // c.TraceOn(os.Stderr)
+
+ // Generate a new random bucket name.
+ bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test")
+
+ // make a new bucket.
+ err = c.MakeBucket(bucketName, "us-east-1")
+ if err != nil {
+ logger().Fatal("Error:", err, bucketName)
+ }
+
+ r := bytes.NewReader(bytes.Repeat([]byte("a"), 128*1024))
+
+ reader, writer := io.Pipe()
+ go func() {
+ i := 0
+ for i < 25 {
+ _, cerr := io.CopyN(writer, r, 128*1024)
+ if cerr != nil {
+ logger().Fatal("Error:", cerr, bucketName)
+ }
+ i++
+ r.Seek(0, 0)
+ }
+ writer.CloseWithError(errors.New("proactively closed to be verified later"))
+ }()
+
+ objectName := bucketName + "-resumable"
+ _, err = c.PutObject(bucketName, objectName, reader, "application/octet-stream")
+ if err == nil {
+ logger().Fatal("Error: PutObject should fail.")
+ }
+ if err.Error() != "proactively closed to be verified later" {
+ logger().Fatal("Error:", err)
+ }
+ err = c.RemoveIncompleteUpload(bucketName, objectName)
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+ err = c.RemoveBucket(bucketName)
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+}
+
+// Tests FPutObject hidden contentType setting
+func testFPutObjectV2() {
+ logger().Info()
+
+ // Seed random based on current time.
+ rand.Seed(time.Now().Unix())
+
+ // Instantiate new minio client object.
+ c, err := minio.NewV2(
+ os.Getenv(serverEndpoint),
+ os.Getenv(accessKey),
+ os.Getenv(secretKey),
+ mustParseBool(os.Getenv(enableHTTPS)),
+ )
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+
+ // Enable tracing, write to stderr.
+ // c.TraceOn(os.Stderr)
+
+ // Set user agent.
+ c.SetAppInfo("Minio-go-FunctionalTest", "0.1.0")
+
+ // Generate a new random bucket name.
+ bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test")
+
+ // Make a new bucket.
+ err = c.MakeBucket(bucketName, "us-east-1")
+ if err != nil {
+ logger().Fatal("Error:", err, bucketName)
+ }
+
+ // Make a temp file with 11*1024*1024 bytes of data.
+ file, err := ioutil.TempFile(os.TempDir(), "FPutObjectTest")
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+
+ r := bytes.NewReader(bytes.Repeat([]byte("b"), 11*1024*1024))
+ n, err := io.CopyN(file, r, 11*1024*1024)
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+ if n != int64(11*1024*1024) {
+ logger().Fatalf("Error: number of bytes does not match, want %v, got %v\n", 11*1024*1024, n)
+ }
+
+ // Close the file pro-actively for windows.
+ err = file.Close()
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+
+ // Set base object name
+ objectName := bucketName + "FPutObject"
+
+ // Perform standard FPutObject with contentType provided (Expecting application/octet-stream)
+ n, err = c.FPutObject(bucketName, objectName+"-standard", file.Name(), "application/octet-stream")
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+ if n != int64(11*1024*1024) {
+ logger().Fatalf("Error: number of bytes does not match, want %v, got %v\n", 11*1024*1024, n)
+ }
+
+ // Perform FPutObject with no contentType provided (Expecting application/octet-stream)
+ n, err = c.FPutObject(bucketName, objectName+"-Octet", file.Name(), "")
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+ if n != int64(11*1024*1024) {
+ logger().Fatalf("Error: number of bytes does not match, want %v, got %v\n", 11*1024*1024, n)
+ }
+
+ // Add extension to temp file name
+ fileName := file.Name()
+ err = os.Rename(file.Name(), fileName+".gtar")
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+
+ // Perform FPutObject with no contentType provided (Expecting application/x-gtar)
+ n, err = c.FPutObject(bucketName, objectName+"-GTar", fileName+".gtar", "")
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+ if n != int64(11*1024*1024) {
+ logger().Fatalf("Error: number of bytes does not match, want %v, got %v\n", 11*1024*1024, n)
+ }
+
+ // Check headers
+ rStandard, err := c.StatObject(bucketName, objectName+"-standard")
+ if err != nil {
+ logger().Fatal("Error:", err, bucketName, objectName+"-standard")
+ }
+ if rStandard.ContentType != "application/octet-stream" {
+ logger().Fatalf("Error: Content-Type headers mismatched, want %v, got %v\n",
+ "application/octet-stream", rStandard.ContentType)
+ }
+
+ rOctet, err := c.StatObject(bucketName, objectName+"-Octet")
+ if err != nil {
+ logger().Fatal("Error:", err, bucketName, objectName+"-Octet")
+ }
+ if rOctet.ContentType != "application/octet-stream" {
+ logger().Fatalf("Error: Content-Type headers mismatched, want %v, got %v\n",
+ "application/octet-stream", rStandard.ContentType)
+ }
+
+ rGTar, err := c.StatObject(bucketName, objectName+"-GTar")
+ if err != nil {
+ logger().Fatal("Error:", err, bucketName, objectName+"-GTar")
+ }
+ if rGTar.ContentType != "application/x-gtar" {
+ logger().Fatalf("Error: Content-Type headers mismatched, want %v, got %v\n",
+ "application/x-gtar", rStandard.ContentType)
+ }
+
+ // Remove all objects and bucket and temp file
+ err = c.RemoveObject(bucketName, objectName+"-standard")
+ if err != nil {
+ logger().Fatal("Error: ", err)
+ }
+
+ err = c.RemoveObject(bucketName, objectName+"-Octet")
+ if err != nil {
+ logger().Fatal("Error: ", err)
+ }
+
+ err = c.RemoveObject(bucketName, objectName+"-GTar")
+ if err != nil {
+ logger().Fatal("Error: ", err)
+ }
+
+ err = c.RemoveBucket(bucketName)
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+
+ err = os.Remove(fileName + ".gtar")
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+
+}
+
+// Tests various bucket supported formats.
+func testMakeBucketRegionsV2() {
+ logger().Info()
+ if os.Getenv(serverEndpoint) != "s3.amazonaws.com" {
+ logger().Info("skipping region functional tests for non s3 runs")
+ return
+ }
+
+ // Seed random based on current time.
+ rand.Seed(time.Now().Unix())
+
+ // Instantiate new minio client object.
+ c, err := minio.NewV2(
+ os.Getenv(serverEndpoint),
+ os.Getenv(accessKey),
+ os.Getenv(secretKey),
+ mustParseBool(os.Getenv(enableHTTPS)),
+ )
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+
+ // Enable tracing, write to stderr.
+ // c.TraceOn(os.Stderr)
+
+ // Set user agent.
+ c.SetAppInfo("Minio-go-FunctionalTest", "0.1.0")
+
+ // Generate a new random bucket name.
+ bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test")
+
+ // Make a new bucket in 'eu-central-1'.
+ if err = c.MakeBucket(bucketName, "eu-west-1"); err != nil {
+ logger().Fatal("Error:", err, bucketName)
+ }
+
+ if err = c.RemoveBucket(bucketName); err != nil {
+ logger().Fatal("Error:", err, bucketName)
+ }
+
+ // Make a new bucket with '.' in its name, in 'us-west-2'. This
+ // request is internally staged into a path style instead of
+ // virtual host style.
+ if err = c.MakeBucket(bucketName+".withperiod", "us-west-2"); err != nil {
+ logger().Fatal("Error:", err, bucketName+".withperiod")
+ }
+
+ // Remove the newly created bucket.
+ if err = c.RemoveBucket(bucketName + ".withperiod"); err != nil {
+ logger().Fatal("Error:", err, bucketName+".withperiod")
+ }
+}
+
+// Tests get object ReaderSeeker interface methods.
+func testGetObjectReadSeekFunctionalV2() {
+ logger().Info()
+ // Seed random based on current time.
+ rand.Seed(time.Now().Unix())
+
+ // Instantiate new minio client object.
+ c, err := minio.NewV2(
+ os.Getenv(serverEndpoint),
+ os.Getenv(accessKey),
+ os.Getenv(secretKey),
+ mustParseBool(os.Getenv(enableHTTPS)),
+ )
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+
+ // Enable tracing, write to stderr.
+ // c.TraceOn(os.Stderr)
+
+ // Set user agent.
+ c.SetAppInfo("Minio-go-FunctionalTest", "0.1.0")
+
+ // Generate a new random bucket name.
+ bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test")
+
+ // Make a new bucket.
+ err = c.MakeBucket(bucketName, "us-east-1")
+ if err != nil {
+ logger().Fatal("Error:", err, bucketName)
+ }
+
+ // Generate data more than 32K.
+ var buf = getDataBuffer("datafile-33-kB", rand.Intn(1<<20)+32*1024)
+
+ // Save the data.
+ objectName := randString(60, rand.NewSource(time.Now().UnixNano()), "")
+ n, err := c.PutObject(bucketName, objectName, bytes.NewReader(buf), "binary/octet-stream")
+ if err != nil {
+ logger().Fatal("Error:", err, bucketName, objectName)
+ }
+
+ if n != int64(len(buf)) {
+ logger().Fatalf("Error: number of bytes does not match, want %v, got %v\n", len(buf), n)
+ }
+
+ // Read the data back
+ r, err := c.GetObject(bucketName, objectName)
+ if err != nil {
+ logger().Fatal("Error:", err, bucketName, objectName)
+ }
+
+ st, err := r.Stat()
+ if err != nil {
+ logger().Fatal("Error:", err, bucketName, objectName)
+ }
+ if st.Size != int64(len(buf)) {
+ logger().Fatalf("Error: number of bytes in stat does not match, want %v, got %v\n",
+ len(buf), st.Size)
+ }
+
+ offset := int64(2048)
+ n, err = r.Seek(offset, 0)
+ if err != nil {
+ logger().Fatal("Error:", err, offset)
+ }
+ if n != offset {
+ logger().Fatalf("Error: number of bytes seeked does not match, want %v, got %v\n",
+ offset, n)
+ }
+ n, err = r.Seek(0, 1)
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+ if n != offset {
+ logger().Fatalf("Error: number of current seek does not match, want %v, got %v\n",
+ offset, n)
+ }
+ _, err = r.Seek(offset, 2)
+ if err == nil {
+ logger().Fatal("Error: seek on positive offset for whence '2' should error out")
+ }
+ n, err = r.Seek(-offset, 2)
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+ if n != st.Size-offset {
+ logger().Fatalf("Error: number of bytes seeked back does not match, want %d, got %v\n", st.Size-offset, n)
+ }
+
+ var buffer1 bytes.Buffer
+ if _, err = io.CopyN(&buffer1, r, st.Size); err != nil {
+ if err != io.EOF {
+ logger().Fatal("Error:", err)
+ }
+ }
+ if !bytes.Equal(buf[len(buf)-int(offset):], buffer1.Bytes()) {
+ logger().Fatal("Error: Incorrect read bytes v/s original buffer.")
+ }
+
+ // Seek again and read again.
+ n, err = r.Seek(offset-1, 0)
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+ if n != (offset - 1) {
+ logger().Fatalf("Error: number of bytes seeked back does not match, want %v, got %v\n", offset-1, n)
+ }
+
+ var buffer2 bytes.Buffer
+ if _, err = io.CopyN(&buffer2, r, st.Size); err != nil {
+ if err != io.EOF {
+ logger().Fatal("Error:", err)
+ }
+ }
+ // Verify now lesser bytes.
+ if !bytes.Equal(buf[2047:], buffer2.Bytes()) {
+ logger().Fatal("Error: Incorrect read bytes v/s original buffer.")
+ }
+
+ err = c.RemoveObject(bucketName, objectName)
+ if err != nil {
+ logger().Fatal("Error: ", err)
+ }
+ err = c.RemoveBucket(bucketName)
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+}
+
+// Tests get object ReaderAt interface methods.
+func testGetObjectReadAtFunctionalV2() {
+ logger().Info()
+ // Seed random based on current time.
+ rand.Seed(time.Now().Unix())
+
+ // Instantiate new minio client object.
+ c, err := minio.NewV2(
+ os.Getenv(serverEndpoint),
+ os.Getenv(accessKey),
+ os.Getenv(secretKey),
+ mustParseBool(os.Getenv(enableHTTPS)),
+ )
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+
+ // Enable tracing, write to stderr.
+ // c.TraceOn(os.Stderr)
+
+ // Set user agent.
+ c.SetAppInfo("Minio-go-FunctionalTest", "0.1.0")
+
+ // Generate a new random bucket name.
+ bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test")
+
+ // Make a new bucket.
+ err = c.MakeBucket(bucketName, "us-east-1")
+ if err != nil {
+ logger().Fatal("Error:", err, bucketName)
+ }
+
+ // Generate data more than 32K
+ var buf = getDataBuffer("datafile-33-kB", rand.Intn(1<<20)+32*1024)
+
+ // Save the data
+ objectName := randString(60, rand.NewSource(time.Now().UnixNano()), "")
+ n, err := c.PutObject(bucketName, objectName, bytes.NewReader(buf), "binary/octet-stream")
+ if err != nil {
+ logger().Fatal("Error:", err, bucketName, objectName)
+ }
+
+ if n != int64(len(buf)) {
+ logger().Fatalf("Error: number of bytes does not match, want %v, got %v\n", len(buf), n)
+ }
+
+ // Read the data back
+ r, err := c.GetObject(bucketName, objectName)
+ if err != nil {
+ logger().Fatal("Error:", err, bucketName, objectName)
+ }
+
+ st, err := r.Stat()
+ if err != nil {
+ logger().Fatal("Error:", err, bucketName, objectName)
+ }
+ if st.Size != int64(len(buf)) {
+ logger().Fatalf("Error: number of bytes in stat does not match, want %v, got %v\n",
+ len(buf), st.Size)
+ }
+
+ offset := int64(2048)
+
+ // Read directly
+ buf2 := make([]byte, 512)
+ buf3 := make([]byte, 512)
+ buf4 := make([]byte, 512)
+
+ m, err := r.ReadAt(buf2, offset)
+ if err != nil {
+ logger().Fatal("Error:", err, st.Size, len(buf2), offset)
+ }
+ if m != len(buf2) {
+ logger().Fatalf("Error: ReadAt read shorter bytes before reaching EOF, want %v, got %v\n", m, len(buf2))
+ }
+ if !bytes.Equal(buf2, buf[offset:offset+512]) {
+ logger().Fatal("Error: Incorrect read between two ReadAt from same offset.")
+ }
+ offset += 512
+ m, err = r.ReadAt(buf3, offset)
+ if err != nil {
+ logger().Fatal("Error:", err, st.Size, len(buf3), offset)
+ }
+ if m != len(buf3) {
+ logger().Fatalf("Error: ReadAt read shorter bytes before reaching EOF, want %v, got %v\n", m, len(buf3))
+ }
+ if !bytes.Equal(buf3, buf[offset:offset+512]) {
+ logger().Fatal("Error: Incorrect read between two ReadAt from same offset.")
+ }
+ offset += 512
+ m, err = r.ReadAt(buf4, offset)
+ if err != nil {
+ logger().Fatal("Error:", err, st.Size, len(buf4), offset)
+ }
+ if m != len(buf4) {
+ logger().Fatalf("Error: ReadAt read shorter bytes before reaching EOF, want %v, got %v\n", m, len(buf4))
+ }
+ if !bytes.Equal(buf4, buf[offset:offset+512]) {
+ logger().Fatal("Error: Incorrect read between two ReadAt from same offset.")
+ }
+
+ buf5 := make([]byte, n)
+ // Read the whole object.
+ m, err = r.ReadAt(buf5, 0)
+ if err != nil {
+ if err != io.EOF {
+ logger().Fatal("Error:", err, len(buf5))
+ }
+ }
+ if m != len(buf5) {
+ logger().Fatalf("Error: ReadAt read shorter bytes before reaching EOF, want %v, got %v\n", m, len(buf5))
+ }
+ if !bytes.Equal(buf, buf5) {
+ logger().Fatal("Error: Incorrect data read in GetObject, than what was previously upoaded.")
+ }
+
+ buf6 := make([]byte, n+1)
+ // Read the whole object and beyond.
+ _, err = r.ReadAt(buf6, 0)
+ if err != nil {
+ if err != io.EOF {
+ logger().Fatal("Error:", err, len(buf6))
+ }
+ }
+ err = c.RemoveObject(bucketName, objectName)
+ if err != nil {
+ logger().Fatal("Error: ", err)
+ }
+ err = c.RemoveBucket(bucketName)
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+}
+
+// Tests copy object
+func testCopyObjectV2() {
+ logger().Info()
+
+ // Seed random based on current time.
+ rand.Seed(time.Now().Unix())
+
+ // Instantiate new minio client object
+ c, err := minio.NewV2(
+ os.Getenv(serverEndpoint),
+ os.Getenv(accessKey),
+ os.Getenv(secretKey),
+ mustParseBool(os.Getenv(enableHTTPS)),
+ )
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+
+ // Enable tracing, write to stderr.
+ // c.TraceOn(os.Stderr)
+
+ // Set user agent.
+ c.SetAppInfo("Minio-go-FunctionalTest", "0.1.0")
+
+ // Generate a new random bucket name.
+ bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test")
+
+ // Make a new bucket in 'us-east-1' (source bucket).
+ err = c.MakeBucket(bucketName, "us-east-1")
+ if err != nil {
+ logger().Fatal("Error:", err, bucketName)
+ }
+
+ // Make a new bucket in 'us-east-1' (destination bucket).
+ err = c.MakeBucket(bucketName+"-copy", "us-east-1")
+ if err != nil {
+ logger().Fatal("Error:", err, bucketName+"-copy")
+ }
+
+ // Generate data more than 32K
+ var buf = getDataBuffer("datafile-33-kB", rand.Intn(1<<20)+32*1024)
+
+ // Save the data
+ objectName := randString(60, rand.NewSource(time.Now().UnixNano()), "")
+ n, err := c.PutObject(bucketName, objectName, bytes.NewReader(buf), "binary/octet-stream")
+ if err != nil {
+ logger().Fatal("Error:", err, bucketName, objectName)
+ }
+
+ if n != int64(len(buf)) {
+ logger().Fatalf("Error: number of bytes does not match want %v, got %v",
+ len(buf), n)
+ }
+
+ r, err := c.GetObject(bucketName, objectName)
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+ // Check the various fields of source object against destination object.
+ objInfo, err := r.Stat()
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+
+ // Copy Source
+ src := minio.NewSourceInfo(bucketName, objectName, nil)
+
+ // Set copy conditions.
+
+ // All invalid conditions first.
+ err = src.SetModifiedSinceCond(time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC))
+ if err == nil {
+ logger().Fatal("Error:", err)
+ }
+ err = src.SetUnmodifiedSinceCond(time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC))
+ if err == nil {
+ logger().Fatal("Error:", err)
+ }
+ err = src.SetMatchETagCond("")
+ if err == nil {
+ logger().Fatal("Error:", err)
+ }
+ err = src.SetMatchETagExceptCond("")
+ if err == nil {
+ logger().Fatal("Error:", err)
+ }
+
+ err = src.SetModifiedSinceCond(time.Date(2014, time.April, 0, 0, 0, 0, 0, time.UTC))
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+ err = src.SetMatchETagCond(objInfo.ETag)
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+
+ dst, err := minio.NewDestinationInfo(bucketName+"-copy", objectName+"-copy", nil, nil)
+ if err != nil {
+ logger().Fatal(err)
+ }
+
+ // Perform the Copy
+ err = c.CopyObject(dst, src)
+ if err != nil {
+ logger().Fatal("Error:", err, bucketName+"-copy", objectName+"-copy")
+ }
+
+ // Source object
+ reader, err := c.GetObject(bucketName, objectName)
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+ // Destination object
+ readerCopy, err := c.GetObject(bucketName+"-copy", objectName+"-copy")
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+ // Check the various fields of source object against destination object.
+ objInfo, err = reader.Stat()
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+ objInfoCopy, err := readerCopy.Stat()
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+ if objInfo.Size != objInfoCopy.Size {
+ logger().Fatalf("Error: number of bytes does not match, want %v, got %v\n",
+ objInfo.Size, objInfoCopy.Size)
+ }
+
+ // CopyObject again but with wrong conditions
+ src = minio.NewSourceInfo(bucketName, objectName, nil)
+ err = src.SetUnmodifiedSinceCond(time.Date(2014, time.April, 0, 0, 0, 0, 0, time.UTC))
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+ err = src.SetMatchETagExceptCond(objInfo.ETag)
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+
+ // Perform the Copy which should fail
+ err = c.CopyObject(dst, src)
+ if err == nil {
+ logger().Fatal("Error:", err, bucketName+"-copy", objectName+"-copy should fail")
+ }
+
+ // Remove all objects and buckets
+ err = c.RemoveObject(bucketName, objectName)
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+
+ err = c.RemoveObject(bucketName+"-copy", objectName+"-copy")
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+
+ err = c.RemoveBucket(bucketName)
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+
+ err = c.RemoveBucket(bucketName + "-copy")
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+}
+
+func testComposeObjectErrorCasesWrapper(c *minio.Client) {
+ // Generate a new random bucket name.
+ bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test")
+
+ // Make a new bucket in 'us-east-1' (source bucket).
+ err := c.MakeBucket(bucketName, "us-east-1")
+ if err != nil {
+ logger().Fatal("Error:", err, bucketName)
+ }
+
+ // Test that more than 10K source objects cannot be
+ // concatenated.
+ srcArr := [10001]minio.SourceInfo{}
+ srcSlice := srcArr[:]
+ dst, err := minio.NewDestinationInfo(bucketName, "object", nil, nil)
+ if err != nil {
+ logger().Fatal(err)
+ }
+
+ if err := c.ComposeObject(dst, srcSlice); err == nil {
+ logger().Fatal("Error was expected.")
+ } else if err.Error() != "There must be as least one and up to 10000 source objects." {
+ logger().Fatal("Got unexpected error: ", err)
+ }
+
+ // Create a source with invalid offset spec and check that
+ // error is returned:
+ // 1. Create the source object.
+ const badSrcSize = 5 * 1024 * 1024
+ buf := bytes.Repeat([]byte("1"), badSrcSize)
+ _, err = c.PutObject(bucketName, "badObject", bytes.NewReader(buf), "")
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+ // 2. Set invalid range spec on the object (going beyond
+ // object size)
+ badSrc := minio.NewSourceInfo(bucketName, "badObject", nil)
+ err = badSrc.SetRange(1, badSrcSize)
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+ // 3. ComposeObject call should fail.
+ if err := c.ComposeObject(dst, []minio.SourceInfo{badSrc}); err == nil {
+ logger().Fatal("Error was expected.")
+ } else if !strings.Contains(err.Error(), "has invalid segment-to-copy") {
+ logger().Fatal("Got unexpected error: ", err)
+ }
+}
+
+// Test expected error cases
+func testComposeObjectErrorCasesV2() {
+ logger().Info()
+
+ // Instantiate new minio client object
+ c, err := minio.NewV2(
+ os.Getenv(serverEndpoint),
+ os.Getenv(accessKey),
+ os.Getenv(secretKey),
+ mustParseBool(os.Getenv(enableHTTPS)),
+ )
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+
+ testComposeObjectErrorCasesWrapper(c)
+}
+
+func testComposeMultipleSources(c *minio.Client) {
+ // Generate a new random bucket name.
+ bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test")
+ // Make a new bucket in 'us-east-1' (source bucket).
+ err := c.MakeBucket(bucketName, "us-east-1")
+ if err != nil {
+ logger().Fatal("Error:", err, bucketName)
+ }
+
+ // Upload a small source object
+ const srcSize = 1024 * 1024 * 5
+ buf := bytes.Repeat([]byte("1"), srcSize)
+ _, err = c.PutObject(bucketName, "srcObject", bytes.NewReader(buf), "binary/octet-stream")
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+
+ // We will append 10 copies of the object.
+ srcs := []minio.SourceInfo{}
+ for i := 0; i < 10; i++ {
+ srcs = append(srcs, minio.NewSourceInfo(bucketName, "srcObject", nil))
+ }
+ // make the last part very small
+ err = srcs[9].SetRange(0, 0)
+ if err != nil {
+ logger().Fatal("unexpected error:", err)
+ }
+
+ dst, err := minio.NewDestinationInfo(bucketName, "dstObject", nil, nil)
+ if err != nil {
+ logger().Fatal(err)
+ }
+ err = c.ComposeObject(dst, srcs)
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+
+ objProps, err := c.StatObject(bucketName, "dstObject")
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+
+ if objProps.Size != 9*srcSize+1 {
+ logger().Fatal("Size mismatched! Expected:", 10000*srcSize, "but got:", objProps.Size)
+ }
+}
+
+// Test concatenating multiple objects objects
+func testCompose10KSourcesV2() {
+ logger().Info()
+
+ // Instantiate new minio client object
+ c, err := minio.NewV2(
+ os.Getenv(serverEndpoint),
+ os.Getenv(accessKey),
+ os.Getenv(secretKey),
+ mustParseBool(os.Getenv(enableHTTPS)),
+ )
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+
+ testComposeMultipleSources(c)
+}
+func testEncryptedCopyObjectWrapper(c *minio.Client) {
+ // Generate a new random bucket name.
+ bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test")
+ // Make a new bucket in 'us-east-1' (source bucket).
+ err := c.MakeBucket(bucketName, "us-east-1")
+ if err != nil {
+ logger().Fatal("Error:", err, bucketName)
+ }
+
+ key1 := minio.NewSSEInfo([]byte("32byteslongsecretkeymustbegiven1"), "AES256")
+ key2 := minio.NewSSEInfo([]byte("32byteslongsecretkeymustbegiven2"), "AES256")
+
+ // 1. create an sse-c encrypted object to copy by uploading
+ const srcSize = 1024 * 1024
+ buf := bytes.Repeat([]byte("abcde"), srcSize) // gives a buffer of 5MiB
+ metadata := make(map[string][]string)
+ for k, v := range key1.GetSSEHeaders() {
+ metadata[k] = append(metadata[k], v)
+ }
+ _, err = c.PutObjectWithSize(bucketName, "srcObject", bytes.NewReader(buf), int64(len(buf)), metadata, nil)
+ if err != nil {
+ logger().Fatal("PutObjectWithSize Error:", err)
+ }
+
+ // 2. copy object and change encryption key
+ src := minio.NewSourceInfo(bucketName, "srcObject", &key1)
+ dst, err := minio.NewDestinationInfo(bucketName, "dstObject", &key2, nil)
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+
+ err = c.CopyObject(dst, src)
+ if err != nil {
+ logger().Fatal("CopyObject Error:", err)
+ }
+
+ // 3. get copied object and check if content is equal
+ reqH := minio.NewGetReqHeaders()
+ for k, v := range key2.GetSSEHeaders() {
+ reqH.Set(k, v)
+ }
+ coreClient := minio.Core{c}
+ reader, _, err := coreClient.GetObject(bucketName, "dstObject", reqH)
+ if err != nil {
+ logger().Fatal("GetObject Error:", err)
+ }
+ defer reader.Close()
+
+ decBytes, err := ioutil.ReadAll(reader)
+ if err != nil {
+ logger().Fatalln(err)
+ }
+ if !bytes.Equal(decBytes, buf) {
+ logger().Fatal("downloaded object mismatched for encrypted object")
+ }
+}
+
+// Test encrypted copy object
+func testEncryptedCopyObject() {
+ logger().Info()
+
+ // Instantiate new minio client object
+ c, err := minio.NewV4(
+ os.Getenv(serverEndpoint),
+ os.Getenv(accessKey),
+ os.Getenv(secretKey),
+ mustParseBool(os.Getenv(enableHTTPS)),
+ )
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+
+ // c.TraceOn(os.Stderr)
+ testEncryptedCopyObjectWrapper(c)
+}
+
+// Test encrypted copy object
+func testEncryptedCopyObjectV2() {
+ logger().Info()
+
+ // Instantiate new minio client object
+ c, err := minio.NewV2(
+ os.Getenv(serverEndpoint),
+ os.Getenv(accessKey),
+ os.Getenv(secretKey),
+ mustParseBool(os.Getenv(enableHTTPS)),
+ )
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+
+ testEncryptedCopyObjectWrapper(c)
+}
+func testUserMetadataCopying() {
+ logger().Info()
+
+ // Instantiate new minio client object
+ c, err := minio.NewV4(
+ os.Getenv(serverEndpoint),
+ os.Getenv(accessKey),
+ os.Getenv(secretKey),
+ mustParseBool(os.Getenv(enableHTTPS)),
+ )
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+
+ // c.TraceOn(os.Stderr)
+ testUserMetadataCopyingWrapper(c)
+}
+func testUserMetadataCopyingWrapper(c *minio.Client) {
+ // Generate a new random bucket name.
+ bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test")
+ // Make a new bucket in 'us-east-1' (source bucket).
+ err := c.MakeBucket(bucketName, "us-east-1")
+ if err != nil {
+ logger().Fatal("Error:", err, bucketName)
+ }
+
+ fetchMeta := func(object string) (h http.Header) {
+ objInfo, err := c.StatObject(bucketName, object)
+ if err != nil {
+ logger().Fatal("Metadata fetch error:", err)
+ }
+ h = make(http.Header)
+ for k, vs := range objInfo.Metadata {
+ if strings.HasPrefix(strings.ToLower(k), "x-amz-meta-") {
+ for _, v := range vs {
+ h.Add(k, v)
+ }
+ }
+ }
+ return h
+ }
+
+ // 1. create a client encrypted object to copy by uploading
+ const srcSize = 1024 * 1024
+ buf := bytes.Repeat([]byte("abcde"), srcSize) // gives a buffer of 5MiB
+ metadata := make(http.Header)
+ metadata.Set("x-amz-meta-myheader", "myvalue")
+ _, err = c.PutObjectWithMetadata(bucketName, "srcObject",
+ bytes.NewReader(buf), metadata, nil)
+ if err != nil {
+ logger().Fatal("Put Error:", err)
+ }
+ if !reflect.DeepEqual(metadata, fetchMeta("srcObject")) {
+ logger().Fatal("Unequal metadata")
+ }
+
+ // 2. create source
+ src := minio.NewSourceInfo(bucketName, "srcObject", nil)
+ // 2.1 create destination with metadata set
+ dst1, err := minio.NewDestinationInfo(bucketName, "dstObject-1", nil, map[string]string{"notmyheader": "notmyvalue"})
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+
+ // 3. Check that copying to an object with metadata set resets
+ // the headers on the copy.
+ err = c.CopyObject(dst1, src)
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+
+ expectedHeaders := make(http.Header)
+ expectedHeaders.Set("x-amz-meta-notmyheader", "notmyvalue")
+ if !reflect.DeepEqual(expectedHeaders, fetchMeta("dstObject-1")) {
+ logger().Fatal("Unequal metadata")
+ }
+
+ // 4. create destination with no metadata set and same source
+ dst2, err := minio.NewDestinationInfo(bucketName, "dstObject-2", nil, nil)
+ if err != nil {
+ logger().Fatal("Error:", err)
+
+ }
+ src = minio.NewSourceInfo(bucketName, "srcObject", nil)
+
+ // 5. Check that copying to an object with no metadata set,
+ // copies metadata.
+ err = c.CopyObject(dst2, src)
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+
+ expectedHeaders = metadata
+ if !reflect.DeepEqual(expectedHeaders, fetchMeta("dstObject-2")) {
+ logger().Fatal("Unequal metadata")
+ }
+
+ // 6. Compose a pair of sources.
+ srcs := []minio.SourceInfo{
+ minio.NewSourceInfo(bucketName, "srcObject", nil),
+ minio.NewSourceInfo(bucketName, "srcObject", nil),
+ }
+ dst3, err := minio.NewDestinationInfo(bucketName, "dstObject-3", nil, nil)
+ if err != nil {
+ logger().Fatal("Error:", err)
+
+ }
+
+ err = c.ComposeObject(dst3, srcs)
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+
+ // Check that no headers are copied in this case
+ if !reflect.DeepEqual(make(http.Header), fetchMeta("dstObject-3")) {
+ logger().Fatal("Unequal metadata")
+ }
+
+ // 7. Compose a pair of sources with dest user metadata set.
+ srcs = []minio.SourceInfo{
+ minio.NewSourceInfo(bucketName, "srcObject", nil),
+ minio.NewSourceInfo(bucketName, "srcObject", nil),
+ }
+ dst4, err := minio.NewDestinationInfo(bucketName, "dstObject-4", nil, map[string]string{"notmyheader": "notmyvalue"})
+ if err != nil {
+ logger().Fatal("Error:", err)
+
+ }
+
+ err = c.ComposeObject(dst4, srcs)
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+
+ // Check that no headers are copied in this case
+ expectedHeaders = make(http.Header)
+ expectedHeaders.Set("x-amz-meta-notmyheader", "notmyvalue")
+ if !reflect.DeepEqual(expectedHeaders, fetchMeta("dstObject-4")) {
+ logger().Fatal("Unequal metadata")
+ }
+}
+
+func testUserMetadataCopyingV2() {
+ logger().Info()
+
+ // Instantiate new minio client object
+ c, err := minio.NewV2(
+ os.Getenv(serverEndpoint),
+ os.Getenv(accessKey),
+ os.Getenv(secretKey),
+ mustParseBool(os.Getenv(enableHTTPS)),
+ )
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+
+ // c.TraceOn(os.Stderr)
+ testUserMetadataCopyingWrapper(c)
+}
+
+// Test put object with size -1 byte object.
+func testPutObjectNoLengthV2() {
+ logger().Info()
+
+ // Seed random based on current time.
+ rand.Seed(time.Now().Unix())
+
+ // Instantiate new minio client object.
+ c, err := minio.NewV2(
+ os.Getenv(serverEndpoint),
+ os.Getenv(accessKey),
+ os.Getenv(secretKey),
+ mustParseBool(os.Getenv(enableHTTPS)),
+ )
+ if err != nil {
+ log.Fatal("Error:", err)
+ }
+
+ // Enable tracing, write to stderr.
+ // c.TraceOn(os.Stderr)
+
+ // Set user agent.
+ c.SetAppInfo("Minio-go-FunctionalTest", "0.1.0")
+
+ // Generate a new random bucket name.
+ bucketName := randString(60, rand.NewSource(time.Now().UnixNano()),
+ "minio-go-test")
+
+ // Make a new bucket.
+ err = c.MakeBucket(bucketName, "us-east-1")
+ if err != nil {
+ log.Fatal("Error:", err, bucketName)
+ }
+
+ objectName := bucketName + "unique"
+
+ // Generate data using 4 parts so that all 3 'workers' are utilized and a part is leftover.
+ // Use different data for each part for multipart tests to ensure part order at the end.
+ var buf = getDataBuffer("datafile-65-MB", MinPartSize)
+
+ // Upload an object.
+ n, err := c.PutObjectWithSize(bucketName, objectName, bytes.NewReader(buf), -1, nil, nil)
+ if err != nil {
+ log.Fatalf("Error: %v %s %s", err, bucketName, objectName)
+ }
+ if n != int64(len(buf)) {
+ log.Error(fmt.Errorf("Expected upload object size %d but got %d", len(buf), n))
+ }
+
+ // Remove the object.
+ err = c.RemoveObject(bucketName, objectName)
+ if err != nil {
+ log.Fatal("Error:", err)
+ }
+
+ // Remove the bucket.
+ err = c.RemoveBucket(bucketName)
+ if err != nil {
+ log.Fatal("Error:", err)
+ }
+}
+
+// Test put object with 0 byte object.
+func testPutObject0ByteV2() {
+ logger().Info()
+
+ // Seed random based on current time.
+ rand.Seed(time.Now().Unix())
+
+ // Instantiate new minio client object.
+ c, err := minio.NewV2(
+ os.Getenv(serverEndpoint),
+ os.Getenv(accessKey),
+ os.Getenv(secretKey),
+ mustParseBool(os.Getenv(enableHTTPS)),
+ )
+ if err != nil {
+ log.Fatal("Error:", err)
+ }
+
+ // Enable tracing, write to stderr.
+ // c.TraceOn(os.Stderr)
+
+ // Set user agent.
+ c.SetAppInfo("Minio-go-FunctionalTest", "0.1.0")
+
+ // Generate a new random bucket name.
+ bucketName := randString(60, rand.NewSource(time.Now().UnixNano()),
+ "minio-go-test")
+
+ // Make a new bucket.
+ err = c.MakeBucket(bucketName, "us-east-1")
+ if err != nil {
+ log.Fatal("Error:", err, bucketName)
+ }
+
+ objectName := bucketName + "unique"
+
+ // Upload an object.
+ n, err := c.PutObjectWithSize(bucketName, objectName, bytes.NewReader([]byte("")), 0, nil, nil)
+ if err != nil {
+ log.Fatalf("Error: %v %s %s", err, bucketName, objectName)
+ }
+ if n != 0 {
+ log.Error(fmt.Errorf("Expected upload object size 0 but got %d", n))
+ }
+
+ // Remove the object.
+ err = c.RemoveObject(bucketName, objectName)
+ if err != nil {
+ log.Fatal("Error:", err)
+ }
+
+ // Remove the bucket.
+ err = c.RemoveBucket(bucketName)
+ if err != nil {
+ log.Fatal("Error:", err)
+ }
+}
+
+// Test expected error cases
+func testComposeObjectErrorCases() {
+ logger().Info()
+
+ // Instantiate new minio client object
+ c, err := minio.NewV4(
+ os.Getenv(serverEndpoint),
+ os.Getenv(accessKey),
+ os.Getenv(secretKey),
+ mustParseBool(os.Getenv(enableHTTPS)),
+ )
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+
+ testComposeObjectErrorCasesWrapper(c)
+}
+
+// Test concatenating 10K objects
+func testCompose10KSources() {
+ logger().Info()
+
+ // Instantiate new minio client object
+ c, err := minio.NewV4(
+ os.Getenv(serverEndpoint),
+ os.Getenv(accessKey),
+ os.Getenv(secretKey),
+ mustParseBool(os.Getenv(enableHTTPS)),
+ )
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+
+ testComposeMultipleSources(c)
+}
+
+// Tests comprehensive list of all methods.
+func testFunctionalV2() {
+ logger().Info()
+ // Seed random based on current time.
+ rand.Seed(time.Now().Unix())
+
+ c, err := minio.NewV2(
+ os.Getenv(serverEndpoint),
+ os.Getenv(accessKey),
+ os.Getenv(secretKey),
+ mustParseBool(os.Getenv(enableHTTPS)),
+ )
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+
+ // Enable to debug
+ // c.TraceOn(os.Stderr)
+
+ // Set user agent.
+ c.SetAppInfo("Minio-go-FunctionalTest", "0.1.0")
+
+ // Generate a new random bucket name.
+ bucketName := randString(60, rand.NewSource(time.Now().UnixNano()), "minio-go-test")
+
+ // Make a new bucket.
+ err = c.MakeBucket(bucketName, "us-east-1")
+ if err != nil {
+ logger().Fatal("Error:", err, bucketName)
+ }
+
+ // Generate a random file name.
+ fileName := randString(60, rand.NewSource(time.Now().UnixNano()), "")
+ file, err := os.Create(fileName)
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+ for i := 0; i < 3; i++ {
+ buf := make([]byte, rand.Intn(1<<19))
+ _, err = file.Write(buf)
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+ }
+ file.Close()
+
+ // Verify if bucket exits and you have access.
+ var exists bool
+ exists, err = c.BucketExists(bucketName)
+ if err != nil {
+ logger().Fatal("Error:", err, bucketName)
+ }
+ if !exists {
+ logger().Fatal("Error: could not find ", bucketName)
+ }
+
+ // Make the bucket 'public read/write'.
+ err = c.SetBucketPolicy(bucketName, "", policy.BucketPolicyReadWrite)
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+
+ // List all buckets.
+ buckets, err := c.ListBuckets()
+ if len(buckets) == 0 {
+ logger().Fatal("Error: list buckets cannot be empty", buckets)
+ }
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+
+ // Verify if previously created bucket is listed in list buckets.
+ bucketFound := false
+ for _, bucket := range buckets {
+ if bucket.Name == bucketName {
+ bucketFound = true
+ }
+ }
+
+ // If bucket not found error out.
+ if !bucketFound {
+ logger().Fatal("Error: bucket ", bucketName, "not found")
+ }
+
+ objectName := bucketName + "unique"
+
+ // Generate data
+ buf := bytes.Repeat([]byte("n"), rand.Intn(1<<19))
+
+ n, err := c.PutObject(bucketName, objectName, bytes.NewReader(buf), "")
+ if err != nil {
+ logger().Fatal("Error: ", err)
+ }
+ if n != int64(len(buf)) {
+ logger().Fatal("Error: bad length ", n, len(buf))
+ }
+
+ n, err = c.PutObject(bucketName, objectName+"-nolength", bytes.NewReader(buf), "binary/octet-stream")
+ if err != nil {
+ logger().Fatal("Error:", err, bucketName, objectName+"-nolength")
+ }
+
+ if n != int64(len(buf)) {
+ logger().Fatalf("Error: number of bytes does not match, want %v, got %v\n", len(buf), n)
+ }
+
+ // Instantiate a done channel to close all listing.
+ doneCh := make(chan struct{})
+ defer close(doneCh)
+
+ objFound := false
+ isRecursive := true // Recursive is true.
+ for obj := range c.ListObjects(bucketName, objectName, isRecursive, doneCh) {
+ if obj.Key == objectName {
+ objFound = true
+ break
+ }
+ }
+ if !objFound {
+ logger().Fatal("Error: object " + objectName + " not found.")
+ }
+
+ objFound = false
+ isRecursive = true // Recursive is true.
+ for obj := range c.ListObjects(bucketName, objectName, isRecursive, doneCh) {
+ if obj.Key == objectName {
+ objFound = true
+ break
+ }
+ }
+ if !objFound {
+ logger().Fatal("Error: object " + objectName + " not found.")
+ }
+
+ incompObjNotFound := true
+ for objIncompl := range c.ListIncompleteUploads(bucketName, objectName, isRecursive, doneCh) {
+ if objIncompl.Key != "" {
+ incompObjNotFound = false
+ break
+ }
+ }
+ if !incompObjNotFound {
+ logger().Fatal("Error: unexpected dangling incomplete upload found.")
+ }
+
+ newReader, err := c.GetObject(bucketName, objectName)
+ if err != nil {
+ logger().Fatal("Error: ", err)
+ }
+
+ newReadBytes, err := ioutil.ReadAll(newReader)
+ if err != nil {
+ logger().Fatal("Error: ", err)
+ }
+
+ if !bytes.Equal(newReadBytes, buf) {
+ logger().Fatal("Error: bytes mismatch.")
+ }
+
+ err = c.FGetObject(bucketName, objectName, fileName+"-f")
+ if err != nil {
+ logger().Fatal("Error: ", err)
+ }
+
+ // Generate presigned GET object url.
+ presignedGetURL, err := c.PresignedGetObject(bucketName, objectName, 3600*time.Second, nil)
+ if err != nil {
+ logger().Fatal("Error: ", err)
+ }
+ // Verify if presigned url works.
+ resp, err := http.Get(presignedGetURL.String())
+ if err != nil {
+ logger().Fatal("Error: ", err)
+ }
+ if resp.StatusCode != http.StatusOK {
+ logger().Fatal("Error: ", resp.Status)
+ }
+ newPresignedBytes, err := ioutil.ReadAll(resp.Body)
+ if err != nil {
+ logger().Fatal("Error: ", err)
+ }
+ if !bytes.Equal(newPresignedBytes, buf) {
+ logger().Fatal("Error: bytes mismatch.")
+ }
+
+ // Set request parameters.
+ reqParams := make(url.Values)
+ reqParams.Set("response-content-disposition", "attachment; filename=\"test.txt\"")
+ // Generate presigned GET object url.
+ presignedGetURL, err = c.PresignedGetObject(bucketName, objectName, 3600*time.Second, reqParams)
+ if err != nil {
+ logger().Fatal("Error: ", err)
+ }
+ // Verify if presigned url works.
+ resp, err = http.Get(presignedGetURL.String())
+ if err != nil {
+ logger().Fatal("Error: ", err)
+ }
+ if resp.StatusCode != http.StatusOK {
+ logger().Fatal("Error: ", resp.Status)
+ }
+ newPresignedBytes, err = ioutil.ReadAll(resp.Body)
+ if err != nil {
+ logger().Fatal("Error: ", err)
+ }
+ if !bytes.Equal(newPresignedBytes, buf) {
+ logger().Fatal("Error: bytes mismatch for presigned GET url.")
+ }
+ // Verify content disposition.
+ if resp.Header.Get("Content-Disposition") != "attachment; filename=\"test.txt\"" {
+ logger().Fatalf("Error: wrong Content-Disposition received %s", resp.Header.Get("Content-Disposition"))
+ }
+
+ presignedPutURL, err := c.PresignedPutObject(bucketName, objectName+"-presigned", 3600*time.Second)
+ if err != nil {
+ logger().Fatal("Error: ", err)
+ }
+ // Generate data more than 32K
+ buf = bytes.Repeat([]byte("1"), rand.Intn(1<<20)+32*1024)
+
+ req, err := http.NewRequest("PUT", presignedPutURL.String(), bytes.NewReader(buf))
+ if err != nil {
+ logger().Fatal("Error: ", err)
+ }
+ httpClient := &http.Client{
+ // Setting a sensible time out of 30secs to wait for response
+ // headers. Request is pro-actively cancelled after 30secs
+ // with no response.
+ Timeout: 30 * time.Second,
+ Transport: http.DefaultTransport,
+ }
+ resp, err = httpClient.Do(req)
+ if err != nil {
+ logger().Fatal("Error: ", err)
+ }
+
+ newReader, err = c.GetObject(bucketName, objectName+"-presigned")
+ if err != nil {
+ logger().Fatal("Error: ", err)
+ }
+
+ newReadBytes, err = ioutil.ReadAll(newReader)
+ if err != nil {
+ logger().Fatal("Error: ", err)
+ }
+
+ if !bytes.Equal(newReadBytes, buf) {
+ logger().Fatal("Error: bytes mismatch.")
+ }
+
+ err = c.RemoveObject(bucketName, objectName)
+ if err != nil {
+ logger().Fatal("Error: ", err)
+ }
+ err = c.RemoveObject(bucketName, objectName+"-f")
+ if err != nil {
+ logger().Fatal("Error: ", err)
+ }
+ err = c.RemoveObject(bucketName, objectName+"-nolength")
+ if err != nil {
+ logger().Fatal("Error: ", err)
+ }
+ err = c.RemoveObject(bucketName, objectName+"-presigned")
+ if err != nil {
+ logger().Fatal("Error: ", err)
+ }
+ err = c.RemoveBucket(bucketName)
+ if err != nil {
+ logger().Fatal("Error:", err)
+ }
+ err = c.RemoveBucket(bucketName)
+ if err == nil {
+ logger().Fatal("Error:")
+ }
+ if err.Error() != "The specified bucket does not exist" {
+ logger().Fatal("Error: ", err)
+ }
+ if err = os.Remove(fileName); err != nil {
+ logger().Fatal("Error: ", err)
+ }
+ if err = os.Remove(fileName + "-f"); err != nil {
+ logger().Fatal("Error: ", err)
+ }
+}
+
+// Convert string to bool and always return false if any error
+func mustParseBool(str string) bool {
+ b, err := strconv.ParseBool(str)
+ if err != nil {
+ return false
+ }
+ return b
+}
+
+func logger() *logrus.Entry {
+ if pc, file, line, ok := runtime.Caller(1); ok {
+ fName := runtime.FuncForPC(pc).Name()
+ return log.WithFields(log.Fields{"file": path.Base(file), "function:": fName, "line#": line})
+
+ }
+ return log.WithFields(nil)
+}
+
+func main() {
+ logger().Info("Running functional tests for minio-go sdk....")
+ if !isQuickMode() {
+ testMakeBucketErrorV2()
+ testGetObjectClosedTwiceV2()
+ testRemovePartiallyUploadedV2()
+ testFPutObjectV2()
+ testMakeBucketRegionsV2()
+ testGetObjectReadSeekFunctionalV2()
+ testGetObjectReadAtFunctionalV2()
+ testCopyObjectV2()
+ testFunctionalV2()
+ testComposeObjectErrorCasesV2()
+ testCompose10KSourcesV2()
+ testEncryptedCopyObjectV2()
+ testUserMetadataCopyingV2()
+ testPutObject0ByteV2()
+ testPutObjectNoLengthV2()
+ testMakeBucketError()
+ testMakeBucketRegions()
+ testPutObjectWithMetadata()
+ testPutObjectReadAt()
+ testPutObjectStreaming()
+ testListPartiallyUploaded()
+ testGetObjectSeekEnd()
+ testGetObjectClosedTwice()
+ testRemoveMultipleObjects()
+ testRemovePartiallyUploaded()
+ testFPutObjectMultipart()
+ testFPutObject()
+ testGetObjectReadSeekFunctional()
+ testGetObjectReadAtFunctional()
+ testPresignedPostPolicy()
+ testCopyObject()
+ testEncryptionPutGet()
+ testComposeObjectErrorCases()
+ testCompose10KSources()
+ testUserMetadataCopying()
+ testEncryptedCopyObject()
+ testBucketNotification()
+ testFunctional()
+ testGetObjectObjectModified()
+ testPutObjectUploadSeekedObject()
+ } else {
+ logger().Info("Running short functional tests")
+ testFunctional()
+ testFunctionalV2()
+ }
+
+ logger().Info("Functional tests complete for minio-go sdk")
+}
diff --git a/vendor/github.com/minio/minio-go/pkg/s3signer/request-signature-streaming.go b/vendor/github.com/minio/minio-go/pkg/s3signer/request-signature-streaming.go
index 22059bb1d..d831436cd 100644
--- a/vendor/github.com/minio/minio-go/pkg/s3signer/request-signature-streaming.go
+++ b/vendor/github.com/minio/minio-go/pkg/s3signer/request-signature-streaming.go
@@ -99,7 +99,7 @@ func prepareStreamingRequest(req *http.Request, sessionToken string, dataLen int
if sessionToken != "" {
req.Header.Set("X-Amz-Security-Token", sessionToken)
}
- req.Header.Set("Content-Encoding", streamingEncoding)
+ req.Header.Add("Content-Encoding", streamingEncoding)
req.Header.Set("X-Amz-Date", timestamp.Format(iso8601DateFormat))
// Set content length with streaming signature for each chunk included.
diff --git a/vendor/github.com/minio/minio-go/pkg/s3signer/request-signature-v2.go b/vendor/github.com/minio/minio-go/pkg/s3signer/request-signature-v2.go
index af0e91515..39c4e0187 100644
--- a/vendor/github.com/minio/minio-go/pkg/s3signer/request-signature-v2.go
+++ b/vendor/github.com/minio/minio-go/pkg/s3signer/request-signature-v2.go
@@ -42,9 +42,7 @@ const (
func encodeURL2Path(u *url.URL) (path string) {
// Encode URL path.
if isS3, _ := filepath.Match("*.s3*.amazonaws.com", u.Host); isS3 {
- hostSplits := strings.SplitN(u.Host, ".", 4)
- // First element is the bucket name.
- bucketName := hostSplits[0]
+ bucketName := u.Host[:strings.LastIndex(u.Host, ".s3")]
path = "/" + bucketName
path += u.Path
path = s3utils.EncodePath(path)
diff --git a/vendor/github.com/minio/minio-go/pkg/s3signer/utils_test.go b/vendor/github.com/minio/minio-go/pkg/s3signer/utils_test.go
index b266e42a1..26f609013 100644
--- a/vendor/github.com/minio/minio-go/pkg/s3signer/utils_test.go
+++ b/vendor/github.com/minio/minio-go/pkg/s3signer/utils_test.go
@@ -25,6 +25,7 @@ import (
// Tests url encoding.
func TestEncodeURL2Path(t *testing.T) {
type urlStrings struct {
+ bucketName string
objName string
encodedObjName string
}
@@ -32,22 +33,27 @@ func TestEncodeURL2Path(t *testing.T) {
bucketName := "bucketName"
want := []urlStrings{
{
+ bucketName: "bucketName",
objName: "本語",
encodedObjName: "%E6%9C%AC%E8%AA%9E",
},
{
+ bucketName: "bucketName",
objName: "本語.1",
encodedObjName: "%E6%9C%AC%E8%AA%9E.1",
},
{
objName: ">123>3123123",
+ bucketName: "bucketName",
encodedObjName: "%3E123%3E3123123",
},
{
+ bucketName: "bucketName",
objName: "test 1 2.txt",
encodedObjName: "test%201%202.txt",
},
{
+ bucketName: "test.bucketName",
objName: "test++ 1.txt",
encodedObjName: "test%2B%2B%201.txt",
},
@@ -63,4 +69,5 @@ func TestEncodeURL2Path(t *testing.T) {
t.Fatal("Error")
}
}
+
}
diff --git a/vendor/github.com/minio/minio-go/pkg/s3utils/utils.go b/vendor/github.com/minio/minio-go/pkg/s3utils/utils.go
index 9d6ac4d81..79bebd99f 100644
--- a/vendor/github.com/minio/minio-go/pkg/s3utils/utils.go
+++ b/vendor/github.com/minio/minio-go/pkg/s3utils/utils.go
@@ -205,7 +205,7 @@ func EncodePath(pathName string) string {
// We support '.' with bucket names but we fallback to using path
// style requests instead for such buckets.
var (
- validBucketName = regexp.MustCompile(`^[A-Za-z0-9][A-Za-z0-9\.\-]{1,61}[A-Za-z0-9]$`)
+ validBucketName = regexp.MustCompile(`^[A-Za-z0-9][A-Za-z0-9\.\-\_\:]{1,61}[A-Za-z0-9]$`)
validBucketNameStrict = regexp.MustCompile(`^[a-z0-9][a-z0-9\.\-]{1,61}[a-z0-9]$`)
ipAddress = regexp.MustCompile(`^(\d+\.){3}\d+$`)
)
@@ -240,14 +240,13 @@ func checkBucketNameCommon(bucketName string, strict bool) (err error) {
}
// CheckValidBucketName - checks if we have a valid input bucket name.
-// This is a non stricter version.
-// - http://docs.aws.amazon.com/AmazonS3/latest/dev/UsingBucket.html
func CheckValidBucketName(bucketName string) (err error) {
return checkBucketNameCommon(bucketName, false)
}
// CheckValidBucketNameStrict - checks if we have a valid input bucket name.
// This is a stricter version.
+// - http://docs.aws.amazon.com/AmazonS3/latest/dev/UsingBucket.html
func CheckValidBucketNameStrict(bucketName string) (err error) {
return checkBucketNameCommon(bucketName, true)
}
diff --git a/vendor/github.com/minio/minio-go/pkg/s3utils/utils_test.go b/vendor/github.com/minio/minio-go/pkg/s3utils/utils_test.go
index 6be701d18..d3b4d4331 100644
--- a/vendor/github.com/minio/minio-go/pkg/s3utils/utils_test.go
+++ b/vendor/github.com/minio/minio-go/pkg/s3utils/utils_test.go
@@ -301,10 +301,14 @@ func TestIsValidBucketName(t *testing.T) {
{"", errors.New("Bucket name cannot be empty"), false},
{"my..bucket", errors.New("Bucket name contains invalid characters"), false},
{"192.168.1.168", errors.New("Bucket name cannot be an ip address"), false},
+ {":bucketname", errors.New("Bucket name contains invalid characters"), false},
+ {"_bucketName", errors.New("Bucket name contains invalid characters"), false},
{"my.bucket.com", nil, true},
{"my-bucket", nil, true},
{"123my-bucket", nil, true},
{"Mybucket", nil, true},
+ {"My_bucket", nil, true},
+ {"My:bucket", nil, true},
}
for i, testCase := range testCases {
diff --git a/vendor/github.com/minio/minio-go/test-utils_test.go b/vendor/github.com/minio/minio-go/test-utils_test.go
index 4134af996..b109dfaf7 100644
--- a/vendor/github.com/minio/minio-go/test-utils_test.go
+++ b/vendor/github.com/minio/minio-go/test-utils_test.go
@@ -64,11 +64,11 @@ func encodeResponse(response interface{}) []byte {
return bytesBuffer.Bytes()
}
-// Convert string to bool and always return true if any error
+// Convert string to bool and always return false if any error
func mustParseBool(str string) bool {
b, err := strconv.ParseBool(str)
if err != nil {
- return true
+ return false
}
return b
}
diff --git a/vendor/github.com/minio/minio-go/transport.go b/vendor/github.com/minio/minio-go/transport.go
new file mode 100644
index 000000000..d286bd7ae
--- /dev/null
+++ b/vendor/github.com/minio/minio-go/transport.go
@@ -0,0 +1,48 @@
+// +build go1.7 go1.8
+
+/*
+ * Minio Go Library for Amazon S3 Compatible Cloud Storage
+ * (C) 2017 Minio, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package minio
+
+import (
+ "net"
+ "net/http"
+ "time"
+)
+
+// This default transport is similar to http.DefaultTransport
+// but with additional DisableCompression:
+var defaultMinioTransport http.RoundTripper = &http.Transport{
+ Proxy: http.ProxyFromEnvironment,
+ DialContext: (&net.Dialer{
+ Timeout: 30 * time.Second,
+ KeepAlive: 30 * time.Second,
+ DualStack: true,
+ }).DialContext,
+ MaxIdleConns: 100,
+ IdleConnTimeout: 90 * time.Second,
+ TLSHandshakeTimeout: 10 * time.Second,
+ ExpectContinueTimeout: 1 * time.Second,
+ // Set this value so that the underlying transport round-tripper
+ // doesn't try to auto decode the body of objects with
+ // content-encoding set to `gzip`.
+ //
+ // Refer:
+ // https://golang.org/src/net/http/transport.go?h=roundTrip#L1843
+ DisableCompression: true,
+}
diff --git a/vendor/github.com/minio/minio-go/transport_1_5.go b/vendor/github.com/minio/minio-go/transport_1_5.go
new file mode 100644
index 000000000..468daafd3
--- /dev/null
+++ b/vendor/github.com/minio/minio-go/transport_1_5.go
@@ -0,0 +1,39 @@
+// +build go1.5,!go1.6,!go1.7,!go1.8
+
+/*
+ * Minio Go Library for Amazon S3 Compatible Cloud Storage
+ * (C) 2017 Minio, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package minio
+
+import (
+ "net/http"
+ "time"
+)
+
+// This default transport is similar to http.DefaultTransport
+// but with additional DisableCompression:
+var defaultMinioTransport http.RoundTripper = &http.Transport{
+ Proxy: http.ProxyFromEnvironment,
+ TLSHandshakeTimeout: 10 * time.Second,
+ // Set this value so that the underlying transport round-tripper
+ // doesn't try to auto decode the body of objects with
+ // content-encoding set to `gzip`.
+ //
+ // Refer:
+ // https://golang.org/src/net/http/transport.go?h=roundTrip#L1843
+ DisableCompression: true,
+}
diff --git a/vendor/github.com/minio/minio-go/transport_1_6.go b/vendor/github.com/minio/minio-go/transport_1_6.go
new file mode 100644
index 000000000..77e7d76fc
--- /dev/null
+++ b/vendor/github.com/minio/minio-go/transport_1_6.go
@@ -0,0 +1,40 @@
+// +build go1.6,!go1.7,!go1.8
+
+/*
+ * Minio Go Library for Amazon S3 Compatible Cloud Storage
+ * (C) 2017 Minio, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package minio
+
+import (
+ "net/http"
+ "time"
+)
+
+// This default transport is similar to http.DefaultTransport
+// but with additional DisableCompression:
+var defaultMinioTransport http.RoundTripper = &http.Transport{
+ Proxy: http.ProxyFromEnvironment,
+ TLSHandshakeTimeout: 10 * time.Second,
+ ExpectContinueTimeout: 1 * time.Second,
+ // Set this value so that the underlying transport round-tripper
+ // doesn't try to auto decode the body of objects with
+ // content-encoding set to `gzip`.
+ //
+ // Refer:
+ // https://golang.org/src/net/http/transport.go?h=roundTrip#L1843
+ DisableCompression: true,
+}
diff --git a/vendor/github.com/pelletier/go-toml/doc_test.go b/vendor/github.com/pelletier/go-toml/doc_test.go
index 9dd773899..a48c04b01 100644
--- a/vendor/github.com/pelletier/go-toml/doc_test.go
+++ b/vendor/github.com/pelletier/go-toml/doc_test.go
@@ -1,13 +1,16 @@
// code examples for godoc
-package toml
+package toml_test
import (
"fmt"
+ "log"
+
+ toml "github.com/pelletier/go-toml"
)
func Example_tree() {
- config, err := LoadFile("config.toml")
+ config, err := toml.LoadFile("config.toml")
if err != nil {
fmt.Println("Error ", err.Error())
@@ -17,7 +20,7 @@ func Example_tree() {
password := config.Get("postgres.password").(string)
// or using an intermediate object
- configTree := config.Get("postgres").(*Tree)
+ configTree := config.Get("postgres").(*toml.Tree)
user = configTree.Get("user").(string)
password = configTree.Get("password").(string)
fmt.Println("User is", user, " and password is", password)
@@ -48,6 +51,50 @@ func Example_unmarshal() {
`)
person := Person{}
- Unmarshal(document, &person)
+ toml.Unmarshal(document, &person)
fmt.Println(person.Name, "is", person.Age, "and works at", person.Employer.Name)
+ // Output:
+ // John is 30 and works at Company Inc.
+}
+
+func ExampleMarshal() {
+ type Postgres struct {
+ User string `toml:"user"`
+ Password string `toml:"password"`
+ }
+ type Config struct {
+ Postgres Postgres `toml:"postgres"`
+ }
+
+ config := Config{Postgres{User: "pelletier", Password: "mypassword"}}
+ b, err := toml.Marshal(config)
+ if err != nil {
+ log.Fatal(err)
+ }
+ fmt.Println(string(b))
+ // Output:
+ // [postgres]
+ // password = "mypassword"
+ // user = "pelletier"
+}
+
+func ExampleUnmarshal() {
+ type Postgres struct {
+ User string
+ Password string
+ }
+ type Config struct {
+ Postgres Postgres
+ }
+
+ doc := []byte(`
+ [postgres]
+ user = "pelletier"
+ password = "mypassword"`)
+
+ config := Config{}
+ toml.Unmarshal(doc, &config)
+ fmt.Println("user=", config.Postgres.User)
+ // Output:
+ // user= pelletier
}
diff --git a/vendor/github.com/pelletier/go-toml/marshal_test.go b/vendor/github.com/pelletier/go-toml/marshal_test.go
index a3fa128d2..dbfc7c1d1 100644
--- a/vendor/github.com/pelletier/go-toml/marshal_test.go
+++ b/vendor/github.com/pelletier/go-toml/marshal_test.go
@@ -177,25 +177,6 @@ func TestDocUnmarshal(t *testing.T) {
}
}
-func ExampleUnmarshal() {
- type Postgres struct {
- User string
- Password string
- }
- type Config struct {
- Postgres Postgres
- }
-
- doc := []byte(`
- [postgres]
- user = "pelletier"
- password = "mypassword"`)
-
- config := Config{}
- Unmarshal(doc, &config)
- fmt.Println("user=", config.Postgres.User)
-}
-
func TestDocPartialUnmarshal(t *testing.T) {
result := testDocSubs{}
diff --git a/vendor/github.com/pmezard/go-difflib/.travis.yml b/vendor/github.com/pmezard/go-difflib/.travis.yml
new file mode 100644
index 000000000..90c9c6f91
--- /dev/null
+++ b/vendor/github.com/pmezard/go-difflib/.travis.yml
@@ -0,0 +1,5 @@
+language: go
+go:
+ - 1.5
+ - tip
+
diff --git a/vendor/github.com/pmezard/go-difflib/LICENSE b/vendor/github.com/pmezard/go-difflib/LICENSE
new file mode 100644
index 000000000..c67dad612
--- /dev/null
+++ b/vendor/github.com/pmezard/go-difflib/LICENSE
@@ -0,0 +1,27 @@
+Copyright (c) 2013, Patrick Mezard
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in the
+documentation and/or other materials provided with the distribution.
+ The names of its contributors may not be used to endorse or promote
+products derived from this software without specific prior written
+permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/vendor/github.com/pmezard/go-difflib/README.md b/vendor/github.com/pmezard/go-difflib/README.md
new file mode 100644
index 000000000..e87f307ed
--- /dev/null
+++ b/vendor/github.com/pmezard/go-difflib/README.md
@@ -0,0 +1,50 @@
+go-difflib
+==========
+
+[![Build Status](https://travis-ci.org/pmezard/go-difflib.png?branch=master)](https://travis-ci.org/pmezard/go-difflib)
+[![GoDoc](https://godoc.org/github.com/pmezard/go-difflib/difflib?status.svg)](https://godoc.org/github.com/pmezard/go-difflib/difflib)
+
+Go-difflib is a partial port of python 3 difflib package. Its main goal
+was to make unified and context diff available in pure Go, mostly for
+testing purposes.
+
+The following class and functions (and related tests) have be ported:
+
+* `SequenceMatcher`
+* `unified_diff()`
+* `context_diff()`
+
+## Installation
+
+```bash
+$ go get github.com/pmezard/go-difflib/difflib
+```
+
+### Quick Start
+
+Diffs are configured with Unified (or ContextDiff) structures, and can
+be output to an io.Writer or returned as a string.
+
+```Go
+diff := UnifiedDiff{
+ A: difflib.SplitLines("foo\nbar\n"),
+ B: difflib.SplitLines("foo\nbaz\n"),
+ FromFile: "Original",
+ ToFile: "Current",
+ Context: 3,
+}
+text, _ := GetUnifiedDiffString(diff)
+fmt.Printf(text)
+```
+
+would output:
+
+```
+--- Original
++++ Current
+@@ -1,3 +1,3 @@
+ foo
+-bar
++baz
+```
+
diff --git a/vendor/github.com/pmezard/go-difflib/difflib/difflib.go b/vendor/github.com/pmezard/go-difflib/difflib/difflib.go
new file mode 100644
index 000000000..64cc40fe1
--- /dev/null
+++ b/vendor/github.com/pmezard/go-difflib/difflib/difflib.go
@@ -0,0 +1,758 @@
+// Package difflib is a partial port of Python difflib module.
+//
+// It provides tools to compare sequences of strings and generate textual diffs.
+//
+// The following class and functions have been ported:
+//
+// - SequenceMatcher
+//
+// - unified_diff
+//
+// - context_diff
+//
+// Getting unified diffs was the main goal of the port. Keep in mind this code
+// is mostly suitable to output text differences in a human friendly way, there
+// are no guarantees generated diffs are consumable by patch(1).
+package difflib
+
+import (
+ "bufio"
+ "bytes"
+ "fmt"
+ "io"
+ "strings"
+)
+
+func min(a, b int) int {
+ if a < b {
+ return a
+ }
+ return b
+}
+
+func max(a, b int) int {
+ if a > b {
+ return a
+ }
+ return b
+}
+
+func calculateRatio(matches, length int) float64 {
+ if length > 0 {
+ return 2.0 * float64(matches) / float64(length)
+ }
+ return 1.0
+}
+
+type Match struct {
+ A int
+ B int
+ Size int
+}
+
+type OpCode struct {
+ Tag byte
+ I1 int
+ I2 int
+ J1 int
+ J2 int
+}
+
+// SequenceMatcher compares sequence of strings. The basic
+// algorithm predates, and is a little fancier than, an algorithm
+// published in the late 1980's by Ratcliff and Obershelp under the
+// hyperbolic name "gestalt pattern matching". The basic idea is to find
+// the longest contiguous matching subsequence that contains no "junk"
+// elements (R-O doesn't address junk). The same idea is then applied
+// recursively to the pieces of the sequences to the left and to the right
+// of the matching subsequence. This does not yield minimal edit
+// sequences, but does tend to yield matches that "look right" to people.
+//
+// SequenceMatcher tries to compute a "human-friendly diff" between two
+// sequences. Unlike e.g. UNIX(tm) diff, the fundamental notion is the
+// longest *contiguous* & junk-free matching subsequence. That's what
+// catches peoples' eyes. The Windows(tm) windiff has another interesting
+// notion, pairing up elements that appear uniquely in each sequence.
+// That, and the method here, appear to yield more intuitive difference
+// reports than does diff. This method appears to be the least vulnerable
+// to synching up on blocks of "junk lines", though (like blank lines in
+// ordinary text files, or maybe "<P>" lines in HTML files). That may be
+// because this is the only method of the 3 that has a *concept* of
+// "junk" <wink>.
+//
+// Timing: Basic R-O is cubic time worst case and quadratic time expected
+// case. SequenceMatcher is quadratic time for the worst case and has
+// expected-case behavior dependent in a complicated way on how many
+// elements the sequences have in common; best case time is linear.
+type SequenceMatcher struct {
+ a []string
+ b []string
+ b2j map[string][]int
+ IsJunk func(string) bool
+ autoJunk bool
+ bJunk map[string]struct{}
+ matchingBlocks []Match
+ fullBCount map[string]int
+ bPopular map[string]struct{}
+ opCodes []OpCode
+}
+
+func NewMatcher(a, b []string) *SequenceMatcher {
+ m := SequenceMatcher{autoJunk: true}
+ m.SetSeqs(a, b)
+ return &m
+}
+
+func NewMatcherWithJunk(a, b []string, autoJunk bool,
+ isJunk func(string) bool) *SequenceMatcher {
+
+ m := SequenceMatcher{IsJunk: isJunk, autoJunk: autoJunk}
+ m.SetSeqs(a, b)
+ return &m
+}
+
+// Set two sequences to be compared.
+func (m *SequenceMatcher) SetSeqs(a, b []string) {
+ m.SetSeq1(a)
+ m.SetSeq2(b)
+}
+
+// Set the first sequence to be compared. The second sequence to be compared is
+// not changed.
+//
+// SequenceMatcher computes and caches detailed information about the second
+// sequence, so if you want to compare one sequence S against many sequences,
+// use .SetSeq2(s) once and call .SetSeq1(x) repeatedly for each of the other
+// sequences.
+//
+// See also SetSeqs() and SetSeq2().
+func (m *SequenceMatcher) SetSeq1(a []string) {
+ if &a == &m.a {
+ return
+ }
+ m.a = a
+ m.matchingBlocks = nil
+ m.opCodes = nil
+}
+
+// Set the second sequence to be compared. The first sequence to be compared is
+// not changed.
+func (m *SequenceMatcher) SetSeq2(b []string) {
+ if &b == &m.b {
+ return
+ }
+ m.b = b
+ m.matchingBlocks = nil
+ m.opCodes = nil
+ m.fullBCount = nil
+ m.chainB()
+}
+
+func (m *SequenceMatcher) chainB() {
+ // Populate line -> index mapping
+ b2j := map[string][]int{}
+ for i, s := range m.b {
+ indices := b2j[s]
+ indices = append(indices, i)
+ b2j[s] = indices
+ }
+
+ // Purge junk elements
+ m.bJunk = map[string]struct{}{}
+ if m.IsJunk != nil {
+ junk := m.bJunk
+ for s, _ := range b2j {
+ if m.IsJunk(s) {
+ junk[s] = struct{}{}
+ }
+ }
+ for s, _ := range junk {
+ delete(b2j, s)
+ }
+ }
+
+ // Purge remaining popular elements
+ popular := map[string]struct{}{}
+ n := len(m.b)
+ if m.autoJunk && n >= 200 {
+ ntest := n/100 + 1
+ for s, indices := range b2j {
+ if len(indices) > ntest {
+ popular[s] = struct{}{}
+ }
+ }
+ for s, _ := range popular {
+ delete(b2j, s)
+ }
+ }
+ m.bPopular = popular
+ m.b2j = b2j
+}
+
+func (m *SequenceMatcher) isBJunk(s string) bool {
+ _, ok := m.bJunk[s]
+ return ok
+}
+
+// Find longest matching block in a[alo:ahi] and b[blo:bhi].
+//
+// If IsJunk is not defined:
+//
+// Return (i,j,k) such that a[i:i+k] is equal to b[j:j+k], where
+// alo <= i <= i+k <= ahi
+// blo <= j <= j+k <= bhi
+// and for all (i',j',k') meeting those conditions,
+// k >= k'
+// i <= i'
+// and if i == i', j <= j'
+//
+// In other words, of all maximal matching blocks, return one that
+// starts earliest in a, and of all those maximal matching blocks that
+// start earliest in a, return the one that starts earliest in b.
+//
+// If IsJunk is defined, first the longest matching block is
+// determined as above, but with the additional restriction that no
+// junk element appears in the block. Then that block is extended as
+// far as possible by matching (only) junk elements on both sides. So
+// the resulting block never matches on junk except as identical junk
+// happens to be adjacent to an "interesting" match.
+//
+// If no blocks match, return (alo, blo, 0).
+func (m *SequenceMatcher) findLongestMatch(alo, ahi, blo, bhi int) Match {
+ // CAUTION: stripping common prefix or suffix would be incorrect.
+ // E.g.,
+ // ab
+ // acab
+ // Longest matching block is "ab", but if common prefix is
+ // stripped, it's "a" (tied with "b"). UNIX(tm) diff does so
+ // strip, so ends up claiming that ab is changed to acab by
+ // inserting "ca" in the middle. That's minimal but unintuitive:
+ // "it's obvious" that someone inserted "ac" at the front.
+ // Windiff ends up at the same place as diff, but by pairing up
+ // the unique 'b's and then matching the first two 'a's.
+ besti, bestj, bestsize := alo, blo, 0
+
+ // find longest junk-free match
+ // during an iteration of the loop, j2len[j] = length of longest
+ // junk-free match ending with a[i-1] and b[j]
+ j2len := map[int]int{}
+ for i := alo; i != ahi; i++ {
+ // look at all instances of a[i] in b; note that because
+ // b2j has no junk keys, the loop is skipped if a[i] is junk
+ newj2len := map[int]int{}
+ for _, j := range m.b2j[m.a[i]] {
+ // a[i] matches b[j]
+ if j < blo {
+ continue
+ }
+ if j >= bhi {
+ break
+ }
+ k := j2len[j-1] + 1
+ newj2len[j] = k
+ if k > bestsize {
+ besti, bestj, bestsize = i-k+1, j-k+1, k
+ }
+ }
+ j2len = newj2len
+ }
+
+ // Extend the best by non-junk elements on each end. In particular,
+ // "popular" non-junk elements aren't in b2j, which greatly speeds
+ // the inner loop above, but also means "the best" match so far
+ // doesn't contain any junk *or* popular non-junk elements.
+ for besti > alo && bestj > blo && !m.isBJunk(m.b[bestj-1]) &&
+ m.a[besti-1] == m.b[bestj-1] {
+ besti, bestj, bestsize = besti-1, bestj-1, bestsize+1
+ }
+ for besti+bestsize < ahi && bestj+bestsize < bhi &&
+ !m.isBJunk(m.b[bestj+bestsize]) &&
+ m.a[besti+bestsize] == m.b[bestj+bestsize] {
+ bestsize += 1
+ }
+
+ // Now that we have a wholly interesting match (albeit possibly
+ // empty!), we may as well suck up the matching junk on each
+ // side of it too. Can't think of a good reason not to, and it
+ // saves post-processing the (possibly considerable) expense of
+ // figuring out what to do with it. In the case of an empty
+ // interesting match, this is clearly the right thing to do,
+ // because no other kind of match is possible in the regions.
+ for besti > alo && bestj > blo && m.isBJunk(m.b[bestj-1]) &&
+ m.a[besti-1] == m.b[bestj-1] {
+ besti, bestj, bestsize = besti-1, bestj-1, bestsize+1
+ }
+ for besti+bestsize < ahi && bestj+bestsize < bhi &&
+ m.isBJunk(m.b[bestj+bestsize]) &&
+ m.a[besti+bestsize] == m.b[bestj+bestsize] {
+ bestsize += 1
+ }
+
+ return Match{A: besti, B: bestj, Size: bestsize}
+}
+
+// Return list of triples describing matching subsequences.
+//
+// Each triple is of the form (i, j, n), and means that
+// a[i:i+n] == b[j:j+n]. The triples are monotonically increasing in
+// i and in j. It's also guaranteed that if (i, j, n) and (i', j', n') are
+// adjacent triples in the list, and the second is not the last triple in the
+// list, then i+n != i' or j+n != j'. IOW, adjacent triples never describe
+// adjacent equal blocks.
+//
+// The last triple is a dummy, (len(a), len(b), 0), and is the only
+// triple with n==0.
+func (m *SequenceMatcher) GetMatchingBlocks() []Match {
+ if m.matchingBlocks != nil {
+ return m.matchingBlocks
+ }
+
+ var matchBlocks func(alo, ahi, blo, bhi int, matched []Match) []Match
+ matchBlocks = func(alo, ahi, blo, bhi int, matched []Match) []Match {
+ match := m.findLongestMatch(alo, ahi, blo, bhi)
+ i, j, k := match.A, match.B, match.Size
+ if match.Size > 0 {
+ if alo < i && blo < j {
+ matched = matchBlocks(alo, i, blo, j, matched)
+ }
+ matched = append(matched, match)
+ if i+k < ahi && j+k < bhi {
+ matched = matchBlocks(i+k, ahi, j+k, bhi, matched)
+ }
+ }
+ return matched
+ }
+ matched := matchBlocks(0, len(m.a), 0, len(m.b), nil)
+
+ // It's possible that we have adjacent equal blocks in the
+ // matching_blocks list now.
+ nonAdjacent := []Match{}
+ i1, j1, k1 := 0, 0, 0
+ for _, b := range matched {
+ // Is this block adjacent to i1, j1, k1?
+ i2, j2, k2 := b.A, b.B, b.Size
+ if i1+k1 == i2 && j1+k1 == j2 {
+ // Yes, so collapse them -- this just increases the length of
+ // the first block by the length of the second, and the first
+ // block so lengthened remains the block to compare against.
+ k1 += k2
+ } else {
+ // Not adjacent. Remember the first block (k1==0 means it's
+ // the dummy we started with), and make the second block the
+ // new block to compare against.
+ if k1 > 0 {
+ nonAdjacent = append(nonAdjacent, Match{i1, j1, k1})
+ }
+ i1, j1, k1 = i2, j2, k2
+ }
+ }
+ if k1 > 0 {
+ nonAdjacent = append(nonAdjacent, Match{i1, j1, k1})
+ }
+
+ nonAdjacent = append(nonAdjacent, Match{len(m.a), len(m.b), 0})
+ m.matchingBlocks = nonAdjacent
+ return m.matchingBlocks
+}
+
+// Return list of 5-tuples describing how to turn a into b.
+//
+// Each tuple is of the form (tag, i1, i2, j1, j2). The first tuple
+// has i1 == j1 == 0, and remaining tuples have i1 == the i2 from the
+// tuple preceding it, and likewise for j1 == the previous j2.
+//
+// The tags are characters, with these meanings:
+//
+// 'r' (replace): a[i1:i2] should be replaced by b[j1:j2]
+//
+// 'd' (delete): a[i1:i2] should be deleted, j1==j2 in this case.
+//
+// 'i' (insert): b[j1:j2] should be inserted at a[i1:i1], i1==i2 in this case.
+//
+// 'e' (equal): a[i1:i2] == b[j1:j2]
+func (m *SequenceMatcher) GetOpCodes() []OpCode {
+ if m.opCodes != nil {
+ return m.opCodes
+ }
+ i, j := 0, 0
+ matching := m.GetMatchingBlocks()
+ opCodes := make([]OpCode, 0, len(matching))
+ for _, m := range matching {
+ // invariant: we've pumped out correct diffs to change
+ // a[:i] into b[:j], and the next matching block is
+ // a[ai:ai+size] == b[bj:bj+size]. So we need to pump
+ // out a diff to change a[i:ai] into b[j:bj], pump out
+ // the matching block, and move (i,j) beyond the match
+ ai, bj, size := m.A, m.B, m.Size
+ tag := byte(0)
+ if i < ai && j < bj {
+ tag = 'r'
+ } else if i < ai {
+ tag = 'd'
+ } else if j < bj {
+ tag = 'i'
+ }
+ if tag > 0 {
+ opCodes = append(opCodes, OpCode{tag, i, ai, j, bj})
+ }
+ i, j = ai+size, bj+size
+ // the list of matching blocks is terminated by a
+ // sentinel with size 0
+ if size > 0 {
+ opCodes = append(opCodes, OpCode{'e', ai, i, bj, j})
+ }
+ }
+ m.opCodes = opCodes
+ return m.opCodes
+}
+
+// Isolate change clusters by eliminating ranges with no changes.
+//
+// Return a generator of groups with up to n lines of context.
+// Each group is in the same format as returned by GetOpCodes().
+func (m *SequenceMatcher) GetGroupedOpCodes(n int) [][]OpCode {
+ if n < 0 {
+ n = 3
+ }
+ codes := m.GetOpCodes()
+ if len(codes) == 0 {
+ codes = []OpCode{OpCode{'e', 0, 1, 0, 1}}
+ }
+ // Fixup leading and trailing groups if they show no changes.
+ if codes[0].Tag == 'e' {
+ c := codes[0]
+ i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2
+ codes[0] = OpCode{c.Tag, max(i1, i2-n), i2, max(j1, j2-n), j2}
+ }
+ if codes[len(codes)-1].Tag == 'e' {
+ c := codes[len(codes)-1]
+ i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2
+ codes[len(codes)-1] = OpCode{c.Tag, i1, min(i2, i1+n), j1, min(j2, j1+n)}
+ }
+ nn := n + n
+ groups := [][]OpCode{}
+ group := []OpCode{}
+ for _, c := range codes {
+ i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2
+ // End the current group and start a new one whenever
+ // there is a large range with no changes.
+ if c.Tag == 'e' && i2-i1 > nn {
+ group = append(group, OpCode{c.Tag, i1, min(i2, i1+n),
+ j1, min(j2, j1+n)})
+ groups = append(groups, group)
+ group = []OpCode{}
+ i1, j1 = max(i1, i2-n), max(j1, j2-n)
+ }
+ group = append(group, OpCode{c.Tag, i1, i2, j1, j2})
+ }
+ if len(group) > 0 && !(len(group) == 1 && group[0].Tag == 'e') {
+ groups = append(groups, group)
+ }
+ return groups
+}
+
+// Return a measure of the sequences' similarity (float in [0,1]).
+//
+// Where T is the total number of elements in both sequences, and
+// M is the number of matches, this is 2.0*M / T.
+// Note that this is 1 if the sequences are identical, and 0 if
+// they have nothing in common.
+//
+// .Ratio() is expensive to compute if you haven't already computed
+// .GetMatchingBlocks() or .GetOpCodes(), in which case you may
+// want to try .QuickRatio() or .RealQuickRation() first to get an
+// upper bound.
+func (m *SequenceMatcher) Ratio() float64 {
+ matches := 0
+ for _, m := range m.GetMatchingBlocks() {
+ matches += m.Size
+ }
+ return calculateRatio(matches, len(m.a)+len(m.b))
+}
+
+// Return an upper bound on ratio() relatively quickly.
+//
+// This isn't defined beyond that it is an upper bound on .Ratio(), and
+// is faster to compute.
+func (m *SequenceMatcher) QuickRatio() float64 {
+ // viewing a and b as multisets, set matches to the cardinality
+ // of their intersection; this counts the number of matches
+ // without regard to order, so is clearly an upper bound
+ if m.fullBCount == nil {
+ m.fullBCount = map[string]int{}
+ for _, s := range m.b {
+ m.fullBCount[s] = m.fullBCount[s] + 1
+ }
+ }
+
+ // avail[x] is the number of times x appears in 'b' less the
+ // number of times we've seen it in 'a' so far ... kinda
+ avail := map[string]int{}
+ matches := 0
+ for _, s := range m.a {
+ n, ok := avail[s]
+ if !ok {
+ n = m.fullBCount[s]
+ }
+ avail[s] = n - 1
+ if n > 0 {
+ matches += 1
+ }
+ }
+ return calculateRatio(matches, len(m.a)+len(m.b))
+}
+
+// Return an upper bound on ratio() very quickly.
+//
+// This isn't defined beyond that it is an upper bound on .Ratio(), and
+// is faster to compute than either .Ratio() or .QuickRatio().
+func (m *SequenceMatcher) RealQuickRatio() float64 {
+ la, lb := len(m.a), len(m.b)
+ return calculateRatio(min(la, lb), la+lb)
+}
+
+// Convert range to the "ed" format
+func formatRangeUnified(start, stop int) string {
+ // Per the diff spec at http://www.unix.org/single_unix_specification/
+ beginning := start + 1 // lines start numbering with one
+ length := stop - start
+ if length == 1 {
+ return fmt.Sprintf("%d", beginning)
+ }
+ if length == 0 {
+ beginning -= 1 // empty ranges begin at line just before the range
+ }
+ return fmt.Sprintf("%d,%d", beginning, length)
+}
+
+// Unified diff parameters
+type UnifiedDiff struct {
+ A []string // First sequence lines
+ FromFile string // First file name
+ FromDate string // First file time
+ B []string // Second sequence lines
+ ToFile string // Second file name
+ ToDate string // Second file time
+ Eol string // Headers end of line, defaults to LF
+ Context int // Number of context lines
+}
+
+// Compare two sequences of lines; generate the delta as a unified diff.
+//
+// Unified diffs are a compact way of showing line changes and a few
+// lines of context. The number of context lines is set by 'n' which
+// defaults to three.
+//
+// By default, the diff control lines (those with ---, +++, or @@) are
+// created with a trailing newline. This is helpful so that inputs
+// created from file.readlines() result in diffs that are suitable for
+// file.writelines() since both the inputs and outputs have trailing
+// newlines.
+//
+// For inputs that do not have trailing newlines, set the lineterm
+// argument to "" so that the output will be uniformly newline free.
+//
+// The unidiff format normally has a header for filenames and modification
+// times. Any or all of these may be specified using strings for
+// 'fromfile', 'tofile', 'fromfiledate', and 'tofiledate'.
+// The modification times are normally expressed in the ISO 8601 format.
+func WriteUnifiedDiff(writer io.Writer, diff UnifiedDiff) error {
+ buf := bufio.NewWriter(writer)
+ defer buf.Flush()
+ w := func(format string, args ...interface{}) error {
+ _, err := buf.WriteString(fmt.Sprintf(format, args...))
+ return err
+ }
+
+ if len(diff.Eol) == 0 {
+ diff.Eol = "\n"
+ }
+
+ started := false
+ m := NewMatcher(diff.A, diff.B)
+ for _, g := range m.GetGroupedOpCodes(diff.Context) {
+ if !started {
+ started = true
+ fromDate := ""
+ if len(diff.FromDate) > 0 {
+ fromDate = "\t" + diff.FromDate
+ }
+ toDate := ""
+ if len(diff.ToDate) > 0 {
+ toDate = "\t" + diff.ToDate
+ }
+ err := w("--- %s%s%s", diff.FromFile, fromDate, diff.Eol)
+ if err != nil {
+ return err
+ }
+ err = w("+++ %s%s%s", diff.ToFile, toDate, diff.Eol)
+ if err != nil {
+ return err
+ }
+ }
+ first, last := g[0], g[len(g)-1]
+ range1 := formatRangeUnified(first.I1, last.I2)
+ range2 := formatRangeUnified(first.J1, last.J2)
+ if err := w("@@ -%s +%s @@%s", range1, range2, diff.Eol); err != nil {
+ return err
+ }
+ for _, c := range g {
+ i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2
+ if c.Tag == 'e' {
+ for _, line := range diff.A[i1:i2] {
+ if err := w(" " + line); err != nil {
+ return err
+ }
+ }
+ continue
+ }
+ if c.Tag == 'r' || c.Tag == 'd' {
+ for _, line := range diff.A[i1:i2] {
+ if err := w("-" + line); err != nil {
+ return err
+ }
+ }
+ }
+ if c.Tag == 'r' || c.Tag == 'i' {
+ for _, line := range diff.B[j1:j2] {
+ if err := w("+" + line); err != nil {
+ return err
+ }
+ }
+ }
+ }
+ }
+ return nil
+}
+
+// Like WriteUnifiedDiff but returns the diff a string.
+func GetUnifiedDiffString(diff UnifiedDiff) (string, error) {
+ w := &bytes.Buffer{}
+ err := WriteUnifiedDiff(w, diff)
+ return string(w.Bytes()), err
+}
+
+// Convert range to the "ed" format.
+func formatRangeContext(start, stop int) string {
+ // Per the diff spec at http://www.unix.org/single_unix_specification/
+ beginning := start + 1 // lines start numbering with one
+ length := stop - start
+ if length == 0 {
+ beginning -= 1 // empty ranges begin at line just before the range
+ }
+ if length <= 1 {
+ return fmt.Sprintf("%d", beginning)
+ }
+ return fmt.Sprintf("%d,%d", beginning, beginning+length-1)
+}
+
+type ContextDiff UnifiedDiff
+
+// Compare two sequences of lines; generate the delta as a context diff.
+//
+// Context diffs are a compact way of showing line changes and a few
+// lines of context. The number of context lines is set by diff.Context
+// which defaults to three.
+//
+// By default, the diff control lines (those with *** or ---) are
+// created with a trailing newline.
+//
+// For inputs that do not have trailing newlines, set the diff.Eol
+// argument to "" so that the output will be uniformly newline free.
+//
+// The context diff format normally has a header for filenames and
+// modification times. Any or all of these may be specified using
+// strings for diff.FromFile, diff.ToFile, diff.FromDate, diff.ToDate.
+// The modification times are normally expressed in the ISO 8601 format.
+// If not specified, the strings default to blanks.
+func WriteContextDiff(writer io.Writer, diff ContextDiff) error {
+ buf := bufio.NewWriter(writer)
+ defer buf.Flush()
+ var diffErr error
+ w := func(format string, args ...interface{}) {
+ _, err := buf.WriteString(fmt.Sprintf(format, args...))
+ if diffErr == nil && err != nil {
+ diffErr = err
+ }
+ }
+
+ if len(diff.Eol) == 0 {
+ diff.Eol = "\n"
+ }
+
+ prefix := map[byte]string{
+ 'i': "+ ",
+ 'd': "- ",
+ 'r': "! ",
+ 'e': " ",
+ }
+
+ started := false
+ m := NewMatcher(diff.A, diff.B)
+ for _, g := range m.GetGroupedOpCodes(diff.Context) {
+ if !started {
+ started = true
+ fromDate := ""
+ if len(diff.FromDate) > 0 {
+ fromDate = "\t" + diff.FromDate
+ }
+ toDate := ""
+ if len(diff.ToDate) > 0 {
+ toDate = "\t" + diff.ToDate
+ }
+ w("*** %s%s%s", diff.FromFile, fromDate, diff.Eol)
+ w("--- %s%s%s", diff.ToFile, toDate, diff.Eol)
+ }
+
+ first, last := g[0], g[len(g)-1]
+ w("***************" + diff.Eol)
+
+ range1 := formatRangeContext(first.I1, last.I2)
+ w("*** %s ****%s", range1, diff.Eol)
+ for _, c := range g {
+ if c.Tag == 'r' || c.Tag == 'd' {
+ for _, cc := range g {
+ if cc.Tag == 'i' {
+ continue
+ }
+ for _, line := range diff.A[cc.I1:cc.I2] {
+ w(prefix[cc.Tag] + line)
+ }
+ }
+ break
+ }
+ }
+
+ range2 := formatRangeContext(first.J1, last.J2)
+ w("--- %s ----%s", range2, diff.Eol)
+ for _, c := range g {
+ if c.Tag == 'r' || c.Tag == 'i' {
+ for _, cc := range g {
+ if cc.Tag == 'd' {
+ continue
+ }
+ for _, line := range diff.B[cc.J1:cc.J2] {
+ w(prefix[cc.Tag] + line)
+ }
+ }
+ break
+ }
+ }
+ }
+ return diffErr
+}
+
+// Like WriteContextDiff but returns the diff a string.
+func GetContextDiffString(diff ContextDiff) (string, error) {
+ w := &bytes.Buffer{}
+ err := WriteContextDiff(w, diff)
+ return string(w.Bytes()), err
+}
+
+// Split a string on "\n" while preserving them. The output can be used
+// as input for UnifiedDiff and ContextDiff structures.
+func SplitLines(s string) []string {
+ lines := strings.SplitAfter(s, "\n")
+ lines[len(lines)-1] += "\n"
+ return lines
+}
diff --git a/vendor/github.com/pmezard/go-difflib/difflib/difflib_test.go b/vendor/github.com/pmezard/go-difflib/difflib/difflib_test.go
new file mode 100644
index 000000000..94670bea3
--- /dev/null
+++ b/vendor/github.com/pmezard/go-difflib/difflib/difflib_test.go
@@ -0,0 +1,352 @@
+package difflib
+
+import (
+ "bytes"
+ "fmt"
+ "math"
+ "reflect"
+ "strings"
+ "testing"
+)
+
+func assertAlmostEqual(t *testing.T, a, b float64, places int) {
+ if math.Abs(a-b) > math.Pow10(-places) {
+ t.Errorf("%.7f != %.7f", a, b)
+ }
+}
+
+func assertEqual(t *testing.T, a, b interface{}) {
+ if !reflect.DeepEqual(a, b) {
+ t.Errorf("%v != %v", a, b)
+ }
+}
+
+func splitChars(s string) []string {
+ chars := make([]string, 0, len(s))
+ // Assume ASCII inputs
+ for i := 0; i != len(s); i++ {
+ chars = append(chars, string(s[i]))
+ }
+ return chars
+}
+
+func TestSequenceMatcherRatio(t *testing.T) {
+ s := NewMatcher(splitChars("abcd"), splitChars("bcde"))
+ assertEqual(t, s.Ratio(), 0.75)
+ assertEqual(t, s.QuickRatio(), 0.75)
+ assertEqual(t, s.RealQuickRatio(), 1.0)
+}
+
+func TestGetOptCodes(t *testing.T) {
+ a := "qabxcd"
+ b := "abycdf"
+ s := NewMatcher(splitChars(a), splitChars(b))
+ w := &bytes.Buffer{}
+ for _, op := range s.GetOpCodes() {
+ fmt.Fprintf(w, "%s a[%d:%d], (%s) b[%d:%d] (%s)\n", string(op.Tag),
+ op.I1, op.I2, a[op.I1:op.I2], op.J1, op.J2, b[op.J1:op.J2])
+ }
+ result := string(w.Bytes())
+ expected := `d a[0:1], (q) b[0:0] ()
+e a[1:3], (ab) b[0:2] (ab)
+r a[3:4], (x) b[2:3] (y)
+e a[4:6], (cd) b[3:5] (cd)
+i a[6:6], () b[5:6] (f)
+`
+ if expected != result {
+ t.Errorf("unexpected op codes: \n%s", result)
+ }
+}
+
+func TestGroupedOpCodes(t *testing.T) {
+ a := []string{}
+ for i := 0; i != 39; i++ {
+ a = append(a, fmt.Sprintf("%02d", i))
+ }
+ b := []string{}
+ b = append(b, a[:8]...)
+ b = append(b, " i")
+ b = append(b, a[8:19]...)
+ b = append(b, " x")
+ b = append(b, a[20:22]...)
+ b = append(b, a[27:34]...)
+ b = append(b, " y")
+ b = append(b, a[35:]...)
+ s := NewMatcher(a, b)
+ w := &bytes.Buffer{}
+ for _, g := range s.GetGroupedOpCodes(-1) {
+ fmt.Fprintf(w, "group\n")
+ for _, op := range g {
+ fmt.Fprintf(w, " %s, %d, %d, %d, %d\n", string(op.Tag),
+ op.I1, op.I2, op.J1, op.J2)
+ }
+ }
+ result := string(w.Bytes())
+ expected := `group
+ e, 5, 8, 5, 8
+ i, 8, 8, 8, 9
+ e, 8, 11, 9, 12
+group
+ e, 16, 19, 17, 20
+ r, 19, 20, 20, 21
+ e, 20, 22, 21, 23
+ d, 22, 27, 23, 23
+ e, 27, 30, 23, 26
+group
+ e, 31, 34, 27, 30
+ r, 34, 35, 30, 31
+ e, 35, 38, 31, 34
+`
+ if expected != result {
+ t.Errorf("unexpected op codes: \n%s", result)
+ }
+}
+
+func ExampleGetUnifiedDiffString() {
+ a := `one
+two
+three
+four`
+ b := `zero
+one
+three
+four`
+ diff := UnifiedDiff{
+ A: SplitLines(a),
+ B: SplitLines(b),
+ FromFile: "Original",
+ FromDate: "2005-01-26 23:30:50",
+ ToFile: "Current",
+ ToDate: "2010-04-02 10:20:52",
+ Context: 3,
+ }
+ result, _ := GetUnifiedDiffString(diff)
+ fmt.Printf(strings.Replace(result, "\t", " ", -1))
+ // Output:
+ // --- Original 2005-01-26 23:30:50
+ // +++ Current 2010-04-02 10:20:52
+ // @@ -1,4 +1,4 @@
+ // +zero
+ // one
+ // -two
+ // three
+ // four
+}
+
+func ExampleGetContextDiffString() {
+ a := `one
+two
+three
+four`
+ b := `zero
+one
+tree
+four`
+ diff := ContextDiff{
+ A: SplitLines(a),
+ B: SplitLines(b),
+ FromFile: "Original",
+ ToFile: "Current",
+ Context: 3,
+ Eol: "\n",
+ }
+ result, _ := GetContextDiffString(diff)
+ fmt.Printf(strings.Replace(result, "\t", " ", -1))
+ // Output:
+ // *** Original
+ // --- Current
+ // ***************
+ // *** 1,4 ****
+ // one
+ // ! two
+ // ! three
+ // four
+ // --- 1,4 ----
+ // + zero
+ // one
+ // ! tree
+ // four
+}
+
+func rep(s string, count int) string {
+ return strings.Repeat(s, count)
+}
+
+func TestWithAsciiOneInsert(t *testing.T) {
+ sm := NewMatcher(splitChars(rep("b", 100)),
+ splitChars("a"+rep("b", 100)))
+ assertAlmostEqual(t, sm.Ratio(), 0.995, 3)
+ assertEqual(t, sm.GetOpCodes(),
+ []OpCode{{'i', 0, 0, 0, 1}, {'e', 0, 100, 1, 101}})
+ assertEqual(t, len(sm.bPopular), 0)
+
+ sm = NewMatcher(splitChars(rep("b", 100)),
+ splitChars(rep("b", 50)+"a"+rep("b", 50)))
+ assertAlmostEqual(t, sm.Ratio(), 0.995, 3)
+ assertEqual(t, sm.GetOpCodes(),
+ []OpCode{{'e', 0, 50, 0, 50}, {'i', 50, 50, 50, 51}, {'e', 50, 100, 51, 101}})
+ assertEqual(t, len(sm.bPopular), 0)
+}
+
+func TestWithAsciiOnDelete(t *testing.T) {
+ sm := NewMatcher(splitChars(rep("a", 40)+"c"+rep("b", 40)),
+ splitChars(rep("a", 40)+rep("b", 40)))
+ assertAlmostEqual(t, sm.Ratio(), 0.994, 3)
+ assertEqual(t, sm.GetOpCodes(),
+ []OpCode{{'e', 0, 40, 0, 40}, {'d', 40, 41, 40, 40}, {'e', 41, 81, 40, 80}})
+}
+
+func TestWithAsciiBJunk(t *testing.T) {
+ isJunk := func(s string) bool {
+ return s == " "
+ }
+ sm := NewMatcherWithJunk(splitChars(rep("a", 40)+rep("b", 40)),
+ splitChars(rep("a", 44)+rep("b", 40)), true, isJunk)
+ assertEqual(t, sm.bJunk, map[string]struct{}{})
+
+ sm = NewMatcherWithJunk(splitChars(rep("a", 40)+rep("b", 40)),
+ splitChars(rep("a", 44)+rep("b", 40)+rep(" ", 20)), false, isJunk)
+ assertEqual(t, sm.bJunk, map[string]struct{}{" ": struct{}{}})
+
+ isJunk = func(s string) bool {
+ return s == " " || s == "b"
+ }
+ sm = NewMatcherWithJunk(splitChars(rep("a", 40)+rep("b", 40)),
+ splitChars(rep("a", 44)+rep("b", 40)+rep(" ", 20)), false, isJunk)
+ assertEqual(t, sm.bJunk, map[string]struct{}{" ": struct{}{}, "b": struct{}{}})
+}
+
+func TestSFBugsRatioForNullSeqn(t *testing.T) {
+ sm := NewMatcher(nil, nil)
+ assertEqual(t, sm.Ratio(), 1.0)
+ assertEqual(t, sm.QuickRatio(), 1.0)
+ assertEqual(t, sm.RealQuickRatio(), 1.0)
+}
+
+func TestSFBugsComparingEmptyLists(t *testing.T) {
+ groups := NewMatcher(nil, nil).GetGroupedOpCodes(-1)
+ assertEqual(t, len(groups), 0)
+ diff := UnifiedDiff{
+ FromFile: "Original",
+ ToFile: "Current",
+ Context: 3,
+ }
+ result, err := GetUnifiedDiffString(diff)
+ assertEqual(t, err, nil)
+ assertEqual(t, result, "")
+}
+
+func TestOutputFormatRangeFormatUnified(t *testing.T) {
+ // Per the diff spec at http://www.unix.org/single_unix_specification/
+ //
+ // Each <range> field shall be of the form:
+ // %1d", <beginning line number> if the range contains exactly one line,
+ // and:
+ // "%1d,%1d", <beginning line number>, <number of lines> otherwise.
+ // If a range is empty, its beginning line number shall be the number of
+ // the line just before the range, or 0 if the empty range starts the file.
+ fm := formatRangeUnified
+ assertEqual(t, fm(3, 3), "3,0")
+ assertEqual(t, fm(3, 4), "4")
+ assertEqual(t, fm(3, 5), "4,2")
+ assertEqual(t, fm(3, 6), "4,3")
+ assertEqual(t, fm(0, 0), "0,0")
+}
+
+func TestOutputFormatRangeFormatContext(t *testing.T) {
+ // Per the diff spec at http://www.unix.org/single_unix_specification/
+ //
+ // The range of lines in file1 shall be written in the following format
+ // if the range contains two or more lines:
+ // "*** %d,%d ****\n", <beginning line number>, <ending line number>
+ // and the following format otherwise:
+ // "*** %d ****\n", <ending line number>
+ // The ending line number of an empty range shall be the number of the preceding line,
+ // or 0 if the range is at the start of the file.
+ //
+ // Next, the range of lines in file2 shall be written in the following format
+ // if the range contains two or more lines:
+ // "--- %d,%d ----\n", <beginning line number>, <ending line number>
+ // and the following format otherwise:
+ // "--- %d ----\n", <ending line number>
+ fm := formatRangeContext
+ assertEqual(t, fm(3, 3), "3")
+ assertEqual(t, fm(3, 4), "4")
+ assertEqual(t, fm(3, 5), "4,5")
+ assertEqual(t, fm(3, 6), "4,6")
+ assertEqual(t, fm(0, 0), "0")
+}
+
+func TestOutputFormatTabDelimiter(t *testing.T) {
+ diff := UnifiedDiff{
+ A: splitChars("one"),
+ B: splitChars("two"),
+ FromFile: "Original",
+ FromDate: "2005-01-26 23:30:50",
+ ToFile: "Current",
+ ToDate: "2010-04-12 10:20:52",
+ Eol: "\n",
+ }
+ ud, err := GetUnifiedDiffString(diff)
+ assertEqual(t, err, nil)
+ assertEqual(t, SplitLines(ud)[:2], []string{
+ "--- Original\t2005-01-26 23:30:50\n",
+ "+++ Current\t2010-04-12 10:20:52\n",
+ })
+ cd, err := GetContextDiffString(ContextDiff(diff))
+ assertEqual(t, err, nil)
+ assertEqual(t, SplitLines(cd)[:2], []string{
+ "*** Original\t2005-01-26 23:30:50\n",
+ "--- Current\t2010-04-12 10:20:52\n",
+ })
+}
+
+func TestOutputFormatNoTrailingTabOnEmptyFiledate(t *testing.T) {
+ diff := UnifiedDiff{
+ A: splitChars("one"),
+ B: splitChars("two"),
+ FromFile: "Original",
+ ToFile: "Current",
+ Eol: "\n",
+ }
+ ud, err := GetUnifiedDiffString(diff)
+ assertEqual(t, err, nil)
+ assertEqual(t, SplitLines(ud)[:2], []string{"--- Original\n", "+++ Current\n"})
+
+ cd, err := GetContextDiffString(ContextDiff(diff))
+ assertEqual(t, err, nil)
+ assertEqual(t, SplitLines(cd)[:2], []string{"*** Original\n", "--- Current\n"})
+}
+
+func TestSplitLines(t *testing.T) {
+ allTests := []struct {
+ input string
+ want []string
+ }{
+ {"foo", []string{"foo\n"}},
+ {"foo\nbar", []string{"foo\n", "bar\n"}},
+ {"foo\nbar\n", []string{"foo\n", "bar\n", "\n"}},
+ }
+ for _, test := range allTests {
+ assertEqual(t, SplitLines(test.input), test.want)
+ }
+}
+
+func benchmarkSplitLines(b *testing.B, count int) {
+ str := strings.Repeat("foo\n", count)
+
+ b.ResetTimer()
+
+ n := 0
+ for i := 0; i < b.N; i++ {
+ n += len(SplitLines(str))
+ }
+}
+
+func BenchmarkSplitLines100(b *testing.B) {
+ benchmarkSplitLines(b, 100)
+}
+
+func BenchmarkSplitLines10000(b *testing.B) {
+ benchmarkSplitLines(b, 10000)
+}
diff --git a/vendor/github.com/prometheus/common/log/log.go b/vendor/github.com/prometheus/common/log/log.go
index 1321741ad..04e906c07 100644
--- a/vendor/github.com/prometheus/common/log/log.go
+++ b/vendor/github.com/prometheus/common/log/log.go
@@ -14,7 +14,6 @@
package log
import (
- "flag"
"fmt"
"io"
"io/ioutil"
@@ -26,25 +25,9 @@ import (
"strings"
"github.com/sirupsen/logrus"
+ "gopkg.in/alecthomas/kingpin.v2"
)
-type levelFlag string
-
-// String implements flag.Value.
-func (f levelFlag) String() string {
- return fmt.Sprintf("%q", origLogger.Level.String())
-}
-
-// Set implements flag.Value.
-func (f levelFlag) Set(level string) error {
- l, err := logrus.ParseLevel(level)
- if err != nil {
- return err
- }
- origLogger.Level = l
- return nil
-}
-
// setSyslogFormatter is nil if the target architecture does not support syslog.
var setSyslogFormatter func(logger, string, string) error
@@ -55,38 +38,32 @@ func setJSONFormatter() {
origLogger.Formatter = &logrus.JSONFormatter{}
}
-type logFormatFlag url.URL
-
-// String implements flag.Value.
-func (f logFormatFlag) String() string {
- u := url.URL(f)
- return fmt.Sprintf("%q", u.String())
-}
-
-// Set implements flag.Value.
-func (f logFormatFlag) Set(format string) error {
- return baseLogger.SetFormat(format)
-}
-
-func init() {
- AddFlags(flag.CommandLine)
+type loggerSettings struct {
+ level string
+ format string
}
-// AddFlags adds the flags used by this package to the given FlagSet. That's
-// useful if working with a custom FlagSet. The init function of this package
-// adds the flags to flag.CommandLine anyway. Thus, it's usually enough to call
-// flag.Parse() to make the logging flags take effect.
-func AddFlags(fs *flag.FlagSet) {
- fs.Var(
- levelFlag(origLogger.Level.String()),
- "log.level",
- "Only log messages with the given severity or above. Valid levels: [debug, info, warn, error, fatal]",
- )
- fs.Var(
- logFormatFlag(url.URL{Scheme: "logger", Opaque: "stderr"}),
- "log.format",
- `Set the log target and format. Example: "logger:syslog?appname=bob&local=7" or "logger:stdout?json=true"`,
- )
+func (s *loggerSettings) apply(ctx *kingpin.ParseContext) error {
+ err := baseLogger.SetLevel(s.level)
+ if err != nil {
+ return err
+ }
+ err = baseLogger.SetFormat(s.format)
+ return err
+}
+
+// AddFlags adds the flags used by this package to the Kingpin application.
+// To use the default Kingpin application, call AddFlags(kingpin.CommandLine)
+func AddFlags(a *kingpin.Application) {
+ s := loggerSettings{}
+ kingpin.Flag("log.level", "Only log messages with the given severity or above. Valid levels: [debug, info, warn, error, fatal]").
+ Default(origLogger.Level.String()).
+ StringVar(&s.level)
+ defaultFormat := url.URL{Scheme: "logger", Opaque: "stderr"}
+ kingpin.Flag("log.format", `Set the log target and format. Example: "logger:syslog?appname=bob&local=7" or "logger:stdout?json=true"`).
+ Default(defaultFormat.String()).
+ StringVar(&s.format)
+ a.Action(s.apply)
}
// Logger is the interface for loggers used in the Prometheus components.
diff --git a/vendor/github.com/prometheus/common/log/log_test.go b/vendor/github.com/prometheus/common/log/log_test.go
index 2cd2b18e2..f63b4417f 100644
--- a/vendor/github.com/prometheus/common/log/log_test.go
+++ b/vendor/github.com/prometheus/common/log/log_test.go
@@ -32,7 +32,7 @@ func TestFileLineLogging(t *testing.T) {
Debug("This debug-level line should not show up in the output.")
Infof("This %s-level line should show up in the output.", "info")
- re := `^time=".*" level=info msg="This info-level line should show up in the output." source="log_test.go:33" \n$`
+ re := `^time=".*" level=info msg="This info-level line should show up in the output." source="log_test.go:33"\n$`
if !regexp.MustCompile(re).Match(buf.Bytes()) {
t.Fatalf("%q did not match expected regex %q", buf.String(), re)
}
diff --git a/vendor/github.com/spf13/cobra/.travis.yml b/vendor/github.com/spf13/cobra/.travis.yml
index cb2bf0d5c..68efa1363 100644
--- a/vendor/github.com/spf13/cobra/.travis.yml
+++ b/vendor/github.com/spf13/cobra/.travis.yml
@@ -2,8 +2,8 @@ language: go
matrix:
include:
- - go: 1.7.5
- - go: 1.8.1
+ - go: 1.7.6
+ - go: 1.8.3
- go: tip
allow_failures:
- go: tip
diff --git a/vendor/github.com/spf13/cobra/README.md b/vendor/github.com/spf13/cobra/README.md
index e249c1bcb..a38137b25 100644
--- a/vendor/github.com/spf13/cobra/README.md
+++ b/vendor/github.com/spf13/cobra/README.md
@@ -485,6 +485,36 @@ when the `--author` flag is not provided by user.
More in [viper documentation](https://github.com/spf13/viper#working-with-flags).
+## Positional and Custom Arguments
+
+Validation of positional arguments can be specified using the `Args` field.
+
+The follow validators are built in:
+
+- `NoArgs` - the command will report an error if there are any positional args.
+- `ArbitraryArgs` - the command will accept any args.
+- `OnlyValidArgs` - the command will report an error if there are any positional args that are not in the ValidArgs list.
+- `MinimumNArgs(int)` - the command will report an error if there are not at least N positional args.
+- `MaximumNArgs(int)` - the command will report an error if there are more than N positional args.
+- `ExactArgs(int)` - the command will report an error if there are not exactly N positional args.
+- `RangeArgs(min, max)` - the command will report an error if the number of args is not between the minimum and maximum number of expected args.
+
+A custom validator can be provided like this:
+
+```go
+
+Args: func validColorArgs(cmd *cobra.Command, args []string) error {
+ if err := cli.RequiresMinArgs(1)(cmd, args); err != nil {
+ return err
+ }
+ if myapp.IsValidColor(args[0]) {
+ return nil
+ }
+ return fmt.Errorf("Invalid color specified: %s", args[0])
+}
+
+```
+
## Example
In the example below, we have defined three commands. Two are at the top level
diff --git a/vendor/github.com/spf13/cobra/args.go b/vendor/github.com/spf13/cobra/args.go
new file mode 100644
index 000000000..94a6ca273
--- /dev/null
+++ b/vendor/github.com/spf13/cobra/args.go
@@ -0,0 +1,98 @@
+package cobra
+
+import (
+ "fmt"
+)
+
+type PositionalArgs func(cmd *Command, args []string) error
+
+// Legacy arg validation has the following behaviour:
+// - root commands with no subcommands can take arbitrary arguments
+// - root commands with subcommands will do subcommand validity checking
+// - subcommands will always accept arbitrary arguments
+func legacyArgs(cmd *Command, args []string) error {
+ // no subcommand, always take args
+ if !cmd.HasSubCommands() {
+ return nil
+ }
+
+ // root command with subcommands, do subcommand checking
+ if !cmd.HasParent() && len(args) > 0 {
+ return fmt.Errorf("unknown command %q for %q%s", args[0], cmd.CommandPath(), cmd.findSuggestions(args[0]))
+ }
+ return nil
+}
+
+// NoArgs returns an error if any args are included
+func NoArgs(cmd *Command, args []string) error {
+ if len(args) > 0 {
+ return fmt.Errorf("unknown command %q for %q", args[0], cmd.CommandPath())
+ }
+ return nil
+}
+
+// OnlyValidArgs returns an error if any args are not in the list of ValidArgs
+func OnlyValidArgs(cmd *Command, args []string) error {
+ if len(cmd.ValidArgs) > 0 {
+ for _, v := range args {
+ if !stringInSlice(v, cmd.ValidArgs) {
+ return fmt.Errorf("invalid argument %q for %q%s", v, cmd.CommandPath(), cmd.findSuggestions(args[0]))
+ }
+ }
+ }
+ return nil
+}
+
+func stringInSlice(a string, list []string) bool {
+ for _, b := range list {
+ if b == a {
+ return true
+ }
+ }
+ return false
+}
+
+// ArbitraryArgs never returns an error
+func ArbitraryArgs(cmd *Command, args []string) error {
+ return nil
+}
+
+// MinimumNArgs returns an error if there is not at least N args
+func MinimumNArgs(n int) PositionalArgs {
+ return func(cmd *Command, args []string) error {
+ if len(args) < n {
+ return fmt.Errorf("requires at least %d arg(s), only received %d", n, len(args))
+ }
+ return nil
+ }
+}
+
+// MaximumNArgs returns an error if there are more than N args
+func MaximumNArgs(n int) PositionalArgs {
+ return func(cmd *Command, args []string) error {
+ if len(args) > n {
+ return fmt.Errorf("accepts at most %d arg(s), received %d", n, len(args))
+ }
+ return nil
+ }
+}
+
+// ExactArgs returns an error if there are not exactly n args
+func ExactArgs(n int) PositionalArgs {
+ return func(cmd *Command, args []string) error {
+ if len(args) != n {
+ return fmt.Errorf("accepts %d arg(s), received %d", n, len(args))
+ }
+ return nil
+ }
+}
+
+// RangeArgs returns an error if the number of args is not within the expected range
+func RangeArgs(min int, max int) PositionalArgs {
+ return func(cmd *Command, args []string) error {
+ if len(args) < min || len(args) > max {
+ return fmt.Errorf("accepts between %d and %d arg(s), received %d", min, max, len(args))
+ }
+ return nil
+ }
+}
diff --git a/vendor/github.com/spf13/cobra/bash_completions_test.go b/vendor/github.com/spf13/cobra/bash_completions_test.go
index 7511376ae..071a6a2a2 100644
--- a/vendor/github.com/spf13/cobra/bash_completions_test.go
+++ b/vendor/github.com/spf13/cobra/bash_completions_test.go
@@ -117,6 +117,8 @@ func TestBashCompletions(t *testing.T) {
// check for filename extension flags
check(t, str, `flags_completion+=("_filedir")`)
// check for filename extension flags
+ check(t, str, `must_have_one_noun+=("three")`)
+ // check for filename extention flags
check(t, str, `flags_completion+=("__handle_filename_extension_flag json|yaml|yml")`)
// check for custom flags
check(t, str, `flags_completion+=("__complete_custom")`)
diff --git a/vendor/github.com/spf13/cobra/cobra/cmd/add.go b/vendor/github.com/spf13/cobra/cobra/cmd/add.go
index 45f00bb54..993ae16f0 100644
--- a/vendor/github.com/spf13/cobra/cobra/cmd/add.go
+++ b/vendor/github.com/spf13/cobra/cobra/cmd/add.go
@@ -24,7 +24,7 @@ import (
func init() {
addCmd.Flags().StringVarP(&packageName, "package", "t", "", "target package name (e.g. github.com/spf13/hugo)")
- addCmd.Flags().StringVarP(&parentName, "parent", "p", "RootCmd", "name of parent command for this command")
+ addCmd.Flags().StringVarP(&parentName, "parent", "p", "RootCmd", "variable name of parent command for this command")
}
var packageName, parentName string
@@ -121,7 +121,7 @@ func validateCmdName(source string) string {
func createCmdFile(license License, path, cmdName string) {
template := `{{comment .copyright}}
-{{comment .license}}
+{{if .license}}{{comment .license}}{{end}}
package {{.cmdPackage}}
diff --git a/vendor/github.com/spf13/cobra/cobra/cmd/add_test.go b/vendor/github.com/spf13/cobra/cobra/cmd/add_test.go
index dacbe838a..b920e2b9d 100644
--- a/vendor/github.com/spf13/cobra/cobra/cmd/add_test.go
+++ b/vendor/github.com/spf13/cobra/cobra/cmd/add_test.go
@@ -6,6 +6,8 @@ import (
"os"
"path/filepath"
"testing"
+
+ "github.com/spf13/viper"
)
// TestGoldenAddCmd initializes the project "github.com/spf13/testproject"
@@ -16,10 +18,17 @@ import (
func TestGoldenAddCmd(t *testing.T) {
projectName := "github.com/spf13/testproject"
project := NewProject(projectName)
+ defer os.RemoveAll(project.AbsPath())
- // Initialize the project at first.
+ viper.Set("author", "NAME HERE <EMAIL ADDRESS>")
+ viper.Set("license", "apache")
+ viper.Set("year", 2017)
+ defer viper.Set("author", nil)
+ defer viper.Set("license", nil)
+ defer viper.Set("year", nil)
+
+ // Initialize the project first.
initializeProject(project)
- defer os.RemoveAll(project.AbsPath())
// Then add the "test" command.
cmdName := "test"
@@ -48,7 +57,7 @@ func TestGoldenAddCmd(t *testing.T) {
goldenPath := filepath.Join("testdata", filepath.Base(path)+".golden")
switch relPath {
- // Know directories.
+ // Known directories.
case ".":
return nil
// Known files.
diff --git a/vendor/github.com/spf13/cobra/cobra/cmd/golden_test.go b/vendor/github.com/spf13/cobra/cobra/cmd/golden_test.go
index 0ac7e8935..59a5a1c9f 100644
--- a/vendor/github.com/spf13/cobra/cobra/cmd/golden_test.go
+++ b/vendor/github.com/spf13/cobra/cobra/cmd/golden_test.go
@@ -39,11 +39,11 @@ func compareFiles(pathA, pathB string) error {
// Don't execute diff if it can't be found.
return nil
}
- diffCmd := exec.Command(diffPath, pathA, pathB)
+ diffCmd := exec.Command(diffPath, "-u", pathA, pathB)
diffCmd.Stdout = output
diffCmd.Stderr = output
- output.WriteString("$ diff " + pathA + " " + pathB + "\n")
+ output.WriteString("$ diff -u " + pathA + " " + pathB + "\n")
if err := diffCmd.Run(); err != nil {
output.WriteString("\n" + err.Error())
}
diff --git a/vendor/github.com/spf13/cobra/cobra/cmd/init_test.go b/vendor/github.com/spf13/cobra/cobra/cmd/init_test.go
index 9a918b9b4..40eb4038e 100644
--- a/vendor/github.com/spf13/cobra/cobra/cmd/init_test.go
+++ b/vendor/github.com/spf13/cobra/cobra/cmd/init_test.go
@@ -6,6 +6,8 @@ import (
"os"
"path/filepath"
"testing"
+
+ "github.com/spf13/viper"
)
// TestGoldenInitCmd initializes the project "github.com/spf13/testproject"
@@ -17,6 +19,13 @@ func TestGoldenInitCmd(t *testing.T) {
project := NewProject(projectName)
defer os.RemoveAll(project.AbsPath())
+ viper.Set("author", "NAME HERE <EMAIL ADDRESS>")
+ viper.Set("license", "apache")
+ viper.Set("year", 2017)
+ defer viper.Set("author", nil)
+ defer viper.Set("license", nil)
+ defer viper.Set("year", nil)
+
os.Args = []string{"cobra", "init", projectName}
if err := rootCmd.Execute(); err != nil {
t.Fatal("Error by execution:", err)
@@ -44,7 +53,7 @@ func TestGoldenInitCmd(t *testing.T) {
goldenPath := filepath.Join("testdata", filepath.Base(path)+".golden")
switch relPath {
- // Know directories.
+ // Known directories.
case ".", "cmd":
return nil
// Known files.
diff --git a/vendor/github.com/spf13/cobra/cobra/cmd/license_agpl.go b/vendor/github.com/spf13/cobra/cobra/cmd/license_agpl.go
index 4ea036ede..bc22e9732 100644
--- a/vendor/github.com/spf13/cobra/cobra/cmd/license_agpl.go
+++ b/vendor/github.com/spf13/cobra/cobra/cmd/license_agpl.go
@@ -4,8 +4,7 @@ func initAgpl() {
Licenses["agpl"] = License{
Name: "GNU Affero General Public License",
PossibleMatches: []string{"agpl", "affero gpl", "gnu agpl"},
- Header: `{{.copyright}}
-
+ Header: `
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
diff --git a/vendor/github.com/spf13/cobra/cobra/cmd/license_apache_2.go b/vendor/github.com/spf13/cobra/cobra/cmd/license_apache_2.go
index 3f330867d..38393d541 100644
--- a/vendor/github.com/spf13/cobra/cobra/cmd/license_apache_2.go
+++ b/vendor/github.com/spf13/cobra/cobra/cmd/license_apache_2.go
@@ -19,7 +19,8 @@ func initApache2() {
Licenses["apache"] = License{
Name: "Apache 2.0",
PossibleMatches: []string{"apache", "apache20", "apache 2.0", "apache2.0", "apache-2.0"},
- Header: `Licensed under the Apache License, Version 2.0 (the "License");
+ Header: `
+Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
diff --git a/vendor/github.com/spf13/cobra/cobra/cmd/license_bsd_clause_2.go b/vendor/github.com/spf13/cobra/cobra/cmd/license_bsd_clause_2.go
index f2982dab3..4a847e04a 100644
--- a/vendor/github.com/spf13/cobra/cobra/cmd/license_bsd_clause_2.go
+++ b/vendor/github.com/spf13/cobra/cobra/cmd/license_bsd_clause_2.go
@@ -20,8 +20,7 @@ func initBsdClause2() {
Name: "Simplified BSD License",
PossibleMatches: []string{"freebsd", "simpbsd", "simple bsd", "2-clause bsd",
"2 clause bsd", "simplified bsd license"},
- Header: `
-All rights reserved.
+ Header: `All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
diff --git a/vendor/github.com/spf13/cobra/cobra/cmd/license_bsd_clause_3.go b/vendor/github.com/spf13/cobra/cobra/cmd/license_bsd_clause_3.go
index 39c9571f2..c7476b31f 100644
--- a/vendor/github.com/spf13/cobra/cobra/cmd/license_bsd_clause_3.go
+++ b/vendor/github.com/spf13/cobra/cobra/cmd/license_bsd_clause_3.go
@@ -19,8 +19,7 @@ func initBsdClause3() {
Licenses["bsd"] = License{
Name: "NewBSD",
PossibleMatches: []string{"bsd", "newbsd", "3 clause bsd", "3-clause bsd"},
- Header: `
-All rights reserved.
+ Header: `All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
diff --git a/vendor/github.com/spf13/cobra/cobra/cmd/license_gpl_2.go b/vendor/github.com/spf13/cobra/cobra/cmd/license_gpl_2.go
index 054b470f1..03e05b3a7 100644
--- a/vendor/github.com/spf13/cobra/cobra/cmd/license_gpl_2.go
+++ b/vendor/github.com/spf13/cobra/cobra/cmd/license_gpl_2.go
@@ -19,20 +19,19 @@ func initGpl2() {
Licenses["gpl2"] = License{
Name: "GNU General Public License 2.0",
PossibleMatches: []string{"gpl2", "gnu gpl2", "gplv2"},
- Header: `{{.copyright}}
-
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License
- as published by the Free Software Foundation; either version 2
- of the License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.`,
+ Header: `
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.`,
Text: ` GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
diff --git a/vendor/github.com/spf13/cobra/cobra/cmd/license_gpl_3.go b/vendor/github.com/spf13/cobra/cobra/cmd/license_gpl_3.go
index d1ef656a7..ce07679c7 100644
--- a/vendor/github.com/spf13/cobra/cobra/cmd/license_gpl_3.go
+++ b/vendor/github.com/spf13/cobra/cobra/cmd/license_gpl_3.go
@@ -19,8 +19,7 @@ func initGpl3() {
Licenses["gpl3"] = License{
Name: "GNU General Public License 3.0",
PossibleMatches: []string{"gpl3", "gplv3", "gpl", "gnu gpl3", "gnu gpl"},
- Header: `{{.copyright}}
-
+ Header: `
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
diff --git a/vendor/github.com/spf13/cobra/cobra/cmd/license_lgpl.go b/vendor/github.com/spf13/cobra/cobra/cmd/license_lgpl.go
index 75fd043b8..0f8b96cad 100644
--- a/vendor/github.com/spf13/cobra/cobra/cmd/license_lgpl.go
+++ b/vendor/github.com/spf13/cobra/cobra/cmd/license_lgpl.go
@@ -4,8 +4,7 @@ func initLgpl() {
Licenses["lgpl"] = License{
Name: "GNU Lesser General Public License",
PossibleMatches: []string{"lgpl", "lesser gpl", "gnu lgpl"},
- Header: `{{.copyright}}
-
+ Header: `
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
diff --git a/vendor/github.com/spf13/cobra/cobra/cmd/license_mit.go b/vendor/github.com/spf13/cobra/cobra/cmd/license_mit.go
index 4ff5a4cdf..bd2d0c4fa 100644
--- a/vendor/github.com/spf13/cobra/cobra/cmd/license_mit.go
+++ b/vendor/github.com/spf13/cobra/cobra/cmd/license_mit.go
@@ -17,7 +17,7 @@ package cmd
func initMit() {
Licenses["mit"] = License{
- Name: "Mit",
+ Name: "MIT License",
PossibleMatches: []string{"mit"},
Header: `
Permission is hereby granted, free of charge, to any person obtaining a copy
diff --git a/vendor/github.com/spf13/cobra/cobra/cmd/licenses.go b/vendor/github.com/spf13/cobra/cobra/cmd/licenses.go
index d73e6fb35..cf2a6b7af 100644
--- a/vendor/github.com/spf13/cobra/cobra/cmd/licenses.go
+++ b/vendor/github.com/spf13/cobra/cobra/cmd/licenses.go
@@ -77,7 +77,11 @@ func getLicense() License {
func copyrightLine() string {
author := viper.GetString("author")
- year := time.Now().Format("2006")
+
+ year := viper.GetString("year") // For tests.
+ if year == "" {
+ year = time.Now().Format("2006")
+ }
return "Copyright © " + year + " " + author
}
diff --git a/vendor/github.com/spf13/cobra/cobra/cmd/root.go b/vendor/github.com/spf13/cobra/cobra/cmd/root.go
index 1c5e69078..19568f980 100644
--- a/vendor/github.com/spf13/cobra/cobra/cmd/root.go
+++ b/vendor/github.com/spf13/cobra/cobra/cmd/root.go
@@ -40,7 +40,7 @@ func Execute() {
}
func init() {
- initViper()
+ cobra.OnInitialize(initConfig)
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.cobra.yaml)")
rootCmd.PersistentFlags().StringP("author", "a", "YOUR NAME", "author name for copyright attribution")
@@ -55,7 +55,7 @@ func init() {
rootCmd.AddCommand(initCmd)
}
-func initViper() {
+func initConfig() {
if cfgFile != "" {
// Use config file from the flag.
viper.SetConfigFile(cfgFile)
diff --git a/vendor/github.com/spf13/cobra/cobra/cmd/testdata/main.go.golden b/vendor/github.com/spf13/cobra/cobra/cmd/testdata/main.go.golden
index 69ecbd48e..cdbe38d70 100644
--- a/vendor/github.com/spf13/cobra/cobra/cmd/testdata/main.go.golden
+++ b/vendor/github.com/spf13/cobra/cobra/cmd/testdata/main.go.golden
@@ -1,4 +1,5 @@
// Copyright © 2017 NAME HERE <EMAIL ADDRESS>
+//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
diff --git a/vendor/github.com/spf13/cobra/cobra/cmd/testdata/root.go.golden b/vendor/github.com/spf13/cobra/cobra/cmd/testdata/root.go.golden
index ecc876012..8eeeae89e 100644
--- a/vendor/github.com/spf13/cobra/cobra/cmd/testdata/root.go.golden
+++ b/vendor/github.com/spf13/cobra/cobra/cmd/testdata/root.go.golden
@@ -1,4 +1,5 @@
// Copyright © 2017 NAME HERE <EMAIL ADDRESS>
+//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
diff --git a/vendor/github.com/spf13/cobra/cobra/cmd/testdata/test.go.golden b/vendor/github.com/spf13/cobra/cobra/cmd/testdata/test.go.golden
index c8319d1dd..584056802 100644
--- a/vendor/github.com/spf13/cobra/cobra/cmd/testdata/test.go.golden
+++ b/vendor/github.com/spf13/cobra/cobra/cmd/testdata/test.go.golden
@@ -1,4 +1,5 @@
// Copyright © 2017 NAME HERE <EMAIL ADDRESS>
+//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
diff --git a/vendor/github.com/spf13/cobra/cobra_test.go b/vendor/github.com/spf13/cobra/cobra_test.go
index 576c97d36..d5df951e6 100644
--- a/vendor/github.com/spf13/cobra/cobra_test.go
+++ b/vendor/github.com/spf13/cobra/cobra_test.go
@@ -36,6 +36,7 @@ var cmdHidden = &Command{
var cmdPrint = &Command{
Use: "print [string to print]",
+ Args: MinimumNArgs(1),
Short: "Print anything to the screen",
Long: `an absolutely utterly useless command for testing.`,
Run: func(cmd *Command, args []string) {
@@ -75,6 +76,7 @@ var cmdDeprecated = &Command{
Deprecated: "Please use echo instead",
Run: func(cmd *Command, args []string) {
},
+ Args: NoArgs,
}
var cmdTimes = &Command{
@@ -88,6 +90,8 @@ var cmdTimes = &Command{
Run: func(cmd *Command, args []string) {
tt = args
},
+ Args: OnlyValidArgs,
+ ValidArgs: []string{"one", "two", "three", "four"},
}
var cmdRootNoRun = &Command{
@@ -105,6 +109,16 @@ var cmdRootSameName = &Command{
Long: "The root description for help",
}
+var cmdRootTakesArgs = &Command{
+ Use: "root-with-args [random args]",
+ Short: "The root can run it's own function and takes args!",
+ Long: "The root description for help, and some args",
+ Run: func(cmd *Command, args []string) {
+ tr = args
+ },
+ Args: ArbitraryArgs,
+}
+
var cmdRootWithRun = &Command{
Use: "cobra-test",
Short: "The root can run its own function",
@@ -458,6 +472,63 @@ func TestUsage(t *testing.T) {
checkResultOmits(t, x, cmdCustomFlags.Use+" [flags]")
}
+func TestRootTakesNoArgs(t *testing.T) {
+ c := initializeWithSameName()
+ c.AddCommand(cmdPrint, cmdEcho)
+ result := simpleTester(c, "illegal")
+
+ if result.Error == nil {
+ t.Fatal("Expected an error")
+ }
+
+ expectedError := `unknown command "illegal" for "print"`
+ if !strings.Contains(result.Error.Error(), expectedError) {
+ t.Errorf("exptected %v, got %v", expectedError, result.Error.Error())
+ }
+}
+
+func TestRootTakesArgs(t *testing.T) {
+ c := cmdRootTakesArgs
+ result := simpleTester(c, "legal")
+
+ if result.Error != nil {
+ t.Errorf("expected no error, but got %v", result.Error)
+ }
+}
+
+func TestSubCmdTakesNoArgs(t *testing.T) {
+ result := fullSetupTest("deprecated", "illegal")
+
+ if result.Error == nil {
+ t.Fatal("Expected an error")
+ }
+
+ expectedError := `unknown command "illegal" for "cobra-test deprecated"`
+ if !strings.Contains(result.Error.Error(), expectedError) {
+ t.Errorf("expected %v, got %v", expectedError, result.Error.Error())
+ }
+}
+
+func TestSubCmdTakesArgs(t *testing.T) {
+ noRRSetupTest("echo", "times", "one", "two")
+ if strings.Join(tt, " ") != "one two" {
+ t.Error("Command didn't parse correctly")
+ }
+}
+
+func TestCmdOnlyValidArgs(t *testing.T) {
+ result := noRRSetupTest("echo", "times", "one", "two", "five")
+
+ if result.Error == nil {
+ t.Fatal("Expected an error")
+ }
+
+ expectedError := `invalid argument "five"`
+ if !strings.Contains(result.Error.Error(), expectedError) {
+ t.Errorf("expected %v, got %v", expectedError, result.Error.Error())
+ }
+}
+
func TestFlagLong(t *testing.T) {
noRRSetupTest("echo", "--intone=13", "something", "--", "here")
@@ -672,9 +743,9 @@ func TestPersistentFlags(t *testing.T) {
}
// persistentFlag should act like normal flag on its own command
- fullSetupTest("echo", "times", "-s", "again", "-c", "-p", "test", "here")
+ fullSetupTest("echo", "times", "-s", "again", "-c", "-p", "one", "two")
- if strings.Join(tt, " ") != "test here" {
+ if strings.Join(tt, " ") != "one two" {
t.Errorf("flags didn't leave proper args remaining. %s given", tt)
}
@@ -1095,7 +1166,7 @@ func TestGlobalNormFuncPropagation(t *testing.T) {
rootCmd := initialize()
rootCmd.SetGlobalNormalizationFunc(normFunc)
- if reflect.ValueOf(normFunc) != reflect.ValueOf(rootCmd.GlobalNormalizationFunc()) {
+ if reflect.ValueOf(normFunc).Pointer() != reflect.ValueOf(rootCmd.GlobalNormalizationFunc()).Pointer() {
t.Error("rootCmd seems to have a wrong normalization function")
}
diff --git a/vendor/github.com/spf13/cobra/command.go b/vendor/github.com/spf13/cobra/command.go
index c3e16b1d1..4f65d7708 100644
--- a/vendor/github.com/spf13/cobra/command.go
+++ b/vendor/github.com/spf13/cobra/command.go
@@ -59,6 +59,8 @@ type Command struct {
// but accepted if entered manually.
ArgAliases []string
+ // Expected arguments
+ Args PositionalArgs
// BashCompletionFunction is custom functions used by the bash autocompletion generator.
BashCompletionFunction string
@@ -513,31 +515,27 @@ func (c *Command) Find(args []string) (*Command, []string, error) {
}
commandFound, a := innerfind(c, args)
- argsWOflags := stripFlags(a, commandFound)
-
- // no subcommand, always take args
- if !commandFound.HasSubCommands() {
- return commandFound, a, nil
+ if commandFound.Args == nil {
+ return commandFound, a, legacyArgs(commandFound, stripFlags(a, commandFound))
}
+ return commandFound, a, nil
+}
- // root command with subcommands, do subcommand checking
- if commandFound == c && len(argsWOflags) > 0 {
- suggestionsString := ""
- if !c.DisableSuggestions {
- if c.SuggestionsMinimumDistance <= 0 {
- c.SuggestionsMinimumDistance = 2
- }
- if suggestions := c.SuggestionsFor(argsWOflags[0]); len(suggestions) > 0 {
- suggestionsString += "\n\nDid you mean this?\n"
- for _, s := range suggestions {
- suggestionsString += fmt.Sprintf("\t%v\n", s)
- }
- }
+func (c *Command) findSuggestions(arg string) string {
+ if c.DisableSuggestions {
+ return ""
+ }
+ if c.SuggestionsMinimumDistance <= 0 {
+ c.SuggestionsMinimumDistance = 2
+ }
+ suggestionsString := ""
+ if suggestions := c.SuggestionsFor(arg); len(suggestions) > 0 {
+ suggestionsString += "\n\nDid you mean this?\n"
+ for _, s := range suggestions {
+ suggestionsString += fmt.Sprintf("\t%v\n", s)
}
- return commandFound, a, fmt.Errorf("unknown command %q for %q%s", argsWOflags[0], commandFound.CommandPath(), suggestionsString)
}
-
- return commandFound, a, nil
+ return suggestionsString
}
// SuggestionsFor provides suggestions for the typedName.
@@ -624,6 +622,10 @@ func (c *Command) execute(a []string) (err error) {
argWoFlags = a
}
+ if err := c.ValidateArgs(argWoFlags); err != nil {
+ return err
+ }
+
for p := c; p != nil; p = p.Parent() {
if p.PersistentPreRunE != nil {
if err := p.PersistentPreRunE(c, argWoFlags); err != nil {
@@ -747,6 +749,13 @@ func (c *Command) ExecuteC() (cmd *Command, err error) {
return cmd, err
}
+func (c *Command) ValidateArgs(args []string) error {
+ if c.Args == nil {
+ return nil
+ }
+ return c.Args(c, args)
+}
+
// InitDefaultHelpFlag adds default help flag to c.
// It is called automatically by executing the c or by calling help and usage.
// If c already has help flag, it will do nothing.
diff --git a/vendor/github.com/spf13/cobra/zsh_completions.go b/vendor/github.com/spf13/cobra/zsh_completions.go
new file mode 100644
index 000000000..b350aeeca
--- /dev/null
+++ b/vendor/github.com/spf13/cobra/zsh_completions.go
@@ -0,0 +1,114 @@
+package cobra
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "strings"
+)
+
+// GenZshCompletion generates a zsh completion file and writes to the passed writer.
+func (cmd *Command) GenZshCompletion(w io.Writer) error {
+ buf := new(bytes.Buffer)
+
+ writeHeader(buf, cmd)
+ maxDepth := maxDepth(cmd)
+ writeLevelMapping(buf, maxDepth)
+ writeLevelCases(buf, maxDepth, cmd)
+
+ _, err := buf.WriteTo(w)
+ return err
+}
+
+func writeHeader(w io.Writer, cmd *Command) {
+ fmt.Fprintf(w, "#compdef %s\n\n", cmd.Name())
+}
+
+func maxDepth(c *Command) int {
+ if len(c.Commands()) == 0 {
+ return 0
+ }
+ maxDepthSub := 0
+ for _, s := range c.Commands() {
+ subDepth := maxDepth(s)
+ if subDepth > maxDepthSub {
+ maxDepthSub = subDepth
+ }
+ }
+ return 1 + maxDepthSub
+}
+
+func writeLevelMapping(w io.Writer, numLevels int) {
+ fmt.Fprintln(w, `_arguments \`)
+ for i := 1; i <= numLevels; i++ {
+ fmt.Fprintf(w, ` '%d: :->level%d' \`, i, i)
+ fmt.Fprintln(w)
+ }
+ fmt.Fprintf(w, ` '%d: :%s'`, numLevels+1, "_files")
+ fmt.Fprintln(w)
+}
+
+func writeLevelCases(w io.Writer, maxDepth int, root *Command) {
+ fmt.Fprintln(w, "case $state in")
+ defer fmt.Fprintln(w, "esac")
+
+ for i := 1; i <= maxDepth; i++ {
+ fmt.Fprintf(w, " level%d)\n", i)
+ writeLevel(w, root, i)
+ fmt.Fprintln(w, " ;;")
+ }
+ fmt.Fprintln(w, " *)")
+ fmt.Fprintln(w, " _arguments '*: :_files'")
+ fmt.Fprintln(w, " ;;")
+}
+
+func writeLevel(w io.Writer, root *Command, i int) {
+ fmt.Fprintf(w, " case $words[%d] in\n", i)
+ defer fmt.Fprintln(w, " esac")
+
+ commands := filterByLevel(root, i)
+ byParent := groupByParent(commands)
+
+ for p, c := range byParent {
+ names := names(c)
+ fmt.Fprintf(w, " %s)\n", p)
+ fmt.Fprintf(w, " _arguments '%d: :(%s)'\n", i, strings.Join(names, " "))
+ fmt.Fprintln(w, " ;;")
+ }
+ fmt.Fprintln(w, " *)")
+ fmt.Fprintln(w, " _arguments '*: :_files'")
+ fmt.Fprintln(w, " ;;")
+
+}
+
+func filterByLevel(c *Command, l int) []*Command {
+ cs := make([]*Command, 0)
+ if l == 0 {
+ cs = append(cs, c)
+ return cs
+ }
+ for _, s := range c.Commands() {
+ cs = append(cs, filterByLevel(s, l-1)...)
+ }
+ return cs
+}
+
+func groupByParent(commands []*Command) map[string][]*Command {
+ m := make(map[string][]*Command)
+ for _, c := range commands {
+ parent := c.Parent()
+ if parent == nil {
+ continue
+ }
+ m[parent.Name()] = append(m[parent.Name()], c)
+ }
+ return m
+}
+
+func names(commands []*Command) []string {
+ ns := make([]string, len(commands))
+ for i, c := range commands {
+ ns[i] = c.Name()
+ }
+ return ns
+}
diff --git a/vendor/github.com/spf13/cobra/zsh_completions_test.go b/vendor/github.com/spf13/cobra/zsh_completions_test.go
new file mode 100644
index 000000000..08b851591
--- /dev/null
+++ b/vendor/github.com/spf13/cobra/zsh_completions_test.go
@@ -0,0 +1,88 @@
+package cobra
+
+import (
+ "bytes"
+ "strings"
+ "testing"
+)
+
+func TestZshCompletion(t *testing.T) {
+ tcs := []struct {
+ name string
+ root *Command
+ expectedExpressions []string
+ }{
+ {
+ name: "trivial",
+ root: &Command{Use: "trivialapp"},
+ expectedExpressions: []string{"#compdef trivial"},
+ },
+ {
+ name: "linear",
+ root: func() *Command {
+ r := &Command{Use: "linear"}
+
+ sub1 := &Command{Use: "sub1"}
+ r.AddCommand(sub1)
+
+ sub2 := &Command{Use: "sub2"}
+ sub1.AddCommand(sub2)
+
+ sub3 := &Command{Use: "sub3"}
+ sub2.AddCommand(sub3)
+ return r
+ }(),
+ expectedExpressions: []string{"sub1", "sub2", "sub3"},
+ },
+ {
+ name: "flat",
+ root: func() *Command {
+ r := &Command{Use: "flat"}
+ r.AddCommand(&Command{Use: "c1"})
+ r.AddCommand(&Command{Use: "c2"})
+ return r
+ }(),
+ expectedExpressions: []string{"(c1 c2)"},
+ },
+ {
+ name: "tree",
+ root: func() *Command {
+ r := &Command{Use: "tree"}
+
+ sub1 := &Command{Use: "sub1"}
+ r.AddCommand(sub1)
+
+ sub11 := &Command{Use: "sub11"}
+ sub12 := &Command{Use: "sub12"}
+
+ sub1.AddCommand(sub11)
+ sub1.AddCommand(sub12)
+
+ sub2 := &Command{Use: "sub2"}
+ r.AddCommand(sub2)
+
+ sub21 := &Command{Use: "sub21"}
+ sub22 := &Command{Use: "sub22"}
+
+ sub2.AddCommand(sub21)
+ sub2.AddCommand(sub22)
+
+ return r
+ }(),
+ expectedExpressions: []string{"(sub11 sub12)", "(sub21 sub22)"},
+ },
+ }
+
+ for _, tc := range tcs {
+ t.Run(tc.name, func(t *testing.T) {
+ buf := new(bytes.Buffer)
+ tc.root.GenZshCompletion(buf)
+ completion := buf.String()
+ for _, expectedExpression := range tc.expectedExpressions {
+ if !strings.Contains(completion, expectedExpression) {
+ t.Errorf("expected completion to contain '%v' somewhere; got '%v'", expectedExpression, completion)
+ }
+ }
+ })
+ }
+}
diff --git a/vendor/github.com/spf13/viper/.travis.yml b/vendor/github.com/spf13/viper/.travis.yml
index d4c2559c2..f1deac3d7 100644
--- a/vendor/github.com/spf13/viper/.travis.yml
+++ b/vendor/github.com/spf13/viper/.travis.yml
@@ -17,6 +17,7 @@ matrix:
script:
- go install ./...
+ - diff -u <(echo -n) <(gofmt -d .)
- go test -v ./...
after_success:
diff --git a/vendor/github.com/spf13/viper/README.md b/vendor/github.com/spf13/viper/README.md
index 577088fbb..848d92d6b 100644
--- a/vendor/github.com/spf13/viper/README.md
+++ b/vendor/github.com/spf13/viper/README.md
@@ -6,18 +6,19 @@ Many Go projects are built using Viper including:
* [Hugo](http://gohugo.io)
* [EMC RexRay](http://rexray.readthedocs.org/en/stable/)
-* [Imgur's Incus](https://github.com/Imgur/incus)
+* [Imgur’s Incus](https://github.com/Imgur/incus)
* [Nanobox](https://github.com/nanobox-io/nanobox)/[Nanopack](https://github.com/nanopack)
* [Docker Notary](https://github.com/docker/Notary)
* [BloomApi](https://www.bloomapi.com/)
* [doctl](https://github.com/digitalocean/doctl)
+* [Clairctl](https://github.com/jgsqware/clairctl)
[![Build Status](https://travis-ci.org/spf13/viper.svg)](https://travis-ci.org/spf13/viper) [![Join the chat at https://gitter.im/spf13/viper](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/spf13/viper?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![GoDoc](https://godoc.org/github.com/spf13/viper?status.svg)](https://godoc.org/github.com/spf13/viper)
## What is Viper?
-Viper is a complete configuration solution for go applications including 12 factor apps. It is designed
+Viper is a complete configuration solution for Go applications including 12-Factor apps. It is designed
to work within an application, and can handle all types of configuration needs
and formats. It supports:
@@ -68,7 +69,7 @@ Viper configuration keys are case insensitive.
### Establishing Defaults
A good configuration system will support default values. A default value is not
-required for a key, but it's useful in the event that a key hasn’t been set via
+required for a key, but it’s useful in the event that a key hasn’t been set via
config file, environment variable, remote configuration or flag.
Examples:
@@ -116,10 +117,10 @@ Optionally you can provide a function for Viper to run each time a change occurs
**Make sure you add all of the configPaths prior to calling `WatchConfig()`**
```go
- viper.WatchConfig()
- viper.OnConfigChange(func(e fsnotify.Event) {
- fmt.Println("Config file changed:", e.Name)
- })
+viper.WatchConfig()
+viper.OnConfigChange(func(e fsnotify.Event) {
+ fmt.Println("Config file changed:", e.Name)
+})
```
### Reading Config from io.Reader
@@ -236,7 +237,7 @@ Like `BindEnv`, the value is not set when the binding method is called, but when
it is accessed. This means you can bind as early as you want, even in an
`init()` function.
-The `BindPFlag()` method provides this functionality.
+For individual flags, the `BindPFlag()` method provides this functionality.
Example:
@@ -245,6 +246,19 @@ serverCmd.Flags().Int("port", 1138, "Port to run Application server on")
viper.BindPFlag("port", serverCmd.Flags().Lookup("port"))
```
+You can also bind an existing set of pflags (pflag.FlagSet):
+
+Example:
+
+```go
+pflag.Int("flagname", 1234, "help message for flagname")
+
+pflag.Parse()
+viper.BindPFlags(pflag.CommandLine)
+
+i := viper.GetInt("flagname") // retrieve values from viper instead of pflag
+```
+
The use of [pflag](https://github.com/spf13/pflag/) in Viper does not preclude
the use of other packages that use the [flag](https://golang.org/pkg/flag/)
package from the standard library. The pflag package can handle the flags
@@ -263,15 +277,23 @@ import (
)
func main() {
+
+ // using standard library "flag" package
+ flag.Int("flagname", 1234, "help message for flagname")
+
pflag.CommandLine.AddGoFlagSet(flag.CommandLine)
pflag.Parse()
- ...
+ viper.BindPFlags(pflag.CommandLine)
+
+ i := viper.GetInt("flagname") // retrieve value from viper
+
+ ...
}
```
#### Flag interfaces
-Viper provides two Go interfaces to bind other flag systems if you don't use `Pflags`.
+Viper provides two Go interfaces to bind other flag systems if you don’t use `Pflags`.
`FlagValue` represents a single flag. This is a very simple example on how to implement this interface:
@@ -401,7 +423,7 @@ go func(){
## Getting Values From Viper
-In Viper, there are a few ways to get a value depending on the value's type.
+In Viper, there are a few ways to get a value depending on the value’s type.
The following functions and methods exist:
* `Get(key string) : interface{}`
@@ -531,7 +553,7 @@ func NewCache(cfg *Viper) *Cache {...}
```
which creates a cache based on config information formatted as `subv`.
-Now it's easy to create these 2 caches separately as:
+Now it’s easy to create these 2 caches separately as:
```go
cfg1 := viper.Sub("app.cache1")
diff --git a/vendor/github.com/spf13/viper/flags_test.go b/vendor/github.com/spf13/viper/flags_test.go
index 5bffca36c..0b976b605 100644
--- a/vendor/github.com/spf13/viper/flags_test.go
+++ b/vendor/github.com/spf13/viper/flags_test.go
@@ -62,5 +62,4 @@ func TestBindFlagValue(t *testing.T) {
flag.Changed = true //hack for pflag usage
assert.Equal(t, "testing_mutate", Get("testvalue"))
-
}
diff --git a/vendor/github.com/spf13/viper/remote/remote.go b/vendor/github.com/spf13/viper/remote/remote.go
index f100a9c7f..68a35d692 100644
--- a/vendor/github.com/spf13/viper/remote/remote.go
+++ b/vendor/github.com/spf13/viper/remote/remote.go
@@ -33,13 +33,14 @@ func (rc remoteConfigProvider) Watch(rp viper.RemoteProvider) (io.Reader, error)
if err != nil {
return nil, err
}
- resp,err := cm.Get(rp.Path())
+ resp, err := cm.Get(rp.Path())
if err != nil {
return nil, err
}
return bytes.NewReader(resp), nil
}
+
func (rc remoteConfigProvider) WatchChannel(rp viper.RemoteProvider) (<-chan *viper.RemoteResponse, chan bool) {
cm, err := getConfigManager(rp)
if err != nil {
@@ -47,13 +48,13 @@ func (rc remoteConfigProvider) WatchChannel(rp viper.RemoteProvider) (<-chan *vi
}
quit := make(chan bool)
quitwc := make(chan bool)
- viperResponsCh := make(chan *viper.RemoteResponse)
+ viperResponsCh := make(chan *viper.RemoteResponse)
cryptoResponseCh := cm.Watch(rp.Path(), quit)
// need this function to convert the Channel response form crypt.Response to viper.Response
- go func(cr <-chan *crypt.Response,vr chan<- *viper.RemoteResponse, quitwc <-chan bool, quit chan<- bool) {
+ go func(cr <-chan *crypt.Response, vr chan<- *viper.RemoteResponse, quitwc <-chan bool, quit chan<- bool) {
for {
select {
- case <- quitwc:
+ case <-quitwc:
quit <- true
return
case resp := <-cr:
@@ -65,15 +66,12 @@ func (rc remoteConfigProvider) WatchChannel(rp viper.RemoteProvider) (<-chan *vi
}
}
- }(cryptoResponseCh,viperResponsCh,quitwc,quit)
-
- return viperResponsCh,quitwc
+ }(cryptoResponseCh, viperResponsCh, quitwc, quit)
+ return viperResponsCh, quitwc
}
-
func getConfigManager(rp viper.RemoteProvider) (crypt.ConfigManager, error) {
-
var cm crypt.ConfigManager
var err error
@@ -99,7 +97,6 @@ func getConfigManager(rp viper.RemoteProvider) (crypt.ConfigManager, error) {
return nil, err
}
return cm, nil
-
}
func init() {
diff --git a/vendor/github.com/spf13/viper/util_test.go b/vendor/github.com/spf13/viper/util_test.go
index 5949e09e4..0af80bb63 100644
--- a/vendor/github.com/spf13/viper/util_test.go
+++ b/vendor/github.com/spf13/viper/util_test.go
@@ -16,7 +16,6 @@ import (
)
func TestCopyAndInsensitiviseMap(t *testing.T) {
-
var (
given = map[string]interface{}{
"Foo": 32,
diff --git a/vendor/github.com/spf13/viper/viper.go b/vendor/github.com/spf13/viper/viper.go
index 39a3c06f5..2a221e53c 100644
--- a/vendor/github.com/spf13/viper/viper.go
+++ b/vendor/github.com/spf13/viper/viper.go
@@ -69,8 +69,7 @@ func (str UnsupportedConfigError) Error() string {
}
// UnsupportedRemoteProviderError denotes encountering an unsupported remote
-// provider. Currently only etcd and Consul are
-// supported.
+// provider. Currently only etcd and Consul are supported.
type UnsupportedRemoteProviderError string
// Error returns the formatted remote provider error.
@@ -283,8 +282,8 @@ func (v *Viper) WatchConfig() {
}()
}
-// SetConfigFile explicitly defines the path, name and extension of the config file
-// Viper will use this and not check any of the config paths
+// SetConfigFile explicitly defines the path, name and extension of the config file.
+// Viper will use this and not check any of the config paths.
func SetConfigFile(in string) { v.SetConfigFile(in) }
func (v *Viper) SetConfigFile(in string) {
if in != "" {
@@ -293,8 +292,8 @@ func (v *Viper) SetConfigFile(in string) {
}
// SetEnvPrefix defines a prefix that ENVIRONMENT variables will use.
-// E.g. if your prefix is "spf", the env registry
-// will look for env. variables that start with "SPF_"
+// E.g. if your prefix is "spf", the env registry will look for env
+// variables that start with "SPF_".
func SetEnvPrefix(in string) { v.SetEnvPrefix(in) }
func (v *Viper) SetEnvPrefix(in string) {
if in != "" {
@@ -312,11 +311,11 @@ func (v *Viper) mergeWithEnvPrefix(in string) string {
// TODO: should getEnv logic be moved into find(). Can generalize the use of
// rewriting keys many things, Ex: Get('someKey') -> some_key
-// (cammel case to snake case for JSON keys perhaps)
+// (camel case to snake case for JSON keys perhaps)
// getEnv is a wrapper around os.Getenv which replaces characters in the original
-// key. This allows env vars which have different keys then the config object
-// keys
+// key. This allows env vars which have different keys than the config object
+// keys.
func (v *Viper) getEnv(key string) string {
if v.envKeyReplacer != nil {
key = v.envKeyReplacer.Replace(key)
@@ -324,7 +323,7 @@ func (v *Viper) getEnv(key string) string {
return os.Getenv(key)
}
-// ConfigFileUsed returns the file used to populate the config registry
+// ConfigFileUsed returns the file used to populate the config registry.
func ConfigFileUsed() string { return v.ConfigFileUsed() }
func (v *Viper) ConfigFileUsed() string { return v.configFile }
@@ -815,7 +814,7 @@ func (v *Viper) BindFlagValues(flags FlagValueSet) (err error) {
}
// BindFlagValue binds a specific key to a FlagValue.
-// Example(where serverCmd is a Cobra instance):
+// Example (where serverCmd is a Cobra instance):
//
// serverCmd.Flags().Int("port", 1138, "Port to run Application server on")
// Viper.BindFlagValue("port", serverCmd.Flags().Lookup("port"))
@@ -1288,7 +1287,7 @@ func (v *Viper) WatchRemoteConfigOnChannel() error {
return v.watchKeyValueConfigOnChannel()
}
-// Unmarshall a Reader into a map.
+// Unmarshal a Reader into a map.
// Should probably be an unexported function.
func unmarshalReader(in io.Reader, c map[string]interface{}) error {
return v.unmarshalReader(in, c)
@@ -1547,7 +1546,6 @@ func (v *Viper) searchInPath(in string) (filename string) {
// Search all configPaths for any config file.
// Returns the first path that exists (and is a config file).
func (v *Viper) findConfigFile() (string, error) {
-
jww.INFO.Println("Searching for config in ", v.configPaths)
for _, cp := range v.configPaths {
diff --git a/vendor/github.com/stretchr/objx/.gitignore b/vendor/github.com/stretchr/objx/.gitignore
new file mode 100644
index 000000000..00268614f
--- /dev/null
+++ b/vendor/github.com/stretchr/objx/.gitignore
@@ -0,0 +1,22 @@
+# 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
diff --git a/vendor/github.com/stretchr/objx/LICENSE.md b/vendor/github.com/stretchr/objx/LICENSE.md
new file mode 100644
index 000000000..219994581
--- /dev/null
+++ b/vendor/github.com/stretchr/objx/LICENSE.md
@@ -0,0 +1,23 @@
+objx - by Mat Ryer and Tyler Bunnell
+
+The MIT License (MIT)
+
+Copyright (c) 2014 Stretchr, Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/vendor/github.com/stretchr/objx/README.md b/vendor/github.com/stretchr/objx/README.md
new file mode 100644
index 000000000..4aa180687
--- /dev/null
+++ b/vendor/github.com/stretchr/objx/README.md
@@ -0,0 +1,3 @@
+# objx
+
+ * Jump into the [API Documentation](http://godoc.org/github.com/stretchr/objx)
diff --git a/vendor/github.com/stretchr/objx/accessors.go b/vendor/github.com/stretchr/objx/accessors.go
new file mode 100644
index 000000000..721bcac79
--- /dev/null
+++ b/vendor/github.com/stretchr/objx/accessors.go
@@ -0,0 +1,179 @@
+package objx
+
+import (
+ "fmt"
+ "regexp"
+ "strconv"
+ "strings"
+)
+
+// arrayAccesRegexString is the regex used to extract the array number
+// from the access path
+const arrayAccesRegexString = `^(.+)\[([0-9]+)\]$`
+
+// arrayAccesRegex is the compiled arrayAccesRegexString
+var arrayAccesRegex = regexp.MustCompile(arrayAccesRegexString)
+
+// Get gets the value using the specified selector and
+// returns it inside a new Obj object.
+//
+// If it cannot find the value, Get will return a nil
+// value inside an instance of Obj.
+//
+// Get can only operate directly on map[string]interface{} and []interface.
+//
+// Example
+//
+// To access the title of the third chapter of the second book, do:
+//
+// o.Get("books[1].chapters[2].title")
+func (m Map) Get(selector string) *Value {
+ rawObj := access(m, selector, nil, false, false)
+ return &Value{data: rawObj}
+}
+
+// Set sets the value using the specified selector and
+// returns the object on which Set was called.
+//
+// Set can only operate directly on map[string]interface{} and []interface
+//
+// Example
+//
+// To set the title of the third chapter of the second book, do:
+//
+// o.Set("books[1].chapters[2].title","Time to Go")
+func (m Map) Set(selector string, value interface{}) Map {
+ access(m, selector, value, true, false)
+ return m
+}
+
+// access accesses the object using the selector and performs the
+// appropriate action.
+func access(current, selector, value interface{}, isSet, panics bool) interface{} {
+
+ switch selector.(type) {
+ case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:
+
+ if array, ok := current.([]interface{}); ok {
+ index := intFromInterface(selector)
+
+ if index >= len(array) {
+ if panics {
+ panic(fmt.Sprintf("objx: Index %d is out of range. Slice only contains %d items.", index, len(array)))
+ }
+ return nil
+ }
+
+ return array[index]
+ }
+
+ return nil
+
+ case string:
+
+ selStr := selector.(string)
+ selSegs := strings.SplitN(selStr, PathSeparator, 2)
+ thisSel := selSegs[0]
+ index := -1
+ var err error
+
+ // https://github.com/stretchr/objx/issues/12
+ if strings.Contains(thisSel, "[") {
+
+ arrayMatches := arrayAccesRegex.FindStringSubmatch(thisSel)
+
+ if len(arrayMatches) > 0 {
+
+ // Get the key into the map
+ thisSel = arrayMatches[1]
+
+ // Get the index into the array at the key
+ index, err = strconv.Atoi(arrayMatches[2])
+
+ if err != nil {
+ // This should never happen. If it does, something has gone
+ // seriously wrong. Panic.
+ panic("objx: Array index is not an integer. Must use array[int].")
+ }
+
+ }
+ }
+
+ if curMap, ok := current.(Map); ok {
+ current = map[string]interface{}(curMap)
+ }
+
+ // get the object in question
+ switch current.(type) {
+ case map[string]interface{}:
+ curMSI := current.(map[string]interface{})
+ if len(selSegs) <= 1 && isSet {
+ curMSI[thisSel] = value
+ return nil
+ } else {
+ current = curMSI[thisSel]
+ }
+ default:
+ current = nil
+ }
+
+ if current == nil && panics {
+ panic(fmt.Sprintf("objx: '%v' invalid on object.", selector))
+ }
+
+ // do we need to access the item of an array?
+ if index > -1 {
+ if array, ok := current.([]interface{}); ok {
+ if index < len(array) {
+ current = array[index]
+ } else {
+ if panics {
+ panic(fmt.Sprintf("objx: Index %d is out of range. Slice only contains %d items.", index, len(array)))
+ }
+ current = nil
+ }
+ }
+ }
+
+ if len(selSegs) > 1 {
+ current = access(current, selSegs[1], value, isSet, panics)
+ }
+
+ }
+
+ return current
+
+}
+
+// intFromInterface converts an interface object to the largest
+// representation of an unsigned integer using a type switch and
+// assertions
+func intFromInterface(selector interface{}) int {
+ var value int
+ switch selector.(type) {
+ case int:
+ value = selector.(int)
+ case int8:
+ value = int(selector.(int8))
+ case int16:
+ value = int(selector.(int16))
+ case int32:
+ value = int(selector.(int32))
+ case int64:
+ value = int(selector.(int64))
+ case uint:
+ value = int(selector.(uint))
+ case uint8:
+ value = int(selector.(uint8))
+ case uint16:
+ value = int(selector.(uint16))
+ case uint32:
+ value = int(selector.(uint32))
+ case uint64:
+ value = int(selector.(uint64))
+ default:
+ panic("objx: array access argument is not an integer type (this should never happen)")
+ }
+
+ return value
+}
diff --git a/vendor/github.com/stretchr/objx/accessors_test.go b/vendor/github.com/stretchr/objx/accessors_test.go
new file mode 100644
index 000000000..ce5d8e4aa
--- /dev/null
+++ b/vendor/github.com/stretchr/objx/accessors_test.go
@@ -0,0 +1,145 @@
+package objx
+
+import (
+ "github.com/stretchr/testify/assert"
+ "testing"
+)
+
+func TestAccessorsAccessGetSingleField(t *testing.T) {
+
+ current := map[string]interface{}{"name": "Tyler"}
+ assert.Equal(t, "Tyler", access(current, "name", nil, false, true))
+
+}
+func TestAccessorsAccessGetDeep(t *testing.T) {
+
+ current := map[string]interface{}{"name": map[string]interface{}{"first": "Tyler", "last": "Bunnell"}}
+ assert.Equal(t, "Tyler", access(current, "name.first", nil, false, true))
+ assert.Equal(t, "Bunnell", access(current, "name.last", nil, false, true))
+
+}
+func TestAccessorsAccessGetDeepDeep(t *testing.T) {
+
+ current := map[string]interface{}{"one": map[string]interface{}{"two": map[string]interface{}{"three": map[string]interface{}{"four": 4}}}}
+ assert.Equal(t, 4, access(current, "one.two.three.four", nil, false, true))
+
+}
+func TestAccessorsAccessGetInsideArray(t *testing.T) {
+
+ current := map[string]interface{}{"names": []interface{}{map[string]interface{}{"first": "Tyler", "last": "Bunnell"}, map[string]interface{}{"first": "Capitol", "last": "Bollocks"}}}
+ assert.Equal(t, "Tyler", access(current, "names[0].first", nil, false, true))
+ assert.Equal(t, "Bunnell", access(current, "names[0].last", nil, false, true))
+ assert.Equal(t, "Capitol", access(current, "names[1].first", nil, false, true))
+ assert.Equal(t, "Bollocks", access(current, "names[1].last", nil, false, true))
+
+ assert.Panics(t, func() {
+ access(current, "names[2]", nil, false, true)
+ })
+ assert.Nil(t, access(current, "names[2]", nil, false, false))
+
+}
+
+func TestAccessorsAccessGetFromArrayWithInt(t *testing.T) {
+
+ current := []interface{}{map[string]interface{}{"first": "Tyler", "last": "Bunnell"}, map[string]interface{}{"first": "Capitol", "last": "Bollocks"}}
+ one := access(current, 0, nil, false, false)
+ two := access(current, 1, nil, false, false)
+ three := access(current, 2, nil, false, false)
+
+ assert.Equal(t, "Tyler", one.(map[string]interface{})["first"])
+ assert.Equal(t, "Capitol", two.(map[string]interface{})["first"])
+ assert.Nil(t, three)
+
+}
+
+func TestAccessorsGet(t *testing.T) {
+
+ current := New(map[string]interface{}{"name": "Tyler"})
+ assert.Equal(t, "Tyler", current.Get("name").data)
+
+}
+
+func TestAccessorsAccessSetSingleField(t *testing.T) {
+
+ current := map[string]interface{}{"name": "Tyler"}
+ access(current, "name", "Mat", true, false)
+ assert.Equal(t, current["name"], "Mat")
+
+ access(current, "age", 29, true, true)
+ assert.Equal(t, current["age"], 29)
+
+}
+
+func TestAccessorsAccessSetSingleFieldNotExisting(t *testing.T) {
+
+ current := map[string]interface{}{}
+ access(current, "name", "Mat", true, false)
+ assert.Equal(t, current["name"], "Mat")
+
+}
+
+func TestAccessorsAccessSetDeep(t *testing.T) {
+
+ current := map[string]interface{}{"name": map[string]interface{}{"first": "Tyler", "last": "Bunnell"}}
+
+ access(current, "name.first", "Mat", true, true)
+ access(current, "name.last", "Ryer", true, true)
+
+ assert.Equal(t, "Mat", access(current, "name.first", nil, false, true))
+ assert.Equal(t, "Ryer", access(current, "name.last", nil, false, true))
+
+}
+func TestAccessorsAccessSetDeepDeep(t *testing.T) {
+
+ current := map[string]interface{}{"one": map[string]interface{}{"two": map[string]interface{}{"three": map[string]interface{}{"four": 4}}}}
+
+ access(current, "one.two.three.four", 5, true, true)
+
+ assert.Equal(t, 5, access(current, "one.two.three.four", nil, false, true))
+
+}
+func TestAccessorsAccessSetArray(t *testing.T) {
+
+ current := map[string]interface{}{"names": []interface{}{"Tyler"}}
+
+ access(current, "names[0]", "Mat", true, true)
+
+ assert.Equal(t, "Mat", access(current, "names[0]", nil, false, true))
+
+}
+func TestAccessorsAccessSetInsideArray(t *testing.T) {
+
+ current := map[string]interface{}{"names": []interface{}{map[string]interface{}{"first": "Tyler", "last": "Bunnell"}, map[string]interface{}{"first": "Capitol", "last": "Bollocks"}}}
+
+ access(current, "names[0].first", "Mat", true, true)
+ access(current, "names[0].last", "Ryer", true, true)
+ access(current, "names[1].first", "Captain", true, true)
+ access(current, "names[1].last", "Underpants", true, true)
+
+ assert.Equal(t, "Mat", access(current, "names[0].first", nil, false, true))
+ assert.Equal(t, "Ryer", access(current, "names[0].last", nil, false, true))
+ assert.Equal(t, "Captain", access(current, "names[1].first", nil, false, true))
+ assert.Equal(t, "Underpants", access(current, "names[1].last", nil, false, true))
+
+}
+
+func TestAccessorsAccessSetFromArrayWithInt(t *testing.T) {
+
+ current := []interface{}{map[string]interface{}{"first": "Tyler", "last": "Bunnell"}, map[string]interface{}{"first": "Capitol", "last": "Bollocks"}}
+ one := access(current, 0, nil, false, false)
+ two := access(current, 1, nil, false, false)
+ three := access(current, 2, nil, false, false)
+
+ assert.Equal(t, "Tyler", one.(map[string]interface{})["first"])
+ assert.Equal(t, "Capitol", two.(map[string]interface{})["first"])
+ assert.Nil(t, three)
+
+}
+
+func TestAccessorsSet(t *testing.T) {
+
+ current := New(map[string]interface{}{"name": "Tyler"})
+ current.Set("name", "Mat")
+ assert.Equal(t, "Mat", current.Get("name").data)
+
+}
diff --git a/vendor/github.com/stretchr/objx/codegen/array-access.txt b/vendor/github.com/stretchr/objx/codegen/array-access.txt
new file mode 100644
index 000000000..306023475
--- /dev/null
+++ b/vendor/github.com/stretchr/objx/codegen/array-access.txt
@@ -0,0 +1,14 @@
+ case []{1}:
+ a := object.([]{1})
+ if isSet {
+ a[index] = value.({1})
+ } else {
+ if index >= len(a) {
+ if panics {
+ panic(fmt.Sprintf("objx: Index %d is out of range because the []{1} only contains %d items.", index, len(a)))
+ }
+ return nil
+ } else {
+ return a[index]
+ }
+ }
diff --git a/vendor/github.com/stretchr/objx/codegen/index.html b/vendor/github.com/stretchr/objx/codegen/index.html
new file mode 100644
index 000000000..379ffc3c0
--- /dev/null
+++ b/vendor/github.com/stretchr/objx/codegen/index.html
@@ -0,0 +1,86 @@
+<html>
+ <head>
+ <title>
+ Codegen
+ </title>
+ <style>
+ body {
+ width: 800px;
+ margin: auto;
+ }
+ textarea {
+ width: 100%;
+ min-height: 100px;
+ font-family: Courier;
+ }
+ </style>
+ </head>
+ <body>
+
+ <h2>
+ Template
+ </h2>
+ <p>
+ Use <code>{x}</code> as a placeholder for each argument.
+ </p>
+ <textarea id="template"></textarea>
+
+ <h2>
+ Arguments (comma separated)
+ </h2>
+ <p>
+ One block per line
+ </p>
+ <textarea id="args"></textarea>
+
+ <h2>
+ Output
+ </h2>
+ <input id="go" type="button" value="Generate code" />
+
+ <textarea id="output"></textarea>
+
+ <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
+ <script>
+
+ $(function(){
+
+ $("#go").click(function(){
+
+ var output = ""
+ var template = $("#template").val()
+ var args = $("#args").val()
+
+ // collect the args
+ var argLines = args.split("\n")
+ for (var line in argLines) {
+
+ var argLine = argLines[line];
+ var thisTemp = template
+
+ // get individual args
+ var args = argLine.split(",")
+
+ for (var argI in args) {
+ var argText = args[argI];
+ var argPlaceholder = "{" + argI + "}";
+
+ while (thisTemp.indexOf(argPlaceholder) > -1) {
+ thisTemp = thisTemp.replace(argPlaceholder, argText);
+ }
+
+ }
+
+ output += thisTemp
+
+ }
+
+ $("#output").val(output);
+
+ });
+
+ });
+
+ </script>
+ </body>
+</html>
diff --git a/vendor/github.com/stretchr/objx/codegen/template.txt b/vendor/github.com/stretchr/objx/codegen/template.txt
new file mode 100644
index 000000000..b396900b8
--- /dev/null
+++ b/vendor/github.com/stretchr/objx/codegen/template.txt
@@ -0,0 +1,286 @@
+/*
+ {4} ({1} and []{1})
+ --------------------------------------------------
+*/
+
+// {4} gets the value as a {1}, returns the optionalDefault
+// value or a system default object if the value is the wrong type.
+func (v *Value) {4}(optionalDefault ...{1}) {1} {
+ if s, ok := v.data.({1}); ok {
+ return s
+ }
+ if len(optionalDefault) == 1 {
+ return optionalDefault[0]
+ }
+ return {3}
+}
+
+// Must{4} gets the value as a {1}.
+//
+// Panics if the object is not a {1}.
+func (v *Value) Must{4}() {1} {
+ return v.data.({1})
+}
+
+// {4}Slice gets the value as a []{1}, returns the optionalDefault
+// value or nil if the value is not a []{1}.
+func (v *Value) {4}Slice(optionalDefault ...[]{1}) []{1} {
+ if s, ok := v.data.([]{1}); ok {
+ return s
+ }
+ if len(optionalDefault) == 1 {
+ return optionalDefault[0]
+ }
+ return nil
+}
+
+// Must{4}Slice gets the value as a []{1}.
+//
+// Panics if the object is not a []{1}.
+func (v *Value) Must{4}Slice() []{1} {
+ return v.data.([]{1})
+}
+
+// Is{4} gets whether the object contained is a {1} or not.
+func (v *Value) Is{4}() bool {
+ _, ok := v.data.({1})
+ return ok
+}
+
+// Is{4}Slice gets whether the object contained is a []{1} or not.
+func (v *Value) Is{4}Slice() bool {
+ _, ok := v.data.([]{1})
+ return ok
+}
+
+// Each{4} calls the specified callback for each object
+// in the []{1}.
+//
+// Panics if the object is the wrong type.
+func (v *Value) Each{4}(callback func(int, {1}) bool) *Value {
+
+ for index, val := range v.Must{4}Slice() {
+ carryon := callback(index, val)
+ if carryon == false {
+ break
+ }
+ }
+
+ return v
+
+}
+
+// Where{4} uses the specified decider function to select items
+// from the []{1}. The object contained in the result will contain
+// only the selected items.
+func (v *Value) Where{4}(decider func(int, {1}) bool) *Value {
+
+ var selected []{1}
+
+ v.Each{4}(func(index int, val {1}) bool {
+ shouldSelect := decider(index, val)
+ if shouldSelect == false {
+ selected = append(selected, val)
+ }
+ return true
+ })
+
+ return &Value{data:selected}
+
+}
+
+// Group{4} uses the specified grouper function to group the items
+// keyed by the return of the grouper. The object contained in the
+// result will contain a map[string][]{1}.
+func (v *Value) Group{4}(grouper func(int, {1}) string) *Value {
+
+ groups := make(map[string][]{1})
+
+ v.Each{4}(func(index int, val {1}) bool {
+ group := grouper(index, val)
+ if _, ok := groups[group]; !ok {
+ groups[group] = make([]{1}, 0)
+ }
+ groups[group] = append(groups[group], val)
+ return true
+ })
+
+ return &Value{data:groups}
+
+}
+
+// Replace{4} uses the specified function to replace each {1}s
+// by iterating each item. The data in the returned result will be a
+// []{1} containing the replaced items.
+func (v *Value) Replace{4}(replacer func(int, {1}) {1}) *Value {
+
+ arr := v.Must{4}Slice()
+ replaced := make([]{1}, len(arr))
+
+ v.Each{4}(func(index int, val {1}) bool {
+ replaced[index] = replacer(index, val)
+ return true
+ })
+
+ return &Value{data:replaced}
+
+}
+
+// Collect{4} uses the specified collector function to collect a value
+// for each of the {1}s in the slice. The data returned will be a
+// []interface{}.
+func (v *Value) Collect{4}(collector func(int, {1}) interface{}) *Value {
+
+ arr := v.Must{4}Slice()
+ collected := make([]interface{}, len(arr))
+
+ v.Each{4}(func(index int, val {1}) bool {
+ collected[index] = collector(index, val)
+ return true
+ })
+
+ return &Value{data:collected}
+}
+
+// ************************************************************
+// TESTS
+// ************************************************************
+
+func Test{4}(t *testing.T) {
+
+ val := {1}( {2} )
+ m := map[string]interface{}{"value": val, "nothing": nil}
+ assert.Equal(t, val, New(m).Get("value").{4}())
+ assert.Equal(t, val, New(m).Get("value").Must{4}())
+ assert.Equal(t, {1}({3}), New(m).Get("nothing").{4}())
+ assert.Equal(t, val, New(m).Get("nothing").{4}({2}))
+
+ assert.Panics(t, func() {
+ New(m).Get("age").Must{4}()
+ })
+
+}
+
+func Test{4}Slice(t *testing.T) {
+
+ val := {1}( {2} )
+ m := map[string]interface{}{"value": []{1}{ val }, "nothing": nil}
+ assert.Equal(t, val, New(m).Get("value").{4}Slice()[0])
+ assert.Equal(t, val, New(m).Get("value").Must{4}Slice()[0])
+ assert.Equal(t, []{1}(nil), New(m).Get("nothing").{4}Slice())
+ assert.Equal(t, val, New(m).Get("nothing").{4}Slice( []{1}{ {1}({2}) } )[0])
+
+ assert.Panics(t, func() {
+ New(m).Get("nothing").Must{4}Slice()
+ })
+
+}
+
+func TestIs{4}(t *testing.T) {
+
+ var v *Value
+
+ v = &Value{data: {1}({2})}
+ assert.True(t, v.Is{4}())
+
+ v = &Value{data: []{1}{ {1}({2}) }}
+ assert.True(t, v.Is{4}Slice())
+
+}
+
+func TestEach{4}(t *testing.T) {
+
+ v := &Value{data: []{1}{ {1}({2}), {1}({2}), {1}({2}), {1}({2}), {1}({2}) }}
+ count := 0
+ replacedVals := make([]{1}, 0)
+ assert.Equal(t, v, v.Each{4}(func(i int, val {1}) bool {
+
+ count++
+ replacedVals = append(replacedVals, val)
+
+ // abort early
+ if i == 2 {
+ return false
+ }
+
+ return true
+
+ }))
+
+ assert.Equal(t, count, 3)
+ assert.Equal(t, replacedVals[0], v.Must{4}Slice()[0])
+ assert.Equal(t, replacedVals[1], v.Must{4}Slice()[1])
+ assert.Equal(t, replacedVals[2], v.Must{4}Slice()[2])
+
+}
+
+func TestWhere{4}(t *testing.T) {
+
+ v := &Value{data: []{1}{ {1}({2}), {1}({2}), {1}({2}), {1}({2}), {1}({2}), {1}({2}) }}
+
+ selected := v.Where{4}(func(i int, val {1}) bool {
+ return i%2==0
+ }).Must{4}Slice()
+
+ assert.Equal(t, 3, len(selected))
+
+}
+
+func TestGroup{4}(t *testing.T) {
+
+ v := &Value{data: []{1}{ {1}({2}), {1}({2}), {1}({2}), {1}({2}), {1}({2}), {1}({2}) }}
+
+ grouped := v.Group{4}(func(i int, val {1}) string {
+ return fmt.Sprintf("%v", i%2==0)
+ }).data.(map[string][]{1})
+
+ assert.Equal(t, 2, len(grouped))
+ assert.Equal(t, 3, len(grouped["true"]))
+ assert.Equal(t, 3, len(grouped["false"]))
+
+}
+
+func TestReplace{4}(t *testing.T) {
+
+ v := &Value{data: []{1}{ {1}({2}), {1}({2}), {1}({2}), {1}({2}), {1}({2}), {1}({2}) }}
+
+ rawArr := v.Must{4}Slice()
+
+ replaced := v.Replace{4}(func(index int, val {1}) {1} {
+ if index < len(rawArr)-1 {
+ return rawArr[index+1]
+ }
+ return rawArr[0]
+ })
+
+ replacedArr := replaced.Must{4}Slice()
+ if assert.Equal(t, 6, len(replacedArr)) {
+ assert.Equal(t, replacedArr[0], rawArr[1])
+ assert.Equal(t, replacedArr[1], rawArr[2])
+ assert.Equal(t, replacedArr[2], rawArr[3])
+ assert.Equal(t, replacedArr[3], rawArr[4])
+ assert.Equal(t, replacedArr[4], rawArr[5])
+ assert.Equal(t, replacedArr[5], rawArr[0])
+ }
+
+}
+
+func TestCollect{4}(t *testing.T) {
+
+ v := &Value{data: []{1}{ {1}({2}), {1}({2}), {1}({2}), {1}({2}), {1}({2}), {1}({2}) }}
+
+ collected := v.Collect{4}(func(index int, val {1}) interface{} {
+ return index
+ })
+
+ collectedArr := collected.MustInterSlice()
+ if assert.Equal(t, 6, len(collectedArr)) {
+ assert.Equal(t, collectedArr[0], 0)
+ assert.Equal(t, collectedArr[1], 1)
+ assert.Equal(t, collectedArr[2], 2)
+ assert.Equal(t, collectedArr[3], 3)
+ assert.Equal(t, collectedArr[4], 4)
+ assert.Equal(t, collectedArr[5], 5)
+ }
+
+}
diff --git a/vendor/github.com/stretchr/objx/codegen/types_list.txt b/vendor/github.com/stretchr/objx/codegen/types_list.txt
new file mode 100644
index 000000000..069d43d8e
--- /dev/null
+++ b/vendor/github.com/stretchr/objx/codegen/types_list.txt
@@ -0,0 +1,20 @@
+Interface,interface{},"something",nil,Inter
+Map,map[string]interface{},map[string]interface{}{"name":"Tyler"},nil,MSI
+ObjxMap,(Map),New(1),New(nil),ObjxMap
+Bool,bool,true,false,Bool
+String,string,"hello","",Str
+Int,int,1,0,Int
+Int8,int8,1,0,Int8
+Int16,int16,1,0,Int16
+Int32,int32,1,0,Int32
+Int64,int64,1,0,Int64
+Uint,uint,1,0,Uint
+Uint8,uint8,1,0,Uint8
+Uint16,uint16,1,0,Uint16
+Uint32,uint32,1,0,Uint32
+Uint64,uint64,1,0,Uint64
+Uintptr,uintptr,1,0,Uintptr
+Float32,float32,1,0,Float32
+Float64,float64,1,0,Float64
+Complex64,complex64,1,0,Complex64
+Complex128,complex128,1,0,Complex128
diff --git a/vendor/github.com/stretchr/objx/constants.go b/vendor/github.com/stretchr/objx/constants.go
new file mode 100644
index 000000000..f9eb42a25
--- /dev/null
+++ b/vendor/github.com/stretchr/objx/constants.go
@@ -0,0 +1,13 @@
+package objx
+
+const (
+ // PathSeparator is the character used to separate the elements
+ // of the keypath.
+ //
+ // For example, `location.address.city`
+ PathSeparator string = "."
+
+ // SignatureSeparator is the character that is used to
+ // separate the Base64 string from the security signature.
+ SignatureSeparator = "_"
+)
diff --git a/vendor/github.com/stretchr/objx/conversions.go b/vendor/github.com/stretchr/objx/conversions.go
new file mode 100644
index 000000000..9cdfa9f9f
--- /dev/null
+++ b/vendor/github.com/stretchr/objx/conversions.go
@@ -0,0 +1,117 @@
+package objx
+
+import (
+ "bytes"
+ "encoding/base64"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "net/url"
+)
+
+// JSON converts the contained object to a JSON string
+// representation
+func (m Map) JSON() (string, error) {
+
+ result, err := json.Marshal(m)
+
+ if err != nil {
+ err = errors.New("objx: JSON encode failed with: " + err.Error())
+ }
+
+ return string(result), err
+
+}
+
+// MustJSON converts the contained object to a JSON string
+// representation and panics if there is an error
+func (m Map) MustJSON() string {
+ result, err := m.JSON()
+ if err != nil {
+ panic(err.Error())
+ }
+ return result
+}
+
+// Base64 converts the contained object to a Base64 string
+// representation of the JSON string representation
+func (m Map) Base64() (string, error) {
+
+ var buf bytes.Buffer
+
+ jsonData, err := m.JSON()
+ if err != nil {
+ return "", err
+ }
+
+ encoder := base64.NewEncoder(base64.StdEncoding, &buf)
+ encoder.Write([]byte(jsonData))
+ encoder.Close()
+
+ return buf.String(), nil
+
+}
+
+// MustBase64 converts the contained object to a Base64 string
+// representation of the JSON string representation and panics
+// if there is an error
+func (m Map) MustBase64() string {
+ result, err := m.Base64()
+ if err != nil {
+ panic(err.Error())
+ }
+ return result
+}
+
+// SignedBase64 converts the contained object to a Base64 string
+// representation of the JSON string representation and signs it
+// using the provided key.
+func (m Map) SignedBase64(key string) (string, error) {
+
+ base64, err := m.Base64()
+ if err != nil {
+ return "", err
+ }
+
+ sig := HashWithKey(base64, key)
+
+ return base64 + SignatureSeparator + sig, nil
+
+}
+
+// MustSignedBase64 converts the contained object to a Base64 string
+// representation of the JSON string representation and signs it
+// using the provided key and panics if there is an error
+func (m Map) MustSignedBase64(key string) string {
+ result, err := m.SignedBase64(key)
+ if err != nil {
+ panic(err.Error())
+ }
+ return result
+}
+
+/*
+ URL Query
+ ------------------------------------------------
+*/
+
+// URLValues creates a url.Values object from an Obj. This
+// function requires that the wrapped object be a map[string]interface{}
+func (m Map) URLValues() url.Values {
+
+ vals := make(url.Values)
+
+ for k, v := range m {
+ //TODO: can this be done without sprintf?
+ vals.Set(k, fmt.Sprintf("%v", v))
+ }
+
+ return vals
+}
+
+// URLQuery gets an encoded URL query representing the given
+// Obj. This function requires that the wrapped object be a
+// map[string]interface{}
+func (m Map) URLQuery() (string, error) {
+ return m.URLValues().Encode(), nil
+}
diff --git a/vendor/github.com/stretchr/objx/conversions_test.go b/vendor/github.com/stretchr/objx/conversions_test.go
new file mode 100644
index 000000000..e9ccd2987
--- /dev/null
+++ b/vendor/github.com/stretchr/objx/conversions_test.go
@@ -0,0 +1,94 @@
+package objx
+
+import (
+ "github.com/stretchr/testify/assert"
+ "testing"
+)
+
+func TestConversionJSON(t *testing.T) {
+
+ jsonString := `{"name":"Mat"}`
+ o := MustFromJSON(jsonString)
+
+ result, err := o.JSON()
+
+ if assert.NoError(t, err) {
+ assert.Equal(t, jsonString, result)
+ }
+
+ assert.Equal(t, jsonString, o.MustJSON())
+
+}
+
+func TestConversionJSONWithError(t *testing.T) {
+
+ o := MSI()
+ o["test"] = func() {}
+
+ assert.Panics(t, func() {
+ o.MustJSON()
+ })
+
+ _, err := o.JSON()
+
+ assert.Error(t, err)
+
+}
+
+func TestConversionBase64(t *testing.T) {
+
+ o := New(map[string]interface{}{"name": "Mat"})
+
+ result, err := o.Base64()
+
+ if assert.NoError(t, err) {
+ assert.Equal(t, "eyJuYW1lIjoiTWF0In0=", result)
+ }
+
+ assert.Equal(t, "eyJuYW1lIjoiTWF0In0=", o.MustBase64())
+
+}
+
+func TestConversionBase64WithError(t *testing.T) {
+
+ o := MSI()
+ o["test"] = func() {}
+
+ assert.Panics(t, func() {
+ o.MustBase64()
+ })
+
+ _, err := o.Base64()
+
+ assert.Error(t, err)
+
+}
+
+func TestConversionSignedBase64(t *testing.T) {
+
+ o := New(map[string]interface{}{"name": "Mat"})
+
+ result, err := o.SignedBase64("key")
+
+ if assert.NoError(t, err) {
+ assert.Equal(t, "eyJuYW1lIjoiTWF0In0=_67ee82916f90b2c0d68c903266e8998c9ef0c3d6", result)
+ }
+
+ assert.Equal(t, "eyJuYW1lIjoiTWF0In0=_67ee82916f90b2c0d68c903266e8998c9ef0c3d6", o.MustSignedBase64("key"))
+
+}
+
+func TestConversionSignedBase64WithError(t *testing.T) {
+
+ o := MSI()
+ o["test"] = func() {}
+
+ assert.Panics(t, func() {
+ o.MustSignedBase64("key")
+ })
+
+ _, err := o.SignedBase64("key")
+
+ assert.Error(t, err)
+
+}
diff --git a/vendor/github.com/stretchr/objx/doc.go b/vendor/github.com/stretchr/objx/doc.go
new file mode 100644
index 000000000..47bf85e46
--- /dev/null
+++ b/vendor/github.com/stretchr/objx/doc.go
@@ -0,0 +1,72 @@
+// objx - Go package for dealing with maps, slices, JSON and other data.
+//
+// Overview
+//
+// Objx provides the `objx.Map` type, which is a `map[string]interface{}` that exposes
+// a powerful `Get` method (among others) that allows you to easily and quickly get
+// access to data within the map, without having to worry too much about type assertions,
+// missing data, default values etc.
+//
+// Pattern
+//
+// Objx uses a preditable pattern to make access data from within `map[string]interface{}'s
+// easy.
+//
+// Call one of the `objx.` functions to create your `objx.Map` to get going:
+//
+// m, err := objx.FromJSON(json)
+//
+// NOTE: Any methods or functions with the `Must` prefix will panic if something goes wrong,
+// the rest will be optimistic and try to figure things out without panicking.
+//
+// Use `Get` to access the value you're interested in. You can use dot and array
+// notation too:
+//
+// m.Get("places[0].latlng")
+//
+// Once you have saught the `Value` you're interested in, you can use the `Is*` methods
+// to determine its type.
+//
+// if m.Get("code").IsStr() { /* ... */ }
+//
+// Or you can just assume the type, and use one of the strong type methods to
+// extract the real value:
+//
+// m.Get("code").Int()
+//
+// If there's no value there (or if it's the wrong type) then a default value
+// will be returned, or you can be explicit about the default value.
+//
+// Get("code").Int(-1)
+//
+// If you're dealing with a slice of data as a value, Objx provides many useful
+// methods for iterating, manipulating and selecting that data. You can find out more
+// by exploring the index below.
+//
+// Reading data
+//
+// A simple example of how to use Objx:
+//
+// // use MustFromJSON to make an objx.Map from some JSON
+// m := objx.MustFromJSON(`{"name": "Mat", "age": 30}`)
+//
+// // get the details
+// name := m.Get("name").Str()
+// age := m.Get("age").Int()
+//
+// // get their nickname (or use their name if they
+// // don't have one)
+// nickname := m.Get("nickname").Str(name)
+//
+// Ranging
+//
+// Since `objx.Map` is a `map[string]interface{}` you can treat it as such. For
+// example, to `range` the data, do what you would expect:
+//
+// m := objx.MustFromJSON(json)
+// for key, value := range m {
+//
+// /* ... do your magic ... */
+//
+// }
+package objx
diff --git a/vendor/github.com/stretchr/objx/fixture_test.go b/vendor/github.com/stretchr/objx/fixture_test.go
new file mode 100644
index 000000000..27f7d9049
--- /dev/null
+++ b/vendor/github.com/stretchr/objx/fixture_test.go
@@ -0,0 +1,98 @@
+package objx
+
+import (
+ "github.com/stretchr/testify/assert"
+ "testing"
+)
+
+var fixtures = []struct {
+ // name is the name of the fixture (used for reporting
+ // failures)
+ name string
+ // data is the JSON data to be worked on
+ data string
+ // get is the argument(s) to pass to Get
+ get interface{}
+ // output is the expected output
+ output interface{}
+}{
+ {
+ name: "Simple get",
+ data: `{"name": "Mat"}`,
+ get: "name",
+ output: "Mat",
+ },
+ {
+ name: "Get with dot notation",
+ data: `{"address": {"city": "Boulder"}}`,
+ get: "address.city",
+ output: "Boulder",
+ },
+ {
+ name: "Deep get with dot notation",
+ data: `{"one": {"two": {"three": {"four": "hello"}}}}`,
+ get: "one.two.three.four",
+ output: "hello",
+ },
+ {
+ name: "Get missing with dot notation",
+ data: `{"one": {"two": {"three": {"four": "hello"}}}}`,
+ get: "one.ten",
+ output: nil,
+ },
+ {
+ name: "Get with array notation",
+ data: `{"tags": ["one", "two", "three"]}`,
+ get: "tags[1]",
+ output: "two",
+ },
+ {
+ name: "Get with array and dot notation",
+ data: `{"types": { "tags": ["one", "two", "three"]}}`,
+ get: "types.tags[1]",
+ output: "two",
+ },
+ {
+ name: "Get with array and dot notation - field after array",
+ data: `{"tags": [{"name":"one"}, {"name":"two"}, {"name":"three"}]}`,
+ get: "tags[1].name",
+ output: "two",
+ },
+ {
+ name: "Complex get with array and dot notation",
+ data: `{"tags": [{"list": [{"one":"pizza"}]}]}`,
+ get: "tags[0].list[0].one",
+ output: "pizza",
+ },
+ {
+ name: "Get field from within string should be nil",
+ data: `{"name":"Tyler"}`,
+ get: "name.something",
+ output: nil,
+ },
+ {
+ name: "Get field from within string (using array accessor) should be nil",
+ data: `{"numbers":["one", "two", "three"]}`,
+ get: "numbers[0].nope",
+ output: nil,
+ },
+}
+
+func TestFixtures(t *testing.T) {
+
+ for _, fixture := range fixtures {
+
+ m := MustFromJSON(fixture.data)
+
+ // get the value
+ t.Logf("Running get fixture: \"%s\" (%v)", fixture.name, fixture)
+ value := m.Get(fixture.get.(string))
+
+ // make sure it matches
+ assert.Equal(t, fixture.output, value.data,
+ "Get fixture \"%s\" failed: %v", fixture.name, fixture,
+ )
+
+ }
+
+}
diff --git a/vendor/github.com/stretchr/objx/map.go b/vendor/github.com/stretchr/objx/map.go
new file mode 100644
index 000000000..eb6ed8e28
--- /dev/null
+++ b/vendor/github.com/stretchr/objx/map.go
@@ -0,0 +1,222 @@
+package objx
+
+import (
+ "encoding/base64"
+ "encoding/json"
+ "errors"
+ "io/ioutil"
+ "net/url"
+ "strings"
+)
+
+// MSIConvertable is an interface that defines methods for converting your
+// custom types to a map[string]interface{} representation.
+type MSIConvertable interface {
+ // MSI gets a map[string]interface{} (msi) representing the
+ // object.
+ MSI() map[string]interface{}
+}
+
+// Map provides extended functionality for working with
+// untyped data, in particular map[string]interface (msi).
+type Map map[string]interface{}
+
+// Value returns the internal value instance
+func (m Map) Value() *Value {
+ return &Value{data: m}
+}
+
+// Nil represents a nil Map.
+var Nil Map = New(nil)
+
+// New creates a new Map containing the map[string]interface{} in the data argument.
+// If the data argument is not a map[string]interface, New attempts to call the
+// MSI() method on the MSIConvertable interface to create one.
+func New(data interface{}) Map {
+ if _, ok := data.(map[string]interface{}); !ok {
+ if converter, ok := data.(MSIConvertable); ok {
+ data = converter.MSI()
+ } else {
+ return nil
+ }
+ }
+ return Map(data.(map[string]interface{}))
+}
+
+// MSI creates a map[string]interface{} and puts it inside a new Map.
+//
+// The arguments follow a key, value pattern.
+//
+// Panics
+//
+// Panics if any key arugment is non-string or if there are an odd number of arguments.
+//
+// Example
+//
+// To easily create Maps:
+//
+// m := objx.MSI("name", "Mat", "age", 29, "subobj", objx.MSI("active", true))
+//
+// // creates an Map equivalent to
+// m := objx.New(map[string]interface{}{"name": "Mat", "age": 29, "subobj": map[string]interface{}{"active": true}})
+func MSI(keyAndValuePairs ...interface{}) Map {
+
+ newMap := make(map[string]interface{})
+ keyAndValuePairsLen := len(keyAndValuePairs)
+
+ if keyAndValuePairsLen%2 != 0 {
+ panic("objx: MSI must have an even number of arguments following the 'key, value' pattern.")
+ }
+
+ for i := 0; i < keyAndValuePairsLen; i = i + 2 {
+
+ key := keyAndValuePairs[i]
+ value := keyAndValuePairs[i+1]
+
+ // make sure the key is a string
+ keyString, keyStringOK := key.(string)
+ if !keyStringOK {
+ panic("objx: MSI must follow 'string, interface{}' pattern. " + keyString + " is not a valid key.")
+ }
+
+ newMap[keyString] = value
+
+ }
+
+ return New(newMap)
+}
+
+// ****** Conversion Constructors
+
+// MustFromJSON creates a new Map containing the data specified in the
+// jsonString.
+//
+// Panics if the JSON is invalid.
+func MustFromJSON(jsonString string) Map {
+ o, err := FromJSON(jsonString)
+
+ if err != nil {
+ panic("objx: MustFromJSON failed with error: " + err.Error())
+ }
+
+ return o
+}
+
+// FromJSON creates a new Map containing the data specified in the
+// jsonString.
+//
+// Returns an error if the JSON is invalid.
+func FromJSON(jsonString string) (Map, error) {
+
+ var data interface{}
+ err := json.Unmarshal([]byte(jsonString), &data)
+
+ if err != nil {
+ return Nil, err
+ }
+
+ return New(data), nil
+
+}
+
+// FromBase64 creates a new Obj containing the data specified
+// in the Base64 string.
+//
+// The string is an encoded JSON string returned by Base64
+func FromBase64(base64String string) (Map, error) {
+
+ decoder := base64.NewDecoder(base64.StdEncoding, strings.NewReader(base64String))
+
+ decoded, err := ioutil.ReadAll(decoder)
+ if err != nil {
+ return nil, err
+ }
+
+ return FromJSON(string(decoded))
+}
+
+// MustFromBase64 creates a new Obj containing the data specified
+// in the Base64 string and panics if there is an error.
+//
+// The string is an encoded JSON string returned by Base64
+func MustFromBase64(base64String string) Map {
+
+ result, err := FromBase64(base64String)
+
+ if err != nil {
+ panic("objx: MustFromBase64 failed with error: " + err.Error())
+ }
+
+ return result
+}
+
+// FromSignedBase64 creates a new Obj containing the data specified
+// in the Base64 string.
+//
+// The string is an encoded JSON string returned by SignedBase64
+func FromSignedBase64(base64String, key string) (Map, error) {
+ parts := strings.Split(base64String, SignatureSeparator)
+ if len(parts) != 2 {
+ return nil, errors.New("objx: Signed base64 string is malformed.")
+ }
+
+ sig := HashWithKey(parts[0], key)
+ if parts[1] != sig {
+ return nil, errors.New("objx: Signature for base64 data does not match.")
+ }
+
+ return FromBase64(parts[0])
+}
+
+// MustFromSignedBase64 creates a new Obj containing the data specified
+// in the Base64 string and panics if there is an error.
+//
+// The string is an encoded JSON string returned by Base64
+func MustFromSignedBase64(base64String, key string) Map {
+
+ result, err := FromSignedBase64(base64String, key)
+
+ if err != nil {
+ panic("objx: MustFromSignedBase64 failed with error: " + err.Error())
+ }
+
+ return result
+}
+
+// FromURLQuery generates a new Obj by parsing the specified
+// query.
+//
+// For queries with multiple values, the first value is selected.
+func FromURLQuery(query string) (Map, error) {
+
+ vals, err := url.ParseQuery(query)
+
+ if err != nil {
+ return nil, err
+ }
+
+ m := make(map[string]interface{})
+ for k, vals := range vals {
+ m[k] = vals[0]
+ }
+
+ return New(m), nil
+}
+
+// MustFromURLQuery generates a new Obj by parsing the specified
+// query.
+//
+// For queries with multiple values, the first value is selected.
+//
+// Panics if it encounters an error
+func MustFromURLQuery(query string) Map {
+
+ o, err := FromURLQuery(query)
+
+ if err != nil {
+ panic("objx: MustFromURLQuery failed with error: " + err.Error())
+ }
+
+ return o
+
+}
diff --git a/vendor/github.com/stretchr/objx/map_for_test.go b/vendor/github.com/stretchr/objx/map_for_test.go
new file mode 100644
index 000000000..6beb50675
--- /dev/null
+++ b/vendor/github.com/stretchr/objx/map_for_test.go
@@ -0,0 +1,10 @@
+package objx
+
+var TestMap map[string]interface{} = map[string]interface{}{
+ "name": "Tyler",
+ "address": map[string]interface{}{
+ "city": "Salt Lake City",
+ "state": "UT",
+ },
+ "numbers": []interface{}{"one", "two", "three", "four", "five"},
+}
diff --git a/vendor/github.com/stretchr/objx/map_test.go b/vendor/github.com/stretchr/objx/map_test.go
new file mode 100644
index 000000000..1f8b45c61
--- /dev/null
+++ b/vendor/github.com/stretchr/objx/map_test.go
@@ -0,0 +1,147 @@
+package objx
+
+import (
+ "github.com/stretchr/testify/assert"
+ "testing"
+)
+
+type Convertable struct {
+ name string
+}
+
+func (c *Convertable) MSI() map[string]interface{} {
+ return map[string]interface{}{"name": c.name}
+}
+
+type Unconvertable struct {
+ name string
+}
+
+func TestMapCreation(t *testing.T) {
+
+ o := New(nil)
+ assert.Nil(t, o)
+
+ o = New("Tyler")
+ assert.Nil(t, o)
+
+ unconvertable := &Unconvertable{name: "Tyler"}
+ o = New(unconvertable)
+ assert.Nil(t, o)
+
+ convertable := &Convertable{name: "Tyler"}
+ o = New(convertable)
+ if assert.NotNil(t, convertable) {
+ assert.Equal(t, "Tyler", o["name"], "Tyler")
+ }
+
+ o = MSI()
+ if assert.NotNil(t, o) {
+ assert.NotNil(t, o)
+ }
+
+ o = MSI("name", "Tyler")
+ if assert.NotNil(t, o) {
+ if assert.NotNil(t, o) {
+ assert.Equal(t, o["name"], "Tyler")
+ }
+ }
+
+}
+
+func TestMapMustFromJSONWithError(t *testing.T) {
+
+ _, err := FromJSON(`"name":"Mat"}`)
+ assert.Error(t, err)
+
+}
+
+func TestMapFromJSON(t *testing.T) {
+
+ o := MustFromJSON(`{"name":"Mat"}`)
+
+ if assert.NotNil(t, o) {
+ if assert.NotNil(t, o) {
+ assert.Equal(t, "Mat", o["name"])
+ }
+ }
+
+}
+
+func TestMapFromJSONWithError(t *testing.T) {
+
+ var m Map
+
+ assert.Panics(t, func() {
+ m = MustFromJSON(`"name":"Mat"}`)
+ })
+
+ assert.Nil(t, m)
+
+}
+
+func TestMapFromBase64String(t *testing.T) {
+
+ base64String := "eyJuYW1lIjoiTWF0In0="
+
+ o, err := FromBase64(base64String)
+
+ if assert.NoError(t, err) {
+ assert.Equal(t, o.Get("name").Str(), "Mat")
+ }
+
+ assert.Equal(t, MustFromBase64(base64String).Get("name").Str(), "Mat")
+
+}
+
+func TestMapFromBase64StringWithError(t *testing.T) {
+
+ base64String := "eyJuYW1lIjoiTWFasd0In0="
+
+ _, err := FromBase64(base64String)
+
+ assert.Error(t, err)
+
+ assert.Panics(t, func() {
+ MustFromBase64(base64String)
+ })
+
+}
+
+func TestMapFromSignedBase64String(t *testing.T) {
+
+ base64String := "eyJuYW1lIjoiTWF0In0=_67ee82916f90b2c0d68c903266e8998c9ef0c3d6"
+
+ o, err := FromSignedBase64(base64String, "key")
+
+ if assert.NoError(t, err) {
+ assert.Equal(t, o.Get("name").Str(), "Mat")
+ }
+
+ assert.Equal(t, MustFromSignedBase64(base64String, "key").Get("name").Str(), "Mat")
+
+}
+
+func TestMapFromSignedBase64StringWithError(t *testing.T) {
+
+ base64String := "eyJuYW1lasdIjoiTWF0In0=_67ee82916f90b2c0d68c903266e8998c9ef0c3d6"
+
+ _, err := FromSignedBase64(base64String, "key")
+
+ assert.Error(t, err)
+
+ assert.Panics(t, func() {
+ MustFromSignedBase64(base64String, "key")
+ })
+
+}
+
+func TestMapFromURLQuery(t *testing.T) {
+
+ m, err := FromURLQuery("name=tyler&state=UT")
+ if assert.NoError(t, err) && assert.NotNil(t, m) {
+ assert.Equal(t, "tyler", m.Get("name").Str())
+ assert.Equal(t, "UT", m.Get("state").Str())
+ }
+
+}
diff --git a/vendor/github.com/stretchr/objx/mutations.go b/vendor/github.com/stretchr/objx/mutations.go
new file mode 100644
index 000000000..b35c86392
--- /dev/null
+++ b/vendor/github.com/stretchr/objx/mutations.go
@@ -0,0 +1,81 @@
+package objx
+
+// Exclude returns a new Map with the keys in the specified []string
+// excluded.
+func (d Map) Exclude(exclude []string) Map {
+
+ excluded := make(Map)
+ for k, v := range d {
+ var shouldInclude bool = true
+ for _, toExclude := range exclude {
+ if k == toExclude {
+ shouldInclude = false
+ break
+ }
+ }
+ if shouldInclude {
+ excluded[k] = v
+ }
+ }
+
+ return excluded
+}
+
+// Copy creates a shallow copy of the Obj.
+func (m Map) Copy() Map {
+ copied := make(map[string]interface{})
+ for k, v := range m {
+ copied[k] = v
+ }
+ return New(copied)
+}
+
+// Merge blends the specified map with a copy of this map and returns the result.
+//
+// Keys that appear in both will be selected from the specified map.
+// This method requires that the wrapped object be a map[string]interface{}
+func (m Map) Merge(merge Map) Map {
+ return m.Copy().MergeHere(merge)
+}
+
+// Merge blends the specified map with this map and returns the current map.
+//
+// Keys that appear in both will be selected from the specified map. The original map
+// will be modified. This method requires that
+// the wrapped object be a map[string]interface{}
+func (m Map) MergeHere(merge Map) Map {
+
+ for k, v := range merge {
+ m[k] = v
+ }
+
+ return m
+
+}
+
+// Transform builds a new Obj giving the transformer a chance
+// to change the keys and values as it goes. This method requires that
+// the wrapped object be a map[string]interface{}
+func (m Map) Transform(transformer func(key string, value interface{}) (string, interface{})) Map {
+ newMap := make(map[string]interface{})
+ for k, v := range m {
+ modifiedKey, modifiedVal := transformer(k, v)
+ newMap[modifiedKey] = modifiedVal
+ }
+ return New(newMap)
+}
+
+// TransformKeys builds a new map using the specified key mapping.
+//
+// Unspecified keys will be unaltered.
+// This method requires that the wrapped object be a map[string]interface{}
+func (m Map) TransformKeys(mapping map[string]string) Map {
+ return m.Transform(func(key string, value interface{}) (string, interface{}) {
+
+ if newKey, ok := mapping[key]; ok {
+ return newKey, value
+ }
+
+ return key, value
+ })
+}
diff --git a/vendor/github.com/stretchr/objx/mutations_test.go b/vendor/github.com/stretchr/objx/mutations_test.go
new file mode 100644
index 000000000..e20ee23bc
--- /dev/null
+++ b/vendor/github.com/stretchr/objx/mutations_test.go
@@ -0,0 +1,77 @@
+package objx
+
+import (
+ "github.com/stretchr/testify/assert"
+ "testing"
+)
+
+func TestExclude(t *testing.T) {
+
+ d := make(Map)
+ d["name"] = "Mat"
+ d["age"] = 29
+ d["secret"] = "ABC"
+
+ excluded := d.Exclude([]string{"secret"})
+
+ assert.Equal(t, d["name"], excluded["name"])
+ assert.Equal(t, d["age"], excluded["age"])
+ assert.False(t, excluded.Has("secret"), "secret should be excluded")
+
+}
+
+func TestCopy(t *testing.T) {
+
+ d1 := make(map[string]interface{})
+ d1["name"] = "Tyler"
+ d1["location"] = "UT"
+
+ d1Obj := New(d1)
+ d2Obj := d1Obj.Copy()
+
+ d2Obj["name"] = "Mat"
+
+ assert.Equal(t, d1Obj.Get("name").Str(), "Tyler")
+ assert.Equal(t, d2Obj.Get("name").Str(), "Mat")
+
+}
+
+func TestMerge(t *testing.T) {
+
+ d := make(map[string]interface{})
+ d["name"] = "Mat"
+
+ d1 := make(map[string]interface{})
+ d1["name"] = "Tyler"
+ d1["location"] = "UT"
+
+ dObj := New(d)
+ d1Obj := New(d1)
+
+ merged := dObj.Merge(d1Obj)
+
+ assert.Equal(t, merged.Get("name").Str(), d1Obj.Get("name").Str())
+ assert.Equal(t, merged.Get("location").Str(), d1Obj.Get("location").Str())
+ assert.Empty(t, dObj.Get("location").Str())
+
+}
+
+func TestMergeHere(t *testing.T) {
+
+ d := make(map[string]interface{})
+ d["name"] = "Mat"
+
+ d1 := make(map[string]interface{})
+ d1["name"] = "Tyler"
+ d1["location"] = "UT"
+
+ dObj := New(d)
+ d1Obj := New(d1)
+
+ merged := dObj.MergeHere(d1Obj)
+
+ assert.Equal(t, dObj, merged, "With MergeHere, it should return the first modified map")
+ assert.Equal(t, merged.Get("name").Str(), d1Obj.Get("name").Str())
+ assert.Equal(t, merged.Get("location").Str(), d1Obj.Get("location").Str())
+ assert.Equal(t, merged.Get("location").Str(), dObj.Get("location").Str())
+}
diff --git a/vendor/github.com/stretchr/objx/security.go b/vendor/github.com/stretchr/objx/security.go
new file mode 100644
index 000000000..fdd6be9cf
--- /dev/null
+++ b/vendor/github.com/stretchr/objx/security.go
@@ -0,0 +1,14 @@
+package objx
+
+import (
+ "crypto/sha1"
+ "encoding/hex"
+)
+
+// HashWithKey hashes the specified string using the security
+// key.
+func HashWithKey(data, key string) string {
+ hash := sha1.New()
+ hash.Write([]byte(data + ":" + key))
+ return hex.EncodeToString(hash.Sum(nil))
+}
diff --git a/vendor/github.com/stretchr/objx/security_test.go b/vendor/github.com/stretchr/objx/security_test.go
new file mode 100644
index 000000000..8f0898f62
--- /dev/null
+++ b/vendor/github.com/stretchr/objx/security_test.go
@@ -0,0 +1,12 @@
+package objx
+
+import (
+ "github.com/stretchr/testify/assert"
+ "testing"
+)
+
+func TestHashWithKey(t *testing.T) {
+
+ assert.Equal(t, "0ce84d8d01f2c7b6e0882b784429c54d280ea2d9", HashWithKey("abc", "def"))
+
+}
diff --git a/vendor/github.com/stretchr/objx/simple_example_test.go b/vendor/github.com/stretchr/objx/simple_example_test.go
new file mode 100644
index 000000000..5408c7fd3
--- /dev/null
+++ b/vendor/github.com/stretchr/objx/simple_example_test.go
@@ -0,0 +1,41 @@
+package objx
+
+import (
+ "github.com/stretchr/testify/assert"
+ "testing"
+)
+
+func TestSimpleExample(t *testing.T) {
+
+ // build a map from a JSON object
+ o := MustFromJSON(`{"name":"Mat","foods":["indian","chinese"], "location":{"county":"hobbiton","city":"the shire"}}`)
+
+ // Map can be used as a straight map[string]interface{}
+ assert.Equal(t, o["name"], "Mat")
+
+ // Get an Value object
+ v := o.Get("name")
+ assert.Equal(t, v, &Value{data: "Mat"})
+
+ // Test the contained value
+ assert.False(t, v.IsInt())
+ assert.False(t, v.IsBool())
+ assert.True(t, v.IsStr())
+
+ // Get the contained value
+ assert.Equal(t, v.Str(), "Mat")
+
+ // Get a default value if the contained value is not of the expected type or does not exist
+ assert.Equal(t, 1, v.Int(1))
+
+ // Get a value by using array notation
+ assert.Equal(t, "indian", o.Get("foods[0]").Data())
+
+ // Set a value by using array notation
+ o.Set("foods[0]", "italian")
+ assert.Equal(t, "italian", o.Get("foods[0]").Str())
+
+ // Get a value by using dot notation
+ assert.Equal(t, "hobbiton", o.Get("location.county").Str())
+
+}
diff --git a/vendor/github.com/stretchr/objx/tests.go b/vendor/github.com/stretchr/objx/tests.go
new file mode 100644
index 000000000..d9e0b479a
--- /dev/null
+++ b/vendor/github.com/stretchr/objx/tests.go
@@ -0,0 +1,17 @@
+package objx
+
+// Has gets whether there is something at the specified selector
+// or not.
+//
+// If m is nil, Has will always return false.
+func (m Map) Has(selector string) bool {
+ if m == nil {
+ return false
+ }
+ return !m.Get(selector).IsNil()
+}
+
+// IsNil gets whether the data is nil or not.
+func (v *Value) IsNil() bool {
+ return v == nil || v.data == nil
+}
diff --git a/vendor/github.com/stretchr/objx/tests_test.go b/vendor/github.com/stretchr/objx/tests_test.go
new file mode 100644
index 000000000..bcc1eb03d
--- /dev/null
+++ b/vendor/github.com/stretchr/objx/tests_test.go
@@ -0,0 +1,24 @@
+package objx
+
+import (
+ "github.com/stretchr/testify/assert"
+ "testing"
+)
+
+func TestHas(t *testing.T) {
+
+ m := New(TestMap)
+
+ assert.True(t, m.Has("name"))
+ assert.True(t, m.Has("address.state"))
+ assert.True(t, m.Has("numbers[4]"))
+
+ assert.False(t, m.Has("address.state.nope"))
+ assert.False(t, m.Has("address.nope"))
+ assert.False(t, m.Has("nope"))
+ assert.False(t, m.Has("numbers[5]"))
+
+ m = nil
+ assert.False(t, m.Has("nothing"))
+
+}
diff --git a/vendor/github.com/stretchr/objx/type_specific_codegen.go b/vendor/github.com/stretchr/objx/type_specific_codegen.go
new file mode 100644
index 000000000..f3ecb29b9
--- /dev/null
+++ b/vendor/github.com/stretchr/objx/type_specific_codegen.go
@@ -0,0 +1,2881 @@
+package objx
+
+/*
+ Inter (interface{} and []interface{})
+ --------------------------------------------------
+*/
+
+// Inter gets the value as a interface{}, returns the optionalDefault
+// value or a system default object if the value is the wrong type.
+func (v *Value) Inter(optionalDefault ...interface{}) interface{} {
+ if s, ok := v.data.(interface{}); ok {
+ return s
+ }
+ if len(optionalDefault) == 1 {
+ return optionalDefault[0]
+ }
+ return nil
+}
+
+// MustInter gets the value as a interface{}.
+//
+// Panics if the object is not a interface{}.
+func (v *Value) MustInter() interface{} {
+ return v.data.(interface{})
+}
+
+// InterSlice gets the value as a []interface{}, returns the optionalDefault
+// value or nil if the value is not a []interface{}.
+func (v *Value) InterSlice(optionalDefault ...[]interface{}) []interface{} {
+ if s, ok := v.data.([]interface{}); ok {
+ return s
+ }
+ if len(optionalDefault) == 1 {
+ return optionalDefault[0]
+ }
+ return nil
+}
+
+// MustInterSlice gets the value as a []interface{}.
+//
+// Panics if the object is not a []interface{}.
+func (v *Value) MustInterSlice() []interface{} {
+ return v.data.([]interface{})
+}
+
+// IsInter gets whether the object contained is a interface{} or not.
+func (v *Value) IsInter() bool {
+ _, ok := v.data.(interface{})
+ return ok
+}
+
+// IsInterSlice gets whether the object contained is a []interface{} or not.
+func (v *Value) IsInterSlice() bool {
+ _, ok := v.data.([]interface{})
+ return ok
+}
+
+// EachInter calls the specified callback for each object
+// in the []interface{}.
+//
+// Panics if the object is the wrong type.
+func (v *Value) EachInter(callback func(int, interface{}) bool) *Value {
+
+ for index, val := range v.MustInterSlice() {
+ carryon := callback(index, val)
+ if carryon == false {
+ break
+ }
+ }
+
+ return v
+
+}
+
+// WhereInter uses the specified decider function to select items
+// from the []interface{}. The object contained in the result will contain
+// only the selected items.
+func (v *Value) WhereInter(decider func(int, interface{}) bool) *Value {
+
+ var selected []interface{}
+
+ v.EachInter(func(index int, val interface{}) bool {
+ shouldSelect := decider(index, val)
+ if shouldSelect == false {
+ selected = append(selected, val)
+ }
+ return true
+ })
+
+ return &Value{data: selected}
+
+}
+
+// GroupInter uses the specified grouper function to group the items
+// keyed by the return of the grouper. The object contained in the
+// result will contain a map[string][]interface{}.
+func (v *Value) GroupInter(grouper func(int, interface{}) string) *Value {
+
+ groups := make(map[string][]interface{})
+
+ v.EachInter(func(index int, val interface{}) bool {
+ group := grouper(index, val)
+ if _, ok := groups[group]; !ok {
+ groups[group] = make([]interface{}, 0)
+ }
+ groups[group] = append(groups[group], val)
+ return true
+ })
+
+ return &Value{data: groups}
+
+}
+
+// ReplaceInter uses the specified function to replace each interface{}s
+// by iterating each item. The data in the returned result will be a
+// []interface{} containing the replaced items.
+func (v *Value) ReplaceInter(replacer func(int, interface{}) interface{}) *Value {
+
+ arr := v.MustInterSlice()
+ replaced := make([]interface{}, len(arr))
+
+ v.EachInter(func(index int, val interface{}) bool {
+ replaced[index] = replacer(index, val)
+ return true
+ })
+
+ return &Value{data: replaced}
+
+}
+
+// CollectInter uses the specified collector function to collect a value
+// for each of the interface{}s in the slice. The data returned will be a
+// []interface{}.
+func (v *Value) CollectInter(collector func(int, interface{}) interface{}) *Value {
+
+ arr := v.MustInterSlice()
+ collected := make([]interface{}, len(arr))
+
+ v.EachInter(func(index int, val interface{}) bool {
+ collected[index] = collector(index, val)
+ return true
+ })
+
+ return &Value{data: collected}
+}
+
+/*
+ MSI (map[string]interface{} and []map[string]interface{})
+ --------------------------------------------------
+*/
+
+// MSI gets the value as a map[string]interface{}, returns the optionalDefault
+// value or a system default object if the value is the wrong type.
+func (v *Value) MSI(optionalDefault ...map[string]interface{}) map[string]interface{} {
+ if s, ok := v.data.(map[string]interface{}); ok {
+ return s
+ }
+ if len(optionalDefault) == 1 {
+ return optionalDefault[0]
+ }
+ return nil
+}
+
+// MustMSI gets the value as a map[string]interface{}.
+//
+// Panics if the object is not a map[string]interface{}.
+func (v *Value) MustMSI() map[string]interface{} {
+ return v.data.(map[string]interface{})
+}
+
+// MSISlice gets the value as a []map[string]interface{}, returns the optionalDefault
+// value or nil if the value is not a []map[string]interface{}.
+func (v *Value) MSISlice(optionalDefault ...[]map[string]interface{}) []map[string]interface{} {
+ if s, ok := v.data.([]map[string]interface{}); ok {
+ return s
+ }
+ if len(optionalDefault) == 1 {
+ return optionalDefault[0]
+ }
+ return nil
+}
+
+// MustMSISlice gets the value as a []map[string]interface{}.
+//
+// Panics if the object is not a []map[string]interface{}.
+func (v *Value) MustMSISlice() []map[string]interface{} {
+ return v.data.([]map[string]interface{})
+}
+
+// IsMSI gets whether the object contained is a map[string]interface{} or not.
+func (v *Value) IsMSI() bool {
+ _, ok := v.data.(map[string]interface{})
+ return ok
+}
+
+// IsMSISlice gets whether the object contained is a []map[string]interface{} or not.
+func (v *Value) IsMSISlice() bool {
+ _, ok := v.data.([]map[string]interface{})
+ return ok
+}
+
+// EachMSI calls the specified callback for each object
+// in the []map[string]interface{}.
+//
+// Panics if the object is the wrong type.
+func (v *Value) EachMSI(callback func(int, map[string]interface{}) bool) *Value {
+
+ for index, val := range v.MustMSISlice() {
+ carryon := callback(index, val)
+ if carryon == false {
+ break
+ }
+ }
+
+ return v
+
+}
+
+// WhereMSI uses the specified decider function to select items
+// from the []map[string]interface{}. The object contained in the result will contain
+// only the selected items.
+func (v *Value) WhereMSI(decider func(int, map[string]interface{}) bool) *Value {
+
+ var selected []map[string]interface{}
+
+ v.EachMSI(func(index int, val map[string]interface{}) bool {
+ shouldSelect := decider(index, val)
+ if shouldSelect == false {
+ selected = append(selected, val)
+ }
+ return true
+ })
+
+ return &Value{data: selected}
+
+}
+
+// GroupMSI uses the specified grouper function to group the items
+// keyed by the return of the grouper. The object contained in the
+// result will contain a map[string][]map[string]interface{}.
+func (v *Value) GroupMSI(grouper func(int, map[string]interface{}) string) *Value {
+
+ groups := make(map[string][]map[string]interface{})
+
+ v.EachMSI(func(index int, val map[string]interface{}) bool {
+ group := grouper(index, val)
+ if _, ok := groups[group]; !ok {
+ groups[group] = make([]map[string]interface{}, 0)
+ }
+ groups[group] = append(groups[group], val)
+ return true
+ })
+
+ return &Value{data: groups}
+
+}
+
+// ReplaceMSI uses the specified function to replace each map[string]interface{}s
+// by iterating each item. The data in the returned result will be a
+// []map[string]interface{} containing the replaced items.
+func (v *Value) ReplaceMSI(replacer func(int, map[string]interface{}) map[string]interface{}) *Value {
+
+ arr := v.MustMSISlice()
+ replaced := make([]map[string]interface{}, len(arr))
+
+ v.EachMSI(func(index int, val map[string]interface{}) bool {
+ replaced[index] = replacer(index, val)
+ return true
+ })
+
+ return &Value{data: replaced}
+
+}
+
+// CollectMSI uses the specified collector function to collect a value
+// for each of the map[string]interface{}s in the slice. The data returned will be a
+// []interface{}.
+func (v *Value) CollectMSI(collector func(int, map[string]interface{}) interface{}) *Value {
+
+ arr := v.MustMSISlice()
+ collected := make([]interface{}, len(arr))
+
+ v.EachMSI(func(index int, val map[string]interface{}) bool {
+ collected[index] = collector(index, val)
+ return true
+ })
+
+ return &Value{data: collected}
+}
+
+/*
+ ObjxMap ((Map) and [](Map))
+ --------------------------------------------------
+*/
+
+// ObjxMap gets the value as a (Map), returns the optionalDefault
+// value or a system default object if the value is the wrong type.
+func (v *Value) ObjxMap(optionalDefault ...(Map)) Map {
+ if s, ok := v.data.((Map)); ok {
+ return s
+ }
+ if len(optionalDefault) == 1 {
+ return optionalDefault[0]
+ }
+ return New(nil)
+}
+
+// MustObjxMap gets the value as a (Map).
+//
+// Panics if the object is not a (Map).
+func (v *Value) MustObjxMap() Map {
+ return v.data.((Map))
+}
+
+// ObjxMapSlice gets the value as a [](Map), returns the optionalDefault
+// value or nil if the value is not a [](Map).
+func (v *Value) ObjxMapSlice(optionalDefault ...[](Map)) [](Map) {
+ if s, ok := v.data.([](Map)); ok {
+ return s
+ }
+ if len(optionalDefault) == 1 {
+ return optionalDefault[0]
+ }
+ return nil
+}
+
+// MustObjxMapSlice gets the value as a [](Map).
+//
+// Panics if the object is not a [](Map).
+func (v *Value) MustObjxMapSlice() [](Map) {
+ return v.data.([](Map))
+}
+
+// IsObjxMap gets whether the object contained is a (Map) or not.
+func (v *Value) IsObjxMap() bool {
+ _, ok := v.data.((Map))
+ return ok
+}
+
+// IsObjxMapSlice gets whether the object contained is a [](Map) or not.
+func (v *Value) IsObjxMapSlice() bool {
+ _, ok := v.data.([](Map))
+ return ok
+}
+
+// EachObjxMap calls the specified callback for each object
+// in the [](Map).
+//
+// Panics if the object is the wrong type.
+func (v *Value) EachObjxMap(callback func(int, Map) bool) *Value {
+
+ for index, val := range v.MustObjxMapSlice() {
+ carryon := callback(index, val)
+ if carryon == false {
+ break
+ }
+ }
+
+ return v
+
+}
+
+// WhereObjxMap uses the specified decider function to select items
+// from the [](Map). The object contained in the result will contain
+// only the selected items.
+func (v *Value) WhereObjxMap(decider func(int, Map) bool) *Value {
+
+ var selected [](Map)
+
+ v.EachObjxMap(func(index int, val Map) bool {
+ shouldSelect := decider(index, val)
+ if shouldSelect == false {
+ selected = append(selected, val)
+ }
+ return true
+ })
+
+ return &Value{data: selected}
+
+}
+
+// GroupObjxMap uses the specified grouper function to group the items
+// keyed by the return of the grouper. The object contained in the
+// result will contain a map[string][](Map).
+func (v *Value) GroupObjxMap(grouper func(int, Map) string) *Value {
+
+ groups := make(map[string][](Map))
+
+ v.EachObjxMap(func(index int, val Map) bool {
+ group := grouper(index, val)
+ if _, ok := groups[group]; !ok {
+ groups[group] = make([](Map), 0)
+ }
+ groups[group] = append(groups[group], val)
+ return true
+ })
+
+ return &Value{data: groups}
+
+}
+
+// ReplaceObjxMap uses the specified function to replace each (Map)s
+// by iterating each item. The data in the returned result will be a
+// [](Map) containing the replaced items.
+func (v *Value) ReplaceObjxMap(replacer func(int, Map) Map) *Value {
+
+ arr := v.MustObjxMapSlice()
+ replaced := make([](Map), len(arr))
+
+ v.EachObjxMap(func(index int, val Map) bool {
+ replaced[index] = replacer(index, val)
+ return true
+ })
+
+ return &Value{data: replaced}
+
+}
+
+// CollectObjxMap uses the specified collector function to collect a value
+// for each of the (Map)s in the slice. The data returned will be a
+// []interface{}.
+func (v *Value) CollectObjxMap(collector func(int, Map) interface{}) *Value {
+
+ arr := v.MustObjxMapSlice()
+ collected := make([]interface{}, len(arr))
+
+ v.EachObjxMap(func(index int, val Map) bool {
+ collected[index] = collector(index, val)
+ return true
+ })
+
+ return &Value{data: collected}
+}
+
+/*
+ Bool (bool and []bool)
+ --------------------------------------------------
+*/
+
+// Bool gets the value as a bool, returns the optionalDefault
+// value or a system default object if the value is the wrong type.
+func (v *Value) Bool(optionalDefault ...bool) bool {
+ if s, ok := v.data.(bool); ok {
+ return s
+ }
+ if len(optionalDefault) == 1 {
+ return optionalDefault[0]
+ }
+ return false
+}
+
+// MustBool gets the value as a bool.
+//
+// Panics if the object is not a bool.
+func (v *Value) MustBool() bool {
+ return v.data.(bool)
+}
+
+// BoolSlice gets the value as a []bool, returns the optionalDefault
+// value or nil if the value is not a []bool.
+func (v *Value) BoolSlice(optionalDefault ...[]bool) []bool {
+ if s, ok := v.data.([]bool); ok {
+ return s
+ }
+ if len(optionalDefault) == 1 {
+ return optionalDefault[0]
+ }
+ return nil
+}
+
+// MustBoolSlice gets the value as a []bool.
+//
+// Panics if the object is not a []bool.
+func (v *Value) MustBoolSlice() []bool {
+ return v.data.([]bool)
+}
+
+// IsBool gets whether the object contained is a bool or not.
+func (v *Value) IsBool() bool {
+ _, ok := v.data.(bool)
+ return ok
+}
+
+// IsBoolSlice gets whether the object contained is a []bool or not.
+func (v *Value) IsBoolSlice() bool {
+ _, ok := v.data.([]bool)
+ return ok
+}
+
+// EachBool calls the specified callback for each object
+// in the []bool.
+//
+// Panics if the object is the wrong type.
+func (v *Value) EachBool(callback func(int, bool) bool) *Value {
+
+ for index, val := range v.MustBoolSlice() {
+ carryon := callback(index, val)
+ if carryon == false {
+ break
+ }
+ }
+
+ return v
+
+}
+
+// WhereBool uses the specified decider function to select items
+// from the []bool. The object contained in the result will contain
+// only the selected items.
+func (v *Value) WhereBool(decider func(int, bool) bool) *Value {
+
+ var selected []bool
+
+ v.EachBool(func(index int, val bool) bool {
+ shouldSelect := decider(index, val)
+ if shouldSelect == false {
+ selected = append(selected, val)
+ }
+ return true
+ })
+
+ return &Value{data: selected}
+
+}
+
+// GroupBool uses the specified grouper function to group the items
+// keyed by the return of the grouper. The object contained in the
+// result will contain a map[string][]bool.
+func (v *Value) GroupBool(grouper func(int, bool) string) *Value {
+
+ groups := make(map[string][]bool)
+
+ v.EachBool(func(index int, val bool) bool {
+ group := grouper(index, val)
+ if _, ok := groups[group]; !ok {
+ groups[group] = make([]bool, 0)
+ }
+ groups[group] = append(groups[group], val)
+ return true
+ })
+
+ return &Value{data: groups}
+
+}
+
+// ReplaceBool uses the specified function to replace each bools
+// by iterating each item. The data in the returned result will be a
+// []bool containing the replaced items.
+func (v *Value) ReplaceBool(replacer func(int, bool) bool) *Value {
+
+ arr := v.MustBoolSlice()
+ replaced := make([]bool, len(arr))
+
+ v.EachBool(func(index int, val bool) bool {
+ replaced[index] = replacer(index, val)
+ return true
+ })
+
+ return &Value{data: replaced}
+
+}
+
+// CollectBool uses the specified collector function to collect a value
+// for each of the bools in the slice. The data returned will be a
+// []interface{}.
+func (v *Value) CollectBool(collector func(int, bool) interface{}) *Value {
+
+ arr := v.MustBoolSlice()
+ collected := make([]interface{}, len(arr))
+
+ v.EachBool(func(index int, val bool) bool {
+ collected[index] = collector(index, val)
+ return true
+ })
+
+ return &Value{data: collected}
+}
+
+/*
+ Str (string and []string)
+ --------------------------------------------------
+*/
+
+// Str gets the value as a string, returns the optionalDefault
+// value or a system default object if the value is the wrong type.
+func (v *Value) Str(optionalDefault ...string) string {
+ if s, ok := v.data.(string); ok {
+ return s
+ }
+ if len(optionalDefault) == 1 {
+ return optionalDefault[0]
+ }
+ return ""
+}
+
+// MustStr gets the value as a string.
+//
+// Panics if the object is not a string.
+func (v *Value) MustStr() string {
+ return v.data.(string)
+}
+
+// StrSlice gets the value as a []string, returns the optionalDefault
+// value or nil if the value is not a []string.
+func (v *Value) StrSlice(optionalDefault ...[]string) []string {
+ if s, ok := v.data.([]string); ok {
+ return s
+ }
+ if len(optionalDefault) == 1 {
+ return optionalDefault[0]
+ }
+ return nil
+}
+
+// MustStrSlice gets the value as a []string.
+//
+// Panics if the object is not a []string.
+func (v *Value) MustStrSlice() []string {
+ return v.data.([]string)
+}
+
+// IsStr gets whether the object contained is a string or not.
+func (v *Value) IsStr() bool {
+ _, ok := v.data.(string)
+ return ok
+}
+
+// IsStrSlice gets whether the object contained is a []string or not.
+func (v *Value) IsStrSlice() bool {
+ _, ok := v.data.([]string)
+ return ok
+}
+
+// EachStr calls the specified callback for each object
+// in the []string.
+//
+// Panics if the object is the wrong type.
+func (v *Value) EachStr(callback func(int, string) bool) *Value {
+
+ for index, val := range v.MustStrSlice() {
+ carryon := callback(index, val)
+ if carryon == false {
+ break
+ }
+ }
+
+ return v
+
+}
+
+// WhereStr uses the specified decider function to select items
+// from the []string. The object contained in the result will contain
+// only the selected items.
+func (v *Value) WhereStr(decider func(int, string) bool) *Value {
+
+ var selected []string
+
+ v.EachStr(func(index int, val string) bool {
+ shouldSelect := decider(index, val)
+ if shouldSelect == false {
+ selected = append(selected, val)
+ }
+ return true
+ })
+
+ return &Value{data: selected}
+
+}
+
+// GroupStr uses the specified grouper function to group the items
+// keyed by the return of the grouper. The object contained in the
+// result will contain a map[string][]string.
+func (v *Value) GroupStr(grouper func(int, string) string) *Value {
+
+ groups := make(map[string][]string)
+
+ v.EachStr(func(index int, val string) bool {
+ group := grouper(index, val)
+ if _, ok := groups[group]; !ok {
+ groups[group] = make([]string, 0)
+ }
+ groups[group] = append(groups[group], val)
+ return true
+ })
+
+ return &Value{data: groups}
+
+}
+
+// ReplaceStr uses the specified function to replace each strings
+// by iterating each item. The data in the returned result will be a
+// []string containing the replaced items.
+func (v *Value) ReplaceStr(replacer func(int, string) string) *Value {
+
+ arr := v.MustStrSlice()
+ replaced := make([]string, len(arr))
+
+ v.EachStr(func(index int, val string) bool {
+ replaced[index] = replacer(index, val)
+ return true
+ })
+
+ return &Value{data: replaced}
+
+}
+
+// CollectStr uses the specified collector function to collect a value
+// for each of the strings in the slice. The data returned will be a
+// []interface{}.
+func (v *Value) CollectStr(collector func(int, string) interface{}) *Value {
+
+ arr := v.MustStrSlice()
+ collected := make([]interface{}, len(arr))
+
+ v.EachStr(func(index int, val string) bool {
+ collected[index] = collector(index, val)
+ return true
+ })
+
+ return &Value{data: collected}
+}
+
+/*
+ Int (int and []int)
+ --------------------------------------------------
+*/
+
+// Int gets the value as a int, returns the optionalDefault
+// value or a system default object if the value is the wrong type.
+func (v *Value) Int(optionalDefault ...int) int {
+ if s, ok := v.data.(int); ok {
+ return s
+ }
+ if len(optionalDefault) == 1 {
+ return optionalDefault[0]
+ }
+ return 0
+}
+
+// MustInt gets the value as a int.
+//
+// Panics if the object is not a int.
+func (v *Value) MustInt() int {
+ return v.data.(int)
+}
+
+// IntSlice gets the value as a []int, returns the optionalDefault
+// value or nil if the value is not a []int.
+func (v *Value) IntSlice(optionalDefault ...[]int) []int {
+ if s, ok := v.data.([]int); ok {
+ return s
+ }
+ if len(optionalDefault) == 1 {
+ return optionalDefault[0]
+ }
+ return nil
+}
+
+// MustIntSlice gets the value as a []int.
+//
+// Panics if the object is not a []int.
+func (v *Value) MustIntSlice() []int {
+ return v.data.([]int)
+}
+
+// IsInt gets whether the object contained is a int or not.
+func (v *Value) IsInt() bool {
+ _, ok := v.data.(int)
+ return ok
+}
+
+// IsIntSlice gets whether the object contained is a []int or not.
+func (v *Value) IsIntSlice() bool {
+ _, ok := v.data.([]int)
+ return ok
+}
+
+// EachInt calls the specified callback for each object
+// in the []int.
+//
+// Panics if the object is the wrong type.
+func (v *Value) EachInt(callback func(int, int) bool) *Value {
+
+ for index, val := range v.MustIntSlice() {
+ carryon := callback(index, val)
+ if carryon == false {
+ break
+ }
+ }
+
+ return v
+
+}
+
+// WhereInt uses the specified decider function to select items
+// from the []int. The object contained in the result will contain
+// only the selected items.
+func (v *Value) WhereInt(decider func(int, int) bool) *Value {
+
+ var selected []int
+
+ v.EachInt(func(index int, val int) bool {
+ shouldSelect := decider(index, val)
+ if shouldSelect == false {
+ selected = append(selected, val)
+ }
+ return true
+ })
+
+ return &Value{data: selected}
+
+}
+
+// GroupInt uses the specified grouper function to group the items
+// keyed by the return of the grouper. The object contained in the
+// result will contain a map[string][]int.
+func (v *Value) GroupInt(grouper func(int, int) string) *Value {
+
+ groups := make(map[string][]int)
+
+ v.EachInt(func(index int, val int) bool {
+ group := grouper(index, val)
+ if _, ok := groups[group]; !ok {
+ groups[group] = make([]int, 0)
+ }
+ groups[group] = append(groups[group], val)
+ return true
+ })
+
+ return &Value{data: groups}
+
+}
+
+// ReplaceInt uses the specified function to replace each ints
+// by iterating each item. The data in the returned result will be a
+// []int containing the replaced items.
+func (v *Value) ReplaceInt(replacer func(int, int) int) *Value {
+
+ arr := v.MustIntSlice()
+ replaced := make([]int, len(arr))
+
+ v.EachInt(func(index int, val int) bool {
+ replaced[index] = replacer(index, val)
+ return true
+ })
+
+ return &Value{data: replaced}
+
+}
+
+// CollectInt uses the specified collector function to collect a value
+// for each of the ints in the slice. The data returned will be a
+// []interface{}.
+func (v *Value) CollectInt(collector func(int, int) interface{}) *Value {
+
+ arr := v.MustIntSlice()
+ collected := make([]interface{}, len(arr))
+
+ v.EachInt(func(index int, val int) bool {
+ collected[index] = collector(index, val)
+ return true
+ })
+
+ return &Value{data: collected}
+}
+
+/*
+ Int8 (int8 and []int8)
+ --------------------------------------------------
+*/
+
+// Int8 gets the value as a int8, returns the optionalDefault
+// value or a system default object if the value is the wrong type.
+func (v *Value) Int8(optionalDefault ...int8) int8 {
+ if s, ok := v.data.(int8); ok {
+ return s
+ }
+ if len(optionalDefault) == 1 {
+ return optionalDefault[0]
+ }
+ return 0
+}
+
+// MustInt8 gets the value as a int8.
+//
+// Panics if the object is not a int8.
+func (v *Value) MustInt8() int8 {
+ return v.data.(int8)
+}
+
+// Int8Slice gets the value as a []int8, returns the optionalDefault
+// value or nil if the value is not a []int8.
+func (v *Value) Int8Slice(optionalDefault ...[]int8) []int8 {
+ if s, ok := v.data.([]int8); ok {
+ return s
+ }
+ if len(optionalDefault) == 1 {
+ return optionalDefault[0]
+ }
+ return nil
+}
+
+// MustInt8Slice gets the value as a []int8.
+//
+// Panics if the object is not a []int8.
+func (v *Value) MustInt8Slice() []int8 {
+ return v.data.([]int8)
+}
+
+// IsInt8 gets whether the object contained is a int8 or not.
+func (v *Value) IsInt8() bool {
+ _, ok := v.data.(int8)
+ return ok
+}
+
+// IsInt8Slice gets whether the object contained is a []int8 or not.
+func (v *Value) IsInt8Slice() bool {
+ _, ok := v.data.([]int8)
+ return ok
+}
+
+// EachInt8 calls the specified callback for each object
+// in the []int8.
+//
+// Panics if the object is the wrong type.
+func (v *Value) EachInt8(callback func(int, int8) bool) *Value {
+
+ for index, val := range v.MustInt8Slice() {
+ carryon := callback(index, val)
+ if carryon == false {
+ break
+ }
+ }
+
+ return v
+
+}
+
+// WhereInt8 uses the specified decider function to select items
+// from the []int8. The object contained in the result will contain
+// only the selected items.
+func (v *Value) WhereInt8(decider func(int, int8) bool) *Value {
+
+ var selected []int8
+
+ v.EachInt8(func(index int, val int8) bool {
+ shouldSelect := decider(index, val)
+ if shouldSelect == false {
+ selected = append(selected, val)
+ }
+ return true
+ })
+
+ return &Value{data: selected}
+
+}
+
+// GroupInt8 uses the specified grouper function to group the items
+// keyed by the return of the grouper. The object contained in the
+// result will contain a map[string][]int8.
+func (v *Value) GroupInt8(grouper func(int, int8) string) *Value {
+
+ groups := make(map[string][]int8)
+
+ v.EachInt8(func(index int, val int8) bool {
+ group := grouper(index, val)
+ if _, ok := groups[group]; !ok {
+ groups[group] = make([]int8, 0)
+ }
+ groups[group] = append(groups[group], val)
+ return true
+ })
+
+ return &Value{data: groups}
+
+}
+
+// ReplaceInt8 uses the specified function to replace each int8s
+// by iterating each item. The data in the returned result will be a
+// []int8 containing the replaced items.
+func (v *Value) ReplaceInt8(replacer func(int, int8) int8) *Value {
+
+ arr := v.MustInt8Slice()
+ replaced := make([]int8, len(arr))
+
+ v.EachInt8(func(index int, val int8) bool {
+ replaced[index] = replacer(index, val)
+ return true
+ })
+
+ return &Value{data: replaced}
+
+}
+
+// CollectInt8 uses the specified collector function to collect a value
+// for each of the int8s in the slice. The data returned will be a
+// []interface{}.
+func (v *Value) CollectInt8(collector func(int, int8) interface{}) *Value {
+
+ arr := v.MustInt8Slice()
+ collected := make([]interface{}, len(arr))
+
+ v.EachInt8(func(index int, val int8) bool {
+ collected[index] = collector(index, val)
+ return true
+ })
+
+ return &Value{data: collected}
+}
+
+/*
+ Int16 (int16 and []int16)
+ --------------------------------------------------
+*/
+
+// Int16 gets the value as a int16, returns the optionalDefault
+// value or a system default object if the value is the wrong type.
+func (v *Value) Int16(optionalDefault ...int16) int16 {
+ if s, ok := v.data.(int16); ok {
+ return s
+ }
+ if len(optionalDefault) == 1 {
+ return optionalDefault[0]
+ }
+ return 0
+}
+
+// MustInt16 gets the value as a int16.
+//
+// Panics if the object is not a int16.
+func (v *Value) MustInt16() int16 {
+ return v.data.(int16)
+}
+
+// Int16Slice gets the value as a []int16, returns the optionalDefault
+// value or nil if the value is not a []int16.
+func (v *Value) Int16Slice(optionalDefault ...[]int16) []int16 {
+ if s, ok := v.data.([]int16); ok {
+ return s
+ }
+ if len(optionalDefault) == 1 {
+ return optionalDefault[0]
+ }
+ return nil
+}
+
+// MustInt16Slice gets the value as a []int16.
+//
+// Panics if the object is not a []int16.
+func (v *Value) MustInt16Slice() []int16 {
+ return v.data.([]int16)
+}
+
+// IsInt16 gets whether the object contained is a int16 or not.
+func (v *Value) IsInt16() bool {
+ _, ok := v.data.(int16)
+ return ok
+}
+
+// IsInt16Slice gets whether the object contained is a []int16 or not.
+func (v *Value) IsInt16Slice() bool {
+ _, ok := v.data.([]int16)
+ return ok
+}
+
+// EachInt16 calls the specified callback for each object
+// in the []int16.
+//
+// Panics if the object is the wrong type.
+func (v *Value) EachInt16(callback func(int, int16) bool) *Value {
+
+ for index, val := range v.MustInt16Slice() {
+ carryon := callback(index, val)
+ if carryon == false {
+ break
+ }
+ }
+
+ return v
+
+}
+
+// WhereInt16 uses the specified decider function to select items
+// from the []int16. The object contained in the result will contain
+// only the selected items.
+func (v *Value) WhereInt16(decider func(int, int16) bool) *Value {
+
+ var selected []int16
+
+ v.EachInt16(func(index int, val int16) bool {
+ shouldSelect := decider(index, val)
+ if shouldSelect == false {
+ selected = append(selected, val)
+ }
+ return true
+ })
+
+ return &Value{data: selected}
+
+}
+
+// GroupInt16 uses the specified grouper function to group the items
+// keyed by the return of the grouper. The object contained in the
+// result will contain a map[string][]int16.
+func (v *Value) GroupInt16(grouper func(int, int16) string) *Value {
+
+ groups := make(map[string][]int16)
+
+ v.EachInt16(func(index int, val int16) bool {
+ group := grouper(index, val)
+ if _, ok := groups[group]; !ok {
+ groups[group] = make([]int16, 0)
+ }
+ groups[group] = append(groups[group], val)
+ return true
+ })
+
+ return &Value{data: groups}
+
+}
+
+// ReplaceInt16 uses the specified function to replace each int16s
+// by iterating each item. The data in the returned result will be a
+// []int16 containing the replaced items.
+func (v *Value) ReplaceInt16(replacer func(int, int16) int16) *Value {
+
+ arr := v.MustInt16Slice()
+ replaced := make([]int16, len(arr))
+
+ v.EachInt16(func(index int, val int16) bool {
+ replaced[index] = replacer(index, val)
+ return true
+ })
+
+ return &Value{data: replaced}
+
+}
+
+// CollectInt16 uses the specified collector function to collect a value
+// for each of the int16s in the slice. The data returned will be a
+// []interface{}.
+func (v *Value) CollectInt16(collector func(int, int16) interface{}) *Value {
+
+ arr := v.MustInt16Slice()
+ collected := make([]interface{}, len(arr))
+
+ v.EachInt16(func(index int, val int16) bool {
+ collected[index] = collector(index, val)
+ return true
+ })
+
+ return &Value{data: collected}
+}
+
+/*
+ Int32 (int32 and []int32)
+ --------------------------------------------------
+*/
+
+// Int32 gets the value as a int32, returns the optionalDefault
+// value or a system default object if the value is the wrong type.
+func (v *Value) Int32(optionalDefault ...int32) int32 {
+ if s, ok := v.data.(int32); ok {
+ return s
+ }
+ if len(optionalDefault) == 1 {
+ return optionalDefault[0]
+ }
+ return 0
+}
+
+// MustInt32 gets the value as a int32.
+//
+// Panics if the object is not a int32.
+func (v *Value) MustInt32() int32 {
+ return v.data.(int32)
+}
+
+// Int32Slice gets the value as a []int32, returns the optionalDefault
+// value or nil if the value is not a []int32.
+func (v *Value) Int32Slice(optionalDefault ...[]int32) []int32 {
+ if s, ok := v.data.([]int32); ok {
+ return s
+ }
+ if len(optionalDefault) == 1 {
+ return optionalDefault[0]
+ }
+ return nil
+}
+
+// MustInt32Slice gets the value as a []int32.
+//
+// Panics if the object is not a []int32.
+func (v *Value) MustInt32Slice() []int32 {
+ return v.data.([]int32)
+}
+
+// IsInt32 gets whether the object contained is a int32 or not.
+func (v *Value) IsInt32() bool {
+ _, ok := v.data.(int32)
+ return ok
+}
+
+// IsInt32Slice gets whether the object contained is a []int32 or not.
+func (v *Value) IsInt32Slice() bool {
+ _, ok := v.data.([]int32)
+ return ok
+}
+
+// EachInt32 calls the specified callback for each object
+// in the []int32.
+//
+// Panics if the object is the wrong type.
+func (v *Value) EachInt32(callback func(int, int32) bool) *Value {
+
+ for index, val := range v.MustInt32Slice() {
+ carryon := callback(index, val)
+ if carryon == false {
+ break
+ }
+ }
+
+ return v
+
+}
+
+// WhereInt32 uses the specified decider function to select items
+// from the []int32. The object contained in the result will contain
+// only the selected items.
+func (v *Value) WhereInt32(decider func(int, int32) bool) *Value {
+
+ var selected []int32
+
+ v.EachInt32(func(index int, val int32) bool {
+ shouldSelect := decider(index, val)
+ if shouldSelect == false {
+ selected = append(selected, val)
+ }
+ return true
+ })
+
+ return &Value{data: selected}
+
+}
+
+// GroupInt32 uses the specified grouper function to group the items
+// keyed by the return of the grouper. The object contained in the
+// result will contain a map[string][]int32.
+func (v *Value) GroupInt32(grouper func(int, int32) string) *Value {
+
+ groups := make(map[string][]int32)
+
+ v.EachInt32(func(index int, val int32) bool {
+ group := grouper(index, val)
+ if _, ok := groups[group]; !ok {
+ groups[group] = make([]int32, 0)
+ }
+ groups[group] = append(groups[group], val)
+ return true
+ })
+
+ return &Value{data: groups}
+
+}
+
+// ReplaceInt32 uses the specified function to replace each int32s
+// by iterating each item. The data in the returned result will be a
+// []int32 containing the replaced items.
+func (v *Value) ReplaceInt32(replacer func(int, int32) int32) *Value {
+
+ arr := v.MustInt32Slice()
+ replaced := make([]int32, len(arr))
+
+ v.EachInt32(func(index int, val int32) bool {
+ replaced[index] = replacer(index, val)
+ return true
+ })
+
+ return &Value{data: replaced}
+
+}
+
+// CollectInt32 uses the specified collector function to collect a value
+// for each of the int32s in the slice. The data returned will be a
+// []interface{}.
+func (v *Value) CollectInt32(collector func(int, int32) interface{}) *Value {
+
+ arr := v.MustInt32Slice()
+ collected := make([]interface{}, len(arr))
+
+ v.EachInt32(func(index int, val int32) bool {
+ collected[index] = collector(index, val)
+ return true
+ })
+
+ return &Value{data: collected}
+}
+
+/*
+ Int64 (int64 and []int64)
+ --------------------------------------------------
+*/
+
+// Int64 gets the value as a int64, returns the optionalDefault
+// value or a system default object if the value is the wrong type.
+func (v *Value) Int64(optionalDefault ...int64) int64 {
+ if s, ok := v.data.(int64); ok {
+ return s
+ }
+ if len(optionalDefault) == 1 {
+ return optionalDefault[0]
+ }
+ return 0
+}
+
+// MustInt64 gets the value as a int64.
+//
+// Panics if the object is not a int64.
+func (v *Value) MustInt64() int64 {
+ return v.data.(int64)
+}
+
+// Int64Slice gets the value as a []int64, returns the optionalDefault
+// value or nil if the value is not a []int64.
+func (v *Value) Int64Slice(optionalDefault ...[]int64) []int64 {
+ if s, ok := v.data.([]int64); ok {
+ return s
+ }
+ if len(optionalDefault) == 1 {
+ return optionalDefault[0]
+ }
+ return nil
+}
+
+// MustInt64Slice gets the value as a []int64.
+//
+// Panics if the object is not a []int64.
+func (v *Value) MustInt64Slice() []int64 {
+ return v.data.([]int64)
+}
+
+// IsInt64 gets whether the object contained is a int64 or not.
+func (v *Value) IsInt64() bool {
+ _, ok := v.data.(int64)
+ return ok
+}
+
+// IsInt64Slice gets whether the object contained is a []int64 or not.
+func (v *Value) IsInt64Slice() bool {
+ _, ok := v.data.([]int64)
+ return ok
+}
+
+// EachInt64 calls the specified callback for each object
+// in the []int64.
+//
+// Panics if the object is the wrong type.
+func (v *Value) EachInt64(callback func(int, int64) bool) *Value {
+
+ for index, val := range v.MustInt64Slice() {
+ carryon := callback(index, val)
+ if carryon == false {
+ break
+ }
+ }
+
+ return v
+
+}
+
+// WhereInt64 uses the specified decider function to select items
+// from the []int64. The object contained in the result will contain
+// only the selected items.
+func (v *Value) WhereInt64(decider func(int, int64) bool) *Value {
+
+ var selected []int64
+
+ v.EachInt64(func(index int, val int64) bool {
+ shouldSelect := decider(index, val)
+ if shouldSelect == false {
+ selected = append(selected, val)
+ }
+ return true
+ })
+
+ return &Value{data: selected}
+
+}
+
+// GroupInt64 uses the specified grouper function to group the items
+// keyed by the return of the grouper. The object contained in the
+// result will contain a map[string][]int64.
+func (v *Value) GroupInt64(grouper func(int, int64) string) *Value {
+
+ groups := make(map[string][]int64)
+
+ v.EachInt64(func(index int, val int64) bool {
+ group := grouper(index, val)
+ if _, ok := groups[group]; !ok {
+ groups[group] = make([]int64, 0)
+ }
+ groups[group] = append(groups[group], val)
+ return true
+ })
+
+ return &Value{data: groups}
+
+}
+
+// ReplaceInt64 uses the specified function to replace each int64s
+// by iterating each item. The data in the returned result will be a
+// []int64 containing the replaced items.
+func (v *Value) ReplaceInt64(replacer func(int, int64) int64) *Value {
+
+ arr := v.MustInt64Slice()
+ replaced := make([]int64, len(arr))
+
+ v.EachInt64(func(index int, val int64) bool {
+ replaced[index] = replacer(index, val)
+ return true
+ })
+
+ return &Value{data: replaced}
+
+}
+
+// CollectInt64 uses the specified collector function to collect a value
+// for each of the int64s in the slice. The data returned will be a
+// []interface{}.
+func (v *Value) CollectInt64(collector func(int, int64) interface{}) *Value {
+
+ arr := v.MustInt64Slice()
+ collected := make([]interface{}, len(arr))
+
+ v.EachInt64(func(index int, val int64) bool {
+ collected[index] = collector(index, val)
+ return true
+ })
+
+ return &Value{data: collected}
+}
+
+/*
+ Uint (uint and []uint)
+ --------------------------------------------------
+*/
+
+// Uint gets the value as a uint, returns the optionalDefault
+// value or a system default object if the value is the wrong type.
+func (v *Value) Uint(optionalDefault ...uint) uint {
+ if s, ok := v.data.(uint); ok {
+ return s
+ }
+ if len(optionalDefault) == 1 {
+ return optionalDefault[0]
+ }
+ return 0
+}
+
+// MustUint gets the value as a uint.
+//
+// Panics if the object is not a uint.
+func (v *Value) MustUint() uint {
+ return v.data.(uint)
+}
+
+// UintSlice gets the value as a []uint, returns the optionalDefault
+// value or nil if the value is not a []uint.
+func (v *Value) UintSlice(optionalDefault ...[]uint) []uint {
+ if s, ok := v.data.([]uint); ok {
+ return s
+ }
+ if len(optionalDefault) == 1 {
+ return optionalDefault[0]
+ }
+ return nil
+}
+
+// MustUintSlice gets the value as a []uint.
+//
+// Panics if the object is not a []uint.
+func (v *Value) MustUintSlice() []uint {
+ return v.data.([]uint)
+}
+
+// IsUint gets whether the object contained is a uint or not.
+func (v *Value) IsUint() bool {
+ _, ok := v.data.(uint)
+ return ok
+}
+
+// IsUintSlice gets whether the object contained is a []uint or not.
+func (v *Value) IsUintSlice() bool {
+ _, ok := v.data.([]uint)
+ return ok
+}
+
+// EachUint calls the specified callback for each object
+// in the []uint.
+//
+// Panics if the object is the wrong type.
+func (v *Value) EachUint(callback func(int, uint) bool) *Value {
+
+ for index, val := range v.MustUintSlice() {
+ carryon := callback(index, val)
+ if carryon == false {
+ break
+ }
+ }
+
+ return v
+
+}
+
+// WhereUint uses the specified decider function to select items
+// from the []uint. The object contained in the result will contain
+// only the selected items.
+func (v *Value) WhereUint(decider func(int, uint) bool) *Value {
+
+ var selected []uint
+
+ v.EachUint(func(index int, val uint) bool {
+ shouldSelect := decider(index, val)
+ if shouldSelect == false {
+ selected = append(selected, val)
+ }
+ return true
+ })
+
+ return &Value{data: selected}
+
+}
+
+// GroupUint uses the specified grouper function to group the items
+// keyed by the return of the grouper. The object contained in the
+// result will contain a map[string][]uint.
+func (v *Value) GroupUint(grouper func(int, uint) string) *Value {
+
+ groups := make(map[string][]uint)
+
+ v.EachUint(func(index int, val uint) bool {
+ group := grouper(index, val)
+ if _, ok := groups[group]; !ok {
+ groups[group] = make([]uint, 0)
+ }
+ groups[group] = append(groups[group], val)
+ return true
+ })
+
+ return &Value{data: groups}
+
+}
+
+// ReplaceUint uses the specified function to replace each uints
+// by iterating each item. The data in the returned result will be a
+// []uint containing the replaced items.
+func (v *Value) ReplaceUint(replacer func(int, uint) uint) *Value {
+
+ arr := v.MustUintSlice()
+ replaced := make([]uint, len(arr))
+
+ v.EachUint(func(index int, val uint) bool {
+ replaced[index] = replacer(index, val)
+ return true
+ })
+
+ return &Value{data: replaced}
+
+}
+
+// CollectUint uses the specified collector function to collect a value
+// for each of the uints in the slice. The data returned will be a
+// []interface{}.
+func (v *Value) CollectUint(collector func(int, uint) interface{}) *Value {
+
+ arr := v.MustUintSlice()
+ collected := make([]interface{}, len(arr))
+
+ v.EachUint(func(index int, val uint) bool {
+ collected[index] = collector(index, val)
+ return true
+ })
+
+ return &Value{data: collected}
+}
+
+/*
+ Uint8 (uint8 and []uint8)
+ --------------------------------------------------
+*/
+
+// Uint8 gets the value as a uint8, returns the optionalDefault
+// value or a system default object if the value is the wrong type.
+func (v *Value) Uint8(optionalDefault ...uint8) uint8 {
+ if s, ok := v.data.(uint8); ok {
+ return s
+ }
+ if len(optionalDefault) == 1 {
+ return optionalDefault[0]
+ }
+ return 0
+}
+
+// MustUint8 gets the value as a uint8.
+//
+// Panics if the object is not a uint8.
+func (v *Value) MustUint8() uint8 {
+ return v.data.(uint8)
+}
+
+// Uint8Slice gets the value as a []uint8, returns the optionalDefault
+// value or nil if the value is not a []uint8.
+func (v *Value) Uint8Slice(optionalDefault ...[]uint8) []uint8 {
+ if s, ok := v.data.([]uint8); ok {
+ return s
+ }
+ if len(optionalDefault) == 1 {
+ return optionalDefault[0]
+ }
+ return nil
+}
+
+// MustUint8Slice gets the value as a []uint8.
+//
+// Panics if the object is not a []uint8.
+func (v *Value) MustUint8Slice() []uint8 {
+ return v.data.([]uint8)
+}
+
+// IsUint8 gets whether the object contained is a uint8 or not.
+func (v *Value) IsUint8() bool {
+ _, ok := v.data.(uint8)
+ return ok
+}
+
+// IsUint8Slice gets whether the object contained is a []uint8 or not.
+func (v *Value) IsUint8Slice() bool {
+ _, ok := v.data.([]uint8)
+ return ok
+}
+
+// EachUint8 calls the specified callback for each object
+// in the []uint8.
+//
+// Panics if the object is the wrong type.
+func (v *Value) EachUint8(callback func(int, uint8) bool) *Value {
+
+ for index, val := range v.MustUint8Slice() {
+ carryon := callback(index, val)
+ if carryon == false {
+ break
+ }
+ }
+
+ return v
+
+}
+
+// WhereUint8 uses the specified decider function to select items
+// from the []uint8. The object contained in the result will contain
+// only the selected items.
+func (v *Value) WhereUint8(decider func(int, uint8) bool) *Value {
+
+ var selected []uint8
+
+ v.EachUint8(func(index int, val uint8) bool {
+ shouldSelect := decider(index, val)
+ if shouldSelect == false {
+ selected = append(selected, val)
+ }
+ return true
+ })
+
+ return &Value{data: selected}
+
+}
+
+// GroupUint8 uses the specified grouper function to group the items
+// keyed by the return of the grouper. The object contained in the
+// result will contain a map[string][]uint8.
+func (v *Value) GroupUint8(grouper func(int, uint8) string) *Value {
+
+ groups := make(map[string][]uint8)
+
+ v.EachUint8(func(index int, val uint8) bool {
+ group := grouper(index, val)
+ if _, ok := groups[group]; !ok {
+ groups[group] = make([]uint8, 0)
+ }
+ groups[group] = append(groups[group], val)
+ return true
+ })
+
+ return &Value{data: groups}
+
+}
+
+// ReplaceUint8 uses the specified function to replace each uint8s
+// by iterating each item. The data in the returned result will be a
+// []uint8 containing the replaced items.
+func (v *Value) ReplaceUint8(replacer func(int, uint8) uint8) *Value {
+
+ arr := v.MustUint8Slice()
+ replaced := make([]uint8, len(arr))
+
+ v.EachUint8(func(index int, val uint8) bool {
+ replaced[index] = replacer(index, val)
+ return true
+ })
+
+ return &Value{data: replaced}
+
+}
+
+// CollectUint8 uses the specified collector function to collect a value
+// for each of the uint8s in the slice. The data returned will be a
+// []interface{}.
+func (v *Value) CollectUint8(collector func(int, uint8) interface{}) *Value {
+
+ arr := v.MustUint8Slice()
+ collected := make([]interface{}, len(arr))
+
+ v.EachUint8(func(index int, val uint8) bool {
+ collected[index] = collector(index, val)
+ return true
+ })
+
+ return &Value{data: collected}
+}
+
+/*
+ Uint16 (uint16 and []uint16)
+ --------------------------------------------------
+*/
+
+// Uint16 gets the value as a uint16, returns the optionalDefault
+// value or a system default object if the value is the wrong type.
+func (v *Value) Uint16(optionalDefault ...uint16) uint16 {
+ if s, ok := v.data.(uint16); ok {
+ return s
+ }
+ if len(optionalDefault) == 1 {
+ return optionalDefault[0]
+ }
+ return 0
+}
+
+// MustUint16 gets the value as a uint16.
+//
+// Panics if the object is not a uint16.
+func (v *Value) MustUint16() uint16 {
+ return v.data.(uint16)
+}
+
+// Uint16Slice gets the value as a []uint16, returns the optionalDefault
+// value or nil if the value is not a []uint16.
+func (v *Value) Uint16Slice(optionalDefault ...[]uint16) []uint16 {
+ if s, ok := v.data.([]uint16); ok {
+ return s
+ }
+ if len(optionalDefault) == 1 {
+ return optionalDefault[0]
+ }
+ return nil
+}
+
+// MustUint16Slice gets the value as a []uint16.
+//
+// Panics if the object is not a []uint16.
+func (v *Value) MustUint16Slice() []uint16 {
+ return v.data.([]uint16)
+}
+
+// IsUint16 gets whether the object contained is a uint16 or not.
+func (v *Value) IsUint16() bool {
+ _, ok := v.data.(uint16)
+ return ok
+}
+
+// IsUint16Slice gets whether the object contained is a []uint16 or not.
+func (v *Value) IsUint16Slice() bool {
+ _, ok := v.data.([]uint16)
+ return ok
+}
+
+// EachUint16 calls the specified callback for each object
+// in the []uint16.
+//
+// Panics if the object is the wrong type.
+func (v *Value) EachUint16(callback func(int, uint16) bool) *Value {
+
+ for index, val := range v.MustUint16Slice() {
+ carryon := callback(index, val)
+ if carryon == false {
+ break
+ }
+ }
+
+ return v
+
+}
+
+// WhereUint16 uses the specified decider function to select items
+// from the []uint16. The object contained in the result will contain
+// only the selected items.
+func (v *Value) WhereUint16(decider func(int, uint16) bool) *Value {
+
+ var selected []uint16
+
+ v.EachUint16(func(index int, val uint16) bool {
+ shouldSelect := decider(index, val)
+ if shouldSelect == false {
+ selected = append(selected, val)
+ }
+ return true
+ })
+
+ return &Value{data: selected}
+
+}
+
+// GroupUint16 uses the specified grouper function to group the items
+// keyed by the return of the grouper. The object contained in the
+// result will contain a map[string][]uint16.
+func (v *Value) GroupUint16(grouper func(int, uint16) string) *Value {
+
+ groups := make(map[string][]uint16)
+
+ v.EachUint16(func(index int, val uint16) bool {
+ group := grouper(index, val)
+ if _, ok := groups[group]; !ok {
+ groups[group] = make([]uint16, 0)
+ }
+ groups[group] = append(groups[group], val)
+ return true
+ })
+
+ return &Value{data: groups}
+
+}
+
+// ReplaceUint16 uses the specified function to replace each uint16s
+// by iterating each item. The data in the returned result will be a
+// []uint16 containing the replaced items.
+func (v *Value) ReplaceUint16(replacer func(int, uint16) uint16) *Value {
+
+ arr := v.MustUint16Slice()
+ replaced := make([]uint16, len(arr))
+
+ v.EachUint16(func(index int, val uint16) bool {
+ replaced[index] = replacer(index, val)
+ return true
+ })
+
+ return &Value{data: replaced}
+
+}
+
+// CollectUint16 uses the specified collector function to collect a value
+// for each of the uint16s in the slice. The data returned will be a
+// []interface{}.
+func (v *Value) CollectUint16(collector func(int, uint16) interface{}) *Value {
+
+ arr := v.MustUint16Slice()
+ collected := make([]interface{}, len(arr))
+
+ v.EachUint16(func(index int, val uint16) bool {
+ collected[index] = collector(index, val)
+ return true
+ })
+
+ return &Value{data: collected}
+}
+
+/*
+ Uint32 (uint32 and []uint32)
+ --------------------------------------------------
+*/
+
+// Uint32 gets the value as a uint32, returns the optionalDefault
+// value or a system default object if the value is the wrong type.
+func (v *Value) Uint32(optionalDefault ...uint32) uint32 {
+ if s, ok := v.data.(uint32); ok {
+ return s
+ }
+ if len(optionalDefault) == 1 {
+ return optionalDefault[0]
+ }
+ return 0
+}
+
+// MustUint32 gets the value as a uint32.
+//
+// Panics if the object is not a uint32.
+func (v *Value) MustUint32() uint32 {
+ return v.data.(uint32)
+}
+
+// Uint32Slice gets the value as a []uint32, returns the optionalDefault
+// value or nil if the value is not a []uint32.
+func (v *Value) Uint32Slice(optionalDefault ...[]uint32) []uint32 {
+ if s, ok := v.data.([]uint32); ok {
+ return s
+ }
+ if len(optionalDefault) == 1 {
+ return optionalDefault[0]
+ }
+ return nil
+}
+
+// MustUint32Slice gets the value as a []uint32.
+//
+// Panics if the object is not a []uint32.
+func (v *Value) MustUint32Slice() []uint32 {
+ return v.data.([]uint32)
+}
+
+// IsUint32 gets whether the object contained is a uint32 or not.
+func (v *Value) IsUint32() bool {
+ _, ok := v.data.(uint32)
+ return ok
+}
+
+// IsUint32Slice gets whether the object contained is a []uint32 or not.
+func (v *Value) IsUint32Slice() bool {
+ _, ok := v.data.([]uint32)
+ return ok
+}
+
+// EachUint32 calls the specified callback for each object
+// in the []uint32.
+//
+// Panics if the object is the wrong type.
+func (v *Value) EachUint32(callback func(int, uint32) bool) *Value {
+
+ for index, val := range v.MustUint32Slice() {
+ carryon := callback(index, val)
+ if carryon == false {
+ break
+ }
+ }
+
+ return v
+
+}
+
+// WhereUint32 uses the specified decider function to select items
+// from the []uint32. The object contained in the result will contain
+// only the selected items.
+func (v *Value) WhereUint32(decider func(int, uint32) bool) *Value {
+
+ var selected []uint32
+
+ v.EachUint32(func(index int, val uint32) bool {
+ shouldSelect := decider(index, val)
+ if shouldSelect == false {
+ selected = append(selected, val)
+ }
+ return true
+ })
+
+ return &Value{data: selected}
+
+}
+
+// GroupUint32 uses the specified grouper function to group the items
+// keyed by the return of the grouper. The object contained in the
+// result will contain a map[string][]uint32.
+func (v *Value) GroupUint32(grouper func(int, uint32) string) *Value {
+
+ groups := make(map[string][]uint32)
+
+ v.EachUint32(func(index int, val uint32) bool {
+ group := grouper(index, val)
+ if _, ok := groups[group]; !ok {
+ groups[group] = make([]uint32, 0)
+ }
+ groups[group] = append(groups[group], val)
+ return true
+ })
+
+ return &Value{data: groups}
+
+}
+
+// ReplaceUint32 uses the specified function to replace each uint32s
+// by iterating each item. The data in the returned result will be a
+// []uint32 containing the replaced items.
+func (v *Value) ReplaceUint32(replacer func(int, uint32) uint32) *Value {
+
+ arr := v.MustUint32Slice()
+ replaced := make([]uint32, len(arr))
+
+ v.EachUint32(func(index int, val uint32) bool {
+ replaced[index] = replacer(index, val)
+ return true
+ })
+
+ return &Value{data: replaced}
+
+}
+
+// CollectUint32 uses the specified collector function to collect a value
+// for each of the uint32s in the slice. The data returned will be a
+// []interface{}.
+func (v *Value) CollectUint32(collector func(int, uint32) interface{}) *Value {
+
+ arr := v.MustUint32Slice()
+ collected := make([]interface{}, len(arr))
+
+ v.EachUint32(func(index int, val uint32) bool {
+ collected[index] = collector(index, val)
+ return true
+ })
+
+ return &Value{data: collected}
+}
+
+/*
+ Uint64 (uint64 and []uint64)
+ --------------------------------------------------
+*/
+
+// Uint64 gets the value as a uint64, returns the optionalDefault
+// value or a system default object if the value is the wrong type.
+func (v *Value) Uint64(optionalDefault ...uint64) uint64 {
+ if s, ok := v.data.(uint64); ok {
+ return s
+ }
+ if len(optionalDefault) == 1 {
+ return optionalDefault[0]
+ }
+ return 0
+}
+
+// MustUint64 gets the value as a uint64.
+//
+// Panics if the object is not a uint64.
+func (v *Value) MustUint64() uint64 {
+ return v.data.(uint64)
+}
+
+// Uint64Slice gets the value as a []uint64, returns the optionalDefault
+// value or nil if the value is not a []uint64.
+func (v *Value) Uint64Slice(optionalDefault ...[]uint64) []uint64 {
+ if s, ok := v.data.([]uint64); ok {
+ return s
+ }
+ if len(optionalDefault) == 1 {
+ return optionalDefault[0]
+ }
+ return nil
+}
+
+// MustUint64Slice gets the value as a []uint64.
+//
+// Panics if the object is not a []uint64.
+func (v *Value) MustUint64Slice() []uint64 {
+ return v.data.([]uint64)
+}
+
+// IsUint64 gets whether the object contained is a uint64 or not.
+func (v *Value) IsUint64() bool {
+ _, ok := v.data.(uint64)
+ return ok
+}
+
+// IsUint64Slice gets whether the object contained is a []uint64 or not.
+func (v *Value) IsUint64Slice() bool {
+ _, ok := v.data.([]uint64)
+ return ok
+}
+
+// EachUint64 calls the specified callback for each object
+// in the []uint64.
+//
+// Panics if the object is the wrong type.
+func (v *Value) EachUint64(callback func(int, uint64) bool) *Value {
+
+ for index, val := range v.MustUint64Slice() {
+ carryon := callback(index, val)
+ if carryon == false {
+ break
+ }
+ }
+
+ return v
+
+}
+
+// WhereUint64 uses the specified decider function to select items
+// from the []uint64. The object contained in the result will contain
+// only the selected items.
+func (v *Value) WhereUint64(decider func(int, uint64) bool) *Value {
+
+ var selected []uint64
+
+ v.EachUint64(func(index int, val uint64) bool {
+ shouldSelect := decider(index, val)
+ if shouldSelect == false {
+ selected = append(selected, val)
+ }
+ return true
+ })
+
+ return &Value{data: selected}
+
+}
+
+// GroupUint64 uses the specified grouper function to group the items
+// keyed by the return of the grouper. The object contained in the
+// result will contain a map[string][]uint64.
+func (v *Value) GroupUint64(grouper func(int, uint64) string) *Value {
+
+ groups := make(map[string][]uint64)
+
+ v.EachUint64(func(index int, val uint64) bool {
+ group := grouper(index, val)
+ if _, ok := groups[group]; !ok {
+ groups[group] = make([]uint64, 0)
+ }
+ groups[group] = append(groups[group], val)
+ return true
+ })
+
+ return &Value{data: groups}
+
+}
+
+// ReplaceUint64 uses the specified function to replace each uint64s
+// by iterating each item. The data in the returned result will be a
+// []uint64 containing the replaced items.
+func (v *Value) ReplaceUint64(replacer func(int, uint64) uint64) *Value {
+
+ arr := v.MustUint64Slice()
+ replaced := make([]uint64, len(arr))
+
+ v.EachUint64(func(index int, val uint64) bool {
+ replaced[index] = replacer(index, val)
+ return true
+ })
+
+ return &Value{data: replaced}
+
+}
+
+// CollectUint64 uses the specified collector function to collect a value
+// for each of the uint64s in the slice. The data returned will be a
+// []interface{}.
+func (v *Value) CollectUint64(collector func(int, uint64) interface{}) *Value {
+
+ arr := v.MustUint64Slice()
+ collected := make([]interface{}, len(arr))
+
+ v.EachUint64(func(index int, val uint64) bool {
+ collected[index] = collector(index, val)
+ return true
+ })
+
+ return &Value{data: collected}
+}
+
+/*
+ Uintptr (uintptr and []uintptr)
+ --------------------------------------------------
+*/
+
+// Uintptr gets the value as a uintptr, returns the optionalDefault
+// value or a system default object if the value is the wrong type.
+func (v *Value) Uintptr(optionalDefault ...uintptr) uintptr {
+ if s, ok := v.data.(uintptr); ok {
+ return s
+ }
+ if len(optionalDefault) == 1 {
+ return optionalDefault[0]
+ }
+ return 0
+}
+
+// MustUintptr gets the value as a uintptr.
+//
+// Panics if the object is not a uintptr.
+func (v *Value) MustUintptr() uintptr {
+ return v.data.(uintptr)
+}
+
+// UintptrSlice gets the value as a []uintptr, returns the optionalDefault
+// value or nil if the value is not a []uintptr.
+func (v *Value) UintptrSlice(optionalDefault ...[]uintptr) []uintptr {
+ if s, ok := v.data.([]uintptr); ok {
+ return s
+ }
+ if len(optionalDefault) == 1 {
+ return optionalDefault[0]
+ }
+ return nil
+}
+
+// MustUintptrSlice gets the value as a []uintptr.
+//
+// Panics if the object is not a []uintptr.
+func (v *Value) MustUintptrSlice() []uintptr {
+ return v.data.([]uintptr)
+}
+
+// IsUintptr gets whether the object contained is a uintptr or not.
+func (v *Value) IsUintptr() bool {
+ _, ok := v.data.(uintptr)
+ return ok
+}
+
+// IsUintptrSlice gets whether the object contained is a []uintptr or not.
+func (v *Value) IsUintptrSlice() bool {
+ _, ok := v.data.([]uintptr)
+ return ok
+}
+
+// EachUintptr calls the specified callback for each object
+// in the []uintptr.
+//
+// Panics if the object is the wrong type.
+func (v *Value) EachUintptr(callback func(int, uintptr) bool) *Value {
+
+ for index, val := range v.MustUintptrSlice() {
+ carryon := callback(index, val)
+ if carryon == false {
+ break
+ }
+ }
+
+ return v
+
+}
+
+// WhereUintptr uses the specified decider function to select items
+// from the []uintptr. The object contained in the result will contain
+// only the selected items.
+func (v *Value) WhereUintptr(decider func(int, uintptr) bool) *Value {
+
+ var selected []uintptr
+
+ v.EachUintptr(func(index int, val uintptr) bool {
+ shouldSelect := decider(index, val)
+ if shouldSelect == false {
+ selected = append(selected, val)
+ }
+ return true
+ })
+
+ return &Value{data: selected}
+
+}
+
+// GroupUintptr uses the specified grouper function to group the items
+// keyed by the return of the grouper. The object contained in the
+// result will contain a map[string][]uintptr.
+func (v *Value) GroupUintptr(grouper func(int, uintptr) string) *Value {
+
+ groups := make(map[string][]uintptr)
+
+ v.EachUintptr(func(index int, val uintptr) bool {
+ group := grouper(index, val)
+ if _, ok := groups[group]; !ok {
+ groups[group] = make([]uintptr, 0)
+ }
+ groups[group] = append(groups[group], val)
+ return true
+ })
+
+ return &Value{data: groups}
+
+}
+
+// ReplaceUintptr uses the specified function to replace each uintptrs
+// by iterating each item. The data in the returned result will be a
+// []uintptr containing the replaced items.
+func (v *Value) ReplaceUintptr(replacer func(int, uintptr) uintptr) *Value {
+
+ arr := v.MustUintptrSlice()
+ replaced := make([]uintptr, len(arr))
+
+ v.EachUintptr(func(index int, val uintptr) bool {
+ replaced[index] = replacer(index, val)
+ return true
+ })
+
+ return &Value{data: replaced}
+
+}
+
+// CollectUintptr uses the specified collector function to collect a value
+// for each of the uintptrs in the slice. The data returned will be a
+// []interface{}.
+func (v *Value) CollectUintptr(collector func(int, uintptr) interface{}) *Value {
+
+ arr := v.MustUintptrSlice()
+ collected := make([]interface{}, len(arr))
+
+ v.EachUintptr(func(index int, val uintptr) bool {
+ collected[index] = collector(index, val)
+ return true
+ })
+
+ return &Value{data: collected}
+}
+
+/*
+ Float32 (float32 and []float32)
+ --------------------------------------------------
+*/
+
+// Float32 gets the value as a float32, returns the optionalDefault
+// value or a system default object if the value is the wrong type.
+func (v *Value) Float32(optionalDefault ...float32) float32 {
+ if s, ok := v.data.(float32); ok {
+ return s
+ }
+ if len(optionalDefault) == 1 {
+ return optionalDefault[0]
+ }
+ return 0
+}
+
+// MustFloat32 gets the value as a float32.
+//
+// Panics if the object is not a float32.
+func (v *Value) MustFloat32() float32 {
+ return v.data.(float32)
+}
+
+// Float32Slice gets the value as a []float32, returns the optionalDefault
+// value or nil if the value is not a []float32.
+func (v *Value) Float32Slice(optionalDefault ...[]float32) []float32 {
+ if s, ok := v.data.([]float32); ok {
+ return s
+ }
+ if len(optionalDefault) == 1 {
+ return optionalDefault[0]
+ }
+ return nil
+}
+
+// MustFloat32Slice gets the value as a []float32.
+//
+// Panics if the object is not a []float32.
+func (v *Value) MustFloat32Slice() []float32 {
+ return v.data.([]float32)
+}
+
+// IsFloat32 gets whether the object contained is a float32 or not.
+func (v *Value) IsFloat32() bool {
+ _, ok := v.data.(float32)
+ return ok
+}
+
+// IsFloat32Slice gets whether the object contained is a []float32 or not.
+func (v *Value) IsFloat32Slice() bool {
+ _, ok := v.data.([]float32)
+ return ok
+}
+
+// EachFloat32 calls the specified callback for each object
+// in the []float32.
+//
+// Panics if the object is the wrong type.
+func (v *Value) EachFloat32(callback func(int, float32) bool) *Value {
+
+ for index, val := range v.MustFloat32Slice() {
+ carryon := callback(index, val)
+ if carryon == false {
+ break
+ }
+ }
+
+ return v
+
+}
+
+// WhereFloat32 uses the specified decider function to select items
+// from the []float32. The object contained in the result will contain
+// only the selected items.
+func (v *Value) WhereFloat32(decider func(int, float32) bool) *Value {
+
+ var selected []float32
+
+ v.EachFloat32(func(index int, val float32) bool {
+ shouldSelect := decider(index, val)
+ if shouldSelect == false {
+ selected = append(selected, val)
+ }
+ return true
+ })
+
+ return &Value{data: selected}
+
+}
+
+// GroupFloat32 uses the specified grouper function to group the items
+// keyed by the return of the grouper. The object contained in the
+// result will contain a map[string][]float32.
+func (v *Value) GroupFloat32(grouper func(int, float32) string) *Value {
+
+ groups := make(map[string][]float32)
+
+ v.EachFloat32(func(index int, val float32) bool {
+ group := grouper(index, val)
+ if _, ok := groups[group]; !ok {
+ groups[group] = make([]float32, 0)
+ }
+ groups[group] = append(groups[group], val)
+ return true
+ })
+
+ return &Value{data: groups}
+
+}
+
+// ReplaceFloat32 uses the specified function to replace each float32s
+// by iterating each item. The data in the returned result will be a
+// []float32 containing the replaced items.
+func (v *Value) ReplaceFloat32(replacer func(int, float32) float32) *Value {
+
+ arr := v.MustFloat32Slice()
+ replaced := make([]float32, len(arr))
+
+ v.EachFloat32(func(index int, val float32) bool {
+ replaced[index] = replacer(index, val)
+ return true
+ })
+
+ return &Value{data: replaced}
+
+}
+
+// CollectFloat32 uses the specified collector function to collect a value
+// for each of the float32s in the slice. The data returned will be a
+// []interface{}.
+func (v *Value) CollectFloat32(collector func(int, float32) interface{}) *Value {
+
+ arr := v.MustFloat32Slice()
+ collected := make([]interface{}, len(arr))
+
+ v.EachFloat32(func(index int, val float32) bool {
+ collected[index] = collector(index, val)
+ return true
+ })
+
+ return &Value{data: collected}
+}
+
+/*
+ Float64 (float64 and []float64)
+ --------------------------------------------------
+*/
+
+// Float64 gets the value as a float64, returns the optionalDefault
+// value or a system default object if the value is the wrong type.
+func (v *Value) Float64(optionalDefault ...float64) float64 {
+ if s, ok := v.data.(float64); ok {
+ return s
+ }
+ if len(optionalDefault) == 1 {
+ return optionalDefault[0]
+ }
+ return 0
+}
+
+// MustFloat64 gets the value as a float64.
+//
+// Panics if the object is not a float64.
+func (v *Value) MustFloat64() float64 {
+ return v.data.(float64)
+}
+
+// Float64Slice gets the value as a []float64, returns the optionalDefault
+// value or nil if the value is not a []float64.
+func (v *Value) Float64Slice(optionalDefault ...[]float64) []float64 {
+ if s, ok := v.data.([]float64); ok {
+ return s
+ }
+ if len(optionalDefault) == 1 {
+ return optionalDefault[0]
+ }
+ return nil
+}
+
+// MustFloat64Slice gets the value as a []float64.
+//
+// Panics if the object is not a []float64.
+func (v *Value) MustFloat64Slice() []float64 {
+ return v.data.([]float64)
+}
+
+// IsFloat64 gets whether the object contained is a float64 or not.
+func (v *Value) IsFloat64() bool {
+ _, ok := v.data.(float64)
+ return ok
+}
+
+// IsFloat64Slice gets whether the object contained is a []float64 or not.
+func (v *Value) IsFloat64Slice() bool {
+ _, ok := v.data.([]float64)
+ return ok
+}
+
+// EachFloat64 calls the specified callback for each object
+// in the []float64.
+//
+// Panics if the object is the wrong type.
+func (v *Value) EachFloat64(callback func(int, float64) bool) *Value {
+
+ for index, val := range v.MustFloat64Slice() {
+ carryon := callback(index, val)
+ if carryon == false {
+ break
+ }
+ }
+
+ return v
+
+}
+
+// WhereFloat64 uses the specified decider function to select items
+// from the []float64. The object contained in the result will contain
+// only the selected items.
+func (v *Value) WhereFloat64(decider func(int, float64) bool) *Value {
+
+ var selected []float64
+
+ v.EachFloat64(func(index int, val float64) bool {
+ shouldSelect := decider(index, val)
+ if shouldSelect == false {
+ selected = append(selected, val)
+ }
+ return true
+ })
+
+ return &Value{data: selected}
+
+}
+
+// GroupFloat64 uses the specified grouper function to group the items
+// keyed by the return of the grouper. The object contained in the
+// result will contain a map[string][]float64.
+func (v *Value) GroupFloat64(grouper func(int, float64) string) *Value {
+
+ groups := make(map[string][]float64)
+
+ v.EachFloat64(func(index int, val float64) bool {
+ group := grouper(index, val)
+ if _, ok := groups[group]; !ok {
+ groups[group] = make([]float64, 0)
+ }
+ groups[group] = append(groups[group], val)
+ return true
+ })
+
+ return &Value{data: groups}
+
+}
+
+// ReplaceFloat64 uses the specified function to replace each float64s
+// by iterating each item. The data in the returned result will be a
+// []float64 containing the replaced items.
+func (v *Value) ReplaceFloat64(replacer func(int, float64) float64) *Value {
+
+ arr := v.MustFloat64Slice()
+ replaced := make([]float64, len(arr))
+
+ v.EachFloat64(func(index int, val float64) bool {
+ replaced[index] = replacer(index, val)
+ return true
+ })
+
+ return &Value{data: replaced}
+
+}
+
+// CollectFloat64 uses the specified collector function to collect a value
+// for each of the float64s in the slice. The data returned will be a
+// []interface{}.
+func (v *Value) CollectFloat64(collector func(int, float64) interface{}) *Value {
+
+ arr := v.MustFloat64Slice()
+ collected := make([]interface{}, len(arr))
+
+ v.EachFloat64(func(index int, val float64) bool {
+ collected[index] = collector(index, val)
+ return true
+ })
+
+ return &Value{data: collected}
+}
+
+/*
+ Complex64 (complex64 and []complex64)
+ --------------------------------------------------
+*/
+
+// Complex64 gets the value as a complex64, returns the optionalDefault
+// value or a system default object if the value is the wrong type.
+func (v *Value) Complex64(optionalDefault ...complex64) complex64 {
+ if s, ok := v.data.(complex64); ok {
+ return s
+ }
+ if len(optionalDefault) == 1 {
+ return optionalDefault[0]
+ }
+ return 0
+}
+
+// MustComplex64 gets the value as a complex64.
+//
+// Panics if the object is not a complex64.
+func (v *Value) MustComplex64() complex64 {
+ return v.data.(complex64)
+}
+
+// Complex64Slice gets the value as a []complex64, returns the optionalDefault
+// value or nil if the value is not a []complex64.
+func (v *Value) Complex64Slice(optionalDefault ...[]complex64) []complex64 {
+ if s, ok := v.data.([]complex64); ok {
+ return s
+ }
+ if len(optionalDefault) == 1 {
+ return optionalDefault[0]
+ }
+ return nil
+}
+
+// MustComplex64Slice gets the value as a []complex64.
+//
+// Panics if the object is not a []complex64.
+func (v *Value) MustComplex64Slice() []complex64 {
+ return v.data.([]complex64)
+}
+
+// IsComplex64 gets whether the object contained is a complex64 or not.
+func (v *Value) IsComplex64() bool {
+ _, ok := v.data.(complex64)
+ return ok
+}
+
+// IsComplex64Slice gets whether the object contained is a []complex64 or not.
+func (v *Value) IsComplex64Slice() bool {
+ _, ok := v.data.([]complex64)
+ return ok
+}
+
+// EachComplex64 calls the specified callback for each object
+// in the []complex64.
+//
+// Panics if the object is the wrong type.
+func (v *Value) EachComplex64(callback func(int, complex64) bool) *Value {
+
+ for index, val := range v.MustComplex64Slice() {
+ carryon := callback(index, val)
+ if carryon == false {
+ break
+ }
+ }
+
+ return v
+
+}
+
+// WhereComplex64 uses the specified decider function to select items
+// from the []complex64. The object contained in the result will contain
+// only the selected items.
+func (v *Value) WhereComplex64(decider func(int, complex64) bool) *Value {
+
+ var selected []complex64
+
+ v.EachComplex64(func(index int, val complex64) bool {
+ shouldSelect := decider(index, val)
+ if shouldSelect == false {
+ selected = append(selected, val)
+ }
+ return true
+ })
+
+ return &Value{data: selected}
+
+}
+
+// GroupComplex64 uses the specified grouper function to group the items
+// keyed by the return of the grouper. The object contained in the
+// result will contain a map[string][]complex64.
+func (v *Value) GroupComplex64(grouper func(int, complex64) string) *Value {
+
+ groups := make(map[string][]complex64)
+
+ v.EachComplex64(func(index int, val complex64) bool {
+ group := grouper(index, val)
+ if _, ok := groups[group]; !ok {
+ groups[group] = make([]complex64, 0)
+ }
+ groups[group] = append(groups[group], val)
+ return true
+ })
+
+ return &Value{data: groups}
+
+}
+
+// ReplaceComplex64 uses the specified function to replace each complex64s
+// by iterating each item. The data in the returned result will be a
+// []complex64 containing the replaced items.
+func (v *Value) ReplaceComplex64(replacer func(int, complex64) complex64) *Value {
+
+ arr := v.MustComplex64Slice()
+ replaced := make([]complex64, len(arr))
+
+ v.EachComplex64(func(index int, val complex64) bool {
+ replaced[index] = replacer(index, val)
+ return true
+ })
+
+ return &Value{data: replaced}
+
+}
+
+// CollectComplex64 uses the specified collector function to collect a value
+// for each of the complex64s in the slice. The data returned will be a
+// []interface{}.
+func (v *Value) CollectComplex64(collector func(int, complex64) interface{}) *Value {
+
+ arr := v.MustComplex64Slice()
+ collected := make([]interface{}, len(arr))
+
+ v.EachComplex64(func(index int, val complex64) bool {
+ collected[index] = collector(index, val)
+ return true
+ })
+
+ return &Value{data: collected}
+}
+
+/*
+ Complex128 (complex128 and []complex128)
+ --------------------------------------------------
+*/
+
+// Complex128 gets the value as a complex128, returns the optionalDefault
+// value or a system default object if the value is the wrong type.
+func (v *Value) Complex128(optionalDefault ...complex128) complex128 {
+ if s, ok := v.data.(complex128); ok {
+ return s
+ }
+ if len(optionalDefault) == 1 {
+ return optionalDefault[0]
+ }
+ return 0
+}
+
+// MustComplex128 gets the value as a complex128.
+//
+// Panics if the object is not a complex128.
+func (v *Value) MustComplex128() complex128 {
+ return v.data.(complex128)
+}
+
+// Complex128Slice gets the value as a []complex128, returns the optionalDefault
+// value or nil if the value is not a []complex128.
+func (v *Value) Complex128Slice(optionalDefault ...[]complex128) []complex128 {
+ if s, ok := v.data.([]complex128); ok {
+ return s
+ }
+ if len(optionalDefault) == 1 {
+ return optionalDefault[0]
+ }
+ return nil
+}
+
+// MustComplex128Slice gets the value as a []complex128.
+//
+// Panics if the object is not a []complex128.
+func (v *Value) MustComplex128Slice() []complex128 {
+ return v.data.([]complex128)
+}
+
+// IsComplex128 gets whether the object contained is a complex128 or not.
+func (v *Value) IsComplex128() bool {
+ _, ok := v.data.(complex128)
+ return ok
+}
+
+// IsComplex128Slice gets whether the object contained is a []complex128 or not.
+func (v *Value) IsComplex128Slice() bool {
+ _, ok := v.data.([]complex128)
+ return ok
+}
+
+// EachComplex128 calls the specified callback for each object
+// in the []complex128.
+//
+// Panics if the object is the wrong type.
+func (v *Value) EachComplex128(callback func(int, complex128) bool) *Value {
+
+ for index, val := range v.MustComplex128Slice() {
+ carryon := callback(index, val)
+ if carryon == false {
+ break
+ }
+ }
+
+ return v
+
+}
+
+// WhereComplex128 uses the specified decider function to select items
+// from the []complex128. The object contained in the result will contain
+// only the selected items.
+func (v *Value) WhereComplex128(decider func(int, complex128) bool) *Value {
+
+ var selected []complex128
+
+ v.EachComplex128(func(index int, val complex128) bool {
+ shouldSelect := decider(index, val)
+ if shouldSelect == false {
+ selected = append(selected, val)
+ }
+ return true
+ })
+
+ return &Value{data: selected}
+
+}
+
+// GroupComplex128 uses the specified grouper function to group the items
+// keyed by the return of the grouper. The object contained in the
+// result will contain a map[string][]complex128.
+func (v *Value) GroupComplex128(grouper func(int, complex128) string) *Value {
+
+ groups := make(map[string][]complex128)
+
+ v.EachComplex128(func(index int, val complex128) bool {
+ group := grouper(index, val)
+ if _, ok := groups[group]; !ok {
+ groups[group] = make([]complex128, 0)
+ }
+ groups[group] = append(groups[group], val)
+ return true
+ })
+
+ return &Value{data: groups}
+
+}
+
+// ReplaceComplex128 uses the specified function to replace each complex128s
+// by iterating each item. The data in the returned result will be a
+// []complex128 containing the replaced items.
+func (v *Value) ReplaceComplex128(replacer func(int, complex128) complex128) *Value {
+
+ arr := v.MustComplex128Slice()
+ replaced := make([]complex128, len(arr))
+
+ v.EachComplex128(func(index int, val complex128) bool {
+ replaced[index] = replacer(index, val)
+ return true
+ })
+
+ return &Value{data: replaced}
+
+}
+
+// CollectComplex128 uses the specified collector function to collect a value
+// for each of the complex128s in the slice. The data returned will be a
+// []interface{}.
+func (v *Value) CollectComplex128(collector func(int, complex128) interface{}) *Value {
+
+ arr := v.MustComplex128Slice()
+ collected := make([]interface{}, len(arr))
+
+ v.EachComplex128(func(index int, val complex128) bool {
+ collected[index] = collector(index, val)
+ return true
+ })
+
+ return &Value{data: collected}
+}
diff --git a/vendor/github.com/stretchr/objx/type_specific_codegen_test.go b/vendor/github.com/stretchr/objx/type_specific_codegen_test.go
new file mode 100644
index 000000000..f7a4fceea
--- /dev/null
+++ b/vendor/github.com/stretchr/objx/type_specific_codegen_test.go
@@ -0,0 +1,2867 @@
+package objx
+
+import (
+ "fmt"
+ "github.com/stretchr/testify/assert"
+ "testing"
+)
+
+// ************************************************************
+// TESTS
+// ************************************************************
+
+func TestInter(t *testing.T) {
+
+ val := interface{}("something")
+ m := map[string]interface{}{"value": val, "nothing": nil}
+ assert.Equal(t, val, New(m).Get("value").Inter())
+ assert.Equal(t, val, New(m).Get("value").MustInter())
+ assert.Equal(t, interface{}(nil), New(m).Get("nothing").Inter())
+ assert.Equal(t, val, New(m).Get("nothing").Inter("something"))
+
+ assert.Panics(t, func() {
+ New(m).Get("age").MustInter()
+ })
+
+}
+
+func TestInterSlice(t *testing.T) {
+
+ val := interface{}("something")
+ m := map[string]interface{}{"value": []interface{}{val}, "nothing": nil}
+ assert.Equal(t, val, New(m).Get("value").InterSlice()[0])
+ assert.Equal(t, val, New(m).Get("value").MustInterSlice()[0])
+ assert.Equal(t, []interface{}(nil), New(m).Get("nothing").InterSlice())
+ assert.Equal(t, val, New(m).Get("nothing").InterSlice([]interface{}{interface{}("something")})[0])
+
+ assert.Panics(t, func() {
+ New(m).Get("nothing").MustInterSlice()
+ })
+
+}
+
+func TestIsInter(t *testing.T) {
+
+ var v *Value
+
+ v = &Value{data: interface{}("something")}
+ assert.True(t, v.IsInter())
+
+ v = &Value{data: []interface{}{interface{}("something")}}
+ assert.True(t, v.IsInterSlice())
+
+}
+
+func TestEachInter(t *testing.T) {
+
+ v := &Value{data: []interface{}{interface{}("something"), interface{}("something"), interface{}("something"), interface{}("something"), interface{}("something")}}
+ count := 0
+ replacedVals := make([]interface{}, 0)
+ assert.Equal(t, v, v.EachInter(func(i int, val interface{}) bool {
+
+ count++
+ replacedVals = append(replacedVals, val)
+
+ // abort early
+ if i == 2 {
+ return false
+ }
+
+ return true
+
+ }))
+
+ assert.Equal(t, count, 3)
+ assert.Equal(t, replacedVals[0], v.MustInterSlice()[0])
+ assert.Equal(t, replacedVals[1], v.MustInterSlice()[1])
+ assert.Equal(t, replacedVals[2], v.MustInterSlice()[2])
+
+}
+
+func TestWhereInter(t *testing.T) {
+
+ v := &Value{data: []interface{}{interface{}("something"), interface{}("something"), interface{}("something"), interface{}("something"), interface{}("something"), interface{}("something")}}
+
+ selected := v.WhereInter(func(i int, val interface{}) bool {
+ return i%2 == 0
+ }).MustInterSlice()
+
+ assert.Equal(t, 3, len(selected))
+
+}
+
+func TestGroupInter(t *testing.T) {
+
+ v := &Value{data: []interface{}{interface{}("something"), interface{}("something"), interface{}("something"), interface{}("something"), interface{}("something"), interface{}("something")}}
+
+ grouped := v.GroupInter(func(i int, val interface{}) string {
+ return fmt.Sprintf("%v", i%2 == 0)
+ }).data.(map[string][]interface{})
+
+ assert.Equal(t, 2, len(grouped))
+ assert.Equal(t, 3, len(grouped["true"]))
+ assert.Equal(t, 3, len(grouped["false"]))
+
+}
+
+func TestReplaceInter(t *testing.T) {
+
+ v := &Value{data: []interface{}{interface{}("something"), interface{}("something"), interface{}("something"), interface{}("something"), interface{}("something"), interface{}("something")}}
+
+ rawArr := v.MustInterSlice()
+
+ replaced := v.ReplaceInter(func(index int, val interface{}) interface{} {
+ if index < len(rawArr)-1 {
+ return rawArr[index+1]
+ }
+ return rawArr[0]
+ })
+
+ replacedArr := replaced.MustInterSlice()
+ if assert.Equal(t, 6, len(replacedArr)) {
+ assert.Equal(t, replacedArr[0], rawArr[1])
+ assert.Equal(t, replacedArr[1], rawArr[2])
+ assert.Equal(t, replacedArr[2], rawArr[3])
+ assert.Equal(t, replacedArr[3], rawArr[4])
+ assert.Equal(t, replacedArr[4], rawArr[5])
+ assert.Equal(t, replacedArr[5], rawArr[0])
+ }
+
+}
+
+func TestCollectInter(t *testing.T) {
+
+ v := &Value{data: []interface{}{interface{}("something"), interface{}("something"), interface{}("something"), interface{}("something"), interface{}("something"), interface{}("something")}}
+
+ collected := v.CollectInter(func(index int, val interface{}) interface{} {
+ return index
+ })
+
+ collectedArr := collected.MustInterSlice()
+ if assert.Equal(t, 6, len(collectedArr)) {
+ assert.Equal(t, collectedArr[0], 0)
+ assert.Equal(t, collectedArr[1], 1)
+ assert.Equal(t, collectedArr[2], 2)
+ assert.Equal(t, collectedArr[3], 3)
+ assert.Equal(t, collectedArr[4], 4)
+ assert.Equal(t, collectedArr[5], 5)
+ }
+
+}
+
+// ************************************************************
+// TESTS
+// ************************************************************
+
+func TestMSI(t *testing.T) {
+
+ val := map[string]interface{}(map[string]interface{}{"name": "Tyler"})
+ m := map[string]interface{}{"value": val, "nothing": nil}
+ assert.Equal(t, val, New(m).Get("value").MSI())
+ assert.Equal(t, val, New(m).Get("value").MustMSI())
+ assert.Equal(t, map[string]interface{}(nil), New(m).Get("nothing").MSI())
+ assert.Equal(t, val, New(m).Get("nothing").MSI(map[string]interface{}{"name": "Tyler"}))
+
+ assert.Panics(t, func() {
+ New(m).Get("age").MustMSI()
+ })
+
+}
+
+func TestMSISlice(t *testing.T) {
+
+ val := map[string]interface{}(map[string]interface{}{"name": "Tyler"})
+ m := map[string]interface{}{"value": []map[string]interface{}{val}, "nothing": nil}
+ assert.Equal(t, val, New(m).Get("value").MSISlice()[0])
+ assert.Equal(t, val, New(m).Get("value").MustMSISlice()[0])
+ assert.Equal(t, []map[string]interface{}(nil), New(m).Get("nothing").MSISlice())
+ assert.Equal(t, val, New(m).Get("nothing").MSISlice([]map[string]interface{}{map[string]interface{}(map[string]interface{}{"name": "Tyler"})})[0])
+
+ assert.Panics(t, func() {
+ New(m).Get("nothing").MustMSISlice()
+ })
+
+}
+
+func TestIsMSI(t *testing.T) {
+
+ var v *Value
+
+ v = &Value{data: map[string]interface{}(map[string]interface{}{"name": "Tyler"})}
+ assert.True(t, v.IsMSI())
+
+ v = &Value{data: []map[string]interface{}{map[string]interface{}(map[string]interface{}{"name": "Tyler"})}}
+ assert.True(t, v.IsMSISlice())
+
+}
+
+func TestEachMSI(t *testing.T) {
+
+ v := &Value{data: []map[string]interface{}{map[string]interface{}(map[string]interface{}{"name": "Tyler"}), map[string]interface{}(map[string]interface{}{"name": "Tyler"}), map[string]interface{}(map[string]interface{}{"name": "Tyler"}), map[string]interface{}(map[string]interface{}{"name": "Tyler"}), map[string]interface{}(map[string]interface{}{"name": "Tyler"})}}
+ count := 0
+ replacedVals := make([]map[string]interface{}, 0)
+ assert.Equal(t, v, v.EachMSI(func(i int, val map[string]interface{}) bool {
+
+ count++
+ replacedVals = append(replacedVals, val)
+
+ // abort early
+ if i == 2 {
+ return false
+ }
+
+ return true
+
+ }))
+
+ assert.Equal(t, count, 3)
+ assert.Equal(t, replacedVals[0], v.MustMSISlice()[0])
+ assert.Equal(t, replacedVals[1], v.MustMSISlice()[1])
+ assert.Equal(t, replacedVals[2], v.MustMSISlice()[2])
+
+}
+
+func TestWhereMSI(t *testing.T) {
+
+ v := &Value{data: []map[string]interface{}{map[string]interface{}(map[string]interface{}{"name": "Tyler"}), map[string]interface{}(map[string]interface{}{"name": "Tyler"}), map[string]interface{}(map[string]interface{}{"name": "Tyler"}), map[string]interface{}(map[string]interface{}{"name": "Tyler"}), map[string]interface{}(map[string]interface{}{"name": "Tyler"}), map[string]interface{}(map[string]interface{}{"name": "Tyler"})}}
+
+ selected := v.WhereMSI(func(i int, val map[string]interface{}) bool {
+ return i%2 == 0
+ }).MustMSISlice()
+
+ assert.Equal(t, 3, len(selected))
+
+}
+
+func TestGroupMSI(t *testing.T) {
+
+ v := &Value{data: []map[string]interface{}{map[string]interface{}(map[string]interface{}{"name": "Tyler"}), map[string]interface{}(map[string]interface{}{"name": "Tyler"}), map[string]interface{}(map[string]interface{}{"name": "Tyler"}), map[string]interface{}(map[string]interface{}{"name": "Tyler"}), map[string]interface{}(map[string]interface{}{"name": "Tyler"}), map[string]interface{}(map[string]interface{}{"name": "Tyler"})}}
+
+ grouped := v.GroupMSI(func(i int, val map[string]interface{}) string {
+ return fmt.Sprintf("%v", i%2 == 0)
+ }).data.(map[string][]map[string]interface{})
+
+ assert.Equal(t, 2, len(grouped))
+ assert.Equal(t, 3, len(grouped["true"]))
+ assert.Equal(t, 3, len(grouped["false"]))
+
+}
+
+func TestReplaceMSI(t *testing.T) {
+
+ v := &Value{data: []map[string]interface{}{map[string]interface{}(map[string]interface{}{"name": "Tyler"}), map[string]interface{}(map[string]interface{}{"name": "Tyler"}), map[string]interface{}(map[string]interface{}{"name": "Tyler"}), map[string]interface{}(map[string]interface{}{"name": "Tyler"}), map[string]interface{}(map[string]interface{}{"name": "Tyler"}), map[string]interface{}(map[string]interface{}{"name": "Tyler"})}}
+
+ rawArr := v.MustMSISlice()
+
+ replaced := v.ReplaceMSI(func(index int, val map[string]interface{}) map[string]interface{} {
+ if index < len(rawArr)-1 {
+ return rawArr[index+1]
+ }
+ return rawArr[0]
+ })
+
+ replacedArr := replaced.MustMSISlice()
+ if assert.Equal(t, 6, len(replacedArr)) {
+ assert.Equal(t, replacedArr[0], rawArr[1])
+ assert.Equal(t, replacedArr[1], rawArr[2])
+ assert.Equal(t, replacedArr[2], rawArr[3])
+ assert.Equal(t, replacedArr[3], rawArr[4])
+ assert.Equal(t, replacedArr[4], rawArr[5])
+ assert.Equal(t, replacedArr[5], rawArr[0])
+ }
+
+}
+
+func TestCollectMSI(t *testing.T) {
+
+ v := &Value{data: []map[string]interface{}{map[string]interface{}(map[string]interface{}{"name": "Tyler"}), map[string]interface{}(map[string]interface{}{"name": "Tyler"}), map[string]interface{}(map[string]interface{}{"name": "Tyler"}), map[string]interface{}(map[string]interface{}{"name": "Tyler"}), map[string]interface{}(map[string]interface{}{"name": "Tyler"}), map[string]interface{}(map[string]interface{}{"name": "Tyler"})}}
+
+ collected := v.CollectMSI(func(index int, val map[string]interface{}) interface{} {
+ return index
+ })
+
+ collectedArr := collected.MustInterSlice()
+ if assert.Equal(t, 6, len(collectedArr)) {
+ assert.Equal(t, collectedArr[0], 0)
+ assert.Equal(t, collectedArr[1], 1)
+ assert.Equal(t, collectedArr[2], 2)
+ assert.Equal(t, collectedArr[3], 3)
+ assert.Equal(t, collectedArr[4], 4)
+ assert.Equal(t, collectedArr[5], 5)
+ }
+
+}
+
+// ************************************************************
+// TESTS
+// ************************************************************
+
+func TestObjxMap(t *testing.T) {
+
+ val := (Map)(New(1))
+ m := map[string]interface{}{"value": val, "nothing": nil}
+ assert.Equal(t, val, New(m).Get("value").ObjxMap())
+ assert.Equal(t, val, New(m).Get("value").MustObjxMap())
+ assert.Equal(t, (Map)(New(nil)), New(m).Get("nothing").ObjxMap())
+ assert.Equal(t, val, New(m).Get("nothing").ObjxMap(New(1)))
+
+ assert.Panics(t, func() {
+ New(m).Get("age").MustObjxMap()
+ })
+
+}
+
+func TestObjxMapSlice(t *testing.T) {
+
+ val := (Map)(New(1))
+ m := map[string]interface{}{"value": [](Map){val}, "nothing": nil}
+ assert.Equal(t, val, New(m).Get("value").ObjxMapSlice()[0])
+ assert.Equal(t, val, New(m).Get("value").MustObjxMapSlice()[0])
+ assert.Equal(t, [](Map)(nil), New(m).Get("nothing").ObjxMapSlice())
+ assert.Equal(t, val, New(m).Get("nothing").ObjxMapSlice([](Map){(Map)(New(1))})[0])
+
+ assert.Panics(t, func() {
+ New(m).Get("nothing").MustObjxMapSlice()
+ })
+
+}
+
+func TestIsObjxMap(t *testing.T) {
+
+ var v *Value
+
+ v = &Value{data: (Map)(New(1))}
+ assert.True(t, v.IsObjxMap())
+
+ v = &Value{data: [](Map){(Map)(New(1))}}
+ assert.True(t, v.IsObjxMapSlice())
+
+}
+
+func TestEachObjxMap(t *testing.T) {
+
+ v := &Value{data: [](Map){(Map)(New(1)), (Map)(New(1)), (Map)(New(1)), (Map)(New(1)), (Map)(New(1))}}
+ count := 0
+ replacedVals := make([](Map), 0)
+ assert.Equal(t, v, v.EachObjxMap(func(i int, val Map) bool {
+
+ count++
+ replacedVals = append(replacedVals, val)
+
+ // abort early
+ if i == 2 {
+ return false
+ }
+
+ return true
+
+ }))
+
+ assert.Equal(t, count, 3)
+ assert.Equal(t, replacedVals[0], v.MustObjxMapSlice()[0])
+ assert.Equal(t, replacedVals[1], v.MustObjxMapSlice()[1])
+ assert.Equal(t, replacedVals[2], v.MustObjxMapSlice()[2])
+
+}
+
+func TestWhereObjxMap(t *testing.T) {
+
+ v := &Value{data: [](Map){(Map)(New(1)), (Map)(New(1)), (Map)(New(1)), (Map)(New(1)), (Map)(New(1)), (Map)(New(1))}}
+
+ selected := v.WhereObjxMap(func(i int, val Map) bool {
+ return i%2 == 0
+ }).MustObjxMapSlice()
+
+ assert.Equal(t, 3, len(selected))
+
+}
+
+func TestGroupObjxMap(t *testing.T) {
+
+ v := &Value{data: [](Map){(Map)(New(1)), (Map)(New(1)), (Map)(New(1)), (Map)(New(1)), (Map)(New(1)), (Map)(New(1))}}
+
+ grouped := v.GroupObjxMap(func(i int, val Map) string {
+ return fmt.Sprintf("%v", i%2 == 0)
+ }).data.(map[string][](Map))
+
+ assert.Equal(t, 2, len(grouped))
+ assert.Equal(t, 3, len(grouped["true"]))
+ assert.Equal(t, 3, len(grouped["false"]))
+
+}
+
+func TestReplaceObjxMap(t *testing.T) {
+
+ v := &Value{data: [](Map){(Map)(New(1)), (Map)(New(1)), (Map)(New(1)), (Map)(New(1)), (Map)(New(1)), (Map)(New(1))}}
+
+ rawArr := v.MustObjxMapSlice()
+
+ replaced := v.ReplaceObjxMap(func(index int, val Map) Map {
+ if index < len(rawArr)-1 {
+ return rawArr[index+1]
+ }
+ return rawArr[0]
+ })
+
+ replacedArr := replaced.MustObjxMapSlice()
+ if assert.Equal(t, 6, len(replacedArr)) {
+ assert.Equal(t, replacedArr[0], rawArr[1])
+ assert.Equal(t, replacedArr[1], rawArr[2])
+ assert.Equal(t, replacedArr[2], rawArr[3])
+ assert.Equal(t, replacedArr[3], rawArr[4])
+ assert.Equal(t, replacedArr[4], rawArr[5])
+ assert.Equal(t, replacedArr[5], rawArr[0])
+ }
+
+}
+
+func TestCollectObjxMap(t *testing.T) {
+
+ v := &Value{data: [](Map){(Map)(New(1)), (Map)(New(1)), (Map)(New(1)), (Map)(New(1)), (Map)(New(1)), (Map)(New(1))}}
+
+ collected := v.CollectObjxMap(func(index int, val Map) interface{} {
+ return index
+ })
+
+ collectedArr := collected.MustInterSlice()
+ if assert.Equal(t, 6, len(collectedArr)) {
+ assert.Equal(t, collectedArr[0], 0)
+ assert.Equal(t, collectedArr[1], 1)
+ assert.Equal(t, collectedArr[2], 2)
+ assert.Equal(t, collectedArr[3], 3)
+ assert.Equal(t, collectedArr[4], 4)
+ assert.Equal(t, collectedArr[5], 5)
+ }
+
+}
+
+// ************************************************************
+// TESTS
+// ************************************************************
+
+func TestBool(t *testing.T) {
+
+ val := bool(true)
+ m := map[string]interface{}{"value": val, "nothing": nil}
+ assert.Equal(t, val, New(m).Get("value").Bool())
+ assert.Equal(t, val, New(m).Get("value").MustBool())
+ assert.Equal(t, bool(false), New(m).Get("nothing").Bool())
+ assert.Equal(t, val, New(m).Get("nothing").Bool(true))
+
+ assert.Panics(t, func() {
+ New(m).Get("age").MustBool()
+ })
+
+}
+
+func TestBoolSlice(t *testing.T) {
+
+ val := bool(true)
+ m := map[string]interface{}{"value": []bool{val}, "nothing": nil}
+ assert.Equal(t, val, New(m).Get("value").BoolSlice()[0])
+ assert.Equal(t, val, New(m).Get("value").MustBoolSlice()[0])
+ assert.Equal(t, []bool(nil), New(m).Get("nothing").BoolSlice())
+ assert.Equal(t, val, New(m).Get("nothing").BoolSlice([]bool{bool(true)})[0])
+
+ assert.Panics(t, func() {
+ New(m).Get("nothing").MustBoolSlice()
+ })
+
+}
+
+func TestIsBool(t *testing.T) {
+
+ var v *Value
+
+ v = &Value{data: bool(true)}
+ assert.True(t, v.IsBool())
+
+ v = &Value{data: []bool{bool(true)}}
+ assert.True(t, v.IsBoolSlice())
+
+}
+
+func TestEachBool(t *testing.T) {
+
+ v := &Value{data: []bool{bool(true), bool(true), bool(true), bool(true), bool(true)}}
+ count := 0
+ replacedVals := make([]bool, 0)
+ assert.Equal(t, v, v.EachBool(func(i int, val bool) bool {
+
+ count++
+ replacedVals = append(replacedVals, val)
+
+ // abort early
+ if i == 2 {
+ return false
+ }
+
+ return true
+
+ }))
+
+ assert.Equal(t, count, 3)
+ assert.Equal(t, replacedVals[0], v.MustBoolSlice()[0])
+ assert.Equal(t, replacedVals[1], v.MustBoolSlice()[1])
+ assert.Equal(t, replacedVals[2], v.MustBoolSlice()[2])
+
+}
+
+func TestWhereBool(t *testing.T) {
+
+ v := &Value{data: []bool{bool(true), bool(true), bool(true), bool(true), bool(true), bool(true)}}
+
+ selected := v.WhereBool(func(i int, val bool) bool {
+ return i%2 == 0
+ }).MustBoolSlice()
+
+ assert.Equal(t, 3, len(selected))
+
+}
+
+func TestGroupBool(t *testing.T) {
+
+ v := &Value{data: []bool{bool(true), bool(true), bool(true), bool(true), bool(true), bool(true)}}
+
+ grouped := v.GroupBool(func(i int, val bool) string {
+ return fmt.Sprintf("%v", i%2 == 0)
+ }).data.(map[string][]bool)
+
+ assert.Equal(t, 2, len(grouped))
+ assert.Equal(t, 3, len(grouped["true"]))
+ assert.Equal(t, 3, len(grouped["false"]))
+
+}
+
+func TestReplaceBool(t *testing.T) {
+
+ v := &Value{data: []bool{bool(true), bool(true), bool(true), bool(true), bool(true), bool(true)}}
+
+ rawArr := v.MustBoolSlice()
+
+ replaced := v.ReplaceBool(func(index int, val bool) bool {
+ if index < len(rawArr)-1 {
+ return rawArr[index+1]
+ }
+ return rawArr[0]
+ })
+
+ replacedArr := replaced.MustBoolSlice()
+ if assert.Equal(t, 6, len(replacedArr)) {
+ assert.Equal(t, replacedArr[0], rawArr[1])
+ assert.Equal(t, replacedArr[1], rawArr[2])
+ assert.Equal(t, replacedArr[2], rawArr[3])
+ assert.Equal(t, replacedArr[3], rawArr[4])
+ assert.Equal(t, replacedArr[4], rawArr[5])
+ assert.Equal(t, replacedArr[5], rawArr[0])
+ }
+
+}
+
+func TestCollectBool(t *testing.T) {
+
+ v := &Value{data: []bool{bool(true), bool(true), bool(true), bool(true), bool(true), bool(true)}}
+
+ collected := v.CollectBool(func(index int, val bool) interface{} {
+ return index
+ })
+
+ collectedArr := collected.MustInterSlice()
+ if assert.Equal(t, 6, len(collectedArr)) {
+ assert.Equal(t, collectedArr[0], 0)
+ assert.Equal(t, collectedArr[1], 1)
+ assert.Equal(t, collectedArr[2], 2)
+ assert.Equal(t, collectedArr[3], 3)
+ assert.Equal(t, collectedArr[4], 4)
+ assert.Equal(t, collectedArr[5], 5)
+ }
+
+}
+
+// ************************************************************
+// TESTS
+// ************************************************************
+
+func TestStr(t *testing.T) {
+
+ val := string("hello")
+ m := map[string]interface{}{"value": val, "nothing": nil}
+ assert.Equal(t, val, New(m).Get("value").Str())
+ assert.Equal(t, val, New(m).Get("value").MustStr())
+ assert.Equal(t, string(""), New(m).Get("nothing").Str())
+ assert.Equal(t, val, New(m).Get("nothing").Str("hello"))
+
+ assert.Panics(t, func() {
+ New(m).Get("age").MustStr()
+ })
+
+}
+
+func TestStrSlice(t *testing.T) {
+
+ val := string("hello")
+ m := map[string]interface{}{"value": []string{val}, "nothing": nil}
+ assert.Equal(t, val, New(m).Get("value").StrSlice()[0])
+ assert.Equal(t, val, New(m).Get("value").MustStrSlice()[0])
+ assert.Equal(t, []string(nil), New(m).Get("nothing").StrSlice())
+ assert.Equal(t, val, New(m).Get("nothing").StrSlice([]string{string("hello")})[0])
+
+ assert.Panics(t, func() {
+ New(m).Get("nothing").MustStrSlice()
+ })
+
+}
+
+func TestIsStr(t *testing.T) {
+
+ var v *Value
+
+ v = &Value{data: string("hello")}
+ assert.True(t, v.IsStr())
+
+ v = &Value{data: []string{string("hello")}}
+ assert.True(t, v.IsStrSlice())
+
+}
+
+func TestEachStr(t *testing.T) {
+
+ v := &Value{data: []string{string("hello"), string("hello"), string("hello"), string("hello"), string("hello")}}
+ count := 0
+ replacedVals := make([]string, 0)
+ assert.Equal(t, v, v.EachStr(func(i int, val string) bool {
+
+ count++
+ replacedVals = append(replacedVals, val)
+
+ // abort early
+ if i == 2 {
+ return false
+ }
+
+ return true
+
+ }))
+
+ assert.Equal(t, count, 3)
+ assert.Equal(t, replacedVals[0], v.MustStrSlice()[0])
+ assert.Equal(t, replacedVals[1], v.MustStrSlice()[1])
+ assert.Equal(t, replacedVals[2], v.MustStrSlice()[2])
+
+}
+
+func TestWhereStr(t *testing.T) {
+
+ v := &Value{data: []string{string("hello"), string("hello"), string("hello"), string("hello"), string("hello"), string("hello")}}
+
+ selected := v.WhereStr(func(i int, val string) bool {
+ return i%2 == 0
+ }).MustStrSlice()
+
+ assert.Equal(t, 3, len(selected))
+
+}
+
+func TestGroupStr(t *testing.T) {
+
+ v := &Value{data: []string{string("hello"), string("hello"), string("hello"), string("hello"), string("hello"), string("hello")}}
+
+ grouped := v.GroupStr(func(i int, val string) string {
+ return fmt.Sprintf("%v", i%2 == 0)
+ }).data.(map[string][]string)
+
+ assert.Equal(t, 2, len(grouped))
+ assert.Equal(t, 3, len(grouped["true"]))
+ assert.Equal(t, 3, len(grouped["false"]))
+
+}
+
+func TestReplaceStr(t *testing.T) {
+
+ v := &Value{data: []string{string("hello"), string("hello"), string("hello"), string("hello"), string("hello"), string("hello")}}
+
+ rawArr := v.MustStrSlice()
+
+ replaced := v.ReplaceStr(func(index int, val string) string {
+ if index < len(rawArr)-1 {
+ return rawArr[index+1]
+ }
+ return rawArr[0]
+ })
+
+ replacedArr := replaced.MustStrSlice()
+ if assert.Equal(t, 6, len(replacedArr)) {
+ assert.Equal(t, replacedArr[0], rawArr[1])
+ assert.Equal(t, replacedArr[1], rawArr[2])
+ assert.Equal(t, replacedArr[2], rawArr[3])
+ assert.Equal(t, replacedArr[3], rawArr[4])
+ assert.Equal(t, replacedArr[4], rawArr[5])
+ assert.Equal(t, replacedArr[5], rawArr[0])
+ }
+
+}
+
+func TestCollectStr(t *testing.T) {
+
+ v := &Value{data: []string{string("hello"), string("hello"), string("hello"), string("hello"), string("hello"), string("hello")}}
+
+ collected := v.CollectStr(func(index int, val string) interface{} {
+ return index
+ })
+
+ collectedArr := collected.MustInterSlice()
+ if assert.Equal(t, 6, len(collectedArr)) {
+ assert.Equal(t, collectedArr[0], 0)
+ assert.Equal(t, collectedArr[1], 1)
+ assert.Equal(t, collectedArr[2], 2)
+ assert.Equal(t, collectedArr[3], 3)
+ assert.Equal(t, collectedArr[4], 4)
+ assert.Equal(t, collectedArr[5], 5)
+ }
+
+}
+
+// ************************************************************
+// TESTS
+// ************************************************************
+
+func TestInt(t *testing.T) {
+
+ val := int(1)
+ m := map[string]interface{}{"value": val, "nothing": nil}
+ assert.Equal(t, val, New(m).Get("value").Int())
+ assert.Equal(t, val, New(m).Get("value").MustInt())
+ assert.Equal(t, int(0), New(m).Get("nothing").Int())
+ assert.Equal(t, val, New(m).Get("nothing").Int(1))
+
+ assert.Panics(t, func() {
+ New(m).Get("age").MustInt()
+ })
+
+}
+
+func TestIntSlice(t *testing.T) {
+
+ val := int(1)
+ m := map[string]interface{}{"value": []int{val}, "nothing": nil}
+ assert.Equal(t, val, New(m).Get("value").IntSlice()[0])
+ assert.Equal(t, val, New(m).Get("value").MustIntSlice()[0])
+ assert.Equal(t, []int(nil), New(m).Get("nothing").IntSlice())
+ assert.Equal(t, val, New(m).Get("nothing").IntSlice([]int{int(1)})[0])
+
+ assert.Panics(t, func() {
+ New(m).Get("nothing").MustIntSlice()
+ })
+
+}
+
+func TestIsInt(t *testing.T) {
+
+ var v *Value
+
+ v = &Value{data: int(1)}
+ assert.True(t, v.IsInt())
+
+ v = &Value{data: []int{int(1)}}
+ assert.True(t, v.IsIntSlice())
+
+}
+
+func TestEachInt(t *testing.T) {
+
+ v := &Value{data: []int{int(1), int(1), int(1), int(1), int(1)}}
+ count := 0
+ replacedVals := make([]int, 0)
+ assert.Equal(t, v, v.EachInt(func(i int, val int) bool {
+
+ count++
+ replacedVals = append(replacedVals, val)
+
+ // abort early
+ if i == 2 {
+ return false
+ }
+
+ return true
+
+ }))
+
+ assert.Equal(t, count, 3)
+ assert.Equal(t, replacedVals[0], v.MustIntSlice()[0])
+ assert.Equal(t, replacedVals[1], v.MustIntSlice()[1])
+ assert.Equal(t, replacedVals[2], v.MustIntSlice()[2])
+
+}
+
+func TestWhereInt(t *testing.T) {
+
+ v := &Value{data: []int{int(1), int(1), int(1), int(1), int(1), int(1)}}
+
+ selected := v.WhereInt(func(i int, val int) bool {
+ return i%2 == 0
+ }).MustIntSlice()
+
+ assert.Equal(t, 3, len(selected))
+
+}
+
+func TestGroupInt(t *testing.T) {
+
+ v := &Value{data: []int{int(1), int(1), int(1), int(1), int(1), int(1)}}
+
+ grouped := v.GroupInt(func(i int, val int) string {
+ return fmt.Sprintf("%v", i%2 == 0)
+ }).data.(map[string][]int)
+
+ assert.Equal(t, 2, len(grouped))
+ assert.Equal(t, 3, len(grouped["true"]))
+ assert.Equal(t, 3, len(grouped["false"]))
+
+}
+
+func TestReplaceInt(t *testing.T) {
+
+ v := &Value{data: []int{int(1), int(1), int(1), int(1), int(1), int(1)}}
+
+ rawArr := v.MustIntSlice()
+
+ replaced := v.ReplaceInt(func(index int, val int) int {
+ if index < len(rawArr)-1 {
+ return rawArr[index+1]
+ }
+ return rawArr[0]
+ })
+
+ replacedArr := replaced.MustIntSlice()
+ if assert.Equal(t, 6, len(replacedArr)) {
+ assert.Equal(t, replacedArr[0], rawArr[1])
+ assert.Equal(t, replacedArr[1], rawArr[2])
+ assert.Equal(t, replacedArr[2], rawArr[3])
+ assert.Equal(t, replacedArr[3], rawArr[4])
+ assert.Equal(t, replacedArr[4], rawArr[5])
+ assert.Equal(t, replacedArr[5], rawArr[0])
+ }
+
+}
+
+func TestCollectInt(t *testing.T) {
+
+ v := &Value{data: []int{int(1), int(1), int(1), int(1), int(1), int(1)}}
+
+ collected := v.CollectInt(func(index int, val int) interface{} {
+ return index
+ })
+
+ collectedArr := collected.MustInterSlice()
+ if assert.Equal(t, 6, len(collectedArr)) {
+ assert.Equal(t, collectedArr[0], 0)
+ assert.Equal(t, collectedArr[1], 1)
+ assert.Equal(t, collectedArr[2], 2)
+ assert.Equal(t, collectedArr[3], 3)
+ assert.Equal(t, collectedArr[4], 4)
+ assert.Equal(t, collectedArr[5], 5)
+ }
+
+}
+
+// ************************************************************
+// TESTS
+// ************************************************************
+
+func TestInt8(t *testing.T) {
+
+ val := int8(1)
+ m := map[string]interface{}{"value": val, "nothing": nil}
+ assert.Equal(t, val, New(m).Get("value").Int8())
+ assert.Equal(t, val, New(m).Get("value").MustInt8())
+ assert.Equal(t, int8(0), New(m).Get("nothing").Int8())
+ assert.Equal(t, val, New(m).Get("nothing").Int8(1))
+
+ assert.Panics(t, func() {
+ New(m).Get("age").MustInt8()
+ })
+
+}
+
+func TestInt8Slice(t *testing.T) {
+
+ val := int8(1)
+ m := map[string]interface{}{"value": []int8{val}, "nothing": nil}
+ assert.Equal(t, val, New(m).Get("value").Int8Slice()[0])
+ assert.Equal(t, val, New(m).Get("value").MustInt8Slice()[0])
+ assert.Equal(t, []int8(nil), New(m).Get("nothing").Int8Slice())
+ assert.Equal(t, val, New(m).Get("nothing").Int8Slice([]int8{int8(1)})[0])
+
+ assert.Panics(t, func() {
+ New(m).Get("nothing").MustInt8Slice()
+ })
+
+}
+
+func TestIsInt8(t *testing.T) {
+
+ var v *Value
+
+ v = &Value{data: int8(1)}
+ assert.True(t, v.IsInt8())
+
+ v = &Value{data: []int8{int8(1)}}
+ assert.True(t, v.IsInt8Slice())
+
+}
+
+func TestEachInt8(t *testing.T) {
+
+ v := &Value{data: []int8{int8(1), int8(1), int8(1), int8(1), int8(1)}}
+ count := 0
+ replacedVals := make([]int8, 0)
+ assert.Equal(t, v, v.EachInt8(func(i int, val int8) bool {
+
+ count++
+ replacedVals = append(replacedVals, val)
+
+ // abort early
+ if i == 2 {
+ return false
+ }
+
+ return true
+
+ }))
+
+ assert.Equal(t, count, 3)
+ assert.Equal(t, replacedVals[0], v.MustInt8Slice()[0])
+ assert.Equal(t, replacedVals[1], v.MustInt8Slice()[1])
+ assert.Equal(t, replacedVals[2], v.MustInt8Slice()[2])
+
+}
+
+func TestWhereInt8(t *testing.T) {
+
+ v := &Value{data: []int8{int8(1), int8(1), int8(1), int8(1), int8(1), int8(1)}}
+
+ selected := v.WhereInt8(func(i int, val int8) bool {
+ return i%2 == 0
+ }).MustInt8Slice()
+
+ assert.Equal(t, 3, len(selected))
+
+}
+
+func TestGroupInt8(t *testing.T) {
+
+ v := &Value{data: []int8{int8(1), int8(1), int8(1), int8(1), int8(1), int8(1)}}
+
+ grouped := v.GroupInt8(func(i int, val int8) string {
+ return fmt.Sprintf("%v", i%2 == 0)
+ }).data.(map[string][]int8)
+
+ assert.Equal(t, 2, len(grouped))
+ assert.Equal(t, 3, len(grouped["true"]))
+ assert.Equal(t, 3, len(grouped["false"]))
+
+}
+
+func TestReplaceInt8(t *testing.T) {
+
+ v := &Value{data: []int8{int8(1), int8(1), int8(1), int8(1), int8(1), int8(1)}}
+
+ rawArr := v.MustInt8Slice()
+
+ replaced := v.ReplaceInt8(func(index int, val int8) int8 {
+ if index < len(rawArr)-1 {
+ return rawArr[index+1]
+ }
+ return rawArr[0]
+ })
+
+ replacedArr := replaced.MustInt8Slice()
+ if assert.Equal(t, 6, len(replacedArr)) {
+ assert.Equal(t, replacedArr[0], rawArr[1])
+ assert.Equal(t, replacedArr[1], rawArr[2])
+ assert.Equal(t, replacedArr[2], rawArr[3])
+ assert.Equal(t, replacedArr[3], rawArr[4])
+ assert.Equal(t, replacedArr[4], rawArr[5])
+ assert.Equal(t, replacedArr[5], rawArr[0])
+ }
+
+}
+
+func TestCollectInt8(t *testing.T) {
+
+ v := &Value{data: []int8{int8(1), int8(1), int8(1), int8(1), int8(1), int8(1)}}
+
+ collected := v.CollectInt8(func(index int, val int8) interface{} {
+ return index
+ })
+
+ collectedArr := collected.MustInterSlice()
+ if assert.Equal(t, 6, len(collectedArr)) {
+ assert.Equal(t, collectedArr[0], 0)
+ assert.Equal(t, collectedArr[1], 1)
+ assert.Equal(t, collectedArr[2], 2)
+ assert.Equal(t, collectedArr[3], 3)
+ assert.Equal(t, collectedArr[4], 4)
+ assert.Equal(t, collectedArr[5], 5)
+ }
+
+}
+
+// ************************************************************
+// TESTS
+// ************************************************************
+
+func TestInt16(t *testing.T) {
+
+ val := int16(1)
+ m := map[string]interface{}{"value": val, "nothing": nil}
+ assert.Equal(t, val, New(m).Get("value").Int16())
+ assert.Equal(t, val, New(m).Get("value").MustInt16())
+ assert.Equal(t, int16(0), New(m).Get("nothing").Int16())
+ assert.Equal(t, val, New(m).Get("nothing").Int16(1))
+
+ assert.Panics(t, func() {
+ New(m).Get("age").MustInt16()
+ })
+
+}
+
+func TestInt16Slice(t *testing.T) {
+
+ val := int16(1)
+ m := map[string]interface{}{"value": []int16{val}, "nothing": nil}
+ assert.Equal(t, val, New(m).Get("value").Int16Slice()[0])
+ assert.Equal(t, val, New(m).Get("value").MustInt16Slice()[0])
+ assert.Equal(t, []int16(nil), New(m).Get("nothing").Int16Slice())
+ assert.Equal(t, val, New(m).Get("nothing").Int16Slice([]int16{int16(1)})[0])
+
+ assert.Panics(t, func() {
+ New(m).Get("nothing").MustInt16Slice()
+ })
+
+}
+
+func TestIsInt16(t *testing.T) {
+
+ var v *Value
+
+ v = &Value{data: int16(1)}
+ assert.True(t, v.IsInt16())
+
+ v = &Value{data: []int16{int16(1)}}
+ assert.True(t, v.IsInt16Slice())
+
+}
+
+func TestEachInt16(t *testing.T) {
+
+ v := &Value{data: []int16{int16(1), int16(1), int16(1), int16(1), int16(1)}}
+ count := 0
+ replacedVals := make([]int16, 0)
+ assert.Equal(t, v, v.EachInt16(func(i int, val int16) bool {
+
+ count++
+ replacedVals = append(replacedVals, val)
+
+ // abort early
+ if i == 2 {
+ return false
+ }
+
+ return true
+
+ }))
+
+ assert.Equal(t, count, 3)
+ assert.Equal(t, replacedVals[0], v.MustInt16Slice()[0])
+ assert.Equal(t, replacedVals[1], v.MustInt16Slice()[1])
+ assert.Equal(t, replacedVals[2], v.MustInt16Slice()[2])
+
+}
+
+func TestWhereInt16(t *testing.T) {
+
+ v := &Value{data: []int16{int16(1), int16(1), int16(1), int16(1), int16(1), int16(1)}}
+
+ selected := v.WhereInt16(func(i int, val int16) bool {
+ return i%2 == 0
+ }).MustInt16Slice()
+
+ assert.Equal(t, 3, len(selected))
+
+}
+
+func TestGroupInt16(t *testing.T) {
+
+ v := &Value{data: []int16{int16(1), int16(1), int16(1), int16(1), int16(1), int16(1)}}
+
+ grouped := v.GroupInt16(func(i int, val int16) string {
+ return fmt.Sprintf("%v", i%2 == 0)
+ }).data.(map[string][]int16)
+
+ assert.Equal(t, 2, len(grouped))
+ assert.Equal(t, 3, len(grouped["true"]))
+ assert.Equal(t, 3, len(grouped["false"]))
+
+}
+
+func TestReplaceInt16(t *testing.T) {
+
+ v := &Value{data: []int16{int16(1), int16(1), int16(1), int16(1), int16(1), int16(1)}}
+
+ rawArr := v.MustInt16Slice()
+
+ replaced := v.ReplaceInt16(func(index int, val int16) int16 {
+ if index < len(rawArr)-1 {
+ return rawArr[index+1]
+ }
+ return rawArr[0]
+ })
+
+ replacedArr := replaced.MustInt16Slice()
+ if assert.Equal(t, 6, len(replacedArr)) {
+ assert.Equal(t, replacedArr[0], rawArr[1])
+ assert.Equal(t, replacedArr[1], rawArr[2])
+ assert.Equal(t, replacedArr[2], rawArr[3])
+ assert.Equal(t, replacedArr[3], rawArr[4])
+ assert.Equal(t, replacedArr[4], rawArr[5])
+ assert.Equal(t, replacedArr[5], rawArr[0])
+ }
+
+}
+
+func TestCollectInt16(t *testing.T) {
+
+ v := &Value{data: []int16{int16(1), int16(1), int16(1), int16(1), int16(1), int16(1)}}
+
+ collected := v.CollectInt16(func(index int, val int16) interface{} {
+ return index
+ })
+
+ collectedArr := collected.MustInterSlice()
+ if assert.Equal(t, 6, len(collectedArr)) {
+ assert.Equal(t, collectedArr[0], 0)
+ assert.Equal(t, collectedArr[1], 1)
+ assert.Equal(t, collectedArr[2], 2)
+ assert.Equal(t, collectedArr[3], 3)
+ assert.Equal(t, collectedArr[4], 4)
+ assert.Equal(t, collectedArr[5], 5)
+ }
+
+}
+
+// ************************************************************
+// TESTS
+// ************************************************************
+
+func TestInt32(t *testing.T) {
+
+ val := int32(1)
+ m := map[string]interface{}{"value": val, "nothing": nil}
+ assert.Equal(t, val, New(m).Get("value").Int32())
+ assert.Equal(t, val, New(m).Get("value").MustInt32())
+ assert.Equal(t, int32(0), New(m).Get("nothing").Int32())
+ assert.Equal(t, val, New(m).Get("nothing").Int32(1))
+
+ assert.Panics(t, func() {
+ New(m).Get("age").MustInt32()
+ })
+
+}
+
+func TestInt32Slice(t *testing.T) {
+
+ val := int32(1)
+ m := map[string]interface{}{"value": []int32{val}, "nothing": nil}
+ assert.Equal(t, val, New(m).Get("value").Int32Slice()[0])
+ assert.Equal(t, val, New(m).Get("value").MustInt32Slice()[0])
+ assert.Equal(t, []int32(nil), New(m).Get("nothing").Int32Slice())
+ assert.Equal(t, val, New(m).Get("nothing").Int32Slice([]int32{int32(1)})[0])
+
+ assert.Panics(t, func() {
+ New(m).Get("nothing").MustInt32Slice()
+ })
+
+}
+
+func TestIsInt32(t *testing.T) {
+
+ var v *Value
+
+ v = &Value{data: int32(1)}
+ assert.True(t, v.IsInt32())
+
+ v = &Value{data: []int32{int32(1)}}
+ assert.True(t, v.IsInt32Slice())
+
+}
+
+func TestEachInt32(t *testing.T) {
+
+ v := &Value{data: []int32{int32(1), int32(1), int32(1), int32(1), int32(1)}}
+ count := 0
+ replacedVals := make([]int32, 0)
+ assert.Equal(t, v, v.EachInt32(func(i int, val int32) bool {
+
+ count++
+ replacedVals = append(replacedVals, val)
+
+ // abort early
+ if i == 2 {
+ return false
+ }
+
+ return true
+
+ }))
+
+ assert.Equal(t, count, 3)
+ assert.Equal(t, replacedVals[0], v.MustInt32Slice()[0])
+ assert.Equal(t, replacedVals[1], v.MustInt32Slice()[1])
+ assert.Equal(t, replacedVals[2], v.MustInt32Slice()[2])
+
+}
+
+func TestWhereInt32(t *testing.T) {
+
+ v := &Value{data: []int32{int32(1), int32(1), int32(1), int32(1), int32(1), int32(1)}}
+
+ selected := v.WhereInt32(func(i int, val int32) bool {
+ return i%2 == 0
+ }).MustInt32Slice()
+
+ assert.Equal(t, 3, len(selected))
+
+}
+
+func TestGroupInt32(t *testing.T) {
+
+ v := &Value{data: []int32{int32(1), int32(1), int32(1), int32(1), int32(1), int32(1)}}
+
+ grouped := v.GroupInt32(func(i int, val int32) string {
+ return fmt.Sprintf("%v", i%2 == 0)
+ }).data.(map[string][]int32)
+
+ assert.Equal(t, 2, len(grouped))
+ assert.Equal(t, 3, len(grouped["true"]))
+ assert.Equal(t, 3, len(grouped["false"]))
+
+}
+
+func TestReplaceInt32(t *testing.T) {
+
+ v := &Value{data: []int32{int32(1), int32(1), int32(1), int32(1), int32(1), int32(1)}}
+
+ rawArr := v.MustInt32Slice()
+
+ replaced := v.ReplaceInt32(func(index int, val int32) int32 {
+ if index < len(rawArr)-1 {
+ return rawArr[index+1]
+ }
+ return rawArr[0]
+ })
+
+ replacedArr := replaced.MustInt32Slice()
+ if assert.Equal(t, 6, len(replacedArr)) {
+ assert.Equal(t, replacedArr[0], rawArr[1])
+ assert.Equal(t, replacedArr[1], rawArr[2])
+ assert.Equal(t, replacedArr[2], rawArr[3])
+ assert.Equal(t, replacedArr[3], rawArr[4])
+ assert.Equal(t, replacedArr[4], rawArr[5])
+ assert.Equal(t, replacedArr[5], rawArr[0])
+ }
+
+}
+
+func TestCollectInt32(t *testing.T) {
+
+ v := &Value{data: []int32{int32(1), int32(1), int32(1), int32(1), int32(1), int32(1)}}
+
+ collected := v.CollectInt32(func(index int, val int32) interface{} {
+ return index
+ })
+
+ collectedArr := collected.MustInterSlice()
+ if assert.Equal(t, 6, len(collectedArr)) {
+ assert.Equal(t, collectedArr[0], 0)
+ assert.Equal(t, collectedArr[1], 1)
+ assert.Equal(t, collectedArr[2], 2)
+ assert.Equal(t, collectedArr[3], 3)
+ assert.Equal(t, collectedArr[4], 4)
+ assert.Equal(t, collectedArr[5], 5)
+ }
+
+}
+
+// ************************************************************
+// TESTS
+// ************************************************************
+
+func TestInt64(t *testing.T) {
+
+ val := int64(1)
+ m := map[string]interface{}{"value": val, "nothing": nil}
+ assert.Equal(t, val, New(m).Get("value").Int64())
+ assert.Equal(t, val, New(m).Get("value").MustInt64())
+ assert.Equal(t, int64(0), New(m).Get("nothing").Int64())
+ assert.Equal(t, val, New(m).Get("nothing").Int64(1))
+
+ assert.Panics(t, func() {
+ New(m).Get("age").MustInt64()
+ })
+
+}
+
+func TestInt64Slice(t *testing.T) {
+
+ val := int64(1)
+ m := map[string]interface{}{"value": []int64{val}, "nothing": nil}
+ assert.Equal(t, val, New(m).Get("value").Int64Slice()[0])
+ assert.Equal(t, val, New(m).Get("value").MustInt64Slice()[0])
+ assert.Equal(t, []int64(nil), New(m).Get("nothing").Int64Slice())
+ assert.Equal(t, val, New(m).Get("nothing").Int64Slice([]int64{int64(1)})[0])
+
+ assert.Panics(t, func() {
+ New(m).Get("nothing").MustInt64Slice()
+ })
+
+}
+
+func TestIsInt64(t *testing.T) {
+
+ var v *Value
+
+ v = &Value{data: int64(1)}
+ assert.True(t, v.IsInt64())
+
+ v = &Value{data: []int64{int64(1)}}
+ assert.True(t, v.IsInt64Slice())
+
+}
+
+func TestEachInt64(t *testing.T) {
+
+ v := &Value{data: []int64{int64(1), int64(1), int64(1), int64(1), int64(1)}}
+ count := 0
+ replacedVals := make([]int64, 0)
+ assert.Equal(t, v, v.EachInt64(func(i int, val int64) bool {
+
+ count++
+ replacedVals = append(replacedVals, val)
+
+ // abort early
+ if i == 2 {
+ return false
+ }
+
+ return true
+
+ }))
+
+ assert.Equal(t, count, 3)
+ assert.Equal(t, replacedVals[0], v.MustInt64Slice()[0])
+ assert.Equal(t, replacedVals[1], v.MustInt64Slice()[1])
+ assert.Equal(t, replacedVals[2], v.MustInt64Slice()[2])
+
+}
+
+func TestWhereInt64(t *testing.T) {
+
+ v := &Value{data: []int64{int64(1), int64(1), int64(1), int64(1), int64(1), int64(1)}}
+
+ selected := v.WhereInt64(func(i int, val int64) bool {
+ return i%2 == 0
+ }).MustInt64Slice()
+
+ assert.Equal(t, 3, len(selected))
+
+}
+
+func TestGroupInt64(t *testing.T) {
+
+ v := &Value{data: []int64{int64(1), int64(1), int64(1), int64(1), int64(1), int64(1)}}
+
+ grouped := v.GroupInt64(func(i int, val int64) string {
+ return fmt.Sprintf("%v", i%2 == 0)
+ }).data.(map[string][]int64)
+
+ assert.Equal(t, 2, len(grouped))
+ assert.Equal(t, 3, len(grouped["true"]))
+ assert.Equal(t, 3, len(grouped["false"]))
+
+}
+
+func TestReplaceInt64(t *testing.T) {
+
+ v := &Value{data: []int64{int64(1), int64(1), int64(1), int64(1), int64(1), int64(1)}}
+
+ rawArr := v.MustInt64Slice()
+
+ replaced := v.ReplaceInt64(func(index int, val int64) int64 {
+ if index < len(rawArr)-1 {
+ return rawArr[index+1]
+ }
+ return rawArr[0]
+ })
+
+ replacedArr := replaced.MustInt64Slice()
+ if assert.Equal(t, 6, len(replacedArr)) {
+ assert.Equal(t, replacedArr[0], rawArr[1])
+ assert.Equal(t, replacedArr[1], rawArr[2])
+ assert.Equal(t, replacedArr[2], rawArr[3])
+ assert.Equal(t, replacedArr[3], rawArr[4])
+ assert.Equal(t, replacedArr[4], rawArr[5])
+ assert.Equal(t, replacedArr[5], rawArr[0])
+ }
+
+}
+
+func TestCollectInt64(t *testing.T) {
+
+ v := &Value{data: []int64{int64(1), int64(1), int64(1), int64(1), int64(1), int64(1)}}
+
+ collected := v.CollectInt64(func(index int, val int64) interface{} {
+ return index
+ })
+
+ collectedArr := collected.MustInterSlice()
+ if assert.Equal(t, 6, len(collectedArr)) {
+ assert.Equal(t, collectedArr[0], 0)
+ assert.Equal(t, collectedArr[1], 1)
+ assert.Equal(t, collectedArr[2], 2)
+ assert.Equal(t, collectedArr[3], 3)
+ assert.Equal(t, collectedArr[4], 4)
+ assert.Equal(t, collectedArr[5], 5)
+ }
+
+}
+
+// ************************************************************
+// TESTS
+// ************************************************************
+
+func TestUint(t *testing.T) {
+
+ val := uint(1)
+ m := map[string]interface{}{"value": val, "nothing": nil}
+ assert.Equal(t, val, New(m).Get("value").Uint())
+ assert.Equal(t, val, New(m).Get("value").MustUint())
+ assert.Equal(t, uint(0), New(m).Get("nothing").Uint())
+ assert.Equal(t, val, New(m).Get("nothing").Uint(1))
+
+ assert.Panics(t, func() {
+ New(m).Get("age").MustUint()
+ })
+
+}
+
+func TestUintSlice(t *testing.T) {
+
+ val := uint(1)
+ m := map[string]interface{}{"value": []uint{val}, "nothing": nil}
+ assert.Equal(t, val, New(m).Get("value").UintSlice()[0])
+ assert.Equal(t, val, New(m).Get("value").MustUintSlice()[0])
+ assert.Equal(t, []uint(nil), New(m).Get("nothing").UintSlice())
+ assert.Equal(t, val, New(m).Get("nothing").UintSlice([]uint{uint(1)})[0])
+
+ assert.Panics(t, func() {
+ New(m).Get("nothing").MustUintSlice()
+ })
+
+}
+
+func TestIsUint(t *testing.T) {
+
+ var v *Value
+
+ v = &Value{data: uint(1)}
+ assert.True(t, v.IsUint())
+
+ v = &Value{data: []uint{uint(1)}}
+ assert.True(t, v.IsUintSlice())
+
+}
+
+func TestEachUint(t *testing.T) {
+
+ v := &Value{data: []uint{uint(1), uint(1), uint(1), uint(1), uint(1)}}
+ count := 0
+ replacedVals := make([]uint, 0)
+ assert.Equal(t, v, v.EachUint(func(i int, val uint) bool {
+
+ count++
+ replacedVals = append(replacedVals, val)
+
+ // abort early
+ if i == 2 {
+ return false
+ }
+
+ return true
+
+ }))
+
+ assert.Equal(t, count, 3)
+ assert.Equal(t, replacedVals[0], v.MustUintSlice()[0])
+ assert.Equal(t, replacedVals[1], v.MustUintSlice()[1])
+ assert.Equal(t, replacedVals[2], v.MustUintSlice()[2])
+
+}
+
+func TestWhereUint(t *testing.T) {
+
+ v := &Value{data: []uint{uint(1), uint(1), uint(1), uint(1), uint(1), uint(1)}}
+
+ selected := v.WhereUint(func(i int, val uint) bool {
+ return i%2 == 0
+ }).MustUintSlice()
+
+ assert.Equal(t, 3, len(selected))
+
+}
+
+func TestGroupUint(t *testing.T) {
+
+ v := &Value{data: []uint{uint(1), uint(1), uint(1), uint(1), uint(1), uint(1)}}
+
+ grouped := v.GroupUint(func(i int, val uint) string {
+ return fmt.Sprintf("%v", i%2 == 0)
+ }).data.(map[string][]uint)
+
+ assert.Equal(t, 2, len(grouped))
+ assert.Equal(t, 3, len(grouped["true"]))
+ assert.Equal(t, 3, len(grouped["false"]))
+
+}
+
+func TestReplaceUint(t *testing.T) {
+
+ v := &Value{data: []uint{uint(1), uint(1), uint(1), uint(1), uint(1), uint(1)}}
+
+ rawArr := v.MustUintSlice()
+
+ replaced := v.ReplaceUint(func(index int, val uint) uint {
+ if index < len(rawArr)-1 {
+ return rawArr[index+1]
+ }
+ return rawArr[0]
+ })
+
+ replacedArr := replaced.MustUintSlice()
+ if assert.Equal(t, 6, len(replacedArr)) {
+ assert.Equal(t, replacedArr[0], rawArr[1])
+ assert.Equal(t, replacedArr[1], rawArr[2])
+ assert.Equal(t, replacedArr[2], rawArr[3])
+ assert.Equal(t, replacedArr[3], rawArr[4])
+ assert.Equal(t, replacedArr[4], rawArr[5])
+ assert.Equal(t, replacedArr[5], rawArr[0])
+ }
+
+}
+
+func TestCollectUint(t *testing.T) {
+
+ v := &Value{data: []uint{uint(1), uint(1), uint(1), uint(1), uint(1), uint(1)}}
+
+ collected := v.CollectUint(func(index int, val uint) interface{} {
+ return index
+ })
+
+ collectedArr := collected.MustInterSlice()
+ if assert.Equal(t, 6, len(collectedArr)) {
+ assert.Equal(t, collectedArr[0], 0)
+ assert.Equal(t, collectedArr[1], 1)
+ assert.Equal(t, collectedArr[2], 2)
+ assert.Equal(t, collectedArr[3], 3)
+ assert.Equal(t, collectedArr[4], 4)
+ assert.Equal(t, collectedArr[5], 5)
+ }
+
+}
+
+// ************************************************************
+// TESTS
+// ************************************************************
+
+func TestUint8(t *testing.T) {
+
+ val := uint8(1)
+ m := map[string]interface{}{"value": val, "nothing": nil}
+ assert.Equal(t, val, New(m).Get("value").Uint8())
+ assert.Equal(t, val, New(m).Get("value").MustUint8())
+ assert.Equal(t, uint8(0), New(m).Get("nothing").Uint8())
+ assert.Equal(t, val, New(m).Get("nothing").Uint8(1))
+
+ assert.Panics(t, func() {
+ New(m).Get("age").MustUint8()
+ })
+
+}
+
+func TestUint8Slice(t *testing.T) {
+
+ val := uint8(1)
+ m := map[string]interface{}{"value": []uint8{val}, "nothing": nil}
+ assert.Equal(t, val, New(m).Get("value").Uint8Slice()[0])
+ assert.Equal(t, val, New(m).Get("value").MustUint8Slice()[0])
+ assert.Equal(t, []uint8(nil), New(m).Get("nothing").Uint8Slice())
+ assert.Equal(t, val, New(m).Get("nothing").Uint8Slice([]uint8{uint8(1)})[0])
+
+ assert.Panics(t, func() {
+ New(m).Get("nothing").MustUint8Slice()
+ })
+
+}
+
+func TestIsUint8(t *testing.T) {
+
+ var v *Value
+
+ v = &Value{data: uint8(1)}
+ assert.True(t, v.IsUint8())
+
+ v = &Value{data: []uint8{uint8(1)}}
+ assert.True(t, v.IsUint8Slice())
+
+}
+
+func TestEachUint8(t *testing.T) {
+
+ v := &Value{data: []uint8{uint8(1), uint8(1), uint8(1), uint8(1), uint8(1)}}
+ count := 0
+ replacedVals := make([]uint8, 0)
+ assert.Equal(t, v, v.EachUint8(func(i int, val uint8) bool {
+
+ count++
+ replacedVals = append(replacedVals, val)
+
+ // abort early
+ if i == 2 {
+ return false
+ }
+
+ return true
+
+ }))
+
+ assert.Equal(t, count, 3)
+ assert.Equal(t, replacedVals[0], v.MustUint8Slice()[0])
+ assert.Equal(t, replacedVals[1], v.MustUint8Slice()[1])
+ assert.Equal(t, replacedVals[2], v.MustUint8Slice()[2])
+
+}
+
+func TestWhereUint8(t *testing.T) {
+
+ v := &Value{data: []uint8{uint8(1), uint8(1), uint8(1), uint8(1), uint8(1), uint8(1)}}
+
+ selected := v.WhereUint8(func(i int, val uint8) bool {
+ return i%2 == 0
+ }).MustUint8Slice()
+
+ assert.Equal(t, 3, len(selected))
+
+}
+
+func TestGroupUint8(t *testing.T) {
+
+ v := &Value{data: []uint8{uint8(1), uint8(1), uint8(1), uint8(1), uint8(1), uint8(1)}}
+
+ grouped := v.GroupUint8(func(i int, val uint8) string {
+ return fmt.Sprintf("%v", i%2 == 0)
+ }).data.(map[string][]uint8)
+
+ assert.Equal(t, 2, len(grouped))
+ assert.Equal(t, 3, len(grouped["true"]))
+ assert.Equal(t, 3, len(grouped["false"]))
+
+}
+
+func TestReplaceUint8(t *testing.T) {
+
+ v := &Value{data: []uint8{uint8(1), uint8(1), uint8(1), uint8(1), uint8(1), uint8(1)}}
+
+ rawArr := v.MustUint8Slice()
+
+ replaced := v.ReplaceUint8(func(index int, val uint8) uint8 {
+ if index < len(rawArr)-1 {
+ return rawArr[index+1]
+ }
+ return rawArr[0]
+ })
+
+ replacedArr := replaced.MustUint8Slice()
+ if assert.Equal(t, 6, len(replacedArr)) {
+ assert.Equal(t, replacedArr[0], rawArr[1])
+ assert.Equal(t, replacedArr[1], rawArr[2])
+ assert.Equal(t, replacedArr[2], rawArr[3])
+ assert.Equal(t, replacedArr[3], rawArr[4])
+ assert.Equal(t, replacedArr[4], rawArr[5])
+ assert.Equal(t, replacedArr[5], rawArr[0])
+ }
+
+}
+
+func TestCollectUint8(t *testing.T) {
+
+ v := &Value{data: []uint8{uint8(1), uint8(1), uint8(1), uint8(1), uint8(1), uint8(1)}}
+
+ collected := v.CollectUint8(func(index int, val uint8) interface{} {
+ return index
+ })
+
+ collectedArr := collected.MustInterSlice()
+ if assert.Equal(t, 6, len(collectedArr)) {
+ assert.Equal(t, collectedArr[0], 0)
+ assert.Equal(t, collectedArr[1], 1)
+ assert.Equal(t, collectedArr[2], 2)
+ assert.Equal(t, collectedArr[3], 3)
+ assert.Equal(t, collectedArr[4], 4)
+ assert.Equal(t, collectedArr[5], 5)
+ }
+
+}
+
+// ************************************************************
+// TESTS
+// ************************************************************
+
+func TestUint16(t *testing.T) {
+
+ val := uint16(1)
+ m := map[string]interface{}{"value": val, "nothing": nil}
+ assert.Equal(t, val, New(m).Get("value").Uint16())
+ assert.Equal(t, val, New(m).Get("value").MustUint16())
+ assert.Equal(t, uint16(0), New(m).Get("nothing").Uint16())
+ assert.Equal(t, val, New(m).Get("nothing").Uint16(1))
+
+ assert.Panics(t, func() {
+ New(m).Get("age").MustUint16()
+ })
+
+}
+
+func TestUint16Slice(t *testing.T) {
+
+ val := uint16(1)
+ m := map[string]interface{}{"value": []uint16{val}, "nothing": nil}
+ assert.Equal(t, val, New(m).Get("value").Uint16Slice()[0])
+ assert.Equal(t, val, New(m).Get("value").MustUint16Slice()[0])
+ assert.Equal(t, []uint16(nil), New(m).Get("nothing").Uint16Slice())
+ assert.Equal(t, val, New(m).Get("nothing").Uint16Slice([]uint16{uint16(1)})[0])
+
+ assert.Panics(t, func() {
+ New(m).Get("nothing").MustUint16Slice()
+ })
+
+}
+
+func TestIsUint16(t *testing.T) {
+
+ var v *Value
+
+ v = &Value{data: uint16(1)}
+ assert.True(t, v.IsUint16())
+
+ v = &Value{data: []uint16{uint16(1)}}
+ assert.True(t, v.IsUint16Slice())
+
+}
+
+func TestEachUint16(t *testing.T) {
+
+ v := &Value{data: []uint16{uint16(1), uint16(1), uint16(1), uint16(1), uint16(1)}}
+ count := 0
+ replacedVals := make([]uint16, 0)
+ assert.Equal(t, v, v.EachUint16(func(i int, val uint16) bool {
+
+ count++
+ replacedVals = append(replacedVals, val)
+
+ // abort early
+ if i == 2 {
+ return false
+ }
+
+ return true
+
+ }))
+
+ assert.Equal(t, count, 3)
+ assert.Equal(t, replacedVals[0], v.MustUint16Slice()[0])
+ assert.Equal(t, replacedVals[1], v.MustUint16Slice()[1])
+ assert.Equal(t, replacedVals[2], v.MustUint16Slice()[2])
+
+}
+
+func TestWhereUint16(t *testing.T) {
+
+ v := &Value{data: []uint16{uint16(1), uint16(1), uint16(1), uint16(1), uint16(1), uint16(1)}}
+
+ selected := v.WhereUint16(func(i int, val uint16) bool {
+ return i%2 == 0
+ }).MustUint16Slice()
+
+ assert.Equal(t, 3, len(selected))
+
+}
+
+func TestGroupUint16(t *testing.T) {
+
+ v := &Value{data: []uint16{uint16(1), uint16(1), uint16(1), uint16(1), uint16(1), uint16(1)}}
+
+ grouped := v.GroupUint16(func(i int, val uint16) string {
+ return fmt.Sprintf("%v", i%2 == 0)
+ }).data.(map[string][]uint16)
+
+ assert.Equal(t, 2, len(grouped))
+ assert.Equal(t, 3, len(grouped["true"]))
+ assert.Equal(t, 3, len(grouped["false"]))
+
+}
+
+func TestReplaceUint16(t *testing.T) {
+
+ v := &Value{data: []uint16{uint16(1), uint16(1), uint16(1), uint16(1), uint16(1), uint16(1)}}
+
+ rawArr := v.MustUint16Slice()
+
+ replaced := v.ReplaceUint16(func(index int, val uint16) uint16 {
+ if index < len(rawArr)-1 {
+ return rawArr[index+1]
+ }
+ return rawArr[0]
+ })
+
+ replacedArr := replaced.MustUint16Slice()
+ if assert.Equal(t, 6, len(replacedArr)) {
+ assert.Equal(t, replacedArr[0], rawArr[1])
+ assert.Equal(t, replacedArr[1], rawArr[2])
+ assert.Equal(t, replacedArr[2], rawArr[3])
+ assert.Equal(t, replacedArr[3], rawArr[4])
+ assert.Equal(t, replacedArr[4], rawArr[5])
+ assert.Equal(t, replacedArr[5], rawArr[0])
+ }
+
+}
+
+func TestCollectUint16(t *testing.T) {
+
+ v := &Value{data: []uint16{uint16(1), uint16(1), uint16(1), uint16(1), uint16(1), uint16(1)}}
+
+ collected := v.CollectUint16(func(index int, val uint16) interface{} {
+ return index
+ })
+
+ collectedArr := collected.MustInterSlice()
+ if assert.Equal(t, 6, len(collectedArr)) {
+ assert.Equal(t, collectedArr[0], 0)
+ assert.Equal(t, collectedArr[1], 1)
+ assert.Equal(t, collectedArr[2], 2)
+ assert.Equal(t, collectedArr[3], 3)
+ assert.Equal(t, collectedArr[4], 4)
+ assert.Equal(t, collectedArr[5], 5)
+ }
+
+}
+
+// ************************************************************
+// TESTS
+// ************************************************************
+
+func TestUint32(t *testing.T) {
+
+ val := uint32(1)
+ m := map[string]interface{}{"value": val, "nothing": nil}
+ assert.Equal(t, val, New(m).Get("value").Uint32())
+ assert.Equal(t, val, New(m).Get("value").MustUint32())
+ assert.Equal(t, uint32(0), New(m).Get("nothing").Uint32())
+ assert.Equal(t, val, New(m).Get("nothing").Uint32(1))
+
+ assert.Panics(t, func() {
+ New(m).Get("age").MustUint32()
+ })
+
+}
+
+func TestUint32Slice(t *testing.T) {
+
+ val := uint32(1)
+ m := map[string]interface{}{"value": []uint32{val}, "nothing": nil}
+ assert.Equal(t, val, New(m).Get("value").Uint32Slice()[0])
+ assert.Equal(t, val, New(m).Get("value").MustUint32Slice()[0])
+ assert.Equal(t, []uint32(nil), New(m).Get("nothing").Uint32Slice())
+ assert.Equal(t, val, New(m).Get("nothing").Uint32Slice([]uint32{uint32(1)})[0])
+
+ assert.Panics(t, func() {
+ New(m).Get("nothing").MustUint32Slice()
+ })
+
+}
+
+func TestIsUint32(t *testing.T) {
+
+ var v *Value
+
+ v = &Value{data: uint32(1)}
+ assert.True(t, v.IsUint32())
+
+ v = &Value{data: []uint32{uint32(1)}}
+ assert.True(t, v.IsUint32Slice())
+
+}
+
+func TestEachUint32(t *testing.T) {
+
+ v := &Value{data: []uint32{uint32(1), uint32(1), uint32(1), uint32(1), uint32(1)}}
+ count := 0
+ replacedVals := make([]uint32, 0)
+ assert.Equal(t, v, v.EachUint32(func(i int, val uint32) bool {
+
+ count++
+ replacedVals = append(replacedVals, val)
+
+ // abort early
+ if i == 2 {
+ return false
+ }
+
+ return true
+
+ }))
+
+ assert.Equal(t, count, 3)
+ assert.Equal(t, replacedVals[0], v.MustUint32Slice()[0])
+ assert.Equal(t, replacedVals[1], v.MustUint32Slice()[1])
+ assert.Equal(t, replacedVals[2], v.MustUint32Slice()[2])
+
+}
+
+func TestWhereUint32(t *testing.T) {
+
+ v := &Value{data: []uint32{uint32(1), uint32(1), uint32(1), uint32(1), uint32(1), uint32(1)}}
+
+ selected := v.WhereUint32(func(i int, val uint32) bool {
+ return i%2 == 0
+ }).MustUint32Slice()
+
+ assert.Equal(t, 3, len(selected))
+
+}
+
+func TestGroupUint32(t *testing.T) {
+
+ v := &Value{data: []uint32{uint32(1), uint32(1), uint32(1), uint32(1), uint32(1), uint32(1)}}
+
+ grouped := v.GroupUint32(func(i int, val uint32) string {
+ return fmt.Sprintf("%v", i%2 == 0)
+ }).data.(map[string][]uint32)
+
+ assert.Equal(t, 2, len(grouped))
+ assert.Equal(t, 3, len(grouped["true"]))
+ assert.Equal(t, 3, len(grouped["false"]))
+
+}
+
+func TestReplaceUint32(t *testing.T) {
+
+ v := &Value{data: []uint32{uint32(1), uint32(1), uint32(1), uint32(1), uint32(1), uint32(1)}}
+
+ rawArr := v.MustUint32Slice()
+
+ replaced := v.ReplaceUint32(func(index int, val uint32) uint32 {
+ if index < len(rawArr)-1 {
+ return rawArr[index+1]
+ }
+ return rawArr[0]
+ })
+
+ replacedArr := replaced.MustUint32Slice()
+ if assert.Equal(t, 6, len(replacedArr)) {
+ assert.Equal(t, replacedArr[0], rawArr[1])
+ assert.Equal(t, replacedArr[1], rawArr[2])
+ assert.Equal(t, replacedArr[2], rawArr[3])
+ assert.Equal(t, replacedArr[3], rawArr[4])
+ assert.Equal(t, replacedArr[4], rawArr[5])
+ assert.Equal(t, replacedArr[5], rawArr[0])
+ }
+
+}
+
+func TestCollectUint32(t *testing.T) {
+
+ v := &Value{data: []uint32{uint32(1), uint32(1), uint32(1), uint32(1), uint32(1), uint32(1)}}
+
+ collected := v.CollectUint32(func(index int, val uint32) interface{} {
+ return index
+ })
+
+ collectedArr := collected.MustInterSlice()
+ if assert.Equal(t, 6, len(collectedArr)) {
+ assert.Equal(t, collectedArr[0], 0)
+ assert.Equal(t, collectedArr[1], 1)
+ assert.Equal(t, collectedArr[2], 2)
+ assert.Equal(t, collectedArr[3], 3)
+ assert.Equal(t, collectedArr[4], 4)
+ assert.Equal(t, collectedArr[5], 5)
+ }
+
+}
+
+// ************************************************************
+// TESTS
+// ************************************************************
+
+func TestUint64(t *testing.T) {
+
+ val := uint64(1)
+ m := map[string]interface{}{"value": val, "nothing": nil}
+ assert.Equal(t, val, New(m).Get("value").Uint64())
+ assert.Equal(t, val, New(m).Get("value").MustUint64())
+ assert.Equal(t, uint64(0), New(m).Get("nothing").Uint64())
+ assert.Equal(t, val, New(m).Get("nothing").Uint64(1))
+
+ assert.Panics(t, func() {
+ New(m).Get("age").MustUint64()
+ })
+
+}
+
+func TestUint64Slice(t *testing.T) {
+
+ val := uint64(1)
+ m := map[string]interface{}{"value": []uint64{val}, "nothing": nil}
+ assert.Equal(t, val, New(m).Get("value").Uint64Slice()[0])
+ assert.Equal(t, val, New(m).Get("value").MustUint64Slice()[0])
+ assert.Equal(t, []uint64(nil), New(m).Get("nothing").Uint64Slice())
+ assert.Equal(t, val, New(m).Get("nothing").Uint64Slice([]uint64{uint64(1)})[0])
+
+ assert.Panics(t, func() {
+ New(m).Get("nothing").MustUint64Slice()
+ })
+
+}
+
+func TestIsUint64(t *testing.T) {
+
+ var v *Value
+
+ v = &Value{data: uint64(1)}
+ assert.True(t, v.IsUint64())
+
+ v = &Value{data: []uint64{uint64(1)}}
+ assert.True(t, v.IsUint64Slice())
+
+}
+
+func TestEachUint64(t *testing.T) {
+
+ v := &Value{data: []uint64{uint64(1), uint64(1), uint64(1), uint64(1), uint64(1)}}
+ count := 0
+ replacedVals := make([]uint64, 0)
+ assert.Equal(t, v, v.EachUint64(func(i int, val uint64) bool {
+
+ count++
+ replacedVals = append(replacedVals, val)
+
+ // abort early
+ if i == 2 {
+ return false
+ }
+
+ return true
+
+ }))
+
+ assert.Equal(t, count, 3)
+ assert.Equal(t, replacedVals[0], v.MustUint64Slice()[0])
+ assert.Equal(t, replacedVals[1], v.MustUint64Slice()[1])
+ assert.Equal(t, replacedVals[2], v.MustUint64Slice()[2])
+
+}
+
+func TestWhereUint64(t *testing.T) {
+
+ v := &Value{data: []uint64{uint64(1), uint64(1), uint64(1), uint64(1), uint64(1), uint64(1)}}
+
+ selected := v.WhereUint64(func(i int, val uint64) bool {
+ return i%2 == 0
+ }).MustUint64Slice()
+
+ assert.Equal(t, 3, len(selected))
+
+}
+
+func TestGroupUint64(t *testing.T) {
+
+ v := &Value{data: []uint64{uint64(1), uint64(1), uint64(1), uint64(1), uint64(1), uint64(1)}}
+
+ grouped := v.GroupUint64(func(i int, val uint64) string {
+ return fmt.Sprintf("%v", i%2 == 0)
+ }).data.(map[string][]uint64)
+
+ assert.Equal(t, 2, len(grouped))
+ assert.Equal(t, 3, len(grouped["true"]))
+ assert.Equal(t, 3, len(grouped["false"]))
+
+}
+
+func TestReplaceUint64(t *testing.T) {
+
+ v := &Value{data: []uint64{uint64(1), uint64(1), uint64(1), uint64(1), uint64(1), uint64(1)}}
+
+ rawArr := v.MustUint64Slice()
+
+ replaced := v.ReplaceUint64(func(index int, val uint64) uint64 {
+ if index < len(rawArr)-1 {
+ return rawArr[index+1]
+ }
+ return rawArr[0]
+ })
+
+ replacedArr := replaced.MustUint64Slice()
+ if assert.Equal(t, 6, len(replacedArr)) {
+ assert.Equal(t, replacedArr[0], rawArr[1])
+ assert.Equal(t, replacedArr[1], rawArr[2])
+ assert.Equal(t, replacedArr[2], rawArr[3])
+ assert.Equal(t, replacedArr[3], rawArr[4])
+ assert.Equal(t, replacedArr[4], rawArr[5])
+ assert.Equal(t, replacedArr[5], rawArr[0])
+ }
+
+}
+
+func TestCollectUint64(t *testing.T) {
+
+ v := &Value{data: []uint64{uint64(1), uint64(1), uint64(1), uint64(1), uint64(1), uint64(1)}}
+
+ collected := v.CollectUint64(func(index int, val uint64) interface{} {
+ return index
+ })
+
+ collectedArr := collected.MustInterSlice()
+ if assert.Equal(t, 6, len(collectedArr)) {
+ assert.Equal(t, collectedArr[0], 0)
+ assert.Equal(t, collectedArr[1], 1)
+ assert.Equal(t, collectedArr[2], 2)
+ assert.Equal(t, collectedArr[3], 3)
+ assert.Equal(t, collectedArr[4], 4)
+ assert.Equal(t, collectedArr[5], 5)
+ }
+
+}
+
+// ************************************************************
+// TESTS
+// ************************************************************
+
+func TestUintptr(t *testing.T) {
+
+ val := uintptr(1)
+ m := map[string]interface{}{"value": val, "nothing": nil}
+ assert.Equal(t, val, New(m).Get("value").Uintptr())
+ assert.Equal(t, val, New(m).Get("value").MustUintptr())
+ assert.Equal(t, uintptr(0), New(m).Get("nothing").Uintptr())
+ assert.Equal(t, val, New(m).Get("nothing").Uintptr(1))
+
+ assert.Panics(t, func() {
+ New(m).Get("age").MustUintptr()
+ })
+
+}
+
+func TestUintptrSlice(t *testing.T) {
+
+ val := uintptr(1)
+ m := map[string]interface{}{"value": []uintptr{val}, "nothing": nil}
+ assert.Equal(t, val, New(m).Get("value").UintptrSlice()[0])
+ assert.Equal(t, val, New(m).Get("value").MustUintptrSlice()[0])
+ assert.Equal(t, []uintptr(nil), New(m).Get("nothing").UintptrSlice())
+ assert.Equal(t, val, New(m).Get("nothing").UintptrSlice([]uintptr{uintptr(1)})[0])
+
+ assert.Panics(t, func() {
+ New(m).Get("nothing").MustUintptrSlice()
+ })
+
+}
+
+func TestIsUintptr(t *testing.T) {
+
+ var v *Value
+
+ v = &Value{data: uintptr(1)}
+ assert.True(t, v.IsUintptr())
+
+ v = &Value{data: []uintptr{uintptr(1)}}
+ assert.True(t, v.IsUintptrSlice())
+
+}
+
+func TestEachUintptr(t *testing.T) {
+
+ v := &Value{data: []uintptr{uintptr(1), uintptr(1), uintptr(1), uintptr(1), uintptr(1)}}
+ count := 0
+ replacedVals := make([]uintptr, 0)
+ assert.Equal(t, v, v.EachUintptr(func(i int, val uintptr) bool {
+
+ count++
+ replacedVals = append(replacedVals, val)
+
+ // abort early
+ if i == 2 {
+ return false
+ }
+
+ return true
+
+ }))
+
+ assert.Equal(t, count, 3)
+ assert.Equal(t, replacedVals[0], v.MustUintptrSlice()[0])
+ assert.Equal(t, replacedVals[1], v.MustUintptrSlice()[1])
+ assert.Equal(t, replacedVals[2], v.MustUintptrSlice()[2])
+
+}
+
+func TestWhereUintptr(t *testing.T) {
+
+ v := &Value{data: []uintptr{uintptr(1), uintptr(1), uintptr(1), uintptr(1), uintptr(1), uintptr(1)}}
+
+ selected := v.WhereUintptr(func(i int, val uintptr) bool {
+ return i%2 == 0
+ }).MustUintptrSlice()
+
+ assert.Equal(t, 3, len(selected))
+
+}
+
+func TestGroupUintptr(t *testing.T) {
+
+ v := &Value{data: []uintptr{uintptr(1), uintptr(1), uintptr(1), uintptr(1), uintptr(1), uintptr(1)}}
+
+ grouped := v.GroupUintptr(func(i int, val uintptr) string {
+ return fmt.Sprintf("%v", i%2 == 0)
+ }).data.(map[string][]uintptr)
+
+ assert.Equal(t, 2, len(grouped))
+ assert.Equal(t, 3, len(grouped["true"]))
+ assert.Equal(t, 3, len(grouped["false"]))
+
+}
+
+func TestReplaceUintptr(t *testing.T) {
+
+ v := &Value{data: []uintptr{uintptr(1), uintptr(1), uintptr(1), uintptr(1), uintptr(1), uintptr(1)}}
+
+ rawArr := v.MustUintptrSlice()
+
+ replaced := v.ReplaceUintptr(func(index int, val uintptr) uintptr {
+ if index < len(rawArr)-1 {
+ return rawArr[index+1]
+ }
+ return rawArr[0]
+ })
+
+ replacedArr := replaced.MustUintptrSlice()
+ if assert.Equal(t, 6, len(replacedArr)) {
+ assert.Equal(t, replacedArr[0], rawArr[1])
+ assert.Equal(t, replacedArr[1], rawArr[2])
+ assert.Equal(t, replacedArr[2], rawArr[3])
+ assert.Equal(t, replacedArr[3], rawArr[4])
+ assert.Equal(t, replacedArr[4], rawArr[5])
+ assert.Equal(t, replacedArr[5], rawArr[0])
+ }
+
+}
+
+func TestCollectUintptr(t *testing.T) {
+
+ v := &Value{data: []uintptr{uintptr(1), uintptr(1), uintptr(1), uintptr(1), uintptr(1), uintptr(1)}}
+
+ collected := v.CollectUintptr(func(index int, val uintptr) interface{} {
+ return index
+ })
+
+ collectedArr := collected.MustInterSlice()
+ if assert.Equal(t, 6, len(collectedArr)) {
+ assert.Equal(t, collectedArr[0], 0)
+ assert.Equal(t, collectedArr[1], 1)
+ assert.Equal(t, collectedArr[2], 2)
+ assert.Equal(t, collectedArr[3], 3)
+ assert.Equal(t, collectedArr[4], 4)
+ assert.Equal(t, collectedArr[5], 5)
+ }
+
+}
+
+// ************************************************************
+// TESTS
+// ************************************************************
+
+func TestFloat32(t *testing.T) {
+
+ val := float32(1)
+ m := map[string]interface{}{"value": val, "nothing": nil}
+ assert.Equal(t, val, New(m).Get("value").Float32())
+ assert.Equal(t, val, New(m).Get("value").MustFloat32())
+ assert.Equal(t, float32(0), New(m).Get("nothing").Float32())
+ assert.Equal(t, val, New(m).Get("nothing").Float32(1))
+
+ assert.Panics(t, func() {
+ New(m).Get("age").MustFloat32()
+ })
+
+}
+
+func TestFloat32Slice(t *testing.T) {
+
+ val := float32(1)
+ m := map[string]interface{}{"value": []float32{val}, "nothing": nil}
+ assert.Equal(t, val, New(m).Get("value").Float32Slice()[0])
+ assert.Equal(t, val, New(m).Get("value").MustFloat32Slice()[0])
+ assert.Equal(t, []float32(nil), New(m).Get("nothing").Float32Slice())
+ assert.Equal(t, val, New(m).Get("nothing").Float32Slice([]float32{float32(1)})[0])
+
+ assert.Panics(t, func() {
+ New(m).Get("nothing").MustFloat32Slice()
+ })
+
+}
+
+func TestIsFloat32(t *testing.T) {
+
+ var v *Value
+
+ v = &Value{data: float32(1)}
+ assert.True(t, v.IsFloat32())
+
+ v = &Value{data: []float32{float32(1)}}
+ assert.True(t, v.IsFloat32Slice())
+
+}
+
+func TestEachFloat32(t *testing.T) {
+
+ v := &Value{data: []float32{float32(1), float32(1), float32(1), float32(1), float32(1)}}
+ count := 0
+ replacedVals := make([]float32, 0)
+ assert.Equal(t, v, v.EachFloat32(func(i int, val float32) bool {
+
+ count++
+ replacedVals = append(replacedVals, val)
+
+ // abort early
+ if i == 2 {
+ return false
+ }
+
+ return true
+
+ }))
+
+ assert.Equal(t, count, 3)
+ assert.Equal(t, replacedVals[0], v.MustFloat32Slice()[0])
+ assert.Equal(t, replacedVals[1], v.MustFloat32Slice()[1])
+ assert.Equal(t, replacedVals[2], v.MustFloat32Slice()[2])
+
+}
+
+func TestWhereFloat32(t *testing.T) {
+
+ v := &Value{data: []float32{float32(1), float32(1), float32(1), float32(1), float32(1), float32(1)}}
+
+ selected := v.WhereFloat32(func(i int, val float32) bool {
+ return i%2 == 0
+ }).MustFloat32Slice()
+
+ assert.Equal(t, 3, len(selected))
+
+}
+
+func TestGroupFloat32(t *testing.T) {
+
+ v := &Value{data: []float32{float32(1), float32(1), float32(1), float32(1), float32(1), float32(1)}}
+
+ grouped := v.GroupFloat32(func(i int, val float32) string {
+ return fmt.Sprintf("%v", i%2 == 0)
+ }).data.(map[string][]float32)
+
+ assert.Equal(t, 2, len(grouped))
+ assert.Equal(t, 3, len(grouped["true"]))
+ assert.Equal(t, 3, len(grouped["false"]))
+
+}
+
+func TestReplaceFloat32(t *testing.T) {
+
+ v := &Value{data: []float32{float32(1), float32(1), float32(1), float32(1), float32(1), float32(1)}}
+
+ rawArr := v.MustFloat32Slice()
+
+ replaced := v.ReplaceFloat32(func(index int, val float32) float32 {
+ if index < len(rawArr)-1 {
+ return rawArr[index+1]
+ }
+ return rawArr[0]
+ })
+
+ replacedArr := replaced.MustFloat32Slice()
+ if assert.Equal(t, 6, len(replacedArr)) {
+ assert.Equal(t, replacedArr[0], rawArr[1])
+ assert.Equal(t, replacedArr[1], rawArr[2])
+ assert.Equal(t, replacedArr[2], rawArr[3])
+ assert.Equal(t, replacedArr[3], rawArr[4])
+ assert.Equal(t, replacedArr[4], rawArr[5])
+ assert.Equal(t, replacedArr[5], rawArr[0])
+ }
+
+}
+
+func TestCollectFloat32(t *testing.T) {
+
+ v := &Value{data: []float32{float32(1), float32(1), float32(1), float32(1), float32(1), float32(1)}}
+
+ collected := v.CollectFloat32(func(index int, val float32) interface{} {
+ return index
+ })
+
+ collectedArr := collected.MustInterSlice()
+ if assert.Equal(t, 6, len(collectedArr)) {
+ assert.Equal(t, collectedArr[0], 0)
+ assert.Equal(t, collectedArr[1], 1)
+ assert.Equal(t, collectedArr[2], 2)
+ assert.Equal(t, collectedArr[3], 3)
+ assert.Equal(t, collectedArr[4], 4)
+ assert.Equal(t, collectedArr[5], 5)
+ }
+
+}
+
+// ************************************************************
+// TESTS
+// ************************************************************
+
+func TestFloat64(t *testing.T) {
+
+ val := float64(1)
+ m := map[string]interface{}{"value": val, "nothing": nil}
+ assert.Equal(t, val, New(m).Get("value").Float64())
+ assert.Equal(t, val, New(m).Get("value").MustFloat64())
+ assert.Equal(t, float64(0), New(m).Get("nothing").Float64())
+ assert.Equal(t, val, New(m).Get("nothing").Float64(1))
+
+ assert.Panics(t, func() {
+ New(m).Get("age").MustFloat64()
+ })
+
+}
+
+func TestFloat64Slice(t *testing.T) {
+
+ val := float64(1)
+ m := map[string]interface{}{"value": []float64{val}, "nothing": nil}
+ assert.Equal(t, val, New(m).Get("value").Float64Slice()[0])
+ assert.Equal(t, val, New(m).Get("value").MustFloat64Slice()[0])
+ assert.Equal(t, []float64(nil), New(m).Get("nothing").Float64Slice())
+ assert.Equal(t, val, New(m).Get("nothing").Float64Slice([]float64{float64(1)})[0])
+
+ assert.Panics(t, func() {
+ New(m).Get("nothing").MustFloat64Slice()
+ })
+
+}
+
+func TestIsFloat64(t *testing.T) {
+
+ var v *Value
+
+ v = &Value{data: float64(1)}
+ assert.True(t, v.IsFloat64())
+
+ v = &Value{data: []float64{float64(1)}}
+ assert.True(t, v.IsFloat64Slice())
+
+}
+
+func TestEachFloat64(t *testing.T) {
+
+ v := &Value{data: []float64{float64(1), float64(1), float64(1), float64(1), float64(1)}}
+ count := 0
+ replacedVals := make([]float64, 0)
+ assert.Equal(t, v, v.EachFloat64(func(i int, val float64) bool {
+
+ count++
+ replacedVals = append(replacedVals, val)
+
+ // abort early
+ if i == 2 {
+ return false
+ }
+
+ return true
+
+ }))
+
+ assert.Equal(t, count, 3)
+ assert.Equal(t, replacedVals[0], v.MustFloat64Slice()[0])
+ assert.Equal(t, replacedVals[1], v.MustFloat64Slice()[1])
+ assert.Equal(t, replacedVals[2], v.MustFloat64Slice()[2])
+
+}
+
+func TestWhereFloat64(t *testing.T) {
+
+ v := &Value{data: []float64{float64(1), float64(1), float64(1), float64(1), float64(1), float64(1)}}
+
+ selected := v.WhereFloat64(func(i int, val float64) bool {
+ return i%2 == 0
+ }).MustFloat64Slice()
+
+ assert.Equal(t, 3, len(selected))
+
+}
+
+func TestGroupFloat64(t *testing.T) {
+
+ v := &Value{data: []float64{float64(1), float64(1), float64(1), float64(1), float64(1), float64(1)}}
+
+ grouped := v.GroupFloat64(func(i int, val float64) string {
+ return fmt.Sprintf("%v", i%2 == 0)
+ }).data.(map[string][]float64)
+
+ assert.Equal(t, 2, len(grouped))
+ assert.Equal(t, 3, len(grouped["true"]))
+ assert.Equal(t, 3, len(grouped["false"]))
+
+}
+
+func TestReplaceFloat64(t *testing.T) {
+
+ v := &Value{data: []float64{float64(1), float64(1), float64(1), float64(1), float64(1), float64(1)}}
+
+ rawArr := v.MustFloat64Slice()
+
+ replaced := v.ReplaceFloat64(func(index int, val float64) float64 {
+ if index < len(rawArr)-1 {
+ return rawArr[index+1]
+ }
+ return rawArr[0]
+ })
+
+ replacedArr := replaced.MustFloat64Slice()
+ if assert.Equal(t, 6, len(replacedArr)) {
+ assert.Equal(t, replacedArr[0], rawArr[1])
+ assert.Equal(t, replacedArr[1], rawArr[2])
+ assert.Equal(t, replacedArr[2], rawArr[3])
+ assert.Equal(t, replacedArr[3], rawArr[4])
+ assert.Equal(t, replacedArr[4], rawArr[5])
+ assert.Equal(t, replacedArr[5], rawArr[0])
+ }
+
+}
+
+func TestCollectFloat64(t *testing.T) {
+
+ v := &Value{data: []float64{float64(1), float64(1), float64(1), float64(1), float64(1), float64(1)}}
+
+ collected := v.CollectFloat64(func(index int, val float64) interface{} {
+ return index
+ })
+
+ collectedArr := collected.MustInterSlice()
+ if assert.Equal(t, 6, len(collectedArr)) {
+ assert.Equal(t, collectedArr[0], 0)
+ assert.Equal(t, collectedArr[1], 1)
+ assert.Equal(t, collectedArr[2], 2)
+ assert.Equal(t, collectedArr[3], 3)
+ assert.Equal(t, collectedArr[4], 4)
+ assert.Equal(t, collectedArr[5], 5)
+ }
+
+}
+
+// ************************************************************
+// TESTS
+// ************************************************************
+
+func TestComplex64(t *testing.T) {
+
+ val := complex64(1)
+ m := map[string]interface{}{"value": val, "nothing": nil}
+ assert.Equal(t, val, New(m).Get("value").Complex64())
+ assert.Equal(t, val, New(m).Get("value").MustComplex64())
+ assert.Equal(t, complex64(0), New(m).Get("nothing").Complex64())
+ assert.Equal(t, val, New(m).Get("nothing").Complex64(1))
+
+ assert.Panics(t, func() {
+ New(m).Get("age").MustComplex64()
+ })
+
+}
+
+func TestComplex64Slice(t *testing.T) {
+
+ val := complex64(1)
+ m := map[string]interface{}{"value": []complex64{val}, "nothing": nil}
+ assert.Equal(t, val, New(m).Get("value").Complex64Slice()[0])
+ assert.Equal(t, val, New(m).Get("value").MustComplex64Slice()[0])
+ assert.Equal(t, []complex64(nil), New(m).Get("nothing").Complex64Slice())
+ assert.Equal(t, val, New(m).Get("nothing").Complex64Slice([]complex64{complex64(1)})[0])
+
+ assert.Panics(t, func() {
+ New(m).Get("nothing").MustComplex64Slice()
+ })
+
+}
+
+func TestIsComplex64(t *testing.T) {
+
+ var v *Value
+
+ v = &Value{data: complex64(1)}
+ assert.True(t, v.IsComplex64())
+
+ v = &Value{data: []complex64{complex64(1)}}
+ assert.True(t, v.IsComplex64Slice())
+
+}
+
+func TestEachComplex64(t *testing.T) {
+
+ v := &Value{data: []complex64{complex64(1), complex64(1), complex64(1), complex64(1), complex64(1)}}
+ count := 0
+ replacedVals := make([]complex64, 0)
+ assert.Equal(t, v, v.EachComplex64(func(i int, val complex64) bool {
+
+ count++
+ replacedVals = append(replacedVals, val)
+
+ // abort early
+ if i == 2 {
+ return false
+ }
+
+ return true
+
+ }))
+
+ assert.Equal(t, count, 3)
+ assert.Equal(t, replacedVals[0], v.MustComplex64Slice()[0])
+ assert.Equal(t, replacedVals[1], v.MustComplex64Slice()[1])
+ assert.Equal(t, replacedVals[2], v.MustComplex64Slice()[2])
+
+}
+
+func TestWhereComplex64(t *testing.T) {
+
+ v := &Value{data: []complex64{complex64(1), complex64(1), complex64(1), complex64(1), complex64(1), complex64(1)}}
+
+ selected := v.WhereComplex64(func(i int, val complex64) bool {
+ return i%2 == 0
+ }).MustComplex64Slice()
+
+ assert.Equal(t, 3, len(selected))
+
+}
+
+func TestGroupComplex64(t *testing.T) {
+
+ v := &Value{data: []complex64{complex64(1), complex64(1), complex64(1), complex64(1), complex64(1), complex64(1)}}
+
+ grouped := v.GroupComplex64(func(i int, val complex64) string {
+ return fmt.Sprintf("%v", i%2 == 0)
+ }).data.(map[string][]complex64)
+
+ assert.Equal(t, 2, len(grouped))
+ assert.Equal(t, 3, len(grouped["true"]))
+ assert.Equal(t, 3, len(grouped["false"]))
+
+}
+
+func TestReplaceComplex64(t *testing.T) {
+
+ v := &Value{data: []complex64{complex64(1), complex64(1), complex64(1), complex64(1), complex64(1), complex64(1)}}
+
+ rawArr := v.MustComplex64Slice()
+
+ replaced := v.ReplaceComplex64(func(index int, val complex64) complex64 {
+ if index < len(rawArr)-1 {
+ return rawArr[index+1]
+ }
+ return rawArr[0]
+ })
+
+ replacedArr := replaced.MustComplex64Slice()
+ if assert.Equal(t, 6, len(replacedArr)) {
+ assert.Equal(t, replacedArr[0], rawArr[1])
+ assert.Equal(t, replacedArr[1], rawArr[2])
+ assert.Equal(t, replacedArr[2], rawArr[3])
+ assert.Equal(t, replacedArr[3], rawArr[4])
+ assert.Equal(t, replacedArr[4], rawArr[5])
+ assert.Equal(t, replacedArr[5], rawArr[0])
+ }
+
+}
+
+func TestCollectComplex64(t *testing.T) {
+
+ v := &Value{data: []complex64{complex64(1), complex64(1), complex64(1), complex64(1), complex64(1), complex64(1)}}
+
+ collected := v.CollectComplex64(func(index int, val complex64) interface{} {
+ return index
+ })
+
+ collectedArr := collected.MustInterSlice()
+ if assert.Equal(t, 6, len(collectedArr)) {
+ assert.Equal(t, collectedArr[0], 0)
+ assert.Equal(t, collectedArr[1], 1)
+ assert.Equal(t, collectedArr[2], 2)
+ assert.Equal(t, collectedArr[3], 3)
+ assert.Equal(t, collectedArr[4], 4)
+ assert.Equal(t, collectedArr[5], 5)
+ }
+
+}
+
+// ************************************************************
+// TESTS
+// ************************************************************
+
+func TestComplex128(t *testing.T) {
+
+ val := complex128(1)
+ m := map[string]interface{}{"value": val, "nothing": nil}
+ assert.Equal(t, val, New(m).Get("value").Complex128())
+ assert.Equal(t, val, New(m).Get("value").MustComplex128())
+ assert.Equal(t, complex128(0), New(m).Get("nothing").Complex128())
+ assert.Equal(t, val, New(m).Get("nothing").Complex128(1))
+
+ assert.Panics(t, func() {
+ New(m).Get("age").MustComplex128()
+ })
+
+}
+
+func TestComplex128Slice(t *testing.T) {
+
+ val := complex128(1)
+ m := map[string]interface{}{"value": []complex128{val}, "nothing": nil}
+ assert.Equal(t, val, New(m).Get("value").Complex128Slice()[0])
+ assert.Equal(t, val, New(m).Get("value").MustComplex128Slice()[0])
+ assert.Equal(t, []complex128(nil), New(m).Get("nothing").Complex128Slice())
+ assert.Equal(t, val, New(m).Get("nothing").Complex128Slice([]complex128{complex128(1)})[0])
+
+ assert.Panics(t, func() {
+ New(m).Get("nothing").MustComplex128Slice()
+ })
+
+}
+
+func TestIsComplex128(t *testing.T) {
+
+ var v *Value
+
+ v = &Value{data: complex128(1)}
+ assert.True(t, v.IsComplex128())
+
+ v = &Value{data: []complex128{complex128(1)}}
+ assert.True(t, v.IsComplex128Slice())
+
+}
+
+func TestEachComplex128(t *testing.T) {
+
+ v := &Value{data: []complex128{complex128(1), complex128(1), complex128(1), complex128(1), complex128(1)}}
+ count := 0
+ replacedVals := make([]complex128, 0)
+ assert.Equal(t, v, v.EachComplex128(func(i int, val complex128) bool {
+
+ count++
+ replacedVals = append(replacedVals, val)
+
+ // abort early
+ if i == 2 {
+ return false
+ }
+
+ return true
+
+ }))
+
+ assert.Equal(t, count, 3)
+ assert.Equal(t, replacedVals[0], v.MustComplex128Slice()[0])
+ assert.Equal(t, replacedVals[1], v.MustComplex128Slice()[1])
+ assert.Equal(t, replacedVals[2], v.MustComplex128Slice()[2])
+
+}
+
+func TestWhereComplex128(t *testing.T) {
+
+ v := &Value{data: []complex128{complex128(1), complex128(1), complex128(1), complex128(1), complex128(1), complex128(1)}}
+
+ selected := v.WhereComplex128(func(i int, val complex128) bool {
+ return i%2 == 0
+ }).MustComplex128Slice()
+
+ assert.Equal(t, 3, len(selected))
+
+}
+
+func TestGroupComplex128(t *testing.T) {
+
+ v := &Value{data: []complex128{complex128(1), complex128(1), complex128(1), complex128(1), complex128(1), complex128(1)}}
+
+ grouped := v.GroupComplex128(func(i int, val complex128) string {
+ return fmt.Sprintf("%v", i%2 == 0)
+ }).data.(map[string][]complex128)
+
+ assert.Equal(t, 2, len(grouped))
+ assert.Equal(t, 3, len(grouped["true"]))
+ assert.Equal(t, 3, len(grouped["false"]))
+
+}
+
+func TestReplaceComplex128(t *testing.T) {
+
+ v := &Value{data: []complex128{complex128(1), complex128(1), complex128(1), complex128(1), complex128(1), complex128(1)}}
+
+ rawArr := v.MustComplex128Slice()
+
+ replaced := v.ReplaceComplex128(func(index int, val complex128) complex128 {
+ if index < len(rawArr)-1 {
+ return rawArr[index+1]
+ }
+ return rawArr[0]
+ })
+
+ replacedArr := replaced.MustComplex128Slice()
+ if assert.Equal(t, 6, len(replacedArr)) {
+ assert.Equal(t, replacedArr[0], rawArr[1])
+ assert.Equal(t, replacedArr[1], rawArr[2])
+ assert.Equal(t, replacedArr[2], rawArr[3])
+ assert.Equal(t, replacedArr[3], rawArr[4])
+ assert.Equal(t, replacedArr[4], rawArr[5])
+ assert.Equal(t, replacedArr[5], rawArr[0])
+ }
+
+}
+
+func TestCollectComplex128(t *testing.T) {
+
+ v := &Value{data: []complex128{complex128(1), complex128(1), complex128(1), complex128(1), complex128(1), complex128(1)}}
+
+ collected := v.CollectComplex128(func(index int, val complex128) interface{} {
+ return index
+ })
+
+ collectedArr := collected.MustInterSlice()
+ if assert.Equal(t, 6, len(collectedArr)) {
+ assert.Equal(t, collectedArr[0], 0)
+ assert.Equal(t, collectedArr[1], 1)
+ assert.Equal(t, collectedArr[2], 2)
+ assert.Equal(t, collectedArr[3], 3)
+ assert.Equal(t, collectedArr[4], 4)
+ assert.Equal(t, collectedArr[5], 5)
+ }
+
+}
diff --git a/vendor/github.com/stretchr/objx/value.go b/vendor/github.com/stretchr/objx/value.go
new file mode 100644
index 000000000..7aaef06b1
--- /dev/null
+++ b/vendor/github.com/stretchr/objx/value.go
@@ -0,0 +1,13 @@
+package objx
+
+// Value provides methods for extracting interface{} data in various
+// types.
+type Value struct {
+ // data contains the raw data being managed by this Value
+ data interface{}
+}
+
+// Data returns the raw data contained by this Value
+func (v *Value) Data() interface{} {
+ return v.data
+}
diff --git a/vendor/github.com/stretchr/objx/value_test.go b/vendor/github.com/stretchr/objx/value_test.go
new file mode 100644
index 000000000..0bc65d92c
--- /dev/null
+++ b/vendor/github.com/stretchr/objx/value_test.go
@@ -0,0 +1 @@
+package objx
diff --git a/vendor/github.com/xenolf/lego/acme/dns_challenge.go b/vendor/github.com/xenolf/lego/acme/dns_challenge.go
index 30f2170ff..7c4cb80de 100644
--- a/vendor/github.com/xenolf/lego/acme/dns_challenge.go
+++ b/vendor/github.com/xenolf/lego/acme/dns_challenge.go
@@ -194,7 +194,7 @@ func dnsQuery(fqdn string, rtype uint16, nameservers []string, recursive bool) (
if err == dns.ErrTruncated {
tcp := &dns.Client{Net: "tcp", Timeout: DNSTimeout}
- // If the TCP request suceeds, the err will reset to nil
+ // If the TCP request succeeds, the err will reset to nil
in, _, err = tcp.Exchange(m, ns)
}
diff --git a/vendor/github.com/xenolf/lego/cli.go b/vendor/github.com/xenolf/lego/cli.go
index ae8354b50..2518d2e8f 100644
--- a/vendor/github.com/xenolf/lego/cli.go
+++ b/vendor/github.com/xenolf/lego/cli.go
@@ -221,6 +221,7 @@ Here is an example bash command using the CloudFlare DNS provider:
fmt.Fprintln(w, "\tovh:\tOVH_ENDPOINT, OVH_APPLICATION_KEY, OVH_APPLICATION_SECRET, OVH_CONSUMER_KEY")
fmt.Fprintln(w, "\tpdns:\tPDNS_API_KEY, PDNS_API_URL")
fmt.Fprintln(w, "\tdnspod:\tDNSPOD_API_KEY")
+ fmt.Fprintln(w, "\totc:\tOTC_USER_NAME, OTC_PASSWORD, OTC_PROJECT_NAME, OTC_DOMAIN_NAME, OTC_IDENTITY_ENDPOINT")
w.Flush()
fmt.Println(`
diff --git a/vendor/github.com/xenolf/lego/providers/dns/dns_providers.go b/vendor/github.com/xenolf/lego/providers/dns/dns_providers.go
index af6c02cca..94c8879b2 100644
--- a/vendor/github.com/xenolf/lego/providers/dns/dns_providers.go
+++ b/vendor/github.com/xenolf/lego/providers/dns/dns_providers.go
@@ -19,6 +19,7 @@ import (
"github.com/xenolf/lego/providers/dns/linode"
"github.com/xenolf/lego/providers/dns/namecheap"
"github.com/xenolf/lego/providers/dns/ns1"
+ "github.com/xenolf/lego/providers/dns/otc"
"github.com/xenolf/lego/providers/dns/ovh"
"github.com/xenolf/lego/providers/dns/pdns"
"github.com/xenolf/lego/providers/dns/rackspace"
@@ -73,6 +74,8 @@ func NewDNSChallengeProviderByName(name string) (acme.ChallengeProvider, error)
provider, err = pdns.NewDNSProvider()
case "ns1":
provider, err = ns1.NewDNSProvider()
+ case "otc":
+ provider, err = otc.NewDNSProvider()
default:
err = fmt.Errorf("Unrecognised DNS provider: %s", name)
}
diff --git a/vendor/github.com/xenolf/lego/providers/dns/otc/mock.go b/vendor/github.com/xenolf/lego/providers/dns/otc/mock.go
new file mode 100644
index 000000000..0f2acb4b4
--- /dev/null
+++ b/vendor/github.com/xenolf/lego/providers/dns/otc/mock.go
@@ -0,0 +1,152 @@
+package otc
+
+import (
+ "fmt"
+ "github.com/stretchr/testify/assert"
+ "io/ioutil"
+ "net/http"
+ "net/http/httptest"
+ "testing"
+)
+
+var fakeOTCUserName = "test"
+var fakeOTCPassword = "test"
+var fakeOTCDomainName = "test"
+var fakeOTCProjectName = "test"
+var fakeOTCToken = "62244bc21da68d03ebac94e6636ff01f"
+
+type DNSMock struct {
+ t *testing.T
+ Server *httptest.Server
+ Mux *http.ServeMux
+}
+
+func NewDNSMock(t *testing.T) *DNSMock {
+ return &DNSMock{
+ t: t,
+ }
+}
+
+// Setup creates the mock server
+func (m *DNSMock) Setup() {
+ m.Mux = http.NewServeMux()
+ m.Server = httptest.NewServer(m.Mux)
+}
+
+// ShutdownServer creates the mock server
+func (m *DNSMock) ShutdownServer() {
+ m.Server.Close()
+}
+
+func (m *DNSMock) HandleAuthSuccessfully() {
+ m.Mux.HandleFunc("/v3/auth/token", func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("X-Subject-Token", fakeOTCToken)
+
+ fmt.Fprintf(w, `{
+ "token": {
+ "catalog": [
+ {
+ "type": "dns",
+ "id": "56cd81db1f8445d98652479afe07c5ba",
+ "name": "",
+ "endpoints": [
+ {
+ "url": "%s",
+ "region": "eu-de",
+ "region_id": "eu-de",
+ "interface": "public",
+ "id": "0047a06690484d86afe04877074efddf"
+ }
+ ]
+ }
+ ]
+ }}`, m.Server.URL)
+ })
+}
+
+func (m *DNSMock) HandleListZonesSuccessfully() {
+ m.Mux.HandleFunc("/v2/zones", func(w http.ResponseWriter, r *http.Request) {
+ fmt.Fprintf(w, `{
+ "zones":[{
+ "id":"123123"
+ }]}
+ `)
+
+ assert.Equal(m.t, r.Method, "GET")
+ assert.Equal(m.t, r.URL.Path, "/v2/zones")
+ assert.Equal(m.t, r.URL.RawQuery, "name=example.com.")
+ assert.Equal(m.t, r.Header.Get("Content-Type"), "application/json")
+ })
+}
+
+func (m *DNSMock) HandleListZonesEmpty() {
+ m.Mux.HandleFunc("/v2/zones", func(w http.ResponseWriter, r *http.Request) {
+ fmt.Fprintf(w, `{
+ "zones":[
+ ]}
+ `)
+
+ assert.Equal(m.t, r.Method, "GET")
+ assert.Equal(m.t, r.URL.Path, "/v2/zones")
+ assert.Equal(m.t, r.URL.RawQuery, "name=example.com.")
+ assert.Equal(m.t, r.Header.Get("Content-Type"), "application/json")
+ })
+}
+
+func (m *DNSMock) HandleDeleteRecordsetsSuccessfully() {
+ m.Mux.HandleFunc("/v2/zones/123123/recordsets/321321", func(w http.ResponseWriter, r *http.Request) {
+ fmt.Fprintf(w, `{
+ "zones":[{
+ "id":"123123"
+ }]}
+ `)
+
+ assert.Equal(m.t, r.Method, "DELETE")
+ assert.Equal(m.t, r.URL.Path, "/v2/zones/123123/recordsets/321321")
+ assert.Equal(m.t, r.Header.Get("Content-Type"), "application/json")
+ })
+}
+
+func (m *DNSMock) HandleListRecordsetsEmpty() {
+ m.Mux.HandleFunc("/v2/zones/123123/recordsets", func(w http.ResponseWriter, r *http.Request) {
+ fmt.Fprintf(w, `{
+ "recordsets":[
+ ]}
+ `)
+
+ assert.Equal(m.t, r.URL.Path, "/v2/zones/123123/recordsets")
+ assert.Equal(m.t, r.URL.RawQuery, "type=TXT&name=_acme-challenge.example.com.")
+ })
+}
+func (m *DNSMock) HandleListRecordsetsSuccessfully() {
+ m.Mux.HandleFunc("/v2/zones/123123/recordsets", func(w http.ResponseWriter, r *http.Request) {
+ if r.Method == "GET" {
+ fmt.Fprintf(w, `{
+ "recordsets":[{
+ "id":"321321"
+ }]}
+ `)
+
+ assert.Equal(m.t, r.URL.Path, "/v2/zones/123123/recordsets")
+ assert.Equal(m.t, r.URL.RawQuery, "type=TXT&name=_acme-challenge.example.com.")
+
+ } else if r.Method == "POST" {
+ body, err := ioutil.ReadAll(r.Body)
+
+ assert.Nil(m.t, err)
+ exceptedString := "{\"name\":\"_acme-challenge.example.com.\",\"description\":\"Added TXT record for ACME dns-01 challenge using lego client\",\"type\":\"TXT\",\"ttl\":300,\"records\":[\"\\\"w6uP8Tcg6K2QR905Rms8iXTlksL6OD1KOWBxTK7wxPI\\\"\"]}"
+ assert.Equal(m.t, string(body), exceptedString)
+
+ fmt.Fprintf(w, `{
+ "recordsets":[{
+ "id":"321321"
+ }]}
+ `)
+
+ } else {
+ m.t.Errorf("Expected method to be 'GET' or 'POST' but got '%s'", r.Method)
+ }
+
+ assert.Equal(m.t, r.Header.Get("Content-Type"), "application/json")
+ })
+}
diff --git a/vendor/github.com/xenolf/lego/providers/dns/otc/otc.go b/vendor/github.com/xenolf/lego/providers/dns/otc/otc.go
new file mode 100644
index 000000000..86bcaa9b7
--- /dev/null
+++ b/vendor/github.com/xenolf/lego/providers/dns/otc/otc.go
@@ -0,0 +1,388 @@
+// Package otc implements a DNS provider for solving the DNS-01 challenge
+// using Open Telekom Cloud Managed DNS.
+package otc
+
+import (
+ "bytes"
+ "encoding/json"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "net/http"
+ "os"
+ "time"
+
+ "github.com/xenolf/lego/acme"
+)
+
+// DNSProvider is an implementation of the acme.ChallengeProvider interface that uses
+// OTC's Managed DNS API to manage TXT records for a domain.
+type DNSProvider struct {
+ identityEndpoint string
+ otcBaseURL string
+ domainName string
+ projectName string
+ userName string
+ password string
+ token string
+}
+
+// NewDNSProvider returns a DNSProvider instance configured for OTC DNS.
+// Credentials must be passed in the environment variables: OTC_USER_NAME,
+// OTC_DOMAIN_NAME, OTC_PASSWORD OTC_PROJECT_NAME and OTC_IDENTITY_ENDPOINT.
+func NewDNSProvider() (*DNSProvider, error) {
+ domainName := os.Getenv("OTC_DOMAIN_NAME")
+ userName := os.Getenv("OTC_USER_NAME")
+ password := os.Getenv("OTC_PASSWORD")
+ projectName := os.Getenv("OTC_PROJECT_NAME")
+ identityEndpoint := os.Getenv("OTC_IDENTITY_ENDPOINT")
+ return NewDNSProviderCredentials(domainName, userName, password, projectName, identityEndpoint)
+}
+
+// NewDNSProviderCredentials uses the supplied credentials to return a
+// DNSProvider instance configured for OTC DNS.
+func NewDNSProviderCredentials(domainName, userName, password, projectName, identityEndpoint string) (*DNSProvider, error) {
+ if domainName == "" || userName == "" || password == "" || projectName == "" {
+ return nil, fmt.Errorf("OTC credentials missing")
+ }
+
+ if identityEndpoint == "" {
+ identityEndpoint = "https://iam.eu-de.otc.t-systems.com:443/v3/auth/tokens"
+ }
+
+ return &DNSProvider{
+ identityEndpoint: identityEndpoint,
+ domainName: domainName,
+ userName: userName,
+ password: password,
+ projectName: projectName,
+ }, nil
+}
+
+func (d *DNSProvider) SendRequest(method, resource string, payload interface{}) (io.Reader, error) {
+ url := fmt.Sprintf("%s/%s", d.otcBaseURL, resource)
+
+ body, err := json.Marshal(payload)
+ if err != nil {
+ return nil, err
+ }
+
+ req, err := http.NewRequest(method, url, bytes.NewReader(body))
+ if err != nil {
+ return nil, err
+ }
+ req.Header.Set("Content-Type", "application/json")
+ if len(d.token) > 0 {
+ req.Header.Set("X-Auth-Token", d.token)
+ }
+
+ // Workaround for keep alive bug in otc api
+ tr := http.DefaultTransport.(*http.Transport)
+ tr.DisableKeepAlives = true
+
+ client := &http.Client{
+ Timeout: time.Duration(10 * time.Second),
+ Transport: tr,
+ }
+ resp, err := client.Do(req)
+ if err != nil {
+ return nil, err
+ }
+ defer resp.Body.Close()
+
+ if resp.StatusCode >= 400 {
+ return nil, fmt.Errorf("OTC API request %s failed with HTTP status code %d", url, resp.StatusCode)
+ }
+
+ body1, err := ioutil.ReadAll(resp.Body)
+ if err != nil {
+ return nil, err
+ }
+
+ return bytes.NewReader(body1), nil
+}
+
+func (d *DNSProvider) loginRequest() error {
+ type nameResponse struct {
+ Name string `json:"name"`
+ }
+
+ type userResponse struct {
+ Name string `json:"name"`
+ Password string `json:"password"`
+ Domain nameResponse `json:"domain"`
+ }
+
+ type passwordResponse struct {
+ User userResponse `json:"user"`
+ }
+ type identityResponse struct {
+ Methods []string `json:"methods"`
+ Password passwordResponse `json:"password"`
+ }
+
+ type scopeResponse struct {
+ Project nameResponse `json:"project"`
+ }
+
+ type authResponse struct {
+ Identity identityResponse `json:"identity"`
+ Scope scopeResponse `json:"scope"`
+ }
+
+ type loginResponse struct {
+ Auth authResponse `json:"auth"`
+ }
+
+ userResp := userResponse{
+ Name: d.userName,
+ Password: d.password,
+ Domain: nameResponse{
+ Name: d.domainName,
+ },
+ }
+
+ loginResp := loginResponse{
+ Auth: authResponse{
+ Identity: identityResponse{
+ Methods: []string{"password"},
+ Password: passwordResponse{
+ User: userResp,
+ },
+ },
+ Scope: scopeResponse{
+ Project: nameResponse{
+ Name: d.projectName,
+ },
+ },
+ },
+ }
+
+ body, err := json.Marshal(loginResp)
+ if err != nil {
+ return err
+ }
+ req, err := http.NewRequest("POST", d.identityEndpoint, bytes.NewReader(body))
+ if err != nil {
+ return err
+ }
+ req.Header.Set("Content-Type", "application/json")
+
+ client := &http.Client{Timeout: time.Duration(10 * time.Second)}
+ resp, err := client.Do(req)
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+
+ if resp.StatusCode >= 400 {
+ return fmt.Errorf("OTC API request failed with HTTP status code %d", resp.StatusCode)
+ }
+
+ d.token = resp.Header.Get("X-Subject-Token")
+
+ if d.token == "" {
+ return fmt.Errorf("unable to get auth token")
+ }
+
+ type endpointResponse struct {
+ Token struct {
+ Catalog []struct {
+ Type string `json:"type"`
+ Endpoints []struct {
+ URL string `json:"url"`
+ } `json:"endpoints"`
+ } `json:"catalog"`
+ } `json:"token"`
+ }
+ var endpointResp endpointResponse
+
+ err = json.NewDecoder(resp.Body).Decode(&endpointResp)
+ if err != nil {
+ return err
+ }
+
+ for _, v := range endpointResp.Token.Catalog {
+ if v.Type == "dns" {
+ for _, endpoint := range v.Endpoints {
+ d.otcBaseURL = fmt.Sprintf("%s/v2", endpoint.URL)
+ continue
+ }
+ }
+ }
+
+ if d.otcBaseURL == "" {
+ return fmt.Errorf("unable to get dns endpoint")
+ }
+
+ return nil
+}
+
+// Starts a new OTC API Session. Authenticates using userName, password
+// and receives a token to be used in for subsequent requests.
+func (d *DNSProvider) login() error {
+ err := d.loginRequest()
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (d *DNSProvider) getZoneID(zone string) (string, error) {
+ type zoneItem struct {
+ ID string `json:"id"`
+ }
+
+ type zonesResponse struct {
+ Zones []zoneItem `json:"zones"`
+ }
+
+ resource := fmt.Sprintf("zones?name=%s", zone)
+ resp, err := d.SendRequest("GET", resource, nil)
+ if err != nil {
+ return "", err
+ }
+
+ var zonesRes zonesResponse
+ err = json.NewDecoder(resp).Decode(&zonesRes)
+ if err != nil {
+ return "", err
+ }
+
+ if len(zonesRes.Zones) < 1 {
+ return "", fmt.Errorf("zone %s not found", zone)
+ }
+
+ if len(zonesRes.Zones) > 1 {
+ return "", fmt.Errorf("to many zones found")
+ }
+
+ if zonesRes.Zones[0].ID == "" {
+ return "", fmt.Errorf("id not found")
+ }
+
+ return zonesRes.Zones[0].ID, nil
+}
+
+func (d *DNSProvider) getRecordSetID(zoneID string, fqdn string) (string, error) {
+ type recordSet struct {
+ ID string `json:"id"`
+ }
+
+ type recordSetsResponse struct {
+ RecordSets []recordSet `json:"recordsets"`
+ }
+
+ resource := fmt.Sprintf("zones/%s/recordsets?type=TXT&name=%s", zoneID, fqdn)
+ resp, err := d.SendRequest("GET", resource, nil)
+ if err != nil {
+ return "", err
+ }
+
+ var recordSetsRes recordSetsResponse
+ err = json.NewDecoder(resp).Decode(&recordSetsRes)
+ if err != nil {
+ return "", err
+ }
+
+ if len(recordSetsRes.RecordSets) < 1 {
+ return "", fmt.Errorf("record not found")
+ }
+
+ if len(recordSetsRes.RecordSets) > 1 {
+ return "", fmt.Errorf("to many records found")
+ }
+
+ if recordSetsRes.RecordSets[0].ID == "" {
+ return "", fmt.Errorf("id not found")
+ }
+
+ return recordSetsRes.RecordSets[0].ID, nil
+}
+
+func (d *DNSProvider) deleteRecordSet(zoneID, recordID string) error {
+ resource := fmt.Sprintf("zones/%s/recordsets/%s", zoneID, recordID)
+
+ _, err := d.SendRequest("DELETE", resource, nil)
+ if err != nil {
+ return err
+ }
+ return nil
+}
+
+// Present creates a TXT record using the specified parameters
+func (d *DNSProvider) Present(domain, token, keyAuth string) error {
+ fqdn, value, ttl := acme.DNS01Record(domain, keyAuth)
+
+ if ttl < 300 {
+ ttl = 300 // 300 is otc minimum value for ttl
+ }
+
+ authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers)
+ if err != nil {
+ return err
+ }
+
+ err = d.login()
+ if err != nil {
+ return err
+ }
+
+ zoneID, err := d.getZoneID(authZone)
+ if err != nil {
+ return fmt.Errorf("unable to get zone: %s", err)
+ }
+
+ resource := fmt.Sprintf("zones/%s/recordsets", zoneID)
+
+ type recordset struct {
+ Name string `json:"name"`
+ Description string `json:"description"`
+ Type string `json:"type"`
+ Ttl int `json:"ttl"`
+ Records []string `json:"records"`
+ }
+
+ r1 := &recordset{
+ Name: fqdn,
+ Description: "Added TXT record for ACME dns-01 challenge using lego client",
+ Type: "TXT",
+ Ttl: 300,
+ Records: []string{fmt.Sprintf("\"%s\"", value)},
+ }
+ _, err = d.SendRequest("POST", resource, r1)
+
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+// CleanUp removes the TXT record matching the specified parameters
+func (d *DNSProvider) CleanUp(domain, token, keyAuth string) error {
+ fqdn, _, _ := acme.DNS01Record(domain, keyAuth)
+
+ authZone, err := acme.FindZoneByFqdn(fqdn, acme.RecursiveNameservers)
+ if err != nil {
+ return err
+ }
+
+ err = d.login()
+ if err != nil {
+ return err
+ }
+
+ zoneID, err := d.getZoneID(authZone)
+
+ if err != nil {
+ return err
+ }
+
+ recordID, err := d.getRecordSetID(zoneID, fqdn)
+ if err != nil {
+ return fmt.Errorf("unable go get record %s for zone %s: %s", fqdn, domain, err)
+ }
+ return d.deleteRecordSet(zoneID, recordID)
+}
diff --git a/vendor/github.com/xenolf/lego/providers/dns/otc/otc_test.go b/vendor/github.com/xenolf/lego/providers/dns/otc/otc_test.go
new file mode 100644
index 000000000..0c05334a9
--- /dev/null
+++ b/vendor/github.com/xenolf/lego/providers/dns/otc/otc_test.go
@@ -0,0 +1,112 @@
+package otc
+
+import (
+ "fmt"
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/suite"
+ "os"
+ "testing"
+)
+
+type OTCDNSTestSuite struct {
+ suite.Suite
+ Mock *DNSMock
+}
+
+func (s *OTCDNSTestSuite) TearDownSuite() {
+ s.Mock.ShutdownServer()
+}
+
+func (s *OTCDNSTestSuite) SetupTest() {
+ s.Mock = NewDNSMock(s.T())
+ s.Mock.Setup()
+ s.Mock.HandleAuthSuccessfully()
+
+}
+
+func TestOTCDNSTestSuite(t *testing.T) {
+ suite.Run(t, new(OTCDNSTestSuite))
+}
+
+func (s *OTCDNSTestSuite) createDNSProvider() (*DNSProvider, error) {
+ url := fmt.Sprintf("%s/v3/auth/token", s.Mock.Server.URL)
+ return NewDNSProviderCredentials(fakeOTCUserName, fakeOTCPassword, fakeOTCDomainName, fakeOTCProjectName, url)
+}
+
+func (s *OTCDNSTestSuite) TestOTCDNSLoginEnv() {
+ os.Setenv("OTC_DOMAIN_NAME", "unittest1")
+ os.Setenv("OTC_USER_NAME", "unittest2")
+ os.Setenv("OTC_PASSWORD", "unittest3")
+ os.Setenv("OTC_PROJECT_NAME", "unittest4")
+ os.Setenv("OTC_IDENTITY_ENDPOINT", "unittest5")
+
+ provider, err := NewDNSProvider()
+ assert.Nil(s.T(), err)
+ assert.Equal(s.T(), provider.domainName, "unittest1")
+ assert.Equal(s.T(), provider.userName, "unittest2")
+ assert.Equal(s.T(), provider.password, "unittest3")
+ assert.Equal(s.T(), provider.projectName, "unittest4")
+ assert.Equal(s.T(), provider.identityEndpoint, "unittest5")
+
+ os.Setenv("OTC_IDENTITY_ENDPOINT", "")
+
+ provider, err = NewDNSProvider()
+ assert.Nil(s.T(), err)
+ assert.Equal(s.T(), provider.identityEndpoint, "https://iam.eu-de.otc.t-systems.com:443/v3/auth/tokens")
+
+ os.Clearenv()
+}
+
+func (s *OTCDNSTestSuite) TestOTCDNSLoginEnvEmpty() {
+ _, err := NewDNSProvider()
+ assert.Equal(s.T(), "OTC credentials missing", err.Error())
+
+ os.Clearenv()
+}
+
+func (s *OTCDNSTestSuite) TestOTCDNSLogin() {
+ otcProvider, err := s.createDNSProvider()
+
+ assert.Nil(s.T(), err)
+ err = otcProvider.loginRequest()
+ assert.Nil(s.T(), err)
+ assert.Equal(s.T(), otcProvider.otcBaseURL, fmt.Sprintf("%s/v2", s.Mock.Server.URL))
+ assert.Equal(s.T(), fakeOTCToken, otcProvider.token)
+}
+
+func (s *OTCDNSTestSuite) TestOTCDNSEmptyZone() {
+ s.Mock.HandleListZonesEmpty()
+ s.Mock.HandleListRecordsetsSuccessfully()
+
+ otcProvider, _ := s.createDNSProvider()
+ err := otcProvider.Present("example.com", "", "foobar")
+ assert.NotNil(s.T(), err)
+}
+
+func (s *OTCDNSTestSuite) TestOTCDNSEmptyRecordset() {
+ s.Mock.HandleListZonesSuccessfully()
+ s.Mock.HandleListRecordsetsEmpty()
+
+ otcProvider, _ := s.createDNSProvider()
+ err := otcProvider.CleanUp("example.com", "", "foobar")
+ assert.NotNil(s.T(), err)
+}
+
+func (s *OTCDNSTestSuite) TestOTCDNSPresent() {
+ s.Mock.HandleListZonesSuccessfully()
+ s.Mock.HandleListRecordsetsSuccessfully()
+
+ otcProvider, _ := s.createDNSProvider()
+ err := otcProvider.Present("example.com", "", "foobar")
+ assert.Nil(s.T(), err)
+}
+
+func (s *OTCDNSTestSuite) TestOTCDNSCleanup() {
+ s.Mock.HandleListZonesSuccessfully()
+ s.Mock.HandleListRecordsetsSuccessfully()
+ s.Mock.HandleDeleteRecordsetsSuccessfully()
+
+ otcProvider, _ := s.createDNSProvider()
+ err := otcProvider.CleanUp("example.com", "", "foobar")
+ assert.Nil(s.T(), err)
+}