##// END OF EJS Templates
scm: cvs: add methods of getting cvs version and add unit lib test (#4273)....
Toshi MARUYAMA -
r4712:2afc8e8c9522
parent child
Show More
@@ -1,381 +1,400
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
19
20 module Redmine
20 module Redmine
21 module Scm
21 module Scm
22 module Adapters
22 module Adapters
23 class CvsAdapter < AbstractAdapter
23 class CvsAdapter < AbstractAdapter
24
24
25 # CVS executable name
25 # CVS executable name
26 CVS_BIN = Redmine::Configuration['scm_cvs_command'] || "cvs"
26 CVS_BIN = Redmine::Configuration['scm_cvs_command'] || "cvs"
27
27
28 class << self
28 class << self
29 def client_command
29 def client_command
30 @@bin ||= CVS_BIN
30 @@bin ||= CVS_BIN
31 end
31 end
32
32
33 def sq_bin
33 def sq_bin
34 @@sq_bin ||= shell_quote(CVS_BIN)
34 @@sq_bin ||= shell_quote(CVS_BIN)
35 end
35 end
36
37 def client_version
38 @@client_version ||= (scm_command_version || [])
39 end
40
41 def client_available
42 !client_version.empty?
43 end
44
45 def scm_command_version
46 scm_version = scm_version_from_command_line
47 if m = scm_version.match(%r{\A(.*?)((\d+\.)+\d+)}m)
48 m[2].scan(%r{\d+}).collect(&:to_i)
49 end
50 end
51
52 def scm_version_from_command_line
53 shellout("#{sq_bin} --version") { |io| io.read }.to_s
54 end
36 end
55 end
37
56
38 # Guidelines for the input:
57 # Guidelines for the input:
39 # url -> the project-path, relative to the cvsroot (eg. module name)
58 # url -> the project-path, relative to the cvsroot (eg. module name)
40 # root_url -> the good old, sometimes damned, CVSROOT
59 # root_url -> the good old, sometimes damned, CVSROOT
41 # login -> unnecessary
60 # login -> unnecessary
42 # password -> unnecessary too
61 # password -> unnecessary too
43 def initialize(url, root_url=nil, login=nil, password=nil)
62 def initialize(url, root_url=nil, login=nil, password=nil)
44 @url = url
63 @url = url
45 @login = login if login && !login.empty?
64 @login = login if login && !login.empty?
46 @password = (password || "") if @login
65 @password = (password || "") if @login
47 #TODO: better Exception here (IllegalArgumentException)
66 #TODO: better Exception here (IllegalArgumentException)
48 raise CommandFailed if root_url.blank?
67 raise CommandFailed if root_url.blank?
49 @root_url = root_url
68 @root_url = root_url
50 end
69 end
51
70
52 def root_url
71 def root_url
53 @root_url
72 @root_url
54 end
73 end
55
74
56 def url
75 def url
57 @url
76 @url
58 end
77 end
59
78
60 def info
79 def info
61 logger.debug "<cvs> info"
80 logger.debug "<cvs> info"
62 Info.new({:root_url => @root_url, :lastrev => nil})
81 Info.new({:root_url => @root_url, :lastrev => nil})
63 end
82 end
64
83
65 def get_previous_revision(revision)
84 def get_previous_revision(revision)
66 CvsRevisionHelper.new(revision).prevRev
85 CvsRevisionHelper.new(revision).prevRev
67 end
86 end
68
87
69 # Returns an Entries collection
88 # Returns an Entries collection
70 # or nil if the given path doesn't exist in the repository
89 # or nil if the given path doesn't exist in the repository
71 # this method is used by the repository-browser (aka LIST)
90 # this method is used by the repository-browser (aka LIST)
72 def entries(path=nil, identifier=nil)
91 def entries(path=nil, identifier=nil)
73 logger.debug "<cvs> entries '#{path}' with identifier '#{identifier}'"
92 logger.debug "<cvs> entries '#{path}' with identifier '#{identifier}'"
74 path_with_project="#{url}#{with_leading_slash(path)}"
93 path_with_project="#{url}#{with_leading_slash(path)}"
75 entries = Entries.new
94 entries = Entries.new
76 cmd = "#{self.class.sq_bin} -d #{shell_quote root_url} rls -e"
95 cmd = "#{self.class.sq_bin} -d #{shell_quote root_url} rls -e"
77 cmd << " -D \"#{time_to_cvstime(identifier)}\"" if identifier
96 cmd << " -D \"#{time_to_cvstime(identifier)}\"" if identifier
78 cmd << " #{shell_quote path_with_project}"
97 cmd << " #{shell_quote path_with_project}"
79 shellout(cmd) do |io|
98 shellout(cmd) do |io|
80 io.each_line(){|line|
99 io.each_line(){|line|
81 fields=line.chop.split('/',-1)
100 fields=line.chop.split('/',-1)
82 logger.debug(">>InspectLine #{fields.inspect}")
101 logger.debug(">>InspectLine #{fields.inspect}")
83
102
84 if fields[0]!="D"
103 if fields[0]!="D"
85 entries << Entry.new({:name => fields[-5],
104 entries << Entry.new({:name => fields[-5],
86 #:path => fields[-4].include?(path)?fields[-4]:(path + "/"+ fields[-4]),
105 #:path => fields[-4].include?(path)?fields[-4]:(path + "/"+ fields[-4]),
87 :path => "#{path}/#{fields[-5]}",
106 :path => "#{path}/#{fields[-5]}",
88 :kind => 'file',
107 :kind => 'file',
89 :size => nil,
108 :size => nil,
90 :lastrev => Revision.new({
109 :lastrev => Revision.new({
91 :revision => fields[-4],
110 :revision => fields[-4],
92 :name => fields[-4],
111 :name => fields[-4],
93 :time => Time.parse(fields[-3]),
112 :time => Time.parse(fields[-3]),
94 :author => ''
113 :author => ''
95 })
114 })
96 })
115 })
97 else
116 else
98 entries << Entry.new({:name => fields[1],
117 entries << Entry.new({:name => fields[1],
99 :path => "#{path}/#{fields[1]}",
118 :path => "#{path}/#{fields[1]}",
100 :kind => 'dir',
119 :kind => 'dir',
101 :size => nil,
120 :size => nil,
102 :lastrev => nil
121 :lastrev => nil
103 })
122 })
104 end
123 end
105 }
124 }
106 end
125 end
107 return nil if $? && $?.exitstatus != 0
126 return nil if $? && $?.exitstatus != 0
108 entries.sort_by_name
127 entries.sort_by_name
109 end
128 end
110
129
111 STARTLOG="----------------------------"
130 STARTLOG="----------------------------"
112 ENDLOG ="============================================================================="
131 ENDLOG ="============================================================================="
113
132
114 # Returns all revisions found between identifier_from and identifier_to
133 # Returns all revisions found between identifier_from and identifier_to
115 # in the repository. both identifier have to be dates or nil.
134 # in the repository. both identifier have to be dates or nil.
116 # these method returns nothing but yield every result in block
135 # these method returns nothing but yield every result in block
117 def revisions(path=nil, identifier_from=nil, identifier_to=nil, options={}, &block)
136 def revisions(path=nil, identifier_from=nil, identifier_to=nil, options={}, &block)
118 logger.debug "<cvs> revisions path:'#{path}',identifier_from #{identifier_from}, identifier_to #{identifier_to}"
137 logger.debug "<cvs> revisions path:'#{path}',identifier_from #{identifier_from}, identifier_to #{identifier_to}"
119
138
120 path_with_project="#{url}#{with_leading_slash(path)}"
139 path_with_project="#{url}#{with_leading_slash(path)}"
121 cmd = "#{self.class.sq_bin} -d #{shell_quote root_url} rlog"
140 cmd = "#{self.class.sq_bin} -d #{shell_quote root_url} rlog"
122 cmd << " -d\">#{time_to_cvstime_rlog(identifier_from)}\"" if identifier_from
141 cmd << " -d\">#{time_to_cvstime_rlog(identifier_from)}\"" if identifier_from
123 cmd << " #{shell_quote path_with_project}"
142 cmd << " #{shell_quote path_with_project}"
124 shellout(cmd) do |io|
143 shellout(cmd) do |io|
125 state="entry_start"
144 state="entry_start"
126
145
127 commit_log=String.new
146 commit_log=String.new
128 revision=nil
147 revision=nil
129 date=nil
148 date=nil
130 author=nil
149 author=nil
131 entry_path=nil
150 entry_path=nil
132 entry_name=nil
151 entry_name=nil
133 file_state=nil
152 file_state=nil
134 branch_map=nil
153 branch_map=nil
135
154
136 io.each_line() do |line|
155 io.each_line() do |line|
137
156
138 if state!="revision" && /^#{ENDLOG}/ =~ line
157 if state!="revision" && /^#{ENDLOG}/ =~ line
139 commit_log=String.new
158 commit_log=String.new
140 revision=nil
159 revision=nil
141 state="entry_start"
160 state="entry_start"
142 end
161 end
143
162
144 if state=="entry_start"
163 if state=="entry_start"
145 branch_map=Hash.new
164 branch_map=Hash.new
146 if /^RCS file: #{Regexp.escape(root_url_path)}\/#{Regexp.escape(path_with_project)}(.+),v$/ =~ line
165 if /^RCS file: #{Regexp.escape(root_url_path)}\/#{Regexp.escape(path_with_project)}(.+),v$/ =~ line
147 entry_path = normalize_cvs_path($1)
166 entry_path = normalize_cvs_path($1)
148 entry_name = normalize_path(File.basename($1))
167 entry_name = normalize_path(File.basename($1))
149 logger.debug("Path #{entry_path} <=> Name #{entry_name}")
168 logger.debug("Path #{entry_path} <=> Name #{entry_name}")
150 elsif /^head: (.+)$/ =~ line
169 elsif /^head: (.+)$/ =~ line
151 entry_headRev = $1 #unless entry.nil?
170 entry_headRev = $1 #unless entry.nil?
152 elsif /^symbolic names:/ =~ line
171 elsif /^symbolic names:/ =~ line
153 state="symbolic" #unless entry.nil?
172 state="symbolic" #unless entry.nil?
154 elsif /^#{STARTLOG}/ =~ line
173 elsif /^#{STARTLOG}/ =~ line
155 commit_log=String.new
174 commit_log=String.new
156 state="revision"
175 state="revision"
157 end
176 end
158 next
177 next
159 elsif state=="symbolic"
178 elsif state=="symbolic"
160 if /^(.*):\s(.*)/ =~ (line.strip)
179 if /^(.*):\s(.*)/ =~ (line.strip)
161 branch_map[$1]=$2
180 branch_map[$1]=$2
162 else
181 else
163 state="tags"
182 state="tags"
164 next
183 next
165 end
184 end
166 elsif state=="tags"
185 elsif state=="tags"
167 if /^#{STARTLOG}/ =~ line
186 if /^#{STARTLOG}/ =~ line
168 commit_log = ""
187 commit_log = ""
169 state="revision"
188 state="revision"
170 elsif /^#{ENDLOG}/ =~ line
189 elsif /^#{ENDLOG}/ =~ line
171 state="head"
190 state="head"
172 end
191 end
173 next
192 next
174 elsif state=="revision"
193 elsif state=="revision"
175 if /^#{ENDLOG}/ =~ line || /^#{STARTLOG}/ =~ line
194 if /^#{ENDLOG}/ =~ line || /^#{STARTLOG}/ =~ line
176 if revision
195 if revision
177
196
178 revHelper=CvsRevisionHelper.new(revision)
197 revHelper=CvsRevisionHelper.new(revision)
179 revBranch="HEAD"
198 revBranch="HEAD"
180
199
181 branch_map.each() do |branch_name,branch_point|
200 branch_map.each() do |branch_name,branch_point|
182 if revHelper.is_in_branch_with_symbol(branch_point)
201 if revHelper.is_in_branch_with_symbol(branch_point)
183 revBranch=branch_name
202 revBranch=branch_name
184 end
203 end
185 end
204 end
186
205
187 logger.debug("********** YIELD Revision #{revision}::#{revBranch}")
206 logger.debug("********** YIELD Revision #{revision}::#{revBranch}")
188
207
189 yield Revision.new({
208 yield Revision.new({
190 :time => date,
209 :time => date,
191 :author => author,
210 :author => author,
192 :message=>commit_log.chomp,
211 :message=>commit_log.chomp,
193 :paths => [{
212 :paths => [{
194 :revision => revision,
213 :revision => revision,
195 :branch=> revBranch,
214 :branch=> revBranch,
196 :path=>entry_path,
215 :path=>entry_path,
197 :name=>entry_name,
216 :name=>entry_name,
198 :kind=>'file',
217 :kind=>'file',
199 :action=>file_state
218 :action=>file_state
200 }]
219 }]
201 })
220 })
202 end
221 end
203
222
204 commit_log=String.new
223 commit_log=String.new
205 revision=nil
224 revision=nil
206
225
207 if /^#{ENDLOG}/ =~ line
226 if /^#{ENDLOG}/ =~ line
208 state="entry_start"
227 state="entry_start"
209 end
228 end
210 next
229 next
211 end
230 end
212
231
213 if /^branches: (.+)$/ =~ line
232 if /^branches: (.+)$/ =~ line
214 #TODO: version.branch = $1
233 #TODO: version.branch = $1
215 elsif /^revision (\d+(?:\.\d+)+).*$/ =~ line
234 elsif /^revision (\d+(?:\.\d+)+).*$/ =~ line
216 revision = $1
235 revision = $1
217 elsif /^date:\s+(\d+.\d+.\d+\s+\d+:\d+:\d+)/ =~ line
236 elsif /^date:\s+(\d+.\d+.\d+\s+\d+:\d+:\d+)/ =~ line
218 date = Time.parse($1)
237 date = Time.parse($1)
219 author = /author: ([^;]+)/.match(line)[1]
238 author = /author: ([^;]+)/.match(line)[1]
220 file_state = /state: ([^;]+)/.match(line)[1]
239 file_state = /state: ([^;]+)/.match(line)[1]
221 #TODO: linechanges only available in CVS.... maybe a feature our SVN implementation. i'm sure, they are
240 #TODO: linechanges only available in CVS.... maybe a feature our SVN implementation. i'm sure, they are
222 # useful for stats or something else
241 # useful for stats or something else
223 # linechanges =/lines: \+(\d+) -(\d+)/.match(line)
242 # linechanges =/lines: \+(\d+) -(\d+)/.match(line)
224 # unless linechanges.nil?
243 # unless linechanges.nil?
225 # version.line_plus = linechanges[1]
244 # version.line_plus = linechanges[1]
226 # version.line_minus = linechanges[2]
245 # version.line_minus = linechanges[2]
227 # else
246 # else
228 # version.line_plus = 0
247 # version.line_plus = 0
229 # version.line_minus = 0
248 # version.line_minus = 0
230 # end
249 # end
231 else
250 else
232 commit_log << line unless line =~ /^\*\*\* empty log message \*\*\*/
251 commit_log << line unless line =~ /^\*\*\* empty log message \*\*\*/
233 end
252 end
234 end
253 end
235 end
254 end
236 end
255 end
237 end
256 end
238
257
239 def diff(path, identifier_from, identifier_to=nil)
258 def diff(path, identifier_from, identifier_to=nil)
240 logger.debug "<cvs> diff path:'#{path}',identifier_from #{identifier_from}, identifier_to #{identifier_to}"
259 logger.debug "<cvs> diff path:'#{path}',identifier_from #{identifier_from}, identifier_to #{identifier_to}"
241 path_with_project="#{url}#{with_leading_slash(path)}"
260 path_with_project="#{url}#{with_leading_slash(path)}"
242 cmd = "#{self.class.sq_bin} -d #{shell_quote root_url} rdiff -u -r#{identifier_to} -r#{identifier_from} #{shell_quote path_with_project}"
261 cmd = "#{self.class.sq_bin} -d #{shell_quote root_url} rdiff -u -r#{identifier_to} -r#{identifier_from} #{shell_quote path_with_project}"
243 diff = []
262 diff = []
244 shellout(cmd) do |io|
263 shellout(cmd) do |io|
245 io.each_line do |line|
264 io.each_line do |line|
246 diff << line
265 diff << line
247 end
266 end
248 end
267 end
249 return nil if $? && $?.exitstatus != 0
268 return nil if $? && $?.exitstatus != 0
250 diff
269 diff
251 end
270 end
252
271
253 def cat(path, identifier=nil)
272 def cat(path, identifier=nil)
254 identifier = (identifier) ? identifier : "HEAD"
273 identifier = (identifier) ? identifier : "HEAD"
255 logger.debug "<cvs> cat path:'#{path}',identifier #{identifier}"
274 logger.debug "<cvs> cat path:'#{path}',identifier #{identifier}"
256 path_with_project="#{url}#{with_leading_slash(path)}"
275 path_with_project="#{url}#{with_leading_slash(path)}"
257 cmd = "#{self.class.sq_bin} -d #{shell_quote root_url} co"
276 cmd = "#{self.class.sq_bin} -d #{shell_quote root_url} co"
258 cmd << " -D \"#{time_to_cvstime(identifier)}\"" if identifier
277 cmd << " -D \"#{time_to_cvstime(identifier)}\"" if identifier
259 cmd << " -p #{shell_quote path_with_project}"
278 cmd << " -p #{shell_quote path_with_project}"
260 cat = nil
279 cat = nil
261 shellout(cmd) do |io|
280 shellout(cmd) do |io|
262 io.binmode
281 io.binmode
263 cat = io.read
282 cat = io.read
264 end
283 end
265 return nil if $? && $?.exitstatus != 0
284 return nil if $? && $?.exitstatus != 0
266 cat
285 cat
267 end
286 end
268
287
269 def annotate(path, identifier=nil)
288 def annotate(path, identifier=nil)
270 identifier = (identifier) ? identifier.to_i : "HEAD"
289 identifier = (identifier) ? identifier.to_i : "HEAD"
271 logger.debug "<cvs> annotate path:'#{path}',identifier #{identifier}"
290 logger.debug "<cvs> annotate path:'#{path}',identifier #{identifier}"
272 path_with_project="#{url}#{with_leading_slash(path)}"
291 path_with_project="#{url}#{with_leading_slash(path)}"
273 cmd = "#{self.class.sq_bin} -d #{shell_quote root_url} rannotate -r#{identifier} #{shell_quote path_with_project}"
292 cmd = "#{self.class.sq_bin} -d #{shell_quote root_url} rannotate -r#{identifier} #{shell_quote path_with_project}"
274 blame = Annotate.new
293 blame = Annotate.new
275 shellout(cmd) do |io|
294 shellout(cmd) do |io|
276 io.each_line do |line|
295 io.each_line do |line|
277 next unless line =~ %r{^([\d\.]+)\s+\(([^\)]+)\s+[^\)]+\):\s(.*)$}
296 next unless line =~ %r{^([\d\.]+)\s+\(([^\)]+)\s+[^\)]+\):\s(.*)$}
278 blame.add_line($3.rstrip, Revision.new(:revision => $1, :author => $2.strip))
297 blame.add_line($3.rstrip, Revision.new(:revision => $1, :author => $2.strip))
279 end
298 end
280 end
299 end
281 return nil if $? && $?.exitstatus != 0
300 return nil if $? && $?.exitstatus != 0
282 blame
301 blame
283 end
302 end
284
303
285 private
304 private
286
305
287 # Returns the root url without the connexion string
306 # Returns the root url without the connexion string
288 # :pserver:anonymous@foo.bar:/path => /path
307 # :pserver:anonymous@foo.bar:/path => /path
289 # :ext:cvsservername:/path => /path
308 # :ext:cvsservername:/path => /path
290 def root_url_path
309 def root_url_path
291 root_url.to_s.gsub(/^:.+:\d*/, '')
310 root_url.to_s.gsub(/^:.+:\d*/, '')
292 end
311 end
293
312
294 # convert a date/time into the CVS-format
313 # convert a date/time into the CVS-format
295 def time_to_cvstime(time)
314 def time_to_cvstime(time)
296 return nil if time.nil?
315 return nil if time.nil?
297 return Time.now if time == 'HEAD'
316 return Time.now if time == 'HEAD'
298
317
299 unless time.kind_of? Time
318 unless time.kind_of? Time
300 time = Time.parse(time)
319 time = Time.parse(time)
301 end
320 end
302 return time.strftime("%Y-%m-%d %H:%M:%S")
321 return time.strftime("%Y-%m-%d %H:%M:%S")
303 end
322 end
304
323
305 def time_to_cvstime_rlog(time)
324 def time_to_cvstime_rlog(time)
306 return nil if time.nil?
325 return nil if time.nil?
307 t1 = time.clone.localtime
326 t1 = time.clone.localtime
308 return t1.strftime("%Y-%m-%d %H:%M:%S")
327 return t1.strftime("%Y-%m-%d %H:%M:%S")
309 end
328 end
310
329
311 def normalize_cvs_path(path)
330 def normalize_cvs_path(path)
312 normalize_path(path.gsub(/Attic\//,''))
331 normalize_path(path.gsub(/Attic\//,''))
313 end
332 end
314
333
315 def normalize_path(path)
334 def normalize_path(path)
316 path.sub(/^(\/)*(.*)/,'\2').sub(/(.*)(,v)+/,'\1')
335 path.sub(/^(\/)*(.*)/,'\2').sub(/(.*)(,v)+/,'\1')
317 end
336 end
318 end
337 end
319
338
320 class CvsRevisionHelper
339 class CvsRevisionHelper
321 attr_accessor :complete_rev, :revision, :base, :branchid
340 attr_accessor :complete_rev, :revision, :base, :branchid
322
341
323 def initialize(complete_rev)
342 def initialize(complete_rev)
324 @complete_rev = complete_rev
343 @complete_rev = complete_rev
325 parseRevision()
344 parseRevision()
326 end
345 end
327
346
328 def branchPoint
347 def branchPoint
329 return @base
348 return @base
330 end
349 end
331
350
332 def branchVersion
351 def branchVersion
333 if isBranchRevision
352 if isBranchRevision
334 return @base+"."+@branchid
353 return @base+"."+@branchid
335 end
354 end
336 return @base
355 return @base
337 end
356 end
338
357
339 def isBranchRevision
358 def isBranchRevision
340 !@branchid.nil?
359 !@branchid.nil?
341 end
360 end
342
361
343 def prevRev
362 def prevRev
344 unless @revision==0
363 unless @revision==0
345 return buildRevision(@revision-1)
364 return buildRevision(@revision-1)
346 end
365 end
347 return buildRevision(@revision)
366 return buildRevision(@revision)
348 end
367 end
349
368
350 def is_in_branch_with_symbol(branch_symbol)
369 def is_in_branch_with_symbol(branch_symbol)
351 bpieces=branch_symbol.split(".")
370 bpieces=branch_symbol.split(".")
352 branch_start="#{bpieces[0..-3].join(".")}.#{bpieces[-1]}"
371 branch_start="#{bpieces[0..-3].join(".")}.#{bpieces[-1]}"
353 return (branchVersion==branch_start)
372 return (branchVersion==branch_start)
354 end
373 end
355
374
356 private
375 private
357 def buildRevision(rev)
376 def buildRevision(rev)
358 if rev== 0
377 if rev== 0
359 @base
378 @base
360 elsif @branchid.nil?
379 elsif @branchid.nil?
361 @base+"."+rev.to_s
380 @base+"."+rev.to_s
362 else
381 else
363 @base+"."+@branchid+"."+rev.to_s
382 @base+"."+@branchid+"."+rev.to_s
364 end
383 end
365 end
384 end
366
385
367 # Interpretiert die cvs revisionsnummern wie z.b. 1.14 oder 1.3.0.15
386 # Interpretiert die cvs revisionsnummern wie z.b. 1.14 oder 1.3.0.15
368 def parseRevision()
387 def parseRevision()
369 pieces=@complete_rev.split(".")
388 pieces=@complete_rev.split(".")
370 @revision=pieces.last.to_i
389 @revision=pieces.last.to_i
371 baseSize=1
390 baseSize=1
372 baseSize+=(pieces.size/2)
391 baseSize+=(pieces.size/2)
373 @base=pieces[0..-baseSize].join(".")
392 @base=pieces[0..-baseSize].join(".")
374 if baseSize > 2
393 if baseSize > 2
375 @branchid=pieces[-2]
394 @branchid=pieces[-2]
376 end
395 end
377 end
396 end
378 end
397 end
379 end
398 end
380 end
399 end
381 end
400 end
@@ -1,43 +1,59
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 CvsAdapterTest < ActiveSupport::TestCase
5 class CvsAdapterTest < ActiveSupport::TestCase
6
6
7 REPOSITORY_PATH = RAILS_ROOT.gsub(%r{config\/\.\.}, '') + '/tmp/test/cvs_repository'
7 REPOSITORY_PATH = RAILS_ROOT.gsub(%r{config\/\.\.}, '') + '/tmp/test/cvs_repository'
8 REPOSITORY_PATH.gsub!(/\//, "\\") if Redmine::Platform.mswin?
8 REPOSITORY_PATH.gsub!(/\//, "\\") if Redmine::Platform.mswin?
9 MODULE_NAME = 'test'
9 MODULE_NAME = 'test'
10
10
11 if File.directory?(REPOSITORY_PATH)
11 if File.directory?(REPOSITORY_PATH)
12 def setup
12 def setup
13 @adapter = Redmine::Scm::Adapters::CvsAdapter.new(MODULE_NAME, REPOSITORY_PATH)
13 @adapter = Redmine::Scm::Adapters::CvsAdapter.new(MODULE_NAME, REPOSITORY_PATH)
14 end
14 end
15
15
16 def test_scm_version
17 to_test = { "\nConcurrent Versions System (CVS) 1.12.13 (client/server)\n" => [1,12,13],
18 "\r\n1.12.12\r\n1.12.11" => [1,12,12],
19 "1.12.11\r\n1.12.10\r\n" => [1,12,11]}
20 to_test.each do |s, v|
21 test_scm_version_for(s, v)
22 end
23 end
24
16 def test_revisions_all
25 def test_revisions_all
17 cnt = 0
26 cnt = 0
18 @adapter.revisions('', nil, nil, :with_paths => true) do |revision|
27 @adapter.revisions('', nil, nil, :with_paths => true) do |revision|
19 cnt += 1
28 cnt += 1
20 end
29 end
21 assert_equal 14, cnt
30 assert_equal 14, cnt
22 end
31 end
23
32
24 def test_revisions_from_rev3
33 def test_revisions_from_rev3
25 rev3_committed_on = Time.gm(2007, 12, 13, 16, 27, 22)
34 rev3_committed_on = Time.gm(2007, 12, 13, 16, 27, 22)
26 cnt = 0
35 cnt = 0
27 @adapter.revisions('', rev3_committed_on, nil, :with_paths => true) do |revision|
36 @adapter.revisions('', rev3_committed_on, nil, :with_paths => true) do |revision|
28 cnt += 1
37 cnt += 1
29 end
38 end
30 assert_equal 2, cnt
39 assert_equal 2, cnt
31 end
40 end
41
42 private
43
44 def test_scm_version_for(scm_command_version, version)
45 @adapter.class.expects(:scm_version_from_command_line).returns(scm_command_version)
46 assert_equal version, @adapter.class.scm_command_version
47 end
32 else
48 else
33 puts "Cvs test repository NOT FOUND. Skipping unit tests !!!"
49 puts "Cvs test repository NOT FOUND. Skipping unit tests !!!"
34 def test_fake; assert true end
50 def test_fake; assert true end
35 end
51 end
36 end
52 end
37
53
38 rescue LoadError
54 rescue LoadError
39 class CvsMochaFake < ActiveSupport::TestCase
55 class CvsMochaFake < ActiveSupport::TestCase
40 def test_fake; assert(false, "Requires mocha to run those tests") end
56 def test_fake; assert(false, "Requires mocha to run those tests") end
41 end
57 end
42 end
58 end
43
59
General Comments 0
You need to be logged in to leave comments. Login now