rspec 0.5.7 → 0.5.8
Sign up to get free protection for your applications and to get access to all the features.
- 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
|