##// END OF EJS Templates
Merged r14164 (#19538)....
Jean-Philippe Lang -
r13818:fea9353152f4
parent child
Show More
@@ -1,289 +1,292
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2015 Jean-Philippe Lang
2 # Copyright (C) 2006-2015 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 Changeset < ActiveRecord::Base
18 class Changeset < ActiveRecord::Base
19 belongs_to :repository
19 belongs_to :repository
20 belongs_to :user
20 belongs_to :user
21 has_many :filechanges, :class_name => 'Change', :dependent => :delete_all
21 has_many :filechanges, :class_name => 'Change', :dependent => :delete_all
22 has_and_belongs_to_many :issues
22 has_and_belongs_to_many :issues
23 has_and_belongs_to_many :parents,
23 has_and_belongs_to_many :parents,
24 :class_name => "Changeset",
24 :class_name => "Changeset",
25 :join_table => "#{table_name_prefix}changeset_parents#{table_name_suffix}",
25 :join_table => "#{table_name_prefix}changeset_parents#{table_name_suffix}",
26 :association_foreign_key => 'parent_id', :foreign_key => 'changeset_id'
26 :association_foreign_key => 'parent_id', :foreign_key => 'changeset_id'
27 has_and_belongs_to_many :children,
27 has_and_belongs_to_many :children,
28 :class_name => "Changeset",
28 :class_name => "Changeset",
29 :join_table => "#{table_name_prefix}changeset_parents#{table_name_suffix}",
29 :join_table => "#{table_name_prefix}changeset_parents#{table_name_suffix}",
30 :association_foreign_key => 'changeset_id', :foreign_key => 'parent_id'
30 :association_foreign_key => 'changeset_id', :foreign_key => 'parent_id'
31
31
32 acts_as_event :title => Proc.new {|o| o.title},
32 acts_as_event :title => Proc.new {|o| o.title},
33 :description => :long_comments,
33 :description => :long_comments,
34 :datetime => :committed_on,
34 :datetime => :committed_on,
35 :url => Proc.new {|o| {:controller => 'repositories', :action => 'revision', :id => o.repository.project, :repository_id => o.repository.identifier_param, :rev => o.identifier}}
35 :url => Proc.new {|o| {:controller => 'repositories', :action => 'revision', :id => o.repository.project, :repository_id => o.repository.identifier_param, :rev => o.identifier}}
36
36
37 acts_as_searchable :columns => 'comments',
37 acts_as_searchable :columns => 'comments',
38 :include => {:repository => :project},
38 :include => {:repository => :project},
39 :project_key => "#{Repository.table_name}.project_id",
39 :project_key => "#{Repository.table_name}.project_id",
40 :date_column => 'committed_on'
40 :date_column => 'committed_on'
41
41
42 acts_as_activity_provider :timestamp => "#{table_name}.committed_on",
42 acts_as_activity_provider :timestamp => "#{table_name}.committed_on",
43 :author_key => :user_id,
43 :author_key => :user_id,
44 :find_options => {:include => [:user, {:repository => :project}]}
44 :find_options => {:include => [:user, {:repository => :project}]}
45
45
46 validates_presence_of :repository_id, :revision, :committed_on, :commit_date
46 validates_presence_of :repository_id, :revision, :committed_on, :commit_date
47 validates_uniqueness_of :revision, :scope => :repository_id
47 validates_uniqueness_of :revision, :scope => :repository_id
48 validates_uniqueness_of :scmid, :scope => :repository_id, :allow_nil => true
48 validates_uniqueness_of :scmid, :scope => :repository_id, :allow_nil => true
49
49
50 scope :visible, lambda {|*args|
50 scope :visible, lambda {|*args|
51 includes(:repository => :project).where(Project.allowed_to_condition(args.shift || User.current, :view_changesets, *args))
51 includes(:repository => :project).where(Project.allowed_to_condition(args.shift || User.current, :view_changesets, *args))
52 }
52 }
53
53
54 after_create :scan_for_issues
54 after_create :scan_for_issues
55 before_create :before_create_cs
55 before_create :before_create_cs
56
56
57 def revision=(r)
57 def revision=(r)
58 write_attribute :revision, (r.nil? ? nil : r.to_s)
58 write_attribute :revision, (r.nil? ? nil : r.to_s)
59 end
59 end
60
60
61 # Returns the identifier of this changeset; depending on repository backends
61 # Returns the identifier of this changeset; depending on repository backends
62 def identifier
62 def identifier
63 if repository.class.respond_to? :changeset_identifier
63 if repository.class.respond_to? :changeset_identifier
64 repository.class.changeset_identifier self
64 repository.class.changeset_identifier self
65 else
65 else
66 revision.to_s
66 revision.to_s
67 end
67 end
68 end
68 end
69
69
70 def committed_on=(date)
70 def committed_on=(date)
71 self.commit_date = date
71 self.commit_date = date
72 super
72 super
73 end
73 end
74
74
75 # Returns the readable identifier
75 # Returns the readable identifier
76 def format_identifier
76 def format_identifier
77 if repository.class.respond_to? :format_changeset_identifier
77 if repository.class.respond_to? :format_changeset_identifier
78 repository.class.format_changeset_identifier self
78 repository.class.format_changeset_identifier self
79 else
79 else
80 identifier
80 identifier
81 end
81 end
82 end
82 end
83
83
84 def project
84 def project
85 repository.project
85 repository.project
86 end
86 end
87
87
88 def author
88 def author
89 user || committer.to_s.split('<').first
89 user || committer.to_s.split('<').first
90 end
90 end
91
91
92 def before_create_cs
92 def before_create_cs
93 self.committer = self.class.to_utf8(self.committer, repository.repo_log_encoding)
93 self.committer = self.class.to_utf8(self.committer, repository.repo_log_encoding)
94 self.comments = self.class.normalize_comments(
94 self.comments = self.class.normalize_comments(
95 self.comments, repository.repo_log_encoding)
95 self.comments, repository.repo_log_encoding)
96 self.user = repository.find_committer_user(self.committer)
96 self.user = repository.find_committer_user(self.committer)
97 end
97 end
98
98
99 def scan_for_issues
99 def scan_for_issues
100 scan_comment_for_issue_ids
100 scan_comment_for_issue_ids
101 end
101 end
102
102
103 TIMELOG_RE = /
103 TIMELOG_RE = /
104 (
104 (
105 ((\d+)(h|hours?))((\d+)(m|min)?)?
105 ((\d+)(h|hours?))((\d+)(m|min)?)?
106 |
106 |
107 ((\d+)(h|hours?|m|min))
107 ((\d+)(h|hours?|m|min))
108 |
108 |
109 (\d+):(\d+)
109 (\d+):(\d+)
110 |
110 |
111 (\d+([\.,]\d+)?)h?
111 (\d+([\.,]\d+)?)h?
112 )
112 )
113 /x
113 /x
114
114
115 def scan_comment_for_issue_ids
115 def scan_comment_for_issue_ids
116 return if comments.blank?
116 return if comments.blank?
117 # keywords used to reference issues
117 # keywords used to reference issues
118 ref_keywords = Setting.commit_ref_keywords.downcase.split(",").collect(&:strip)
118 ref_keywords = Setting.commit_ref_keywords.downcase.split(",").collect(&:strip)
119 ref_keywords_any = ref_keywords.delete('*')
119 ref_keywords_any = ref_keywords.delete('*')
120 # keywords used to fix issues
120 # keywords used to fix issues
121 fix_keywords = Setting.commit_update_keywords_array.map {|r| r['keywords']}.flatten.compact
121 fix_keywords = Setting.commit_update_keywords_array.map {|r| r['keywords']}.flatten.compact
122
122
123 kw_regexp = (ref_keywords + fix_keywords).collect{|kw| Regexp.escape(kw)}.join("|")
123 kw_regexp = (ref_keywords + fix_keywords).collect{|kw| Regexp.escape(kw)}.join("|")
124
124
125 referenced_issues = []
125 referenced_issues = []
126
126
127 comments.scan(/([\s\(\[,-]|^)((#{kw_regexp})[\s:]+)?(#\d+(\s+@#{TIMELOG_RE})?([\s,;&]+#\d+(\s+@#{TIMELOG_RE})?)*)(?=[[:punct:]]|\s|<|$)/i) do |match|
127 comments.scan(/([\s\(\[,-]|^)((#{kw_regexp})[\s:]+)?(#\d+(\s+@#{TIMELOG_RE})?([\s,;&]+#\d+(\s+@#{TIMELOG_RE})?)*)(?=[[:punct:]]|\s|<|$)/i) do |match|
128 action, refs = match[2].to_s.downcase, match[3]
128 action, refs = match[2].to_s.downcase, match[3]
129 next unless action.present? || ref_keywords_any
129 next unless action.present? || ref_keywords_any
130
130
131 refs.scan(/#(\d+)(\s+@#{TIMELOG_RE})?/).each do |m|
131 refs.scan(/#(\d+)(\s+@#{TIMELOG_RE})?/).each do |m|
132 issue, hours = find_referenced_issue_by_id(m[0].to_i), m[2]
132 issue, hours = find_referenced_issue_by_id(m[0].to_i), m[2]
133 if issue && !issue_linked_to_same_commit?(issue)
133 if issue && !issue_linked_to_same_commit?(issue)
134 referenced_issues << issue
134 referenced_issues << issue
135 # Don't update issues or log time when importing old commits
135 # Don't update issues or log time when importing old commits
136 unless repository.created_on && committed_on && committed_on < repository.created_on
136 unless repository.created_on && committed_on && committed_on < repository.created_on
137 fix_issue(issue, action) if fix_keywords.include?(action)
137 fix_issue(issue, action) if fix_keywords.include?(action)
138 log_time(issue, hours) if hours && Setting.commit_logtime_enabled?
138 log_time(issue, hours) if hours && Setting.commit_logtime_enabled?
139 end
139 end
140 end
140 end
141 end
141 end
142 end
142 end
143
143
144 referenced_issues.uniq!
144 referenced_issues.uniq!
145 self.issues = referenced_issues unless referenced_issues.empty?
145 self.issues = referenced_issues unless referenced_issues.empty?
146 end
146 end
147
147
148 def short_comments
148 def short_comments
149 @short_comments || split_comments.first
149 @short_comments || split_comments.first
150 end
150 end
151
151
152 def long_comments
152 def long_comments
153 @long_comments || split_comments.last
153 @long_comments || split_comments.last
154 end
154 end
155
155
156 def text_tag(ref_project=nil)
156 def text_tag(ref_project=nil)
157 repo = ""
157 repo = ""
158 if repository && repository.identifier.present?
158 if repository && repository.identifier.present?
159 repo = "#{repository.identifier}|"
159 repo = "#{repository.identifier}|"
160 end
160 end
161 tag = if scmid?
161 tag = if scmid?
162 "commit:#{repo}#{scmid}"
162 "commit:#{repo}#{scmid}"
163 else
163 else
164 "#{repo}r#{revision}"
164 "#{repo}r#{revision}"
165 end
165 end
166 if ref_project && project && ref_project != project
166 if ref_project && project && ref_project != project
167 tag = "#{project.identifier}:#{tag}"
167 tag = "#{project.identifier}:#{tag}"
168 end
168 end
169 tag
169 tag
170 end
170 end
171
171
172 # Returns the title used for the changeset in the activity/search results
172 # Returns the title used for the changeset in the activity/search results
173 def title
173 def title
174 repo = (repository && repository.identifier.present?) ? " (#{repository.identifier})" : ''
174 repo = (repository && repository.identifier.present?) ? " (#{repository.identifier})" : ''
175 comm = short_comments.blank? ? '' : (': ' + short_comments)
175 comm = short_comments.blank? ? '' : (': ' + short_comments)
176 "#{l(:label_revision)} #{format_identifier}#{repo}#{comm}"
176 "#{l(:label_revision)} #{format_identifier}#{repo}#{comm}"
177 end
177 end
178
178
179 # Returns the previous changeset
179 # Returns the previous changeset
180 def previous
180 def previous
181 @previous ||= Changeset.where(["id < ? AND repository_id = ?", id, repository_id]).order('id DESC').first
181 @previous ||= Changeset.where(["id < ? AND repository_id = ?", id, repository_id]).order('id DESC').first
182 end
182 end
183
183
184 # Returns the next changeset
184 # Returns the next changeset
185 def next
185 def next
186 @next ||= Changeset.where(["id > ? AND repository_id = ?", id, repository_id]).order('id ASC').first
186 @next ||= Changeset.where(["id > ? AND repository_id = ?", id, repository_id]).order('id ASC').first
187 end
187 end
188
188
189 # Creates a new Change from it's common parameters
189 # Creates a new Change from it's common parameters
190 def create_change(change)
190 def create_change(change)
191 Change.create(:changeset => self,
191 Change.create(:changeset => self,
192 :action => change[:action],
192 :action => change[:action],
193 :path => change[:path],
193 :path => change[:path],
194 :from_path => change[:from_path],
194 :from_path => change[:from_path],
195 :from_revision => change[:from_revision])
195 :from_revision => change[:from_revision])
196 end
196 end
197
197
198 # Finds an issue that can be referenced by the commit message
198 # Finds an issue that can be referenced by the commit message
199 def find_referenced_issue_by_id(id)
199 def find_referenced_issue_by_id(id)
200 return nil if id.blank?
200 return nil if id.blank?
201 issue = Issue.includes(:project).where(:id => id.to_i).first
201 issue = Issue.includes(:project).where(:id => id.to_i).first
202 if Setting.commit_cross_project_ref?
202 if Setting.commit_cross_project_ref?
203 # all issues can be referenced/fixed
203 # all issues can be referenced/fixed
204 elsif issue
204 elsif issue
205 # issue that belong to the repository project, a subproject or a parent project only
205 # issue that belong to the repository project, a subproject or a parent project only
206 unless issue.project &&
206 unless issue.project &&
207 (project == issue.project || project.is_ancestor_of?(issue.project) ||
207 (project == issue.project || project.is_ancestor_of?(issue.project) ||
208 project.is_descendant_of?(issue.project))
208 project.is_descendant_of?(issue.project))
209 issue = nil
209 issue = nil
210 end
210 end
211 end
211 end
212 issue
212 issue
213 end
213 end
214
214
215 private
215 private
216
216
217 # Returns true if the issue is already linked to the same commit
217 # Returns true if the issue is already linked to the same commit
218 # from a different repository
218 # from a different repository
219 def issue_linked_to_same_commit?(issue)
219 def issue_linked_to_same_commit?(issue)
220 repository.same_commits_in_scope(issue.changesets, self).any?
220 repository.same_commits_in_scope(issue.changesets, self).any?
221 end
221 end
222
222
223 # Updates the +issue+ according to +action+
223 # Updates the +issue+ according to +action+
224 def fix_issue(issue, action)
224 def fix_issue(issue, action)
225 # the issue may have been updated by the closure of another one (eg. duplicate)
225 # the issue may have been updated by the closure of another one (eg. duplicate)
226 issue.reload
226 issue.reload
227 # don't change the status is the issue is closed
227 # don't change the status is the issue is closed
228 return if issue.status && issue.status.is_closed?
228 return if issue.status && issue.status.is_closed?
229
229
230 journal = issue.init_journal(user || User.anonymous,
230 journal = issue.init_journal(user || User.anonymous,
231 ll(Setting.default_language,
231 ll(Setting.default_language,
232 :text_status_changed_by_changeset,
232 :text_status_changed_by_changeset,
233 text_tag(issue.project)))
233 text_tag(issue.project)))
234 rule = Setting.commit_update_keywords_array.detect do |rule|
234 rule = Setting.commit_update_keywords_array.detect do |rule|
235 rule['keywords'].include?(action) &&
235 rule['keywords'].include?(action) &&
236 (rule['if_tracker_id'].blank? || rule['if_tracker_id'] == issue.tracker_id.to_s)
236 (rule['if_tracker_id'].blank? || rule['if_tracker_id'] == issue.tracker_id.to_s)
237 end
237 end
238 if rule
238 if rule
239 issue.assign_attributes rule.slice(*Issue.attribute_names)
239 issue.assign_attributes rule.slice(*Issue.attribute_names)
240 end
240 end
241 Redmine::Hook.call_hook(:model_changeset_scan_commit_for_issue_ids_pre_issue_update,
241 Redmine::Hook.call_hook(:model_changeset_scan_commit_for_issue_ids_pre_issue_update,
242 { :changeset => self, :issue => issue, :action => action })
242 { :changeset => self, :issue => issue, :action => action })
243 unless issue.save
243
244 logger.warn("Issue ##{issue.id} could not be saved by changeset #{id}: #{issue.errors.full_messages}") if logger
244 if issue.changes.any?
245 unless issue.save
246 logger.warn("Issue ##{issue.id} could not be saved by changeset #{id}: #{issue.errors.full_messages}") if logger
247 end
245 end
248 end
246 issue
249 issue
247 end
250 end
248
251
249 def log_time(issue, hours)
252 def log_time(issue, hours)
250 time_entry = TimeEntry.new(
253 time_entry = TimeEntry.new(
251 :user => user,
254 :user => user,
252 :hours => hours,
255 :hours => hours,
253 :issue => issue,
256 :issue => issue,
254 :spent_on => commit_date,
257 :spent_on => commit_date,
255 :comments => l(:text_time_logged_by_changeset, :value => text_tag(issue.project),
258 :comments => l(:text_time_logged_by_changeset, :value => text_tag(issue.project),
256 :locale => Setting.default_language)
259 :locale => Setting.default_language)
257 )
260 )
258 time_entry.activity = log_time_activity unless log_time_activity.nil?
261 time_entry.activity = log_time_activity unless log_time_activity.nil?
259
262
260 unless time_entry.save
263 unless time_entry.save
261 logger.warn("TimeEntry could not be created by changeset #{id}: #{time_entry.errors.full_messages}") if logger
264 logger.warn("TimeEntry could not be created by changeset #{id}: #{time_entry.errors.full_messages}") if logger
262 end
265 end
263 time_entry
266 time_entry
264 end
267 end
265
268
266 def log_time_activity
269 def log_time_activity
267 if Setting.commit_logtime_activity_id.to_i > 0
270 if Setting.commit_logtime_activity_id.to_i > 0
268 TimeEntryActivity.find_by_id(Setting.commit_logtime_activity_id.to_i)
271 TimeEntryActivity.find_by_id(Setting.commit_logtime_activity_id.to_i)
269 end
272 end
270 end
273 end
271
274
272 def split_comments
275 def split_comments
273 comments =~ /\A(.+?)\r?\n(.*)$/m
276 comments =~ /\A(.+?)\r?\n(.*)$/m
274 @short_comments = $1 || comments
277 @short_comments = $1 || comments
275 @long_comments = $2.to_s.strip
278 @long_comments = $2.to_s.strip
276 return @short_comments, @long_comments
279 return @short_comments, @long_comments
277 end
280 end
278
281
279 public
282 public
280
283
281 # Strips and reencodes a commit log before insertion into the database
284 # Strips and reencodes a commit log before insertion into the database
282 def self.normalize_comments(str, encoding)
285 def self.normalize_comments(str, encoding)
283 Changeset.to_utf8(str.to_s.strip, encoding)
286 Changeset.to_utf8(str.to_s.strip, encoding)
284 end
287 end
285
288
286 def self.to_utf8(str, encoding)
289 def self.to_utf8(str, encoding)
287 Redmine::CodesetUtil.to_utf8(str, encoding)
290 Redmine::CodesetUtil.to_utf8(str, encoding)
288 end
291 end
289 end
292 end
@@ -1,566 +1,590
1 # encoding: utf-8
1 # encoding: utf-8
2 #
2 #
3 # Redmine - project management software
3 # Redmine - project management software
4 # Copyright (C) 2006-2015 Jean-Philippe Lang
4 # Copyright (C) 2006-2015 Jean-Philippe Lang
5 #
5 #
6 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; either version 2
8 # as published by the Free Software Foundation; either version 2
9 # of the License, or (at your option) any later version.
9 # of the License, or (at your option) any later version.
10 #
10 #
11 # This program is distributed in the hope that it will be useful,
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
14 # GNU General Public License for more details.
15 #
15 #
16 # You should have received a copy of the GNU General Public License
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19
19
20 require File.expand_path('../../test_helper', __FILE__)
20 require File.expand_path('../../test_helper', __FILE__)
21
21
22 class ChangesetTest < ActiveSupport::TestCase
22 class ChangesetTest < ActiveSupport::TestCase
23 fixtures :projects, :repositories,
23 fixtures :projects, :repositories,
24 :issues, :issue_statuses, :issue_categories,
24 :issues, :issue_statuses, :issue_categories,
25 :changesets, :changes,
25 :changesets, :changes,
26 :enumerations,
26 :enumerations,
27 :custom_fields, :custom_values,
27 :custom_fields, :custom_values,
28 :users, :members, :member_roles,
28 :users, :members, :member_roles,
29 :trackers, :projects_trackers,
29 :trackers, :projects_trackers,
30 :enabled_modules, :roles
30 :enabled_modules, :roles
31
31
32 def test_ref_keywords_any
32 def test_ref_keywords_any
33 ActionMailer::Base.deliveries.clear
33 ActionMailer::Base.deliveries.clear
34 Setting.commit_ref_keywords = '*'
34 Setting.commit_ref_keywords = '*'
35 Setting.commit_update_keywords = [{'keywords' => 'fixes , closes', 'status_id' => '5', 'done_ratio' => '90'}]
35 Setting.commit_update_keywords = [{'keywords' => 'fixes , closes', 'status_id' => '5', 'done_ratio' => '90'}]
36
36
37 c = Changeset.new(:repository => Project.find(1).repository,
37 c = Changeset.new(:repository => Project.find(1).repository,
38 :committed_on => Time.now,
38 :committed_on => Time.now,
39 :comments => 'New commit (#2). Fixes #1',
39 :comments => 'New commit (#2). Fixes #1',
40 :revision => '12345')
40 :revision => '12345')
41 assert c.save
41 assert c.save
42 assert_equal [1, 2], c.issue_ids.sort
42 assert_equal [1, 2], c.issue_ids.sort
43 fixed = Issue.find(1)
43 fixed = Issue.find(1)
44 assert fixed.closed?
44 assert fixed.closed?
45 assert_equal 90, fixed.done_ratio
45 assert_equal 90, fixed.done_ratio
46 assert_equal 1, ActionMailer::Base.deliveries.size
46 assert_equal 1, ActionMailer::Base.deliveries.size
47 end
47 end
48
48
49 def test_ref_keywords
49 def test_ref_keywords
50 Setting.commit_ref_keywords = 'refs'
50 Setting.commit_ref_keywords = 'refs'
51 Setting.commit_update_keywords = ''
51 Setting.commit_update_keywords = ''
52 c = Changeset.new(:repository => Project.find(1).repository,
52 c = Changeset.new(:repository => Project.find(1).repository,
53 :committed_on => Time.now,
53 :committed_on => Time.now,
54 :comments => 'Ignores #2. Refs #1',
54 :comments => 'Ignores #2. Refs #1',
55 :revision => '12345')
55 :revision => '12345')
56 assert c.save
56 assert c.save
57 assert_equal [1], c.issue_ids.sort
57 assert_equal [1], c.issue_ids.sort
58 end
58 end
59
59
60 def test_ref_keywords_any_only
60 def test_ref_keywords_any_only
61 Setting.commit_ref_keywords = '*'
61 Setting.commit_ref_keywords = '*'
62 Setting.commit_update_keywords = ''
62 Setting.commit_update_keywords = ''
63 c = Changeset.new(:repository => Project.find(1).repository,
63 c = Changeset.new(:repository => Project.find(1).repository,
64 :committed_on => Time.now,
64 :committed_on => Time.now,
65 :comments => 'Ignores #2. Refs #1',
65 :comments => 'Ignores #2. Refs #1',
66 :revision => '12345')
66 :revision => '12345')
67 assert c.save
67 assert c.save
68 assert_equal [1, 2], c.issue_ids.sort
68 assert_equal [1, 2], c.issue_ids.sort
69 end
69 end
70
70
71 def test_ref_keywords_any_with_timelog
71 def test_ref_keywords_any_with_timelog
72 Setting.commit_ref_keywords = '*'
72 Setting.commit_ref_keywords = '*'
73 Setting.commit_logtime_enabled = '1'
73 Setting.commit_logtime_enabled = '1'
74
74
75 {
75 {
76 '2' => 2.0,
76 '2' => 2.0,
77 '2h' => 2.0,
77 '2h' => 2.0,
78 '2hours' => 2.0,
78 '2hours' => 2.0,
79 '15m' => 0.25,
79 '15m' => 0.25,
80 '15min' => 0.25,
80 '15min' => 0.25,
81 '3h15' => 3.25,
81 '3h15' => 3.25,
82 '3h15m' => 3.25,
82 '3h15m' => 3.25,
83 '3h15min' => 3.25,
83 '3h15min' => 3.25,
84 '3:15' => 3.25,
84 '3:15' => 3.25,
85 '3.25' => 3.25,
85 '3.25' => 3.25,
86 '3.25h' => 3.25,
86 '3.25h' => 3.25,
87 '3,25' => 3.25,
87 '3,25' => 3.25,
88 '3,25h' => 3.25,
88 '3,25h' => 3.25,
89 }.each do |syntax, expected_hours|
89 }.each do |syntax, expected_hours|
90 c = Changeset.new(:repository => Project.find(1).repository,
90 c = Changeset.new(:repository => Project.find(1).repository,
91 :committed_on => 24.hours.ago,
91 :committed_on => 24.hours.ago,
92 :comments => "Worked on this issue #1 @#{syntax}",
92 :comments => "Worked on this issue #1 @#{syntax}",
93 :revision => '520',
93 :revision => '520',
94 :user => User.find(2))
94 :user => User.find(2))
95 assert_difference 'TimeEntry.count' do
95 assert_difference 'TimeEntry.count' do
96 c.scan_comment_for_issue_ids
96 c.scan_comment_for_issue_ids
97 end
97 end
98 assert_equal [1], c.issue_ids.sort
98 assert_equal [1], c.issue_ids.sort
99
99
100 time = TimeEntry.order('id desc').first
100 time = TimeEntry.order('id desc').first
101 assert_equal 1, time.issue_id
101 assert_equal 1, time.issue_id
102 assert_equal 1, time.project_id
102 assert_equal 1, time.project_id
103 assert_equal 2, time.user_id
103 assert_equal 2, time.user_id
104 assert_equal expected_hours, time.hours,
104 assert_equal expected_hours, time.hours,
105 "@#{syntax} should be logged as #{expected_hours} hours but was #{time.hours}"
105 "@#{syntax} should be logged as #{expected_hours} hours but was #{time.hours}"
106 assert_equal Date.yesterday, time.spent_on
106 assert_equal Date.yesterday, time.spent_on
107 assert time.activity.is_default?
107 assert time.activity.is_default?
108 assert time.comments.include?('r520'),
108 assert time.comments.include?('r520'),
109 "r520 was expected in time_entry comments: #{time.comments}"
109 "r520 was expected in time_entry comments: #{time.comments}"
110 end
110 end
111 end
111 end
112
112
113 def test_ref_keywords_closing_with_timelog
113 def test_ref_keywords_closing_with_timelog
114 Setting.commit_ref_keywords = '*'
114 Setting.commit_ref_keywords = '*'
115 Setting.commit_update_keywords = [{'keywords' => 'fixes , closes',
115 Setting.commit_update_keywords = [{'keywords' => 'fixes , closes',
116 'status_id' => IssueStatus.where(:is_closed => true).first.id.to_s}]
116 'status_id' => IssueStatus.where(:is_closed => true).first.id.to_s}]
117 Setting.commit_logtime_enabled = '1'
117 Setting.commit_logtime_enabled = '1'
118
118
119 c = Changeset.new(:repository => Project.find(1).repository,
119 c = Changeset.new(:repository => Project.find(1).repository,
120 :committed_on => Time.now,
120 :committed_on => Time.now,
121 :comments => 'This is a comment. Fixes #1 @4.5, #2 @1',
121 :comments => 'This is a comment. Fixes #1 @4.5, #2 @1',
122 :user => User.find(2))
122 :user => User.find(2))
123 assert_difference 'TimeEntry.count', 2 do
123 assert_difference 'TimeEntry.count', 2 do
124 c.scan_comment_for_issue_ids
124 c.scan_comment_for_issue_ids
125 end
125 end
126
126
127 assert_equal [1, 2], c.issue_ids.sort
127 assert_equal [1, 2], c.issue_ids.sort
128 assert Issue.find(1).closed?
128 assert Issue.find(1).closed?
129 assert Issue.find(2).closed?
129 assert Issue.find(2).closed?
130
130
131 times = TimeEntry.order('id desc').limit(2)
131 times = TimeEntry.order('id desc').limit(2)
132 assert_equal [1, 2], times.collect(&:issue_id).sort
132 assert_equal [1, 2], times.collect(&:issue_id).sort
133 end
133 end
134
134
135 def test_ref_keywords_any_line_start
135 def test_ref_keywords_any_line_start
136 Setting.commit_ref_keywords = '*'
136 Setting.commit_ref_keywords = '*'
137 c = Changeset.new(:repository => Project.find(1).repository,
137 c = Changeset.new(:repository => Project.find(1).repository,
138 :committed_on => Time.now,
138 :committed_on => Time.now,
139 :comments => '#1 is the reason of this commit',
139 :comments => '#1 is the reason of this commit',
140 :revision => '12345')
140 :revision => '12345')
141 assert c.save
141 assert c.save
142 assert_equal [1], c.issue_ids.sort
142 assert_equal [1], c.issue_ids.sort
143 end
143 end
144
144
145 def test_ref_keywords_allow_brackets_around_a_issue_number
145 def test_ref_keywords_allow_brackets_around_a_issue_number
146 Setting.commit_ref_keywords = '*'
146 Setting.commit_ref_keywords = '*'
147 c = Changeset.new(:repository => Project.find(1).repository,
147 c = Changeset.new(:repository => Project.find(1).repository,
148 :committed_on => Time.now,
148 :committed_on => Time.now,
149 :comments => '[#1] Worked on this issue',
149 :comments => '[#1] Worked on this issue',
150 :revision => '12345')
150 :revision => '12345')
151 assert c.save
151 assert c.save
152 assert_equal [1], c.issue_ids.sort
152 assert_equal [1], c.issue_ids.sort
153 end
153 end
154
154
155 def test_ref_keywords_allow_brackets_around_multiple_issue_numbers
155 def test_ref_keywords_allow_brackets_around_multiple_issue_numbers
156 Setting.commit_ref_keywords = '*'
156 Setting.commit_ref_keywords = '*'
157 c = Changeset.new(:repository => Project.find(1).repository,
157 c = Changeset.new(:repository => Project.find(1).repository,
158 :committed_on => Time.now,
158 :committed_on => Time.now,
159 :comments => '[#1 #2, #3] Worked on these',
159 :comments => '[#1 #2, #3] Worked on these',
160 :revision => '12345')
160 :revision => '12345')
161 assert c.save
161 assert c.save
162 assert_equal [1,2,3], c.issue_ids.sort
162 assert_equal [1,2,3], c.issue_ids.sort
163 end
163 end
164
164
165 def test_update_keywords_with_changes_should_create_journal
166 issue = Issue.generate!(:project_id => 1, :status_id => 1)
167
168 with_settings :commit_update_keywords => [{'keywords' => 'fixes', 'status_id' => '3'}] do
169 assert_difference 'Journal.count' do
170 c = Changeset.generate!(:repository => Project.find(1).repository,:comments => "Fixes ##{issue.id}")
171 assert_include c.id, issue.reload.changeset_ids
172 journal = Journal.order('id DESC').first
173 assert_equal 1, journal.details.count
174 end
175 end
176 end
177
178 def test_update_keywords_without_change_should_not_create_journal
179 issue = Issue.generate!(:project_id => 1, :status_id => 3)
180
181 with_settings :commit_update_keywords => [{'keywords' => 'fixes', 'status_id' => '3'}] do
182 assert_no_difference 'Journal.count' do
183 c = Changeset.generate!(:repository => Project.find(1).repository,:comments => "Fixes ##{issue.id}")
184 assert_include c.id, issue.reload.changeset_ids
185 end
186 end
187 end
188
165 def test_update_keywords_with_multiple_rules
189 def test_update_keywords_with_multiple_rules
166 with_settings :commit_update_keywords => [
190 with_settings :commit_update_keywords => [
167 {'keywords' => 'fixes, closes', 'status_id' => '5'},
191 {'keywords' => 'fixes, closes', 'status_id' => '5'},
168 {'keywords' => 'resolves', 'status_id' => '3'}
192 {'keywords' => 'resolves', 'status_id' => '3'}
169 ] do
193 ] do
170
194
171 issue1 = Issue.generate!
195 issue1 = Issue.generate!
172 issue2 = Issue.generate!
196 issue2 = Issue.generate!
173 Changeset.generate!(:comments => "Closes ##{issue1.id}\nResolves ##{issue2.id}")
197 Changeset.generate!(:comments => "Closes ##{issue1.id}\nResolves ##{issue2.id}")
174 assert_equal 5, issue1.reload.status_id
198 assert_equal 5, issue1.reload.status_id
175 assert_equal 3, issue2.reload.status_id
199 assert_equal 3, issue2.reload.status_id
176 end
200 end
177 end
201 end
178
202
179 def test_update_keywords_with_multiple_rules_should_match_tracker
203 def test_update_keywords_with_multiple_rules_should_match_tracker
180 with_settings :commit_update_keywords => [
204 with_settings :commit_update_keywords => [
181 {'keywords' => 'fixes', 'status_id' => '5', 'if_tracker_id' => '2'},
205 {'keywords' => 'fixes', 'status_id' => '5', 'if_tracker_id' => '2'},
182 {'keywords' => 'fixes', 'status_id' => '3', 'if_tracker_id' => ''}
206 {'keywords' => 'fixes', 'status_id' => '3', 'if_tracker_id' => ''}
183 ] do
207 ] do
184
208
185 issue1 = Issue.generate!(:tracker_id => 2)
209 issue1 = Issue.generate!(:tracker_id => 2)
186 issue2 = Issue.generate!
210 issue2 = Issue.generate!
187 Changeset.generate!(:comments => "Fixes ##{issue1.id}, ##{issue2.id}")
211 Changeset.generate!(:comments => "Fixes ##{issue1.id}, ##{issue2.id}")
188 assert_equal 5, issue1.reload.status_id
212 assert_equal 5, issue1.reload.status_id
189 assert_equal 3, issue2.reload.status_id
213 assert_equal 3, issue2.reload.status_id
190 end
214 end
191 end
215 end
192
216
193 def test_update_keywords_with_multiple_rules_and_no_match
217 def test_update_keywords_with_multiple_rules_and_no_match
194 with_settings :commit_update_keywords => [
218 with_settings :commit_update_keywords => [
195 {'keywords' => 'fixes', 'status_id' => '5', 'if_tracker_id' => '2'},
219 {'keywords' => 'fixes', 'status_id' => '5', 'if_tracker_id' => '2'},
196 {'keywords' => 'fixes', 'status_id' => '3', 'if_tracker_id' => '3'}
220 {'keywords' => 'fixes', 'status_id' => '3', 'if_tracker_id' => '3'}
197 ] do
221 ] do
198
222
199 issue1 = Issue.generate!(:tracker_id => 2)
223 issue1 = Issue.generate!(:tracker_id => 2)
200 issue2 = Issue.generate!
224 issue2 = Issue.generate!
201 Changeset.generate!(:comments => "Fixes ##{issue1.id}, ##{issue2.id}")
225 Changeset.generate!(:comments => "Fixes ##{issue1.id}, ##{issue2.id}")
202 assert_equal 5, issue1.reload.status_id
226 assert_equal 5, issue1.reload.status_id
203 assert_equal 1, issue2.reload.status_id # no updates
227 assert_equal 1, issue2.reload.status_id # no updates
204 end
228 end
205 end
229 end
206
230
207 def test_commit_referencing_a_subproject_issue
231 def test_commit_referencing_a_subproject_issue
208 c = Changeset.new(:repository => Project.find(1).repository,
232 c = Changeset.new(:repository => Project.find(1).repository,
209 :committed_on => Time.now,
233 :committed_on => Time.now,
210 :comments => 'refs #5, a subproject issue',
234 :comments => 'refs #5, a subproject issue',
211 :revision => '12345')
235 :revision => '12345')
212 assert c.save
236 assert c.save
213 assert_equal [5], c.issue_ids.sort
237 assert_equal [5], c.issue_ids.sort
214 assert c.issues.first.project != c.project
238 assert c.issues.first.project != c.project
215 end
239 end
216
240
217 def test_commit_closing_a_subproject_issue
241 def test_commit_closing_a_subproject_issue
218 with_settings :commit_update_keywords => [{'keywords' => 'closes', 'status_id' => '5'}],
242 with_settings :commit_update_keywords => [{'keywords' => 'closes', 'status_id' => '5'}],
219 :default_language => 'en' do
243 :default_language => 'en' do
220 issue = Issue.find(5)
244 issue = Issue.find(5)
221 assert !issue.closed?
245 assert !issue.closed?
222 assert_difference 'Journal.count' do
246 assert_difference 'Journal.count' do
223 c = Changeset.new(:repository => Project.find(1).repository,
247 c = Changeset.new(:repository => Project.find(1).repository,
224 :committed_on => Time.now,
248 :committed_on => Time.now,
225 :comments => 'closes #5, a subproject issue',
249 :comments => 'closes #5, a subproject issue',
226 :revision => '12345')
250 :revision => '12345')
227 assert c.save
251 assert c.save
228 end
252 end
229 assert issue.reload.closed?
253 assert issue.reload.closed?
230 journal = Journal.order('id DESC').first
254 journal = Journal.order('id DESC').first
231 assert_equal issue, journal.issue
255 assert_equal issue, journal.issue
232 assert_include "Applied in changeset ecookbook:r12345.", journal.notes
256 assert_include "Applied in changeset ecookbook:r12345.", journal.notes
233 end
257 end
234 end
258 end
235
259
236 def test_commit_referencing_a_parent_project_issue
260 def test_commit_referencing_a_parent_project_issue
237 # repository of child project
261 # repository of child project
238 r = Repository::Subversion.create!(
262 r = Repository::Subversion.create!(
239 :project => Project.find(3),
263 :project => Project.find(3),
240 :url => 'svn://localhost/test')
264 :url => 'svn://localhost/test')
241 c = Changeset.new(:repository => r,
265 c = Changeset.new(:repository => r,
242 :committed_on => Time.now,
266 :committed_on => Time.now,
243 :comments => 'refs #2, an issue of a parent project',
267 :comments => 'refs #2, an issue of a parent project',
244 :revision => '12345')
268 :revision => '12345')
245 assert c.save
269 assert c.save
246 assert_equal [2], c.issue_ids.sort
270 assert_equal [2], c.issue_ids.sort
247 assert c.issues.first.project != c.project
271 assert c.issues.first.project != c.project
248 end
272 end
249
273
250 def test_commit_referencing_a_project_with_commit_cross_project_ref_disabled
274 def test_commit_referencing_a_project_with_commit_cross_project_ref_disabled
251 r = Repository::Subversion.create!(
275 r = Repository::Subversion.create!(
252 :project => Project.find(3),
276 :project => Project.find(3),
253 :url => 'svn://localhost/test')
277 :url => 'svn://localhost/test')
254 with_settings :commit_cross_project_ref => '0' do
278 with_settings :commit_cross_project_ref => '0' do
255 c = Changeset.new(:repository => r,
279 c = Changeset.new(:repository => r,
256 :committed_on => Time.now,
280 :committed_on => Time.now,
257 :comments => 'refs #4, an issue of a different project',
281 :comments => 'refs #4, an issue of a different project',
258 :revision => '12345')
282 :revision => '12345')
259 assert c.save
283 assert c.save
260 assert_equal [], c.issue_ids
284 assert_equal [], c.issue_ids
261 end
285 end
262 end
286 end
263
287
264 def test_commit_referencing_a_project_with_commit_cross_project_ref_enabled
288 def test_commit_referencing_a_project_with_commit_cross_project_ref_enabled
265 r = Repository::Subversion.create!(
289 r = Repository::Subversion.create!(
266 :project => Project.find(3),
290 :project => Project.find(3),
267 :url => 'svn://localhost/test')
291 :url => 'svn://localhost/test')
268 with_settings :commit_cross_project_ref => '1' do
292 with_settings :commit_cross_project_ref => '1' do
269 c = Changeset.new(:repository => r,
293 c = Changeset.new(:repository => r,
270 :committed_on => Time.now,
294 :committed_on => Time.now,
271 :comments => 'refs #4, an issue of a different project',
295 :comments => 'refs #4, an issue of a different project',
272 :revision => '12345')
296 :revision => '12345')
273 assert c.save
297 assert c.save
274 assert_equal [4], c.issue_ids
298 assert_equal [4], c.issue_ids
275 end
299 end
276 end
300 end
277
301
278 def test_old_commits_should_not_update_issues_nor_log_time
302 def test_old_commits_should_not_update_issues_nor_log_time
279 Setting.commit_ref_keywords = '*'
303 Setting.commit_ref_keywords = '*'
280 Setting.commit_update_keywords = {'fixes , closes' => {'status_id' => '5', 'done_ratio' => '90'}}
304 Setting.commit_update_keywords = {'fixes , closes' => {'status_id' => '5', 'done_ratio' => '90'}}
281 Setting.commit_logtime_enabled = '1'
305 Setting.commit_logtime_enabled = '1'
282
306
283 repository = Project.find(1).repository
307 repository = Project.find(1).repository
284 repository.created_on = Time.now
308 repository.created_on = Time.now
285 repository.save!
309 repository.save!
286
310
287 c = Changeset.new(:repository => repository,
311 c = Changeset.new(:repository => repository,
288 :committed_on => 1.month.ago,
312 :committed_on => 1.month.ago,
289 :comments => 'New commit (#2). Fixes #1 @1h',
313 :comments => 'New commit (#2). Fixes #1 @1h',
290 :revision => '12345')
314 :revision => '12345')
291 assert_no_difference 'TimeEntry.count' do
315 assert_no_difference 'TimeEntry.count' do
292 assert c.save
316 assert c.save
293 end
317 end
294 assert_equal [1, 2], c.issue_ids.sort
318 assert_equal [1, 2], c.issue_ids.sort
295 issue = Issue.find(1)
319 issue = Issue.find(1)
296 assert_equal 1, issue.status_id
320 assert_equal 1, issue.status_id
297 assert_equal 0, issue.done_ratio
321 assert_equal 0, issue.done_ratio
298 end
322 end
299
323
300 def test_2_repositories_with_same_backend_should_not_link_issue_multiple_times
324 def test_2_repositories_with_same_backend_should_not_link_issue_multiple_times
301 Setting.commit_ref_keywords = '*'
325 Setting.commit_ref_keywords = '*'
302 r1 = Repository::Subversion.create!(:project_id => 1, :identifier => 'svn1', :url => 'file:///svn1')
326 r1 = Repository::Subversion.create!(:project_id => 1, :identifier => 'svn1', :url => 'file:///svn1')
303 r2 = Repository::Subversion.create!(:project_id => 1, :identifier => 'svn2', :url => 'file:///svn1')
327 r2 = Repository::Subversion.create!(:project_id => 1, :identifier => 'svn2', :url => 'file:///svn1')
304 now = Time.now
328 now = Time.now
305 assert_difference 'Issue.find(1).changesets.count' do
329 assert_difference 'Issue.find(1).changesets.count' do
306 c1 = Changeset.create!(:repository => r1, :committed_on => now, :comments => 'Fixes #1', :revision => '12345')
330 c1 = Changeset.create!(:repository => r1, :committed_on => now, :comments => 'Fixes #1', :revision => '12345')
307 c1 = Changeset.create!(:repository => r2, :committed_on => now, :comments => 'Fixes #1', :revision => '12345')
331 c1 = Changeset.create!(:repository => r2, :committed_on => now, :comments => 'Fixes #1', :revision => '12345')
308 end
332 end
309 end
333 end
310
334
311 def test_text_tag_revision
335 def test_text_tag_revision
312 c = Changeset.new(:revision => '520')
336 c = Changeset.new(:revision => '520')
313 assert_equal 'r520', c.text_tag
337 assert_equal 'r520', c.text_tag
314 end
338 end
315
339
316 def test_text_tag_revision_with_same_project
340 def test_text_tag_revision_with_same_project
317 c = Changeset.new(:revision => '520', :repository => Project.find(1).repository)
341 c = Changeset.new(:revision => '520', :repository => Project.find(1).repository)
318 assert_equal 'r520', c.text_tag(Project.find(1))
342 assert_equal 'r520', c.text_tag(Project.find(1))
319 end
343 end
320
344
321 def test_text_tag_revision_with_different_project
345 def test_text_tag_revision_with_different_project
322 c = Changeset.new(:revision => '520', :repository => Project.find(1).repository)
346 c = Changeset.new(:revision => '520', :repository => Project.find(1).repository)
323 assert_equal 'ecookbook:r520', c.text_tag(Project.find(2))
347 assert_equal 'ecookbook:r520', c.text_tag(Project.find(2))
324 end
348 end
325
349
326 def test_text_tag_revision_with_repository_identifier
350 def test_text_tag_revision_with_repository_identifier
327 r = Repository::Subversion.create!(
351 r = Repository::Subversion.create!(
328 :project_id => 1,
352 :project_id => 1,
329 :url => 'svn://localhost/test',
353 :url => 'svn://localhost/test',
330 :identifier => 'documents')
354 :identifier => 'documents')
331 c = Changeset.new(:revision => '520', :repository => r)
355 c = Changeset.new(:revision => '520', :repository => r)
332 assert_equal 'documents|r520', c.text_tag
356 assert_equal 'documents|r520', c.text_tag
333 assert_equal 'ecookbook:documents|r520', c.text_tag(Project.find(2))
357 assert_equal 'ecookbook:documents|r520', c.text_tag(Project.find(2))
334 end
358 end
335
359
336 def test_text_tag_hash
360 def test_text_tag_hash
337 c = Changeset.new(
361 c = Changeset.new(
338 :scmid => '7234cb2750b63f47bff735edc50a1c0a433c2518',
362 :scmid => '7234cb2750b63f47bff735edc50a1c0a433c2518',
339 :revision => '7234cb2750b63f47bff735edc50a1c0a433c2518')
363 :revision => '7234cb2750b63f47bff735edc50a1c0a433c2518')
340 assert_equal 'commit:7234cb2750b63f47bff735edc50a1c0a433c2518', c.text_tag
364 assert_equal 'commit:7234cb2750b63f47bff735edc50a1c0a433c2518', c.text_tag
341 end
365 end
342
366
343 def test_text_tag_hash_with_same_project
367 def test_text_tag_hash_with_same_project
344 c = Changeset.new(:revision => '7234cb27', :scmid => '7234cb27', :repository => Project.find(1).repository)
368 c = Changeset.new(:revision => '7234cb27', :scmid => '7234cb27', :repository => Project.find(1).repository)
345 assert_equal 'commit:7234cb27', c.text_tag(Project.find(1))
369 assert_equal 'commit:7234cb27', c.text_tag(Project.find(1))
346 end
370 end
347
371
348 def test_text_tag_hash_with_different_project
372 def test_text_tag_hash_with_different_project
349 c = Changeset.new(:revision => '7234cb27', :scmid => '7234cb27', :repository => Project.find(1).repository)
373 c = Changeset.new(:revision => '7234cb27', :scmid => '7234cb27', :repository => Project.find(1).repository)
350 assert_equal 'ecookbook:commit:7234cb27', c.text_tag(Project.find(2))
374 assert_equal 'ecookbook:commit:7234cb27', c.text_tag(Project.find(2))
351 end
375 end
352
376
353 def test_text_tag_hash_all_number
377 def test_text_tag_hash_all_number
354 c = Changeset.new(:scmid => '0123456789', :revision => '0123456789')
378 c = Changeset.new(:scmid => '0123456789', :revision => '0123456789')
355 assert_equal 'commit:0123456789', c.text_tag
379 assert_equal 'commit:0123456789', c.text_tag
356 end
380 end
357
381
358 def test_text_tag_hash_with_repository_identifier
382 def test_text_tag_hash_with_repository_identifier
359 r = Repository::Subversion.new(
383 r = Repository::Subversion.new(
360 :project_id => 1,
384 :project_id => 1,
361 :url => 'svn://localhost/test',
385 :url => 'svn://localhost/test',
362 :identifier => 'documents')
386 :identifier => 'documents')
363 c = Changeset.new(:revision => '7234cb27', :scmid => '7234cb27', :repository => r)
387 c = Changeset.new(:revision => '7234cb27', :scmid => '7234cb27', :repository => r)
364 assert_equal 'commit:documents|7234cb27', c.text_tag
388 assert_equal 'commit:documents|7234cb27', c.text_tag
365 assert_equal 'ecookbook:commit:documents|7234cb27', c.text_tag(Project.find(2))
389 assert_equal 'ecookbook:commit:documents|7234cb27', c.text_tag(Project.find(2))
366 end
390 end
367
391
368 def test_previous
392 def test_previous
369 changeset = Changeset.find_by_revision('3')
393 changeset = Changeset.find_by_revision('3')
370 assert_equal Changeset.find_by_revision('2'), changeset.previous
394 assert_equal Changeset.find_by_revision('2'), changeset.previous
371 end
395 end
372
396
373 def test_previous_nil
397 def test_previous_nil
374 changeset = Changeset.find_by_revision('1')
398 changeset = Changeset.find_by_revision('1')
375 assert_nil changeset.previous
399 assert_nil changeset.previous
376 end
400 end
377
401
378 def test_next
402 def test_next
379 changeset = Changeset.find_by_revision('2')
403 changeset = Changeset.find_by_revision('2')
380 assert_equal Changeset.find_by_revision('3'), changeset.next
404 assert_equal Changeset.find_by_revision('3'), changeset.next
381 end
405 end
382
406
383 def test_next_nil
407 def test_next_nil
384 changeset = Changeset.find_by_revision('10')
408 changeset = Changeset.find_by_revision('10')
385 assert_nil changeset.next
409 assert_nil changeset.next
386 end
410 end
387
411
388 def test_comments_should_be_converted_to_utf8
412 def test_comments_should_be_converted_to_utf8
389 proj = Project.find(3)
413 proj = Project.find(3)
390 # str = File.read("#{RAILS_ROOT}/test/fixtures/encoding/iso-8859-1.txt")
414 # str = File.read("#{RAILS_ROOT}/test/fixtures/encoding/iso-8859-1.txt")
391 str = "Texte encod\xe9 en ISO-8859-1."
415 str = "Texte encod\xe9 en ISO-8859-1."
392 str.force_encoding("ASCII-8BIT") if str.respond_to?(:force_encoding)
416 str.force_encoding("ASCII-8BIT") if str.respond_to?(:force_encoding)
393 r = Repository::Bazaar.create!(
417 r = Repository::Bazaar.create!(
394 :project => proj,
418 :project => proj,
395 :url => '/tmp/test/bazaar',
419 :url => '/tmp/test/bazaar',
396 :log_encoding => 'ISO-8859-1' )
420 :log_encoding => 'ISO-8859-1' )
397 assert r
421 assert r
398 c = Changeset.new(:repository => r,
422 c = Changeset.new(:repository => r,
399 :committed_on => Time.now,
423 :committed_on => Time.now,
400 :revision => '123',
424 :revision => '123',
401 :scmid => '12345',
425 :scmid => '12345',
402 :comments => str)
426 :comments => str)
403 assert( c.save )
427 assert( c.save )
404 str_utf8 = "Texte encod\xc3\xa9 en ISO-8859-1."
428 str_utf8 = "Texte encod\xc3\xa9 en ISO-8859-1."
405 str_utf8.force_encoding("UTF-8") if str_utf8.respond_to?(:force_encoding)
429 str_utf8.force_encoding("UTF-8") if str_utf8.respond_to?(:force_encoding)
406 assert_equal str_utf8, c.comments
430 assert_equal str_utf8, c.comments
407 end
431 end
408
432
409 def test_invalid_utf8_sequences_in_comments_should_be_replaced_latin1
433 def test_invalid_utf8_sequences_in_comments_should_be_replaced_latin1
410 proj = Project.find(3)
434 proj = Project.find(3)
411 # str = File.read("#{RAILS_ROOT}/test/fixtures/encoding/iso-8859-1.txt")
435 # str = File.read("#{RAILS_ROOT}/test/fixtures/encoding/iso-8859-1.txt")
412 str1 = "Texte encod\xe9 en ISO-8859-1."
436 str1 = "Texte encod\xe9 en ISO-8859-1."
413 str2 = "\xe9a\xe9b\xe9c\xe9d\xe9e test"
437 str2 = "\xe9a\xe9b\xe9c\xe9d\xe9e test"
414 str1.force_encoding("UTF-8") if str1.respond_to?(:force_encoding)
438 str1.force_encoding("UTF-8") if str1.respond_to?(:force_encoding)
415 str2.force_encoding("ASCII-8BIT") if str2.respond_to?(:force_encoding)
439 str2.force_encoding("ASCII-8BIT") if str2.respond_to?(:force_encoding)
416 r = Repository::Bazaar.create!(
440 r = Repository::Bazaar.create!(
417 :project => proj,
441 :project => proj,
418 :url => '/tmp/test/bazaar',
442 :url => '/tmp/test/bazaar',
419 :log_encoding => 'UTF-8' )
443 :log_encoding => 'UTF-8' )
420 assert r
444 assert r
421 c = Changeset.new(:repository => r,
445 c = Changeset.new(:repository => r,
422 :committed_on => Time.now,
446 :committed_on => Time.now,
423 :revision => '123',
447 :revision => '123',
424 :scmid => '12345',
448 :scmid => '12345',
425 :comments => str1,
449 :comments => str1,
426 :committer => str2)
450 :committer => str2)
427 assert( c.save )
451 assert( c.save )
428 assert_equal "Texte encod? en ISO-8859-1.", c.comments
452 assert_equal "Texte encod? en ISO-8859-1.", c.comments
429 assert_equal "?a?b?c?d?e test", c.committer
453 assert_equal "?a?b?c?d?e test", c.committer
430 end
454 end
431
455
432 def test_invalid_utf8_sequences_in_comments_should_be_replaced_ja_jis
456 def test_invalid_utf8_sequences_in_comments_should_be_replaced_ja_jis
433 proj = Project.find(3)
457 proj = Project.find(3)
434 str = "test\xb5\xfetest\xb5\xfe"
458 str = "test\xb5\xfetest\xb5\xfe"
435 if str.respond_to?(:force_encoding)
459 if str.respond_to?(:force_encoding)
436 str.force_encoding('ASCII-8BIT')
460 str.force_encoding('ASCII-8BIT')
437 end
461 end
438 r = Repository::Bazaar.create!(
462 r = Repository::Bazaar.create!(
439 :project => proj,
463 :project => proj,
440 :url => '/tmp/test/bazaar',
464 :url => '/tmp/test/bazaar',
441 :log_encoding => 'ISO-2022-JP' )
465 :log_encoding => 'ISO-2022-JP' )
442 assert r
466 assert r
443 c = Changeset.new(:repository => r,
467 c = Changeset.new(:repository => r,
444 :committed_on => Time.now,
468 :committed_on => Time.now,
445 :revision => '123',
469 :revision => '123',
446 :scmid => '12345',
470 :scmid => '12345',
447 :comments => str)
471 :comments => str)
448 assert( c.save )
472 assert( c.save )
449 assert_equal "test??test??", c.comments
473 assert_equal "test??test??", c.comments
450 end
474 end
451
475
452 def test_comments_should_be_converted_all_latin1_to_utf8
476 def test_comments_should_be_converted_all_latin1_to_utf8
453 s1 = "\xC2\x80"
477 s1 = "\xC2\x80"
454 s2 = "\xc3\x82\xc2\x80"
478 s2 = "\xc3\x82\xc2\x80"
455 s4 = s2.dup
479 s4 = s2.dup
456 if s1.respond_to?(:force_encoding)
480 if s1.respond_to?(:force_encoding)
457 s3 = s1.dup
481 s3 = s1.dup
458 s1.force_encoding('ASCII-8BIT')
482 s1.force_encoding('ASCII-8BIT')
459 s2.force_encoding('ASCII-8BIT')
483 s2.force_encoding('ASCII-8BIT')
460 s3.force_encoding('ISO-8859-1')
484 s3.force_encoding('ISO-8859-1')
461 s4.force_encoding('UTF-8')
485 s4.force_encoding('UTF-8')
462 assert_equal s3.encode('UTF-8'), s4
486 assert_equal s3.encode('UTF-8'), s4
463 end
487 end
464 proj = Project.find(3)
488 proj = Project.find(3)
465 r = Repository::Bazaar.create!(
489 r = Repository::Bazaar.create!(
466 :project => proj,
490 :project => proj,
467 :url => '/tmp/test/bazaar',
491 :url => '/tmp/test/bazaar',
468 :log_encoding => 'ISO-8859-1' )
492 :log_encoding => 'ISO-8859-1' )
469 assert r
493 assert r
470 c = Changeset.new(:repository => r,
494 c = Changeset.new(:repository => r,
471 :committed_on => Time.now,
495 :committed_on => Time.now,
472 :revision => '123',
496 :revision => '123',
473 :scmid => '12345',
497 :scmid => '12345',
474 :comments => s1)
498 :comments => s1)
475 assert( c.save )
499 assert( c.save )
476 assert_equal s4, c.comments
500 assert_equal s4, c.comments
477 end
501 end
478
502
479 def test_invalid_utf8_sequences_in_paths_should_be_replaced
503 def test_invalid_utf8_sequences_in_paths_should_be_replaced
480 proj = Project.find(3)
504 proj = Project.find(3)
481 str1 = "Texte encod\xe9 en ISO-8859-1"
505 str1 = "Texte encod\xe9 en ISO-8859-1"
482 str2 = "\xe9a\xe9b\xe9c\xe9d\xe9e test"
506 str2 = "\xe9a\xe9b\xe9c\xe9d\xe9e test"
483 str1.force_encoding("UTF-8") if str1.respond_to?(:force_encoding)
507 str1.force_encoding("UTF-8") if str1.respond_to?(:force_encoding)
484 str2.force_encoding("ASCII-8BIT") if str2.respond_to?(:force_encoding)
508 str2.force_encoding("ASCII-8BIT") if str2.respond_to?(:force_encoding)
485 r = Repository::Bazaar.create!(
509 r = Repository::Bazaar.create!(
486 :project => proj,
510 :project => proj,
487 :url => '/tmp/test/bazaar',
511 :url => '/tmp/test/bazaar',
488 :log_encoding => 'UTF-8' )
512 :log_encoding => 'UTF-8' )
489 assert r
513 assert r
490 cs = Changeset.new(
514 cs = Changeset.new(
491 :repository => r,
515 :repository => r,
492 :committed_on => Time.now,
516 :committed_on => Time.now,
493 :revision => '123',
517 :revision => '123',
494 :scmid => '12345',
518 :scmid => '12345',
495 :comments => "test")
519 :comments => "test")
496 assert(cs.save)
520 assert(cs.save)
497 ch = Change.new(
521 ch = Change.new(
498 :changeset => cs,
522 :changeset => cs,
499 :action => "A",
523 :action => "A",
500 :path => str1,
524 :path => str1,
501 :from_path => str2,
525 :from_path => str2,
502 :from_revision => "345")
526 :from_revision => "345")
503 assert(ch.save)
527 assert(ch.save)
504 assert_equal "Texte encod? en ISO-8859-1", ch.path
528 assert_equal "Texte encod? en ISO-8859-1", ch.path
505 assert_equal "?a?b?c?d?e test", ch.from_path
529 assert_equal "?a?b?c?d?e test", ch.from_path
506 end
530 end
507
531
508 def test_comments_nil
532 def test_comments_nil
509 proj = Project.find(3)
533 proj = Project.find(3)
510 r = Repository::Bazaar.create!(
534 r = Repository::Bazaar.create!(
511 :project => proj,
535 :project => proj,
512 :url => '/tmp/test/bazaar',
536 :url => '/tmp/test/bazaar',
513 :log_encoding => 'ISO-8859-1' )
537 :log_encoding => 'ISO-8859-1' )
514 assert r
538 assert r
515 c = Changeset.new(:repository => r,
539 c = Changeset.new(:repository => r,
516 :committed_on => Time.now,
540 :committed_on => Time.now,
517 :revision => '123',
541 :revision => '123',
518 :scmid => '12345',
542 :scmid => '12345',
519 :comments => nil,
543 :comments => nil,
520 :committer => nil)
544 :committer => nil)
521 assert( c.save )
545 assert( c.save )
522 assert_equal "", c.comments
546 assert_equal "", c.comments
523 assert_equal nil, c.committer
547 assert_equal nil, c.committer
524 if c.comments.respond_to?(:force_encoding)
548 if c.comments.respond_to?(:force_encoding)
525 assert_equal "UTF-8", c.comments.encoding.to_s
549 assert_equal "UTF-8", c.comments.encoding.to_s
526 end
550 end
527 end
551 end
528
552
529 def test_comments_empty
553 def test_comments_empty
530 proj = Project.find(3)
554 proj = Project.find(3)
531 r = Repository::Bazaar.create!(
555 r = Repository::Bazaar.create!(
532 :project => proj,
556 :project => proj,
533 :url => '/tmp/test/bazaar',
557 :url => '/tmp/test/bazaar',
534 :log_encoding => 'ISO-8859-1' )
558 :log_encoding => 'ISO-8859-1' )
535 assert r
559 assert r
536 c = Changeset.new(:repository => r,
560 c = Changeset.new(:repository => r,
537 :committed_on => Time.now,
561 :committed_on => Time.now,
538 :revision => '123',
562 :revision => '123',
539 :scmid => '12345',
563 :scmid => '12345',
540 :comments => "",
564 :comments => "",
541 :committer => "")
565 :committer => "")
542 assert( c.save )
566 assert( c.save )
543 assert_equal "", c.comments
567 assert_equal "", c.comments
544 assert_equal "", c.committer
568 assert_equal "", c.committer
545 if c.comments.respond_to?(:force_encoding)
569 if c.comments.respond_to?(:force_encoding)
546 assert_equal "UTF-8", c.comments.encoding.to_s
570 assert_equal "UTF-8", c.comments.encoding.to_s
547 assert_equal "UTF-8", c.committer.encoding.to_s
571 assert_equal "UTF-8", c.committer.encoding.to_s
548 end
572 end
549 end
573 end
550
574
551 def test_comments_should_accept_more_than_64k
575 def test_comments_should_accept_more_than_64k
552 c = Changeset.new(:repository => Repository.first,
576 c = Changeset.new(:repository => Repository.first,
553 :committed_on => Time.now,
577 :committed_on => Time.now,
554 :revision => '123',
578 :revision => '123',
555 :scmid => '12345',
579 :scmid => '12345',
556 :comments => "a" * 500.kilobyte)
580 :comments => "a" * 500.kilobyte)
557 assert c.save
581 assert c.save
558 c.reload
582 c.reload
559 assert_equal 500.kilobyte, c.comments.size
583 assert_equal 500.kilobyte, c.comments.size
560 end
584 end
561
585
562 def test_identifier
586 def test_identifier
563 c = Changeset.find_by_revision('1')
587 c = Changeset.find_by_revision('1')
564 assert_equal c.revision, c.identifier
588 assert_equal c.revision, c.identifier
565 end
589 end
566 end
590 end
General Comments 0
You need to be logged in to leave comments. Login now