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