##// END OF EJS Templates
Fixes calculation of version estimated hours with subtasks (#5265)....
Jean-Philippe Lang -
r3519:71a4158fd049
parent child
Show More
@@ -1,209 +1,210
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006 Jean-Philippe Lang
2 # Copyright (C) 2006 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.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 class Version < ActiveRecord::Base
18 class Version < ActiveRecord::Base
19 before_destroy :check_integrity
19 before_destroy :check_integrity
20 after_update :update_issues_from_sharing_change
20 after_update :update_issues_from_sharing_change
21 belongs_to :project
21 belongs_to :project
22 has_many :fixed_issues, :class_name => 'Issue', :foreign_key => 'fixed_version_id'
22 has_many :fixed_issues, :class_name => 'Issue', :foreign_key => 'fixed_version_id'
23 acts_as_customizable
23 acts_as_customizable
24 acts_as_attachable :view_permission => :view_files,
24 acts_as_attachable :view_permission => :view_files,
25 :delete_permission => :manage_files
25 :delete_permission => :manage_files
26
26
27 VERSION_STATUSES = %w(open locked closed)
27 VERSION_STATUSES = %w(open locked closed)
28 VERSION_SHARINGS = %w(none descendants hierarchy tree system)
28 VERSION_SHARINGS = %w(none descendants hierarchy tree system)
29
29
30 validates_presence_of :name
30 validates_presence_of :name
31 validates_uniqueness_of :name, :scope => [:project_id]
31 validates_uniqueness_of :name, :scope => [:project_id]
32 validates_length_of :name, :maximum => 60
32 validates_length_of :name, :maximum => 60
33 validates_format_of :effective_date, :with => /^\d{4}-\d{2}-\d{2}$/, :message => :not_a_date, :allow_nil => true
33 validates_format_of :effective_date, :with => /^\d{4}-\d{2}-\d{2}$/, :message => :not_a_date, :allow_nil => true
34 validates_inclusion_of :status, :in => VERSION_STATUSES
34 validates_inclusion_of :status, :in => VERSION_STATUSES
35 validates_inclusion_of :sharing, :in => VERSION_SHARINGS
35 validates_inclusion_of :sharing, :in => VERSION_SHARINGS
36
36
37 named_scope :open, :conditions => {:status => 'open'}
37 named_scope :open, :conditions => {:status => 'open'}
38 named_scope :visible, lambda {|*args| { :include => :project,
38 named_scope :visible, lambda {|*args| { :include => :project,
39 :conditions => Project.allowed_to_condition(args.first || User.current, :view_issues) } }
39 :conditions => Project.allowed_to_condition(args.first || User.current, :view_issues) } }
40
40
41 # Returns true if +user+ or current user is allowed to view the version
41 # Returns true if +user+ or current user is allowed to view the version
42 def visible?(user=User.current)
42 def visible?(user=User.current)
43 user.allowed_to?(:view_issues, self.project)
43 user.allowed_to?(:view_issues, self.project)
44 end
44 end
45
45
46 def start_date
46 def start_date
47 effective_date
47 effective_date
48 end
48 end
49
49
50 def due_date
50 def due_date
51 effective_date
51 effective_date
52 end
52 end
53
53
54 # Returns the total estimated time for this version
54 # Returns the total estimated time for this version
55 # (sum of leaves estimated_hours)
55 def estimated_hours
56 def estimated_hours
56 @estimated_hours ||= fixed_issues.sum(:estimated_hours).to_f
57 @estimated_hours ||= fixed_issues.leaves.sum(:estimated_hours).to_f
57 end
58 end
58
59
59 # Returns the total reported time for this version
60 # Returns the total reported time for this version
60 def spent_hours
61 def spent_hours
61 @spent_hours ||= TimeEntry.sum(:hours, :include => :issue, :conditions => ["#{Issue.table_name}.fixed_version_id = ?", id]).to_f
62 @spent_hours ||= TimeEntry.sum(:hours, :include => :issue, :conditions => ["#{Issue.table_name}.fixed_version_id = ?", id]).to_f
62 end
63 end
63
64
64 def closed?
65 def closed?
65 status == 'closed'
66 status == 'closed'
66 end
67 end
67
68
68 def open?
69 def open?
69 status == 'open'
70 status == 'open'
70 end
71 end
71
72
72 # Returns true if the version is completed: due date reached and no open issues
73 # Returns true if the version is completed: due date reached and no open issues
73 def completed?
74 def completed?
74 effective_date && (effective_date <= Date.today) && (open_issues_count == 0)
75 effective_date && (effective_date <= Date.today) && (open_issues_count == 0)
75 end
76 end
76
77
77 # Returns the completion percentage of this version based on the amount of open/closed issues
78 # Returns the completion percentage of this version based on the amount of open/closed issues
78 # and the time spent on the open issues.
79 # and the time spent on the open issues.
79 def completed_pourcent
80 def completed_pourcent
80 if issues_count == 0
81 if issues_count == 0
81 0
82 0
82 elsif open_issues_count == 0
83 elsif open_issues_count == 0
83 100
84 100
84 else
85 else
85 issues_progress(false) + issues_progress(true)
86 issues_progress(false) + issues_progress(true)
86 end
87 end
87 end
88 end
88
89
89 # Returns the percentage of issues that have been marked as 'closed'.
90 # Returns the percentage of issues that have been marked as 'closed'.
90 def closed_pourcent
91 def closed_pourcent
91 if issues_count == 0
92 if issues_count == 0
92 0
93 0
93 else
94 else
94 issues_progress(false)
95 issues_progress(false)
95 end
96 end
96 end
97 end
97
98
98 # Returns true if the version is overdue: due date reached and some open issues
99 # Returns true if the version is overdue: due date reached and some open issues
99 def overdue?
100 def overdue?
100 effective_date && (effective_date < Date.today) && (open_issues_count > 0)
101 effective_date && (effective_date < Date.today) && (open_issues_count > 0)
101 end
102 end
102
103
103 # Returns assigned issues count
104 # Returns assigned issues count
104 def issues_count
105 def issues_count
105 @issue_count ||= fixed_issues.count
106 @issue_count ||= fixed_issues.count
106 end
107 end
107
108
108 # Returns the total amount of open issues for this version.
109 # Returns the total amount of open issues for this version.
109 def open_issues_count
110 def open_issues_count
110 @open_issues_count ||= Issue.count(:all, :conditions => ["fixed_version_id = ? AND is_closed = ?", self.id, false], :include => :status)
111 @open_issues_count ||= Issue.count(:all, :conditions => ["fixed_version_id = ? AND is_closed = ?", self.id, false], :include => :status)
111 end
112 end
112
113
113 # Returns the total amount of closed issues for this version.
114 # Returns the total amount of closed issues for this version.
114 def closed_issues_count
115 def closed_issues_count
115 @closed_issues_count ||= Issue.count(:all, :conditions => ["fixed_version_id = ? AND is_closed = ?", self.id, true], :include => :status)
116 @closed_issues_count ||= Issue.count(:all, :conditions => ["fixed_version_id = ? AND is_closed = ?", self.id, true], :include => :status)
116 end
117 end
117
118
118 def wiki_page
119 def wiki_page
119 if project.wiki && !wiki_page_title.blank?
120 if project.wiki && !wiki_page_title.blank?
120 @wiki_page ||= project.wiki.find_page(wiki_page_title)
121 @wiki_page ||= project.wiki.find_page(wiki_page_title)
121 end
122 end
122 @wiki_page
123 @wiki_page
123 end
124 end
124
125
125 def to_s; name end
126 def to_s; name end
126
127
127 # Versions are sorted by effective_date and name
128 # Versions are sorted by effective_date and name
128 # Those with no effective_date are at the end, sorted by name
129 # Those with no effective_date are at the end, sorted by name
129 def <=>(version)
130 def <=>(version)
130 if self.effective_date
131 if self.effective_date
131 version.effective_date ? (self.effective_date == version.effective_date ? self.name <=> version.name : self.effective_date <=> version.effective_date) : -1
132 version.effective_date ? (self.effective_date == version.effective_date ? self.name <=> version.name : self.effective_date <=> version.effective_date) : -1
132 else
133 else
133 version.effective_date ? 1 : (self.name <=> version.name)
134 version.effective_date ? 1 : (self.name <=> version.name)
134 end
135 end
135 end
136 end
136
137
137 # Returns the sharings that +user+ can set the version to
138 # Returns the sharings that +user+ can set the version to
138 def allowed_sharings(user = User.current)
139 def allowed_sharings(user = User.current)
139 VERSION_SHARINGS.select do |s|
140 VERSION_SHARINGS.select do |s|
140 if sharing == s
141 if sharing == s
141 true
142 true
142 else
143 else
143 case s
144 case s
144 when 'system'
145 when 'system'
145 # Only admin users can set a systemwide sharing
146 # Only admin users can set a systemwide sharing
146 user.admin?
147 user.admin?
147 when 'hierarchy', 'tree'
148 when 'hierarchy', 'tree'
148 # Only users allowed to manage versions of the root project can
149 # Only users allowed to manage versions of the root project can
149 # set sharing to hierarchy or tree
150 # set sharing to hierarchy or tree
150 project.nil? || user.allowed_to?(:manage_versions, project.root)
151 project.nil? || user.allowed_to?(:manage_versions, project.root)
151 else
152 else
152 true
153 true
153 end
154 end
154 end
155 end
155 end
156 end
156 end
157 end
157
158
158 private
159 private
159 def check_integrity
160 def check_integrity
160 raise "Can't delete version" if self.fixed_issues.find(:first)
161 raise "Can't delete version" if self.fixed_issues.find(:first)
161 end
162 end
162
163
163 # Update the issue's fixed versions. Used if a version's sharing changes.
164 # Update the issue's fixed versions. Used if a version's sharing changes.
164 def update_issues_from_sharing_change
165 def update_issues_from_sharing_change
165 if sharing_changed?
166 if sharing_changed?
166 if VERSION_SHARINGS.index(sharing_was).nil? ||
167 if VERSION_SHARINGS.index(sharing_was).nil? ||
167 VERSION_SHARINGS.index(sharing).nil? ||
168 VERSION_SHARINGS.index(sharing).nil? ||
168 VERSION_SHARINGS.index(sharing_was) > VERSION_SHARINGS.index(sharing)
169 VERSION_SHARINGS.index(sharing_was) > VERSION_SHARINGS.index(sharing)
169 Issue.update_versions_from_sharing_change self
170 Issue.update_versions_from_sharing_change self
170 end
171 end
171 end
172 end
172 end
173 end
173
174
174 # Returns the average estimated time of assigned issues
175 # Returns the average estimated time of assigned issues
175 # or 1 if no issue has an estimated time
176 # or 1 if no issue has an estimated time
176 # Used to weigth unestimated issues in progress calculation
177 # Used to weigth unestimated issues in progress calculation
177 def estimated_average
178 def estimated_average
178 if @estimated_average.nil?
179 if @estimated_average.nil?
179 average = fixed_issues.average(:estimated_hours).to_f
180 average = fixed_issues.average(:estimated_hours).to_f
180 if average == 0
181 if average == 0
181 average = 1
182 average = 1
182 end
183 end
183 @estimated_average = average
184 @estimated_average = average
184 end
185 end
185 @estimated_average
186 @estimated_average
186 end
187 end
187
188
188 # Returns the total progress of open or closed issues. The returned percentage takes into account
189 # Returns the total progress of open or closed issues. The returned percentage takes into account
189 # the amount of estimated time set for this version.
190 # the amount of estimated time set for this version.
190 #
191 #
191 # Examples:
192 # Examples:
192 # issues_progress(true) => returns the progress percentage for open issues.
193 # issues_progress(true) => returns the progress percentage for open issues.
193 # issues_progress(false) => returns the progress percentage for closed issues.
194 # issues_progress(false) => returns the progress percentage for closed issues.
194 def issues_progress(open)
195 def issues_progress(open)
195 @issues_progress ||= {}
196 @issues_progress ||= {}
196 @issues_progress[open] ||= begin
197 @issues_progress[open] ||= begin
197 progress = 0
198 progress = 0
198 if issues_count > 0
199 if issues_count > 0
199 ratio = open ? 'done_ratio' : 100
200 ratio = open ? 'done_ratio' : 100
200
201
201 done = fixed_issues.sum("COALESCE(estimated_hours, #{estimated_average}) * #{ratio}",
202 done = fixed_issues.sum("COALESCE(estimated_hours, #{estimated_average}) * #{ratio}",
202 :include => :status,
203 :include => :status,
203 :conditions => ["is_closed = ?", !open]).to_f
204 :conditions => ["is_closed = ?", !open]).to_f
204 progress = done / (estimated_average * issues_count)
205 progress = done / (estimated_average * issues_count)
205 end
206 end
206 progress
207 progress
207 end
208 end
208 end
209 end
209 end
210 end
@@ -1,156 +1,184
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2008 Jean-Philippe Lang
2 # Copyright (C) 2006-2008 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.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require File.dirname(__FILE__) + '/../test_helper'
18 require File.dirname(__FILE__) + '/../test_helper'
19
19
20 class VersionTest < ActiveSupport::TestCase
20 class VersionTest < ActiveSupport::TestCase
21 fixtures :projects, :users, :issues, :issue_statuses, :trackers, :enumerations, :versions
21 fixtures :projects, :users, :issues, :issue_statuses, :trackers, :enumerations, :versions
22
22
23 def setup
23 def setup
24 end
24 end
25
25
26 def test_create
26 def test_create
27 v = Version.new(:project => Project.find(1), :name => '1.1', :effective_date => '2011-03-25')
27 v = Version.new(:project => Project.find(1), :name => '1.1', :effective_date => '2011-03-25')
28 assert v.save
28 assert v.save
29 assert_equal 'open', v.status
29 assert_equal 'open', v.status
30 end
30 end
31
31
32 def test_invalid_effective_date_validation
32 def test_invalid_effective_date_validation
33 v = Version.new(:project => Project.find(1), :name => '1.1', :effective_date => '99999-01-01')
33 v = Version.new(:project => Project.find(1), :name => '1.1', :effective_date => '99999-01-01')
34 assert !v.save
34 assert !v.save
35 assert_equal I18n.translate('activerecord.errors.messages.not_a_date'), v.errors.on(:effective_date)
35 assert_equal I18n.translate('activerecord.errors.messages.not_a_date'), v.errors.on(:effective_date)
36 end
36 end
37
37
38 def test_progress_should_be_0_with_no_assigned_issues
38 def test_progress_should_be_0_with_no_assigned_issues
39 project = Project.find(1)
39 project = Project.find(1)
40 v = Version.create!(:project => project, :name => 'Progress')
40 v = Version.create!(:project => project, :name => 'Progress')
41 assert_equal 0, v.completed_pourcent
41 assert_equal 0, v.completed_pourcent
42 assert_equal 0, v.closed_pourcent
42 assert_equal 0, v.closed_pourcent
43 end
43 end
44
44
45 def test_progress_should_be_0_with_unbegun_assigned_issues
45 def test_progress_should_be_0_with_unbegun_assigned_issues
46 project = Project.find(1)
46 project = Project.find(1)
47 v = Version.create!(:project => project, :name => 'Progress')
47 v = Version.create!(:project => project, :name => 'Progress')
48 add_issue(v)
48 add_issue(v)
49 add_issue(v, :done_ratio => 0)
49 add_issue(v, :done_ratio => 0)
50 assert_progress_equal 0, v.completed_pourcent
50 assert_progress_equal 0, v.completed_pourcent
51 assert_progress_equal 0, v.closed_pourcent
51 assert_progress_equal 0, v.closed_pourcent
52 end
52 end
53
53
54 def test_progress_should_be_100_with_closed_assigned_issues
54 def test_progress_should_be_100_with_closed_assigned_issues
55 project = Project.find(1)
55 project = Project.find(1)
56 status = IssueStatus.find(:first, :conditions => {:is_closed => true})
56 status = IssueStatus.find(:first, :conditions => {:is_closed => true})
57 v = Version.create!(:project => project, :name => 'Progress')
57 v = Version.create!(:project => project, :name => 'Progress')
58 add_issue(v, :status => status)
58 add_issue(v, :status => status)
59 add_issue(v, :status => status, :done_ratio => 20)
59 add_issue(v, :status => status, :done_ratio => 20)
60 add_issue(v, :status => status, :done_ratio => 70, :estimated_hours => 25)
60 add_issue(v, :status => status, :done_ratio => 70, :estimated_hours => 25)
61 add_issue(v, :status => status, :estimated_hours => 15)
61 add_issue(v, :status => status, :estimated_hours => 15)
62 assert_progress_equal 100.0, v.completed_pourcent
62 assert_progress_equal 100.0, v.completed_pourcent
63 assert_progress_equal 100.0, v.closed_pourcent
63 assert_progress_equal 100.0, v.closed_pourcent
64 end
64 end
65
65
66 def test_progress_should_consider_done_ratio_of_open_assigned_issues
66 def test_progress_should_consider_done_ratio_of_open_assigned_issues
67 project = Project.find(1)
67 project = Project.find(1)
68 v = Version.create!(:project => project, :name => 'Progress')
68 v = Version.create!(:project => project, :name => 'Progress')
69 add_issue(v)
69 add_issue(v)
70 add_issue(v, :done_ratio => 20)
70 add_issue(v, :done_ratio => 20)
71 add_issue(v, :done_ratio => 70)
71 add_issue(v, :done_ratio => 70)
72 assert_progress_equal (0.0 + 20.0 + 70.0)/3, v.completed_pourcent
72 assert_progress_equal (0.0 + 20.0 + 70.0)/3, v.completed_pourcent
73 assert_progress_equal 0, v.closed_pourcent
73 assert_progress_equal 0, v.closed_pourcent
74 end
74 end
75
75
76 def test_progress_should_consider_closed_issues_as_completed
76 def test_progress_should_consider_closed_issues_as_completed
77 project = Project.find(1)
77 project = Project.find(1)
78 v = Version.create!(:project => project, :name => 'Progress')
78 v = Version.create!(:project => project, :name => 'Progress')
79 add_issue(v)
79 add_issue(v)
80 add_issue(v, :done_ratio => 20)
80 add_issue(v, :done_ratio => 20)
81 add_issue(v, :status => IssueStatus.find(:first, :conditions => {:is_closed => true}))
81 add_issue(v, :status => IssueStatus.find(:first, :conditions => {:is_closed => true}))
82 assert_progress_equal (0.0 + 20.0 + 100.0)/3, v.completed_pourcent
82 assert_progress_equal (0.0 + 20.0 + 100.0)/3, v.completed_pourcent
83 assert_progress_equal (100.0)/3, v.closed_pourcent
83 assert_progress_equal (100.0)/3, v.closed_pourcent
84 end
84 end
85
85
86 def test_progress_should_consider_estimated_hours_to_weigth_issues
86 def test_progress_should_consider_estimated_hours_to_weigth_issues
87 project = Project.find(1)
87 project = Project.find(1)
88 v = Version.create!(:project => project, :name => 'Progress')
88 v = Version.create!(:project => project, :name => 'Progress')
89 add_issue(v, :estimated_hours => 10)
89 add_issue(v, :estimated_hours => 10)
90 add_issue(v, :estimated_hours => 20, :done_ratio => 30)
90 add_issue(v, :estimated_hours => 20, :done_ratio => 30)
91 add_issue(v, :estimated_hours => 40, :done_ratio => 10)
91 add_issue(v, :estimated_hours => 40, :done_ratio => 10)
92 add_issue(v, :estimated_hours => 25, :status => IssueStatus.find(:first, :conditions => {:is_closed => true}))
92 add_issue(v, :estimated_hours => 25, :status => IssueStatus.find(:first, :conditions => {:is_closed => true}))
93 assert_progress_equal (10.0*0 + 20.0*0.3 + 40*0.1 + 25.0*1)/95.0*100, v.completed_pourcent
93 assert_progress_equal (10.0*0 + 20.0*0.3 + 40*0.1 + 25.0*1)/95.0*100, v.completed_pourcent
94 assert_progress_equal 25.0/95.0*100, v.closed_pourcent
94 assert_progress_equal 25.0/95.0*100, v.closed_pourcent
95 end
95 end
96
96
97 def test_progress_should_consider_average_estimated_hours_to_weigth_unestimated_issues
97 def test_progress_should_consider_average_estimated_hours_to_weigth_unestimated_issues
98 project = Project.find(1)
98 project = Project.find(1)
99 v = Version.create!(:project => project, :name => 'Progress')
99 v = Version.create!(:project => project, :name => 'Progress')
100 add_issue(v, :done_ratio => 20)
100 add_issue(v, :done_ratio => 20)
101 add_issue(v, :status => IssueStatus.find(:first, :conditions => {:is_closed => true}))
101 add_issue(v, :status => IssueStatus.find(:first, :conditions => {:is_closed => true}))
102 add_issue(v, :estimated_hours => 10, :done_ratio => 30)
102 add_issue(v, :estimated_hours => 10, :done_ratio => 30)
103 add_issue(v, :estimated_hours => 40, :done_ratio => 10)
103 add_issue(v, :estimated_hours => 40, :done_ratio => 10)
104 assert_progress_equal (25.0*0.2 + 25.0*1 + 10.0*0.3 + 40.0*0.1)/100.0*100, v.completed_pourcent
104 assert_progress_equal (25.0*0.2 + 25.0*1 + 10.0*0.3 + 40.0*0.1)/100.0*100, v.completed_pourcent
105 assert_progress_equal 25.0/100.0*100, v.closed_pourcent
105 assert_progress_equal 25.0/100.0*100, v.closed_pourcent
106 end
106 end
107
108 context "#estimated_hours" do
109 setup do
110 @version = Version.create!(:project_id => 1, :name => '#estimated_hours')
111 end
112
113 should "return 0 with no assigned issues" do
114 assert_equal 0, @version.estimated_hours
115 end
116
117 should "return 0 with no estimated hours" do
118 add_issue(@version)
119 assert_equal 0, @version.estimated_hours
120 end
121
122 should "return the sum of estimated hours" do
123 add_issue(@version, :estimated_hours => 2.5)
124 add_issue(@version, :estimated_hours => 5)
125 assert_equal 7.5, @version.estimated_hours
126 end
127
128 should "return the sum of leaves estimated hours" do
129 parent = add_issue(@version)
130 add_issue(@version, :estimated_hours => 2.5, :parent_issue_id => parent.id)
131 add_issue(@version, :estimated_hours => 5, :parent_issue_id => parent.id)
132 assert_equal 7.5, @version.estimated_hours
133 end
134 end
107
135
108 test "should update all issue's fixed_version associations in case the hierarchy changed XXX" do
136 test "should update all issue's fixed_version associations in case the hierarchy changed XXX" do
109 User.current = User.find(1) # Need the admin's permissions
137 User.current = User.find(1) # Need the admin's permissions
110
138
111 @version = Version.find(7)
139 @version = Version.find(7)
112 # Separate hierarchy
140 # Separate hierarchy
113 project_1_issue = Issue.find(1)
141 project_1_issue = Issue.find(1)
114 project_1_issue.fixed_version = @version
142 project_1_issue.fixed_version = @version
115 assert project_1_issue.save, project_1_issue.errors.full_messages
143 assert project_1_issue.save, project_1_issue.errors.full_messages
116
144
117 project_5_issue = Issue.find(6)
145 project_5_issue = Issue.find(6)
118 project_5_issue.fixed_version = @version
146 project_5_issue.fixed_version = @version
119 assert project_5_issue.save
147 assert project_5_issue.save
120
148
121 # Project
149 # Project
122 project_2_issue = Issue.find(4)
150 project_2_issue = Issue.find(4)
123 project_2_issue.fixed_version = @version
151 project_2_issue.fixed_version = @version
124 assert project_2_issue.save
152 assert project_2_issue.save
125
153
126 # Update the sharing
154 # Update the sharing
127 @version.sharing = 'none'
155 @version.sharing = 'none'
128 assert @version.save
156 assert @version.save
129
157
130 # Project 1 now out of the shared scope
158 # Project 1 now out of the shared scope
131 project_1_issue.reload
159 project_1_issue.reload
132 assert_equal nil, project_1_issue.fixed_version, "Fixed version is still set after changing the Version's sharing"
160 assert_equal nil, project_1_issue.fixed_version, "Fixed version is still set after changing the Version's sharing"
133
161
134 # Project 5 now out of the shared scope
162 # Project 5 now out of the shared scope
135 project_5_issue.reload
163 project_5_issue.reload
136 assert_equal nil, project_5_issue.fixed_version, "Fixed version is still set after changing the Version's sharing"
164 assert_equal nil, project_5_issue.fixed_version, "Fixed version is still set after changing the Version's sharing"
137
165
138 # Project 2 issue remains
166 # Project 2 issue remains
139 project_2_issue.reload
167 project_2_issue.reload
140 assert_equal @version, project_2_issue.fixed_version
168 assert_equal @version, project_2_issue.fixed_version
141 end
169 end
142
170
143 private
171 private
144
172
145 def add_issue(version, attributes={})
173 def add_issue(version, attributes={})
146 Issue.create!({:project => version.project,
174 Issue.create!({:project => version.project,
147 :fixed_version => version,
175 :fixed_version => version,
148 :subject => 'Test',
176 :subject => 'Test',
149 :author => User.find(:first),
177 :author => User.find(:first),
150 :tracker => version.project.trackers.find(:first)}.merge(attributes))
178 :tracker => version.project.trackers.find(:first)}.merge(attributes))
151 end
179 end
152
180
153 def assert_progress_equal(expected_float, actual_float, message="")
181 def assert_progress_equal(expected_float, actual_float, message="")
154 assert_in_delta(expected_float, actual_float, 0.000001, message="")
182 assert_in_delta(expected_float, actual_float, 0.000001, message="")
155 end
183 end
156 end
184 end
General Comments 0
You need to be logged in to leave comments. Login now