rscm-accurev 0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
+
|