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
3// found in the LICENSE file.
4
5#include "components/policy/core/browser/managed_bookmarks_tracker.h"
6
7#include "base/bind.h"
8#include "base/files/file_path.h"
9#include "base/memory/scoped_ptr.h"
10#include "base/message_loop/message_loop.h"
11#include "base/prefs/pref_registry_simple.h"
12#include "base/prefs/testing_pref_service.h"
13#include "base/strings/utf_string_conversions.h"
14#include "components/bookmarks/browser/bookmark_model.h"
15#include "components/bookmarks/browser/bookmark_model_observer.h"
16#include "components/bookmarks/browser/bookmark_node.h"
17#include "components/bookmarks/common/bookmark_pref_names.h"
18#include "components/bookmarks/test/bookmark_test_helpers.h"
19#include "components/bookmarks/test/mock_bookmark_model_observer.h"
20#include "components/bookmarks/test/test_bookmark_client.h"
21#include "grit/components_strings.h"
22#include "testing/gmock/include/gmock/gmock.h"
23#include "testing/gtest/include/gtest/gtest.h"
24#include "ui/base/l10n/l10n_util.h"
25#include "url/gurl.h"
26
27using testing::Mock;
28using testing::_;
29
30namespace policy {
31
32class ManagedBookmarksTrackerTest : public testing::Test {
33 public:
34  ManagedBookmarksTrackerTest() : managed_node_(NULL) {}
35  virtual ~ManagedBookmarksTrackerTest() {}
36
37  virtual void SetUp() OVERRIDE {
38    prefs_.registry()->RegisterListPref(prefs::kManagedBookmarks);
39    prefs_.registry()->RegisterListPref(prefs::kBookmarkEditorExpandedNodes);
40  }
41
42  virtual void TearDown() OVERRIDE {
43    if (model_)
44      model_->RemoveObserver(&observer_);
45  }
46
47  void CreateModel() {
48    // Simulate the creation of the managed node by the BookmarkClient.
49    BookmarkPermanentNode* managed_node = new BookmarkPermanentNode(100);
50    policy::ManagedBookmarksTracker::LoadInitial(
51        managed_node, prefs_.GetList(prefs::kManagedBookmarks), 101);
52    managed_node->set_visible(!managed_node->empty());
53    managed_node->SetTitle(l10n_util::GetStringUTF16(
54        IDS_BOOKMARK_BAR_MANAGED_FOLDER_DEFAULT_NAME));
55
56    bookmarks::BookmarkPermanentNodeList extra_nodes;
57    extra_nodes.push_back(managed_node);
58    client_.SetExtraNodesToLoad(extra_nodes.Pass());
59
60    model_.reset(new BookmarkModel(&client_, false));
61    model_->AddObserver(&observer_);
62    EXPECT_CALL(observer_, BookmarkModelLoaded(model_.get(), _));
63    model_->Load(&prefs_,
64                 std::string(),
65                 base::FilePath(),
66                 base::MessageLoopProxy::current(),
67                 base::MessageLoopProxy::current());
68    test::WaitForBookmarkModelToLoad(model_.get());
69    Mock::VerifyAndClearExpectations(&observer_);
70
71    ASSERT_EQ(1u, client_.extra_nodes().size());
72    managed_node_ = client_.extra_nodes()[0];
73    ASSERT_EQ(managed_node, managed_node_);
74
75    managed_bookmarks_tracker_.reset(new ManagedBookmarksTracker(
76        model_.get(),
77        &prefs_,
78        base::Bind(&ManagedBookmarksTrackerTest::GetManagementDomain)));
79    managed_bookmarks_tracker_->Init(managed_node_);
80  }
81
82  const BookmarkNode* managed_node() {
83    return managed_node_;
84  }
85
86  bool IsManaged(const BookmarkNode* node) {
87    return node && node->HasAncestor(managed_node_);
88  }
89
90  static base::DictionaryValue* CreateBookmark(const std::string& title,
91                                               const std::string& url) {
92    EXPECT_TRUE(GURL(url).is_valid());
93    base::DictionaryValue* dict = new base::DictionaryValue();
94    dict->SetString("name", title);
95    dict->SetString("url", GURL(url).spec());
96    return dict;
97  }
98
99  static base::DictionaryValue* CreateFolder(const std::string& title,
100                                             base::ListValue* children) {
101    base::DictionaryValue* dict = new base::DictionaryValue();
102    dict->SetString("name", title);
103    dict->Set("children", children);
104    return dict;
105  }
106
107  static base::ListValue* CreateTestTree() {
108    base::ListValue* folder = new base::ListValue();
109    base::ListValue* empty = new base::ListValue();
110    folder->Append(CreateFolder("Empty", empty));
111    folder->Append(CreateBookmark("Youtube", "http://youtube.com/"));
112
113    base::ListValue* list = new base::ListValue();
114    list->Append(CreateBookmark("Google", "http://google.com/"));
115    list->Append(CreateFolder("Folder", folder));
116
117    return list;
118  }
119
120  static std::string GetManagementDomain() {
121    return std::string();
122  }
123
124  static std::string GetManagedFolderTitle() {
125    return l10n_util::GetStringUTF8(
126        IDS_BOOKMARK_BAR_MANAGED_FOLDER_DEFAULT_NAME);
127  }
128
129  static base::DictionaryValue* CreateExpectedTree() {
130    return CreateFolder(GetManagedFolderTitle(), CreateTestTree());
131  }
132
133  static bool NodeMatchesValue(const BookmarkNode* node,
134                               const base::DictionaryValue* dict) {
135    base::string16 title;
136    if (!dict->GetString("name", &title) || node->GetTitle() != title)
137      return false;
138
139    if (node->is_folder()) {
140      const base::ListValue* children = NULL;
141      if (!dict->GetList("children", &children) ||
142          node->child_count() != static_cast<int>(children->GetSize())) {
143        return false;
144      }
145      for (int i = 0; i < node->child_count(); ++i) {
146        const base::DictionaryValue* child = NULL;
147        if (!children->GetDictionary(i, &child) ||
148            !NodeMatchesValue(node->GetChild(i), child)) {
149          return false;
150        }
151      }
152    } else if (node->is_url()) {
153      std::string url;
154      if (!dict->GetString("url", &url) || node->url() != GURL(url))
155        return false;
156    } else {
157      return false;
158    }
159    return true;
160  }
161
162  base::MessageLoop loop_;
163  TestingPrefServiceSimple prefs_;
164  test::TestBookmarkClient client_;
165  scoped_ptr<BookmarkModel> model_;
166  MockBookmarkModelObserver observer_;
167  BookmarkPermanentNode* managed_node_;
168  scoped_ptr<ManagedBookmarksTracker> managed_bookmarks_tracker_;
169};
170
171TEST_F(ManagedBookmarksTrackerTest, Empty) {
172  CreateModel();
173  EXPECT_TRUE(model_->bookmark_bar_node()->empty());
174  EXPECT_TRUE(model_->other_node()->empty());
175  EXPECT_TRUE(managed_node()->empty());
176  EXPECT_FALSE(managed_node()->IsVisible());
177}
178
179TEST_F(ManagedBookmarksTrackerTest, LoadInitial) {
180  // Set a policy before loading the model.
181  prefs_.SetManagedPref(prefs::kManagedBookmarks, CreateTestTree());
182  CreateModel();
183  EXPECT_TRUE(model_->bookmark_bar_node()->empty());
184  EXPECT_TRUE(model_->other_node()->empty());
185  EXPECT_FALSE(managed_node()->empty());
186  EXPECT_TRUE(managed_node()->IsVisible());
187
188  scoped_ptr<base::DictionaryValue> expected(CreateExpectedTree());
189  EXPECT_TRUE(NodeMatchesValue(managed_node(), expected.get()));
190}
191
192TEST_F(ManagedBookmarksTrackerTest, SwapNodes) {
193  prefs_.SetManagedPref(prefs::kManagedBookmarks, CreateTestTree());
194  CreateModel();
195
196  // Swap the Google bookmark with the Folder.
197  scoped_ptr<base::ListValue> updated(CreateTestTree());
198  scoped_ptr<base::Value> removed;
199  ASSERT_TRUE(updated->Remove(0, &removed));
200  updated->Append(removed.release());
201
202  // These two nodes should just be swapped.
203  const BookmarkNode* parent = managed_node();
204  EXPECT_CALL(observer_, BookmarkNodeMoved(model_.get(), parent, 1, parent, 0));
205  prefs_.SetManagedPref(prefs::kManagedBookmarks, updated->DeepCopy());
206  Mock::VerifyAndClearExpectations(&observer_);
207
208  // Verify the final tree.
209  scoped_ptr<base::DictionaryValue> expected(
210      CreateFolder(GetManagedFolderTitle(), updated.release()));
211  EXPECT_TRUE(NodeMatchesValue(managed_node(), expected.get()));
212}
213
214TEST_F(ManagedBookmarksTrackerTest, RemoveNode) {
215  prefs_.SetManagedPref(prefs::kManagedBookmarks, CreateTestTree());
216  CreateModel();
217
218  // Remove the Folder.
219  scoped_ptr<base::ListValue> updated(CreateTestTree());
220  ASSERT_TRUE(updated->Remove(1, NULL));
221
222  const BookmarkNode* parent = managed_node();
223  EXPECT_CALL(observer_, BookmarkNodeRemoved(model_.get(), parent, 1, _, _));
224  prefs_.SetManagedPref(prefs::kManagedBookmarks, updated->DeepCopy());
225  Mock::VerifyAndClearExpectations(&observer_);
226
227  // Verify the final tree.
228  scoped_ptr<base::DictionaryValue> expected(
229      CreateFolder(GetManagedFolderTitle(), updated.release()));
230  EXPECT_TRUE(NodeMatchesValue(managed_node(), expected.get()));
231}
232
233TEST_F(ManagedBookmarksTrackerTest, CreateNewNodes) {
234  prefs_.SetManagedPref(prefs::kManagedBookmarks, CreateTestTree());
235  CreateModel();
236
237  // Put all the nodes inside another folder.
238  scoped_ptr<base::ListValue> updated(new base::ListValue);
239  updated->Append(CreateFolder("Container", CreateTestTree()));
240
241  EXPECT_CALL(observer_, BookmarkNodeAdded(model_.get(), _, _)).Times(5);
242  // The remaining nodes have been pushed to positions 1 and 2; they'll both be
243  // removed when at position 1.
244  const BookmarkNode* parent = managed_node();
245  EXPECT_CALL(observer_, BookmarkNodeRemoved(model_.get(), parent, 1, _, _))
246      .Times(2);
247  prefs_.SetManagedPref(prefs::kManagedBookmarks, updated->DeepCopy());
248  Mock::VerifyAndClearExpectations(&observer_);
249
250  // Verify the final tree.
251  scoped_ptr<base::DictionaryValue> expected(
252      CreateFolder(GetManagedFolderTitle(), updated.release()));
253  EXPECT_TRUE(NodeMatchesValue(managed_node(), expected.get()));
254}
255
256TEST_F(ManagedBookmarksTrackerTest, RemoveAll) {
257  prefs_.SetManagedPref(prefs::kManagedBookmarks, CreateTestTree());
258  CreateModel();
259  EXPECT_TRUE(managed_node()->IsVisible());
260
261  // Remove the policy.
262  const BookmarkNode* parent = managed_node();
263  EXPECT_CALL(observer_, BookmarkNodeRemoved(model_.get(), parent, 0, _, _))
264      .Times(2);
265  prefs_.RemoveManagedPref(prefs::kManagedBookmarks);
266  Mock::VerifyAndClearExpectations(&observer_);
267
268  EXPECT_TRUE(managed_node()->empty());
269  EXPECT_FALSE(managed_node()->IsVisible());
270}
271
272TEST_F(ManagedBookmarksTrackerTest, IsManaged) {
273  prefs_.SetManagedPref(prefs::kManagedBookmarks, CreateTestTree());
274  CreateModel();
275
276  EXPECT_FALSE(IsManaged(model_->root_node()));
277  EXPECT_FALSE(IsManaged(model_->bookmark_bar_node()));
278  EXPECT_FALSE(IsManaged(model_->other_node()));
279  EXPECT_FALSE(IsManaged(model_->mobile_node()));
280  EXPECT_TRUE(IsManaged(managed_node()));
281
282  const BookmarkNode* parent = managed_node();
283  ASSERT_EQ(2, parent->child_count());
284  EXPECT_TRUE(IsManaged(parent->GetChild(0)));
285  EXPECT_TRUE(IsManaged(parent->GetChild(1)));
286
287  parent = parent->GetChild(1);
288  ASSERT_EQ(2, parent->child_count());
289  EXPECT_TRUE(IsManaged(parent->GetChild(0)));
290  EXPECT_TRUE(IsManaged(parent->GetChild(1)));
291}
292
293TEST_F(ManagedBookmarksTrackerTest, RemoveAllUserBookmarksDoesntRemoveManaged) {
294  prefs_.SetManagedPref(prefs::kManagedBookmarks, CreateTestTree());
295  CreateModel();
296  EXPECT_EQ(2, managed_node()->child_count());
297
298  EXPECT_CALL(observer_,
299              BookmarkNodeAdded(model_.get(), model_->bookmark_bar_node(), 0));
300  EXPECT_CALL(observer_,
301              BookmarkNodeAdded(model_.get(), model_->bookmark_bar_node(), 1));
302  model_->AddURL(model_->bookmark_bar_node(),
303                 0,
304                 base::ASCIIToUTF16("Test"),
305                 GURL("http://google.com/"));
306  model_->AddFolder(
307      model_->bookmark_bar_node(), 1, base::ASCIIToUTF16("Test Folder"));
308  EXPECT_EQ(2, model_->bookmark_bar_node()->child_count());
309  Mock::VerifyAndClearExpectations(&observer_);
310
311  EXPECT_CALL(observer_, BookmarkAllUserNodesRemoved(model_.get(), _));
312  model_->RemoveAllUserBookmarks();
313  EXPECT_EQ(2, managed_node()->child_count());
314  EXPECT_EQ(0, model_->bookmark_bar_node()->child_count());
315  Mock::VerifyAndClearExpectations(&observer_);
316}
317
318}  // namespace policy
319