##// END OF EJS Templates
Don't update issues nor log time when importing old changesets (#4823)....
Jean-Philippe Lang -
r11969:7b1b605ae8e6
parent child
Show More
@@ -0,0 +1,9
1 class AddRepositoriesCreatedOn < ActiveRecord::Migration
2 def up
3 add_column :repositories, :created_on, :timestamp
4 end
5
6 def down
7 remove_column :repositories, :created_on
8 end
9 end
@@ -1,273 +1,276
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_by_keyword.keys
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 fix_issue(issue, action) if fix_keywords.include?(action)
136 log_time(issue, hours) if hours && Setting.commit_logtime_enabled?
135 # Don't update issues or log time when importing old commits
136 unless repository.created_on && committed_on && committed_on < repository.created_on
137 fix_issue(issue, action) if fix_keywords.include?(action)
138 log_time(issue, hours) if hours && Setting.commit_logtime_enabled?
139 end
137 140 end
138 141 end
139 142 end
140 143
141 144 referenced_issues.uniq!
142 145 self.issues = referenced_issues unless referenced_issues.empty?
143 146 end
144 147
145 148 def short_comments
146 149 @short_comments || split_comments.first
147 150 end
148 151
149 152 def long_comments
150 153 @long_comments || split_comments.last
151 154 end
152 155
153 156 def text_tag(ref_project=nil)
154 157 tag = if scmid?
155 158 "commit:#{scmid}"
156 159 else
157 160 "r#{revision}"
158 161 end
159 162 if repository && repository.identifier.present?
160 163 tag = "#{repository.identifier}|#{tag}"
161 164 end
162 165 if ref_project && project && ref_project != project
163 166 tag = "#{project.identifier}:#{tag}"
164 167 end
165 168 tag
166 169 end
167 170
168 171 # Returns the title used for the changeset in the activity/search results
169 172 def title
170 173 repo = (repository && repository.identifier.present?) ? " (#{repository.identifier})" : ''
171 174 comm = short_comments.blank? ? '' : (': ' + short_comments)
172 175 "#{l(:label_revision)} #{format_identifier}#{repo}#{comm}"
173 176 end
174 177
175 178 # Returns the previous changeset
176 179 def previous
177 180 @previous ||= Changeset.where(["id < ? AND repository_id = ?", id, repository_id]).order('id DESC').first
178 181 end
179 182
180 183 # Returns the next changeset
181 184 def next
182 185 @next ||= Changeset.where(["id > ? AND repository_id = ?", id, repository_id]).order('id ASC').first
183 186 end
184 187
185 188 # Creates a new Change from it's common parameters
186 189 def create_change(change)
187 190 Change.create(:changeset => self,
188 191 :action => change[:action],
189 192 :path => change[:path],
190 193 :from_path => change[:from_path],
191 194 :from_revision => change[:from_revision])
192 195 end
193 196
194 197 # Finds an issue that can be referenced by the commit message
195 198 def find_referenced_issue_by_id(id)
196 199 return nil if id.blank?
197 200 issue = Issue.find_by_id(id.to_i, :include => :project)
198 201 if Setting.commit_cross_project_ref?
199 202 # all issues can be referenced/fixed
200 203 elsif issue
201 204 # issue that belong to the repository project, a subproject or a parent project only
202 205 unless issue.project &&
203 206 (project == issue.project || project.is_ancestor_of?(issue.project) ||
204 207 project.is_descendant_of?(issue.project))
205 208 issue = nil
206 209 end
207 210 end
208 211 issue
209 212 end
210 213
211 214 private
212 215
213 216 # Updates the +issue+ according to +action+
214 217 def fix_issue(issue, action)
215 218 updates = Setting.commit_update_by_keyword[action]
216 219 return unless updates.is_a?(Hash)
217 220
218 221 # the issue may have been updated by the closure of another one (eg. duplicate)
219 222 issue.reload
220 223 # don't change the status is the issue is closed
221 224 return if issue.status && issue.status.is_closed?
222 225
223 226 journal = issue.init_journal(user || User.anonymous, ll(Setting.default_language, :text_status_changed_by_changeset, text_tag(issue.project)))
224 227 issue.assign_attributes updates.slice(*Issue.attribute_names)
225 228 Redmine::Hook.call_hook(:model_changeset_scan_commit_for_issue_ids_pre_issue_update,
226 229 { :changeset => self, :issue => issue, :action => action })
227 230 unless issue.save
228 231 logger.warn("Issue ##{issue.id} could not be saved by changeset #{id}: #{issue.errors.full_messages}") if logger
229 232 end
230 233 issue
231 234 end
232 235
233 236 def log_time(issue, hours)
234 237 time_entry = TimeEntry.new(
235 238 :user => user,
236 239 :hours => hours,
237 240 :issue => issue,
238 241 :spent_on => commit_date,
239 242 :comments => l(:text_time_logged_by_changeset, :value => text_tag(issue.project),
240 243 :locale => Setting.default_language)
241 244 )
242 245 time_entry.activity = log_time_activity unless log_time_activity.nil?
243 246
244 247 unless time_entry.save
245 248 logger.warn("TimeEntry could not be created by changeset #{id}: #{time_entry.errors.full_messages}") if logger
246 249 end
247 250 time_entry
248 251 end
249 252
250 253 def log_time_activity
251 254 if Setting.commit_logtime_activity_id.to_i > 0
252 255 TimeEntryActivity.find_by_id(Setting.commit_logtime_activity_id.to_i)
253 256 end
254 257 end
255 258
256 259 def split_comments
257 260 comments =~ /\A(.+?)\r?\n(.*)$/m
258 261 @short_comments = $1 || comments
259 262 @long_comments = $2.to_s.strip
260 263 return @short_comments, @long_comments
261 264 end
262 265
263 266 public
264 267
265 268 # Strips and reencodes a commit log before insertion into the database
266 269 def self.normalize_comments(str, encoding)
267 270 Changeset.to_utf8(str.to_s.strip, encoding)
268 271 end
269 272
270 273 def self.to_utf8(str, encoding)
271 274 Redmine::CodesetUtil.to_utf8(str, encoding)
272 275 end
273 276 end
@@ -1,19 +1,21
1 1 ---
2 2 repositories_001:
3 3 project_id: 1
4 4 url: file:///<%= Rails.root %>/tmp/test/subversion_repository
5 5 id: 10
6 6 root_url: file:///<%= Rails.root %>/tmp/test/subversion_repository
7 7 password: ""
8 8 login: ""
9 9 type: Repository::Subversion
10 10 is_default: true
11 created_on: 2006-07-19 19:04:21 +02:00
11 12 repositories_002:
12 13 project_id: 2
13 14 url: svn://localhost/test
14 15 id: 11
15 16 root_url: svn://localhost
16 17 password: ""
17 18 login: ""
18 19 type: Repository::Subversion
19 20 is_default: true
21 created_on: 2006-07-19 19:04:21 +02:00
@@ -1,492 +1,514
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 = {'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 = {
115 115 'fixes , closes' => {
116 116 'status_id' => IssueStatus.where(:is_closed => true).first.id.to_s
117 117 }
118 118 }
119 119 Setting.commit_logtime_enabled = '1'
120 120
121 121 c = Changeset.new(:repository => Project.find(1).repository,
122 122 :committed_on => Time.now,
123 123 :comments => 'This is a comment. Fixes #1 @4.5, #2 @1',
124 124 :user => User.find(2))
125 125 assert_difference 'TimeEntry.count', 2 do
126 126 c.scan_comment_for_issue_ids
127 127 end
128 128
129 129 assert_equal [1, 2], c.issue_ids.sort
130 130 assert Issue.find(1).closed?
131 131 assert Issue.find(2).closed?
132 132
133 133 times = TimeEntry.all(:order => 'id desc', :limit => 2)
134 134 assert_equal [1, 2], times.collect(&:issue_id).sort
135 135 end
136 136
137 137 def test_ref_keywords_any_line_start
138 138 Setting.commit_ref_keywords = '*'
139 139 c = Changeset.new(:repository => Project.find(1).repository,
140 140 :committed_on => Time.now,
141 141 :comments => '#1 is the reason of this commit',
142 142 :revision => '12345')
143 143 assert c.save
144 144 assert_equal [1], c.issue_ids.sort
145 145 end
146 146
147 147 def test_ref_keywords_allow_brackets_around_a_issue_number
148 148 Setting.commit_ref_keywords = '*'
149 149 c = Changeset.new(:repository => Project.find(1).repository,
150 150 :committed_on => Time.now,
151 151 :comments => '[#1] Worked on this issue',
152 152 :revision => '12345')
153 153 assert c.save
154 154 assert_equal [1], c.issue_ids.sort
155 155 end
156 156
157 157 def test_ref_keywords_allow_brackets_around_multiple_issue_numbers
158 158 Setting.commit_ref_keywords = '*'
159 159 c = Changeset.new(:repository => Project.find(1).repository,
160 160 :committed_on => Time.now,
161 161 :comments => '[#1 #2, #3] Worked on these',
162 162 :revision => '12345')
163 163 assert c.save
164 164 assert_equal [1,2,3], c.issue_ids.sort
165 165 end
166 166
167 167 def test_update_keywords_with_multiple_rules
168 168 Setting.commit_update_keywords = {
169 169 'fixes, closes' => {'status_id' => '5'},
170 170 'resolves' => {'status_id' => '3'}
171 171 }
172 172 issue1 = Issue.generate!
173 173 issue2 = Issue.generate!
174 174
175 175 c = Changeset.new(:repository => Project.find(1).repository,
176 176 :committed_on => Time.now,
177 177 :comments => "Closes ##{issue1.id}\nResolves ##{issue2.id}",
178 178 :revision => '12345')
179 179 assert c.save
180 180 assert_equal 5, issue1.reload.status_id
181 181 assert_equal 3, issue2.reload.status_id
182 182 end
183 183
184 184 def test_commit_referencing_a_subproject_issue
185 185 c = Changeset.new(:repository => Project.find(1).repository,
186 186 :committed_on => Time.now,
187 187 :comments => 'refs #5, a subproject issue',
188 188 :revision => '12345')
189 189 assert c.save
190 190 assert_equal [5], c.issue_ids.sort
191 191 assert c.issues.first.project != c.project
192 192 end
193 193
194 194 def test_commit_closing_a_subproject_issue
195 195 with_settings :commit_update_keywords => {'closes' => {'status_id' => '5'}},
196 196 :default_language => 'en' do
197 197 issue = Issue.find(5)
198 198 assert !issue.closed?
199 199 assert_difference 'Journal.count' do
200 200 c = Changeset.new(:repository => Project.find(1).repository,
201 201 :committed_on => Time.now,
202 202 :comments => 'closes #5, a subproject issue',
203 203 :revision => '12345')
204 204 assert c.save
205 205 end
206 206 assert issue.reload.closed?
207 207 journal = Journal.first(:order => 'id DESC')
208 208 assert_equal issue, journal.issue
209 209 assert_include "Applied in changeset ecookbook:r12345.", journal.notes
210 210 end
211 211 end
212 212
213 213 def test_commit_referencing_a_parent_project_issue
214 214 # repository of child project
215 215 r = Repository::Subversion.create!(
216 216 :project => Project.find(3),
217 217 :url => 'svn://localhost/test')
218 218 c = Changeset.new(:repository => r,
219 219 :committed_on => Time.now,
220 220 :comments => 'refs #2, an issue of a parent project',
221 221 :revision => '12345')
222 222 assert c.save
223 223 assert_equal [2], c.issue_ids.sort
224 224 assert c.issues.first.project != c.project
225 225 end
226 226
227 227 def test_commit_referencing_a_project_with_commit_cross_project_ref_disabled
228 228 r = Repository::Subversion.create!(
229 229 :project => Project.find(3),
230 230 :url => 'svn://localhost/test')
231 231
232 232 with_settings :commit_cross_project_ref => '0' do
233 233 c = Changeset.new(:repository => r,
234 234 :committed_on => Time.now,
235 235 :comments => 'refs #4, an issue of a different project',
236 236 :revision => '12345')
237 237 assert c.save
238 238 assert_equal [], c.issue_ids
239 239 end
240 240 end
241 241
242 242 def test_commit_referencing_a_project_with_commit_cross_project_ref_enabled
243 243 r = Repository::Subversion.create!(
244 244 :project => Project.find(3),
245 245 :url => 'svn://localhost/test')
246 246
247 247 with_settings :commit_cross_project_ref => '1' do
248 248 c = Changeset.new(:repository => r,
249 249 :committed_on => Time.now,
250 250 :comments => 'refs #4, an issue of a different project',
251 251 :revision => '12345')
252 252 assert c.save
253 253 assert_equal [4], c.issue_ids
254 254 end
255 255 end
256 256
257 def test_old_commits_should_not_update_issues_nor_log_time
258 Setting.commit_ref_keywords = '*'
259 Setting.commit_update_keywords = {'fixes , closes' => {'status_id' => '5', 'done_ratio' => '90'}}
260 Setting.commit_logtime_enabled = '1'
261
262 repository = Project.find(1).repository
263 repository.created_on = Time.now
264 repository.save!
265
266 c = Changeset.new(:repository => repository,
267 :committed_on => 1.month.ago,
268 :comments => 'New commit (#2). Fixes #1 @1h',
269 :revision => '12345')
270 assert_no_difference 'TimeEntry.count' do
271 assert c.save
272 end
273 assert_equal [1, 2], c.issue_ids.sort
274 issue = Issue.find(1)
275 assert_equal 1, issue.status_id
276 assert_equal 0, issue.done_ratio
277 end
278
257 279 def test_text_tag_revision
258 280 c = Changeset.new(:revision => '520')
259 281 assert_equal 'r520', c.text_tag
260 282 end
261 283
262 284 def test_text_tag_revision_with_same_project
263 285 c = Changeset.new(:revision => '520', :repository => Project.find(1).repository)
264 286 assert_equal 'r520', c.text_tag(Project.find(1))
265 287 end
266 288
267 289 def test_text_tag_revision_with_different_project
268 290 c = Changeset.new(:revision => '520', :repository => Project.find(1).repository)
269 291 assert_equal 'ecookbook:r520', c.text_tag(Project.find(2))
270 292 end
271 293
272 294 def test_text_tag_revision_with_repository_identifier
273 295 r = Repository::Subversion.create!(
274 296 :project_id => 1,
275 297 :url => 'svn://localhost/test',
276 298 :identifier => 'documents')
277 299
278 300 c = Changeset.new(:revision => '520', :repository => r)
279 301 assert_equal 'documents|r520', c.text_tag
280 302 assert_equal 'ecookbook:documents|r520', c.text_tag(Project.find(2))
281 303 end
282 304
283 305 def test_text_tag_hash
284 306 c = Changeset.new(
285 307 :scmid => '7234cb2750b63f47bff735edc50a1c0a433c2518',
286 308 :revision => '7234cb2750b63f47bff735edc50a1c0a433c2518')
287 309 assert_equal 'commit:7234cb2750b63f47bff735edc50a1c0a433c2518', c.text_tag
288 310 end
289 311
290 312 def test_text_tag_hash_with_same_project
291 313 c = Changeset.new(:revision => '7234cb27', :scmid => '7234cb27', :repository => Project.find(1).repository)
292 314 assert_equal 'commit:7234cb27', c.text_tag(Project.find(1))
293 315 end
294 316
295 317 def test_text_tag_hash_with_different_project
296 318 c = Changeset.new(:revision => '7234cb27', :scmid => '7234cb27', :repository => Project.find(1).repository)
297 319 assert_equal 'ecookbook:commit:7234cb27', c.text_tag(Project.find(2))
298 320 end
299 321
300 322 def test_text_tag_hash_all_number
301 323 c = Changeset.new(:scmid => '0123456789', :revision => '0123456789')
302 324 assert_equal 'commit:0123456789', c.text_tag
303 325 end
304 326
305 327 def test_previous
306 328 changeset = Changeset.find_by_revision('3')
307 329 assert_equal Changeset.find_by_revision('2'), changeset.previous
308 330 end
309 331
310 332 def test_previous_nil
311 333 changeset = Changeset.find_by_revision('1')
312 334 assert_nil changeset.previous
313 335 end
314 336
315 337 def test_next
316 338 changeset = Changeset.find_by_revision('2')
317 339 assert_equal Changeset.find_by_revision('3'), changeset.next
318 340 end
319 341
320 342 def test_next_nil
321 343 changeset = Changeset.find_by_revision('10')
322 344 assert_nil changeset.next
323 345 end
324 346
325 347 def test_comments_should_be_converted_to_utf8
326 348 proj = Project.find(3)
327 349 # str = File.read("#{RAILS_ROOT}/test/fixtures/encoding/iso-8859-1.txt")
328 350 str = "Texte encod\xe9 en ISO-8859-1."
329 351 str.force_encoding("ASCII-8BIT") if str.respond_to?(:force_encoding)
330 352 r = Repository::Bazaar.create!(
331 353 :project => proj,
332 354 :url => '/tmp/test/bazaar',
333 355 :log_encoding => 'ISO-8859-1' )
334 356 assert r
335 357 c = Changeset.new(:repository => r,
336 358 :committed_on => Time.now,
337 359 :revision => '123',
338 360 :scmid => '12345',
339 361 :comments => str)
340 362 assert( c.save )
341 363 str_utf8 = "Texte encod\xc3\xa9 en ISO-8859-1."
342 364 str_utf8.force_encoding("UTF-8") if str_utf8.respond_to?(:force_encoding)
343 365 assert_equal str_utf8, c.comments
344 366 end
345 367
346 368 def test_invalid_utf8_sequences_in_comments_should_be_replaced_latin1
347 369 proj = Project.find(3)
348 370 # str = File.read("#{RAILS_ROOT}/test/fixtures/encoding/iso-8859-1.txt")
349 371 str1 = "Texte encod\xe9 en ISO-8859-1."
350 372 str2 = "\xe9a\xe9b\xe9c\xe9d\xe9e test"
351 373 str1.force_encoding("UTF-8") if str1.respond_to?(:force_encoding)
352 374 str2.force_encoding("ASCII-8BIT") if str2.respond_to?(:force_encoding)
353 375 r = Repository::Bazaar.create!(
354 376 :project => proj,
355 377 :url => '/tmp/test/bazaar',
356 378 :log_encoding => 'UTF-8' )
357 379 assert r
358 380 c = Changeset.new(:repository => r,
359 381 :committed_on => Time.now,
360 382 :revision => '123',
361 383 :scmid => '12345',
362 384 :comments => str1,
363 385 :committer => str2)
364 386 assert( c.save )
365 387 assert_equal "Texte encod? en ISO-8859-1.", c.comments
366 388 assert_equal "?a?b?c?d?e test", c.committer
367 389 end
368 390
369 391 def test_invalid_utf8_sequences_in_comments_should_be_replaced_ja_jis
370 392 proj = Project.find(3)
371 393 str = "test\xb5\xfetest\xb5\xfe"
372 394 if str.respond_to?(:force_encoding)
373 395 str.force_encoding('ASCII-8BIT')
374 396 end
375 397 r = Repository::Bazaar.create!(
376 398 :project => proj,
377 399 :url => '/tmp/test/bazaar',
378 400 :log_encoding => 'ISO-2022-JP' )
379 401 assert r
380 402 c = Changeset.new(:repository => r,
381 403 :committed_on => Time.now,
382 404 :revision => '123',
383 405 :scmid => '12345',
384 406 :comments => str)
385 407 assert( c.save )
386 408 assert_equal "test??test??", c.comments
387 409 end
388 410
389 411 def test_comments_should_be_converted_all_latin1_to_utf8
390 412 s1 = "\xC2\x80"
391 413 s2 = "\xc3\x82\xc2\x80"
392 414 s4 = s2.dup
393 415 if s1.respond_to?(:force_encoding)
394 416 s3 = s1.dup
395 417 s1.force_encoding('ASCII-8BIT')
396 418 s2.force_encoding('ASCII-8BIT')
397 419 s3.force_encoding('ISO-8859-1')
398 420 s4.force_encoding('UTF-8')
399 421 assert_equal s3.encode('UTF-8'), s4
400 422 end
401 423 proj = Project.find(3)
402 424 r = Repository::Bazaar.create!(
403 425 :project => proj,
404 426 :url => '/tmp/test/bazaar',
405 427 :log_encoding => 'ISO-8859-1' )
406 428 assert r
407 429 c = Changeset.new(:repository => r,
408 430 :committed_on => Time.now,
409 431 :revision => '123',
410 432 :scmid => '12345',
411 433 :comments => s1)
412 434 assert( c.save )
413 435 assert_equal s4, c.comments
414 436 end
415 437
416 438 def test_invalid_utf8_sequences_in_paths_should_be_replaced
417 439 proj = Project.find(3)
418 440 str1 = "Texte encod\xe9 en ISO-8859-1"
419 441 str2 = "\xe9a\xe9b\xe9c\xe9d\xe9e test"
420 442 str1.force_encoding("UTF-8") if str1.respond_to?(:force_encoding)
421 443 str2.force_encoding("ASCII-8BIT") if str2.respond_to?(:force_encoding)
422 444 r = Repository::Bazaar.create!(
423 445 :project => proj,
424 446 :url => '/tmp/test/bazaar',
425 447 :log_encoding => 'UTF-8' )
426 448 assert r
427 449 cs = Changeset.new(
428 450 :repository => r,
429 451 :committed_on => Time.now,
430 452 :revision => '123',
431 453 :scmid => '12345',
432 454 :comments => "test")
433 455 assert(cs.save)
434 456 ch = Change.new(
435 457 :changeset => cs,
436 458 :action => "A",
437 459 :path => str1,
438 460 :from_path => str2,
439 461 :from_revision => "345")
440 462 assert(ch.save)
441 463 assert_equal "Texte encod? en ISO-8859-1", ch.path
442 464 assert_equal "?a?b?c?d?e test", ch.from_path
443 465 end
444 466
445 467 def test_comments_nil
446 468 proj = Project.find(3)
447 469 r = Repository::Bazaar.create!(
448 470 :project => proj,
449 471 :url => '/tmp/test/bazaar',
450 472 :log_encoding => 'ISO-8859-1' )
451 473 assert r
452 474 c = Changeset.new(:repository => r,
453 475 :committed_on => Time.now,
454 476 :revision => '123',
455 477 :scmid => '12345',
456 478 :comments => nil,
457 479 :committer => nil)
458 480 assert( c.save )
459 481 assert_equal "", c.comments
460 482 assert_equal nil, c.committer
461 483 if c.comments.respond_to?(:force_encoding)
462 484 assert_equal "UTF-8", c.comments.encoding.to_s
463 485 end
464 486 end
465 487
466 488 def test_comments_empty
467 489 proj = Project.find(3)
468 490 r = Repository::Bazaar.create!(
469 491 :project => proj,
470 492 :url => '/tmp/test/bazaar',
471 493 :log_encoding => 'ISO-8859-1' )
472 494 assert r
473 495 c = Changeset.new(:repository => r,
474 496 :committed_on => Time.now,
475 497 :revision => '123',
476 498 :scmid => '12345',
477 499 :comments => "",
478 500 :committer => "")
479 501 assert( c.save )
480 502 assert_equal "", c.comments
481 503 assert_equal "", c.committer
482 504 if c.comments.respond_to?(:force_encoding)
483 505 assert_equal "UTF-8", c.comments.encoding.to_s
484 506 assert_equal "UTF-8", c.committer.encoding.to_s
485 507 end
486 508 end
487 509
488 510 def test_identifier
489 511 c = Changeset.find_by_revision('1')
490 512 assert_equal c.revision, c.identifier
491 513 end
492 514 end
General Comments 0
You need to be logged in to leave comments. Login now