##// END OF EJS Templates
scm: mercurial: diff '-c' option supports above Mercurial 1.2 (#3724, #7253)....
Toshi MARUYAMA -
r4566:a6f05b5e56fd
parent child
Show More
@@ -1,221 +1,225
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 'redmine/scm/adapters/abstract_adapter'
18 require 'redmine/scm/adapters/abstract_adapter'
19 require 'cgi'
19 require 'cgi'
20
20
21 module Redmine
21 module Redmine
22 module Scm
22 module Scm
23 module Adapters
23 module Adapters
24 class MercurialAdapter < AbstractAdapter
24 class MercurialAdapter < AbstractAdapter
25
25
26 # Mercurial executable name
26 # Mercurial executable name
27 HG_BIN = "hg"
27 HG_BIN = "hg"
28 TEMPLATES_DIR = File.dirname(__FILE__) + "/mercurial"
28 TEMPLATES_DIR = File.dirname(__FILE__) + "/mercurial"
29 TEMPLATE_NAME = "hg-template"
29 TEMPLATE_NAME = "hg-template"
30 TEMPLATE_EXTENSION = "tmpl"
30 TEMPLATE_EXTENSION = "tmpl"
31
31
32 class << self
32 class << self
33 def client_version
33 def client_version
34 @@client_version ||= (hgversion || [])
34 @@client_version ||= (hgversion || [])
35 end
35 end
36
36
37 def hgversion
37 def hgversion
38 # The hg version is expressed either as a
38 # The hg version is expressed either as a
39 # release number (eg 0.9.5 or 1.0) or as a revision
39 # release number (eg 0.9.5 or 1.0) or as a revision
40 # id composed of 12 hexa characters.
40 # id composed of 12 hexa characters.
41 theversion = hgversion_from_command_line
41 theversion = hgversion_from_command_line
42 if m = theversion.match(%r{\A(.*?)((\d+\.)+\d+)})
42 if m = theversion.match(%r{\A(.*?)((\d+\.)+\d+)})
43 m[2].scan(%r{\d+}).collect(&:to_i)
43 m[2].scan(%r{\d+}).collect(&:to_i)
44 end
44 end
45 end
45 end
46
46
47 def hgversion_from_command_line
47 def hgversion_from_command_line
48 shellout("#{HG_BIN} --version") { |io| io.read }.to_s
48 shellout("#{HG_BIN} --version") { |io| io.read }.to_s
49 end
49 end
50
50
51 def template_path
51 def template_path
52 @@template_path ||= template_path_for(client_version)
52 @@template_path ||= template_path_for(client_version)
53 end
53 end
54
54
55 def template_path_for(version)
55 def template_path_for(version)
56 if ((version <=> [0,9,5]) > 0) || version.empty?
56 if ((version <=> [0,9,5]) > 0) || version.empty?
57 ver = "1.0"
57 ver = "1.0"
58 else
58 else
59 ver = "0.9.5"
59 ver = "0.9.5"
60 end
60 end
61 "#{TEMPLATES_DIR}/#{TEMPLATE_NAME}-#{ver}.#{TEMPLATE_EXTENSION}"
61 "#{TEMPLATES_DIR}/#{TEMPLATE_NAME}-#{ver}.#{TEMPLATE_EXTENSION}"
62 end
62 end
63 end
63 end
64
64
65 def info
65 def info
66 cmd = "#{HG_BIN} -R #{target('')} root"
66 cmd = "#{HG_BIN} -R #{target('')} root"
67 root_url = nil
67 root_url = nil
68 shellout(cmd) do |io|
68 shellout(cmd) do |io|
69 root_url = io.read
69 root_url = io.read
70 end
70 end
71 return nil if $? && $?.exitstatus != 0
71 return nil if $? && $?.exitstatus != 0
72 info = Info.new({:root_url => root_url.chomp,
72 info = Info.new({:root_url => root_url.chomp,
73 :lastrev => revisions(nil,nil,nil,{:limit => 1}).last
73 :lastrev => revisions(nil,nil,nil,{:limit => 1}).last
74 })
74 })
75 info
75 info
76 rescue CommandFailed
76 rescue CommandFailed
77 return nil
77 return nil
78 end
78 end
79
79
80 def entries(path=nil, identifier=nil)
80 def entries(path=nil, identifier=nil)
81 path ||= ''
81 path ||= ''
82 entries = Entries.new
82 entries = Entries.new
83 cmd = "#{HG_BIN} -R #{target('')} --cwd #{target('')} locate"
83 cmd = "#{HG_BIN} -R #{target('')} --cwd #{target('')} locate"
84 cmd << " -r #{hgrev(identifier)}"
84 cmd << " -r #{hgrev(identifier)}"
85 cmd << " " + shell_quote("path:#{path}") unless path.empty?
85 cmd << " " + shell_quote("path:#{path}") unless path.empty?
86 shellout(cmd) do |io|
86 shellout(cmd) do |io|
87 io.each_line do |line|
87 io.each_line do |line|
88 # HG uses antislashs as separator on Windows
88 # HG uses antislashs as separator on Windows
89 line = line.gsub(/\\/, "/")
89 line = line.gsub(/\\/, "/")
90 if path.empty? or e = line.gsub!(%r{^#{with_trailling_slash(path)}},'')
90 if path.empty? or e = line.gsub!(%r{^#{with_trailling_slash(path)}},'')
91 e ||= line
91 e ||= line
92 e = e.chomp.split(%r{[\/\\]})
92 e = e.chomp.split(%r{[\/\\]})
93 entries << Entry.new({:name => e.first,
93 entries << Entry.new({:name => e.first,
94 :path => (path.nil? or path.empty? ? e.first : "#{with_trailling_slash(path)}#{e.first}"),
94 :path => (path.nil? or path.empty? ? e.first : "#{with_trailling_slash(path)}#{e.first}"),
95 :kind => (e.size > 1 ? 'dir' : 'file'),
95 :kind => (e.size > 1 ? 'dir' : 'file'),
96 :lastrev => Revision.new
96 :lastrev => Revision.new
97 }) unless e.empty? || entries.detect{|entry| entry.name == e.first}
97 }) unless e.empty? || entries.detect{|entry| entry.name == e.first}
98 end
98 end
99 end
99 end
100 end
100 end
101 return nil if $? && $?.exitstatus != 0
101 return nil if $? && $?.exitstatus != 0
102 entries.sort_by_name
102 entries.sort_by_name
103 end
103 end
104
104
105 # Fetch the revisions by using a template file that
105 # Fetch the revisions by using a template file that
106 # makes Mercurial produce a xml output.
106 # makes Mercurial produce a xml output.
107 def revisions(path=nil, identifier_from=nil, identifier_to=nil, options={})
107 def revisions(path=nil, identifier_from=nil, identifier_to=nil, options={})
108 revisions = Revisions.new
108 revisions = Revisions.new
109 cmd = "#{HG_BIN} --debug --encoding utf8 -R #{target('')} log -C --style #{shell_quote self.class.template_path}"
109 cmd = "#{HG_BIN} --debug --encoding utf8 -R #{target('')} log -C --style #{shell_quote self.class.template_path}"
110 if identifier_from && identifier_to
110 if identifier_from && identifier_to
111 cmd << " -r #{hgrev(identifier_from)}:#{hgrev(identifier_to)}"
111 cmd << " -r #{hgrev(identifier_from)}:#{hgrev(identifier_to)}"
112 elsif identifier_from
112 elsif identifier_from
113 cmd << " -r #{hgrev(identifier_from)}:"
113 cmd << " -r #{hgrev(identifier_from)}:"
114 end
114 end
115 cmd << " --limit #{options[:limit].to_i}" if options[:limit]
115 cmd << " --limit #{options[:limit].to_i}" if options[:limit]
116 cmd << " #{shell_quote path}" unless path.blank?
116 cmd << " #{shell_quote path}" unless path.blank?
117 shellout(cmd) do |io|
117 shellout(cmd) do |io|
118 begin
118 begin
119 # HG doesn't close the XML Document...
119 # HG doesn't close the XML Document...
120 doc = REXML::Document.new(io.read << "</log>")
120 doc = REXML::Document.new(io.read << "</log>")
121 doc.elements.each("log/logentry") do |logentry|
121 doc.elements.each("log/logentry") do |logentry|
122 paths = []
122 paths = []
123 copies = logentry.get_elements('paths/path-copied')
123 copies = logentry.get_elements('paths/path-copied')
124 logentry.elements.each("paths/path") do |path|
124 logentry.elements.each("paths/path") do |path|
125 # Detect if the added file is a copy
125 # Detect if the added file is a copy
126 if path.attributes['action'] == 'A' and c = copies.find{ |e| e.text == path.text }
126 if path.attributes['action'] == 'A' and c = copies.find{ |e| e.text == path.text }
127 from_path = c.attributes['copyfrom-path']
127 from_path = c.attributes['copyfrom-path']
128 from_rev = logentry.attributes['revision']
128 from_rev = logentry.attributes['revision']
129 end
129 end
130 paths << {:action => path.attributes['action'],
130 paths << {:action => path.attributes['action'],
131 :path => "/#{CGI.unescape(path.text)}",
131 :path => "/#{CGI.unescape(path.text)}",
132 :from_path => from_path ? "/#{CGI.unescape(from_path)}" : nil,
132 :from_path => from_path ? "/#{CGI.unescape(from_path)}" : nil,
133 :from_revision => from_rev ? from_rev : nil
133 :from_revision => from_rev ? from_rev : nil
134 }
134 }
135 end
135 end
136 paths.sort! { |x,y| x[:path] <=> y[:path] }
136 paths.sort! { |x,y| x[:path] <=> y[:path] }
137
137
138 revisions << Revision.new({:identifier => logentry.attributes['revision'],
138 revisions << Revision.new({:identifier => logentry.attributes['revision'],
139 :scmid => logentry.attributes['node'],
139 :scmid => logentry.attributes['node'],
140 :author => (logentry.elements['author'] ? logentry.elements['author'].text : ""),
140 :author => (logentry.elements['author'] ? logentry.elements['author'].text : ""),
141 :time => Time.parse(logentry.elements['date'].text).localtime,
141 :time => Time.parse(logentry.elements['date'].text).localtime,
142 :message => logentry.elements['msg'].text,
142 :message => logentry.elements['msg'].text,
143 :paths => paths
143 :paths => paths
144 })
144 })
145 end
145 end
146 rescue
146 rescue
147 logger.debug($!)
147 logger.debug($!)
148 end
148 end
149 end
149 end
150 return nil if $? && $?.exitstatus != 0
150 return nil if $? && $?.exitstatus != 0
151 revisions
151 revisions
152 end
152 end
153
153
154 def diff(path, identifier_from, identifier_to=nil)
154 def diff(path, identifier_from, identifier_to=nil)
155 path ||= ''
155 path ||= ''
156 diff_args = ''
156 diff_args = ''
157 diff = []
157 if identifier_to
158 if identifier_to
158 diff_args = "-r #{hgrev(identifier_to)} -r #{hgrev(identifier_from)}"
159 diff_args = "-r #{hgrev(identifier_to)} -r #{hgrev(identifier_from)}"
159 else
160 else
160 diff_args = "-c #{hgrev(identifier_from)}"
161 if self.class.client_version_above?([1, 2])
162 diff_args = "-c #{hgrev(identifier_from)}"
163 else
164 return []
165 end
161 end
166 end
162 cmd = "#{HG_BIN} -R #{target('')} diff --nodates --git #{diff_args}"
167 cmd = "#{HG_BIN} -R #{target('')} diff --nodates --git #{diff_args}"
163 cmd << " -I #{target(path)}" unless path.empty?
168 cmd << " -I #{target(path)}" unless path.empty?
164 diff = []
165 shellout(cmd) do |io|
169 shellout(cmd) do |io|
166 io.each_line do |line|
170 io.each_line do |line|
167 diff << line
171 diff << line
168 end
172 end
169 end
173 end
170 return nil if $? && $?.exitstatus != 0
174 return nil if $? && $?.exitstatus != 0
171 diff
175 diff
172 end
176 end
173
177
174 def cat(path, identifier=nil)
178 def cat(path, identifier=nil)
175 cmd = "#{HG_BIN} -R #{target('')} cat"
179 cmd = "#{HG_BIN} -R #{target('')} cat"
176 cmd << " -r #{hgrev(identifier)}"
180 cmd << " -r #{hgrev(identifier)}"
177 cmd << " #{target(path)}"
181 cmd << " #{target(path)}"
178 cat = nil
182 cat = nil
179 shellout(cmd) do |io|
183 shellout(cmd) do |io|
180 io.binmode
184 io.binmode
181 cat = io.read
185 cat = io.read
182 end
186 end
183 return nil if $? && $?.exitstatus != 0
187 return nil if $? && $?.exitstatus != 0
184 cat
188 cat
185 end
189 end
186
190
187 def annotate(path, identifier=nil)
191 def annotate(path, identifier=nil)
188 path ||= ''
192 path ||= ''
189 cmd = "#{HG_BIN} -R #{target('')}"
193 cmd = "#{HG_BIN} -R #{target('')}"
190 cmd << " annotate -ncu"
194 cmd << " annotate -ncu"
191 cmd << " -r #{hgrev(identifier)}"
195 cmd << " -r #{hgrev(identifier)}"
192 cmd << " #{target(path)}"
196 cmd << " #{target(path)}"
193 blame = Annotate.new
197 blame = Annotate.new
194 shellout(cmd) do |io|
198 shellout(cmd) do |io|
195 io.each_line do |line|
199 io.each_line do |line|
196 next unless line =~ %r{^([^:]+)\s(\d+)\s([0-9a-f]+):\s(.*)$}
200 next unless line =~ %r{^([^:]+)\s(\d+)\s([0-9a-f]+):\s(.*)$}
197 r = Revision.new(:author => $1.strip, :revision => $2, :scmid => $3,
201 r = Revision.new(:author => $1.strip, :revision => $2, :scmid => $3,
198 :identifier => $3)
202 :identifier => $3)
199 blame.add_line($4.rstrip, r)
203 blame.add_line($4.rstrip, r)
200 end
204 end
201 end
205 end
202 return nil if $? && $?.exitstatus != 0
206 return nil if $? && $?.exitstatus != 0
203 blame
207 blame
204 end
208 end
205
209
206 class Revision < Redmine::Scm::Adapters::Revision
210 class Revision < Redmine::Scm::Adapters::Revision
207 # Returns the readable identifier
211 # Returns the readable identifier
208 def format_identifier
212 def format_identifier
209 "#{revision}:#{scmid}"
213 "#{revision}:#{scmid}"
210 end
214 end
211 end
215 end
212
216
213 # Returns correct revision identifier
217 # Returns correct revision identifier
214 def hgrev(identifier)
218 def hgrev(identifier)
215 shell_quote(identifier.blank? ? 'tip' : identifier.to_s)
219 shell_quote(identifier.blank? ? 'tip' : identifier.to_s)
216 end
220 end
217 private :hgrev
221 private :hgrev
218 end
222 end
219 end
223 end
220 end
224 end
221 end
225 end
@@ -1,176 +1,180
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 RepositoriesMercurialControllerTest < ActionController::TestCase
24 class RepositoriesMercurialControllerTest < 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/mercurial_repository'
28 REPOSITORY_PATH = RAILS_ROOT.gsub(%r{config\/\.\.}, '') + '/tmp/test/mercurial_repository'
29
29
30 def setup
30 def setup
31 @controller = RepositoriesController.new
31 @controller = RepositoriesController.new
32 @request = ActionController::TestRequest.new
32 @request = ActionController::TestRequest.new
33 @response = ActionController::TestResponse.new
33 @response = ActionController::TestResponse.new
34 User.current = nil
34 User.current = nil
35 Repository::Mercurial.create(:project => Project.find(3), :url => REPOSITORY_PATH)
35 @repository = Repository::Mercurial.create(:project => Project.find(3), :url => REPOSITORY_PATH)
36 assert @repository
36 end
37 end
37
38
38 if File.directory?(REPOSITORY_PATH)
39 if File.directory?(REPOSITORY_PATH)
39 def test_show
40 def test_show
40 get :show, :id => 3
41 get :show, :id => 3
41 assert_response :success
42 assert_response :success
42 assert_template 'show'
43 assert_template 'show'
43 assert_not_nil assigns(:entries)
44 assert_not_nil assigns(:entries)
44 assert_not_nil assigns(:changesets)
45 assert_not_nil assigns(:changesets)
45 end
46 end
46
47
47 def test_show_root
48 def test_show_root
48 get :show, :id => 3
49 get :show, :id => 3
49 assert_response :success
50 assert_response :success
50 assert_template 'show'
51 assert_template 'show'
51 assert_not_nil assigns(:entries)
52 assert_not_nil assigns(:entries)
52 assert_equal 4, assigns(:entries).size
53 assert_equal 4, assigns(:entries).size
53 assert assigns(:entries).detect {|e| e.name == 'images' && e.kind == 'dir'}
54 assert assigns(:entries).detect {|e| e.name == 'images' && e.kind == 'dir'}
54 assert assigns(:entries).detect {|e| e.name == 'sources' && e.kind == 'dir'}
55 assert assigns(:entries).detect {|e| e.name == 'sources' && e.kind == 'dir'}
55 assert assigns(:entries).detect {|e| e.name == 'README' && e.kind == 'file'}
56 assert assigns(:entries).detect {|e| e.name == 'README' && e.kind == 'file'}
56 end
57 end
57
58
58 def test_show_directory
59 def test_show_directory
59 get :show, :id => 3, :path => ['images']
60 get :show, :id => 3, :path => ['images']
60 assert_response :success
61 assert_response :success
61 assert_template 'show'
62 assert_template 'show'
62 assert_not_nil assigns(:entries)
63 assert_not_nil assigns(:entries)
63 assert_equal ['delete.png', 'edit.png'], assigns(:entries).collect(&:name)
64 assert_equal ['delete.png', 'edit.png'], assigns(:entries).collect(&:name)
64 entry = assigns(:entries).detect {|e| e.name == 'edit.png'}
65 entry = assigns(:entries).detect {|e| e.name == 'edit.png'}
65 assert_not_nil entry
66 assert_not_nil entry
66 assert_equal 'file', entry.kind
67 assert_equal 'file', entry.kind
67 assert_equal 'images/edit.png', entry.path
68 assert_equal 'images/edit.png', entry.path
68 end
69 end
69
70
70 def test_show_at_given_revision
71 def test_show_at_given_revision
71 [0, '0', '0885933ad4f6'].each do |r1|
72 [0, '0', '0885933ad4f6'].each do |r1|
72 get :show, :id => 3, :path => ['images'], :rev => r1
73 get :show, :id => 3, :path => ['images'], :rev => r1
73 assert_response :success
74 assert_response :success
74 assert_template 'show'
75 assert_template 'show'
75 assert_not_nil assigns(:entries)
76 assert_not_nil assigns(:entries)
76 assert_equal ['delete.png'], assigns(:entries).collect(&:name)
77 assert_equal ['delete.png'], assigns(:entries).collect(&:name)
77 end
78 end
78 end
79 end
79
80
80 def test_show_directory_sql_escape_percent
81 def test_show_directory_sql_escape_percent
81 [13, '13', '3a330eb32958'].each do |r1|
82 [13, '13', '3a330eb32958'].each do |r1|
82 get :show, :id => 3, :path => ['sql_escape', 'percent%dir'], :rev => r1
83 get :show, :id => 3, :path => ['sql_escape', 'percent%dir'], :rev => r1
83 assert_response :success
84 assert_response :success
84 assert_template 'show'
85 assert_template 'show'
85
86
86 assert_not_nil assigns(:entries)
87 assert_not_nil assigns(:entries)
87 assert_equal ['percent%file1.txt', 'percentfile1.txt'], assigns(:entries).collect(&:name)
88 assert_equal ['percent%file1.txt', 'percentfile1.txt'], assigns(:entries).collect(&:name)
88 changesets = assigns(:changesets)
89 changesets = assigns(:changesets)
89
90
90 ## This is not yet implemented.
91 ## This is not yet implemented.
91 # assert_not_nil changesets
92 # assert_not_nil changesets
92 # assert_equal %w(13 11 10 9), changesets.collect(&:revision)
93 # assert_equal %w(13 11 10 9), changesets.collect(&:revision)
93 end
94 end
94 end
95 end
95
96
96 def test_changes
97 def test_changes
97 get :changes, :id => 3, :path => ['images', 'edit.png']
98 get :changes, :id => 3, :path => ['images', 'edit.png']
98 assert_response :success
99 assert_response :success
99 assert_template 'changes'
100 assert_template 'changes'
100 assert_tag :tag => 'h2', :content => 'edit.png'
101 assert_tag :tag => 'h2', :content => 'edit.png'
101 end
102 end
102
103
103 def test_entry_show
104 def test_entry_show
104 get :entry, :id => 3, :path => ['sources', 'watchers_controller.rb']
105 get :entry, :id => 3, :path => ['sources', 'watchers_controller.rb']
105 assert_response :success
106 assert_response :success
106 assert_template 'entry'
107 assert_template 'entry'
107 # Line 10
108 # Line 10
108 assert_tag :tag => 'th',
109 assert_tag :tag => 'th',
109 :content => '10',
110 :content => '10',
110 :attributes => { :class => 'line-num' },
111 :attributes => { :class => 'line-num' },
111 :sibling => { :tag => 'td', :content => /WITHOUT ANY WARRANTY/ }
112 :sibling => { :tag => 'td', :content => /WITHOUT ANY WARRANTY/ }
112 end
113 end
113
114
114 def test_entry_download
115 def test_entry_download
115 get :entry, :id => 3, :path => ['sources', 'watchers_controller.rb'], :format => 'raw'
116 get :entry, :id => 3, :path => ['sources', 'watchers_controller.rb'], :format => 'raw'
116 assert_response :success
117 assert_response :success
117 # File content
118 # File content
118 assert @response.body.include?('WITHOUT ANY WARRANTY')
119 assert @response.body.include?('WITHOUT ANY WARRANTY')
119 end
120 end
120
121
121 def test_directory_entry
122 def test_directory_entry
122 get :entry, :id => 3, :path => ['sources']
123 get :entry, :id => 3, :path => ['sources']
123 assert_response :success
124 assert_response :success
124 assert_template 'show'
125 assert_template 'show'
125 assert_not_nil assigns(:entry)
126 assert_not_nil assigns(:entry)
126 assert_equal 'sources', assigns(:entry).name
127 assert_equal 'sources', assigns(:entry).name
127 end
128 end
128
129
129 def test_diff
130 def test_diff
130 [4, '4', 'def6d2f1254a'].each do |r1|
131 [4, '4', 'def6d2f1254a'].each do |r1|
131 # Full diff of changeset 4
132 # Full diff of changeset 4
132 get :diff, :id => 3, :rev => 4
133 get :diff, :id => 3, :rev => 4
133 assert_response :success
134 assert_response :success
134 assert_template 'diff'
135 assert_template 'diff'
135 # Line 22 removed
136
136 assert_tag :tag => 'th',
137 if @repository.scm.class.client_version_above?([1, 2])
137 :content => '22',
138 # Line 22 removed
138 :sibling => { :tag => 'td',
139 assert_tag :tag => 'th',
139 :attributes => { :class => /diff_out/ },
140 :content => '22',
140 :content => /def remove/ }
141 :sibling => { :tag => 'td',
142 :attributes => { :class => /diff_out/ },
143 :content => /def remove/ }
144 end
141 end
145 end
142 end
146 end
143
147
144 def test_annotate
148 def test_annotate
145 get :annotate, :id => 3, :path => ['sources', 'watchers_controller.rb']
149 get :annotate, :id => 3, :path => ['sources', 'watchers_controller.rb']
146 assert_response :success
150 assert_response :success
147 assert_template 'annotate'
151 assert_template 'annotate'
148 # Line 23, revision 4:def6d2f1254a
152 # Line 23, revision 4:def6d2f1254a
149 assert_tag :tag => 'th',
153 assert_tag :tag => 'th',
150 :content => '23',
154 :content => '23',
151 :attributes => { :class => 'line-num' },
155 :attributes => { :class => 'line-num' },
152 :sibling =>
156 :sibling =>
153 {
157 {
154 :tag => 'td',
158 :tag => 'td',
155 :attributes => { :class => 'revision' },
159 :attributes => { :class => 'revision' },
156 :child => { :tag => 'a', :content => '4:def6d2f1254a' }
160 :child => { :tag => 'a', :content => '4:def6d2f1254a' }
157 }
161 }
158 assert_tag :tag => 'th',
162 assert_tag :tag => 'th',
159 :content => '23',
163 :content => '23',
160 :attributes => { :class => 'line-num' },
164 :attributes => { :class => 'line-num' },
161 :sibling =>
165 :sibling =>
162 {
166 {
163 :tag => 'td' ,
167 :tag => 'td' ,
164 :content => 'jsmith' ,
168 :content => 'jsmith' ,
165 :attributes => { :class => 'author' },
169 :attributes => { :class => 'author' },
166 }
170 }
167 assert_tag :tag => 'th',
171 assert_tag :tag => 'th',
168 :content => '23',
172 :content => '23',
169 :attributes => { :class => 'line-num' },
173 :attributes => { :class => 'line-num' },
170 :sibling => { :tag => 'td', :content => /watcher =/ }
174 :sibling => { :tag => 'td', :content => /watcher =/ }
171 end
175 end
172 else
176 else
173 puts "Mercurial test repository NOT FOUND. Skipping functional tests !!!"
177 puts "Mercurial test repository NOT FOUND. Skipping functional tests !!!"
174 def test_fake; assert true end
178 def test_fake; assert true end
175 end
179 end
176 end
180 end
@@ -1,124 +1,130
1 require File.expand_path('../../../../../../test_helper', __FILE__)
1 require File.expand_path('../../../../../../test_helper', __FILE__)
2 begin
2 begin
3 require 'mocha'
3 require 'mocha'
4
4
5 class MercurialAdapterTest < ActiveSupport::TestCase
5 class MercurialAdapterTest < ActiveSupport::TestCase
6
6
7 TEMPLATES_DIR = Redmine::Scm::Adapters::MercurialAdapter::TEMPLATES_DIR
7 TEMPLATES_DIR = Redmine::Scm::Adapters::MercurialAdapter::TEMPLATES_DIR
8 TEMPLATE_NAME = Redmine::Scm::Adapters::MercurialAdapter::TEMPLATE_NAME
8 TEMPLATE_NAME = Redmine::Scm::Adapters::MercurialAdapter::TEMPLATE_NAME
9 TEMPLATE_EXTENSION = Redmine::Scm::Adapters::MercurialAdapter::TEMPLATE_EXTENSION
9 TEMPLATE_EXTENSION = Redmine::Scm::Adapters::MercurialAdapter::TEMPLATE_EXTENSION
10
10
11 REPOSITORY_PATH = RAILS_ROOT.gsub(%r{config\/\.\.}, '') + '/tmp/test/mercurial_repository'
11 REPOSITORY_PATH = RAILS_ROOT.gsub(%r{config\/\.\.}, '') + '/tmp/test/mercurial_repository'
12
12
13 if File.directory?(REPOSITORY_PATH)
13 if File.directory?(REPOSITORY_PATH)
14 def setup
14 def setup
15 @adapter = Redmine::Scm::Adapters::MercurialAdapter.new(REPOSITORY_PATH)
15 @adapter = Redmine::Scm::Adapters::MercurialAdapter.new(REPOSITORY_PATH)
16 end
16 end
17
17
18 def test_hgversion
18 def test_hgversion
19 to_test = { "Mercurial Distributed SCM (version 0.9.5)\n" => [0,9,5],
19 to_test = { "Mercurial Distributed SCM (version 0.9.5)\n" => [0,9,5],
20 "Mercurial Distributed SCM (1.0)\n" => [1,0],
20 "Mercurial Distributed SCM (1.0)\n" => [1,0],
21 "Mercurial Distributed SCM (1e4ddc9ac9f7+20080325)\n" => nil,
21 "Mercurial Distributed SCM (1e4ddc9ac9f7+20080325)\n" => nil,
22 "Mercurial Distributed SCM (1.0.1+20080525)\n" => [1,0,1],
22 "Mercurial Distributed SCM (1.0.1+20080525)\n" => [1,0,1],
23 "Mercurial Distributed SCM (1916e629a29d)\n" => nil,
23 "Mercurial Distributed SCM (1916e629a29d)\n" => nil,
24 "Mercurial SCM Distribuito (versione 0.9.5)\n" => [0,9,5],
24 "Mercurial SCM Distribuito (versione 0.9.5)\n" => [0,9,5],
25 "(1.6)\n(1.7)\n(1.8)" => [1,6],
25 "(1.6)\n(1.7)\n(1.8)" => [1,6],
26 "(1.7.1)\r\n(1.8.1)\r\n(1.9.1)" => [1,7,1]}
26 "(1.7.1)\r\n(1.8.1)\r\n(1.9.1)" => [1,7,1]}
27
27
28 to_test.each do |s, v|
28 to_test.each do |s, v|
29 test_hgversion_for(s, v)
29 test_hgversion_for(s, v)
30 end
30 end
31 end
31 end
32
32
33 def test_template_path
33 def test_template_path
34 to_test = { [0,9,5] => "0.9.5",
34 to_test = { [0,9,5] => "0.9.5",
35 [1,0] => "1.0",
35 [1,0] => "1.0",
36 [] => "1.0",
36 [] => "1.0",
37 [1,0,1] => "1.0",
37 [1,0,1] => "1.0",
38 [1,7] => "1.0",
38 [1,7] => "1.0",
39 [1,7,1] => "1.0" }
39 [1,7,1] => "1.0" }
40 to_test.each do |v, template|
40 to_test.each do |v, template|
41 test_template_path_for(v, template)
41 test_template_path_for(v, template)
42 end
42 end
43 end
43 end
44
44
45 def test_diff
45 def test_diff
46 assert_nil @adapter.diff(nil, '100000')
46 if @adapter.class.client_version_above?([1, 2])
47 assert_nil @adapter.diff(nil, '100000')
48 end
47 assert_nil @adapter.diff(nil, '100000', '200000')
49 assert_nil @adapter.diff(nil, '100000', '200000')
48 [2, '400bb8672109', '400', 400].each do |r1|
50 [2, '400bb8672109', '400', 400].each do |r1|
49 diff1 = @adapter.diff(nil, r1)
51 diff1 = @adapter.diff(nil, r1)
50 assert_equal 28, diff1.size
52 if @adapter.class.client_version_above?([1, 2])
51 buf = diff1[24].gsub(/\r\n|\r|\n/, "")
53 assert_equal 28, diff1.size
52 assert_equal "+ return true unless klass.respond_to?('watched_by')", buf
54 buf = diff1[24].gsub(/\r\n|\r|\n/, "")
55 assert_equal "+ return true unless klass.respond_to?('watched_by')", buf
56 else
57 assert_equal 0, diff1.size
58 end
53 [4, 'def6d2f1254a'].each do |r2|
59 [4, 'def6d2f1254a'].each do |r2|
54 diff2 = @adapter.diff(nil,r1,r2)
60 diff2 = @adapter.diff(nil,r1,r2)
55 assert_equal 50, diff2.size
61 assert_equal 50, diff2.size
56 buf = diff2[42].gsub(/\r\n|\r|\n/, "")
62 buf = diff2[42].gsub(/\r\n|\r|\n/, "")
57 assert_equal "+class WelcomeController < ApplicationController", buf
63 assert_equal "+class WelcomeController < ApplicationController", buf
58 diff3 = @adapter.diff('sources/watchers_controller.rb', r1, r2)
64 diff3 = @adapter.diff('sources/watchers_controller.rb', r1, r2)
59 assert_equal 20, diff3.size
65 assert_equal 20, diff3.size
60 buf = diff3[12].gsub(/\r\n|\r|\n/, "")
66 buf = diff3[12].gsub(/\r\n|\r|\n/, "")
61 assert_equal "+ @watched.remove_watcher(user)", buf
67 assert_equal "+ @watched.remove_watcher(user)", buf
62 end
68 end
63 end
69 end
64 end
70 end
65
71
66 def test_cat
72 def test_cat
67 [2, '400bb8672109', '400', 400].each do |r|
73 [2, '400bb8672109', '400', 400].each do |r|
68 buf = @adapter.cat('sources/welcome_controller.rb', r)
74 buf = @adapter.cat('sources/welcome_controller.rb', r)
69 assert buf
75 assert buf
70 lines = buf.split("\r\n")
76 lines = buf.split("\r\n")
71 assert_equal 25, lines.length
77 assert_equal 25, lines.length
72 assert_equal 'class WelcomeController < ApplicationController', lines[17]
78 assert_equal 'class WelcomeController < ApplicationController', lines[17]
73 end
79 end
74 assert_nil @adapter.cat('sources/welcome_controller.rb')
80 assert_nil @adapter.cat('sources/welcome_controller.rb')
75 end
81 end
76
82
77 def test_annotate
83 def test_annotate
78 assert_equal [], @adapter.annotate("sources/welcome_controller.rb").lines
84 assert_equal [], @adapter.annotate("sources/welcome_controller.rb").lines
79 [2, '400bb8672109', '400', 400].each do |r|
85 [2, '400bb8672109', '400', 400].each do |r|
80 ann = @adapter.annotate('sources/welcome_controller.rb', r)
86 ann = @adapter.annotate('sources/welcome_controller.rb', r)
81 assert ann
87 assert ann
82 assert_equal '1', ann.revisions[17].revision
88 assert_equal '1', ann.revisions[17].revision
83 assert_equal '9d5b5b004199', ann.revisions[17].identifier
89 assert_equal '9d5b5b004199', ann.revisions[17].identifier
84 assert_equal 'jsmith', ann.revisions[0].author
90 assert_equal 'jsmith', ann.revisions[0].author
85 assert_equal 25, ann.lines.length
91 assert_equal 25, ann.lines.length
86 assert_equal 'class WelcomeController < ApplicationController', ann.lines[17]
92 assert_equal 'class WelcomeController < ApplicationController', ann.lines[17]
87 end
93 end
88 end
94 end
89
95
90 def test_access_by_nodeid
96 def test_access_by_nodeid
91 path = 'sources/welcome_controller.rb'
97 path = 'sources/welcome_controller.rb'
92 assert_equal @adapter.cat(path, 2), @adapter.cat(path, '400bb8672109')
98 assert_equal @adapter.cat(path, 2), @adapter.cat(path, '400bb8672109')
93 end
99 end
94
100
95 def test_access_by_fuzzy_nodeid
101 def test_access_by_fuzzy_nodeid
96 path = 'sources/welcome_controller.rb'
102 path = 'sources/welcome_controller.rb'
97 # falls back to nodeid
103 # falls back to nodeid
98 assert_equal @adapter.cat(path, 2), @adapter.cat(path, '400')
104 assert_equal @adapter.cat(path, 2), @adapter.cat(path, '400')
99 end
105 end
100
106
101 private
107 private
102
108
103 def test_hgversion_for(hgversion, version)
109 def test_hgversion_for(hgversion, version)
104 @adapter.class.expects(:hgversion_from_command_line).returns(hgversion)
110 @adapter.class.expects(:hgversion_from_command_line).returns(hgversion)
105 assert_equal version, @adapter.class.hgversion
111 assert_equal version, @adapter.class.hgversion
106 end
112 end
107
113
108 def test_template_path_for(version, template)
114 def test_template_path_for(version, template)
109 assert_equal "#{TEMPLATES_DIR}/#{TEMPLATE_NAME}-#{template}.#{TEMPLATE_EXTENSION}",
115 assert_equal "#{TEMPLATES_DIR}/#{TEMPLATE_NAME}-#{template}.#{TEMPLATE_EXTENSION}",
110 @adapter.class.template_path_for(version)
116 @adapter.class.template_path_for(version)
111 assert File.exist?(@adapter.class.template_path_for(version))
117 assert File.exist?(@adapter.class.template_path_for(version))
112 end
118 end
113 else
119 else
114 puts "Mercurial test repository NOT FOUND. Skipping unit tests !!!"
120 puts "Mercurial test repository NOT FOUND. Skipping unit tests !!!"
115 def test_fake; assert true end
121 def test_fake; assert true end
116 end
122 end
117 end
123 end
118
124
119 rescue LoadError
125 rescue LoadError
120 class MercurialMochaFake < ActiveSupport::TestCase
126 class MercurialMochaFake < ActiveSupport::TestCase
121 def test_fake; assert(false, "Requires mocha to run those tests") end
127 def test_fake; assert(false, "Requires mocha to run those tests") end
122 end
128 end
123 end
129 end
124
130
General Comments 0
You need to be logged in to leave comments. Login now