/*
Copyright 2017 Google LLC

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 spanner

import (
	"math"
	"reflect"
	"testing"
	"time"

	"cloud.google.com/go/civil"
	proto3 "github.com/golang/protobuf/ptypes/struct"
	sppb "google.golang.org/genproto/googleapis/spanner/v1"
)

var (
	t1 = mustParseTime("2016-11-15T15:04:05.999999999Z")
	// Boundaries
	t2 = mustParseTime("0000-01-01T00:00:00.000000000Z")
	t3 = mustParseTime("9999-12-31T23:59:59.999999999Z")
	// Local timezone
	t4 = time.Now()
	d1 = mustParseDate("2016-11-15")
	d2 = mustParseDate("1678-01-01")
)

func mustParseTime(s string) time.Time {
	t, err := time.Parse(time.RFC3339Nano, s)
	if err != nil {
		panic(err)
	}
	return t
}

func mustParseDate(s string) civil.Date {
	d, err := civil.ParseDate(s)
	if err != nil {
		panic(err)
	}
	return d
}

// Test encoding Values.
func TestEncodeValue(t *testing.T) {
	var (
		tString = stringType()
		tInt    = intType()
		tBool   = boolType()
		tFloat  = floatType()
		tBytes  = bytesType()
		tTime   = timeType()
		tDate   = dateType()
	)
	for i, test := range []struct {
		in       interface{}
		want     *proto3.Value
		wantType *sppb.Type
	}{
		// STRING / STRING ARRAY
		{"abc", stringProto("abc"), tString},
		{NullString{"abc", true}, stringProto("abc"), tString},
		{NullString{"abc", false}, nullProto(), tString},
		{[]string(nil), nullProto(), listType(tString)},
		{[]string{"abc", "bcd"}, listProto(stringProto("abc"), stringProto("bcd")), listType(tString)},
		{[]NullString{{"abcd", true}, {"xyz", false}}, listProto(stringProto("abcd"), nullProto()), listType(tString)},
		// BYTES / BYTES ARRAY
		{[]byte("foo"), bytesProto([]byte("foo")), tBytes},
		{[]byte(nil), nullProto(), tBytes},
		{[][]byte{nil, []byte("ab")}, listProto(nullProto(), bytesProto([]byte("ab"))), listType(tBytes)},
		{[][]byte(nil), nullProto(), listType(tBytes)},
		// INT64 / INT64 ARRAY
		{7, intProto(7), tInt},
		{[]int(nil), nullProto(), listType(tInt)},
		{[]int{31, 127}, listProto(intProto(31), intProto(127)), listType(tInt)},
		{int64(81), intProto(81), tInt},
		{[]int64(nil), nullProto(), listType(tInt)},
		{[]int64{33, 129}, listProto(intProto(33), intProto(129)), listType(tInt)},
		{NullInt64{11, true}, intProto(11), tInt},
		{NullInt64{11, false}, nullProto(), tInt},
		{[]NullInt64{{35, true}, {131, false}}, listProto(intProto(35), nullProto()), listType(tInt)},
		// BOOL / BOOL ARRAY
		{true, boolProto(true), tBool},
		{NullBool{true, true}, boolProto(true), tBool},
		{NullBool{true, false}, nullProto(), tBool},
		{[]bool{true, false}, listProto(boolProto(true), boolProto(false)), listType(tBool)},
		{[]NullBool{{true, true}, {true, false}}, listProto(boolProto(true), nullProto()), listType(tBool)},
		// FLOAT64 / FLOAT64 ARRAY
		{3.14, floatProto(3.14), tFloat},
		{NullFloat64{3.1415, true}, floatProto(3.1415), tFloat},
		{NullFloat64{math.Inf(1), true}, floatProto(math.Inf(1)), tFloat},
		{NullFloat64{3.14159, false}, nullProto(), tFloat},
		{[]float64(nil), nullProto(), listType(tFloat)},
		{[]float64{3.141, 0.618, math.Inf(-1)}, listProto(floatProto(3.141), floatProto(0.618), floatProto(math.Inf(-1))), listType(tFloat)},
		{[]NullFloat64{{3.141, true}, {0.618, false}}, listProto(floatProto(3.141), nullProto()), listType(tFloat)},
		// TIMESTAMP / TIMESTAMP ARRAY
		{t1, timeProto(t1), tTime},
		{NullTime{t1, true}, timeProto(t1), tTime},
		{NullTime{t1, false}, nullProto(), tTime},
		{[]time.Time(nil), nullProto(), listType(tTime)},
		{[]time.Time{t1, t2, t3, t4}, listProto(timeProto(t1), timeProto(t2), timeProto(t3), timeProto(t4)), listType(tTime)},
		{[]NullTime{{t1, true}, {t1, false}}, listProto(timeProto(t1), nullProto()), listType(tTime)},
		// DATE / DATE ARRAY
		{d1, dateProto(d1), tDate},
		{NullDate{d1, true}, dateProto(d1), tDate},
		{NullDate{civil.Date{}, false}, nullProto(), tDate},
		{[]civil.Date(nil), nullProto(), listType(tDate)},
		{[]civil.Date{d1, d2}, listProto(dateProto(d1), dateProto(d2)), listType(tDate)},
		{[]NullDate{{d1, true}, {civil.Date{}, false}}, listProto(dateProto(d1), nullProto()), listType(tDate)},
		// GenericColumnValue
		{GenericColumnValue{tString, stringProto("abc")}, stringProto("abc"), tString},
		{GenericColumnValue{tString, nullProto()}, nullProto(), tString},
		// not actually valid (stringProto inside int list), but demonstrates pass-through.
		{
			GenericColumnValue{
				Type:  listType(tInt),
				Value: listProto(intProto(5), nullProto(), stringProto("bcd")),
			},
			listProto(intProto(5), nullProto(), stringProto("bcd")),
			listType(tInt),
		},
		// placeholder
		{CommitTimestamp, stringProto(commitTimestampPlaceholderString), tTime},
	} {
		got, gotType, err := encodeValue(test.in)
		if err != nil {
			t.Fatalf("#%d: got error during encoding: %v, want nil", i, err)
		}
		if !testEqual(got, test.want) {
			t.Errorf("#%d: got encode result: %v, want %v", i, got, test.want)
		}
		if !testEqual(gotType, test.wantType) {
			t.Errorf("#%d: got encode type: %v, want %v", i, gotType, test.wantType)
		}
	}
}

type encodeTest struct {
	desc     string
	in       interface{}
	want     *proto3.Value
	wantType *sppb.Type
}

func checkStructEncoding(desc string, got *proto3.Value, gotType *sppb.Type,
	want *proto3.Value, wantType *sppb.Type, t *testing.T) {
	if !testEqual(got, want) {
		t.Errorf("Test %s: got encode result: %v, want %v", desc, got, want)
	}
	if !testEqual(gotType, wantType) {
		t.Errorf("Test %s: got encode type: %v, want %v", desc, gotType, wantType)
	}
}

// Testcase code
func encodeStructValue(test encodeTest, t *testing.T) {
	got, gotType, err := encodeValue(test.in)
	if err != nil {
		t.Fatalf("Test %s: got error during encoding: %v, want nil", test.desc, err)
	}
	checkStructEncoding(test.desc, got, gotType, test.want, test.wantType, t)
}

func TestEncodeStructValuePointers(t *testing.T) {
	type structf struct {
		F int `spanner:"ff2"`
	}
	nestedStructProto := structType(mkField("ff2", intType()))

	type testType struct {
		Stringf    string
		Structf    *structf
		ArrStructf []*structf
	}
	testTypeProto := structType(
		mkField("Stringf", stringType()),
		mkField("Structf", nestedStructProto),
		mkField("ArrStructf", listType(nestedStructProto)))

	for _, test := range []encodeTest{
		{
			"Pointer to Go struct with pointers-to-(array)-struct fields.",
			&testType{"hello", &structf{50}, []*structf{&structf{30}, &structf{40}}},
			listProto(
				stringProto("hello"),
				listProto(intProto(50)),
				listProto(
					listProto(intProto(30)),
					listProto(intProto(40)))),
			testTypeProto,
		},
		{
			"Nil pointer to Go struct representing a NULL struct value.",
			(*testType)(nil),
			nullProto(),
			testTypeProto,
		},
		{
			"Slice of pointers to Go structs with NULL and non-NULL elements.",
			[]*testType{
				(*testType)(nil),
				&testType{"hello", nil, []*structf{nil, &structf{40}}},
				&testType{"world", &structf{70}, nil},
			},
			listProto(
				nullProto(),
				listProto(
					stringProto("hello"),
					nullProto(),
					listProto(nullProto(), listProto(intProto(40)))),
				listProto(
					stringProto("world"),
					listProto(intProto(70)),
					nullProto())),
			listType(testTypeProto),
		},
		{
			"Nil slice of pointers to structs representing a NULL array of structs.",
			[]*testType(nil),
			nullProto(),
			listType(testTypeProto),
		},
		{
			"Empty slice of pointers to structs representing an empty array of structs.",
			[]*testType{},
			listProto(),
			listType(testTypeProto),
		},
	} {
		encodeStructValue(test, t)
	}
}

func TestEncodeStructValueErrors(t *testing.T) {
	type Embedded struct {
		A int
	}
	type embedded struct {
		B bool
	}
	x := 0

	for _, test := range []struct {
		desc    string
		in      interface{}
		wantErr error
	}{
		{
			"Unsupported embedded fields.",
			struct{ Embedded }{Embedded{10}},
			errUnsupportedEmbeddedStructFields("Embedded"),
		},
		{
			"Unsupported pointer to embedded fields.",
			struct{ *Embedded }{&Embedded{10}},
			errUnsupportedEmbeddedStructFields("Embedded"),
		},
		{
			"Unsupported embedded + unexported fields.",
			struct {
				int
				*bool
				embedded
			}{10, nil, embedded{false}},
			errUnsupportedEmbeddedStructFields("int"),
		},
		{
			"Unsupported type.",
			(**struct{})(nil),
			errEncoderUnsupportedType((**struct{})(nil)),
		},
		{
			"Unsupported type.",
			3,
			errEncoderUnsupportedType(3),
		},
		{
			"Unsupported type.",
			&x,
			errEncoderUnsupportedType(&x),
		},
	} {
		_, _, got := encodeStruct(test.in)
		if got == nil || !testEqual(test.wantErr, got) {
			t.Errorf("Test: %s, expected error %v during decoding, got %v", test.desc, test.wantErr, got)
		}
	}
}

func TestEncodeStructValueArrayStructFields(t *testing.T) {
	type structf struct {
		Intff int
	}

	structfType := structType(mkField("Intff", intType()))
	for _, test := range []encodeTest{
		{
			"Unnamed array-of-struct-typed field.",
			struct {
				Intf       int
				ArrStructf []structf `spanner:""`
			}{10, []structf{structf{1}, structf{2}}},
			listProto(
				intProto(10),
				listProto(
					listProto(intProto(1)),
					listProto(intProto(2)))),
			structType(
				mkField("Intf", intType()),
				mkField("", listType(structfType))),
		},
		{
			"Null array-of-struct-typed field.",
			struct {
				Intf       int
				ArrStructf []structf
			}{10, []structf(nil)},
			listProto(intProto(10), nullProto()),
			structType(
				mkField("Intf", intType()),
				mkField("ArrStructf", listType(structfType))),
		},
		{
			"Array-of-struct-typed field representing empty array.",
			struct {
				Intf       int
				ArrStructf []structf
			}{10, []structf{}},
			listProto(intProto(10), listProto([]*proto3.Value{}...)),
			structType(
				mkField("Intf", intType()),
				mkField("ArrStructf", listType(structfType))),
		},
		{
			"Array-of-struct-typed field with nullable struct elements.",
			struct {
				Intf       int
				ArrStructf []*structf
			}{
				10,
				[]*structf{(*structf)(nil), &structf{1}},
			},
			listProto(
				intProto(10),
				listProto(
					nullProto(),
					listProto(intProto(1)))),
			structType(
				mkField("Intf", intType()),
				mkField("ArrStructf", listType(structfType))),
		},
	} {
		encodeStructValue(test, t)
	}
}

func TestEncodeStructValueStructFields(t *testing.T) {
	type structf struct {
		Intff int
	}
	structfType := structType(mkField("Intff", intType()))
	for _, test := range []encodeTest{
		{
			"Named struct-type field.",
			struct {
				Intf    int
				Structf structf
			}{10, structf{10}},
			listProto(intProto(10), listProto(intProto(10))),
			structType(
				mkField("Intf", intType()),
				mkField("Structf", structfType)),
		},
		{
			"Unnamed struct-type field.",
			struct {
				Intf    int
				Structf structf `spanner:""`
			}{10, structf{10}},
			listProto(intProto(10), listProto(intProto(10))),
			structType(
				mkField("Intf", intType()),
				mkField("", structfType)),
		},
		{
			"Duplicate struct-typed field.",
			struct {
				Structf1 structf `spanner:""`
				Structf2 structf `spanner:""`
			}{structf{10}, structf{20}},
			listProto(listProto(intProto(10)), listProto(intProto(20))),
			structType(
				mkField("", structfType),
				mkField("", structfType)),
		},
		{
			"Null struct-typed field.",
			struct {
				Intf    int
				Structf *structf
			}{10, nil},
			listProto(intProto(10), nullProto()),
			structType(
				mkField("Intf", intType()),
				mkField("Structf", structfType)),
		},
		{
			"Empty struct-typed field.",
			struct {
				Intf    int
				Structf struct{}
			}{10, struct{}{}},
			listProto(intProto(10), listProto([]*proto3.Value{}...)),
			structType(
				mkField("Intf", intType()),
				mkField("Structf", structType([]*sppb.StructType_Field{}...))),
		},
	} {
		encodeStructValue(test, t)
	}
}

func TestEncodeStructValueFieldNames(t *testing.T) {
	type embedded struct {
		B bool
	}

	for _, test := range []encodeTest{
		{
			"Duplicate fields.",
			struct {
				Field1    int `spanner:"field"`
				DupField1 int `spanner:"field"`
			}{10, 20},
			listProto(intProto(10), intProto(20)),
			structType(
				mkField("field", intType()),
				mkField("field", intType())),
		},
		{
			"Duplicate Fields (different types).",
			struct {
				IntField    int    `spanner:"field"`
				StringField string `spanner:"field"`
			}{10, "abc"},
			listProto(intProto(10), stringProto("abc")),
			structType(
				mkField("field", intType()),
				mkField("field", stringType())),
		},
		{
			"Duplicate unnamed fields.",
			struct {
				Dup  int `spanner:""`
				Dup1 int `spanner:""`
			}{10, 20},
			listProto(intProto(10), intProto(20)),
			structType(
				mkField("", intType()),
				mkField("", intType())),
		},
		{
			"Named and unnamed fields.",
			struct {
				Field  string
				Field1 int    `spanner:""`
				Field2 string `spanner:"field"`
			}{"abc", 10, "def"},
			listProto(stringProto("abc"), intProto(10), stringProto("def")),
			structType(
				mkField("Field", stringType()),
				mkField("", intType()),
				mkField("field", stringType())),
		},
		{
			"Ignored unexported fields.",
			struct {
				Field  int
				field  bool
				Field1 string `spanner:"field"`
			}{10, false, "abc"},
			listProto(intProto(10), stringProto("abc")),
			structType(
				mkField("Field", intType()),
				mkField("field", stringType())),
		},
		{
			"Ignored unexported struct/slice fields.",
			struct {
				a      []*embedded
				b      []embedded
				c      embedded
				d      *embedded
				Field1 string `spanner:"field"`
			}{nil, nil, embedded{}, nil, "def"},
			listProto(stringProto("def")),
			structType(
				mkField("field", stringType())),
		},
	} {
		encodeStructValue(test, t)
	}
}

func TestEncodeStructValueBasicFields(t *testing.T) {
	StructTypeProto := structType(
		mkField("Stringf", stringType()),
		mkField("Intf", intType()),
		mkField("Boolf", boolType()),
		mkField("Floatf", floatType()),
		mkField("Bytef", bytesType()),
		mkField("Timef", timeType()),
		mkField("Datef", dateType()))

	for _, test := range []encodeTest{
		{
			"Basic types.",
			struct {
				Stringf string
				Intf    int
				Boolf   bool
				Floatf  float64
				Bytef   []byte
				Timef   time.Time
				Datef   civil.Date
			}{"abc", 300, false, 3.45, []byte("foo"), t1, d1},
			listProto(
				stringProto("abc"),
				intProto(300),
				boolProto(false),
				floatProto(3.45),
				bytesProto([]byte("foo")),
				timeProto(t1),
				dateProto(d1)),
			StructTypeProto,
		},
		{
			"Basic types null values.",
			struct {
				Stringf NullString
				Intf    NullInt64
				Boolf   NullBool
				Floatf  NullFloat64
				Bytef   []byte
				Timef   NullTime
				Datef   NullDate
			}{
				NullString{"abc", false},
				NullInt64{4, false},
				NullBool{false, false},
				NullFloat64{5.6, false},
				nil,
				NullTime{t1, false},
				NullDate{d1, false},
			},
			listProto(
				nullProto(),
				nullProto(),
				nullProto(),
				nullProto(),
				nullProto(),
				nullProto(),
				nullProto()),
			StructTypeProto,
		},
	} {
		encodeStructValue(test, t)
	}
}

func TestEncodeStructValueArrayFields(t *testing.T) {
	StructTypeProto := structType(
		mkField("Stringf", listType(stringType())),
		mkField("Intf", listType(intType())),
		mkField("Int64f", listType(intType())),
		mkField("Boolf", listType(boolType())),
		mkField("Floatf", listType(floatType())),
		mkField("Bytef", listType(bytesType())),
		mkField("Timef", listType(timeType())),
		mkField("Datef", listType(dateType())))

	for _, test := range []encodeTest{
		{
			"Arrays of basic types with non-nullable elements",
			struct {
				Stringf []string
				Intf    []int
				Int64f  []int64
				Boolf   []bool
				Floatf  []float64
				Bytef   [][]byte
				Timef   []time.Time
				Datef   []civil.Date
			}{
				[]string{"abc", "def"},
				[]int{4, 67},
				[]int64{5, 68},
				[]bool{false, true},
				[]float64{3.45, 0.93},
				[][]byte{[]byte("foo"), nil},
				[]time.Time{t1, t2},
				[]civil.Date{d1, d2},
			},
			listProto(
				listProto(stringProto("abc"), stringProto("def")),
				listProto(intProto(4), intProto(67)),
				listProto(intProto(5), intProto(68)),
				listProto(boolProto(false), boolProto(true)),
				listProto(floatProto(3.45), floatProto(0.93)),
				listProto(bytesProto([]byte("foo")), nullProto()),
				listProto(timeProto(t1), timeProto(t2)),
				listProto(dateProto(d1), dateProto(d2))),
			StructTypeProto,
		},
		{
			"Arrays of basic types with nullable elements.",
			struct {
				Stringf []NullString
				Intf    []NullInt64
				Int64f  []NullInt64
				Boolf   []NullBool
				Floatf  []NullFloat64
				Bytef   [][]byte
				Timef   []NullTime
				Datef   []NullDate
			}{
				[]NullString{NullString{"abc", false}, NullString{"def", true}},
				[]NullInt64{NullInt64{4, false}, NullInt64{67, true}},
				[]NullInt64{NullInt64{5, false}, NullInt64{68, true}},
				[]NullBool{NullBool{true, false}, NullBool{false, true}},
				[]NullFloat64{NullFloat64{3.45, false}, NullFloat64{0.93, true}},
				[][]byte{[]byte("foo"), nil},
				[]NullTime{NullTime{t1, false}, NullTime{t2, true}},
				[]NullDate{NullDate{d1, false}, NullDate{d2, true}},
			},
			listProto(
				listProto(nullProto(), stringProto("def")),
				listProto(nullProto(), intProto(67)),
				listProto(nullProto(), intProto(68)),
				listProto(nullProto(), boolProto(false)),
				listProto(nullProto(), floatProto(0.93)),
				listProto(bytesProto([]byte("foo")), nullProto()),
				listProto(nullProto(), timeProto(t2)),
				listProto(nullProto(), dateProto(d2))),
			StructTypeProto,
		},
		{
			"Null arrays of basic types.",
			struct {
				Stringf []NullString
				Intf    []NullInt64
				Int64f  []NullInt64
				Boolf   []NullBool
				Floatf  []NullFloat64
				Bytef   [][]byte
				Timef   []NullTime
				Datef   []NullDate
			}{
				nil,
				nil,
				nil,
				nil,
				nil,
				nil,
				nil,
				nil,
			},
			listProto(
				nullProto(),
				nullProto(),
				nullProto(),
				nullProto(),
				nullProto(),
				nullProto(),
				nullProto(),
				nullProto()),
			StructTypeProto,
		},
	} {
		encodeStructValue(test, t)
	}
}

// Test decoding Values.
func TestDecodeValue(t *testing.T) {
	for i, test := range []struct {
		in   *proto3.Value
		t    *sppb.Type
		want interface{}
		fail bool
	}{
		// STRING
		{stringProto("abc"), stringType(), "abc", false},
		{nullProto(), stringType(), "abc", true},
		{stringProto("abc"), stringType(), NullString{"abc", true}, false},
		{nullProto(), stringType(), NullString{}, false},
		// STRING ARRAY with []NullString
		{
			listProto(stringProto("abc"), nullProto(), stringProto("bcd")),
			listType(stringType()),
			[]NullString{{"abc", true}, {}, {"bcd", true}},
			false,
		},
		{nullProto(), listType(stringType()), []NullString(nil), false},
		// STRING ARRAY with []string
		{
			listProto(stringProto("abc"), stringProto("bcd")),
			listType(stringType()),
			[]string{"abc", "bcd"},
			false,
		},
		// BYTES
		{bytesProto([]byte("ab")), bytesType(), []byte("ab"), false},
		{nullProto(), bytesType(), []byte(nil), false},
		// BYTES ARRAY
		{listProto(bytesProto([]byte("ab")), nullProto()), listType(bytesType()), [][]byte{[]byte("ab"), nil}, false},
		{nullProto(), listType(bytesType()), [][]byte(nil), false},
		//INT64
		{intProto(15), intType(), int64(15), false},
		{nullProto(), intType(), int64(0), true},
		{intProto(15), intType(), NullInt64{15, true}, false},
		{nullProto(), intType(), NullInt64{}, false},
		// INT64 ARRAY with []NullInt64
		{listProto(intProto(91), nullProto(), intProto(87)), listType(intType()), []NullInt64{{91, true}, {}, {87, true}}, false},
		{nullProto(), listType(intType()), []NullInt64(nil), false},
		// INT64 ARRAY with []int64
		{listProto(intProto(91), intProto(87)), listType(intType()), []int64{91, 87}, false},
		// BOOL
		{boolProto(true), boolType(), true, false},
		{nullProto(), boolType(), true, true},
		{boolProto(true), boolType(), NullBool{true, true}, false},
		{nullProto(), boolType(), NullBool{}, false},
		// BOOL ARRAY with []NullBool
		{listProto(boolProto(true), boolProto(false), nullProto()), listType(boolType()), []NullBool{{true, true}, {false, true}, {}}, false},
		{nullProto(), listType(boolType()), []NullBool(nil), false},
		// BOOL ARRAY with []bool
		{listProto(boolProto(true), boolProto(false)), listType(boolType()), []bool{true, false}, false},
		// FLOAT64
		{floatProto(3.14), floatType(), 3.14, false},
		{nullProto(), floatType(), 0.00, true},
		{floatProto(3.14), floatType(), NullFloat64{3.14, true}, false},
		{nullProto(), floatType(), NullFloat64{}, false},
		// FLOAT64 ARRAY with []NullFloat64
		{
			listProto(floatProto(math.Inf(1)), floatProto(math.Inf(-1)), nullProto(), floatProto(3.1)),
			listType(floatType()),
			[]NullFloat64{{math.Inf(1), true}, {math.Inf(-1), true}, {}, {3.1, true}},
			false,
		},
		{nullProto(), listType(floatType()), []NullFloat64(nil), false},
		// FLOAT64 ARRAY with []float64
		{
			listProto(floatProto(math.Inf(1)), floatProto(math.Inf(-1)), floatProto(3.1)),
			listType(floatType()),
			[]float64{math.Inf(1), math.Inf(-1), 3.1},
			false,
		},
		// TIMESTAMP
		{timeProto(t1), timeType(), t1, false},
		{timeProto(t1), timeType(), NullTime{t1, true}, false},
		{nullProto(), timeType(), NullTime{}, false},
		// TIMESTAMP ARRAY with []NullTime
		{listProto(timeProto(t1), timeProto(t2), timeProto(t3), nullProto()), listType(timeType()), []NullTime{{t1, true}, {t2, true}, {t3, true}, {}}, false},
		{nullProto(), listType(timeType()), []NullTime(nil), false},
		// TIMESTAMP ARRAY with []time.Time
		{listProto(timeProto(t1), timeProto(t2), timeProto(t3)), listType(timeType()), []time.Time{t1, t2, t3}, false},
		// DATE
		{dateProto(d1), dateType(), d1, false},
		{dateProto(d1), dateType(), NullDate{d1, true}, false},
		{nullProto(), dateType(), NullDate{}, false},
		// DATE ARRAY with []NullDate
		{listProto(dateProto(d1), dateProto(d2), nullProto()), listType(dateType()), []NullDate{{d1, true}, {d2, true}, {}}, false},
		{nullProto(), listType(dateType()), []NullDate(nil), false},
		// DATE ARRAY with []civil.Date
		{listProto(dateProto(d1), dateProto(d2)), listType(dateType()), []civil.Date{d1, d2}, false},
		// STRUCT ARRAY
		// STRUCT schema is equal to the following Go struct:
		// type s struct {
		//     Col1 NullInt64
		//     Col2 []struct {
		//         SubCol1 float64
		//         SubCol2 string
		//     }
		// }
		{
			in: listProto(
				listProto(
					intProto(3),
					listProto(
						listProto(floatProto(3.14), stringProto("this")),
						listProto(floatProto(0.57), stringProto("siht")),
					),
				),
				listProto(
					nullProto(),
					nullProto(),
				),
				nullProto(),
			),
			t: listType(
				structType(
					mkField("Col1", intType()),
					mkField(
						"Col2",
						listType(
							structType(
								mkField("SubCol1", floatType()),
								mkField("SubCol2", stringType()),
							),
						),
					),
				),
			),
			want: []NullRow{
				{
					Row: Row{
						fields: []*sppb.StructType_Field{
							mkField("Col1", intType()),
							mkField(
								"Col2",
								listType(
									structType(
										mkField("SubCol1", floatType()),
										mkField("SubCol2", stringType()),
									),
								),
							),
						},
						vals: []*proto3.Value{
							intProto(3),
							listProto(
								listProto(floatProto(3.14), stringProto("this")),
								listProto(floatProto(0.57), stringProto("siht")),
							),
						},
					},
					Valid: true,
				},
				{
					Row: Row{
						fields: []*sppb.StructType_Field{
							mkField("Col1", intType()),
							mkField(
								"Col2",
								listType(
									structType(
										mkField("SubCol1", floatType()),
										mkField("SubCol2", stringType()),
									),
								),
							),
						},
						vals: []*proto3.Value{
							nullProto(),
							nullProto(),
						},
					},
					Valid: true,
				},
				{},
			},
			fail: false,
		},
		{
			in: listProto(
				listProto(
					intProto(3),
					listProto(
						listProto(floatProto(3.14), stringProto("this")),
						listProto(floatProto(0.57), stringProto("siht")),
					),
				),
				listProto(
					nullProto(),
					nullProto(),
				),
				nullProto(),
			),
			t: listType(
				structType(
					mkField("Col1", intType()),
					mkField(
						"Col2",
						listType(
							structType(
								mkField("SubCol1", floatType()),
								mkField("SubCol2", stringType()),
							),
						),
					),
				),
			),
			want: []*struct {
				Col1      NullInt64
				StructCol []*struct {
					SubCol1 NullFloat64
					SubCol2 string
				} `spanner:"Col2"`
			}{
				{
					Col1: NullInt64{3, true},
					StructCol: []*struct {
						SubCol1 NullFloat64
						SubCol2 string
					}{
						{
							SubCol1: NullFloat64{3.14, true},
							SubCol2: "this",
						},
						{
							SubCol1: NullFloat64{0.57, true},
							SubCol2: "siht",
						},
					},
				},
				{
					Col1: NullInt64{},
					StructCol: []*struct {
						SubCol1 NullFloat64
						SubCol2 string
					}(nil),
				},
				nil,
			},
			fail: false,
		},
		// GenericColumnValue
		{stringProto("abc"), stringType(), GenericColumnValue{stringType(), stringProto("abc")}, false},
		{nullProto(), stringType(), GenericColumnValue{stringType(), nullProto()}, false},
		// not actually valid (stringProto inside int list), but demonstrates pass-through.
		{
			in: listProto(intProto(5), nullProto(), stringProto("bcd")),
			t:  listType(intType()),
			want: GenericColumnValue{
				Type:  listType(intType()),
				Value: listProto(intProto(5), nullProto(), stringProto("bcd")),
			},
			fail: false,
		},
	} {
		gotp := reflect.New(reflect.TypeOf(test.want))
		if err := decodeValue(test.in, test.t, gotp.Interface()); err != nil {
			if !test.fail {
				t.Errorf("%d: cannot decode %v(%v): %v", i, test.in, test.t, err)
			}
			continue
		}
		if test.fail {
			t.Errorf("%d: decoding %v(%v) succeeds unexpectedly, want error", i, test.in, test.t)
			continue
		}
		got := reflect.Indirect(gotp).Interface()
		if !testEqual(got, test.want) {
			t.Errorf("%d: unexpected decoding result - got %v, want %v", i, got, test.want)
			continue
		}
	}
}

// Test error cases for decodeValue.
func TestDecodeValueErrors(t *testing.T) {
	var s string
	for i, test := range []struct {
		in *proto3.Value
		t  *sppb.Type
		v  interface{}
	}{
		{nullProto(), stringType(), nil},
		{nullProto(), stringType(), 1},
		{timeProto(t1), timeType(), &s},
	} {
		err := decodeValue(test.in, test.t, test.v)
		if err == nil {
			t.Errorf("#%d: want error, got nil", i)
		}
	}
}

// Test NaN encoding/decoding.
func TestNaN(t *testing.T) {
	// Decode NaN value.
	f := 0.0
	nf := NullFloat64{}
	// To float64
	if err := decodeValue(floatProto(math.NaN()), floatType(), &f); err != nil {
		t.Errorf("decodeValue returns %q for %v, want nil", err, floatProto(math.NaN()))
	}
	if !math.IsNaN(f) {
		t.Errorf("f = %v, want %v", f, math.NaN())
	}
	// To NullFloat64
	if err := decodeValue(floatProto(math.NaN()), floatType(), &nf); err != nil {
		t.Errorf("decodeValue returns %q for %v, want nil", err, floatProto(math.NaN()))
	}
	if !math.IsNaN(nf.Float64) || !nf.Valid {
		t.Errorf("f = %v, want %v", f, NullFloat64{math.NaN(), true})
	}
	// Encode NaN value
	// From float64
	v, _, err := encodeValue(math.NaN())
	if err != nil {
		t.Errorf("encodeValue returns %q for NaN, want nil", err)
	}
	x, ok := v.GetKind().(*proto3.Value_NumberValue)
	if !ok {
		t.Errorf("incorrect type for v.GetKind(): %T, want *proto3.Value_NumberValue", v.GetKind())
	}
	if !math.IsNaN(x.NumberValue) {
		t.Errorf("x.NumberValue = %v, want %v", x.NumberValue, math.NaN())
	}
	// From NullFloat64
	v, _, err = encodeValue(NullFloat64{math.NaN(), true})
	if err != nil {
		t.Errorf("encodeValue returns %q for NaN, want nil", err)
	}
	x, ok = v.GetKind().(*proto3.Value_NumberValue)
	if !ok {
		t.Errorf("incorrect type for v.GetKind(): %T, want *proto3.Value_NumberValue", v.GetKind())
	}
	if !math.IsNaN(x.NumberValue) {
		t.Errorf("x.NumberValue = %v, want %v", x.NumberValue, math.NaN())
	}
}

func TestGenericColumnValue(t *testing.T) {
	for _, test := range []struct {
		in   GenericColumnValue
		want interface{}
		fail bool
	}{
		{GenericColumnValue{stringType(), stringProto("abc")}, "abc", false},
		{GenericColumnValue{stringType(), stringProto("abc")}, 5, true},
		{GenericColumnValue{listType(intType()), listProto(intProto(91), nullProto(), intProto(87))}, []NullInt64{{91, true}, {}, {87, true}}, false},
		{GenericColumnValue{intType(), intProto(42)}, GenericColumnValue{intType(), intProto(42)}, false}, // trippy! :-)
	} {
		gotp := reflect.New(reflect.TypeOf(test.want))
		if err := test.in.Decode(gotp.Interface()); err != nil {
			if !test.fail {
				t.Errorf("cannot decode %v to %v: %v", test.in, test.want, err)
			}
			continue
		}
		if test.fail {
			t.Errorf("decoding %v to %v succeeds unexpectedly", test.in, test.want)
		}

		// Test we can go backwards as well.
		v, err := newGenericColumnValue(test.want)
		if err != nil {
			t.Errorf("NewGenericColumnValue failed: %v", err)
			continue
		}
		if !testEqual(*v, test.in) {
			t.Errorf("unexpected encode result - got %v, want %v", v, test.in)
		}
	}
}

func TestDecodeStruct(t *testing.T) {
	stype := &sppb.StructType{Fields: []*sppb.StructType_Field{
		{Name: "Id", Type: stringType()},
		{Name: "Time", Type: timeType()},
	}}
	lv := listValueProto(stringProto("id"), timeProto(t1))

	type (
		S1 struct {
			Id   string
			Time time.Time
		}
		S2 struct {
			Id   string
			Time string
		}
	)
	var (
		s1 S1
		s2 S2
	)

	for i, test := range []struct {
		ptr  interface{}
		want interface{}
		fail bool
	}{
		{
			ptr:  &s1,
			want: &S1{Id: "id", Time: t1},
		},
		{
			ptr:  &s2,
			fail: true,
		},
	} {
		err := decodeStruct(stype, lv, test.ptr)
		if (err != nil) != test.fail {
			t.Errorf("#%d: got error %v, wanted fail: %v", i, err, test.fail)
		}
		if err == nil && !testEqual(test.ptr, test.want) {
			t.Errorf("#%d: got %+v, want %+v", i, test.ptr, test.want)
		}
	}
}
