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