Mercurial > hg > soundsoftware-site
comparison test/unit/project_nested_set_test.rb @ 1337:077b8890835a cannam
Merge from live branch
author | Chris Cannam |
---|---|
date | Thu, 20 Jun 2013 13:14:02 +0100 |
parents | 433d4f72a19b |
children | 622f24f53b42 |
comparison
equal
deleted
inserted
replaced
1304:6137548ba453 | 1337:077b8890835a |
---|---|
1 # Redmine - project management software | 1 # Redmine - project management software |
2 # Copyright (C) 2006-2011 Jean-Philippe Lang | 2 # Copyright (C) 2006-2012 Jean-Philippe Lang |
3 # | 3 # |
4 # This program is free software; you can redistribute it and/or | 4 # This program is free software; you can redistribute it and/or |
5 # modify it under the terms of the GNU General Public License | 5 # modify it under the terms of the GNU General Public License |
6 # as published by the Free Software Foundation; either version 2 | 6 # as published by the Free Software Foundation; either version 2 |
7 # of the License, or (at your option) any later version. | 7 # of the License, or (at your option) any later version. |
17 | 17 |
18 require File.expand_path('../../test_helper', __FILE__) | 18 require File.expand_path('../../test_helper', __FILE__) |
19 | 19 |
20 class ProjectNestedSetTest < ActiveSupport::TestCase | 20 class ProjectNestedSetTest < ActiveSupport::TestCase |
21 | 21 |
22 context "nested set" do | 22 def setup |
23 setup do | 23 Project.delete_all |
24 Project.delete_all | |
25 | 24 |
26 @a = Project.create!(:name => 'Project A', :identifier => 'projecta') | 25 @a = Project.create!(:name => 'A', :identifier => 'projecta') |
27 @a1 = Project.create!(:name => 'Project A1', :identifier => 'projecta1') | 26 @a1 = Project.create!(:name => 'A1', :identifier => 'projecta1') |
28 @a1.set_parent!(@a) | 27 @a1.set_parent!(@a) |
29 @a2 = Project.create!(:name => 'Project A2', :identifier => 'projecta2') | 28 @a2 = Project.create!(:name => 'A2', :identifier => 'projecta2') |
30 @a2.set_parent!(@a) | 29 @a2.set_parent!(@a) |
31 | 30 |
32 @b = Project.create!(:name => 'Project B', :identifier => 'projectb') | 31 @c = Project.create!(:name => 'C', :identifier => 'projectc') |
33 @b1 = Project.create!(:name => 'Project B1', :identifier => 'projectb1') | 32 @c1 = Project.create!(:name => 'C1', :identifier => 'projectc1') |
34 @b1.set_parent!(@b) | 33 @c1.set_parent!(@c) |
35 @b11 = Project.create!(:name => 'Project B11', :identifier => 'projectb11') | |
36 @b11.set_parent!(@b1) | |
37 @b2 = Project.create!(:name => 'Project B2', :identifier => 'projectb2') | |
38 @b2.set_parent!(@b) | |
39 | 34 |
40 @c = Project.create!(:name => 'Project C', :identifier => 'projectc') | 35 @b = Project.create!(:name => 'B', :identifier => 'projectb') |
41 @c1 = Project.create!(:name => 'Project C1', :identifier => 'projectc1') | 36 @b2 = Project.create!(:name => 'B2', :identifier => 'projectb2') |
42 @c1.set_parent!(@c) | 37 @b2.set_parent!(@b) |
38 @b1 = Project.create!(:name => 'B1', :identifier => 'projectb1') | |
39 @b1.set_parent!(@b) | |
40 @b11 = Project.create!(:name => 'B11', :identifier => 'projectb11') | |
41 @b11.set_parent!(@b1) | |
43 | 42 |
44 [@a, @a1, @a2, @b, @b1, @b11, @b2, @c, @c1].each(&:reload) | 43 @a, @a1, @a2, @b, @b1, @b11, @b2, @c, @c1 = *(Project.all.sort_by(&:name)) |
44 end | |
45 | |
46 def test_valid_tree | |
47 assert_valid_nested_set | |
48 end | |
49 | |
50 def test_rebuild_should_build_valid_tree | |
51 Project.update_all "lft = NULL, rgt = NULL" | |
52 | |
53 Project.rebuild! | |
54 assert_valid_nested_set | |
55 end | |
56 | |
57 def test_rebuild_tree_should_build_valid_tree_even_with_valid_lft_rgt_values | |
58 Project.update_all "name = 'YY'", {:id => @a.id } | |
59 # lft and rgt values are still valid (Project.rebuild! would not update anything) | |
60 # but projects are not ordered properly (YY is in the first place) | |
61 | |
62 Project.rebuild_tree! | |
63 assert_valid_nested_set | |
64 end | |
65 | |
66 def test_moving_a_child_to_a_different_parent_should_keep_valid_tree | |
67 assert_no_difference 'Project.count' do | |
68 Project.find_by_name('B1').set_parent!(Project.find_by_name('A2')) | |
45 end | 69 end |
70 assert_valid_nested_set | |
71 end | |
46 | 72 |
47 context "#create" do | 73 def test_renaming_a_root_to_first_position_should_update_nested_set_order |
48 should "build valid tree" do | 74 @c.name = '1' |
49 assert_nested_set_values({ | 75 @c.save! |
50 @a => [nil, 1, 6], | 76 assert_valid_nested_set |
51 @a1 => [@a.id, 2, 3], | 77 end |
52 @a2 => [@a.id, 4, 5], | 78 |
53 @b => [nil, 7, 14], | 79 def test_renaming_a_root_to_middle_position_should_update_nested_set_order |
54 @b1 => [@b.id, 8, 11], | 80 @a.name = 'BA' |
55 @b11 => [@b1.id,9, 10], | 81 @a.save! |
56 @b2 => [@b.id,12, 13], | 82 assert_valid_nested_set |
57 @c => [nil, 15, 18], | 83 end |
58 @c1 => [@c.id,16, 17] | 84 |
59 }) | 85 def test_renaming_a_root_to_last_position_should_update_nested_set_order |
60 end | 86 @a.name = 'D' |
87 @a.save! | |
88 assert_valid_nested_set | |
89 end | |
90 | |
91 def test_renaming_a_root_to_same_position_should_update_nested_set_order | |
92 @c.name = 'D' | |
93 @c.save! | |
94 assert_valid_nested_set | |
95 end | |
96 | |
97 def test_renaming_a_child_should_update_nested_set_order | |
98 @a1.name = 'A3' | |
99 @a1.save! | |
100 assert_valid_nested_set | |
101 end | |
102 | |
103 def test_renaming_a_child_with_child_should_update_nested_set_order | |
104 @b1.name = 'B3' | |
105 @b1.save! | |
106 assert_valid_nested_set | |
107 end | |
108 | |
109 def test_adding_a_root_to_first_position_should_update_nested_set_order | |
110 project = Project.create!(:name => '1', :identifier => 'projectba') | |
111 assert_valid_nested_set | |
112 end | |
113 | |
114 def test_adding_a_root_to_middle_position_should_update_nested_set_order | |
115 project = Project.create!(:name => 'BA', :identifier => 'projectba') | |
116 assert_valid_nested_set | |
117 end | |
118 | |
119 def test_adding_a_root_to_last_position_should_update_nested_set_order | |
120 project = Project.create!(:name => 'Z', :identifier => 'projectba') | |
121 assert_valid_nested_set | |
122 end | |
123 | |
124 def test_destroying_a_root_with_children_should_keep_valid_tree | |
125 assert_difference 'Project.count', -4 do | |
126 Project.find_by_name('B').destroy | |
61 end | 127 end |
128 assert_valid_nested_set | |
129 end | |
62 | 130 |
63 context "#set_parent!" do | 131 def test_destroying_a_child_with_children_should_keep_valid_tree |
64 should "keep valid tree" do | 132 assert_difference 'Project.count', -2 do |
65 assert_no_difference 'Project.count' do | 133 Project.find_by_name('B1').destroy |
66 Project.find_by_name('Project B1').set_parent!(Project.find_by_name('Project A2')) | |
67 end | |
68 assert_nested_set_values({ | |
69 @a => [nil, 1, 10], | |
70 @a2 => [@a.id, 4, 9], | |
71 @b1 => [@a2.id,5, 8], | |
72 @b11 => [@b1.id,6, 7], | |
73 @b => [nil, 11, 14], | |
74 @c => [nil, 15, 18] | |
75 }) | |
76 end | |
77 end | 134 end |
135 assert_valid_nested_set | |
136 end | |
78 | 137 |
79 context "#destroy" do | 138 private |
80 context "a root with children" do | |
81 should "not mess up the tree" do | |
82 assert_difference 'Project.count', -4 do | |
83 Project.find_by_name('Project B').destroy | |
84 end | |
85 assert_nested_set_values({ | |
86 @a => [nil, 1, 6], | |
87 @a1 => [@a.id, 2, 3], | |
88 @a2 => [@a.id, 4, 5], | |
89 @c => [nil, 7, 10], | |
90 @c1 => [@c.id, 8, 9] | |
91 }) | |
92 end | |
93 end | |
94 | |
95 context "a child with children" do | |
96 should "not mess up the tree" do | |
97 assert_difference 'Project.count', -2 do | |
98 Project.find_by_name('Project B1').destroy | |
99 end | |
100 assert_nested_set_values({ | |
101 @a => [nil, 1, 6], | |
102 @b => [nil, 7, 10], | |
103 @b2 => [@b.id, 8, 9], | |
104 @c => [nil, 11, 14] | |
105 }) | |
106 end | |
107 end | |
108 end | |
109 end | |
110 | 139 |
111 def assert_nested_set_values(h) | 140 def assert_nested_set_values(h) |
112 assert Project.valid? | 141 assert Project.valid? |
113 h.each do |project, expected| | 142 h.each do |project, expected| |
114 project.reload | 143 project.reload |
115 assert_equal expected, [project.parent_id, project.lft, project.rgt], "Unexpected nested set values for #{project.name}" | 144 assert_equal expected, [project.parent_id, project.lft, project.rgt], "Unexpected nested set values for #{project.name}" |
116 end | 145 end |
117 end | 146 end |
147 | |
148 def assert_valid_nested_set | |
149 projects = Project.all | |
150 lft_rgt = projects.map {|p| [p.lft, p.rgt]}.flatten | |
151 assert_equal projects.size * 2, lft_rgt.uniq.size | |
152 assert_equal 1, lft_rgt.min | |
153 assert_equal projects.size * 2, lft_rgt.max | |
154 | |
155 projects.each do |project| | |
156 # lft should always be < rgt | |
157 assert project.lft < project.rgt, "lft=#{project.lft} was not < rgt=#{project.rgt} for project #{project.name}" | |
158 if project.parent_id | |
159 # child lft/rgt values must be greater/lower | |
160 assert_not_nil project.parent, "parent was nil for project #{project.name}" | |
161 assert project.lft > project.parent.lft, "lft=#{project.lft} was not > parent.lft=#{project.parent.lft} for project #{project.name}" | |
162 assert project.rgt < project.parent.rgt, "rgt=#{project.rgt} was not < parent.rgt=#{project.parent.rgt} for project #{project.name}" | |
163 end | |
164 # no overlapping lft/rgt values | |
165 overlapping = projects.detect {|other| | |
166 other != project && ( | |
167 (other.lft > project.lft && other.lft < project.rgt && other.rgt > project.rgt) || | |
168 (other.rgt > project.lft && other.rgt < project.rgt && other.lft < project.lft) | |
169 ) | |
170 } | |
171 assert_nil overlapping, (overlapping && "Project #{overlapping.name} (#{overlapping.lft}/#{overlapping.rgt}) overlapped #{project.name} (#{project.lft}/#{project.rgt})") | |
172 end | |
173 | |
174 # root projects sorted alphabetically | |
175 assert_equal Project.roots.map(&:name).sort, Project.roots.sort_by(&:lft).map(&:name), "Root projects were not properly sorted" | |
176 projects.each do |project| | |
177 if project.children.any? | |
178 # sibling projects sorted alphabetically | |
179 assert_equal project.children.map(&:name).sort, project.children.order('lft').map(&:name), "Project #{project.name}'s children were not properly sorted" | |
180 end | |
181 end | |
182 end | |
118 end | 183 end |