提交 cb2e9c06 编写于 作者: S Sebastian Florek 提交者: GitHub

Fix apiserver-host parameter recognition (#998)

上级 969b727b
...@@ -8,30 +8,9 @@ Also a lovely [comune](http://en.wikipedia.org/wiki/Mergo) (municipality) in the ...@@ -8,30 +8,9 @@ Also a lovely [comune](http://en.wikipedia.org/wiki/Mergo) (municipality) in the
## Status ## Status
It is ready for production use. It works fine after extensive use in the wild. It is ready for production use. It works fine although it may use more of testing. Here some projects in the wild using Mergo:
[![Build Status][1]][2] - [GoogleCloudPlatform/kubernetes](https://github.com/GoogleCloudPlatform/kubernetes)
[![GoDoc][3]][4]
[![GoCard][5]][6]
[1]: https://travis-ci.org/imdario/mergo.png
[2]: https://travis-ci.org/imdario/mergo
[3]: https://godoc.org/github.com/imdario/mergo?status.svg
[4]: https://godoc.org/github.com/imdario/mergo
[5]: https://goreportcard.com/badge/imdario/mergo
[6]: https://goreportcard.com/report/github.com/imdario/mergo
### Important note
Mergo is intended to assign **only** zero value fields on destination with source value. Since April 6th it works like this. Before it didn't work properly, causing some random overwrites. After some issues and PRs I found it didn't merge as I designed it. Thanks to [imdario/mergo#8](https://github.com/imdario/mergo/pull/8) overwriting functions were added and the wrong behavior was clearly detected.
If you were using Mergo **before** April 6th 2015, please check your project works as intended after updating your local copy with ```go get -u github.com/imdario/mergo```. I apologize for any issue caused by its previous behavior and any future bug that Mergo could cause (I hope it won't!) in existing projects after the change (release 0.2.0).
### Mergo in the wild
- [docker/docker](https://github.com/docker/docker/)
- [kubernetes/kubernetes](https://github.com/kubernetes/kubernetes)
- [imdario/zas](https://github.com/imdario/zas)
- [soniah/dnsmadeeasy](https://github.com/soniah/dnsmadeeasy) - [soniah/dnsmadeeasy](https://github.com/soniah/dnsmadeeasy)
- [EagerIO/Stout](https://github.com/EagerIO/Stout) - [EagerIO/Stout](https://github.com/EagerIO/Stout)
- [lynndylanhurley/defsynth-api](https://github.com/lynndylanhurley/defsynth-api) - [lynndylanhurley/defsynth-api](https://github.com/lynndylanhurley/defsynth-api)
...@@ -40,17 +19,12 @@ If you were using Mergo **before** April 6th 2015, please check your project wor ...@@ -40,17 +19,12 @@ If you were using Mergo **before** April 6th 2015, please check your project wor
- [casualjim/exeggutor](https://github.com/casualjim/exeggutor) - [casualjim/exeggutor](https://github.com/casualjim/exeggutor)
- [divshot/gitling](https://github.com/divshot/gitling) - [divshot/gitling](https://github.com/divshot/gitling)
- [RWJMurphy/gorl](https://github.com/RWJMurphy/gorl) - [RWJMurphy/gorl](https://github.com/RWJMurphy/gorl)
- [andrerocker/deploy42](https://github.com/andrerocker/deploy42)
- [elwinar/rambler](https://github.com/elwinar/rambler) [![Build Status][1]][2]
- [tmaiaroto/gopartman](https://github.com/tmaiaroto/gopartman) [![GoDoc](https://godoc.org/github.com/imdario/mergo?status.svg)](https://godoc.org/github.com/imdario/mergo)
- [jfbus/impressionist](https://github.com/jfbus/impressionist)
- [Jmeyering/zealot](https://github.com/Jmeyering/zealot) [1]: https://travis-ci.org/imdario/mergo.png
- [godep-migrator/rigger-host](https://github.com/godep-migrator/rigger-host) [2]: https://travis-ci.org/imdario/mergo
- [Dronevery/MultiwaySwitch-Go](https://github.com/Dronevery/MultiwaySwitch-Go)
- [thoas/picfit](https://github.com/thoas/picfit)
- [mantasmatelis/whooplist-server](https://github.com/mantasmatelis/whooplist-server)
- [jnuthong/item_search](https://github.com/jnuthong/item_search)
- [Iris Web Framework](https://github.com/kataras/iris)
## Installation ## Installation
...@@ -79,39 +53,6 @@ Warning: if you map a struct to map, it won't do it recursively. Don't expect Me ...@@ -79,39 +53,6 @@ Warning: if you map a struct to map, it won't do it recursively. Don't expect Me
More information and examples in [godoc documentation](http://godoc.org/github.com/imdario/mergo). More information and examples in [godoc documentation](http://godoc.org/github.com/imdario/mergo).
### Nice example
```go
package main
import (
"fmt"
"github.com/imdario/mergo"
)
type Foo struct {
A string
B int64
}
func main() {
src := Foo{
A: "one",
}
dest := Foo{
A: "two",
B: 2,
}
mergo.Merge(&dest, src)
fmt.Println(dest)
// Will print
// {two 2}
}
```
Note: if test are failing due missing package, please execute: Note: if test are failing due missing package, please execute:
go get gopkg.in/yaml.v1 go get gopkg.in/yaml.v1
......
package mergo
import (
"encoding/json"
"testing"
)
var (
request = `{"timestamp":null, "name": "foo"}`
maprequest = map[string]interface{}{
"timestamp": nil,
"name": "foo",
"newStuff": "foo",
}
)
func TestIssue17MergeWithOverwrite(t *testing.T) {
var something map[string]interface{}
if err := json.Unmarshal([]byte(request), &something); err != nil {
t.Errorf("Error while Unmarshalling maprequest %s", err)
}
if err := MergeWithOverwrite(&something, maprequest); err != nil {
t.Errorf("Error while merging %s", err)
}
}
package mergo
import (
"testing"
"time"
)
type document struct {
Created *time.Time
}
func TestIssue23MergeWithOverwrite(t *testing.T) {
now := time.Now()
dst := document{
&now,
}
expected := time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC)
src := document{
&expected,
}
if err := MergeWithOverwrite(&dst, src); err != nil {
t.Errorf("Error while merging %s", err)
}
if dst.Created != src.Created {
t.Fatalf("Created not merged in properly: dst.Created(%v) != src.Created(%v)", dst.Created, src.Created)
}
}
...@@ -31,7 +31,7 @@ func isExported(field reflect.StructField) bool { ...@@ -31,7 +31,7 @@ func isExported(field reflect.StructField) bool {
// Traverses recursively both values, assigning src's fields values to dst. // Traverses recursively both values, assigning src's fields values to dst.
// The map argument tracks comparisons that have already been seen, which allows // The map argument tracks comparisons that have already been seen, which allows
// short circuiting on recursive types. // short circuiting on recursive types.
func deepMap(dst, src reflect.Value, visited map[uintptr]*visit, depth int, overwrite bool) (err error) { func deepMap(dst, src reflect.Value, visited map[uintptr]*visit, depth int) (err error) {
if dst.CanAddr() { if dst.CanAddr() {
addr := dst.UnsafeAddr() addr := dst.UnsafeAddr()
h := 17 * addr h := 17 * addr
...@@ -57,7 +57,7 @@ func deepMap(dst, src reflect.Value, visited map[uintptr]*visit, depth int, over ...@@ -57,7 +57,7 @@ func deepMap(dst, src reflect.Value, visited map[uintptr]*visit, depth int, over
} }
fieldName := field.Name fieldName := field.Name
fieldName = changeInitialCase(fieldName, unicode.ToLower) fieldName = changeInitialCase(fieldName, unicode.ToLower)
if v, ok := dstMap[fieldName]; !ok || (isEmptyValue(reflect.ValueOf(v)) || overwrite) { if v, ok := dstMap[fieldName]; !ok || isEmptyValue(reflect.ValueOf(v)) {
dstMap[fieldName] = src.Field(i).Interface() dstMap[fieldName] = src.Field(i).Interface()
} }
} }
...@@ -89,12 +89,12 @@ func deepMap(dst, src reflect.Value, visited map[uintptr]*visit, depth int, over ...@@ -89,12 +89,12 @@ func deepMap(dst, src reflect.Value, visited map[uintptr]*visit, depth int, over
continue continue
} }
if srcKind == dstKind { if srcKind == dstKind {
if err = deepMerge(dstElement, srcElement, visited, depth+1, overwrite); err != nil { if err = deepMerge(dstElement, srcElement, visited, depth+1); err != nil {
return return
} }
} else { } else {
if srcKind == reflect.Map { if srcKind == reflect.Map {
if err = deepMap(dstElement, srcElement, visited, depth+1, overwrite); err != nil { if err = deepMap(dstElement, srcElement, visited, depth+1); err != nil {
return return
} }
} else { } else {
...@@ -118,16 +118,6 @@ func deepMap(dst, src reflect.Value, visited map[uintptr]*visit, depth int, over ...@@ -118,16 +118,6 @@ func deepMap(dst, src reflect.Value, visited map[uintptr]*visit, depth int, over
// This is separated method from Merge because it is cleaner and it keeps sane // This is separated method from Merge because it is cleaner and it keeps sane
// semantics: merging equal types, mapping different (restricted) types. // semantics: merging equal types, mapping different (restricted) types.
func Map(dst, src interface{}) error { func Map(dst, src interface{}) error {
return _map(dst, src, false)
}
// MapWithOverwrite will do the same as Map except that non-empty dst attributes will be overriden by
// non-empty src attribute values.
func MapWithOverwrite(dst, src interface{}) error {
return _map(dst, src, true)
}
func _map(dst, src interface{}, overwrite bool) error {
var ( var (
vDst, vSrc reflect.Value vDst, vSrc reflect.Value
err error err error
...@@ -138,7 +128,7 @@ func _map(dst, src interface{}, overwrite bool) error { ...@@ -138,7 +128,7 @@ func _map(dst, src interface{}, overwrite bool) error {
// To be friction-less, we redirect equal-type arguments // To be friction-less, we redirect equal-type arguments
// to deepMerge. Only because arguments can be anything. // to deepMerge. Only because arguments can be anything.
if vSrc.Kind() == vDst.Kind() { if vSrc.Kind() == vDst.Kind() {
return deepMerge(vDst, vSrc, make(map[uintptr]*visit), 0, overwrite) return deepMerge(vDst, vSrc, make(map[uintptr]*visit), 0)
} }
switch vSrc.Kind() { switch vSrc.Kind() {
case reflect.Struct: case reflect.Struct:
...@@ -152,5 +142,5 @@ func _map(dst, src interface{}, overwrite bool) error { ...@@ -152,5 +142,5 @@ func _map(dst, src interface{}, overwrite bool) error {
default: default:
return ErrNotSupported return ErrNotSupported
} }
return deepMap(vDst, vSrc, make(map[uintptr]*visit), 0, overwrite) return deepMap(vDst, vSrc, make(map[uintptr]*visit), 0)
} }
...@@ -15,7 +15,7 @@ import ( ...@@ -15,7 +15,7 @@ import (
// Traverses recursively both values, assigning src's fields values to dst. // Traverses recursively both values, assigning src's fields values to dst.
// The map argument tracks comparisons that have already been seen, which allows // The map argument tracks comparisons that have already been seen, which allows
// short circuiting on recursive types. // short circuiting on recursive types.
func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, overwrite bool) (err error) { func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int) (err error) {
if !src.IsValid() { if !src.IsValid() {
return return
} }
...@@ -35,7 +35,7 @@ func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, ov ...@@ -35,7 +35,7 @@ func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, ov
switch dst.Kind() { switch dst.Kind() {
case reflect.Struct: case reflect.Struct:
for i, n := 0, dst.NumField(); i < n; i++ { for i, n := 0, dst.NumField(); i < n; i++ {
if err = deepMerge(dst.Field(i), src.Field(i), visited, depth+1, overwrite); err != nil { if err = deepMerge(dst.Field(i), src.Field(i), visited, depth+1); err != nil {
return return
} }
} }
...@@ -46,31 +46,17 @@ func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, ov ...@@ -46,31 +46,17 @@ func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, ov
continue continue
} }
dstElement := dst.MapIndex(key) dstElement := dst.MapIndex(key)
switch srcElement.Kind() {
case reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr, reflect.Interface, reflect.Slice:
if srcElement.IsNil() {
continue
}
fallthrough
default:
if !srcElement.CanInterface() {
continue
}
switch reflect.TypeOf(srcElement.Interface()).Kind() { switch reflect.TypeOf(srcElement.Interface()).Kind() {
case reflect.Struct: case reflect.Struct:
fallthrough fallthrough
case reflect.Ptr:
fallthrough
case reflect.Map: case reflect.Map:
if err = deepMerge(dstElement, srcElement, visited, depth+1, overwrite); err != nil { if err = deepMerge(dstElement, srcElement, visited, depth+1); err != nil {
return return
} }
if !dstElement.IsValid() {
dst.SetMapIndex(key, srcElement)
} }
} default:
if !isEmptyValue(srcElement) && (overwrite || (!dstElement.IsValid() || isEmptyValue(dst))) {
if dst.IsNil() {
dst.Set(reflect.MakeMap(dst.Type()))
}
dst.SetMapIndex(key, srcElement) dst.SetMapIndex(key, srcElement)
} }
} }
...@@ -79,36 +65,28 @@ func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, ov ...@@ -79,36 +65,28 @@ func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, ov
case reflect.Interface: case reflect.Interface:
if src.IsNil() { if src.IsNil() {
break break
} else if dst.IsNil() || overwrite { } else if dst.IsNil() {
if dst.CanSet() && (overwrite || isEmptyValue(dst)) { if dst.CanSet() && isEmptyValue(dst) {
dst.Set(src) dst.Set(src)
} }
} else if err = deepMerge(dst.Elem(), src.Elem(), visited, depth+1, overwrite); err != nil { } else if err = deepMerge(dst.Elem(), src.Elem(), visited, depth+1); err != nil {
return return
} }
default: default:
if dst.CanSet() && !isEmptyValue(src) && (overwrite || isEmptyValue(dst)) { if dst.CanSet() && !isEmptyValue(src) {
dst.Set(src) dst.Set(src)
} }
} }
return return
} }
// Merge will fill any empty for value type attributes on the dst struct using corresponding // Merge sets fields' values in dst from src if they have a zero
// src attributes if they themselves are not empty. dst and src must be valid same-type structs // value of their type.
// and dst must be a pointer to struct. // dst and src must be valid same-type structs and dst must be
// It won't merge unexported (private) fields and will do recursively any exported field. // a pointer to struct.
// It won't merge unexported (private) fields and will do recursively
// any exported field.
func Merge(dst, src interface{}) error { func Merge(dst, src interface{}) error {
return merge(dst, src, false)
}
// MergeWithOverwrite will do the same as Merge except that non-empty dst attributes will be overriden by
// non-empty src attribute values.
func MergeWithOverwrite(dst, src interface{}) error {
return merge(dst, src, true)
}
func merge(dst, src interface{}, overwrite bool) error {
var ( var (
vDst, vSrc reflect.Value vDst, vSrc reflect.Value
err error err error
...@@ -119,5 +97,5 @@ func merge(dst, src interface{}, overwrite bool) error { ...@@ -119,5 +97,5 @@ func merge(dst, src interface{}, overwrite bool) error {
if vDst.Type() != vSrc.Type() { if vDst.Type() != vSrc.Type() {
return ErrDifferentArgumentsTypes return ErrDifferentArgumentsTypes
} }
return deepMerge(vDst, vSrc, make(map[uintptr]*visit), 0, overwrite) return deepMerge(vDst, vSrc, make(map[uintptr]*visit), 0)
} }
...@@ -6,11 +6,10 @@ ...@@ -6,11 +6,10 @@
package mergo package mergo
import ( import (
"gopkg.in/yaml.v1"
"io/ioutil" "io/ioutil"
"reflect" "reflect"
"testing" "testing"
"time"
"gopkg.in/yaml.v1"
) )
type simpleTest struct { type simpleTest struct {
...@@ -20,7 +19,7 @@ type simpleTest struct { ...@@ -20,7 +19,7 @@ type simpleTest struct {
type complexTest struct { type complexTest struct {
St simpleTest St simpleTest
sz int sz int
ID string Id string
} }
type moreComplextText struct { type moreComplextText struct {
...@@ -37,41 +36,6 @@ type sliceTest struct { ...@@ -37,41 +36,6 @@ type sliceTest struct {
S []int S []int
} }
func TestKb(t *testing.T) {
type testStruct struct {
Name string
KeyValue map[string]interface{}
}
akv := make(map[string]interface{})
akv["Key1"] = "not value 1"
akv["Key2"] = "value2"
a := testStruct{}
a.Name = "A"
a.KeyValue = akv
bkv := make(map[string]interface{})
bkv["Key1"] = "value1"
bkv["Key3"] = "value3"
b := testStruct{}
b.Name = "B"
b.KeyValue = bkv
ekv := make(map[string]interface{})
ekv["Key1"] = "value1"
ekv["Key2"] = "value2"
ekv["Key3"] = "value3"
expected := testStruct{}
expected.Name = "B"
expected.KeyValue = ekv
Merge(&b, a)
if !reflect.DeepEqual(b, expected) {
t.Errorf("Actual: %#v did not match \nExpected: %#v", b, expected)
}
}
func TestNil(t *testing.T) { func TestNil(t *testing.T) {
if err := Merge(nil, nil); err != ErrNilArguments { if err := Merge(nil, nil); err != ErrNilArguments {
t.Fail() t.Fail()
...@@ -93,7 +57,7 @@ func TestSimpleStruct(t *testing.T) { ...@@ -93,7 +57,7 @@ func TestSimpleStruct(t *testing.T) {
t.FailNow() t.FailNow()
} }
if a.Value != 42 { if a.Value != 42 {
t.Fatalf("b not merged in properly: a.Value(%d) != b.Value(%d)", a.Value, b.Value) t.Fatalf("b not merged in a properly: a.Value(%d) != b.Value(%d)", a.Value, b.Value)
} }
if !reflect.DeepEqual(a, b) { if !reflect.DeepEqual(a, b) {
t.FailNow() t.FailNow()
...@@ -102,33 +66,19 @@ func TestSimpleStruct(t *testing.T) { ...@@ -102,33 +66,19 @@ func TestSimpleStruct(t *testing.T) {
func TestComplexStruct(t *testing.T) { func TestComplexStruct(t *testing.T) {
a := complexTest{} a := complexTest{}
a.ID = "athing" a.Id = "athing"
b := complexTest{simpleTest{42}, 1, "bthing"} b := complexTest{simpleTest{42}, 1, "bthing"}
if err := Merge(&a, b); err != nil { if err := Merge(&a, b); err != nil {
t.FailNow() t.FailNow()
} }
if a.St.Value != 42 { if a.St.Value != 42 {
t.Fatalf("b not merged in properly: a.St.Value(%d) != b.St.Value(%d)", a.St.Value, b.St.Value) t.Fatalf("b not merged in a properly: a.St.Value(%d) != b.St.Value(%d)", a.St.Value, b.St.Value)
} }
if a.sz == 1 { if a.sz == 1 {
t.Fatalf("a's private field sz not preserved from merge: a.sz(%d) == b.sz(%d)", a.sz, b.sz) t.Fatalf("a's private field sz not preserved from merge: a.sz(%d) == b.sz(%d)", a.sz, b.sz)
} }
if a.ID == b.ID { if a.Id != b.Id {
t.Fatalf("a's field ID merged unexpectedly: a.ID(%s) == b.ID(%s)", a.ID, b.ID) t.Fatalf("a's field Id not merged properly: a.Id(%s) != b.Id(%s)", a.Id, b.Id)
}
}
func TestComplexStructWithOverwrite(t *testing.T) {
a := complexTest{simpleTest{1}, 1, "do-not-overwrite-with-empty-value"}
b := complexTest{simpleTest{42}, 2, ""}
expect := complexTest{simpleTest{42}, 1, "do-not-overwrite-with-empty-value"}
if err := MergeWithOverwrite(&a, b); err != nil {
t.FailNow()
}
if !reflect.DeepEqual(a, expect) {
t.Fatalf("Test failed:\ngot :\n%#v\n\nwant :\n%#v\n\n", a, expect)
} }
} }
...@@ -141,68 +91,7 @@ func TestPointerStruct(t *testing.T) { ...@@ -141,68 +91,7 @@ func TestPointerStruct(t *testing.T) {
t.FailNow() t.FailNow()
} }
if a.C.Value != b.C.Value { if a.C.Value != b.C.Value {
t.Fatalf("b not merged in properly: a.C.Value(%d) != b.C.Value(%d)", a.C.Value, b.C.Value) //t.Fatalf("b not merged in a properly: a.C.Value(%d) != b.C.Value(%d)", a.C.Value, b.C.Value)
}
}
type embeddingStruct struct {
embeddedStruct
}
type embeddedStruct struct {
A string
}
func TestEmbeddedStruct(t *testing.T) {
tests := []struct {
src embeddingStruct
dst embeddingStruct
expected embeddingStruct
}{
{
src: embeddingStruct{
embeddedStruct{"foo"},
},
dst: embeddingStruct{
embeddedStruct{""},
},
expected: embeddingStruct{
embeddedStruct{"foo"},
},
},
{
src: embeddingStruct{
embeddedStruct{""},
},
dst: embeddingStruct{
embeddedStruct{"bar"},
},
expected: embeddingStruct{
embeddedStruct{"bar"},
},
},
{
src: embeddingStruct{
embeddedStruct{"foo"},
},
dst: embeddingStruct{
embeddedStruct{"bar"},
},
expected: embeddingStruct{
embeddedStruct{"bar"},
},
},
}
for _, test := range tests {
err := Merge(&test.dst, test.src)
if err != nil {
t.Errorf("unexpected error: %v", err)
continue
}
if !reflect.DeepEqual(test.dst, test.expected) {
t.Errorf("unexpected output\nexpected:\n%+v\nsaw:\n%+v\n", test.expected, test.dst)
}
} }
} }
...@@ -227,7 +116,7 @@ func TestSliceStruct(t *testing.T) { ...@@ -227,7 +116,7 @@ func TestSliceStruct(t *testing.T) {
t.FailNow() t.FailNow()
} }
if len(a.S) != len(b.S) { if len(a.S) != len(b.S) {
t.Fatalf("b not merged in a proper way %d != %d", len(a.S), len(b.S)) t.Fatalf("b not merged in a properly %d != %d", len(a.S), len(b.S))
} }
a = sliceTest{[]int{1}} a = sliceTest{[]int{1}}
...@@ -235,63 +124,32 @@ func TestSliceStruct(t *testing.T) { ...@@ -235,63 +124,32 @@ func TestSliceStruct(t *testing.T) {
if err := Merge(&a, b); err != nil { if err := Merge(&a, b); err != nil {
t.FailNow() t.FailNow()
} }
if len(a.S) != 1 { if len(b.S) != 3 {
t.FailNow() t.FailNow()
} }
if len(a.S) == len(b.S) { if len(a.S) != len(b.S) {
t.Fatalf("b merged unexpectedly %d != %d", len(a.S), len(b.S)) t.Fatalf("b not merged in a properly %d != %d", len(a.S), len(b.S))
}
}
func TestMapsWithOverwrite(t *testing.T) {
m := map[string]simpleTest{
"a": {}, // overwritten by 16
"b": {42}, // not overwritten by empty value
"c": {13}, // overwritten by 12
"d": {61},
}
n := map[string]simpleTest{
"a": {16},
"b": {},
"c": {12},
"e": {14},
}
expect := map[string]simpleTest{
"a": {16},
"b": {},
"c": {12},
"d": {61},
"e": {14},
}
if err := MergeWithOverwrite(&m, n); err != nil {
t.Fatalf(err.Error())
}
if !reflect.DeepEqual(m, expect) {
t.Fatalf("Test failed:\ngot :\n%#v\n\nwant :\n%#v\n\n", m, expect)
} }
} }
func TestMaps(t *testing.T) { func TestMaps(t *testing.T) {
m := map[string]simpleTest{ m := map[string]simpleTest{
"a": {}, "a": simpleTest{},
"b": {42}, "b": simpleTest{42},
"c": {13}, "d": simpleTest{61},
"d": {61},
} }
n := map[string]simpleTest{ n := map[string]simpleTest{
"a": {16}, "a": simpleTest{16},
"b": {}, "b": simpleTest{},
"c": {12}, "c": simpleTest{12},
"e": {14}, "e": simpleTest{14},
} }
expect := map[string]simpleTest{ expect := map[string]simpleTest{
"a": {0}, "a": simpleTest{0},
"b": {42}, "b": simpleTest{42},
"c": {13}, "c": simpleTest{12},
"d": {61}, "d": simpleTest{61},
"e": {14}, "e": simpleTest{14},
} }
if err := Merge(&m, n); err != nil { if err := Merge(&m, n); err != nil {
...@@ -307,8 +165,8 @@ func TestMaps(t *testing.T) { ...@@ -307,8 +165,8 @@ func TestMaps(t *testing.T) {
if m["b"].Value != 42 { if m["b"].Value != 42 {
t.Fatalf(`n wrongly merged in m: m["b"].Value(%d) != n["b"].Value(%d)`, m["b"].Value, n["b"].Value) t.Fatalf(`n wrongly merged in m: m["b"].Value(%d) != n["b"].Value(%d)`, m["b"].Value, n["b"].Value)
} }
if m["c"].Value != 13 { if m["c"].Value != 12 {
t.Fatalf(`n overwritten in m: m["c"].Value(%d) != n["c"].Value(%d)`, m["c"].Value, n["c"].Value) t.Fatalf(`n not merged in m: m["c"].Value(%d) != n["c"].Value(%d)`, m["c"].Value, n["c"].Value)
} }
} }
...@@ -341,7 +199,7 @@ func TestTwoPointerValues(t *testing.T) { ...@@ -341,7 +199,7 @@ func TestTwoPointerValues(t *testing.T) {
func TestMap(t *testing.T) { func TestMap(t *testing.T) {
a := complexTest{} a := complexTest{}
a.ID = "athing" a.Id = "athing"
c := moreComplextText{a, simpleTest{}, simpleTest{}} c := moreComplextText{a, simpleTest{}, simpleTest{}}
b := map[string]interface{}{ b := map[string]interface{}{
"ct": map[string]interface{}{ "ct": map[string]interface{}{
...@@ -363,19 +221,19 @@ func TestMap(t *testing.T) { ...@@ -363,19 +221,19 @@ func TestMap(t *testing.T) {
o := b["st"].(*simpleTest) o := b["st"].(*simpleTest)
p := b["nt"].(simpleTest) p := b["nt"].(simpleTest)
if c.Ct.St.Value != 42 { if c.Ct.St.Value != 42 {
t.Fatalf("b not merged in properly: c.Ct.St.Value(%d) != b.Ct.St.Value(%d)", c.Ct.St.Value, n["value"]) t.Fatalf("b not merged in a properly: c.Ct.St.Value(%d) != b.Ct.St.Value(%d)", c.Ct.St.Value, n["value"])
} }
if c.St.Value != 144 { if c.St.Value != 144 {
t.Fatalf("b not merged in properly: c.St.Value(%d) != b.St.Value(%d)", c.St.Value, o.Value) t.Fatalf("b not merged in a properly: c.St.Value(%d) != b.St.Value(%d)", c.St.Value, o.Value)
} }
if c.Nt.Value != 3 { if c.Nt.Value != 3 {
t.Fatalf("b not merged in properly: c.Nt.Value(%d) != b.Nt.Value(%d)", c.St.Value, p.Value) t.Fatalf("b not merged in a properly: c.Nt.Value(%d) != b.Nt.Value(%d)", c.St.Value, p.Value)
} }
if c.Ct.sz == 1 { if c.Ct.sz == 1 {
t.Fatalf("a's private field sz not preserved from merge: c.Ct.sz(%d) == b.Ct.sz(%d)", c.Ct.sz, m["sz"]) t.Fatalf("a's private field sz not preserved from merge: c.Ct.sz(%d) == b.Ct.sz(%d)", c.Ct.sz, m["sz"])
} }
if c.Ct.ID == m["id"] { if c.Ct.Id != m["id"] {
t.Fatalf("a's field ID merged unexpectedly: c.Ct.ID(%s) == b.Ct.ID(%s)", c.Ct.ID, m["id"]) t.Fatalf("a's field Id not merged properly: c.Ct.Id(%s) != b.Ct.Id(%s)", c.Ct.Id, m["id"])
} }
} }
...@@ -388,7 +246,7 @@ func TestSimpleMap(t *testing.T) { ...@@ -388,7 +246,7 @@ func TestSimpleMap(t *testing.T) {
t.FailNow() t.FailNow()
} }
if a.Value != 42 { if a.Value != 42 {
t.Fatalf("b not merged in properly: a.Value(%d) != b.Value(%v)", a.Value, b["value"]) t.Fatalf("b not merged in a properly: a.Value(%d) != b.Value(%v)", a.Value, b["value"])
} }
} }
...@@ -409,10 +267,10 @@ func TestBackAndForth(t *testing.T) { ...@@ -409,10 +267,10 @@ func TestBackAndForth(t *testing.T) {
ok bool ok bool
) )
if v, ok = m["a"]; v.(int) != pt.A || !ok { if v, ok = m["a"]; v.(int) != pt.A || !ok {
t.Fatalf("pt not merged in properly: m[`a`](%d) != pt.A(%d)", v, pt.A) t.Fatalf("pt not merged properly: m[`a`](%d) != pt.A(%d)", v, pt.A)
} }
if v, ok = m["b"]; !ok { if v, ok = m["b"]; !ok {
t.Fatalf("pt not merged in properly: B is missing in m") t.Fatalf("pt not merged properly: B is missing in m")
} }
var st *simpleTest var st *simpleTest
if st = v.(*simpleTest); st.Value != 66 { if st = v.(*simpleTest); st.Value != 66 {
...@@ -423,73 +281,13 @@ func TestBackAndForth(t *testing.T) { ...@@ -423,73 +281,13 @@ func TestBackAndForth(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
if bpt.A != pt.A { if bpt.A != pt.A {
t.Fatalf("pt not merged in properly: bpt.A(%d) != pt.A(%d)", bpt.A, pt.A) t.Fatalf("pt not merged properly: bpt.A(%d) != pt.A(%d)", bpt.A, pt.A)
} }
if bpt.hidden == pt.hidden { if bpt.hidden == pt.hidden {
t.Fatalf("pt unexpectedly merged: bpt.hidden(%d) == pt.hidden(%d)", bpt.hidden, pt.hidden) t.Fatalf("pt unexpectedly merged: bpt.hidden(%d) == pt.hidden(%d)", bpt.hidden, pt.hidden)
} }
if bpt.B.Value != pt.B.Value { if bpt.B.Value != pt.B.Value {
t.Fatalf("pt not merged in properly: bpt.B.Value(%d) != pt.B.Value(%d)", bpt.B.Value, pt.B.Value) t.Fatalf("pt not merged properly: bpt.B.Value(%d) != pt.B.Value(%d)", bpt.B.Value, pt.B.Value)
}
}
type structWithTimePointer struct {
Birth *time.Time
}
func TestTime(t *testing.T) {
now := time.Now()
dataStruct := structWithTimePointer{
Birth: &now,
}
dataMap := map[string]interface{}{
"Birth": &now,
}
b := structWithTimePointer{}
if err := Merge(&b, dataStruct); err != nil {
t.FailNow()
}
if b.Birth.IsZero() {
t.Fatalf("time.Time not merged in properly: b.Birth(%v) != dataStruct['Birth'](%v)", b.Birth, dataStruct.Birth)
}
if b.Birth != dataStruct.Birth {
t.Fatalf("time.Time not merged in properly: b.Birth(%v) != dataStruct['Birth'](%v)", b.Birth, dataStruct.Birth)
}
b = structWithTimePointer{}
if err := Map(&b, dataMap); err != nil {
t.FailNow()
}
if b.Birth.IsZero() {
t.Fatalf("time.Time not merged in properly: b.Birth(%v) != dataMap['Birth'](%v)", b.Birth, dataMap["Birth"])
}
}
type simpleNested struct {
A int
}
type structWithNestedPtrValueMap struct {
NestedPtrValue map[string]*simpleNested
}
func TestNestedPtrValueInMap(t *testing.T) {
src := &structWithNestedPtrValueMap{
NestedPtrValue: map[string]*simpleNested{
"x": {
A: 1,
},
},
}
dst := &structWithNestedPtrValueMap{
NestedPtrValue: map[string]*simpleNested{
"x": {},
},
}
if err := Map(dst, src); err != nil {
t.FailNow()
}
if dst.NestedPtrValue["x"].A == 0 {
t.Fatalf("Nested Ptr value not merged in properly: dst.NestedPtrValue[\"x\"].A(%v) != src.NestedPtrValue[\"x\"].A(%v)", dst.NestedPtrValue["x"].A, src.NestedPtrValue["x"].A)
} }
} }
...@@ -499,49 +297,3 @@ func loadYAML(path string) (m map[string]interface{}) { ...@@ -499,49 +297,3 @@ func loadYAML(path string) (m map[string]interface{}) {
_ = yaml.Unmarshal(raw, &m) _ = yaml.Unmarshal(raw, &m)
return return
} }
type structWithMap struct {
m map[string]structWithUnexportedProperty
}
type structWithUnexportedProperty struct {
s string
}
func TestUnexportedProperty(t *testing.T) {
a := structWithMap{map[string]structWithUnexportedProperty{
"key": structWithUnexportedProperty{"hello"},
}}
b := structWithMap{map[string]structWithUnexportedProperty{
"key": structWithUnexportedProperty{"hi"},
}}
defer func() {
if r := recover(); r != nil {
t.Errorf("Should not have panicked")
}
}()
Merge(&a, b)
}
type structWithBoolPointer struct {
C *bool
}
func TestBooleanPointer(t *testing.T) {
bt, bf := true, false
src := structWithBoolPointer{
&bt,
}
dst := structWithBoolPointer{
&bf,
}
if err := Merge(&dst, src); err != nil {
t.FailNow()
}
if dst.C == src.C {
t.Fatalf("dst.C should be a different pointer than src.C")
}
if *dst.C != *src.C {
t.Fatalf("dst.C should be true")
}
}
...@@ -715,10 +715,12 @@ ...@@ -715,10 +715,12 @@
"revisionTime": "2016-02-01T17:48:07Z" "revisionTime": "2016-02-01T17:48:07Z"
}, },
{ {
"checksumSHA1": "H9S5xwBvrCqJqte/RGxwl68MWJg=", "checksumSHA1": "n/llKRPzxcWttjZtpAWA7VW9iJA=",
"path": "github.com/imdario/mergo", "path": "github.com/imdario/mergo",
"revision": "50d4dbd4eb0e84778abe37cefef140271d96fade", "revision": "86cdca063f41835f7acf068df463f14eecd11e17",
"revisionTime": "2016-05-17T06:44:35Z" "revisionTime": "2015-01-29T15:29:59Z",
"version": "=0.1.4",
"versionExact": "0.1.4"
}, },
{ {
"checksumSHA1": "40vJyUB4ezQSn/NSadsKEOrudMc=", "checksumSHA1": "40vJyUB4ezQSn/NSadsKEOrudMc=",
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册