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.
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