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