OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "components/bookmarks/browser/bookmark_model.h" | 5 #include "components/bookmarks/browser/bookmark_model.h" |
6 | 6 |
7 #include <set> | 7 #include <set> |
8 #include <string> | 8 #include <string> |
9 | 9 |
10 #include "base/base_paths.h" | 10 #include "base/base_paths.h" |
(...skipping 85 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
96 BookmarkNode* AsMutable(const BookmarkNode* node) { | 96 BookmarkNode* AsMutable(const BookmarkNode* node) { |
97 return const_cast<BookmarkNode*>(node); | 97 return const_cast<BookmarkNode*>(node); |
98 } | 98 } |
99 | 99 |
100 void SwapDateAdded(BookmarkNode* n1, BookmarkNode* n2) { | 100 void SwapDateAdded(BookmarkNode* n1, BookmarkNode* n2) { |
101 Time tmp = n1->date_added(); | 101 Time tmp = n1->date_added(); |
102 n1->set_date_added(n2->date_added()); | 102 n1->set_date_added(n2->date_added()); |
103 n2->set_date_added(tmp); | 103 n2->set_date_added(tmp); |
104 } | 104 } |
105 | 105 |
| 106 // See comment in PopulateNodeFromString. |
| 107 using TestNode = ui::TreeNodeWithValue<BookmarkNode::Type>; |
| 108 |
| 109 // Does the work of PopulateNodeFromString. index gives the index of the current |
| 110 // element in description to process. |
| 111 void PopulateNodeImpl(const std::vector<std::string>& description, |
| 112 size_t* index, |
| 113 TestNode* parent) { |
| 114 while (*index < description.size()) { |
| 115 const std::string& element = description[*index]; |
| 116 (*index)++; |
| 117 if (element == "[") { |
| 118 // Create a new folder and recurse to add all the children. |
| 119 // Folders are given a unique named by way of an ever increasing integer |
| 120 // value. The folders need not have a name, but one is assigned to help |
| 121 // in debugging. |
| 122 static int next_folder_id = 1; |
| 123 TestNode* new_node = new TestNode(base::IntToString16(next_folder_id++), |
| 124 BookmarkNode::FOLDER); |
| 125 parent->Add(new_node, parent->child_count()); |
| 126 PopulateNodeImpl(description, index, new_node); |
| 127 } else if (element == "]") { |
| 128 // End the current folder. |
| 129 return; |
| 130 } else { |
| 131 // Add a new URL. |
| 132 |
| 133 // All tokens must be space separated. If there is a [ or ] in the name it |
| 134 // likely means a space was forgotten. |
| 135 DCHECK(element.find('[') == std::string::npos); |
| 136 DCHECK(element.find(']') == std::string::npos); |
| 137 parent->Add(new TestNode(base::UTF8ToUTF16(element), BookmarkNode::URL), |
| 138 parent->child_count()); |
| 139 } |
| 140 } |
| 141 } |
| 142 |
| 143 // Creates and adds nodes to parent based on description. description consists |
| 144 // of the following tokens (all space separated): |
| 145 // [ : creates a new USER_FOLDER node. All elements following the [ until the |
| 146 // next balanced ] is encountered are added as children to the node. |
| 147 // ] : closes the last folder created by [ so that any further nodes are added |
| 148 // to the current folders parent. |
| 149 // text: creates a new URL node. |
| 150 // For example, "a [b] c" creates the following nodes: |
| 151 // a 1 c |
| 152 // | |
| 153 // b |
| 154 // In words: a node of type URL with the title a, followed by a folder node with |
| 155 // the title 1 having the single child of type url with name b, followed by |
| 156 // the url node with the title c. |
| 157 // |
| 158 // NOTE: each name must be unique, and folders are assigned a unique title by |
| 159 // way of an increasing integer. |
| 160 void PopulateNodeFromString(const std::string& description, TestNode* parent) { |
| 161 std::vector<std::string> elements; |
| 162 base::SplitStringAlongWhitespace(description, &elements); |
| 163 size_t index = 0; |
| 164 PopulateNodeImpl(elements, &index, parent); |
| 165 } |
| 166 |
| 167 // Populates the BookmarkNode with the children of parent. |
| 168 void PopulateBookmarkNode(TestNode* parent, |
| 169 BookmarkModel* model, |
| 170 const BookmarkNode* bb_node) { |
| 171 for (int i = 0; i < parent->child_count(); ++i) { |
| 172 TestNode* child = parent->GetChild(i); |
| 173 if (child->value == BookmarkNode::FOLDER) { |
| 174 const BookmarkNode* new_bb_node = |
| 175 model->AddFolder(bb_node, i, child->GetTitle()); |
| 176 PopulateBookmarkNode(child, model, new_bb_node); |
| 177 } else { |
| 178 model->AddURL(bb_node, i, child->GetTitle(), |
| 179 GURL("http://" + base::UTF16ToASCII(child->GetTitle()))); |
| 180 } |
| 181 } |
| 182 } |
| 183 |
| 184 // Verifies the contents of the bookmark bar node match the contents of the |
| 185 // TestNode. |
| 186 void VerifyModelMatchesNode(TestNode* expected, const BookmarkNode* actual) { |
| 187 ASSERT_EQ(expected->child_count(), actual->child_count()); |
| 188 for (int i = 0; i < expected->child_count(); ++i) { |
| 189 TestNode* expected_child = expected->GetChild(i); |
| 190 const BookmarkNode* actual_child = actual->GetChild(i); |
| 191 ASSERT_EQ(expected_child->GetTitle(), actual_child->GetTitle()); |
| 192 if (expected_child->value == BookmarkNode::FOLDER) { |
| 193 ASSERT_TRUE(actual_child->type() == BookmarkNode::FOLDER); |
| 194 // Recurse throught children. |
| 195 VerifyModelMatchesNode(expected_child, actual_child); |
| 196 } else { |
| 197 // No need to check the URL, just the title is enough. |
| 198 ASSERT_TRUE(actual_child->is_url()); |
| 199 } |
| 200 } |
| 201 } |
| 202 |
| 203 void VerifyNoDuplicateIDs(BookmarkModel* model) { |
| 204 ui::TreeNodeIterator<const BookmarkNode> it(model->root_node()); |
| 205 base::hash_set<int64> ids; |
| 206 while (it.has_next()) |
| 207 ASSERT_TRUE(ids.insert(it.Next()->id()).second); |
| 208 } |
| 209 |
106 class BookmarkModelTest : public testing::Test, | 210 class BookmarkModelTest : public testing::Test, |
107 public BookmarkModelObserver { | 211 public BookmarkModelObserver { |
108 public: | 212 public: |
109 struct ObserverDetails { | 213 struct ObserverDetails { |
110 ObserverDetails() { | 214 ObserverDetails() { |
111 Set(NULL, NULL, -1, -1); | 215 Set(NULL, NULL, -1, -1); |
112 } | 216 } |
113 | 217 |
114 void Set(const BookmarkNode* node1, | 218 void Set(const BookmarkNode* node1, |
115 const BookmarkNode* node2, | 219 const BookmarkNode* node2, |
(...skipping 703 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
819 EXPECT_EQ(1U, bookmarks.size()); | 923 EXPECT_EQ(1U, bookmarks.size()); |
820 } | 924 } |
821 | 925 |
822 TEST_F(BookmarkModelTest, HasBookmarks) { | 926 TEST_F(BookmarkModelTest, HasBookmarks) { |
823 const GURL url("http://foo.com/"); | 927 const GURL url("http://foo.com/"); |
824 model_->AddURL(model_->bookmark_bar_node(), 0, ASCIIToUTF16("bar"), url); | 928 model_->AddURL(model_->bookmark_bar_node(), 0, ASCIIToUTF16("bar"), url); |
825 | 929 |
826 EXPECT_TRUE(model_->HasBookmarks()); | 930 EXPECT_TRUE(model_->HasBookmarks()); |
827 } | 931 } |
828 | 932 |
829 // See comment in PopulateNodeFromString. | |
830 typedef ui::TreeNodeWithValue<BookmarkNode::Type> TestNode; | |
831 | |
832 // Does the work of PopulateNodeFromString. index gives the index of the current | |
833 // element in description to process. | |
834 void PopulateNodeImpl(const std::vector<std::string>& description, | |
835 size_t* index, | |
836 TestNode* parent) { | |
837 while (*index < description.size()) { | |
838 const std::string& element = description[*index]; | |
839 (*index)++; | |
840 if (element == "[") { | |
841 // Create a new folder and recurse to add all the children. | |
842 // Folders are given a unique named by way of an ever increasing integer | |
843 // value. The folders need not have a name, but one is assigned to help | |
844 // in debugging. | |
845 static int next_folder_id = 1; | |
846 TestNode* new_node = | |
847 new TestNode(base::IntToString16(next_folder_id++), | |
848 BookmarkNode::FOLDER); | |
849 parent->Add(new_node, parent->child_count()); | |
850 PopulateNodeImpl(description, index, new_node); | |
851 } else if (element == "]") { | |
852 // End the current folder. | |
853 return; | |
854 } else { | |
855 // Add a new URL. | |
856 | |
857 // All tokens must be space separated. If there is a [ or ] in the name it | |
858 // likely means a space was forgotten. | |
859 DCHECK(element.find('[') == std::string::npos); | |
860 DCHECK(element.find(']') == std::string::npos); | |
861 parent->Add(new TestNode(base::UTF8ToUTF16(element), BookmarkNode::URL), | |
862 parent->child_count()); | |
863 } | |
864 } | |
865 } | |
866 | |
867 // Creates and adds nodes to parent based on description. description consists | |
868 // of the following tokens (all space separated): | |
869 // [ : creates a new USER_FOLDER node. All elements following the [ until the | |
870 // next balanced ] is encountered are added as children to the node. | |
871 // ] : closes the last folder created by [ so that any further nodes are added | |
872 // to the current folders parent. | |
873 // text: creates a new URL node. | |
874 // For example, "a [b] c" creates the following nodes: | |
875 // a 1 c | |
876 // | | |
877 // b | |
878 // In words: a node of type URL with the title a, followed by a folder node with | |
879 // the title 1 having the single child of type url with name b, followed by | |
880 // the url node with the title c. | |
881 // | |
882 // NOTE: each name must be unique, and folders are assigned a unique title by | |
883 // way of an increasing integer. | |
884 void PopulateNodeFromString(const std::string& description, TestNode* parent) { | |
885 std::vector<std::string> elements; | |
886 base::SplitStringAlongWhitespace(description, &elements); | |
887 size_t index = 0; | |
888 PopulateNodeImpl(elements, &index, parent); | |
889 } | |
890 | |
891 // Populates the BookmarkNode with the children of parent. | |
892 void PopulateBookmarkNode(TestNode* parent, | |
893 BookmarkModel* model, | |
894 const BookmarkNode* bb_node) { | |
895 for (int i = 0; i < parent->child_count(); ++i) { | |
896 TestNode* child = parent->GetChild(i); | |
897 if (child->value == BookmarkNode::FOLDER) { | |
898 const BookmarkNode* new_bb_node = | |
899 model->AddFolder(bb_node, i, child->GetTitle()); | |
900 PopulateBookmarkNode(child, model, new_bb_node); | |
901 } else { | |
902 model->AddURL(bb_node, i, child->GetTitle(), | |
903 GURL("http://" + base::UTF16ToASCII(child->GetTitle()))); | |
904 } | |
905 } | |
906 } | |
907 | |
908 // Test class that creates a BookmarkModel with a real history backend. | |
909 class BookmarkModelTestWithProfile : public testing::Test { | |
910 public: | |
911 BookmarkModelTestWithProfile() {} | |
912 | |
913 protected: | |
914 // Verifies the contents of the bookmark bar node match the contents of the | |
915 // TestNode. | |
916 void VerifyModelMatchesNode(TestNode* expected, const BookmarkNode* actual) { | |
917 ASSERT_EQ(expected->child_count(), actual->child_count()); | |
918 for (int i = 0; i < expected->child_count(); ++i) { | |
919 TestNode* expected_child = expected->GetChild(i); | |
920 const BookmarkNode* actual_child = actual->GetChild(i); | |
921 ASSERT_EQ(expected_child->GetTitle(), actual_child->GetTitle()); | |
922 if (expected_child->value == BookmarkNode::FOLDER) { | |
923 ASSERT_TRUE(actual_child->type() == BookmarkNode::FOLDER); | |
924 // Recurse throught children. | |
925 VerifyModelMatchesNode(expected_child, actual_child); | |
926 if (HasFatalFailure()) | |
927 return; | |
928 } else { | |
929 // No need to check the URL, just the title is enough. | |
930 ASSERT_TRUE(actual_child->is_url()); | |
931 } | |
932 } | |
933 } | |
934 | |
935 void VerifyNoDuplicateIDs(BookmarkModel* model) { | |
936 ui::TreeNodeIterator<const BookmarkNode> it(model->root_node()); | |
937 base::hash_set<int64> ids; | |
938 while (it.has_next()) | |
939 ASSERT_TRUE(ids.insert(it.Next()->id()).second); | |
940 } | |
941 | |
942 TestBookmarkClient client_; | |
943 scoped_ptr<BookmarkModel> model_; | |
944 }; | |
945 | |
946 // Creates a set of nodes in the bookmark bar model, then recreates the | |
947 // bookmark bar model which triggers loading from the db and checks the loaded | |
948 // structure to make sure it is what we first created. | |
949 TEST_F(BookmarkModelTestWithProfile, CreateAndRestore) { | |
950 struct TestData { | |
951 // Structure of the children of the bookmark bar model node. | |
952 const std::string bbn_contents; | |
953 // Structure of the children of the other node. | |
954 const std::string other_contents; | |
955 // Structure of the children of the synced node. | |
956 const std::string mobile_contents; | |
957 } data[] = { | |
958 // See PopulateNodeFromString for a description of these strings. | |
959 { "", "" }, | |
960 { "a", "b" }, | |
961 { "a [ b ]", "" }, | |
962 { "", "[ b ] a [ c [ d e [ f ] ] ]" }, | |
963 { "a [ b ]", "" }, | |
964 { "a b c [ d e [ f ] ]", "g h i [ j k [ l ] ]"}, | |
965 }; | |
966 for (size_t i = 0; i < arraysize(data); ++i) { | |
967 model_ = client_.CreateModel(); | |
968 | |
969 TestNode bbn; | |
970 PopulateNodeFromString(data[i].bbn_contents, &bbn); | |
971 PopulateBookmarkNode(&bbn, model_.get(), model_->bookmark_bar_node()); | |
972 | |
973 TestNode other; | |
974 PopulateNodeFromString(data[i].other_contents, &other); | |
975 PopulateBookmarkNode(&other, model_.get(), model_->other_node()); | |
976 | |
977 TestNode mobile; | |
978 PopulateNodeFromString(data[i].mobile_contents, &mobile); | |
979 PopulateBookmarkNode(&mobile, model_.get(), model_->mobile_node()); | |
980 | |
981 VerifyModelMatchesNode(&bbn, model_->bookmark_bar_node()); | |
982 VerifyModelMatchesNode(&other, model_->other_node()); | |
983 VerifyModelMatchesNode(&mobile, model_->mobile_node()); | |
984 VerifyNoDuplicateIDs(model_.get()); | |
985 } | |
986 } | |
987 | |
988 // http://crbug.com/450464 | 933 // http://crbug.com/450464 |
989 TEST_F(BookmarkModelTest, DISABLED_Sort) { | 934 TEST_F(BookmarkModelTest, DISABLED_Sort) { |
990 // Populate the bookmark bar node with nodes for 'B', 'a', 'd' and 'C'. | 935 // Populate the bookmark bar node with nodes for 'B', 'a', 'd' and 'C'. |
991 // 'C' and 'a' are folders. | 936 // 'C' and 'a' are folders. |
992 TestNode bbn; | 937 TestNode bbn; |
993 PopulateNodeFromString("B [ a ] d [ a ]", &bbn); | 938 PopulateNodeFromString("B [ a ] d [ a ]", &bbn); |
994 const BookmarkNode* parent = model_->bookmark_bar_node(); | 939 const BookmarkNode* parent = model_->bookmark_bar_node(); |
995 PopulateBookmarkNode(&bbn, model_.get(), parent); | 940 PopulateBookmarkNode(&bbn, model_.get(), parent); |
996 | 941 |
997 BookmarkNode* child1 = AsMutable(parent->GetChild(1)); | 942 BookmarkNode* child1 = AsMutable(parent->GetChild(1)); |
(...skipping 188 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1186 EXPECT_TRUE(node.DeleteMetaInfo("key2.subkey1")); | 1131 EXPECT_TRUE(node.DeleteMetaInfo("key2.subkey1")); |
1187 EXPECT_TRUE(node.DeleteMetaInfo("key2.subkey2.leaf")); | 1132 EXPECT_TRUE(node.DeleteMetaInfo("key2.subkey2.leaf")); |
1188 EXPECT_FALSE(node.DeleteMetaInfo("key3")); | 1133 EXPECT_FALSE(node.DeleteMetaInfo("key3")); |
1189 EXPECT_FALSE(node.GetMetaInfo("key1", &out_value)); | 1134 EXPECT_FALSE(node.GetMetaInfo("key1", &out_value)); |
1190 EXPECT_FALSE(node.GetMetaInfo("key2.subkey1", &out_value)); | 1135 EXPECT_FALSE(node.GetMetaInfo("key2.subkey1", &out_value)); |
1191 EXPECT_FALSE(node.GetMetaInfo("key2.subkey2", &out_value)); | 1136 EXPECT_FALSE(node.GetMetaInfo("key2.subkey2", &out_value)); |
1192 EXPECT_FALSE(node.GetMetaInfo("key2.subkey2.leaf", &out_value)); | 1137 EXPECT_FALSE(node.GetMetaInfo("key2.subkey2.leaf", &out_value)); |
1193 EXPECT_FALSE(node.GetMetaInfoMap()); | 1138 EXPECT_FALSE(node.GetMetaInfoMap()); |
1194 } | 1139 } |
1195 | 1140 |
| 1141 // Creates a set of nodes in the bookmark model, and checks that the loaded |
| 1142 // structure is what we first created. |
| 1143 TEST(BookmarkModelTest2, CreateAndRestore) { |
| 1144 struct TestData { |
| 1145 // Structure of the children of the bookmark model node. |
| 1146 const std::string bbn_contents; |
| 1147 // Structure of the children of the other node. |
| 1148 const std::string other_contents; |
| 1149 // Structure of the children of the synced node. |
| 1150 const std::string mobile_contents; |
| 1151 } data[] = { |
| 1152 // See PopulateNodeFromString for a description of these strings. |
| 1153 { "", "" }, |
| 1154 { "a", "b" }, |
| 1155 { "a [ b ]", "" }, |
| 1156 { "", "[ b ] a [ c [ d e [ f ] ] ]" }, |
| 1157 { "a [ b ]", "" }, |
| 1158 { "a b c [ d e [ f ] ]", "g h i [ j k [ l ] ]"}, |
| 1159 }; |
| 1160 TestBookmarkClient client; |
| 1161 scoped_ptr<BookmarkModel> model; |
| 1162 for (size_t i = 0; i < arraysize(data); ++i) { |
| 1163 model = client.CreateModel(); |
| 1164 |
| 1165 TestNode bbn; |
| 1166 PopulateNodeFromString(data[i].bbn_contents, &bbn); |
| 1167 PopulateBookmarkNode(&bbn, model.get(), model->bookmark_bar_node()); |
| 1168 |
| 1169 TestNode other; |
| 1170 PopulateNodeFromString(data[i].other_contents, &other); |
| 1171 PopulateBookmarkNode(&other, model.get(), model->other_node()); |
| 1172 |
| 1173 TestNode mobile; |
| 1174 PopulateNodeFromString(data[i].mobile_contents, &mobile); |
| 1175 PopulateBookmarkNode(&mobile, model.get(), model->mobile_node()); |
| 1176 |
| 1177 VerifyModelMatchesNode(&bbn, model->bookmark_bar_node()); |
| 1178 VerifyModelMatchesNode(&other, model->other_node()); |
| 1179 VerifyModelMatchesNode(&mobile, model->mobile_node()); |
| 1180 VerifyNoDuplicateIDs(model.get()); |
| 1181 } |
| 1182 } |
| 1183 |
1196 } // namespace | 1184 } // namespace |
1197 } // namespace bookmarks | 1185 } // namespace bookmarks |
OLD | NEW |