summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDave Collins <[email protected]>2013-01-20 22:25:51 -0600
committerDave Collins <[email protected]>2013-01-20 22:31:09 -0600
commit2fc049e83c263a23e23f71e1546b2ba662221f81 (patch)
tree9fe160d44f01291fd1df1d08b811dd18f9f10094
parent3b5249e43ec83523352aa525755535deff059be7 (diff)
Add tests for unrecognized reflect values.
In order to help future proof itself, spew handles unrecognized reflect values by passing them on to the standard fmt library. Since spew handles all current reflect values in the language, this condition has to be manually tested with a bit of hackery by using unsafe to change the kind to a nonexistent value. As of this commit, there is now 100% test coverage.
-rw-r--r--spew/internal_test.go76
1 files changed, 76 insertions, 0 deletions
diff --git a/spew/internal_test.go b/spew/internal_test.go
index 7a872e4..05bab97 100644
--- a/spew/internal_test.go
+++ b/spew/internal_test.go
@@ -26,6 +26,7 @@ import (
"bytes"
"reflect"
"testing"
+ "unsafe"
)
// dummyFmtState implements a fake fmt.State to use for testing invalid
@@ -79,3 +80,78 @@ func TestInvalidReflectValue(t *testing.T) {
t.Errorf("InvalidReflectValue #%d got: %s want: %s", i, s, want)
}
}
+
+// flagRO, flagKindShift and flagKindWidth indicate various bit flags that the
+// reflect package uses internally to track kind and state information.
+const flagRO = 1 << 0
+const flagKindShift = 4
+const flagKindWidth = 5
+
+// 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)) +
+ unsafe.Offsetof(reflectValue.flag)))
+ *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)
+ }
+}