spekmachine 0.1
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/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:
|