spekmachine 0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +40 -0
- data/bin/spekm +20 -0
- data/lib/spekmachine/rake/spectask.rb +170 -0
- data/lib/spekmachine/runner/context.rb +152 -0
- data/lib/spekmachine/runner/context_eval.rb +121 -0
- data/lib/spekmachine/runner/context_runner.rb +62 -0
- data/lib/spekmachine/runner/kernel_ext.rb +19 -0
- data/lib/spekmachine/runner/path.rb +213 -0
- data/lib/spekmachine.rb +5 -0
- metadata +60 -0
data/Rakefile
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
$:.unshift('lib')
|
2
|
+
require 'rubygems'
|
3
|
+
require 'rake/gempackagetask'
|
4
|
+
require 'rake/contrib/rubyforgepublisher'
|
5
|
+
require 'rake/clean'
|
6
|
+
require 'rake/testtask'
|
7
|
+
require 'rake/rdoctask'
|
8
|
+
|
9
|
+
PKG_NAME = "spekmachine"
|
10
|
+
PKG_VERSION = '0.1'
|
11
|
+
PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
|
12
|
+
PKG_FILES = FileList[ '[A-Z]*', 'lib/**/*.rb' ]
|
13
|
+
|
14
|
+
spec = Gem::Specification.new do |s|
|
15
|
+
s.name = PKG_NAME
|
16
|
+
s.summary = "Stateful specs"
|
17
|
+
s.version = PKG_VERSION
|
18
|
+
s.description = <<-EOF
|
19
|
+
spekmachine is a modification of RSpec, (http://rspec.rubyforge.org/) which
|
20
|
+
uses a state machine to test all transitions. It uses the Chinese Postman
|
21
|
+
algorithm to find the shortest way through all transitions.
|
22
|
+
EOF
|
23
|
+
s.files = PKG_FILES.to_a
|
24
|
+
s.require_path = 'lib'
|
25
|
+
s.has_rdoc = false
|
26
|
+
|
27
|
+
s.require_path = 'lib'
|
28
|
+
s.autorequire = 'spekmachine'
|
29
|
+
s.bindir = "bin"
|
30
|
+
s.executables = ["spekm"]
|
31
|
+
s.author = "Michiel de Mare"
|
32
|
+
s.homepage = "http://persia.rubyforge.org"
|
33
|
+
s.rubyforge_project = "persia"
|
34
|
+
s.add_dependency 'rspec' , '>= 0.6.3'
|
35
|
+
end
|
36
|
+
|
37
|
+
Rake::GemPackageTask.new(spec) do |pkg|
|
38
|
+
pkg.need_zip = true
|
39
|
+
pkg.need_tar = true
|
40
|
+
end
|
data/bin/spekm
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'rubygems'
|
3
|
+
require_gem 'rspec'
|
4
|
+
require File.expand_path(File.dirname(__FILE__) + "/../lib/spekmachine") # better stack traces this way
|
5
|
+
|
6
|
+
$context_runner = ::Spec::Runner::OptionParser.create_context_runner(ARGV, false, STDERR, STDOUT)
|
7
|
+
|
8
|
+
# If ARGV is a glob, it will actually each over each one of the matching files.
|
9
|
+
ARGV.each do |file_or_dir|
|
10
|
+
if File.directory?(file_or_dir)
|
11
|
+
Dir["#{file_or_dir}/**/*.rb"].each do |file|
|
12
|
+
require file
|
13
|
+
end
|
14
|
+
else
|
15
|
+
require file_or_dir
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
$context_runner.run(true)
|
20
|
+
|
@@ -0,0 +1,170 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# Define a task library for running RSpec contexts.
|
4
|
+
|
5
|
+
require 'rake'
|
6
|
+
require 'rake/tasklib'
|
7
|
+
|
8
|
+
module Spec
|
9
|
+
module Rake
|
10
|
+
|
11
|
+
# A Rake task that runs a set of RSpec contexts.
|
12
|
+
#
|
13
|
+
# Example:
|
14
|
+
#
|
15
|
+
# Rake::SpecTask.new do |t|
|
16
|
+
# t.warning = true
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# This will create a task that can be run with:
|
20
|
+
#
|
21
|
+
# rake spec
|
22
|
+
#
|
23
|
+
class SpecTask < ::Rake::TaskLib
|
24
|
+
|
25
|
+
# Name of spec task. (default is :spec)
|
26
|
+
attr_accessor :name
|
27
|
+
|
28
|
+
# Array of directories to be added to $LOAD_PATH before running the
|
29
|
+
# specs. Defaults to ['<the absolute path to RSpec's lib directory>']
|
30
|
+
attr_accessor :libs
|
31
|
+
|
32
|
+
# If true, requests that the specs be run with the warning flag set.
|
33
|
+
# E.g. warning=true implies "ruby -w" used to run the specs. Defaults to false.
|
34
|
+
attr_accessor :warning
|
35
|
+
|
36
|
+
# Glob pattern to match spec files. (default is 'spec/spec*.rb')
|
37
|
+
attr_accessor :pattern
|
38
|
+
|
39
|
+
# Array of commandline options to pass to RSpec. Defaults to [].
|
40
|
+
attr_accessor :spec_opts
|
41
|
+
|
42
|
+
# Where RSpec's output is written. Defaults to STDOUT.
|
43
|
+
attr_accessor :out
|
44
|
+
|
45
|
+
# Whether or not to use RCov (default is false)
|
46
|
+
# See http://eigenclass.org/hiki.rb?rcov
|
47
|
+
attr_accessor :rcov
|
48
|
+
|
49
|
+
# Array of commandline options to pass to RCov. Defaults to ['--exclude', 'lib\/spec,bin\/spec'].
|
50
|
+
# Ignored if rcov=false
|
51
|
+
attr_accessor :rcov_opts
|
52
|
+
|
53
|
+
# Directory where the RCov report is written. Defaults to "coverage"
|
54
|
+
# Ignored if rcov=false
|
55
|
+
attr_accessor :rcov_dir
|
56
|
+
|
57
|
+
# Array of commandline options to pass to ruby. Defaults to [].
|
58
|
+
attr_accessor :ruby_opts
|
59
|
+
|
60
|
+
# Whether or not to fail Rake when an error occurs (typically when specs fail).
|
61
|
+
# Defaults to true.
|
62
|
+
attr_accessor :fail_on_error
|
63
|
+
|
64
|
+
# A message to print to stdout when there are failures.
|
65
|
+
attr_accessor :failure_message
|
66
|
+
|
67
|
+
# Explicitly define the list of spec files to be included in a
|
68
|
+
# spec. +list+ is expected to be an array of file names (a
|
69
|
+
# FileList is acceptable). If both +pattern+ and +spec_files+ are
|
70
|
+
# used, then the list of spec files is the union of the two.
|
71
|
+
def spec_files=(list)
|
72
|
+
@spec_files = list
|
73
|
+
end
|
74
|
+
|
75
|
+
# Create a specing task.
|
76
|
+
def initialize(name=:spec)
|
77
|
+
@name = name
|
78
|
+
@libs = [File.expand_path(File.dirname(__FILE__) + '/../../../lib')]
|
79
|
+
@pattern = nil
|
80
|
+
@spec_files = nil
|
81
|
+
@spec_opts = []
|
82
|
+
@warning = false
|
83
|
+
@ruby_opts = []
|
84
|
+
@out = nil
|
85
|
+
@fail_on_error = true
|
86
|
+
@rcov = false
|
87
|
+
@rcov_opts = ['--exclude', 'lib\/spec,bin\/spec']
|
88
|
+
@rcov_dir = "coverage"
|
89
|
+
|
90
|
+
yield self if block_given?
|
91
|
+
@pattern = 'spec/**/*_spek.rb' if @pattern.nil? && @spec_files.nil?
|
92
|
+
define
|
93
|
+
end
|
94
|
+
|
95
|
+
def define
|
96
|
+
#raise "No spec files found." if file_list.empty?
|
97
|
+
spec_script = File.expand_path(File.dirname(__FILE__) + '/../../../bin/spekm')
|
98
|
+
|
99
|
+
lib_path = @libs.join(File::PATH_SEPARATOR)
|
100
|
+
actual_name = Hash === name ? name.keys.first : name
|
101
|
+
unless ::Rake.application.last_comment
|
102
|
+
desc "Run RSpec for #{actual_name}" + (@rcov ? " using RCov" : "")
|
103
|
+
end
|
104
|
+
task @name do
|
105
|
+
RakeFileUtils.verbose(@verbose) do
|
106
|
+
ruby_opts = @ruby_opts.clone
|
107
|
+
ruby_opts.push( "-I\"#{lib_path}\"" )
|
108
|
+
ruby_opts.push( "-S rcov" ) if @rcov
|
109
|
+
ruby_opts.push( "-w" ) if @warning
|
110
|
+
|
111
|
+
redirect = @out.nil? ? "" : " > #{@out}"
|
112
|
+
# ruby [ruby_opts] -Ilib -S rcov [rcov_opts] bin/spec -- [spec_opts] examples
|
113
|
+
# or
|
114
|
+
# ruby [ruby_opts] -Ilib bin/spec [spec_opts] examples
|
115
|
+
begin
|
116
|
+
ruby(
|
117
|
+
ruby_opts.join(" ") + " " +
|
118
|
+
rcov_option_list +
|
119
|
+
(@rcov ? %[ -o "#{@rcov_dir}" ] : "") +
|
120
|
+
'"' + spec_script + '"' + " " +
|
121
|
+
(@rcov ? "-- " : "") +
|
122
|
+
spec_option_list + " " +
|
123
|
+
file_list.collect { |fn| %["#{fn}"] }.join(' ') + " " +
|
124
|
+
redirect
|
125
|
+
)
|
126
|
+
rescue => e
|
127
|
+
puts @failure_message if @failure_message
|
128
|
+
raise e if @fail_on_error
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
if @rcov
|
134
|
+
desc "Remove rcov products for #{actual_name}"
|
135
|
+
task paste("clobber_", actual_name) do
|
136
|
+
rm_r @rcov_dir rescue nil
|
137
|
+
end
|
138
|
+
|
139
|
+
clobber_task = paste("clobber_", actual_name)
|
140
|
+
task :clobber => [clobber_task]
|
141
|
+
|
142
|
+
task actual_name => clobber_task
|
143
|
+
end
|
144
|
+
self
|
145
|
+
end
|
146
|
+
|
147
|
+
def rcov_option_list # :nodoc:
|
148
|
+
return "" unless @rcov
|
149
|
+
ENV['RCOVOPTS'] || @rcov_opts.join(" ") || ""
|
150
|
+
end
|
151
|
+
|
152
|
+
def spec_option_list # :nodoc:
|
153
|
+
ENV['RSPECOPTS'] || @spec_opts.join(" ") || ""
|
154
|
+
end
|
155
|
+
|
156
|
+
def file_list # :nodoc:
|
157
|
+
if ENV['SPEC']
|
158
|
+
FileList[ ENV['SPEC'] ]
|
159
|
+
else
|
160
|
+
result = []
|
161
|
+
result += @spec_files.to_a if @spec_files
|
162
|
+
result += FileList[ @pattern ].to_a if @pattern
|
163
|
+
FileList[result]
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
@@ -0,0 +1,152 @@
|
|
1
|
+
module Spec
|
2
|
+
module Runner
|
3
|
+
class Context
|
4
|
+
module InstanceMethods
|
5
|
+
def initialize(name, desc, &context_block)
|
6
|
+
@context_id = name
|
7
|
+
@name = desc
|
8
|
+
|
9
|
+
@context_eval_module = Module.new
|
10
|
+
@context_eval_module.extend ContextEval::ModuleMethods
|
11
|
+
@context_eval_module.include ContextEval::InstanceMethods
|
12
|
+
before_context_eval
|
13
|
+
@context_eval_module.class_eval(&context_block)
|
14
|
+
end
|
15
|
+
|
16
|
+
def context_id
|
17
|
+
@context_id
|
18
|
+
end
|
19
|
+
|
20
|
+
def before_context_eval
|
21
|
+
end
|
22
|
+
|
23
|
+
def inherit(klass)
|
24
|
+
@context_eval_module.inherit klass
|
25
|
+
end
|
26
|
+
|
27
|
+
def include(mod)
|
28
|
+
@context_eval_module.include mod
|
29
|
+
end
|
30
|
+
|
31
|
+
def goto(name, &block)
|
32
|
+
@context_eval_module.goto(name, &block)
|
33
|
+
end
|
34
|
+
|
35
|
+
def setup(&block)
|
36
|
+
@context_eval_module.setup(&block)
|
37
|
+
end
|
38
|
+
|
39
|
+
def teardown(&block)
|
40
|
+
@context_eval_module.teardown(&block)
|
41
|
+
end
|
42
|
+
|
43
|
+
def specify(spec_name, &block)
|
44
|
+
@context_eval_module.specify(spec_name, &block)
|
45
|
+
end
|
46
|
+
|
47
|
+
def jump_to(name)
|
48
|
+
goto_parts[name].call
|
49
|
+
end
|
50
|
+
|
51
|
+
def goto_parts
|
52
|
+
@context_eval_module.goto_parts
|
53
|
+
end
|
54
|
+
|
55
|
+
def run(reporter, dry_run=false)
|
56
|
+
reporter.add_context(@name)
|
57
|
+
|
58
|
+
prepare_execution_context_class
|
59
|
+
specifications.each do |specification|
|
60
|
+
execution_context = execution_context_class.new(specification)
|
61
|
+
#@exec_context.instance_variables.each {|v|
|
62
|
+
# val = @exec_context.instance_variable_get(v)
|
63
|
+
# execution_context.instance_variable_set v, val
|
64
|
+
#}
|
65
|
+
specification.run(reporter, nil, nil, dry_run, execution_context)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def number_of_specs
|
70
|
+
specifications.length
|
71
|
+
end
|
72
|
+
|
73
|
+
def matches? name, matcher=nil
|
74
|
+
matcher ||= SpecMatcher.new name, @name
|
75
|
+
specifications.each do |spec|
|
76
|
+
return true if spec.matches_matcher? matcher
|
77
|
+
end
|
78
|
+
return false
|
79
|
+
end
|
80
|
+
|
81
|
+
def run_single_spec name
|
82
|
+
return if @name == name
|
83
|
+
matcher = SpecMatcher.new name, @name
|
84
|
+
specifications.reject! do |spec|
|
85
|
+
!spec.matches_matcher? matcher
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def methods
|
90
|
+
my_methods = super
|
91
|
+
my_methods |= @context_eval_module.methods
|
92
|
+
my_methods
|
93
|
+
end
|
94
|
+
|
95
|
+
protected
|
96
|
+
|
97
|
+
def method_missing(*args)
|
98
|
+
@context_eval_module.method_missing(*args)
|
99
|
+
end
|
100
|
+
|
101
|
+
def specifications
|
102
|
+
@context_eval_module.send :specifications
|
103
|
+
end
|
104
|
+
|
105
|
+
def setup_block
|
106
|
+
@context_eval_module.send :setup_block
|
107
|
+
end
|
108
|
+
|
109
|
+
def teardown_block
|
110
|
+
@context_eval_module.send :teardown_block
|
111
|
+
end
|
112
|
+
|
113
|
+
def setup_parts
|
114
|
+
@context_eval_module.send :setup_parts
|
115
|
+
end
|
116
|
+
|
117
|
+
def teardown_parts
|
118
|
+
@context_eval_module.send :teardown_parts
|
119
|
+
end
|
120
|
+
|
121
|
+
def prepare_execution_context_class
|
122
|
+
weave_in_context_modules
|
123
|
+
execution_context_class
|
124
|
+
end
|
125
|
+
|
126
|
+
def weave_in_context_modules
|
127
|
+
mods = context_modules
|
128
|
+
context_eval_module = @context_eval_module
|
129
|
+
execution_context_class.class_eval do
|
130
|
+
include context_eval_module
|
131
|
+
mods.each do |mod|
|
132
|
+
include mod
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
def context_modules
|
138
|
+
@context_eval_module.send :context_modules
|
139
|
+
end
|
140
|
+
|
141
|
+
def execution_context_class
|
142
|
+
@context_eval_module.send :execution_context_class
|
143
|
+
end
|
144
|
+
|
145
|
+
def context_superclass
|
146
|
+
@context_eval_module.send :context_superclass
|
147
|
+
end
|
148
|
+
end
|
149
|
+
include InstanceMethods
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
module Spec
|
2
|
+
module Runner
|
3
|
+
module ContextEval
|
4
|
+
module ModuleMethods
|
5
|
+
def inherit(klass)
|
6
|
+
@context_superclass = klass
|
7
|
+
derive_execution_context_class_from_context_superclass
|
8
|
+
end
|
9
|
+
|
10
|
+
def include(mod)
|
11
|
+
context_modules << mod
|
12
|
+
end
|
13
|
+
|
14
|
+
def setup(&block)
|
15
|
+
setup_parts << block
|
16
|
+
end
|
17
|
+
|
18
|
+
def teardown(&block)
|
19
|
+
teardown_parts << block
|
20
|
+
end
|
21
|
+
|
22
|
+
def goto(name, &block)
|
23
|
+
goto_parts[name] = block
|
24
|
+
end
|
25
|
+
|
26
|
+
def specify(spec_name, &block)
|
27
|
+
specifications << Specification.new(spec_name, &block)
|
28
|
+
end
|
29
|
+
|
30
|
+
def methods
|
31
|
+
my_methods = super
|
32
|
+
my_methods |= context_superclass.methods
|
33
|
+
my_methods
|
34
|
+
end
|
35
|
+
|
36
|
+
def goto_parts
|
37
|
+
@goto_parts ||= {}
|
38
|
+
end
|
39
|
+
|
40
|
+
protected
|
41
|
+
|
42
|
+
def method_missing(method_name, *args)
|
43
|
+
if context_superclass.respond_to?(method_name)
|
44
|
+
return execution_context_class.send(method_name, *args)
|
45
|
+
end
|
46
|
+
super
|
47
|
+
end
|
48
|
+
|
49
|
+
def specifications
|
50
|
+
@specifications ||= []
|
51
|
+
end
|
52
|
+
|
53
|
+
def setup_parts
|
54
|
+
@setup_parts ||= []
|
55
|
+
end
|
56
|
+
|
57
|
+
def teardown_parts
|
58
|
+
@teardown_parts ||= []
|
59
|
+
end
|
60
|
+
|
61
|
+
def setup_block
|
62
|
+
parts = setup_parts.dup
|
63
|
+
|
64
|
+
setup_method = begin
|
65
|
+
context_superclass.instance_method(:setup)
|
66
|
+
rescue
|
67
|
+
nil
|
68
|
+
end
|
69
|
+
parts.unshift setup_method if setup_method
|
70
|
+
create_block_from_parts(parts)
|
71
|
+
end
|
72
|
+
|
73
|
+
def teardown_block
|
74
|
+
parts = teardown_parts.dup
|
75
|
+
|
76
|
+
teardown_method = begin
|
77
|
+
context_superclass.instance_method(:teardown)
|
78
|
+
rescue
|
79
|
+
nil
|
80
|
+
end
|
81
|
+
parts.unshift teardown_method if teardown_method
|
82
|
+
create_block_from_parts(parts)
|
83
|
+
end
|
84
|
+
|
85
|
+
def create_block_from_parts(parts)
|
86
|
+
proc do
|
87
|
+
parts.each do |part|
|
88
|
+
if part.is_a?(UnboundMethod)
|
89
|
+
puts part.inspect
|
90
|
+
part.bind(self).call
|
91
|
+
else
|
92
|
+
instance_exec(&part)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def execution_context_class
|
99
|
+
@execution_context_class ||= derive_execution_context_class_from_context_superclass
|
100
|
+
end
|
101
|
+
|
102
|
+
def derive_execution_context_class_from_context_superclass
|
103
|
+
@execution_context_class = Class.new(context_superclass)
|
104
|
+
@execution_context_class.class_eval do
|
105
|
+
include ::Spec::Runner::ExecutionContext::InstanceMethods
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def context_superclass
|
110
|
+
@context_superclass ||= Object
|
111
|
+
end
|
112
|
+
|
113
|
+
def context_modules
|
114
|
+
@context_modules ||= []
|
115
|
+
end
|
116
|
+
end
|
117
|
+
module InstanceMethods
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
#require File.dirname(__FILE__) + '/../../spekmachine'
|
2
|
+
|
3
|
+
module Spec
|
4
|
+
module Runner
|
5
|
+
class ContextRunner
|
6
|
+
attr_reader :standalone
|
7
|
+
|
8
|
+
def initialize(reporter, standalone, dry_run, single_spec=nil)
|
9
|
+
@contexts = []
|
10
|
+
@reporter = reporter
|
11
|
+
@standalone = standalone
|
12
|
+
@dry_run = dry_run
|
13
|
+
@single_spec = single_spec
|
14
|
+
end
|
15
|
+
|
16
|
+
def set_start(name, block)
|
17
|
+
@start_name = name
|
18
|
+
@setup = block
|
19
|
+
end
|
20
|
+
|
21
|
+
def add_context(context)
|
22
|
+
return if !@single_spec.nil? unless context.matches?(@single_spec)
|
23
|
+
context.run_single_spec @single_spec if context.matches?(@single_spec)
|
24
|
+
@contexts << context
|
25
|
+
end
|
26
|
+
|
27
|
+
def create_path(ctxMap)
|
28
|
+
g = CPP.new
|
29
|
+
@contexts.each do |c|
|
30
|
+
c.goto_parts.keys.each {|dest| g.addArc(nil, c.context_id, dest) }
|
31
|
+
end
|
32
|
+
g.solve
|
33
|
+
mp = g.printCPT(g.convert_to_index(@start_name)).map {|arcLabel, from, to| [g.convert(from), g.convert(to)] }
|
34
|
+
mp.map {|from, to| [ctxMap[from], to] }
|
35
|
+
end
|
36
|
+
|
37
|
+
def run(exit_when_done)
|
38
|
+
raise unless @setup
|
39
|
+
@setup.call
|
40
|
+
cm = {}
|
41
|
+
@contexts.each {|c| cm[c.context_id] = c }
|
42
|
+
@reporter.start(number_of_specs)
|
43
|
+
path = create_path(cm)
|
44
|
+
path.each do |ctx, dest|
|
45
|
+
ctx.run(@reporter, @dry_run)
|
46
|
+
ctx.jump_to dest
|
47
|
+
end
|
48
|
+
@reporter.end
|
49
|
+
failure_count = @reporter.dump
|
50
|
+
if(exit_when_done)
|
51
|
+
exit_code = (failure_count == 0) ? 0 : 1
|
52
|
+
exit(exit_code)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def number_of_specs
|
57
|
+
@contexts.inject(0) {|sum, context| sum + context.number_of_specs}
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Kernel
|
2
|
+
def setup(name, &block)
|
3
|
+
runner = context_runner
|
4
|
+
runner.set_start(name, block)
|
5
|
+
end
|
6
|
+
|
7
|
+
def context(name, desc, &block)
|
8
|
+
context = Spec::Runner::Context.new(name, desc, &block)
|
9
|
+
runner = context_runner
|
10
|
+
runner.add_context(context)
|
11
|
+
runner.run(false) if runner.standalone
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def context_runner
|
17
|
+
$context_runner ||= ::Spec::Runner::OptionParser.create_context_runner(ARGV.dup, true, STDERR, STDOUT)
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,213 @@
|
|
1
|
+
class CPP
|
2
|
+
attr_reader :c, :path
|
3
|
+
def initialize
|
4
|
+
@delta = Hash.new(0)
|
5
|
+
@defined = Hash.new {|h,k|h[k] = {}}
|
6
|
+
@label = Hash.new {|h,k|h[k] = {}}
|
7
|
+
@c = Hash.new {|h,k|h[k] = Hash.new(0)}
|
8
|
+
@f = Hash.new {|h,k|h[k] = Hash.new(0.0)}
|
9
|
+
@pos = []
|
10
|
+
@neg = []
|
11
|
+
@arcs = Hash.new {|h,k|h[k] = Hash.new(0) }
|
12
|
+
@cheapestLabel = Hash.new {|h,k|h[k] = {}}
|
13
|
+
@path = Hash.new {|h,k|h[k] = {}}
|
14
|
+
@basicCost = 0;
|
15
|
+
end
|
16
|
+
|
17
|
+
def solve
|
18
|
+
leastCostPaths
|
19
|
+
checkValid
|
20
|
+
findUnbalanced
|
21
|
+
findFeasible
|
22
|
+
loop do break unless improvements end
|
23
|
+
end
|
24
|
+
|
25
|
+
def convert(index)
|
26
|
+
@nodeList[index]
|
27
|
+
end
|
28
|
+
|
29
|
+
def convert_to_index(name)
|
30
|
+
@nodeMap[name]
|
31
|
+
end
|
32
|
+
|
33
|
+
def updateMap(a)
|
34
|
+
@nodeList ||= []
|
35
|
+
@nodeMap ||= {}
|
36
|
+
if @nodeMap[a]
|
37
|
+
u = @nodeMap[a]
|
38
|
+
else
|
39
|
+
u = @nodeList.size
|
40
|
+
@nodeMap[a] = u
|
41
|
+
@nodeList << a
|
42
|
+
end
|
43
|
+
u
|
44
|
+
end
|
45
|
+
|
46
|
+
def addArc(lab, a, b, cost = 1)
|
47
|
+
u = updateMap a
|
48
|
+
v = updateMap b
|
49
|
+
@n = @nodeList.size
|
50
|
+
@label[u][v] = [] if !@defined[u][v]
|
51
|
+
@label[u][v] << lab
|
52
|
+
@basicCost += cost
|
53
|
+
if !@defined[u][v] or @c[u][v] > cost
|
54
|
+
@c[u][v] = cost
|
55
|
+
@cheapestLabel[u][v] = lab
|
56
|
+
@defined[u][v] = true
|
57
|
+
@path[u][v] = v
|
58
|
+
end
|
59
|
+
@arcs[u][v] += 1
|
60
|
+
@delta[u] += 1
|
61
|
+
@delta[v] -= 1;
|
62
|
+
self
|
63
|
+
end
|
64
|
+
|
65
|
+
def leastCostPaths
|
66
|
+
@n.times do |k|
|
67
|
+
@n.times do |i|
|
68
|
+
if @defined[i][k]
|
69
|
+
@n.times do |j|
|
70
|
+
if @defined[k][j] and (!@defined[i][j] || @c[i][j] > @c[i][k]+@c[k][j])
|
71
|
+
@path[i][j] = @path[i][k];
|
72
|
+
@c[i][j] = @c[i][k]+@c[k][j];
|
73
|
+
@defined[i][j] = true;
|
74
|
+
return if i == j and @c[i][j] < 0
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def checkValid
|
83
|
+
@n.times do |i|
|
84
|
+
@n.times do |j|
|
85
|
+
raise "Graph is not strongly connected for #{i} and #{j}" if !@defined[i][j]
|
86
|
+
end
|
87
|
+
raise "Graph has a negative cycle" if( @c[i][i] < 0 )
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def cost
|
92
|
+
@basicCost + phi
|
93
|
+
end
|
94
|
+
|
95
|
+
def phi
|
96
|
+
phi = 0.0
|
97
|
+
@n.times do |i|
|
98
|
+
@n.times do |j|
|
99
|
+
phi += @c[i][j]*@f[i][j];
|
100
|
+
end
|
101
|
+
end
|
102
|
+
phi
|
103
|
+
end
|
104
|
+
|
105
|
+
def findUnbalanced
|
106
|
+
nn = np = 0
|
107
|
+
|
108
|
+
@n.times do |i|
|
109
|
+
nn += 1 if @delta[i] < 0
|
110
|
+
np += 1 if @delta[i] > 0
|
111
|
+
end
|
112
|
+
|
113
|
+
@neg = []
|
114
|
+
@pos = []
|
115
|
+
nn = np = 0;
|
116
|
+
@n.times do |i|
|
117
|
+
@neg[nn] = i and nn += 1 if @delta[i] < 0
|
118
|
+
@pos[np] = i and np += 1 if @delta[i] > 0
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def findFeasible
|
123
|
+
@neg.length.times do |u|
|
124
|
+
i = @neg[u];
|
125
|
+
@pos.length.times do |v|
|
126
|
+
j = @pos[v];
|
127
|
+
@f[i][j] = -@delta[i] < @delta[j] ? -@delta[i] : @delta[j];
|
128
|
+
@delta[i] += @f[i][j];
|
129
|
+
@delta[j] -= @f[i][j];
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def improvements
|
135
|
+
residual = CPP.new
|
136
|
+
@neg.length.times do |u|
|
137
|
+
i = @neg[u]
|
138
|
+
@pos.length.times do |v|
|
139
|
+
j = @pos[v]
|
140
|
+
residual.addArc(nil, i, j, @c[i][j])
|
141
|
+
residual.addArc(nil, j, i, -@c[i][j]) if @f[i][j] != 0
|
142
|
+
end
|
143
|
+
end
|
144
|
+
residual.leastCostPaths()
|
145
|
+
@n.times do |i|
|
146
|
+
if residual.c[i][i] < 0
|
147
|
+
k = 0
|
148
|
+
kunset = true;
|
149
|
+
u = i
|
150
|
+
loop do
|
151
|
+
v = residual.path[u][i];
|
152
|
+
if residual.c[u][v] < 0 and (kunset || k > @f[v][u])
|
153
|
+
k = @f[v][u]
|
154
|
+
kunset = false
|
155
|
+
end
|
156
|
+
break if (u = v) == i
|
157
|
+
end
|
158
|
+
u = i;
|
159
|
+
loop do
|
160
|
+
v = residual.path[u][i];
|
161
|
+
if residual.c[u][v] < 0
|
162
|
+
@f[v][u] -= k;
|
163
|
+
else
|
164
|
+
@f[u][v] += k;
|
165
|
+
end
|
166
|
+
return true if (u = v) == i
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
false;
|
171
|
+
end
|
172
|
+
|
173
|
+
def findPath(from)
|
174
|
+
((0...@n).detect {|i| @f[from][i] > 0 }) or -1
|
175
|
+
end
|
176
|
+
|
177
|
+
def printCPT(startVertex)
|
178
|
+
result = []
|
179
|
+
v = startVertex;
|
180
|
+
|
181
|
+
loop do
|
182
|
+
u = v;
|
183
|
+
if (v = findPath(u)) != -1
|
184
|
+
@f[u][v] -= 1;
|
185
|
+
while u != v do
|
186
|
+
p = @path[u][v];
|
187
|
+
result << [@cheapestLabel[u][p], u, p]
|
188
|
+
u = p
|
189
|
+
end
|
190
|
+
else
|
191
|
+
bridgeVertex = @path[u][startVertex];
|
192
|
+
if @arcs[u][bridgeVertex] == 0
|
193
|
+
return result
|
194
|
+
end
|
195
|
+
v = bridgeVertex;
|
196
|
+
@n.times do |i|
|
197
|
+
if i != bridgeVertex && @arcs[u][i] > 0
|
198
|
+
v = i;
|
199
|
+
break;
|
200
|
+
end
|
201
|
+
end
|
202
|
+
@arcs[u][v] -= 1;
|
203
|
+
result << [@label[u][v][@arcs[u][v]], u, v]
|
204
|
+
end
|
205
|
+
end
|
206
|
+
raise 'cant get here'
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
# g = CPP.new()
|
211
|
+
# g.addArc("a", :a, :b).addArc("b", :a, :c).addArc("c", :b, :c) .addArc("d", :b, :d).addArc("e", :c, :d).addArc("f", :d, :a)
|
212
|
+
# g.solve()
|
213
|
+
# puts g.printCPT(0).map {|a,b,c| "Take arc #{a} from #{b} to #{c}"}.join("\n")
|
data/lib/spekmachine.rb
ADDED
metadata
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
rubygems_version: 0.8.11
|
3
|
+
specification_version: 1
|
4
|
+
name: spekmachine
|
5
|
+
version: !ruby/object:Gem::Version
|
6
|
+
version: "0.1"
|
7
|
+
date: 2006-09-14 00:00:00 +02:00
|
8
|
+
summary: Stateful specs
|
9
|
+
require_paths:
|
10
|
+
- lib
|
11
|
+
email:
|
12
|
+
homepage: http://persia.rubyforge.org
|
13
|
+
rubyforge_project: persia
|
14
|
+
description: spekmachine is a modification of RSpec, (http://rspec.rubyforge.org/) which uses a state machine to test all transitions. It uses the Chinese Postman algorithm to find the shortest way through all transitions.
|
15
|
+
autorequire: spekmachine
|
16
|
+
default_executable:
|
17
|
+
bindir: bin
|
18
|
+
has_rdoc: false
|
19
|
+
required_ruby_version: !ruby/object:Gem::Version::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">"
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 0.0.0
|
24
|
+
version:
|
25
|
+
platform: ruby
|
26
|
+
signing_key:
|
27
|
+
cert_chain:
|
28
|
+
authors:
|
29
|
+
- Michiel de Mare
|
30
|
+
files:
|
31
|
+
- Rakefile
|
32
|
+
- lib/spekmachine.rb
|
33
|
+
- lib/spekmachine/rake/spectask.rb
|
34
|
+
- lib/spekmachine/runner/context.rb
|
35
|
+
- lib/spekmachine/runner/context_eval.rb
|
36
|
+
- lib/spekmachine/runner/context_runner.rb
|
37
|
+
- lib/spekmachine/runner/kernel_ext.rb
|
38
|
+
- lib/spekmachine/runner/path.rb
|
39
|
+
test_files: []
|
40
|
+
|
41
|
+
rdoc_options: []
|
42
|
+
|
43
|
+
extra_rdoc_files: []
|
44
|
+
|
45
|
+
executables:
|
46
|
+
- spekm
|
47
|
+
extensions: []
|
48
|
+
|
49
|
+
requirements: []
|
50
|
+
|
51
|
+
dependencies:
|
52
|
+
- !ruby/object:Gem::Dependency
|
53
|
+
name: rspec
|
54
|
+
version_requirement:
|
55
|
+
version_requirements: !ruby/object:Gem::Version::Requirement
|
56
|
+
requirements:
|
57
|
+
- - ">="
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: 0.6.3
|
60
|
+
version:
|