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