rspec 0.5.7 → 0.5.8
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/CHANGES +9 -0
- data/Rakefile +7 -10
- data/bin/spec +1 -1
- data/bin/test2spec +93 -0
- data/doc/src/default.template +1 -1
- data/doc/src/documentation/index.page +37 -37
- data/doc/src/documentation/mocks.page +88 -60
- data/doc/src/tools/index.page +2 -2
- data/doc/src/tools/meta.info +1 -1
- data/doc/src/tools/test2spec.page +73 -0
- data/doc/src/tutorials/notes.txt +1 -1
- data/doc/src/tutorials/stack_01.page +4 -4
- data/doc/src/tutorials/stack_02.page +6 -6
- data/doc/src/tutorials/stack_03.page +8 -8
- data/doc/src/tutorials/stack_04.page +2 -2
- data/doc/src/tutorials/stack_05.page +1 -1
- data/lib/spec/api/sugar.rb +3 -3
- data/lib/spec/runner/base_text_formatter.rb +11 -5
- data/lib/spec/runner/option_parser.rb +1 -1
- data/lib/spec/runner/progress_bar_formatter.rb +1 -1
- data/lib/spec/runner/rdoc_formatter.rb +1 -1
- data/lib/spec/runner/reporter.rb +6 -3
- data/lib/spec/runner/specdoc_formatter.rb +1 -1
- data/lib/spec/runner/specification.rb +1 -0
- data/lib/spec/tool/ruby2ruby.rb +485 -0
- data/lib/spec/tool/test_unit_translator.rb +114 -83
- data/lib/spec/tool/translation_test_runner.rb +118 -0
- data/lib/spec/version.rb +1 -1
- data/test/spec/runner/context_test.rb +4 -0
- data/test/spec/runner/progress_bar_formatter_test.rb +1 -1
- data/test/spec/runner/rdoc_formatter_test.rb +1 -1
- data/test/spec/runner/reporter_test.rb +24 -7
- data/test/spec/runner/specdoc_formatter_test.rb +1 -1
- data/test/spec/runner/specification_test.rb +8 -0
- data/test/spec/tool/ruby_to_ruby_test.rb +79 -0
- data/test/spec/tool/test_unit_api_spec.rb +45 -31
- data/test/spec/tool/test_unit_api_test.rb +13 -6
- data/test/spec/tool/test_unit_translator_test.rb +6 -1
- metadata +11 -10
- data/bin/test2rspec +0 -35
- data/doc/src/tools/test2rspec.page +0 -13
- data/lib/spec/tool/command_line.rb +0 -39
- data/test/spec/tool/command_line_test.rb +0 -21
@@ -1,112 +1,143 @@
|
|
1
|
+
require 'spec/tool/ruby2ruby'
|
2
|
+
|
1
3
|
module Spec
|
2
4
|
module Tool
|
3
|
-
# Translates Test::Unit tests to RSpec specs
|
4
|
-
|
5
|
-
|
6
|
-
|
5
|
+
# Translates Test::Unit tests to RSpec specs,
|
6
|
+
# Using RubyToRuby and Sexp
|
7
|
+
class TestUnitTranslator < RubyToRuby
|
8
|
+
ONE_ARG_ASSERTIONS = ["assert", "assert_nil", "assert_not_nil"]
|
9
|
+
|
10
|
+
PLAIN_TRANSLATIONS = {
|
11
|
+
"assert" => "should_be true",
|
7
12
|
"assert_nil" => "should_be nil",
|
8
|
-
"assert_not_nil" => "should_not_be nil"
|
9
|
-
|
10
|
-
}
|
11
|
-
TWO_ARG_TRANSLATIONS = {
|
13
|
+
"assert_not_nil" => "should_not_be nil",
|
12
14
|
"assert_equal" => "should_equal",
|
15
|
+
"assert_in_delta" => "should_be_close",
|
13
16
|
"assert_instance_of" => "should_be_instance_of",
|
14
17
|
"assert_kind_of" => "should_be_kind_of",
|
15
18
|
"assert_match" => "should_match",
|
16
19
|
"assert_no_match" => "should_not_match",
|
17
20
|
"assert_not_equal" => "should_not_equal",
|
18
21
|
"assert_not_same" => "should_not_be",
|
22
|
+
"assert_respond_to" => "should_respond_to",
|
19
23
|
"assert_same" => "should_be"
|
20
24
|
}
|
21
|
-
|
25
|
+
PLAIN_PATTERN = /^#{PLAIN_TRANSLATIONS.keys.join("$|^")}$/
|
26
|
+
|
27
|
+
BLOCK_TRANSLATIONS = {
|
28
|
+
"assert_block" => "should_be true",
|
22
29
|
"assert_nothing_raised" => "should_not_raise",
|
23
30
|
"assert_nothing_thrown" => "should_not_throw",
|
24
31
|
"assert_raise" => "should_raise",
|
25
32
|
"assert_raises" => "should_raise",
|
26
33
|
"assert_throws" => "should_throw",
|
27
34
|
}
|
28
|
-
|
29
|
-
def translate(test_unit_file)
|
30
|
-
content = File.open(test_unit_file)
|
31
|
-
end_replacement = nil
|
32
|
-
translated = ""
|
33
|
-
content.each do |line|
|
34
|
-
if line =~ /^require .*test.*/
|
35
|
-
line = "require 'spec'\n"
|
36
|
-
elsif line =~ /^(\s*)def\s+setup/
|
37
|
-
spaces = $1
|
38
|
-
line = "#{spaces}setup do\n"
|
39
|
-
elsif line =~ /^(\s*)def\s+teardown/
|
40
|
-
spaces = $1
|
41
|
-
line = "#{spaces}teardown do\n"
|
42
|
-
elsif line =~ /^(\s*)class\s+(.*)\s+<\s+Test::Unit::TestCase/
|
43
|
-
spaces = $1
|
44
|
-
class_name = $2
|
35
|
+
BLOCK_PATTERN = /^#{BLOCK_TRANSLATIONS.keys.join("$|^")}$/
|
45
36
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
elsif line =~ /(\s*)def\s+test_(.*)/
|
50
|
-
spaces = $1
|
51
|
-
method_meaning = $2
|
37
|
+
def translate(klass)
|
38
|
+
process(ParseTree.new.parse_tree(klass).first)
|
39
|
+
end
|
52
40
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
41
|
+
def process_class(exp)
|
42
|
+
if(exp[1].to_s == "Test::Unit::TestCase")
|
43
|
+
module_and_class_name = exp.shift.to_s.split("::")
|
44
|
+
modules = module_and_class_name[0..-2]
|
45
|
+
class_name = module_and_class_name[-1]
|
46
|
+
super_class_name = exp.shift.to_s
|
47
|
+
context_name = class_name.match(/(.*)Test/)[1]
|
48
|
+
|
49
|
+
s = ""
|
50
|
+
s << modules.collect{|m| "module #{m}\n"}.join("") unless modules.empty?
|
51
|
+
s << "context \"#{context_name}\" do\n"
|
52
|
+
body = ""
|
53
|
+
body << "#{process exp.shift}\n\n" until exp.empty?
|
54
|
+
s += indent(body) + "end\n"
|
55
|
+
s += modules.collect{|m| "end\n"}.join("") unless modules.empty?
|
56
|
+
else
|
57
|
+
super
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def process_defn(exp)
|
62
|
+
method_name = exp[0].to_s
|
63
|
+
if exp[1].first != :cfunc
|
64
|
+
if method_name == "setup" || method_name == "teardown"
|
65
|
+
name = exp.shift
|
66
|
+
body = process(exp.shift)
|
67
|
+
return "#{method_name} do #{body}end".gsub(/\n\s*\n+/, "\n")
|
68
|
+
elsif method_name =~ /^test_(.*)/
|
69
|
+
exp.shift
|
70
|
+
spec_name = $1.gsub(/_/, " ")
|
71
|
+
body = process(exp.shift)
|
72
|
+
return "specify \"#{spec_name}\" do #{body}end".gsub(/\n\s*\n+/, "\n")
|
73
|
+
else
|
74
|
+
super
|
75
|
+
end
|
76
|
+
else
|
77
|
+
super
|
78
|
+
end
|
79
|
+
end
|
67
80
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
line = "#{spaces}#{actual}.#{translation}\n"
|
79
|
-
|
80
|
-
elsif translation = RAISE_TRANSLATIONS[assertion] and suffix =~ /\{.*\}/
|
81
|
-
expected, message = args.split(",").collect{|arg| arg.strip}
|
82
|
-
line = "#{spaces}lambda #{suffix}.#{translation} #{expected}\n"
|
83
|
-
|
84
|
-
elsif translation = RAISE_TRANSLATIONS[assertion]
|
85
|
-
expected, message = args.split(",").collect{|arg| arg.strip}
|
86
|
-
line = "#{spaces}lambda do\n"
|
87
|
-
end_replacement = "#{translation} #{expected}\n"
|
88
|
-
|
89
|
-
elsif assertion == "assert_block" and suffix =~ /\{.*\}/
|
90
|
-
line = "#{spaces}lambda #{suffix}.should_be true\n"
|
91
|
-
|
92
|
-
elsif assertion == "assert_block"
|
93
|
-
line = "#{spaces}lambda do\n"
|
94
|
-
end_replacement = "should_be true\n"
|
95
|
-
|
96
|
-
elsif assertion == "assert_in_delta"
|
97
|
-
expected, actual, delta, message = args.split(",").collect{|arg| arg.strip}
|
98
|
-
line = "#{spaces}#{actual}.should_be_close #{expected}, #{delta}\n"
|
81
|
+
def process_fcall(exp)
|
82
|
+
if exp[0].to_s =~ PLAIN_PATTERN
|
83
|
+
name = exp.shift.to_s
|
84
|
+
args = exp.shift
|
85
|
+
code = []
|
86
|
+
unless args.nil?
|
87
|
+
assert_type args, :array
|
88
|
+
args.shift # :array
|
89
|
+
until args.empty? do
|
90
|
+
code << process(args.shift)
|
99
91
|
end
|
100
|
-
elsif end_replacement && line =~ /(\s+)end/
|
101
|
-
spaces = $1
|
102
|
-
line = "#{spaces}end.#{end_replacement}"
|
103
|
-
end_replacement = nil
|
104
92
|
end
|
105
93
|
|
106
|
-
|
94
|
+
if ONE_ARG_ASSERTIONS.index(name)
|
95
|
+
expected = ""
|
96
|
+
actual = code[0]
|
97
|
+
elsif(name == "assert_in_delta")
|
98
|
+
expected = " #{code[0]}, #{code[2]}"
|
99
|
+
actual = code[1]
|
100
|
+
elsif(name == "assert_respond_to")
|
101
|
+
# test::unit got this wrong. we have to swap them
|
102
|
+
expected = " #{code[1]}"
|
103
|
+
actual = code[0]
|
104
|
+
else
|
105
|
+
expected = " #{code[0]}"
|
106
|
+
actual = code[1]
|
107
|
+
end
|
108
|
+
translation = PLAIN_TRANSLATIONS[name]
|
109
|
+
raise "No translation for '#{name}'" if translation.nil?
|
110
|
+
return "#{actual}.#{translation}#{expected}"
|
111
|
+
elsif exp[0].to_s =~ BLOCK_PATTERN
|
112
|
+
name = exp.shift.to_s
|
113
|
+
args = exp.shift
|
114
|
+
code = []
|
115
|
+
unless args.nil? then
|
116
|
+
assert_type args, :array
|
117
|
+
args.shift # :array
|
118
|
+
until args.empty? do
|
119
|
+
code << process(args.shift)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
suffix_arg = code.empty? ? "" : "(#{code[0]})"
|
123
|
+
@lambda_suffix = "#{BLOCK_TRANSLATIONS[name]}#{suffix_arg}"
|
124
|
+
return "lambda"
|
125
|
+
else
|
126
|
+
super
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def process_iter(exp)
|
131
|
+
result = super
|
132
|
+
if(@lambda_suffix)
|
133
|
+
suffix = @lambda_suffix
|
134
|
+
@lambda_suffix = nil
|
135
|
+
"#{result}.#{suffix}"
|
136
|
+
else
|
137
|
+
result
|
107
138
|
end
|
108
|
-
translated
|
109
139
|
end
|
140
|
+
|
110
141
|
end
|
111
142
|
end
|
112
143
|
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
require 'spec/tool/test_unit_translator'
|
2
|
+
require 'fileutils'
|
3
|
+
|
4
|
+
module Spec
|
5
|
+
module Tool
|
6
|
+
# A Test::Unit runner that doesn't run tests, but translates them instead!
|
7
|
+
class TranslationTestRunner
|
8
|
+
include FileUtils
|
9
|
+
|
10
|
+
def self.run(suite, output_level)
|
11
|
+
self.new(suite)
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize(suite)
|
15
|
+
puts "Writing translated specs to #{$test2spec_options[:specdir]}"
|
16
|
+
translator = TestUnitTranslator.new
|
17
|
+
ObjectSpace.each_object(Class) do |klass|
|
18
|
+
if klass < ::Test::Unit::TestCase
|
19
|
+
begin
|
20
|
+
translation = translator.translate(klass)
|
21
|
+
|
22
|
+
unless $test2spec_options[:dry_run]
|
23
|
+
relative_path = underscore(klass.name)
|
24
|
+
relative_path.gsub! /_test$/, "_spec"
|
25
|
+
relative_path += ".rb"
|
26
|
+
write(translation, relative_path)
|
27
|
+
else
|
28
|
+
puts "Successfully translated #{klass}"
|
29
|
+
end
|
30
|
+
rescue SexpProcessorError => e
|
31
|
+
puts "Failed to translate #{klass}"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
puts "\nDone"
|
36
|
+
end
|
37
|
+
|
38
|
+
def passed?
|
39
|
+
true
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def destination_path(relative_destination)
|
45
|
+
File.join($test2spec_options[:specdir], relative_destination)
|
46
|
+
end
|
47
|
+
|
48
|
+
def write(source, relative_destination)
|
49
|
+
destination = destination_path(relative_destination)
|
50
|
+
destination_exists = File.exists?(destination)
|
51
|
+
if destination_exists and identical?(source, destination)
|
52
|
+
return puts("Identical : #{relative_destination}")
|
53
|
+
end
|
54
|
+
|
55
|
+
if destination_exists
|
56
|
+
choice = case ($test2spec_options[:collision]).to_sym #|| :ask
|
57
|
+
when :ask then force_file_collision?(relative_destination)
|
58
|
+
when :force then :force
|
59
|
+
when :skip then :skip
|
60
|
+
else raise "Invalid collision option: #{$test2spec_options[:collision].inspect}"
|
61
|
+
end
|
62
|
+
|
63
|
+
case choice
|
64
|
+
when :force then puts("Forcing : #{relative_destination}")
|
65
|
+
when :skip then return(puts("Skipping : #{relative_destination}"))
|
66
|
+
else raise "Invalid collision choice: #{choice}.inspect"
|
67
|
+
end
|
68
|
+
else
|
69
|
+
dir = File.dirname(destination)
|
70
|
+
unless File.directory?(dir)
|
71
|
+
puts "Creating : #{dir}"
|
72
|
+
mkdir_p dir
|
73
|
+
end
|
74
|
+
puts "Creating : #{destination}"
|
75
|
+
end
|
76
|
+
|
77
|
+
File.open(destination, 'w') do |df|
|
78
|
+
df.write(source)
|
79
|
+
end
|
80
|
+
|
81
|
+
if $test2spec_options[:chmod]
|
82
|
+
chmod($test2spec_options[:chmod], destination)
|
83
|
+
end
|
84
|
+
|
85
|
+
system("svn add #{destination}") if $test2spec_options[:svn]
|
86
|
+
end
|
87
|
+
|
88
|
+
def identical?(source, destination, &block)
|
89
|
+
return false if File.directory? destination
|
90
|
+
destination = IO.read(destination)
|
91
|
+
source == destination
|
92
|
+
end
|
93
|
+
|
94
|
+
def force_file_collision?(destination)
|
95
|
+
$stdout.print "overwrite #{destination}? [Ynaq] "
|
96
|
+
case $stdin.gets
|
97
|
+
when /a/i
|
98
|
+
$test2spec_options[:collision] = :force
|
99
|
+
$stdout.puts "Forcing ALL"
|
100
|
+
:force
|
101
|
+
when /q/i
|
102
|
+
$stdout.puts "Quitting"
|
103
|
+
raise SystemExit
|
104
|
+
when /n/i then :skip
|
105
|
+
else :force
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def underscore(camel_cased_word)
|
110
|
+
camel_cased_word.to_s.gsub(/::/, '/').
|
111
|
+
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
|
112
|
+
gsub(/([a-z\d])([A-Z])/,'\1_\2').
|
113
|
+
tr("-", "_").
|
114
|
+
downcase
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
data/lib/spec/version.rb
CHANGED
@@ -17,6 +17,7 @@ module Spec
|
|
17
17
|
|
18
18
|
def test_should_run_spec
|
19
19
|
@formatter.should.receive(:add_context).with :any_args
|
20
|
+
@formatter.should.receive(:spec_started).with "test"
|
20
21
|
@formatter.should.receive(:spec_finished).with "test", :anything, :anything
|
21
22
|
$spec_ran = false
|
22
23
|
@context.specify("test") {$spec_ran = true}
|
@@ -27,6 +28,7 @@ module Spec
|
|
27
28
|
|
28
29
|
def test_should_run_spec_dry
|
29
30
|
@formatter.should.receive(:add_context).with :any_args
|
31
|
+
@formatter.should.receive(:spec_started).with "test"
|
30
32
|
@formatter.should.receive(:spec_finished).with "test"
|
31
33
|
$spec_ran = false
|
32
34
|
@context.specify("test") {$spec_ran = true}
|
@@ -37,6 +39,7 @@ module Spec
|
|
37
39
|
|
38
40
|
def test_setup
|
39
41
|
@formatter.should.receive(:add_context).with :any_args
|
42
|
+
@formatter.should.receive(:spec_started).with "test"
|
40
43
|
@formatter.should.receive(:spec_finished).with :any_args
|
41
44
|
$setup_ran = false
|
42
45
|
@context.setup {$setup_ran = true}
|
@@ -48,6 +51,7 @@ module Spec
|
|
48
51
|
|
49
52
|
def test_teardwown
|
50
53
|
@formatter.should.receive(:add_context).with :any_args
|
54
|
+
@formatter.should.receive(:spec_started).with "test"
|
51
55
|
@formatter.should.receive(:spec_finished).with :any_args
|
52
56
|
$teardwown_ran = false
|
53
57
|
@context.teardown {$teardwown_ran = true}
|
@@ -39,24 +39,26 @@ module Spec
|
|
39
39
|
end
|
40
40
|
|
41
41
|
def test_should_account_for_spec_in_stats_for_pass
|
42
|
-
|
42
|
+
@formatter.should_receive(:spec_started)
|
43
43
|
@formatter.should_receive(:spec_passed)
|
44
44
|
@formatter.should_receive(:start_dump)
|
45
45
|
@formatter.should_receive(:dump_summary).with(:anything, 0, 1, 0)
|
46
|
-
@reporter.
|
46
|
+
@reporter.spec_started "spec"
|
47
|
+
@reporter.spec_finished "spec"
|
47
48
|
@reporter.dump
|
48
49
|
end
|
49
50
|
|
50
51
|
def test_should_account_for_spec_and_error_in_stats_for_pass
|
51
|
-
spec = Specification.new("spec")
|
52
52
|
@formatter.should_receive(:add_context)
|
53
|
-
@formatter.should_receive(:
|
53
|
+
@formatter.should_receive(:spec_started).with("spec")
|
54
|
+
@formatter.should_receive(:spec_failed).with("spec", 1, failure)
|
54
55
|
@formatter.should_receive(:start_dump)
|
55
56
|
@formatter.should_receive(:dump_failure).with(1, :anything)
|
56
57
|
@formatter.should_receive(:dump_summary).with(:anything, 1, 1, 1)
|
57
58
|
@backtrace_tweaker.should.receive(:tweak_backtrace)
|
58
59
|
@reporter.add_context "context"
|
59
|
-
@reporter.
|
60
|
+
@reporter.spec_started "spec"
|
61
|
+
@reporter.spec_finished "spec", RuntimeError.new
|
60
62
|
@reporter.dump
|
61
63
|
end
|
62
64
|
|
@@ -74,19 +76,30 @@ module Spec
|
|
74
76
|
def test_should_handle_multiple_specs_same_name
|
75
77
|
error = RuntimeError.new
|
76
78
|
@formatter.should_receive(:add_context).exactly(2).times
|
79
|
+
@formatter.should.receive(:spec_started).with("spec").exactly(4).times
|
77
80
|
@formatter.should_receive(:spec_passed).with("spec").exactly(2).times
|
78
|
-
@formatter.should_receive(:spec_failed).with("spec", 1)
|
79
|
-
@formatter.should_receive(:spec_failed).with("spec", 2)
|
81
|
+
@formatter.should_receive(:spec_failed).with("spec", 1, failure)
|
82
|
+
@formatter.should_receive(:spec_failed).with("spec", 2, failure)
|
80
83
|
@formatter.should_receive(:dump_failure).exactly(2).times
|
81
84
|
@formatter.should_receive(:start_dump)
|
82
85
|
@formatter.should_receive(:dump_summary).with(:anything, 2, 4, 2)
|
83
86
|
@backtrace_tweaker.should.receive(:tweak_backtrace)
|
84
87
|
@reporter.add_context "context"
|
88
|
+
|
89
|
+
@reporter.spec_started "spec"
|
85
90
|
@reporter.spec_finished "spec"
|
91
|
+
|
92
|
+
@reporter.spec_started "spec"
|
86
93
|
@reporter.spec_finished "spec", error
|
94
|
+
|
87
95
|
@reporter.add_context "context"
|
96
|
+
|
97
|
+
@reporter.spec_started "spec"
|
88
98
|
@reporter.spec_finished "spec"
|
99
|
+
|
100
|
+
@reporter.spec_started "spec"
|
89
101
|
@reporter.spec_finished "spec", error
|
102
|
+
|
90
103
|
@reporter.dump
|
91
104
|
end
|
92
105
|
|
@@ -99,6 +112,10 @@ module Spec
|
|
99
112
|
@backtrace_tweaker.__verify
|
100
113
|
end
|
101
114
|
|
115
|
+
def failure
|
116
|
+
Api::DuckTypeArgConstraint.new(:header, :message, :backtrace)
|
117
|
+
end
|
118
|
+
|
102
119
|
end
|
103
120
|
end
|
104
121
|
end
|