loupe 0.1.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -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