Home | History | Annotate | Download | only in tree
      1 // Copyright (c) 2012 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 "ui/views/controls/tree/tree_view.h"
      6 
      7 #include <string>
      8 
      9 #include "base/strings/string_util.h"
     10 #include "base/strings/utf_string_conversions.h"
     11 #include "ui/base/models/tree_node_model.h"
     12 #include "ui/views/controls/prefix_selector.h"
     13 #include "ui/views/controls/textfield/textfield.h"
     14 #include "ui/views/test/views_test_base.h"
     15 
     16 using ui::TreeModel;
     17 using ui::TreeModelNode;
     18 using ui::TreeNode;
     19 
     20 namespace views {
     21 
     22 class TestNode : public TreeNode<TestNode> {
     23  public:
     24   TestNode() {}
     25   virtual ~TestNode() {}
     26 
     27  private:
     28   DISALLOW_COPY_AND_ASSIGN(TestNode);
     29 };
     30 
     31 // Creates the following structure:
     32 // 'root'
     33 //   'a'
     34 //   'b'
     35 //     'b1'
     36 //   'c'
     37 class TreeViewTest : public ViewsTestBase {
     38  public:
     39   TreeViewTest() : model_(new TestNode) {
     40     static_cast<TestNode*>(model_.GetRoot())->SetTitle(ASCIIToUTF16("root"));
     41     Add(model_.GetRoot(), 0, "a");
     42     Add(Add(model_.GetRoot(), 1, "b"), 0, "b1");
     43     Add(model_.GetRoot(), 2, "c");
     44   }
     45 
     46  protected:
     47   TestNode* Add(TestNode* parent,
     48                 int index,
     49                 const std::string& title);
     50 
     51   std::string TreeViewContentsAsString();
     52 
     53   std::string GetSelectedNodeTitle();
     54 
     55   std::string GetEditingNodeTitle();
     56 
     57   TestNode* GetNodeByTitle(const std::string& title);
     58 
     59   void IncrementSelection(bool next);
     60   void CollapseOrSelectParent();
     61   void ExpandOrSelectChild();
     62   int GetRowCount();
     63   PrefixSelector* selector() { return tree_.selector_.get(); }
     64 
     65   ui::TreeNodeModel<TestNode > model_;
     66   TreeView tree_;
     67 
     68  private:
     69   std::string InternalNodeAsString(TreeView::InternalNode* node);
     70 
     71   TestNode* GetNodeByTitleImpl(TestNode* node, const string16& title);
     72 
     73   DISALLOW_COPY_AND_ASSIGN(TreeViewTest);
     74 };
     75 
     76 TestNode* TreeViewTest::Add(TestNode* parent,
     77                             int index,
     78                             const std::string& title) {
     79   TestNode* new_node = new TestNode;
     80   new_node->SetTitle(ASCIIToUTF16(title));
     81   model_.Add(parent, new_node, index);
     82   return new_node;
     83 }
     84 
     85 std::string TreeViewTest::TreeViewContentsAsString() {
     86   return InternalNodeAsString(&tree_.root_);
     87 }
     88 
     89 std::string TreeViewTest::GetSelectedNodeTitle() {
     90   TreeModelNode* model_node = tree_.GetSelectedNode();
     91   return model_node ? UTF16ToASCII(model_node->GetTitle()) : std::string();
     92 }
     93 
     94 std::string TreeViewTest::GetEditingNodeTitle() {
     95   TreeModelNode* model_node = tree_.GetEditingNode();
     96   return model_node ? UTF16ToASCII(model_node->GetTitle()) : std::string();
     97 }
     98 
     99 TestNode* TreeViewTest::GetNodeByTitle(const std::string& title) {
    100   return GetNodeByTitleImpl(model_.GetRoot(), ASCIIToUTF16(title));
    101 }
    102 
    103 void TreeViewTest::IncrementSelection(bool next) {
    104   tree_.IncrementSelection(next ? TreeView::INCREMENT_NEXT :
    105                            TreeView::INCREMENT_PREVIOUS);
    106 }
    107 
    108 void TreeViewTest::CollapseOrSelectParent() {
    109   tree_.CollapseOrSelectParent();
    110 }
    111 
    112 void TreeViewTest::ExpandOrSelectChild() {
    113   tree_.ExpandOrSelectChild();
    114 }
    115 
    116 int TreeViewTest::GetRowCount() {
    117   return tree_.GetRowCount();
    118 }
    119 
    120 TestNode* TreeViewTest::GetNodeByTitleImpl(TestNode* node,
    121                                            const string16& title) {
    122   if (node->GetTitle() == title)
    123     return node;
    124   for (int i = 0; i < node->child_count(); ++i) {
    125     TestNode* child = GetNodeByTitleImpl(node->GetChild(i), title);
    126     if (child)
    127       return child;
    128   }
    129   return NULL;
    130 }
    131 
    132 std::string TreeViewTest::InternalNodeAsString(
    133     TreeView::InternalNode* node) {
    134   std::string result = UTF16ToASCII(node->model_node()->GetTitle());
    135   if (node->is_expanded() && node->child_count()) {
    136     result += " [";
    137     for (int i = 0; i < node->child_count(); ++i) {
    138       if (i > 0)
    139         result += " ";
    140       result += InternalNodeAsString(node->GetChild(i));
    141     }
    142     result += "]";
    143   }
    144   return result;
    145 }
    146 
    147 // Verifies setting model correctly updates internal state.
    148 TEST_F(TreeViewTest, SetModel) {
    149   tree_.SetModel(&model_);
    150   EXPECT_EQ("root [a b c]", TreeViewContentsAsString());
    151   EXPECT_EQ("root", GetSelectedNodeTitle());
    152   EXPECT_EQ(4, GetRowCount());
    153 }
    154 
    155 // Verifies SetSelectedNode works.
    156 TEST_F(TreeViewTest, SetSelectedNode) {
    157   tree_.SetModel(&model_);
    158   EXPECT_EQ("root", GetSelectedNodeTitle());
    159 
    160   // NULL should clear the selection.
    161   tree_.SetSelectedNode(NULL);
    162   EXPECT_EQ(std::string(), GetSelectedNodeTitle());
    163 
    164   // Select 'c'.
    165   tree_.SetSelectedNode(GetNodeByTitle("c"));
    166   EXPECT_EQ("c", GetSelectedNodeTitle());
    167 
    168   // Select 'b1', which should expand 'b'.
    169   tree_.SetSelectedNode(GetNodeByTitle("b1"));
    170   EXPECT_EQ("root [a b [b1] c]", TreeViewContentsAsString());
    171   EXPECT_EQ("b1", GetSelectedNodeTitle());
    172 }
    173 
    174 // Makes sure SetRootShown doesn't blow up.
    175 TEST_F(TreeViewTest, HideRoot) {
    176   tree_.SetModel(&model_);
    177   tree_.SetRootShown(false);
    178   EXPECT_EQ("root [a b c]", TreeViewContentsAsString());
    179   EXPECT_EQ("a", GetSelectedNodeTitle());
    180   EXPECT_EQ(3, GetRowCount());
    181 }
    182 
    183 // Expands a node and verifies the children are loaded correctly.
    184 TEST_F(TreeViewTest, Expand) {
    185   tree_.SetModel(&model_);
    186   tree_.Expand(GetNodeByTitle("b1"));
    187   EXPECT_EQ("root [a b [b1] c]", TreeViewContentsAsString());
    188   EXPECT_EQ("root",GetSelectedNodeTitle());
    189   EXPECT_EQ(5, GetRowCount());
    190 }
    191 
    192 // Collapes a node and verifies state.
    193 TEST_F(TreeViewTest, Collapse) {
    194   tree_.SetModel(&model_);
    195   tree_.Expand(GetNodeByTitle("b1"));
    196   EXPECT_EQ("root [a b [b1] c]", TreeViewContentsAsString());
    197   EXPECT_EQ(5, GetRowCount());
    198   tree_.SetSelectedNode(GetNodeByTitle("b1"));
    199   EXPECT_EQ("b1", GetSelectedNodeTitle());
    200   tree_.Collapse(GetNodeByTitle("b"));
    201   EXPECT_EQ("root [a b c]", TreeViewContentsAsString());
    202   // Selected node should have moved to 'b'
    203   EXPECT_EQ("b", GetSelectedNodeTitle());
    204   EXPECT_EQ(4, GetRowCount());
    205 }
    206 
    207 // Verifies adding nodes works.
    208 TEST_F(TreeViewTest, TreeNodesAdded) {
    209   tree_.SetModel(&model_);
    210   EXPECT_EQ("root [a b c]", TreeViewContentsAsString());
    211   // Add a node between b and c.
    212   Add(model_.GetRoot(), 2, "B");
    213   EXPECT_EQ("root [a b B c]", TreeViewContentsAsString());
    214   EXPECT_EQ("root", GetSelectedNodeTitle());
    215   EXPECT_EQ(5, GetRowCount());
    216 
    217   // Add a child of b1, which hasn't been loaded and shouldn't do anything.
    218   Add(GetNodeByTitle("b1"), 0, "b11");
    219   EXPECT_EQ("root [a b B c]", TreeViewContentsAsString());
    220   EXPECT_EQ("root", GetSelectedNodeTitle());
    221   EXPECT_EQ(5, GetRowCount());
    222 
    223   // Add a child of b, which isn't expanded yet, so it shouldn't effect
    224   // anything.
    225   Add(GetNodeByTitle("b"), 1, "b2");
    226   EXPECT_EQ("root [a b B c]", TreeViewContentsAsString());
    227   EXPECT_EQ("root", GetSelectedNodeTitle());
    228   EXPECT_EQ(5, GetRowCount());
    229 
    230   // Expand b and make sure b2 is there.
    231   tree_.Expand(GetNodeByTitle("b"));
    232   EXPECT_EQ("root [a b [b1 b2] B c]", TreeViewContentsAsString());
    233   EXPECT_EQ("root",GetSelectedNodeTitle());
    234   EXPECT_EQ(7, GetRowCount());
    235 }
    236 
    237 // Verifies removing nodes works.
    238 TEST_F(TreeViewTest, TreeNodesRemoved) {
    239   // Add c1 as a child of c and c11 as a child of c1.
    240   Add(Add(GetNodeByTitle("c"), 0, "c1"), 0, "c11");
    241   tree_.SetModel(&model_);
    242 
    243   // Remove c11, which shouldn't have any effect on the tree.
    244   EXPECT_EQ("root [a b c]", TreeViewContentsAsString());
    245   EXPECT_EQ("root", GetSelectedNodeTitle());
    246   EXPECT_EQ(4, GetRowCount());
    247 
    248   // Expand b1, then collapse it and remove its only child, b1. This shouldn't
    249   // effect the tree.
    250   tree_.Expand(GetNodeByTitle("b"));
    251   tree_.Collapse(GetNodeByTitle("b"));
    252   delete model_.Remove(GetNodeByTitle("b1")->parent(), GetNodeByTitle("b1"));
    253   EXPECT_EQ("root [a b c]", TreeViewContentsAsString());
    254   EXPECT_EQ("root", GetSelectedNodeTitle());
    255   EXPECT_EQ(4, GetRowCount());
    256 
    257   // Remove 'b'.
    258   delete model_.Remove(GetNodeByTitle("b")->parent(), GetNodeByTitle("b"));
    259   EXPECT_EQ("root [a c]", TreeViewContentsAsString());
    260   EXPECT_EQ("root", GetSelectedNodeTitle());
    261   EXPECT_EQ(3, GetRowCount());
    262 
    263   // Remove 'c11', shouldn't visually change anything.
    264   delete model_.Remove(GetNodeByTitle("c11")->parent(), GetNodeByTitle("c11"));
    265   EXPECT_EQ("root [a c]", TreeViewContentsAsString());
    266   EXPECT_EQ("root", GetSelectedNodeTitle());
    267   EXPECT_EQ(3, GetRowCount());
    268 
    269   // Select 'c1', remove 'c' and make sure selection changes.
    270   tree_.SetSelectedNode(GetNodeByTitle("c1"));
    271   EXPECT_EQ("c1", GetSelectedNodeTitle());
    272   delete model_.Remove(GetNodeByTitle("c")->parent(), GetNodeByTitle("c"));
    273   EXPECT_EQ("root [a]", TreeViewContentsAsString());
    274   EXPECT_EQ("root", GetSelectedNodeTitle());
    275   EXPECT_EQ(2, GetRowCount());
    276 
    277   tree_.SetRootShown(false);
    278   // Add 'b' select it and remove it. Because we're not showing the root
    279   // selection should change to 'a'.
    280   Add(GetNodeByTitle("root"), 1, "b");
    281   tree_.SetSelectedNode(GetNodeByTitle("b"));
    282   delete model_.Remove(GetNodeByTitle("b")->parent(), GetNodeByTitle("b"));
    283   EXPECT_EQ("root [a]", TreeViewContentsAsString());
    284   EXPECT_EQ("a", GetSelectedNodeTitle());
    285   EXPECT_EQ(1, GetRowCount());
    286 }
    287 
    288 // Verifies changing a node title works.
    289 TEST_F(TreeViewTest, TreeNodeChanged) {
    290   // Add c1 as a child of c and c11 as a child of c1.
    291   Add(Add(GetNodeByTitle("c"), 0, "c1"), 0, "c11");
    292   tree_.SetModel(&model_);
    293 
    294   // Change c11, shouldn't do anything.
    295   model_.SetTitle(GetNodeByTitle("c11"), ASCIIToUTF16("c11.new"));
    296   EXPECT_EQ("root [a b c]", TreeViewContentsAsString());
    297   EXPECT_EQ("root", GetSelectedNodeTitle());
    298   EXPECT_EQ(4, GetRowCount());
    299 
    300   // Change 'b1', shouldn't do anything.
    301   model_.SetTitle(GetNodeByTitle("b1"), ASCIIToUTF16("b1.new"));
    302   EXPECT_EQ("root [a b c]", TreeViewContentsAsString());
    303   EXPECT_EQ("root", GetSelectedNodeTitle());
    304   EXPECT_EQ(4, GetRowCount());
    305 
    306   // Change 'b'.
    307   model_.SetTitle(GetNodeByTitle("b"), ASCIIToUTF16("b.new"));
    308   EXPECT_EQ("root [a b.new c]", TreeViewContentsAsString());
    309   EXPECT_EQ("root", GetSelectedNodeTitle());
    310   EXPECT_EQ(4, GetRowCount());
    311 }
    312 
    313 // Verifies IncrementSelection() works.
    314 TEST_F(TreeViewTest, IncrementSelection) {
    315   tree_.SetModel(&model_);
    316 
    317   IncrementSelection(true);
    318   EXPECT_EQ("a", GetSelectedNodeTitle());
    319   IncrementSelection(true);
    320   EXPECT_EQ("b", GetSelectedNodeTitle());
    321   IncrementSelection(true);
    322   tree_.Expand(GetNodeByTitle("b"));
    323   IncrementSelection(false);
    324   EXPECT_EQ("b1", GetSelectedNodeTitle());
    325   IncrementSelection(true);
    326   EXPECT_EQ("c", GetSelectedNodeTitle());
    327   IncrementSelection(true);
    328   EXPECT_EQ("c", GetSelectedNodeTitle());
    329 
    330   tree_.SetRootShown(false);
    331   tree_.SetSelectedNode(GetNodeByTitle("a"));
    332   EXPECT_EQ("a", GetSelectedNodeTitle());
    333   IncrementSelection(false);
    334   EXPECT_EQ("a", GetSelectedNodeTitle());
    335 }
    336 
    337 // Verifies CollapseOrSelectParent works.
    338 TEST_F(TreeViewTest, CollapseOrSelectParent) {
    339   tree_.SetModel(&model_);
    340 
    341   tree_.SetSelectedNode(GetNodeByTitle("root"));
    342   CollapseOrSelectParent();
    343   EXPECT_EQ("root", TreeViewContentsAsString());
    344   EXPECT_EQ("root", GetSelectedNodeTitle());
    345 
    346   // Hide the root, which should implicitly expand the root.
    347   tree_.SetRootShown(false);
    348   EXPECT_EQ("root [a b c]", TreeViewContentsAsString());
    349   EXPECT_EQ("a", GetSelectedNodeTitle());
    350 
    351   tree_.SetSelectedNode(GetNodeByTitle("b1"));
    352   EXPECT_EQ("root [a b [b1] c]", TreeViewContentsAsString());
    353   EXPECT_EQ("b1", GetSelectedNodeTitle());
    354   CollapseOrSelectParent();
    355   EXPECT_EQ("root [a b [b1] c]", TreeViewContentsAsString());
    356   EXPECT_EQ("b", GetSelectedNodeTitle());
    357   CollapseOrSelectParent();
    358   EXPECT_EQ("root [a b c]", TreeViewContentsAsString());
    359   EXPECT_EQ("b", GetSelectedNodeTitle());
    360 }
    361 
    362 // Verifies ExpandOrSelectChild works.
    363 TEST_F(TreeViewTest, ExpandOrSelectChild) {
    364   tree_.SetModel(&model_);
    365 
    366   tree_.SetSelectedNode(GetNodeByTitle("root"));
    367   ExpandOrSelectChild();
    368   EXPECT_EQ("root [a b c]", TreeViewContentsAsString());
    369   EXPECT_EQ("a", GetSelectedNodeTitle());
    370 
    371   ExpandOrSelectChild();
    372   EXPECT_EQ("root [a b c]", TreeViewContentsAsString());
    373   EXPECT_EQ("a", GetSelectedNodeTitle());
    374 
    375   tree_.SetSelectedNode(GetNodeByTitle("b"));
    376   ExpandOrSelectChild();
    377   EXPECT_EQ("root [a b [b1] c]", TreeViewContentsAsString());
    378   EXPECT_EQ("b", GetSelectedNodeTitle());
    379   ExpandOrSelectChild();
    380   EXPECT_EQ("root [a b [b1] c]", TreeViewContentsAsString());
    381   EXPECT_EQ("b1", GetSelectedNodeTitle());
    382   ExpandOrSelectChild();
    383   EXPECT_EQ("root [a b [b1] c]", TreeViewContentsAsString());
    384   EXPECT_EQ("b1", GetSelectedNodeTitle());
    385 }
    386 
    387 // Verify selection is properly updated on each keystroke.
    388 TEST_F(TreeViewTest, SelectOnKeyStroke) {
    389   tree_.SetModel(&model_);
    390   tree_.ExpandAll(model_.GetRoot());
    391   tree_.GetTextInputClient();
    392   selector()->InsertText(ASCIIToUTF16("b"));
    393   EXPECT_EQ("b", GetSelectedNodeTitle());
    394   selector()->InsertText(ASCIIToUTF16("1"));
    395   EXPECT_EQ("b1", GetSelectedNodeTitle());
    396 
    397   // Invoke OnViewBlur() to reset time.
    398   selector()->OnViewBlur();
    399   selector()->InsertText(ASCIIToUTF16("z"));
    400   EXPECT_EQ("b1", GetSelectedNodeTitle());
    401 
    402   selector()->OnViewBlur();
    403   selector()->InsertText(ASCIIToUTF16("a"));
    404   EXPECT_EQ("a", GetSelectedNodeTitle());
    405 }
    406 
    407 // Verifies edits are committed when focus is lost.
    408 TEST_F(TreeViewTest, CommitOnFocusLost) {
    409   tree_.SetModel(&model_);
    410 
    411   tree_.SetSelectedNode(GetNodeByTitle("root"));
    412   ExpandOrSelectChild();
    413   tree_.SetEditable(true);
    414   tree_.StartEditing(GetNodeByTitle("a"));
    415   tree_.editor()->SetText(ASCIIToUTF16("a changed"));
    416   tree_.OnDidChangeFocus(NULL, NULL);
    417   EXPECT_TRUE(GetNodeByTitle("a changed") != NULL);
    418 }
    419 
    420 }  // namespace views
    421