// Copyright (C) MongoDB, Inc. 2017-present.
//
// 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

package bsoncodec

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"math"
	"net/url"
	"reflect"
	"strings"
	"testing"
	"time"

	"github.com/google/go-cmp/cmp"
	"go.mongodb.org/mongo-driver/bson/bsonrw"
	"go.mongodb.org/mongo-driver/bson/bsonrw/bsonrwtest"
	"go.mongodb.org/mongo-driver/bson/bsontype"
	"go.mongodb.org/mongo-driver/bson/primitive"
	"go.mongodb.org/mongo-driver/internal/testutil/assert"
	"go.mongodb.org/mongo-driver/x/bsonx/bsoncore"
)

var (
	defaultTestStructCodec = newDefaultStructCodec()
)

func TestDefaultValueDecoders(t *testing.T) {
	var dvd DefaultValueDecoders
	var wrong = func(string, string) string { return "wrong" }

	type mybool bool
	type myint8 int8
	type myint16 int16
	type myint32 int32
	type myint64 int64
	type myint int
	type myuint8 uint8
	type myuint16 uint16
	type myuint32 uint32
	type myuint64 uint64
	type myuint uint
	type myfloat32 float32
	type myfloat64 float64
	type mystring string
	type mystruct struct{}

	const cansetreflectiontest = "cansetreflectiontest"
	const cansettest = "cansettest"

	now := time.Now().Truncate(time.Millisecond)
	d128 := primitive.NewDecimal128(12345, 67890)
	var pbool = func(b bool) *bool { return &b }
	var pi32 = func(i32 int32) *int32 { return &i32 }
	var pi64 = func(i64 int64) *int64 { return &i64 }

	type subtest struct {
		name   string
		val    interface{}
		dctx   *DecodeContext
		llvrw  *bsonrwtest.ValueReaderWriter
		invoke bsonrwtest.Invoked
		err    error
	}

	testCases := []struct {
		name     string
		vd       ValueDecoder
		subtests []subtest
	}{
		{
			"BooleanDecodeValue",
			ValueDecoderFunc(dvd.BooleanDecodeValue),
			[]subtest{
				{
					"wrong type",
					wrong,
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Boolean},
					bsonrwtest.Nothing,
					ValueDecoderError{Name: "BooleanDecodeValue", Kinds: []reflect.Kind{reflect.Bool}, Received: reflect.ValueOf(wrong)},
				},
				{
					"type not boolean",
					bool(false),
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.String},
					bsonrwtest.Nothing,
					fmt.Errorf("cannot decode %v into a boolean", bsontype.String),
				},
				{
					"fast path",
					bool(true),
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Boolean, Return: bool(true)},
					bsonrwtest.ReadBoolean,
					nil,
				},
				{
					"reflection path",
					mybool(true),
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Boolean, Return: bool(true)},
					bsonrwtest.ReadBoolean,
					nil,
				},
				{
					"reflection path error",
					mybool(true),
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Boolean, Return: bool(true), Err: errors.New("ReadBoolean Error"), ErrAfter: bsonrwtest.ReadBoolean},
					bsonrwtest.ReadBoolean, errors.New("ReadBoolean Error"),
				},
				{
					"can set false",
					cansettest,
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Boolean},
					bsonrwtest.Nothing,
					ValueDecoderError{Name: "BooleanDecodeValue", Kinds: []reflect.Kind{reflect.Bool}},
				},
				{
					"decode null",
					mybool(false),
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Null},
					bsonrwtest.ReadNull,
					nil,
				},
				{
					"decode undefined",
					mybool(false),
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Undefined},
					bsonrwtest.ReadUndefined,
					nil,
				},
			},
		},
		{
			"IntDecodeValue",
			ValueDecoderFunc(dvd.IntDecodeValue),
			[]subtest{
				{
					"wrong type",
					wrong,
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Int32, Return: int32(0)},
					bsonrwtest.ReadInt32,
					ValueDecoderError{
						Name:     "IntDecodeValue",
						Kinds:    []reflect.Kind{reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int},
						Received: reflect.ValueOf(wrong),
					},
				},
				{
					"type not int32/int64",
					0,
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.String},
					bsonrwtest.Nothing,
					fmt.Errorf("cannot decode %v into an integer type", bsontype.String),
				},
				{
					"ReadInt32 error",
					0,
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Int32, Return: int32(0), Err: errors.New("ReadInt32 error"), ErrAfter: bsonrwtest.ReadInt32},
					bsonrwtest.ReadInt32,
					errors.New("ReadInt32 error"),
				},
				{
					"ReadInt64 error",
					0,
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Int64, Return: int64(0), Err: errors.New("ReadInt64 error"), ErrAfter: bsonrwtest.ReadInt64},
					bsonrwtest.ReadInt64,
					errors.New("ReadInt64 error"),
				},
				{
					"ReadDouble error",
					0,
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Double, Return: float64(0), Err: errors.New("ReadDouble error"), ErrAfter: bsonrwtest.ReadDouble},
					bsonrwtest.ReadDouble,
					errors.New("ReadDouble error"),
				},
				{
					"ReadDouble", int64(3), &DecodeContext{},
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Double, Return: float64(3.00)}, bsonrwtest.ReadDouble,
					nil,
				},
				{
					"ReadDouble (truncate)", int64(3), &DecodeContext{Truncate: true},
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Double, Return: float64(3.14)}, bsonrwtest.ReadDouble,
					nil,
				},
				{
					"ReadDouble (no truncate)", int64(0), nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Double, Return: float64(3.14)}, bsonrwtest.ReadDouble,
					errCannotTruncate,
				},
				{
					"ReadDouble overflows int64", int64(0), nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Double, Return: math.MaxFloat64}, bsonrwtest.ReadDouble,
					fmt.Errorf("%g overflows int64", math.MaxFloat64),
				},
				{"int8/fast path", int8(127), nil, &bsonrwtest.ValueReaderWriter{BSONType: bsontype.Int32, Return: int32(127)}, bsonrwtest.ReadInt32, nil},
				{"int16/fast path", int16(32676), nil, &bsonrwtest.ValueReaderWriter{BSONType: bsontype.Int32, Return: int32(32676)}, bsonrwtest.ReadInt32, nil},
				{"int32/fast path", int32(1234), nil, &bsonrwtest.ValueReaderWriter{BSONType: bsontype.Int32, Return: int32(1234)}, bsonrwtest.ReadInt32, nil},
				{"int64/fast path", int64(1234), nil, &bsonrwtest.ValueReaderWriter{BSONType: bsontype.Int64, Return: int64(1234)}, bsonrwtest.ReadInt64, nil},
				{"int/fast path", int(1234), nil, &bsonrwtest.ValueReaderWriter{BSONType: bsontype.Int64, Return: int64(1234)}, bsonrwtest.ReadInt64, nil},
				{
					"int8/fast path - nil", (*int8)(nil), nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Int32, Return: int32(0)}, bsonrwtest.ReadInt32,
					ValueDecoderError{
						Name:     "IntDecodeValue",
						Kinds:    []reflect.Kind{reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int},
						Received: reflect.ValueOf((*int8)(nil)),
					},
				},
				{
					"int16/fast path - nil", (*int16)(nil), nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Int32, Return: int32(0)}, bsonrwtest.ReadInt32,
					ValueDecoderError{
						Name:     "IntDecodeValue",
						Kinds:    []reflect.Kind{reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int},
						Received: reflect.ValueOf((*int16)(nil)),
					},
				},
				{
					"int32/fast path - nil", (*int32)(nil), nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Int32, Return: int32(0)}, bsonrwtest.ReadInt32,
					ValueDecoderError{
						Name:     "IntDecodeValue",
						Kinds:    []reflect.Kind{reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int},
						Received: reflect.ValueOf((*int32)(nil)),
					},
				},
				{
					"int64/fast path - nil", (*int64)(nil), nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Int32, Return: int32(0)}, bsonrwtest.ReadInt32,
					ValueDecoderError{
						Name:     "IntDecodeValue",
						Kinds:    []reflect.Kind{reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int},
						Received: reflect.ValueOf((*int64)(nil)),
					},
				},
				{
					"int/fast path - nil", (*int)(nil), nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Int32, Return: int32(0)}, bsonrwtest.ReadInt32,
					ValueDecoderError{
						Name:     "IntDecodeValue",
						Kinds:    []reflect.Kind{reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int},
						Received: reflect.ValueOf((*int)(nil)),
					},
				},
				{
					"int8/fast path - overflow", int8(0), nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Int32, Return: int32(129)}, bsonrwtest.ReadInt32,
					fmt.Errorf("%d overflows int8", 129),
				},
				{
					"int16/fast path - overflow", int16(0), nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Int32, Return: int32(32768)}, bsonrwtest.ReadInt32,
					fmt.Errorf("%d overflows int16", 32768),
				},
				{
					"int32/fast path - overflow", int32(0), nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Int64, Return: int64(2147483648)}, bsonrwtest.ReadInt64,
					fmt.Errorf("%d overflows int32", int64(2147483648)),
				},
				{
					"int8/fast path - overflow (negative)", int8(0), nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Int32, Return: int32(-129)}, bsonrwtest.ReadInt32,
					fmt.Errorf("%d overflows int8", -129),
				},
				{
					"int16/fast path - overflow (negative)", int16(0), nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Int32, Return: int32(-32769)}, bsonrwtest.ReadInt32,
					fmt.Errorf("%d overflows int16", -32769),
				},
				{
					"int32/fast path - overflow (negative)", int32(0), nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Int64, Return: int64(-2147483649)}, bsonrwtest.ReadInt64,
					fmt.Errorf("%d overflows int32", int64(-2147483649)),
				},
				{
					"int8/reflection path", myint8(127), nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Int32, Return: int32(127)}, bsonrwtest.ReadInt32,
					nil,
				},
				{
					"int16/reflection path", myint16(255), nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Int32, Return: int32(255)}, bsonrwtest.ReadInt32,
					nil,
				},
				{
					"int32/reflection path", myint32(511), nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Int32, Return: int32(511)}, bsonrwtest.ReadInt32,
					nil,
				},
				{
					"int64/reflection path", myint64(1023), nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Int32, Return: int32(1023)}, bsonrwtest.ReadInt32,
					nil,
				},
				{
					"int/reflection path", myint(2047), nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Int32, Return: int32(2047)}, bsonrwtest.ReadInt32,
					nil,
				},
				{
					"int8/reflection path - overflow", myint8(0), nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Int32, Return: int32(129)}, bsonrwtest.ReadInt32,
					fmt.Errorf("%d overflows int8", 129),
				},
				{
					"int16/reflection path - overflow", myint16(0), nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Int32, Return: int32(32768)}, bsonrwtest.ReadInt32,
					fmt.Errorf("%d overflows int16", 32768),
				},
				{
					"int32/reflection path - overflow", myint32(0), nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Int64, Return: int64(2147483648)}, bsonrwtest.ReadInt64,
					fmt.Errorf("%d overflows int32", int64(2147483648)),
				},
				{
					"int8/reflection path - overflow (negative)", myint8(0), nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Int32, Return: int32(-129)}, bsonrwtest.ReadInt32,
					fmt.Errorf("%d overflows int8", -129),
				},
				{
					"int16/reflection path - overflow (negative)", myint16(0), nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Int32, Return: int32(-32769)}, bsonrwtest.ReadInt32,
					fmt.Errorf("%d overflows int16", -32769),
				},
				{
					"int32/reflection path - overflow (negative)", myint32(0), nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Int64, Return: int64(-2147483649)}, bsonrwtest.ReadInt64,
					fmt.Errorf("%d overflows int32", int64(-2147483649)),
				},
				{
					"can set false",
					cansettest,
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Int32, Return: int32(0)},
					bsonrwtest.Nothing,
					ValueDecoderError{
						Name:  "IntDecodeValue",
						Kinds: []reflect.Kind{reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int},
					},
				},
				{
					"decode null",
					myint(0),
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Null},
					bsonrwtest.ReadNull,
					nil,
				},
				{
					"decode undefined",
					myint(0),
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Undefined},
					bsonrwtest.ReadUndefined,
					nil,
				},
			},
		},
		{
			"defaultUIntCodec.DecodeValue",
			defaultUIntCodec,
			[]subtest{
				{
					"wrong type",
					wrong,
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Int32, Return: int32(0)},
					bsonrwtest.ReadInt32,
					ValueDecoderError{
						Name:     "UintDecodeValue",
						Kinds:    []reflect.Kind{reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint},
						Received: reflect.ValueOf(wrong),
					},
				},
				{
					"type not int32/int64",
					0,
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.String},
					bsonrwtest.Nothing,
					fmt.Errorf("cannot decode %v into an integer type", bsontype.String),
				},
				{
					"ReadInt32 error",
					uint(0),
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Int32, Return: int32(0), Err: errors.New("ReadInt32 error"), ErrAfter: bsonrwtest.ReadInt32},
					bsonrwtest.ReadInt32,
					errors.New("ReadInt32 error"),
				},
				{
					"ReadInt64 error",
					uint(0),
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Int64, Return: int64(0), Err: errors.New("ReadInt64 error"), ErrAfter: bsonrwtest.ReadInt64},
					bsonrwtest.ReadInt64,
					errors.New("ReadInt64 error"),
				},
				{
					"ReadDouble error",
					0,
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Double, Return: float64(0), Err: errors.New("ReadDouble error"), ErrAfter: bsonrwtest.ReadDouble},
					bsonrwtest.ReadDouble,
					errors.New("ReadDouble error"),
				},
				{
					"ReadDouble", uint64(3), &DecodeContext{},
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Double, Return: float64(3.00)}, bsonrwtest.ReadDouble,
					nil,
				},
				{
					"ReadDouble (truncate)", uint64(3), &DecodeContext{Truncate: true},
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Double, Return: float64(3.14)}, bsonrwtest.ReadDouble,
					nil,
				},
				{
					"ReadDouble (no truncate)", uint64(0), nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Double, Return: float64(3.14)}, bsonrwtest.ReadDouble,
					errCannotTruncate,
				},
				{
					"ReadDouble overflows int64", uint64(0), nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Double, Return: math.MaxFloat64}, bsonrwtest.ReadDouble,
					fmt.Errorf("%g overflows int64", math.MaxFloat64),
				},
				{"uint8/fast path", uint8(127), nil, &bsonrwtest.ValueReaderWriter{BSONType: bsontype.Int32, Return: int32(127)}, bsonrwtest.ReadInt32, nil},
				{"uint16/fast path", uint16(255), nil, &bsonrwtest.ValueReaderWriter{BSONType: bsontype.Int32, Return: int32(255)}, bsonrwtest.ReadInt32, nil},
				{"uint32/fast path", uint32(1234), nil, &bsonrwtest.ValueReaderWriter{BSONType: bsontype.Int32, Return: int32(1234)}, bsonrwtest.ReadInt32, nil},
				{"uint64/fast path", uint64(1234), nil, &bsonrwtest.ValueReaderWriter{BSONType: bsontype.Int64, Return: int64(1234)}, bsonrwtest.ReadInt64, nil},
				{"uint/fast path", uint(1234), nil, &bsonrwtest.ValueReaderWriter{BSONType: bsontype.Int64, Return: int64(1234)}, bsonrwtest.ReadInt64, nil},
				{
					"uint8/fast path - nil", (*uint8)(nil), nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Int32, Return: int32(0)}, bsonrwtest.ReadInt32,
					ValueDecoderError{
						Name:     "UintDecodeValue",
						Kinds:    []reflect.Kind{reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint},
						Received: reflect.ValueOf((*uint8)(nil)),
					},
				},
				{
					"uint16/fast path - nil", (*uint16)(nil), nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Int32, Return: int32(0)}, bsonrwtest.ReadInt32,
					ValueDecoderError{
						Name:     "UintDecodeValue",
						Kinds:    []reflect.Kind{reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint},
						Received: reflect.ValueOf((*uint16)(nil)),
					},
				},
				{
					"uint32/fast path - nil", (*uint32)(nil), nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Int32, Return: int32(0)}, bsonrwtest.ReadInt32,
					ValueDecoderError{
						Name:     "UintDecodeValue",
						Kinds:    []reflect.Kind{reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint},
						Received: reflect.ValueOf((*uint32)(nil)),
					},
				},
				{
					"uint64/fast path - nil", (*uint64)(nil), nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Int32, Return: int32(0)}, bsonrwtest.ReadInt32,
					ValueDecoderError{
						Name:     "UintDecodeValue",
						Kinds:    []reflect.Kind{reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint},
						Received: reflect.ValueOf((*uint64)(nil)),
					},
				},
				{
					"uint/fast path - nil", (*uint)(nil), nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Int32, Return: int32(0)}, bsonrwtest.ReadInt32,
					ValueDecoderError{
						Name:     "UintDecodeValue",
						Kinds:    []reflect.Kind{reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint},
						Received: reflect.ValueOf((*uint)(nil)),
					},
				},
				{
					"uint8/fast path - overflow", uint8(0), nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Int32, Return: int32(1 << 8)}, bsonrwtest.ReadInt32,
					fmt.Errorf("%d overflows uint8", 1<<8),
				},
				{
					"uint16/fast path - overflow", uint16(0), nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Int32, Return: int32(1 << 16)}, bsonrwtest.ReadInt32,
					fmt.Errorf("%d overflows uint16", 1<<16),
				},
				{
					"uint32/fast path - overflow", uint32(0), nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Int64, Return: int64(1 << 32)}, bsonrwtest.ReadInt64,
					fmt.Errorf("%d overflows uint32", int64(1<<32)),
				},
				{
					"uint8/fast path - overflow (negative)", uint8(0), nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Int32, Return: int32(-1)}, bsonrwtest.ReadInt32,
					fmt.Errorf("%d overflows uint8", -1),
				},
				{
					"uint16/fast path - overflow (negative)", uint16(0), nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Int32, Return: int32(-1)}, bsonrwtest.ReadInt32,
					fmt.Errorf("%d overflows uint16", -1),
				},
				{
					"uint32/fast path - overflow (negative)", uint32(0), nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Int64, Return: int64(-1)}, bsonrwtest.ReadInt64,
					fmt.Errorf("%d overflows uint32", -1),
				},
				{
					"uint64/fast path - overflow (negative)", uint64(0), nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Int64, Return: int64(-1)}, bsonrwtest.ReadInt64,
					fmt.Errorf("%d overflows uint64", -1),
				},
				{
					"uint/fast path - overflow (negative)", uint(0), nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Int64, Return: int64(-1)}, bsonrwtest.ReadInt64,
					fmt.Errorf("%d overflows uint", -1),
				},
				{
					"uint8/reflection path", myuint8(127), nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Int32, Return: int32(127)}, bsonrwtest.ReadInt32,
					nil,
				},
				{
					"uint16/reflection path", myuint16(255), nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Int32, Return: int32(255)}, bsonrwtest.ReadInt32,
					nil,
				},
				{
					"uint32/reflection path", myuint32(511), nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Int32, Return: int32(511)}, bsonrwtest.ReadInt32,
					nil,
				},
				{
					"uint64/reflection path", myuint64(1023), nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Int32, Return: int32(1023)}, bsonrwtest.ReadInt32,
					nil,
				},
				{
					"uint/reflection path", myuint(2047), nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Int32, Return: int32(2047)}, bsonrwtest.ReadInt32,
					nil,
				},
				{
					"uint8/reflection path - overflow", myuint8(0), nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Int32, Return: int32(1 << 8)}, bsonrwtest.ReadInt32,
					fmt.Errorf("%d overflows uint8", 1<<8),
				},
				{
					"uint16/reflection path - overflow", myuint16(0), nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Int32, Return: int32(1 << 16)}, bsonrwtest.ReadInt32,
					fmt.Errorf("%d overflows uint16", 1<<16),
				},
				{
					"uint32/reflection path - overflow", myuint32(0), nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Int64, Return: int64(1 << 32)}, bsonrwtest.ReadInt64,
					fmt.Errorf("%d overflows uint32", int64(1<<32)),
				},
				{
					"uint8/reflection path - overflow (negative)", myuint8(0), nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Int32, Return: int32(-1)}, bsonrwtest.ReadInt32,
					fmt.Errorf("%d overflows uint8", -1),
				},
				{
					"uint16/reflection path - overflow (negative)", myuint16(0), nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Int32, Return: int32(-1)}, bsonrwtest.ReadInt32,
					fmt.Errorf("%d overflows uint16", -1),
				},
				{
					"uint32/reflection path - overflow (negative)", myuint32(0), nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Int64, Return: int64(-1)}, bsonrwtest.ReadInt64,
					fmt.Errorf("%d overflows uint32", -1),
				},
				{
					"uint64/reflection path - overflow (negative)", myuint64(0), nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Int64, Return: int64(-1)}, bsonrwtest.ReadInt64,
					fmt.Errorf("%d overflows uint64", -1),
				},
				{
					"uint/reflection path - overflow (negative)", myuint(0), nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Int64, Return: int64(-1)}, bsonrwtest.ReadInt64,
					fmt.Errorf("%d overflows uint", -1),
				},
				{
					"can set false",
					cansettest,
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Int32, Return: int32(0)},
					bsonrwtest.Nothing,
					ValueDecoderError{
						Name:  "UintDecodeValue",
						Kinds: []reflect.Kind{reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint},
					},
				},
			},
		},
		{
			"FloatDecodeValue",
			ValueDecoderFunc(dvd.FloatDecodeValue),
			[]subtest{
				{
					"wrong type",
					wrong,
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Double, Return: float64(0)},
					bsonrwtest.ReadDouble,
					ValueDecoderError{
						Name:     "FloatDecodeValue",
						Kinds:    []reflect.Kind{reflect.Float32, reflect.Float64},
						Received: reflect.ValueOf(wrong),
					},
				},
				{
					"type not double",
					0,
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.String},
					bsonrwtest.Nothing,
					fmt.Errorf("cannot decode %v into a float32 or float64 type", bsontype.String),
				},
				{
					"ReadDouble error",
					float64(0),
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Double, Return: float64(0), Err: errors.New("ReadDouble error"), ErrAfter: bsonrwtest.ReadDouble},
					bsonrwtest.ReadDouble,
					errors.New("ReadDouble error"),
				},
				{
					"ReadInt32 error",
					float64(0),
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Int32, Return: int32(0), Err: errors.New("ReadInt32 error"), ErrAfter: bsonrwtest.ReadInt32},
					bsonrwtest.ReadInt32,
					errors.New("ReadInt32 error"),
				},
				{
					"ReadInt64 error",
					float64(0),
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Int64, Return: int64(0), Err: errors.New("ReadInt64 error"), ErrAfter: bsonrwtest.ReadInt64},
					bsonrwtest.ReadInt64,
					errors.New("ReadInt64 error"),
				},
				{
					"float64/int32", float32(32.0), nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Int32, Return: int32(32)}, bsonrwtest.ReadInt32,
					nil,
				},
				{
					"float64/int64", float32(64.0), nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Int64, Return: int64(64)}, bsonrwtest.ReadInt64,
					nil,
				},
				{
					"float32/fast path (equal)", float32(3.0), nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Double, Return: float64(3.0)}, bsonrwtest.ReadDouble,
					nil,
				},
				{
					"float64/fast path", float64(3.14159), nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Double, Return: float64(3.14159)}, bsonrwtest.ReadDouble,
					nil,
				},
				{
					"float32/fast path (truncate)", float32(3.14), &DecodeContext{Truncate: true},
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Double, Return: float64(3.14)}, bsonrwtest.ReadDouble,
					nil,
				},
				{
					"float32/fast path (no truncate)", float32(0), nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Double, Return: float64(3.14)}, bsonrwtest.ReadDouble,
					errCannotTruncate,
				},
				{
					"float32/fast path - nil", (*float32)(nil), nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Double, Return: float64(0)}, bsonrwtest.ReadDouble,
					ValueDecoderError{
						Name:     "FloatDecodeValue",
						Kinds:    []reflect.Kind{reflect.Float32, reflect.Float64},
						Received: reflect.ValueOf((*float32)(nil)),
					},
				},
				{
					"float64/fast path - nil", (*float64)(nil), nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Double, Return: float64(0)}, bsonrwtest.ReadDouble,
					ValueDecoderError{
						Name:     "FloatDecodeValue",
						Kinds:    []reflect.Kind{reflect.Float32, reflect.Float64},
						Received: reflect.ValueOf((*float64)(nil)),
					},
				},
				{
					"float32/reflection path (equal)", myfloat32(3.0), nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Double, Return: float64(3.0)}, bsonrwtest.ReadDouble,
					nil,
				},
				{
					"float64/reflection path", myfloat64(3.14159), nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Double, Return: float64(3.14159)}, bsonrwtest.ReadDouble,
					nil,
				},
				{
					"float32/reflection path (truncate)", myfloat32(3.14), &DecodeContext{Truncate: true},
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Double, Return: float64(3.14)}, bsonrwtest.ReadDouble,
					nil,
				},
				{
					"float32/reflection path (no truncate)", myfloat32(0), nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Double, Return: float64(3.14)}, bsonrwtest.ReadDouble,
					errCannotTruncate,
				},
				{
					"can set false",
					cansettest,
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Double, Return: float64(0)},
					bsonrwtest.Nothing,
					ValueDecoderError{
						Name:  "FloatDecodeValue",
						Kinds: []reflect.Kind{reflect.Float32, reflect.Float64},
					},
				},
			},
		},
		{
			"defaultTimeCodec.DecodeValue",
			defaultTimeCodec,
			[]subtest{
				{
					"wrong type",
					wrong,
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.DateTime, Return: int64(0)},
					bsonrwtest.Nothing,
					ValueDecoderError{Name: "TimeDecodeValue", Types: []reflect.Type{tTime}, Received: reflect.ValueOf(wrong)},
				},
				{
					"ReadDateTime error",
					time.Time{},
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.DateTime, Return: int64(0), Err: errors.New("ReadDateTime error"), ErrAfter: bsonrwtest.ReadDateTime},
					bsonrwtest.ReadDateTime,
					errors.New("ReadDateTime error"),
				},
				{
					"time.Time",
					now,
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.DateTime, Return: now.UnixNano() / int64(time.Millisecond)},
					bsonrwtest.ReadDateTime,
					nil,
				},
				{
					"can set false",
					cansettest,
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.DateTime, Return: int64(0)},
					bsonrwtest.Nothing,
					ValueDecoderError{Name: "TimeDecodeValue", Types: []reflect.Type{tTime}},
				},
				{
					"decode null",
					time.Time{},
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Null},
					bsonrwtest.ReadNull,
					nil,
				},
				{
					"decode undefined",
					time.Time{},
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Undefined},
					bsonrwtest.ReadUndefined,
					nil,
				},
			},
		},
		{
			"defaultMapCodec.DecodeValue",
			defaultMapCodec,
			[]subtest{
				{
					"wrong kind",
					wrong,
					nil,
					&bsonrwtest.ValueReaderWriter{},
					bsonrwtest.Nothing,
					ValueDecoderError{Name: "MapDecodeValue", Kinds: []reflect.Kind{reflect.Map}, Received: reflect.ValueOf(wrong)},
				},
				{
					"wrong kind (non-string key)",
					map[bool]interface{}{},
					&DecodeContext{Registry: buildDefaultRegistry()},
					&bsonrwtest.ValueReaderWriter{},
					bsonrwtest.ReadElement,
					fmt.Errorf("unsupported key type: %T", false),
				},
				{
					"ReadDocument Error",
					make(map[string]interface{}),
					nil,
					&bsonrwtest.ValueReaderWriter{Err: errors.New("rd error"), ErrAfter: bsonrwtest.ReadDocument},
					bsonrwtest.ReadDocument,
					errors.New("rd error"),
				},
				{
					"Lookup Error",
					map[string]string{},
					&DecodeContext{Registry: NewRegistryBuilder().Build()},
					&bsonrwtest.ValueReaderWriter{},
					bsonrwtest.ReadDocument,
					ErrNoDecoder{Type: reflect.TypeOf("")},
				},
				{
					"ReadElement Error",
					make(map[string]interface{}),
					&DecodeContext{Registry: buildDefaultRegistry()},
					&bsonrwtest.ValueReaderWriter{Err: errors.New("re error"), ErrAfter: bsonrwtest.ReadElement},
					bsonrwtest.ReadElement,
					errors.New("re error"),
				},
				{
					"can set false",
					cansettest,
					nil,
					&bsonrwtest.ValueReaderWriter{},
					bsonrwtest.Nothing,
					ValueDecoderError{Name: "MapDecodeValue", Kinds: []reflect.Kind{reflect.Map}},
				},
				{
					"wrong BSON type",
					map[string]interface{}{},
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.String},
					bsonrwtest.Nothing,
					errors.New("cannot decode string into a map[string]interface {}"),
				},
				{
					"decode null",
					(map[string]interface{})(nil),
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Null},
					bsonrwtest.ReadNull,
					nil,
				},
				{
					"decode undefined",
					(map[string]interface{})(nil),
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Undefined},
					bsonrwtest.ReadUndefined,
					nil,
				},
			},
		},
		{
			"ArrayDecodeValue",
			ValueDecoderFunc(dvd.ArrayDecodeValue),
			[]subtest{
				{
					"wrong kind",
					wrong,
					nil,
					&bsonrwtest.ValueReaderWriter{},
					bsonrwtest.Nothing,
					ValueDecoderError{Name: "ArrayDecodeValue", Kinds: []reflect.Kind{reflect.Array}, Received: reflect.ValueOf(wrong)},
				},
				{
					"can set false",
					cansettest,
					nil,
					&bsonrwtest.ValueReaderWriter{},
					bsonrwtest.Nothing,
					ValueDecoderError{Name: "ArrayDecodeValue", Kinds: []reflect.Kind{reflect.Array}},
				},
				{
					"Not Type Array",
					[1]interface{}{},
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.String},
					bsonrwtest.Nothing,
					errors.New("cannot decode string into an array"),
				},
				{
					"ReadArray Error",
					[1]interface{}{},
					nil,
					&bsonrwtest.ValueReaderWriter{Err: errors.New("ra error"), ErrAfter: bsonrwtest.ReadArray, BSONType: bsontype.Array},
					bsonrwtest.ReadArray,
					errors.New("ra error"),
				},
				{
					"Lookup Error",
					[1]string{},
					&DecodeContext{Registry: NewRegistryBuilder().Build()},
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Array},
					bsonrwtest.ReadArray,
					ErrNoDecoder{Type: reflect.TypeOf("")},
				},
				{
					"ReadValue Error",
					[1]string{},
					&DecodeContext{Registry: buildDefaultRegistry()},
					&bsonrwtest.ValueReaderWriter{Err: errors.New("rv error"), ErrAfter: bsonrwtest.ReadValue, BSONType: bsontype.Array},
					bsonrwtest.ReadValue,
					errors.New("rv error"),
				},
				{
					"DecodeValue Error",
					[1]string{},
					&DecodeContext{Registry: buildDefaultRegistry()},
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Array},
					bsonrwtest.ReadValue,
					&DecodeError{keys: []string{"0"}, wrapped: errors.New("cannot decode array into a string type")},
				},
				{
					"Document but not D",
					[1]string{},
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Type(0)},
					bsonrwtest.Nothing,
					errors.New("cannot decode document into [1]string"),
				},
				{
					"EmbeddedDocument but not D",
					[1]string{},
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.EmbeddedDocument},
					bsonrwtest.Nothing,
					errors.New("cannot decode document into [1]string"),
				},
				{
					"decode null",
					[1]string{},
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Null},
					bsonrwtest.ReadNull,
					nil,
				},
				{
					"decode undefined",
					[1]string{},
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Undefined},
					bsonrwtest.ReadUndefined,
					nil,
				},
			},
		},
		{
			"defaultSliceCodec.DecodeValue",
			defaultSliceCodec,
			[]subtest{
				{
					"wrong kind",
					wrong,
					nil,
					&bsonrwtest.ValueReaderWriter{},
					bsonrwtest.Nothing,
					ValueDecoderError{Name: "SliceDecodeValue", Kinds: []reflect.Kind{reflect.Slice}, Received: reflect.ValueOf(wrong)},
				},
				{
					"can set false",
					cansettest,
					nil,
					&bsonrwtest.ValueReaderWriter{},
					bsonrwtest.Nothing,
					ValueDecoderError{Name: "SliceDecodeValue", Kinds: []reflect.Kind{reflect.Slice}},
				},
				{
					"Not Type Array",
					[]interface{}{},
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Int32},
					bsonrwtest.Nothing,
					errors.New("cannot decode 32-bit integer into a slice"),
				},
				{
					"ReadArray Error",
					[]interface{}{},
					nil,
					&bsonrwtest.ValueReaderWriter{Err: errors.New("ra error"), ErrAfter: bsonrwtest.ReadArray, BSONType: bsontype.Array},
					bsonrwtest.ReadArray,
					errors.New("ra error"),
				},
				{
					"Lookup Error",
					[]string{},
					&DecodeContext{Registry: NewRegistryBuilder().Build()},
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Array},
					bsonrwtest.ReadArray,
					ErrNoDecoder{Type: reflect.TypeOf("")},
				},
				{
					"ReadValue Error",
					[]string{},
					&DecodeContext{Registry: buildDefaultRegistry()},
					&bsonrwtest.ValueReaderWriter{Err: errors.New("rv error"), ErrAfter: bsonrwtest.ReadValue, BSONType: bsontype.Array},
					bsonrwtest.ReadValue,
					errors.New("rv error"),
				},
				{
					"DecodeValue Error",
					[]string{},
					&DecodeContext{Registry: buildDefaultRegistry()},
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Array},
					bsonrwtest.ReadValue,
					&DecodeError{keys: []string{"0"}, wrapped: errors.New("cannot decode array into a string type")},
				},
				{
					"Document but not D",
					[]string{},
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Type(0)},
					bsonrwtest.Nothing,
					errors.New("cannot decode document into []string"),
				},
				{
					"EmbeddedDocument but not D",
					[]string{},
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.EmbeddedDocument},
					bsonrwtest.Nothing,
					errors.New("cannot decode document into []string"),
				},
				{
					"decode null",
					([]string)(nil),
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Null},
					bsonrwtest.ReadNull,
					nil,
				},
				{
					"decode undefined",
					([]string)(nil),
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Undefined},
					bsonrwtest.ReadUndefined,
					nil,
				},
			},
		},
		{
			"ObjectIDDecodeValue",
			ValueDecoderFunc(dvd.ObjectIDDecodeValue),
			[]subtest{
				{
					"wrong type",
					wrong,
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.ObjectID},
					bsonrwtest.Nothing,
					ValueDecoderError{Name: "ObjectIDDecodeValue", Types: []reflect.Type{tOID}, Received: reflect.ValueOf(wrong)},
				},
				{
					"type not objectID",
					primitive.ObjectID{},
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Int32},
					bsonrwtest.Nothing,
					fmt.Errorf("cannot decode %v into an ObjectID", bsontype.Int32),
				},
				{
					"ReadObjectID Error",
					primitive.ObjectID{},
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.ObjectID, Err: errors.New("roid error"), ErrAfter: bsonrwtest.ReadObjectID},
					bsonrwtest.ReadObjectID,
					errors.New("roid error"),
				},
				{
					"can set false",
					cansettest,
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.ObjectID, Return: primitive.ObjectID{}},
					bsonrwtest.Nothing,
					ValueDecoderError{Name: "ObjectIDDecodeValue", Types: []reflect.Type{tOID}},
				},
				{
					"success",
					primitive.ObjectID{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C},
					nil,
					&bsonrwtest.ValueReaderWriter{
						BSONType: bsontype.ObjectID,
						Return:   primitive.ObjectID{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C},
					},
					bsonrwtest.ReadObjectID,
					nil,
				},
				{
					"success/string",
					primitive.ObjectID{0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x61, 0x62},
					nil,
					&bsonrwtest.ValueReaderWriter{
						BSONType: bsontype.String,
						Return:   "0123456789ab",
					},
					bsonrwtest.ReadString,
					nil,
				},
				{
					"success/string-hex",
					primitive.ObjectID{0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x61, 0x62},
					nil,
					&bsonrwtest.ValueReaderWriter{
						BSONType: bsontype.String,
						Return:   "303132333435363738396162",
					},
					bsonrwtest.ReadString,
					nil,
				},
				{
					"decode null",
					primitive.ObjectID{},
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Null},
					bsonrwtest.ReadNull,
					nil,
				},
				{
					"decode undefined",
					primitive.ObjectID{},
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Undefined},
					bsonrwtest.ReadUndefined,
					nil,
				},
			},
		},
		{
			"Decimal128DecodeValue",
			ValueDecoderFunc(dvd.Decimal128DecodeValue),
			[]subtest{
				{
					"wrong type",
					wrong,
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Decimal128},
					bsonrwtest.Nothing,
					ValueDecoderError{Name: "Decimal128DecodeValue", Types: []reflect.Type{tDecimal}, Received: reflect.ValueOf(wrong)},
				},
				{
					"type not decimal128",
					primitive.Decimal128{},
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.String},
					bsonrwtest.Nothing,
					fmt.Errorf("cannot decode %v into a primitive.Decimal128", bsontype.String),
				},
				{
					"ReadDecimal128 Error",
					primitive.Decimal128{},
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Decimal128, Err: errors.New("rd128 error"), ErrAfter: bsonrwtest.ReadDecimal128},
					bsonrwtest.ReadDecimal128,
					errors.New("rd128 error"),
				},
				{
					"can set false",
					cansettest,
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Decimal128, Return: d128},
					bsonrwtest.Nothing,
					ValueDecoderError{Name: "Decimal128DecodeValue", Types: []reflect.Type{tDecimal}},
				},
				{
					"success",
					d128,
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Decimal128, Return: d128},
					bsonrwtest.ReadDecimal128,
					nil,
				},
				{
					"decode null",
					primitive.Decimal128{},
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Null},
					bsonrwtest.ReadNull,
					nil,
				},
				{
					"decode undefined",
					primitive.Decimal128{},
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Undefined},
					bsonrwtest.ReadUndefined,
					nil,
				},
			},
		},
		{
			"JSONNumberDecodeValue",
			ValueDecoderFunc(dvd.JSONNumberDecodeValue),
			[]subtest{
				{
					"wrong type",
					wrong,
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.ObjectID},
					bsonrwtest.Nothing,
					ValueDecoderError{Name: "JSONNumberDecodeValue", Types: []reflect.Type{tJSONNumber}, Received: reflect.ValueOf(wrong)},
				},
				{
					"type not double/int32/int64",
					json.Number(""),
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.String},
					bsonrwtest.Nothing,
					fmt.Errorf("cannot decode %v into a json.Number", bsontype.String),
				},
				{
					"ReadDouble Error",
					json.Number(""),
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Double, Err: errors.New("rd error"), ErrAfter: bsonrwtest.ReadDouble},
					bsonrwtest.ReadDouble,
					errors.New("rd error"),
				},
				{
					"ReadInt32 Error",
					json.Number(""),
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Int32, Err: errors.New("ri32 error"), ErrAfter: bsonrwtest.ReadInt32},
					bsonrwtest.ReadInt32,
					errors.New("ri32 error"),
				},
				{
					"ReadInt64 Error",
					json.Number(""),
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Int64, Err: errors.New("ri64 error"), ErrAfter: bsonrwtest.ReadInt64},
					bsonrwtest.ReadInt64,
					errors.New("ri64 error"),
				},
				{
					"can set false",
					cansettest,
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.ObjectID, Return: primitive.ObjectID{}},
					bsonrwtest.Nothing,
					ValueDecoderError{Name: "JSONNumberDecodeValue", Types: []reflect.Type{tJSONNumber}},
				},
				{
					"success/double",
					json.Number("3.14159"),
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Double, Return: float64(3.14159)},
					bsonrwtest.ReadDouble,
					nil,
				},
				{
					"success/int32",
					json.Number("12345"),
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Int32, Return: int32(12345)},
					bsonrwtest.ReadInt32,
					nil,
				},
				{
					"success/int64",
					json.Number("1234567890"),
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Int64, Return: int64(1234567890)},
					bsonrwtest.ReadInt64,
					nil,
				},
				{
					"decode null",
					json.Number(""),
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Null},
					bsonrwtest.ReadNull,
					nil,
				},
				{
					"decode undefined",
					json.Number(""),
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Undefined},
					bsonrwtest.ReadUndefined,
					nil,
				},
			},
		},
		{
			"URLDecodeValue",
			ValueDecoderFunc(dvd.URLDecodeValue),
			[]subtest{
				{
					"wrong type",
					url.URL{},
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Int32},
					bsonrwtest.Nothing,
					fmt.Errorf("cannot decode %v into a *url.URL", bsontype.Int32),
				},
				{
					"type not *url.URL",
					int64(0),
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.String, Return: "http://example.com"},
					bsonrwtest.Nothing,
					ValueDecoderError{Name: "URLDecodeValue", Types: []reflect.Type{tURL}, Received: reflect.ValueOf(int64(0))},
				},
				{
					"ReadString error",
					url.URL{},
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.String, Err: errors.New("rs error"), ErrAfter: bsonrwtest.ReadString},
					bsonrwtest.ReadString,
					errors.New("rs error"),
				},
				{
					"url.Parse error",
					url.URL{},
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.String, Return: "not-valid-%%%%://"},
					bsonrwtest.ReadString,
					&url.Error{
						Op:  "parse",
						URL: "not-valid-%%%%://",
						Err: errors.New("first path segment in URL cannot contain colon"),
					},
				},
				{
					"can set false",
					cansettest,
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.String, Return: "http://example.com"},
					bsonrwtest.Nothing,
					ValueDecoderError{Name: "URLDecodeValue", Types: []reflect.Type{tURL}},
				},
				{
					"url.URL",
					url.URL{Scheme: "http", Host: "example.com"},
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.String, Return: "http://example.com"},
					bsonrwtest.ReadString,
					nil,
				},
				{
					"decode null",
					url.URL{},
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Null},
					bsonrwtest.ReadNull,
					nil,
				},
				{
					"decode undefined",
					url.URL{},
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Undefined},
					bsonrwtest.ReadUndefined,
					nil,
				},
			},
		},
		{
			"defaultByteSliceCodec.DecodeValue",
			defaultByteSliceCodec,
			[]subtest{
				{
					"wrong type",
					[]byte{},
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Int32},
					bsonrwtest.Nothing,
					fmt.Errorf("cannot decode %v into a []byte", bsontype.Int32),
				},
				{
					"type not []byte",
					int64(0),
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Binary, Return: bsoncore.Value{Type: bsontype.Binary}},
					bsonrwtest.Nothing,
					ValueDecoderError{Name: "ByteSliceDecodeValue", Types: []reflect.Type{tByteSlice}, Received: reflect.ValueOf(int64(0))},
				},
				{
					"ReadBinary error",
					[]byte{},
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Binary, Err: errors.New("rb error"), ErrAfter: bsonrwtest.ReadBinary},
					bsonrwtest.ReadBinary,
					errors.New("rb error"),
				},
				{
					"incorrect subtype",
					[]byte{},
					nil,
					&bsonrwtest.ValueReaderWriter{
						BSONType: bsontype.Binary,
						Return: bsoncore.Value{
							Type: bsontype.Binary,
							Data: bsoncore.AppendBinary(nil, 0xFF, []byte{0x01, 0x02, 0x03}),
						},
					},
					bsonrwtest.ReadBinary,
					decodeBinaryError{subtype: byte(0xFF), typeName: "[]byte"},
				},
				{
					"can set false",
					cansettest,
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Binary, Return: bsoncore.AppendBinary(nil, 0x00, []byte{0x01, 0x02, 0x03})},
					bsonrwtest.Nothing,
					ValueDecoderError{Name: "ByteSliceDecodeValue", Types: []reflect.Type{tByteSlice}},
				},
				{
					"decode null",
					([]byte)(nil),
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Null},
					bsonrwtest.ReadNull,
					nil,
				},
				{
					"decode undefined",
					([]byte)(nil),
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Undefined},
					bsonrwtest.ReadUndefined,
					nil,
				},
			},
		},
		{
			"defaultStringCodec.DecodeValue",
			defaultStringCodec,
			[]subtest{
				{
					"symbol",
					"var hello = 'world';",
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Symbol, Return: "var hello = 'world';"},
					bsonrwtest.ReadSymbol,
					nil,
				},
				{
					"decode null",
					"",
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Null},
					bsonrwtest.ReadNull,
					nil,
				},
				{
					"decode undefined",
					"",
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Undefined},
					bsonrwtest.ReadUndefined,
					nil,
				},
			},
		},
		{
			"ValueUnmarshalerDecodeValue",
			ValueDecoderFunc(dvd.ValueUnmarshalerDecodeValue),
			[]subtest{
				{
					"wrong type",
					wrong,
					nil,
					nil,
					bsonrwtest.Nothing,
					ValueDecoderError{
						Name:     "ValueUnmarshalerDecodeValue",
						Types:    []reflect.Type{tValueUnmarshaler},
						Received: reflect.ValueOf(wrong),
					},
				},
				{
					"copy error",
					&testValueUnmarshaler{},
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.String, Err: errors.New("copy error"), ErrAfter: bsonrwtest.ReadString},
					bsonrwtest.ReadString,
					errors.New("copy error"),
				},
				{
					"ValueUnmarshaler",
					&testValueUnmarshaler{t: bsontype.String, val: bsoncore.AppendString(nil, "hello, world")},
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.String, Return: "hello, world"},
					bsonrwtest.ReadString,
					nil,
				},
			},
		},
		{
			"UnmarshalerDecodeValue",
			ValueDecoderFunc(dvd.UnmarshalerDecodeValue),
			[]subtest{
				{
					"wrong type",
					wrong,
					nil,
					nil,
					bsonrwtest.Nothing,
					ValueDecoderError{Name: "UnmarshalerDecodeValue", Types: []reflect.Type{tUnmarshaler}, Received: reflect.ValueOf(wrong)},
				},
				{
					"copy error",
					&testUnmarshaler{},
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.String, Err: errors.New("copy error"), ErrAfter: bsonrwtest.ReadString},
					bsonrwtest.ReadString,
					errors.New("copy error"),
				},
				{
					"Unmarshaler",
					testUnmarshaler{Val: bsoncore.AppendDouble(nil, 3.14159)},
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Double, Return: float64(3.14159)},
					bsonrwtest.ReadDouble,
					nil,
				},
			},
		},
		{
			"PointerCodec.DecodeValue",
			NewPointerCodec(),
			[]subtest{
				{
					"not valid", nil, nil, nil, bsonrwtest.Nothing,
					ValueDecoderError{Name: "PointerCodec.DecodeValue", Kinds: []reflect.Kind{reflect.Ptr}, Received: reflect.Value{}},
				},
				{
					"can set", cansettest, nil, nil, bsonrwtest.Nothing,
					ValueDecoderError{Name: "PointerCodec.DecodeValue", Kinds: []reflect.Kind{reflect.Ptr}},
				},
				{
					"No Decoder", &wrong, &DecodeContext{Registry: buildDefaultRegistry()}, nil, bsonrwtest.Nothing,
					ErrNoDecoder{Type: reflect.TypeOf(wrong)},
				},
				{
					"decode null",
					(*mystruct)(nil),
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Null},
					bsonrwtest.ReadNull,
					nil,
				},
				{
					"decode undefined",
					(*mystruct)(nil),
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Undefined},
					bsonrwtest.ReadUndefined,
					nil,
				},
			},
		},
		{
			"BinaryDecodeValue",
			ValueDecoderFunc(dvd.BinaryDecodeValue),
			[]subtest{
				{
					"wrong type",
					wrong,
					nil,
					&bsonrwtest.ValueReaderWriter{},
					bsonrwtest.Nothing,
					ValueDecoderError{Name: "BinaryDecodeValue", Types: []reflect.Type{tBinary}, Received: reflect.ValueOf(wrong)},
				},
				{
					"type not binary",
					primitive.Binary{},
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.String},
					bsonrwtest.Nothing,
					fmt.Errorf("cannot decode %v into a Binary", bsontype.String),
				},
				{
					"ReadBinary Error",
					primitive.Binary{},
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Binary, Err: errors.New("rb error"), ErrAfter: bsonrwtest.ReadBinary},
					bsonrwtest.ReadBinary,
					errors.New("rb error"),
				},
				{
					"Binary/success",
					primitive.Binary{Data: []byte{0x01, 0x02, 0x03}, Subtype: 0xFF},
					nil,
					&bsonrwtest.ValueReaderWriter{
						BSONType: bsontype.Binary,
						Return: bsoncore.Value{
							Type: bsontype.Binary,
							Data: bsoncore.AppendBinary(nil, 0xFF, []byte{0x01, 0x02, 0x03}),
						},
					},
					bsonrwtest.ReadBinary,
					nil,
				},
				{
					"decode null",
					primitive.Binary{},
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Null},
					bsonrwtest.ReadNull,
					nil,
				},
				{
					"decode undefined",
					primitive.Binary{},
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Undefined},
					bsonrwtest.ReadUndefined,
					nil,
				},
			},
		},
		{
			"UndefinedDecodeValue",
			ValueDecoderFunc(dvd.UndefinedDecodeValue),
			[]subtest{
				{
					"wrong type",
					wrong,
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Undefined},
					bsonrwtest.Nothing,
					ValueDecoderError{Name: "UndefinedDecodeValue", Types: []reflect.Type{tUndefined}, Received: reflect.ValueOf(wrong)},
				},
				{
					"type not undefined",
					primitive.Undefined{},
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.String},
					bsonrwtest.Nothing,
					fmt.Errorf("cannot decode %v into an Undefined", bsontype.String),
				},
				{
					"ReadUndefined Error",
					primitive.Undefined{},
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Undefined, Err: errors.New("ru error"), ErrAfter: bsonrwtest.ReadUndefined},
					bsonrwtest.ReadUndefined,
					errors.New("ru error"),
				},
				{
					"ReadUndefined/success",
					primitive.Undefined{},
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Undefined},
					bsonrwtest.ReadUndefined,
					nil,
				},
				{
					"decode null",
					primitive.Undefined{},
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Null},
					bsonrwtest.ReadNull,
					nil,
				},
			},
		},
		{
			"DateTimeDecodeValue",
			ValueDecoderFunc(dvd.DateTimeDecodeValue),
			[]subtest{
				{
					"wrong type",
					wrong,
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.DateTime},
					bsonrwtest.Nothing,
					ValueDecoderError{Name: "DateTimeDecodeValue", Types: []reflect.Type{tDateTime}, Received: reflect.ValueOf(wrong)},
				},
				{
					"type not datetime",
					primitive.DateTime(0),
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.String},
					bsonrwtest.Nothing,
					fmt.Errorf("cannot decode %v into a DateTime", bsontype.String),
				},
				{
					"ReadDateTime Error",
					primitive.DateTime(0),
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.DateTime, Err: errors.New("rdt error"), ErrAfter: bsonrwtest.ReadDateTime},
					bsonrwtest.ReadDateTime,
					errors.New("rdt error"),
				},
				{
					"success",
					primitive.DateTime(1234567890),
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.DateTime, Return: int64(1234567890)},
					bsonrwtest.ReadDateTime,
					nil,
				},
				{
					"decode null",
					primitive.DateTime(0),
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Null},
					bsonrwtest.ReadNull,
					nil,
				},
				{
					"decode undefined",
					primitive.DateTime(0),
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Undefined},
					bsonrwtest.ReadUndefined,
					nil,
				},
			},
		},
		{
			"NullDecodeValue",
			ValueDecoderFunc(dvd.NullDecodeValue),
			[]subtest{
				{
					"wrong type",
					wrong,
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Null},
					bsonrwtest.Nothing,
					ValueDecoderError{Name: "NullDecodeValue", Types: []reflect.Type{tNull}, Received: reflect.ValueOf(wrong)},
				},
				{
					"type not null",
					primitive.Null{},
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.String},
					bsonrwtest.Nothing,
					fmt.Errorf("cannot decode %v into a Null", bsontype.String),
				},
				{
					"ReadNull Error",
					primitive.Null{},
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Null, Err: errors.New("rn error"), ErrAfter: bsonrwtest.ReadNull},
					bsonrwtest.ReadNull,
					errors.New("rn error"),
				},
				{
					"success",
					primitive.Null{},
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Null},
					bsonrwtest.ReadNull,
					nil,
				},
			},
		},
		{
			"RegexDecodeValue",
			ValueDecoderFunc(dvd.RegexDecodeValue),
			[]subtest{
				{
					"wrong type",
					wrong,
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Regex},
					bsonrwtest.Nothing,
					ValueDecoderError{Name: "RegexDecodeValue", Types: []reflect.Type{tRegex}, Received: reflect.ValueOf(wrong)},
				},
				{
					"type not regex",
					primitive.Regex{},
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.String},
					bsonrwtest.Nothing,
					fmt.Errorf("cannot decode %v into a Regex", bsontype.String),
				},
				{
					"ReadRegex Error",
					primitive.Regex{},
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Regex, Err: errors.New("rr error"), ErrAfter: bsonrwtest.ReadRegex},
					bsonrwtest.ReadRegex,
					errors.New("rr error"),
				},
				{
					"success",
					primitive.Regex{Pattern: "foo", Options: "bar"},
					nil,
					&bsonrwtest.ValueReaderWriter{
						BSONType: bsontype.Regex,
						Return: bsoncore.Value{
							Type: bsontype.Regex,
							Data: bsoncore.AppendRegex(nil, "foo", "bar"),
						},
					},
					bsonrwtest.ReadRegex,
					nil,
				},
				{
					"decode null",
					primitive.Regex{},
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Null},
					bsonrwtest.ReadNull,
					nil,
				},
				{
					"decode undefined",
					primitive.Regex{},
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Undefined},
					bsonrwtest.ReadUndefined,
					nil,
				},
			},
		},
		{
			"DBPointerDecodeValue",
			ValueDecoderFunc(dvd.DBPointerDecodeValue),
			[]subtest{
				{
					"wrong type",
					wrong,
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.DBPointer},
					bsonrwtest.Nothing,
					ValueDecoderError{Name: "DBPointerDecodeValue", Types: []reflect.Type{tDBPointer}, Received: reflect.ValueOf(wrong)},
				},
				{
					"type not dbpointer",
					primitive.DBPointer{},
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.String},
					bsonrwtest.Nothing,
					fmt.Errorf("cannot decode %v into a DBPointer", bsontype.String),
				},
				{
					"ReadDBPointer Error",
					primitive.DBPointer{},
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.DBPointer, Err: errors.New("rdbp error"), ErrAfter: bsonrwtest.ReadDBPointer},
					bsonrwtest.ReadDBPointer,
					errors.New("rdbp error"),
				},
				{
					"success",
					primitive.DBPointer{
						DB:      "foobar",
						Pointer: primitive.ObjectID{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C},
					},
					nil,
					&bsonrwtest.ValueReaderWriter{
						BSONType: bsontype.DBPointer,
						Return: bsoncore.Value{
							Type: bsontype.DBPointer,
							Data: bsoncore.AppendDBPointer(
								nil, "foobar", primitive.ObjectID{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C},
							),
						},
					},
					bsonrwtest.ReadDBPointer,
					nil,
				},
				{
					"decode null",
					primitive.DBPointer{},
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Null},
					bsonrwtest.ReadNull,
					nil,
				},
				{
					"decode undefined",
					primitive.DBPointer{},
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Undefined},
					bsonrwtest.ReadUndefined,
					nil,
				},
			},
		},
		{
			"TimestampDecodeValue",
			ValueDecoderFunc(dvd.TimestampDecodeValue),
			[]subtest{
				{
					"wrong type",
					wrong,
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Timestamp},
					bsonrwtest.Nothing,
					ValueDecoderError{Name: "TimestampDecodeValue", Types: []reflect.Type{tTimestamp}, Received: reflect.ValueOf(wrong)},
				},
				{
					"type not timestamp",
					primitive.Timestamp{},
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.String},
					bsonrwtest.Nothing,
					fmt.Errorf("cannot decode %v into a Timestamp", bsontype.String),
				},
				{
					"ReadTimestamp Error",
					primitive.Timestamp{},
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Timestamp, Err: errors.New("rt error"), ErrAfter: bsonrwtest.ReadTimestamp},
					bsonrwtest.ReadTimestamp,
					errors.New("rt error"),
				},
				{
					"success",
					primitive.Timestamp{T: 12345, I: 67890},
					nil,
					&bsonrwtest.ValueReaderWriter{
						BSONType: bsontype.Timestamp,
						Return: bsoncore.Value{
							Type: bsontype.Timestamp,
							Data: bsoncore.AppendTimestamp(nil, 12345, 67890),
						},
					},
					bsonrwtest.ReadTimestamp,
					nil,
				},
				{
					"decode null",
					primitive.Timestamp{},
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Null},
					bsonrwtest.ReadNull,
					nil,
				},
				{
					"decode undefined",
					primitive.Timestamp{},
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Undefined},
					bsonrwtest.ReadUndefined,
					nil,
				},
			},
		},
		{
			"MinKeyDecodeValue",
			ValueDecoderFunc(dvd.MinKeyDecodeValue),
			[]subtest{
				{
					"wrong type",
					wrong,
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.MinKey},
					bsonrwtest.Nothing,
					ValueDecoderError{Name: "MinKeyDecodeValue", Types: []reflect.Type{tMinKey}, Received: reflect.ValueOf(wrong)},
				},
				{
					"type not null",
					primitive.MinKey{},
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.String},
					bsonrwtest.Nothing,
					fmt.Errorf("cannot decode %v into a MinKey", bsontype.String),
				},
				{
					"ReadMinKey Error",
					primitive.MinKey{},
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.MinKey, Err: errors.New("rn error"), ErrAfter: bsonrwtest.ReadMinKey},
					bsonrwtest.ReadMinKey,
					errors.New("rn error"),
				},
				{
					"success",
					primitive.MinKey{},
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.MinKey},
					bsonrwtest.ReadMinKey,
					nil,
				},
				{
					"decode null",
					primitive.MinKey{},
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Null},
					bsonrwtest.ReadNull,
					nil,
				},
				{
					"decode undefined",
					primitive.MinKey{},
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Undefined},
					bsonrwtest.ReadUndefined,
					nil,
				},
			},
		},
		{
			"MaxKeyDecodeValue",
			ValueDecoderFunc(dvd.MaxKeyDecodeValue),
			[]subtest{
				{
					"wrong type",
					wrong,
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.MaxKey},
					bsonrwtest.Nothing,
					ValueDecoderError{Name: "MaxKeyDecodeValue", Types: []reflect.Type{tMaxKey}, Received: reflect.ValueOf(wrong)},
				},
				{
					"type not null",
					primitive.MaxKey{},
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.String},
					bsonrwtest.Nothing,
					fmt.Errorf("cannot decode %v into a MaxKey", bsontype.String),
				},
				{
					"ReadMaxKey Error",
					primitive.MaxKey{},
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.MaxKey, Err: errors.New("rn error"), ErrAfter: bsonrwtest.ReadMaxKey},
					bsonrwtest.ReadMaxKey,
					errors.New("rn error"),
				},
				{
					"success",
					primitive.MaxKey{},
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.MaxKey},
					bsonrwtest.ReadMaxKey,
					nil,
				},
				{
					"decode null",
					primitive.MaxKey{},
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Null},
					bsonrwtest.ReadNull,
					nil,
				},
				{
					"decode undefined",
					primitive.MaxKey{},
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Undefined},
					bsonrwtest.ReadUndefined,
					nil,
				},
			},
		},
		{
			"JavaScriptDecodeValue",
			ValueDecoderFunc(dvd.JavaScriptDecodeValue),
			[]subtest{
				{
					"wrong type",
					wrong,
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.JavaScript, Return: ""},
					bsonrwtest.Nothing,
					ValueDecoderError{Name: "JavaScriptDecodeValue", Types: []reflect.Type{tJavaScript}, Received: reflect.ValueOf(wrong)},
				},
				{
					"type not Javascript",
					primitive.JavaScript(""),
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.String},
					bsonrwtest.Nothing,
					fmt.Errorf("cannot decode %v into a primitive.JavaScript", bsontype.String),
				},
				{
					"ReadJavascript Error",
					primitive.JavaScript(""),
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.JavaScript, Err: errors.New("rjs error"), ErrAfter: bsonrwtest.ReadJavascript},
					bsonrwtest.ReadJavascript,
					errors.New("rjs error"),
				},
				{
					"JavaScript/success",
					primitive.JavaScript("var hello = 'world';"),
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.JavaScript, Return: "var hello = 'world';"},
					bsonrwtest.ReadJavascript,
					nil,
				},
				{
					"decode null",
					primitive.JavaScript(""),
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Null},
					bsonrwtest.ReadNull,
					nil,
				},
				{
					"decode undefined",
					primitive.JavaScript(""),
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Undefined},
					bsonrwtest.ReadUndefined,
					nil,
				},
			},
		},
		{
			"SymbolDecodeValue",
			ValueDecoderFunc(dvd.SymbolDecodeValue),
			[]subtest{
				{
					"wrong type",
					wrong,
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Symbol, Return: ""},
					bsonrwtest.Nothing,
					ValueDecoderError{Name: "SymbolDecodeValue", Types: []reflect.Type{tSymbol}, Received: reflect.ValueOf(wrong)},
				},
				{
					"type not Symbol",
					primitive.Symbol(""),
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Int32},
					bsonrwtest.Nothing,
					fmt.Errorf("cannot decode %v into a primitive.Symbol", bsontype.Int32),
				},
				{
					"ReadSymbol Error",
					primitive.Symbol(""),
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Symbol, Err: errors.New("rjs error"), ErrAfter: bsonrwtest.ReadSymbol},
					bsonrwtest.ReadSymbol,
					errors.New("rjs error"),
				},
				{
					"Symbol/success",
					primitive.Symbol("var hello = 'world';"),
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Symbol, Return: "var hello = 'world';"},
					bsonrwtest.ReadSymbol,
					nil,
				},
				{
					"decode null",
					primitive.Symbol(""),
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Null},
					bsonrwtest.ReadNull,
					nil,
				},
				{
					"decode undefined",
					primitive.Symbol(""),
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Undefined},
					bsonrwtest.ReadUndefined,
					nil,
				},
			},
		},
		{
			"CoreDocumentDecodeValue",
			ValueDecoderFunc(dvd.CoreDocumentDecodeValue),
			[]subtest{
				{
					"wrong type",
					wrong,
					nil,
					&bsonrwtest.ValueReaderWriter{},
					bsonrwtest.Nothing,
					ValueDecoderError{
						Name:     "CoreDocumentDecodeValue",
						Types:    []reflect.Type{tCoreDocument},
						Received: reflect.ValueOf(wrong),
					},
				},
				{
					"*bsoncore.Document is nil",
					(*bsoncore.Document)(nil),
					nil,
					nil,
					bsonrwtest.Nothing,
					ValueDecoderError{
						Name:     "CoreDocumentDecodeValue",
						Types:    []reflect.Type{tCoreDocument},
						Received: reflect.ValueOf((*bsoncore.Document)(nil)),
					},
				},
				{
					"Copy error",
					bsoncore.Document{},
					nil,
					&bsonrwtest.ValueReaderWriter{Err: errors.New("copy error"), ErrAfter: bsonrwtest.ReadDocument},
					bsonrwtest.ReadDocument,
					errors.New("copy error"),
				},
			},
		},
		{
			"StructCodec.DecodeValue",
			defaultTestStructCodec,
			[]subtest{
				{
					"Not struct",
					reflect.New(reflect.TypeOf(struct{ Foo string }{})).Elem().Interface(),
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.String},
					bsonrwtest.Nothing,
					errors.New("cannot decode string into a struct { Foo string }"),
				},
				{
					"decode null",
					reflect.New(reflect.TypeOf(struct{ Foo string }{})).Elem().Interface(),
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Null},
					bsonrwtest.ReadNull,
					nil,
				},
				{
					"decode undefined",
					reflect.New(reflect.TypeOf(struct{ Foo string }{})).Elem().Interface(),
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Undefined},
					bsonrwtest.ReadUndefined,
					nil,
				},
			},
		},
		{
			"CodeWithScopeDecodeValue",
			ValueDecoderFunc(dvd.CodeWithScopeDecodeValue),
			[]subtest{
				{
					"wrong type",
					wrong,
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.CodeWithScope},
					bsonrwtest.Nothing,
					ValueDecoderError{
						Name:     "CodeWithScopeDecodeValue",
						Types:    []reflect.Type{tCodeWithScope},
						Received: reflect.ValueOf(wrong),
					},
				},
				{
					"type not codewithscope",
					primitive.CodeWithScope{},
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.String},
					bsonrwtest.Nothing,
					fmt.Errorf("cannot decode %v into a primitive.CodeWithScope", bsontype.String),
				},
				{
					"ReadCodeWithScope Error",
					primitive.CodeWithScope{},
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.CodeWithScope, Err: errors.New("rcws error"), ErrAfter: bsonrwtest.ReadCodeWithScope},
					bsonrwtest.ReadCodeWithScope,
					errors.New("rcws error"),
				},
				{
					"decodeDocument Error",
					primitive.CodeWithScope{
						Code:  "var hello = 'world';",
						Scope: primitive.D{{"foo", nil}},
					},
					&DecodeContext{Registry: buildDefaultRegistry()},
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.CodeWithScope, Err: errors.New("dd error"), ErrAfter: bsonrwtest.ReadElement},
					bsonrwtest.ReadElement,
					errors.New("dd error"),
				},
				{
					"decode null",
					primitive.CodeWithScope{},
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Null},
					bsonrwtest.ReadNull,
					nil,
				},
				{
					"decode undefined",
					primitive.CodeWithScope{},
					nil,
					&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Undefined},
					bsonrwtest.ReadUndefined,
					nil,
				},
			},
		},
		{
			"CoreArrayDecodeValue",
			defaultArrayCodec,
			[]subtest{
				{
					"wrong type",
					wrong,
					nil,
					&bsonrwtest.ValueReaderWriter{},
					bsonrwtest.Nothing,
					ValueDecoderError{
						Name:     "CoreArrayDecodeValue",
						Types:    []reflect.Type{tCoreArray},
						Received: reflect.ValueOf(wrong),
					},
				},
				{
					"*bsoncore.Array is nil",
					(*bsoncore.Array)(nil),
					nil,
					nil,
					bsonrwtest.Nothing,
					ValueDecoderError{
						Name:     "CoreArrayDecodeValue",
						Types:    []reflect.Type{tCoreArray},
						Received: reflect.ValueOf((*bsoncore.Array)(nil)),
					},
				},
			},
		},
	}

	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			for _, rc := range tc.subtests {
				t.Run(rc.name, func(t *testing.T) {
					var dc DecodeContext
					if rc.dctx != nil {
						dc = *rc.dctx
					}
					llvrw := new(bsonrwtest.ValueReaderWriter)
					if rc.llvrw != nil {
						llvrw = rc.llvrw
					}
					llvrw.T = t
					// var got interface{}
					if rc.val == cansetreflectiontest { // We're doing a CanSet reflection test
						err := tc.vd.DecodeValue(dc, llvrw, reflect.Value{})
						if !compareErrors(err, rc.err) {
							t.Errorf("Errors do not match. got %v; want %v", err, rc.err)
						}

						val := reflect.New(reflect.TypeOf(rc.val)).Elem()
						err = tc.vd.DecodeValue(dc, llvrw, val)
						if !compareErrors(err, rc.err) {
							t.Errorf("Errors do not match. got %v; want %v", err, rc.err)
						}
						return
					}
					if rc.val == cansettest { // We're doing an IsValid and CanSet test
						wanterr, ok := rc.err.(ValueDecoderError)
						if !ok {
							t.Fatalf("Error must be a DecodeValueError, but got a %T", rc.err)
						}

						err := tc.vd.DecodeValue(dc, llvrw, reflect.Value{})
						wanterr.Received = reflect.ValueOf(nil)
						if !compareErrors(err, wanterr) {
							t.Errorf("Errors do not match. got %v; want %v", err, wanterr)
						}

						err = tc.vd.DecodeValue(dc, llvrw, reflect.ValueOf(int(12345)))
						wanterr.Received = reflect.ValueOf(int(12345))
						if !compareErrors(err, wanterr) {
							t.Errorf("Errors do not match. got %v; want %v", err, wanterr)
						}
						return
					}
					var val reflect.Value
					if rtype := reflect.TypeOf(rc.val); rtype != nil {
						val = reflect.New(rtype).Elem()
					}
					want := rc.val
					defer func() {
						if err := recover(); err != nil {
							fmt.Println(t.Name())
							panic(err)
						}
					}()
					err := tc.vd.DecodeValue(dc, llvrw, val)
					if !compareErrors(err, rc.err) {
						t.Errorf("Errors do not match. got %v; want %v", err, rc.err)
					}
					invoked := llvrw.Invoked
					if !cmp.Equal(invoked, rc.invoke) {
						t.Errorf("Incorrect method invoked. got %v; want %v", invoked, rc.invoke)
					}
					var got interface{}
					if val.IsValid() && val.CanInterface() {
						got = val.Interface()
					}
					if rc.err == nil && !cmp.Equal(got, want, cmp.Comparer(compareDecimal128)) {
						t.Errorf("Values do not match. got (%T)%v; want (%T)%v", got, got, want, want)
					}
				})
			}
		})
	}

	t.Run("CodeWithScopeCodec/DecodeValue/success", func(t *testing.T) {
		dc := DecodeContext{Registry: buildDefaultRegistry()}
		b := bsoncore.BuildDocument(nil,
			bsoncore.AppendCodeWithScopeElement(
				nil, "foo", "var hello = 'world';",
				buildDocument(bsoncore.AppendNullElement(nil, "bar")),
			),
		)
		dvr := bsonrw.NewBSONDocumentReader(b)
		dr, err := dvr.ReadDocument()
		noerr(t, err)
		_, vr, err := dr.ReadElement()
		noerr(t, err)

		want := primitive.CodeWithScope{
			Code:  "var hello = 'world';",
			Scope: primitive.D{{"bar", nil}},
		}
		val := reflect.New(tCodeWithScope).Elem()
		err = dvd.CodeWithScopeDecodeValue(dc, vr, val)
		noerr(t, err)

		got := val.Interface().(primitive.CodeWithScope)
		if got.Code != want.Code && !cmp.Equal(got.Scope, want.Scope) {
			t.Errorf("CodeWithScopes do not match. got %v; want %v", got, want)
		}
	})
	t.Run("ValueUnmarshalerDecodeValue/UnmarshalBSONValue error", func(t *testing.T) {
		var dc DecodeContext
		llvrw := &bsonrwtest.ValueReaderWriter{BSONType: bsontype.String, Return: string("hello, world!")}
		llvrw.T = t

		want := errors.New("ubsonv error")
		valUnmarshaler := &testValueUnmarshaler{err: want}
		got := dvd.ValueUnmarshalerDecodeValue(dc, llvrw, reflect.ValueOf(valUnmarshaler))
		if !compareErrors(got, want) {
			t.Errorf("Errors do not match. got %v; want %v", got, want)
		}
	})
	t.Run("ValueUnmarshalerDecodeValue/Unaddressable value", func(t *testing.T) {
		var dc DecodeContext
		llvrw := &bsonrwtest.ValueReaderWriter{BSONType: bsontype.String, Return: string("hello, world!")}
		llvrw.T = t

		val := reflect.ValueOf(testValueUnmarshaler{})
		want := ValueDecoderError{Name: "ValueUnmarshalerDecodeValue", Types: []reflect.Type{tValueUnmarshaler}, Received: val}
		got := dvd.ValueUnmarshalerDecodeValue(dc, llvrw, val)
		if !compareErrors(got, want) {
			t.Errorf("Errors do not match. got %v; want %v", got, want)
		}
	})

	t.Run("SliceCodec/DecodeValue/can't set slice", func(t *testing.T) {
		var val []string
		want := ValueDecoderError{Name: "SliceDecodeValue", Kinds: []reflect.Kind{reflect.Slice}, Received: reflect.ValueOf(val)}
		got := dvd.SliceDecodeValue(DecodeContext{}, nil, reflect.ValueOf(val))
		if !compareErrors(got, want) {
			t.Errorf("Errors do not match. got %v; want %v", got, want)
		}
	})
	t.Run("SliceCodec/DecodeValue/too many elements", func(t *testing.T) {
		idx, doc := bsoncore.AppendDocumentStart(nil)
		aidx, doc := bsoncore.AppendArrayElementStart(doc, "foo")
		doc = bsoncore.AppendStringElement(doc, "0", "foo")
		doc = bsoncore.AppendStringElement(doc, "1", "bar")
		doc, err := bsoncore.AppendArrayEnd(doc, aidx)
		noerr(t, err)
		doc, err = bsoncore.AppendDocumentEnd(doc, idx)
		noerr(t, err)
		dvr := bsonrw.NewBSONDocumentReader(doc)
		noerr(t, err)
		dr, err := dvr.ReadDocument()
		noerr(t, err)
		_, vr, err := dr.ReadElement()
		noerr(t, err)
		var val [1]string
		want := fmt.Errorf("more elements returned in array than can fit inside %T, got 2 elements", val)

		dc := DecodeContext{Registry: buildDefaultRegistry()}
		got := dvd.ArrayDecodeValue(dc, vr, reflect.ValueOf(val))
		if !compareErrors(got, want) {
			t.Errorf("Errors do not match. got %v; want %v", got, want)
		}
	})

	t.Run("success path", func(t *testing.T) {
		oid := primitive.NewObjectID()
		oids := []primitive.ObjectID{primitive.NewObjectID(), primitive.NewObjectID(), primitive.NewObjectID()}
		var str = new(string)
		*str = "bar"
		now := time.Now().Truncate(time.Millisecond).UTC()
		murl, err := url.Parse("https://mongodb.com/random-url?hello=world")
		if err != nil {
			t.Errorf("Error parsing URL: %v", err)
			t.FailNow()
		}
		decimal128, err := primitive.ParseDecimal128("1.5e10")
		if err != nil {
			t.Errorf("Error parsing decimal128: %v", err)
			t.FailNow()
		}

		testCases := []struct {
			name  string
			value interface{}
			b     []byte
			err   error
		}{
			{
				"map[string]int",
				map[string]int32{"foo": 1},
				[]byte{
					0x0E, 0x00, 0x00, 0x00,
					0x10, 'f', 'o', 'o', 0x00,
					0x01, 0x00, 0x00, 0x00,
					0x00,
				},
				nil,
			},
			{
				"map[string]primitive.ObjectID",
				map[string]primitive.ObjectID{"foo": oid},
				func() []byte {
					idx, doc := bsoncore.AppendDocumentStart(nil)
					doc = bsoncore.AppendObjectIDElement(doc, "foo", oid)
					doc, _ = bsoncore.AppendDocumentEnd(doc, idx)
					return doc
				}(),
				nil,
			},
			{
				"map[string][]int32",
				map[string][]int32{"Z": {1, 2, 3}},
				buildDocumentArray(func(doc []byte) []byte {
					doc = bsoncore.AppendInt32Element(doc, "0", 1)
					doc = bsoncore.AppendInt32Element(doc, "1", 2)
					return bsoncore.AppendInt32Element(doc, "2", 3)
				}),
				nil,
			},
			{
				"map[string][]primitive.ObjectID",
				map[string][]primitive.ObjectID{"Z": oids},
				buildDocumentArray(func(doc []byte) []byte {
					doc = bsoncore.AppendObjectIDElement(doc, "0", oids[0])
					doc = bsoncore.AppendObjectIDElement(doc, "1", oids[1])
					return bsoncore.AppendObjectIDElement(doc, "2", oids[2])
				}),
				nil,
			},
			{
				"map[string][]json.Number(int64)",
				map[string][]json.Number{"Z": {json.Number("5"), json.Number("10")}},
				buildDocumentArray(func(doc []byte) []byte {
					doc = bsoncore.AppendInt64Element(doc, "0", 5)
					return bsoncore.AppendInt64Element(doc, "1", 10)
				}),
				nil,
			},
			{
				"map[string][]json.Number(float64)",
				map[string][]json.Number{"Z": {json.Number("5"), json.Number("10.1")}},
				buildDocumentArray(func(doc []byte) []byte {
					doc = bsoncore.AppendInt64Element(doc, "0", 5)
					return bsoncore.AppendDoubleElement(doc, "1", 10.1)
				}),
				nil,
			},
			{
				"map[string][]*url.URL",
				map[string][]*url.URL{"Z": {murl}},
				buildDocumentArray(func(doc []byte) []byte {
					return bsoncore.AppendStringElement(doc, "0", murl.String())
				}),
				nil,
			},
			{
				"map[string][]primitive.Decimal128",
				map[string][]primitive.Decimal128{"Z": {decimal128}},
				buildDocumentArray(func(doc []byte) []byte {
					return bsoncore.AppendDecimal128Element(doc, "0", decimal128)
				}),
				nil,
			},
			{
				"map[mystring]interface{}",
				map[mystring]interface{}{"pi": 3.14159},
				buildDocument(bsoncore.AppendDoubleElement(nil, "pi", 3.14159)),
				nil,
			},
			{
				"-",
				struct {
					A string `bson:"-"`
				}{
					A: "",
				},
				[]byte{0x05, 0x00, 0x00, 0x00, 0x00},
				nil,
			},
			{
				"omitempty",
				struct {
					A string `bson:",omitempty"`
				}{
					A: "",
				},
				[]byte{0x05, 0x00, 0x00, 0x00, 0x00},
				nil,
			},
			{
				"omitempty, empty time",
				struct {
					A time.Time `bson:",omitempty"`
				}{
					A: time.Time{},
				},
				[]byte{0x05, 0x00, 0x00, 0x00, 0x00},
				nil,
			},
			{
				"no private fields",
				noPrivateFields{a: "should be empty"},
				[]byte{0x05, 0x00, 0x00, 0x00, 0x00},
				nil,
			},
			{
				"minsize",
				struct {
					A int64 `bson:",minsize"`
				}{
					A: 12345,
				},
				buildDocument(bsoncore.AppendInt32Element(nil, "a", 12345)),
				nil,
			},
			{
				"inline",
				struct {
					Foo struct {
						A int64 `bson:",minsize"`
					} `bson:",inline"`
				}{
					Foo: struct {
						A int64 `bson:",minsize"`
					}{
						A: 12345,
					},
				},
				buildDocument(bsoncore.AppendInt32Element(nil, "a", 12345)),
				nil,
			},
			{
				"inline struct pointer",
				struct {
					Foo *struct {
						A int64 `bson:",minsize"`
					} `bson:",inline"`
					Bar *struct {
						B int64
					} `bson:",inline"`
				}{
					Foo: &struct {
						A int64 `bson:",minsize"`
					}{
						A: 12345,
					},
					Bar: nil,
				},
				buildDocument(bsoncore.AppendInt32Element(nil, "a", 12345)),
				nil,
			},
			{
				"nested inline struct pointer",
				struct {
					Foo *struct {
						Bar *struct {
							A int64 `bson:",minsize"`
						} `bson:",inline"`
					} `bson:",inline"`
				}{
					Foo: &struct {
						Bar *struct {
							A int64 `bson:",minsize"`
						} `bson:",inline"`
					}{
						Bar: &struct {
							A int64 `bson:",minsize"`
						}{
							A: 12345,
						},
					},
				},
				buildDocument(bsoncore.AppendInt32Element(nil, "a", 12345)),
				nil,
			},
			{
				"inline nil struct pointer",
				struct {
					Foo *struct {
						A int64 `bson:",minsize"`
					} `bson:",inline"`
				}{
					Foo: nil,
				},
				buildDocument([]byte{}),
				nil,
			},
			{
				"inline overwrite",
				struct {
					Foo struct {
						A int32
						B string
					} `bson:",inline"`
					A int64
				}{
					Foo: struct {
						A int32
						B string
					}{
						A: 0,
						B: "foo",
					},
					A: 54321,
				},
				buildDocument(func(doc []byte) []byte {
					doc = bsoncore.AppendStringElement(doc, "b", "foo")
					doc = bsoncore.AppendInt64Element(doc, "a", 54321)
					return doc
				}(nil)),
				nil,
			},
			{
				"inline overwrite with nested structs",
				struct {
					Foo struct {
						A int32
					} `bson:",inline"`
					Bar struct {
						A int32
					} `bson:",inline"`
					A int64
				}{
					Foo: struct {
						A int32
					}{},
					Bar: struct {
						A int32
					}{},
					A: 54321,
				},
				buildDocument(bsoncore.AppendInt64Element(nil, "a", 54321)),
				nil,
			},
			{
				"inline map",
				struct {
					Foo map[string]string `bson:",inline"`
				}{
					Foo: map[string]string{"foo": "bar"},
				},
				buildDocument(bsoncore.AppendStringElement(nil, "foo", "bar")),
				nil,
			},
			{
				"alternate name bson:name",
				struct {
					A string `bson:"foo"`
				}{
					A: "bar",
				},
				buildDocument(bsoncore.AppendStringElement(nil, "foo", "bar")),
				nil,
			},
			{
				"alternate name",
				struct {
					A string `bson:"foo"`
				}{
					A: "bar",
				},
				buildDocument(bsoncore.AppendStringElement(nil, "foo", "bar")),
				nil,
			},
			{
				"inline, omitempty",
				struct {
					A   string
					Foo zeroTest `bson:"omitempty,inline"`
				}{
					A:   "bar",
					Foo: zeroTest{true},
				},
				buildDocument(bsoncore.AppendStringElement(nil, "a", "bar")),
				nil,
			},
			{
				"struct{}",
				struct {
					A bool
					B int32
					C int64
					D uint16
					E uint64
					F float64
					G string
					H map[string]string
					I []byte
					K [2]string
					L struct {
						M string
					}
					Q  primitive.ObjectID
					T  []struct{}
					Y  json.Number
					Z  time.Time
					AA json.Number
					AB *url.URL
					AC primitive.Decimal128
					AD *time.Time
					AE *testValueUnmarshaler
					AF *bool
					AG *bool
					AH *int32
					AI *int64
					AJ *primitive.ObjectID
					AK *primitive.ObjectID
					AL testValueUnmarshaler
					AM interface{}
					AN interface{}
					AO interface{}
					AP primitive.D
					AQ primitive.A
					AR [2]primitive.E
					AS []byte
					AT map[string]interface{}
					AU primitive.CodeWithScope
					AV primitive.M
					AW primitive.D
					AX map[string]interface{}
					AY []primitive.E
					AZ interface{}
				}{
					A: true,
					B: 123,
					C: 456,
					D: 789,
					E: 101112,
					F: 3.14159,
					G: "Hello, world",
					H: map[string]string{"foo": "bar"},
					I: []byte{0x01, 0x02, 0x03},
					K: [2]string{"baz", "qux"},
					L: struct {
						M string
					}{
						M: "foobar",
					},
					Q:  oid,
					T:  nil,
					Y:  json.Number("5"),
					Z:  now,
					AA: json.Number("10.1"),
					AB: murl,
					AC: decimal128,
					AD: &now,
					AE: &testValueUnmarshaler{t: bsontype.String, val: bsoncore.AppendString(nil, "hello, world!")},
					AF: func(b bool) *bool { return &b }(true),
					AG: nil,
					AH: func(i32 int32) *int32 { return &i32 }(12345),
					AI: func(i64 int64) *int64 { return &i64 }(1234567890),
					AJ: &oid,
					AK: nil,
					AL: testValueUnmarshaler{t: bsontype.String, val: bsoncore.AppendString(nil, "hello, world!")},
					AM: "hello, world",
					AN: int32(12345),
					AO: oid,
					AP: primitive.D{{"foo", "bar"}},
					AQ: primitive.A{"foo", "bar"},
					AR: [2]primitive.E{{"hello", "world"}, {"pi", 3.14159}},
					AS: nil,
					AT: nil,
					AU: primitive.CodeWithScope{Code: "var hello = 'world';", Scope: primitive.D{{"pi", 3.14159}}},
					AV: primitive.M{"foo": primitive.M{"bar": "baz"}},
					AW: primitive.D{{"foo", primitive.D{{"bar", "baz"}}}},
					AX: map[string]interface{}{"foo": map[string]interface{}{"bar": "baz"}},
					AY: []primitive.E{{"foo", []primitive.E{{"bar", "baz"}}}},
					AZ: primitive.D{{"foo", primitive.D{{"bar", "baz"}}}},
				},
				buildDocument(func(doc []byte) []byte {
					doc = bsoncore.AppendBooleanElement(doc, "a", true)
					doc = bsoncore.AppendInt32Element(doc, "b", 123)
					doc = bsoncore.AppendInt64Element(doc, "c", 456)
					doc = bsoncore.AppendInt32Element(doc, "d", 789)
					doc = bsoncore.AppendInt64Element(doc, "e", 101112)
					doc = bsoncore.AppendDoubleElement(doc, "f", 3.14159)
					doc = bsoncore.AppendStringElement(doc, "g", "Hello, world")
					doc = bsoncore.AppendDocumentElement(doc, "h", buildDocument(bsoncore.AppendStringElement(nil, "foo", "bar")))
					doc = bsoncore.AppendBinaryElement(doc, "i", 0x00, []byte{0x01, 0x02, 0x03})
					doc = bsoncore.AppendArrayElement(doc, "k",
						buildArray(bsoncore.AppendStringElement(bsoncore.AppendStringElement(nil, "0", "baz"), "1", "qux")),
					)
					doc = bsoncore.AppendDocumentElement(doc, "l", buildDocument(bsoncore.AppendStringElement(nil, "m", "foobar")))
					doc = bsoncore.AppendObjectIDElement(doc, "q", oid)
					doc = bsoncore.AppendNullElement(doc, "t")
					doc = bsoncore.AppendInt64Element(doc, "y", 5)
					doc = bsoncore.AppendDateTimeElement(doc, "z", now.UnixNano()/int64(time.Millisecond))
					doc = bsoncore.AppendDoubleElement(doc, "aa", 10.1)
					doc = bsoncore.AppendStringElement(doc, "ab", murl.String())
					doc = bsoncore.AppendDecimal128Element(doc, "ac", decimal128)
					doc = bsoncore.AppendDateTimeElement(doc, "ad", now.UnixNano()/int64(time.Millisecond))
					doc = bsoncore.AppendStringElement(doc, "ae", "hello, world!")
					doc = bsoncore.AppendBooleanElement(doc, "af", true)
					doc = bsoncore.AppendNullElement(doc, "ag")
					doc = bsoncore.AppendInt32Element(doc, "ah", 12345)
					doc = bsoncore.AppendInt32Element(doc, "ai", 1234567890)
					doc = bsoncore.AppendObjectIDElement(doc, "aj", oid)
					doc = bsoncore.AppendNullElement(doc, "ak")
					doc = bsoncore.AppendStringElement(doc, "al", "hello, world!")
					doc = bsoncore.AppendStringElement(doc, "am", "hello, world")
					doc = bsoncore.AppendInt32Element(doc, "an", 12345)
					doc = bsoncore.AppendObjectIDElement(doc, "ao", oid)
					doc = bsoncore.AppendDocumentElement(doc, "ap", buildDocument(bsoncore.AppendStringElement(nil, "foo", "bar")))
					doc = bsoncore.AppendArrayElement(doc, "aq",
						buildArray(bsoncore.AppendStringElement(bsoncore.AppendStringElement(nil, "0", "foo"), "1", "bar")),
					)
					doc = bsoncore.AppendDocumentElement(doc, "ar",
						buildDocument(bsoncore.AppendDoubleElement(bsoncore.AppendStringElement(nil, "hello", "world"), "pi", 3.14159)),
					)
					doc = bsoncore.AppendNullElement(doc, "as")
					doc = bsoncore.AppendNullElement(doc, "at")
					doc = bsoncore.AppendCodeWithScopeElement(doc, "au",
						"var hello = 'world';", buildDocument(bsoncore.AppendDoubleElement(nil, "pi", 3.14159)),
					)
					for _, name := range [5]string{"av", "aw", "ax", "ay", "az"} {
						doc = bsoncore.AppendDocumentElement(doc, name, buildDocument(
							bsoncore.AppendDocumentElement(nil, "foo", buildDocument(
								bsoncore.AppendStringElement(nil, "bar", "baz"),
							)),
						))
					}
					return doc
				}(nil)),
				nil,
			},
			{
				"struct{[]interface{}}",
				struct {
					A []bool
					B []int32
					C []int64
					D []uint16
					E []uint64
					F []float64
					G []string
					H []map[string]string
					I [][]byte
					K [1][2]string
					L []struct {
						M string
					}
					N  [][]string
					R  []primitive.ObjectID
					T  []struct{}
					W  []map[string]struct{}
					X  []map[string]struct{}
					Y  []map[string]struct{}
					Z  []time.Time
					AA []json.Number
					AB []*url.URL
					AC []primitive.Decimal128
					AD []*time.Time
					AE []*testValueUnmarshaler
					AF []*bool
					AG []*int32
					AH []*int64
					AI []*primitive.ObjectID
					AJ []primitive.D
					AK []primitive.A
					AL [][2]primitive.E
				}{
					A: []bool{true},
					B: []int32{123},
					C: []int64{456},
					D: []uint16{789},
					E: []uint64{101112},
					F: []float64{3.14159},
					G: []string{"Hello, world"},
					H: []map[string]string{{"foo": "bar"}},
					I: [][]byte{{0x01, 0x02, 0x03}},
					K: [1][2]string{{"baz", "qux"}},
					L: []struct {
						M string
					}{
						{
							M: "foobar",
						},
					},
					N:  [][]string{{"foo", "bar"}},
					R:  oids,
					T:  nil,
					W:  nil,
					X:  []map[string]struct{}{},   // Should be empty BSON Array
					Y:  []map[string]struct{}{{}}, // Should be BSON array with one element, an empty BSON SubDocument
					Z:  []time.Time{now, now},
					AA: []json.Number{json.Number("5"), json.Number("10.1")},
					AB: []*url.URL{murl},
					AC: []primitive.Decimal128{decimal128},
					AD: []*time.Time{&now, &now},
					AE: []*testValueUnmarshaler{
						{t: bsontype.String, val: bsoncore.AppendString(nil, "hello")},
						{t: bsontype.String, val: bsoncore.AppendString(nil, "world")},
					},
					AF: []*bool{pbool(true), nil},
					AG: []*int32{pi32(12345), nil},
					AH: []*int64{pi64(1234567890), nil, pi64(9012345678)},
					AI: []*primitive.ObjectID{&oid, nil},
					AJ: []primitive.D{{{"foo", "bar"}}, nil},
					AK: []primitive.A{{"foo", "bar"}, nil},
					AL: [][2]primitive.E{{{"hello", "world"}, {"pi", 3.14159}}},
				},
				buildDocument(func(doc []byte) []byte {
					doc = appendArrayElement(doc, "a", bsoncore.AppendBooleanElement(nil, "0", true))
					doc = appendArrayElement(doc, "b", bsoncore.AppendInt32Element(nil, "0", 123))
					doc = appendArrayElement(doc, "c", bsoncore.AppendInt64Element(nil, "0", 456))
					doc = appendArrayElement(doc, "d", bsoncore.AppendInt32Element(nil, "0", 789))
					doc = appendArrayElement(doc, "e", bsoncore.AppendInt64Element(nil, "0", 101112))
					doc = appendArrayElement(doc, "f", bsoncore.AppendDoubleElement(nil, "0", 3.14159))
					doc = appendArrayElement(doc, "g", bsoncore.AppendStringElement(nil, "0", "Hello, world"))
					doc = appendArrayElement(doc, "h", bsoncore.BuildDocumentElement(nil, "0", bsoncore.AppendStringElement(nil, "foo", "bar")))
					doc = appendArrayElement(doc, "i", bsoncore.AppendBinaryElement(nil, "0", 0x00, []byte{0x01, 0x02, 0x03}))
					doc = appendArrayElement(doc, "k",
						appendArrayElement(nil, "0",
							bsoncore.AppendStringElement(bsoncore.AppendStringElement(nil, "0", "baz"), "1", "qux")),
					)
					doc = appendArrayElement(doc, "l", bsoncore.BuildDocumentElement(nil, "0", bsoncore.AppendStringElement(nil, "m", "foobar")))
					doc = appendArrayElement(doc, "n",
						appendArrayElement(nil, "0",
							bsoncore.AppendStringElement(bsoncore.AppendStringElement(nil, "0", "foo"), "1", "bar")),
					)
					doc = appendArrayElement(doc, "r",
						bsoncore.AppendObjectIDElement(
							bsoncore.AppendObjectIDElement(
								bsoncore.AppendObjectIDElement(nil,
									"0", oids[0]),
								"1", oids[1]),
							"2", oids[2]),
					)
					doc = bsoncore.AppendNullElement(doc, "t")
					doc = bsoncore.AppendNullElement(doc, "w")
					doc = appendArrayElement(doc, "x", nil)
					doc = appendArrayElement(doc, "y", bsoncore.BuildDocumentElement(nil, "0", nil))
					doc = appendArrayElement(doc, "z",
						bsoncore.AppendDateTimeElement(
							bsoncore.AppendDateTimeElement(
								nil, "0", now.UnixNano()/int64(time.Millisecond)),
							"1", now.UnixNano()/int64(time.Millisecond)),
					)
					doc = appendArrayElement(doc, "aa", bsoncore.AppendDoubleElement(bsoncore.AppendInt64Element(nil, "0", 5), "1", 10.10))
					doc = appendArrayElement(doc, "ab", bsoncore.AppendStringElement(nil, "0", murl.String()))
					doc = appendArrayElement(doc, "ac", bsoncore.AppendDecimal128Element(nil, "0", decimal128))
					doc = appendArrayElement(doc, "ad",
						bsoncore.AppendDateTimeElement(
							bsoncore.AppendDateTimeElement(nil, "0", now.UnixNano()/int64(time.Millisecond)),
							"1", now.UnixNano()/int64(time.Millisecond)),
					)
					doc = appendArrayElement(doc, "ae",
						bsoncore.AppendStringElement(bsoncore.AppendStringElement(nil, "0", "hello"), "1", "world"),
					)
					doc = appendArrayElement(doc, "af",
						bsoncore.AppendNullElement(bsoncore.AppendBooleanElement(nil, "0", true), "1"),
					)
					doc = appendArrayElement(doc, "ag",
						bsoncore.AppendNullElement(bsoncore.AppendInt32Element(nil, "0", 12345), "1"),
					)
					doc = appendArrayElement(doc, "ah",
						bsoncore.AppendInt64Element(
							bsoncore.AppendNullElement(bsoncore.AppendInt64Element(nil, "0", 1234567890), "1"),
							"2", 9012345678,
						),
					)
					doc = appendArrayElement(doc, "ai",
						bsoncore.AppendNullElement(bsoncore.AppendObjectIDElement(nil, "0", oid), "1"),
					)
					doc = appendArrayElement(doc, "aj",
						bsoncore.AppendNullElement(
							bsoncore.AppendDocumentElement(nil, "0", buildDocument(bsoncore.AppendStringElement(nil, "foo", "bar"))),
							"1",
						),
					)
					doc = appendArrayElement(doc, "ak",
						bsoncore.AppendNullElement(
							appendArrayElement(nil, "0",
								bsoncore.AppendStringElement(bsoncore.AppendStringElement(nil, "0", "foo"), "1", "bar"),
							),
							"1",
						),
					)
					doc = appendArrayElement(doc, "al",
						bsoncore.BuildDocumentElement(nil, "0",
							bsoncore.AppendDoubleElement(bsoncore.AppendStringElement(nil, "hello", "world"), "pi", 3.14159),
						),
					)
					return doc
				}(nil)),
				nil,
			},
		}

		t.Run("Decode", func(t *testing.T) {
			for _, tc := range testCases {
				t.Run(tc.name, func(t *testing.T) {
					vr := bsonrw.NewBSONDocumentReader(tc.b)
					reg := buildDefaultRegistry()
					vtype := reflect.TypeOf(tc.value)
					dec, err := reg.LookupDecoder(vtype)
					noerr(t, err)

					gotVal := reflect.New(reflect.TypeOf(tc.value)).Elem()
					err = dec.DecodeValue(DecodeContext{Registry: reg}, vr, gotVal)
					noerr(t, err)

					got := gotVal.Interface()
					want := tc.value
					if diff := cmp.Diff(
						got, want,
						cmp.Comparer(compareDecimal128),
						cmp.Comparer(compareNoPrivateFields),
						cmp.Comparer(compareZeroTest),
						cmp.Comparer(compareTime),
					); diff != "" {
						t.Errorf("difference:\n%s", diff)
						t.Errorf("Values are not equal.\ngot: %#v\nwant:%#v", got, want)
					}
				})
			}
		})
	})
	t.Run("error path", func(t *testing.T) {
		testCases := []struct {
			name  string
			value interface{}
			b     []byte
			err   error
		}{
			{
				"duplicate name struct",
				struct {
					A int64
					B int64 `bson:"a"`
				}{
					A: 0,
					B: 54321,
				},
				buildDocument(bsoncore.AppendInt32Element(nil, "a", 12345)),
				fmt.Errorf("duplicated key a"),
			},
		}

		for _, tc := range testCases {
			t.Run(tc.name, func(t *testing.T) {
				vr := bsonrw.NewBSONDocumentReader(tc.b)
				reg := buildDefaultRegistry()
				vtype := reflect.TypeOf(tc.value)
				dec, err := reg.LookupDecoder(vtype)
				noerr(t, err)

				gotVal := reflect.New(reflect.TypeOf(tc.value)).Elem()
				err = dec.DecodeValue(DecodeContext{Registry: reg}, vr, gotVal)
				if err == nil || !strings.Contains(err.Error(), tc.err.Error()) {
					t.Errorf("Did not receive expected error. got %v; want %v", err, tc.err)
				}
			})
		}
	})

	t.Run("defaultEmptyInterfaceCodec.DecodeValue", func(t *testing.T) {
		t.Run("DecodeValue", func(t *testing.T) {
			testCases := []struct {
				name     string
				val      interface{}
				bsontype bsontype.Type
			}{
				{
					"Double - float64",
					float64(3.14159),
					bsontype.Double,
				},
				{
					"String - string",
					"foo bar baz",
					bsontype.String,
				},
				{
					"Array - primitive.A",
					primitive.A{3.14159},
					bsontype.Array,
				},
				{
					"Binary - Binary",
					primitive.Binary{Subtype: 0xFF, Data: []byte{0x01, 0x02, 0x03}},
					bsontype.Binary,
				},
				{
					"Undefined - Undefined",
					primitive.Undefined{},
					bsontype.Undefined,
				},
				{
					"ObjectID - primitive.ObjectID",
					primitive.ObjectID{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C},
					bsontype.ObjectID,
				},
				{
					"Boolean - bool",
					bool(true),
					bsontype.Boolean,
				},
				{
					"DateTime - DateTime",
					primitive.DateTime(1234567890),
					bsontype.DateTime,
				},
				{
					"Null - Null",
					nil,
					bsontype.Null,
				},
				{
					"Regex - Regex",
					primitive.Regex{Pattern: "foo", Options: "bar"},
					bsontype.Regex,
				},
				{
					"DBPointer - DBPointer",
					primitive.DBPointer{
						DB:      "foobar",
						Pointer: primitive.ObjectID{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C},
					},
					bsontype.DBPointer,
				},
				{
					"JavaScript - JavaScript",
					primitive.JavaScript("var foo = 'bar';"),
					bsontype.JavaScript,
				},
				{
					"Symbol - Symbol",
					primitive.Symbol("foobarbazlolz"),
					bsontype.Symbol,
				},
				{
					"Int32 - int32",
					int32(123456),
					bsontype.Int32,
				},
				{
					"Int64 - int64",
					int64(1234567890),
					bsontype.Int64,
				},
				{
					"Timestamp - Timestamp",
					primitive.Timestamp{T: 12345, I: 67890},
					bsontype.Timestamp,
				},
				{
					"Decimal128 - decimal.Decimal128",
					primitive.NewDecimal128(12345, 67890),
					bsontype.Decimal128,
				},
				{
					"MinKey - MinKey",
					primitive.MinKey{},
					bsontype.MinKey,
				},
				{
					"MaxKey - MaxKey",
					primitive.MaxKey{},
					bsontype.MaxKey,
				},
			}
			for _, tc := range testCases {
				t.Run(tc.name, func(t *testing.T) {
					llvr := &bsonrwtest.ValueReaderWriter{BSONType: tc.bsontype}

					t.Run("Type Map failure", func(t *testing.T) {
						if tc.bsontype == bsontype.Null {
							t.Skip()
						}
						val := reflect.New(tEmpty).Elem()
						dc := DecodeContext{Registry: NewRegistryBuilder().Build()}
						want := ErrNoTypeMapEntry{Type: tc.bsontype}
						got := defaultEmptyInterfaceCodec.DecodeValue(dc, llvr, val)
						if !compareErrors(got, want) {
							t.Errorf("Errors are not equal. got %v; want %v", got, want)
						}
					})

					t.Run("Lookup failure", func(t *testing.T) {
						if tc.bsontype == bsontype.Null {
							t.Skip()
						}
						val := reflect.New(tEmpty).Elem()
						dc := DecodeContext{
							Registry: NewRegistryBuilder().
								RegisterTypeMapEntry(tc.bsontype, reflect.TypeOf(tc.val)).
								Build(),
						}
						want := ErrNoDecoder{Type: reflect.TypeOf(tc.val)}
						got := defaultEmptyInterfaceCodec.DecodeValue(dc, llvr, val)
						if !compareErrors(got, want) {
							t.Errorf("Errors are not equal. got %v; want %v", got, want)
						}
					})

					t.Run("DecodeValue failure", func(t *testing.T) {
						if tc.bsontype == bsontype.Null {
							t.Skip()
						}
						want := errors.New("DecodeValue failure error")
						llc := &llCodec{t: t, err: want}
						dc := DecodeContext{
							Registry: NewRegistryBuilder().
								RegisterTypeDecoder(reflect.TypeOf(tc.val), llc).
								RegisterTypeMapEntry(tc.bsontype, reflect.TypeOf(tc.val)).
								Build(),
						}
						got := defaultEmptyInterfaceCodec.DecodeValue(dc, llvr, reflect.New(tEmpty).Elem())
						if !compareErrors(got, want) {
							t.Errorf("Errors are not equal. got %v; want %v", got, want)
						}
					})

					t.Run("Success", func(t *testing.T) {
						want := tc.val
						llc := &llCodec{t: t, decodeval: tc.val}
						dc := DecodeContext{
							Registry: NewRegistryBuilder().
								RegisterTypeDecoder(reflect.TypeOf(tc.val), llc).
								RegisterTypeMapEntry(tc.bsontype, reflect.TypeOf(tc.val)).
								Build(),
						}
						got := reflect.New(tEmpty).Elem()
						err := defaultEmptyInterfaceCodec.DecodeValue(dc, llvr, got)
						noerr(t, err)
						if !cmp.Equal(got.Interface(), want, cmp.Comparer(compareDecimal128)) {
							t.Errorf("Did not receive expected value. got %v; want %v", got.Interface(), want)
						}
					})
				})
			}
		})

		t.Run("non-interface{}", func(t *testing.T) {
			val := uint64(1234567890)
			want := ValueDecoderError{Name: "EmptyInterfaceDecodeValue", Types: []reflect.Type{tEmpty}, Received: reflect.ValueOf(val)}
			got := defaultEmptyInterfaceCodec.DecodeValue(DecodeContext{}, nil, reflect.ValueOf(val))
			if !compareErrors(got, want) {
				t.Errorf("Errors are not equal. got %v; want %v", got, want)
			}
		})

		t.Run("nil *interface{}", func(t *testing.T) {
			var val interface{}
			want := ValueDecoderError{Name: "EmptyInterfaceDecodeValue", Types: []reflect.Type{tEmpty}, Received: reflect.ValueOf(val)}
			got := defaultEmptyInterfaceCodec.DecodeValue(DecodeContext{}, nil, reflect.ValueOf(val))
			if !compareErrors(got, want) {
				t.Errorf("Errors are not equal. got %v; want %v", got, want)
			}
		})

		t.Run("no type registered", func(t *testing.T) {
			llvr := &bsonrwtest.ValueReaderWriter{BSONType: bsontype.Double}
			want := ErrNoTypeMapEntry{Type: bsontype.Double}
			val := reflect.New(tEmpty).Elem()
			got := defaultEmptyInterfaceCodec.DecodeValue(DecodeContext{Registry: NewRegistryBuilder().Build()}, llvr, val)
			if !compareErrors(got, want) {
				t.Errorf("Errors are not equal. got %v; want %v", got, want)
			}
		})
		t.Run("top level document", func(t *testing.T) {
			data := bsoncore.BuildDocument(nil, bsoncore.AppendDoubleElement(nil, "pi", 3.14159))
			vr := bsonrw.NewBSONDocumentReader(data)
			want := primitive.D{{"pi", 3.14159}}
			var got interface{}
			val := reflect.ValueOf(&got).Elem()
			err := defaultEmptyInterfaceCodec.DecodeValue(DecodeContext{Registry: buildDefaultRegistry()}, vr, val)
			noerr(t, err)
			if !cmp.Equal(got, want) {
				t.Errorf("Did not get correct result. got %v; want %v", got, want)
			}
		})
		t.Run("custom type map entry", func(t *testing.T) {
			// registering a custom type map entry for both bsontype.Type(0) anad bsontype.EmbeddedDocument should cause
			// both top-level and embedded documents to decode to registered type when unmarshalling to interface{}

			topLevelRb := NewRegistryBuilder()
			defaultValueEncoders.RegisterDefaultEncoders(topLevelRb)
			defaultValueDecoders.RegisterDefaultDecoders(topLevelRb)
			topLevelRb.RegisterTypeMapEntry(bsontype.Type(0), reflect.TypeOf(primitive.M{}))

			embeddedRb := NewRegistryBuilder()
			defaultValueEncoders.RegisterDefaultEncoders(embeddedRb)
			defaultValueDecoders.RegisterDefaultDecoders(embeddedRb)
			embeddedRb.RegisterTypeMapEntry(bsontype.Type(0), reflect.TypeOf(primitive.M{}))

			// create doc {"nested": {"foo": 1}}
			innerDoc := bsoncore.BuildDocument(
				nil,
				bsoncore.AppendInt32Element(nil, "foo", 1),
			)
			doc := bsoncore.BuildDocument(
				nil,
				bsoncore.AppendDocumentElement(nil, "nested", innerDoc),
			)
			want := primitive.M{
				"nested": primitive.M{
					"foo": int32(1),
				},
			}

			testCases := []struct {
				name     string
				registry *Registry
			}{
				{"top level", topLevelRb.Build()},
				{"embedded", embeddedRb.Build()},
			}
			for _, tc := range testCases {
				var got interface{}
				vr := bsonrw.NewBSONDocumentReader(doc)
				val := reflect.ValueOf(&got).Elem()

				err := defaultEmptyInterfaceCodec.DecodeValue(DecodeContext{Registry: tc.registry}, vr, val)
				noerr(t, err)
				if !cmp.Equal(got, want) {
					t.Fatalf("got %v, want %v", got, want)
				}
			}
		})
		t.Run("ancestor info is used over custom type map entry", func(t *testing.T) {
			// If a type map entry is registered for bsontype.EmbeddedDocument, the decoder should use ancestor
			// information if available instead of the registered entry.

			rb := NewRegistryBuilder()
			defaultValueEncoders.RegisterDefaultEncoders(rb)
			defaultValueDecoders.RegisterDefaultDecoders(rb)
			rb.RegisterTypeMapEntry(bsontype.EmbeddedDocument, reflect.TypeOf(primitive.M{}))
			reg := rb.Build()

			// build document {"nested": {"foo": 10}}
			inner := bsoncore.BuildDocument(
				nil,
				bsoncore.AppendInt32Element(nil, "foo", 10),
			)
			doc := bsoncore.BuildDocument(
				nil,
				bsoncore.AppendDocumentElement(nil, "nested", inner),
			)
			want := primitive.D{
				{"nested", primitive.D{
					{"foo", int32(10)},
				}},
			}

			var got primitive.D
			vr := bsonrw.NewBSONDocumentReader(doc)
			val := reflect.ValueOf(&got).Elem()
			err := defaultSliceCodec.DecodeValue(DecodeContext{Registry: reg}, vr, val)
			noerr(t, err)
			if !cmp.Equal(got, want) {
				t.Fatalf("got %v, want %v", got, want)
			}
		})
	})

	t.Run("decode errors contain key information", func(t *testing.T) {
		decodeValueError := errors.New("decode value error")
		emptyInterfaceErrorDecode := func(DecodeContext, bsonrw.ValueReader, reflect.Value) error {
			return decodeValueError
		}
		emptyInterfaceErrorRegistry := NewRegistryBuilder().
			RegisterTypeDecoder(tEmpty, ValueDecoderFunc(emptyInterfaceErrorDecode)).Build()

		// Set up a document {foo: 10} and an error that would happen if the value were decoded into interface{}
		// using the registry defined above.
		docBytes := bsoncore.BuildDocumentFromElements(
			nil,
			bsoncore.AppendInt32Element(nil, "foo", 10),
		)
		docEmptyInterfaceErr := &DecodeError{
			keys:    []string{"foo"},
			wrapped: decodeValueError,
		}

		// Set up struct definitions where Foo maps to interface{} and string. When decoded using the registry defined
		// above, the interface{} struct will get an error when calling DecodeValue and the string struct will get an
		// error when looking up a decoder.
		type emptyInterfaceStruct struct {
			Foo interface{}
		}
		type stringStruct struct {
			Foo string
		}
		emptyInterfaceStructErr := &DecodeError{
			keys:    []string{"foo"},
			wrapped: decodeValueError,
		}
		stringStructErr := &DecodeError{
			keys:    []string{"foo"},
			wrapped: ErrNoDecoder{reflect.TypeOf("")},
		}

		// Test a deeply nested struct mixed with maps and slices.
		// Build document {"first": {"second": {"randomKey": {"third": [{}, {"fourth": "value"}]}}}}
		type inner3 struct{ Fourth interface{} }
		type inner2 struct{ Third []inner3 }
		type inner1 struct{ Second map[string]inner2 }
		type outer struct{ First inner1 }
		inner3EmptyDoc := buildDocument(nil)
		inner3Doc := buildDocument(bsoncore.AppendStringElement(nil, "fourth", "value"))
		inner3Array := buildArray(
			// buildArray takes []byte so we first append() all of the values into a single []byte
			append(
				bsoncore.AppendDocumentElement(nil, "0", inner3EmptyDoc),
				bsoncore.AppendDocumentElement(nil, "1", inner3Doc)...,
			),
		)
		inner2Doc := buildDocument(bsoncore.AppendArrayElement(nil, "third", inner3Array))
		inner2Map := buildDocument(bsoncore.AppendDocumentElement(nil, "randomKey", inner2Doc))
		inner1Doc := buildDocument(bsoncore.AppendDocumentElement(nil, "second", inner2Map))
		outerDoc := buildDocument(bsoncore.AppendDocumentElement(nil, "first", inner1Doc))

		// Use a registry that has all default decoders with the custom interface{} decoder that always errors.
		nestedRegistryBuilder := NewRegistryBuilder()
		defaultValueDecoders.RegisterDefaultDecoders(nestedRegistryBuilder)
		nestedRegistry := nestedRegistryBuilder.
			RegisterTypeDecoder(tEmpty, ValueDecoderFunc(emptyInterfaceErrorDecode)).
			Build()
		nestedErr := &DecodeError{
			keys:    []string{"fourth", "1", "third", "randomKey", "second", "first"},
			wrapped: decodeValueError,
		}

		testCases := []struct {
			name     string
			val      interface{}
			vr       bsonrw.ValueReader
			registry *Registry // buildDefaultRegistry will be used if this is nil
			decoder  ValueDecoder
			err      error
		}{
			{
				// DecodeValue error when decoding into a primitive.D.
				"primitive.D slice",
				primitive.D{},
				bsonrw.NewBSONDocumentReader(docBytes),
				emptyInterfaceErrorRegistry,
				defaultSliceCodec,
				docEmptyInterfaceErr,
			},
			{
				// DecodeValue error when decoding into a []string.
				"string slice",
				[]string{},
				&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Array},
				nil,
				defaultSliceCodec,
				&DecodeError{
					keys:    []string{"0"},
					wrapped: errors.New("cannot decode array into a string type"),
				},
			},
			{
				// DecodeValue error when decoding into a primitive.E array. This should have the same behavior as
				// the "primitive.D slice" test above because both the defaultSliceCodec and ArrayDecodeValue use
				// the decodeD helper function.
				"primitive.D array",
				[1]primitive.E{},
				bsonrw.NewBSONDocumentReader(docBytes),
				emptyInterfaceErrorRegistry,
				ValueDecoderFunc(dvd.ArrayDecodeValue),
				docEmptyInterfaceErr,
			},
			{
				// DecodeValue error when decoding into a string array. This should have the same behavior as
				// the "primitive.D slice" test above because both the defaultSliceCodec and ArrayDecodeValue use
				// the decodeDefault helper function.
				"string array",
				[1]string{},
				&bsonrwtest.ValueReaderWriter{BSONType: bsontype.Array},
				nil,
				ValueDecoderFunc(dvd.ArrayDecodeValue),
				&DecodeError{
					keys:    []string{"0"},
					wrapped: errors.New("cannot decode array into a string type"),
				},
			},
			{
				// DecodeValue error when decoding into a map.
				"map",
				map[string]interface{}{},
				bsonrw.NewBSONDocumentReader(docBytes),
				emptyInterfaceErrorRegistry,
				defaultMapCodec,
				docEmptyInterfaceErr,
			},
			{
				// DecodeValue error when decoding into a struct.
				"struct - DecodeValue error",
				emptyInterfaceStruct{},
				bsonrw.NewBSONDocumentReader(docBytes),
				emptyInterfaceErrorRegistry,
				defaultTestStructCodec,
				emptyInterfaceStructErr,
			},
			{
				// ErrNoDecoder when decoding into a struct.
				// This test uses NewRegistryBuilder().Build rather than buildDefaultRegistry to ensure that there is
				// no decoder for strings.
				"struct - no decoder found",
				stringStruct{},
				bsonrw.NewBSONDocumentReader(docBytes),
				NewRegistryBuilder().Build(),
				defaultTestStructCodec,
				stringStructErr,
			},
			{
				"deeply nested struct",
				outer{},
				bsonrw.NewBSONDocumentReader(outerDoc),
				nestedRegistry,
				defaultTestStructCodec,
				nestedErr,
			},
		}
		for _, tc := range testCases {
			t.Run(tc.name, func(t *testing.T) {
				dc := DecodeContext{Registry: tc.registry}
				if dc.Registry == nil {
					dc.Registry = buildDefaultRegistry()
				}

				var val reflect.Value
				if rtype := reflect.TypeOf(tc.val); rtype != nil {
					val = reflect.New(rtype).Elem()
				}
				err := tc.decoder.DecodeValue(dc, tc.vr, val)
				assert.Equal(t, tc.err, err, "expected error %v, got %v", tc.err, err)
			})
		}

		t.Run("keys are correctly reversed", func(t *testing.T) {
			innerBytes := bsoncore.BuildDocumentFromElements(nil, bsoncore.AppendInt32Element(nil, "bar", 10))
			outerBytes := bsoncore.BuildDocumentFromElements(nil, bsoncore.AppendDocumentElement(nil, "foo", innerBytes))

			type inner struct{ Bar string }
			type outer struct{ Foo inner }

			dc := DecodeContext{Registry: buildDefaultRegistry()}
			vr := bsonrw.NewBSONDocumentReader(outerBytes)
			val := reflect.New(reflect.TypeOf(outer{})).Elem()
			err := defaultTestStructCodec.DecodeValue(dc, vr, val)

			decodeErr, ok := err.(*DecodeError)
			assert.True(t, ok, "expected DecodeError, got %v of type %T", err, err)
			expectedKeys := []string{"foo", "bar"}
			assert.Equal(t, expectedKeys, decodeErr.Keys(), "expected keys slice %v, got %v", expectedKeys,
				decodeErr.Keys())
			keyPath := strings.Join(expectedKeys, ".")
			assert.True(t, strings.Contains(decodeErr.Error(), keyPath),
				"expected error %v to contain key pattern %s", decodeErr, keyPath)
		})
	})

	t.Run("values are converted", func(t *testing.T) {
		// When decoding into a D or M, values must be converted if they are not being decoded to the default type.

		t.Run("D", func(t *testing.T) {
			trueValue := bsoncore.Value{
				Type: bsontype.Boolean,
				Data: bsoncore.AppendBoolean(nil, true),
			}
			docBytes := bsoncore.BuildDocumentFromElements(nil,
				bsoncore.AppendBooleanElement(nil, "bool", true),
				bsoncore.BuildArrayElement(nil, "boolArray", trueValue),
			)

			rb := NewRegistryBuilder()
			defaultValueDecoders.RegisterDefaultDecoders(rb)
			reg := rb.RegisterTypeMapEntry(bsontype.Boolean, reflect.TypeOf(mybool(true))).Build()

			dc := DecodeContext{Registry: reg}
			vr := bsonrw.NewBSONDocumentReader(docBytes)
			val := reflect.New(tD).Elem()
			err := defaultValueDecoders.DDecodeValue(dc, vr, val)
			assert.Nil(t, err, "DDecodeValue error: %v", err)

			want := primitive.D{
				{"bool", mybool(true)},
				{"boolArray", primitive.A{mybool(true)}},
			}
			got := val.Interface().(primitive.D)
			assert.Equal(t, want, got, "want document %v, got %v", want, got)
		})
		t.Run("M", func(t *testing.T) {
			docBytes := bsoncore.BuildDocumentFromElements(nil,
				bsoncore.AppendBooleanElement(nil, "bool", true),
			)

			type myMap map[string]mybool
			dc := DecodeContext{Registry: buildDefaultRegistry()}
			vr := bsonrw.NewBSONDocumentReader(docBytes)
			val := reflect.New(reflect.TypeOf(myMap{})).Elem()
			err := defaultMapCodec.DecodeValue(dc, vr, val)
			assert.Nil(t, err, "DecodeValue error: %v", err)

			want := myMap{
				"bool": mybool(true),
			}
			got := val.Interface().(myMap)
			assert.Equal(t, want, got, "expected map %v, got %v", want, got)
		})
	})
}

type testValueUnmarshaler struct {
	t   bsontype.Type
	val []byte
	err error
}

func (tvu *testValueUnmarshaler) UnmarshalBSONValue(t bsontype.Type, val []byte) error {
	tvu.t, tvu.val = t, val
	return tvu.err
}

type testUnmarshaler struct {
	Val []byte
	Err error
}

func (tvu *testUnmarshaler) UnmarshalBSON(val []byte) error {
	tvu.Val = val
	return tvu.Err
}

func (tvu testValueUnmarshaler) Equal(tvu2 testValueUnmarshaler) bool {
	return tvu.t == tvu2.t && bytes.Equal(tvu.val, tvu2.val)
}

// buildDocumentArray inserts vals inside of an array inside of a document.
func buildDocumentArray(fn func([]byte) []byte) []byte {
	aix, doc := bsoncore.AppendArrayElementStart(nil, "Z")
	doc = fn(doc)
	doc, _ = bsoncore.AppendArrayEnd(doc, aix)
	return buildDocument(doc)
}

func buildArray(vals []byte) []byte {
	aix, doc := bsoncore.AppendArrayStart(nil)
	doc = append(doc, vals...)
	doc, _ = bsoncore.AppendArrayEnd(doc, aix)
	return doc
}

func appendArrayElement(dst []byte, key string, vals []byte) []byte {
	aix, doc := bsoncore.AppendArrayElementStart(dst, key)
	doc = append(doc, vals...)
	doc, _ = bsoncore.AppendArrayEnd(doc, aix)
	return doc
}

// buildDocument inserts elems inside of a document.
func buildDocument(elems []byte) []byte {
	idx, doc := bsoncore.AppendDocumentStart(nil)
	doc = append(doc, elems...)
	doc, _ = bsoncore.AppendDocumentEnd(doc, idx)
	return doc
}
