rscm-accurev 0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +25 -0
- data/README +9 -0
- data/Rakefile +137 -0
- data/STATUS +63 -0
- data/TODO +43 -0
- data/apitest.rb +21 -0
- data/bumprelease.sh +13 -0
- data/lib/rscm/accurev.rb +18 -0
- data/lib/rscm/scm/accurev/api.rb +411 -0
- data/lib/rscm/scm/accurev/api.rb.mine +382 -0
- data/lib/rscm/scm/accurev/api.rb.r263 +364 -0
- data/lib/rscm/scm/accurev/api.rb.r265 +393 -0
- data/lib/rscm/scm/accurev/command.rb +151 -0
- data/lib/rscm/scm/accurev/exception.rb +38 -0
- data/lib/rscm/scm/accurev/filterio.rb +57 -0
- data/lib/rscm/scm/accurev/xml.rb +224 -0
- data/lib/test/unit/ui/xml/testrunner.rb +165 -0
- data/lib/test/unit/ui/xml/xmltestrunner.xslt +79 -0
- data/test/acreplay.rb +22 -0
- data/test/coverage/analyzer.rb +127 -0
- data/test/coverage/c_loader.rb +34 -0
- data/test/coverage/cover.rb +91 -0
- data/test/coverage/coverage_loader.rb +21 -0
- data/test/coverage/coveragetask.rb +38 -0
- data/test/coverage/index_tmpl.html +42 -0
- data/test/coverage/template.html +36 -0
- data/test/eg/ac-files.xml +172 -0
- data/test/eg/ac-pop.txt +195 -0
- data/test/eg/files-various-states.xml +188 -0
- data/test/eg/hist-oneweek-all.xml +1483 -0
- data/test/eg/hist-oneweek-external.xml +246 -0
- data/test/eg/hist-oneweek-promotes.xml +1092 -0
- data/test/eg/info.txt +14 -0
- data/test/eg/stat-a-various.xml +1789 -0
- data/test/eg/stat-m.xml +13 -0
- data/test/eg/stat-overlap.xml +13 -0
- data/test/eg/stat-x.xml +20 -0
- data/test/eg/update-i-mods-and-updates-and-overlap.xml +73 -0
- data/test/eg/update-i-nochanges.xml +8 -0
- data/test/eg/update-i-stale.xml +0 -0
- data/test/eg/update-i-updates.xml +125 -0
- data/test/eg/update-newwksp.xml +183 -0
- data/test/eg/update-nochanges.xml +7 -0
- data/test/eg/update-stale.xml +12 -0
- data/test/eg/update-updates.xml +147 -0
- data/test/t_api.rb +163 -0
- data/test/t_command.rb +85 -0
- data/test/t_filterio.rb +60 -0
- data/test/t_load.rb +11 -0
- data/test/t_scrubio.rb +117 -0
- data/test/t_xmlmapper.rb +75 -0
- metadata +106 -0
@@ -0,0 +1,38 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
#
|
3
|
+
# = exception.rb
|
4
|
+
#
|
5
|
+
# Exception classes for RSCM::Accurev
|
6
|
+
#
|
7
|
+
# Includes:
|
8
|
+
#
|
9
|
+
# * AccurevException
|
10
|
+
#
|
11
|
+
# * StaleWorkspaceException
|
12
|
+
#
|
13
|
+
module RSCM
|
14
|
+
module Accurev
|
15
|
+
|
16
|
+
#
|
17
|
+
# General exception class for errors processing commands.
|
18
|
+
#
|
19
|
+
# @attr error_msg Error message output by accurev.
|
20
|
+
#
|
21
|
+
class AccurevException < Exception
|
22
|
+
attr_reader :error_msg
|
23
|
+
def initialize( msg, error_msg=nil )
|
24
|
+
super( "#{msg}: #{error_msg}" )
|
25
|
+
@error_msg = error_msg
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
#
|
30
|
+
# Exception thrown when an update is performed
|
31
|
+
# on a workspace with unkept/unanchored modifications.
|
32
|
+
# Accurev requires that all modifications be handled before
|
33
|
+
# an update is performed.
|
34
|
+
#
|
35
|
+
class StaleWorkspaceException < AccurevException; end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'stringio'
|
2
|
+
|
3
|
+
module RSCM
|
4
|
+
module Accurev
|
5
|
+
|
6
|
+
#
|
7
|
+
# IO stream which cleans irregularities out of accurev xml output.
|
8
|
+
#
|
9
|
+
# In particular:
|
10
|
+
#
|
11
|
+
# * Unlike other commands, the +-fx+ output from _accurev update_
|
12
|
+
# has a root element of +<acResponse>+, not +<AcResponse>+.
|
13
|
+
#
|
14
|
+
# * _accurev update_ emits broken XML when there are no files
|
15
|
+
# to update.
|
16
|
+
#
|
17
|
+
class AcXMLScrubIO < StringIO
|
18
|
+
|
19
|
+
# @param: sourceio - io or string source to wrap
|
20
|
+
def initialize( sourceio )
|
21
|
+
if sourceio.respond_to?( :read )
|
22
|
+
super( clean( sourceio.read ) )
|
23
|
+
else
|
24
|
+
super( clean(sourceio) )
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def clean( source )
|
29
|
+
File.open( "/tmp/scrubio.outA", "w" ).puts( source )
|
30
|
+
source.gsub!( /<acResponse/, "<AcResponse" )
|
31
|
+
source.gsub!( /<\/acResponse/, "<\/AcResponse" )
|
32
|
+
File.open( "/tmp/scrubio.outB", "w" ).puts( source )
|
33
|
+
source.gsub!( /command="update"[^>]*(>?)/, 'command="update"\1' )
|
34
|
+
File.open( "/tmp/scrubio.outC", "w" ).puts( source )
|
35
|
+
return source
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
#
|
41
|
+
# Simple filtering IO implementation.
|
42
|
+
#
|
43
|
+
# io = RSCM::Accurev::FilterIO.new( otherio ) do |string|
|
44
|
+
# string.gsub( "2", "fifty-one" )
|
45
|
+
# end
|
46
|
+
#
|
47
|
+
class FilterIO < StringIO
|
48
|
+
def initialize( sourceio, &filter )
|
49
|
+
str = filter.call( sourceio.read )
|
50
|
+
super( str )
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
@@ -0,0 +1,224 @@
|
|
1
|
+
module RSCM; module Accurev; end; end
|
2
|
+
|
3
|
+
require 'rexml/element'
|
4
|
+
|
5
|
+
module RSCM::Accurev
|
6
|
+
|
7
|
+
#
|
8
|
+
# XMLMapper creates an object tree from an XML element and its children.
|
9
|
+
#
|
10
|
+
# For each XML node, it locates a class associated with that node's name,
|
11
|
+
# and builds a new object of that type from the element.
|
12
|
+
#
|
13
|
+
# The nodename-class associations are determined by finding all
|
14
|
+
# classes derived from ElementBackedClass and checking
|
15
|
+
# each of those classes +element_name+ methods.
|
16
|
+
#
|
17
|
+
# See ElementBackedClass for examples.
|
18
|
+
#
|
19
|
+
class XMLMapper
|
20
|
+
|
21
|
+
# Map of element (tag) names to (ruby) class.
|
22
|
+
attr_accessor :classmap
|
23
|
+
|
24
|
+
def initialize()
|
25
|
+
@classmap = {}
|
26
|
+
ObjectSpace.each_object( Class ) do |k|
|
27
|
+
if k.ancestors.delete( ElementBackedClass ) and k != ElementBackedClass
|
28
|
+
if k.respond_to?( :element_name )
|
29
|
+
@classmap[ k.element_name ] = k
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
#
|
36
|
+
# Build an object tree from the given REXML element and its children.
|
37
|
+
# The returned object will be a subclass of +ElementBackedClass+.
|
38
|
+
#
|
39
|
+
def map( e )
|
40
|
+
unless @classmap.has_key?( e.name )
|
41
|
+
return nil
|
42
|
+
end
|
43
|
+
o = @classmap[e.name].new( e )
|
44
|
+
e.children.each do |child|
|
45
|
+
if child.kind_of?( REXML::Element )
|
46
|
+
a = self.map( child )
|
47
|
+
unless a.nil?
|
48
|
+
if o.children[ a.class.element_name ].nil?
|
49
|
+
o.children[ a.class.element_name ] = []
|
50
|
+
end
|
51
|
+
o.children[ a.class.element_name ] << a
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
return o
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
#
|
60
|
+
# Abstract base class for defining typed mirror classes for XML elements.
|
61
|
+
#
|
62
|
+
# A subclass of ElementBackedClass has:
|
63
|
+
#
|
64
|
+
# * A class static +element_name+ method, identifying what
|
65
|
+
# type of XML element it shadows
|
66
|
+
#
|
67
|
+
# * A set of attributes shadowing the XML attributes of its target element
|
68
|
+
#
|
69
|
+
# * A map (+children+) of child elements, keyed by element name
|
70
|
+
#
|
71
|
+
# * A (optional) +text_content+ attribute, populated when the
|
72
|
+
# target element has text subnodes.
|
73
|
+
#
|
74
|
+
# ---
|
75
|
+
#
|
76
|
+
# === Example
|
77
|
+
#
|
78
|
+
# For the XML element:
|
79
|
+
#
|
80
|
+
# <book title="The Monkey Wrench Gang" author="Edward Albee">
|
81
|
+
# <note>Lots of beer and dynamite</note>
|
82
|
+
# </book>
|
83
|
+
#
|
84
|
+
# You can define:
|
85
|
+
#
|
86
|
+
# class Book < ElementBackedClass
|
87
|
+
# element_name 'book'
|
88
|
+
# attr_accessor :title, :author
|
89
|
+
# end
|
90
|
+
#
|
91
|
+
# And then use XMLMapper to create instances:
|
92
|
+
#
|
93
|
+
# e = ...; # <book> node: REXML::Element
|
94
|
+
# book = XMLMapper.map(e)
|
95
|
+
# puts "Title: #{book.title}"
|
96
|
+
# puts "Author: #{book.author}"
|
97
|
+
# puts "Notes:"
|
98
|
+
# book['note'].each do |n|
|
99
|
+
# puts n.text_content
|
100
|
+
# end
|
101
|
+
#
|
102
|
+
class ElementBackedClass
|
103
|
+
|
104
|
+
attr_accessor :children
|
105
|
+
|
106
|
+
def self.element_name( name )
|
107
|
+
class << self
|
108
|
+
self
|
109
|
+
end.send( :define_method, "element_name" ) {name}
|
110
|
+
end
|
111
|
+
|
112
|
+
# currently advisory only
|
113
|
+
def self.children( *kidtypes )
|
114
|
+
# nothing XXX
|
115
|
+
end
|
116
|
+
|
117
|
+
def initialize( element=nil )
|
118
|
+
@children = {}
|
119
|
+
unless element.nil?
|
120
|
+
self.set_from_element(element)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
# ebc['foo'] is a synonym for ebc.children['foo']
|
125
|
+
def []( key )
|
126
|
+
return self.children[key]
|
127
|
+
end
|
128
|
+
|
129
|
+
def to_s
|
130
|
+
s = "<#{self.class.element_name} "
|
131
|
+
self.instance_variables.each do |a|
|
132
|
+
unless a == "@children"
|
133
|
+
attr = a.sub( /^@/, "" )
|
134
|
+
s += "#{attr}='#{self.send(attr)}' "
|
135
|
+
end
|
136
|
+
end
|
137
|
+
s += "/>"
|
138
|
+
return s
|
139
|
+
end
|
140
|
+
|
141
|
+
protected
|
142
|
+
# Does the work of calling object setters for each XML attribute.
|
143
|
+
def set_from_element( e )
|
144
|
+
e.attributes.each do |attr, value|
|
145
|
+
# downcase the attr name for method-happiness
|
146
|
+
# (also hacks around some inconsistent accurev attr names...)
|
147
|
+
mattr = attr.downcase
|
148
|
+
if self.respond_to?( "#{mattr}=" )
|
149
|
+
self.send( "#{mattr}=", value )
|
150
|
+
end
|
151
|
+
end
|
152
|
+
if self.respond_to?( :text_content= ) and ! e.text.nil?
|
153
|
+
self.text_content = e.text
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
end
|
158
|
+
|
159
|
+
#
|
160
|
+
# Data from the typical AcResponse root node.
|
161
|
+
#
|
162
|
+
class AcResponseData < ElementBackedClass
|
163
|
+
element_name 'AcResponse'
|
164
|
+
attr_accessor :command, :directory
|
165
|
+
|
166
|
+
# Returns this response's error message, or nil if no error detected.
|
167
|
+
def error
|
168
|
+
if self.children['message']
|
169
|
+
zmsg = self.children['message'][0]
|
170
|
+
if zmsg.error and zmsg.error == 'true'
|
171
|
+
return zmsg.text_content
|
172
|
+
end
|
173
|
+
else
|
174
|
+
return nil
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
#
|
180
|
+
# Data from "accurev file", "accure update", "accurev stat" commands.
|
181
|
+
# Identifies workspace objects (files, directories, etc).
|
182
|
+
#
|
183
|
+
class ElementData < ElementBackedClass
|
184
|
+
element_name 'element'
|
185
|
+
attr_accessor :location, :status, :dir, :id
|
186
|
+
attr_accessor :elemType, :namedVersion, :virtual, :real
|
187
|
+
end
|
188
|
+
|
189
|
+
#
|
190
|
+
# Data from "accurev {file,update,stat}" commands.
|
191
|
+
# Streamed status/info messages, usually interleaved with ElementData
|
192
|
+
# nodes.
|
193
|
+
#
|
194
|
+
class MessageData < ElementBackedClass
|
195
|
+
element_name 'message'
|
196
|
+
attr_accessor :error, :text_content
|
197
|
+
end
|
198
|
+
|
199
|
+
#
|
200
|
+
# Version (element) records in a history transaction
|
201
|
+
#
|
202
|
+
class VersionData < ElementBackedClass
|
203
|
+
element_name 'version'
|
204
|
+
attr_accessor :path, :eid, :virtual, :real, :elem_type
|
205
|
+
end
|
206
|
+
|
207
|
+
#
|
208
|
+
# Commit messages on transactions
|
209
|
+
#
|
210
|
+
class CommentData < ElementBackedClass
|
211
|
+
element_name 'comment'
|
212
|
+
end
|
213
|
+
|
214
|
+
#
|
215
|
+
# Data from "accurev hist" - transaction groups
|
216
|
+
# Contains one or more VersionData.
|
217
|
+
#
|
218
|
+
class TransactionData < ElementBackedClass
|
219
|
+
element_name 'transaction'
|
220
|
+
children VersionData, CommentData
|
221
|
+
attr_accessor :id, :type, :time, :user
|
222
|
+
end
|
223
|
+
|
224
|
+
end
|
@@ -0,0 +1,165 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'test/unit/ui/testrunnermediator'
|
4
|
+
require 'test/unit/ui/testrunnerutilities'
|
5
|
+
require 'test/unit/ui/console/testrunner'
|
6
|
+
require 'test/unit/autorunner'
|
7
|
+
require 'rexml/document'
|
8
|
+
|
9
|
+
module Test
|
10
|
+
module Unit
|
11
|
+
module UI
|
12
|
+
module XML
|
13
|
+
|
14
|
+
#
|
15
|
+
# XML::TestRunner - generate xml output for test results
|
16
|
+
#
|
17
|
+
# Example use:
|
18
|
+
#
|
19
|
+
# $ ruby -rtest/unit/ui/xml/testrunner test/test_1.rb --runner=xml
|
20
|
+
#
|
21
|
+
# By default, XML::TestRunner will output to stdout.
|
22
|
+
# You can set the environment variable $XMLTEST_OUTPUT to
|
23
|
+
# a filename to send the output to that file.
|
24
|
+
#
|
25
|
+
# The summary file created by this testrunner is XML, but
|
26
|
+
# this module also includes a stylesheet
|
27
|
+
# (test/unit/ui/xml/xmltestrunner.xslt) which converts it to
|
28
|
+
# HTML. Copy the XSLT file into the same directory as the
|
29
|
+
# test results file, and open the results file with a browser.
|
30
|
+
#
|
31
|
+
# ---
|
32
|
+
#
|
33
|
+
# (todo: use with rake)
|
34
|
+
#
|
35
|
+
class TestRunner < Test::Unit::UI::Console::TestRunner
|
36
|
+
|
37
|
+
def initialize( suite, output_level )
|
38
|
+
super( suite )
|
39
|
+
if ENV['XMLTEST_OUTPUT']
|
40
|
+
fn = ENV['XMLTEST_OUTPUT']
|
41
|
+
puts "Writing to #{fn}"
|
42
|
+
@io = File.open( fn, "w" )
|
43
|
+
@using_stdout = false
|
44
|
+
else
|
45
|
+
puts "Writing to stdout (along with everyone else...)"
|
46
|
+
@io = STDOUT
|
47
|
+
@using_stdout = true
|
48
|
+
end
|
49
|
+
create_document()
|
50
|
+
end
|
51
|
+
|
52
|
+
def create_document()
|
53
|
+
@doc = REXML::Document.new()
|
54
|
+
@doc << REXML::XMLDecl.new()
|
55
|
+
|
56
|
+
pi = REXML::Instruction.new(
|
57
|
+
"xml-stylesheet",
|
58
|
+
"type='text/xsl' href='xmltestrunner.xslt' "
|
59
|
+
)
|
60
|
+
@doc << pi
|
61
|
+
|
62
|
+
e = REXML::Element.new( "testsuite" )
|
63
|
+
e.attributes['rundate'] = Time.now
|
64
|
+
@doc << e
|
65
|
+
end
|
66
|
+
|
67
|
+
def to_s
|
68
|
+
@doc.to_s
|
69
|
+
end
|
70
|
+
|
71
|
+
def start
|
72
|
+
@current_test = nil
|
73
|
+
# setup_mediator
|
74
|
+
@mediator = TestRunnerMediator.new( @suite )
|
75
|
+
suite_name = @suite.to_s
|
76
|
+
if @suite.kind_of?(Module)
|
77
|
+
suite_name = @suite.name
|
78
|
+
end
|
79
|
+
@doc.root.attributes['name'] = suite_name
|
80
|
+
# attach_to_mediator - define callbacks
|
81
|
+
@mediator.add_listener( TestResult::FAULT,
|
82
|
+
&method(:add_fault) )
|
83
|
+
@mediator.add_listener( TestRunnerMediator::STARTED,
|
84
|
+
&method(:started) )
|
85
|
+
@mediator.add_listener( TestRunnerMediator::FINISHED,
|
86
|
+
&method(:finished) )
|
87
|
+
@mediator.add_listener( TestCase::STARTED,
|
88
|
+
&method(:test_started) )
|
89
|
+
@mediator.add_listener( TestCase::FINISHED,
|
90
|
+
&method(:test_finished) )
|
91
|
+
# return start_mediator
|
92
|
+
return @mediator.run_suite
|
93
|
+
end
|
94
|
+
|
95
|
+
# callbacks
|
96
|
+
|
97
|
+
def add_fault( fault )
|
98
|
+
##STDERR.puts "Fault:"
|
99
|
+
@faults << fault
|
100
|
+
e = REXML::Element.new( "fault" )
|
101
|
+
e << REXML::CData.new( fault.long_display )
|
102
|
+
@current_test << e
|
103
|
+
end
|
104
|
+
|
105
|
+
def started( result )
|
106
|
+
#STDERR.puts "Started"
|
107
|
+
@result = result
|
108
|
+
end
|
109
|
+
|
110
|
+
def finished( elapsed_time )
|
111
|
+
#STDERR.puts "Finished"
|
112
|
+
res = REXML::Element.new( "result" )
|
113
|
+
summ = REXML::Element.new( "summary" )
|
114
|
+
summ.text = @result
|
115
|
+
res << summ
|
116
|
+
# @result is a Test::Unit::TestResults
|
117
|
+
res.attributes['passed'] = @result.passed?
|
118
|
+
res.attributes['testcount'] = @result.run_count
|
119
|
+
res.attributes['assertcount'] = @result.assertion_count
|
120
|
+
res.attributes['failures'] = @result.failure_count
|
121
|
+
res.attributes['errors'] = @result.error_count
|
122
|
+
@doc.root << res
|
123
|
+
|
124
|
+
e = REXML::Element.new( "elapsed-time" )
|
125
|
+
e.text = elapsed_time
|
126
|
+
@doc.root << e
|
127
|
+
@io.puts( @doc.to_s )
|
128
|
+
|
129
|
+
unless @using_stdout
|
130
|
+
puts @result
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def test_started( name )
|
135
|
+
#STDERR.puts "Test: #{name} started"
|
136
|
+
e = REXML::Element.new( "test" )
|
137
|
+
e.attributes['name'] = name
|
138
|
+
#e.attributes['status'] = "failed"
|
139
|
+
@doc.root << e
|
140
|
+
@current_test = e
|
141
|
+
end
|
142
|
+
|
143
|
+
def test_finished( name )
|
144
|
+
#STDERR.puts "Test: #{name} finished"
|
145
|
+
# find //test[@name='name']
|
146
|
+
@current_test = nil
|
147
|
+
end
|
148
|
+
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
# "plug in" xmltestrunner into autorunner's list of known runners
|
156
|
+
# This enables the "--runner=xml" commandline option.
|
157
|
+
Test::Unit::AutoRunner::RUNNERS[:xml] = proc do |r|
|
158
|
+
require 'test/unit/ui/xml/testrunner'
|
159
|
+
Test::Unit::UI::XML::TestRunner
|
160
|
+
end
|
161
|
+
|
162
|
+
if __FILE__ == $0
|
163
|
+
Test::Unit::UI::XML::TestRunner.start_command_line_test
|
164
|
+
end
|
165
|
+
|