opal 0.7.0.beta3 → 0.7.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (94) hide show
  1. checksums.yaml +4 -4
  2. data/.gitmodules +4 -0
  3. data/.travis.yml +7 -3
  4. data/.yardopts +6 -0
  5. data/CHANGELOG.md +28 -0
  6. data/Gemfile +1 -1
  7. data/README.md +3 -12
  8. data/Rakefile +4 -150
  9. data/bin/opal-mspec +1 -1
  10. data/docs/compiler_directives.md +127 -0
  11. data/examples/rack/.gitignore +1 -0
  12. data/examples/rack/app/user.rb +1 -0
  13. data/lib/mspec/opal/special_calls.rb +15 -2
  14. data/lib/opal/builder.rb +15 -8
  15. data/lib/opal/compiler.rb +75 -4
  16. data/lib/opal/erb.rb +22 -2
  17. data/lib/opal/fragment.rb +17 -5
  18. data/lib/opal/nodes/def.rb +174 -53
  19. data/lib/opal/nodes/if.rb +14 -0
  20. data/lib/opal/nodes/module.rb +0 -1
  21. data/lib/opal/nodes/rescue.rb +10 -2
  22. data/lib/opal/nodes/scope.rb +0 -17
  23. data/lib/opal/parser.rb +83 -19
  24. data/lib/opal/parser/grammar.rb +2511 -2414
  25. data/lib/opal/parser/grammar.y +71 -9
  26. data/lib/opal/parser/lexer.rb +44 -12
  27. data/lib/opal/parser/parser_scope.rb +3 -0
  28. data/lib/opal/parser/sexp.rb +7 -1
  29. data/lib/opal/paths.rb +5 -5
  30. data/lib/opal/sprockets/environment.rb +2 -10
  31. data/lib/opal/sprockets/path_reader.rb +1 -1
  32. data/lib/opal/sprockets/processor.rb +1 -0
  33. data/lib/opal/sprockets/server.rb +2 -0
  34. data/lib/opal/util.rb +7 -2
  35. data/lib/opal/version.rb +1 -1
  36. data/opal.gemspec +1 -0
  37. data/opal/README.md +1 -1
  38. data/opal/corelib/dir.rb +1 -1
  39. data/opal/corelib/enumerable.rb +3 -1
  40. data/opal/corelib/error.rb +3 -0
  41. data/opal/corelib/file.rb +2 -0
  42. data/opal/corelib/hash.rb +3 -0
  43. data/opal/corelib/io.rb +15 -1
  44. data/opal/corelib/kernel.rb +8 -0
  45. data/opal/corelib/module.rb +42 -17
  46. data/opal/corelib/runtime.js +223 -49
  47. data/opal/corelib/string.rb +1 -1
  48. data/opal/corelib/struct.rb +1 -7
  49. data/spec/README.md +8 -0
  50. data/spec/filters/bugs/language.rb +1 -0
  51. data/spec/filters/bugs/module.rb +4 -0
  52. data/spec/filters/unsupported/frozen.rb +2 -0
  53. data/spec/lib/compiler/pre_processed_conditionals_spec.rb +87 -0
  54. data/spec/lib/compiler_spec.rb +1 -67
  55. data/spec/lib/fixtures/file_with_directives.js +2 -0
  56. data/spec/lib/fixtures/required_file.js +1 -0
  57. data/spec/lib/parser/def_spec.rb +29 -16
  58. data/spec/lib/parser/variables_spec.rb +5 -5
  59. data/spec/lib/sprockets/path_reader_spec.rb +24 -8
  60. data/spec/lib/sprockets/server_spec.rb +10 -3
  61. data/spec/opal/core/date_spec.rb +14 -0
  62. data/spec/opal/core/language/versions/def_2_0_spec.rb +62 -0
  63. data/spec/opal/core/language_spec.rb +23 -0
  64. data/spec/opal/core/runtime/donate_spec.rb +53 -0
  65. data/spec/opal/stdlib/native/native_alias_spec.rb +19 -0
  66. data/spec/opal/stdlib/native/native_class_spec.rb +18 -0
  67. data/spec/opal/stdlib/native/native_module_spec.rb +13 -0
  68. data/spec/rubyspecs +2 -0
  69. data/stdlib/buffer.rb +1 -0
  70. data/stdlib/date.rb +18 -0
  71. data/stdlib/encoding.rb +3 -2
  72. data/stdlib/minitest.rb +780 -0
  73. data/stdlib/minitest/assertions.rb +662 -0
  74. data/stdlib/minitest/autorun.rb +12 -0
  75. data/stdlib/minitest/benchmark.rb +426 -0
  76. data/stdlib/minitest/expectations.rb +281 -0
  77. data/stdlib/minitest/hell.rb +11 -0
  78. data/stdlib/minitest/mock.rb +220 -0
  79. data/stdlib/minitest/parallel.rb +65 -0
  80. data/stdlib/minitest/pride.rb +4 -0
  81. data/stdlib/minitest/pride_plugin.rb +142 -0
  82. data/stdlib/minitest/spec.rb +310 -0
  83. data/stdlib/minitest/test.rb +293 -0
  84. data/stdlib/minitest/unit.rb +45 -0
  85. data/stdlib/native.rb +12 -3
  86. data/stdlib/nodejs/process.rb +16 -2
  87. data/stdlib/promise.rb +99 -0
  88. data/stdlib/test/unit.rb +10 -0
  89. data/stdlib/thread.rb +4 -0
  90. data/tasks/building.rake +58 -0
  91. data/tasks/documentation.rake +38 -0
  92. data/tasks/documenting.rake +37 -0
  93. data/tasks/testing.rake +102 -0
  94. metadata +57 -2
@@ -0,0 +1,11 @@
1
+ require "minitest/parallel"
2
+
3
+ class Minitest::Test
4
+ class << self
5
+ alias :old_test_order :test_order # :nodoc:
6
+
7
+ def test_order # :nodoc:
8
+ :parallel
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,220 @@
1
+ class MockExpectationError < StandardError; end # :nodoc:
2
+
3
+ module Minitest # :nodoc:
4
+
5
+ ##
6
+ # A simple and clean mock object framework.
7
+ #
8
+ # All mock objects are an instance of Mock
9
+
10
+ class Mock
11
+ alias :__respond_to? :respond_to?
12
+
13
+ overridden_methods = %w(
14
+ ===
15
+ inspect
16
+ object_id
17
+ public_send
18
+ respond_to_missing?
19
+ send
20
+ to_s
21
+ )
22
+
23
+ instance_methods.each do |m|
24
+ undef_method m unless overridden_methods.include?(m.to_s) || m =~ /^__/
25
+ end
26
+
27
+ overridden_methods.map(&:to_sym).each do |method_id|
28
+ define_method method_id do |*args, &b|
29
+ if @expected_calls.has_key? method_id then
30
+ method_missing(method_id, *args, &b)
31
+ else
32
+ super(*args, &b)
33
+ end
34
+ end
35
+ end
36
+
37
+ def initialize # :nodoc:
38
+ @expected_calls = Hash.new { |calls, name| calls[name] = [] }
39
+ @actual_calls = Hash.new { |calls, name| calls[name] = [] }
40
+ end
41
+
42
+ ##
43
+ # Expect that method +name+ is called, optionally with +args+ or a
44
+ # +blk+, and returns +retval+.
45
+ #
46
+ # @mock.expect(:meaning_of_life, 42)
47
+ # @mock.meaning_of_life # => 42
48
+ #
49
+ # @mock.expect(:do_something_with, true, [some_obj, true])
50
+ # @mock.do_something_with(some_obj, true) # => true
51
+ #
52
+ # @mock.expect(:do_something_else, true) do |a1, a2|
53
+ # a1 == "buggs" && a2 == :bunny
54
+ # end
55
+ #
56
+ # +args+ is compared to the expected args using case equality (ie, the
57
+ # '===' operator), allowing for less specific expectations.
58
+ #
59
+ # @mock.expect(:uses_any_string, true, [String])
60
+ # @mock.uses_any_string("foo") # => true
61
+ # @mock.verify # => true
62
+ #
63
+ # @mock.expect(:uses_one_string, true, ["foo"])
64
+ # @mock.uses_one_string("bar") # => true
65
+ # @mock.verify # => raises MockExpectationError
66
+
67
+ def expect(name, retval, args=[], &blk)
68
+ name = name.to_sym
69
+
70
+ if block_given?
71
+ raise ArgumentError, "args ignored when block given" unless args.empty?
72
+ @expected_calls[name] << { :retval => retval, :block => blk }
73
+ else
74
+ raise ArgumentError, "args must be an array" unless Array === args
75
+ @expected_calls[name] << { :retval => retval, :args => args }
76
+ end
77
+ self
78
+ end
79
+
80
+ def __call name, data # :nodoc:
81
+ case data
82
+ when Hash then
83
+ "#{name}(#{data[:args].inspect[1..-2]}) => #{data[:retval].inspect}"
84
+ else
85
+ data.map { |d| __call name, d }.join ", "
86
+ end
87
+ end
88
+
89
+ ##
90
+ # Verify that all methods were called as expected. Raises
91
+ # +MockExpectationError+ if the mock object was not called as
92
+ # expected.
93
+
94
+ def verify
95
+ @expected_calls.each do |name, calls|
96
+ calls.each do |expected|
97
+ msg1 = "expected #{__call name, expected}"
98
+ msg2 = "#{msg1}, got [#{__call name, @actual_calls[name]}]"
99
+
100
+ raise MockExpectationError, msg2 if
101
+ @actual_calls.has_key?(name) and
102
+ not @actual_calls[name].include?(expected)
103
+
104
+ raise MockExpectationError, msg1 unless
105
+ @actual_calls.has_key?(name) and
106
+ @actual_calls[name].include?(expected)
107
+ end
108
+ end
109
+ true
110
+ end
111
+
112
+ def method_missing(sym, *args, &block) # :nodoc:
113
+ unless @expected_calls.has_key?(sym) then
114
+ raise NoMethodError, "unmocked method %p, expected one of %p" %
115
+ [sym, @expected_calls.keys.sort_by(&:to_s)]
116
+ end
117
+
118
+ index = @actual_calls[sym].length
119
+ expected_call = @expected_calls[sym][index]
120
+
121
+ unless expected_call then
122
+ raise MockExpectationError, "No more expects available for %p: %p" %
123
+ [sym, args]
124
+ end
125
+
126
+ expected_args, retval, val_block =
127
+ expected_call.values_at(:args, :retval, :block)
128
+
129
+ if val_block then
130
+ raise MockExpectationError, "mocked method %p failed block w/ %p" %
131
+ [sym, args] unless val_block.call(*args, &block)
132
+
133
+ # keep "verify" happy
134
+ @actual_calls[sym] << expected_call
135
+ return retval
136
+ end
137
+
138
+ if expected_args.size != args.size then
139
+ raise ArgumentError, "mocked method %p expects %d arguments, got %d" %
140
+ [sym, expected_args.size, args.size]
141
+ end
142
+
143
+ fully_matched = expected_args.zip(args).all? { |mod, a|
144
+ mod === a or mod == a
145
+ }
146
+
147
+ unless fully_matched then
148
+ raise MockExpectationError, "mocked method %p called with unexpected arguments %p" %
149
+ [sym, args]
150
+ end
151
+
152
+ @actual_calls[sym] << {
153
+ :retval => retval,
154
+ :args => expected_args.zip(args).map { |mod, a| mod === a ? mod : a }
155
+ }
156
+
157
+ retval
158
+ end
159
+
160
+ def respond_to?(sym, include_private = false) # :nodoc:
161
+ return true if @expected_calls.has_key? sym.to_sym
162
+ return __respond_to?(sym, include_private)
163
+ end
164
+ end
165
+ end
166
+
167
+ class Object # :nodoc:
168
+
169
+ ##
170
+ # Add a temporary stubbed method replacing +name+ for the duration
171
+ # of the +block+. If +val_or_callable+ responds to #call, then it
172
+ # returns the result of calling it, otherwise returns the value
173
+ # as-is. If stubbed method yields a block, +block_args+ will be
174
+ # passed along. Cleans up the stub at the end of the +block+. The
175
+ # method +name+ must exist before stubbing.
176
+ #
177
+ # def test_stale_eh
178
+ # obj_under_test = Something.new
179
+ # refute obj_under_test.stale?
180
+ #
181
+ # Time.stub :now, Time.at(0) do
182
+ # assert obj_under_test.stale?
183
+ # end
184
+ # end
185
+ #
186
+
187
+ def stub name, val_or_callable, *block_args, &block
188
+ new_name = "__minitest_stub__#{name}"
189
+
190
+ metaclass = class << self; self; end
191
+
192
+ if respond_to? name and not methods.map(&:to_s).include? name.to_s then
193
+ metaclass.send :define_method, name do |*args|
194
+ super(*args)
195
+ end
196
+ end
197
+
198
+ metaclass.send :alias_method, new_name, name
199
+
200
+ metaclass.send :define_method, name do |*args, &blk|
201
+
202
+ ret = if val_or_callable.respond_to? :call then
203
+ val_or_callable.call(*args)
204
+ else
205
+ val_or_callable
206
+ end
207
+
208
+ blk.call(*block_args) if blk
209
+
210
+ ret
211
+ end
212
+
213
+ yield self
214
+ ensure
215
+ metaclass.send :undef_method, name
216
+ metaclass.send :alias_method, name, new_name
217
+ metaclass.send :undef_method, new_name
218
+ end
219
+
220
+ end
@@ -0,0 +1,65 @@
1
+ module Minitest
2
+ module Parallel
3
+
4
+ ##
5
+ # The engine used to run multiple tests in parallel.
6
+
7
+ class Executor
8
+
9
+ ##
10
+ # The size of the pool of workers.
11
+
12
+ attr_reader :size
13
+
14
+ ##
15
+ # Create a parallel test executor of with +size+ workers.
16
+
17
+ def initialize size
18
+ @size = size
19
+ @queue = Queue.new
20
+ # @pool = size.times.map {
21
+ # Thread.new(@queue) do |queue|
22
+ # Thread.current.abort_on_exception = true
23
+ # while job = queue.pop
24
+ # klass, method, reporter = job
25
+ # result = Minitest.run_one_method klass, method
26
+ # reporter.synchronize { reporter.record result }
27
+ # end
28
+ # end
29
+ # }
30
+ end
31
+
32
+ ##
33
+ # Add a job to the queue
34
+
35
+ def << work; @queue << work; end
36
+
37
+ ##
38
+ # Shuts down the pool of workers by signalling them to quit and
39
+ # waiting for them all to finish what they're currently working
40
+ # on.
41
+
42
+ def shutdown
43
+ # size.times { @queue << nil }
44
+ # @pool.each(&:join)
45
+ @queue.each do |job|
46
+ klass, method, reporter = job
47
+ result = Minitest.run_one_method klass, method
48
+ reporter.record result
49
+ # reporter.synchronize { reporter.record result }
50
+ end
51
+ end
52
+ end
53
+
54
+ module Test
55
+ def _synchronize; Test.io_lock.synchronize { yield }; end # :nodoc:
56
+
57
+ module ClassMethods
58
+ def run_one_method klass, method_name, reporter # :nodoc:
59
+ Minitest.parallel_executor << [klass, method_name, reporter]
60
+ end
61
+ def test_order; :parallel; end # :nodoc:
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,4 @@
1
+ require "minitest"
2
+
3
+ Minitest.load_plugins
4
+ Minitest::PrideIO.pride!
@@ -0,0 +1,142 @@
1
+ require "minitest"
2
+
3
+ module Minitest
4
+ def self.plugin_pride_options opts, options # :nodoc:
5
+ opts.on "-p", "--pride", "Pride. Show your testing pride!" do
6
+ PrideIO.pride!
7
+ end
8
+ end
9
+
10
+ def self.plugin_pride_init options # :nodoc:
11
+ if PrideIO.pride? then
12
+ klass = ENV["TERM"] =~ /^xterm|-256color$/ ? PrideLOL : PrideIO
13
+ io = klass.new options[:io]
14
+
15
+ self.reporter.reporters.grep(Minitest::Reporter).each do |rep|
16
+ rep.io = io
17
+ end
18
+ end
19
+ end
20
+
21
+ ##
22
+ # Show your testing pride!
23
+
24
+ class PrideIO
25
+ ##
26
+ # Activate the pride plugin. Called from both -p option and minitest/pride
27
+
28
+ def self.pride!
29
+ @pride = true
30
+ end
31
+
32
+ ##
33
+ # Are we showing our testing pride?
34
+
35
+ def self.pride?
36
+ @pride ||= false
37
+ end
38
+
39
+ # Start an escape sequence
40
+ ESC = "\e["
41
+
42
+ # End the escape sequence
43
+ NND = "#{ESC}0m"
44
+
45
+ # The IO we're going to pipe through.
46
+ attr_reader :io
47
+
48
+ def initialize io # :nodoc:
49
+ @io = io
50
+ # stolen from /System/Library/Perl/5.10.0/Term/ANSIColor.pm
51
+ # also reference http://en.wikipedia.org/wiki/ANSI_escape_code
52
+ @colors ||= (31..36).to_a
53
+ @size = @colors.size
54
+ @index = 0
55
+ end
56
+
57
+ ##
58
+ # Wrap print to colorize the output.
59
+
60
+ def print o
61
+ case o
62
+ when "." then
63
+ io.print pride o
64
+ when "E", "F" then
65
+ io.print "#{ESC}41m#{ESC}37m#{o}#{NND}"
66
+ when "S" then
67
+ io.print pride o
68
+ else
69
+ io.print o
70
+ end
71
+ end
72
+
73
+ def puts(*o) # :nodoc:
74
+ o.map! { |s|
75
+ s.to_s.sub(/Finished/) {
76
+ @index = 0
77
+ 'Fabulous run'.split(//).map { |c|
78
+ pride(c)
79
+ }.join
80
+ }
81
+ }
82
+
83
+ io.puts(*o)
84
+ end
85
+
86
+ ##
87
+ # Color a string.
88
+
89
+ def pride string
90
+ string = "*" if string == "."
91
+ c = @colors[@index % @size]
92
+ @index += 1
93
+ "#{ESC}#{c}m#{string}#{NND}"
94
+ end
95
+
96
+ def method_missing msg, *args # :nodoc:
97
+ io.send(msg, *args)
98
+ end
99
+ end
100
+
101
+ ##
102
+ # If you thought the PrideIO was colorful...
103
+ #
104
+ # (Inspired by lolcat, but with clean math)
105
+
106
+ class PrideLOL < PrideIO
107
+ PI_3 = Math::PI / 3 # :nodoc:
108
+
109
+ def initialize io # :nodoc:
110
+ # walk red, green, and blue around a circle separated by equal thirds.
111
+ #
112
+ # To visualize, type this into wolfram-alpha:
113
+ #
114
+ # plot (3*sin(x)+3), (3*sin(x+2*pi/3)+3), (3*sin(x+4*pi/3)+3)
115
+
116
+ # 6 has wide pretty gradients. 3 == lolcat, about half the width
117
+ @colors = (0...(6 * 7)).map { |n|
118
+ n *= 1.0 / 6
119
+ r = (3 * Math.sin(n ) + 3).to_i
120
+ g = (3 * Math.sin(n + 2 * PI_3) + 3).to_i
121
+ b = (3 * Math.sin(n + 4 * PI_3) + 3).to_i
122
+
123
+ # Then we take rgb and encode them in a single number using base 6.
124
+ # For some mysterious reason, we add 16... to clear the bottom 4 bits?
125
+ # Yes... they're ugly.
126
+
127
+ 36 * r + 6 * g + b + 16
128
+ }
129
+
130
+ super
131
+ end
132
+
133
+ ##
134
+ # Make the string even more colorful. Damnit.
135
+
136
+ def pride string
137
+ c = @colors[@index % @size]
138
+ @index += 1
139
+ "#{ESC}38;5;#{c}m#{string}#{NND}"
140
+ end
141
+ end
142
+ end
@@ -0,0 +1,310 @@
1
+ require "minitest/test"
2
+
3
+ class Module # :nodoc:
4
+ def infect_an_assertion meth, new_name, dont_flip = false # :nodoc:
5
+ # warn "%-22p -> %p %p" % [meth, new_name, dont_flip]
6
+ dont_flip = !!dont_flip
7
+ self.class_eval do
8
+ define_method(new_name) do |*args|
9
+ case
10
+ when dont_flip
11
+ Minitest::Spec.current.send(meth, self, *args)
12
+ when Proc === self
13
+ Minitest::Spec.current.send(meth, *args, &self)
14
+ else
15
+ Minitest::Spec.current.send(meth, args.first, self, *args[1..-1])
16
+ end
17
+ end
18
+ end
19
+
20
+ # <<-EOM
21
+ # def #{new_name} *args
22
+ # case
23
+ # when #{dont_flip} then
24
+ # Minitest::Spec.current.#{meth}(self, *args)
25
+ # when Proc === self then
26
+ # Minitest::Spec.current.#{meth}(*args, &self)
27
+ # else
28
+ # Minitest::Spec.current.#{meth}(args.first, self, *args[1..-1])
29
+ # end
30
+ # end
31
+ # EOM
32
+ end
33
+ end
34
+
35
+ module Kernel # :nodoc:
36
+ ##
37
+ # Describe a series of expectations for a given target +desc+.
38
+ #
39
+ # Defines a test class subclassing from either Minitest::Spec or
40
+ # from the surrounding describe's class. The surrounding class may
41
+ # subclass Minitest::Spec manually in order to easily share code:
42
+ #
43
+ # class MySpec < Minitest::Spec
44
+ # # ... shared code ...
45
+ # end
46
+ #
47
+ # class TestStuff < MySpec
48
+ # it "does stuff" do
49
+ # # shared code available here
50
+ # end
51
+ # describe "inner stuff" do
52
+ # it "still does stuff" do
53
+ # # ...and here
54
+ # end
55
+ # end
56
+ # end
57
+ #
58
+ # For more information on getting started with writing specs, see:
59
+ #
60
+ # http://www.rubyinside.com/a-minitestspec-tutorial-elegant-spec-style-testing-that-comes-with-ruby-5354.html
61
+ #
62
+ # For some suggestions on how to improve your specs, try:
63
+ #
64
+ # http://betterspecs.org
65
+ #
66
+ # but do note that several items there are debatable or specific to
67
+ # rspec.
68
+ #
69
+ # For more information about expectations, see Minitest::Expectations.
70
+
71
+ def describe desc, *additional_desc, &block # :doc:
72
+ stack = Minitest::Spec.describe_stack
73
+ name = [stack.last, desc, *additional_desc].compact.join("::")
74
+ sclas = stack.last || if Class === self && is_a?(Minitest::Spec::DSL) then
75
+ self
76
+ else
77
+ Minitest::Spec.spec_type desc, *additional_desc
78
+ end
79
+
80
+ cls = sclas.create name, desc
81
+
82
+ stack.push cls
83
+ cls.class_eval(&block)
84
+ stack.pop
85
+ cls
86
+ end
87
+ private :describe
88
+ end
89
+
90
+ ##
91
+ # Minitest::Spec -- The faster, better, less-magical spec framework!
92
+ #
93
+ # For a list of expectations, see Minitest::Expectations.
94
+
95
+ class Minitest::Spec < Minitest::Test
96
+
97
+ def self.current # :nodoc:
98
+ Thread.current[:current_spec]
99
+ end
100
+
101
+ def initialize name # :nodoc:
102
+ super
103
+ Thread.current[:current_spec] = self
104
+ end
105
+
106
+ ##
107
+ # Oh look! A Minitest::Spec::DSL module! Eat your heart out DHH.
108
+
109
+ module DSL
110
+ ##
111
+ # Contains pairs of matchers and Spec classes to be used to
112
+ # calculate the superclass of a top-level describe. This allows for
113
+ # automatically customizable spec types.
114
+ #
115
+ # See: register_spec_type and spec_type
116
+
117
+ TYPES = [[//, Minitest::Spec]]
118
+
119
+ ##
120
+ # Register a new type of spec that matches the spec's description.
121
+ # This method can take either a Regexp and a spec class or a spec
122
+ # class and a block that takes the description and returns true if
123
+ # it matches.
124
+ #
125
+ # Eg:
126
+ #
127
+ # register_spec_type(/Controller$/, Minitest::Spec::Rails)
128
+ #
129
+ # or:
130
+ #
131
+ # register_spec_type(Minitest::Spec::RailsModel) do |desc|
132
+ # desc.superclass == ActiveRecord::Base
133
+ # end
134
+
135
+ def register_spec_type(*args, &block)
136
+ if block then
137
+ matcher, klass = block, args.first
138
+ else
139
+ matcher, klass = *args
140
+ end
141
+ TYPES.unshift [matcher, klass]
142
+ end
143
+
144
+ ##
145
+ # Figure out the spec class to use based on a spec's description. Eg:
146
+ #
147
+ # spec_type("BlahController") # => Minitest::Spec::Rails
148
+
149
+ def spec_type desc, *additional
150
+ TYPES.find { |matcher, klass|
151
+ if matcher.respond_to? :call then
152
+ matcher.call desc, *additional
153
+ else
154
+ matcher === desc.to_s
155
+ end
156
+ }.last
157
+ end
158
+
159
+ def describe_stack # :nodoc:
160
+ Thread.current[:describe_stack] ||= []
161
+ end
162
+
163
+ ##
164
+ # Returns the children of this spec.
165
+
166
+ def children
167
+ @children ||= []
168
+ end
169
+
170
+ def nuke_test_methods! # :nodoc:
171
+ self.public_instance_methods.grep(/^test_/).each do |name|
172
+ self.send :undef_method, name
173
+ end
174
+ end
175
+
176
+ ##
177
+ # Define a 'before' action. Inherits the way normal methods should.
178
+ #
179
+ # NOTE: +type+ is ignored and is only there to make porting easier.
180
+ #
181
+ # Equivalent to Minitest::Test#setup.
182
+
183
+ def before type = nil, &block
184
+ define_method :setup do
185
+ super()
186
+ self.instance_eval(&block)
187
+ end
188
+ end
189
+
190
+ ##
191
+ # Define an 'after' action. Inherits the way normal methods should.
192
+ #
193
+ # NOTE: +type+ is ignored and is only there to make porting easier.
194
+ #
195
+ # Equivalent to Minitest::Test#teardown.
196
+
197
+ def after type = nil, &block
198
+ define_method :teardown do
199
+ self.instance_eval(&block)
200
+ super()
201
+ end
202
+ end
203
+
204
+ ##
205
+ # Define an expectation with name +desc+. Name gets morphed to a
206
+ # proper test method name. For some freakish reason, people who
207
+ # write specs don't like class inheritance, so this goes way out of
208
+ # its way to make sure that expectations aren't inherited.
209
+ #
210
+ # This is also aliased to #specify and doesn't require a +desc+ arg.
211
+ #
212
+ # Hint: If you _do_ want inheritance, use minitest/test. You can mix
213
+ # and match between assertions and expectations as much as you want.
214
+
215
+ def it desc = "anonymous", &block
216
+ block ||= proc { skip "(no tests defined)" }
217
+
218
+ @specs ||= 0
219
+ @specs += 1
220
+
221
+ name = "test_%04d_%s" % [ @specs, desc ]
222
+
223
+ undef_klasses = self.children.reject { |c| c.public_method_defined? name }
224
+
225
+ define_method name, &block
226
+
227
+ undef_klasses.each do |undef_klass|
228
+ undef_klass.send :undef_method, name
229
+ end
230
+
231
+ name
232
+ end
233
+
234
+ ##
235
+ # Essentially, define an accessor for +name+ with +block+.
236
+ #
237
+ # Why use let instead of def? I honestly don't know.
238
+
239
+ def let name, &block
240
+ name = name.to_s
241
+ pre, post = "let '#{name}' cannot ", ". Please use another name."
242
+ methods = Minitest::Spec.instance_methods.map(&:to_s) - %w[subject]
243
+ raise ArgumentError, "#{pre}begin with 'test'#{post}" if
244
+ name =~ /\Atest/
245
+ raise ArgumentError, "#{pre}override a method in Minitest::Spec#{post}" if
246
+ methods.include? name
247
+
248
+ define_method name do
249
+ @_memoized ||= {}
250
+ @_memoized.fetch(name) { |k| @_memoized[k] = instance_eval(&block) }
251
+ end
252
+ end
253
+
254
+ ##
255
+ # Another lazy man's accessor generator. Made even more lazy by
256
+ # setting the name for you to +subject+.
257
+
258
+ def subject &block
259
+ let :subject, &block
260
+ end
261
+
262
+ def create name, desc # :nodoc:
263
+ cls = Class.new(self) do
264
+ @name = name
265
+ @desc = desc
266
+
267
+ nuke_test_methods!
268
+ end
269
+
270
+ children << cls
271
+
272
+ cls
273
+ end
274
+
275
+ def name # :nodoc:
276
+ defined?(@name) ? @name : super
277
+ end
278
+
279
+ def to_s # :nodoc:
280
+ name # Can't alias due to 1.8.7, not sure why
281
+ end
282
+
283
+ # :stopdoc:
284
+ attr_reader :desc
285
+ alias :specify :it
286
+
287
+ module InstanceMethods
288
+ def before_setup
289
+ super
290
+ Thread.current[:current_spec] = self
291
+ end
292
+ end
293
+
294
+ def self.extended obj
295
+ obj.send :include, InstanceMethods
296
+ end
297
+
298
+ # :startdoc:
299
+ end
300
+
301
+ extend DSL
302
+
303
+ TYPES = DSL::TYPES # :nodoc:
304
+ end
305
+
306
+ require "minitest/expectations"
307
+
308
+ class Object # :nodoc:
309
+ include Minitest::Expectations unless ENV["MT_NO_EXPECTATIONS"]
310
+ end