root/ui/views/controls/tree/tree_view_unittest.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. selector
  2. Add
  3. TreeViewContentsAsString
  4. GetSelectedNodeTitle
  5. GetEditingNodeTitle
  6. GetNodeByTitle
  7. IncrementSelection
  8. CollapseOrSelectParent
  9. ExpandOrSelectChild
  10. GetRowCount
  11. GetNodeByTitleImpl
  12. InternalNodeAsString
  13. TEST_F
  14. TEST_F
  15. TEST_F
  16. TEST_F
  17. TEST_F
  18. TEST_F
  19. TEST_F
  20. TEST_F
  21. TEST_F
  22. TEST_F
  23. TEST_F
  24. TEST_F
  25. TEST_F

// 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 file.

#include "ui/views/controls/tree/tree_view.h"

#include <string>

#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "ui/base/models/tree_node_model.h"
#include "ui/views/controls/prefix_selector.h"
#include "ui/views/controls/textfield/textfield.h"
#include "ui/views/test/views_test_base.h"

using ui::TreeModel;
using ui::TreeModelNode;
using ui::TreeNode;

using base::ASCIIToUTF16;

namespace views {

class TestNode : public TreeNode<TestNode> {
 public:
  TestNode() {}
  virtual ~TestNode() {}

 private:
  DISALLOW_COPY_AND_ASSIGN(TestNode);
};

// Creates the following structure:
// 'root'
//   'a'
//   'b'
//     'b1'
//   'c'
class TreeViewTest : public ViewsTestBase {
 public:
  TreeViewTest() : model_(new TestNode) {
    static_cast<TestNode*>(model_.GetRoot())->SetTitle(ASCIIToUTF16("root"));
    Add(model_.GetRoot(), 0, "a");
    Add(Add(model_.GetRoot(), 1, "b"), 0, "b1");
    Add(model_.GetRoot(), 2, "c");
  }

 protected:
  TestNode* Add(TestNode* parent,
                int index,
                const std::string& title);

  std::string TreeViewContentsAsString();

  std::string GetSelectedNodeTitle();

  std::string GetEditingNodeTitle();

  TestNode* GetNodeByTitle(const std::string& title);

  void IncrementSelection(bool next);
  void CollapseOrSelectParent();
  void ExpandOrSelectChild();
  int GetRowCount();
  PrefixSelector* selector() { return tree_.selector_.get(); }

  ui::TreeNodeModel<TestNode > model_;
  TreeView tree_;

 private:
  std::string InternalNodeAsString(TreeView::InternalNode* node);

  TestNode* GetNodeByTitleImpl(TestNode* node, const base::string16& title);

  DISALLOW_COPY_AND_ASSIGN(TreeViewTest);
};

TestNode* TreeViewTest::Add(TestNode* parent,
                            int index,
                            const std::string& title) {
  TestNode* new_node = new TestNode;
  new_node->SetTitle(ASCIIToUTF16(title));
  model_.Add(parent, new_node, index);
  return new_node;
}

std::string TreeViewTest::TreeViewContentsAsString() {
  return InternalNodeAsString(&tree_.root_);
}

std::string TreeViewTest::GetSelectedNodeTitle() {
  TreeModelNode* model_node = tree_.GetSelectedNode();
  return model_node ? base::UTF16ToASCII(model_node->GetTitle())
                    : std::string();
}

std::string TreeViewTest::GetEditingNodeTitle() {
  TreeModelNode* model_node = tree_.GetEditingNode();
  return model_node ? base::UTF16ToASCII(model_node->GetTitle())
                    : std::string();
}

TestNode* TreeViewTest::GetNodeByTitle(const std::string& title) {
  return GetNodeByTitleImpl(model_.GetRoot(), ASCIIToUTF16(title));
}

void TreeViewTest::IncrementSelection(bool next) {
  tree_.IncrementSelection(next ? TreeView::INCREMENT_NEXT :
                           TreeView::INCREMENT_PREVIOUS);
}

void TreeViewTest::CollapseOrSelectParent() {
  tree_.CollapseOrSelectParent();
}

void TreeViewTest::ExpandOrSelectChild() {
  tree_.ExpandOrSelectChild();
}

int TreeViewTest::GetRowCount() {
  return tree_.GetRowCount();
}

TestNode* TreeViewTest::GetNodeByTitleImpl(TestNode* node,
                                           const base::string16& title) {
  if (node->GetTitle() == title)
    return node;
  for (int i = 0; i < node->child_count(); ++i) {
    TestNode* child = GetNodeByTitleImpl(node->GetChild(i), title);
    if (child)
      return child;
  }
  return NULL;
}

std::string TreeViewTest::InternalNodeAsString(
    TreeView::InternalNode* node) {
  std::string result = base::UTF16ToASCII(node->model_node()->GetTitle());
  if (node->is_expanded() && node->child_count()) {
    result += " [";
    for (int i = 0; i < node->child_count(); ++i) {
      if (i > 0)
        result += " ";
      result += InternalNodeAsString(node->GetChild(i));
    }
    result += "]";
  }
  return result;
}

// Verifies setting model correctly updates internal state.
TEST_F(TreeViewTest, SetModel) {
  tree_.SetModel(&model_);
  EXPECT_EQ("root [a b c]", TreeViewContentsAsString());
  EXPECT_EQ("root", GetSelectedNodeTitle());
  EXPECT_EQ(4, GetRowCount());
}

// Verifies SetSelectedNode works.
TEST_F(TreeViewTest, SetSelectedNode) {
  tree_.SetModel(&model_);
  EXPECT_EQ("root", GetSelectedNodeTitle());

  // NULL should clear the selection.
  tree_.SetSelectedNode(NULL);
  EXPECT_EQ(std::string(), GetSelectedNodeTitle());

  // Select 'c'.
  tree_.SetSelectedNode(GetNodeByTitle("c"));
  EXPECT_EQ("c", GetSelectedNodeTitle());

  // Select 'b1', which should expand 'b'.
  tree_.SetSelectedNode(GetNodeByTitle("b1"));
  EXPECT_EQ("root [a b [b1] c]", TreeViewContentsAsString());
  EXPECT_EQ("b1", GetSelectedNodeTitle());
}

// Makes sure SetRootShown doesn't blow up.
TEST_F(TreeViewTest, HideRoot) {
  tree_.SetModel(&model_);
  tree_.SetRootShown(false);
  EXPECT_EQ("root [a b c]", TreeViewContentsAsString());
  EXPECT_EQ("a", GetSelectedNodeTitle());
  EXPECT_EQ(3, GetRowCount());
}

// Expands a node and verifies the children are loaded correctly.
TEST_F(TreeViewTest, Expand) {
  tree_.SetModel(&model_);
  tree_.Expand(GetNodeByTitle("b1"));
  EXPECT_EQ("root [a b [b1] c]", TreeViewContentsAsString());
  EXPECT_EQ("root",GetSelectedNodeTitle());
  EXPECT_EQ(5, GetRowCount());
}

// Collapes a node and verifies state.
TEST_F(TreeViewTest, Collapse) {
  tree_.SetModel(&model_);
  tree_.Expand(GetNodeByTitle("b1"));
  EXPECT_EQ("root [a b [b1] c]", TreeViewContentsAsString());
  EXPECT_EQ(5, GetRowCount());
  tree_.SetSelectedNode(GetNodeByTitle("b1"));
  EXPECT_EQ("b1", GetSelectedNodeTitle());
  tree_.Collapse(GetNodeByTitle("b"));
  EXPECT_EQ("root [a b c]", TreeViewContentsAsString());
  // Selected node should have moved to 'b'
  EXPECT_EQ("b", GetSelectedNodeTitle());
  EXPECT_EQ(4, GetRowCount());
}

// Verifies adding nodes works.
TEST_F(TreeViewTest, TreeNodesAdded) {
  tree_.SetModel(&model_);
  EXPECT_EQ("root [a b c]", TreeViewContentsAsString());
  // Add a node between b and c.
  Add(model_.GetRoot(), 2, "B");
  EXPECT_EQ("root [a b B c]", TreeViewContentsAsString());
  EXPECT_EQ("root", GetSelectedNodeTitle());
  EXPECT_EQ(5, GetRowCount());

  // Add a child of b1, which hasn't been loaded and shouldn't do anything.
  Add(GetNodeByTitle("b1"), 0, "b11");
  EXPECT_EQ("root [a b B c]", TreeViewContentsAsString());
  EXPECT_EQ("root", GetSelectedNodeTitle());
  EXPECT_EQ(5, GetRowCount());

  // Add a child of b, which isn't expanded yet, so it shouldn't effect
  // anything.
  Add(GetNodeByTitle("b"), 1, "b2");
  EXPECT_EQ("root [a b B c]", TreeViewContentsAsString());
  EXPECT_EQ("root", GetSelectedNodeTitle());
  EXPECT_EQ(5, GetRowCount());

  // Expand b and make sure b2 is there.
  tree_.Expand(GetNodeByTitle("b"));
  EXPECT_EQ("root [a b [b1 b2] B c]", TreeViewContentsAsString());
  EXPECT_EQ("root",GetSelectedNodeTitle());
  EXPECT_EQ(7, GetRowCount());
}

// Verifies removing nodes works.
TEST_F(TreeViewTest, TreeNodesRemoved) {
  // Add c1 as a child of c and c11 as a child of c1.
  Add(Add(GetNodeByTitle("c"), 0, "c1"), 0, "c11");
  tree_.SetModel(&model_);

  // Remove c11, which shouldn't have any effect on the tree.
  EXPECT_EQ("root [a b c]", TreeViewContentsAsString());
  EXPECT_EQ("root", GetSelectedNodeTitle());
  EXPECT_EQ(4, GetRowCount());

  // Expand b1, then collapse it and remove its only child, b1. This shouldn't
  // effect the tree.
  tree_.Expand(GetNodeByTitle("b"));
  tree_.Collapse(GetNodeByTitle("b"));
  delete model_.Remove(GetNodeByTitle("b1")->parent(), GetNodeByTitle("b1"));
  EXPECT_EQ("root [a b c]", TreeViewContentsAsString());
  EXPECT_EQ("root", GetSelectedNodeTitle());
  EXPECT_EQ(4, GetRowCount());

  // Remove 'b'.
  delete model_.Remove(GetNodeByTitle("b")->parent(), GetNodeByTitle("b"));
  EXPECT_EQ("root [a c]", TreeViewContentsAsString());
  EXPECT_EQ("root", GetSelectedNodeTitle());
  EXPECT_EQ(3, GetRowCount());

  // Remove 'c11', shouldn't visually change anything.
  delete model_.Remove(GetNodeByTitle("c11")->parent(), GetNodeByTitle("c11"));
  EXPECT_EQ("root [a c]", TreeViewContentsAsString());
  EXPECT_EQ("root", GetSelectedNodeTitle());
  EXPECT_EQ(3, GetRowCount());

  // Select 'c1', remove 'c' and make sure selection changes.
  tree_.SetSelectedNode(GetNodeByTitle("c1"));
  EXPECT_EQ("c1", GetSelectedNodeTitle());
  delete model_.Remove(GetNodeByTitle("c")->parent(), GetNodeByTitle("c"));
  EXPECT_EQ("root [a]", TreeViewContentsAsString());
  EXPECT_EQ("root", GetSelectedNodeTitle());
  EXPECT_EQ(2, GetRowCount());

  tree_.SetRootShown(false);
  // Add 'b' select it and remove it. Because we're not showing the root
  // selection should change to 'a'.
  Add(GetNodeByTitle("root"), 1, "b");
  tree_.SetSelectedNode(GetNodeByTitle("b"));
  delete model_.Remove(GetNodeByTitle("b")->parent(), GetNodeByTitle("b"));
  EXPECT_EQ("root [a]", TreeViewContentsAsString());
  EXPECT_EQ("a", GetSelectedNodeTitle());
  EXPECT_EQ(1, GetRowCount());
}

// Verifies changing a node title works.
TEST_F(TreeViewTest, TreeNodeChanged) {
  // Add c1 as a child of c and c11 as a child of c1.
  Add(Add(GetNodeByTitle("c"), 0, "c1"), 0, "c11");
  tree_.SetModel(&model_);

  // Change c11, shouldn't do anything.
  model_.SetTitle(GetNodeByTitle("c11"), ASCIIToUTF16("c11.new"));
  EXPECT_EQ("root [a b c]", TreeViewContentsAsString());
  EXPECT_EQ("root", GetSelectedNodeTitle());
  EXPECT_EQ(4, GetRowCount());

  // Change 'b1', shouldn't do anything.
  model_.SetTitle(GetNodeByTitle("b1"), ASCIIToUTF16("b1.new"));
  EXPECT_EQ("root [a b c]", TreeViewContentsAsString());
  EXPECT_EQ("root", GetSelectedNodeTitle());
  EXPECT_EQ(4, GetRowCount());

  // Change 'b'.
  model_.SetTitle(GetNodeByTitle("b"), ASCIIToUTF16("b.new"));
  EXPECT_EQ("root [a b.new c]", TreeViewContentsAsString());
  EXPECT_EQ("root", GetSelectedNodeTitle());
  EXPECT_EQ(4, GetRowCount());
}

// Verifies IncrementSelection() works.
TEST_F(TreeViewTest, IncrementSelection) {
  tree_.SetModel(&model_);

  IncrementSelection(true);
  EXPECT_EQ("a", GetSelectedNodeTitle());
  IncrementSelection(true);
  EXPECT_EQ("b", GetSelectedNodeTitle());
  IncrementSelection(true);
  tree_.Expand(GetNodeByTitle("b"));
  IncrementSelection(false);
  EXPECT_EQ("b1", GetSelectedNodeTitle());
  IncrementSelection(true);
  EXPECT_EQ("c", GetSelectedNodeTitle());
  IncrementSelection(true);
  EXPECT_EQ("c", GetSelectedNodeTitle());

  tree_.SetRootShown(false);
  tree_.SetSelectedNode(GetNodeByTitle("a"));
  EXPECT_EQ("a", GetSelectedNodeTitle());
  IncrementSelection(false);
  EXPECT_EQ("a", GetSelectedNodeTitle());
}

// Verifies CollapseOrSelectParent works.
TEST_F(TreeViewTest, CollapseOrSelectParent) {
  tree_.SetModel(&model_);

  tree_.SetSelectedNode(GetNodeByTitle("root"));
  CollapseOrSelectParent();
  EXPECT_EQ("root", TreeViewContentsAsString());
  EXPECT_EQ("root", GetSelectedNodeTitle());

  // Hide the root, which should implicitly expand the root.
  tree_.SetRootShown(false);
  EXPECT_EQ("root [a b c]", TreeViewContentsAsString());
  EXPECT_EQ("a", GetSelectedNodeTitle());

  tree_.SetSelectedNode(GetNodeByTitle("b1"));
  EXPECT_EQ("root [a b [b1] c]", TreeViewContentsAsString());
  EXPECT_EQ("b1", GetSelectedNodeTitle());
  CollapseOrSelectParent();
  EXPECT_EQ("root [a b [b1] c]", TreeViewContentsAsString());
  EXPECT_EQ("b", GetSelectedNodeTitle());
  CollapseOrSelectParent();
  EXPECT_EQ("root [a b c]", TreeViewContentsAsString());
  EXPECT_EQ("b", GetSelectedNodeTitle());
}

// Verifies ExpandOrSelectChild works.
TEST_F(TreeViewTest, ExpandOrSelectChild) {
  tree_.SetModel(&model_);

  tree_.SetSelectedNode(GetNodeByTitle("root"));
  ExpandOrSelectChild();
  EXPECT_EQ("root [a b c]", TreeViewContentsAsString());
  EXPECT_EQ("a", GetSelectedNodeTitle());

  ExpandOrSelectChild();
  EXPECT_EQ("root [a b c]", TreeViewContentsAsString());
  EXPECT_EQ("a", GetSelectedNodeTitle());

  tree_.SetSelectedNode(GetNodeByTitle("b"));
  ExpandOrSelectChild();
  EXPECT_EQ("root [a b [b1] c]", TreeViewContentsAsString());
  EXPECT_EQ("b", GetSelectedNodeTitle());
  ExpandOrSelectChild();
  EXPECT_EQ("root [a b [b1] c]", TreeViewContentsAsString());
  EXPECT_EQ("b1", GetSelectedNodeTitle());
  ExpandOrSelectChild();
  EXPECT_EQ("root [a b [b1] c]", TreeViewContentsAsString());
  EXPECT_EQ("b1", GetSelectedNodeTitle());
}

// Verify selection is properly updated on each keystroke.
TEST_F(TreeViewTest, SelectOnKeyStroke) {
  tree_.SetModel(&model_);
  tree_.ExpandAll(model_.GetRoot());
  tree_.GetTextInputClient();
  selector()->InsertText(ASCIIToUTF16("b"));
  EXPECT_EQ("b", GetSelectedNodeTitle());
  selector()->InsertText(ASCIIToUTF16("1"));
  EXPECT_EQ("b1", GetSelectedNodeTitle());

  // Invoke OnViewBlur() to reset time.
  selector()->OnViewBlur();
  selector()->InsertText(ASCIIToUTF16("z"));
  EXPECT_EQ("b1", GetSelectedNodeTitle());

  selector()->OnViewBlur();
  selector()->InsertText(ASCIIToUTF16("a"));
  EXPECT_EQ("a", GetSelectedNodeTitle());
}

// Verifies edits are committed when focus is lost.
TEST_F(TreeViewTest, CommitOnFocusLost) {
  tree_.SetModel(&model_);

  tree_.SetSelectedNode(GetNodeByTitle("root"));
  ExpandOrSelectChild();
  tree_.SetEditable(true);
  tree_.StartEditing(GetNodeByTitle("a"));
  tree_.editor()->SetText(ASCIIToUTF16("a changed"));
  tree_.OnDidChangeFocus(NULL, NULL);
  EXPECT_TRUE(GetNodeByTitle("a changed") != NULL);
}

}  // namespace views

/* [<][>][^][v][top][bottom][index][help] */