loupe 0.1.5
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.
- checksums.yaml +7 -0
- data/LICENSE.txt +71 -0
- data/README.md +101 -0
- data/Rakefile +16 -0
- data/exe/loupe +9 -0
- data/lib/loupe/cli.rb +83 -0
- data/lib/loupe/color.rb +31 -0
- data/lib/loupe/executor.rb +46 -0
- data/lib/loupe/expectation.rb +538 -0
- data/lib/loupe/failure.rb +49 -0
- data/lib/loupe/paged_reporter.rb +195 -0
- data/lib/loupe/plain_reporter.rb +27 -0
- data/lib/loupe/process_executor.rb +56 -0
- data/lib/loupe/queue_server.rb +49 -0
- data/lib/loupe/ractor_executor.rb +52 -0
- data/lib/loupe/rake_task.rb +47 -0
- data/lib/loupe/reporter.rb +106 -0
- data/lib/loupe/test.rb +270 -0
- data/lib/loupe/version.rb +6 -0
- data/lib/loupe.rb +18 -0
- metadata +95 -0
|
@@ -0,0 +1,538 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Loupe's expectations and execution flow are heavily inspired by or adapted from Minitest and rspec-expectations
|
|
4
|
+
# implementations. The originals licenses can be found below.
|
|
5
|
+
#
|
|
6
|
+
# Minitest
|
|
7
|
+
# https://github.com/seattlerb/minitest
|
|
8
|
+
#
|
|
9
|
+
# (The MIT License)
|
|
10
|
+
#
|
|
11
|
+
# Copyright © Ryan Davis, seattle.rb
|
|
12
|
+
#
|
|
13
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
|
|
14
|
+
# documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the
|
|
15
|
+
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
|
|
16
|
+
# permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
17
|
+
#
|
|
18
|
+
# The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
|
|
19
|
+
# Software.
|
|
20
|
+
#
|
|
21
|
+
# THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
|
22
|
+
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
|
|
23
|
+
# OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
24
|
+
# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
25
|
+
|
|
26
|
+
# Rspec-expectations
|
|
27
|
+
#
|
|
28
|
+
# https://github.com/rspec/rspec-expectations
|
|
29
|
+
#
|
|
30
|
+
# The MIT License (MIT)
|
|
31
|
+
#
|
|
32
|
+
# Copyright © 2012 David Chelimsky, Myron Marston Copyright © 2006 David Chelimsky, The RSpec Development Team
|
|
33
|
+
# Copyright © 2005 Steven Baker
|
|
34
|
+
#
|
|
35
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
|
|
36
|
+
# documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
|
|
37
|
+
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
|
|
38
|
+
# permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
39
|
+
#
|
|
40
|
+
# The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
|
|
41
|
+
# Software.
|
|
42
|
+
#
|
|
43
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
|
44
|
+
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
|
|
45
|
+
# OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
46
|
+
# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
47
|
+
|
|
48
|
+
module Loupe
|
|
49
|
+
# Expectation
|
|
50
|
+
#
|
|
51
|
+
# This class is responsible for instantiating a target
|
|
52
|
+
# so that expectations can be invoked on it.
|
|
53
|
+
#
|
|
54
|
+
# Example:
|
|
55
|
+
# expectation = Expectation.new(target, test)
|
|
56
|
+
# expectation.to_be_empty
|
|
57
|
+
#
|
|
58
|
+
# The goal of ths class is so that expectations can be written
|
|
59
|
+
# in such a way that they read in plain English.
|
|
60
|
+
# E.g.: expect(something).to_be_truthy
|
|
61
|
+
#
|
|
62
|
+
# @see Loupe::Test#expect
|
|
63
|
+
class Expectation # rubocop:disable Metrics/ClassLength
|
|
64
|
+
class ExpectationFailed < StandardError; end
|
|
65
|
+
|
|
66
|
+
# @param target [BasicObject]
|
|
67
|
+
# @param test [Loupe::Test]
|
|
68
|
+
# @return [Loupe::Expectation]
|
|
69
|
+
def initialize(target, test)
|
|
70
|
+
@target = target
|
|
71
|
+
@color = test.color
|
|
72
|
+
@test = test
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# expect(target).to_be_truthy
|
|
76
|
+
#
|
|
77
|
+
# Expects `target` to be a truthy value (not `nil` or `false`).
|
|
78
|
+
#
|
|
79
|
+
# @return [Loupe::Expectation]
|
|
80
|
+
def to_be_truthy
|
|
81
|
+
assert(@target, "Expected #{@color.p(@target.inspect, :red)} to be #{@color.p('truthy', :green)}.")
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# expect(target).to_be_falsey
|
|
85
|
+
#
|
|
86
|
+
# Expects `target` to be a falsey value (`nil` or `false`).
|
|
87
|
+
#
|
|
88
|
+
# @return [Loupe::Expectation]
|
|
89
|
+
def to_be_falsey
|
|
90
|
+
assert(!@target, "Expected #{@color.p(@target.inspect, :red)} to be #{@color.p('falsey', :green)}.")
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# expect(target).to_be_equal_to(value)
|
|
94
|
+
#
|
|
95
|
+
# Expects `target` to be equal to `value`. Compares if the objects are equal in terms of values
|
|
96
|
+
# but not if the objects are the exact same instance. For comparing identity, use {#to_be_the_same_as}.
|
|
97
|
+
#
|
|
98
|
+
# @param value [#==]
|
|
99
|
+
# @return [Loupe::Expectation]
|
|
100
|
+
def to_be_equal_to(value)
|
|
101
|
+
assert(
|
|
102
|
+
@target == value,
|
|
103
|
+
"Expected #{@color.p(@target.inspect, :red)} to be equal to #{@color.p(value.inspect, :green)}."
|
|
104
|
+
)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# expect(target).to_not_be_equal_to(value)
|
|
108
|
+
#
|
|
109
|
+
# Expects `target` to not be equal to `value`. Compares if the objects are different in terms of values,
|
|
110
|
+
# but not if the objects are the different instances. For comparing identity, use {#to_not_be_the_same_as}.
|
|
111
|
+
#
|
|
112
|
+
# @param value [#!=]
|
|
113
|
+
# @return [Loupe::Expectation]
|
|
114
|
+
def to_not_be_equal_to(value)
|
|
115
|
+
assert(
|
|
116
|
+
@target != value,
|
|
117
|
+
"Expected #{@color.p(@target.inspect, :red)} to not be equal to #{@color.p(value.inspect, :green)}."
|
|
118
|
+
)
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# expect(target).to_be_empty
|
|
122
|
+
#
|
|
123
|
+
# Expects `target` to be empty. Which means invoking `empty?` must return `true`.
|
|
124
|
+
#
|
|
125
|
+
# @return [Loupe::Expectation]
|
|
126
|
+
def to_be_empty
|
|
127
|
+
assert(@target.empty?, "Expected #{@color.p(@target.inspect, :red)} to be empty.")
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
# expect(target).to_not_be_empty
|
|
131
|
+
#
|
|
132
|
+
# Expects `target` to not be empty. Which means invoking `empty?` must return `false`.
|
|
133
|
+
#
|
|
134
|
+
# @return [Loupe::Expectation]
|
|
135
|
+
def to_not_be_empty
|
|
136
|
+
assert(!@target.empty?, "Expected #{@color.p(@target.inspect, :red)} to not be empty.")
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
# expect(target).to_respond_to(method)
|
|
140
|
+
#
|
|
141
|
+
# Expects `target` to respond to `method`. This expectation passes if `method` exists in `target`.
|
|
142
|
+
#
|
|
143
|
+
# @param method [String, Symbol]
|
|
144
|
+
# @return [Loupe::Expectation]
|
|
145
|
+
def to_respond_to(method)
|
|
146
|
+
assert(
|
|
147
|
+
@target.respond_to?(method.to_sym),
|
|
148
|
+
"Expected #{@color.p(@target.inspect, :red)} to respond to #{@color.p(method, :green)}."
|
|
149
|
+
)
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
# expect(target).to_not_respond_to(method)
|
|
153
|
+
#
|
|
154
|
+
# Expects `target` to not respond to `method`. This expectation passes if `method` does not exist in `target`.
|
|
155
|
+
#
|
|
156
|
+
# @param method [String, Symbol]
|
|
157
|
+
# @return [Loupe::Expectation]
|
|
158
|
+
def to_not_respond_to(method)
|
|
159
|
+
assert(
|
|
160
|
+
!@target.respond_to?(method.to_sym),
|
|
161
|
+
"Expected #{@color.p(@target.inspect, :red)} to not respond to #{@color.p(method, :green)}."
|
|
162
|
+
)
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
# expect(target).to_include(object)
|
|
166
|
+
#
|
|
167
|
+
# Expects `target` to include `object`. In this expectation, `target` is typically a collection
|
|
168
|
+
# or another object that responds to `include?`. The expectation passes if `target` includes `object`.
|
|
169
|
+
#
|
|
170
|
+
# @param object [BasicObject]
|
|
171
|
+
# @return [Loupe::Expectation]
|
|
172
|
+
def to_include(object)
|
|
173
|
+
assert(
|
|
174
|
+
@target.include?(object),
|
|
175
|
+
"Expected #{@color.p(@target.inspect, :red)} to include #{@color.p(object, :green)}."
|
|
176
|
+
)
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
# expect(target).to_not_include(object)
|
|
180
|
+
#
|
|
181
|
+
# Expects `target` to not include `object`. In this expectation, `target` is typically a collection
|
|
182
|
+
# or another object that responds to `include?`. The expectation passes if `target` does not include `object`.
|
|
183
|
+
#
|
|
184
|
+
# @param object [BasicObject]
|
|
185
|
+
# @return [Loupe::Expectation]
|
|
186
|
+
def to_not_include(object)
|
|
187
|
+
assert(
|
|
188
|
+
!@target.include?(object),
|
|
189
|
+
"Expected #{@color.p(@target.inspect, :red)} to not include #{@color.p(object, :green)}."
|
|
190
|
+
)
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
# expect(target).to_be_nil
|
|
194
|
+
#
|
|
195
|
+
# Expects `target` to be `nil`.
|
|
196
|
+
#
|
|
197
|
+
# @return [Loupe::Expectation]
|
|
198
|
+
def to_be_nil
|
|
199
|
+
assert(@target.nil?, "Expected #{@color.p(@target.inspect, :red)} to be nil.")
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
# expect(target).to_not_be_nil
|
|
203
|
+
#
|
|
204
|
+
# Expects `target` not to be `nil`.
|
|
205
|
+
#
|
|
206
|
+
# @return [Loupe::Expectation]
|
|
207
|
+
def to_not_be_nil
|
|
208
|
+
assert(!@target.nil?, "Expected #{@color.p(@target.inspect, :red)} to not be nil.")
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
# expect(target).to_be_an_instance_of(klass)
|
|
212
|
+
#
|
|
213
|
+
# Expects `target` to be an instance of `klass`. For example, "ruby is awesome" is
|
|
214
|
+
# an instance of `String` and will pass the expectation.
|
|
215
|
+
#
|
|
216
|
+
# @param klass [Class]
|
|
217
|
+
# @return [Loupe::Expectation]
|
|
218
|
+
def to_be_an_instance_of(klass)
|
|
219
|
+
failure_message = "Expected #{@color.p(@target.inspect, :red)} to be an instance " \
|
|
220
|
+
"of #{@color.p(klass, :green)}, not #{@color.p(@target.inspect.class, :red)}."
|
|
221
|
+
|
|
222
|
+
assert(@target.instance_of?(klass), failure_message)
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
# expect(target).to_not_be_an_instance_of(klass)
|
|
226
|
+
#
|
|
227
|
+
# Expects `target` to not be an instance of `klass`. For example, "ruby is awesome" is
|
|
228
|
+
# not an instance of `Integer` and will not pass the expectation.
|
|
229
|
+
#
|
|
230
|
+
# @param klass [Class]
|
|
231
|
+
# @return [Loupe::Expectation]
|
|
232
|
+
def to_not_be_an_instance_of(klass)
|
|
233
|
+
failure_message = "Expected #{@color.p(@target.inspect, :red)} to not be an instance of " \
|
|
234
|
+
"#{@color.p(klass, :green)}, not #{@color.p(@target.inspect.class, :red)}."
|
|
235
|
+
|
|
236
|
+
assert(!@target.instance_of?(klass), failure_message)
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
# expect(target).to_be_a_kind_of(klass)
|
|
240
|
+
#
|
|
241
|
+
# Expects `target` to be a kind of `klass`. Which means that if `target.is_a?(klass)` returns true,
|
|
242
|
+
# the expectation will pass.
|
|
243
|
+
#
|
|
244
|
+
# @param klass [Class]
|
|
245
|
+
# @return [Loupe::Expectation]
|
|
246
|
+
def to_be_a_kind_of(klass)
|
|
247
|
+
failure_message = "Expected #{@color.p(@target.inspect, :red)} to be a kind of " \
|
|
248
|
+
"#{@color.p(klass, :green)}, not #{@color.p(@target.inspect.class, :red)}."
|
|
249
|
+
|
|
250
|
+
assert(@target.is_a?(klass), failure_message)
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
# expect(target).to_not_be_a_kind_of(klass)
|
|
254
|
+
#
|
|
255
|
+
# Expects `target` to not be a kind of `klass`. Which means that if `target.is_a?(klass)` returns false,
|
|
256
|
+
# the expectation will pass.
|
|
257
|
+
#
|
|
258
|
+
# @param klass [Class]
|
|
259
|
+
# @return [Loupe::Expectation]
|
|
260
|
+
def to_not_be_a_kind_of(klass)
|
|
261
|
+
failure_message = "Expected #{@color.p(@target.inspect, :red)} to not be a kind of " \
|
|
262
|
+
"#{@color.p(klass, :green)}, not #{@color.p(@target.inspect.class, :red)}."
|
|
263
|
+
|
|
264
|
+
assert(!@target.is_a?(klass), failure_message)
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
# expect(target).to_be(predicate)
|
|
268
|
+
#
|
|
269
|
+
# Expects `target` to return `true` for a given `predicate`. In this expectation, `predicate` is a method name and
|
|
270
|
+
# the expectation will pass if invoking the method on `target` returns `true`.
|
|
271
|
+
#
|
|
272
|
+
# Example:
|
|
273
|
+
# Verify if the result of a calculation is an odd number. The `target` is the calculation result
|
|
274
|
+
# and the `predicate` is the method `odd?`.
|
|
275
|
+
#
|
|
276
|
+
# calculation_result = a_complex_math_operation
|
|
277
|
+
# expect(calculation_result).to_be(:odd?)
|
|
278
|
+
#
|
|
279
|
+
# @param predicate [String, Symbol]
|
|
280
|
+
# @return [Loupe::Expectation]
|
|
281
|
+
def to_be(predicate)
|
|
282
|
+
assert(
|
|
283
|
+
@target.public_send(predicate),
|
|
284
|
+
"Expected #{@color.p(@target.inspect, :red)} to be #{@color.p(predicate, :green)}."
|
|
285
|
+
)
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
# expect(target).to_not_be(predicate)
|
|
289
|
+
#
|
|
290
|
+
# Expects `target` to return `false` for a given `predicate`. In this expectation, `predicate` is a method name and
|
|
291
|
+
# the expectation will pass if invoking the method on `target` returns `false`.
|
|
292
|
+
#
|
|
293
|
+
# Example:
|
|
294
|
+
# Verify if the result of a calculation is not zero. The `target` is the calculation result
|
|
295
|
+
# and the `predicate` is the method `zero?`.
|
|
296
|
+
#
|
|
297
|
+
# calculation_result = a_complex_math_operation
|
|
298
|
+
# expect(calculation_result).to_not_be(:zero?)
|
|
299
|
+
#
|
|
300
|
+
# @param predicate [String, Symbol]
|
|
301
|
+
# @return [Loupe::Expectation]
|
|
302
|
+
def to_not_be(predicate)
|
|
303
|
+
assert(
|
|
304
|
+
!@target.public_send(predicate),
|
|
305
|
+
"Expected #{@color.p(@target.inspect, :red)} to not be #{@color.p(predicate, :green)}."
|
|
306
|
+
)
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
# expect(target).to_match(object)
|
|
310
|
+
#
|
|
311
|
+
# Expects `target` to match `object`. In this expectation, `target` is a matcher (string or a regex)
|
|
312
|
+
# that must match `object`. The matcher needs to respond to `=~` and needs to return `true` when it is
|
|
313
|
+
# invoked with `object`.
|
|
314
|
+
#
|
|
315
|
+
# Example:
|
|
316
|
+
# expect(/ruby .*/).to_match("ruby is awesome")
|
|
317
|
+
# expect("awesome").to_match("ruby is awesome")
|
|
318
|
+
#
|
|
319
|
+
# @param object [BasicObject]
|
|
320
|
+
# @return [Loupe::Expectation]
|
|
321
|
+
def to_match(object)
|
|
322
|
+
to_respond_to(:=~)
|
|
323
|
+
|
|
324
|
+
@target = Regexp.new(Regexp.escape(@target)) if @target.is_a?(String)
|
|
325
|
+
assert(@target =~ object, "Expected #{@color.p(@target.inspect, :red)} to match #{@color.p(object, :green)}.")
|
|
326
|
+
end
|
|
327
|
+
|
|
328
|
+
# expect(target).to_not_match(object)
|
|
329
|
+
#
|
|
330
|
+
# Expects `target` to not match `object`. In this expectation, `target` is a matcher (string or a regex)
|
|
331
|
+
# that must not match `object`. The matcher needs to respond to `=~` and needs to return `false` when it is
|
|
332
|
+
# invoked with `object`.
|
|
333
|
+
#
|
|
334
|
+
# Example:
|
|
335
|
+
# expect(/python .*/).to_not_match("ruby is awesome")
|
|
336
|
+
# expect("terrible").to_not_match("ruby is awesome")
|
|
337
|
+
#
|
|
338
|
+
# @param object [BasicObject]
|
|
339
|
+
# @return [Loupe::Expectation]
|
|
340
|
+
def to_not_match(object)
|
|
341
|
+
to_respond_to(:=~)
|
|
342
|
+
|
|
343
|
+
@target = Regexp.new(Regexp.escape(@target)) if @target.is_a?(String)
|
|
344
|
+
assert(@target !~ object, "Expected #{@color.p(@target.inspect, :red)} to not match #{@color.p(object, :green)}.")
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
# expect(target).to_be_the_same_as(object)
|
|
348
|
+
#
|
|
349
|
+
# Expects `target` to be the same as `object`. This means, `target` and `object` must be the exact same
|
|
350
|
+
# object, with the same `object_id`.
|
|
351
|
+
#
|
|
352
|
+
# @param object [BasicObject]
|
|
353
|
+
# @return [Loupe::Expectation]
|
|
354
|
+
def to_be_the_same_as(object)
|
|
355
|
+
failure_message = "Expected #{@color.p(@target.inspect, :red)} (#{@color.p(@target.inspect.object_id, :red)}) " \
|
|
356
|
+
"to be the same as #{@color.p(object, :green)} (#{@color.p(object.object_id, :green)})."
|
|
357
|
+
|
|
358
|
+
assert(@target.equal?(object), failure_message)
|
|
359
|
+
end
|
|
360
|
+
|
|
361
|
+
# expect(target).to_not_be_the_same_as(object)
|
|
362
|
+
#
|
|
363
|
+
# Expects `target` to not be the same as `object`. This means, `target` and `object` are not the exact
|
|
364
|
+
# same object with the same `object_id`.
|
|
365
|
+
#
|
|
366
|
+
# Note: this is an identity comparison. If the values of both objects are the same, but the object IDs are
|
|
367
|
+
# different, this expectation will pass.
|
|
368
|
+
#
|
|
369
|
+
# @param object [BasicObject]
|
|
370
|
+
# @return [Loupe::Expectation]
|
|
371
|
+
def to_not_be_the_same_as(object)
|
|
372
|
+
failure_message = "Expected #{@color.p(@target.inspect, :red)} (#{@color.p(@target.inspect.object_id, :red)}) " \
|
|
373
|
+
"to not be the same as #{@color.p(object, :green)} (#{@color.p(object.object_id, :green)})."
|
|
374
|
+
|
|
375
|
+
assert(!@target.equal?(object), failure_message)
|
|
376
|
+
end
|
|
377
|
+
|
|
378
|
+
# expect(target).to_be_an_existing_path
|
|
379
|
+
#
|
|
380
|
+
# Expects `target` to be an existing path in the file system. That means, invoking `File.exist?(target)`
|
|
381
|
+
# must return `true` for this expectation to pass.
|
|
382
|
+
#
|
|
383
|
+
# @return [Loupe::Expectation]
|
|
384
|
+
def to_be_an_existing_path
|
|
385
|
+
assert(File.exist?(@target), "Expected path '#{@color.p(@target.inspect, :red)}' to exist.")
|
|
386
|
+
end
|
|
387
|
+
|
|
388
|
+
# expect(target).to_not_be_an_existing_path
|
|
389
|
+
#
|
|
390
|
+
# Expects `target` to not be an existing path in the file system. That means, invoking `File.exist?(target)`
|
|
391
|
+
# must return `false` for this expectation to pass.
|
|
392
|
+
#
|
|
393
|
+
# @return [Loupe::Expectation]
|
|
394
|
+
def to_not_be_an_existing_path
|
|
395
|
+
assert(!File.exist?(@target), "Expected path '#{@color.p(@target.inspect, :red)}' to not exist.")
|
|
396
|
+
end
|
|
397
|
+
|
|
398
|
+
# expect(target).to_be_in_delta_of(value, delta = 0.001)
|
|
399
|
+
#
|
|
400
|
+
# Expects `target` to be within `delta` of `value`. This means that the absolute difference between
|
|
401
|
+
# `target` and `value` cannot be bigger than `delta`.
|
|
402
|
+
#
|
|
403
|
+
# Example:
|
|
404
|
+
# expect(5.0).to_be_in_delta_of(5.1, 0.2)
|
|
405
|
+
#
|
|
406
|
+
# @param value [Numeric]
|
|
407
|
+
# @param delta [Numeric]
|
|
408
|
+
# @return [Loupe::Expectation]
|
|
409
|
+
def to_be_in_delta_of(value, delta = 0.001)
|
|
410
|
+
difference = (@target - value).abs
|
|
411
|
+
|
|
412
|
+
failure_message = "Expected |#{@target} - #{value}| " \
|
|
413
|
+
"(#{@color.p(difference, :red)}) to be <= #{@color.p(delta, :green)}."
|
|
414
|
+
|
|
415
|
+
assert(delta >= difference, failure_message)
|
|
416
|
+
end
|
|
417
|
+
|
|
418
|
+
# expect(target).to_not_be_in_delta_of(value, delta = 0.001)
|
|
419
|
+
#
|
|
420
|
+
# Expects `target` to not be within `delta` of `value`. This means that the absolute difference between
|
|
421
|
+
# `target` and `value` must be bigger than `delta`.
|
|
422
|
+
#
|
|
423
|
+
# Example:
|
|
424
|
+
# expect(5.0).to_not_be_in_delta_of(5.1, 0.01)
|
|
425
|
+
#
|
|
426
|
+
# @param value [Numeric]
|
|
427
|
+
# @param delta [Numeric]
|
|
428
|
+
# @return [Loupe::Expectation]
|
|
429
|
+
def to_not_be_in_delta_of(value, delta = 0.001)
|
|
430
|
+
difference = (@target - value).abs
|
|
431
|
+
|
|
432
|
+
failure_message = "Expected |#{@target} - #{value}| " \
|
|
433
|
+
"(#{@color.p(difference, :red)}) to not be <= #{@color.p(delta, :green)}."
|
|
434
|
+
|
|
435
|
+
assert(delta <= difference, failure_message)
|
|
436
|
+
end
|
|
437
|
+
|
|
438
|
+
# expect(target).to_be_in_epsilon_of(value, epsilon = 0.001)
|
|
439
|
+
#
|
|
440
|
+
# Expects `target` to be within `epsilon` times the smallest absolute value between `value` and `target`.
|
|
441
|
+
# This expectation simply invokes {#to_be_in_delta_of},
|
|
442
|
+
# where the `delta` is `epsilon * [@target.abs, value.abs].min`.
|
|
443
|
+
#
|
|
444
|
+
# Example:
|
|
445
|
+
# The minimum absolute value between 5.0 and 5.1 is equal to 5.0.
|
|
446
|
+
# The delta is equal to 5.0 * 0.1, which is 0.5.
|
|
447
|
+
# The absolute difference 5.1 - 5.0 is 0.1.
|
|
448
|
+
# 0.1 is smaller than 0.5, therefore the expectation passes.
|
|
449
|
+
#
|
|
450
|
+
# expect(5.0).to_be_in_epsilon_of(5.1, 0.1)
|
|
451
|
+
#
|
|
452
|
+
# @param value [Numeric]
|
|
453
|
+
# @param epsilon [Numeric]
|
|
454
|
+
# @return [Loupe::Expectation]
|
|
455
|
+
def to_be_in_epsilon_of(value, epsilon = 0.001)
|
|
456
|
+
to_be_in_delta_of(value, [@target.abs, value.abs].min * epsilon)
|
|
457
|
+
end
|
|
458
|
+
|
|
459
|
+
# expect(target).to_not_be_in_epsilon_of(value, epsilon = 0.001)
|
|
460
|
+
#
|
|
461
|
+
# Expects `target` to not be within `epsilon` times the smallest absolute value between `value` and `target`.
|
|
462
|
+
# This expectation simply invokes {#to_not_be_in_delta_of},
|
|
463
|
+
# where the `delta` is `epsilon * [@target.abs, value.abs].min`.
|
|
464
|
+
#
|
|
465
|
+
# Example:
|
|
466
|
+
# The minimum absolute value between 5.0 and 5.1 is equal to 5.0.
|
|
467
|
+
# The delta is equal to 5.0 * 0.01, which is 0.05.
|
|
468
|
+
# The absolute difference 5.1 - 5.0 is 0.1.
|
|
469
|
+
# 0.1 is not smaller than 0.05, therefore the expectation passes.
|
|
470
|
+
#
|
|
471
|
+
# expect(5.0).to_not_be_in_epsilon_of(5.1, 0.01)
|
|
472
|
+
#
|
|
473
|
+
# @param value [Numeric]
|
|
474
|
+
# @param epsilon [Numeric]
|
|
475
|
+
# @return [Loupe::Expectation]
|
|
476
|
+
def to_not_be_in_epsilon_of(value, epsilon = 0.001)
|
|
477
|
+
to_not_be_in_delta_of(value, [@target.abs, value.abs].min * epsilon)
|
|
478
|
+
end
|
|
479
|
+
|
|
480
|
+
# expect(target).to_satisfy_operator(operator, other)
|
|
481
|
+
#
|
|
482
|
+
# Expects `target` to return true when the `operator` is applied on `other`.
|
|
483
|
+
# This expectation is used to verify operations between two objects.
|
|
484
|
+
#
|
|
485
|
+
# Example:
|
|
486
|
+
#
|
|
487
|
+
# expect(5.0).to_satisfy_operator(:>, 4.9)
|
|
488
|
+
#
|
|
489
|
+
# @param operator [Symbol]
|
|
490
|
+
# @param other [BasicObject]
|
|
491
|
+
# @return [Loupe::Expectation]
|
|
492
|
+
def to_satisfy_operator(operator, other)
|
|
493
|
+
return to_be(operator) unless other
|
|
494
|
+
|
|
495
|
+
failure_message = "Expected #{@color.p(@target.inspect, :red)} to be #{operator}" \
|
|
496
|
+
" #{@color.p(other, :green)}."
|
|
497
|
+
|
|
498
|
+
assert(@target.public_send(operator, other), failure_message)
|
|
499
|
+
end
|
|
500
|
+
|
|
501
|
+
# expect(target).to_not_satisfy_operator(operator, other)
|
|
502
|
+
#
|
|
503
|
+
# Expects `target` to return false when the `operator` is applied on `other`.
|
|
504
|
+
# This expectation is used to verify operations between two objects.
|
|
505
|
+
#
|
|
506
|
+
# Example:
|
|
507
|
+
#
|
|
508
|
+
# expect(5.0).to_not_satisfy_operator(:<, 4.9)
|
|
509
|
+
#
|
|
510
|
+
# @param operator [Symbol]
|
|
511
|
+
# @param other [BasicObject]
|
|
512
|
+
# @return [Loupe::Expectation]
|
|
513
|
+
def to_not_satisfy_operator(operator, other)
|
|
514
|
+
return to_not_be(operator) unless other
|
|
515
|
+
|
|
516
|
+
failure_message = "Expected #{@color.p(@target.inspect, :red)} to not be #{operator}" \
|
|
517
|
+
" #{@color.p(other, :green)}."
|
|
518
|
+
|
|
519
|
+
assert(!@target.public_send(operator, other), failure_message)
|
|
520
|
+
end
|
|
521
|
+
|
|
522
|
+
private
|
|
523
|
+
|
|
524
|
+
# Base assertion for all expectations. This is where result are passed to the reported
|
|
525
|
+
# and where the test execution is halted if any expectation fails.
|
|
526
|
+
#
|
|
527
|
+
# @param value [BasicObject]
|
|
528
|
+
# @param failure_message [String]
|
|
529
|
+
# @return [Loupe::Expectation]
|
|
530
|
+
def assert(value, failure_message)
|
|
531
|
+
@test.reporter.increment_expectation_count
|
|
532
|
+
return self if value
|
|
533
|
+
|
|
534
|
+
@test.reporter.increment_failure_count(@test, failure_message)
|
|
535
|
+
raise ExpectationFailed
|
|
536
|
+
end
|
|
537
|
+
end
|
|
538
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Loupe
|
|
4
|
+
# Failure
|
|
5
|
+
#
|
|
6
|
+
# This class represents a single test failure. It corresponds
|
|
7
|
+
# to one method that was executed and had failed expectations.
|
|
8
|
+
class Failure
|
|
9
|
+
# @return [String]
|
|
10
|
+
attr_reader :file_name
|
|
11
|
+
|
|
12
|
+
# @return [String]
|
|
13
|
+
attr_reader :test_name
|
|
14
|
+
|
|
15
|
+
# @return [String]
|
|
16
|
+
attr_reader :message
|
|
17
|
+
|
|
18
|
+
# @return [Integer]
|
|
19
|
+
attr_reader :line_number
|
|
20
|
+
|
|
21
|
+
# @return [Class]
|
|
22
|
+
attr_reader :klass
|
|
23
|
+
|
|
24
|
+
# @param test [Loupe::Test]
|
|
25
|
+
# @param message [String]
|
|
26
|
+
# @return [Loupe::Failure]
|
|
27
|
+
def initialize(test, message)
|
|
28
|
+
@file_name = test.file
|
|
29
|
+
@test_name = test.name
|
|
30
|
+
@line_number = test.line_number
|
|
31
|
+
@klass = test.class
|
|
32
|
+
@color = test.color
|
|
33
|
+
@message = message
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# @return [String]
|
|
37
|
+
def to_s
|
|
38
|
+
"#{file_name}:#{line_number} at #{@color.p(test_name, :yellow)}. #{message}"
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# @return [Array<String>]
|
|
42
|
+
def location_and_message
|
|
43
|
+
[
|
|
44
|
+
"#{file_name}:#{line_number} at #{@color.p(test_name, :yellow)}",
|
|
45
|
+
message
|
|
46
|
+
]
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|