From a8ffde6f21e04047ef87741421370323c86427c5 Mon Sep 17 00:00:00 2001 From: Audrius Butkevicius Date: Sun, 6 Mar 2016 20:32:10 +0000 Subject: [PATCH] Add deps --- lib/config/config_test.go | 46 ++--- lib/db/set_test.go | 12 +- lib/model/queue_test.go | 41 ++-- lib/osutil/osutil_test.go | 2 + lib/scanner/walk_test.go | 6 +- vendor/github.com/d4l3k/messagediff/LICENSE | 22 ++ vendor/github.com/d4l3k/messagediff/README.md | 65 ++++++ vendor/github.com/d4l3k/messagediff/bypass.go | 151 ++++++++++++++ .../d4l3k/messagediff/bypasssafe.go | 37 ++++ .../d4l3k/messagediff/messagediff.go | 188 ++++++++++++++++++ .../d4l3k/messagediff/messagediff_test.go | 116 +++++++++++ vendor/manifest | 6 + 12 files changed, 641 insertions(+), 51 deletions(-) create mode 100644 vendor/github.com/d4l3k/messagediff/LICENSE create mode 100644 vendor/github.com/d4l3k/messagediff/README.md create mode 100644 vendor/github.com/d4l3k/messagediff/bypass.go create mode 100644 vendor/github.com/d4l3k/messagediff/bypasssafe.go create mode 100644 vendor/github.com/d4l3k/messagediff/messagediff.go create mode 100644 vendor/github.com/d4l3k/messagediff/messagediff_test.go diff --git a/lib/config/config_test.go b/lib/config/config_test.go index 008402d26..698977b3b 100644 --- a/lib/config/config_test.go +++ b/lib/config/config_test.go @@ -12,11 +12,11 @@ import ( "fmt" "os" "path/filepath" - "reflect" "runtime" "strings" "testing" + "github.com/d4l3k/messagediff" "github.com/syncthing/syncthing/lib/protocol" ) @@ -65,8 +65,8 @@ func TestDefaultValues(t *testing.T) { cfg := New(device1) - if !reflect.DeepEqual(cfg.Options, expected) { - t.Errorf("Default config differs;\n E: %#v\n A: %#v", expected, cfg.Options) + if diff, equal := messagediff.PrettyDiff(cfg.Options, expected); !equal { + t.Errorf("Default config differs. Diff:\n%s", diff) } } @@ -133,14 +133,14 @@ func TestDeviceConfig(t *testing.T) { if cfg.Version != CurrentVersion { t.Errorf("%d: Incorrect version %d != %d", i, cfg.Version, CurrentVersion) } - if !reflect.DeepEqual(cfg.Folders, expectedFolders) { - t.Errorf("%d: Incorrect Folders\n A: %#v\n E: %#v", i, cfg.Folders, expectedFolders) + if diff, equal := messagediff.PrettyDiff(cfg.Folders, expectedFolders); !equal { + t.Errorf("%d: Incorrect Folders. Diff:\n%s", i, diff) } - if !reflect.DeepEqual(cfg.Devices, expectedDevices) { - t.Errorf("%d: Incorrect Devices\n A: %#v\n E: %#v", i, cfg.Devices, expectedDevices) + if diff, equal := messagediff.PrettyDiff(cfg.Devices, expectedDevices); !equal { + t.Errorf("%d: Incorrect Devices. Diff:\n%s", i, diff) } - if !reflect.DeepEqual(cfg.Folders[0].DeviceIDs(), expectedDeviceIDs) { - t.Errorf("%d: Incorrect DeviceIDs\n A: %#v\n E: %#v", i, cfg.Folders[0].DeviceIDs(), expectedDeviceIDs) + if diff, equal := messagediff.PrettyDiff(cfg.Folders[0].DeviceIDs(), expectedDeviceIDs); !equal { + t.Errorf("%d: Incorrect DeviceIDs. Diff:\n%s", i, diff) } } } @@ -153,8 +153,8 @@ func TestNoListenAddress(t *testing.T) { expected := []string{""} actual := cfg.Options().ListenAddress - if !reflect.DeepEqual(actual, expected) { - t.Errorf("Unexpected ListenAddress %#v", actual) + if diff, equal := messagediff.PrettyDiff(actual, expected); !equal { + t.Errorf("Unexpected ListenAddress. Diff:\n%s", diff) } } @@ -197,8 +197,8 @@ func TestOverriddenValues(t *testing.T) { t.Error(err) } - if !reflect.DeepEqual(cfg.Options(), expected) { - t.Errorf("Overridden config differs;\n E: %#v\n A: %#v", expected, cfg.Options()) + if diff, equal := messagediff.PrettyDiff(cfg.Options(), expected); !equal { + t.Errorf("Overridden config differs. Diff:\n%s", diff) } } @@ -231,8 +231,8 @@ func TestDeviceAddressesDynamic(t *testing.T) { } actual := cfg.Devices() - if !reflect.DeepEqual(actual, expected) { - t.Errorf("Devices differ;\n E: %#v\n A: %#v", expected, actual) + if diff, equal := messagediff.PrettyDiff(actual, expected); !equal { + t.Errorf("Devices differ. Diff:\n%s", diff) } } @@ -268,8 +268,8 @@ func TestDeviceCompression(t *testing.T) { } actual := cfg.Devices() - if !reflect.DeepEqual(actual, expected) { - t.Errorf("Devices differ;\n E: %#v\n A: %#v", expected, actual) + if diff, equal := messagediff.PrettyDiff(actual, expected); !equal { + t.Errorf("Devices differ. Diff:\n%s", diff) } } @@ -302,8 +302,8 @@ func TestDeviceAddressesStatic(t *testing.T) { } actual := cfg.Devices() - if !reflect.DeepEqual(actual, expected) { - t.Errorf("Devices differ;\n E: %#v\n A: %#v", expected, actual) + if diff, equal := messagediff.PrettyDiff(actual, expected); !equal { + t.Errorf("Devices differ. Diff:\n%s", diff) } } @@ -325,8 +325,8 @@ func TestVersioningConfig(t *testing.T) { "foo": "bar", "baz": "quux", } - if !reflect.DeepEqual(vc.Params, expected) { - t.Errorf("vc.Params differ;\n E: %#v\n A: %#v", expected, vc.Params) + if diff, equal := messagediff.PrettyDiff(vc.Params, expected); !equal { + t.Errorf("vc.Params differ. Diff:\n%s", diff) } } @@ -447,8 +447,8 @@ func TestNewSaveLoad(t *testing.T) { t.Error(err) } - if !reflect.DeepEqual(cfg.Raw(), cfg2.Raw()) { - t.Errorf("Configs are not equal;\n E: %#v\n A: %#v", cfg.Raw(), cfg2.Raw()) + if diff, equal := messagediff.PrettyDiff(cfg.Raw(), cfg2.Raw()); !equal { + t.Errorf("Configs are not equal. Diff:\n%s", diff) } os.Remove(path) diff --git a/lib/db/set_test.go b/lib/db/set_test.go index 2d791deeb..b193884b3 100644 --- a/lib/db/set_test.go +++ b/lib/db/set_test.go @@ -10,10 +10,10 @@ import ( "bytes" "fmt" "os" - "reflect" "sort" "testing" + "github.com/d4l3k/messagediff" "github.com/syncthing/syncthing/lib/db" "github.com/syncthing/syncthing/lib/protocol" ) @@ -532,8 +532,9 @@ func TestListDropFolder(t *testing.T) { // Check that we have both folders and their data is in the global list expectedFolderList := []string{"test0", "test1"} - if actualFolderList := ldb.ListFolders(); !reflect.DeepEqual(actualFolderList, expectedFolderList) { - t.Fatalf("FolderList mismatch\nE: %v\nA: %v", expectedFolderList, actualFolderList) + actualFolderList := ldb.ListFolders() + if diff, equal := messagediff.PrettyDiff(actualFolderList, expectedFolderList); !equal { + t.Fatalf("FolderList mismatch. Diff:\n%s", diff) } if l := len(globalList(s0)); l != 3 { t.Errorf("Incorrect global length %d != 3 for s0", l) @@ -547,8 +548,9 @@ func TestListDropFolder(t *testing.T) { db.DropFolder(ldb, "test1") expectedFolderList = []string{"test0"} - if actualFolderList := ldb.ListFolders(); !reflect.DeepEqual(actualFolderList, expectedFolderList) { - t.Fatalf("FolderList mismatch\nE: %v\nA: %v", expectedFolderList, actualFolderList) + actualFolderList = ldb.ListFolders() + if diff, equal := messagediff.PrettyDiff(actualFolderList, expectedFolderList); !equal { + t.Fatalf("FolderList mismatch. Diff:\n%s", diff) } if l := len(globalList(s0)); l != 3 { t.Errorf("Incorrect global length %d != 3 for s0", l) diff --git a/lib/model/queue_test.go b/lib/model/queue_test.go index c218b8a00..295ade4eb 100644 --- a/lib/model/queue_test.go +++ b/lib/model/queue_test.go @@ -8,8 +8,9 @@ package model import ( "fmt" - "reflect" "testing" + + "github.com/d4l3k/messagediff" ) func TestJobQueue(t *testing.T) { @@ -126,36 +127,36 @@ func TestBringToFront(t *testing.T) { q.Push("f4", 0, 0) _, queued := q.Jobs() - if !reflect.DeepEqual(queued, []string{"f1", "f2", "f3", "f4"}) { - t.Errorf("Incorrect order %v at start", queued) + if diff, equal := messagediff.PrettyDiff(queued, []string{"f1", "f2", "f3", "f4"}); !equal { + t.Errorf("Order does not match. Diff:\n%s", diff) } q.BringToFront("f1") // corner case: does nothing _, queued = q.Jobs() - if !reflect.DeepEqual(queued, []string{"f1", "f2", "f3", "f4"}) { - t.Errorf("Incorrect order %v", queued) + if diff, equal := messagediff.PrettyDiff(queued, []string{"f1", "f2", "f3", "f4"}); !equal { + t.Errorf("Order does not match. Diff:\n%s", diff) } q.BringToFront("f3") _, queued = q.Jobs() - if !reflect.DeepEqual(queued, []string{"f3", "f1", "f2", "f4"}) { - t.Errorf("Incorrect order %v", queued) + if diff, equal := messagediff.PrettyDiff(queued, []string{"f3", "f1", "f2", "f4"}); !equal { + t.Errorf("Order does not match. Diff:\n%s", diff) } q.BringToFront("f2") _, queued = q.Jobs() - if !reflect.DeepEqual(queued, []string{"f2", "f3", "f1", "f4"}) { - t.Errorf("Incorrect order %v", queued) + if diff, equal := messagediff.PrettyDiff(queued, []string{"f2", "f3", "f1", "f4"}); !equal { + t.Errorf("Order does not match. Diff:\n%s", diff) } q.BringToFront("f4") // corner case: last element _, queued = q.Jobs() - if !reflect.DeepEqual(queued, []string{"f4", "f2", "f3", "f1"}) { - t.Errorf("Incorrect order %v", queued) + if diff, equal := messagediff.PrettyDiff(queued, []string{"f4", "f2", "f3", "f1"}); !equal { + t.Errorf("Order does not match. Diff:\n%s", diff) } } @@ -175,7 +176,7 @@ func TestShuffle(t *testing.T) { } t.Logf("%v", queued) - if !reflect.DeepEqual(queued, []string{"f1", "f2", "f3", "f4"}) { + if _, equal := messagediff.PrettyDiff(queued, []string{"f1", "f2", "f3", "f4"}); !equal { // The queue was shuffled return } @@ -199,8 +200,8 @@ func TestSortBySize(t *testing.T) { } expected := []string{"f4", "f1", "f3", "f2"} - if !reflect.DeepEqual(actual, expected) { - t.Errorf("SortSmallestFirst(): %#v != %#v", actual, expected) + if diff, equal := messagediff.PrettyDiff(actual, expected); !equal { + t.Errorf("SortSmallestFirst() diff:\n%s", diff) } q.SortLargestFirst() @@ -211,8 +212,8 @@ func TestSortBySize(t *testing.T) { } expected = []string{"f2", "f3", "f1", "f4"} - if !reflect.DeepEqual(actual, expected) { - t.Errorf("SortLargestFirst(): %#v != %#v", actual, expected) + if diff, equal := messagediff.PrettyDiff(actual, expected); !equal { + t.Errorf("SortLargestFirst() diff:\n%s", diff) } } @@ -231,8 +232,8 @@ func TestSortByAge(t *testing.T) { } expected := []string{"f4", "f1", "f3", "f2"} - if !reflect.DeepEqual(actual, expected) { - t.Errorf("SortOldestFirst(): %#v != %#v", actual, expected) + if diff, equal := messagediff.PrettyDiff(actual, expected); !equal { + t.Errorf("SortOldestFirst() diff:\n%s", diff) } q.SortNewestFirst() @@ -243,8 +244,8 @@ func TestSortByAge(t *testing.T) { } expected = []string{"f2", "f3", "f1", "f4"} - if !reflect.DeepEqual(actual, expected) { - t.Errorf("SortNewestFirst(): %#v != %#v", actual, expected) + if diff, equal := messagediff.PrettyDiff(actual, expected); !equal { + t.Errorf("SortNewestFirst() diff:\n%s", diff) } } diff --git a/lib/osutil/osutil_test.go b/lib/osutil/osutil_test.go index d4d5c15bc..96f0d98a2 100644 --- a/lib/osutil/osutil_test.go +++ b/lib/osutil/osutil_test.go @@ -80,6 +80,7 @@ func TestInWritableDirWindowsRemove(t *testing.T) { if err != nil { t.Fatal(err) } + defer os.Chmod("testdata/windows/ro/readonlynew", 0700) defer os.RemoveAll("testdata") create := func(name string) error { @@ -123,6 +124,7 @@ func TestInWritableDirWindowsRename(t *testing.T) { if err != nil { t.Fatal(err) } + defer os.Chmod("testdata/windows/ro/readonlynew", 0700) defer os.RemoveAll("testdata") create := func(name string) error { diff --git a/lib/scanner/walk_test.go b/lib/scanner/walk_test.go index d356277fd..d1140415d 100644 --- a/lib/scanner/walk_test.go +++ b/lib/scanner/walk_test.go @@ -13,13 +13,13 @@ import ( "io" "os" "path/filepath" - "reflect" "runtime" rdebug "runtime/debug" "sort" "sync" "testing" + "github.com/d4l3k/messagediff" "github.com/syncthing/syncthing/lib/ignore" "github.com/syncthing/syncthing/lib/osutil" "github.com/syncthing/syncthing/lib/protocol" @@ -120,8 +120,8 @@ func TestWalk(t *testing.T) { sort.Sort(fileList(tmp)) files := fileList(tmp).testfiles() - if !reflect.DeepEqual(files, testdata) { - t.Errorf("Walk returned unexpected data\nExpected: %v\nActual: %v", testdata, files) + if diff, equal := messagediff.PrettyDiff(files, testdata); !equal { + t.Errorf("Walk returned unexpected data. Diff:\n%s", diff) } } diff --git a/vendor/github.com/d4l3k/messagediff/LICENSE b/vendor/github.com/d4l3k/messagediff/LICENSE new file mode 100644 index 000000000..0074f6ae2 --- /dev/null +++ b/vendor/github.com/d4l3k/messagediff/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2015 Tristan Rice + +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/d4l3k/messagediff/README.md b/vendor/github.com/d4l3k/messagediff/README.md new file mode 100644 index 000000000..086f50200 --- /dev/null +++ b/vendor/github.com/d4l3k/messagediff/README.md @@ -0,0 +1,65 @@ +# messagediff [![Build Status](https://travis-ci.org/d4l3k/messagediff.svg?branch=master)](https://travis-ci.org/d4l3k/messagediff) [![Coverage Status](https://coveralls.io/repos/github/d4l3k/messagediff/badge.svg?branch=master)](https://coveralls.io/github/d4l3k/messagediff?branch=master) [![GoDoc](https://godoc.org/github.com/d4l3k/messagediff?status.svg)](https://godoc.org/github.com/d4l3k/messagediff) + +A library for doing diffs of arbitrary Golang structs. + +If the unsafe package is available messagediff will diff unexported fields in +addition to exported fields. This is primarily used for testing purposes as it +allows for providing informative error messages. + + +## Example Usage +In a normal file: +```go +package main + +import "github.com/d4l3k/messagediff" + +type someStruct struct { + A, b int + C []int +} + +func main() { + a := someStruct{1, 2, []int{1}} + b := someStruct{1, 3, []int{1, 2}} + diff, equal := messagediff.PrettyDiff(a, b) + /* + diff = + `added: .C[1] = 2 + modified: .b = 3` + + equal = false + */ +} + +``` +In a test: +```go +import "github.com/d4l3k/messagediff" + +... + +type someStruct struct { + A, b int + C []int +} + +func TestSomething(t *testing.T) { + want := someStruct{1, 2, []int{1}} + got := someStruct{1, 3, []int{1, 2}} + if diff, equal := messagediff.PrettyDiff(want, got); !equal { + t.Errorf("Something() = %#v\n%s", got, diff) + } +} +``` + +See the `DeepDiff` function for using the diff results programmatically. + +## License +Copyright (c) 2015 [Tristan Rice](https://fn.lc) + +messagediff is licensed under the MIT license. See the LICENSE file for more information. + +bypass.go and bypasssafe.go are borrowed from +[go-spew](https://github.com/davecgh/go-spew) and have a seperate copyright +notice. diff --git a/vendor/github.com/d4l3k/messagediff/bypass.go b/vendor/github.com/d4l3k/messagediff/bypass.go new file mode 100644 index 000000000..8f1882657 --- /dev/null +++ b/vendor/github.com/d4l3k/messagediff/bypass.go @@ -0,0 +1,151 @@ +// Copyright (c) 2015 Dave Collins +// +// 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 and "-tags disableunsafe" +// is not added to the go build command line. +// +build !appengine,!disableunsafe + +package messagediff + +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<>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/d4l3k/messagediff/bypasssafe.go b/vendor/github.com/d4l3k/messagediff/bypasssafe.go new file mode 100644 index 000000000..99f7ffd8e --- /dev/null +++ b/vendor/github.com/d4l3k/messagediff/bypasssafe.go @@ -0,0 +1,37 @@ +// Copyright (c) 2015 Dave Collins +// +// 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 the code is running on Google App Engine or "-tags disableunsafe" +// is added to the go build command line. +// +build appengine disableunsafe + +package messagediff + +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/d4l3k/messagediff/messagediff.go b/vendor/github.com/d4l3k/messagediff/messagediff.go new file mode 100644 index 000000000..f5c692ec0 --- /dev/null +++ b/vendor/github.com/d4l3k/messagediff/messagediff.go @@ -0,0 +1,188 @@ +package messagediff + +import ( + "fmt" + "reflect" + "sort" + "strings" +) + +// PrettyDiff does a deep comparison and returns the nicely formated results. +func PrettyDiff(a, b interface{}) (string, bool) { + d, equal := DeepDiff(a, b) + var dstr []string + for path, added := range d.Added { + dstr = append(dstr, fmt.Sprintf("added: %s = %#v\n", path.String(), added)) + } + for path, removed := range d.Removed { + dstr = append(dstr, fmt.Sprintf("removed: %s = %#v\n", path.String(), removed)) + } + for path, modified := range d.Modified { + dstr = append(dstr, fmt.Sprintf("modified: %s = %#v\n", path.String(), modified)) + } + sort.Strings(dstr) + return strings.Join(dstr, ""), equal +} + +// DeepDiff does a deep comparison and returns the results. +func DeepDiff(a, b interface{}) (*Diff, bool) { + d := newdiff() + return d, diff(a, b, nil, d) +} + +func newdiff() *Diff { + return &Diff{ + Added: make(map[*Path]interface{}), + Removed: make(map[*Path]interface{}), + Modified: make(map[*Path]interface{}), + } +} + +func diff(a, b interface{}, path Path, d *Diff) bool { + aVal := reflect.ValueOf(a) + bVal := reflect.ValueOf(b) + if !aVal.IsValid() && !bVal.IsValid() { + // Both are nil. + return true + } + if !aVal.IsValid() || !bVal.IsValid() { + // One is nil and the other isn't. + d.Modified[&path] = b + return false + } + if aVal.Type() != bVal.Type() { + d.Modified[&path] = b + return false + } + kind := aVal.Type().Kind() + equal := true + switch kind { + case reflect.Array, reflect.Slice: + aLen := aVal.Len() + bLen := bVal.Len() + for i := 0; i < min(aLen, bLen); i++ { + localPath := append(path, SliceIndex(i)) + if eq := diff(aVal.Index(i).Interface(), bVal.Index(i).Interface(), localPath, d); !eq { + equal = false + } + } + if aLen > bLen { + for i := bLen; i < aLen; i++ { + localPath := append(path, SliceIndex(i)) + d.Removed[&localPath] = aVal.Index(i).Interface() + equal = false + } + } else if aLen < bLen { + for i := aLen; i < bLen; i++ { + localPath := append(path, SliceIndex(i)) + d.Added[&localPath] = bVal.Index(i).Interface() + equal = false + } + } + case reflect.Map: + for _, key := range aVal.MapKeys() { + aI := aVal.MapIndex(key) + bI := bVal.MapIndex(key) + localPath := append(path, MapKey{key.Interface()}) + if !bI.IsValid() { + d.Removed[&localPath] = aI.Interface() + equal = false + } else if eq := diff(aI.Interface(), bI.Interface(), localPath, d); !eq { + equal = false + } + } + for _, key := range bVal.MapKeys() { + aI := aVal.MapIndex(key) + if !aI.IsValid() { + bI := bVal.MapIndex(key) + localPath := append(path, MapKey{key.Interface()}) + d.Added[&localPath] = bI.Interface() + equal = false + } + } + case reflect.Struct: + typ := aVal.Type() + for i := 0; i < typ.NumField(); i++ { + index := []int{i} + field := typ.FieldByIndex(index) + localPath := append(path, StructField(field.Name)) + aI := unsafeReflectValue(aVal.FieldByIndex(index)).Interface() + bI := unsafeReflectValue(bVal.FieldByIndex(index)).Interface() + if eq := diff(aI, bI, localPath, d); !eq { + equal = false + } + } + case reflect.Ptr: + aVal = aVal.Elem() + bVal = bVal.Elem() + if !aVal.IsValid() && !bVal.IsValid() { + // Both are nil. + equal = true + } else if !aVal.IsValid() || !bVal.IsValid() { + // One is nil and the other isn't. + d.Modified[&path] = b + equal = false + } else { + equal = diff(aVal.Interface(), bVal.Interface(), path, d) + } + default: + if reflect.DeepEqual(a, b) { + equal = true + } else { + d.Modified[&path] = b + equal = false + } + } + return equal +} + +func min(a, b int) int { + if a < b { + return a + } + return b +} + +// Diff represents a change in a struct. +type Diff struct { + Added, Removed, Modified map[*Path]interface{} +} + +// Path represents a path to a changed datum. +type Path []PathNode + +func (p Path) String() string { + var out string + for _, n := range p { + out += n.String() + } + return out +} + +// PathNode represents one step in the path. +type PathNode interface { + String() string +} + +// StructField is a path element representing a field of a struct. +type StructField string + +func (n StructField) String() string { + return fmt.Sprintf(".%s", string(n)) +} + +// MapKey is a path element representing a key of a map. +type MapKey struct { + Key interface{} +} + +func (n MapKey) String() string { + return fmt.Sprintf("[%#v]", n.Key) +} + +// SliceIndex is a path element representing a index of a slice. +type SliceIndex int + +func (n SliceIndex) String() string { + return fmt.Sprintf("[%d]", n) +} diff --git a/vendor/github.com/d4l3k/messagediff/messagediff_test.go b/vendor/github.com/d4l3k/messagediff/messagediff_test.go new file mode 100644 index 000000000..c74168e2a --- /dev/null +++ b/vendor/github.com/d4l3k/messagediff/messagediff_test.go @@ -0,0 +1,116 @@ +package messagediff + +import ( + "testing" + "time" +) + +type testStruct struct { + A, b int + C []int +} + +func TestPrettyDiff(t *testing.T) { + testData := []struct { + a, b interface{} + diff string + equal bool + }{ + { + true, + false, + "modified: = false\n", + false, + }, + { + true, + 0, + "modified: = 0\n", + false, + }, + { + []int{0, 1, 2}, + []int{0, 1, 2, 3}, + "added: [3] = 3\n", + false, + }, + { + []int{0, 1, 2, 3}, + []int{0, 1, 2}, + "removed: [3] = 3\n", + false, + }, + { + []int{0}, + []int{1}, + "modified: [0] = 1\n", + false, + }, + { + &[]int{0}, + &[]int{1}, + "modified: [0] = 1\n", + false, + }, + { + map[string]int{"a": 1, "b": 2}, + map[string]int{"b": 4, "c": 3}, + "added: [\"c\"] = 3\nmodified: [\"b\"] = 4\nremoved: [\"a\"] = 1\n", + false, + }, + { + testStruct{1, 2, []int{1}}, + testStruct{1, 3, []int{1, 2}}, + "added: .C[1] = 2\nmodified: .b = 3\n", + false, + }, + { + nil, + nil, + "", + true, + }, + { + &time.Time{}, + nil, + "modified: = \n", + false, + }, + { + time.Time{}, + time.Time{}, + "", + true, + }, + { + time.Date(1970, 1, 1, 0, 0, 0, 0, time.UTC), + time.Time{}, + "modified: .loc = (*time.Location)(nil)\nmodified: .sec = 0\n", + false, + }, + } + for i, td := range testData { + diff, equal := PrettyDiff(td.a, td.b) + if diff != td.diff { + t.Errorf("%d. PrettyDiff(%#v, %#v) diff = %#v; not %#v", i, td.a, td.b, diff, td.diff) + } + if equal != td.equal { + t.Errorf("%d. PrettyDiff(%#v, %#v) equal = %#v; not %#v", i, td.a, td.b, equal, td.equal) + } + } +} + +func TestPathString(t *testing.T) { + testData := []struct { + in Path + want string + }{{ + Path{StructField("test"), SliceIndex(1), MapKey{"blue"}, MapKey{12.3}}, + ".test[1][\"blue\"][12.3]", + }} + for i, td := range testData { + if out := td.in.String(); out != td.want { + t.Errorf("%d. %#v.String() = %#v; not %#v", i, td.in, out, td.want) + } + } +} diff --git a/vendor/manifest b/vendor/manifest index cca9ee7fa..950b91c8e 100644 --- a/vendor/manifest +++ b/vendor/manifest @@ -25,6 +25,12 @@ "revision": "b6e0c321c9b5b28ba5ee21e828323e4b982c6976", "branch": "master" }, + { + "importpath": "github.com/d4l3k/messagediff", + "repository": "https://github.com/d4l3k/messagediff", + "revision": "bf29d7cd9038386a5b4a22e2d73c8fb20ae14602", + "branch": "master" + }, { "importpath": "github.com/golang/snappy", "repository": "https://github.com/golang/snappy",