summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--buildscripts/idl/idl/generator.py149
-rw-r--r--buildscripts/idl/tests/test_binder.py14
-rw-r--r--src/mongo/idl/idl_parser.cpp17
-rw-r--r--src/mongo/idl/idl_parser.h40
-rw-r--r--src/mongo/idl/idl_test.cpp626
-rw-r--r--src/mongo/idl/unittest.idl10
6 files changed, 738 insertions, 118 deletions
diff --git a/buildscripts/idl/idl/generator.py b/buildscripts/idl/idl/generator.py
index c2d198e50cf..38094c787eb 100644
--- a/buildscripts/idl/idl/generator.py
+++ b/buildscripts/idl/idl/generator.py
@@ -1275,6 +1275,90 @@ class _CppSourceFileWriter(_CppFileWriterBase):
else:
self._writer.write_line('%s = std::move(values);' % (_get_field_member_name(field)))
+ def _gen_variant_deserializer(self, field, bson_element):
+ # type: (ast.Field, str) -> None
+ # pylint: disable=too-many-statements
+ """Generate the C++ deserializer piece for a variant field."""
+ self._writer.write_empty_line()
+ self._writer.write_line('const BSONType variantType = %s.type();' % (bson_element, ))
+
+ array_types = [v for v in field.type.variant_types if v.is_array]
+ scalar_types = [v for v in field.type.variant_types if not v.is_array]
+
+ self._writer.write_line('switch (variantType) {')
+ if array_types:
+ self._writer.write_line('case Array:')
+ self._writer.indent()
+ with self._predicate('%s.Obj().isEmpty()' % (bson_element, )):
+ # Can't determine element type of an empty array, use the first array type.
+ self._gen_array_deserializer(field, bson_element, array_types[0])
+
+ with self._block('else {', '}'):
+ self._writer.write_line(
+ 'const BSONType elemType = %s.Obj().firstElement().type();' % (bson_element, ))
+
+ # Start inner switch statement, for each type the first element could be.
+ self._writer.write_line('switch (elemType) {')
+ for array_type in array_types:
+ for bson_type in array_type.bson_serialization_type:
+ self._writer.write_line('case %s:' % (bson.cpp_bson_type_name(bson_type), ))
+ # Each copy of the array deserialization code gets an anonymous block.
+ with self._block('{', '}'):
+ self._gen_array_deserializer(field, bson_element, array_type)
+ self._writer.write_line('break;')
+
+ self._writer.write_line('default:')
+ self._writer.indent()
+ expected_types = ', '.join(
+ 'BSONType::%s' % bson.cpp_bson_type_name(t.bson_serialization_type[0])
+ for t in array_types)
+ self._writer.write_line(
+ 'ctxt.throwBadType(%s, {%s});' % (bson_element, expected_types))
+ self._writer.write_line('break;')
+ self._writer.unindent()
+ # End of inner switch.
+ self._writer.write_line('}')
+
+ # End of "case Array:".
+ self._writer.write_line('break;')
+ self._writer.unindent()
+
+ for scalar_type in scalar_types:
+ for bson_type in scalar_type.bson_serialization_type:
+ self._writer.write_line('case %s:' % (bson.cpp_bson_type_name(bson_type), ))
+ self._writer.indent()
+ self.gen_field_deserializer(field, scalar_type, "bsonObject", bson_element, None,
+ check_type=False)
+ self._writer.write_line('break;')
+ self._writer.unindent()
+
+ if field.type.variant_struct_type:
+ self._writer.write_line('case Object:')
+ self._writer.indent()
+ object_value = '%s::parse(ctxt, %s.Obj())' % (field.type.variant_struct_type.cpp_type,
+ bson_element)
+
+ if field.chained_struct_field:
+ self._writer.write_line(
+ '%s.%s(%s);' % (_get_field_member_name(field.chained_struct_field),
+ _get_field_member_setter_name(field), object_value))
+ else:
+ self._writer.write_line('%s = %s;' % (_get_field_member_name(field), object_value))
+ self._writer.write_line('break;')
+ self._writer.unindent()
+
+ self._writer.write_line('default:')
+ self._writer.indent()
+ expected_types = ', '.join(
+ 'BSONType::%s' % bson.cpp_bson_type_name(t.bson_serialization_type[0])
+ for t in scalar_types)
+ self._writer.write_line('ctxt.throwBadType(%s, {%s});' % (bson_element, expected_types))
+ self._writer.write_line('break;')
+ self._writer.unindent()
+
+ # End of outer switch statement.
+ self._writer.write_line('}')
+
def _gen_usage_check(self, field, bson_element, field_usage_check):
# type: (ast.Field, str, _FieldUsageCheckerBase) -> None
"""Generate the field usage check and insert the required field check."""
@@ -1284,22 +1368,25 @@ class _CppSourceFileWriter(_CppFileWriterBase):
if _is_required_serializer_field(field):
self._writer.write_line('%s = true;' % (_get_has_field_member_name(field)))
- def gen_field_deserializer(self, field, bson_object, bson_element, field_usage_check,
- is_command_field=False):
- # type: (ast.Field, str, str, _FieldUsageCheckerBase, bool) -> None
- """Generate the C++ deserializer piece for a field."""
+ def gen_field_deserializer(self, field, field_type, bson_object, bson_element,
+ field_usage_check, is_command_field=False, check_type=True):
+ # type: (ast.Field, ast.Type, str, str, _FieldUsageCheckerBase, bool, bool) -> None
+ """Generate the C++ deserializer piece for a field.
+
+ If field_type is scalar and check_type is True (the default), generate type-checking code.
+ Array elements are always type-checked.
+ """
# pylint: disable=too-many-arguments
- if field.type.is_array:
+ if field_type.is_array:
predicate = "MONGO_likely(ctxt.checkAndAssertType(%s, Array))" % (bson_element)
with self._predicate(predicate):
self._gen_usage_check(field, bson_element, field_usage_check)
-
- self._gen_array_deserializer(field, bson_element, field.type)
+ self._gen_array_deserializer(field, bson_element, field_type)
return
- elif field.type.is_variant:
+ elif field_type.is_variant:
self._gen_usage_check(field, bson_element, field_usage_check)
- # TODO (SERVER-51369): implement _gen_variant_deserializer.
+ self._gen_variant_deserializer(field, bson_element)
return
def validate_and_assign_or_uassert(field, expression):
@@ -1318,28 +1405,31 @@ class _CppSourceFileWriter(_CppFileWriterBase):
if field.chained:
# Do not generate a predicate check since we always call these deserializers.
- if field.type.is_struct:
+ if field_type.is_struct:
# Do not generate a new parser context, reuse the current one since we are not
# entering a nested document.
- expression = '%s::parse(ctxt, %s)' % (field.type.cpp_type, bson_object)
+ expression = '%s::parse(ctxt, %s)' % (field_type.cpp_type, bson_object)
else:
method_name = writer.get_method_name_from_qualified_method_name(
- field.type.deserializer)
+ field_type.deserializer)
expression = "%s(%s)" % (method_name, bson_object)
self._gen_usage_check(field, bson_element, field_usage_check)
validate_and_assign_or_uassert(field, expression)
else:
- predicate = _get_bson_type_check(bson_element, 'ctxt', field.type)
- if predicate:
- predicate = "MONGO_likely(%s)" % (predicate)
+ predicate = None
+ if check_type:
+ predicate = _get_bson_type_check(bson_element, 'ctxt', field_type)
+ if predicate:
+ predicate = "MONGO_likely(%s)" % (predicate)
+
with self._predicate(predicate):
self._gen_usage_check(field, bson_element, field_usage_check)
object_value = self._gen_field_deserializer_expression(
- bson_element, field, field.type)
+ bson_element, field, field_type)
if field.chained_struct_field:
if field.optional:
# We must invoke the boost::optional constructor when setting optional view
@@ -1497,8 +1587,9 @@ class _CppSourceFileWriter(_CppFileWriterBase):
if isinstance(struct, ast.Command) and struct.command_field:
with self._block('{', '}'):
- self.gen_field_deserializer(struct.command_field, bson_object, "commandElement",
- None, is_command_field=True)
+ self.gen_field_deserializer(struct.command_field, struct.command_field.type,
+ bson_object, "commandElement", None,
+ is_command_field=True)
else:
struct_type_info = struct_types.get_struct_info(struct)
@@ -1549,7 +1640,7 @@ class _CppSourceFileWriter(_CppFileWriterBase):
self._writer.write_line('// ignore field')
else:
- self.gen_field_deserializer(field, bson_object, "element",
+ self.gen_field_deserializer(field, field.type, bson_object, "element",
field_usage_check)
if first_field:
@@ -1576,7 +1667,7 @@ class _CppSourceFileWriter(_CppFileWriterBase):
continue
# Simply generate deserializers since these are all 'any' types
- self.gen_field_deserializer(field, bson_object, "element", None)
+ self.gen_field_deserializer(field, field.type, bson_object, "element", None)
self._writer.write_empty_line()
self._writer.write_empty_line()
@@ -1854,6 +1945,21 @@ class _CppSourceFileWriter(_CppFileWriterBase):
'BSONObjBuilder subObjBuilder(builder->subobjStart(${field_name}));')
self._writer.write_template('${access_member}.serialize(&subObjBuilder);')
+ def _gen_serializer_method_variant(self, field):
+ # type: (ast.Field) -> None
+ """Generate the serialize method definition for a variant type."""
+ template_params = {
+ 'field_name': _get_field_constant_name(field),
+ 'access_member': _access_member(field),
+ }
+
+ with self._with_template(template_params):
+ # See https://en.cppreference.com/w/cpp/utility/variant/visit
+ # This lambda is a template instantiated for each alternate type. Use "if constexpr"
+ # to compile the appropriate serialization code for each.
+ with self._block('stdx::visit([builder](auto&& arg) {', '}, ${access_member});'):
+ self._writer.write_template('idlSerialize(builder, ${field_name}, arg);')
+
def _gen_serializer_method_common(self, field):
# type: (ast.Field) -> None
# pylint: disable=too-many-locals
@@ -1879,8 +1985,7 @@ class _CppSourceFileWriter(_CppFileWriterBase):
if needs_custom_serializer:
self._gen_serializer_method_custom(field)
elif field.type.is_variant:
- # TODO (SERVER-51369): implement deserializer.
- return
+ self._gen_serializer_method_variant(field)
else:
# Generate default serialization using BSONObjBuilder::append
# Note: BSONObjBuilder::append has overrides for std::vector also
diff --git a/buildscripts/idl/tests/test_binder.py b/buildscripts/idl/tests/test_binder.py
index 5e20a27a346..86bd63db773 100644
--- a/buildscripts/idl/tests/test_binder.py
+++ b/buildscripts/idl/tests/test_binder.py
@@ -756,6 +756,20 @@ class TestBinder(testcase.IDLTestcase):
- array<struct1>
"""), idl.errors.ERROR_ID_VARIANT_DUPLICATE_TYPES)
+ # At most one array can have BSON serialization type NumberInt.
+ self.assert_bind_fail(
+ test_preamble + textwrap.dedent("""
+ structs:
+ foo:
+ description: foo
+ fields:
+ my_variant_field:
+ type:
+ variant:
+ - array<int>
+ - array<safeInt>
+ """), idl.errors.ERROR_ID_VARIANT_DUPLICATE_TYPES)
+
self.assert_bind_fail(
test_preamble + textwrap.dedent("""
structs:
diff --git a/src/mongo/idl/idl_parser.cpp b/src/mongo/idl/idl_parser.cpp
index 89158281e92..9cad14bffca 100644
--- a/src/mongo/idl/idl_parser.cpp
+++ b/src/mongo/idl/idl_parser.cpp
@@ -110,12 +110,7 @@ bool IDLParserErrorContext::checkAndAssertTypes(const BSONElement& element,
return false;
}
- std::string path = getElementPath(element);
- std::string type_str = toCommaDelimitedList(types);
- uasserted(ErrorCodes::TypeMismatch,
- str::stream() << "BSON field '" << path << "' is the wrong type '"
- << typeName(element.type()) << "', expected types '[" << type_str
- << "']");
+ throwBadType(element, types);
}
return true;
@@ -225,6 +220,16 @@ void IDLParserErrorContext::throwBadEnumValue(StringData enumValue) const {
<< "' is not a valid value.");
}
+void IDLParserErrorContext::throwBadType(const BSONElement& element,
+ const std::vector<BSONType>& types) const {
+ std::string path = getElementPath(element);
+ std::string type_str = toCommaDelimitedList(types);
+ uasserted(ErrorCodes::TypeMismatch,
+ str::stream() << "BSON field '" << path << "' is the wrong type '"
+ << typeName(element.type()) << "', expected types '[" << type_str
+ << "']");
+}
+
void IDLParserErrorContext::throwAPIStrictErrorIfApplicable(BSONElement field) const {
throwAPIStrictErrorIfApplicable(field.fieldNameStringData());
}
diff --git a/src/mongo/idl/idl_parser.h b/src/mongo/idl/idl_parser.h
index 551c87bf309..47096c0d117 100644
--- a/src/mongo/idl/idl_parser.h
+++ b/src/mongo/idl/idl_parser.h
@@ -34,11 +34,45 @@
#include "mongo/base/string_data.h"
#include "mongo/bson/bsonelement.h"
+#include "mongo/bson/bsonobjbuilder.h"
#include "mongo/bson/bsontypes.h"
#include "mongo/db/namespace_string.h"
namespace mongo {
+namespace idl {
+template <typename T>
+using HasBSONSerializeOp = decltype(std::declval<T>().serialize(std::declval<BSONObjBuilder*>()));
+
+template <typename T>
+constexpr bool hasBSONSerialize = stdx::is_detected_v<HasBSONSerializeOp, T>;
+
+template <typename T>
+void idlSerialize(BSONObjBuilder* builder, StringData fieldName, T arg) {
+ if constexpr (hasBSONSerialize<decltype(arg)>) {
+ BSONObjBuilder subObj(builder->subobjStart(fieldName));
+ arg.serialize(&subObj);
+ } else {
+ builder->append(fieldName, arg);
+ }
+}
+
+template <typename T>
+void idlSerialize(BSONObjBuilder* builder, StringData fieldName, std::vector<T> arg) {
+ BSONArrayBuilder arrayBuilder(builder->subarrayStart(fieldName));
+ for (const auto& item : arg) {
+ if constexpr (hasBSONSerialize<decltype(item)>) {
+ BSONObjBuilder subObj(arrayBuilder.subobjStart());
+ item.serialize(&subObj);
+ } else {
+ arrayBuilder.append(item);
+ }
+ }
+}
+
+
+} // namespace idl
+
/**
* IDLParserErrorContext manages the current parser context for parsing BSON documents.
*
@@ -154,6 +188,12 @@ public:
MONGO_COMPILER_NORETURN void throwBadEnumValue(int enumValue) const;
/**
+ * Throw an error about a field having the wrong type.
+ */
+ MONGO_COMPILER_NORETURN void throwBadType(const BSONElement& element,
+ const std::vector<BSONType>& types) const;
+
+ /**
* Throw an 'APIStrictError' if the user command has 'apiStrict' field as true.
*/
void throwAPIStrictErrorIfApplicable(StringData fieldName) const;
diff --git a/src/mongo/idl/idl_test.cpp b/src/mongo/idl/idl_test.cpp
index cdbfeb70210..2215b62745c 100644
--- a/src/mongo/idl/idl_test.cpp
+++ b/src/mongo/idl/idl_test.cpp
@@ -136,6 +136,12 @@ BSONObj appendDB(const BSONObj& obj, StringData dbName) {
return builder.obj();
}
+template <typename T>
+BSONObj serializeCmd(const T& cmd) {
+ auto reply = cmd.serialize({});
+ return reply.body;
+}
+
// Use a separate function to get better error messages when types do not match.
template <typename T1, typename T2>
void assert_same_types() {
@@ -464,6 +470,468 @@ TEST(IDLOneTypeTests, TestObjectTypeNegative) {
}
}
+// Trait check used in TestLoopbackVariant.
+template <typename T>
+struct IsVector : std::false_type {};
+template <typename T>
+struct IsVector<std::vector<T>> : std::true_type {};
+template <typename T>
+constexpr bool isVector = IsVector<T>::value;
+
+// We don't generate comparison operators like "==" for variants, so test only for BSON equality.
+template <typename ParserT, typename TestT, BSONType Test_bson_type>
+void TestLoopbackVariant(TestT test_value) {
+ IDLParserErrorContext ctxt("root");
+
+ BSONObjBuilder bob;
+ if constexpr (idl::hasBSONSerialize<TestT>) {
+ // TestT might be an IDL struct type like One_string.
+ BSONObjBuilder subObj(bob.subobjStart("value"));
+ test_value.serialize(&subObj);
+ } else if constexpr (isVector<TestT>) {
+ BSONArrayBuilder arrayBuilder(bob.subarrayStart("value"));
+ for (const auto& item : test_value) {
+ if constexpr (idl::hasBSONSerialize<decltype(item)>) {
+ BSONObjBuilder subObjBuilder(arrayBuilder.subobjStart());
+ item.serialize(&subObjBuilder);
+ } else {
+ arrayBuilder.append(item);
+ }
+ }
+ } else {
+ bob.append("value", test_value);
+ }
+
+ auto obj = bob.obj();
+ auto element = obj.firstElement();
+ ASSERT_EQUALS(element.type(), Test_bson_type);
+
+ auto parsed = ParserT::parse(ctxt, obj);
+ if constexpr (std::is_same_v<TestT, BSONObj>) {
+ ASSERT_BSONOBJ_EQ(stdx::get<TestT>(parsed.getValue()), test_value);
+ } else {
+ // Use ASSERT instead of ASSERT_EQ to avoid operator<<
+ ASSERT(stdx::get<TestT>(parsed.getValue()) == test_value);
+ }
+ ASSERT_BSONOBJ_EQ(obj, parsed.toBSON());
+
+ // Test setValue.
+ ParserT assembled;
+ assembled.setValue(test_value);
+ ASSERT_BSONOBJ_EQ(obj, assembled.toBSON());
+
+ // Test the constructor.
+ ParserT constructed(test_value);
+ if constexpr (std::is_same_v<TestT, BSONObj>) {
+ ASSERT_BSONOBJ_EQ(stdx::get<TestT>(parsed.getValue()), test_value);
+ } else {
+ ASSERT(stdx::get<TestT>(parsed.getValue()) == test_value);
+ }
+ ASSERT_BSONOBJ_EQ(obj, constructed.toBSON());
+}
+
+TEST(IDLVariantTests, TestVariantRoundtrip) {
+ TestLoopbackVariant<One_variant, int, NumberInt>(1);
+ TestLoopbackVariant<One_variant, std::string, String>("test_value");
+
+ TestLoopbackVariant<One_variant_compound, std::string, String>("test_value");
+ TestLoopbackVariant<One_variant_compound, BSONObj, Object>(BSON("x" << 1));
+ TestLoopbackVariant<One_variant_compound, std::vector<std::string>, Array>({});
+ TestLoopbackVariant<One_variant_compound, std::vector<std::string>, Array>({"a"});
+ TestLoopbackVariant<One_variant_compound, std::vector<std::string>, Array>({"a", "b"});
+
+ TestLoopbackVariant<One_variant_struct, int, NumberInt>(1);
+ TestLoopbackVariant<One_variant_struct, One_string, Object>(One_string("test_value"));
+
+ TestLoopbackVariant<One_variant_struct_array, int, NumberInt>(1);
+ TestLoopbackVariant<One_variant_struct_array, std::vector<One_string>, Array>(
+ std::vector<One_string>());
+ TestLoopbackVariant<One_variant_struct_array, std::vector<One_string>, Array>(
+ {One_string("a")});
+ TestLoopbackVariant<One_variant_struct_array, std::vector<One_string>, Array>(
+ {One_string("a"), One_string("b")});
+}
+
+TEST(IDLVariantTests, TestVariantSafeInt) {
+ TestLoopbackVariant<One_variant_safeInt, std::string, String>("test_value");
+ TestLoopbackVariant<One_variant_safeInt, int, NumberInt>(1);
+
+ // safeInt accepts all numbers, but always deserializes and serializes as int32.
+ IDLParserErrorContext ctxt("root");
+ ASSERT_EQ(stdx::get<std::int32_t>(
+ One_variant_safeInt::parse(ctxt, BSON("value" << Decimal128(1))).getValue()),
+ 1);
+ ASSERT_EQ(
+ stdx::get<std::int32_t>(One_variant_safeInt::parse(ctxt, BSON("value" << 1LL)).getValue()),
+ 1);
+ ASSERT_EQ(
+ stdx::get<std::int32_t>(One_variant_safeInt::parse(ctxt, BSON("value" << 1.0)).getValue()),
+ 1);
+}
+
+TEST(IDLVariantTests, TestVariantSafeIntArray) {
+ using int32vec = std::vector<std::int32_t>;
+
+ TestLoopbackVariant<One_variant_safeInt_array, std::string, String>("test_value");
+ TestLoopbackVariant<One_variant_safeInt_array, int32vec, Array>({});
+ TestLoopbackVariant<One_variant_safeInt_array, int32vec, Array>({1});
+ TestLoopbackVariant<One_variant_safeInt_array, int32vec, Array>({1, 2});
+
+ // Use ASSERT instead of ASSERT_EQ to avoid operator<<
+ IDLParserErrorContext ctxt("root");
+ ASSERT(stdx::get<int32vec>(
+ One_variant_safeInt_array::parse(ctxt, BSON("value" << BSON_ARRAY(Decimal128(1))))
+ .getValue()) == int32vec{1});
+ ASSERT(
+ stdx::get<int32vec>(
+ One_variant_safeInt_array::parse(ctxt, BSON("value" << BSON_ARRAY(1LL))).getValue()) ==
+ int32vec{1});
+ ASSERT(
+ stdx::get<int32vec>(
+ One_variant_safeInt_array::parse(ctxt, BSON("value" << BSON_ARRAY(1.0))).getValue()) ==
+ int32vec{1});
+ ASSERT(
+ stdx::get<int32vec>(One_variant_safeInt_array::parse(
+ ctxt, BSON("value" << BSON_ARRAY(1.0 << 2LL << 3 << Decimal128(4))))
+ .getValue()) == (int32vec{1, 2, 3, 4}));
+}
+
+TEST(IDLVariantTests, TestVariantTwoArrays) {
+ TestLoopbackVariant<One_variant_two_arrays, std::vector<int>, Array>({});
+ TestLoopbackVariant<One_variant_two_arrays, std::vector<int>, Array>({1});
+ TestLoopbackVariant<One_variant_two_arrays, std::vector<int>, Array>({1, 2});
+ TestLoopbackVariant<One_variant_two_arrays, std::vector<std::string>, Array>({"a"});
+ TestLoopbackVariant<One_variant_two_arrays, std::vector<std::string>, Array>({"a", "b"});
+
+ // This variant can be array<int> or array<string>. It assumes an empty array is array<int>
+ // because that type is declared first in the IDL.
+ auto obj = BSON("value" << BSONArray());
+ auto parsed = One_variant_two_arrays::parse({"root"}, obj);
+ ASSERT(stdx::get<std::vector<int>>(parsed.getValue()) == std::vector<int>());
+ ASSERT_THROWS(stdx::get<std::vector<std::string>>(parsed.getValue()), stdx::bad_variant_access);
+
+ // Corrupt array: its first key isn't "0".
+ BSONObjBuilder bob;
+ {
+ BSONObjBuilder arrayBob(bob.subarrayStart("value"));
+ arrayBob.append("1", "test_value");
+ }
+
+ ASSERT_THROWS_CODE(
+ One_variant_two_arrays::parse({"root"}, bob.obj()), AssertionException, 40423);
+}
+
+TEST(IDLVariantTests, TestVariantOptional) {
+ {
+ auto obj = BSON("value" << 1);
+ auto parsed = One_variant_optional::parse({"root"}, obj);
+ ASSERT_BSONOBJ_EQ(obj, parsed.toBSON());
+ ASSERT_EQ(stdx::get<int>(*parsed.getValue()), 1);
+ }
+
+ {
+ auto obj = BSON("value"
+ << "test_value");
+ auto parsed = One_variant_optional::parse({"root"}, obj);
+ ASSERT_BSONOBJ_EQ(obj, parsed.toBSON());
+ ASSERT_EQ(stdx::get<std::string>(*parsed.getValue()), "test_value");
+ }
+
+ // The optional key is absent.
+ auto parsed = One_variant_optional::parse({"root"}, BSONObj());
+ ASSERT_FALSE(parsed.getValue().is_initialized());
+ ASSERT_BSONOBJ_EQ(BSONObj(), parsed.toBSON());
+}
+
+TEST(IDLVariantTests, TestTwoVariants) {
+ // Combinations of value0 (int or string) and value1 (object or array<string>). For each, test
+ // parse(), toBSON(), getValue0(), getValue1(), and the constructor.
+ {
+ auto obj = BSON("value0" << 1 << "value1" << BSONObj());
+ auto parsed = Two_variants::parse({"root"}, obj);
+ ASSERT_BSONOBJ_EQ(obj, parsed.toBSON());
+ ASSERT_EQ(stdx::get<int>(parsed.getValue0()), 1);
+ ASSERT_BSONOBJ_EQ(stdx::get<BSONObj>(parsed.getValue1()), BSONObj());
+ ASSERT_BSONOBJ_EQ(Two_variants(1, BSONObj()).toBSON(), obj);
+ }
+
+ {
+ auto obj = BSON("value0"
+ << "test_value"
+ << "value1" << BSONObj());
+ auto parsed = Two_variants::parse({"root"}, obj);
+ ASSERT_BSONOBJ_EQ(obj, parsed.toBSON());
+ ASSERT_EQ(stdx::get<std::string>(parsed.getValue0()), "test_value");
+ ASSERT_BSONOBJ_EQ(stdx::get<BSONObj>(parsed.getValue1()), BSONObj());
+ ASSERT_BSONOBJ_EQ(Two_variants("test_value", BSONObj()).toBSON(), obj);
+ }
+
+ {
+ auto obj = BSON("value0" << 1 << "value1"
+ << BSON_ARRAY("x"
+ << "y"));
+ auto parsed = Two_variants::parse({"root"}, obj);
+ ASSERT_BSONOBJ_EQ(obj, parsed.toBSON());
+ ASSERT_EQ(stdx::get<int>(parsed.getValue0()), 1);
+ ASSERT(stdx::get<std::vector<std::string>>(parsed.getValue1()) ==
+ (std::vector<std::string>{"x", "y"}));
+ ASSERT_BSONOBJ_EQ(Two_variants(1, std::vector<std::string>{"x", "y"}).toBSON(), obj);
+ }
+
+ {
+ auto obj = BSON("value0"
+ << "test_value"
+ << "value1"
+ << BSON_ARRAY("x"
+ << "y"));
+ auto parsed = Two_variants::parse({"root"}, obj);
+ ASSERT_BSONOBJ_EQ(obj, parsed.toBSON());
+ ASSERT_EQ(stdx::get<std::string>(parsed.getValue0()), "test_value");
+ ASSERT(stdx::get<std::vector<std::string>>(parsed.getValue1()) ==
+ (std::vector<std::string>{"x", "y"}));
+ ASSERT_BSONOBJ_EQ(Two_variants("test_value", std::vector<std::string>{"x", "y"}).toBSON(),
+ obj);
+ }
+}
+
+TEST(IDLVariantTests, TestChainedStructVariant) {
+ IDLParserErrorContext ctxt("root");
+ {
+ auto obj = BSON("value"
+ << "x"
+ << "field1"
+ << "y");
+ auto parsed = Chained_struct_variant::parse(ctxt, obj);
+ ASSERT_EQ(stdx::get<std::string>(parsed.getOne_variant_compound().getValue()), "x");
+ ASSERT_EQ(parsed.getField1(), "y");
+ ASSERT_BSONOBJ_EQ(obj, parsed.toBSON());
+
+ Chained_struct_variant assembled;
+ assembled.setOne_variant_compound(One_variant_compound("x"));
+ assembled.setField1("y");
+ ASSERT_BSONOBJ_EQ(obj, assembled.toBSON());
+
+ // Test the constructor.
+ Chained_struct_variant constructed("y");
+ constructed.setOne_variant_compound(One_variant_compound("x"));
+ ASSERT_EQ(stdx::get<std::string>(constructed.getOne_variant_compound().getValue()), "x");
+ ASSERT_EQ(constructed.getField1(), "y");
+ ASSERT_BSONOBJ_EQ(obj, constructed.toBSON());
+ }
+ {
+ auto obj = BSON("value" << BSON_ARRAY("x"
+ << "y")
+ << "field1"
+ << "y");
+ auto parsed = Chained_struct_variant::parse(ctxt, obj);
+ ASSERT(stdx::get<std::vector<std::string>>(parsed.getOne_variant_compound().getValue()) ==
+ (std::vector<std::string>{"x", "y"}));
+ ASSERT_EQ(parsed.getField1(), "y");
+ ASSERT_BSONOBJ_EQ(obj, parsed.toBSON());
+
+ Chained_struct_variant assembled;
+ assembled.setOne_variant_compound(One_variant_compound(std::vector<std::string>{"x", "y"}));
+ assembled.setField1("y");
+ ASSERT_BSONOBJ_EQ(obj, assembled.toBSON());
+
+ // Test the constructor.
+ Chained_struct_variant constructed("y");
+ constructed.setOne_variant_compound(
+ One_variant_compound(std::vector<std::string>{"x", "y"}));
+ ASSERT(
+ stdx::get<std::vector<std::string>>(constructed.getOne_variant_compound().getValue()) ==
+ (std::vector<std::string>{"x", "y"}));
+ ASSERT_EQ(constructed.getField1(), "y");
+ ASSERT_BSONOBJ_EQ(obj, constructed.toBSON());
+ }
+ {
+ auto obj = BSON("value" << BSONObj() << "field1"
+ << "y");
+ auto parsed = Chained_struct_variant::parse(ctxt, obj);
+ ASSERT_BSONOBJ_EQ(stdx::get<BSONObj>(parsed.getOne_variant_compound().getValue()),
+ BSONObj());
+ ASSERT_EQ(parsed.getField1(), "y");
+ ASSERT_BSONOBJ_EQ(obj, parsed.toBSON());
+
+ Chained_struct_variant assembled;
+ assembled.setOne_variant_compound(One_variant_compound(BSONObj()));
+ assembled.setField1("y");
+ ASSERT_BSONOBJ_EQ(obj, assembled.toBSON());
+
+ // Test the constructor.
+ Chained_struct_variant constructed("y");
+ constructed.setOne_variant_compound({BSONObj()});
+ ASSERT_BSONOBJ_EQ(stdx::get<BSONObj>(constructed.getOne_variant_compound().getValue()),
+ BSONObj());
+ ASSERT_EQ(constructed.getField1(), "y");
+ ASSERT_BSONOBJ_EQ(obj, constructed.toBSON());
+ }
+}
+
+TEST(IDLVariantTests, TestChainedStructVariantInline) {
+ IDLParserErrorContext ctxt("root");
+ {
+ auto obj = BSON("value"
+ << "x"
+ << "field1"
+ << "y");
+ auto parsed = Chained_struct_variant_inline::parse(ctxt, obj);
+ ASSERT_EQ(stdx::get<std::string>(parsed.getValue()), "x");
+ ASSERT_EQ(parsed.getField1(), "y");
+ ASSERT_BSONOBJ_EQ(obj, parsed.toBSON());
+
+ Chained_struct_variant_inline assembled;
+ assembled.setOne_variant_compound(One_variant_compound("x"));
+ assembled.setField1("y");
+ ASSERT_BSONOBJ_EQ(obj, assembled.toBSON());
+
+ // Test the constructor.
+ Chained_struct_variant_inline constructed("y");
+ constructed.setOne_variant_compound(One_variant_compound("x"));
+ ASSERT_EQ(stdx::get<std::string>(constructed.getValue()), "x");
+ ASSERT_EQ(constructed.getField1(), "y");
+ ASSERT_BSONOBJ_EQ(obj, constructed.toBSON());
+ }
+ {
+ auto obj = BSON("value" << BSON_ARRAY("x"
+ << "y")
+ << "field1"
+ << "y");
+ auto parsed = Chained_struct_variant_inline::parse(ctxt, obj);
+ ASSERT(stdx::get<std::vector<std::string>>(parsed.getValue()) ==
+ (std::vector<std::string>{"x", "y"}));
+ ASSERT_EQ(parsed.getField1(), "y");
+ ASSERT_BSONOBJ_EQ(obj, parsed.toBSON());
+
+ Chained_struct_variant_inline assembled;
+ assembled.setOne_variant_compound(One_variant_compound(std::vector<std::string>{"x", "y"}));
+ assembled.setField1("y");
+ ASSERT_BSONOBJ_EQ(obj, assembled.toBSON());
+
+ // Test the constructor.
+ Chained_struct_variant_inline constructed("y");
+ constructed.setOne_variant_compound(
+ One_variant_compound(std::vector<std::string>{"x", "y"}));
+ ASSERT(stdx::get<std::vector<std::string>>(constructed.getValue()) ==
+ (std::vector<std::string>{"x", "y"}));
+ ASSERT_EQ(constructed.getField1(), "y");
+ ASSERT_BSONOBJ_EQ(obj, constructed.toBSON());
+ }
+ {
+ auto obj = BSON("value" << BSONObj() << "field1"
+ << "y");
+ auto parsed = Chained_struct_variant_inline::parse(ctxt, obj);
+ ASSERT_BSONOBJ_EQ(stdx::get<BSONObj>(parsed.getValue()), BSONObj());
+ ASSERT_EQ(parsed.getField1(), "y");
+ ASSERT_BSONOBJ_EQ(obj, parsed.toBSON());
+
+ Chained_struct_variant_inline assembled;
+ assembled.setOne_variant_compound(One_variant_compound(BSONObj()));
+ assembled.setField1("y");
+ ASSERT_BSONOBJ_EQ(obj, assembled.toBSON());
+
+ // Test the constructor.
+ Chained_struct_variant_inline constructed("y");
+ constructed.setOne_variant_compound({BSONObj()});
+ ASSERT_BSONOBJ_EQ(stdx::get<BSONObj>(constructed.getValue()), BSONObj());
+ ASSERT_EQ(constructed.getField1(), "y");
+ ASSERT_BSONOBJ_EQ(obj, constructed.toBSON());
+ }
+}
+
+TEST(IDLVariantTests, TestChainedStructVariantStruct) {
+ IDLParserErrorContext ctxt("root");
+ {
+ auto obj = BSON("value" << 1 << "field1"
+ << "y");
+ auto parsed = Chained_struct_variant_struct::parse(ctxt, obj);
+ ASSERT_EQ(stdx::get<int>(parsed.getOne_variant_struct().getValue()), 1);
+ ASSERT_EQ(parsed.getField1(), "y");
+ ASSERT_BSONOBJ_EQ(obj, parsed.toBSON());
+
+ Chained_struct_variant_struct assembled;
+ assembled.setOne_variant_struct(One_variant_struct(1));
+ assembled.setField1("y");
+ ASSERT_BSONOBJ_EQ(obj, assembled.toBSON());
+
+ // Test the constructor.
+ Chained_struct_variant_struct constructed("y");
+ constructed.setOne_variant_struct(One_variant_struct(1));
+ ASSERT_EQ(stdx::get<int>(constructed.getOne_variant_struct().getValue()), 1);
+ ASSERT_EQ(constructed.getField1(), "y");
+ ASSERT_BSONOBJ_EQ(obj, constructed.toBSON());
+ }
+ {
+ auto obj = BSON("value" << BSON("value"
+ << "x")
+ << "field1"
+ << "y");
+ auto parsed = Chained_struct_variant_struct::parse(ctxt, obj);
+ ASSERT_EQ(stdx::get<One_string>(parsed.getOne_variant_struct().getValue()).getValue(), "x");
+ ASSERT_EQ(parsed.getField1(), "y");
+ ASSERT_BSONOBJ_EQ(obj, parsed.toBSON());
+
+ Chained_struct_variant_struct assembled;
+ assembled.setOne_variant_struct(One_variant_struct(One_string("x")));
+ assembled.setField1("y");
+ ASSERT_BSONOBJ_EQ(obj, assembled.toBSON());
+
+ // Test the constructor.
+ Chained_struct_variant_struct constructed("y");
+ constructed.setOne_variant_struct(One_variant_struct(One_string("x")));
+ ASSERT_EQ(stdx::get<One_string>(constructed.getOne_variant_struct().getValue()).getValue(),
+ "x");
+ ASSERT_EQ(constructed.getField1(), "y");
+ ASSERT_BSONOBJ_EQ(obj, constructed.toBSON());
+ }
+}
+
+TEST(IDLVariantTests, TestChainedStructVariantStructInline) {
+ IDLParserErrorContext ctxt("root");
+ {
+ auto obj = BSON("value" << 1 << "field1"
+ << "y");
+ auto parsed = Chained_struct_variant_struct_inline::parse(ctxt, obj);
+ ASSERT_EQ(stdx::get<int>(parsed.getValue()), 1);
+ ASSERT_EQ(parsed.getField1(), "y");
+ ASSERT_BSONOBJ_EQ(obj, parsed.toBSON());
+
+ Chained_struct_variant_struct_inline assembled;
+ assembled.setOne_variant_struct(One_variant_struct(1));
+ assembled.setField1("y");
+ ASSERT_BSONOBJ_EQ(obj, assembled.toBSON());
+
+ // Test the constructor.
+ Chained_struct_variant_struct_inline constructed("y");
+ constructed.setOne_variant_struct(One_variant_struct(1));
+ ASSERT_EQ(stdx::get<int>(constructed.getValue()), 1);
+ ASSERT_EQ(constructed.getField1(), "y");
+ ASSERT_BSONOBJ_EQ(obj, constructed.toBSON());
+ }
+ {
+ auto obj = BSON("value" << BSON("value"
+ << "x")
+ << "field1"
+ << "y");
+ auto parsed = Chained_struct_variant_struct_inline::parse(ctxt, obj);
+ ASSERT_EQ(stdx::get<One_string>(parsed.getValue()).getValue(), "x");
+ ASSERT_EQ(parsed.getField1(), "y");
+ ASSERT_BSONOBJ_EQ(obj, parsed.toBSON());
+
+ Chained_struct_variant_struct_inline assembled;
+ assembled.setOne_variant_struct(One_variant_struct(One_string("x")));
+ assembled.setField1("y");
+ ASSERT_BSONOBJ_EQ(obj, assembled.toBSON());
+
+ // Test the constructor.
+ Chained_struct_variant_struct_inline constructed("y");
+ constructed.setOne_variant_struct(One_variant_struct(One_string("x")));
+ ASSERT_EQ(stdx::get<One_string>(constructed.getValue()).getValue(), "x");
+ ASSERT_EQ(constructed.getField1(), "y");
+ ASSERT_BSONOBJ_EQ(obj, constructed.toBSON());
+ }
+}
+
/// Struct tests:
// Positive: strict, 3 required fields
// Negative: strict, ensure extra fields fail
@@ -1783,12 +2251,7 @@ TEST(IDLCommand, TestConcatentateWithDb) {
assert_same_types<decltype(testStruct.getNamespace()), const NamespaceString&>();
// Positive: Test we can roundtrip from the just parsed document
- {
- BSONObjBuilder builder;
- OpMsgRequest reply = testStruct.serialize(BSONObj());
-
- ASSERT_BSONOBJ_EQ(testDoc, reply.body);
- }
+ ASSERT_BSONOBJ_EQ(testDoc, serializeCmd(testStruct));
// Positive: Test we can serialize from nothing the same document except for $db
{
@@ -1809,13 +2272,10 @@ TEST(IDLCommand, TestConcatentateWithDb) {
// Positive: Test we can serialize from nothing the same document
{
- BSONObjBuilder builder;
BasicConcatenateWithDbCommand one_new(NamespaceString("db.coll1"));
one_new.setField1(3);
one_new.setField2("five");
- OpMsgRequest reply = one_new.serialize(BSONObj());
-
- ASSERT_BSONOBJ_EQ(testDoc, reply.body);
+ ASSERT_BSONOBJ_EQ(testDoc, serializeCmd(testStruct));
}
}
@@ -1901,12 +2361,7 @@ TEST(IDLCommand, TestConcatentateWithDbOrUUID_TestNSS) {
assert_same_types<decltype(testStruct.getNamespaceOrUUID()), const NamespaceStringOrUUID&>();
// Positive: Test we can roundtrip from the just parsed document
- {
- BSONObjBuilder builder;
- OpMsgRequest reply = testStruct.serialize(BSONObj());
-
- ASSERT_BSONOBJ_EQ(testDoc, reply.body);
- }
+ ASSERT_BSONOBJ_EQ(testDoc, serializeCmd(testStruct));
// Positive: Test we can serialize from nothing the same document except for $db
{
@@ -1927,13 +2382,10 @@ TEST(IDLCommand, TestConcatentateWithDbOrUUID_TestNSS) {
// Positive: Test we can serialize from nothing the same document
{
- BSONObjBuilder builder;
BasicConcatenateWithDbOrUUIDCommand one_new(NamespaceString("db.coll1"));
one_new.setField1(3);
one_new.setField2("five");
- OpMsgRequest reply = one_new.serialize(BSONObj());
-
- ASSERT_BSONOBJ_EQ(testDoc, reply.body);
+ ASSERT_BSONOBJ_EQ(testDoc, serializeCmd(one_new));
}
}
@@ -1958,12 +2410,7 @@ TEST(IDLCommand, TestConcatentateWithDbOrUUID_TestUUID) {
assert_same_types<decltype(testStruct.getNamespaceOrUUID()), const NamespaceStringOrUUID&>();
// Positive: Test we can roundtrip from the just parsed document
- {
- BSONObjBuilder builder;
- OpMsgRequest reply = testStruct.serialize(BSONObj());
-
- ASSERT_BSONOBJ_EQ(testDoc, reply.body);
- }
+ ASSERT_BSONOBJ_EQ(testDoc, serializeCmd(testStruct));
// Positive: Test we can serialize from nothing the same document except for $db
{
@@ -1983,13 +2430,10 @@ TEST(IDLCommand, TestConcatentateWithDbOrUUID_TestUUID) {
// Positive: Test we can serialize from nothing the same document
{
- BSONObjBuilder builder;
BasicConcatenateWithDbOrUUIDCommand one_new(NamespaceStringOrUUID("db", uuid));
one_new.setField1(3);
one_new.setField2("five");
- OpMsgRequest reply = one_new.serialize(BSONObj());
-
- ASSERT_BSONOBJ_EQ(testDoc, reply.body);
+ ASSERT_BSONOBJ_EQ(testDoc, serializeCmd(one_new));
}
}
@@ -2067,14 +2511,11 @@ TEST(IDLCommand, TestIgnore) {
// Positive: Test we can serialize from nothing the same document
{
- BSONObjBuilder builder;
BasicIgnoredCommand one_new;
one_new.setField1(3);
one_new.setField2("five");
one_new.setDbName("admin");
- OpMsgRequest reply = one_new.serialize(BSONObj());
-
- ASSERT_BSONOBJ_EQ(testDocWithDB, reply.body);
+ ASSERT_BSONOBJ_EQ(testDocWithDB, serializeCmd(one_new));
}
}
@@ -2105,7 +2546,56 @@ TEST(IDLCommand, TestIgnoredNegative) {
}
}
-// Positive: Test a command read and written to OpMsgRequest works
+// We don't generate comparison operators like "==" for variants, so test only for BSON equality.
+template <typename CommandT, typename TestT, BSONType Test_bson_type>
+void TestLoopbackCommandTypeVariant(TestT test_value) {
+ IDLParserErrorContext ctxt("root");
+
+ BSONObjBuilder bob;
+ if constexpr (idl::hasBSONSerialize<TestT>) {
+ // TestT might be an IDL struct type like One_string.
+ BSONObjBuilder subObj(bob.subobjStart(CommandT::kCommandParameterFieldName));
+ test_value.serialize(&subObj);
+ } else {
+ bob.append(CommandT::kCommandParameterFieldName, test_value);
+ }
+
+ bob.append("$db", "db");
+ auto obj = bob.obj();
+ auto element = obj.firstElement();
+ ASSERT_EQUALS(element.type(), Test_bson_type);
+
+ auto parsed = CommandT::parse(ctxt, obj);
+ if constexpr (std::is_same_v<TestT, BSONObj>) {
+ ASSERT_BSONOBJ_EQ(stdx::get<TestT>(parsed.getValue()), test_value);
+ } else {
+ // Use ASSERT instead of ASSERT_EQ to avoid operator<<
+ ASSERT(stdx::get<TestT>(parsed.getCommandParameter()) == test_value);
+ }
+ ASSERT_BSONOBJ_EQ(obj, serializeCmd(parsed));
+
+ // Test the constructor.
+ CommandT constructed(test_value);
+ constructed.setDbName("db");
+ if constexpr (std::is_same_v<TestT, BSONObj>) {
+ ASSERT_BSONOBJ_EQ(stdx::get<TestT>(parsed.getValue()), test_value);
+ } else {
+ ASSERT(stdx::get<TestT>(parsed.getCommandParameter()) == test_value);
+ }
+ ASSERT_BSONOBJ_EQ(obj, serializeCmd(constructed));
+}
+
+TEST(IDLCommand, TestCommandTypeVariant) {
+ TestLoopbackCommandTypeVariant<CommandTypeVariantCommand, int, NumberInt>(1);
+ TestLoopbackCommandTypeVariant<CommandTypeVariantCommand, std::string, String>("test_value");
+ TestLoopbackCommandTypeVariant<CommandTypeVariantCommand, std::vector<std::string>, Array>(
+ {"x", "y"});
+
+ TestLoopbackCommandTypeVariant<CommandTypeVariantStructCommand, bool, Bool>(true);
+ TestLoopbackCommandTypeVariant<CommandTypeVariantStructCommand, One_string, Object>(
+ One_string("test_value"));
+}
+
TEST(IDLDocSequence, TestBasic) {
IDLParserErrorContext ctxt("root");
@@ -2828,12 +3318,7 @@ TEST(IDLTypeCommand, TestString) {
assert_same_types<decltype(testStruct.getCommandParameter()), const StringData>();
// Positive: Test we can roundtrip from the just parsed document
- {
- BSONObjBuilder builder;
- OpMsgRequest reply = testStruct.serialize(BSONObj());
-
- ASSERT_BSONOBJ_EQ(testDoc, reply.body);
- }
+ ASSERT_BSONOBJ_EQ(testDoc, serializeCmd(testStruct));
// Positive: Test we can serialize from nothing the same document except for $db
{
@@ -2852,13 +3337,11 @@ TEST(IDLTypeCommand, TestString) {
// Positive: Test we can serialize from nothing the same document
{
- BSONObjBuilder builder;
CommandTypeStringCommand one_new("foo");
one_new.setField1(3);
one_new.setDbName("db");
OpMsgRequest reply = one_new.serialize(BSONObj());
-
- ASSERT_BSONOBJ_EQ(testDoc, reply.body);
+ ASSERT_BSONOBJ_EQ(testDoc, serializeCmd(one_new));
}
}
@@ -2878,24 +3361,16 @@ TEST(IDLTypeCommand, TestArrayObject) {
const std::vector<mongo::BSONObj>&>();
// Positive: Test we can roundtrip from the just parsed document
- {
- BSONObjBuilder builder;
- OpMsgRequest reply = testStruct.serialize(BSONObj());
-
- ASSERT_BSONOBJ_EQ(testDoc, reply.body);
- }
+ ASSERT_BSONOBJ_EQ(testDoc, serializeCmd(testStruct));
// Positive: Test we can serialize from nothing the same document
{
- BSONObjBuilder builder;
std::vector<BSONObj> vec;
vec.emplace_back(BSON("sample"
<< "doc"));
CommandTypeArrayObjectCommand one_new(vec);
one_new.setDbName("db");
- OpMsgRequest reply = one_new.serialize(BSONObj());
-
- ASSERT_BSONOBJ_EQ(testDoc, reply.body);
+ ASSERT_BSONOBJ_EQ(testDoc, serializeCmd(one_new));
}
}
@@ -2923,23 +3398,15 @@ TEST(IDLTypeCommand, TestStruct) {
}
// Positive: Test we can roundtrip from the just parsed document
- {
- BSONObjBuilder builder;
- OpMsgRequest reply = testStruct.serialize(BSONObj());
-
- ASSERT_BSONOBJ_EQ(testDoc, reply.body);
- }
+ ASSERT_BSONOBJ_EQ(testDoc, serializeCmd(testStruct));
// Positive: Test we can serialize from nothing the same document
{
- BSONObjBuilder builder;
One_string os;
os.setValue("sample");
CommandTypeStructCommand one_new(os);
one_new.setDbName("db");
- OpMsgRequest reply = one_new.serialize(BSONObj());
-
- ASSERT_BSONOBJ_EQ(testDoc, reply.body);
+ ASSERT_BSONOBJ_EQ(testDoc, serializeCmd(one_new));
}
}
@@ -2959,25 +3426,17 @@ TEST(IDLTypeCommand, TestStructArray) {
const std::vector<mongo::idl::import::One_string>&>();
// Positive: Test we can roundtrip from the just parsed document
- {
- BSONObjBuilder builder;
- OpMsgRequest reply = testStruct.serialize(BSONObj());
-
- ASSERT_BSONOBJ_EQ(testDoc, reply.body);
- }
+ ASSERT_BSONOBJ_EQ(testDoc, serializeCmd(testStruct));
// Positive: Test we can serialize from nothing the same document
{
- BSONObjBuilder builder;
std::vector<One_string> vec;
One_string os;
os.setValue("sample");
vec.push_back(os);
CommandTypeArrayStructCommand one_new(vec);
one_new.setDbName("db");
- OpMsgRequest reply = one_new.serialize(BSONObj());
-
- ASSERT_BSONOBJ_EQ(testDoc, reply.body);
+ ASSERT_BSONOBJ_EQ(testDoc, serializeCmd(one_new));
}
}
@@ -2996,12 +3455,7 @@ TEST(IDLTypeCommand, TestUnderscoreCommand) {
assert_same_types<decltype(testStruct.getCommandParameter()), const StringData>();
// Positive: Test we can roundtrip from the just parsed document
- {
- BSONObjBuilder builder;
- OpMsgRequest reply = testStruct.serialize(BSONObj());
-
- ASSERT_BSONOBJ_EQ(testDoc, reply.body);
- }
+ ASSERT_BSONOBJ_EQ(testDoc, serializeCmd(testStruct));
// Positive: Test we can serialize from nothing the same document except for $db
{
@@ -3020,13 +3474,10 @@ TEST(IDLTypeCommand, TestUnderscoreCommand) {
// Positive: Test we can serialize from nothing the same document
{
- BSONObjBuilder builder;
WellNamedCommand one_new("foo");
one_new.setField1(3);
one_new.setDbName("db");
- OpMsgRequest reply = one_new.serialize(BSONObj());
-
- ASSERT_BSONOBJ_EQ(testDoc, reply.body);
+ ASSERT_BSONOBJ_EQ(testDoc, serializeCmd(one_new));
}
}
@@ -3138,12 +3589,7 @@ TEST(IDLCommand, BasicNamespaceConstGetterCommand_TestNonConstGetterGeneration)
const NamespaceStringOrUUID&>();
// Test we can roundtrip from the just parsed document.
- {
- BSONObjBuilder builder;
- OpMsgRequest reply = testStruct.serialize(BSONObj());
-
- ASSERT_BSONOBJ_EQ(testDoc, reply.body);
- }
+ ASSERT_BSONOBJ_EQ(testDoc, serializeCmd(testStruct));
// Test mutable getter modifies the command object.
{
diff --git a/src/mongo/idl/unittest.idl b/src/mongo/idl/unittest.idl
index 00723b8e173..1f1af1ceb24 100644
--- a/src/mongo/idl/unittest.idl
+++ b/src/mongo/idl/unittest.idl
@@ -609,6 +609,16 @@ structs:
- int
- one_string
+ one_variant_safeInt_array:
+ description: UnitTest for a single variant which accepts a string or array of safeInt
+ strict: false
+ fields:
+ value:
+ type:
+ variant:
+ - string
+ - array<safeInt>
+
one_variant_struct_array:
description: UnitTest for a single variant which accepts an int or array of structs
strict: false