Skip to content

Commit e3fb71c

Browse files
committed
Improve RSpec and Minitest adapters
1 parent 1e65da9 commit e3fb71c

12 files changed

Lines changed: 469 additions & 247 deletions

File tree

lib/quickdraw.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ module Quickdraw
1717
autoload :Configuration, "quickdraw/configuration"
1818
autoload :HTMLPrettifier, "quickdraw/html_prettifier"
1919
autoload :ConcurrentArray, "quickdraw/concurrent_array"
20-
autoload :MinitestAdapter, "quickdraw/minitest_adapter"
2120
autoload :ConcurrentInteger, "quickdraw/concurrent_integer"
21+
autoload :BasicTest, "quickdraw/basic_test"
2222

2323
Null = Object.new.freeze
2424
Error = Module.new

lib/quickdraw/basic_test.rb

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
# frozen_string_literal: true
2+
3+
class Quickdraw::BasicTest
4+
def self.test(description = nil, skip: false, &block)
5+
$quickdraw_runner << [self, description, skip, block]
6+
end
7+
8+
def initialize(description:, skip:, block:, runner:)
9+
@description = description
10+
@skip = skip
11+
@block = block
12+
@runner = runner
13+
end
14+
15+
def run
16+
setup
17+
around_test { instance_exec(&@block) }
18+
teardown
19+
rescue Exception => error
20+
error!({
21+
"location" => @block.source_location,
22+
"description" => @description,
23+
"message" => error.message,
24+
"name" => error.class.name,
25+
# "detailed_message" => error.detailed_message,
26+
"backtrace" => error.backtrace,
27+
})
28+
end
29+
30+
def match
31+
yield
32+
success!
33+
rescue NoMatchingPatternError => e
34+
failure! { e.message }
35+
end
36+
37+
def assert(value)
38+
if value
39+
success!
40+
elsif block_given?
41+
failure! { yield(value) }
42+
else
43+
failure! { "expected #{value.inspect} to be truthy" }
44+
end
45+
46+
nil
47+
end
48+
49+
def refute(value, ...)
50+
assert(!value, ...)
51+
end
52+
53+
# Indicate that an assertion passed successfully.
54+
def success!
55+
if @skip
56+
@runner.failure!("The skipped test `#{@description}` started passing.")
57+
else
58+
@runner.success!(@description)
59+
end
60+
61+
nil
62+
end
63+
64+
# Indicate that an assertion failed.
65+
def failure!(&)
66+
if @skip
67+
@runner.success!(@description)
68+
else
69+
test_location = @block.source_location
70+
locations = caller_locations
71+
location = locations.find { |it| it.path.end_with?(".test.rb") } || locations.first
72+
73+
@runner.failure!({
74+
"class_name" => self.class.name,
75+
"test_path" => test_location[0],
76+
"test_line" => test_location[1],
77+
"description" => @description,
78+
"message" => yield,
79+
"path" => location.path,
80+
"line" => location.lineno,
81+
})
82+
end
83+
84+
nil
85+
end
86+
87+
def error!(...)
88+
if @skip
89+
@runner.success!(@description)
90+
else
91+
@runner.error!(...)
92+
end
93+
94+
nil
95+
end
96+
97+
def setup = nil
98+
def teardown = nil
99+
def around_test = yield
100+
end

lib/quickdraw/minitest.rb

Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
# frozen_string_literal: true
2+
3+
begin
4+
require "minitest/assertions"
5+
rescue LoadError
6+
raise LoadError.new("You need to add `minitest` to your Gemfile")
7+
end
8+
9+
module Quickdraw
10+
module Minitest
11+
class Test < Quickdraw::BasicTest
12+
def self.method_added(name)
13+
name = name.to_s
14+
15+
return unless name.start_with?("test_")
16+
17+
location = instance_method(name).source_location
18+
19+
class_eval(<<~RUBY, *location)
20+
test("#{name[5..].tr('_', ' ')}") do
21+
#{name}
22+
end
23+
RUBY
24+
end
25+
26+
def assert(value, message = nil)
27+
case message
28+
when nil
29+
super(value) { yield }
30+
when Proc
31+
super(value, &message)
32+
else
33+
super(value) { message }
34+
end
35+
end
36+
37+
def refute(value, message = nil)
38+
super(value) { message || yield }
39+
end
40+
41+
# Fails unless obj is empty.
42+
# [Docs](https://www.rubydoc.info/gems/minitest/5.25.4/Minitest/Assertions#assert_empty-instance_method)
43+
def assert_empty(obj, message = nil) = nil
44+
define_method :assert_empty, ::Minitest::Assertions.instance_method(:assert_empty)
45+
46+
# Fails unless exp == act printing the difference between the two, if possible.
47+
# [Docs](https://www.rubydoc.info/gems/minitest/5.25.4/Minitest/Assertions#assert_equal-instance_method)
48+
def assert_equal(exp, act, msg = nil) = nil
49+
define_method :assert_equal, ::Minitest::Assertions.instance_method(:assert_equal)
50+
51+
# For comparing Floats. Fails unless exp and act are within delta of each other.
52+
# [Docs](https://www.rubydoc.info/gems/minitest/5.25.4/Minitest/Assertions#assert_in_delta-instance_method)
53+
def assert_in_delta(exp, act, delta = 0.001, msg = nil) = nil
54+
define_method :assert_in_delta, ::Minitest::Assertions.instance_method(:assert_in_delta)
55+
56+
# For comparing Floats. Fails unless exp and act have a relative error less than epsilon.
57+
# [Docs](https://www.rubydoc.info/gems/minitest/5.25.4/Minitest/Assertions#assert_in_epsilon-instance_method)
58+
def assert_in_epsilon(exp, act, epsilon = 0.001, msg = nil) = nil
59+
define_method :assert_in_epsilon, ::Minitest::Assertions.instance_method(:assert_in_epsilon)
60+
61+
# Fails unless collection includes obj.
62+
# [Docs](https://www.rubydoc.info/gems/minitest/5.25.4/Minitest/Assertions#assert_includes-instance_method)
63+
def assert_includes(collection, obj, msg = nil) = nil
64+
define_method :assert_includes, ::Minitest::Assertions.instance_method(:assert_includes)
65+
66+
# Fails unless obj is an instance of cls.
67+
# [Docs](https://www.rubydoc.info/gems/minitest/5.25.4/Minitest/Assertions#assert_instance_of-instance_method)
68+
def assert_instance_of(cls, obj, msg = nil) = nil
69+
define_method :assert_instance_of, ::Minitest::Assertions.instance_method(:assert_instance_of)
70+
71+
# Fails unless obj is a kind of cls.
72+
# [Docs](https://www.rubydoc.info/gems/minitest/5.25.4/Minitest/Assertions#assert_kind_of-instance_method)
73+
def assert_kind_of(cls, obj, msg = nil) = nil
74+
define_method :assert_kind_of, ::Minitest::Assertions.instance_method(:assert_kind_of)
75+
76+
# Fails unless matcher =~ obj.
77+
# [Docs](https://www.rubydoc.info/gems/minitest/5.25.4/Minitest/Assertions#assert_match-instance_method)
78+
def assert_match(matcher, obj, msg = nil) = nil
79+
define_method :assert_match, ::Minitest::Assertions.instance_method(:assert_match)
80+
81+
# Fails unless obj is nil.
82+
# [Docs](https://www.rubydoc.info/gems/minitest/5.25.4/Minitest/Assertions#assert_nil-instance_method)
83+
def assert_nil(obj, msg = nil) = nil
84+
define_method :assert_nil, ::Minitest::Assertions.instance_method(:assert_nil)
85+
86+
# For testing with binary operators.
87+
# [Docs](https://www.rubydoc.info/gems/minitest/5.25.4/Minitest/Assertions#assert_operator-instance_method)
88+
def assert_operator(o1, op, o2 = nil, msg = nil) = nil
89+
define_method :assert_operator, ::Minitest::Assertions.instance_method(:assert_operator)
90+
91+
# Fails unless path exists.
92+
# [Docs](https://www.rubydoc.info/gems/minitest/5.25.4/Minitest/Assertions#assert_path_exists-instance_method)
93+
def assert_path_exists(path, msg = nil) = nil
94+
define_method :assert_path_exists, ::Minitest::Assertions.instance_method(:assert_path_exists)
95+
96+
# For testing with pattern matching (only supported with Ruby 3.0 and later).
97+
# [Docs](https://www.rubydoc.info/gems/minitest/5.25.4/Minitest/Assertions#assert_pattern-instance_method)
98+
def assert_pattern(&block) = nil
99+
define_method :assert_pattern, ::Minitest::Assertions.instance_method(:assert_pattern)
100+
101+
# For testing with predicates.
102+
# [Docs](https://www.rubydoc.info/gems/minitest/5.25.4/Minitest/Assertions#assert_predicate-instance_method)
103+
def assert_predicate(o1, op, msg = nil) = nil
104+
define_method :assert_predicate, ::Minitest::Assertions.instance_method(:assert_predicate)
105+
106+
# Fails unless the block raises one of exp. Returns the exception matched.
107+
# [Docs](https://www.rubydoc.info/gems/minitest/5.25.4/Minitest/Assertions#assert_raises-instance_method)
108+
def assert_raises(*exp, &block) = nil
109+
define_method :assert_raises, ::Minitest::Assertions.instance_method(:assert_raises)
110+
111+
# Fails unless obj responds to meth.
112+
# [Docs](https://www.rubydoc.info/gems/minitest/5.25.4/Minitest/Assertions#assert_respond_to-instance_method)
113+
def assert_respond_to(obj, meth, msg = nil) = nil
114+
define_method :assert_respond_to, ::Minitest::Assertions.instance_method(:assert_respond_to)
115+
116+
# Fails unless exp and act are #equal?
117+
# [Docs](https://www.rubydoc.info/gems/minitest/5.25.4/Minitest/Assertions#assert_same-instance_method)
118+
def assert_same(exp, act, msg = nil) = nil
119+
define_method :assert_same, ::Minitest::Assertions.instance_method(:assert_same)
120+
121+
# Fails unless the block throws sym.
122+
# [Docs](https://www.rubydoc.info/gems/minitest/5.25.4/Minitest/Assertions#assert_throws-instance_method)
123+
def assert_throws(sym, msg = nil, &block) = nil
124+
define_method :assert_throws, ::Minitest::Assertions.instance_method(:assert_throws)
125+
126+
# Fails if obj is empty.
127+
# [Docs](https://www.rubydoc.info/gems/minitest/5.25.4/Minitest/Assertions#refute_empty-instance_method)
128+
def refute_empty(obj, msg = nil) = nil
129+
define_method :refute_empty, ::Minitest::Assertions.instance_method(:refute_empty)
130+
131+
# Fails if exp == act.
132+
# [Docs](https://www.rubydoc.info/gems/minitest/5.25.4/Minitest/Assertions#refute_equal-instance_method)
133+
def refute_equal(exp, act, msg = nil) = nil
134+
define_method :refute_equal, ::Minitest::Assertions.instance_method(:refute_equal)
135+
136+
# For comparing Floats. Fails if exp is within delta of act.
137+
# [Docs](https://www.rubydoc.info/gems/minitest/5.25.4/Minitest/Assertions#refute_in_delta-instance_method)
138+
def refute_in_delta(exp, act, delta = 0.001, msg = nil) = nil
139+
define_method :refute_in_delta, ::Minitest::Assertions.instance_method(:refute_in_delta)
140+
141+
# For comparing Floats. Fails if exp and act have a relative error less than epsilon.
142+
# [Docs](https://www.rubydoc.info/gems/minitest/5.25.4/Minitest/Assertions#refute_in_epsilon-instance_method)
143+
def refute_in_epsilon(a, b, epsilon = 0.001, msg = nil) = nil
144+
define_method :refute_in_epsilon, ::Minitest::Assertions.instance_method(:refute_in_epsilon)
145+
146+
# Fails if collection includes obj.
147+
# [Docs](https://www.rubydoc.info/gems/minitest/5.25.4/Minitest/Assertions#refute_includes-instance_method)
148+
def refute_includes(collection, obj, msg = nil) = nil
149+
define_method :refute_includes, ::Minitest::Assertions.instance_method(:refute_includes)
150+
151+
# Fails if obj is an instance of cls.
152+
# [Docs](https://www.rubydoc.info/gems/minitest/5.25.4/Minitest/Assertions#refute_instance_of-instance_method)
153+
def refute_instance_of(cls, obj, msg = nil) = nil
154+
define_method :refute_instance_of, ::Minitest::Assertions.instance_method(:refute_instance_of)
155+
156+
# Fails if obj is a kind of cls.
157+
# [Docs](https://www.rubydoc.info/gems/minitest/5.25.4/Minitest/Assertions#refute_kind_of-instance_method)
158+
def refute_kind_of(cls, obj, msg = nil) = nil
159+
define_method :refute_kind_of, ::Minitest::Assertions.instance_method(:refute_kind_of)
160+
161+
# Fails if matcher =~ obj.
162+
# [Docs](https://www.rubydoc.info/gems/minitest/5.25.4/Minitest/Assertions#refute_match-instance_method)
163+
def refute_match(matcher, obj, msg = nil) = nil
164+
define_method :refute_match, ::Minitest::Assertions.instance_method(:refute_match)
165+
166+
# Fails if obj is nil.
167+
# [Docs](https://www.rubydoc.info/gems/minitest/5.25.4/Minitest/Assertions#refute_nil-instance_method)
168+
def refute_nil(obj, msg = nil) = nil
169+
define_method :refute_nil, ::Minitest::Assertions.instance_method(:refute_nil)
170+
171+
# Fails if o1 is not op o2.
172+
# [Docs](https://www.rubydoc.info/gems/minitest/5.25.4/Minitest/Assertions#refute_operator-instance_method)
173+
def refute_operator(o1, op, o2 = nil, msg = nil) = nil
174+
define_method :refute_operator, ::Minitest::Assertions.instance_method(:refute_operator)
175+
176+
# Fails if path exists.
177+
# [Docs](https://www.rubydoc.info/gems/minitest/5.25.4/Minitest/Assertions#refute_path_exists-instance_method)
178+
def refute_path_exists(path, msg = nil) = nil
179+
define_method :refute_path_exists, ::Minitest::Assertions.instance_method(:refute_path_exists)
180+
181+
# For testing with pattern matching (only supported with Ruby 3.0 and later).
182+
# [Docs](https://www.rubydoc.info/gems/minitest/5.25.4/Minitest/Assertions#refute_pattern-instance_method)
183+
def refute_pattern(&block) = nil
184+
define_method :refute_pattern, ::Minitest::Assertions.instance_method(:refute_pattern)
185+
186+
# For testing with predicates.
187+
# [Docs](https://www.rubydoc.info/gems/minitest/5.25.4/Minitest/Assertions#refute_predicate-instance_method)
188+
def refute_predicate(o1, op, msg = nil) = nil
189+
define_method :refute_predicate, ::Minitest::Assertions.instance_method(:refute_predicate)
190+
191+
# Fails if obj responds to the message meth.
192+
# [Docs](https://www.rubydoc.info/gems/minitest/5.25.4/Minitest/Assertions#refute_respond_to-instance_method)
193+
def refute_respond_to(obj, meth, msg = nil) = nil
194+
define_method :refute_respond_to, ::Minitest::Assertions.instance_method(:refute_respond_to)
195+
196+
# Fails if exp is the same (by object identity) as act.
197+
# [Docs](https://www.rubydoc.info/gems/minitest/5.25.4/Minitest/Assertions#refute_same-instance_method)
198+
def refute_same(exp, act, msg = nil) = nil
199+
define_method :refute_same, ::Minitest::Assertions.instance_method(:refute_same)
200+
201+
private define_method :message, ::Minitest::Assertions.instance_method(:message)
202+
private define_method :diff, ::Minitest::Assertions.instance_method(:diff)
203+
private define_method :things_to_diff, ::Minitest::Assertions.instance_method(:things_to_diff)
204+
private define_method :mu_pp_for_diff, ::Minitest::Assertions.instance_method(:mu_pp_for_diff)
205+
private define_method :mu_pp, ::Minitest::Assertions.instance_method(:mu_pp)
206+
207+
def flunk(message)
208+
failure! { message }
209+
end
210+
211+
def pass
212+
success!
213+
end
214+
end
215+
end
216+
end

0 commit comments

Comments
 (0)