values_unittest.cc revision 21d179b334e59e9a3bfcaed4c4430bef1bc5759d
1// Copyright (c) 2010 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <limits>
6
7#include "base/scoped_ptr.h"
8#include "base/string16.h"
9#include "base/utf_string_conversions.h"
10#include "base/values.h"
11#include "testing/gtest/include/gtest/gtest.h"
12
13class ValuesTest : public testing::Test {
14};
15
16TEST_F(ValuesTest, Basic) {
17  // Test basic dictionary getting/setting
18  DictionaryValue settings;
19  std::string homepage = "http://google.com";
20  ASSERT_FALSE(settings.GetString("global.homepage", &homepage));
21  ASSERT_EQ(std::string("http://google.com"), homepage);
22
23  ASSERT_FALSE(settings.Get("global", NULL));
24  settings.Set("global", Value::CreateBooleanValue(true));
25  ASSERT_TRUE(settings.Get("global", NULL));
26  settings.SetString("global.homepage", "http://scurvy.com");
27  ASSERT_TRUE(settings.Get("global", NULL));
28  homepage = "http://google.com";
29  ASSERT_TRUE(settings.GetString("global.homepage", &homepage));
30  ASSERT_EQ(std::string("http://scurvy.com"), homepage);
31
32  // Test storing a dictionary in a list.
33  ListValue* toolbar_bookmarks;
34  ASSERT_FALSE(
35    settings.GetList("global.toolbar.bookmarks", &toolbar_bookmarks));
36
37  toolbar_bookmarks = new ListValue;
38  settings.Set("global.toolbar.bookmarks", toolbar_bookmarks);
39  ASSERT_TRUE(settings.GetList("global.toolbar.bookmarks", &toolbar_bookmarks));
40
41  DictionaryValue* new_bookmark = new DictionaryValue;
42  new_bookmark->SetString("name", "Froogle");
43  new_bookmark->SetString("url", "http://froogle.com");
44  toolbar_bookmarks->Append(new_bookmark);
45
46  ListValue* bookmark_list;
47  ASSERT_TRUE(settings.GetList("global.toolbar.bookmarks", &bookmark_list));
48  DictionaryValue* bookmark;
49  ASSERT_EQ(1U, bookmark_list->GetSize());
50  ASSERT_TRUE(bookmark_list->GetDictionary(0, &bookmark));
51  std::string bookmark_name = "Unnamed";
52  ASSERT_TRUE(bookmark->GetString("name", &bookmark_name));
53  ASSERT_EQ(std::string("Froogle"), bookmark_name);
54  std::string bookmark_url;
55  ASSERT_TRUE(bookmark->GetString("url", &bookmark_url));
56  ASSERT_EQ(std::string("http://froogle.com"), bookmark_url);
57}
58
59TEST_F(ValuesTest, List) {
60  scoped_ptr<ListValue> mixed_list(new ListValue());
61  mixed_list->Set(0, Value::CreateBooleanValue(true));
62  mixed_list->Set(1, Value::CreateIntegerValue(42));
63  mixed_list->Set(2, Value::CreateRealValue(88.8));
64  mixed_list->Set(3, Value::CreateStringValue("foo"));
65  ASSERT_EQ(4u, mixed_list->GetSize());
66
67  Value *value = NULL;
68  bool bool_value = false;
69  int int_value = 0;
70  double double_value = 0.0;
71  std::string string_value;
72
73  ASSERT_FALSE(mixed_list->Get(4, &value));
74
75  ASSERT_FALSE(mixed_list->GetInteger(0, &int_value));
76  ASSERT_EQ(0, int_value);
77  ASSERT_FALSE(mixed_list->GetReal(1, &double_value));
78  ASSERT_EQ(0.0, double_value);
79  ASSERT_FALSE(mixed_list->GetString(2, &string_value));
80  ASSERT_EQ("", string_value);
81  ASSERT_FALSE(mixed_list->GetBoolean(3, &bool_value));
82  ASSERT_FALSE(bool_value);
83
84  ASSERT_TRUE(mixed_list->GetBoolean(0, &bool_value));
85  ASSERT_TRUE(bool_value);
86  ASSERT_TRUE(mixed_list->GetInteger(1, &int_value));
87  ASSERT_EQ(42, int_value);
88  ASSERT_TRUE(mixed_list->GetReal(2, &double_value));
89  ASSERT_EQ(88.8, double_value);
90  ASSERT_TRUE(mixed_list->GetString(3, &string_value));
91  ASSERT_EQ("foo", string_value);
92}
93
94TEST_F(ValuesTest, BinaryValue) {
95  char* buffer = NULL;
96  // Passing a null buffer pointer doesn't yield a BinaryValue
97  scoped_ptr<BinaryValue> binary(BinaryValue::Create(buffer, 0));
98  ASSERT_FALSE(binary.get());
99
100  // If you want to represent an empty binary value, use a zero-length buffer.
101  buffer = new char[1];
102  ASSERT_TRUE(buffer);
103  binary.reset(BinaryValue::Create(buffer, 0));
104  ASSERT_TRUE(binary.get());
105  ASSERT_TRUE(binary->GetBuffer());
106  ASSERT_EQ(buffer, binary->GetBuffer());
107  ASSERT_EQ(0U, binary->GetSize());
108
109  // Test the common case of a non-empty buffer
110  buffer = new char[15];
111  binary.reset(BinaryValue::Create(buffer, 15));
112  ASSERT_TRUE(binary.get());
113  ASSERT_TRUE(binary->GetBuffer());
114  ASSERT_EQ(buffer, binary->GetBuffer());
115  ASSERT_EQ(15U, binary->GetSize());
116
117  char stack_buffer[42];
118  memset(stack_buffer, '!', 42);
119  binary.reset(BinaryValue::CreateWithCopiedBuffer(stack_buffer, 42));
120  ASSERT_TRUE(binary.get());
121  ASSERT_TRUE(binary->GetBuffer());
122  ASSERT_NE(stack_buffer, binary->GetBuffer());
123  ASSERT_EQ(42U, binary->GetSize());
124  ASSERT_EQ(0, memcmp(stack_buffer, binary->GetBuffer(), binary->GetSize()));
125}
126
127TEST_F(ValuesTest, StringValue) {
128  // Test overloaded CreateStringValue.
129  scoped_ptr<Value> narrow_value(Value::CreateStringValue("narrow"));
130  ASSERT_TRUE(narrow_value.get());
131  ASSERT_TRUE(narrow_value->IsType(Value::TYPE_STRING));
132  scoped_ptr<Value> utf16_value(
133      Value::CreateStringValue(ASCIIToUTF16("utf16")));
134  ASSERT_TRUE(utf16_value.get());
135  ASSERT_TRUE(utf16_value->IsType(Value::TYPE_STRING));
136
137  // Test overloaded GetString.
138  std::string narrow = "http://google.com";
139  string16 utf16 = ASCIIToUTF16("http://google.com");
140  ASSERT_TRUE(narrow_value->GetAsString(&narrow));
141  ASSERT_TRUE(narrow_value->GetAsString(&utf16));
142  ASSERT_EQ(std::string("narrow"), narrow);
143  ASSERT_EQ(ASCIIToUTF16("narrow"), utf16);
144
145  ASSERT_TRUE(utf16_value->GetAsString(&narrow));
146  ASSERT_TRUE(utf16_value->GetAsString(&utf16));
147  ASSERT_EQ(std::string("utf16"), narrow);
148  ASSERT_EQ(ASCIIToUTF16("utf16"), utf16);
149}
150
151// This is a Value object that allows us to tell if it's been
152// properly deleted by modifying the value of external flag on destruction.
153class DeletionTestValue : public Value {
154 public:
155  explicit DeletionTestValue(bool* deletion_flag) : Value(TYPE_NULL) {
156    Init(deletion_flag);  // Separate function so that we can use ASSERT_*
157  }
158
159  void Init(bool* deletion_flag) {
160    ASSERT_TRUE(deletion_flag);
161    deletion_flag_ = deletion_flag;
162    *deletion_flag_ = false;
163  }
164
165  ~DeletionTestValue() {
166    *deletion_flag_ = true;
167  }
168
169 private:
170  bool* deletion_flag_;
171};
172
173TEST_F(ValuesTest, ListDeletion) {
174  bool deletion_flag = true;
175
176  {
177    ListValue list;
178    list.Append(new DeletionTestValue(&deletion_flag));
179    EXPECT_FALSE(deletion_flag);
180  }
181  EXPECT_TRUE(deletion_flag);
182
183  {
184    ListValue list;
185    list.Append(new DeletionTestValue(&deletion_flag));
186    EXPECT_FALSE(deletion_flag);
187    list.Clear();
188    EXPECT_TRUE(deletion_flag);
189  }
190
191  {
192    ListValue list;
193    list.Append(new DeletionTestValue(&deletion_flag));
194    EXPECT_FALSE(deletion_flag);
195    EXPECT_TRUE(list.Set(0, Value::CreateNullValue()));
196    EXPECT_TRUE(deletion_flag);
197  }
198}
199
200TEST_F(ValuesTest, ListRemoval) {
201  bool deletion_flag = true;
202  Value* removed_item = NULL;
203
204  {
205    ListValue list;
206    list.Append(new DeletionTestValue(&deletion_flag));
207    EXPECT_FALSE(deletion_flag);
208    EXPECT_EQ(1U, list.GetSize());
209    EXPECT_FALSE(list.Remove(std::numeric_limits<size_t>::max(),
210                             &removed_item));
211    EXPECT_FALSE(list.Remove(1, &removed_item));
212    EXPECT_TRUE(list.Remove(0, &removed_item));
213    ASSERT_TRUE(removed_item);
214    EXPECT_EQ(0U, list.GetSize());
215  }
216  EXPECT_FALSE(deletion_flag);
217  delete removed_item;
218  removed_item = NULL;
219  EXPECT_TRUE(deletion_flag);
220
221  {
222    ListValue list;
223    list.Append(new DeletionTestValue(&deletion_flag));
224    EXPECT_FALSE(deletion_flag);
225    EXPECT_TRUE(list.Remove(0, NULL));
226    EXPECT_TRUE(deletion_flag);
227    EXPECT_EQ(0U, list.GetSize());
228  }
229
230  {
231    ListValue list;
232    DeletionTestValue* value = new DeletionTestValue(&deletion_flag);
233    list.Append(value);
234    EXPECT_FALSE(deletion_flag);
235    EXPECT_EQ(0, list.Remove(*value));
236    EXPECT_TRUE(deletion_flag);
237    EXPECT_EQ(0U, list.GetSize());
238  }
239}
240
241TEST_F(ValuesTest, DictionaryDeletion) {
242  std::string key = "test";
243  bool deletion_flag = true;
244
245  {
246    DictionaryValue dict;
247    dict.Set(key, new DeletionTestValue(&deletion_flag));
248    EXPECT_FALSE(deletion_flag);
249  }
250  EXPECT_TRUE(deletion_flag);
251
252  {
253    DictionaryValue dict;
254    dict.Set(key, new DeletionTestValue(&deletion_flag));
255    EXPECT_FALSE(deletion_flag);
256    dict.Clear();
257    EXPECT_TRUE(deletion_flag);
258  }
259
260  {
261    DictionaryValue dict;
262    dict.Set(key, new DeletionTestValue(&deletion_flag));
263    EXPECT_FALSE(deletion_flag);
264    dict.Set(key, Value::CreateNullValue());
265    EXPECT_TRUE(deletion_flag);
266  }
267}
268
269TEST_F(ValuesTest, DictionaryRemoval) {
270  std::string key = "test";
271  bool deletion_flag = true;
272  Value* removed_item = NULL;
273
274  {
275    DictionaryValue dict;
276    dict.Set(key, new DeletionTestValue(&deletion_flag));
277    EXPECT_FALSE(deletion_flag);
278    EXPECT_TRUE(dict.HasKey(key));
279    EXPECT_FALSE(dict.Remove("absent key", &removed_item));
280    EXPECT_TRUE(dict.Remove(key, &removed_item));
281    EXPECT_FALSE(dict.HasKey(key));
282    ASSERT_TRUE(removed_item);
283  }
284  EXPECT_FALSE(deletion_flag);
285  delete removed_item;
286  removed_item = NULL;
287  EXPECT_TRUE(deletion_flag);
288
289  {
290    DictionaryValue dict;
291    dict.Set(key, new DeletionTestValue(&deletion_flag));
292    EXPECT_FALSE(deletion_flag);
293    EXPECT_TRUE(dict.HasKey(key));
294    EXPECT_TRUE(dict.Remove(key, NULL));
295    EXPECT_TRUE(deletion_flag);
296    EXPECT_FALSE(dict.HasKey(key));
297  }
298}
299
300TEST_F(ValuesTest, DictionaryWithoutPathExpansion) {
301  DictionaryValue dict;
302  dict.Set("this.is.expanded", Value::CreateNullValue());
303  dict.SetWithoutPathExpansion("this.isnt.expanded", Value::CreateNullValue());
304
305  EXPECT_FALSE(dict.HasKey("this.is.expanded"));
306  EXPECT_TRUE(dict.HasKey("this"));
307  Value* value1;
308  EXPECT_TRUE(dict.Get("this", &value1));
309  DictionaryValue* value2;
310  ASSERT_TRUE(dict.GetDictionaryWithoutPathExpansion("this", &value2));
311  EXPECT_EQ(value1, value2);
312  EXPECT_EQ(1U, value2->size());
313
314  EXPECT_TRUE(dict.HasKey("this.isnt.expanded"));
315  Value* value3;
316  EXPECT_FALSE(dict.Get("this.isnt.expanded", &value3));
317  Value* value4;
318  ASSERT_TRUE(dict.GetWithoutPathExpansion("this.isnt.expanded", &value4));
319  EXPECT_EQ(Value::TYPE_NULL, value4->GetType());
320}
321
322TEST_F(ValuesTest, DeepCopy) {
323  DictionaryValue original_dict;
324  Value* original_null = Value::CreateNullValue();
325  original_dict.Set("null", original_null);
326  Value* original_bool = Value::CreateBooleanValue(true);
327  original_dict.Set("bool", original_bool);
328  Value* original_int = Value::CreateIntegerValue(42);
329  original_dict.Set("int", original_int);
330  Value* original_real = Value::CreateRealValue(3.14);
331  original_dict.Set("real", original_real);
332  Value* original_string = Value::CreateStringValue("hello");
333  original_dict.Set("string", original_string);
334  Value* original_string16 = Value::CreateStringValue(ASCIIToUTF16("hello16"));
335  original_dict.Set("string16", original_string16);
336
337  char* original_buffer = new char[42];
338  memset(original_buffer, '!', 42);
339  BinaryValue* original_binary = Value::CreateBinaryValue(original_buffer, 42);
340  original_dict.Set("binary", original_binary);
341
342  ListValue* original_list = new ListValue();
343  Value* original_list_element_0 = Value::CreateIntegerValue(0);
344  original_list->Append(original_list_element_0);
345  Value* original_list_element_1 = Value::CreateIntegerValue(1);
346  original_list->Append(original_list_element_1);
347  original_dict.Set("list", original_list);
348
349  scoped_ptr<DictionaryValue> copy_dict(
350      static_cast<DictionaryValue*>(original_dict.DeepCopy()));
351  ASSERT_TRUE(copy_dict.get());
352  ASSERT_NE(copy_dict.get(), &original_dict);
353
354  Value* copy_null = NULL;
355  ASSERT_TRUE(copy_dict->Get("null", &copy_null));
356  ASSERT_TRUE(copy_null);
357  ASSERT_NE(copy_null, original_null);
358  ASSERT_TRUE(copy_null->IsType(Value::TYPE_NULL));
359
360  Value* copy_bool = NULL;
361  ASSERT_TRUE(copy_dict->Get("bool", &copy_bool));
362  ASSERT_TRUE(copy_bool);
363  ASSERT_NE(copy_bool, original_bool);
364  ASSERT_TRUE(copy_bool->IsType(Value::TYPE_BOOLEAN));
365  bool copy_bool_value = false;
366  ASSERT_TRUE(copy_bool->GetAsBoolean(&copy_bool_value));
367  ASSERT_TRUE(copy_bool_value);
368
369  Value* copy_int = NULL;
370  ASSERT_TRUE(copy_dict->Get("int", &copy_int));
371  ASSERT_TRUE(copy_int);
372  ASSERT_NE(copy_int, original_int);
373  ASSERT_TRUE(copy_int->IsType(Value::TYPE_INTEGER));
374  int copy_int_value = 0;
375  ASSERT_TRUE(copy_int->GetAsInteger(&copy_int_value));
376  ASSERT_EQ(42, copy_int_value);
377
378  Value* copy_real = NULL;
379  ASSERT_TRUE(copy_dict->Get("real", &copy_real));
380  ASSERT_TRUE(copy_real);
381  ASSERT_NE(copy_real, original_real);
382  ASSERT_TRUE(copy_real->IsType(Value::TYPE_REAL));
383  double copy_real_value = 0;
384  ASSERT_TRUE(copy_real->GetAsReal(&copy_real_value));
385  ASSERT_EQ(3.14, copy_real_value);
386
387  Value* copy_string = NULL;
388  ASSERT_TRUE(copy_dict->Get("string", &copy_string));
389  ASSERT_TRUE(copy_string);
390  ASSERT_NE(copy_string, original_string);
391  ASSERT_TRUE(copy_string->IsType(Value::TYPE_STRING));
392  std::string copy_string_value;
393  string16 copy_string16_value;
394  ASSERT_TRUE(copy_string->GetAsString(&copy_string_value));
395  ASSERT_TRUE(copy_string->GetAsString(&copy_string16_value));
396  ASSERT_EQ(std::string("hello"), copy_string_value);
397  ASSERT_EQ(ASCIIToUTF16("hello"), copy_string16_value);
398
399  Value* copy_string16 = NULL;
400  ASSERT_TRUE(copy_dict->Get("string16", &copy_string16));
401  ASSERT_TRUE(copy_string16);
402  ASSERT_NE(copy_string16, original_string16);
403  ASSERT_TRUE(copy_string16->IsType(Value::TYPE_STRING));
404  ASSERT_TRUE(copy_string16->GetAsString(&copy_string_value));
405  ASSERT_TRUE(copy_string16->GetAsString(&copy_string16_value));
406  ASSERT_EQ(std::string("hello16"), copy_string_value);
407  ASSERT_EQ(ASCIIToUTF16("hello16"), copy_string16_value);
408
409  Value* copy_binary = NULL;
410  ASSERT_TRUE(copy_dict->Get("binary", &copy_binary));
411  ASSERT_TRUE(copy_binary);
412  ASSERT_NE(copy_binary, original_binary);
413  ASSERT_TRUE(copy_binary->IsType(Value::TYPE_BINARY));
414  ASSERT_NE(original_binary->GetBuffer(),
415    static_cast<BinaryValue*>(copy_binary)->GetBuffer());
416  ASSERT_EQ(original_binary->GetSize(),
417    static_cast<BinaryValue*>(copy_binary)->GetSize());
418  ASSERT_EQ(0, memcmp(original_binary->GetBuffer(),
419               static_cast<BinaryValue*>(copy_binary)->GetBuffer(),
420               original_binary->GetSize()));
421
422  Value* copy_value = NULL;
423  ASSERT_TRUE(copy_dict->Get("list", &copy_value));
424  ASSERT_TRUE(copy_value);
425  ASSERT_NE(copy_value, original_list);
426  ASSERT_TRUE(copy_value->IsType(Value::TYPE_LIST));
427  ListValue* copy_list = static_cast<ListValue*>(copy_value);
428  ASSERT_EQ(2U, copy_list->GetSize());
429
430  Value* copy_list_element_0;
431  ASSERT_TRUE(copy_list->Get(0, &copy_list_element_0));
432  ASSERT_TRUE(copy_list_element_0);
433  ASSERT_NE(copy_list_element_0, original_list_element_0);
434  int copy_list_element_0_value;
435  ASSERT_TRUE(copy_list_element_0->GetAsInteger(&copy_list_element_0_value));
436  ASSERT_EQ(0, copy_list_element_0_value);
437
438  Value* copy_list_element_1;
439  ASSERT_TRUE(copy_list->Get(1, &copy_list_element_1));
440  ASSERT_TRUE(copy_list_element_1);
441  ASSERT_NE(copy_list_element_1, original_list_element_1);
442  int copy_list_element_1_value;
443  ASSERT_TRUE(copy_list_element_1->GetAsInteger(&copy_list_element_1_value));
444  ASSERT_EQ(1, copy_list_element_1_value);
445}
446
447TEST_F(ValuesTest, Equals) {
448  Value* null1 = Value::CreateNullValue();
449  Value* null2 = Value::CreateNullValue();
450  EXPECT_NE(null1, null2);
451  EXPECT_TRUE(null1->Equals(null2));
452
453  Value* boolean = Value::CreateBooleanValue(false);
454  EXPECT_FALSE(null1->Equals(boolean));
455  delete null1;
456  delete null2;
457  delete boolean;
458
459  DictionaryValue dv;
460  dv.SetBoolean("a", false);
461  dv.SetInteger("b", 2);
462  dv.SetReal("c", 2.5);
463  dv.SetString("d1", "string");
464  dv.SetString("d2", ASCIIToUTF16("http://google.com"));
465  dv.Set("e", Value::CreateNullValue());
466
467  scoped_ptr<DictionaryValue> copy;
468  copy.reset(static_cast<DictionaryValue*>(dv.DeepCopy()));
469  EXPECT_TRUE(dv.Equals(copy.get()));
470
471  ListValue* list = new ListValue;
472  list->Append(Value::CreateNullValue());
473  list->Append(new DictionaryValue);
474  dv.Set("f", list);
475
476  EXPECT_FALSE(dv.Equals(copy.get()));
477  copy->Set("f", list->DeepCopy());
478  EXPECT_TRUE(dv.Equals(copy.get()));
479
480  list->Append(Value::CreateBooleanValue(true));
481  EXPECT_FALSE(dv.Equals(copy.get()));
482
483  // Check if Equals detects differences in only the keys.
484  copy.reset(static_cast<DictionaryValue*>(dv.DeepCopy()));
485  EXPECT_TRUE(dv.Equals(copy.get()));
486  copy->Remove("a", NULL);
487  copy->SetBoolean("aa", false);
488  EXPECT_FALSE(dv.Equals(copy.get()));
489}
490
491TEST_F(ValuesTest, StaticEquals) {
492  scoped_ptr<Value> null1(Value::CreateNullValue());
493  scoped_ptr<Value> null2(Value::CreateNullValue());
494  EXPECT_TRUE(Value::Equals(null1.get(), null2.get()));
495  EXPECT_TRUE(Value::Equals(NULL, NULL));
496
497  scoped_ptr<Value> i42(Value::CreateIntegerValue(42));
498  scoped_ptr<Value> j42(Value::CreateIntegerValue(42));
499  scoped_ptr<Value> i17(Value::CreateIntegerValue(17));
500  EXPECT_TRUE(Value::Equals(i42.get(), i42.get()));
501  EXPECT_TRUE(Value::Equals(j42.get(), i42.get()));
502  EXPECT_TRUE(Value::Equals(i42.get(), j42.get()));
503  EXPECT_FALSE(Value::Equals(i42.get(), i17.get()));
504  EXPECT_FALSE(Value::Equals(i42.get(), NULL));
505  EXPECT_FALSE(Value::Equals(NULL, i42.get()));
506
507  // NULL and Value::CreateNullValue() are intentionally different: We need
508  // support for NULL as a return value for "undefined" without caring for
509  // ownership of the pointer.
510  EXPECT_FALSE(Value::Equals(null1.get(), NULL));
511  EXPECT_FALSE(Value::Equals(NULL, null1.get()));
512}
513
514TEST_F(ValuesTest, RemoveEmptyChildren) {
515  scoped_ptr<DictionaryValue> root(new DictionaryValue);
516  // Remove empty lists and dictionaries.
517  root->Set("empty_dict", new DictionaryValue);
518  root->Set("empty_list", new ListValue);
519  root->SetWithoutPathExpansion("a.b.c.d.e", new DictionaryValue);
520  root.reset(root->DeepCopyWithoutEmptyChildren());
521  EXPECT_TRUE(root->empty());
522
523  // Make sure we don't prune too much.
524  root->SetBoolean("bool", true);
525  root->Set("empty_dict", new DictionaryValue);
526  root->SetString("empty_string", "");
527  root.reset(root->DeepCopyWithoutEmptyChildren());
528  EXPECT_EQ(2U, root->size());
529
530  // Should do nothing.
531  root.reset(root->DeepCopyWithoutEmptyChildren());
532  EXPECT_EQ(2U, root->size());
533
534  // Nested test cases.  These should all reduce back to the bool and string
535  // set above.
536  {
537    root->Set("a.b.c.d.e", new DictionaryValue);
538    root.reset(root->DeepCopyWithoutEmptyChildren());
539    EXPECT_EQ(2U, root->size());
540  }
541  {
542    DictionaryValue* inner = new DictionaryValue;
543    root->Set("dict_with_emtpy_children", inner);
544    inner->Set("empty_dict", new DictionaryValue);
545    inner->Set("empty_list", new ListValue);
546    root.reset(root->DeepCopyWithoutEmptyChildren());
547    EXPECT_EQ(2U, root->size());
548  }
549  {
550    ListValue* inner = new ListValue;
551    root->Set("list_with_empty_children", inner);
552    inner->Append(new DictionaryValue);
553    inner->Append(new ListValue);
554    root.reset(root->DeepCopyWithoutEmptyChildren());
555    EXPECT_EQ(2U, root->size());
556  }
557
558  // Nested with siblings.
559  {
560    ListValue* inner = new ListValue;
561    root->Set("list_with_empty_children", inner);
562    inner->Append(new DictionaryValue);
563    inner->Append(new ListValue);
564    DictionaryValue* inner2 = new DictionaryValue;
565    root->Set("dict_with_empty_children", inner2);
566    inner2->Set("empty_dict", new DictionaryValue);
567    inner2->Set("empty_list", new ListValue);
568    root.reset(root->DeepCopyWithoutEmptyChildren());
569    EXPECT_EQ(2U, root->size());
570  }
571
572  // Make sure nested values don't get pruned.
573  {
574    ListValue* inner = new ListValue;
575    root->Set("list_with_empty_children", inner);
576    ListValue* inner2 = new ListValue;
577    inner->Append(new DictionaryValue);
578    inner->Append(inner2);
579    inner2->Append(Value::CreateStringValue("hello"));
580    root.reset(root->DeepCopyWithoutEmptyChildren());
581    EXPECT_EQ(3U, root->size());
582    EXPECT_TRUE(root->GetList("list_with_empty_children", &inner));
583    EXPECT_EQ(1U, inner->GetSize());  // Dictionary was pruned.
584    EXPECT_TRUE(inner->GetList(0, &inner2));
585    EXPECT_EQ(1U, inner2->GetSize());
586  }
587}
588
589TEST_F(ValuesTest, MergeDictionary) {
590  scoped_ptr<DictionaryValue> base(new DictionaryValue);
591  base->SetString("base_key", "base_key_value_base");
592  base->SetString("collide_key", "collide_key_value_base");
593  DictionaryValue* base_sub_dict = new DictionaryValue;
594  base_sub_dict->SetString("sub_base_key", "sub_base_key_value_base");
595  base_sub_dict->SetString("sub_collide_key", "sub_collide_key_value_base");
596  base->Set("sub_dict_key", base_sub_dict);
597
598  scoped_ptr<DictionaryValue> merge(new DictionaryValue);
599  merge->SetString("merge_key", "merge_key_value_merge");
600  merge->SetString("collide_key", "collide_key_value_merge");
601  DictionaryValue* merge_sub_dict = new DictionaryValue;
602  merge_sub_dict->SetString("sub_merge_key", "sub_merge_key_value_merge");
603  merge_sub_dict->SetString("sub_collide_key", "sub_collide_key_value_merge");
604  merge->Set("sub_dict_key", merge_sub_dict);
605
606  base->MergeDictionary(merge.get());
607
608  EXPECT_EQ(4U, base->size());
609  std::string base_key_value;
610  EXPECT_TRUE(base->GetString("base_key", &base_key_value));
611  EXPECT_EQ("base_key_value_base", base_key_value); // Base value preserved.
612  std::string collide_key_value;
613  EXPECT_TRUE(base->GetString("collide_key", &collide_key_value));
614  EXPECT_EQ("collide_key_value_merge", collide_key_value); // Replaced.
615  std::string merge_key_value;
616  EXPECT_TRUE(base->GetString("merge_key", &merge_key_value));
617  EXPECT_EQ("merge_key_value_merge", merge_key_value); // Merged in.
618
619  DictionaryValue* res_sub_dict;
620  EXPECT_TRUE(base->GetDictionary("sub_dict_key", &res_sub_dict));
621  EXPECT_EQ(3U, res_sub_dict->size());
622  std::string sub_base_key_value;
623  EXPECT_TRUE(res_sub_dict->GetString("sub_base_key", &sub_base_key_value));
624  EXPECT_EQ("sub_base_key_value_base", sub_base_key_value); // Preserved.
625  std::string sub_collide_key_value;
626  EXPECT_TRUE(res_sub_dict->GetString("sub_collide_key",
627                                      &sub_collide_key_value));
628  EXPECT_EQ("sub_collide_key_value_merge", sub_collide_key_value); // Replaced.
629  std::string sub_merge_key_value;
630  EXPECT_TRUE(res_sub_dict->GetString("sub_merge_key", &sub_merge_key_value));
631  EXPECT_EQ("sub_merge_key_value_merge", sub_merge_key_value); // Merged in.
632}
633