Skip to content

GODRIVER-2612 Merge bsoncodec, bsontype, bsonrw, primitive into bson. #1565

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Apr 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 5 additions & 6 deletions bson/bsoncodec/array_codec.go → bson/array_codec.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,11 @@
// 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
package bson

import (
"reflect"

"go.mongodb.org/mongo-driver/bson/bsonrw"
"go.mongodb.org/mongo-driver/x/bsonx/bsoncore"
)

Expand All @@ -30,17 +29,17 @@ func NewArrayCodec() *ArrayCodec {
}

// EncodeValue is the ValueEncoder for bsoncore.Array values.
func (ac *ArrayCodec) EncodeValue(_ EncodeContext, vw bsonrw.ValueWriter, val reflect.Value) error {
func (ac *ArrayCodec) EncodeValue(_ EncodeContext, vw ValueWriter, val reflect.Value) error {
if !val.IsValid() || val.Type() != tCoreArray {
return ValueEncoderError{Name: "CoreArrayEncodeValue", Types: []reflect.Type{tCoreArray}, Received: val}
}

arr := val.Interface().(bsoncore.Array)
return bsonrw.Copier{}.CopyArrayFromBytes(vw, arr)
return copyArrayFromBytes(vw, arr)
}

// DecodeValue is the ValueDecoder for bsoncore.Array values.
func (ac *ArrayCodec) DecodeValue(_ DecodeContext, vr bsonrw.ValueReader, val reflect.Value) error {
func (ac *ArrayCodec) DecodeValue(_ DecodeContext, vr ValueReader, val reflect.Value) error {
if !val.CanSet() || val.Type() != tCoreArray {
return ValueDecoderError{Name: "CoreArrayDecodeValue", Types: []reflect.Type{tCoreArray}, Received: val}
}
Expand All @@ -50,7 +49,7 @@ func (ac *ArrayCodec) DecodeValue(_ DecodeContext, vr bsonrw.ValueReader, val re
}

val.SetLen(0)
arr, err := bsonrw.Copier{}.AppendArrayBytes(val.Interface().(bsoncore.Array), vr)
arr, err := appendArrayBytes(val.Interface().(bsoncore.Array), vr)
val.Set(reflect.ValueOf(arr))
return err
}
50 changes: 0 additions & 50 deletions bson/bson.go

This file was deleted.

24 changes: 1 addition & 23 deletions bson/bson_corpus_spec_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import (
"encoding/hex"
"encoding/json"
"fmt"
"math"
"os"
"path"
"reflect"
Expand All @@ -22,7 +21,6 @@ import (
"unicode/utf8"

"github.com/google/go-cmp/cmp"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/internal/assert"
"go.mongodb.org/mongo-driver/internal/require"
)
Expand Down Expand Up @@ -185,26 +183,6 @@ func unescapeUnicode(s, bsonType string) string {
return newS
}

func formatDouble(f float64) string {
var s string
if math.IsInf(f, 1) {
s = "Infinity"
} else if math.IsInf(f, -1) {
s = "-Infinity"
} else if math.IsNaN(f) {
s = "NaN"
} else {
// Print exactly one decimalType place for integers; otherwise, print as many are necessary to
// perfectly represent it.
s = strconv.FormatFloat(f, 'G', -1, 64)
if !strings.ContainsRune(s, 'E') && !strings.ContainsRune(s, '.') {
s += ".0"
}
}

return s
}

func normalizeCanonicalDouble(t *testing.T, key string, cEJ string) string {
// Unmarshal string into map
cEJMap := make(map[string]map[string]string)
Expand Down Expand Up @@ -410,7 +388,7 @@ func runTest(t *testing.T, file string) {
for _, elem := range doc {
value := reflect.ValueOf(elem.Value)
invalidString := (value.Kind() == reflect.String) && !utf8.ValidString(value.String())
dbPtr, ok := elem.Value.(primitive.DBPointer)
dbPtr, ok := elem.Value.(DBPointer)
invalidDBPtr := ok && !utf8.ValidString(dbPtr.DB)

if invalidString || invalidDBPtr {
Expand Down
191 changes: 185 additions & 6 deletions bson/bson_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ package bson

import (
"bytes"
"encoding/json"
"fmt"
"reflect"
"strconv"
Expand All @@ -16,11 +17,9 @@ import (
"time"

"github.com/google/go-cmp/cmp"
"go.mongodb.org/mongo-driver/bson/bsoncodec"
"go.mongodb.org/mongo-driver/bson/bsonoptions"
"go.mongodb.org/mongo-driver/bson/bsonrw"
"go.mongodb.org/mongo-driver/bson/bsontype"
"go.mongodb.org/mongo-driver/internal/assert"
"go.mongodb.org/mongo-driver/internal/require"
"go.mongodb.org/mongo-driver/x/bsonx/bsoncore"
)

Expand All @@ -32,6 +31,186 @@ func noerr(t *testing.T, err error) {
}
}

func TestCompareTimestamp(t *testing.T) {
testcases := []struct {
name string
tp Timestamp
tp2 Timestamp
expected int
}{
{"equal", Timestamp{T: 12345, I: 67890}, Timestamp{T: 12345, I: 67890}, 0},
{"T greater than", Timestamp{T: 12345, I: 67890}, Timestamp{T: 2345, I: 67890}, 1},
{"I greater than", Timestamp{T: 12345, I: 67890}, Timestamp{T: 12345, I: 7890}, 1},
{"T less than", Timestamp{T: 12345, I: 67890}, Timestamp{T: 112345, I: 67890}, -1},
{"I less than", Timestamp{T: 12345, I: 67890}, Timestamp{T: 12345, I: 167890}, -1},
}

for _, tc := range testcases {
t.Run(tc.name, func(t *testing.T) {
result := CompareTimestamp(tc.tp, tc.tp2)
require.Equal(t, tc.expected, result)
})
}
}

func TestTimestamp(t *testing.T) {
t.Parallel()

testCases := []struct {
description string
tp Timestamp
tp2 Timestamp
expectedAfter bool
expectedBefore bool
expectedEqual bool
expectedCompare int
}{
{
description: "equal",
tp: Timestamp{T: 12345, I: 67890},
tp2: Timestamp{T: 12345, I: 67890},
expectedBefore: false,
expectedAfter: false,
expectedEqual: true,
expectedCompare: 0,
},
{
description: "T greater than",
tp: Timestamp{T: 12345, I: 67890},
tp2: Timestamp{T: 2345, I: 67890},
expectedBefore: false,
expectedAfter: true,
expectedEqual: false,
expectedCompare: 1,
},
{
description: "I greater than",
tp: Timestamp{T: 12345, I: 67890},
tp2: Timestamp{T: 12345, I: 7890},
expectedBefore: false,
expectedAfter: true,
expectedEqual: false,
expectedCompare: 1,
},
{
description: "T less than",
tp: Timestamp{T: 12345, I: 67890},
tp2: Timestamp{T: 112345, I: 67890},
expectedBefore: true,
expectedAfter: false,
expectedEqual: false,
expectedCompare: -1,
},
{
description: "I less than",
tp: Timestamp{T: 12345, I: 67890},
tp2: Timestamp{T: 12345, I: 167890},
expectedBefore: true,
expectedAfter: false,
expectedEqual: false,
expectedCompare: -1,
},
}

for _, tc := range testCases {
tc := tc // Capture range variable.

t.Run(tc.description, func(t *testing.T) {
t.Parallel()

assert.Equal(t, tc.expectedAfter, tc.tp.After(tc.tp2), "expected After results to be the same")
assert.Equal(t, tc.expectedBefore, tc.tp.Before(tc.tp2), "expected Before results to be the same")
assert.Equal(t, tc.expectedEqual, tc.tp.Equal(tc.tp2), "expected Equal results to be the same")
assert.Equal(t, tc.expectedCompare, tc.tp.Compare(tc.tp2), "expected Compare result to be the same")
})
}
}

func TestPrimitiveIsZero(t *testing.T) {
testcases := []struct {
name string
zero Zeroer
nonzero Zeroer
}{
{"binary", Binary{}, Binary{Data: []byte{0x01, 0x02, 0x03}, Subtype: 0xFF}},
{"decimal128", Decimal128{}, NewDecimal128(1, 2)},
{"objectID", ObjectID{}, NewObjectID()},
{"regex", Regex{}, Regex{Pattern: "foo", Options: "bar"}},
{"dbPointer", DBPointer{}, DBPointer{DB: "foobar", Pointer: ObjectID{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C}}},
{"timestamp", Timestamp{}, Timestamp{T: 12345, I: 67890}},
}

for _, tc := range testcases {
t.Run(tc.name, func(t *testing.T) {
require.True(t, tc.zero.IsZero())
require.False(t, tc.nonzero.IsZero())
})
}
}

func TestRegexCompare(t *testing.T) {
testcases := []struct {
name string
r1 Regex
r2 Regex
eq bool
}{
{"equal", Regex{Pattern: "foo1", Options: "bar1"}, Regex{Pattern: "foo1", Options: "bar1"}, true},
{"not equal", Regex{Pattern: "foo1", Options: "bar1"}, Regex{Pattern: "foo2", Options: "bar2"}, false},
{"not equal", Regex{Pattern: "foo1", Options: "bar1"}, Regex{Pattern: "foo1", Options: "bar2"}, false},
{"not equal", Regex{Pattern: "foo1", Options: "bar1"}, Regex{Pattern: "foo2", Options: "bar1"}, false},
}

for _, tc := range testcases {
t.Run(tc.name, func(t *testing.T) {
require.True(t, tc.r1.Equal(tc.r2) == tc.eq)
})
}
}

func TestDateTime(t *testing.T) {
t.Run("json", func(t *testing.T) {
t.Run("round trip", func(t *testing.T) {
original := DateTime(1000)
jsonBytes, err := json.Marshal(original)
assert.Nil(t, err, "Marshal error: %v", err)

var unmarshalled DateTime
err = json.Unmarshal(jsonBytes, &unmarshalled)
assert.Nil(t, err, "Unmarshal error: %v", err)

assert.Equal(t, original, unmarshalled, "expected DateTime %v, got %v", original, unmarshalled)
})
t.Run("decode null", func(t *testing.T) {
jsonBytes := []byte("null")
var dt DateTime
err := json.Unmarshal(jsonBytes, &dt)
assert.Nil(t, err, "Unmarshal error: %v", err)
assert.Equal(t, DateTime(0), dt, "expected DateTime value to be 0, got %v", dt)
})
t.Run("UTC", func(t *testing.T) {
dt := DateTime(1681145535123)
jsonBytes, err := json.Marshal(dt)
assert.Nil(t, err, "Marshal error: %v", err)
assert.Equal(t, `"2023-04-10T16:52:15.123Z"`, string(jsonBytes))
})
})
t.Run("NewDateTimeFromTime", func(t *testing.T) {
t.Run("range is not limited", func(t *testing.T) {
// If the implementation internally calls time.Time.UnixNano(), the constructor cannot handle times after
// the year 2262.

timeFormat := "2006-01-02T15:04:05.999Z07:00"
timeString := "3001-01-01T00:00:00Z"
tt, err := time.Parse(timeFormat, timeString)
assert.Nil(t, err, "Parse error: %v", err)

dt := NewDateTimeFromTime(tt)
assert.True(t, dt > 0, "expected a valid DateTime greater than 0, got %v", dt)
})
})
}

func TestTimeRoundTrip(t *testing.T) {
val := struct {
Value time.Time
Expand Down Expand Up @@ -180,11 +359,11 @@ func TestMapCodec(t *testing.T) {
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
mapCodec := bsoncodec.NewMapCodec(tc.opts)
mapCodec := NewMapCodec(tc.opts)
mapRegistry := NewRegistry()
mapRegistry.RegisterKindEncoder(reflect.Map, mapCodec)
buf := new(bytes.Buffer)
vw := bsonrw.NewValueWriter(buf)
vw := NewValueWriter(buf)
enc := NewEncoder(vw)
enc.SetRegistry(mapRegistry)
err := enc.Encode(mapObj)
Expand Down Expand Up @@ -282,5 +461,5 @@ func TestBsoncoreArray(t *testing.T) {

doc := bsoncore.Document(actualBSON)
v := doc.Lookup("array")
assert.Equal(t, bsontype.Array, v.Type, "expected type array, got %v", v.Type)
assert.Equal(t, bsoncore.TypeArray, v.Type, "expected type array, got %v", v.Type)
}
Loading