summaryrefslogtreecommitdiff
path: root/src/mongo/idl
diff options
context:
space:
mode:
authorSara Golemon <sara.golemon@mongodb.com>2018-09-26 15:17:56 +0000
committerSara Golemon <sara.golemon@mongodb.com>2018-10-05 23:33:33 +0000
commitf9600749453db5dec58bd1bbfd967e16b1578a24 (patch)
tree22e0f6e8f773483ad12828d92f4bf8d1496fbe70 /src/mongo/idl
parent2db04b524dc5e2121b74829814db7c8e84e5696d (diff)
downloadmongo-f9600749453db5dec58bd1bbfd967e16b1578a24.tar.gz
SERVER-37168 Add validators for IDL fields
Diffstat (limited to 'src/mongo/idl')
-rw-r--r--src/mongo/idl/idl_test.cpp208
-rw-r--r--src/mongo/idl/idl_test.h70
-rw-r--r--src/mongo/idl/unittest.idl63
3 files changed, 340 insertions, 1 deletions
diff --git a/src/mongo/idl/idl_test.cpp b/src/mongo/idl/idl_test.cpp
index ed5ea3dc6ab..341509638f5 100644
--- a/src/mongo/idl/idl_test.cpp
+++ b/src/mongo/idl/idl_test.cpp
@@ -2401,6 +2401,214 @@ TEST(IDLChainedStruct, TestInline) {
}
}
+TEST(IDLValidatedField, Int_basic_ranges) {
+ // Explicitly call setters.
+ Int_basic_ranges obj0;
+ obj0.setPositive_int(42);
+ ASSERT_THROWS(obj0.setPositive_int(0), AssertionException);
+ ASSERT_THROWS(obj0.setPositive_int(-42), AssertionException);
+
+ ASSERT_THROWS(obj0.setNegative_int(42), AssertionException);
+ ASSERT_THROWS(obj0.setNegative_int(0), AssertionException);
+ obj0.setNegative_int(-42);
+
+ obj0.setNon_negative_int(42);
+ obj0.setNon_negative_int(0);
+ ASSERT_THROWS(obj0.setNon_negative_int(-42), AssertionException);
+
+ ASSERT_THROWS(obj0.setNon_positive_int(42), AssertionException);
+ obj0.setNon_positive_int(0);
+ obj0.setNon_positive_int(-42);
+
+ ASSERT_THROWS(obj0.setByte_range_int(-1), AssertionException);
+ obj0.setByte_range_int(0);
+ obj0.setByte_range_int(127);
+ obj0.setByte_range_int(128);
+ obj0.setByte_range_int(255);
+ ASSERT_THROWS(obj0.setByte_range_int(256), AssertionException);
+
+ // IDL ints *are* int32_t, so no number we can pass to the func will actually fail.
+ obj0.setRange_int(-2147483648);
+ obj0.setRange_int(-65536);
+ obj0.setRange_int(0);
+ obj0.setRange_int(65536);
+ obj0.setRange_int(2147483647);
+
+ // Positive case parsing.
+ const auto tryPass = [](std::int32_t pos,
+ std::int32_t neg,
+ std::int32_t nonneg,
+ std::int32_t nonpos,
+ std::int32_t byte_range,
+ std::int32_t int_range) {
+ IDLParserErrorContext ctxt("root");
+ auto doc =
+ BSON("positive_int" << pos << "negative_int" << neg << "non_negative_int" << nonneg
+ << "non_positive_int"
+ << nonpos
+ << "byte_range_int"
+ << byte_range
+ << "range_int"
+ << int_range);
+ auto obj = Int_basic_ranges::parse(ctxt, doc);
+ ASSERT_EQUALS(obj.getPositive_int(), pos);
+ ASSERT_EQUALS(obj.getNegative_int(), neg);
+ ASSERT_EQUALS(obj.getNon_negative_int(), nonneg);
+ ASSERT_EQUALS(obj.getNon_positive_int(), nonpos);
+ ASSERT_EQUALS(obj.getByte_range_int(), byte_range);
+ ASSERT_EQUALS(obj.getRange_int(), int_range);
+ };
+
+ // Negative case parsing.
+ const auto tryFail = [](std::int32_t pos,
+ std::int32_t neg,
+ std::int32_t nonneg,
+ std::int32_t nonpos,
+ std::int32_t byte_range,
+ std::int32_t int_range) {
+ IDLParserErrorContext ctxt("root");
+ auto doc =
+ BSON("positive_int" << pos << "negative_int" << neg << "non_negative_int" << nonneg
+ << "non_positive_int"
+ << nonpos
+ << "byte_range_int"
+ << byte_range
+ << "range_int"
+ << int_range);
+ ASSERT_THROWS(Int_basic_ranges::parse(ctxt, doc), AssertionException);
+ };
+
+ tryPass(1, -1, 0, 0, 128, 65537);
+ tryFail(0, -1, 0, 0, 128, 65537);
+ tryFail(1, 0, 0, 0, 128, 65537);
+ tryFail(1, -1, -1, 0, 128, 65537);
+ tryFail(1, -1, 0, 1, 128, 65537);
+ tryFail(1, -1, 0, 0, 256, 65537);
+ tryFail(0, 0, -1, 1, 257, 0);
+
+ tryPass(1000, -1000, 1, -1, 127, 0x7FFFFFFF);
+}
+
+TEST(IDLValidatedField, Double_basic_ranges) {
+ // Explicitly call setters.
+ Double_basic_ranges obj0;
+ obj0.setPositive_double(42.0);
+ obj0.setPositive_double(0.000000000001);
+ ASSERT_THROWS(obj0.setPositive_double(0.0), AssertionException);
+ ASSERT_THROWS(obj0.setPositive_double(-42.0), AssertionException);
+
+ ASSERT_THROWS(obj0.setNegative_double(42.0), AssertionException);
+ ASSERT_THROWS(obj0.setNegative_double(0.0), AssertionException);
+ obj0.setNegative_double(-0.000000000001);
+ obj0.setNegative_double(-42.0);
+
+ obj0.setNon_negative_double(42.0);
+ obj0.setNon_negative_double(0.0);
+ ASSERT_THROWS(obj0.setNon_negative_double(-42.0), AssertionException);
+
+ ASSERT_THROWS(obj0.setNon_positive_double(42.0), AssertionException);
+ obj0.setNon_positive_double(0.0);
+ obj0.setNon_positive_double(-42.0);
+
+ ASSERT_THROWS(obj0.setRange_double(-12345678901234600000.0), AssertionException);
+ obj0.setRange_double(-12345678901234500000.0);
+ obj0.setRange_double(-3000000000.0);
+ obj0.setRange_double(0);
+ obj0.setRange_double(3000000000);
+ obj0.setRange_double(12345678901234500000.0);
+ ASSERT_THROWS(obj0.setRange_double(12345678901234600000.0), AssertionException);
+
+ // Positive case parsing.
+ const auto tryPass =
+ [](double pos, double neg, double nonneg, double nonpos, double double_range) {
+ IDLParserErrorContext ctxt("root");
+ auto doc =
+ BSON("positive_double" << pos << "negative_double" << neg << "non_negative_double"
+ << nonneg
+ << "non_positive_double"
+ << nonpos
+ << "range_double"
+ << double_range);
+ auto obj = Double_basic_ranges::parse(ctxt, doc);
+ ASSERT_EQUALS(obj.getPositive_double(), pos);
+ ASSERT_EQUALS(obj.getNegative_double(), neg);
+ ASSERT_EQUALS(obj.getNon_negative_double(), nonneg);
+ ASSERT_EQUALS(obj.getNon_positive_double(), nonpos);
+ ASSERT_EQUALS(obj.getRange_double(), double_range);
+ };
+
+ // Negative case parsing.
+ const auto tryFail =
+ [](double pos, double neg, double nonneg, double nonpos, double double_range) {
+ IDLParserErrorContext ctxt("root");
+ auto doc =
+ BSON("positive_double" << pos << "negative_double" << neg << "non_negative_double"
+ << nonneg
+ << "non_positive_double"
+ << nonpos
+ << "range_double"
+ << double_range);
+ ASSERT_THROWS(Double_basic_ranges::parse(ctxt, doc), AssertionException);
+ };
+
+ tryPass(1, -1, 0, 0, 123456789012345);
+ tryFail(0, -1, 0, 0, 123456789012345);
+ tryFail(1, 0, 0, 0, 123456789012345);
+ tryFail(1, -1, -1, 0, 123456789012345);
+ tryFail(1, -1, 0, 1, 123456789012345);
+ tryFail(1, -1, 0, -1, 12345678901234600000.0);
+ tryPass(0.00000000001, -0.00000000001, 0.0, 0.0, 1.23456789012345);
+}
+
+TEST(IDLValidatedField, Callback_validators) {
+ // Explicitly call setters.
+ Callback_validators obj0;
+ obj0.setInt_even(42);
+ ASSERT_THROWS(obj0.setInt_even(7), AssertionException);
+ obj0.setInt_even(0);
+ ASSERT_THROWS(obj0.setInt_even(-7), AssertionException);
+ obj0.setInt_even(-42);
+
+ ASSERT_THROWS(obj0.setDouble_nearly_int(3.141592), AssertionException);
+ ASSERT_THROWS(obj0.setDouble_nearly_int(-2.71828), AssertionException);
+ obj0.setDouble_nearly_int(0.0);
+ obj0.setDouble_nearly_int(1.0);
+ obj0.setDouble_nearly_int(1.05);
+ obj0.setDouble_nearly_int(-123456789.01234500000);
+
+ ASSERT_THROWS(obj0.setString_starts_with_x("whiskey"), AssertionException);
+ obj0.setString_starts_with_x("x-ray");
+ ASSERT_THROWS(obj0.setString_starts_with_x("yankee"), AssertionException);
+
+ // Positive case parsing.
+ const auto tryPass =
+ [](std::int32_t int_even, double double_nearly_int, StringData string_starts_with_x) {
+ IDLParserErrorContext ctxt("root");
+ auto doc = BSON("int_even" << int_even << "double_nearly_int" << double_nearly_int
+ << "string_starts_with_x"
+ << string_starts_with_x);
+ auto obj = Callback_validators::parse(ctxt, doc);
+ ASSERT_EQUALS(obj.getInt_even(), int_even);
+ ASSERT_EQUALS(obj.getDouble_nearly_int(), double_nearly_int);
+ ASSERT_EQUALS(obj.getString_starts_with_x(), string_starts_with_x);
+ };
+
+ // Negative case parsing.
+ const auto tryFail =
+ [](std::int32_t int_even, double double_nearly_int, StringData string_starts_with_x) {
+ IDLParserErrorContext ctxt("root");
+ auto doc = BSON("int_even" << int_even << "double_nearly_int" << double_nearly_int
+ << "string_starts_with_x"
+ << string_starts_with_x);
+ ASSERT_THROWS(Callback_validators::parse(ctxt, doc), AssertionException);
+ };
+
+ tryPass(42, 123456789.01, "x-ray");
+ tryFail(43, 123456789.01, "x-ray");
+ tryFail(42, 123456789.11, "x-ray");
+ tryFail(42, 123456789.01, "uniform");
+}
+
// Positive: verify a command a string arg
TEST(IDLTypeCommand, TestString) {
IDLParserErrorContext ctxt("root");
diff --git a/src/mongo/idl/idl_test.h b/src/mongo/idl/idl_test.h
new file mode 100644
index 00000000000..ba9cbf7dfe6
--- /dev/null
+++ b/src/mongo/idl/idl_test.h
@@ -0,0 +1,70 @@
+/**
+ * Copyright (C) 2017 MongoDB Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * As a special exception, the copyright holders give permission to link the
+ * code of portions of this program with the OpenSSL library under certain
+ * conditions as described in each individual source file and distribute
+ * linked combinations including the program with the OpenSSL library. You
+ * must comply with the GNU Affero General Public License in all respects
+ * for all of the code used other than as permitted herein. If you modify
+ * file(s) with this exception, you may extend this exception to your
+ * version of the file(s), but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version. If you
+ * delete this exception statement from all source files in the program,
+ * then also delete it in the license file.
+ */
+
+#include "mongo/base/status.h"
+
+namespace mongo {
+namespace idl {
+namespace test {
+
+/**
+ * Validates the given number is even
+ */
+inline Status validateEvenNumber(std::int32_t value) {
+ if (value & 1) {
+ return {ErrorCodes::BadValue, "Value must be even"};
+ }
+ return Status::OK();
+}
+
+/**
+ * Validates that the number presented is within 0.1 of an integer value
+ */
+inline Status validateNearlyInt(double value) {
+ value = fabs(value);
+ value = value - static_cast<std::uint64_t>(value);
+ if ((value > 0.1) && (value < 0.9)) {
+ return {ErrorCodes::BadValue, "Value is too far from being an integer"};
+ }
+ return Status::OK();
+}
+
+/**
+ * Validates that the provided string starts with a given letter.
+ */
+template <char letter>
+Status validateStartsWith(StringData value) {
+ if ((value.empty() || value[0] != letter)) {
+ return {ErrorCodes::BadValue, "Value does not begin with correct letter"};
+ }
+ return Status::OK();
+}
+
+} // namespace test
+} // namespace idl
+} // namespace mongo
diff --git a/src/mongo/idl/unittest.idl b/src/mongo/idl/unittest.idl
index ddbe386fd91..56c175e095a 100644
--- a/src/mongo/idl/unittest.idl
+++ b/src/mongo/idl/unittest.idl
@@ -19,6 +19,7 @@ global:
cpp_namespace: "mongo::idl::test"
cpp_includes:
- "mongo/idl/idl_test_types.h"
+ - "mongo/idl/idl_test.h"
imports:
- "mongo/idl/basic_types.idl"
@@ -498,6 +499,67 @@ structs:
##################################################################################################
#
+# Using Validators
+#
+##################################################################################################
+
+ int_basic_ranges:
+ description: Struct using basic range validators on ints
+ fields:
+ positive_int:
+ type: int
+ validator: { gt: 0 }
+ negative_int:
+ type: int
+ validator: { lt: 0 }
+ non_negative_int:
+ type: int
+ validator: { gte: 0 }
+ non_positive_int:
+ type: int
+ validator: { lte: 0 }
+ byte_range_int:
+ type: int
+ validator: { gte: 0, lt: 256 }
+ range_int:
+ type: int
+ validator: { gte: -2147483658, lte: 2147483647 }
+
+ double_basic_ranges:
+ description: Struct using basic range validators on doubles
+ fields:
+ positive_double:
+ type: double
+ validator: { gt: 0.0 }
+ negative_double:
+ type: double
+ validator: { lt: 0.0 }
+ non_negative_double:
+ type: double
+ validator: { gte: 0.0 }
+ non_positive_double:
+ type: double
+ validator: { lte: 0.0 }
+ range_double:
+ type: double
+ validator: { gte: -12345678901234500000, lte: 12345678901234500000 }
+
+ callback_validators:
+ description: Struct using fields with callback validators
+ fields:
+ int_even:
+ type: int
+ validator: { callback: 'validateEvenNumber' }
+ double_nearly_int:
+ type: double
+ validator: { callback: 'validateNearlyInt' }
+ string_starts_with_x:
+ type: string
+ validator: { callback: "validateStartsWith<'x'>" }
+
+
+##################################################################################################
+#
# Test commands
#
##################################################################################################
@@ -618,4 +680,3 @@ commands:
fields:
field1: int
-