1
2 /**
3 * Copyright (C) 2018-present MongoDB, Inc.
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the Server Side Public License, version 1,
7 * as published by MongoDB, Inc.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * Server Side Public License for more details.
13 *
14 * You should have received a copy of the Server Side Public License
15 * along with this program. If not, see
16 * <http://www.mongodb.com/licensing/server-side-public-license>.
17 *
18 * As a special exception, the copyright holders give permission to link the
19 * code of portions of this program with the OpenSSL library under certain
20 * conditions as described in each individual source file and distribute
21 * linked combinations including the program with the OpenSSL library. You
22 * must comply with the Server Side Public License in all respects for
23 * all of the code used other than as permitted herein. If you modify file(s)
24 * with this exception, you may extend this exception to your version of the
25 * file(s), but you are not obligated to do so. If you do not wish to do so,
26 * delete this exception statement from your version. If you delete this
27 * exception statement from all source files in the program, then also delete
28 * it in the license file.
29 */
30
31 #include "mongo/platform/basic.h"
32
33 #include "mongo/db/update/bit_node.h"
34
35 #include "mongo/bson/mutable/algorithm.h"
36 #include "mongo/bson/mutable/mutable_bson_test_utils.h"
37 #include "mongo/db/json.h"
38 #include "mongo/db/pipeline/expression_context_for_test.h"
39 #include "mongo/db/update/update_node_test_fixture.h"
40 #include "mongo/unittest/death_test.h"
41 #include "mongo/unittest/unittest.h"
42
43 namespace mongo {
44 namespace {
45
46 using BitNodeTest = UpdateNodeTest;
47 using mongo::mutablebson::Element;
48 using mongo::mutablebson::countChildren;
49
TEST(BitNodeTest,InitWithDoubleFails)50 TEST(BitNodeTest, InitWithDoubleFails) {
51 boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
52 auto update = fromjson("{$bit: {a: 0}}");
53 BitNode node;
54 ASSERT_NOT_OK(node.init(update["$bit"]["a"], expCtx));
55 }
56
TEST(BitNodeTest,InitWithStringFails)57 TEST(BitNodeTest, InitWithStringFails) {
58 boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
59 auto update = fromjson("{$bit: {a: ''}}");
60 BitNode node;
61 ASSERT_NOT_OK(node.init(update["$bit"]["a"], expCtx));
62 }
63
TEST(BitNodeTest,InitWithArrayFails)64 TEST(BitNodeTest, InitWithArrayFails) {
65 boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
66 auto update = fromjson("{$bit: {a: []}}");
67 BitNode node;
68 ASSERT_NOT_OK(node.init(update["$bit"]["a"], expCtx));
69 }
70
TEST(BitNodeTest,InitWithEmptyDocumentFails)71 TEST(BitNodeTest, InitWithEmptyDocumentFails) {
72 boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
73 auto update = fromjson("{$bit: {a: {}}}}");
74 BitNode node;
75 ASSERT_NOT_OK(node.init(update["$bit"]["a"], expCtx));
76 }
77
TEST(BitNodeTest,InitWithUnknownOperatorFails)78 TEST(BitNodeTest, InitWithUnknownOperatorFails) {
79 boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
80 auto update = fromjson("{$bit: {a: {foo: 4}}}");
81 BitNode node;
82 ASSERT_NOT_OK(node.init(update["$bit"]["a"], expCtx));
83 }
84
TEST(BitNodeTest,InitWithArrayArgumentToOperatorFails)85 TEST(BitNodeTest, InitWithArrayArgumentToOperatorFails) {
86 boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
87 auto update = fromjson("{$bit: {a: {or: []}}}");
88 BitNode node;
89 ASSERT_NOT_OK(node.init(update["$bit"]["a"], expCtx));
90 }
91
TEST(BitNodeTest,InitWithStringArgumentToOperatorFails)92 TEST(BitNodeTest, InitWithStringArgumentToOperatorFails) {
93 boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
94 auto update = fromjson("{$bit: {a: {or: 'foo'}}}");
95 BitNode node;
96 ASSERT_NOT_OK(node.init(update["$bit"]["a"], expCtx));
97 }
98
TEST(BitNodeTest,InitWithDoubleArgumentToOperatorFails)99 TEST(BitNodeTest, InitWithDoubleArgumentToOperatorFails) {
100 boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
101 auto update = fromjson("{$bit: {a: {or: 1.0}}}");
102 BitNode node;
103 ASSERT_NOT_OK(node.init(update["$bit"]["a"], expCtx));
104 }
105
TEST(BitNodeTest,InitWithDecimalArgumentToOperatorFails)106 TEST(BitNodeTest, InitWithDecimalArgumentToOperatorFails) {
107 boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
108 auto update = fromjson("{$bit: {a: {or: NumberDecimal(\"1.0\")}}}");
109 BitNode node;
110 ASSERT_NOT_OK(node.init(update["$bit"]["a"], expCtx));
111 }
112
TEST(BitNodeTest,ParsesAndInt)113 TEST(BitNodeTest, ParsesAndInt) {
114 auto update = fromjson("{$bit: {a: {and: NumberInt(1)}}}");
115 boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
116 BitNode node;
117 ASSERT_OK(node.init(update["$bit"]["a"], expCtx));
118 }
119
TEST(BitNodeTest,ParsesOrInt)120 TEST(BitNodeTest, ParsesOrInt) {
121 auto update = fromjson("{$bit: {a: {or: NumberInt(1)}}}");
122 boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
123 BitNode node;
124 ASSERT_OK(node.init(update["$bit"]["a"], expCtx));
125 }
126
TEST(BitNodeTest,ParsesXorInt)127 TEST(BitNodeTest, ParsesXorInt) {
128 auto update = fromjson("{$bit: {a: {xor: NumberInt(1)}}}");
129 boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
130 BitNode node;
131 ASSERT_OK(node.init(update["$bit"]["a"], expCtx));
132 }
133
TEST(BitNodeTest,ParsesAndLong)134 TEST(BitNodeTest, ParsesAndLong) {
135 auto update = fromjson("{$bit: {a: {and: NumberLong(1)}}}");
136 boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
137 BitNode node;
138 ASSERT_OK(node.init(update["$bit"]["a"], expCtx));
139 }
140
TEST(BitNodeTest,ParsesOrLong)141 TEST(BitNodeTest, ParsesOrLong) {
142 auto update = fromjson("{$bit: {a: {or: NumberLong(1)}}}");
143 boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
144 BitNode node;
145 ASSERT_OK(node.init(update["$bit"]["a"], expCtx));
146 }
147
TEST(BitNodeTest,ParsesXorLong)148 TEST(BitNodeTest, ParsesXorLong) {
149 auto update = fromjson("{$bit: {a: {xor: NumberLong(1)}}}");
150 boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
151 BitNode node;
152 ASSERT_OK(node.init(update["$bit"]["a"], expCtx));
153 }
154
TEST_F(BitNodeTest,ApplyAndLogEmptyDocumentAnd)155 TEST_F(BitNodeTest, ApplyAndLogEmptyDocumentAnd) {
156 auto update = fromjson("{$bit: {a: {and: 1}}}");
157 boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
158 BitNode node;
159 ASSERT_OK(node.init(update["$bit"]["a"], expCtx));
160
161 mutablebson::Document doc(fromjson("{}"));
162 setPathToCreate("a");
163 auto result = node.apply(getApplyParams(doc.root()));
164 ASSERT_FALSE(result.noop);
165 ASSERT_EQUALS(fromjson("{a: 0}"), doc);
166 ASSERT_FALSE(doc.isInPlaceModeEnabled());
167 ASSERT_EQUALS(fromjson("{$set: {a: 0}}"), getLogDoc());
168 }
169
TEST_F(BitNodeTest,ApplyAndLogEmptyDocumentOr)170 TEST_F(BitNodeTest, ApplyAndLogEmptyDocumentOr) {
171 auto update = fromjson("{$bit: {a: {or: 1}}}");
172 boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
173 BitNode node;
174 ASSERT_OK(node.init(update["$bit"]["a"], expCtx));
175
176 mutablebson::Document doc(fromjson("{}"));
177 setPathToCreate("a");
178 auto result = node.apply(getApplyParams(doc.root()));
179 ASSERT_FALSE(result.noop);
180 ASSERT_EQUALS(fromjson("{a: 1}"), doc);
181 ASSERT_FALSE(doc.isInPlaceModeEnabled());
182 ASSERT_EQUALS(fromjson("{$set: {a: 1}}"), getLogDoc());
183 }
184
TEST_F(BitNodeTest,ApplyAndLogEmptyDocumentXor)185 TEST_F(BitNodeTest, ApplyAndLogEmptyDocumentXor) {
186 auto update = fromjson("{$bit: {a: {xor: 1}}}");
187 boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
188 BitNode node;
189 ASSERT_OK(node.init(update["$bit"]["a"], expCtx));
190
191 mutablebson::Document doc(fromjson("{}"));
192 setPathToCreate("a");
193 auto result = node.apply(getApplyParams(doc.root()));
194 ASSERT_FALSE(result.noop);
195 ASSERT_EQUALS(fromjson("{a: 1}"), doc);
196 ASSERT_FALSE(doc.isInPlaceModeEnabled());
197 ASSERT_EQUALS(fromjson("{$set: {a: 1}}"), getLogDoc());
198 }
199
TEST_F(BitNodeTest,ApplyAndLogSimpleDocumentAnd)200 TEST_F(BitNodeTest, ApplyAndLogSimpleDocumentAnd) {
201 auto update = BSON("$bit" << BSON("a" << BSON("and" << 0b0110)));
202 boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
203 BitNode node;
204 ASSERT_OK(node.init(update["$bit"]["a"], expCtx));
205
206 mutablebson::Document doc(BSON("a" << 0b0101));
207 setPathTaken("a");
208 auto result = node.apply(getApplyParams(doc.root()["a"]));
209 ASSERT_FALSE(result.noop);
210 ASSERT_EQUALS(BSON("a" << 0b0100), doc);
211 ASSERT_TRUE(doc.isInPlaceModeEnabled());
212 ASSERT_EQUALS(BSON("$set" << BSON("a" << 0b0100)), getLogDoc());
213 }
214
TEST_F(BitNodeTest,ApplyAndLogSimpleDocumentOr)215 TEST_F(BitNodeTest, ApplyAndLogSimpleDocumentOr) {
216 auto update = BSON("$bit" << BSON("a" << BSON("or" << 0b0110)));
217 boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
218 BitNode node;
219 ASSERT_OK(node.init(update["$bit"]["a"], expCtx));
220
221 mutablebson::Document doc(BSON("a" << 0b0101));
222 setPathTaken("a");
223 auto result = node.apply(getApplyParams(doc.root()["a"]));
224 ASSERT_FALSE(result.noop);
225 ASSERT_EQUALS(BSON("a" << 0b0111), doc);
226 ASSERT_TRUE(doc.isInPlaceModeEnabled());
227 ASSERT_EQUALS(BSON("$set" << BSON("a" << 0b0111)), getLogDoc());
228 }
229
TEST_F(BitNodeTest,ApplyAndLogSimpleDocumentXor)230 TEST_F(BitNodeTest, ApplyAndLogSimpleDocumentXor) {
231 auto update = BSON("$bit" << BSON("a" << BSON("xor" << 0b0110)));
232 boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
233 BitNode node;
234 ASSERT_OK(node.init(update["$bit"]["a"], expCtx));
235
236 mutablebson::Document doc(BSON("a" << 0b0101));
237 setPathTaken("a");
238 auto result = node.apply(getApplyParams(doc.root()["a"]));
239 ASSERT_FALSE(result.noop);
240 ASSERT_EQUALS(BSON("a" << 0b0011), doc);
241 ASSERT_TRUE(doc.isInPlaceModeEnabled());
242 ASSERT_EQUALS(BSON("$set" << BSON("a" << 0b0011)), getLogDoc());
243 }
244
TEST_F(BitNodeTest,ApplyShouldReportNoOp)245 TEST_F(BitNodeTest, ApplyShouldReportNoOp) {
246 auto update = BSON("$bit" << BSON("a" << BSON("and" << static_cast<int>(1))));
247 boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
248 BitNode node;
249 ASSERT_OK(node.init(update["$bit"]["a"], expCtx));
250
251 mutablebson::Document doc(BSON("a" << 1));
252 setPathTaken("a");
253 auto result = node.apply(getApplyParams(doc.root()["a"]));
254 ASSERT_TRUE(result.noop);
255 ASSERT_EQUALS(BSON("a" << static_cast<int>(1)), doc);
256 ASSERT_TRUE(doc.isInPlaceModeEnabled());
257 ASSERT_EQUALS(fromjson("{}"), getLogDoc());
258 }
259
TEST_F(BitNodeTest,ApplyMultipleBitOps)260 TEST_F(BitNodeTest, ApplyMultipleBitOps) {
261 // End-of-line comments help clang-format break up this line more readably.
262 auto update = BSON("$bit" << BSON("a" << BSON("and" << 0b1111000011110000 //
263 << //
264 "or" << 0b1100110011001100 //
265 << //
266 "xor" << 0b1010101010101010)));
267 boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
268 BitNode node;
269 ASSERT_OK(node.init(update["$bit"]["a"], expCtx));
270
271 mutablebson::Document doc(BSON("a" << 0b1111111100000000));
272 setPathTaken("a");
273 auto result = node.apply(getApplyParams(doc.root()["a"]));
274 ASSERT_FALSE(result.noop);
275 ASSERT_EQUALS(BSON("a" << 0b0101011001100110), doc);
276 ASSERT_TRUE(doc.isInPlaceModeEnabled());
277 ASSERT_EQUALS(BSON("$set" << BSON("a" << 0b0101011001100110)), getLogDoc());
278 }
279
TEST_F(BitNodeTest,ApplyRepeatedBitOps)280 TEST_F(BitNodeTest, ApplyRepeatedBitOps) {
281 auto update = BSON("$bit" << BSON("a" << BSON("xor" << 0b11001100 << "xor" << 0b10101010)));
282 boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
283 BitNode node;
284 ASSERT_OK(node.init(update["$bit"]["a"], expCtx));
285
286 mutablebson::Document doc(BSON("a" << 0b11110000));
287 setPathTaken("a");
288 auto result = node.apply(getApplyParams(doc.root()["a"]));
289 ASSERT_FALSE(result.noop);
290 ASSERT_EQUALS(BSON("a" << 0b10010110), doc);
291 ASSERT_TRUE(doc.isInPlaceModeEnabled());
292 ASSERT_EQUALS(BSON("$set" << BSON("a" << 0b10010110)), getLogDoc());
293 }
294
295 } // namespace
296 } // namepace mongo
297