[Unit tests] Add unit test target and few unit tests. 63/35663/3
authorJakub Izydorczyk <j.izydorczyk@samsung.com>
Thu, 5 Feb 2015 15:18:21 +0000 (16:18 +0100)
committerJakub Izydorczyk <j.izydorczyk@samsung.com>
Thu, 26 Feb 2015 08:41:44 +0000 (09:41 +0100)
This patch adds tests target which includes unit tests executables:
- manifest_handler_unittest (3/3 tests PASS)
- manifest_unittest (2/2 tests PASS)
- manifest_util_unittest(2/2 tests PASS)
- values_unittest (20/20 tests PASS)
- widget_manifest_parser_unittest (2/2 tests PASS)

Tests are installed in /usr/bin/app-installers-ut directory.
Resources for tests are installed in /usr/share/app-installers-ut
directory.

Change-Id: I4883a1d4d6499cad3cde5e0b635467cfa042c513

12 files changed:
CMakeLists.txt
packaging/app-installers.spec
src/CMakeLists.txt
src/unit_tests/CMakeLists.txt [new file with mode: 0644]
src/unit_tests/test_samples/bad_manifest.xml [new file with mode: 0644]
src/unit_tests/test_samples/good_manifest.xml [new file with mode: 0644]
src/unit_tests/utils_values_unittest.cc [new file with mode: 0644]
src/unit_tests/widget_manifest_parser_manifest_handler_unittest.cc [new file with mode: 0644]
src/unit_tests/widget_manifest_parser_manifest_unittest.cc [new file with mode: 0644]
src/unit_tests/widget_manifest_parser_manifest_util_unittest.cc [new file with mode: 0644]
src/unit_tests/widget_manifest_parser_unittest.cc [new file with mode: 0644]
src/widget-manifest-parser/application_data.cc

index c2050a2..969589b 100644 (file)
@@ -2,6 +2,7 @@ CMAKE_MINIMUM_REQUIRED(VERSION 2.8.12)
 PROJECT(CommonAppInstaller)
 SET(BINDIR "${CMAKE_INSTALL_PREFIX}/bin")
 SET(LIBDIR "${CMAKE_INSTALL_LIBDIR}")
+SET(SHAREDIR "${CMAKE_INSTALL_PREFIX}/share")
 SET(INCLUDEDIR "${PREFIX}/include")
 SET(VERSION_MAJOR 0)
 SET(VERSION "${VERSION_MAJOR}.1.0")
@@ -43,7 +44,6 @@ ADD_DEFINITIONS("-DXMLSEC_NO_SIZE_T")
 SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules/")
 INCLUDE(FindPkgConfig)
 INCLUDE(ApplyPkgConfig)
-include_directories(${Boost_INCLUDE_DIR})
 
 # Find all needed packages once
 PKG_CHECK_MODULES(PKGMGR_DEPS REQUIRED pkgmgr)
@@ -60,6 +60,7 @@ PKG_CHECK_MODULES(SECURITY_MANAGER_DEPS REQUIRED security-manager)
 PKG_CHECK_MODULES(VCONF_DEPS REQUIRED vconf)
 
 FIND_PACKAGE(Boost REQUIRED COMPONENTS system filesystem)
+FIND_PACKAGE(GTest REQUIRED)
 
 # xmlsec1 - choose crypto library which to use
 ADD_DEFINITIONS("-DXMLSEC_CRYPTO_OPENSSL")
index 53fc9d1..8ac5ff2 100644 (file)
@@ -10,6 +10,7 @@ Source1001:     wgt-backend.manifest
 BuildRequires:  boost-devel
 BuildRequires:  cmake
 BuildRequires:  libcap-devel
+BuildRequires:  gtest-devel
 BuildRequires:  pkgconfig(pkgmgr)
 BuildRequires:  pkgconfig(pkgmgr-parser)
 BuildRequires:  pkgconfig(pkgmgr-info)
@@ -49,6 +50,13 @@ Summary: Backend of TPK files
 %description -n tpk-backend
 Backend for tizen package files
 
+%package tests
+Summary: Unit tests for app-installers
+Requires: %{name} = %{version}
+
+%description tests
+Unit tests for al modules of app-installers
+
 %prep
 %setup -q
 
@@ -91,3 +99,6 @@ ln -s %{_bindir}/tpk-backend %{buildroot}%{_sysconfdir}/package-manager/backend/
 %{_sysconfdir}/package-manager/backend/tpk
 %{_bindir}/tpk-backend
 
+%files tests
+%{_bindir}/app-installers-ut/*
+%{_datadir}/app-installers-ut/*
index e35d46a..386a09f 100644 (file)
@@ -6,3 +6,5 @@ ADD_SUBDIRECTORY(widget-manifest-parser)
 ADD_SUBDIRECTORY(tpk)
 #ADD_SUBDIRECTORY(xpk)
 #ADD_SUBDIRECTORY(native)
+
+ADD_SUBDIRECTORY(unit_tests)
diff --git a/src/unit_tests/CMakeLists.txt b/src/unit_tests/CMakeLists.txt
new file mode 100644 (file)
index 0000000..7ea624a
--- /dev/null
@@ -0,0 +1,47 @@
+SET(PRJ_PATH ..)
+
+SET(TESTS
+  values_unittest
+  manifest_unittest
+  manifest_handler_unittest
+  manifest_util_unittest
+  widget_manifest_parser_unittest
+)
+
+# Executables
+ADD_EXECUTABLE(values_unittest
+               utils_values_unittest.cc)
+ADD_EXECUTABLE(manifest_unittest
+               widget_manifest_parser_manifest_unittest.cc)
+ADD_EXECUTABLE(manifest_handler_unittest
+               widget_manifest_parser_manifest_handler_unittest.cc)
+ADD_EXECUTABLE(manifest_util_unittest
+               widget_manifest_parser_manifest_util_unittest.cc)
+ADD_EXECUTABLE(widget_manifest_parser_unittest
+               widget_manifest_parser_unittest.cc)
+
+INSTALL(DIRECTORY test_samples/ DESTINATION ${SHAREDIR}/app-installers-ut/test_samples)
+
+FOREACH(test ${TESTS})
+APPLY_PKG_CONFIG(${test} PUBLIC
+  LIBXML_DEPS
+  PKGMGR_INFO_DEPS
+  PKGMGR_PARSER_DEPS
+  VCONF_DEPS
+  Boost
+  GTEST
+)
+ENDFOREACH(test)
+
+# FindGTest module do not sets all needed libraries in GTEST_LIBRARIES and
+# GTest main libraries is still missing, so additional linking of
+# GTEST_MAIN_LIBRARIES is needed.
+target_link_libraries(values_unittest PUBLIC ${TARGET_LIBNAME_UTILS} ${GTEST_MAIN_LIBRARIES})
+target_link_libraries(manifest_unittest PUBLIC ${TARGET_LIBNAME_WIDGET_MANIFEST_PARSER} ${GTEST_MAIN_LIBRARIES})
+target_link_libraries(manifest_handler_unittest PUBLIC ${TARGET_LIBNAME_WIDGET_MANIFEST_PARSER} ${GTEST_MAIN_LIBRARIES})
+target_link_libraries(manifest_util_unittest PUBLIC ${TARGET_LIBNAME_WIDGET_MANIFEST_PARSER} ${GTEST_MAIN_LIBRARIES})
+target_link_libraries(widget_manifest_parser_unittest PUBLIC ${TARGET_LIBNAME_WIDGET_MANIFEST_PARSER} ${GTEST_MAIN_LIBRARIES})
+
+FOREACH(test ${TESTS})
+INSTALL(TARGETS ${test} DESTINATION ${BINDIR}/app-installers-ut)
+ENDFOREACH(test)
diff --git a/src/unit_tests/test_samples/bad_manifest.xml b/src/unit_tests/test_samples/bad_manifest.xml
new file mode 100644 (file)
index 0000000..26214ca
--- /dev/null
@@ -0,0 +1,8 @@
+<widget id="http://yourdomain/WebSettingSample" version="1.0.0" viewmodes="maximized">
+  <tizen:application id="nNBDOItqjN.WebSettingSample" package="nNBDOItqjN" required_version="2.2"/>
+  <content>index.html</content>
+  <feature>http://tizen.org/feature/screen.size.all</content>
+  <icon>icon.png</icon>
+  <name name="WebSettingSample"/>
+  <tizen:privilege>http://tizen.org/privilege/websetting</tizen:privilege>
+</widget>
diff --git a/src/unit_tests/test_samples/good_manifest.xml b/src/unit_tests/test_samples/good_manifest.xml
new file mode 100644 (file)
index 0000000..be0c148
--- /dev/null
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<widget xmlns="http://www.w3.org/ns/widgets" xmlns:tizen="http://tizen.org/ns/widgets"
+  id="http://yourdomain/WebSettingSample" version="1.0.0" viewmodes="maximized">
+    <tizen:application id="nNBDOItqjN.WebSettingSample" package="nNBDOItqjN" required_version="2.2"/>
+    <content src="index.html"/>
+    <feature name="http://tizen.org/feature/screen.size.all"/>
+    <icon src="icon.png"/>
+    <name>WebSettingSample</name>
+    <tizen:privilege name="http://tizen.org/privilege/websetting"/>
+</widget>
diff --git a/src/unit_tests/utils_values_unittest.cc b/src/unit_tests/utils_values_unittest.cc
new file mode 100644 (file)
index 0000000..990d5d8
--- /dev/null
@@ -0,0 +1,1122 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE-xwalk file.
+
+#include <gtest/gtest.h>
+
+#include <limits>
+#include <memory>
+
+#include "utils/values.h"
+
+namespace common_installer {
+namespace utils {
+
+TEST(ValuesTest, Basic) {
+  // Test basic dictionary getting/setting
+  DictionaryValue settings;
+  std::string homepage = "http://google.com";
+  ASSERT_FALSE(settings.GetString("global.homepage", &homepage));
+  ASSERT_EQ(std::string("http://google.com"), homepage);
+
+  ASSERT_FALSE(settings.Get("global", nullptr));
+  settings.Set("global", new FundamentalValue(true));
+  ASSERT_TRUE(settings.Get("global", nullptr));
+  settings.SetString("global.homepage", "http://scurvy.com");
+  ASSERT_TRUE(settings.Get("global", nullptr));
+  homepage = "http://google.com";
+  ASSERT_TRUE(settings.GetString("global.homepage", &homepage));
+  ASSERT_EQ(std::string("http://scurvy.com"), homepage);
+
+  // Test storing a dictionary in a list.
+  ListValue* toolbar_bookmarks;
+  ASSERT_FALSE(
+    settings.GetList("global.toolbar.bookmarks", &toolbar_bookmarks));
+
+  toolbar_bookmarks = new ListValue;
+  settings.Set("global.toolbar.bookmarks", toolbar_bookmarks);
+  ASSERT_TRUE(settings.GetList("global.toolbar.bookmarks", &toolbar_bookmarks));
+
+  DictionaryValue* new_bookmark = new DictionaryValue;
+  new_bookmark->SetString("name", "Froogle");
+  new_bookmark->SetString("url", "http://froogle.com");
+  toolbar_bookmarks->Append(new_bookmark);
+
+  ListValue* bookmark_list;
+  ASSERT_TRUE(settings.GetList("global.toolbar.bookmarks", &bookmark_list));
+  DictionaryValue* bookmark;
+  ASSERT_EQ(1U, bookmark_list->GetSize());
+  ASSERT_TRUE(bookmark_list->GetDictionary(0, &bookmark));
+  std::string bookmark_name = "Unnamed";
+  ASSERT_TRUE(bookmark->GetString("name", &bookmark_name));
+  ASSERT_EQ(std::string("Froogle"), bookmark_name);
+  std::string bookmark_url;
+  ASSERT_TRUE(bookmark->GetString("url", &bookmark_url));
+  ASSERT_EQ(std::string("http://froogle.com"), bookmark_url);
+}
+
+TEST(ValuesTest, List) {
+  std::unique_ptr<ListValue> mixed_list(new ListValue());
+  mixed_list->Set(0, new FundamentalValue(true));
+  mixed_list->Set(1, new FundamentalValue(42));
+  mixed_list->Set(2, new FundamentalValue(88.8));
+  mixed_list->Set(3, new StringValue("foo"));
+  ASSERT_EQ(4u, mixed_list->GetSize());
+
+  Value *value = nullptr;
+  bool bool_value = false;
+  int int_value = 0;
+  double double_value = 0.0;
+  std::string string_value;
+
+  ASSERT_FALSE(mixed_list->Get(4, &value));
+
+  ASSERT_FALSE(mixed_list->GetInteger(0, &int_value));
+  ASSERT_EQ(0, int_value);
+  ASSERT_FALSE(mixed_list->GetBoolean(1, &bool_value));
+  ASSERT_FALSE(bool_value);
+  ASSERT_FALSE(mixed_list->GetString(2, &string_value));
+  ASSERT_EQ("", string_value);
+  ASSERT_FALSE(mixed_list->GetInteger(2, &int_value));
+  ASSERT_EQ(0, int_value);
+  ASSERT_FALSE(mixed_list->GetBoolean(3, &bool_value));
+  ASSERT_FALSE(bool_value);
+
+  ASSERT_TRUE(mixed_list->GetBoolean(0, &bool_value));
+  ASSERT_TRUE(bool_value);
+  ASSERT_TRUE(mixed_list->GetInteger(1, &int_value));
+  ASSERT_EQ(42, int_value);
+  // implicit conversion from Integer to Double should be possible.
+  ASSERT_TRUE(mixed_list->GetDouble(1, &double_value));
+  ASSERT_EQ(42, double_value);
+  ASSERT_TRUE(mixed_list->GetDouble(2, &double_value));
+  ASSERT_EQ(88.8, double_value);
+  ASSERT_TRUE(mixed_list->GetString(3, &string_value));
+  ASSERT_EQ("foo", string_value);
+
+  // Try searching in the mixed list.
+  utils::FundamentalValue sought_value(42);
+  utils::FundamentalValue not_found_value(false);
+
+  ASSERT_NE(mixed_list->end(), mixed_list->Find(sought_value));
+  ASSERT_TRUE((*mixed_list->Find(sought_value))->GetAsInteger(&int_value));
+  ASSERT_EQ(42, int_value);
+  ASSERT_EQ(mixed_list->end(), mixed_list->Find(not_found_value));
+}
+
+TEST(ValuesTest, BinaryValue) {
+  // Default constructor creates a BinaryValue with a nullptr buffer and size 0.
+  std::unique_ptr<BinaryValue> binary(new BinaryValue());
+  ASSERT_TRUE(binary.get());
+  ASSERT_EQ(nullptr, binary->GetBuffer());
+  ASSERT_EQ(0U, binary->GetSize());
+
+  // Test the common case of a non-empty buffer
+  char* buffer = new char[15];
+  binary.reset(new BinaryValue(std::unique_ptr<char[]>(buffer), 15));
+  ASSERT_TRUE(binary.get());
+  ASSERT_TRUE(binary->GetBuffer());
+  ASSERT_EQ(buffer, binary->GetBuffer());
+  ASSERT_EQ(15U, binary->GetSize());
+
+  char stack_buffer[42];
+  memset(stack_buffer, '!', 42);
+  binary.reset(BinaryValue::CreateWithCopiedBuffer(stack_buffer, 42));
+  ASSERT_TRUE(binary.get());
+  ASSERT_TRUE(binary->GetBuffer());
+  ASSERT_NE(stack_buffer, binary->GetBuffer());
+  ASSERT_EQ(42U, binary->GetSize());
+  ASSERT_EQ(0, memcmp(stack_buffer, binary->GetBuffer(), binary->GetSize()));
+}
+
+TEST(ValuesTest, StringValue) {
+  // Test overloaded StringValue constructor.
+  std::unique_ptr<Value> narrow_value(new StringValue("narrow"));
+  ASSERT_TRUE(narrow_value.get());
+  ASSERT_TRUE(narrow_value->IsType(Value::TYPE_STRING));
+  std::unique_ptr<Value> utf16_value(new StringValue("utf16"));
+  ASSERT_TRUE(utf16_value.get());
+  ASSERT_TRUE(utf16_value->IsType(Value::TYPE_STRING));
+
+  // Test overloaded GetAsString.
+  std::string narrow = "http://google.com";
+  std::string utf16 = "http://google.com";
+  const StringValue* string_value = nullptr;
+  ASSERT_TRUE(narrow_value->GetAsString(&narrow));
+  ASSERT_TRUE(narrow_value->GetAsString(&utf16));
+  ASSERT_TRUE(narrow_value->GetAsString(&string_value));
+  ASSERT_EQ(std::string("narrow"), narrow);
+  ASSERT_EQ(std::string("narrow"), utf16);
+  ASSERT_EQ(string_value->GetString(), narrow);
+
+  ASSERT_TRUE(utf16_value->GetAsString(&narrow));
+  ASSERT_TRUE(utf16_value->GetAsString(&utf16));
+  ASSERT_TRUE(utf16_value->GetAsString(&string_value));
+  ASSERT_EQ(std::string("utf16"), narrow);
+  ASSERT_EQ(std::string("utf16"), utf16);
+  ASSERT_EQ(string_value->GetString(), narrow);
+
+  // Don't choke on nullptr values.
+  ASSERT_TRUE(narrow_value->GetAsString(static_cast<std::string*>(nullptr)));
+  ASSERT_TRUE(narrow_value->GetAsString(static_cast<std::string*>(nullptr)));
+  ASSERT_TRUE(narrow_value->GetAsString(
+                  static_cast<const StringValue**>(nullptr)));
+}
+
+// This is a Value object that allows us to tell if it's been
+// properly deleted by modifying the value of external flag on destruction.
+class DeletionTestValue : public Value {
+ public:
+  explicit DeletionTestValue(bool* deletion_flag) : Value(TYPE_NULL) {
+    Init(deletion_flag);  // Separate function so that we can use ASSERT_*
+  }
+
+  void Init(bool* deletion_flag) {
+    ASSERT_TRUE(deletion_flag);
+    deletion_flag_ = deletion_flag;
+    *deletion_flag_ = false;
+  }
+
+  ~DeletionTestValue() override { *deletion_flag_ = true; }
+
+ private:
+  bool* deletion_flag_;
+};
+
+TEST(ValuesTest, ListDeletion) {
+  bool deletion_flag = true;
+
+  {
+    ListValue list;
+    list.Append(new DeletionTestValue(&deletion_flag));
+    EXPECT_FALSE(deletion_flag);
+  }
+  EXPECT_TRUE(deletion_flag);
+
+  {
+    ListValue list;
+    list.Append(new DeletionTestValue(&deletion_flag));
+    EXPECT_FALSE(deletion_flag);
+    list.Clear();
+    EXPECT_TRUE(deletion_flag);
+  }
+
+  {
+    ListValue list;
+    list.Append(new DeletionTestValue(&deletion_flag));
+    EXPECT_FALSE(deletion_flag);
+    EXPECT_TRUE(list.Set(0, Value::CreateNullValue()));
+    EXPECT_TRUE(deletion_flag);
+  }
+}
+
+TEST(ValuesTest, ListRemoval) {
+  bool deletion_flag = true;
+  std::unique_ptr<Value> removed_item;
+
+  {
+    ListValue list;
+    list.Append(new DeletionTestValue(&deletion_flag));
+    EXPECT_FALSE(deletion_flag);
+    EXPECT_EQ(1U, list.GetSize());
+    EXPECT_FALSE(list.Remove(std::numeric_limits<size_t>::max(),
+                             &removed_item));
+    EXPECT_FALSE(list.Remove(1, &removed_item));
+    EXPECT_TRUE(list.Remove(0, &removed_item));
+    ASSERT_TRUE(removed_item ? true : false);
+    EXPECT_EQ(0U, list.GetSize());
+  }
+  EXPECT_FALSE(deletion_flag);
+  removed_item.reset();
+  EXPECT_TRUE(deletion_flag);
+
+  {
+    ListValue list;
+    list.Append(new DeletionTestValue(&deletion_flag));
+    EXPECT_FALSE(deletion_flag);
+    EXPECT_TRUE(list.Remove(0, nullptr));
+    EXPECT_TRUE(deletion_flag);
+    EXPECT_EQ(0U, list.GetSize());
+  }
+
+  {
+    ListValue list;
+    DeletionTestValue* value = new DeletionTestValue(&deletion_flag);
+    list.Append(value);
+    EXPECT_FALSE(deletion_flag);
+    size_t index = 0;
+    list.Remove(*value, &index);
+    EXPECT_EQ(0U, index);
+    EXPECT_TRUE(deletion_flag);
+    EXPECT_EQ(0U, list.GetSize());
+  }
+}
+
+TEST(ValuesTest, DictionaryDeletion) {
+  std::string key = "test";
+  bool deletion_flag = true;
+
+  {
+    DictionaryValue dict;
+    dict.Set(key, new DeletionTestValue(&deletion_flag));
+    EXPECT_FALSE(deletion_flag);
+  }
+  EXPECT_TRUE(deletion_flag);
+
+  {
+    DictionaryValue dict;
+    dict.Set(key, new DeletionTestValue(&deletion_flag));
+    EXPECT_FALSE(deletion_flag);
+    dict.Clear();
+    EXPECT_TRUE(deletion_flag);
+  }
+
+  {
+    DictionaryValue dict;
+    dict.Set(key, new DeletionTestValue(&deletion_flag));
+    EXPECT_FALSE(deletion_flag);
+    dict.Set(key, Value::CreateNullValue());
+    EXPECT_TRUE(deletion_flag);
+  }
+}
+
+TEST(ValuesTest, DictionaryRemoval) {
+  std::string key = "test";
+  bool deletion_flag = true;
+  std::unique_ptr<Value> removed_item;
+
+  {
+    DictionaryValue dict;
+    dict.Set(key, new DeletionTestValue(&deletion_flag));
+    EXPECT_FALSE(deletion_flag);
+    EXPECT_TRUE(dict.HasKey(key));
+    EXPECT_FALSE(dict.Remove("absent key", &removed_item));
+    EXPECT_TRUE(dict.Remove(key, &removed_item));
+    EXPECT_FALSE(dict.HasKey(key));
+    ASSERT_TRUE(removed_item ? true : false);
+  }
+  EXPECT_FALSE(deletion_flag);
+  removed_item.reset();
+  EXPECT_TRUE(deletion_flag);
+
+  {
+    DictionaryValue dict;
+    dict.Set(key, new DeletionTestValue(&deletion_flag));
+    EXPECT_FALSE(deletion_flag);
+    EXPECT_TRUE(dict.HasKey(key));
+    EXPECT_TRUE(dict.Remove(key, nullptr));
+    EXPECT_TRUE(deletion_flag);
+    EXPECT_FALSE(dict.HasKey(key));
+  }
+}
+
+TEST(ValuesTest, DictionaryWithoutPathExpansion) {
+  DictionaryValue dict;
+  dict.Set("this.is.expanded", Value::CreateNullValue());
+  dict.SetWithoutPathExpansion("this.isnt.expanded", Value::CreateNullValue());
+
+  EXPECT_FALSE(dict.HasKey("this.is.expanded"));
+  EXPECT_TRUE(dict.HasKey("this"));
+  Value* value1;
+  EXPECT_TRUE(dict.Get("this", &value1));
+  DictionaryValue* value2;
+  ASSERT_TRUE(dict.GetDictionaryWithoutPathExpansion("this", &value2));
+  EXPECT_EQ(value1, value2);
+  EXPECT_EQ(1U, value2->size());
+
+  EXPECT_TRUE(dict.HasKey("this.isnt.expanded"));
+  Value* value3;
+  EXPECT_FALSE(dict.Get("this.isnt.expanded", &value3));
+  Value* value4;
+  ASSERT_TRUE(dict.GetWithoutPathExpansion("this.isnt.expanded", &value4));
+  EXPECT_EQ(Value::TYPE_NULL, value4->GetType());
+}
+
+// Tests the deprecated version of SetWithoutPathExpansion.
+// TODO(estade): remove.
+TEST(ValuesTest, DictionaryWithoutPathExpansionDeprecated) {
+  DictionaryValue dict;
+  dict.Set("this.is.expanded", Value::CreateNullValue());
+  dict.SetWithoutPathExpansion("this.isnt.expanded", Value::CreateNullValue());
+
+  EXPECT_FALSE(dict.HasKey("this.is.expanded"));
+  EXPECT_TRUE(dict.HasKey("this"));
+  Value* value1;
+  EXPECT_TRUE(dict.Get("this", &value1));
+  DictionaryValue* value2;
+  ASSERT_TRUE(dict.GetDictionaryWithoutPathExpansion("this", &value2));
+  EXPECT_EQ(value1, value2);
+  EXPECT_EQ(1U, value2->size());
+
+  EXPECT_TRUE(dict.HasKey("this.isnt.expanded"));
+  Value* value3;
+  EXPECT_FALSE(dict.Get("this.isnt.expanded", &value3));
+  Value* value4;
+  ASSERT_TRUE(dict.GetWithoutPathExpansion("this.isnt.expanded", &value4));
+  EXPECT_EQ(Value::TYPE_NULL, value4->GetType());
+}
+
+TEST(ValuesTest, DictionaryRemovePath) {
+  DictionaryValue dict;
+  dict.SetInteger("a.long.way.down", 1);
+  dict.SetBoolean("a.long.key.path", true);
+
+  std::unique_ptr<Value> removed_item;
+  EXPECT_TRUE(dict.RemovePath("a.long.way.down", &removed_item));
+  ASSERT_TRUE(removed_item ? true : false);
+  EXPECT_TRUE(removed_item->IsType(utils::Value::TYPE_INTEGER));
+  EXPECT_FALSE(dict.HasKey("a.long.way.down"));
+  EXPECT_FALSE(dict.HasKey("a.long.way"));
+  EXPECT_TRUE(dict.Get("a.long.key.path", nullptr));
+
+  removed_item.reset();
+  EXPECT_FALSE(dict.RemovePath("a.long.way.down", &removed_item));
+  EXPECT_FALSE(removed_item);
+  EXPECT_TRUE(dict.Get("a.long.key.path", nullptr));
+
+  removed_item.reset();
+  EXPECT_TRUE(dict.RemovePath("a.long.key.path", &removed_item));
+  ASSERT_TRUE(removed_item ? true : false);
+  EXPECT_TRUE(removed_item->IsType(utils::Value::TYPE_BOOLEAN));
+  EXPECT_TRUE(dict.empty());
+}
+
+TEST(ValuesTest, DeepCopy) {
+  DictionaryValue original_dict;
+  Value* original_nullptr = Value::CreateNullValue();
+  original_dict.Set("nullptr", original_nullptr);
+  FundamentalValue* original_bool = new FundamentalValue(true);
+  original_dict.Set("bool", original_bool);
+  FundamentalValue* original_int = new FundamentalValue(42);
+  original_dict.Set("int", original_int);
+  FundamentalValue* original_double = new FundamentalValue(3.14);
+  original_dict.Set("double", original_double);
+  StringValue* original_string = new StringValue("hello");
+  original_dict.Set("string", original_string);
+  StringValue* original_string16 = new StringValue("hello16");
+  original_dict.Set("string16", original_string16);
+
+  std::unique_ptr<char[]> original_buffer(new char[42]);
+  memset(original_buffer.get(), '!', 42);
+  BinaryValue* original_binary =
+      new BinaryValue(std::move(original_buffer), 42);
+  original_dict.Set("binary", original_binary);
+
+  ListValue* original_list = new ListValue();
+  FundamentalValue* original_list_element_0 = new FundamentalValue(0);
+  original_list->Append(original_list_element_0);
+  FundamentalValue* original_list_element_1 = new FundamentalValue(1);
+  original_list->Append(original_list_element_1);
+  original_dict.Set("list", original_list);
+
+  DictionaryValue* original_nested_dictionary = new DictionaryValue();
+  original_nested_dictionary->SetString("key", "value");
+  original_dict.Set("dictionary", original_nested_dictionary);
+
+  std::unique_ptr<DictionaryValue> copy_dict(original_dict.DeepCopy());
+  ASSERT_TRUE(copy_dict.get());
+  ASSERT_NE(copy_dict.get(), &original_dict);
+
+  Value* copy_nullptr = nullptr;
+  ASSERT_TRUE(copy_dict->Get("nullptr", &copy_nullptr));
+  ASSERT_TRUE(copy_nullptr);
+  ASSERT_NE(copy_nullptr, original_nullptr);
+  ASSERT_TRUE(copy_nullptr->IsType(Value::TYPE_NULL));
+
+  Value* copy_bool = nullptr;
+  ASSERT_TRUE(copy_dict->Get("bool", &copy_bool));
+  ASSERT_TRUE(copy_bool);
+  ASSERT_NE(copy_bool, original_bool);
+  ASSERT_TRUE(copy_bool->IsType(Value::TYPE_BOOLEAN));
+  bool copy_bool_value = false;
+  ASSERT_TRUE(copy_bool->GetAsBoolean(&copy_bool_value));
+  ASSERT_TRUE(copy_bool_value);
+
+  Value* copy_int = nullptr;
+  ASSERT_TRUE(copy_dict->Get("int", &copy_int));
+  ASSERT_TRUE(copy_int);
+  ASSERT_NE(copy_int, original_int);
+  ASSERT_TRUE(copy_int->IsType(Value::TYPE_INTEGER));
+  int copy_int_value = 0;
+  ASSERT_TRUE(copy_int->GetAsInteger(&copy_int_value));
+  ASSERT_EQ(42, copy_int_value);
+
+  Value* copy_double = nullptr;
+  ASSERT_TRUE(copy_dict->Get("double", &copy_double));
+  ASSERT_TRUE(copy_double);
+  ASSERT_NE(copy_double, original_double);
+  ASSERT_TRUE(copy_double->IsType(Value::TYPE_DOUBLE));
+  double copy_double_value = 0;
+  ASSERT_TRUE(copy_double->GetAsDouble(&copy_double_value));
+  ASSERT_EQ(3.14, copy_double_value);
+
+  Value* copy_string = nullptr;
+  ASSERT_TRUE(copy_dict->Get("string", &copy_string));
+  ASSERT_TRUE(copy_string);
+  ASSERT_NE(copy_string, original_string);
+  ASSERT_TRUE(copy_string->IsType(Value::TYPE_STRING));
+  std::string copy_string_value;
+  std::string copy_string16_value;
+  ASSERT_TRUE(copy_string->GetAsString(&copy_string_value));
+  ASSERT_TRUE(copy_string->GetAsString(&copy_string16_value));
+  ASSERT_EQ(std::string("hello"), copy_string_value);
+  ASSERT_EQ(std::string("hello"), copy_string16_value);
+
+  Value* copy_string16 = nullptr;
+  ASSERT_TRUE(copy_dict->Get("string16", &copy_string16));
+  ASSERT_TRUE(copy_string16);
+  ASSERT_NE(copy_string16, original_string16);
+  ASSERT_TRUE(copy_string16->IsType(Value::TYPE_STRING));
+  ASSERT_TRUE(copy_string16->GetAsString(&copy_string_value));
+  ASSERT_TRUE(copy_string16->GetAsString(&copy_string16_value));
+  ASSERT_EQ(std::string("hello16"), copy_string_value);
+  ASSERT_EQ(std::string("hello16"), copy_string16_value);
+
+  Value* copy_binary = nullptr;
+  ASSERT_TRUE(copy_dict->Get("binary", &copy_binary));
+  ASSERT_TRUE(copy_binary);
+  ASSERT_NE(copy_binary, original_binary);
+  ASSERT_TRUE(copy_binary->IsType(Value::TYPE_BINARY));
+  ASSERT_NE(original_binary->GetBuffer(),
+    static_cast<BinaryValue*>(copy_binary)->GetBuffer());
+  ASSERT_EQ(original_binary->GetSize(),
+    static_cast<BinaryValue*>(copy_binary)->GetSize());
+  ASSERT_EQ(0, memcmp(original_binary->GetBuffer(),
+               static_cast<BinaryValue*>(copy_binary)->GetBuffer(),
+               original_binary->GetSize()));
+
+  Value* copy_value = nullptr;
+  ASSERT_TRUE(copy_dict->Get("list", &copy_value));
+  ASSERT_TRUE(copy_value);
+  ASSERT_NE(copy_value, original_list);
+  ASSERT_TRUE(copy_value->IsType(Value::TYPE_LIST));
+  ListValue* copy_list = nullptr;
+  ASSERT_TRUE(copy_value->GetAsList(&copy_list));
+  ASSERT_TRUE(copy_list);
+  ASSERT_EQ(2U, copy_list->GetSize());
+
+  Value* copy_list_element_0;
+  ASSERT_TRUE(copy_list->Get(0, &copy_list_element_0));
+  ASSERT_TRUE(copy_list_element_0);
+  ASSERT_NE(copy_list_element_0, original_list_element_0);
+  int copy_list_element_0_value;
+  ASSERT_TRUE(copy_list_element_0->GetAsInteger(&copy_list_element_0_value));
+  ASSERT_EQ(0, copy_list_element_0_value);
+
+  Value* copy_list_element_1;
+  ASSERT_TRUE(copy_list->Get(1, &copy_list_element_1));
+  ASSERT_TRUE(copy_list_element_1);
+  ASSERT_NE(copy_list_element_1, original_list_element_1);
+  int copy_list_element_1_value;
+  ASSERT_TRUE(copy_list_element_1->GetAsInteger(&copy_list_element_1_value));
+  ASSERT_EQ(1, copy_list_element_1_value);
+
+  copy_value = nullptr;
+  ASSERT_TRUE(copy_dict->Get("dictionary", &copy_value));
+  ASSERT_TRUE(copy_value);
+  ASSERT_NE(copy_value, original_nested_dictionary);
+  ASSERT_TRUE(copy_value->IsType(Value::TYPE_DICTIONARY));
+  DictionaryValue* copy_nested_dictionary = nullptr;
+  ASSERT_TRUE(copy_value->GetAsDictionary(&copy_nested_dictionary));
+  ASSERT_TRUE(copy_nested_dictionary);
+  EXPECT_TRUE(copy_nested_dictionary->HasKey("key"));
+}
+
+TEST(ValuesTest, Equals) {
+  Value* nullptr1 = Value::CreateNullValue();
+  Value* nullptr2 = Value::CreateNullValue();
+  EXPECT_NE(nullptr1, nullptr2);
+  EXPECT_TRUE(nullptr1->Equals(nullptr2));
+
+  Value* boolean = new FundamentalValue(false);
+  EXPECT_FALSE(nullptr1->Equals(boolean));
+  delete nullptr1;
+  delete nullptr2;
+  delete boolean;
+
+  DictionaryValue dv;
+  dv.SetBoolean("a", false);
+  dv.SetInteger("b", 2);
+  dv.SetDouble("c", 2.5);
+  dv.SetString("d1", "string");
+  dv.SetString("d2", "http://google.com");
+  dv.Set("e", Value::CreateNullValue());
+
+  std::unique_ptr<DictionaryValue> copy;
+  copy.reset(dv.DeepCopy());
+  EXPECT_TRUE(dv.Equals(copy.get()));
+
+  ListValue* list = new ListValue;
+  list->Append(Value::CreateNullValue());
+  list->Append(new DictionaryValue);
+  dv.Set("f", list);
+
+  EXPECT_FALSE(dv.Equals(copy.get()));
+  copy->Set("f", list->DeepCopy());
+  EXPECT_TRUE(dv.Equals(copy.get()));
+
+  list->Append(new FundamentalValue(true));
+  EXPECT_FALSE(dv.Equals(copy.get()));
+
+  // Check if Equals detects differences in only the keys.
+  copy.reset(dv.DeepCopy());
+  EXPECT_TRUE(dv.Equals(copy.get()));
+  copy->Remove("a", nullptr);
+  copy->SetBoolean("aa", false);
+  EXPECT_FALSE(dv.Equals(copy.get()));
+}
+
+TEST(ValuesTest, StaticEquals) {
+  std::unique_ptr<Value> nullptr1(Value::CreateNullValue());
+  std::unique_ptr<Value> nullptr2(Value::CreateNullValue());
+  EXPECT_TRUE(Value::Equals(nullptr1.get(), nullptr2.get()));
+  EXPECT_TRUE(Value::Equals(nullptr, nullptr));
+
+  std::unique_ptr<Value> i42(new FundamentalValue(42));
+  std::unique_ptr<Value> j42(new FundamentalValue(42));
+  std::unique_ptr<Value> i17(new FundamentalValue(17));
+  EXPECT_TRUE(Value::Equals(i42.get(), i42.get()));
+  EXPECT_TRUE(Value::Equals(j42.get(), i42.get()));
+  EXPECT_TRUE(Value::Equals(i42.get(), j42.get()));
+  EXPECT_FALSE(Value::Equals(i42.get(), i17.get()));
+  EXPECT_FALSE(Value::Equals(i42.get(), nullptr));
+  EXPECT_FALSE(Value::Equals(nullptr, i42.get()));
+
+  // nullptr and Value::CreateNullValue() are intentionally different: We need
+  // support for nullptr as a return value for "undefined" without caring for
+  // ownership of the pointer.
+  EXPECT_FALSE(Value::Equals(nullptr1.get(), nullptr));
+  EXPECT_FALSE(Value::Equals(nullptr, nullptr1.get()));
+}
+
+TEST(ValuesTest, DeepCopyCovariantReturnTypes) {
+  DictionaryValue original_dict;
+  Value* original_nullptr = Value::CreateNullValue();
+  original_dict.Set("nullptr", original_nullptr);
+  FundamentalValue* original_bool = new FundamentalValue(true);
+  original_dict.Set("bool", original_bool);
+  FundamentalValue* original_int = new FundamentalValue(42);
+  original_dict.Set("int", original_int);
+  FundamentalValue* original_double = new FundamentalValue(3.14);
+  original_dict.Set("double", original_double);
+  StringValue* original_string = new StringValue("hello");
+  original_dict.Set("string", original_string);
+  StringValue* original_string16 = new StringValue("hello16");
+  original_dict.Set("string16", original_string16);
+
+  std::unique_ptr<char[]> original_buffer(new char[42]);
+  memset(original_buffer.get(), '!', 42);
+  BinaryValue* original_binary =
+      new BinaryValue(std::move(original_buffer), 42);
+  original_dict.Set("binary", original_binary);
+
+  ListValue* original_list = new ListValue();
+  FundamentalValue* original_list_element_0 = new FundamentalValue(0);
+  original_list->Append(original_list_element_0);
+  FundamentalValue* original_list_element_1 = new FundamentalValue(1);
+  original_list->Append(original_list_element_1);
+  original_dict.Set("list", original_list);
+
+  Value* original_dict_value = &original_dict;
+  Value* original_bool_value = original_bool;
+  Value* original_int_value = original_int;
+  Value* original_double_value = original_double;
+  Value* original_string_value = original_string;
+  Value* original_string16_value = original_string16;
+  Value* original_binary_value = original_binary;
+  Value* original_list_value = original_list;
+
+  std::unique_ptr<Value> copy_dict_value(original_dict_value->DeepCopy());
+  std::unique_ptr<Value> copy_bool_value(original_bool_value->DeepCopy());
+  std::unique_ptr<Value> copy_int_value(original_int_value->DeepCopy());
+  std::unique_ptr<Value> copy_double_value(original_double_value->DeepCopy());
+  std::unique_ptr<Value> copy_string_value(original_string_value->DeepCopy());
+  std::unique_ptr<Value> copy_string16_value(original_string16_value->DeepCopy());
+  std::unique_ptr<Value> copy_binary_value(original_binary_value->DeepCopy());
+  std::unique_ptr<Value> copy_list_value(original_list_value->DeepCopy());
+
+  EXPECT_TRUE(original_dict_value->Equals(copy_dict_value.get()));
+  EXPECT_TRUE(original_bool_value->Equals(copy_bool_value.get()));
+  EXPECT_TRUE(original_int_value->Equals(copy_int_value.get()));
+  EXPECT_TRUE(original_double_value->Equals(copy_double_value.get()));
+  EXPECT_TRUE(original_string_value->Equals(copy_string_value.get()));
+  EXPECT_TRUE(original_string16_value->Equals(copy_string16_value.get()));
+  EXPECT_TRUE(original_binary_value->Equals(copy_binary_value.get()));
+  EXPECT_TRUE(original_list_value->Equals(copy_list_value.get()));
+}
+
+TEST(ValuesTest, RemoveEmptyChildren) {
+  std::unique_ptr<DictionaryValue> root(new DictionaryValue);
+  // Remove empty lists and dictionaries.
+  root->Set("empty_dict", new DictionaryValue);
+  root->Set("empty_list", new ListValue);
+  root->SetWithoutPathExpansion("a.b.c.d.e", new DictionaryValue);
+  root.reset(root->DeepCopyWithoutEmptyChildren());
+  EXPECT_TRUE(root->empty());
+
+  // Make sure we don't prune too much.
+  root->SetBoolean("bool", true);
+  root->Set("empty_dict", new DictionaryValue);
+  root->SetString("empty_string", std::string());
+  root.reset(root->DeepCopyWithoutEmptyChildren());
+  EXPECT_EQ(2U, root->size());
+
+  // Should do nothing.
+  root.reset(root->DeepCopyWithoutEmptyChildren());
+  EXPECT_EQ(2U, root->size());
+
+  // Nested test cases.  These should all reduce back to the bool and string
+  // set above.
+  {
+    root->Set("a.b.c.d.e", new DictionaryValue);
+    root.reset(root->DeepCopyWithoutEmptyChildren());
+    EXPECT_EQ(2U, root->size());
+  }
+  {
+    DictionaryValue* inner = new DictionaryValue;
+    root->Set("dict_with_emtpy_children", inner);
+    inner->Set("empty_dict", new DictionaryValue);
+    inner->Set("empty_list", new ListValue);
+    root.reset(root->DeepCopyWithoutEmptyChildren());
+    EXPECT_EQ(2U, root->size());
+  }
+  {
+    ListValue* inner = new ListValue;
+    root->Set("list_with_empty_children", inner);
+    inner->Append(new DictionaryValue);
+    inner->Append(new ListValue);
+    root.reset(root->DeepCopyWithoutEmptyChildren());
+    EXPECT_EQ(2U, root->size());
+  }
+
+  // Nested with siblings.
+  {
+    ListValue* inner = new ListValue;
+    root->Set("list_with_empty_children", inner);
+    inner->Append(new DictionaryValue);
+    inner->Append(new ListValue);
+    DictionaryValue* inner2 = new DictionaryValue;
+    root->Set("dict_with_empty_children", inner2);
+    inner2->Set("empty_dict", new DictionaryValue);
+    inner2->Set("empty_list", new ListValue);
+    root.reset(root->DeepCopyWithoutEmptyChildren());
+    EXPECT_EQ(2U, root->size());
+  }
+
+  // Make sure nested values don't get pruned.
+  {
+    ListValue* inner = new ListValue;
+    root->Set("list_with_empty_children", inner);
+    ListValue* inner2 = new ListValue;
+    inner->Append(new DictionaryValue);
+    inner->Append(inner2);
+    inner2->Append(new StringValue("hello"));
+    root.reset(root->DeepCopyWithoutEmptyChildren());
+    EXPECT_EQ(3U, root->size());
+    EXPECT_TRUE(root->GetList("list_with_empty_children", &inner));
+    EXPECT_EQ(1U, inner->GetSize());  // Dictionary was pruned.
+    EXPECT_TRUE(inner->GetList(0, &inner2));
+    EXPECT_EQ(1U, inner2->GetSize());
+  }
+}
+
+TEST(ValuesTest, MergeDictionary) {
+  std::unique_ptr<DictionaryValue> base(new DictionaryValue);
+  base->SetString("base_key", "base_key_value_base");
+  base->SetString("collide_key", "collide_key_value_base");
+  DictionaryValue* base_sub_dict = new DictionaryValue;
+  base_sub_dict->SetString("sub_base_key", "sub_base_key_value_base");
+  base_sub_dict->SetString("sub_collide_key", "sub_collide_key_value_base");
+  base->Set("sub_dict_key", base_sub_dict);
+
+  std::unique_ptr<DictionaryValue> merge(new DictionaryValue);
+  merge->SetString("merge_key", "merge_key_value_merge");
+  merge->SetString("collide_key", "collide_key_value_merge");
+  DictionaryValue* merge_sub_dict = new DictionaryValue;
+  merge_sub_dict->SetString("sub_merge_key", "sub_merge_key_value_merge");
+  merge_sub_dict->SetString("sub_collide_key", "sub_collide_key_value_merge");
+  merge->Set("sub_dict_key", merge_sub_dict);
+
+  base->MergeDictionary(merge.get());
+
+  EXPECT_EQ(4U, base->size());
+  std::string base_key_value;
+  EXPECT_TRUE(base->GetString("base_key", &base_key_value));
+  EXPECT_EQ("base_key_value_base", base_key_value); // Base value preserved.
+  std::string collide_key_value;
+  EXPECT_TRUE(base->GetString("collide_key", &collide_key_value));
+  EXPECT_EQ("collide_key_value_merge", collide_key_value); // Replaced.
+  std::string merge_key_value;
+  EXPECT_TRUE(base->GetString("merge_key", &merge_key_value));
+  EXPECT_EQ("merge_key_value_merge", merge_key_value); // Merged in.
+
+  DictionaryValue* res_sub_dict;
+  EXPECT_TRUE(base->GetDictionary("sub_dict_key", &res_sub_dict));
+  EXPECT_EQ(3U, res_sub_dict->size());
+  std::string sub_base_key_value;
+  EXPECT_TRUE(res_sub_dict->GetString("sub_base_key", &sub_base_key_value));
+  EXPECT_EQ("sub_base_key_value_base", sub_base_key_value); // Preserved.
+  std::string sub_collide_key_value;
+  EXPECT_TRUE(res_sub_dict->GetString("sub_collide_key",
+                                      &sub_collide_key_value));
+  EXPECT_EQ("sub_collide_key_value_merge", sub_collide_key_value); // Replaced.
+  std::string sub_merge_key_value;
+  EXPECT_TRUE(res_sub_dict->GetString("sub_merge_key", &sub_merge_key_value));
+  EXPECT_EQ("sub_merge_key_value_merge", sub_merge_key_value); // Merged in.
+}
+
+TEST(ValuesTest, MergeDictionaryDeepCopy) {
+  DictionaryValue* child = new DictionaryValue;
+  child->SetString("test", "value");
+  EXPECT_EQ(1U, child->size());
+
+  std::string value;
+  EXPECT_TRUE(child->GetString("test", &value));
+  EXPECT_EQ("value", value);
+
+  std::unique_ptr<DictionaryValue> base(new DictionaryValue);
+  base->Set("dict", child);
+  EXPECT_EQ(1U, base->size());
+
+  DictionaryValue* ptr;
+  EXPECT_TRUE(base->GetDictionary("dict", &ptr));
+  EXPECT_EQ(child, ptr);
+
+  std::unique_ptr<DictionaryValue> merged(new DictionaryValue);
+  merged->MergeDictionary(base.get());
+  EXPECT_EQ(1U, merged->size());
+  EXPECT_TRUE(merged->GetDictionary("dict", &ptr));
+  EXPECT_NE(child, ptr);
+  EXPECT_TRUE(ptr->GetString("test", &value));
+  EXPECT_EQ("value", value);
+
+  child->SetString("test", "overwrite");
+  base.reset();
+  EXPECT_TRUE(ptr->GetString("test", &value));
+  EXPECT_EQ("value", value);
+}
+
+TEST(ValuesTest, DictionaryIterator) {
+  DictionaryValue dict;
+  for (DictionaryValue::Iterator it(dict); !it.IsAtEnd(); it.Advance()) {
+    ADD_FAILURE();
+  }
+
+  StringValue value1("value1");
+  dict.Set("key1", value1.DeepCopy());
+  bool seen1 = false;
+  for (DictionaryValue::Iterator it(dict); !it.IsAtEnd(); it.Advance()) {
+    EXPECT_FALSE(seen1);
+    EXPECT_EQ("key1", it.key());
+    EXPECT_TRUE(value1.Equals(&it.value()));
+    seen1 = true;
+  }
+  EXPECT_TRUE(seen1);
+
+  StringValue value2("value2");
+  dict.Set("key2", value2.DeepCopy());
+  bool seen2 = seen1 = false;
+  for (DictionaryValue::Iterator it(dict); !it.IsAtEnd(); it.Advance()) {
+    if (it.key() == "key1") {
+      EXPECT_FALSE(seen1);
+      EXPECT_TRUE(value1.Equals(&it.value()));
+      seen1 = true;
+    } else if (it.key() == "key2") {
+      EXPECT_FALSE(seen2);
+      EXPECT_TRUE(value2.Equals(&it.value()));
+      seen2 = true;
+    } else {
+      ADD_FAILURE();
+    }
+  }
+  EXPECT_TRUE(seen1);
+  EXPECT_TRUE(seen2);
+}
+
+// DictionaryValue/ListValue's Get*() methods should accept nullptr as an out-value
+// and still return true/false based on success.
+TEST(ValuesTest, GetWithnullptrOutValue) {
+  DictionaryValue main_dict;
+  ListValue main_list;
+
+  FundamentalValue bool_value(false);
+  FundamentalValue int_value(1234);
+  FundamentalValue double_value(12.34567);
+  StringValue string_value("foo");
+  BinaryValue binary_value;
+  DictionaryValue dict_value;
+  ListValue list_value;
+
+  main_dict.Set("bool", bool_value.DeepCopy());
+  main_dict.Set("int", int_value.DeepCopy());
+  main_dict.Set("double", double_value.DeepCopy());
+  main_dict.Set("string", string_value.DeepCopy());
+  main_dict.Set("binary", binary_value.DeepCopy());
+  main_dict.Set("dict", dict_value.DeepCopy());
+  main_dict.Set("list", list_value.DeepCopy());
+
+  main_list.Append(bool_value.DeepCopy());
+  main_list.Append(int_value.DeepCopy());
+  main_list.Append(double_value.DeepCopy());
+  main_list.Append(string_value.DeepCopy());
+  main_list.Append(binary_value.DeepCopy());
+  main_list.Append(dict_value.DeepCopy());
+  main_list.Append(list_value.DeepCopy());
+
+  EXPECT_TRUE(main_dict.Get("bool", nullptr));
+  EXPECT_TRUE(main_dict.Get("int", nullptr));
+  EXPECT_TRUE(main_dict.Get("double", nullptr));
+  EXPECT_TRUE(main_dict.Get("string", nullptr));
+  EXPECT_TRUE(main_dict.Get("binary", nullptr));
+  EXPECT_TRUE(main_dict.Get("dict", nullptr));
+  EXPECT_TRUE(main_dict.Get("list", nullptr));
+  EXPECT_FALSE(main_dict.Get("DNE", nullptr));
+
+  EXPECT_TRUE(main_dict.GetBoolean("bool", nullptr));
+  EXPECT_FALSE(main_dict.GetBoolean("int", nullptr));
+  EXPECT_FALSE(main_dict.GetBoolean("double", nullptr));
+  EXPECT_FALSE(main_dict.GetBoolean("string", nullptr));
+  EXPECT_FALSE(main_dict.GetBoolean("binary", nullptr));
+  EXPECT_FALSE(main_dict.GetBoolean("dict", nullptr));
+  EXPECT_FALSE(main_dict.GetBoolean("list", nullptr));
+  EXPECT_FALSE(main_dict.GetBoolean("DNE", nullptr));
+
+  EXPECT_FALSE(main_dict.GetInteger("bool", nullptr));
+  EXPECT_TRUE(main_dict.GetInteger("int", nullptr));
+  EXPECT_FALSE(main_dict.GetInteger("double", nullptr));
+  EXPECT_FALSE(main_dict.GetInteger("string", nullptr));
+  EXPECT_FALSE(main_dict.GetInteger("binary", nullptr));
+  EXPECT_FALSE(main_dict.GetInteger("dict", nullptr));
+  EXPECT_FALSE(main_dict.GetInteger("list", nullptr));
+  EXPECT_FALSE(main_dict.GetInteger("DNE", nullptr));
+
+  // Both int and double values can be obtained from GetDouble.
+  EXPECT_FALSE(main_dict.GetDouble("bool", nullptr));
+  EXPECT_TRUE(main_dict.GetDouble("int", nullptr));
+  EXPECT_TRUE(main_dict.GetDouble("double", nullptr));
+  EXPECT_FALSE(main_dict.GetDouble("string", nullptr));
+  EXPECT_FALSE(main_dict.GetDouble("binary", nullptr));
+  EXPECT_FALSE(main_dict.GetDouble("dict", nullptr));
+  EXPECT_FALSE(main_dict.GetDouble("list", nullptr));
+  EXPECT_FALSE(main_dict.GetDouble("DNE", nullptr));
+
+  EXPECT_FALSE(main_dict.GetString("bool", static_cast<std::string*>(nullptr)));
+  EXPECT_FALSE(main_dict.GetString("int", static_cast<std::string*>(nullptr)));
+  EXPECT_FALSE(main_dict.GetString("double", static_cast<std::string*>(nullptr)));
+  EXPECT_TRUE(main_dict.GetString("string", static_cast<std::string*>(nullptr)));
+  EXPECT_FALSE(main_dict.GetString("binary", static_cast<std::string*>(nullptr)));
+  EXPECT_FALSE(main_dict.GetString("dict", static_cast<std::string*>(nullptr)));
+  EXPECT_FALSE(main_dict.GetString("list", static_cast<std::string*>(nullptr)));
+  EXPECT_FALSE(main_dict.GetString("DNE", static_cast<std::string*>(nullptr)));
+
+  EXPECT_FALSE(main_dict.GetString("bool", static_cast<std::string*>(nullptr)));
+  EXPECT_FALSE(main_dict.GetString("int", static_cast<std::string*>(nullptr)));
+  EXPECT_FALSE(main_dict.GetString("double", static_cast<std::string*>(nullptr)));
+  EXPECT_TRUE(main_dict.GetString("string", static_cast<std::string*>(nullptr)));
+  EXPECT_FALSE(main_dict.GetString("binary", static_cast<std::string*>(nullptr)));
+  EXPECT_FALSE(main_dict.GetString("dict", static_cast<std::string*>(nullptr)));
+  EXPECT_FALSE(main_dict.GetString("list", static_cast<std::string*>(nullptr)));
+  EXPECT_FALSE(main_dict.GetString("DNE", static_cast<std::string*>(nullptr)));
+
+  EXPECT_FALSE(main_dict.GetBinary("bool", nullptr));
+  EXPECT_FALSE(main_dict.GetBinary("int", nullptr));
+  EXPECT_FALSE(main_dict.GetBinary("double", nullptr));
+  EXPECT_FALSE(main_dict.GetBinary("string", nullptr));
+  EXPECT_TRUE(main_dict.GetBinary("binary", nullptr));
+  EXPECT_FALSE(main_dict.GetBinary("dict", nullptr));
+  EXPECT_FALSE(main_dict.GetBinary("list", nullptr));
+  EXPECT_FALSE(main_dict.GetBinary("DNE", nullptr));
+
+  EXPECT_FALSE(main_dict.GetDictionary("bool", nullptr));
+  EXPECT_FALSE(main_dict.GetDictionary("int", nullptr));
+  EXPECT_FALSE(main_dict.GetDictionary("double", nullptr));
+  EXPECT_FALSE(main_dict.GetDictionary("string", nullptr));
+  EXPECT_FALSE(main_dict.GetDictionary("binary", nullptr));
+  EXPECT_TRUE(main_dict.GetDictionary("dict", nullptr));
+  EXPECT_FALSE(main_dict.GetDictionary("list", nullptr));
+  EXPECT_FALSE(main_dict.GetDictionary("DNE", nullptr));
+
+  EXPECT_FALSE(main_dict.GetList("bool", nullptr));
+  EXPECT_FALSE(main_dict.GetList("int", nullptr));
+  EXPECT_FALSE(main_dict.GetList("double", nullptr));
+  EXPECT_FALSE(main_dict.GetList("string", nullptr));
+  EXPECT_FALSE(main_dict.GetList("binary", nullptr));
+  EXPECT_FALSE(main_dict.GetList("dict", nullptr));
+  EXPECT_TRUE(main_dict.GetList("list", nullptr));
+  EXPECT_FALSE(main_dict.GetList("DNE", nullptr));
+
+  EXPECT_TRUE(main_dict.GetWithoutPathExpansion("bool", nullptr));
+  EXPECT_TRUE(main_dict.GetWithoutPathExpansion("int", nullptr));
+  EXPECT_TRUE(main_dict.GetWithoutPathExpansion("double", nullptr));
+  EXPECT_TRUE(main_dict.GetWithoutPathExpansion("string", nullptr));
+  EXPECT_TRUE(main_dict.GetWithoutPathExpansion("binary", nullptr));
+  EXPECT_TRUE(main_dict.GetWithoutPathExpansion("dict", nullptr));
+  EXPECT_TRUE(main_dict.GetWithoutPathExpansion("list", nullptr));
+  EXPECT_FALSE(main_dict.GetWithoutPathExpansion("DNE", nullptr));
+
+  EXPECT_TRUE(main_dict.GetBooleanWithoutPathExpansion("bool", nullptr));
+  EXPECT_FALSE(main_dict.GetBooleanWithoutPathExpansion("int", nullptr));
+  EXPECT_FALSE(main_dict.GetBooleanWithoutPathExpansion("double", nullptr));
+  EXPECT_FALSE(main_dict.GetBooleanWithoutPathExpansion("string", nullptr));
+  EXPECT_FALSE(main_dict.GetBooleanWithoutPathExpansion("binary", nullptr));
+  EXPECT_FALSE(main_dict.GetBooleanWithoutPathExpansion("dict", nullptr));
+  EXPECT_FALSE(main_dict.GetBooleanWithoutPathExpansion("list", nullptr));
+  EXPECT_FALSE(main_dict.GetBooleanWithoutPathExpansion("DNE", nullptr));
+
+  EXPECT_FALSE(main_dict.GetIntegerWithoutPathExpansion("bool", nullptr));
+  EXPECT_TRUE(main_dict.GetIntegerWithoutPathExpansion("int", nullptr));
+  EXPECT_FALSE(main_dict.GetIntegerWithoutPathExpansion("double", nullptr));
+  EXPECT_FALSE(main_dict.GetIntegerWithoutPathExpansion("string", nullptr));
+  EXPECT_FALSE(main_dict.GetIntegerWithoutPathExpansion("binary", nullptr));
+  EXPECT_FALSE(main_dict.GetIntegerWithoutPathExpansion("dict", nullptr));
+  EXPECT_FALSE(main_dict.GetIntegerWithoutPathExpansion("list", nullptr));
+  EXPECT_FALSE(main_dict.GetIntegerWithoutPathExpansion("DNE", nullptr));
+
+  EXPECT_FALSE(main_dict.GetDoubleWithoutPathExpansion("bool", nullptr));
+  EXPECT_TRUE(main_dict.GetDoubleWithoutPathExpansion("int", nullptr));
+  EXPECT_TRUE(main_dict.GetDoubleWithoutPathExpansion("double", nullptr));
+  EXPECT_FALSE(main_dict.GetDoubleWithoutPathExpansion("string", nullptr));
+  EXPECT_FALSE(main_dict.GetDoubleWithoutPathExpansion("binary", nullptr));
+  EXPECT_FALSE(main_dict.GetDoubleWithoutPathExpansion("dict", nullptr));
+  EXPECT_FALSE(main_dict.GetDoubleWithoutPathExpansion("list", nullptr));
+  EXPECT_FALSE(main_dict.GetDoubleWithoutPathExpansion("DNE", nullptr));
+
+  EXPECT_FALSE(main_dict.GetStringWithoutPathExpansion(
+      "bool", static_cast<std::string*>(nullptr)));
+  EXPECT_FALSE(main_dict.GetStringWithoutPathExpansion(
+      "int", static_cast<std::string*>(nullptr)));
+  EXPECT_FALSE(main_dict.GetStringWithoutPathExpansion(
+      "double", static_cast<std::string*>(nullptr)));
+  EXPECT_TRUE(main_dict.GetStringWithoutPathExpansion(
+      "string", static_cast<std::string*>(nullptr)));
+  EXPECT_FALSE(main_dict.GetStringWithoutPathExpansion(
+      "binary", static_cast<std::string*>(nullptr)));
+  EXPECT_FALSE(main_dict.GetStringWithoutPathExpansion(
+      "dict", static_cast<std::string*>(nullptr)));
+  EXPECT_FALSE(main_dict.GetStringWithoutPathExpansion(
+      "list", static_cast<std::string*>(nullptr)));
+  EXPECT_FALSE(main_dict.GetStringWithoutPathExpansion(
+      "DNE", static_cast<std::string*>(nullptr)));
+
+  EXPECT_FALSE(main_dict.GetStringWithoutPathExpansion(
+      "bool", static_cast<std::string*>(nullptr)));
+  EXPECT_FALSE(main_dict.GetStringWithoutPathExpansion(
+      "int", static_cast<std::string*>(nullptr)));
+  EXPECT_FALSE(main_dict.GetStringWithoutPathExpansion(
+      "double", static_cast<std::string*>(nullptr)));
+  EXPECT_TRUE(main_dict.GetStringWithoutPathExpansion(
+      "string", static_cast<std::string*>(nullptr)));
+  EXPECT_FALSE(main_dict.GetStringWithoutPathExpansion(
+      "binary", static_cast<std::string*>(nullptr)));
+  EXPECT_FALSE(main_dict.GetStringWithoutPathExpansion(
+      "dict", static_cast<std::string*>(nullptr)));
+  EXPECT_FALSE(main_dict.GetStringWithoutPathExpansion(
+      "list", static_cast<std::string*>(nullptr)));
+  EXPECT_FALSE(main_dict.GetStringWithoutPathExpansion(
+      "DNE", static_cast<std::string*>(nullptr)));
+
+  // There is no GetBinaryWithoutPathExpansion for some reason, but if there
+  // were it should be tested here...
+
+  EXPECT_FALSE(main_dict.GetDictionaryWithoutPathExpansion("bool", nullptr));
+  EXPECT_FALSE(main_dict.GetDictionaryWithoutPathExpansion("int", nullptr));
+  EXPECT_FALSE(main_dict.GetDictionaryWithoutPathExpansion("double", nullptr));
+  EXPECT_FALSE(main_dict.GetDictionaryWithoutPathExpansion("string", nullptr));
+  EXPECT_FALSE(main_dict.GetDictionaryWithoutPathExpansion("binary", nullptr));
+  EXPECT_TRUE(main_dict.GetDictionaryWithoutPathExpansion("dict", nullptr));
+  EXPECT_FALSE(main_dict.GetDictionaryWithoutPathExpansion("list", nullptr));
+  EXPECT_FALSE(main_dict.GetDictionaryWithoutPathExpansion("DNE", nullptr));
+
+  EXPECT_FALSE(main_dict.GetListWithoutPathExpansion("bool", nullptr));
+  EXPECT_FALSE(main_dict.GetListWithoutPathExpansion("int", nullptr));
+  EXPECT_FALSE(main_dict.GetListWithoutPathExpansion("double", nullptr));
+  EXPECT_FALSE(main_dict.GetListWithoutPathExpansion("string", nullptr));
+  EXPECT_FALSE(main_dict.GetListWithoutPathExpansion("binary", nullptr));
+  EXPECT_FALSE(main_dict.GetListWithoutPathExpansion("dict", nullptr));
+  EXPECT_TRUE(main_dict.GetListWithoutPathExpansion("list", nullptr));
+  EXPECT_FALSE(main_dict.GetListWithoutPathExpansion("DNE", nullptr));
+
+  EXPECT_TRUE(main_list.Get(0, nullptr));
+  EXPECT_TRUE(main_list.Get(1, nullptr));
+  EXPECT_TRUE(main_list.Get(2, nullptr));
+  EXPECT_TRUE(main_list.Get(3, nullptr));
+  EXPECT_TRUE(main_list.Get(4, nullptr));
+  EXPECT_TRUE(main_list.Get(5, nullptr));
+  EXPECT_TRUE(main_list.Get(6, nullptr));
+  EXPECT_FALSE(main_list.Get(7, nullptr));
+
+  EXPECT_TRUE(main_list.GetBoolean(0, nullptr));
+  EXPECT_FALSE(main_list.GetBoolean(1, nullptr));
+  EXPECT_FALSE(main_list.GetBoolean(2, nullptr));
+  EXPECT_FALSE(main_list.GetBoolean(3, nullptr));
+  EXPECT_FALSE(main_list.GetBoolean(4, nullptr));
+  EXPECT_FALSE(main_list.GetBoolean(5, nullptr));
+  EXPECT_FALSE(main_list.GetBoolean(6, nullptr));
+  EXPECT_FALSE(main_list.GetBoolean(7, nullptr));
+
+  EXPECT_FALSE(main_list.GetInteger(0, nullptr));
+  EXPECT_TRUE(main_list.GetInteger(1, nullptr));
+  EXPECT_FALSE(main_list.GetInteger(2, nullptr));
+  EXPECT_FALSE(main_list.GetInteger(3, nullptr));
+  EXPECT_FALSE(main_list.GetInteger(4, nullptr));
+  EXPECT_FALSE(main_list.GetInteger(5, nullptr));
+  EXPECT_FALSE(main_list.GetInteger(6, nullptr));
+  EXPECT_FALSE(main_list.GetInteger(7, nullptr));
+
+  EXPECT_FALSE(main_list.GetDouble(0, nullptr));
+  EXPECT_TRUE(main_list.GetDouble(1, nullptr));
+  EXPECT_TRUE(main_list.GetDouble(2, nullptr));
+  EXPECT_FALSE(main_list.GetDouble(3, nullptr));
+  EXPECT_FALSE(main_list.GetDouble(4, nullptr));
+  EXPECT_FALSE(main_list.GetDouble(5, nullptr));
+  EXPECT_FALSE(main_list.GetDouble(6, nullptr));
+  EXPECT_FALSE(main_list.GetDouble(7, nullptr));
+
+  EXPECT_FALSE(main_list.GetString(0, static_cast<std::string*>(nullptr)));
+  EXPECT_FALSE(main_list.GetString(1, static_cast<std::string*>(nullptr)));
+  EXPECT_FALSE(main_list.GetString(2, static_cast<std::string*>(nullptr)));
+  EXPECT_TRUE(main_list.GetString(3, static_cast<std::string*>(nullptr)));
+  EXPECT_FALSE(main_list.GetString(4, static_cast<std::string*>(nullptr)));
+  EXPECT_FALSE(main_list.GetString(5, static_cast<std::string*>(nullptr)));
+  EXPECT_FALSE(main_list.GetString(6, static_cast<std::string*>(nullptr)));
+  EXPECT_FALSE(main_list.GetString(7, static_cast<std::string*>(nullptr)));
+
+  EXPECT_FALSE(main_list.GetString(0, static_cast<std::string*>(nullptr)));
+  EXPECT_FALSE(main_list.GetString(1, static_cast<std::string*>(nullptr)));
+  EXPECT_FALSE(main_list.GetString(2, static_cast<std::string*>(nullptr)));
+  EXPECT_TRUE(main_list.GetString(3, static_cast<std::string*>(nullptr)));
+  EXPECT_FALSE(main_list.GetString(4, static_cast<std::string*>(nullptr)));
+  EXPECT_FALSE(main_list.GetString(5, static_cast<std::string*>(nullptr)));
+  EXPECT_FALSE(main_list.GetString(6, static_cast<std::string*>(nullptr)));
+  EXPECT_FALSE(main_list.GetString(7, static_cast<std::string*>(nullptr)));
+
+  EXPECT_FALSE(main_list.GetBinary(0, nullptr));
+  EXPECT_FALSE(main_list.GetBinary(1, nullptr));
+  EXPECT_FALSE(main_list.GetBinary(2, nullptr));
+  EXPECT_FALSE(main_list.GetBinary(3, nullptr));
+  EXPECT_TRUE(main_list.GetBinary(4, nullptr));
+  EXPECT_FALSE(main_list.GetBinary(5, nullptr));
+  EXPECT_FALSE(main_list.GetBinary(6, nullptr));
+  EXPECT_FALSE(main_list.GetBinary(7, nullptr));
+
+  EXPECT_FALSE(main_list.GetDictionary(0, nullptr));
+  EXPECT_FALSE(main_list.GetDictionary(1, nullptr));
+  EXPECT_FALSE(main_list.GetDictionary(2, nullptr));
+  EXPECT_FALSE(main_list.GetDictionary(3, nullptr));
+  EXPECT_FALSE(main_list.GetDictionary(4, nullptr));
+  EXPECT_TRUE(main_list.GetDictionary(5, nullptr));
+  EXPECT_FALSE(main_list.GetDictionary(6, nullptr));
+  EXPECT_FALSE(main_list.GetDictionary(7, nullptr));
+
+  EXPECT_FALSE(main_list.GetList(0, nullptr));
+  EXPECT_FALSE(main_list.GetList(1, nullptr));
+  EXPECT_FALSE(main_list.GetList(2, nullptr));
+  EXPECT_FALSE(main_list.GetList(3, nullptr));
+  EXPECT_FALSE(main_list.GetList(4, nullptr));
+  EXPECT_FALSE(main_list.GetList(5, nullptr));
+  EXPECT_TRUE(main_list.GetList(6, nullptr));
+  EXPECT_FALSE(main_list.GetList(7, nullptr));
+}
+
+}  // namespace utils
+}  // namespace common_installer
diff --git a/src/unit_tests/widget_manifest_parser_manifest_handler_unittest.cc b/src/unit_tests/widget_manifest_parser_manifest_handler_unittest.cc
new file mode 100644 (file)
index 0000000..129159e
--- /dev/null
@@ -0,0 +1,324 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE-xwalk file.
+
+#include <boost/filesystem/path.hpp>
+#include <gtest/gtest.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "utils/values.h"
+#include "widget-manifest-parser/application_data.h"
+#include "widget-manifest-parser/manifest_handler.h"
+
+namespace bf = boost::filesystem;
+
+namespace common_installer {
+namespace widget_manifest_parser {
+
+namespace {
+
+std::vector<std::string> SingleKey(const std::string& key) {
+  return std::vector<std::string>(1, key);
+}
+
+}  // namespace
+
+class ScopedTestingManifestHandlerRegistry {
+ public:
+  ScopedTestingManifestHandlerRegistry(
+      const std::vector<ManifestHandler*>& handlers)
+      : registry_(
+          new ManifestHandlerRegistry(handlers)),
+        prev_registry_(
+          ManifestHandlerRegistry::GetInstance(Manifest::TYPE_MANIFEST)) {
+    ManifestHandlerRegistry::SetInstanceForTesting(
+        registry_, Manifest::TYPE_MANIFEST);
+  }
+
+  ~ScopedTestingManifestHandlerRegistry() {
+    ManifestHandlerRegistry::SetInstanceForTesting(
+        prev_registry_, Manifest::TYPE_MANIFEST);
+  }
+
+  ManifestHandlerRegistry* registry_;
+  ManifestHandlerRegistry* prev_registry_;
+};
+
+class ManifestHandlerTest : public testing::Test {
+ public:
+  class ParsingWatcher {
+   public:
+    // Called when a manifest handler parses.
+    void Record(const std::string& name) {
+      parsed_names_.push_back(name);
+    }
+
+    const std::vector<std::string>& parsed_names() {
+      return parsed_names_;
+    }
+
+    // Returns true if |name_before| was parsed before |name_after|.
+    bool ParsedBefore(const std::string& name_before,
+                      const std::string& name_after) {
+      size_t prev_iterator = parsed_names_.size();
+      size_t next_iterator = 0;
+      for (size_t i = 0; i < parsed_names_.size(); ++i) {
+        if (parsed_names_[i] == name_before)
+          prev_iterator = i;
+        if (parsed_names_[i] == name_after)
+          next_iterator = i;
+      }
+
+      if (prev_iterator < next_iterator)
+        return true;
+
+      return false;
+    }
+
+   private:
+    // The order of manifest handlers that we watched parsing.
+    std::vector<std::string> parsed_names_;
+  };
+
+  class TestManifestHandler : public ManifestHandler {
+   public:
+    TestManifestHandler(const std::string& name,
+                        const std::vector<std::string>& keys,
+                        const std::vector<std::string>& prereqs,
+                        ParsingWatcher* watcher)
+        : name_(name), keys_(keys), prereqs_(prereqs), watcher_(watcher) {
+    }
+
+    ~TestManifestHandler() override {}
+
+    bool Parse(
+        std::shared_ptr<ApplicationData> application,
+        std::string* error) override {
+      watcher_->Record(name_);
+      return true;
+    }
+
+    std::vector<std::string> PrerequisiteKeys() const override {
+      return prereqs_;
+    }
+
+    std::vector<std::string> Keys() const override {
+      return keys_;
+    }
+
+   protected:
+    std::string name_;
+    std::vector<std::string> keys_;
+    std::vector<std::string> prereqs_;
+    ParsingWatcher* watcher_;
+  };
+
+  class FailingTestManifestHandler : public TestManifestHandler {
+   public:
+    FailingTestManifestHandler(const std::string& name,
+                               const std::vector<std::string>& keys,
+                               const std::vector<std::string>& prereqs,
+                               ParsingWatcher* watcher)
+        : TestManifestHandler(name, keys, prereqs, watcher) {
+    }
+    bool Parse(
+        std::shared_ptr<ApplicationData> application,
+        std::string* error) override {
+      *error = name_;
+      return false;
+    }
+  };
+
+  class AlwaysParseTestManifestHandler : public TestManifestHandler {
+   public:
+    AlwaysParseTestManifestHandler(const std::string& name,
+                                   const std::vector<std::string>& keys,
+                                   const std::vector<std::string>& prereqs,
+                                   ParsingWatcher* watcher)
+        : TestManifestHandler(name, keys, prereqs, watcher) {
+    }
+
+    bool AlwaysParseForType(Manifest::Type type) const override {
+      return true;
+    }
+  };
+
+  class TestManifestValidator : public ManifestHandler {
+   public:
+    TestManifestValidator(bool return_value,
+                          bool always_validate,
+                          std::vector<std::string> keys)
+        : return_value_(return_value),
+          always_validate_(always_validate),
+          keys_(keys) {
+    }
+
+    bool Parse(
+        std::shared_ptr<ApplicationData> application,
+        std::string* error) override {
+      return true;
+    }
+
+    bool Validate(
+        std::shared_ptr<const ApplicationData> application,
+        std::string* error) const override {
+      return return_value_;
+    }
+
+    bool AlwaysValidateForType(Manifest::Type type) const override {
+      return always_validate_;
+    }
+
+    std::vector<std::string> Keys() const override {
+      return keys_;
+    }
+
+   protected:
+    bool return_value_;
+    bool always_validate_;
+    std::vector<std::string> keys_;
+  };
+};
+
+TEST_F(ManifestHandlerTest, DependentHandlers) {
+  std::vector<ManifestHandler*> handlers;
+  ParsingWatcher watcher;
+  std::vector<std::string> prereqs;
+  handlers.push_back(
+      new TestManifestHandler("A", SingleKey("a"), prereqs, &watcher));
+  handlers.push_back(
+      new TestManifestHandler("B", SingleKey("b"), prereqs, &watcher));
+  handlers.push_back(
+      new TestManifestHandler("J", SingleKey("j"), prereqs, &watcher));
+  handlers.push_back(
+      new AlwaysParseTestManifestHandler(
+          "K", SingleKey("k"), prereqs, &watcher));
+  prereqs.push_back("c.d");
+  std::vector<std::string> keys;
+  keys.push_back("c.e");
+  keys.push_back("c.z");
+  handlers.push_back(
+      new TestManifestHandler("C.EZ", keys, prereqs, &watcher));
+  prereqs.clear();
+  prereqs.push_back("b");
+  prereqs.push_back("k");
+  handlers.push_back(
+      new TestManifestHandler("C.D", SingleKey("c.d"), prereqs, &watcher));
+  ScopedTestingManifestHandlerRegistry registry(handlers);
+
+  utils::DictionaryValue manifest;
+  manifest.SetString("name", "no name");
+  manifest.SetString("version", "0");
+  manifest.SetInteger("manifest_version", 2);
+  manifest.SetInteger("a", 1);
+  manifest.SetInteger("b", 2);
+  manifest.SetInteger("c.d", 3);
+  manifest.SetInteger("c.e", 4);
+  manifest.SetInteger("c.f", 5);
+  manifest.SetInteger("g", 6);
+  std::string error;
+  std::shared_ptr<ApplicationData> application = ApplicationData::Create(
+      bf::path(), std::string(),
+      ApplicationData::LOCAL_DIRECTORY,
+      std::unique_ptr<Manifest>(new Manifest(
+          std::unique_ptr<utils::DictionaryValue>(manifest.DeepCopy()))),
+      &error);
+  fprintf(stderr, "error: %s\n", error.c_str());
+  EXPECT_TRUE(application.get());
+  // A, B, C.EZ, C.D, K
+  EXPECT_EQ(5u, watcher.parsed_names().size());
+  EXPECT_TRUE(watcher.ParsedBefore("B", "C.D"));
+  EXPECT_TRUE(watcher.ParsedBefore("K", "C.D"));
+  EXPECT_TRUE(watcher.ParsedBefore("C.D", "C.EZ"));
+}
+
+TEST_F(ManifestHandlerTest, FailingHandlers) {
+  std::unique_ptr<ScopedTestingManifestHandlerRegistry> registry(
+      new ScopedTestingManifestHandlerRegistry(
+          std::vector<ManifestHandler*>()));
+  // Can't use ApplicationBuilder, because this application will fail to
+  // be parsed.
+  utils::DictionaryValue manifest_a;
+  manifest_a.SetString("name", "no name");
+  manifest_a.SetString("version", "0");
+  manifest_a.SetInteger("manifest_version", 2);
+  manifest_a.SetInteger("a", 1);
+
+  // Succeeds when "a" is not recognized.
+  std::string error;
+  std::shared_ptr<ApplicationData> application = ApplicationData::Create(
+      bf::path(), std::string(),
+      ApplicationData::LOCAL_DIRECTORY,
+      std::unique_ptr<Manifest>(new Manifest(
+          std::unique_ptr<utils::DictionaryValue>(manifest_a.DeepCopy()))),
+      &error);
+  EXPECT_TRUE(application.get());
+
+  // Register a handler for "a" that fails.
+  std::vector<ManifestHandler*> handlers;
+  ParsingWatcher watcher;
+  handlers.push_back(
+      new FailingTestManifestHandler(
+          "A", SingleKey("a"), std::vector<std::string>(), &watcher));
+  registry.reset();
+  registry.reset(new ScopedTestingManifestHandlerRegistry(handlers));
+
+  application = ApplicationData::Create(
+      bf::path(), std::string(),
+      ApplicationData::LOCAL_DIRECTORY,
+      std::unique_ptr<Manifest>(new Manifest(
+          std::unique_ptr<utils::DictionaryValue>(manifest_a.DeepCopy()))),
+      &error);
+  EXPECT_FALSE(application.get());
+  EXPECT_EQ("A", error);
+}
+
+TEST_F(ManifestHandlerTest, Validate) {
+  std::unique_ptr<ScopedTestingManifestHandlerRegistry> registry(
+      new ScopedTestingManifestHandlerRegistry(
+          std::vector<ManifestHandler*>()));
+  utils::DictionaryValue manifest;
+  manifest.SetString("name", "no name");
+  manifest.SetString("version", "0");
+  manifest.SetInteger("manifest_version", 2);
+  manifest.SetInteger("a", 1);
+  manifest.SetInteger("b", 2);
+  std::string error;
+  std::shared_ptr<ApplicationData> application = ApplicationData::Create(
+      bf::path(), std::string(),
+      ApplicationData::LOCAL_DIRECTORY,
+      std::unique_ptr<Manifest>(new Manifest(
+          std::unique_ptr<utils::DictionaryValue>(manifest.DeepCopy()))),
+      &error);
+  EXPECT_TRUE(application.get());
+
+  std::vector<ManifestHandler*> handlers;
+  // Always validates and fails.
+  handlers.push_back(
+      new TestManifestValidator(false, true, SingleKey("c")));
+  registry.reset();
+  registry.reset(new ScopedTestingManifestHandlerRegistry(handlers));
+  EXPECT_FALSE(
+      registry->registry_->ValidateAppManifest(application, &error));
+
+  handlers.push_back(
+      new TestManifestValidator(false, false, SingleKey("c")));
+  registry.reset();
+  registry.reset(new ScopedTestingManifestHandlerRegistry(handlers));
+  EXPECT_TRUE(
+      registry->registry_->ValidateAppManifest(application, &error));
+
+  // Validates "a" and fails.
+  handlers.push_back
+      (new TestManifestValidator(false, true, SingleKey("a")));
+  registry.reset();
+  registry.reset(new ScopedTestingManifestHandlerRegistry(handlers));
+  EXPECT_FALSE(
+      registry->registry_->ValidateAppManifest(application, &error));
+}
+
+}  // namespace widget_manifest_parser
+}  // namespace common_installer
diff --git a/src/unit_tests/widget_manifest_parser_manifest_unittest.cc b/src/unit_tests/widget_manifest_parser_manifest_unittest.cc
new file mode 100644 (file)
index 0000000..b4517d5
--- /dev/null
@@ -0,0 +1,90 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE-xwalk file.
+
+#include "widget-manifest-parser/manifest.h"
+
+#include <gtest/gtest.h>
+
+#include <algorithm>
+#include <memory>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "utils/values.h"
+#include "widget-manifest-parser/application_manifest_constants.h"
+
+namespace errors = common_installer::application_manifest_errors;
+namespace keys = common_installer::application_widget_keys;
+
+namespace common_installer {
+namespace widget_manifest_parser {
+
+class ManifestTest : public testing::Test {
+ public:
+  ManifestTest() : default_value_("test") {}
+
+ protected:
+  // Helper function that replaces the Manifest held by |manifest| with a copy
+  // with its |key| changed to |value|. If |value| is nullptr, then |key| will
+  // instead be deleted.
+  void MutateManifest(std::unique_ptr<Manifest>* manifest,
+                      const std::string& key,
+                      utils::Value* value) {
+    std::unique_ptr<utils::DictionaryValue> manifest_value(
+        manifest->get()->value()->DeepCopy());
+    if (value)
+      manifest_value->Set(key, value);
+    else
+      manifest_value->Remove(key, nullptr);
+    manifest->reset(new Manifest(std::move(manifest_value)));
+  }
+
+  std::string default_value_;
+};
+
+// Verifies that application can access the correct keys.
+TEST_F(ManifestTest, ApplicationData) {
+  std::unique_ptr<utils::DictionaryValue> manifest_value(new utils::DictionaryValue());
+  manifest_value->SetString(keys::kNameKey, "extension");
+  manifest_value->SetString(keys::kVersionKey, "1");
+  manifest_value->SetString("unknown_key", "foo");
+
+  std::unique_ptr<Manifest> manifest(
+      new Manifest(std::move(manifest_value)));
+  std::string error;
+  EXPECT_TRUE(manifest->ValidateManifest(&error));
+  EXPECT_TRUE(error.empty());
+
+  // The unknown key 'unknown_key' should be accesible.
+  std::string value;
+  EXPECT_TRUE(manifest->GetString("unknown_key", &value));
+  EXPECT_EQ("foo", value);
+
+  // Test DeepCopy and Equals.
+  std::unique_ptr<Manifest> manifest2(manifest->DeepCopy());
+  EXPECT_TRUE(manifest->Equals(manifest2.get()));
+  EXPECT_TRUE(manifest2->Equals(manifest.get()));
+  MutateManifest(
+      &manifest, "foo", new utils::StringValue("blah"));
+  EXPECT_FALSE(manifest->Equals(manifest2.get()));
+}
+
+// Verifies that key restriction based on type works.
+TEST_F(ManifestTest, ApplicationTypes) {
+  std::unique_ptr<utils::DictionaryValue> value(new utils::DictionaryValue());
+  value->SetString(keys::kNameKey, "extension");
+  value->SetString(keys::kVersionKey, "1");
+
+  std::unique_ptr<Manifest> manifest(
+      new Manifest(std::move(value)));
+  std::string error;
+  EXPECT_TRUE(manifest->ValidateManifest(&error));
+  EXPECT_TRUE(error.empty());
+}
+
+}  // namespace widget_manifest_parser
+}  // namespace common_installer
+
+
diff --git a/src/unit_tests/widget_manifest_parser_manifest_util_unittest.cc b/src/unit_tests/widget_manifest_parser_manifest_util_unittest.cc
new file mode 100644 (file)
index 0000000..ae9362e
--- /dev/null
@@ -0,0 +1,66 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE-xwalk file.
+
+#include "widget-manifest-parser/manifest_util.h"
+
+#include <boost/filesystem/fstream.hpp>
+#include <boost/filesystem/path.hpp>
+#include <boost/filesystem/operations.hpp>
+#include <gtest/gtest.h>
+
+#include "widget-manifest-parser/application_data.h"
+#include "widget-manifest-parser/application_manifest_constants.h"
+#include "widget-manifest-parser/manifest.h"
+
+using common_installer::widget_manifest_parser::ApplicationData;
+using common_installer::widget_manifest_parser::Manifest;
+
+namespace keys = common_installer::application_manifest_keys;
+namespace bf = boost::filesystem;
+
+namespace common_installer {
+namespace widget_mnanifest_parser {
+
+class ManifestUtilTest : public testing::Test {
+};
+
+TEST_F(ManifestUtilTest, LoadApplicationWithValidPath) {
+  boost::filesystem::path install_dir("/usr/share/app-installers-ut");
+  ASSERT_TRUE(&install_dir);
+  install_dir /= "test_samples";
+  install_dir /= "good_manifest.xml";
+
+  std::string error;
+  std::unique_ptr<Manifest> manifest(LoadManifest(install_dir.string(),
+                                                  Manifest::Type::TYPE_WIDGET,
+                                                  &error));
+  ASSERT_TRUE(error.empty());
+  std::shared_ptr<common_installer::widget_manifest_parser::ApplicationData>
+      app_data =
+      common_installer::widget_manifest_parser::ApplicationData::Create(
+          bf::path(), std::string(),
+          common_installer::widget_manifest_parser::ApplicationData::INTERNAL,
+          std::move(manifest), &error);
+  ASSERT_TRUE(error.empty());
+  EXPECT_EQ("nNBDOItqjN.WebSettingSample", app_data->ID());
+}
+
+TEST_F(ManifestUtilTest,
+       LoadApplicationGivesHelpfullErrorOnMissingManifest) {
+  boost::filesystem::path install_dir("/usr/share/app-installers-ut");
+  ASSERT_TRUE(&install_dir);
+  install_dir /= "test_samples";
+  install_dir /= "bad_manifest.xml";
+  std::string error;
+  std::unique_ptr<Manifest> manifest(LoadManifest(install_dir.string(),
+                                                  Manifest::Type::TYPE_WIDGET,
+                                                  &error));
+  ASSERT_TRUE(!error.empty());
+  ASSERT_FALSE(error.empty());
+  ASSERT_STREQ("Manifest file is missing or unreadable.", error.c_str());
+}
+
+}  // namespace widget_manifest_parser
+}  // namespace common_installer
diff --git a/src/unit_tests/widget_manifest_parser_unittest.cc b/src/unit_tests/widget_manifest_parser_unittest.cc
new file mode 100644 (file)
index 0000000..e6e3a40
--- /dev/null
@@ -0,0 +1,56 @@
+// Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+// Use of this source code is governed by an apache 2.0 license that can be
+// found in the LICENSE file.
+
+#include "widget-manifest-parser/widget_manifest_parser.h"
+
+#include <boost/filesystem/path.hpp>
+#include <gtest/gtest.h>
+#include <pkgmgr/pkgmgr_parser.h>
+
+namespace bf = boost::filesystem;
+
+namespace common_installer {
+namespace widget_manifest_parser {
+
+class ParseManifestTest : public testing::Test {
+ protected:
+  manifest_x* manifest_;
+  std::unique_ptr<WidgetManifestParser> parser_;
+
+  virtual void SetUp() {
+    parser_.reset(new WidgetManifestParser);
+    manifest_ = static_cast<manifest_x*>(calloc(1, sizeof(manifest_x)));
+  }
+
+  virtual void TearDown() {
+    pkgmgr_parser_free_manifest_xml(manifest_);
+  }
+};
+
+// Tests manifest parser with proper manifest
+TEST_F(ParseManifestTest, HandlesProperManifestFile) {
+  bf::path path = "/usr/share/app-installers-ut/test_samples/good_manifest.xml";
+  ASSERT_TRUE(parser_->ParseManifest(path));
+  ASSERT_TRUE(parser_->FillManifestX(manifest_));
+  EXPECT_STREQ("nNBDOItqjN.WebSettingSample", manifest_->uiapplication->appid);
+  EXPECT_STREQ("nNBDOItqjN", manifest_->package);
+  EXPECT_STREQ("WebSettingSample", manifest_->description->name);
+  EXPECT_STREQ("en-us", parser_->GetShortName().c_str());
+  EXPECT_STREQ("1.0.0", manifest_->version);
+  EXPECT_STREQ("icon.png", manifest_->icon->name);
+  EXPECT_STREQ("2.2", parser_->GetRequiredAPIVersion().c_str());
+  EXPECT_STREQ("http://tizen.org/privilege/websetting",
+               manifest_->privileges->privilege->text);
+}
+
+// Tests manifest parser with broken manifest
+TEST_F(ParseManifestTest, HandlesBrokenManifestFile) {
+  bf::path path = "/usr/share/app-installers-ut/test_samples/bad_manifest.xml";
+  ASSERT_FALSE(parser_->ParseManifest(path));
+  EXPECT_STREQ("Manifest file is missing or unreadable.",
+               parser_->GetErrorMessage().c_str());
+}
+
+}  // namespace widget_manifest_parser
+}  // namespace common_installer
index 2887cf4..2aa75b1 100644 (file)
@@ -52,22 +52,24 @@ std::shared_ptr<ApplicationData> ApplicationData::Create(
     SourceType source_type, std::unique_ptr<Manifest> manifest,
     std::string* error_message) {
   assert(error_message);
-  std::string error;
   if (!manifest->ValidateManifest(error_message))
     return nullptr;
 
   std::shared_ptr<ApplicationData> app_data(
       new ApplicationData(path, source_type, std::move(manifest)));
-  if (!app_data->Init(explicit_id, &error)) {
-    *error_message = error;
+  if (!app_data->Init(explicit_id, error_message))
     return nullptr;
-  }
 
   ManifestHandlerRegistry* registry =
       ManifestHandlerRegistry::GetInstance(app_data->manifest_type());
 
   if (!registry->ValidateAppManifest(app_data, error_message))
     return nullptr;
+
+  app_data->LoadID(explicit_id, error_message);
+  app_data->LoadName(error_message);
+  app_data->LoadVersion(error_message);
+
   return app_data;
 }
 
@@ -135,13 +137,6 @@ bool ApplicationData::Init(const std::string& explicit_id,
   if (!registry->ParseAppManifest(shared_from_this(), error))
     return false;
 
-  if (!LoadID(explicit_id, error))
-    return false;
-  if (!LoadName(error))
-    return false;
-  if (!LoadVersion(error))
-    return false;
-
   application_url_ = ApplicationData::GetBaseURLFromApplicationId(ID());
   finished_parsing_manifest_ = true;
   return true;
@@ -151,12 +146,13 @@ bool ApplicationData::LoadID(const std::string& explicit_id,
                              std::string* error) {
   std::string application_id;
   auto iter = manifest_data_.find(widget_keys::kTizenApplicationKey);
+  const TizenApplicationInfo* tizen_app_info;
   if (iter == manifest_data_.end())
-    return false;
-  const TizenApplicationInfo* tizen_app_info =
-      static_cast<TizenApplicationInfo*>(iter->second.get());
-  assert(tizen_app_info);
-  application_id = tizen_app_info->id();
+    tizen_app_info = nullptr;
+  else {
+    tizen_app_info = static_cast<TizenApplicationInfo*>(iter->second.get());
+    application_id = tizen_app_info->id();
+  }
   if (!application_id.empty()) {
     application_id_ = application_id;
     return true;