<commit_before>// Copyright (c) 2006-2008 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 file.

#include <set>

#include "app/tree_node_iterator.h"
#include "app/tree_node_model.h"
#include "base/hash_tables.h"
#include "base/string_util.h"
#include "chrome/browser/bookmarks/bookmark_codec.h"
#include "chrome/browser/bookmarks/bookmark_model.h"
#include "chrome/browser/bookmarks/bookmark_utils.h"
#include "chrome/browser/chrome_thread.h"
#include "chrome/browser/history/history_notifications.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/notification_registrar.h"
#include "chrome/common/notification_service.h"
#include "chrome/test/testing_profile.h"
#include "testing/gtest/include/gtest/gtest.h"

using base::Time;
using base::TimeDelta;

namespace {

// Helper to get a mutable bookmark node.
static BookmarkNode* AsMutable(const BookmarkNode* node) {
  return const_cast<BookmarkNode*>(node);
}

void SwapDateAdded(BookmarkNode* n1, BookmarkNode* n2) {
  Time tmp = n1->date_added();
  n1->set_date_added(n2->date_added());
  n2->set_date_added(tmp);
}

// Helper function used to verify node arrangements.
std::wstring ModelStringFromNode(const BookmarkNode* node) {
  // Since the children of the node are not available as a vector,
  // we'll just have to do it the hard way.
  int childCount = node->GetChildCount();
  std::wstring childString;
  for (int i = 0; i < childCount; ++i) {
    const BookmarkNode* child = node->GetChild(i);
    if (child->is_folder()) {
      childString += child->GetTitle() + L":[ " + ModelStringFromNode(child)
                     + L"] ";
    } else {
      childString += child->GetTitle() + L" ";
    }
  }
  return childString;
}

}  // anonymous namespace

class BookmarkModelTest : public testing::Test, public BookmarkModelObserver {
 public:
  struct ObserverDetails {
    ObserverDetails() {
      Set(NULL, NULL, -1, -1);
    }

    void Set(const BookmarkNode* node1,
             const BookmarkNode* node2,
             int index1,
             int index2) {
      this->node1 = node1;
      this->node2 = node2;
      this->index1 = index1;
      this->index2 = index2;
    }

    void AssertEquals(const BookmarkNode* node1,
                      const BookmarkNode* node2,
                      int index1,
                      int index2) {
      ASSERT_TRUE(this->node1 == node1);
      ASSERT_TRUE(this->node2 == node2);
      ASSERT_EQ(index1, this->index1);
      ASSERT_EQ(index2, this->index2);
    }

    const BookmarkNode* node1;
    const BookmarkNode* node2;
    int index1;
    int index2;
  };

  BookmarkModelTest() : model(NULL) {
    model.AddObserver(this);
    ClearCounts();
  }


  void Loaded(BookmarkModel* model) {
    // We never load from the db, so that this should never get invoked.
    NOTREACHED();
  }

  virtual void BookmarkNodeMoved(BookmarkModel* model,
                                 const BookmarkNode* old_parent,
                                 int old_index,
                                 const BookmarkNode* new_parent,
                                 int new_index) {
    moved_count++;
    observer_details.Set(old_parent, new_parent, old_index, new_index);
  }

  virtual void BookmarkNodeAdded(BookmarkModel* model,
                                 const BookmarkNode* parent,
                                 int index) {
    added_count++;
    observer_details.Set(parent, NULL, index, -1);
  }

  virtual void BookmarkNodeRemoved(BookmarkModel* model,
                                   const BookmarkNode* parent,
                                   int old_index,
                                   const BookmarkNode* node) {
    removed_count++;
    observer_details.Set(parent, NULL, old_index, -1);
  }

  virtual void BookmarkNodeChanged(BookmarkModel* model,
                                   const BookmarkNode* node) {
    changed_count++;
    observer_details.Set(node, NULL, -1, -1);
  }

  virtual void BookmarkNodeChildrenReordered(BookmarkModel* model,
                                             const BookmarkNode* node) {
    reordered_count_++;
  }

  virtual void BookmarkNodeFavIconLoaded(BookmarkModel* model,
                                         const BookmarkNode* node) {
    // We never attempt to load favicons, so that this method never
    // gets invoked.
  }

  void ClearCounts() {
    reordered_count_ = moved_count = added_count = removed_count =
        changed_count = 0;
  }

  void AssertObserverCount(int added_count,
                           int moved_count,
                           int removed_count,
                           int changed_count,
                           int reordered_count) {
    ASSERT_EQ(added_count, this->added_count);
    ASSERT_EQ(moved_count, this->moved_count);
    ASSERT_EQ(removed_count, this->removed_count);
    ASSERT_EQ(changed_count, this->changed_count);
    ASSERT_EQ(reordered_count, reordered_count_);
  }

  BookmarkModel model;

  int moved_count;

  int added_count;

  int removed_count;

  int changed_count;

  int reordered_count_;

  ObserverDetails observer_details;
};

TEST_F(BookmarkModelTest, InitialState) {
  const BookmarkNode* bb_node = model.GetBookmarkBarNode();
  ASSERT_TRUE(bb_node != NULL);
  EXPECT_EQ(0, bb_node->GetChildCount());
  EXPECT_EQ(BookmarkNode::BOOKMARK_BAR, bb_node->type());

  const BookmarkNode* other_node = model.other_node();
  ASSERT_TRUE(other_node != NULL);
  EXPECT_EQ(0, other_node->GetChildCount());
  EXPECT_EQ(BookmarkNode::OTHER_NODE, other_node->type());

  EXPECT_TRUE(bb_node->id() != other_node->id());
}

TEST_F(BookmarkModelTest, AddURL) {
  const BookmarkNode* root = model.GetBookmarkBarNode();
  const std::wstring title(L"foo");
  const GURL url("http://foo.com");

  const BookmarkNode* new_node = model.AddURL(root, 0, title, url);
  AssertObserverCount(1, 0, 0, 0, 0);
  observer_details.AssertEquals(root, NULL, 0, -1);

  ASSERT_EQ(1, root->GetChildCount());
  ASSERT_EQ(title, new_node->GetTitle());
  ASSERT_TRUE(url == new_node->GetURL());
  ASSERT_EQ(BookmarkNode::URL, new_node->type());
  ASSERT_TRUE(new_node == model.GetMostRecentlyAddedNodeForURL(url));

  EXPECT_TRUE(new_node->id() != root->id() &&
              new_node->id() != model.other_node()->id());
}

TEST_F(BookmarkModelTest, AddGroup) {
  const BookmarkNode* root = model.GetBookmarkBarNode();
  const std::wstring title(L"foo");

  const BookmarkNode* new_node = model.AddGroup(root, 0, title);
  AssertObserverCount(1, 0, 0, 0, 0);
  observer_details.AssertEquals(root, NULL, 0, -1);

  ASSERT_EQ(1, root->GetChildCount());
  ASSERT_EQ(title, new_node->GetTitle());
  ASSERT_EQ(BookmarkNode::FOLDER, new_node->type());

  EXPECT_TRUE(new_node->id() != root->id() &&
              new_node->id() != model.other_node()->id());

  // Add another group, just to make sure group_ids are incremented correctly.
  ClearCounts();
  model.AddGroup(root, 0, title);
  AssertObserverCount(1, 0, 0, 0, 0);
  observer_details.AssertEquals(root, NULL, 0, -1);
}

TEST_F(BookmarkModelTest, RemoveURL) {
  const BookmarkNode* root = model.GetBookmarkBarNode();
  const std::wstring title(L"foo");
  const GURL url("http://foo.com");
  model.AddURL(root, 0, title, url);
  ClearCounts();

  model.Remove(root, 0);
  ASSERT_EQ(0, root->GetChildCount());
  AssertObserverCount(0, 0, 1, 0, 0);
  observer_details.AssertEquals(root, NULL, 0, -1);

  // Make sure there is no mapping for the URL.
  ASSERT_TRUE(model.GetMostRecentlyAddedNodeForURL(url) == NULL);
}

TEST_F(BookmarkModelTest, RemoveGroup) {
  const BookmarkNode* root = model.GetBookmarkBarNode();
  const BookmarkNode* group = model.AddGroup(root, 0, L"foo");

  ClearCounts();

  // Add a URL as a child.
  const std::wstring title(L"foo");
  const GURL url("http://foo.com");
  model.AddURL(group, 0, title, url);

  ClearCounts();

  // Now remove the group.
  model.Remove(root, 0);
  ASSERT_EQ(0, root->GetChildCount());
  AssertObserverCount(0, 0, 1, 0, 0);
  observer_details.AssertEquals(root, NULL, 0, -1);

  // Make sure there is no mapping for the URL.
  ASSERT_TRUE(model.GetMostRecentlyAddedNodeForURL(url) == NULL);
}

TEST_F(BookmarkModelTest, SetTitle) {
  const BookmarkNode* root = model.GetBookmarkBarNode();
  std::wstring title(L"foo");
  const GURL url("http://foo.com");
  const BookmarkNode* node = model.AddURL(root, 0, title, url);

  ClearCounts();

  title = L"foo2";
  model.SetTitle(node, title);
  AssertObserverCount(0, 0, 0, 1, 0);
  observer_details.AssertEquals(node, NULL, -1, -1);
  EXPECT_EQ(title, node->GetTitle());
}

TEST_F(BookmarkModelTest, SetURL) {
  const BookmarkNode* root = model.GetBookmarkBarNode();
  const std::wstring title(L"foo");
  GURL url("http://foo.com");
  const BookmarkNode* node = model.AddURL(root, 0, title, url);

  ClearCounts();

  url = GURL("http://foo2.com");
  model.SetURL(node, url);
  AssertObserverCount(0, 0, 0, 1, 0);
  observer_details.AssertEquals(node, NULL, -1, -1);
  EXPECT_EQ(url, node->GetURL());
}

TEST_F(BookmarkModelTest, Move) {
  const BookmarkNode* root = model.GetBookmarkBarNode();
  std::wstring title(L"foo");
  const GURL url("http://foo.com");
  const BookmarkNode* node = model.AddURL(root, 0, title, url);
  const BookmarkNode* group1 = model.AddGroup(root, 0, L"foo");
  ClearCounts();

  model.Move(node, group1, 0);

  AssertObserverCount(0, 1, 0, 0, 0);
  observer_details.AssertEquals(root, group1, 1, 0);
  EXPECT_TRUE(group1 == node->GetParent());
  EXPECT_EQ(1, root->GetChildCount());
  EXPECT_EQ(group1, root->GetChild(0));
  EXPECT_EQ(1, group1->GetChildCount());
  EXPECT_EQ(node, group1->GetChild(0));

  // And remove the group.
  ClearCounts();
  model.Remove(root, 0);
  AssertObserverCount(0, 0, 1, 0, 0);
  observer_details.AssertEquals(root, NULL, 0, -1);
  EXPECT_TRUE(model.GetMostRecentlyAddedNodeForURL(url) == NULL);
  EXPECT_EQ(0, root->GetChildCount());
}

TEST_F(BookmarkModelTest, Copy) {
  const BookmarkNode* root = model.GetBookmarkBarNode();
  model.AddURL(root, 0, L"a", GURL("http://a.com"));
  const BookmarkNode* node1 = model.AddGroup(root, 1, L"1");
  model.AddURL(node1, 0, L"b", GURL("http://b.com"));
  model.AddURL(node1, 1, L"c", GURL("http://c.com"));
  model.AddURL(root, 2, L"d", GURL("http://d.com"));
  const BookmarkNode* node2 = model.AddGroup(root, 3, L"2");
  model.AddURL(node2, 0, L"e", GURL("http://e.com"));
  model.AddURL(node2, 1, L"f", GURL("http://f.com"));
  model.AddURL(node2, 2, L"g", GURL("http://g.com"));
  model.AddURL(root, 4, L"h", GURL("http://d.com"));

  // Validate initial model.
  std::wstring actualModelString = ModelStringFromNode(root);
  EXPECT_EQ(L"a 1:[ b c ] d 2:[ e f g ] h ", ModelStringFromNode(root));

  // Copy 'd' to be after '1:b': URL item from bar to folder.
  const BookmarkNode* nodeToCopy = root->GetChild(2);
  const BookmarkNode* destination = root->GetChild(1);
  model.Copy(nodeToCopy, destination, 1);
  actualModelString = ModelStringFromNode(root);
  EXPECT_EQ(L"a 1:[ b d c ] d 2:[ e f g ] h ", actualModelString);

  // Copy '1:d' to be after 'a': URL item from folder to bar.
  const BookmarkNode* group = root->GetChild(1);
  nodeToCopy = group->GetChild(1);
  model.Copy(nodeToCopy, root, 1);
  actualModelString = ModelStringFromNode(root);
  EXPECT_EQ(L"a d 1:[ b d c ] d 2:[ e f g ] h ", actualModelString);

  // Copy '1' to be after '2:e': Folder from bar to folder.
  nodeToCopy = root->GetChild(2);
  destination = root->GetChild(4);
  model.Copy(nodeToCopy, destination, 1);
  actualModelString = ModelStringFromNode(root);
  EXPECT_EQ(L"a d 1:[ b d c ] d 2:[ e 1:[ b d c ] f g ] h ", actualModelString);

  // Copy '2:1' to be after '2:f': Folder within same folder.
  group = root->GetChild(4);
  nodeToCopy = group->GetChild(1);
  model.Copy(nodeToCopy, group, 3);
  actualModelString = ModelStringFromNode(root);
  EXPECT_EQ(L"a d 1:[ b d c ] d 2:[ e 1:[ b d c ] f 1:[ b d c ] g ] h ",
            actualModelString);

  // Copy first 'd' to be after 'h': URL item within the bar.
  nodeToCopy = root->GetChild(1);
  model.Copy(nodeToCopy, root, 6);
  actualModelString = ModelStringFromNode(root);
  EXPECT_EQ(L"a d 1:[ b d c ] d 2:[ e 1:[ b d c ] f 1:[ b d c ] g ] h d ",
            actualModelString);

  // Copy '2' to be after 'a': Folder within the bar.
  nodeToCopy = root->GetChild(4);
  model.Copy(nodeToCopy, root, 1);
  actualModelString = ModelStringFromNode(root);
  EXPECT_EQ(L"a 2:[ e 1:[ b d c ] f 1:[ b d c ] g ] d 1:[ b d c ] "
             "d 2:[ e 1:[ b d c ] f 1:[ b d c ] g ] h d ",
            actualModelString);
}

// Tests that adding a URL to a folder updates the last modified time.
TEST_F(BookmarkModelTest, ParentForNewNodes) {
  ASSERT_EQ(model.GetBookmarkBarNode(), model.GetParentForNewNodes());

  const std::wstring title(L"foo");
  const GURL url("http://foo.com");<commit_msg>Long string continuation lines require an 'L' on each continuation.<commit_after>// Copyright (c) 2006-2008 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 file.

#include <set>

#include "app/tree_node_iterator.h"
#include "app/tree_node_model.h"
#include "base/hash_tables.h"
#include "base/string_util.h"
#include "chrome/browser/bookmarks/bookmark_codec.h"
#include "chrome/browser/bookmarks/bookmark_model.h"
#include "chrome/browser/bookmarks/bookmark_utils.h"
#include "chrome/browser/chrome_thread.h"
#include "chrome/browser/history/history_notifications.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/notification_registrar.h"
#include "chrome/common/notification_service.h"
#include "chrome/test/testing_profile.h"
#include "testing/gtest/include/gtest/gtest.h"

using base::Time;
using base::TimeDelta;

namespace {

// Helper to get a mutable bookmark node.
static BookmarkNode* AsMutable(const BookmarkNode* node) {
  return const_cast<BookmarkNode*>(node);
}

void SwapDateAdded(BookmarkNode* n1, BookmarkNode* n2) {
  Time tmp = n1->date_added();
  n1->set_date_added(n2->date_added());
  n2->set_date_added(tmp);
}

// Helper function used to verify node arrangements.
std::wstring ModelStringFromNode(const BookmarkNode* node) {
  // Since the children of the node are not available as a vector,
  // we'll just have to do it the hard way.
  int childCount = node->GetChildCount();
  std::wstring childString;
  for (int i = 0; i < childCount; ++i) {
    const BookmarkNode* child = node->GetChild(i);
    if (child->is_folder()) {
      childString += child->GetTitle() + L":[ " + ModelStringFromNode(child)
                     + L"] ";
    } else {
      childString += child->GetTitle() + L" ";
    }
  }
  return childString;
}

}  // anonymous namespace

class BookmarkModelTest : public testing::Test, public BookmarkModelObserver {
 public:
  struct ObserverDetails {
    ObserverDetails() {
      Set(NULL, NULL, -1, -1);
    }

    void Set(const BookmarkNode* node1,
             const BookmarkNode* node2,
             int index1,
             int index2) {
      this->node1 = node1;
      this->node2 = node2;
      this->index1 = index1;
      this->index2 = index2;
    }

    void AssertEquals(const BookmarkNode* node1,
                      const BookmarkNode* node2,
                      int index1,
                      int index2) {
      ASSERT_TRUE(this->node1 == node1);
      ASSERT_TRUE(this->node2 == node2);
      ASSERT_EQ(index1, this->index1);
      ASSERT_EQ(index2, this->index2);
    }

    const BookmarkNode* node1;
    const BookmarkNode* node2;
    int index1;
    int index2;
  };

  BookmarkModelTest() : model(NULL) {
    model.AddObserver(this);
    ClearCounts();
  }


  void Loaded(BookmarkModel* model) {
    // We never load from the db, so that this should never get invoked.
    NOTREACHED();
  }

  virtual void BookmarkNodeMoved(BookmarkModel* model,
                                 const BookmarkNode* old_parent,
                                 int old_index,
                                 const BookmarkNode* new_parent,
                                 int new_index) {
    moved_count++;
    observer_details.Set(old_parent, new_parent, old_index, new_index);
  }

  virtual void BookmarkNodeAdded(BookmarkModel* model,
                                 const BookmarkNode* parent,
                                 int index) {
    added_count++;
    observer_details.Set(parent, NULL, index, -1);
  }

  virtual void BookmarkNodeRemoved(BookmarkModel* model,
                                   const BookmarkNode* parent,
                                   int old_index,
                                   const BookmarkNode* node) {
    removed_count++;
    observer_details.Set(parent, NULL, old_index, -1);
  }

  virtual void BookmarkNodeChanged(BookmarkModel* model,
                                   const BookmarkNode* node) {
    changed_count++;
    observer_details.Set(node, NULL, -1, -1);
  }

  virtual void BookmarkNodeChildrenReordered(BookmarkModel* model,
                                             const BookmarkNode* node) {
    reordered_count_++;
  }

  virtual void BookmarkNodeFavIconLoaded(BookmarkModel* model,
                                         const BookmarkNode* node) {
    // We never attempt to load favicons, so that this method never
    // gets invoked.
  }

  void ClearCounts() {
    reordered_count_ = moved_count = added_count = removed_count =
        changed_count = 0;
  }

  void AssertObserverCount(int added_count,
                           int moved_count,
                           int removed_count,
                           int changed_count,
                           int reordered_count) {
    ASSERT_EQ(added_count, this->added_count);
    ASSERT_EQ(moved_count, this->moved_count);
    ASSERT_EQ(removed_count, this->removed_count);
    ASSERT_EQ(changed_count, this->changed_count);
    ASSERT_EQ(reordered_count, reordered_count_);
  }

  BookmarkModel model;

  int moved_count;

  int added_count;

  int removed_count;

  int changed_count;

  int reordered_count_;

  ObserverDetails observer_details;
};

TEST_F(BookmarkModelTest, InitialState) {
  const BookmarkNode* bb_node = model.GetBookmarkBarNode();
  ASSERT_TRUE(bb_node != NULL);
  EXPECT_EQ(0, bb_node->GetChildCount());
  EXPECT_EQ(BookmarkNode::BOOKMARK_BAR, bb_node->type());

  const BookmarkNode* other_node = model.other_node();
  ASSERT_TRUE(other_node != NULL);
  EXPECT_EQ(0, other_node->GetChildCount());
  EXPECT_EQ(BookmarkNode::OTHER_NODE, other_node->type());

  EXPECT_TRUE(bb_node->id() != other_node->id());
}

TEST_F(BookmarkModelTest, AddURL) {
  const BookmarkNode* root = model.GetBookmarkBarNode();
  const std::wstring title(L"foo");
  const GURL url("http://foo.com");

  const BookmarkNode* new_node = model.AddURL(root, 0, title, url);
  AssertObserverCount(1, 0, 0, 0, 0);
  observer_details.AssertEquals(root, NULL, 0, -1);

  ASSERT_EQ(1, root->GetChildCount());
  ASSERT_EQ(title, new_node->GetTitle());
  ASSERT_TRUE(url == new_node->GetURL());
  ASSERT_EQ(BookmarkNode::URL, new_node->type());
  ASSERT_TRUE(new_node == model.GetMostRecentlyAddedNodeForURL(url));

  EXPECT_TRUE(new_node->id() != root->id() &&
              new_node->id() != model.other_node()->id());
}

TEST_F(BookmarkModelTest, AddGroup) {
  const BookmarkNode* root = model.GetBookmarkBarNode();
  const std::wstring title(L"foo");

  const BookmarkNode* new_node = model.AddGroup(root, 0, title);
  AssertObserverCount(1, 0, 0, 0, 0);
  observer_details.AssertEquals(root, NULL, 0, -1);

  ASSERT_EQ(1, root->GetChildCount());
  ASSERT_EQ(title, new_node->GetTitle());
  ASSERT_EQ(BookmarkNode::FOLDER, new_node->type());

  EXPECT_TRUE(new_node->id() != root->id() &&
              new_node->id() != model.other_node()->id());

  // Add another group, just to make sure group_ids are incremented correctly.
  ClearCounts();
  model.AddGroup(root, 0, title);
  AssertObserverCount(1, 0, 0, 0, 0);
  observer_details.AssertEquals(root, NULL, 0, -1);
}

TEST_F(BookmarkModelTest, RemoveURL) {
  const BookmarkNode* root = model.GetBookmarkBarNode();
  const std::wstring title(L"foo");
  const GURL url("http://foo.com");
  model.AddURL(root, 0, title, url);
  ClearCounts();

  model.Remove(root, 0);
  ASSERT_EQ(0, root->GetChildCount());
  AssertObserverCount(0, 0, 1, 0, 0);
  observer_details.AssertEquals(root, NULL, 0, -1);

  // Make sure there is no mapping for the URL.
  ASSERT_TRUE(model.GetMostRecentlyAddedNodeForURL(url) == NULL);
}

TEST_F(BookmarkModelTest, RemoveGroup) {
  const BookmarkNode* root = model.GetBookmarkBarNode();
  const BookmarkNode* group = model.AddGroup(root, 0, L"foo");

  ClearCounts();

  // Add a URL as a child.
  const std::wstring title(L"foo");
  const GURL url("http://foo.com");
  model.AddURL(group, 0, title, url);

  ClearCounts();

  // Now remove the group.
  model.Remove(root, 0);
  ASSERT_EQ(0, root->GetChildCount());
  AssertObserverCount(0, 0, 1, 0, 0);
  observer_details.AssertEquals(root, NULL, 0, -1);

  // Make sure there is no mapping for the URL.
  ASSERT_TRUE(model.GetMostRecentlyAddedNodeForURL(url) == NULL);
}

TEST_F(BookmarkModelTest, SetTitle) {
  const BookmarkNode* root = model.GetBookmarkBarNode();
  std::wstring title(L"foo");
  const GURL url("http://foo.com");
  const BookmarkNode* node = model.AddURL(root, 0, title, url);

  ClearCounts();

  title = L"foo2";
  model.SetTitle(node, title);
  AssertObserverCount(0, 0, 0, 1, 0);
  observer_details.AssertEquals(node, NULL, -1, -1);
  EXPECT_EQ(title, node->GetTitle());
}

TEST_F(BookmarkModelTest, SetURL) {
  const BookmarkNode* root = model.GetBookmarkBarNode();
  const std::wstring title(L"foo");
  GURL url("http://foo.com");
  const BookmarkNode* node = model.AddURL(root, 0, title, url);

  ClearCounts();

  url = GURL("http://foo2.com");
  model.SetURL(node, url);
  AssertObserverCount(0, 0, 0, 1, 0);
  observer_details.AssertEquals(node, NULL, -1, -1);
  EXPECT_EQ(url, node->GetURL());
}

TEST_F(BookmarkModelTest, Move) {
  const BookmarkNode* root = model.GetBookmarkBarNode();
  std::wstring title(L"foo");
  const GURL url("http://foo.com");
  const BookmarkNode* node = model.AddURL(root, 0, title, url);
  const BookmarkNode* group1 = model.AddGroup(root, 0, L"foo");
  ClearCounts();

  model.Move(node, group1, 0);

  AssertObserverCount(0, 1, 0, 0, 0);
  observer_details.AssertEquals(root, group1, 1, 0);
  EXPECT_TRUE(group1 == node->GetParent());
  EXPECT_EQ(1, root->GetChildCount());
  EXPECT_EQ(group1, root->GetChild(0));
  EXPECT_EQ(1, group1->GetChildCount());
  EXPECT_EQ(node, group1->GetChild(0));

  // And remove the group.
  ClearCounts();
  model.Remove(root, 0);
  AssertObserverCount(0, 0, 1, 0, 0);
  observer_details.AssertEquals(root, NULL, 0, -1);
  EXPECT_TRUE(model.GetMostRecentlyAddedNodeForURL(url) == NULL);
  EXPECT_EQ(0, root->GetChildCount());
}

TEST_F(BookmarkModelTest, Copy) {
  const BookmarkNode* root = model.GetBookmarkBarNode();
  model.AddURL(root, 0, L"a", GURL("http://a.com"));
  const BookmarkNode* node1 = model.AddGroup(root, 1, L"1");
  model.AddURL(node1, 0, L"b", GURL("http://b.com"));
  model.AddURL(node1, 1, L"c", GURL("http://c.com"));
  model.AddURL(root, 2, L"d", GURL("http://d.com"));
  const BookmarkNode* node2 = model.AddGroup(root, 3, L"2");
  model.AddURL(node2, 0, L"e", GURL("http://e.com"));
  model.AddURL(node2, 1, L"f", GURL("http://f.com"));
  model.AddURL(node2, 2, L"g", GURL("http://g.com"));
  model.AddURL(root, 4, L"h", GURL("http://d.com"));

  // Validate initial model.
  std::wstring actualModelString = ModelStringFromNode(root);
  EXPECT_EQ(L"a 1:[ b c ] d 2:[ e f g ] h ", ModelStringFromNode(root));

  // Copy 'd' to be after '1:b': URL item from bar to folder.
  const BookmarkNode* nodeToCopy = root->GetChild(2);
  const BookmarkNode* destination = root->GetChild(1);
  model.Copy(nodeToCopy, destination, 1);
  actualModelString = ModelStringFromNode(root);
  EXPECT_EQ(L"a 1:[ b d c ] d 2:[ e f g ] h ", actualModelString);

  // Copy '1:d' to be after 'a': URL item from folder to bar.
  const BookmarkNode* group = root->GetChild(1);
  nodeToCopy = group->GetChild(1);
  model.Copy(nodeToCopy, root, 1);
  actualModelString = ModelStringFromNode(root);
  EXPECT_EQ(L"a d 1:[ b d c ] d 2:[ e f g ] h ", actualModelString);

  // Copy '1' to be after '2:e': Folder from bar to folder.
  nodeToCopy = root->GetChild(2);
  destination = root->GetChild(4);
  model.Copy(nodeToCopy, destination, 1);
  actualModelString = ModelStringFromNode(root);
  EXPECT_EQ(L"a d 1:[ b d c ] d 2:[ e 1:[ b d c ] f g ] h ", actualModelString);

  // Copy '2:1' to be after '2:f': Folder within same folder.
  group = root->GetChild(4);
  nodeToCopy = group->GetChild(1);
  model.Copy(nodeToCopy, group, 3);
  actualModelString = ModelStringFromNode(root);
  EXPECT_EQ(L"a d 1:[ b d c ] d 2:[ e 1:[ b d c ] f 1:[ b d c ] g ] h ",
            actualModelString);

  // Copy first 'd' to be after 'h': URL item within the bar.
  nodeToCopy = root->GetChild(1);
  model.Copy(nodeToCopy, root, 6);
  actualModelString = ModelStringFromNode(root);
  EXPECT_EQ(L"a d 1:[ b d c ] d 2:[ e 1:[ b d c ] f 1:[ b d c ] g ] h d ",
            actualModelString);

  // Copy '2' to be after 'a': Folder within the bar.
  nodeToCopy = root->GetChild(4);
  model.Copy(nodeToCopy, root, 1);
  actualModelString = ModelStringFromNode(root);
  EXPECT_EQ(L"a 2:[ e 1:[ b d c ] f 1:[ b d c ] g ] d 1:[ b d c ] "
            L"d 2:[ e 1:[ b d c ] f 1:[ b d c ] g ] h d ",
            actualModelString);
}

// Tests that adding a URL to a folder updates the last modified time.
TEST_F(BookmarkModelTest, ParentForNewNodes) {
  ASSERT_EQ(model.GetBookmarkBarNode(), model.GetParentForNewNodes());

  const std::wstring title(L"foo");
  const GURL url("http://foo.com");
{'fraction_non_alphanumeric': 0.13936439702157058, 'fraction_numerical': 0.016427419973900362, 'mean_word_length': 3.370177792687018, 'pattern_counts': {'":': 2, '<': 9, '<?xml version=': 0, '>': 131, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 34, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1557130', 'n_tokens_mistral': 9053, 'n_tokens_neox': 8694, 'n_words': 2126}