##// END OF EJS Templates
Merged r4703 from trunk (scm: fix error on revision page for empty revision)....
Toshi MARUYAMA -
r4587:b877215261d4
parent child
Show More
@@ -1,334 +1,335
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2009 Jean-Philippe Lang
2 # Copyright (C) 2006-2009 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require 'SVG/Graph/Bar'
18 require 'SVG/Graph/Bar'
19 require 'SVG/Graph/BarHorizontal'
19 require 'SVG/Graph/BarHorizontal'
20 require 'digest/sha1'
20 require 'digest/sha1'
21
21
22 class ChangesetNotFound < Exception; end
22 class ChangesetNotFound < Exception; end
23 class InvalidRevisionParam < Exception; end
23 class InvalidRevisionParam < Exception; end
24
24
25 class RepositoriesController < ApplicationController
25 class RepositoriesController < ApplicationController
26 menu_item :repository
26 menu_item :repository
27 menu_item :settings, :only => :edit
27 menu_item :settings, :only => :edit
28 default_search_scope :changesets
28 default_search_scope :changesets
29
29
30 before_filter :find_repository, :except => :edit
30 before_filter :find_repository, :except => :edit
31 before_filter :find_project, :only => :edit
31 before_filter :find_project, :only => :edit
32 before_filter :authorize
32 before_filter :authorize
33 accept_key_auth :revisions
33 accept_key_auth :revisions
34
34
35 rescue_from Redmine::Scm::Adapters::CommandFailed, :with => :show_error_command_failed
35 rescue_from Redmine::Scm::Adapters::CommandFailed, :with => :show_error_command_failed
36
36
37 def edit
37 def edit
38 @repository = @project.repository
38 @repository = @project.repository
39 if !@repository
39 if !@repository
40 @repository = Repository.factory(params[:repository_scm])
40 @repository = Repository.factory(params[:repository_scm])
41 @repository.project = @project if @repository
41 @repository.project = @project if @repository
42 end
42 end
43 if request.post? && @repository
43 if request.post? && @repository
44 @repository.attributes = params[:repository]
44 @repository.attributes = params[:repository]
45 @repository.save
45 @repository.save
46 end
46 end
47 render(:update) do |page|
47 render(:update) do |page|
48 page.replace_html "tab-content-repository", :partial => 'projects/settings/repository'
48 page.replace_html "tab-content-repository", :partial => 'projects/settings/repository'
49 if @repository && !@project.repository
49 if @repository && !@project.repository
50 @project.reload #needed to reload association
50 @project.reload #needed to reload association
51 page.replace_html "main-menu", render_main_menu(@project)
51 page.replace_html "main-menu", render_main_menu(@project)
52 end
52 end
53 end
53 end
54 end
54 end
55
55
56 def committers
56 def committers
57 @committers = @repository.committers
57 @committers = @repository.committers
58 @users = @project.users
58 @users = @project.users
59 additional_user_ids = @committers.collect(&:last).collect(&:to_i) - @users.collect(&:id)
59 additional_user_ids = @committers.collect(&:last).collect(&:to_i) - @users.collect(&:id)
60 @users += User.find_all_by_id(additional_user_ids) unless additional_user_ids.empty?
60 @users += User.find_all_by_id(additional_user_ids) unless additional_user_ids.empty?
61 @users.compact!
61 @users.compact!
62 @users.sort!
62 @users.sort!
63 if request.post? && params[:committers].is_a?(Hash)
63 if request.post? && params[:committers].is_a?(Hash)
64 # Build a hash with repository usernames as keys and corresponding user ids as values
64 # Build a hash with repository usernames as keys and corresponding user ids as values
65 @repository.committer_ids = params[:committers].values.inject({}) {|h, c| h[c.first] = c.last; h}
65 @repository.committer_ids = params[:committers].values.inject({}) {|h, c| h[c.first] = c.last; h}
66 flash[:notice] = l(:notice_successful_update)
66 flash[:notice] = l(:notice_successful_update)
67 redirect_to :action => 'committers', :id => @project
67 redirect_to :action => 'committers', :id => @project
68 end
68 end
69 end
69 end
70
70
71 def destroy
71 def destroy
72 @repository.destroy
72 @repository.destroy
73 redirect_to :controller => 'projects', :action => 'settings', :id => @project, :tab => 'repository'
73 redirect_to :controller => 'projects', :action => 'settings', :id => @project, :tab => 'repository'
74 end
74 end
75
75
76 def show
76 def show
77 @repository.fetch_changesets if Setting.autofetch_changesets? && @path.empty?
77 @repository.fetch_changesets if Setting.autofetch_changesets? && @path.empty?
78
78
79 @entries = @repository.entries(@path, @rev)
79 @entries = @repository.entries(@path, @rev)
80 if request.xhr?
80 if request.xhr?
81 @entries ? render(:partial => 'dir_list_content') : render(:nothing => true)
81 @entries ? render(:partial => 'dir_list_content') : render(:nothing => true)
82 else
82 else
83 (show_error_not_found; return) unless @entries
83 (show_error_not_found; return) unless @entries
84 @changesets = @repository.latest_changesets(@path, @rev)
84 @changesets = @repository.latest_changesets(@path, @rev)
85 @properties = @repository.properties(@path, @rev)
85 @properties = @repository.properties(@path, @rev)
86 render :action => 'show'
86 render :action => 'show'
87 end
87 end
88 end
88 end
89
89
90 alias_method :browse, :show
90 alias_method :browse, :show
91
91
92 def changes
92 def changes
93 @entry = @repository.entry(@path, @rev)
93 @entry = @repository.entry(@path, @rev)
94 (show_error_not_found; return) unless @entry
94 (show_error_not_found; return) unless @entry
95 @changesets = @repository.latest_changesets(@path, @rev, Setting.repository_log_display_limit.to_i)
95 @changesets = @repository.latest_changesets(@path, @rev, Setting.repository_log_display_limit.to_i)
96 @properties = @repository.properties(@path, @rev)
96 @properties = @repository.properties(@path, @rev)
97 end
97 end
98
98
99 def revisions
99 def revisions
100 @changeset_count = @repository.changesets.count
100 @changeset_count = @repository.changesets.count
101 @changeset_pages = Paginator.new self, @changeset_count,
101 @changeset_pages = Paginator.new self, @changeset_count,
102 per_page_option,
102 per_page_option,
103 params['page']
103 params['page']
104 @changesets = @repository.changesets.find(:all,
104 @changesets = @repository.changesets.find(:all,
105 :limit => @changeset_pages.items_per_page,
105 :limit => @changeset_pages.items_per_page,
106 :offset => @changeset_pages.current.offset,
106 :offset => @changeset_pages.current.offset,
107 :include => [:user, :repository])
107 :include => [:user, :repository])
108
108
109 respond_to do |format|
109 respond_to do |format|
110 format.html { render :layout => false if request.xhr? }
110 format.html { render :layout => false if request.xhr? }
111 format.atom { render_feed(@changesets, :title => "#{@project.name}: #{l(:label_revision_plural)}") }
111 format.atom { render_feed(@changesets, :title => "#{@project.name}: #{l(:label_revision_plural)}") }
112 end
112 end
113 end
113 end
114
114
115 def entry
115 def entry
116 @entry = @repository.entry(@path, @rev)
116 @entry = @repository.entry(@path, @rev)
117 (show_error_not_found; return) unless @entry
117 (show_error_not_found; return) unless @entry
118
118
119 # If the entry is a dir, show the browser
119 # If the entry is a dir, show the browser
120 (show; return) if @entry.is_dir?
120 (show; return) if @entry.is_dir?
121
121
122 @content = @repository.cat(@path, @rev)
122 @content = @repository.cat(@path, @rev)
123 (show_error_not_found; return) unless @content
123 (show_error_not_found; return) unless @content
124 if 'raw' == params[:format] || @content.is_binary_data? || (@entry.size && @entry.size > Setting.file_max_size_displayed.to_i.kilobyte)
124 if 'raw' == params[:format] || @content.is_binary_data? || (@entry.size && @entry.size > Setting.file_max_size_displayed.to_i.kilobyte)
125 # Force the download
125 # Force the download
126 send_data @content, :filename => @path.split('/').last
126 send_data @content, :filename => @path.split('/').last
127 else
127 else
128 # Prevent empty lines when displaying a file with Windows style eol
128 # Prevent empty lines when displaying a file with Windows style eol
129 @content.gsub!("\r\n", "\n")
129 @content.gsub!("\r\n", "\n")
130 end
130 end
131 end
131 end
132
132
133 def annotate
133 def annotate
134 @entry = @repository.entry(@path, @rev)
134 @entry = @repository.entry(@path, @rev)
135 (show_error_not_found; return) unless @entry
135 (show_error_not_found; return) unless @entry
136
136
137 @annotate = @repository.scm.annotate(@path, @rev)
137 @annotate = @repository.scm.annotate(@path, @rev)
138 (render_error l(:error_scm_annotate); return) if @annotate.nil? || @annotate.empty?
138 (render_error l(:error_scm_annotate); return) if @annotate.nil? || @annotate.empty?
139 end
139 end
140
140
141 def revision
141 def revision
142 raise ChangesetNotFound if @rev.nil? || @rev.empty?
142 @changeset = @repository.find_changeset_by_name(@rev)
143 @changeset = @repository.find_changeset_by_name(@rev)
143 raise ChangesetNotFound unless @changeset
144 raise ChangesetNotFound unless @changeset
144
145
145 respond_to do |format|
146 respond_to do |format|
146 format.html
147 format.html
147 format.js {render :layout => false}
148 format.js {render :layout => false}
148 end
149 end
149 rescue ChangesetNotFound
150 rescue ChangesetNotFound
150 show_error_not_found
151 show_error_not_found
151 end
152 end
152
153
153 def diff
154 def diff
154 if params[:format] == 'diff'
155 if params[:format] == 'diff'
155 @diff = @repository.diff(@path, @rev, @rev_to)
156 @diff = @repository.diff(@path, @rev, @rev_to)
156 (show_error_not_found; return) unless @diff
157 (show_error_not_found; return) unless @diff
157 filename = "changeset_r#{@rev}"
158 filename = "changeset_r#{@rev}"
158 filename << "_r#{@rev_to}" if @rev_to
159 filename << "_r#{@rev_to}" if @rev_to
159 send_data @diff.join, :filename => "#{filename}.diff",
160 send_data @diff.join, :filename => "#{filename}.diff",
160 :type => 'text/x-patch',
161 :type => 'text/x-patch',
161 :disposition => 'attachment'
162 :disposition => 'attachment'
162 else
163 else
163 @diff_type = params[:type] || User.current.pref[:diff_type] || 'inline'
164 @diff_type = params[:type] || User.current.pref[:diff_type] || 'inline'
164 @diff_type = 'inline' unless %w(inline sbs).include?(@diff_type)
165 @diff_type = 'inline' unless %w(inline sbs).include?(@diff_type)
165
166
166 # Save diff type as user preference
167 # Save diff type as user preference
167 if User.current.logged? && @diff_type != User.current.pref[:diff_type]
168 if User.current.logged? && @diff_type != User.current.pref[:diff_type]
168 User.current.pref[:diff_type] = @diff_type
169 User.current.pref[:diff_type] = @diff_type
169 User.current.preference.save
170 User.current.preference.save
170 end
171 end
171
172
172 @cache_key = "repositories/diff/#{@repository.id}/" + Digest::MD5.hexdigest("#{@path}-#{@rev}-#{@rev_to}-#{@diff_type}")
173 @cache_key = "repositories/diff/#{@repository.id}/" + Digest::MD5.hexdigest("#{@path}-#{@rev}-#{@rev_to}-#{@diff_type}")
173 unless read_fragment(@cache_key)
174 unless read_fragment(@cache_key)
174 @diff = @repository.diff(@path, @rev, @rev_to)
175 @diff = @repository.diff(@path, @rev, @rev_to)
175 show_error_not_found unless @diff
176 show_error_not_found unless @diff
176 end
177 end
177
178
178 @changeset = @repository.find_changeset_by_name(@rev)
179 @changeset = @repository.find_changeset_by_name(@rev)
179 @changeset_to = @rev_to ? @repository.find_changeset_by_name(@rev_to) : nil
180 @changeset_to = @rev_to ? @repository.find_changeset_by_name(@rev_to) : nil
180 end
181 end
181 end
182 end
182
183
183 def stats
184 def stats
184 end
185 end
185
186
186 def graph
187 def graph
187 data = nil
188 data = nil
188 case params[:graph]
189 case params[:graph]
189 when "commits_per_month"
190 when "commits_per_month"
190 data = graph_commits_per_month(@repository)
191 data = graph_commits_per_month(@repository)
191 when "commits_per_author"
192 when "commits_per_author"
192 data = graph_commits_per_author(@repository)
193 data = graph_commits_per_author(@repository)
193 end
194 end
194 if data
195 if data
195 headers["Content-Type"] = "image/svg+xml"
196 headers["Content-Type"] = "image/svg+xml"
196 send_data(data, :type => "image/svg+xml", :disposition => "inline")
197 send_data(data, :type => "image/svg+xml", :disposition => "inline")
197 else
198 else
198 render_404
199 render_404
199 end
200 end
200 end
201 end
201
202
202 private
203 private
203
204
204 REV_PARAM_RE = %r{\A[a-f0-9]*\Z}i
205 REV_PARAM_RE = %r{\A[a-f0-9]*\Z}i
205
206
206 def find_repository
207 def find_repository
207 @project = Project.find(params[:id])
208 @project = Project.find(params[:id])
208 @repository = @project.repository
209 @repository = @project.repository
209 (render_404; return false) unless @repository
210 (render_404; return false) unless @repository
210 @path = params[:path].join('/') unless params[:path].nil?
211 @path = params[:path].join('/') unless params[:path].nil?
211 @path ||= ''
212 @path ||= ''
212 @rev = params[:rev].blank? ? @repository.default_branch : params[:rev].strip
213 @rev = params[:rev].blank? ? @repository.default_branch : params[:rev].strip
213 @rev_to = params[:rev_to]
214 @rev_to = params[:rev_to]
214
215
215 unless @rev.to_s.match(REV_PARAM_RE) && @rev.to_s.match(REV_PARAM_RE)
216 unless @rev.to_s.match(REV_PARAM_RE) && @rev.to_s.match(REV_PARAM_RE)
216 if @repository.branches.blank?
217 if @repository.branches.blank?
217 raise InvalidRevisionParam
218 raise InvalidRevisionParam
218 end
219 end
219 end
220 end
220 rescue ActiveRecord::RecordNotFound
221 rescue ActiveRecord::RecordNotFound
221 render_404
222 render_404
222 rescue InvalidRevisionParam
223 rescue InvalidRevisionParam
223 show_error_not_found
224 show_error_not_found
224 end
225 end
225
226
226 def show_error_not_found
227 def show_error_not_found
227 render_error l(:error_scm_not_found)
228 render_error l(:error_scm_not_found)
228 end
229 end
229
230
230 # Handler for Redmine::Scm::Adapters::CommandFailed exception
231 # Handler for Redmine::Scm::Adapters::CommandFailed exception
231 def show_error_command_failed(exception)
232 def show_error_command_failed(exception)
232 render_error l(:error_scm_command_failed, exception.message)
233 render_error l(:error_scm_command_failed, exception.message)
233 end
234 end
234
235
235 def graph_commits_per_month(repository)
236 def graph_commits_per_month(repository)
236 @date_to = Date.today
237 @date_to = Date.today
237 @date_from = @date_to << 11
238 @date_from = @date_to << 11
238 @date_from = Date.civil(@date_from.year, @date_from.month, 1)
239 @date_from = Date.civil(@date_from.year, @date_from.month, 1)
239 commits_by_day = repository.changesets.count(:all, :group => :commit_date, :conditions => ["commit_date BETWEEN ? AND ?", @date_from, @date_to])
240 commits_by_day = repository.changesets.count(:all, :group => :commit_date, :conditions => ["commit_date BETWEEN ? AND ?", @date_from, @date_to])
240 commits_by_month = [0] * 12
241 commits_by_month = [0] * 12
241 commits_by_day.each {|c| commits_by_month[c.first.to_date.months_ago] += c.last }
242 commits_by_day.each {|c| commits_by_month[c.first.to_date.months_ago] += c.last }
242
243
243 changes_by_day = repository.changes.count(:all, :group => :commit_date, :conditions => ["commit_date BETWEEN ? AND ?", @date_from, @date_to])
244 changes_by_day = repository.changes.count(:all, :group => :commit_date, :conditions => ["commit_date BETWEEN ? AND ?", @date_from, @date_to])
244 changes_by_month = [0] * 12
245 changes_by_month = [0] * 12
245 changes_by_day.each {|c| changes_by_month[c.first.to_date.months_ago] += c.last }
246 changes_by_day.each {|c| changes_by_month[c.first.to_date.months_ago] += c.last }
246
247
247 fields = []
248 fields = []
248 12.times {|m| fields << month_name(((Date.today.month - 1 - m) % 12) + 1)}
249 12.times {|m| fields << month_name(((Date.today.month - 1 - m) % 12) + 1)}
249
250
250 graph = SVG::Graph::Bar.new(
251 graph = SVG::Graph::Bar.new(
251 :height => 300,
252 :height => 300,
252 :width => 800,
253 :width => 800,
253 :fields => fields.reverse,
254 :fields => fields.reverse,
254 :stack => :side,
255 :stack => :side,
255 :scale_integers => true,
256 :scale_integers => true,
256 :step_x_labels => 2,
257 :step_x_labels => 2,
257 :show_data_values => false,
258 :show_data_values => false,
258 :graph_title => l(:label_commits_per_month),
259 :graph_title => l(:label_commits_per_month),
259 :show_graph_title => true
260 :show_graph_title => true
260 )
261 )
261
262
262 graph.add_data(
263 graph.add_data(
263 :data => commits_by_month[0..11].reverse,
264 :data => commits_by_month[0..11].reverse,
264 :title => l(:label_revision_plural)
265 :title => l(:label_revision_plural)
265 )
266 )
266
267
267 graph.add_data(
268 graph.add_data(
268 :data => changes_by_month[0..11].reverse,
269 :data => changes_by_month[0..11].reverse,
269 :title => l(:label_change_plural)
270 :title => l(:label_change_plural)
270 )
271 )
271
272
272 graph.burn
273 graph.burn
273 end
274 end
274
275
275 def graph_commits_per_author(repository)
276 def graph_commits_per_author(repository)
276 commits_by_author = repository.changesets.count(:all, :group => :committer)
277 commits_by_author = repository.changesets.count(:all, :group => :committer)
277 commits_by_author.to_a.sort! {|x, y| x.last <=> y.last}
278 commits_by_author.to_a.sort! {|x, y| x.last <=> y.last}
278
279
279 changes_by_author = repository.changes.count(:all, :group => :committer)
280 changes_by_author = repository.changes.count(:all, :group => :committer)
280 h = changes_by_author.inject({}) {|o, i| o[i.first] = i.last; o}
281 h = changes_by_author.inject({}) {|o, i| o[i.first] = i.last; o}
281
282
282 fields = commits_by_author.collect {|r| r.first}
283 fields = commits_by_author.collect {|r| r.first}
283 commits_data = commits_by_author.collect {|r| r.last}
284 commits_data = commits_by_author.collect {|r| r.last}
284 changes_data = commits_by_author.collect {|r| h[r.first] || 0}
285 changes_data = commits_by_author.collect {|r| h[r.first] || 0}
285
286
286 fields = fields + [""]*(10 - fields.length) if fields.length<10
287 fields = fields + [""]*(10 - fields.length) if fields.length<10
287 commits_data = commits_data + [0]*(10 - commits_data.length) if commits_data.length<10
288 commits_data = commits_data + [0]*(10 - commits_data.length) if commits_data.length<10
288 changes_data = changes_data + [0]*(10 - changes_data.length) if changes_data.length<10
289 changes_data = changes_data + [0]*(10 - changes_data.length) if changes_data.length<10
289
290
290 # Remove email adress in usernames
291 # Remove email adress in usernames
291 fields = fields.collect {|c| c.gsub(%r{<.+@.+>}, '') }
292 fields = fields.collect {|c| c.gsub(%r{<.+@.+>}, '') }
292
293
293 graph = SVG::Graph::BarHorizontal.new(
294 graph = SVG::Graph::BarHorizontal.new(
294 :height => 400,
295 :height => 400,
295 :width => 800,
296 :width => 800,
296 :fields => fields,
297 :fields => fields,
297 :stack => :side,
298 :stack => :side,
298 :scale_integers => true,
299 :scale_integers => true,
299 :show_data_values => false,
300 :show_data_values => false,
300 :rotate_y_labels => false,
301 :rotate_y_labels => false,
301 :graph_title => l(:label_commits_per_author),
302 :graph_title => l(:label_commits_per_author),
302 :show_graph_title => true
303 :show_graph_title => true
303 )
304 )
304
305
305 graph.add_data(
306 graph.add_data(
306 :data => commits_data,
307 :data => commits_data,
307 :title => l(:label_revision_plural)
308 :title => l(:label_revision_plural)
308 )
309 )
309
310
310 graph.add_data(
311 graph.add_data(
311 :data => changes_data,
312 :data => changes_data,
312 :title => l(:label_change_plural)
313 :title => l(:label_change_plural)
313 )
314 )
314
315
315 graph.burn
316 graph.burn
316 end
317 end
317
318
318 end
319 end
319
320
320 class Date
321 class Date
321 def months_ago(date = Date.today)
322 def months_ago(date = Date.today)
322 (date.year - self.year)*12 + (date.month - self.month)
323 (date.year - self.year)*12 + (date.month - self.month)
323 end
324 end
324
325
325 def weeks_ago(date = Date.today)
326 def weeks_ago(date = Date.today)
326 (date.year - self.year)*52 + (date.cweek - self.cweek)
327 (date.year - self.year)*52 + (date.cweek - self.cweek)
327 end
328 end
328 end
329 end
329
330
330 class String
331 class String
331 def with_leading_slash
332 def with_leading_slash
332 starts_with?('/') ? self : "/#{self}"
333 starts_with?('/') ? self : "/#{self}"
333 end
334 end
334 end
335 end
@@ -1,215 +1,216
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 class Repository < ActiveRecord::Base
18 class Repository < ActiveRecord::Base
19 belongs_to :project
19 belongs_to :project
20 has_many :changesets, :order => "#{Changeset.table_name}.committed_on DESC, #{Changeset.table_name}.id DESC"
20 has_many :changesets, :order => "#{Changeset.table_name}.committed_on DESC, #{Changeset.table_name}.id DESC"
21 has_many :changes, :through => :changesets
21 has_many :changes, :through => :changesets
22
22
23 # Raw SQL to delete changesets and changes in the database
23 # Raw SQL to delete changesets and changes in the database
24 # has_many :changesets, :dependent => :destroy is too slow for big repositories
24 # has_many :changesets, :dependent => :destroy is too slow for big repositories
25 before_destroy :clear_changesets
25 before_destroy :clear_changesets
26
26
27 # Checks if the SCM is enabled when creating a repository
27 # Checks if the SCM is enabled when creating a repository
28 validate_on_create { |r| r.errors.add(:type, :invalid) unless Setting.enabled_scm.include?(r.class.name.demodulize) }
28 validate_on_create { |r| r.errors.add(:type, :invalid) unless Setting.enabled_scm.include?(r.class.name.demodulize) }
29
29
30 # Removes leading and trailing whitespace
30 # Removes leading and trailing whitespace
31 def url=(arg)
31 def url=(arg)
32 write_attribute(:url, arg ? arg.to_s.strip : nil)
32 write_attribute(:url, arg ? arg.to_s.strip : nil)
33 end
33 end
34
34
35 # Removes leading and trailing whitespace
35 # Removes leading and trailing whitespace
36 def root_url=(arg)
36 def root_url=(arg)
37 write_attribute(:root_url, arg ? arg.to_s.strip : nil)
37 write_attribute(:root_url, arg ? arg.to_s.strip : nil)
38 end
38 end
39
39
40 def scm
40 def scm
41 @scm ||= self.scm_adapter.new url, root_url, login, password
41 @scm ||= self.scm_adapter.new url, root_url, login, password
42 update_attribute(:root_url, @scm.root_url) if root_url.blank?
42 update_attribute(:root_url, @scm.root_url) if root_url.blank?
43 @scm
43 @scm
44 end
44 end
45
45
46 def scm_name
46 def scm_name
47 self.class.scm_name
47 self.class.scm_name
48 end
48 end
49
49
50 def supports_cat?
50 def supports_cat?
51 scm.supports_cat?
51 scm.supports_cat?
52 end
52 end
53
53
54 def supports_annotate?
54 def supports_annotate?
55 scm.supports_annotate?
55 scm.supports_annotate?
56 end
56 end
57
57
58 def entry(path=nil, identifier=nil)
58 def entry(path=nil, identifier=nil)
59 scm.entry(path, identifier)
59 scm.entry(path, identifier)
60 end
60 end
61
61
62 def entries(path=nil, identifier=nil)
62 def entries(path=nil, identifier=nil)
63 scm.entries(path, identifier)
63 scm.entries(path, identifier)
64 end
64 end
65
65
66 def branches
66 def branches
67 scm.branches
67 scm.branches
68 end
68 end
69
69
70 def tags
70 def tags
71 scm.tags
71 scm.tags
72 end
72 end
73
73
74 def default_branch
74 def default_branch
75 scm.default_branch
75 scm.default_branch
76 end
76 end
77
77
78 def properties(path, identifier=nil)
78 def properties(path, identifier=nil)
79 scm.properties(path, identifier)
79 scm.properties(path, identifier)
80 end
80 end
81
81
82 def cat(path, identifier=nil)
82 def cat(path, identifier=nil)
83 scm.cat(path, identifier)
83 scm.cat(path, identifier)
84 end
84 end
85
85
86 def diff(path, rev, rev_to)
86 def diff(path, rev, rev_to)
87 scm.diff(path, rev, rev_to)
87 scm.diff(path, rev, rev_to)
88 end
88 end
89
89
90 # Returns a path relative to the url of the repository
90 # Returns a path relative to the url of the repository
91 def relative_path(path)
91 def relative_path(path)
92 path
92 path
93 end
93 end
94
94
95 # Finds and returns a revision with a number or the beginning of a hash
95 # Finds and returns a revision with a number or the beginning of a hash
96 def find_changeset_by_name(name)
96 def find_changeset_by_name(name)
97 return nil if name.nil? || name.empty?
97 changesets.find(:first, :conditions => (name.match(/^\d*$/) ? ["revision = ?", name.to_s] : ["revision LIKE ?", name + '%']))
98 changesets.find(:first, :conditions => (name.match(/^\d*$/) ? ["revision = ?", name.to_s] : ["revision LIKE ?", name + '%']))
98 end
99 end
99
100
100 def latest_changeset
101 def latest_changeset
101 @latest_changeset ||= changesets.find(:first)
102 @latest_changeset ||= changesets.find(:first)
102 end
103 end
103
104
104 # Returns the latest changesets for +path+
105 # Returns the latest changesets for +path+
105 # Default behaviour is to search in cached changesets
106 # Default behaviour is to search in cached changesets
106 def latest_changesets(path, rev, limit=10)
107 def latest_changesets(path, rev, limit=10)
107 if path.blank?
108 if path.blank?
108 changesets.find(:all, :include => :user,
109 changesets.find(:all, :include => :user,
109 :order => "#{Changeset.table_name}.committed_on DESC, #{Changeset.table_name}.id DESC",
110 :order => "#{Changeset.table_name}.committed_on DESC, #{Changeset.table_name}.id DESC",
110 :limit => limit)
111 :limit => limit)
111 else
112 else
112 changes.find(:all, :include => {:changeset => :user},
113 changes.find(:all, :include => {:changeset => :user},
113 :conditions => ["path = ?", path.with_leading_slash],
114 :conditions => ["path = ?", path.with_leading_slash],
114 :order => "#{Changeset.table_name}.committed_on DESC, #{Changeset.table_name}.id DESC",
115 :order => "#{Changeset.table_name}.committed_on DESC, #{Changeset.table_name}.id DESC",
115 :limit => limit).collect(&:changeset)
116 :limit => limit).collect(&:changeset)
116 end
117 end
117 end
118 end
118
119
119 def scan_changesets_for_issue_ids
120 def scan_changesets_for_issue_ids
120 self.changesets.each(&:scan_comment_for_issue_ids)
121 self.changesets.each(&:scan_comment_for_issue_ids)
121 end
122 end
122
123
123 # Returns an array of committers usernames and associated user_id
124 # Returns an array of committers usernames and associated user_id
124 def committers
125 def committers
125 @committers ||= Changeset.connection.select_rows("SELECT DISTINCT committer, user_id FROM #{Changeset.table_name} WHERE repository_id = #{id}")
126 @committers ||= Changeset.connection.select_rows("SELECT DISTINCT committer, user_id FROM #{Changeset.table_name} WHERE repository_id = #{id}")
126 end
127 end
127
128
128 # Maps committers username to a user ids
129 # Maps committers username to a user ids
129 def committer_ids=(h)
130 def committer_ids=(h)
130 if h.is_a?(Hash)
131 if h.is_a?(Hash)
131 committers.each do |committer, user_id|
132 committers.each do |committer, user_id|
132 new_user_id = h[committer]
133 new_user_id = h[committer]
133 if new_user_id && (new_user_id.to_i != user_id.to_i)
134 if new_user_id && (new_user_id.to_i != user_id.to_i)
134 new_user_id = (new_user_id.to_i > 0 ? new_user_id.to_i : nil)
135 new_user_id = (new_user_id.to_i > 0 ? new_user_id.to_i : nil)
135 Changeset.update_all("user_id = #{ new_user_id.nil? ? 'NULL' : new_user_id }", ["repository_id = ? AND committer = ?", id, committer])
136 Changeset.update_all("user_id = #{ new_user_id.nil? ? 'NULL' : new_user_id }", ["repository_id = ? AND committer = ?", id, committer])
136 end
137 end
137 end
138 end
138 @committers = nil
139 @committers = nil
139 @found_committer_users = nil
140 @found_committer_users = nil
140 true
141 true
141 else
142 else
142 false
143 false
143 end
144 end
144 end
145 end
145
146
146 # Returns the Redmine User corresponding to the given +committer+
147 # Returns the Redmine User corresponding to the given +committer+
147 # It will return nil if the committer is not yet mapped and if no User
148 # It will return nil if the committer is not yet mapped and if no User
148 # with the same username or email was found
149 # with the same username or email was found
149 def find_committer_user(committer)
150 def find_committer_user(committer)
150 unless committer.blank?
151 unless committer.blank?
151 @found_committer_users ||= {}
152 @found_committer_users ||= {}
152 return @found_committer_users[committer] if @found_committer_users.has_key?(committer)
153 return @found_committer_users[committer] if @found_committer_users.has_key?(committer)
153
154
154 user = nil
155 user = nil
155 c = changesets.find(:first, :conditions => {:committer => committer}, :include => :user)
156 c = changesets.find(:first, :conditions => {:committer => committer}, :include => :user)
156 if c && c.user
157 if c && c.user
157 user = c.user
158 user = c.user
158 elsif committer.strip =~ /^([^<]+)(<(.*)>)?$/
159 elsif committer.strip =~ /^([^<]+)(<(.*)>)?$/
159 username, email = $1.strip, $3
160 username, email = $1.strip, $3
160 u = User.find_by_login(username)
161 u = User.find_by_login(username)
161 u ||= User.find_by_mail(email) unless email.blank?
162 u ||= User.find_by_mail(email) unless email.blank?
162 user = u
163 user = u
163 end
164 end
164 @found_committer_users[committer] = user
165 @found_committer_users[committer] = user
165 user
166 user
166 end
167 end
167 end
168 end
168
169
169 # Fetches new changesets for all repositories of active projects
170 # Fetches new changesets for all repositories of active projects
170 # Can be called periodically by an external script
171 # Can be called periodically by an external script
171 # eg. ruby script/runner "Repository.fetch_changesets"
172 # eg. ruby script/runner "Repository.fetch_changesets"
172 def self.fetch_changesets
173 def self.fetch_changesets
173 Project.active.has_module(:repository).find(:all, :include => :repository).each do |project|
174 Project.active.has_module(:repository).find(:all, :include => :repository).each do |project|
174 if project.repository
175 if project.repository
175 project.repository.fetch_changesets
176 project.repository.fetch_changesets
176 end
177 end
177 end
178 end
178 end
179 end
179
180
180 # scan changeset comments to find related and fixed issues for all repositories
181 # scan changeset comments to find related and fixed issues for all repositories
181 def self.scan_changesets_for_issue_ids
182 def self.scan_changesets_for_issue_ids
182 find(:all).each(&:scan_changesets_for_issue_ids)
183 find(:all).each(&:scan_changesets_for_issue_ids)
183 end
184 end
184
185
185 def self.scm_name
186 def self.scm_name
186 'Abstract'
187 'Abstract'
187 end
188 end
188
189
189 def self.available_scm
190 def self.available_scm
190 subclasses.collect {|klass| [klass.scm_name, klass.name]}
191 subclasses.collect {|klass| [klass.scm_name, klass.name]}
191 end
192 end
192
193
193 def self.factory(klass_name, *args)
194 def self.factory(klass_name, *args)
194 klass = "Repository::#{klass_name}".constantize
195 klass = "Repository::#{klass_name}".constantize
195 klass.new(*args)
196 klass.new(*args)
196 rescue
197 rescue
197 nil
198 nil
198 end
199 end
199
200
200 private
201 private
201
202
202 def before_save
203 def before_save
203 # Strips url and root_url
204 # Strips url and root_url
204 url.strip!
205 url.strip!
205 root_url.strip!
206 root_url.strip!
206 true
207 true
207 end
208 end
208
209
209 def clear_changesets
210 def clear_changesets
210 cs, ch, ci = Changeset.table_name, Change.table_name, "#{table_name_prefix}changesets_issues#{table_name_suffix}"
211 cs, ch, ci = Changeset.table_name, Change.table_name, "#{table_name_prefix}changesets_issues#{table_name_suffix}"
211 connection.delete("DELETE FROM #{ch} WHERE #{ch}.changeset_id IN (SELECT #{cs}.id FROM #{cs} WHERE #{cs}.repository_id = #{id})")
212 connection.delete("DELETE FROM #{ch} WHERE #{ch}.changeset_id IN (SELECT #{cs}.id FROM #{cs} WHERE #{cs}.repository_id = #{id})")
212 connection.delete("DELETE FROM #{ci} WHERE #{ci}.changeset_id IN (SELECT #{cs}.id FROM #{cs} WHERE #{cs}.repository_id = #{id})")
213 connection.delete("DELETE FROM #{ci} WHERE #{ci}.changeset_id IN (SELECT #{cs}.id FROM #{cs} WHERE #{cs}.repository_id = #{id})")
213 connection.delete("DELETE FROM #{cs} WHERE #{cs}.repository_id = #{id}")
214 connection.delete("DELETE FROM #{cs} WHERE #{cs}.repository_id = #{id}")
214 end
215 end
215 end
216 end
@@ -1,183 +1,203
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006-2008 Jean-Philippe Lang
2 # Copyright (C) 2006-2008 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require File.expand_path('../../test_helper', __FILE__)
18 require File.expand_path('../../test_helper', __FILE__)
19 require 'repositories_controller'
19 require 'repositories_controller'
20
20
21 # Re-raise errors caught by the controller.
21 # Re-raise errors caught by the controller.
22 class RepositoriesController; def rescue_action(e) raise e end; end
22 class RepositoriesController; def rescue_action(e) raise e end; end
23
23
24 class RepositoriesGitControllerTest < ActionController::TestCase
24 class RepositoriesGitControllerTest < ActionController::TestCase
25 fixtures :projects, :users, :roles, :members, :member_roles, :repositories, :enabled_modules
25 fixtures :projects, :users, :roles, :members, :member_roles, :repositories, :enabled_modules
26
26
27 # No '..' in the repository path
27 # No '..' in the repository path
28 REPOSITORY_PATH = RAILS_ROOT.gsub(%r{config\/\.\.}, '') + '/tmp/test/git_repository'
28 REPOSITORY_PATH = RAILS_ROOT.gsub(%r{config\/\.\.}, '') + '/tmp/test/git_repository'
29 REPOSITORY_PATH.gsub!(/\//, "\\") if Redmine::Platform.mswin?
29 REPOSITORY_PATH.gsub!(/\//, "\\") if Redmine::Platform.mswin?
30
30
31 def setup
31 def setup
32 @controller = RepositoriesController.new
32 @controller = RepositoriesController.new
33 @request = ActionController::TestRequest.new
33 @request = ActionController::TestRequest.new
34 @response = ActionController::TestResponse.new
34 @response = ActionController::TestResponse.new
35 User.current = nil
35 User.current = nil
36 @repository = Repository::Git.create(:project => Project.find(3), :url => REPOSITORY_PATH)
36 @repository = Repository::Git.create(:project => Project.find(3), :url => REPOSITORY_PATH)
37 assert @repository
37 assert @repository
38 end
38 end
39
39
40 if File.directory?(REPOSITORY_PATH)
40 if File.directory?(REPOSITORY_PATH)
41 def test_show
41 def test_show
42 get :show, :id => 3
42 get :show, :id => 3
43 assert_response :success
43 assert_response :success
44 assert_template 'show'
44 assert_template 'show'
45 assert_not_nil assigns(:entries)
45 assert_not_nil assigns(:entries)
46 assert_not_nil assigns(:changesets)
46 assert_not_nil assigns(:changesets)
47 end
47 end
48
48
49 def test_browse_root
49 def test_browse_root
50 get :show, :id => 3
50 get :show, :id => 3
51 assert_response :success
51 assert_response :success
52 assert_template 'show'
52 assert_template 'show'
53 assert_not_nil assigns(:entries)
53 assert_not_nil assigns(:entries)
54 assert_equal 9, assigns(:entries).size
54 assert_equal 9, assigns(:entries).size
55 assert assigns(:entries).detect {|e| e.name == 'images' && e.kind == 'dir'}
55 assert assigns(:entries).detect {|e| e.name == 'images' && e.kind == 'dir'}
56 assert assigns(:entries).detect {|e| e.name == 'this_is_a_really_long_and_verbose_directory_name' && e.kind == 'dir'}
56 assert assigns(:entries).detect {|e| e.name == 'this_is_a_really_long_and_verbose_directory_name' && e.kind == 'dir'}
57 assert assigns(:entries).detect {|e| e.name == 'sources' && e.kind == 'dir'}
57 assert assigns(:entries).detect {|e| e.name == 'sources' && e.kind == 'dir'}
58 assert assigns(:entries).detect {|e| e.name == 'README' && e.kind == 'file'}
58 assert assigns(:entries).detect {|e| e.name == 'README' && e.kind == 'file'}
59 assert assigns(:entries).detect {|e| e.name == 'copied_README' && e.kind == 'file'}
59 assert assigns(:entries).detect {|e| e.name == 'copied_README' && e.kind == 'file'}
60 assert assigns(:entries).detect {|e| e.name == 'new_file.txt' && e.kind == 'file'}
60 assert assigns(:entries).detect {|e| e.name == 'new_file.txt' && e.kind == 'file'}
61 assert assigns(:entries).detect {|e| e.name == 'renamed_test.txt' && e.kind == 'file'}
61 assert assigns(:entries).detect {|e| e.name == 'renamed_test.txt' && e.kind == 'file'}
62 assert assigns(:entries).detect {|e| e.name == 'filemane with spaces.txt' && e.kind == 'file'}
62 assert assigns(:entries).detect {|e| e.name == 'filemane with spaces.txt' && e.kind == 'file'}
63 assert assigns(:entries).detect {|e| e.name == ' filename with a leading space.txt ' && e.kind == 'file'}
63 assert assigns(:entries).detect {|e| e.name == ' filename with a leading space.txt ' && e.kind == 'file'}
64 end
64 end
65
65
66 def test_browse_branch
66 def test_browse_branch
67 get :show, :id => 3, :rev => 'test_branch'
67 get :show, :id => 3, :rev => 'test_branch'
68 assert_response :success
68 assert_response :success
69 assert_template 'show'
69 assert_template 'show'
70 assert_not_nil assigns(:entries)
70 assert_not_nil assigns(:entries)
71 assert_equal 4, assigns(:entries).size
71 assert_equal 4, assigns(:entries).size
72 assert assigns(:entries).detect {|e| e.name == 'images' && e.kind == 'dir'}
72 assert assigns(:entries).detect {|e| e.name == 'images' && e.kind == 'dir'}
73 assert assigns(:entries).detect {|e| e.name == 'sources' && e.kind == 'dir'}
73 assert assigns(:entries).detect {|e| e.name == 'sources' && e.kind == 'dir'}
74 assert assigns(:entries).detect {|e| e.name == 'README' && e.kind == 'file'}
74 assert assigns(:entries).detect {|e| e.name == 'README' && e.kind == 'file'}
75 assert assigns(:entries).detect {|e| e.name == 'test.txt' && e.kind == 'file'}
75 assert assigns(:entries).detect {|e| e.name == 'test.txt' && e.kind == 'file'}
76 end
76 end
77
77
78 def test_browse_directory
78 def test_browse_directory
79 get :show, :id => 3, :path => ['images']
79 get :show, :id => 3, :path => ['images']
80 assert_response :success
80 assert_response :success
81 assert_template 'show'
81 assert_template 'show'
82 assert_not_nil assigns(:entries)
82 assert_not_nil assigns(:entries)
83 assert_equal ['edit.png'], assigns(:entries).collect(&:name)
83 assert_equal ['edit.png'], assigns(:entries).collect(&:name)
84 entry = assigns(:entries).detect {|e| e.name == 'edit.png'}
84 entry = assigns(:entries).detect {|e| e.name == 'edit.png'}
85 assert_not_nil entry
85 assert_not_nil entry
86 assert_equal 'file', entry.kind
86 assert_equal 'file', entry.kind
87 assert_equal 'images/edit.png', entry.path
87 assert_equal 'images/edit.png', entry.path
88 end
88 end
89
89
90 def test_browse_at_given_revision
90 def test_browse_at_given_revision
91 get :show, :id => 3, :path => ['images'], :rev => '7234cb2750b63f47bff735edc50a1c0a433c2518'
91 get :show, :id => 3, :path => ['images'], :rev => '7234cb2750b63f47bff735edc50a1c0a433c2518'
92 assert_response :success
92 assert_response :success
93 assert_template 'show'
93 assert_template 'show'
94 assert_not_nil assigns(:entries)
94 assert_not_nil assigns(:entries)
95 assert_equal ['delete.png'], assigns(:entries).collect(&:name)
95 assert_equal ['delete.png'], assigns(:entries).collect(&:name)
96 end
96 end
97
97
98 def test_changes
98 def test_changes
99 get :changes, :id => 3, :path => ['images', 'edit.png']
99 get :changes, :id => 3, :path => ['images', 'edit.png']
100 assert_response :success
100 assert_response :success
101 assert_template 'changes'
101 assert_template 'changes'
102 assert_tag :tag => 'h2', :content => 'edit.png'
102 assert_tag :tag => 'h2', :content => 'edit.png'
103 end
103 end
104
104
105 def test_entry_show
105 def test_entry_show
106 get :entry, :id => 3, :path => ['sources', 'watchers_controller.rb']
106 get :entry, :id => 3, :path => ['sources', 'watchers_controller.rb']
107 assert_response :success
107 assert_response :success
108 assert_template 'entry'
108 assert_template 'entry'
109 # Line 19
109 # Line 19
110 assert_tag :tag => 'th',
110 assert_tag :tag => 'th',
111 :content => /11/,
111 :content => /11/,
112 :attributes => { :class => /line-num/ },
112 :attributes => { :class => /line-num/ },
113 :sibling => { :tag => 'td', :content => /WITHOUT ANY WARRANTY/ }
113 :sibling => { :tag => 'td', :content => /WITHOUT ANY WARRANTY/ }
114 end
114 end
115
115
116 def test_entry_download
116 def test_entry_download
117 get :entry, :id => 3, :path => ['sources', 'watchers_controller.rb'], :format => 'raw'
117 get :entry, :id => 3, :path => ['sources', 'watchers_controller.rb'], :format => 'raw'
118 assert_response :success
118 assert_response :success
119 # File content
119 # File content
120 assert @response.body.include?('WITHOUT ANY WARRANTY')
120 assert @response.body.include?('WITHOUT ANY WARRANTY')
121 end
121 end
122
122
123 def test_directory_entry
123 def test_directory_entry
124 get :entry, :id => 3, :path => ['sources']
124 get :entry, :id => 3, :path => ['sources']
125 assert_response :success
125 assert_response :success
126 assert_template 'show'
126 assert_template 'show'
127 assert_not_nil assigns(:entry)
127 assert_not_nil assigns(:entry)
128 assert_equal 'sources', assigns(:entry).name
128 assert_equal 'sources', assigns(:entry).name
129 end
129 end
130
130
131 def test_diff
131 def test_diff
132 @repository.fetch_changesets
132 @repository.fetch_changesets
133 @repository.reload
133 @repository.reload
134
134
135 # Full diff of changeset 2f9c0091
135 # Full diff of changeset 2f9c0091
136 get :diff, :id => 3, :rev => '2f9c0091c754a91af7a9c478e36556b4bde8dcf7'
136 get :diff, :id => 3, :rev => '2f9c0091c754a91af7a9c478e36556b4bde8dcf7'
137 assert_response :success
137 assert_response :success
138 assert_template 'diff'
138 assert_template 'diff'
139 # Line 22 removed
139 # Line 22 removed
140 assert_tag :tag => 'th',
140 assert_tag :tag => 'th',
141 :content => /22/,
141 :content => /22/,
142 :sibling => { :tag => 'td',
142 :sibling => { :tag => 'td',
143 :attributes => { :class => /diff_out/ },
143 :attributes => { :class => /diff_out/ },
144 :content => /def remove/ }
144 :content => /def remove/ }
145 assert_tag :tag => 'h2', :content => /2f9c0091/
145 assert_tag :tag => 'h2', :content => /2f9c0091/
146 end
146 end
147
147
148 def test_diff_two_revs
148 def test_diff_two_revs
149 @repository.fetch_changesets
149 @repository.fetch_changesets
150 @repository.reload
150 @repository.reload
151
151
152 get :diff, :id => 3, :rev => '61b685fbe55ab05b5ac68402d5720c1a6ac973d1',
152 get :diff, :id => 3, :rev => '61b685fbe55ab05b5ac68402d5720c1a6ac973d1',
153 :rev_to => '2f9c0091c754a91af7a9c478e36556b4bde8dcf7'
153 :rev_to => '2f9c0091c754a91af7a9c478e36556b4bde8dcf7'
154 assert_response :success
154 assert_response :success
155 assert_template 'diff'
155 assert_template 'diff'
156
156
157 diff = assigns(:diff)
157 diff = assigns(:diff)
158 assert_not_nil diff
158 assert_not_nil diff
159 assert_tag :tag => 'h2', :content => /2f9c0091:61b685fb/
159 assert_tag :tag => 'h2', :content => /2f9c0091:61b685fb/
160 end
160 end
161
161
162 def test_annotate
162 def test_annotate
163 get :annotate, :id => 3, :path => ['sources', 'watchers_controller.rb']
163 get :annotate, :id => 3, :path => ['sources', 'watchers_controller.rb']
164 assert_response :success
164 assert_response :success
165 assert_template 'annotate'
165 assert_template 'annotate'
166 # Line 23, changeset 2f9c0091
166 # Line 23, changeset 2f9c0091
167 assert_tag :tag => 'th', :content => /24/,
167 assert_tag :tag => 'th', :content => /24/,
168 :sibling => { :tag => 'td', :child => { :tag => 'a', :content => /2f9c0091/ } },
168 :sibling => { :tag => 'td', :child => { :tag => 'a', :content => /2f9c0091/ } },
169 :sibling => { :tag => 'td', :content => /jsmith/ },
169 :sibling => { :tag => 'td', :content => /jsmith/ },
170 :sibling => { :tag => 'td', :content => /watcher =/ }
170 :sibling => { :tag => 'td', :content => /watcher =/ }
171 end
171 end
172
172
173 def test_annotate_binary_file
173 def test_annotate_binary_file
174 get :annotate, :id => 3, :path => ['images', 'edit.png']
174 get :annotate, :id => 3, :path => ['images', 'edit.png']
175 assert_response 500
175 assert_response 500
176 assert_tag :tag => 'p', :attributes => { :id => /errorExplanation/ },
176 assert_tag :tag => 'p', :attributes => { :id => /errorExplanation/ },
177 :content => /can not be annotated/
177 :content => /can not be annotated/
178 end
178 end
179
180 def test_revision
181 @repository.fetch_changesets
182 @repository.reload
183 ['61b685fbe55ab05b5ac68402d5720c1a6ac973d1', '61b685f'].each do |r|
184 get :revision, :id => 3, :rev => r
185 assert_response :success
186 assert_template 'revision'
187 end
188 end
189
190 def test_empty_revision
191 @repository.fetch_changesets
192 @repository.reload
193 ['', ' ', nil].each do |r|
194 get :revision, :id => 1, :rev => r
195 assert_response 500
196 assert_error_tag :content => /was not found/
197 end
198 end
179 else
199 else
180 puts "Git test repository NOT FOUND. Skipping functional tests !!!"
200 puts "Git test repository NOT FOUND. Skipping functional tests !!!"
181 def test_fake; assert true end
201 def test_fake; assert true end
182 end
202 end
183 end
203 end
@@ -1,227 +1,235
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006-2008 Jean-Philippe Lang
2 # Copyright (C) 2006-2008 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require File.expand_path('../../test_helper', __FILE__)
18 require File.expand_path('../../test_helper', __FILE__)
19 require 'repositories_controller'
19 require 'repositories_controller'
20
20
21 # Re-raise errors caught by the controller.
21 # Re-raise errors caught by the controller.
22 class RepositoriesController; def rescue_action(e) raise e end; end
22 class RepositoriesController; def rescue_action(e) raise e end; end
23
23
24 class RepositoriesSubversionControllerTest < ActionController::TestCase
24 class RepositoriesSubversionControllerTest < ActionController::TestCase
25 fixtures :projects, :users, :roles, :members, :member_roles, :enabled_modules,
25 fixtures :projects, :users, :roles, :members, :member_roles, :enabled_modules,
26 :repositories, :issues, :issue_statuses, :changesets, :changes,
26 :repositories, :issues, :issue_statuses, :changesets, :changes,
27 :issue_categories, :enumerations, :custom_fields, :custom_values, :trackers
27 :issue_categories, :enumerations, :custom_fields, :custom_values, :trackers
28
28
29 def setup
29 def setup
30 @controller = RepositoriesController.new
30 @controller = RepositoriesController.new
31 @request = ActionController::TestRequest.new
31 @request = ActionController::TestRequest.new
32 @response = ActionController::TestResponse.new
32 @response = ActionController::TestResponse.new
33 Setting.default_language = 'en'
33 Setting.default_language = 'en'
34 User.current = nil
34 User.current = nil
35 end
35 end
36
36
37 if repository_configured?('subversion')
37 if repository_configured?('subversion')
38 def test_show
38 def test_show
39 get :show, :id => 1
39 get :show, :id => 1
40 assert_response :success
40 assert_response :success
41 assert_template 'show'
41 assert_template 'show'
42 assert_not_nil assigns(:entries)
42 assert_not_nil assigns(:entries)
43 assert_not_nil assigns(:changesets)
43 assert_not_nil assigns(:changesets)
44 end
44 end
45
45
46 def test_browse_root
46 def test_browse_root
47 get :show, :id => 1
47 get :show, :id => 1
48 assert_response :success
48 assert_response :success
49 assert_template 'show'
49 assert_template 'show'
50 assert_not_nil assigns(:entries)
50 assert_not_nil assigns(:entries)
51 entry = assigns(:entries).detect {|e| e.name == 'subversion_test'}
51 entry = assigns(:entries).detect {|e| e.name == 'subversion_test'}
52 assert_equal 'dir', entry.kind
52 assert_equal 'dir', entry.kind
53 end
53 end
54
54
55 def test_browse_directory
55 def test_browse_directory
56 get :show, :id => 1, :path => ['subversion_test']
56 get :show, :id => 1, :path => ['subversion_test']
57 assert_response :success
57 assert_response :success
58 assert_template 'show'
58 assert_template 'show'
59 assert_not_nil assigns(:entries)
59 assert_not_nil assigns(:entries)
60 assert_equal ['[folder_with_brackets]', 'folder', '.project', 'helloworld.c', 'textfile.txt'], assigns(:entries).collect(&:name)
60 assert_equal ['[folder_with_brackets]', 'folder', '.project', 'helloworld.c', 'textfile.txt'], assigns(:entries).collect(&:name)
61 entry = assigns(:entries).detect {|e| e.name == 'helloworld.c'}
61 entry = assigns(:entries).detect {|e| e.name == 'helloworld.c'}
62 assert_equal 'file', entry.kind
62 assert_equal 'file', entry.kind
63 assert_equal 'subversion_test/helloworld.c', entry.path
63 assert_equal 'subversion_test/helloworld.c', entry.path
64 assert_tag :a, :content => 'helloworld.c', :attributes => { :class => /text\-x\-c/ }
64 assert_tag :a, :content => 'helloworld.c', :attributes => { :class => /text\-x\-c/ }
65 end
65 end
66
66
67 def test_browse_at_given_revision
67 def test_browse_at_given_revision
68 get :show, :id => 1, :path => ['subversion_test'], :rev => 4
68 get :show, :id => 1, :path => ['subversion_test'], :rev => 4
69 assert_response :success
69 assert_response :success
70 assert_template 'show'
70 assert_template 'show'
71 assert_not_nil assigns(:entries)
71 assert_not_nil assigns(:entries)
72 assert_equal ['folder', '.project', 'helloworld.c', 'helloworld.rb', 'textfile.txt'], assigns(:entries).collect(&:name)
72 assert_equal ['folder', '.project', 'helloworld.c', 'helloworld.rb', 'textfile.txt'], assigns(:entries).collect(&:name)
73 end
73 end
74
74
75 def test_file_changes
75 def test_file_changes
76 get :changes, :id => 1, :path => ['subversion_test', 'folder', 'helloworld.rb' ]
76 get :changes, :id => 1, :path => ['subversion_test', 'folder', 'helloworld.rb' ]
77 assert_response :success
77 assert_response :success
78 assert_template 'changes'
78 assert_template 'changes'
79
79
80 changesets = assigns(:changesets)
80 changesets = assigns(:changesets)
81 assert_not_nil changesets
81 assert_not_nil changesets
82 assert_equal %w(6 3 2), changesets.collect(&:revision)
82 assert_equal %w(6 3 2), changesets.collect(&:revision)
83
83
84 # svn properties displayed with svn >= 1.5 only
84 # svn properties displayed with svn >= 1.5 only
85 if Redmine::Scm::Adapters::SubversionAdapter.client_version_above?([1, 5, 0])
85 if Redmine::Scm::Adapters::SubversionAdapter.client_version_above?([1, 5, 0])
86 assert_not_nil assigns(:properties)
86 assert_not_nil assigns(:properties)
87 assert_equal 'native', assigns(:properties)['svn:eol-style']
87 assert_equal 'native', assigns(:properties)['svn:eol-style']
88 assert_tag :ul,
88 assert_tag :ul,
89 :child => { :tag => 'li',
89 :child => { :tag => 'li',
90 :child => { :tag => 'b', :content => 'svn:eol-style' },
90 :child => { :tag => 'b', :content => 'svn:eol-style' },
91 :child => { :tag => 'span', :content => 'native' } }
91 :child => { :tag => 'span', :content => 'native' } }
92 end
92 end
93 end
93 end
94
94
95 def test_directory_changes
95 def test_directory_changes
96 get :changes, :id => 1, :path => ['subversion_test', 'folder' ]
96 get :changes, :id => 1, :path => ['subversion_test', 'folder' ]
97 assert_response :success
97 assert_response :success
98 assert_template 'changes'
98 assert_template 'changes'
99
99
100 changesets = assigns(:changesets)
100 changesets = assigns(:changesets)
101 assert_not_nil changesets
101 assert_not_nil changesets
102 assert_equal %w(10 9 7 6 5 2), changesets.collect(&:revision)
102 assert_equal %w(10 9 7 6 5 2), changesets.collect(&:revision)
103 end
103 end
104
104
105 def test_entry
105 def test_entry
106 get :entry, :id => 1, :path => ['subversion_test', 'helloworld.c']
106 get :entry, :id => 1, :path => ['subversion_test', 'helloworld.c']
107 assert_response :success
107 assert_response :success
108 assert_template 'entry'
108 assert_template 'entry'
109 end
109 end
110
110
111 def test_entry_should_send_if_too_big
111 def test_entry_should_send_if_too_big
112 # no files in the test repo is larger than 1KB...
112 # no files in the test repo is larger than 1KB...
113 with_settings :file_max_size_displayed => 0 do
113 with_settings :file_max_size_displayed => 0 do
114 get :entry, :id => 1, :path => ['subversion_test', 'helloworld.c']
114 get :entry, :id => 1, :path => ['subversion_test', 'helloworld.c']
115 assert_response :success
115 assert_response :success
116 assert_template ''
116 assert_template ''
117 assert_equal 'attachment; filename="helloworld.c"', @response.headers['Content-Disposition']
117 assert_equal 'attachment; filename="helloworld.c"', @response.headers['Content-Disposition']
118 end
118 end
119 end
119 end
120
120
121 def test_entry_at_given_revision
121 def test_entry_at_given_revision
122 get :entry, :id => 1, :path => ['subversion_test', 'helloworld.rb'], :rev => 2
122 get :entry, :id => 1, :path => ['subversion_test', 'helloworld.rb'], :rev => 2
123 assert_response :success
123 assert_response :success
124 assert_template 'entry'
124 assert_template 'entry'
125 # this line was removed in r3 and file was moved in r6
125 # this line was removed in r3 and file was moved in r6
126 assert_tag :tag => 'td', :attributes => { :class => /line-code/},
126 assert_tag :tag => 'td', :attributes => { :class => /line-code/},
127 :content => /Here's the code/
127 :content => /Here's the code/
128 end
128 end
129
129
130 def test_entry_not_found
130 def test_entry_not_found
131 get :entry, :id => 1, :path => ['subversion_test', 'zzz.c']
131 get :entry, :id => 1, :path => ['subversion_test', 'zzz.c']
132 assert_tag :tag => 'p', :attributes => { :id => /errorExplanation/ },
132 assert_tag :tag => 'p', :attributes => { :id => /errorExplanation/ },
133 :content => /The entry or revision was not found in the repository/
133 :content => /The entry or revision was not found in the repository/
134 end
134 end
135
135
136 def test_entry_download
136 def test_entry_download
137 get :entry, :id => 1, :path => ['subversion_test', 'helloworld.c'], :format => 'raw'
137 get :entry, :id => 1, :path => ['subversion_test', 'helloworld.c'], :format => 'raw'
138 assert_response :success
138 assert_response :success
139 assert_template ''
139 assert_template ''
140 assert_equal 'attachment; filename="helloworld.c"', @response.headers['Content-Disposition']
140 assert_equal 'attachment; filename="helloworld.c"', @response.headers['Content-Disposition']
141 end
141 end
142
142
143 def test_directory_entry
143 def test_directory_entry
144 get :entry, :id => 1, :path => ['subversion_test', 'folder']
144 get :entry, :id => 1, :path => ['subversion_test', 'folder']
145 assert_response :success
145 assert_response :success
146 assert_template 'show'
146 assert_template 'show'
147 assert_not_nil assigns(:entry)
147 assert_not_nil assigns(:entry)
148 assert_equal 'folder', assigns(:entry).name
148 assert_equal 'folder', assigns(:entry).name
149 end
149 end
150
150
151 def test_revision
151 def test_revision
152 get :revision, :id => 1, :rev => 2
152 get :revision, :id => 1, :rev => 2
153 assert_response :success
153 assert_response :success
154 assert_template 'revision'
154 assert_template 'revision'
155 assert_tag :tag => 'ul',
155 assert_tag :tag => 'ul',
156 :child => { :tag => 'li',
156 :child => { :tag => 'li',
157 # link to the entry at rev 2
157 # link to the entry at rev 2
158 :child => { :tag => 'a',
158 :child => { :tag => 'a',
159 :attributes => {:href => '/projects/ecookbook/repository/revisions/2/entry/test/some/path/in/the/repo'},
159 :attributes => {:href => '/projects/ecookbook/repository/revisions/2/entry/test/some/path/in/the/repo'},
160 :content => 'repo',
160 :content => 'repo',
161 # link to partial diff
161 # link to partial diff
162 :sibling => { :tag => 'a',
162 :sibling => { :tag => 'a',
163 :attributes => { :href => '/projects/ecookbook/repository/revisions/2/diff/test/some/path/in/the/repo' }
163 :attributes => { :href => '/projects/ecookbook/repository/revisions/2/diff/test/some/path/in/the/repo' }
164 }
164 }
165 }
165 }
166 }
166 }
167 end
167 end
168
168
169 def test_invalid_revision
169 def test_invalid_revision
170 get :revision, :id => 1, :rev => 'something_weird'
170 get :revision, :id => 1, :rev => 'something_weird'
171 assert_response 500
171 assert_response 500
172 assert_error_tag :content => /was not found/
172 assert_error_tag :content => /was not found/
173 end
173 end
174
174
175 def test_empty_revision
176 ['', ' ', nil].each do |r|
177 get :revision, :id => 1, :rev => r
178 assert_response 500
179 assert_error_tag :content => /was not found/
180 end
181 end
182
175 def test_revision_with_repository_pointing_to_a_subdirectory
183 def test_revision_with_repository_pointing_to_a_subdirectory
176 r = Project.find(1).repository
184 r = Project.find(1).repository
177 # Changes repository url to a subdirectory
185 # Changes repository url to a subdirectory
178 r.update_attribute :url, (r.url + '/test/some')
186 r.update_attribute :url, (r.url + '/test/some')
179
187
180 get :revision, :id => 1, :rev => 2
188 get :revision, :id => 1, :rev => 2
181 assert_response :success
189 assert_response :success
182 assert_template 'revision'
190 assert_template 'revision'
183 assert_tag :tag => 'ul',
191 assert_tag :tag => 'ul',
184 :child => { :tag => 'li',
192 :child => { :tag => 'li',
185 # link to the entry at rev 2
193 # link to the entry at rev 2
186 :child => { :tag => 'a',
194 :child => { :tag => 'a',
187 :attributes => {:href => '/projects/ecookbook/repository/revisions/2/entry/path/in/the/repo'},
195 :attributes => {:href => '/projects/ecookbook/repository/revisions/2/entry/path/in/the/repo'},
188 :content => 'repo',
196 :content => 'repo',
189 # link to partial diff
197 # link to partial diff
190 :sibling => { :tag => 'a',
198 :sibling => { :tag => 'a',
191 :attributes => { :href => '/projects/ecookbook/repository/revisions/2/diff/path/in/the/repo' }
199 :attributes => { :href => '/projects/ecookbook/repository/revisions/2/diff/path/in/the/repo' }
192 }
200 }
193 }
201 }
194 }
202 }
195 end
203 end
196
204
197 def test_revision_diff
205 def test_revision_diff
198 get :diff, :id => 1, :rev => 3
206 get :diff, :id => 1, :rev => 3
199 assert_response :success
207 assert_response :success
200 assert_template 'diff'
208 assert_template 'diff'
201
209
202 assert_tag :tag => 'h2', :content => /3/
210 assert_tag :tag => 'h2', :content => /3/
203 end
211 end
204
212
205 def test_directory_diff
213 def test_directory_diff
206 get :diff, :id => 1, :rev => 6, :rev_to => 2, :path => ['subversion_test', 'folder']
214 get :diff, :id => 1, :rev => 6, :rev_to => 2, :path => ['subversion_test', 'folder']
207 assert_response :success
215 assert_response :success
208 assert_template 'diff'
216 assert_template 'diff'
209
217
210 diff = assigns(:diff)
218 diff = assigns(:diff)
211 assert_not_nil diff
219 assert_not_nil diff
212 # 2 files modified
220 # 2 files modified
213 assert_equal 2, Redmine::UnifiedDiff.new(diff).size
221 assert_equal 2, Redmine::UnifiedDiff.new(diff).size
214
222
215 assert_tag :tag => 'h2', :content => /2:6/
223 assert_tag :tag => 'h2', :content => /2:6/
216 end
224 end
217
225
218 def test_annotate
226 def test_annotate
219 get :annotate, :id => 1, :path => ['subversion_test', 'helloworld.c']
227 get :annotate, :id => 1, :path => ['subversion_test', 'helloworld.c']
220 assert_response :success
228 assert_response :success
221 assert_template 'annotate'
229 assert_template 'annotate'
222 end
230 end
223 else
231 else
224 puts "Subversion test repository NOT FOUND. Skipping functional tests !!!"
232 puts "Subversion test repository NOT FOUND. Skipping functional tests !!!"
225 def test_fake; assert true end
233 def test_fake; assert true end
226 end
234 end
227 end
235 end
@@ -1,93 +1,110
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require File.expand_path('../../test_helper', __FILE__)
18 require File.expand_path('../../test_helper', __FILE__)
19
19
20 class RepositoryGitTest < ActiveSupport::TestCase
20 class RepositoryGitTest < ActiveSupport::TestCase
21 fixtures :projects, :repositories, :enabled_modules, :users, :roles
21 fixtures :projects, :repositories, :enabled_modules, :users, :roles
22
22
23 # No '..' in the repository path
23 # No '..' in the repository path
24 REPOSITORY_PATH = RAILS_ROOT.gsub(%r{config\/\.\.}, '') + '/tmp/test/git_repository'
24 REPOSITORY_PATH = RAILS_ROOT.gsub(%r{config\/\.\.}, '') + '/tmp/test/git_repository'
25 REPOSITORY_PATH.gsub!(/\//, "\\") if Redmine::Platform.mswin?
25 REPOSITORY_PATH.gsub!(/\//, "\\") if Redmine::Platform.mswin?
26
26
27 def setup
27 def setup
28 @project = Project.find(1)
28 @project = Project.find(1)
29 assert @repository = Repository::Git.create(:project => @project, :url => REPOSITORY_PATH)
29 assert @repository = Repository::Git.create(:project => @project, :url => REPOSITORY_PATH)
30 end
30 end
31
31
32 if File.directory?(REPOSITORY_PATH)
32 if File.directory?(REPOSITORY_PATH)
33 def test_fetch_changesets_from_scratch
33 def test_fetch_changesets_from_scratch
34 @repository.fetch_changesets
34 @repository.fetch_changesets
35 @repository.reload
35 @repository.reload
36
36
37 assert_equal 15, @repository.changesets.count
37 assert_equal 15, @repository.changesets.count
38 assert_equal 24, @repository.changes.count
38 assert_equal 24, @repository.changes.count
39
39
40 commit = @repository.changesets.find(:first, :order => 'committed_on ASC')
40 commit = @repository.changesets.find(:first, :order => 'committed_on ASC')
41 assert_equal "Initial import.\nThe repository contains 3 files.", commit.comments
41 assert_equal "Initial import.\nThe repository contains 3 files.", commit.comments
42 assert_equal "jsmith <jsmith@foo.bar>", commit.committer
42 assert_equal "jsmith <jsmith@foo.bar>", commit.committer
43 assert_equal User.find_by_login('jsmith'), commit.user
43 assert_equal User.find_by_login('jsmith'), commit.user
44 # TODO: add a commit with commit time <> author time to the test repository
44 # TODO: add a commit with commit time <> author time to the test repository
45 assert_equal "2007-12-14 09:22:52".to_time, commit.committed_on
45 assert_equal "2007-12-14 09:22:52".to_time, commit.committed_on
46 assert_equal "2007-12-14".to_date, commit.commit_date
46 assert_equal "2007-12-14".to_date, commit.commit_date
47 assert_equal "7234cb2750b63f47bff735edc50a1c0a433c2518", commit.revision
47 assert_equal "7234cb2750b63f47bff735edc50a1c0a433c2518", commit.revision
48 assert_equal "7234cb2750b63f47bff735edc50a1c0a433c2518", commit.scmid
48 assert_equal "7234cb2750b63f47bff735edc50a1c0a433c2518", commit.scmid
49 assert_equal 3, commit.changes.count
49 assert_equal 3, commit.changes.count
50 change = commit.changes.sort_by(&:path).first
50 change = commit.changes.sort_by(&:path).first
51 assert_equal "README", change.path
51 assert_equal "README", change.path
52 assert_equal "A", change.action
52 assert_equal "A", change.action
53 end
53 end
54
54
55 def test_fetch_changesets_incremental
55 def test_fetch_changesets_incremental
56 @repository.fetch_changesets
56 @repository.fetch_changesets
57 # Remove the 3 latest changesets
57 # Remove the 3 latest changesets
58 @repository.changesets.find(:all, :order => 'committed_on DESC', :limit => 3).each(&:destroy)
58 @repository.changesets.find(:all, :order => 'committed_on DESC', :limit => 3).each(&:destroy)
59 @repository.reload
59 @repository.reload
60 assert_equal 12, @repository.changesets.count
60 assert_equal 12, @repository.changesets.count
61
61
62 @repository.fetch_changesets
62 @repository.fetch_changesets
63 assert_equal 15, @repository.changesets.count
63 assert_equal 15, @repository.changesets.count
64 end
64 end
65
65
66 def test_find_changeset_by_name
67 @repository.fetch_changesets
68 @repository.reload
69 ['7234cb2750b63f47bff735edc50a1c0a433c2518', '7234cb2750b'].each do |r|
70 assert_equal '7234cb2750b63f47bff735edc50a1c0a433c2518',
71 @repository.find_changeset_by_name(r).revision
72 end
73 end
74
75 def test_find_changeset_by_empty_name
76 @repository.fetch_changesets
77 @repository.reload
78 ['', ' ', nil].each do |r|
79 assert_nil @repository.find_changeset_by_name(r)
80 end
81 end
82
66 def test_identifier
83 def test_identifier
67 @repository.fetch_changesets
84 @repository.fetch_changesets
68 @repository.reload
85 @repository.reload
69 c = @repository.changesets.find_by_revision('7234cb2750b63f47bff735edc50a1c0a433c2518')
86 c = @repository.changesets.find_by_revision('7234cb2750b63f47bff735edc50a1c0a433c2518')
70 assert_equal c.scmid, c.identifier
87 assert_equal c.scmid, c.identifier
71 end
88 end
72
89
73 def test_format_identifier
90 def test_format_identifier
74 @repository.fetch_changesets
91 @repository.fetch_changesets
75 @repository.reload
92 @repository.reload
76 c = @repository.changesets.find_by_revision('7234cb2750b63f47bff735edc50a1c0a433c2518')
93 c = @repository.changesets.find_by_revision('7234cb2750b63f47bff735edc50a1c0a433c2518')
77 assert_equal '7234cb27', c.format_identifier
94 assert_equal '7234cb27', c.format_identifier
78 end
95 end
79
96
80 def test_activities
97 def test_activities
81 c = Changeset.new(:repository => @repository,
98 c = Changeset.new(:repository => @repository,
82 :committed_on => Time.now,
99 :committed_on => Time.now,
83 :revision => 'abc7234cb2750b63f47bff735edc50a1c0a433c2',
100 :revision => 'abc7234cb2750b63f47bff735edc50a1c0a433c2',
84 :scmid => 'abc7234cb2750b63f47bff735edc50a1c0a433c2',
101 :scmid => 'abc7234cb2750b63f47bff735edc50a1c0a433c2',
85 :comments => 'test')
102 :comments => 'test')
86 assert c.event_title.include?('abc7234c:')
103 assert c.event_title.include?('abc7234c:')
87 assert_equal 'abc7234cb2750b63f47bff735edc50a1c0a433c2', c.event_url[:rev]
104 assert_equal 'abc7234cb2750b63f47bff735edc50a1c0a433c2', c.event_url[:rev]
88 end
105 end
89 else
106 else
90 puts "Git test repository NOT FOUND. Skipping unit tests !!!"
107 puts "Git test repository NOT FOUND. Skipping unit tests !!!"
91 def test_fake; assert true end
108 def test_fake; assert true end
92 end
109 end
93 end
110 end
@@ -1,135 +1,143
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require File.expand_path('../../test_helper', __FILE__)
18 require File.expand_path('../../test_helper', __FILE__)
19
19
20 class RepositorySubversionTest < ActiveSupport::TestCase
20 class RepositorySubversionTest < ActiveSupport::TestCase
21 fixtures :projects, :repositories, :enabled_modules, :users, :roles
21 fixtures :projects, :repositories, :enabled_modules, :users, :roles
22
22
23 def setup
23 def setup
24 @project = Project.find(1)
24 @project = Project.find(1)
25 assert @repository = Repository::Subversion.create(:project => @project, :url => "file:///#{self.class.repository_path('subversion')}")
25 assert @repository = Repository::Subversion.create(:project => @project, :url => "file:///#{self.class.repository_path('subversion')}")
26 end
26 end
27
27
28 if repository_configured?('subversion')
28 if repository_configured?('subversion')
29 def test_fetch_changesets_from_scratch
29 def test_fetch_changesets_from_scratch
30 @repository.fetch_changesets
30 @repository.fetch_changesets
31 @repository.reload
31 @repository.reload
32
32
33 assert_equal 11, @repository.changesets.count
33 assert_equal 11, @repository.changesets.count
34 assert_equal 20, @repository.changes.count
34 assert_equal 20, @repository.changes.count
35 assert_equal 'Initial import.', @repository.changesets.find_by_revision('1').comments
35 assert_equal 'Initial import.', @repository.changesets.find_by_revision('1').comments
36 end
36 end
37
37
38 def test_fetch_changesets_incremental
38 def test_fetch_changesets_incremental
39 @repository.fetch_changesets
39 @repository.fetch_changesets
40 # Remove changesets with revision > 5
40 # Remove changesets with revision > 5
41 @repository.changesets.find(:all).each {|c| c.destroy if c.revision.to_i > 5}
41 @repository.changesets.find(:all).each {|c| c.destroy if c.revision.to_i > 5}
42 @repository.reload
42 @repository.reload
43 assert_equal 5, @repository.changesets.count
43 assert_equal 5, @repository.changesets.count
44
44
45 @repository.fetch_changesets
45 @repository.fetch_changesets
46 assert_equal 11, @repository.changesets.count
46 assert_equal 11, @repository.changesets.count
47 end
47 end
48
48
49 def test_latest_changesets
49 def test_latest_changesets
50 @repository.fetch_changesets
50 @repository.fetch_changesets
51
51
52 # with limit
52 # with limit
53 changesets = @repository.latest_changesets('', nil, 2)
53 changesets = @repository.latest_changesets('', nil, 2)
54 assert_equal 2, changesets.size
54 assert_equal 2, changesets.size
55 assert_equal @repository.latest_changesets('', nil).slice(0,2), changesets
55 assert_equal @repository.latest_changesets('', nil).slice(0,2), changesets
56
56
57 # with path
57 # with path
58 changesets = @repository.latest_changesets('subversion_test/folder', nil)
58 changesets = @repository.latest_changesets('subversion_test/folder', nil)
59 assert_equal ["10", "9", "7", "6", "5", "2"], changesets.collect(&:revision)
59 assert_equal ["10", "9", "7", "6", "5", "2"], changesets.collect(&:revision)
60
60
61 # with path and revision
61 # with path and revision
62 changesets = @repository.latest_changesets('subversion_test/folder', 8)
62 changesets = @repository.latest_changesets('subversion_test/folder', 8)
63 assert_equal ["7", "6", "5", "2"], changesets.collect(&:revision)
63 assert_equal ["7", "6", "5", "2"], changesets.collect(&:revision)
64 end
64 end
65
65
66 def test_directory_listing_with_square_brackets_in_path
66 def test_directory_listing_with_square_brackets_in_path
67 @repository.fetch_changesets
67 @repository.fetch_changesets
68 @repository.reload
68 @repository.reload
69
69
70 entries = @repository.entries('subversion_test/[folder_with_brackets]')
70 entries = @repository.entries('subversion_test/[folder_with_brackets]')
71 assert_not_nil entries, 'Expect to find entries in folder_with_brackets'
71 assert_not_nil entries, 'Expect to find entries in folder_with_brackets'
72 assert_equal 1, entries.size, 'Expect one entry in folder_with_brackets'
72 assert_equal 1, entries.size, 'Expect one entry in folder_with_brackets'
73 assert_equal 'README.txt', entries.first.name
73 assert_equal 'README.txt', entries.first.name
74 end
74 end
75
75
76 def test_directory_listing_with_square_brackets_in_base
76 def test_directory_listing_with_square_brackets_in_base
77 @project = Project.find(1)
77 @project = Project.find(1)
78 @repository = Repository::Subversion.create(:project => @project, :url => "file:///#{self.class.repository_path('subversion')}/subversion_test/[folder_with_brackets]")
78 @repository = Repository::Subversion.create(:project => @project, :url => "file:///#{self.class.repository_path('subversion')}/subversion_test/[folder_with_brackets]")
79
79
80 @repository.fetch_changesets
80 @repository.fetch_changesets
81 @repository.reload
81 @repository.reload
82
82
83 assert_equal 1, @repository.changesets.count, 'Expected to see 1 revision'
83 assert_equal 1, @repository.changesets.count, 'Expected to see 1 revision'
84 assert_equal 2, @repository.changes.count, 'Expected to see 2 changes, dir add and file add'
84 assert_equal 2, @repository.changes.count, 'Expected to see 2 changes, dir add and file add'
85
85
86 entries = @repository.entries('')
86 entries = @repository.entries('')
87 assert_not_nil entries, 'Expect to find entries'
87 assert_not_nil entries, 'Expect to find entries'
88 assert_equal 1, entries.size, 'Expect a single entry'
88 assert_equal 1, entries.size, 'Expect a single entry'
89 assert_equal 'README.txt', entries.first.name
89 assert_equal 'README.txt', entries.first.name
90 end
90 end
91
91
92 def test_identifier
92 def test_identifier
93 @repository.fetch_changesets
93 @repository.fetch_changesets
94 @repository.reload
94 @repository.reload
95 c = @repository.changesets.find_by_revision('1')
95 c = @repository.changesets.find_by_revision('1')
96 assert_equal c.revision, c.identifier
96 assert_equal c.revision, c.identifier
97 end
97 end
98
98
99 def test_find_changeset_by_empty_name
100 @repository.fetch_changesets
101 @repository.reload
102 ['', ' ', nil].each do |r|
103 assert_nil @repository.find_changeset_by_name(r)
104 end
105 end
106
99 def test_identifier_nine_digit
107 def test_identifier_nine_digit
100 c = Changeset.new(:repository => @repository, :committed_on => Time.now,
108 c = Changeset.new(:repository => @repository, :committed_on => Time.now,
101 :revision => '123456789', :comments => 'test')
109 :revision => '123456789', :comments => 'test')
102 assert_equal c.identifier, c.revision
110 assert_equal c.identifier, c.revision
103 end
111 end
104
112
105 def test_format_identifier
113 def test_format_identifier
106 @repository.fetch_changesets
114 @repository.fetch_changesets
107 @repository.reload
115 @repository.reload
108 c = @repository.changesets.find_by_revision('1')
116 c = @repository.changesets.find_by_revision('1')
109 assert_equal c.format_identifier, c.revision
117 assert_equal c.format_identifier, c.revision
110 end
118 end
111
119
112 def test_format_identifier_nine_digit
120 def test_format_identifier_nine_digit
113 c = Changeset.new(:repository => @repository, :committed_on => Time.now,
121 c = Changeset.new(:repository => @repository, :committed_on => Time.now,
114 :revision => '123456789', :comments => 'test')
122 :revision => '123456789', :comments => 'test')
115 assert_equal c.format_identifier, c.revision
123 assert_equal c.format_identifier, c.revision
116 end
124 end
117
125
118 def test_activities
126 def test_activities
119 c = Changeset.new(:repository => @repository, :committed_on => Time.now,
127 c = Changeset.new(:repository => @repository, :committed_on => Time.now,
120 :revision => '1', :comments => 'test')
128 :revision => '1', :comments => 'test')
121 assert c.event_title.include?('1:')
129 assert c.event_title.include?('1:')
122 assert_equal '1', c.event_url[:rev]
130 assert_equal '1', c.event_url[:rev]
123 end
131 end
124
132
125 def test_activities_nine_digit
133 def test_activities_nine_digit
126 c = Changeset.new(:repository => @repository, :committed_on => Time.now,
134 c = Changeset.new(:repository => @repository, :committed_on => Time.now,
127 :revision => '123456789', :comments => 'test')
135 :revision => '123456789', :comments => 'test')
128 assert c.event_title.include?('123456789:')
136 assert c.event_title.include?('123456789:')
129 assert_equal '123456789', c.event_url[:rev]
137 assert_equal '123456789', c.event_url[:rev]
130 end
138 end
131 else
139 else
132 puts "Subversion test repository NOT FOUND. Skipping unit tests !!!"
140 puts "Subversion test repository NOT FOUND. Skipping unit tests !!!"
133 def test_fake; assert true end
141 def test_fake; assert true end
134 end
142 end
135 end
143 end
General Comments 0
You need to be logged in to leave comments. Login now