407ed5fe62c2f2f4a64d3e136f237f00d35ad287
[folly.git] / folly / test / DynamicTest.cpp
1 /*
2  * Copyright 2017 Facebook, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include <folly/dynamic.h>
18
19 #include <folly/portability/GTest.h>
20
21 #include <boost/next_prior.hpp>
22
23 using folly::dynamic;
24
25 #ifndef FOLLY_SANITIZE_ADDRESS
26 // This test runs without any external dependencies, including json.
27 // This means that if there's a test failure, there's no way to print
28 // a useful runtime representation of the folly::dynamic.  We will
29 // live with this in order to test dependencies.  This method is
30 // normally provided by json.cpp.
31 void dynamic::print_as_pseudo_json(std::ostream& out) const {
32   out << "<folly::dynamic object of type " << type_ << ">";
33 }
34 #endif
35
36 TEST(Dynamic, Default) {
37   dynamic obj;
38   EXPECT_TRUE(obj.isNull());
39 }
40
41 TEST(Dynamic, ObjectBasics) {
42   dynamic obj = dynamic::object("a", false);
43   EXPECT_EQ(obj.at("a"), false);
44   EXPECT_EQ(obj.size(), 1);
45   obj.insert("a", true);
46   EXPECT_EQ(obj.size(), 1);
47   EXPECT_EQ(obj.at("a"), true);
48   obj.at("a") = nullptr;
49   EXPECT_EQ(obj.size(), 1);
50   EXPECT_TRUE(obj.at("a") == nullptr);
51
52   dynamic newObject = dynamic::object;
53
54   newObject["z"] = 12;
55   EXPECT_EQ(newObject.size(), 1);
56   newObject["a"] = true;
57   EXPECT_EQ(newObject.size(), 2);
58
59   EXPECT_EQ(*newObject.keys().begin(), newObject.items().begin()->first);
60   EXPECT_EQ(*newObject.values().begin(), newObject.items().begin()->second);
61   std::vector<std::pair<std::string, dynamic>> found;
62   found.emplace_back(newObject.keys().begin()->asString(),
63                      *newObject.values().begin());
64
65   EXPECT_EQ(*boost::next(newObject.keys().begin()),
66             boost::next(newObject.items().begin())->first);
67   EXPECT_EQ(*boost::next(newObject.values().begin()),
68             boost::next(newObject.items().begin())->second);
69   found.emplace_back(boost::next(newObject.keys().begin())->asString(),
70                      *boost::next(newObject.values().begin()));
71
72   std::sort(found.begin(), found.end());
73
74   EXPECT_EQ("a", found[0].first);
75   EXPECT_TRUE(found[0].second.asBool());
76
77   EXPECT_EQ("z", found[1].first);
78   EXPECT_EQ(12, found[1].second.asInt());
79
80   dynamic obj2 = dynamic::object;
81   EXPECT_TRUE(obj2.isObject());
82
83   dynamic d3 = nullptr;
84   EXPECT_TRUE(d3 == nullptr);
85   d3 = dynamic::object;
86   EXPECT_TRUE(d3.isObject());
87   d3["foo"] = dynamic::array(1, 2, 3);
88   EXPECT_EQ(d3.count("foo"), 1);
89
90   d3[123] = 321;
91   EXPECT_EQ(d3.at(123), 321);
92
93   d3["123"] = 42;
94   EXPECT_EQ(d3.at("123"), 42);
95   EXPECT_EQ(d3.at(123), 321);
96
97   dynamic objInsert = folly::dynamic::object();
98   dynamic objA = folly::dynamic::object("1", "2");
99   dynamic objB = folly::dynamic::object("1", "2");
100
101   objInsert.insert("1", std::move(objA));
102   objInsert.insert("1", std::move(objB));
103
104   EXPECT_EQ(objInsert.find("1")->second.size(), 1);
105
106   // We don't allow objects as keys in objects.
107   EXPECT_ANY_THROW(newObject[d3] = 12);
108
109   // Merge two objects
110   dynamic origMergeObj1 = folly::dynamic::object();
111   dynamic mergeObj1 = origMergeObj1 = folly::dynamic::object
112     ("key1", "value1")
113     ("key2", "value2");
114   dynamic mergeObj2 = folly::dynamic::object
115     ("key2", "value3")
116     ("key3", "value4");
117
118   // Merged object where we prefer the values in mergeObj2
119   dynamic combinedPreferObj2 = folly::dynamic::object
120     ("key1", "value1")
121     ("key2", "value3")
122     ("key3", "value4");
123
124   // Merged object where we prefer the values in mergeObj1
125   dynamic combinedPreferObj1 = folly::dynamic::object
126     ("key1", "value1")
127     ("key2", "value2")
128     ("key3", "value4");
129
130   auto newMergeObj = dynamic::merge(mergeObj1, mergeObj2);
131   EXPECT_EQ(newMergeObj, combinedPreferObj2);
132   EXPECT_EQ(mergeObj1, origMergeObj1); // mergeObj1 should be unchanged
133
134   mergeObj1.update(mergeObj2);
135   EXPECT_EQ(mergeObj1, combinedPreferObj2);
136   dynamic arr = dynamic::array(1, 2, 3, 4, 5, 6);
137   EXPECT_THROW(mergeObj1.update(arr), std::exception);
138
139   mergeObj1 = origMergeObj1; // reset it
140   mergeObj1.update_missing(mergeObj2);
141   EXPECT_EQ(mergeObj1, combinedPreferObj1);
142 }
143
144 TEST(Dynamic, ObjectErase) {
145   dynamic obj = dynamic::object("key1", "val")
146                                ("key2", "val2");
147   EXPECT_EQ(obj.count("key1"), 1);
148   EXPECT_EQ(obj.count("key2"), 1);
149   EXPECT_EQ(obj.erase("key1"), 1);
150   EXPECT_EQ(obj.count("key1"), 0);
151   EXPECT_EQ(obj.count("key2"), 1);
152   EXPECT_EQ(obj.erase("key1"), 0);
153   obj["key1"] = 12;
154   EXPECT_EQ(obj.count("key1"), 1);
155   EXPECT_EQ(obj.count("key2"), 1);
156   auto it = obj.find("key2");
157   obj.erase(it);
158   EXPECT_EQ(obj.count("key1"), 1);
159   EXPECT_EQ(obj.count("key2"), 0);
160
161   obj["asd"] = 42.0;
162   obj["foo"] = 42.0;
163   EXPECT_EQ(obj.size(), 3);
164   auto ret = obj.erase(boost::next(obj.items().begin()), obj.items().end());
165   EXPECT_TRUE(ret == obj.items().end());
166   EXPECT_EQ(obj.size(), 1);
167   obj.erase(obj.items().begin());
168   EXPECT_TRUE(obj.empty());
169 }
170
171 TEST(Dynamic, ArrayErase) {
172   dynamic arr = dynamic::array(1, 2, 3, 4, 5, 6);
173
174   EXPECT_THROW(arr.erase(1), std::exception);
175   EXPECT_EQ(arr.size(), 6);
176   EXPECT_EQ(arr[0], 1);
177   arr.erase(arr.begin());
178   EXPECT_EQ(arr.size(), 5);
179
180   arr.erase(boost::next(arr.begin()), boost::prior(arr.end()));
181   EXPECT_EQ(arr.size(), 2);
182   EXPECT_EQ(arr[0], 2);
183   EXPECT_EQ(arr[1], 6);
184 }
185
186 TEST(Dynamic, StringBasics) {
187   dynamic str = "hello world";
188   EXPECT_EQ(11, str.size());
189   EXPECT_FALSE(str.empty());
190   str = "";
191   EXPECT_TRUE(str.empty());
192 }
193
194 TEST(Dynamic, ArrayBasics) {
195   dynamic array = dynamic::array(1, 2, 3);
196   EXPECT_EQ(array.size(), 3);
197   EXPECT_EQ(array.at(0), 1);
198   EXPECT_EQ(array.at(1), 2);
199   EXPECT_EQ(array.at(2), 3);
200
201   EXPECT_ANY_THROW(array.at(-1));
202   EXPECT_ANY_THROW(array.at(3));
203
204   array.push_back("foo");
205   EXPECT_EQ(array.size(), 4);
206
207   array.resize(12, "something");
208   EXPECT_EQ(array.size(), 12);
209   EXPECT_EQ(array[11], "something");
210 }
211
212 TEST(Dynamic, DeepCopy) {
213   dynamic val = dynamic::array("foo", "bar", dynamic::array("foo1", "bar1"));
214   EXPECT_EQ(val.at(2).at(0), "foo1");
215   EXPECT_EQ(val.at(2).at(1), "bar1");
216   dynamic val2 = val;
217   EXPECT_EQ(val2.at(2).at(0), "foo1");
218   EXPECT_EQ(val2.at(2).at(1), "bar1");
219   EXPECT_EQ(val.at(2).at(0), "foo1");
220   EXPECT_EQ(val.at(2).at(1), "bar1");
221   val2.at(2).at(0) = "foo3";
222   val2.at(2).at(1) = "bar3";
223   EXPECT_EQ(val.at(2).at(0), "foo1");
224   EXPECT_EQ(val.at(2).at(1), "bar1");
225   EXPECT_EQ(val2.at(2).at(0), "foo3");
226   EXPECT_EQ(val2.at(2).at(1), "bar3");
227
228   dynamic obj =
229     dynamic::object("a", "b")
230                    ("c", dynamic::array("d", "e", "f"));
231   EXPECT_EQ(obj.at("a"), "b");
232   dynamic obj2 = obj;
233   obj2.at("a") = dynamic::array(1, 2, 3);
234   EXPECT_EQ(obj.at("a"), "b");
235   dynamic expected = dynamic::array(1, 2, 3);
236   EXPECT_EQ(obj2.at("a"), expected);
237 }
238
239 TEST(Dynamic, ArrayReassignment) {
240   dynamic o = 1;
241   dynamic d1 = dynamic::array(o);
242   EXPECT_EQ(dynamic::ARRAY, d1.type());
243
244   d1 = dynamic::array(o);
245   EXPECT_EQ(dynamic::ARRAY, d1.type());
246 }
247
248 TEST(Dynamic, Operator) {
249   bool caught = false;
250   try {
251     dynamic d1 = dynamic::object;
252     dynamic d2 = dynamic::object;
253     auto foo = d1 < d2;
254     LOG(ERROR) << "operator < returned "
255                << static_cast<int>(foo)
256                << " instead of throwing";
257   } catch (std::exception const&) {
258     caught = true;
259   }
260   EXPECT_TRUE(caught);
261
262   dynamic foo = "asd";
263   dynamic bar = "bar";
264   dynamic sum = foo + bar;
265   EXPECT_EQ(sum, "asdbar");
266
267   dynamic some = 12;
268   dynamic nums = 4;
269   dynamic math = some / nums;
270   EXPECT_EQ(math, 3);
271 }
272
273 TEST(Dynamic, Conversions) {
274   dynamic str = "12.0";
275   EXPECT_EQ(str.asDouble(), 12.0);
276   EXPECT_ANY_THROW(str.asInt());
277   EXPECT_ANY_THROW(str.asBool());
278
279   str = "12";
280   EXPECT_EQ(str.asInt(), 12);
281   EXPECT_EQ(str.asDouble(), 12.0);
282   str = "0";
283   EXPECT_EQ(str.asBool(), false);
284   EXPECT_EQ(str.asInt(), 0);
285   EXPECT_EQ(str.asDouble(), 0);
286   EXPECT_EQ(str.asString(), "0");
287
288   dynamic num = 12;
289   EXPECT_EQ("12", num.asString());
290   EXPECT_EQ(12.0, num.asDouble());
291 }
292
293 TEST(Dynamic, GetSetDefaultTest) {
294   dynamic d1 = dynamic::object("foo", "bar");
295   EXPECT_EQ(d1.getDefault("foo", "baz"), "bar");
296   EXPECT_EQ(d1.getDefault("quux", "baz"), "baz");
297
298   dynamic d2 = dynamic::object("foo", "bar");
299   EXPECT_EQ(d2.setDefault("foo", "quux"), "bar");
300   d2.setDefault("bar", dynamic::array).push_back(42);
301   EXPECT_EQ(d2["bar"][0], 42);
302
303   dynamic d3 = dynamic::object, empty = dynamic::object;
304   EXPECT_EQ(d3.getDefault("foo"), empty);
305   d3.setDefault("foo")["bar"] = "baz";
306   EXPECT_EQ(d3["foo"]["bar"], "baz");
307
308   // we do not allow getDefault/setDefault on arrays
309   dynamic d4 = dynamic::array;
310   EXPECT_ANY_THROW(d4.getDefault("foo", "bar"));
311   EXPECT_ANY_THROW(d4.setDefault("foo", "bar"));
312 }
313
314 TEST(Dynamic, ObjectForwarding) {
315   // Make sure dynamic::object can be constructed the same way as any
316   // dynamic.
317   dynamic d = dynamic::object("asd", dynamic::array("foo", "bar"));
318   dynamic d2 = dynamic::object("key2", dynamic::array("value", "words"))
319                               ("key", "value1");
320 }
321
322 TEST(Dynamic, GetPtr) {
323   dynamic array = dynamic::array(1, 2, "three");
324   EXPECT_TRUE(array.get_ptr(0));
325   EXPECT_FALSE(array.get_ptr(-1));
326   EXPECT_FALSE(array.get_ptr(3));
327   EXPECT_EQ(dynamic("three"), *array.get_ptr(2));
328   const dynamic& carray = array;
329   EXPECT_EQ(dynamic("three"), *carray.get_ptr(2));
330
331   dynamic object = dynamic::object("one", 1)("two", 2);
332   EXPECT_TRUE(object.get_ptr("one"));
333   EXPECT_FALSE(object.get_ptr("three"));
334   EXPECT_EQ(dynamic(2), *object.get_ptr("two"));
335   *object.get_ptr("one") = 11;
336   EXPECT_EQ(dynamic(11), *object.get_ptr("one"));
337   const dynamic& cobject = object;
338   EXPECT_EQ(dynamic(2), *cobject.get_ptr("two"));
339 }
340
341 TEST(Dynamic, Assignment) {
342   const dynamic ds[] = { dynamic::array(1, 2, 3),
343                          dynamic::object("a", true),
344                          24,
345                          26.5,
346                          true,
347                          "hello", };
348   const dynamic dd[] = { dynamic::array(5, 6),
349                          dynamic::object("t", "T")(1, 7),
350                          9000,
351                          3.14159,
352                          false,
353                          "world", };
354   for (const auto& source : ds) {
355     for (const auto& dest : dd) {
356       dynamic tmp(dest);
357       EXPECT_EQ(tmp, dest);
358       tmp = source;
359       EXPECT_EQ(tmp, source);
360     }
361   }
362 }
363
364 std::string make_long_string() {
365   return std::string(100, 'a');
366 }
367
368 TEST(Dynamic, GetDefault) {
369   const auto s = make_long_string();
370   dynamic ds(s);
371   dynamic tmp(s);
372   dynamic d1 = dynamic::object("key1", s);
373   dynamic d2 = dynamic::object("key2", s);
374   dynamic d3 = dynamic::object("key3", s);
375   dynamic d4 = dynamic::object("key4", s);
376   // lvalue - lvalue
377   dynamic ayy("ayy");
378   EXPECT_EQ(ds, d1.getDefault("key1", ayy));
379   EXPECT_EQ(ds, d1.getDefault("key1", ayy));
380   EXPECT_EQ(ds, d1.getDefault("not-a-key", tmp));
381   EXPECT_EQ(ds, tmp);
382   // lvalue - rvalue
383   EXPECT_EQ(ds, d1.getDefault("key1", "ayy"));
384   EXPECT_EQ(ds, d1.getDefault("key1", "ayy"));
385   EXPECT_EQ(ds, d1.getDefault("not-a-key", std::move(tmp)));
386   EXPECT_NE(ds, tmp);
387   // rvalue - lvalue
388   tmp = s;
389   EXPECT_EQ(ds, std::move(d1).getDefault("key1", ayy));
390   EXPECT_NE(ds, d1["key1"]);
391   EXPECT_EQ(ds, std::move(d2).getDefault("not-a-key", tmp));
392   EXPECT_EQ(dynamic(dynamic::object("key2", s)), d2);
393   EXPECT_EQ(ds, tmp);
394   // rvalue - rvalue
395   EXPECT_EQ(ds, std::move(d3).getDefault("key3", std::move(tmp)));
396   EXPECT_NE(ds, d3["key3"]);
397   EXPECT_EQ(ds, tmp);
398   EXPECT_EQ(ds, std::move(d4).getDefault("not-a-key", std::move(tmp)));
399   EXPECT_EQ(dynamic(dynamic::object("key4", s)), d4);
400   EXPECT_NE(ds, tmp);
401 }
402
403 TEST(Dynamic, GetString) {
404   const dynamic c(make_long_string());
405   dynamic d(make_long_string());
406   dynamic m(make_long_string());
407
408   auto s = make_long_string();
409
410   EXPECT_EQ(s, c.getString());
411   EXPECT_EQ(s, c.getString());
412
413   d.getString() += " hello";
414   EXPECT_EQ(s + " hello", d.getString());
415   EXPECT_EQ(s + " hello", d.getString());
416
417   EXPECT_EQ(s, std::move(m).getString());
418   EXPECT_EQ(s, m.getString());
419   auto moved = std::move(m).getString();
420   EXPECT_EQ(s, moved);
421   EXPECT_NE(dynamic(s), m);
422 }
423
424 TEST(Dynamic, GetSmallThings) {
425   const dynamic cint(5);
426   const dynamic cdouble(5.0);
427   const dynamic cbool(true);
428   dynamic dint(5);
429   dynamic ddouble(5.0);
430   dynamic dbool(true);
431   dynamic mint(5);
432   dynamic mdouble(5.0);
433   dynamic mbool(true);
434
435   EXPECT_EQ(5, cint.getInt());
436   dint.getInt() = 6;
437   EXPECT_EQ(6, dint.getInt());
438   EXPECT_EQ(5, std::move(mint).getInt());
439
440   EXPECT_EQ(5.0, cdouble.getDouble());
441   ddouble.getDouble() = 6.0;
442   EXPECT_EQ(6.0, ddouble.getDouble());
443   EXPECT_EQ(5.0, std::move(mdouble).getDouble());
444
445   EXPECT_TRUE(cbool.getBool());
446   dbool.getBool() = false;
447   EXPECT_FALSE(dbool.getBool());
448   EXPECT_TRUE(std::move(mbool).getBool());
449 }
450
451 TEST(Dynamic, At) {
452   const dynamic cd = dynamic::object("key1", make_long_string());
453   dynamic dd = dynamic::object("key1", make_long_string());
454   dynamic md = dynamic::object("key1", make_long_string());
455
456   dynamic ds(make_long_string());
457   EXPECT_EQ(ds, cd.at("key1"));
458   EXPECT_EQ(ds, cd.at("key1"));
459
460   dd.at("key1").getString() += " hello";
461   EXPECT_EQ(dynamic(make_long_string() + " hello"), dd.at("key1"));
462   EXPECT_EQ(dynamic(make_long_string() + " hello"), dd.at("key1"));
463
464   EXPECT_EQ(ds, std::move(md).at("key1")); // move available, but not performed
465   EXPECT_EQ(ds, md.at("key1"));
466   dynamic moved = std::move(md).at("key1"); // move performed
467   EXPECT_EQ(ds, moved);
468   EXPECT_NE(ds, md.at("key1"));
469 }
470
471 TEST(Dynamic, Brackets) {
472   const dynamic cd = dynamic::object("key1", make_long_string());
473   dynamic dd = dynamic::object("key1", make_long_string());
474   dynamic md = dynamic::object("key1", make_long_string());
475
476   dynamic ds(make_long_string());
477   EXPECT_EQ(ds, cd["key1"]);
478   EXPECT_EQ(ds, cd["key1"]);
479
480   dd["key1"].getString() += " hello";
481   EXPECT_EQ(dynamic(make_long_string() + " hello"), dd["key1"]);
482   EXPECT_EQ(dynamic(make_long_string() + " hello"), dd["key1"]);
483
484   EXPECT_EQ(ds, std::move(md)["key1"]); // move available, but not performed
485   EXPECT_EQ(ds, md["key1"]);
486   dynamic moved = std::move(md)["key1"]; // move performed
487   EXPECT_EQ(ds, moved);
488   EXPECT_NE(ds, md["key1"]);
489 }
490
491 TEST(Dynamic, PrintNull) {
492   std::stringstream ss;
493   ss << folly::dynamic(nullptr);
494   EXPECT_EQ("null", ss.str());
495 }
496
497 TEST(Dynamic, WriteThroughArrayIterators) {
498   dynamic const cint(0);
499   dynamic d = dynamic::array(cint, cint, cint);
500   size_t size = d.size();
501
502   for (auto& val : d) {
503     EXPECT_EQ(val, cint);
504   }
505   EXPECT_EQ(d.size(), size);
506
507   dynamic ds(make_long_string());
508   for (auto& val : d) {
509     val = ds; // assign through reference
510   }
511
512   ds = "short string";
513   dynamic ds2(make_long_string());
514
515   for (auto& val : d) {
516     EXPECT_EQ(val, ds2);
517   }
518   EXPECT_EQ(d.size(), size);
519 }
520
521 TEST(Dynamic, MoveOutOfArrayIterators) {
522   dynamic ds(make_long_string());
523   dynamic d = dynamic::array(ds, ds, ds);
524   size_t size = d.size();
525
526   for (auto& val : d) {
527     EXPECT_EQ(val, ds);
528   }
529   EXPECT_EQ(d.size(), size);
530
531   for (auto& val : d) {
532     dynamic waste = std::move(val); // force moving out
533     EXPECT_EQ(waste, ds);
534   }
535
536   for (auto& val : d) {
537     EXPECT_NE(val, ds);
538   }
539   EXPECT_EQ(d.size(), size);
540 }
541
542 TEST(Dynamic, WriteThroughObjectIterators) {
543   dynamic const cint(0);
544   dynamic d = dynamic::object("key1", cint)("key2", cint);
545   size_t size = d.size();
546
547   for (auto& val : d.items()) {
548     EXPECT_EQ(val.second, cint);
549   }
550   EXPECT_EQ(d.size(), size);
551
552   dynamic ds(make_long_string());
553   for (auto& val : d.items()) {
554     val.second = ds; // assign through reference
555   }
556
557   ds = "short string";
558   dynamic ds2(make_long_string());
559   for (auto& val : d.items()) {
560     EXPECT_EQ(val.second, ds2);
561   }
562   EXPECT_EQ(d.size(), size);
563 }
564
565 TEST(Dynamic, MoveOutOfObjectIterators) {
566   dynamic ds(make_long_string());
567   dynamic d = dynamic::object("key1", ds)("key2", ds);
568   size_t size = d.size();
569
570   for (auto& val : d.items()) {
571     EXPECT_EQ(val.second, ds);
572   }
573   EXPECT_EQ(d.size(), size);
574
575   for (auto& val : d.items()) {
576     dynamic waste = std::move(val.second); // force moving out
577     EXPECT_EQ(waste, ds);
578   }
579
580   for (auto& val : d.items()) {
581     EXPECT_NE(val.second, ds);
582   }
583   EXPECT_EQ(d.size(), size);
584 }
585
586 TEST(Dynamic, ArrayIteratorInterop) {
587   dynamic d = dynamic::array(0, 1, 2);
588   dynamic const& cdref = d;
589
590   auto it = d.begin();
591   auto cit = cdref.begin();
592
593   EXPECT_EQ(it, cit);
594   EXPECT_EQ(cit, d.begin());
595   EXPECT_EQ(it, cdref.begin());
596
597   // Erase using non-const iterator
598   it = d.erase(it);
599   cit = cdref.begin();
600   EXPECT_EQ(*it, 1);
601   EXPECT_EQ(cit, it);
602
603   // Assign from non-const to const, preserve equality
604   decltype(cit) cit2 = it;
605   EXPECT_EQ(cit, cit2);
606 }
607
608 TEST(Dynamic, ObjectIteratorInterop) {
609   dynamic ds = make_long_string();
610   dynamic d = dynamic::object(0, ds)(1, ds)(2, ds);
611   dynamic const& cdref = d;
612
613   auto it = d.find(0);
614   auto cit = cdref.find(0);
615   EXPECT_NE(it, cdref.items().end());
616   EXPECT_NE(cit, cdref.items().end());
617   EXPECT_EQ(it, cit);
618
619   ++cit;
620   // Erase using non-const iterator
621   auto it2 = d.erase(it);
622   EXPECT_EQ(cit, it2);
623
624   // Assign from non-const to const, preserve equality
625   decltype(cit) cit2 = it2;
626   EXPECT_EQ(cit, cit2);
627 }