##// END OF EJS Templates
Merged r14156 and r14161 (#19400)....
Jean-Philippe Lang -
r13816:94d91e75f539
parent child
Show More
@@ -1,495 +1,500
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2015 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 ScmFetchError < Exception; end
19 19
20 20 class Repository < ActiveRecord::Base
21 21 include Redmine::Ciphering
22 22 include Redmine::SafeAttributes
23 23
24 24 # Maximum length for repository identifiers
25 25 IDENTIFIER_MAX_LENGTH = 255
26 26
27 27 belongs_to :project
28 28 has_many :changesets, :order => "#{Changeset.table_name}.committed_on DESC, #{Changeset.table_name}.id DESC"
29 29 has_many :filechanges, :class_name => 'Change', :through => :changesets
30 30
31 31 serialize :extra_info
32 32
33 before_validation :normalize_identifier
33 34 before_save :check_default
34 35
35 36 # Raw SQL to delete changesets and changes in the database
36 37 # has_many :changesets, :dependent => :destroy is too slow for big repositories
37 38 before_destroy :clear_changesets
38 39
39 40 validates_length_of :password, :maximum => 255, :allow_nil => true
40 41 validates_length_of :identifier, :maximum => IDENTIFIER_MAX_LENGTH, :allow_blank => true
41 42 validates_uniqueness_of :identifier, :scope => :project_id
42 43 validates_exclusion_of :identifier, :in => %w(browse show entry raw changes annotate diff statistics graph revisions revision)
43 44 # donwcase letters, digits, dashes, underscores but not digits only
44 45 validates_format_of :identifier, :with => /\A(?!\d+$)[a-z0-9\-_]*\z/, :allow_blank => true
45 46 # Checks if the SCM is enabled when creating a repository
46 47 validate :repo_create_validation, :on => :create
47 48
48 49 safe_attributes 'identifier',
49 50 'login',
50 51 'password',
51 52 'path_encoding',
52 53 'log_encoding',
53 54 'is_default'
54 55
55 56 safe_attributes 'url',
56 57 :if => lambda {|repository, user| repository.new_record?}
57 58
58 59 def repo_create_validation
59 60 unless Setting.enabled_scm.include?(self.class.name.demodulize)
60 61 errors.add(:type, :invalid)
61 62 end
62 63 end
63 64
64 65 def self.human_attribute_name(attribute_key_name, *args)
65 66 attr_name = attribute_key_name.to_s
66 67 if attr_name == "log_encoding"
67 68 attr_name = "commit_logs_encoding"
68 69 end
69 70 super(attr_name, *args)
70 71 end
71 72
72 73 # Removes leading and trailing whitespace
73 74 def url=(arg)
74 75 write_attribute(:url, arg ? arg.to_s.strip : nil)
75 76 end
76 77
77 78 # Removes leading and trailing whitespace
78 79 def root_url=(arg)
79 80 write_attribute(:root_url, arg ? arg.to_s.strip : nil)
80 81 end
81 82
82 83 def password
83 84 read_ciphered_attribute(:password)
84 85 end
85 86
86 87 def password=(arg)
87 88 write_ciphered_attribute(:password, arg)
88 89 end
89 90
90 91 def scm_adapter
91 92 self.class.scm_adapter_class
92 93 end
93 94
94 95 def scm
95 96 unless @scm
96 97 @scm = self.scm_adapter.new(url, root_url,
97 98 login, password, path_encoding)
98 99 if root_url.blank? && @scm.root_url.present?
99 100 update_attribute(:root_url, @scm.root_url)
100 101 end
101 102 end
102 103 @scm
103 104 end
104 105
105 106 def scm_name
106 107 self.class.scm_name
107 108 end
108 109
109 110 def name
110 111 if identifier.present?
111 112 identifier
112 113 elsif is_default?
113 114 l(:field_repository_is_default)
114 115 else
115 116 scm_name
116 117 end
117 118 end
118 119
119 120 def identifier=(identifier)
120 121 super unless identifier_frozen?
121 122 end
122 123
123 124 def identifier_frozen?
124 125 errors[:identifier].blank? && !(new_record? || identifier.blank?)
125 126 end
126 127
127 128 def identifier_param
128 129 if is_default?
129 130 nil
130 131 elsif identifier.present?
131 132 identifier
132 133 else
133 134 id.to_s
134 135 end
135 136 end
136 137
137 138 def <=>(repository)
138 139 if is_default?
139 140 -1
140 141 elsif repository.is_default?
141 142 1
142 143 else
143 144 identifier.to_s <=> repository.identifier.to_s
144 145 end
145 146 end
146 147
147 148 def self.find_by_identifier_param(param)
148 149 if param.to_s =~ /^\d+$/
149 150 find_by_id(param)
150 151 else
151 152 find_by_identifier(param)
152 153 end
153 154 end
154 155
155 156 # TODO: should return an empty hash instead of nil to avoid many ||{}
156 157 def extra_info
157 158 h = read_attribute(:extra_info)
158 159 h.is_a?(Hash) ? h : nil
159 160 end
160 161
161 162 def merge_extra_info(arg)
162 163 h = extra_info || {}
163 164 return h if arg.nil?
164 165 h.merge!(arg)
165 166 write_attribute(:extra_info, h)
166 167 end
167 168
168 169 def report_last_commit
169 170 true
170 171 end
171 172
172 173 def supports_cat?
173 174 scm.supports_cat?
174 175 end
175 176
176 177 def supports_annotate?
177 178 scm.supports_annotate?
178 179 end
179 180
180 181 def supports_all_revisions?
181 182 true
182 183 end
183 184
184 185 def supports_directory_revisions?
185 186 false
186 187 end
187 188
188 189 def supports_revision_graph?
189 190 false
190 191 end
191 192
192 193 def entry(path=nil, identifier=nil)
193 194 scm.entry(path, identifier)
194 195 end
195 196
196 197 def scm_entries(path=nil, identifier=nil)
197 198 scm.entries(path, identifier)
198 199 end
199 200 protected :scm_entries
200 201
201 202 def entries(path=nil, identifier=nil)
202 203 entries = scm_entries(path, identifier)
203 204 load_entries_changesets(entries)
204 205 entries
205 206 end
206 207
207 208 def branches
208 209 scm.branches
209 210 end
210 211
211 212 def tags
212 213 scm.tags
213 214 end
214 215
215 216 def default_branch
216 217 nil
217 218 end
218 219
219 220 def properties(path, identifier=nil)
220 221 scm.properties(path, identifier)
221 222 end
222 223
223 224 def cat(path, identifier=nil)
224 225 scm.cat(path, identifier)
225 226 end
226 227
227 228 def diff(path, rev, rev_to)
228 229 scm.diff(path, rev, rev_to)
229 230 end
230 231
231 232 def diff_format_revisions(cs, cs_to, sep=':')
232 233 text = ""
233 234 text << cs_to.format_identifier + sep if cs_to
234 235 text << cs.format_identifier if cs
235 236 text
236 237 end
237 238
238 239 # Returns a path relative to the url of the repository
239 240 def relative_path(path)
240 241 path
241 242 end
242 243
243 244 # Finds and returns a revision with a number or the beginning of a hash
244 245 def find_changeset_by_name(name)
245 246 return nil if name.blank?
246 247 s = name.to_s
247 248 if s.match(/^\d*$/)
248 249 changesets.where("revision = ?", s).first
249 250 else
250 251 changesets.where("revision LIKE ?", s + '%').first
251 252 end
252 253 end
253 254
254 255 def latest_changeset
255 256 @latest_changeset ||= changesets.first
256 257 end
257 258
258 259 # Returns the latest changesets for +path+
259 260 # Default behaviour is to search in cached changesets
260 261 def latest_changesets(path, rev, limit=10)
261 262 if path.blank?
262 263 changesets.
263 264 reorder("#{Changeset.table_name}.committed_on DESC, #{Changeset.table_name}.id DESC").
264 265 limit(limit).
265 266 preload(:user).
266 267 all
267 268 else
268 269 filechanges.
269 270 where("path = ?", path.with_leading_slash).
270 271 reorder("#{Changeset.table_name}.committed_on DESC, #{Changeset.table_name}.id DESC").
271 272 limit(limit).
272 273 preload(:changeset => :user).
273 274 collect(&:changeset)
274 275 end
275 276 end
276 277
277 278 def scan_changesets_for_issue_ids
278 279 self.changesets.each(&:scan_comment_for_issue_ids)
279 280 end
280 281
281 282 # Returns an array of committers usernames and associated user_id
282 283 def committers
283 284 @committers ||= Changeset.connection.select_rows(
284 285 "SELECT DISTINCT committer, user_id FROM #{Changeset.table_name} WHERE repository_id = #{id}")
285 286 end
286 287
287 288 # Maps committers username to a user ids
288 289 def committer_ids=(h)
289 290 if h.is_a?(Hash)
290 291 committers.each do |committer, user_id|
291 292 new_user_id = h[committer]
292 293 if new_user_id && (new_user_id.to_i != user_id.to_i)
293 294 new_user_id = (new_user_id.to_i > 0 ? new_user_id.to_i : nil)
294 295 Changeset.where(["repository_id = ? AND committer = ?", id, committer]).
295 296 update_all("user_id = #{new_user_id.nil? ? 'NULL' : new_user_id}")
296 297 end
297 298 end
298 299 @committers = nil
299 300 @found_committer_users = nil
300 301 true
301 302 else
302 303 false
303 304 end
304 305 end
305 306
306 307 # Returns the Redmine User corresponding to the given +committer+
307 308 # It will return nil if the committer is not yet mapped and if no User
308 309 # with the same username or email was found
309 310 def find_committer_user(committer)
310 311 unless committer.blank?
311 312 @found_committer_users ||= {}
312 313 return @found_committer_users[committer] if @found_committer_users.has_key?(committer)
313 314
314 315 user = nil
315 316 c = changesets.where(:committer => committer).includes(:user).first
316 317 if c && c.user
317 318 user = c.user
318 319 elsif committer.strip =~ /^([^<]+)(<(.*)>)?$/
319 320 username, email = $1.strip, $3
320 321 u = User.find_by_login(username)
321 322 u ||= User.find_by_mail(email) unless email.blank?
322 323 user = u
323 324 end
324 325 @found_committer_users[committer] = user
325 326 user
326 327 end
327 328 end
328 329
329 330 def repo_log_encoding
330 331 encoding = log_encoding.to_s.strip
331 332 encoding.blank? ? 'UTF-8' : encoding
332 333 end
333 334
334 335 # Fetches new changesets for all repositories of active projects
335 336 # Can be called periodically by an external script
336 337 # eg. ruby script/runner "Repository.fetch_changesets"
337 338 def self.fetch_changesets
338 339 Project.active.has_module(:repository).all.each do |project|
339 340 project.repositories.each do |repository|
340 341 begin
341 342 repository.fetch_changesets
342 343 rescue Redmine::Scm::Adapters::CommandFailed => e
343 344 logger.error "scm: error during fetching changesets: #{e.message}"
344 345 end
345 346 end
346 347 end
347 348 end
348 349
349 350 # scan changeset comments to find related and fixed issues for all repositories
350 351 def self.scan_changesets_for_issue_ids
351 352 all.each(&:scan_changesets_for_issue_ids)
352 353 end
353 354
354 355 def self.scm_name
355 356 'Abstract'
356 357 end
357 358
358 359 def self.available_scm
359 360 subclasses.collect {|klass| [klass.scm_name, klass.name]}
360 361 end
361 362
362 363 def self.factory(klass_name, *args)
363 364 klass = "Repository::#{klass_name}".constantize
364 365 klass.new(*args)
365 366 rescue
366 367 nil
367 368 end
368 369
369 370 def self.scm_adapter_class
370 371 nil
371 372 end
372 373
373 374 def self.scm_command
374 375 ret = ""
375 376 begin
376 377 ret = self.scm_adapter_class.client_command if self.scm_adapter_class
377 378 rescue Exception => e
378 379 logger.error "scm: error during get command: #{e.message}"
379 380 end
380 381 ret
381 382 end
382 383
383 384 def self.scm_version_string
384 385 ret = ""
385 386 begin
386 387 ret = self.scm_adapter_class.client_version_string if self.scm_adapter_class
387 388 rescue Exception => e
388 389 logger.error "scm: error during get version string: #{e.message}"
389 390 end
390 391 ret
391 392 end
392 393
393 394 def self.scm_available
394 395 ret = false
395 396 begin
396 397 ret = self.scm_adapter_class.client_available if self.scm_adapter_class
397 398 rescue Exception => e
398 399 logger.error "scm: error during get scm available: #{e.message}"
399 400 end
400 401 ret
401 402 end
402 403
403 404 def set_as_default?
404 405 new_record? && project && Repository.where(:project_id => project.id).empty?
405 406 end
406 407
407 408 # Returns a hash with statistics by author in the following form:
408 409 # {
409 410 # "John Smith" => { :commits => 45, :changes => 324 },
410 411 # "Bob" => { ... }
411 412 # }
412 413 #
413 414 # Notes:
414 415 # - this hash honnors the users mapping defined for the repository
415 416 def stats_by_author
416 417 commits = Changeset.where("repository_id = ?", id).select("committer, user_id, count(*) as count").group("committer, user_id")
417 418
418 419 #TODO: restore ordering ; this line probably never worked
419 420 #commits.to_a.sort! {|x, y| x.last <=> y.last}
420 421
421 422 changes = Change.joins(:changeset).where("#{Changeset.table_name}.repository_id = ?", id).select("committer, user_id, count(*) as count").group("committer, user_id")
422 423
423 424 user_ids = changesets.map(&:user_id).compact.uniq
424 425 authors_names = User.where(:id => user_ids).inject({}) do |memo, user|
425 426 memo[user.id] = user.to_s
426 427 memo
427 428 end
428 429
429 430 (commits + changes).inject({}) do |hash, element|
430 431 mapped_name = element.committer
431 432 if username = authors_names[element.user_id.to_i]
432 433 mapped_name = username
433 434 end
434 435 hash[mapped_name] ||= { :commits_count => 0, :changes_count => 0 }
435 436 if element.is_a?(Changeset)
436 437 hash[mapped_name][:commits_count] += element.count.to_i
437 438 else
438 439 hash[mapped_name][:changes_count] += element.count.to_i
439 440 end
440 441 hash
441 442 end
442 443 end
443 444
444 445 # Returns a scope of changesets that come from the same commit as the given changeset
445 446 # in different repositories that point to the same backend
446 447 def same_commits_in_scope(scope, changeset)
447 448 scope = scope.joins(:repository).where(:repositories => {:url => url, :root_url => root_url, :type => type})
448 449 if changeset.scmid.present?
449 450 scope = scope.where(:scmid => changeset.scmid)
450 451 else
451 452 scope = scope.where(:revision => changeset.revision)
452 453 end
453 454 scope
454 455 end
455 456
456 457 protected
457 458
459 def normalize_identifier
460 self.identifier = identifier.to_s.strip
461 end
462
458 463 def check_default
459 464 if !is_default? && set_as_default?
460 465 self.is_default = true
461 466 end
462 467 if is_default? && is_default_changed?
463 468 Repository.where(["project_id = ?", project_id]).update_all(["is_default = ?", false])
464 469 end
465 470 end
466 471
467 472 def load_entries_changesets(entries)
468 473 if entries
469 474 entries.each do |entry|
470 475 if entry.lastrev && entry.lastrev.identifier
471 476 entry.changeset = find_changeset_by_name(entry.lastrev.identifier)
472 477 end
473 478 end
474 479 end
475 480 end
476 481
477 482 private
478 483
479 484 # Deletes repository data
480 485 def clear_changesets
481 486 cs = Changeset.table_name
482 487 ch = Change.table_name
483 488 ci = "#{table_name_prefix}changesets_issues#{table_name_suffix}"
484 489 cp = "#{table_name_prefix}changeset_parents#{table_name_suffix}"
485 490
486 491 connection.delete("DELETE FROM #{ch} WHERE #{ch}.changeset_id IN (SELECT #{cs}.id FROM #{cs} WHERE #{cs}.repository_id = #{id})")
487 492 connection.delete("DELETE FROM #{ci} WHERE #{ci}.changeset_id IN (SELECT #{cs}.id FROM #{cs} WHERE #{cs}.repository_id = #{id})")
488 493 connection.delete("DELETE FROM #{cp} WHERE #{cp}.changeset_id IN (SELECT #{cs}.id FROM #{cs} WHERE #{cs}.repository_id = #{id})")
489 494 connection.delete("DELETE FROM #{cs} WHERE #{cs}.repository_id = #{id}")
490 495 clear_extra_info_of_changesets
491 496 end
492 497
493 498 def clear_extra_info_of_changesets
494 499 end
495 500 end
@@ -1,662 +1,662
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2015 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 require File.expand_path('../../test_helper', __FILE__)
19 19
20 20 class RepositoriesGitControllerTest < ActionController::TestCase
21 21 tests RepositoriesController
22 22
23 23 fixtures :projects, :users, :roles, :members, :member_roles,
24 24 :repositories, :enabled_modules
25 25
26 26 REPOSITORY_PATH = Rails.root.join('tmp/test/git_repository').to_s
27 27 REPOSITORY_PATH.gsub!(/\//, "\\") if Redmine::Platform.mswin?
28 28 PRJ_ID = 3
29 29 CHAR_1_HEX = "\xc3\x9c"
30 30 FELIX_HEX = "Felix Sch\xC3\xA4fer"
31 31 NUM_REV = 28
32 32
33 33 ## Git, Mercurial and CVS path encodings are binary.
34 34 ## Subversion supports URL encoding for path.
35 35 ## Redmine Mercurial adapter and extension use URL encoding.
36 36 ## Git accepts only binary path in command line parameter.
37 37 ## So, there is no way to use binary command line parameter in JRuby.
38 38 JRUBY_SKIP = (RUBY_PLATFORM == 'java')
39 39 JRUBY_SKIP_STR = "TODO: This test fails in JRuby"
40 40
41 41 def setup
42 42 @ruby19_non_utf8_pass =
43 43 (RUBY_VERSION >= '1.9' && Encoding.default_external.to_s != 'UTF-8')
44 44
45 45 User.current = nil
46 46 @project = Project.find(PRJ_ID)
47 47 @repository = Repository::Git.create(
48 48 :project => @project,
49 49 :url => REPOSITORY_PATH,
50 50 :path_encoding => 'ISO-8859-1'
51 51 )
52 52 assert @repository
53 53 @char_1 = CHAR_1_HEX.dup
54 54 @felix_utf8 = FELIX_HEX.dup
55 55 if @char_1.respond_to?(:force_encoding)
56 56 @char_1.force_encoding('UTF-8')
57 57 @felix_utf8.force_encoding('UTF-8')
58 58 end
59 59 end
60 60
61 61 def test_create_and_update
62 62 @request.session[:user_id] = 1
63 63 assert_difference 'Repository.count' do
64 64 post :create, :project_id => 'subproject1',
65 65 :repository_scm => 'Git',
66 66 :repository => {
67 67 :url => '/test',
68 68 :is_default => '0',
69 69 :identifier => 'test-create',
70 70 :extra_report_last_commit => '1',
71 71 }
72 72 end
73 73 assert_response 302
74 74 repository = Repository.order('id DESC').first
75 75 assert_kind_of Repository::Git, repository
76 76 assert_equal '/test', repository.url
77 77 assert_equal true, repository.extra_report_last_commit
78 78
79 79 put :update, :id => repository.id,
80 80 :repository => {
81 81 :extra_report_last_commit => '0'
82 82 }
83 83 assert_response 302
84 84 repo2 = Repository.find(repository.id)
85 85 assert_equal false, repo2.extra_report_last_commit
86 86 end
87 87
88 88 if File.directory?(REPOSITORY_PATH)
89 89 ## Ruby uses ANSI api to fork a process on Windows.
90 90 ## Japanese Shift_JIS and Traditional Chinese Big5 have 0x5c(backslash) problem
91 91 ## and these are incompatible with ASCII.
92 92 ## Git for Windows (msysGit) changed internal API from ANSI to Unicode in 1.7.10
93 93 ## http://code.google.com/p/msysgit/issues/detail?id=80
94 94 ## So, Latin-1 path tests fail on Japanese Windows
95 95 WINDOWS_PASS = (Redmine::Platform.mswin? &&
96 96 Redmine::Scm::Adapters::GitAdapter.client_version_above?([1, 7, 10]))
97 97 WINDOWS_SKIP_STR = "TODO: This test fails in Git for Windows above 1.7.10"
98 98
99 99 def test_get_new
100 100 @request.session[:user_id] = 1
101 101 @project.repository.destroy
102 102 get :new, :project_id => 'subproject1', :repository_scm => 'Git'
103 103 assert_response :success
104 104 assert_template 'new'
105 105 assert_kind_of Repository::Git, assigns(:repository)
106 106 assert assigns(:repository).new_record?
107 107 end
108 108
109 109 def test_browse_root
110 110 assert_equal 0, @repository.changesets.count
111 111 @repository.fetch_changesets
112 112 @project.reload
113 113 assert_equal NUM_REV, @repository.changesets.count
114 114
115 115 get :show, :id => PRJ_ID
116 116 assert_response :success
117 117 assert_template 'show'
118 118 assert_not_nil assigns(:entries)
119 119 assert_equal 9, assigns(:entries).size
120 120 assert assigns(:entries).detect {|e| e.name == 'images' && e.kind == 'dir'}
121 121 assert assigns(:entries).detect {|e| e.name == 'this_is_a_really_long_and_verbose_directory_name' && e.kind == 'dir'}
122 122 assert assigns(:entries).detect {|e| e.name == 'sources' && e.kind == 'dir'}
123 123 assert assigns(:entries).detect {|e| e.name == 'README' && e.kind == 'file'}
124 124 assert assigns(:entries).detect {|e| e.name == 'copied_README' && e.kind == 'file'}
125 125 assert assigns(:entries).detect {|e| e.name == 'new_file.txt' && e.kind == 'file'}
126 126 assert assigns(:entries).detect {|e| e.name == 'renamed_test.txt' && e.kind == 'file'}
127 127 assert assigns(:entries).detect {|e| e.name == 'filemane with spaces.txt' && e.kind == 'file'}
128 128 assert assigns(:entries).detect {|e| e.name == ' filename with a leading space.txt ' && e.kind == 'file'}
129 129 assert_not_nil assigns(:changesets)
130 130 assert assigns(:changesets).size > 0
131 131 end
132 132
133 133 def test_browse_branch
134 134 assert_equal 0, @repository.changesets.count
135 135 @repository.fetch_changesets
136 136 @project.reload
137 137 assert_equal NUM_REV, @repository.changesets.count
138 138 get :show, :id => PRJ_ID, :rev => 'test_branch'
139 139 assert_response :success
140 140 assert_template 'show'
141 141 assert_not_nil assigns(:entries)
142 142 assert_equal 4, assigns(:entries).size
143 143 assert assigns(:entries).detect {|e| e.name == 'images' && e.kind == 'dir'}
144 144 assert assigns(:entries).detect {|e| e.name == 'sources' && e.kind == 'dir'}
145 145 assert assigns(:entries).detect {|e| e.name == 'README' && e.kind == 'file'}
146 146 assert assigns(:entries).detect {|e| e.name == 'test.txt' && e.kind == 'file'}
147 147 assert_not_nil assigns(:changesets)
148 148 assert assigns(:changesets).size > 0
149 149 end
150 150
151 151 def test_browse_tag
152 152 assert_equal 0, @repository.changesets.count
153 153 @repository.fetch_changesets
154 154 @project.reload
155 155 assert_equal NUM_REV, @repository.changesets.count
156 156 [
157 157 "tag00.lightweight",
158 158 "tag01.annotated",
159 159 ].each do |t1|
160 160 get :show, :id => PRJ_ID, :rev => t1
161 161 assert_response :success
162 162 assert_template 'show'
163 163 assert_not_nil assigns(:entries)
164 164 assert assigns(:entries).size > 0
165 165 assert_not_nil assigns(:changesets)
166 166 assert assigns(:changesets).size > 0
167 167 end
168 168 end
169 169
170 170 def test_browse_directory
171 171 assert_equal 0, @repository.changesets.count
172 172 @repository.fetch_changesets
173 173 @project.reload
174 174 assert_equal NUM_REV, @repository.changesets.count
175 175 get :show, :id => PRJ_ID, :path => repository_path_hash(['images'])[:param]
176 176 assert_response :success
177 177 assert_template 'show'
178 178 assert_not_nil assigns(:entries)
179 179 assert_equal ['edit.png'], assigns(:entries).collect(&:name)
180 180 entry = assigns(:entries).detect {|e| e.name == 'edit.png'}
181 181 assert_not_nil entry
182 182 assert_equal 'file', entry.kind
183 183 assert_equal 'images/edit.png', entry.path
184 184 assert_not_nil assigns(:changesets)
185 185 assert assigns(:changesets).size > 0
186 186 end
187 187
188 188 def test_browse_at_given_revision
189 189 assert_equal 0, @repository.changesets.count
190 190 @repository.fetch_changesets
191 191 @project.reload
192 192 assert_equal NUM_REV, @repository.changesets.count
193 193 get :show, :id => PRJ_ID, :path => repository_path_hash(['images'])[:param],
194 194 :rev => '7234cb2750b63f47bff735edc50a1c0a433c2518'
195 195 assert_response :success
196 196 assert_template 'show'
197 197 assert_not_nil assigns(:entries)
198 198 assert_equal ['delete.png'], assigns(:entries).collect(&:name)
199 199 assert_not_nil assigns(:changesets)
200 200 assert assigns(:changesets).size > 0
201 201 end
202 202
203 203 def test_changes
204 204 get :changes, :id => PRJ_ID,
205 205 :path => repository_path_hash(['images', 'edit.png'])[:param]
206 206 assert_response :success
207 207 assert_template 'changes'
208 208 assert_tag :tag => 'h2', :content => 'edit.png'
209 209 end
210 210
211 211 def test_entry_show
212 212 get :entry, :id => PRJ_ID,
213 213 :path => repository_path_hash(['sources', 'watchers_controller.rb'])[:param]
214 214 assert_response :success
215 215 assert_template 'entry'
216 216 # Line 19
217 217 assert_tag :tag => 'th',
218 218 :content => '11',
219 219 :attributes => { :class => 'line-num' },
220 220 :sibling => { :tag => 'td', :content => /WITHOUT ANY WARRANTY/ }
221 221 end
222 222
223 223 def test_entry_show_latin_1
224 224 if @ruby19_non_utf8_pass
225 225 puts_ruby19_non_utf8_pass()
226 226 elsif WINDOWS_PASS
227 227 puts WINDOWS_SKIP_STR
228 228 elsif JRUBY_SKIP
229 229 puts JRUBY_SKIP_STR
230 230 else
231 231 with_settings :repositories_encodings => 'UTF-8,ISO-8859-1' do
232 232 ['57ca437c', '57ca437c0acbbcb749821fdf3726a1367056d364'].each do |r1|
233 233 get :entry, :id => PRJ_ID,
234 234 :path => repository_path_hash(['latin-1-dir', "test-#{@char_1}.txt"])[:param],
235 235 :rev => r1
236 236 assert_response :success
237 237 assert_template 'entry'
238 238 assert_tag :tag => 'th',
239 239 :content => '1',
240 240 :attributes => { :class => 'line-num' },
241 241 :sibling => { :tag => 'td',
242 242 :content => /test-#{@char_1}.txt/ }
243 243 end
244 244 end
245 245 end
246 246 end
247 247
248 248 def test_entry_download
249 249 get :entry, :id => PRJ_ID,
250 250 :path => repository_path_hash(['sources', 'watchers_controller.rb'])[:param],
251 251 :format => 'raw'
252 252 assert_response :success
253 253 # File content
254 254 assert @response.body.include?('WITHOUT ANY WARRANTY')
255 255 end
256 256
257 257 def test_directory_entry
258 258 get :entry, :id => PRJ_ID,
259 259 :path => repository_path_hash(['sources'])[:param]
260 260 assert_response :success
261 261 assert_template 'show'
262 262 assert_not_nil assigns(:entry)
263 263 assert_equal 'sources', assigns(:entry).name
264 264 end
265 265
266 266 def test_diff
267 267 assert_equal true, @repository.is_default
268 assert_nil @repository.identifier
268 assert @repository.identifier.blank?
269 269 assert_equal 0, @repository.changesets.count
270 270 @repository.fetch_changesets
271 271 @project.reload
272 272 assert_equal NUM_REV, @repository.changesets.count
273 273 # Full diff of changeset 2f9c0091
274 274 ['inline', 'sbs'].each do |dt|
275 275 get :diff,
276 276 :id => PRJ_ID,
277 277 :rev => '2f9c0091c754a91af7a9c478e36556b4bde8dcf7',
278 278 :type => dt
279 279 assert_response :success
280 280 assert_template 'diff'
281 281 # Line 22 removed
282 282 assert_tag :tag => 'th',
283 283 :content => /22/,
284 284 :sibling => { :tag => 'td',
285 285 :attributes => { :class => /diff_out/ },
286 286 :content => /def remove/ }
287 287 assert_tag :tag => 'h2', :content => /2f9c0091/
288 288 end
289 289 end
290 290
291 291 def test_diff_with_rev_and_path
292 292 assert_equal 0, @repository.changesets.count
293 293 @repository.fetch_changesets
294 294 @project.reload
295 295 assert_equal NUM_REV, @repository.changesets.count
296 296 with_settings :diff_max_lines_displayed => 1000 do
297 297 # Full diff of changeset 2f9c0091
298 298 ['inline', 'sbs'].each do |dt|
299 299 get :diff,
300 300 :id => PRJ_ID,
301 301 :rev => '2f9c0091c754a91af7a9c478e36556b4bde8dcf7',
302 302 :path => repository_path_hash(['sources', 'watchers_controller.rb'])[:param],
303 303 :type => dt
304 304 assert_response :success
305 305 assert_template 'diff'
306 306 # Line 22 removed
307 307 assert_tag :tag => 'th',
308 308 :content => '22',
309 309 :sibling => { :tag => 'td',
310 310 :attributes => { :class => /diff_out/ },
311 311 :content => /def remove/ }
312 312 assert_tag :tag => 'h2', :content => /2f9c0091/
313 313 end
314 314 end
315 315 end
316 316
317 317 def test_diff_truncated
318 318 assert_equal 0, @repository.changesets.count
319 319 @repository.fetch_changesets
320 320 @project.reload
321 321 assert_equal NUM_REV, @repository.changesets.count
322 322
323 323 with_settings :diff_max_lines_displayed => 5 do
324 324 # Truncated diff of changeset 2f9c0091
325 325 with_cache do
326 326 with_settings :default_language => 'en' do
327 327 get :diff, :id => PRJ_ID, :type => 'inline',
328 328 :rev => '2f9c0091c754a91af7a9c478e36556b4bde8dcf7'
329 329 assert_response :success
330 330 assert @response.body.include?("... This diff was truncated")
331 331 end
332 332 with_settings :default_language => 'fr' do
333 333 get :diff, :id => PRJ_ID, :type => 'inline',
334 334 :rev => '2f9c0091c754a91af7a9c478e36556b4bde8dcf7'
335 335 assert_response :success
336 336 assert ! @response.body.include?("... This diff was truncated")
337 337 assert @response.body.include?("... Ce diff")
338 338 end
339 339 end
340 340 end
341 341 end
342 342
343 343 def test_diff_two_revs
344 344 assert_equal 0, @repository.changesets.count
345 345 @repository.fetch_changesets
346 346 @project.reload
347 347 assert_equal NUM_REV, @repository.changesets.count
348 348 ['inline', 'sbs'].each do |dt|
349 349 get :diff,
350 350 :id => PRJ_ID,
351 351 :rev => '61b685fbe55ab05b5ac68402d5720c1a6ac973d1',
352 352 :rev_to => '2f9c0091c754a91af7a9c478e36556b4bde8dcf7',
353 353 :type => dt
354 354 assert_response :success
355 355 assert_template 'diff'
356 356 diff = assigns(:diff)
357 357 assert_not_nil diff
358 358 assert_tag :tag => 'h2', :content => /2f9c0091:61b685fb/
359 359 assert_tag :tag => "form",
360 360 :attributes => {
361 361 :action => "/projects/subproject1/repository/revisions/" +
362 362 "61b685fbe55ab05b5ac68402d5720c1a6ac973d1/diff"
363 363 }
364 364 assert_tag :tag => 'input',
365 365 :attributes => {
366 366 :id => "rev_to",
367 367 :name => "rev_to",
368 368 :type => "hidden",
369 369 :value => '2f9c0091c754a91af7a9c478e36556b4bde8dcf7'
370 370 }
371 371 end
372 372 end
373 373
374 374 def test_diff_path_in_subrepo
375 375 repo = Repository::Git.create(
376 376 :project => @project,
377 377 :url => REPOSITORY_PATH,
378 378 :identifier => 'test-diff-path',
379 379 :path_encoding => 'ISO-8859-1'
380 380 )
381 381 assert repo
382 382 assert_equal false, repo.is_default
383 383 assert_equal 'test-diff-path', repo.identifier
384 384 get :diff,
385 385 :id => PRJ_ID,
386 386 :repository_id => 'test-diff-path',
387 387 :rev => '61b685fbe55ab05b',
388 388 :rev_to => '2f9c0091c754a91a',
389 389 :type => 'inline'
390 390 assert_response :success
391 391 assert_template 'diff'
392 392 diff = assigns(:diff)
393 393 assert_not_nil diff
394 394 assert_tag :tag => "form",
395 395 :attributes => {
396 396 :action => "/projects/subproject1/repository/test-diff-path/" +
397 397 "revisions/61b685fbe55ab05b/diff"
398 398 }
399 399 assert_tag :tag => 'input',
400 400 :attributes => {
401 401 :id => "rev_to",
402 402 :name => "rev_to",
403 403 :type => "hidden",
404 404 :value => '2f9c0091c754a91a'
405 405 }
406 406 end
407 407
408 408 def test_diff_latin_1
409 409 if @ruby19_non_utf8_pass
410 410 puts_ruby19_non_utf8_pass()
411 411 else
412 412 with_settings :repositories_encodings => 'UTF-8,ISO-8859-1' do
413 413 ['57ca437c', '57ca437c0acbbcb749821fdf3726a1367056d364'].each do |r1|
414 414 ['inline', 'sbs'].each do |dt|
415 415 get :diff, :id => PRJ_ID, :rev => r1, :type => dt
416 416 assert_response :success
417 417 assert_template 'diff'
418 418 assert_tag :tag => 'thead',
419 419 :descendant => {
420 420 :tag => 'th',
421 421 :attributes => { :class => 'filename' } ,
422 422 :content => /latin-1-dir\/test-#{@char_1}.txt/ ,
423 423 },
424 424 :sibling => {
425 425 :tag => 'tbody',
426 426 :descendant => {
427 427 :tag => 'td',
428 428 :attributes => { :class => /diff_in/ },
429 429 :content => /test-#{@char_1}.txt/
430 430 }
431 431 }
432 432 end
433 433 end
434 434 end
435 435 end
436 436 end
437 437
438 438 def test_diff_should_show_filenames
439 439 get :diff, :id => PRJ_ID, :rev => 'deff712f05a90d96edbd70facc47d944be5897e3', :type => 'inline'
440 440 assert_response :success
441 441 assert_template 'diff'
442 442 # modified file
443 443 assert_select 'th.filename', :text => 'sources/watchers_controller.rb'
444 444 # deleted file
445 445 assert_select 'th.filename', :text => 'test.txt'
446 446 end
447 447
448 448 def test_save_diff_type
449 449 user1 = User.find(1)
450 450 user1.pref[:diff_type] = nil
451 451 user1.preference.save
452 452 user = User.find(1)
453 453 assert_nil user.pref[:diff_type]
454 454
455 455 @request.session[:user_id] = 1 # admin
456 456 get :diff,
457 457 :id => PRJ_ID,
458 458 :rev => '2f9c0091c754a91af7a9c478e36556b4bde8dcf7'
459 459 assert_response :success
460 460 assert_template 'diff'
461 461 user.reload
462 462 assert_equal "inline", user.pref[:diff_type]
463 463 get :diff,
464 464 :id => PRJ_ID,
465 465 :rev => '2f9c0091c754a91af7a9c478e36556b4bde8dcf7',
466 466 :type => 'sbs'
467 467 assert_response :success
468 468 assert_template 'diff'
469 469 user.reload
470 470 assert_equal "sbs", user.pref[:diff_type]
471 471 end
472 472
473 473 def test_annotate
474 474 get :annotate, :id => PRJ_ID,
475 475 :path => repository_path_hash(['sources', 'watchers_controller.rb'])[:param]
476 476 assert_response :success
477 477 assert_template 'annotate'
478 478
479 479 # Line 23, changeset 2f9c0091
480 480 assert_select 'tr' do
481 481 assert_select 'th.line-num', :text => '23'
482 482 assert_select 'td.revision', :text => /2f9c0091/
483 483 assert_select 'td.author', :text => 'jsmith'
484 484 assert_select 'td', :text => /remove_watcher/
485 485 end
486 486 end
487 487
488 488 def test_annotate_at_given_revision
489 489 assert_equal 0, @repository.changesets.count
490 490 @repository.fetch_changesets
491 491 @project.reload
492 492 assert_equal NUM_REV, @repository.changesets.count
493 493 get :annotate, :id => PRJ_ID, :rev => 'deff7',
494 494 :path => repository_path_hash(['sources', 'watchers_controller.rb'])[:param]
495 495 assert_response :success
496 496 assert_template 'annotate'
497 497 assert_tag :tag => 'h2', :content => /@ deff712f/
498 498 end
499 499
500 500 def test_annotate_binary_file
501 501 get :annotate, :id => PRJ_ID,
502 502 :path => repository_path_hash(['images', 'edit.png'])[:param]
503 503 assert_response 500
504 504 assert_tag :tag => 'p', :attributes => { :id => /errorExplanation/ },
505 505 :content => /cannot be annotated/
506 506 end
507 507
508 508 def test_annotate_error_when_too_big
509 509 with_settings :file_max_size_displayed => 1 do
510 510 get :annotate, :id => PRJ_ID,
511 511 :path => repository_path_hash(['sources', 'watchers_controller.rb'])[:param],
512 512 :rev => 'deff712f'
513 513 assert_response 500
514 514 assert_tag :tag => 'p', :attributes => { :id => /errorExplanation/ },
515 515 :content => /exceeds the maximum text file size/
516 516
517 517 get :annotate, :id => PRJ_ID,
518 518 :path => repository_path_hash(['README'])[:param],
519 519 :rev => '7234cb2'
520 520 assert_response :success
521 521 assert_template 'annotate'
522 522 end
523 523 end
524 524
525 525 def test_annotate_latin_1
526 526 if @ruby19_non_utf8_pass
527 527 puts_ruby19_non_utf8_pass()
528 528 elsif WINDOWS_PASS
529 529 puts WINDOWS_SKIP_STR
530 530 elsif JRUBY_SKIP
531 531 puts JRUBY_SKIP_STR
532 532 else
533 533 with_settings :repositories_encodings => 'UTF-8,ISO-8859-1' do
534 534 ['57ca437c', '57ca437c0acbbcb749821fdf3726a1367056d364'].each do |r1|
535 535 get :annotate, :id => PRJ_ID,
536 536 :path => repository_path_hash(['latin-1-dir', "test-#{@char_1}.txt"])[:param],
537 537 :rev => r1
538 538 assert_select "th.line-num", :text => '1' do
539 539 assert_select "+ td.revision" do
540 540 assert_select "a", :text => '57ca437c'
541 541 assert_select "+ td.author", :text => "jsmith" do
542 542 assert_select "+ td",
543 543 :text => "test-#{@char_1}.txt"
544 544 end
545 545 end
546 546 end
547 547 end
548 548 end
549 549 end
550 550 end
551 551
552 552 def test_annotate_latin_1_author
553 553 ['83ca5fd546063a3c7dc2e568ba3355661a9e2b2c', '83ca5fd546063a'].each do |r1|
554 554 get :annotate, :id => PRJ_ID,
555 555 :path => repository_path_hash([" filename with a leading space.txt "])[:param],
556 556 :rev => r1
557 557 assert_select "th.line-num", :text => '1' do
558 558 assert_select "+ td.revision" do
559 559 assert_select "a", :text => '83ca5fd5'
560 560 assert_select "+ td.author", :text => @felix_utf8 do
561 561 assert_select "+ td",
562 562 :text => "And this is a file with a leading and trailing space..."
563 563 end
564 564 end
565 565 end
566 566 end
567 567 end
568 568
569 569 def test_revisions
570 570 assert_equal 0, @repository.changesets.count
571 571 @repository.fetch_changesets
572 572 @project.reload
573 573 assert_equal NUM_REV, @repository.changesets.count
574 574 get :revisions, :id => PRJ_ID
575 575 assert_response :success
576 576 assert_template 'revisions'
577 577 assert_tag :tag => 'form',
578 578 :attributes => {
579 579 :method => 'get',
580 580 :action => '/projects/subproject1/repository/revision'
581 581 }
582 582 end
583 583
584 584 def test_revision
585 585 assert_equal 0, @repository.changesets.count
586 586 @repository.fetch_changesets
587 587 @project.reload
588 588 assert_equal NUM_REV, @repository.changesets.count
589 589 ['61b685fbe55ab05b5ac68402d5720c1a6ac973d1', '61b685f'].each do |r|
590 590 get :revision, :id => PRJ_ID, :rev => r
591 591 assert_response :success
592 592 assert_template 'revision'
593 593 end
594 594 end
595 595
596 596 def test_empty_revision
597 597 assert_equal 0, @repository.changesets.count
598 598 @repository.fetch_changesets
599 599 @project.reload
600 600 assert_equal NUM_REV, @repository.changesets.count
601 601 ['', ' ', nil].each do |r|
602 602 get :revision, :id => PRJ_ID, :rev => r
603 603 assert_response 404
604 604 assert_error_tag :content => /was not found/
605 605 end
606 606 end
607 607
608 608 def test_destroy_valid_repository
609 609 @request.session[:user_id] = 1 # admin
610 610 assert_equal 0, @repository.changesets.count
611 611 @repository.fetch_changesets
612 612 @project.reload
613 613 assert_equal NUM_REV, @repository.changesets.count
614 614
615 615 assert_difference 'Repository.count', -1 do
616 616 delete :destroy, :id => @repository.id
617 617 end
618 618 assert_response 302
619 619 @project.reload
620 620 assert_nil @project.repository
621 621 end
622 622
623 623 def test_destroy_invalid_repository
624 624 @request.session[:user_id] = 1 # admin
625 625 @project.repository.destroy
626 626 @repository = Repository::Git.create!(
627 627 :project => @project,
628 628 :url => "/invalid",
629 629 :path_encoding => 'ISO-8859-1'
630 630 )
631 631 @repository.fetch_changesets
632 632 @repository.reload
633 633 assert_equal 0, @repository.changesets.count
634 634
635 635 assert_difference 'Repository.count', -1 do
636 636 delete :destroy, :id => @repository.id
637 637 end
638 638 assert_response 302
639 639 @project.reload
640 640 assert_nil @project.repository
641 641 end
642 642
643 643 private
644 644
645 645 def puts_ruby19_non_utf8_pass
646 646 puts "TODO: This test fails in Ruby 1.9 " +
647 647 "and Encoding.default_external is not UTF-8. " +
648 648 "Current value is '#{Encoding.default_external.to_s}'"
649 649 end
650 650 else
651 651 puts "Git test repository NOT FOUND. Skipping functional tests !!!"
652 652 def test_fake; assert true end
653 653 end
654 654
655 655 private
656 656 def with_cache(&block)
657 657 before = ActionController::Base.perform_caching
658 658 ActionController::Base.perform_caching = true
659 659 block.call
660 660 ActionController::Base.perform_caching = before
661 661 end
662 662 end
@@ -1,630 +1,632
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2015 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 require File.expand_path('../../test_helper', __FILE__)
19 19
20 20 class RepositoryGitTest < ActiveSupport::TestCase
21 21 fixtures :projects, :repositories, :enabled_modules, :users, :roles
22 22
23 23 include Redmine::I18n
24 24
25 25 REPOSITORY_PATH = Rails.root.join('tmp/test/git_repository').to_s
26 26 REPOSITORY_PATH.gsub!(/\//, "\\") if Redmine::Platform.mswin?
27 27
28 28 NUM_REV = 28
29 29 NUM_HEAD = 6
30 30
31 31 FELIX_HEX = "Felix Sch\xC3\xA4fer"
32 32 CHAR_1_HEX = "\xc3\x9c"
33 33
34 34 ## Git, Mercurial and CVS path encodings are binary.
35 35 ## Subversion supports URL encoding for path.
36 36 ## Redmine Mercurial adapter and extension use URL encoding.
37 37 ## Git accepts only binary path in command line parameter.
38 38 ## So, there is no way to use binary command line parameter in JRuby.
39 39 JRUBY_SKIP = (RUBY_PLATFORM == 'java')
40 40 JRUBY_SKIP_STR = "TODO: This test fails in JRuby"
41 41
42 42 def setup
43 43 @project = Project.find(3)
44 44 @repository = Repository::Git.create(
45 45 :project => @project,
46 46 :url => REPOSITORY_PATH,
47 47 :path_encoding => 'ISO-8859-1'
48 48 )
49 49 assert @repository
50 50 @char_1 = CHAR_1_HEX.dup
51 51 if @char_1.respond_to?(:force_encoding)
52 52 @char_1.force_encoding('UTF-8')
53 53 end
54 54 end
55 55
56 56 def test_nondefault_repo_with_blank_identifier_destruction
57 Repository.delete_all
58
57 59 repo1 = Repository::Git.new(
58 60 :project => @project,
59 61 :url => REPOSITORY_PATH,
60 62 :identifier => '',
61 63 :is_default => true
62 64 )
63 65 assert repo1.save
64 66 repo1.fetch_changesets
65 67
66 68 repo2 = Repository::Git.new(
67 69 :project => @project,
68 70 :url => REPOSITORY_PATH,
69 71 :identifier => 'repo2',
70 72 :is_default => true
71 73 )
72 74 assert repo2.save
73 75 repo2.fetch_changesets
74 76
75 77 repo1.reload
76 78 repo2.reload
77 79 assert !repo1.is_default?
78 80 assert repo2.is_default?
79 81
80 82 assert_difference 'Repository.count', -1 do
81 83 repo1.destroy
82 84 end
83 85 end
84 86
85 87 def test_blank_path_to_repository_error_message
86 88 set_language_if_valid 'en'
87 89 repo = Repository::Git.new(
88 90 :project => @project,
89 91 :identifier => 'test'
90 92 )
91 93 assert !repo.save
92 94 assert_include "Path to repository can't be blank",
93 95 repo.errors.full_messages
94 96 end
95 97
96 98 def test_blank_path_to_repository_error_message_fr
97 99 set_language_if_valid 'fr'
98 100 str = "Chemin du d\xc3\xa9p\xc3\xb4t doit \xc3\xaatre renseign\xc3\xa9(e)"
99 101 str.force_encoding('UTF-8') if str.respond_to?(:force_encoding)
100 102 repo = Repository::Git.new(
101 103 :project => @project,
102 104 :url => "",
103 105 :identifier => 'test',
104 106 :path_encoding => ''
105 107 )
106 108 assert !repo.save
107 109 assert_include str, repo.errors.full_messages
108 110 end
109 111
110 112 if File.directory?(REPOSITORY_PATH)
111 113 ## Ruby uses ANSI api to fork a process on Windows.
112 114 ## Japanese Shift_JIS and Traditional Chinese Big5 have 0x5c(backslash) problem
113 115 ## and these are incompatible with ASCII.
114 116 ## Git for Windows (msysGit) changed internal API from ANSI to Unicode in 1.7.10
115 117 ## http://code.google.com/p/msysgit/issues/detail?id=80
116 118 ## So, Latin-1 path tests fail on Japanese Windows
117 119 WINDOWS_PASS = (Redmine::Platform.mswin? &&
118 120 Redmine::Scm::Adapters::GitAdapter.client_version_above?([1, 7, 10]))
119 121 WINDOWS_SKIP_STR = "TODO: This test fails in Git for Windows above 1.7.10"
120 122
121 123 def test_scm_available
122 124 klass = Repository::Git
123 125 assert_equal "Git", klass.scm_name
124 126 assert klass.scm_adapter_class
125 127 assert_not_equal "", klass.scm_command
126 128 assert_equal true, klass.scm_available
127 129 end
128 130
129 131 def test_entries
130 132 entries = @repository.entries
131 133 assert_kind_of Redmine::Scm::Adapters::Entries, entries
132 134 end
133 135
134 136 def test_fetch_changesets_from_scratch
135 137 assert_nil @repository.extra_info
136 138
137 139 assert_equal 0, @repository.changesets.count
138 140 @repository.fetch_changesets
139 141 @project.reload
140 142
141 143 assert_equal NUM_REV, @repository.changesets.count
142 144 assert_equal 39, @repository.filechanges.count
143 145
144 146 commit = @repository.changesets.find_by_revision("7234cb2750b63f47bff735edc50a1c0a433c2518")
145 147 assert_equal "7234cb2750b63f47bff735edc50a1c0a433c2518", commit.scmid
146 148 assert_equal "Initial import.\nThe repository contains 3 files.", commit.comments
147 149 assert_equal "jsmith <jsmith@foo.bar>", commit.committer
148 150 assert_equal User.find_by_login('jsmith'), commit.user
149 151 # TODO: add a commit with commit time <> author time to the test repository
150 152 assert_equal Time.gm(2007, 12, 14, 9, 22, 52), commit.committed_on
151 153 assert_equal "2007-12-14".to_date, commit.commit_date
152 154 assert_equal 3, commit.filechanges.count
153 155 change = commit.filechanges.sort_by(&:path).first
154 156 assert_equal "README", change.path
155 157 assert_equal nil, change.from_path
156 158 assert_equal "A", change.action
157 159
158 160 assert_equal NUM_HEAD, @repository.extra_info["heads"].size
159 161 end
160 162
161 163 def test_fetch_changesets_incremental
162 164 assert_equal 0, @repository.changesets.count
163 165 @repository.fetch_changesets
164 166 @project.reload
165 167 assert_equal NUM_REV, @repository.changesets.count
166 168 extra_info_heads = @repository.extra_info["heads"].dup
167 169 assert_equal NUM_HEAD, extra_info_heads.size
168 170 extra_info_heads.delete_if { |x| x == "83ca5fd546063a3c7dc2e568ba3355661a9e2b2c" }
169 171 assert_equal 4, extra_info_heads.size
170 172
171 173 del_revs = [
172 174 "83ca5fd546063a3c7dc2e568ba3355661a9e2b2c",
173 175 "ed5bb786bbda2dee66a2d50faf51429dbc043a7b",
174 176 "4f26664364207fa8b1af9f8722647ab2d4ac5d43",
175 177 "deff712f05a90d96edbd70facc47d944be5897e3",
176 178 "32ae898b720c2f7eec2723d5bdd558b4cb2d3ddf",
177 179 "7e61ac704deecde634b51e59daa8110435dcb3da",
178 180 ]
179 181 @repository.changesets.each do |rev|
180 182 rev.destroy if del_revs.detect {|r| r == rev.scmid.to_s }
181 183 end
182 184 @project.reload
183 185 cs1 = @repository.changesets
184 186 assert_equal NUM_REV - 6, cs1.count
185 187 extra_info_heads << "4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8"
186 188 h = {}
187 189 h["heads"] = extra_info_heads
188 190 @repository.merge_extra_info(h)
189 191 @repository.save
190 192 @project.reload
191 193 assert @repository.extra_info["heads"].index("4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8")
192 194 @repository.fetch_changesets
193 195 @project.reload
194 196 assert_equal NUM_REV, @repository.changesets.count
195 197 assert_equal NUM_HEAD, @repository.extra_info["heads"].size
196 198 assert @repository.extra_info["heads"].index("83ca5fd546063a3c7dc2e568ba3355661a9e2b2c")
197 199 end
198 200
199 201 def test_fetch_changesets_history_editing
200 202 assert_equal 0, @repository.changesets.count
201 203 @repository.fetch_changesets
202 204 @project.reload
203 205 assert_equal NUM_REV, @repository.changesets.count
204 206 extra_info_heads = @repository.extra_info["heads"].dup
205 207 assert_equal NUM_HEAD, extra_info_heads.size
206 208 extra_info_heads.delete_if { |x| x == "83ca5fd546063a3c7dc2e568ba3355661a9e2b2c" }
207 209 assert_equal 4, extra_info_heads.size
208 210
209 211 del_revs = [
210 212 "83ca5fd546063a3c7dc2e568ba3355661a9e2b2c",
211 213 "ed5bb786bbda2dee66a2d50faf51429dbc043a7b",
212 214 "4f26664364207fa8b1af9f8722647ab2d4ac5d43",
213 215 "deff712f05a90d96edbd70facc47d944be5897e3",
214 216 "32ae898b720c2f7eec2723d5bdd558b4cb2d3ddf",
215 217 "7e61ac704deecde634b51e59daa8110435dcb3da",
216 218 ]
217 219 @repository.changesets.each do |rev|
218 220 rev.destroy if del_revs.detect {|r| r == rev.scmid.to_s }
219 221 end
220 222 @project.reload
221 223 assert_equal NUM_REV - 6, @repository.changesets.count
222 224
223 225 c = Changeset.new(:repository => @repository,
224 226 :committed_on => Time.now,
225 227 :revision => "abcd1234efgh",
226 228 :scmid => "abcd1234efgh",
227 229 :comments => 'test')
228 230 assert c.save
229 231 @project.reload
230 232 assert_equal NUM_REV - 5, @repository.changesets.count
231 233
232 234 extra_info_heads << "1234abcd5678"
233 235 h = {}
234 236 h["heads"] = extra_info_heads
235 237 @repository.merge_extra_info(h)
236 238 @repository.save
237 239 @project.reload
238 240 h1 = @repository.extra_info["heads"].dup
239 241 assert h1.index("1234abcd5678")
240 242 assert_equal 5, h1.size
241 243
242 244 @repository.fetch_changesets
243 245 @project.reload
244 246 assert_equal NUM_REV - 5, @repository.changesets.count
245 247 h2 = @repository.extra_info["heads"].dup
246 248 assert_equal h1, h2
247 249 end
248 250
249 251 def test_keep_extra_report_last_commit_in_clear_changesets
250 252 assert_nil @repository.extra_info
251 253 h = {}
252 254 h["extra_report_last_commit"] = "1"
253 255 @repository.merge_extra_info(h)
254 256 @repository.save
255 257 @project.reload
256 258
257 259 assert_equal 0, @repository.changesets.count
258 260 @repository.fetch_changesets
259 261 @project.reload
260 262
261 263 assert_equal NUM_REV, @repository.changesets.count
262 264 @repository.send(:clear_changesets)
263 265 assert_equal 1, @repository.extra_info.size
264 266 assert_equal "1", @repository.extra_info["extra_report_last_commit"]
265 267 end
266 268
267 269 def test_refetch_after_clear_changesets
268 270 assert_nil @repository.extra_info
269 271 assert_equal 0, @repository.changesets.count
270 272 @repository.fetch_changesets
271 273 @project.reload
272 274 assert_equal NUM_REV, @repository.changesets.count
273 275
274 276 @repository.send(:clear_changesets)
275 277 @project.reload
276 278 assert_equal 0, @repository.changesets.count
277 279
278 280 @repository.fetch_changesets
279 281 @project.reload
280 282 assert_equal NUM_REV, @repository.changesets.count
281 283 end
282 284
283 285 def test_parents
284 286 assert_equal 0, @repository.changesets.count
285 287 @repository.fetch_changesets
286 288 @project.reload
287 289 assert_equal NUM_REV, @repository.changesets.count
288 290 r1 = @repository.find_changeset_by_name("7234cb2750b63")
289 291 assert_equal [], r1.parents
290 292 r2 = @repository.find_changeset_by_name("899a15dba03a3")
291 293 assert_equal 1, r2.parents.length
292 294 assert_equal "7234cb2750b63f47bff735edc50a1c0a433c2518",
293 295 r2.parents[0].identifier
294 296 r3 = @repository.find_changeset_by_name("32ae898b720c2")
295 297 assert_equal 2, r3.parents.length
296 298 r4 = [r3.parents[0].identifier, r3.parents[1].identifier].sort
297 299 assert_equal "4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8", r4[0]
298 300 assert_equal "7e61ac704deecde634b51e59daa8110435dcb3da", r4[1]
299 301 end
300 302
301 303 def test_db_consistent_ordering_init
302 304 assert_nil @repository.extra_info
303 305 assert_equal 0, @repository.changesets.count
304 306 @repository.fetch_changesets
305 307 @project.reload
306 308 assert_equal 1, @repository.extra_info["db_consistent"]["ordering"]
307 309 end
308 310
309 311 def test_db_consistent_ordering_before_1_2
310 312 assert_nil @repository.extra_info
311 313 assert_equal 0, @repository.changesets.count
312 314 @repository.fetch_changesets
313 315 @project.reload
314 316 assert_equal NUM_REV, @repository.changesets.count
315 317 assert_not_nil @repository.extra_info
316 318 h = {}
317 319 h["heads"] = []
318 320 h["branches"] = {}
319 321 h["db_consistent"] = {}
320 322 @repository.merge_extra_info(h)
321 323 @repository.save
322 324 assert_equal NUM_REV, @repository.changesets.count
323 325 @repository.fetch_changesets
324 326 @project.reload
325 327 assert_equal 0, @repository.extra_info["db_consistent"]["ordering"]
326 328
327 329 extra_info_heads = @repository.extra_info["heads"].dup
328 330 extra_info_heads.delete_if { |x| x == "83ca5fd546063a3c7dc2e568ba3355661a9e2b2c" }
329 331 del_revs = [
330 332 "83ca5fd546063a3c7dc2e568ba3355661a9e2b2c",
331 333 "ed5bb786bbda2dee66a2d50faf51429dbc043a7b",
332 334 "4f26664364207fa8b1af9f8722647ab2d4ac5d43",
333 335 "deff712f05a90d96edbd70facc47d944be5897e3",
334 336 "32ae898b720c2f7eec2723d5bdd558b4cb2d3ddf",
335 337 "7e61ac704deecde634b51e59daa8110435dcb3da",
336 338 ]
337 339 @repository.changesets.each do |rev|
338 340 rev.destroy if del_revs.detect {|r| r == rev.scmid.to_s }
339 341 end
340 342 @project.reload
341 343 cs1 = @repository.changesets
342 344 assert_equal NUM_REV - 6, cs1.count
343 345 assert_equal 0, @repository.extra_info["db_consistent"]["ordering"]
344 346
345 347 extra_info_heads << "4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8"
346 348 h = {}
347 349 h["heads"] = extra_info_heads
348 350 @repository.merge_extra_info(h)
349 351 @repository.save
350 352 @project.reload
351 353 assert @repository.extra_info["heads"].index("4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8")
352 354 @repository.fetch_changesets
353 355 @project.reload
354 356 assert_equal NUM_REV, @repository.changesets.count
355 357 assert_equal NUM_HEAD, @repository.extra_info["heads"].size
356 358
357 359 assert_equal 0, @repository.extra_info["db_consistent"]["ordering"]
358 360 end
359 361
360 362 def test_heads_from_branches_hash
361 363 assert_nil @repository.extra_info
362 364 assert_equal 0, @repository.changesets.count
363 365 assert_equal [], @repository.heads_from_branches_hash
364 366 h = {}
365 367 h["branches"] = {}
366 368 h["branches"]["test1"] = {}
367 369 h["branches"]["test1"]["last_scmid"] = "1234abcd"
368 370 h["branches"]["test2"] = {}
369 371 h["branches"]["test2"]["last_scmid"] = "abcd1234"
370 372 @repository.merge_extra_info(h)
371 373 @repository.save
372 374 @project.reload
373 375 assert_equal ["1234abcd", "abcd1234"], @repository.heads_from_branches_hash.sort
374 376 end
375 377
376 378 def test_latest_changesets
377 379 assert_equal 0, @repository.changesets.count
378 380 @repository.fetch_changesets
379 381 @project.reload
380 382 assert_equal NUM_REV, @repository.changesets.count
381 383 # with limit
382 384 changesets = @repository.latest_changesets('', 'master', 2)
383 385 assert_equal 2, changesets.size
384 386
385 387 # with path
386 388 changesets = @repository.latest_changesets('images', 'master')
387 389 assert_equal [
388 390 'deff712f05a90d96edbd70facc47d944be5897e3',
389 391 '899a15dba03a3b350b89c3f537e4bbe02a03cdc9',
390 392 '7234cb2750b63f47bff735edc50a1c0a433c2518',
391 393 ], changesets.collect(&:revision)
392 394
393 395 changesets = @repository.latest_changesets('README', nil)
394 396 assert_equal [
395 397 '32ae898b720c2f7eec2723d5bdd558b4cb2d3ddf',
396 398 '4a07fe31bffcf2888791f3e6cbc9c4545cefe3e8',
397 399 '713f4944648826f558cf548222f813dabe7cbb04',
398 400 '61b685fbe55ab05b5ac68402d5720c1a6ac973d1',
399 401 '899a15dba03a3b350b89c3f537e4bbe02a03cdc9',
400 402 '7234cb2750b63f47bff735edc50a1c0a433c2518',
401 403 ], changesets.collect(&:revision)
402 404
403 405 # with path, revision and limit
404 406 changesets = @repository.latest_changesets('images', '899a15dba')
405 407 assert_equal [
406 408 '899a15dba03a3b350b89c3f537e4bbe02a03cdc9',
407 409 '7234cb2750b63f47bff735edc50a1c0a433c2518',
408 410 ], changesets.collect(&:revision)
409 411
410 412 changesets = @repository.latest_changesets('images', '899a15dba', 1)
411 413 assert_equal [
412 414 '899a15dba03a3b350b89c3f537e4bbe02a03cdc9',
413 415 ], changesets.collect(&:revision)
414 416
415 417 changesets = @repository.latest_changesets('README', '899a15dba')
416 418 assert_equal [
417 419 '899a15dba03a3b350b89c3f537e4bbe02a03cdc9',
418 420 '7234cb2750b63f47bff735edc50a1c0a433c2518',
419 421 ], changesets.collect(&:revision)
420 422
421 423 changesets = @repository.latest_changesets('README', '899a15dba', 1)
422 424 assert_equal [
423 425 '899a15dba03a3b350b89c3f537e4bbe02a03cdc9',
424 426 ], changesets.collect(&:revision)
425 427
426 428 # with path, tag and limit
427 429 changesets = @repository.latest_changesets('images', 'tag01.annotated')
428 430 assert_equal [
429 431 '899a15dba03a3b350b89c3f537e4bbe02a03cdc9',
430 432 '7234cb2750b63f47bff735edc50a1c0a433c2518',
431 433 ], changesets.collect(&:revision)
432 434
433 435 changesets = @repository.latest_changesets('images', 'tag01.annotated', 1)
434 436 assert_equal [
435 437 '899a15dba03a3b350b89c3f537e4bbe02a03cdc9',
436 438 ], changesets.collect(&:revision)
437 439
438 440 changesets = @repository.latest_changesets('README', 'tag01.annotated')
439 441 assert_equal [
440 442 '899a15dba03a3b350b89c3f537e4bbe02a03cdc9',
441 443 '7234cb2750b63f47bff735edc50a1c0a433c2518',
442 444 ], changesets.collect(&:revision)
443 445
444 446 changesets = @repository.latest_changesets('README', 'tag01.annotated', 1)
445 447 assert_equal [
446 448 '899a15dba03a3b350b89c3f537e4bbe02a03cdc9',
447 449 ], changesets.collect(&:revision)
448 450
449 451 # with path, branch and limit
450 452 changesets = @repository.latest_changesets('images', 'test_branch')
451 453 assert_equal [
452 454 '899a15dba03a3b350b89c3f537e4bbe02a03cdc9',
453 455 '7234cb2750b63f47bff735edc50a1c0a433c2518',
454 456 ], changesets.collect(&:revision)
455 457
456 458 changesets = @repository.latest_changesets('images', 'test_branch', 1)
457 459 assert_equal [
458 460 '899a15dba03a3b350b89c3f537e4bbe02a03cdc9',
459 461 ], changesets.collect(&:revision)
460 462
461 463 changesets = @repository.latest_changesets('README', 'test_branch')
462 464 assert_equal [
463 465 '713f4944648826f558cf548222f813dabe7cbb04',
464 466 '61b685fbe55ab05b5ac68402d5720c1a6ac973d1',
465 467 '899a15dba03a3b350b89c3f537e4bbe02a03cdc9',
466 468 '7234cb2750b63f47bff735edc50a1c0a433c2518',
467 469 ], changesets.collect(&:revision)
468 470
469 471 changesets = @repository.latest_changesets('README', 'test_branch', 2)
470 472 assert_equal [
471 473 '713f4944648826f558cf548222f813dabe7cbb04',
472 474 '61b685fbe55ab05b5ac68402d5720c1a6ac973d1',
473 475 ], changesets.collect(&:revision)
474 476
475 477 if WINDOWS_PASS
476 478 puts WINDOWS_SKIP_STR
477 479 elsif JRUBY_SKIP
478 480 puts JRUBY_SKIP_STR
479 481 else
480 482 # latin-1 encoding path
481 483 changesets = @repository.latest_changesets(
482 484 "latin-1-dir/test-#{@char_1}-2.txt", '64f1f3e89')
483 485 assert_equal [
484 486 '64f1f3e89ad1cb57976ff0ad99a107012ba3481d',
485 487 '4fc55c43bf3d3dc2efb66145365ddc17639ce81e',
486 488 ], changesets.collect(&:revision)
487 489
488 490 changesets = @repository.latest_changesets(
489 491 "latin-1-dir/test-#{@char_1}-2.txt", '64f1f3e89', 1)
490 492 assert_equal [
491 493 '64f1f3e89ad1cb57976ff0ad99a107012ba3481d',
492 494 ], changesets.collect(&:revision)
493 495 end
494 496 end
495 497
496 498 def test_latest_changesets_latin_1_dir
497 499 if WINDOWS_PASS
498 500 puts WINDOWS_SKIP_STR
499 501 elsif JRUBY_SKIP
500 502 puts JRUBY_SKIP_STR
501 503 else
502 504 assert_equal 0, @repository.changesets.count
503 505 @repository.fetch_changesets
504 506 @project.reload
505 507 assert_equal NUM_REV, @repository.changesets.count
506 508 changesets = @repository.latest_changesets(
507 509 "latin-1-dir/test-#{@char_1}-subdir", '1ca7f5ed')
508 510 assert_equal [
509 511 '1ca7f5ed374f3cb31a93ae5215c2e25cc6ec5127',
510 512 ], changesets.collect(&:revision)
511 513 end
512 514 end
513 515
514 516 def test_find_changeset_by_name
515 517 assert_equal 0, @repository.changesets.count
516 518 @repository.fetch_changesets
517 519 @project.reload
518 520 assert_equal NUM_REV, @repository.changesets.count
519 521 ['7234cb2750b63f47bff735edc50a1c0a433c2518', '7234cb2750b'].each do |r|
520 522 assert_equal '7234cb2750b63f47bff735edc50a1c0a433c2518',
521 523 @repository.find_changeset_by_name(r).revision
522 524 end
523 525 end
524 526
525 527 def test_find_changeset_by_empty_name
526 528 assert_equal 0, @repository.changesets.count
527 529 @repository.fetch_changesets
528 530 @project.reload
529 531 assert_equal NUM_REV, @repository.changesets.count
530 532 ['', ' ', nil].each do |r|
531 533 assert_nil @repository.find_changeset_by_name(r)
532 534 end
533 535 end
534 536
535 537 def test_identifier
536 538 assert_equal 0, @repository.changesets.count
537 539 @repository.fetch_changesets
538 540 @project.reload
539 541 assert_equal NUM_REV, @repository.changesets.count
540 542 c = @repository.changesets.find_by_revision(
541 543 '7234cb2750b63f47bff735edc50a1c0a433c2518')
542 544 assert_equal c.scmid, c.identifier
543 545 end
544 546
545 547 def test_format_identifier
546 548 assert_equal 0, @repository.changesets.count
547 549 @repository.fetch_changesets
548 550 @project.reload
549 551 assert_equal NUM_REV, @repository.changesets.count
550 552 c = @repository.changesets.find_by_revision(
551 553 '7234cb2750b63f47bff735edc50a1c0a433c2518')
552 554 assert_equal '7234cb27', c.format_identifier
553 555 end
554 556
555 557 def test_activities
556 558 c = Changeset.new(:repository => @repository,
557 559 :committed_on => Time.now,
558 560 :revision => 'abc7234cb2750b63f47bff735edc50a1c0a433c2',
559 561 :scmid => 'abc7234cb2750b63f47bff735edc50a1c0a433c2',
560 562 :comments => 'test')
561 563 assert c.event_title.include?('abc7234c:')
562 564 assert_equal 'abc7234cb2750b63f47bff735edc50a1c0a433c2', c.event_url[:rev]
563 565 end
564 566
565 567 def test_log_utf8
566 568 assert_equal 0, @repository.changesets.count
567 569 @repository.fetch_changesets
568 570 @project.reload
569 571 assert_equal NUM_REV, @repository.changesets.count
570 572 str_felix_hex = FELIX_HEX.dup
571 573 if str_felix_hex.respond_to?(:force_encoding)
572 574 str_felix_hex.force_encoding('UTF-8')
573 575 end
574 576 c = @repository.changesets.find_by_revision(
575 577 'ed5bb786bbda2dee66a2d50faf51429dbc043a7b')
576 578 assert_equal "#{str_felix_hex} <felix@fachschaften.org>", c.committer
577 579 end
578 580
579 581 def test_previous
580 582 assert_equal 0, @repository.changesets.count
581 583 @repository.fetch_changesets
582 584 @project.reload
583 585 assert_equal NUM_REV, @repository.changesets.count
584 586 %w|1ca7f5ed374f3cb31a93ae5215c2e25cc6ec5127 1ca7f5ed|.each do |r1|
585 587 changeset = @repository.find_changeset_by_name(r1)
586 588 %w|64f1f3e89ad1cb57976ff0ad99a107012ba3481d 64f1f3e89ad1|.each do |r2|
587 589 assert_equal @repository.find_changeset_by_name(r2), changeset.previous
588 590 end
589 591 end
590 592 end
591 593
592 594 def test_previous_nil
593 595 assert_equal 0, @repository.changesets.count
594 596 @repository.fetch_changesets
595 597 @project.reload
596 598 assert_equal NUM_REV, @repository.changesets.count
597 599 %w|7234cb2750b63f47bff735edc50a1c0a433c2518 7234cb275|.each do |r1|
598 600 changeset = @repository.find_changeset_by_name(r1)
599 601 assert_nil changeset.previous
600 602 end
601 603 end
602 604
603 605 def test_next
604 606 assert_equal 0, @repository.changesets.count
605 607 @repository.fetch_changesets
606 608 @project.reload
607 609 assert_equal NUM_REV, @repository.changesets.count
608 610 %w|64f1f3e89ad1cb57976ff0ad99a107012ba3481d 64f1f3e89ad1|.each do |r2|
609 611 changeset = @repository.find_changeset_by_name(r2)
610 612 %w|1ca7f5ed374f3cb31a93ae5215c2e25cc6ec5127 1ca7f5ed|.each do |r1|
611 613 assert_equal @repository.find_changeset_by_name(r1), changeset.next
612 614 end
613 615 end
614 616 end
615 617
616 618 def test_next_nil
617 619 assert_equal 0, @repository.changesets.count
618 620 @repository.fetch_changesets
619 621 @project.reload
620 622 assert_equal NUM_REV, @repository.changesets.count
621 623 %w|2a682156a3b6e77a8bf9cd4590e8db757f3c6c78 2a682156a3b6e77a|.each do |r1|
622 624 changeset = @repository.find_changeset_by_name(r1)
623 625 assert_nil changeset.next
624 626 end
625 627 end
626 628 else
627 629 puts "Git test repository NOT FOUND. Skipping unit tests !!!"
628 630 def test_fake; assert true end
629 631 end
630 632 end
@@ -1,481 +1,493
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2015 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 require File.expand_path('../../test_helper', __FILE__)
19 19
20 20 class RepositoryTest < ActiveSupport::TestCase
21 21 fixtures :projects,
22 22 :trackers,
23 23 :projects_trackers,
24 24 :enabled_modules,
25 25 :repositories,
26 26 :issues,
27 27 :issue_statuses,
28 28 :issue_categories,
29 29 :changesets,
30 30 :changes,
31 31 :users,
32 32 :members,
33 33 :member_roles,
34 34 :roles,
35 35 :enumerations
36 36
37 37 include Redmine::I18n
38 38
39 39 def setup
40 40 @repository = Project.find(1).repository
41 41 end
42 42
43 43 def test_blank_log_encoding_error_message
44 44 set_language_if_valid 'en'
45 45 repo = Repository::Bazaar.new(
46 46 :project => Project.find(3),
47 47 :url => "/test",
48 48 :log_encoding => ''
49 49 )
50 50 assert !repo.save
51 51 assert_include "Commit messages encoding can't be blank",
52 52 repo.errors.full_messages
53 53 end
54 54
55 55 def test_blank_log_encoding_error_message_fr
56 56 set_language_if_valid 'fr'
57 57 str = "Encodage des messages de commit doit \xc3\xaatre renseign\xc3\xa9(e)"
58 58 str.force_encoding('UTF-8') if str.respond_to?(:force_encoding)
59 59 repo = Repository::Bazaar.new(
60 60 :project => Project.find(3),
61 61 :url => "/test"
62 62 )
63 63 assert !repo.save
64 64 assert_include str, repo.errors.full_messages
65 65 end
66 66
67 67 def test_create
68 68 repository = Repository::Subversion.new(:project => Project.find(3))
69 69 assert !repository.save
70 70
71 71 repository.url = "svn://localhost"
72 72 assert repository.save
73 73 repository.reload
74 74
75 75 project = Project.find(3)
76 76 assert_equal repository, project.repository
77 77 end
78 78
79 79 def test_2_repositories_with_same_identifier_in_different_projects_should_be_valid
80 80 Repository::Subversion.create!(:project_id => 2, :identifier => 'foo', :url => 'file:///foo')
81 81 r = Repository::Subversion.new(:project_id => 3, :identifier => 'foo', :url => 'file:///bar')
82 82 assert r.save
83 83 end
84 84
85 85 def test_2_repositories_with_same_identifier_should_not_be_valid
86 86 Repository::Subversion.create!(:project_id => 3, :identifier => 'foo', :url => 'file:///foo')
87 87 r = Repository::Subversion.new(:project_id => 3, :identifier => 'foo', :url => 'file:///bar')
88 88 assert !r.save
89 89 end
90 90
91 91 def test_2_repositories_with_blank_identifier_should_not_be_valid
92 92 Repository::Subversion.create!(:project_id => 3, :identifier => '', :url => 'file:///foo')
93 93 r = Repository::Subversion.new(:project_id => 3, :identifier => '', :url => 'file:///bar')
94 94 assert !r.save
95 95 end
96 96
97 def test_2_repositories_with_blank_identifier_and_one_as_default_should_not_be_valid
98 Repository::Subversion.create!(:project_id => 3, :identifier => '', :url => 'file:///foo', :is_default => true)
99 r = Repository::Subversion.new(:project_id => 3, :identifier => '', :url => 'file:///bar')
100 assert !r.save
101 end
102
103 def test_2_repositories_with_blank_and_nil_identifier_should_not_be_valid
104 Repository::Subversion.create!(:project_id => 3, :identifier => nil, :url => 'file:///foo')
105 r = Repository::Subversion.new(:project_id => 3, :identifier => '', :url => 'file:///bar')
106 assert !r.save
107 end
108
97 109 def test_first_repository_should_be_set_as_default
98 110 repository1 = Repository::Subversion.new(
99 111 :project => Project.find(3),
100 112 :identifier => 'svn1',
101 113 :url => 'file:///svn1'
102 114 )
103 115 assert repository1.save
104 116 assert repository1.is_default?
105 117
106 118 repository2 = Repository::Subversion.new(
107 119 :project => Project.find(3),
108 120 :identifier => 'svn2',
109 121 :url => 'file:///svn2'
110 122 )
111 123 assert repository2.save
112 124 assert !repository2.is_default?
113 125
114 126 assert_equal repository1, Project.find(3).repository
115 127 assert_equal [repository1, repository2], Project.find(3).repositories.sort
116 128 end
117 129
118 130 def test_default_repository_should_be_one
119 131 assert_equal 0, Project.find(3).repositories.count
120 132 repository1 = Repository::Subversion.new(
121 133 :project => Project.find(3),
122 134 :identifier => 'svn1',
123 135 :url => 'file:///svn1'
124 136 )
125 137 assert repository1.save
126 138 assert repository1.is_default?
127 139
128 140 repository2 = Repository::Subversion.new(
129 141 :project => Project.find(3),
130 142 :identifier => 'svn2',
131 143 :url => 'file:///svn2',
132 144 :is_default => true
133 145 )
134 146 assert repository2.save
135 147 assert repository2.is_default?
136 148 repository1.reload
137 149 assert !repository1.is_default?
138 150
139 151 assert_equal repository2, Project.find(3).repository
140 152 assert_equal [repository2, repository1], Project.find(3).repositories.sort
141 153 end
142 154
143 155 def test_identifier_should_accept_letters_digits_dashes_and_underscores
144 156 r = Repository::Subversion.new(
145 157 :project_id => 3,
146 158 :identifier => 'svn-123_45',
147 159 :url => 'file:///svn'
148 160 )
149 161 assert r.save
150 162 end
151 163
152 164 def test_identifier_should_not_be_frozen_for_a_new_repository
153 165 assert_equal false, Repository.new.identifier_frozen?
154 166 end
155 167
156 168 def test_identifier_should_not_be_frozen_for_a_saved_repository_with_blank_identifier
157 169 Repository.where(:id => 10).update_all(["identifier = ''"])
158 170 assert_equal false, Repository.find(10).identifier_frozen?
159 171 end
160 172
161 173 def test_identifier_should_be_frozen_for_a_saved_repository_with_valid_identifier
162 174 Repository.where(:id => 10).update_all(["identifier = 'abc123'"])
163 175 assert_equal true, Repository.find(10).identifier_frozen?
164 176 end
165 177
166 178 def test_identifier_should_not_accept_change_if_frozen
167 179 r = Repository.new(:identifier => 'foo')
168 180 r.stubs(:identifier_frozen?).returns(true)
169 181
170 182 r.identifier = 'bar'
171 183 assert_equal 'foo', r.identifier
172 184 end
173 185
174 186 def test_identifier_should_accept_change_if_not_frozen
175 187 r = Repository.new(:identifier => 'foo')
176 188 r.stubs(:identifier_frozen?).returns(false)
177 189
178 190 r.identifier = 'bar'
179 191 assert_equal 'bar', r.identifier
180 192 end
181 193
182 194 def test_destroy
183 195 repository = Repository.find(10)
184 196 changesets = repository.changesets.count
185 197 changes = repository.filechanges.count
186 198
187 199 assert_difference 'Changeset.count', -changesets do
188 200 assert_difference 'Change.count', -changes do
189 201 Repository.find(10).destroy
190 202 end
191 203 end
192 204 end
193 205
194 206 def test_destroy_should_delete_parents_associations
195 207 changeset = Changeset.find(102)
196 208 changeset.parents = Changeset.where(:id => [100, 101]).all
197 209 assert_difference 'Changeset.connection.select_all("select * from changeset_parents").count', -2 do
198 210 Repository.find(10).destroy
199 211 end
200 212 end
201 213
202 214 def test_destroy_should_delete_issues_associations
203 215 changeset = Changeset.find(102)
204 216 changeset.issues = Issue.where(:id => [1, 2]).all
205 217 assert_difference 'Changeset.connection.select_all("select * from changesets_issues").count', -2 do
206 218 Repository.find(10).destroy
207 219 end
208 220 end
209 221
210 222 def test_should_not_create_with_disabled_scm
211 223 # disable Subversion
212 224 with_settings :enabled_scm => ['Darcs', 'Git'] do
213 225 repository = Repository::Subversion.new(
214 226 :project => Project.find(3), :url => "svn://localhost")
215 227 assert !repository.save
216 228 assert_include I18n.translate('activerecord.errors.messages.invalid'),
217 229 repository.errors[:type]
218 230 end
219 231 end
220 232
221 233 def test_scan_changesets_for_issue_ids
222 234 Setting.default_language = 'en'
223 235 Setting.commit_ref_keywords = 'refs , references, IssueID'
224 236 Setting.commit_update_keywords = [
225 237 {'keywords' => 'fixes , closes',
226 238 'status_id' => IssueStatus.where(:is_closed => true).first.id,
227 239 'done_ratio' => '90'}
228 240 ]
229 241 Setting.default_language = 'en'
230 242 ActionMailer::Base.deliveries.clear
231 243
232 244 # make sure issue 1 is not already closed
233 245 fixed_issue = Issue.find(1)
234 246 assert !fixed_issue.status.is_closed?
235 247 old_status = fixed_issue.status
236 248
237 249 with_settings :notified_events => %w(issue_added issue_updated) do
238 250 Repository.scan_changesets_for_issue_ids
239 251 end
240 252 assert_equal [101, 102], Issue.find(3).changeset_ids
241 253
242 254 # fixed issues
243 255 fixed_issue.reload
244 256 assert fixed_issue.status.is_closed?
245 257 assert_equal 90, fixed_issue.done_ratio
246 258 assert_equal [101], fixed_issue.changeset_ids
247 259
248 260 # issue change
249 261 journal = fixed_issue.journals.reorder('created_on desc').first
250 262 assert_equal User.find_by_login('dlopper'), journal.user
251 263 assert_equal 'Applied in changeset r2.', journal.notes
252 264
253 265 # 2 email notifications
254 266 assert_equal 2, ActionMailer::Base.deliveries.size
255 267 mail = ActionMailer::Base.deliveries.first
256 268 assert_not_nil mail
257 269 assert mail.subject.starts_with?(
258 270 "[#{fixed_issue.project.name} - #{fixed_issue.tracker.name} ##{fixed_issue.id}]")
259 271 assert_mail_body_match(
260 272 "Status changed from #{old_status} to #{fixed_issue.status}", mail)
261 273
262 274 # ignoring commits referencing an issue of another project
263 275 assert_equal [], Issue.find(4).changesets
264 276 end
265 277
266 278 def test_for_changeset_comments_strip
267 279 repository = Repository::Mercurial.create(
268 280 :project => Project.find( 4 ),
269 281 :url => '/foo/bar/baz' )
270 282 comment = <<-COMMENT
271 283 This is a loooooooooooooooooooooooooooong comment
272 284
273 285
274 286 COMMENT
275 287 changeset = Changeset.new(
276 288 :comments => comment, :commit_date => Time.now,
277 289 :revision => 0, :scmid => 'f39b7922fb3c',
278 290 :committer => 'foo <foo@example.com>',
279 291 :committed_on => Time.now, :repository => repository )
280 292 assert( changeset.save )
281 293 assert_not_equal( comment, changeset.comments )
282 294 assert_equal( 'This is a loooooooooooooooooooooooooooong comment',
283 295 changeset.comments )
284 296 end
285 297
286 298 def test_for_urls_strip_cvs
287 299 repository = Repository::Cvs.create(
288 300 :project => Project.find(4),
289 301 :url => ' :pserver:login:password@host:/path/to/the/repository',
290 302 :root_url => 'foo ',
291 303 :log_encoding => 'UTF-8')
292 304 assert repository.save
293 305 repository.reload
294 306 assert_equal ':pserver:login:password@host:/path/to/the/repository',
295 307 repository.url
296 308 assert_equal 'foo', repository.root_url
297 309 end
298 310
299 311 def test_for_urls_strip_subversion
300 312 repository = Repository::Subversion.create(
301 313 :project => Project.find(4),
302 314 :url => ' file:///dummy ')
303 315 assert repository.save
304 316 repository.reload
305 317 assert_equal 'file:///dummy', repository.url
306 318 end
307 319
308 320 def test_for_urls_strip_git
309 321 repository = Repository::Git.create(
310 322 :project => Project.find(4),
311 323 :url => ' c:\dummy ')
312 324 assert repository.save
313 325 repository.reload
314 326 assert_equal 'c:\dummy', repository.url
315 327 end
316 328
317 329 def test_manual_user_mapping
318 330 assert_no_difference "Changeset.where('user_id <> 2').count" do
319 331 c = Changeset.create!(
320 332 :repository => @repository,
321 333 :committer => 'foo',
322 334 :committed_on => Time.now,
323 335 :revision => 100,
324 336 :comments => 'Committed by foo.'
325 337 )
326 338 assert_nil c.user
327 339 @repository.committer_ids = {'foo' => '2'}
328 340 assert_equal User.find(2), c.reload.user
329 341 # committer is now mapped
330 342 c = Changeset.create!(
331 343 :repository => @repository,
332 344 :committer => 'foo',
333 345 :committed_on => Time.now,
334 346 :revision => 101,
335 347 :comments => 'Another commit by foo.'
336 348 )
337 349 assert_equal User.find(2), c.user
338 350 end
339 351 end
340 352
341 353 def test_auto_user_mapping_by_username
342 354 c = Changeset.create!(
343 355 :repository => @repository,
344 356 :committer => 'jsmith',
345 357 :committed_on => Time.now,
346 358 :revision => 100,
347 359 :comments => 'Committed by john.'
348 360 )
349 361 assert_equal User.find(2), c.user
350 362 end
351 363
352 364 def test_auto_user_mapping_by_email
353 365 c = Changeset.create!(
354 366 :repository => @repository,
355 367 :committer => 'john <jsmith@somenet.foo>',
356 368 :committed_on => Time.now,
357 369 :revision => 100,
358 370 :comments => 'Committed by john.'
359 371 )
360 372 assert_equal User.find(2), c.user
361 373 end
362 374
363 375 def test_filesystem_avaialbe
364 376 klass = Repository::Filesystem
365 377 assert klass.scm_adapter_class
366 378 assert_equal true, klass.scm_available
367 379 end
368 380
369 381 def test_extra_info_should_not_return_non_hash_value
370 382 repo = Repository.new
371 383 repo.extra_info = "foo"
372 384 assert_nil repo.extra_info
373 385 end
374 386
375 387 def test_merge_extra_info
376 388 repo = Repository::Subversion.new(:project => Project.find(3))
377 389 assert !repo.save
378 390 repo.url = "svn://localhost"
379 391 assert repo.save
380 392 repo.reload
381 393 project = Project.find(3)
382 394 assert_equal repo, project.repository
383 395 assert_nil repo.extra_info
384 396 h1 = {"test_1" => {"test_11" => "test_value_11"}}
385 397 repo.merge_extra_info(h1)
386 398 assert_equal h1, repo.extra_info
387 399 h2 = {"test_2" => {
388 400 "test_21" => "test_value_21",
389 401 "test_22" => "test_value_22",
390 402 }}
391 403 repo.merge_extra_info(h2)
392 404 assert_equal (h = {"test_11" => "test_value_11"}),
393 405 repo.extra_info["test_1"]
394 406 assert_equal "test_value_21",
395 407 repo.extra_info["test_2"]["test_21"]
396 408 h3 = {"test_2" => {
397 409 "test_23" => "test_value_23",
398 410 "test_24" => "test_value_24",
399 411 }}
400 412 repo.merge_extra_info(h3)
401 413 assert_equal (h = {"test_11" => "test_value_11"}),
402 414 repo.extra_info["test_1"]
403 415 assert_nil repo.extra_info["test_2"]["test_21"]
404 416 assert_equal "test_value_23",
405 417 repo.extra_info["test_2"]["test_23"]
406 418 end
407 419
408 420 def test_sort_should_not_raise_an_error_with_nil_identifiers
409 421 r1 = Repository.new
410 422 r2 = Repository.new
411 423
412 424 assert_nothing_raised do
413 425 [r1, r2].sort
414 426 end
415 427 end
416 428
417 429 def test_stats_by_author_reflect_changesets_and_changes
418 430 repository = Repository.find(10)
419 431
420 432 expected = {"Dave Lopper"=>{:commits_count=>10, :changes_count=>3}}
421 433 assert_equal expected, repository.stats_by_author
422 434
423 435 set = Changeset.create!(
424 436 :repository => repository,
425 437 :committer => 'dlopper',
426 438 :committed_on => Time.now,
427 439 :revision => 101,
428 440 :comments => 'Another commit by foo.'
429 441 )
430 442 Change.create!(:changeset => set, :action => 'A', :path => '/path/to/file1')
431 443 Change.create!(:changeset => set, :action => 'A', :path => '/path/to/file2')
432 444 expected = {"Dave Lopper"=>{:commits_count=>11, :changes_count=>5}}
433 445 assert_equal expected, repository.stats_by_author
434 446 end
435 447
436 448 def test_stats_by_author_honnor_committers
437 449 # in fact it is really tested above, but let's have a dedicated test
438 450 # to ensure things are dynamically linked to Users
439 451 User.find_by_login("dlopper").update_attribute(:firstname, "Dave's")
440 452 repository = Repository.find(10)
441 453 expected = {"Dave's Lopper"=>{:commits_count=>10, :changes_count=>3}}
442 454 assert_equal expected, repository.stats_by_author
443 455 end
444 456
445 457 def test_stats_by_author_doesnt_drop_unmapped_users
446 458 repository = Repository.find(10)
447 459 Changeset.create!(
448 460 :repository => repository,
449 461 :committer => 'unnamed <foo@bar.net>',
450 462 :committed_on => Time.now,
451 463 :revision => 101,
452 464 :comments => 'Another commit by foo.'
453 465 )
454 466
455 467 assert repository.stats_by_author.has_key?("unnamed <foo@bar.net>")
456 468 end
457 469
458 470 def test_stats_by_author_merge_correctly
459 471 # as we honnor users->committer map and it's not injective,
460 472 # we must be sure merges happen correctly and stats are not
461 473 # wiped out when two source counts map to the same user.
462 474 #
463 475 # Here we have Changeset's with committer="dlopper" and others
464 476 # with committer="dlopper <dlopper@somefoo.net>"
465 477 repository = Repository.find(10)
466 478
467 479 expected = {"Dave Lopper"=>{:commits_count=>10, :changes_count=>3}}
468 480 assert_equal expected, repository.stats_by_author
469 481
470 482 set = Changeset.create!(
471 483 :repository => repository,
472 484 :committer => 'dlopper <dlopper@somefoo.net>',
473 485 :committed_on => Time.now,
474 486 :revision => 101,
475 487 :comments => 'Another commit by foo.'
476 488 )
477 489
478 490 expected = {"Dave Lopper"=>{:commits_count=>11, :changes_count=>3}}
479 491 assert_equal expected, repository.stats_by_author
480 492 end
481 493 end
General Comments 0
You need to be logged in to leave comments. Login now