rspec 0.5.7 → 0.5.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. data/CHANGES +9 -0
  2. data/Rakefile +7 -10
  3. data/bin/spec +1 -1
  4. data/bin/test2spec +93 -0
  5. data/doc/src/default.template +1 -1
  6. data/doc/src/documentation/index.page +37 -37
  7. data/doc/src/documentation/mocks.page +88 -60
  8. data/doc/src/tools/index.page +2 -2
  9. data/doc/src/tools/meta.info +1 -1
  10. data/doc/src/tools/test2spec.page +73 -0
  11. data/doc/src/tutorials/notes.txt +1 -1
  12. data/doc/src/tutorials/stack_01.page +4 -4
  13. data/doc/src/tutorials/stack_02.page +6 -6
  14. data/doc/src/tutorials/stack_03.page +8 -8
  15. data/doc/src/tutorials/stack_04.page +2 -2
  16. data/doc/src/tutorials/stack_05.page +1 -1
  17. data/lib/spec/api/sugar.rb +3 -3
  18. data/lib/spec/runner/base_text_formatter.rb +11 -5
  19. data/lib/spec/runner/option_parser.rb +1 -1
  20. data/lib/spec/runner/progress_bar_formatter.rb +1 -1
  21. data/lib/spec/runner/rdoc_formatter.rb +1 -1
  22. data/lib/spec/runner/reporter.rb +6 -3
  23. data/lib/spec/runner/specdoc_formatter.rb +1 -1
  24. data/lib/spec/runner/specification.rb +1 -0
  25. data/lib/spec/tool/ruby2ruby.rb +485 -0
  26. data/lib/spec/tool/test_unit_translator.rb +114 -83
  27. data/lib/spec/tool/translation_test_runner.rb +118 -0
  28. data/lib/spec/version.rb +1 -1
  29. data/test/spec/runner/context_test.rb +4 -0
  30. data/test/spec/runner/progress_bar_formatter_test.rb +1 -1
  31. data/test/spec/runner/rdoc_formatter_test.rb +1 -1
  32. data/test/spec/runner/reporter_test.rb +24 -7
  33. data/test/spec/runner/specdoc_formatter_test.rb +1 -1
  34. data/test/spec/runner/specification_test.rb +8 -0
  35. data/test/spec/tool/ruby_to_ruby_test.rb +79 -0
  36. data/test/spec/tool/test_unit_api_spec.rb +45 -31
  37. data/test/spec/tool/test_unit_api_test.rb +13 -6
  38. data/test/spec/tool/test_unit_translator_test.rb +6 -1
  39. metadata +11 -10
  40. data/bin/test2rspec +0 -35
  41. data/doc/src/tools/test2rspec.page +0 -13
  42. data/lib/spec/tool/command_line.rb +0 -39
  43. 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
- class TestUnitTranslator
5
- ONE_ARG_TRANSLATIONS = {
6
- "assert" => "should_not_be nil",
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
- RAISE_TRANSLATIONS = {
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
- context_name = class_name.match(/(.*)Test/)[1]
47
- line = "#{spaces}context \"#{context_name}\" do\n"
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
- specification_name = method_meaning.gsub(/_/, " ").capitalize
54
- line = "#{spaces}specify \"#{specification_name}\" do\n"
55
-
56
- elsif line =~ /(\s*)(assert[^\s$\(]*)\s*\(?([^\)^\{]*)\)?\s*(.*)/
57
- spaces = $1
58
- assertion = $2
59
- args = $3
60
- suffix = $4
61
-
62
- if(args =~ /^do/n and suffix =="")
63
- # hack to handle methods taking no args and do at the end
64
- args = ""
65
- suffix = "do"
66
- end
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
- if translation = TWO_ARG_TRANSLATIONS[assertion]
69
- expected, actual, message = args.split(",").collect{|arg| arg.strip}
70
- line = "#{spaces}#{actual}.#{translation} #{expected}\n"
71
-
72
- elsif assertion == "assert_respond_to"
73
- actual, method, message = args.split(",").collect{|arg| arg.strip}
74
- line = "#{spaces}#{actual}.should_respond_to #{method}\n"
75
-
76
- elsif translation = ONE_ARG_TRANSLATIONS[assertion]
77
- actual, message = args.split(",").collect{|arg| arg.strip}
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
- translated += line unless line.nil?
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
@@ -3,7 +3,7 @@ module Spec
3
3
  unless defined? MAJOR
4
4
  MAJOR = 0
5
5
  MINOR = 5
6
- TINY = 7
6
+ TINY = 8
7
7
 
8
8
  STRING = [MAJOR, MINOR, TINY].join('.')
9
9
  TAG = "REL_" + [MAJOR, MINOR, TINY].join('_')
@@ -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}
@@ -24,7 +24,7 @@ module Spec
24
24
  end
25
25
 
26
26
  def test_should_push_F_for_failing_spec
27
- @formatter.spec_failed("spec", 98)
27
+ @formatter.spec_failed("spec", 98, nil)
28
28
  assert_equal("F", @io.string)
29
29
  end
30
30
 
@@ -19,7 +19,7 @@ module Spec
19
19
  end
20
20
 
21
21
  def test_should_push_out_failed_spec
22
- @formatter.spec_failed("spec", 98)
22
+ @formatter.spec_failed("spec", 98, nil)
23
23
  assert_equal("# * spec [98 - FAILED]\n", @io.string)
24
24
  end
25
25
 
@@ -39,24 +39,26 @@ module Spec
39
39
  end
40
40
 
41
41
  def test_should_account_for_spec_in_stats_for_pass
42
- spec = Specification.new("spec")
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.spec_finished spec
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(:spec_failed).with(spec, 1)
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.spec_finished spec, RuntimeError.new
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