Skip to content

Commit bcdc989

Browse files
CSHARP-1821: Implement BSON Corpus tests
1 parent 2a41907 commit bcdc989

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+5713
-45
lines changed

src/MongoDB.Bson/IO/BsonStreamExtensions.cs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -98,11 +98,18 @@ public static bool ReadBoolean(this BsonStream stream)
9898
}
9999

100100
var b = stream.ReadByte();
101-
if (b == -1)
101+
102+
switch (b)
102103
{
103-
throw new EndOfStreamException();
104+
case -1:
105+
throw new EndOfStreamException();
106+
case 0:
107+
return false;
108+
case 1:
109+
return true;
110+
default:
111+
throw new FormatException($"Invalid BsonBoolean value: {b}.");
104112
}
105-
return b != 0;
106113
}
107114

108115
/// <summary>

src/MongoDB.Bson/IO/JsonOutputMode.cs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,26 @@ namespace MongoDB.Bson.IO
2323
public enum JsonOutputMode
2424
{
2525
/// <summary>
26-
/// Output strict JSON.
26+
/// Output strict JSON (an obsolete output mode that is similar to canonical extended JSON).
2727
/// </summary>
28+
[Obsolete("Use CanonicalExtendedJson instead.")]
2829
Strict,
2930

3031
/// <summary>
3132
/// Use a format that can be pasted in to the MongoDB shell.
3233
/// </summary>
3334
Shell,
3435

36+
/// <summary>
37+
/// Output canonical extended JSON.
38+
/// </summary>
39+
CanonicalExtendedJson,
40+
41+
/// <summary>
42+
/// Output relaxed extended JSON.
43+
/// </summary>
44+
RelaxedExtendedJson,
45+
3546
/// <summary>
3647
/// Use JavaScript data types for some values.
3748
/// </summary>

src/MongoDB.Bson/IO/JsonReader.cs

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1330,7 +1330,9 @@ private BsonValue ParseDateTimeConstructor(bool withNew)
13301330

13311331
private BsonType ParseExtendedJson()
13321332
{
1333+
var bookmark = GetBookmark();
13331334
var nameToken = PopToken();
1335+
13341336
if (nameToken.Type == JsonTokenType.String || nameToken.Type == JsonTokenType.UnquotedString)
13351337
{
13361338
switch (nameToken.StringValue)
@@ -1345,14 +1347,20 @@ private BsonType ParseExtendedJson()
13451347
case "$numberInt": _currentValue = ParseNumberIntExtendedJson(); return BsonType.Int32;
13461348
case "$numberLong": _currentValue = ParseNumberLongExtendedJson(); return BsonType.Int64;
13471349
case "$oid": _currentValue = ParseObjectIdExtendedJson(); return BsonType.ObjectId;
1348-
case "$regex": _currentValue = ParseRegularExpressionExtendedJsonLegacy(); return BsonType.RegularExpression;
1350+
case "$regex":
1351+
if (TryParseRegularExpressionExtendedJsonLegacy(out _currentValue))
1352+
{
1353+
return BsonType.RegularExpression;
1354+
}
1355+
break;
13491356
case "$regularExpression": _currentValue = ParseRegularExpressionExtendedJsonCanonical(); return BsonType.RegularExpression;
13501357
case "$symbol": _currentValue = ParseSymbolExtendedJson(); return BsonType.Symbol;
13511358
case "$timestamp": _currentValue = ParseTimestampExtendedJson(); return BsonType.Timestamp;
13521359
case "$undefined": _currentValue = ParseUndefinedExtendedJson(); return BsonType.Undefined;
13531360
}
13541361
}
1355-
PushToken(nameToken);
1362+
ReturnToBookmark(bookmark);
1363+
13561364
return BsonType.Document;
13571365
}
13581366

@@ -1873,15 +1881,23 @@ private BsonValue ParseRegularExpressionConstructor()
18731881
return new BsonRegularExpression(patternToken.StringValue, options);
18741882
}
18751883

1876-
private BsonValue ParseRegularExpressionExtendedJsonLegacy()
1884+
private bool TryParseRegularExpressionExtendedJsonLegacy(out BsonValue value)
18771885
{
18781886
VerifyToken(":");
18791887
var patternToken = PopToken();
1888+
1889+
if (patternToken.Type == JsonTokenType.BeginObject)
1890+
{
1891+
value = null;
1892+
return false;
1893+
}
1894+
18801895
if (patternToken.Type != JsonTokenType.String)
18811896
{
18821897
var message = string.Format("JSON reader expected a string but found '{0}'.", patternToken.Lexeme);
18831898
throw new FormatException(message);
18841899
}
1900+
18851901
var options = "";
18861902
var commaToken = PopToken();
18871903
if (commaToken.Lexeme == ",")
@@ -1901,7 +1917,10 @@ private BsonValue ParseRegularExpressionExtendedJsonLegacy()
19011917
PushToken(commaToken);
19021918
}
19031919
VerifyToken("}");
1904-
return new BsonRegularExpression(patternToken.StringValue, options);
1920+
1921+
value = new BsonRegularExpression(patternToken.StringValue, options);
1922+
1923+
return true;
19051924
}
19061925

19071926
private BsonValue ParseSymbolExtendedJson()
@@ -1982,20 +2001,20 @@ private BsonValue ParseTimestampExtendedJsonNewRepresentation()
19822001
}
19832002

19842003
token = PopToken();
1985-
if (token.Type != JsonTokenType.Int32)
2004+
if (token.Type != JsonTokenType.Int64 && token.Type != JsonTokenType.Int32)
19862005
{
19872006
throw new FormatException($"JSON reader expected an integer but found '{token.Lexeme}'.");
19882007
}
1989-
var value = token.Int32Value;
2008+
var value = token.Int64Value;
19902009

19912010
switch (name)
19922011
{
19932012
case "t":
1994-
timestamp = value;
2013+
timestamp = (int)value;
19952014
break;
19962015

19972016
case "i":
1998-
increment = value;
2017+
increment = (int)value;
19992018
break;
20002019

20012020
default:

src/MongoDB.Bson/IO/JsonWriter.cs

Lines changed: 104 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -130,15 +130,22 @@ public override void WriteBinaryData(BsonBinaryData binaryData)
130130
{
131131
guidRepresentation = subType == BsonBinarySubType.UuidStandard ? GuidRepresentation.Standard : GuidRepresentation.Unspecified;
132132
}
133-
#pragma warning disable 618
133+
#pragma warning restore 618
134134

135135
WriteNameHelper(Name);
136136
switch (Settings.OutputMode)
137137
{
138+
#pragma warning disable 618
138139
case JsonOutputMode.Strict:
140+
#pragma warning restore 618
139141
_textWriter.Write("{{ \"$binary\" : \"{0}\", \"$type\" : \"{1}\" }}", Convert.ToBase64String(bytes), ((int)subType).ToString("x2"));
140142
break;
141143

144+
case JsonOutputMode.CanonicalExtendedJson:
145+
case JsonOutputMode.RelaxedExtendedJson:
146+
_textWriter.Write("{{ \"$binary\" : {{ \"base64\" : \"{0}\", \"subType\" : \"{1}\" }} }}", Convert.ToBase64String(bytes), ((int)subType).ToString("x2"));
147+
break;
148+
142149
case JsonOutputMode.Shell:
143150
default:
144151
switch (subType)
@@ -200,10 +207,28 @@ public override void WriteDateTime(long value)
200207
WriteNameHelper(Name);
201208
switch (Settings.OutputMode)
202209
{
210+
#pragma warning disable 618
203211
case JsonOutputMode.Strict:
212+
#pragma warning restore 618
204213
_textWriter.Write("{{ \"$date\" : {0} }}", value);
205214
break;
206215

216+
case JsonOutputMode.RelaxedExtendedJson:
217+
if (value >= 0 && value <= BsonConstants.DateTimeMaxValueMillisecondsSinceEpoch)
218+
{
219+
var utcDateTime = BsonUtils.ToDateTimeFromMillisecondsSinceEpoch(value);
220+
_textWriter.Write("{{ \"$date\" : \"{0}\" }}", utcDateTime.ToString("yyyy-MM-ddTHH:mm:ss.FFFZ"));
221+
}
222+
else
223+
{
224+
_textWriter.Write("{{ \"$date\" : {{ \"$numberLong\" : \"{0}\" }} }}", value);
225+
}
226+
break;
227+
228+
case JsonOutputMode.CanonicalExtendedJson:
229+
_textWriter.Write("{{ \"$date\" : {{ \"$numberLong\" : \"{0}\" }} }}", value);
230+
break;
231+
207232
case JsonOutputMode.Shell:
208233
default:
209234
// use ISODate for values that fall within .NET's DateTime range, and "new Date" for all others
@@ -239,6 +264,11 @@ public override void WriteDecimal128(Decimal128 value)
239264
_textWriter.Write("NumberDecimal(\"{0}\")", value.ToString());
240265
break;
241266

267+
#pragma warning disable 618
268+
case JsonOutputMode.Strict:
269+
#pragma warning restore 618
270+
case JsonOutputMode.CanonicalExtendedJson:
271+
case JsonOutputMode.RelaxedExtendedJson:
242272
default:
243273
_textWriter.Write("{{ \"$numberDecimal\" : \"{0}\" }}", value.ToString());
244274
break;
@@ -267,7 +297,29 @@ public override void WriteDouble(double value)
267297
}
268298

269299
WriteNameHelper(Name);
270-
_textWriter.Write(stringRepresentation);
300+
switch (Settings.OutputMode)
301+
{
302+
case JsonOutputMode.CanonicalExtendedJson:
303+
_textWriter.Write("{{ \"$numberDouble\" : \"{0}\" }}", stringRepresentation);
304+
break;
305+
case JsonOutputMode.RelaxedExtendedJson:
306+
if (double.IsNaN(value) || double.IsInfinity(value))
307+
{
308+
_textWriter.Write("{{ \"$numberDouble\" : \"{0}\" }}", stringRepresentation);
309+
}
310+
else
311+
{
312+
_textWriter.Write(stringRepresentation);
313+
}
314+
break;
315+
#pragma warning disable 618
316+
case JsonOutputMode.Strict:
317+
#pragma warning restore 618
318+
case JsonOutputMode.Shell:
319+
default:
320+
_textWriter.Write(stringRepresentation);
321+
break;
322+
}
271323

272324
State = GetNextState();
273325
}
@@ -349,7 +401,20 @@ public override void WriteInt32(int value)
349401
}
350402

351403
WriteNameHelper(Name);
352-
_textWriter.Write(value);
404+
switch (Settings.OutputMode)
405+
{
406+
case JsonOutputMode.CanonicalExtendedJson:
407+
_textWriter.Write("{{ \"$numberInt\" : \"{0}\" }}", value);
408+
break;
409+
#pragma warning disable 618
410+
case JsonOutputMode.Strict:
411+
#pragma warning restore 618
412+
case JsonOutputMode.RelaxedExtendedJson:
413+
case JsonOutputMode.Shell:
414+
default:
415+
_textWriter.Write(value);
416+
break;
417+
}
353418

354419
State = GetNextState();
355420
}
@@ -369,10 +434,17 @@ public override void WriteInt64(long value)
369434
WriteNameHelper(Name);
370435
switch (Settings.OutputMode)
371436
{
437+
#pragma warning disable 618
372438
case JsonOutputMode.Strict:
439+
#pragma warning restore 618
440+
case JsonOutputMode.RelaxedExtendedJson:
373441
_textWriter.Write(value);
374442
break;
375443

444+
case JsonOutputMode.CanonicalExtendedJson:
445+
_textWriter.Write("{{ \"$numberLong\" : \"{0}\" }}", value);
446+
break;
447+
376448
case JsonOutputMode.Shell:
377449
default:
378450
if (value >= int.MinValue && value <= int.MaxValue)
@@ -441,7 +513,11 @@ public override void WriteMaxKey()
441513
WriteNameHelper(Name);
442514
switch (Settings.OutputMode)
443515
{
516+
#pragma warning disable 618
444517
case JsonOutputMode.Strict:
518+
#pragma warning restore 618
519+
case JsonOutputMode.CanonicalExtendedJson:
520+
case JsonOutputMode.RelaxedExtendedJson:
445521
_textWriter.Write("{ \"$maxKey\" : 1 }");
446522
break;
447523

@@ -468,7 +544,11 @@ public override void WriteMinKey()
468544
WriteNameHelper(Name);
469545
switch (Settings.OutputMode)
470546
{
547+
#pragma warning disable 618
471548
case JsonOutputMode.Strict:
549+
#pragma warning restore 618
550+
case JsonOutputMode.CanonicalExtendedJson:
551+
case JsonOutputMode.RelaxedExtendedJson:
472552
_textWriter.Write("{ \"$minKey\" : 1 }");
473553
break;
474554

@@ -513,7 +593,11 @@ public override void WriteObjectId(ObjectId objectId)
513593
WriteNameHelper(Name);
514594
switch (Settings.OutputMode)
515595
{
596+
#pragma warning disable 618
516597
case JsonOutputMode.Strict:
598+
#pragma warning restore 618
599+
case JsonOutputMode.CanonicalExtendedJson:
600+
case JsonOutputMode.RelaxedExtendedJson:
517601
_textWriter.Write("{{ \"$oid\" : \"{0}\" }}", objectId.ToString());
518602
break;
519603

@@ -544,10 +628,17 @@ public override void WriteRegularExpression(BsonRegularExpression regex)
544628
WriteNameHelper(Name);
545629
switch (Settings.OutputMode)
546630
{
631+
#pragma warning disable 618
547632
case JsonOutputMode.Strict:
633+
#pragma warning restore 618
548634
_textWriter.Write("{{ \"$regex\" : \"{0}\", \"$options\" : \"{1}\" }}", EscapedString(pattern), EscapedString(options));
549635
break;
550636

637+
case JsonOutputMode.CanonicalExtendedJson:
638+
case JsonOutputMode.RelaxedExtendedJson:
639+
_textWriter.Write("{{ \"$regularExpression\" : {{ \"pattern\" : \"{0}\", \"options\" : \"{1}\" }} }}", EscapedString(pattern), EscapedString(options));
640+
break;
641+
551642
case JsonOutputMode.Shell:
552643
default:
553644
var escapedPattern = (pattern == "") ? "(?:)" : pattern.Replace("/", @"\/");
@@ -648,13 +739,17 @@ public override void WriteTimestamp(long value)
648739
ThrowInvalidState("WriteTimestamp", BsonWriterState.Value, BsonWriterState.Initial);
649740
}
650741

651-
var secondsSinceEpoch = (int)((value >> 32) & 0xffffffff);
652-
var increment = (int)(value & 0xffffffff);
742+
var secondsSinceEpoch = (uint)((value >> 32) & 0xffffffff);
743+
var increment = (uint)(value & 0xffffffff);
653744

654745
WriteNameHelper(Name);
655746
switch (Settings.OutputMode)
656747
{
748+
#pragma warning disable 618
657749
case JsonOutputMode.Strict:
750+
#pragma warning restore 618
751+
case JsonOutputMode.CanonicalExtendedJson:
752+
case JsonOutputMode.RelaxedExtendedJson:
658753
_textWriter.Write("{{ \"$timestamp\" : {{ \"t\" : {0}, \"i\" : {1} }} }}", secondsSinceEpoch, increment);
659754
break;
660755

@@ -681,7 +776,11 @@ public override void WriteUndefined()
681776
WriteNameHelper(Name);
682777
switch (Settings.OutputMode)
683778
{
779+
#pragma warning disable 618
684780
case JsonOutputMode.Strict:
781+
#pragma warning restore 618
782+
case JsonOutputMode.CanonicalExtendedJson:
783+
case JsonOutputMode.RelaxedExtendedJson:
685784
_textWriter.Write("{ \"$undefined\" : true }");
686785
break;
687786

0 commit comments

Comments
 (0)