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