##// END OF EJS Templates
Merged r8917 from trunk....
Jean-Philippe Lang -
r8997:41dba6864aa0
parent child
Show More
@@ -1,269 +1,273
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
2 # Copyright (C) 2006-2011 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 require 'iconv'
18 require 'iconv'
19
19
20 class Changeset < ActiveRecord::Base
20 class Changeset < ActiveRecord::Base
21 belongs_to :repository
21 belongs_to :repository
22 belongs_to :user
22 belongs_to :user
23 has_many :changes, :dependent => :delete_all
23 has_many :changes, :dependent => :delete_all
24 has_and_belongs_to_many :issues
24 has_and_belongs_to_many :issues
25 has_and_belongs_to_many :parents,
25 has_and_belongs_to_many :parents,
26 :class_name => "Changeset",
26 :class_name => "Changeset",
27 :join_table => "#{table_name_prefix}changeset_parents#{table_name_suffix}",
27 :join_table => "#{table_name_prefix}changeset_parents#{table_name_suffix}",
28 :association_foreign_key => 'parent_id', :foreign_key => 'changeset_id'
28 :association_foreign_key => 'parent_id', :foreign_key => 'changeset_id'
29 has_and_belongs_to_many :children,
29 has_and_belongs_to_many :children,
30 :class_name => "Changeset",
30 :class_name => "Changeset",
31 :join_table => "#{table_name_prefix}changeset_parents#{table_name_suffix}",
31 :join_table => "#{table_name_prefix}changeset_parents#{table_name_suffix}",
32 :association_foreign_key => 'changeset_id', :foreign_key => 'parent_id'
32 :association_foreign_key => 'changeset_id', :foreign_key => 'parent_id'
33
33
34 acts_as_event :title => Proc.new {|o| "#{l(:label_revision)} #{o.format_identifier}" + (o.short_comments.blank? ? '' : (': ' + o.short_comments))},
34 acts_as_event :title => Proc.new {|o| "#{l(:label_revision)} #{o.format_identifier}" + (o.short_comments.blank? ? '' : (': ' + o.short_comments))},
35 :description => :long_comments,
35 :description => :long_comments,
36 :datetime => :committed_on,
36 :datetime => :committed_on,
37 :url => Proc.new {|o| {:controller => 'repositories', :action => 'revision', :id => o.repository.project, :rev => o.identifier}}
37 :url => Proc.new {|o| {:controller => 'repositories', :action => 'revision', :id => o.repository.project, :rev => o.identifier}}
38
38
39 acts_as_searchable :columns => 'comments',
39 acts_as_searchable :columns => 'comments',
40 :include => {:repository => :project},
40 :include => {:repository => :project},
41 :project_key => "#{Repository.table_name}.project_id",
41 :project_key => "#{Repository.table_name}.project_id",
42 :date_column => 'committed_on'
42 :date_column => 'committed_on'
43
43
44 acts_as_activity_provider :timestamp => "#{table_name}.committed_on",
44 acts_as_activity_provider :timestamp => "#{table_name}.committed_on",
45 :author_key => :user_id,
45 :author_key => :user_id,
46 :find_options => {:include => [:user, {:repository => :project}]}
46 :find_options => {:include => [:user, {:repository => :project}]}
47
47
48 validates_presence_of :repository_id, :revision, :committed_on, :commit_date
48 validates_presence_of :repository_id, :revision, :committed_on, :commit_date
49 validates_uniqueness_of :revision, :scope => :repository_id
49 validates_uniqueness_of :revision, :scope => :repository_id
50 validates_uniqueness_of :scmid, :scope => :repository_id, :allow_nil => true
50 validates_uniqueness_of :scmid, :scope => :repository_id, :allow_nil => true
51
51
52 named_scope :visible, lambda {|*args| { :include => {:repository => :project},
52 named_scope :visible, lambda {|*args| { :include => {:repository => :project},
53 :conditions => Project.allowed_to_condition(args.shift || User.current, :view_changesets, *args) } }
53 :conditions => Project.allowed_to_condition(args.shift || User.current, :view_changesets, *args) } }
54
54
55 after_create :scan_for_issues
55 after_create :scan_for_issues
56 before_create :before_create_cs
56 before_create :before_create_cs
57
57
58 def revision=(r)
58 def revision=(r)
59 write_attribute :revision, (r.nil? ? nil : r.to_s)
59 write_attribute :revision, (r.nil? ? nil : r.to_s)
60 end
60 end
61
61
62 # Returns the identifier of this changeset; depending on repository backends
62 # Returns the identifier of this changeset; depending on repository backends
63 def identifier
63 def identifier
64 if repository.class.respond_to? :changeset_identifier
64 if repository.class.respond_to? :changeset_identifier
65 repository.class.changeset_identifier self
65 repository.class.changeset_identifier self
66 else
66 else
67 revision.to_s
67 revision.to_s
68 end
68 end
69 end
69 end
70
70
71 def committed_on=(date)
71 def committed_on=(date)
72 self.commit_date = date
72 self.commit_date = date
73 super
73 super
74 end
74 end
75
75
76 # Returns the readable identifier
76 # Returns the readable identifier
77 def format_identifier
77 def format_identifier
78 if repository.class.respond_to? :format_changeset_identifier
78 if repository.class.respond_to? :format_changeset_identifier
79 repository.class.format_changeset_identifier self
79 repository.class.format_changeset_identifier self
80 else
80 else
81 identifier
81 identifier
82 end
82 end
83 end
83 end
84
84
85 def project
85 def project
86 repository.project
86 repository.project
87 end
87 end
88
88
89 def author
89 def author
90 user || committer.to_s.split('<').first
90 user || committer.to_s.split('<').first
91 end
91 end
92
92
93 def before_create_cs
93 def before_create_cs
94 self.committer = self.class.to_utf8(self.committer, repository.repo_log_encoding)
94 self.committer = self.class.to_utf8(self.committer, repository.repo_log_encoding)
95 self.comments = self.class.normalize_comments(
95 self.comments = self.class.normalize_comments(
96 self.comments, repository.repo_log_encoding)
96 self.comments, repository.repo_log_encoding)
97 self.user = repository.find_committer_user(self.committer)
97 self.user = repository.find_committer_user(self.committer)
98 end
98 end
99
99
100 def scan_for_issues
100 def scan_for_issues
101 scan_comment_for_issue_ids
101 scan_comment_for_issue_ids
102 end
102 end
103
103
104 TIMELOG_RE = /
104 TIMELOG_RE = /
105 (
105 (
106 ((\d+)(h|hours?))((\d+)(m|min)?)?
106 ((\d+)(h|hours?))((\d+)(m|min)?)?
107 |
107 |
108 ((\d+)(h|hours?|m|min))
108 ((\d+)(h|hours?|m|min))
109 |
109 |
110 (\d+):(\d+)
110 (\d+):(\d+)
111 |
111 |
112 (\d+([\.,]\d+)?)h?
112 (\d+([\.,]\d+)?)h?
113 )
113 )
114 /x
114 /x
115
115
116 def scan_comment_for_issue_ids
116 def scan_comment_for_issue_ids
117 return if comments.blank?
117 return if comments.blank?
118 # keywords used to reference issues
118 # keywords used to reference issues
119 ref_keywords = Setting.commit_ref_keywords.downcase.split(",").collect(&:strip)
119 ref_keywords = Setting.commit_ref_keywords.downcase.split(",").collect(&:strip)
120 ref_keywords_any = ref_keywords.delete('*')
120 ref_keywords_any = ref_keywords.delete('*')
121 # keywords used to fix issues
121 # keywords used to fix issues
122 fix_keywords = Setting.commit_fix_keywords.downcase.split(",").collect(&:strip)
122 fix_keywords = Setting.commit_fix_keywords.downcase.split(",").collect(&:strip)
123
123
124 kw_regexp = (ref_keywords + fix_keywords).collect{|kw| Regexp.escape(kw)}.join("|")
124 kw_regexp = (ref_keywords + fix_keywords).collect{|kw| Regexp.escape(kw)}.join("|")
125
125
126 referenced_issues = []
126 referenced_issues = []
127
127
128 comments.scan(/([\s\(\[,-]|^)((#{kw_regexp})[\s:]+)?(#\d+(\s+@#{TIMELOG_RE})?([\s,;&]+#\d+(\s+@#{TIMELOG_RE})?)*)(?=[[:punct:]]|\s|<|$)/i) do |match|
128 comments.scan(/([\s\(\[,-]|^)((#{kw_regexp})[\s:]+)?(#\d+(\s+@#{TIMELOG_RE})?([\s,;&]+#\d+(\s+@#{TIMELOG_RE})?)*)(?=[[:punct:]]|\s|<|$)/i) do |match|
129 action, refs = match[2], match[3]
129 action, refs = match[2], match[3]
130 next unless action.present? || ref_keywords_any
130 next unless action.present? || ref_keywords_any
131
131
132 refs.scan(/#(\d+)(\s+@#{TIMELOG_RE})?/).each do |m|
132 refs.scan(/#(\d+)(\s+@#{TIMELOG_RE})?/).each do |m|
133 issue, hours = find_referenced_issue_by_id(m[0].to_i), m[2]
133 issue, hours = find_referenced_issue_by_id(m[0].to_i), m[2]
134 if issue
134 if issue
135 referenced_issues << issue
135 referenced_issues << issue
136 fix_issue(issue) if fix_keywords.include?(action.to_s.downcase)
136 fix_issue(issue) if fix_keywords.include?(action.to_s.downcase)
137 log_time(issue, hours) if hours && Setting.commit_logtime_enabled?
137 log_time(issue, hours) if hours && Setting.commit_logtime_enabled?
138 end
138 end
139 end
139 end
140 end
140 end
141
141
142 referenced_issues.uniq!
142 referenced_issues.uniq!
143 self.issues = referenced_issues unless referenced_issues.empty?
143 self.issues = referenced_issues unless referenced_issues.empty?
144 end
144 end
145
145
146 def short_comments
146 def short_comments
147 @short_comments || split_comments.first
147 @short_comments || split_comments.first
148 end
148 end
149
149
150 def long_comments
150 def long_comments
151 @long_comments || split_comments.last
151 @long_comments || split_comments.last
152 end
152 end
153
153
154 def text_tag
154 def text_tag(ref_project=nil)
155 if scmid?
155 tag = if scmid?
156 "commit:#{scmid}"
156 "commit:#{scmid}"
157 else
157 else
158 "r#{revision}"
158 "r#{revision}"
159 end
159 end
160 if ref_project && project && ref_project != project
161 tag = "#{project.identifier}:#{tag}"
162 end
163 tag
160 end
164 end
161
165
162 # Returns the previous changeset
166 # Returns the previous changeset
163 def previous
167 def previous
164 @previous ||= Changeset.find(:first,
168 @previous ||= Changeset.find(:first,
165 :conditions => ['id < ? AND repository_id = ?',
169 :conditions => ['id < ? AND repository_id = ?',
166 self.id, self.repository_id],
170 self.id, self.repository_id],
167 :order => 'id DESC')
171 :order => 'id DESC')
168 end
172 end
169
173
170 # Returns the next changeset
174 # Returns the next changeset
171 def next
175 def next
172 @next ||= Changeset.find(:first,
176 @next ||= Changeset.find(:first,
173 :conditions => ['id > ? AND repository_id = ?',
177 :conditions => ['id > ? AND repository_id = ?',
174 self.id, self.repository_id],
178 self.id, self.repository_id],
175 :order => 'id ASC')
179 :order => 'id ASC')
176 end
180 end
177
181
178 # Creates a new Change from it's common parameters
182 # Creates a new Change from it's common parameters
179 def create_change(change)
183 def create_change(change)
180 Change.create(:changeset => self,
184 Change.create(:changeset => self,
181 :action => change[:action],
185 :action => change[:action],
182 :path => change[:path],
186 :path => change[:path],
183 :from_path => change[:from_path],
187 :from_path => change[:from_path],
184 :from_revision => change[:from_revision])
188 :from_revision => change[:from_revision])
185 end
189 end
186
190
187 private
191 private
188
192
189 # Finds an issue that can be referenced by the commit message
193 # Finds an issue that can be referenced by the commit message
190 # i.e. an issue that belong to the repository project, a subproject or a parent project
194 # i.e. an issue that belong to the repository project, a subproject or a parent project
191 def find_referenced_issue_by_id(id)
195 def find_referenced_issue_by_id(id)
192 return nil if id.blank?
196 return nil if id.blank?
193 issue = Issue.find_by_id(id.to_i, :include => :project)
197 issue = Issue.find_by_id(id.to_i, :include => :project)
194 if issue
198 if issue
195 unless issue.project &&
199 unless issue.project &&
196 (project == issue.project || project.is_ancestor_of?(issue.project) ||
200 (project == issue.project || project.is_ancestor_of?(issue.project) ||
197 project.is_descendant_of?(issue.project))
201 project.is_descendant_of?(issue.project))
198 issue = nil
202 issue = nil
199 end
203 end
200 end
204 end
201 issue
205 issue
202 end
206 end
203
207
204 def fix_issue(issue)
208 def fix_issue(issue)
205 status = IssueStatus.find_by_id(Setting.commit_fix_status_id.to_i)
209 status = IssueStatus.find_by_id(Setting.commit_fix_status_id.to_i)
206 if status.nil?
210 if status.nil?
207 logger.warn("No status matches commit_fix_status_id setting (#{Setting.commit_fix_status_id})") if logger
211 logger.warn("No status matches commit_fix_status_id setting (#{Setting.commit_fix_status_id})") if logger
208 return issue
212 return issue
209 end
213 end
210
214
211 # the issue may have been updated by the closure of another one (eg. duplicate)
215 # the issue may have been updated by the closure of another one (eg. duplicate)
212 issue.reload
216 issue.reload
213 # don't change the status is the issue is closed
217 # don't change the status is the issue is closed
214 return if issue.status && issue.status.is_closed?
218 return if issue.status && issue.status.is_closed?
215
219
216 journal = issue.init_journal(user || User.anonymous, ll(Setting.default_language, :text_status_changed_by_changeset, text_tag))
220 journal = issue.init_journal(user || User.anonymous, ll(Setting.default_language, :text_status_changed_by_changeset, text_tag(issue.project)))
217 issue.status = status
221 issue.status = status
218 unless Setting.commit_fix_done_ratio.blank?
222 unless Setting.commit_fix_done_ratio.blank?
219 issue.done_ratio = Setting.commit_fix_done_ratio.to_i
223 issue.done_ratio = Setting.commit_fix_done_ratio.to_i
220 end
224 end
221 Redmine::Hook.call_hook(:model_changeset_scan_commit_for_issue_ids_pre_issue_update,
225 Redmine::Hook.call_hook(:model_changeset_scan_commit_for_issue_ids_pre_issue_update,
222 { :changeset => self, :issue => issue })
226 { :changeset => self, :issue => issue })
223 unless issue.save
227 unless issue.save
224 logger.warn("Issue ##{issue.id} could not be saved by changeset #{id}: #{issue.errors.full_messages}") if logger
228 logger.warn("Issue ##{issue.id} could not be saved by changeset #{id}: #{issue.errors.full_messages}") if logger
225 end
229 end
226 issue
230 issue
227 end
231 end
228
232
229 def log_time(issue, hours)
233 def log_time(issue, hours)
230 time_entry = TimeEntry.new(
234 time_entry = TimeEntry.new(
231 :user => user,
235 :user => user,
232 :hours => hours,
236 :hours => hours,
233 :issue => issue,
237 :issue => issue,
234 :spent_on => commit_date,
238 :spent_on => commit_date,
235 :comments => l(:text_time_logged_by_changeset, :value => text_tag,
239 :comments => l(:text_time_logged_by_changeset, :value => text_tag(issue.project),
236 :locale => Setting.default_language)
240 :locale => Setting.default_language)
237 )
241 )
238 time_entry.activity = log_time_activity unless log_time_activity.nil?
242 time_entry.activity = log_time_activity unless log_time_activity.nil?
239
243
240 unless time_entry.save
244 unless time_entry.save
241 logger.warn("TimeEntry could not be created by changeset #{id}: #{time_entry.errors.full_messages}") if logger
245 logger.warn("TimeEntry could not be created by changeset #{id}: #{time_entry.errors.full_messages}") if logger
242 end
246 end
243 time_entry
247 time_entry
244 end
248 end
245
249
246 def log_time_activity
250 def log_time_activity
247 if Setting.commit_logtime_activity_id.to_i > 0
251 if Setting.commit_logtime_activity_id.to_i > 0
248 TimeEntryActivity.find_by_id(Setting.commit_logtime_activity_id.to_i)
252 TimeEntryActivity.find_by_id(Setting.commit_logtime_activity_id.to_i)
249 end
253 end
250 end
254 end
251
255
252 def split_comments
256 def split_comments
253 comments =~ /\A(.+?)\r?\n(.*)$/m
257 comments =~ /\A(.+?)\r?\n(.*)$/m
254 @short_comments = $1 || comments
258 @short_comments = $1 || comments
255 @long_comments = $2.to_s.strip
259 @long_comments = $2.to_s.strip
256 return @short_comments, @long_comments
260 return @short_comments, @long_comments
257 end
261 end
258
262
259 public
263 public
260
264
261 # Strips and reencodes a commit log before insertion into the database
265 # Strips and reencodes a commit log before insertion into the database
262 def self.normalize_comments(str, encoding)
266 def self.normalize_comments(str, encoding)
263 Changeset.to_utf8(str.to_s.strip, encoding)
267 Changeset.to_utf8(str.to_s.strip, encoding)
264 end
268 end
265
269
266 def self.to_utf8(str, encoding)
270 def self.to_utf8(str, encoding)
267 Redmine::CodesetUtil.to_utf8(str, encoding)
271 Redmine::CodesetUtil.to_utf8(str, encoding)
268 end
272 end
269 end
273 end
@@ -1,399 +1,437
1 # encoding: utf-8
1 # encoding: utf-8
2 #
2 #
3 # Redmine - project management software
3 # Redmine - project management software
4 # Copyright (C) 2006-2011 Jean-Philippe Lang
4 # Copyright (C) 2006-2011 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 setup
31 def setup
32 end
32 end
33
33
34 def test_ref_keywords_any
34 def test_ref_keywords_any
35 ActionMailer::Base.deliveries.clear
35 ActionMailer::Base.deliveries.clear
36 Setting.commit_fix_status_id = IssueStatus.find(
36 Setting.commit_fix_status_id = IssueStatus.find(
37 :first, :conditions => ["is_closed = ?", true]).id
37 :first, :conditions => ["is_closed = ?", true]).id
38 Setting.commit_fix_done_ratio = '90'
38 Setting.commit_fix_done_ratio = '90'
39 Setting.commit_ref_keywords = '*'
39 Setting.commit_ref_keywords = '*'
40 Setting.commit_fix_keywords = 'fixes , closes'
40 Setting.commit_fix_keywords = 'fixes , closes'
41
41
42 c = Changeset.new(:repository => Project.find(1).repository,
42 c = Changeset.new(:repository => Project.find(1).repository,
43 :committed_on => Time.now,
43 :committed_on => Time.now,
44 :comments => 'New commit (#2). Fixes #1',
44 :comments => 'New commit (#2). Fixes #1',
45 :revision => '12345')
45 :revision => '12345')
46 assert c.save
46 assert c.save
47 assert_equal [1, 2], c.issue_ids.sort
47 assert_equal [1, 2], c.issue_ids.sort
48 fixed = Issue.find(1)
48 fixed = Issue.find(1)
49 assert fixed.closed?
49 assert fixed.closed?
50 assert_equal 90, fixed.done_ratio
50 assert_equal 90, fixed.done_ratio
51 assert_equal 1, ActionMailer::Base.deliveries.size
51 assert_equal 1, ActionMailer::Base.deliveries.size
52 end
52 end
53
53
54 def test_ref_keywords
54 def test_ref_keywords
55 Setting.commit_ref_keywords = 'refs'
55 Setting.commit_ref_keywords = 'refs'
56 Setting.commit_fix_keywords = ''
56 Setting.commit_fix_keywords = ''
57 c = Changeset.new(:repository => Project.find(1).repository,
57 c = Changeset.new(:repository => Project.find(1).repository,
58 :committed_on => Time.now,
58 :committed_on => Time.now,
59 :comments => 'Ignores #2. Refs #1',
59 :comments => 'Ignores #2. Refs #1',
60 :revision => '12345')
60 :revision => '12345')
61 assert c.save
61 assert c.save
62 assert_equal [1], c.issue_ids.sort
62 assert_equal [1], c.issue_ids.sort
63 end
63 end
64
64
65 def test_ref_keywords_any_only
65 def test_ref_keywords_any_only
66 Setting.commit_ref_keywords = '*'
66 Setting.commit_ref_keywords = '*'
67 Setting.commit_fix_keywords = ''
67 Setting.commit_fix_keywords = ''
68 c = Changeset.new(:repository => Project.find(1).repository,
68 c = Changeset.new(:repository => Project.find(1).repository,
69 :committed_on => Time.now,
69 :committed_on => Time.now,
70 :comments => 'Ignores #2. Refs #1',
70 :comments => 'Ignores #2. Refs #1',
71 :revision => '12345')
71 :revision => '12345')
72 assert c.save
72 assert c.save
73 assert_equal [1, 2], c.issue_ids.sort
73 assert_equal [1, 2], c.issue_ids.sort
74 end
74 end
75
75
76 def test_ref_keywords_any_with_timelog
76 def test_ref_keywords_any_with_timelog
77 Setting.commit_ref_keywords = '*'
77 Setting.commit_ref_keywords = '*'
78 Setting.commit_logtime_enabled = '1'
78 Setting.commit_logtime_enabled = '1'
79
79
80 {
80 {
81 '2' => 2.0,
81 '2' => 2.0,
82 '2h' => 2.0,
82 '2h' => 2.0,
83 '2hours' => 2.0,
83 '2hours' => 2.0,
84 '15m' => 0.25,
84 '15m' => 0.25,
85 '15min' => 0.25,
85 '15min' => 0.25,
86 '3h15' => 3.25,
86 '3h15' => 3.25,
87 '3h15m' => 3.25,
87 '3h15m' => 3.25,
88 '3h15min' => 3.25,
88 '3h15min' => 3.25,
89 '3:15' => 3.25,
89 '3:15' => 3.25,
90 '3.25' => 3.25,
90 '3.25' => 3.25,
91 '3.25h' => 3.25,
91 '3.25h' => 3.25,
92 '3,25' => 3.25,
92 '3,25' => 3.25,
93 '3,25h' => 3.25,
93 '3,25h' => 3.25,
94 }.each do |syntax, expected_hours|
94 }.each do |syntax, expected_hours|
95 c = Changeset.new(:repository => Project.find(1).repository,
95 c = Changeset.new(:repository => Project.find(1).repository,
96 :committed_on => 24.hours.ago,
96 :committed_on => 24.hours.ago,
97 :comments => "Worked on this issue #1 @#{syntax}",
97 :comments => "Worked on this issue #1 @#{syntax}",
98 :revision => '520',
98 :revision => '520',
99 :user => User.find(2))
99 :user => User.find(2))
100 assert_difference 'TimeEntry.count' do
100 assert_difference 'TimeEntry.count' do
101 c.scan_comment_for_issue_ids
101 c.scan_comment_for_issue_ids
102 end
102 end
103 assert_equal [1], c.issue_ids.sort
103 assert_equal [1], c.issue_ids.sort
104
104
105 time = TimeEntry.first(:order => 'id desc')
105 time = TimeEntry.first(:order => 'id desc')
106 assert_equal 1, time.issue_id
106 assert_equal 1, time.issue_id
107 assert_equal 1, time.project_id
107 assert_equal 1, time.project_id
108 assert_equal 2, time.user_id
108 assert_equal 2, time.user_id
109 assert_equal expected_hours, time.hours,
109 assert_equal expected_hours, time.hours,
110 "@#{syntax} should be logged as #{expected_hours} hours but was #{time.hours}"
110 "@#{syntax} should be logged as #{expected_hours} hours but was #{time.hours}"
111 assert_equal Date.yesterday, time.spent_on
111 assert_equal Date.yesterday, time.spent_on
112 assert time.activity.is_default?
112 assert time.activity.is_default?
113 assert time.comments.include?('r520'),
113 assert time.comments.include?('r520'),
114 "r520 was expected in time_entry comments: #{time.comments}"
114 "r520 was expected in time_entry comments: #{time.comments}"
115 end
115 end
116 end
116 end
117
117
118 def test_ref_keywords_closing_with_timelog
118 def test_ref_keywords_closing_with_timelog
119 Setting.commit_fix_status_id = IssueStatus.find(
119 Setting.commit_fix_status_id = IssueStatus.find(
120 :first, :conditions => ["is_closed = ?", true]).id
120 :first, :conditions => ["is_closed = ?", true]).id
121 Setting.commit_ref_keywords = '*'
121 Setting.commit_ref_keywords = '*'
122 Setting.commit_fix_keywords = 'fixes , closes'
122 Setting.commit_fix_keywords = 'fixes , closes'
123 Setting.commit_logtime_enabled = '1'
123 Setting.commit_logtime_enabled = '1'
124
124
125 c = Changeset.new(:repository => Project.find(1).repository,
125 c = Changeset.new(:repository => Project.find(1).repository,
126 :committed_on => Time.now,
126 :committed_on => Time.now,
127 :comments => 'This is a comment. Fixes #1 @4.5, #2 @1',
127 :comments => 'This is a comment. Fixes #1 @4.5, #2 @1',
128 :user => User.find(2))
128 :user => User.find(2))
129 assert_difference 'TimeEntry.count', 2 do
129 assert_difference 'TimeEntry.count', 2 do
130 c.scan_comment_for_issue_ids
130 c.scan_comment_for_issue_ids
131 end
131 end
132
132
133 assert_equal [1, 2], c.issue_ids.sort
133 assert_equal [1, 2], c.issue_ids.sort
134 assert Issue.find(1).closed?
134 assert Issue.find(1).closed?
135 assert Issue.find(2).closed?
135 assert Issue.find(2).closed?
136
136
137 times = TimeEntry.all(:order => 'id desc', :limit => 2)
137 times = TimeEntry.all(:order => 'id desc', :limit => 2)
138 assert_equal [1, 2], times.collect(&:issue_id).sort
138 assert_equal [1, 2], times.collect(&:issue_id).sort
139 end
139 end
140
140
141 def test_ref_keywords_any_line_start
141 def test_ref_keywords_any_line_start
142 Setting.commit_ref_keywords = '*'
142 Setting.commit_ref_keywords = '*'
143 c = Changeset.new(:repository => Project.find(1).repository,
143 c = Changeset.new(:repository => Project.find(1).repository,
144 :committed_on => Time.now,
144 :committed_on => Time.now,
145 :comments => '#1 is the reason of this commit',
145 :comments => '#1 is the reason of this commit',
146 :revision => '12345')
146 :revision => '12345')
147 assert c.save
147 assert c.save
148 assert_equal [1], c.issue_ids.sort
148 assert_equal [1], c.issue_ids.sort
149 end
149 end
150
150
151 def test_ref_keywords_allow_brackets_around_a_issue_number
151 def test_ref_keywords_allow_brackets_around_a_issue_number
152 Setting.commit_ref_keywords = '*'
152 Setting.commit_ref_keywords = '*'
153 c = Changeset.new(:repository => Project.find(1).repository,
153 c = Changeset.new(:repository => Project.find(1).repository,
154 :committed_on => Time.now,
154 :committed_on => Time.now,
155 :comments => '[#1] Worked on this issue',
155 :comments => '[#1] Worked on this issue',
156 :revision => '12345')
156 :revision => '12345')
157 assert c.save
157 assert c.save
158 assert_equal [1], c.issue_ids.sort
158 assert_equal [1], c.issue_ids.sort
159 end
159 end
160
160
161 def test_ref_keywords_allow_brackets_around_multiple_issue_numbers
161 def test_ref_keywords_allow_brackets_around_multiple_issue_numbers
162 Setting.commit_ref_keywords = '*'
162 Setting.commit_ref_keywords = '*'
163 c = Changeset.new(:repository => Project.find(1).repository,
163 c = Changeset.new(:repository => Project.find(1).repository,
164 :committed_on => Time.now,
164 :committed_on => Time.now,
165 :comments => '[#1 #2, #3] Worked on these',
165 :comments => '[#1 #2, #3] Worked on these',
166 :revision => '12345')
166 :revision => '12345')
167 assert c.save
167 assert c.save
168 assert_equal [1,2,3], c.issue_ids.sort
168 assert_equal [1,2,3], c.issue_ids.sort
169 end
169 end
170
170
171 def test_commit_referencing_a_subproject_issue
171 def test_commit_referencing_a_subproject_issue
172 c = Changeset.new(:repository => Project.find(1).repository,
172 c = Changeset.new(:repository => Project.find(1).repository,
173 :committed_on => Time.now,
173 :committed_on => Time.now,
174 :comments => 'refs #5, a subproject issue',
174 :comments => 'refs #5, a subproject issue',
175 :revision => '12345')
175 :revision => '12345')
176 assert c.save
176 assert c.save
177 assert_equal [5], c.issue_ids.sort
177 assert_equal [5], c.issue_ids.sort
178 assert c.issues.first.project != c.project
178 assert c.issues.first.project != c.project
179 end
179 end
180
180
181 def test_commit_closing_a_subproject_issue
182 with_settings :commit_fix_status_id => 5, :commit_fix_keywords => 'closes' do
183 issue = Issue.find(5)
184 assert !issue.closed?
185 assert_difference 'Journal.count' do
186 c = Changeset.new(:repository => Project.find(1).repository,
187 :committed_on => Time.now,
188 :comments => 'closes #5, a subproject issue',
189 :revision => '12345')
190 assert c.save
191 end
192 assert issue.reload.closed?
193 journal = Journal.first(:order => 'id DESC')
194 assert_equal issue, journal.issue
195 assert_include "Applied in changeset ecookbook:r12345.", journal.notes
196 end
197 end
198
181 def test_commit_referencing_a_parent_project_issue
199 def test_commit_referencing_a_parent_project_issue
182 # repository of child project
200 # repository of child project
183 r = Repository::Subversion.create!(
201 r = Repository::Subversion.create!(
184 :project => Project.find(3),
202 :project => Project.find(3),
185 :url => 'svn://localhost/test')
203 :url => 'svn://localhost/test')
186 c = Changeset.new(:repository => r,
204 c = Changeset.new(:repository => r,
187 :committed_on => Time.now,
205 :committed_on => Time.now,
188 :comments => 'refs #2, an issue of a parent project',
206 :comments => 'refs #2, an issue of a parent project',
189 :revision => '12345')
207 :revision => '12345')
190 assert c.save
208 assert c.save
191 assert_equal [2], c.issue_ids.sort
209 assert_equal [2], c.issue_ids.sort
192 assert c.issues.first.project != c.project
210 assert c.issues.first.project != c.project
193 end
211 end
194
212
195 def test_text_tag_revision
213 def test_text_tag_revision
196 c = Changeset.new(:revision => '520')
214 c = Changeset.new(:revision => '520')
197 assert_equal 'r520', c.text_tag
215 assert_equal 'r520', c.text_tag
198 end
216 end
199
217
218 def test_text_tag_revision_with_same_project
219 c = Changeset.new(:revision => '520', :repository => Project.find(1).repository)
220 assert_equal 'r520', c.text_tag(Project.find(1))
221 end
222
223 def test_text_tag_revision_with_different_project
224 c = Changeset.new(:revision => '520', :repository => Project.find(1).repository)
225 assert_equal 'ecookbook:r520', c.text_tag(Project.find(2))
226 end
227
200 def test_text_tag_hash
228 def test_text_tag_hash
201 c = Changeset.new(
229 c = Changeset.new(
202 :scmid => '7234cb2750b63f47bff735edc50a1c0a433c2518',
230 :scmid => '7234cb2750b63f47bff735edc50a1c0a433c2518',
203 :revision => '7234cb2750b63f47bff735edc50a1c0a433c2518')
231 :revision => '7234cb2750b63f47bff735edc50a1c0a433c2518')
204 assert_equal 'commit:7234cb2750b63f47bff735edc50a1c0a433c2518', c.text_tag
232 assert_equal 'commit:7234cb2750b63f47bff735edc50a1c0a433c2518', c.text_tag
205 end
233 end
206
234
235 def test_text_tag_hash_with_same_project
236 c = Changeset.new(:revision => '7234cb27', :scmid => '7234cb27', :repository => Project.find(1).repository)
237 assert_equal 'commit:7234cb27', c.text_tag(Project.find(1))
238 end
239
240 def test_text_tag_hash_with_different_project
241 c = Changeset.new(:revision => '7234cb27', :scmid => '7234cb27', :repository => Project.find(1).repository)
242 assert_equal 'ecookbook:commit:7234cb27', c.text_tag(Project.find(2))
243 end
244
207 def test_text_tag_hash_all_number
245 def test_text_tag_hash_all_number
208 c = Changeset.new(:scmid => '0123456789', :revision => '0123456789')
246 c = Changeset.new(:scmid => '0123456789', :revision => '0123456789')
209 assert_equal 'commit:0123456789', c.text_tag
247 assert_equal 'commit:0123456789', c.text_tag
210 end
248 end
211
249
212 def test_previous
250 def test_previous
213 changeset = Changeset.find_by_revision('3')
251 changeset = Changeset.find_by_revision('3')
214 assert_equal Changeset.find_by_revision('2'), changeset.previous
252 assert_equal Changeset.find_by_revision('2'), changeset.previous
215 end
253 end
216
254
217 def test_previous_nil
255 def test_previous_nil
218 changeset = Changeset.find_by_revision('1')
256 changeset = Changeset.find_by_revision('1')
219 assert_nil changeset.previous
257 assert_nil changeset.previous
220 end
258 end
221
259
222 def test_next
260 def test_next
223 changeset = Changeset.find_by_revision('2')
261 changeset = Changeset.find_by_revision('2')
224 assert_equal Changeset.find_by_revision('3'), changeset.next
262 assert_equal Changeset.find_by_revision('3'), changeset.next
225 end
263 end
226
264
227 def test_next_nil
265 def test_next_nil
228 changeset = Changeset.find_by_revision('10')
266 changeset = Changeset.find_by_revision('10')
229 assert_nil changeset.next
267 assert_nil changeset.next
230 end
268 end
231
269
232 def test_comments_should_be_converted_to_utf8
270 def test_comments_should_be_converted_to_utf8
233 proj = Project.find(3)
271 proj = Project.find(3)
234 # str = File.read("#{RAILS_ROOT}/test/fixtures/encoding/iso-8859-1.txt")
272 # str = File.read("#{RAILS_ROOT}/test/fixtures/encoding/iso-8859-1.txt")
235 str = "Texte encod\xe9 en ISO-8859-1."
273 str = "Texte encod\xe9 en ISO-8859-1."
236 str.force_encoding("ASCII-8BIT") if str.respond_to?(:force_encoding)
274 str.force_encoding("ASCII-8BIT") if str.respond_to?(:force_encoding)
237 r = Repository::Bazaar.create!(
275 r = Repository::Bazaar.create!(
238 :project => proj,
276 :project => proj,
239 :url => '/tmp/test/bazaar',
277 :url => '/tmp/test/bazaar',
240 :log_encoding => 'ISO-8859-1' )
278 :log_encoding => 'ISO-8859-1' )
241 assert r
279 assert r
242 c = Changeset.new(:repository => r,
280 c = Changeset.new(:repository => r,
243 :committed_on => Time.now,
281 :committed_on => Time.now,
244 :revision => '123',
282 :revision => '123',
245 :scmid => '12345',
283 :scmid => '12345',
246 :comments => str)
284 :comments => str)
247 assert( c.save )
285 assert( c.save )
248 str_utf8 = "Texte encod\xc3\xa9 en ISO-8859-1."
286 str_utf8 = "Texte encod\xc3\xa9 en ISO-8859-1."
249 str_utf8.force_encoding("UTF-8") if str_utf8.respond_to?(:force_encoding)
287 str_utf8.force_encoding("UTF-8") if str_utf8.respond_to?(:force_encoding)
250 assert_equal str_utf8, c.comments
288 assert_equal str_utf8, c.comments
251 end
289 end
252
290
253 def test_invalid_utf8_sequences_in_comments_should_be_replaced_latin1
291 def test_invalid_utf8_sequences_in_comments_should_be_replaced_latin1
254 proj = Project.find(3)
292 proj = Project.find(3)
255 # str = File.read("#{RAILS_ROOT}/test/fixtures/encoding/iso-8859-1.txt")
293 # str = File.read("#{RAILS_ROOT}/test/fixtures/encoding/iso-8859-1.txt")
256 str1 = "Texte encod\xe9 en ISO-8859-1."
294 str1 = "Texte encod\xe9 en ISO-8859-1."
257 str2 = "\xe9a\xe9b\xe9c\xe9d\xe9e test"
295 str2 = "\xe9a\xe9b\xe9c\xe9d\xe9e test"
258 str1.force_encoding("UTF-8") if str1.respond_to?(:force_encoding)
296 str1.force_encoding("UTF-8") if str1.respond_to?(:force_encoding)
259 str2.force_encoding("ASCII-8BIT") if str2.respond_to?(:force_encoding)
297 str2.force_encoding("ASCII-8BIT") if str2.respond_to?(:force_encoding)
260 r = Repository::Bazaar.create!(
298 r = Repository::Bazaar.create!(
261 :project => proj,
299 :project => proj,
262 :url => '/tmp/test/bazaar',
300 :url => '/tmp/test/bazaar',
263 :log_encoding => 'UTF-8' )
301 :log_encoding => 'UTF-8' )
264 assert r
302 assert r
265 c = Changeset.new(:repository => r,
303 c = Changeset.new(:repository => r,
266 :committed_on => Time.now,
304 :committed_on => Time.now,
267 :revision => '123',
305 :revision => '123',
268 :scmid => '12345',
306 :scmid => '12345',
269 :comments => str1,
307 :comments => str1,
270 :committer => str2)
308 :committer => str2)
271 assert( c.save )
309 assert( c.save )
272 assert_equal "Texte encod? en ISO-8859-1.", c.comments
310 assert_equal "Texte encod? en ISO-8859-1.", c.comments
273 assert_equal "?a?b?c?d?e test", c.committer
311 assert_equal "?a?b?c?d?e test", c.committer
274 end
312 end
275
313
276 def test_invalid_utf8_sequences_in_comments_should_be_replaced_ja_jis
314 def test_invalid_utf8_sequences_in_comments_should_be_replaced_ja_jis
277 proj = Project.find(3)
315 proj = Project.find(3)
278 str = "test\xb5\xfetest\xb5\xfe"
316 str = "test\xb5\xfetest\xb5\xfe"
279 if str.respond_to?(:force_encoding)
317 if str.respond_to?(:force_encoding)
280 str.force_encoding('ASCII-8BIT')
318 str.force_encoding('ASCII-8BIT')
281 end
319 end
282 r = Repository::Bazaar.create!(
320 r = Repository::Bazaar.create!(
283 :project => proj,
321 :project => proj,
284 :url => '/tmp/test/bazaar',
322 :url => '/tmp/test/bazaar',
285 :log_encoding => 'ISO-2022-JP' )
323 :log_encoding => 'ISO-2022-JP' )
286 assert r
324 assert r
287 c = Changeset.new(:repository => r,
325 c = Changeset.new(:repository => r,
288 :committed_on => Time.now,
326 :committed_on => Time.now,
289 :revision => '123',
327 :revision => '123',
290 :scmid => '12345',
328 :scmid => '12345',
291 :comments => str)
329 :comments => str)
292 assert( c.save )
330 assert( c.save )
293 assert_equal "test??test??", c.comments
331 assert_equal "test??test??", c.comments
294 end
332 end
295
333
296 def test_comments_should_be_converted_all_latin1_to_utf8
334 def test_comments_should_be_converted_all_latin1_to_utf8
297 s1 = "\xC2\x80"
335 s1 = "\xC2\x80"
298 s2 = "\xc3\x82\xc2\x80"
336 s2 = "\xc3\x82\xc2\x80"
299 s4 = s2.dup
337 s4 = s2.dup
300 if s1.respond_to?(:force_encoding)
338 if s1.respond_to?(:force_encoding)
301 s3 = s1.dup
339 s3 = s1.dup
302 s1.force_encoding('ASCII-8BIT')
340 s1.force_encoding('ASCII-8BIT')
303 s2.force_encoding('ASCII-8BIT')
341 s2.force_encoding('ASCII-8BIT')
304 s3.force_encoding('ISO-8859-1')
342 s3.force_encoding('ISO-8859-1')
305 s4.force_encoding('UTF-8')
343 s4.force_encoding('UTF-8')
306 assert_equal s3.encode('UTF-8'), s4
344 assert_equal s3.encode('UTF-8'), s4
307 end
345 end
308 proj = Project.find(3)
346 proj = Project.find(3)
309 r = Repository::Bazaar.create!(
347 r = Repository::Bazaar.create!(
310 :project => proj,
348 :project => proj,
311 :url => '/tmp/test/bazaar',
349 :url => '/tmp/test/bazaar',
312 :log_encoding => 'ISO-8859-1' )
350 :log_encoding => 'ISO-8859-1' )
313 assert r
351 assert r
314 c = Changeset.new(:repository => r,
352 c = Changeset.new(:repository => r,
315 :committed_on => Time.now,
353 :committed_on => Time.now,
316 :revision => '123',
354 :revision => '123',
317 :scmid => '12345',
355 :scmid => '12345',
318 :comments => s1)
356 :comments => s1)
319 assert( c.save )
357 assert( c.save )
320 assert_equal s4, c.comments
358 assert_equal s4, c.comments
321 end
359 end
322
360
323 def test_invalid_utf8_sequences_in_paths_should_be_replaced
361 def test_invalid_utf8_sequences_in_paths_should_be_replaced
324 proj = Project.find(3)
362 proj = Project.find(3)
325 str1 = "Texte encod\xe9 en ISO-8859-1"
363 str1 = "Texte encod\xe9 en ISO-8859-1"
326 str2 = "\xe9a\xe9b\xe9c\xe9d\xe9e test"
364 str2 = "\xe9a\xe9b\xe9c\xe9d\xe9e test"
327 str1.force_encoding("UTF-8") if str1.respond_to?(:force_encoding)
365 str1.force_encoding("UTF-8") if str1.respond_to?(:force_encoding)
328 str2.force_encoding("ASCII-8BIT") if str2.respond_to?(:force_encoding)
366 str2.force_encoding("ASCII-8BIT") if str2.respond_to?(:force_encoding)
329 r = Repository::Bazaar.create!(
367 r = Repository::Bazaar.create!(
330 :project => proj,
368 :project => proj,
331 :url => '/tmp/test/bazaar',
369 :url => '/tmp/test/bazaar',
332 :log_encoding => 'UTF-8' )
370 :log_encoding => 'UTF-8' )
333 assert r
371 assert r
334 cs = Changeset.new(
372 cs = Changeset.new(
335 :repository => r,
373 :repository => r,
336 :committed_on => Time.now,
374 :committed_on => Time.now,
337 :revision => '123',
375 :revision => '123',
338 :scmid => '12345',
376 :scmid => '12345',
339 :comments => "test")
377 :comments => "test")
340 assert(cs.save)
378 assert(cs.save)
341 ch = Change.new(
379 ch = Change.new(
342 :changeset => cs,
380 :changeset => cs,
343 :action => "A",
381 :action => "A",
344 :path => str1,
382 :path => str1,
345 :from_path => str2,
383 :from_path => str2,
346 :from_revision => "345")
384 :from_revision => "345")
347 assert(ch.save)
385 assert(ch.save)
348 assert_equal "Texte encod? en ISO-8859-1", ch.path
386 assert_equal "Texte encod? en ISO-8859-1", ch.path
349 assert_equal "?a?b?c?d?e test", ch.from_path
387 assert_equal "?a?b?c?d?e test", ch.from_path
350 end
388 end
351
389
352 def test_comments_nil
390 def test_comments_nil
353 proj = Project.find(3)
391 proj = Project.find(3)
354 r = Repository::Bazaar.create!(
392 r = Repository::Bazaar.create!(
355 :project => proj,
393 :project => proj,
356 :url => '/tmp/test/bazaar',
394 :url => '/tmp/test/bazaar',
357 :log_encoding => 'ISO-8859-1' )
395 :log_encoding => 'ISO-8859-1' )
358 assert r
396 assert r
359 c = Changeset.new(:repository => r,
397 c = Changeset.new(:repository => r,
360 :committed_on => Time.now,
398 :committed_on => Time.now,
361 :revision => '123',
399 :revision => '123',
362 :scmid => '12345',
400 :scmid => '12345',
363 :comments => nil,
401 :comments => nil,
364 :committer => nil)
402 :committer => nil)
365 assert( c.save )
403 assert( c.save )
366 assert_equal "", c.comments
404 assert_equal "", c.comments
367 assert_equal nil, c.committer
405 assert_equal nil, c.committer
368 if c.comments.respond_to?(:force_encoding)
406 if c.comments.respond_to?(:force_encoding)
369 assert_equal "UTF-8", c.comments.encoding.to_s
407 assert_equal "UTF-8", c.comments.encoding.to_s
370 end
408 end
371 end
409 end
372
410
373 def test_comments_empty
411 def test_comments_empty
374 proj = Project.find(3)
412 proj = Project.find(3)
375 r = Repository::Bazaar.create!(
413 r = Repository::Bazaar.create!(
376 :project => proj,
414 :project => proj,
377 :url => '/tmp/test/bazaar',
415 :url => '/tmp/test/bazaar',
378 :log_encoding => 'ISO-8859-1' )
416 :log_encoding => 'ISO-8859-1' )
379 assert r
417 assert r
380 c = Changeset.new(:repository => r,
418 c = Changeset.new(:repository => r,
381 :committed_on => Time.now,
419 :committed_on => Time.now,
382 :revision => '123',
420 :revision => '123',
383 :scmid => '12345',
421 :scmid => '12345',
384 :comments => "",
422 :comments => "",
385 :committer => "")
423 :committer => "")
386 assert( c.save )
424 assert( c.save )
387 assert_equal "", c.comments
425 assert_equal "", c.comments
388 assert_equal "", c.committer
426 assert_equal "", c.committer
389 if c.comments.respond_to?(:force_encoding)
427 if c.comments.respond_to?(:force_encoding)
390 assert_equal "UTF-8", c.comments.encoding.to_s
428 assert_equal "UTF-8", c.comments.encoding.to_s
391 assert_equal "UTF-8", c.committer.encoding.to_s
429 assert_equal "UTF-8", c.committer.encoding.to_s
392 end
430 end
393 end
431 end
394
432
395 def test_identifier
433 def test_identifier
396 c = Changeset.find_by_revision('1')
434 c = Changeset.find_by_revision('1')
397 assert_equal c.revision, c.identifier
435 assert_equal c.revision, c.identifier
398 end
436 end
399 end
437 end
General Comments 0
You need to be logged in to leave comments. Login now