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