spectus 3.0.10 → 3.1.0

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.
@@ -1,109 +1,84 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'aw'
4
-
5
3
  module Spectus
6
4
  # Namespace for the requirement levels.
7
- #
8
- # @api private
9
- #
10
5
  module RequirementLevel
11
6
  # Requirement level's base class.
12
- #
13
7
  class Base
14
8
  # Initialize the requirement level class.
15
9
  #
16
- # @param matcher [#matches?] The matcher.
17
- # @param negate [Boolean] Evaluate to a negative assertion.
18
- # @param subject [#object_id] The front object to test.
19
- # @param challenges [Array] A list of challenges.
20
- def initialize(matcher, negate, subject, *challenges)
21
- @matcher = matcher
22
- @negate = negate
23
- @subject = subject
24
- @challenges = challenges
10
+ # @param callable [#call] The callable object to test.
11
+ # @param isolation [Boolean] Compute actual in isolation?
12
+ # @param negate [Boolean] Positive or negative assertion?
13
+ # @param matcher [#matches?] The matcher.
14
+ def initialize(callable:, isolation:, negate:, matcher:)
15
+ @negate = negate
16
+ @matcher = matcher
17
+
18
+ @exam = Exam.new(
19
+ callable: callable,
20
+ isolation: isolation,
21
+ negate: negate,
22
+ matcher: matcher
23
+ )
25
24
  end
26
25
 
27
- # @!attribute [r] matcher
28
- #
29
- # @return [#matches?] The matcher.
26
+ # @return [#Exam] The exam.
27
+ attr_reader :exam
28
+
29
+ # @return [#matches?] The matcher that performed a boolean comparison
30
+ # between the actual value and the expected value.
30
31
  attr_reader :matcher
31
32
 
32
- # The value of the negate instance variable.
33
+ # The result of the expectation.
33
34
  #
34
- # @return [Boolean] Evaluated to a negative assertion or not.
35
- def negate?
36
- @negate
35
+ # @return [Result::Fail, Result::Pass] The test result.
36
+ def call
37
+ pass? ? pass! : fail!
37
38
  end
38
39
 
39
- # @!attribute [r] subject
40
- #
41
- # @return [#object_id] The front object to test.
42
- attr_reader :subject
43
-
44
- # @!attribute [r] challenges
45
- #
46
- # @return [Array] A list of challenges.
47
- attr_reader :challenges
48
-
49
40
  protected
50
41
 
51
- # @param state [Sandbox] The sandbox that tested the code.
52
- #
53
- # @return [Result::Pass] Pass the spec.
54
- def pass!(state)
55
- r = Report.new(matcher, negate?, state, true)
56
-
57
- Result::Pass.new(r, *result_signature(state))
42
+ # @return [Result::Pass] A passed spec result.
43
+ def pass!
44
+ Result::Pass.new(**details)
58
45
  end
59
46
 
60
- # @param state [Sandbox] The sandbox that tested the code.
61
- #
62
- # @raise [Result::Fail] Fail the spec.
63
- def fail!(state)
64
- r = Report.new(matcher, negate?, state, false)
65
-
66
- raise Result::Fail.new(r, *result_signature(state)), r, caller[2..-1]
47
+ # @raise [Result::Fail] A failed spec result.
48
+ def fail!
49
+ raise Result::Fail.new(**details)
67
50
  end
68
51
 
69
- # @param state [Sandbox] The sandbox that tested the code.
70
- #
71
- # @return [Array] List of parameters.
72
- def result_signature(state)
73
- [
74
- subject,
75
- state.last_challenge,
76
- state.actual,
77
- matcher,
78
- state.got,
79
- state.exception,
80
- level,
81
- negate?,
82
- state.valid?
83
- ]
52
+ # @return [Hash] List of parameters.
53
+ def details
54
+ {
55
+ actual: exam.actual,
56
+ error: exam.exception,
57
+ expected: matcher.expected,
58
+ got: exam.got,
59
+ negate: negate?,
60
+ valid: exam.valid?,
61
+ matcher: matcher.class.to_sym,
62
+ level: level
63
+ }
84
64
  end
85
65
 
86
66
  # @return [Symbol] The requirement level.
87
67
  def level
88
- self.class.name.split('::').last.to_sym
68
+ self.class.name.split('::').fetch(-1).upcase.to_sym
89
69
  end
90
70
 
91
- # @param isolation [Boolean] Test in isolation.
71
+ # @note The boolean comparison between the actual value and the expected
72
+ # value can be evaluated to a negative assertion.
92
73
  #
93
- # @return [Sandbox] The sandbox.
94
- def sandbox(isolation)
95
- isolation ? ::Aw.fork! { execute } : execute
96
- end
97
-
98
- # @return [Sandbox] The sandbox.
99
- def execute
100
- Sandbox.new(matcher, negate?, subject, *challenges)
74
+ # @return [Boolean] Positive or negative assertion?
75
+ def negate?
76
+ @negate
101
77
  end
102
78
  end
103
79
  end
104
80
  end
105
81
 
106
- require_relative File.join('..', 'report')
82
+ require_relative File.join('..', 'exam')
107
83
  require_relative File.join('..', 'result', 'fail')
108
84
  require_relative File.join('..', 'result', 'pass')
109
- require_relative File.join('..', 'sandbox')
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'must'
4
+
5
+ module Spectus
6
+ module RequirementLevel
7
+ # May requirement level's class.
8
+ class May < Must
9
+ # Evaluate the expectation.
10
+ #
11
+ # @return [Boolean] Report if the low expectation pass or fail?
12
+ def pass?
13
+ super || exam.exception.is_a?(::NoMethodError)
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+
5
+ module Spectus
6
+ module RequirementLevel
7
+ # Must requirement level's class.
8
+ class Must < Base
9
+ # Evaluate the expectation.
10
+ #
11
+ # @return [Boolean] Report if the high expectation pass or fail?
12
+ def pass?
13
+ exam.valid?
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'must'
4
+
5
+ module Spectus
6
+ module RequirementLevel
7
+ # Should requirement level's class.
8
+ class Should < Must
9
+ # Evaluate the expectation.
10
+ #
11
+ # @return [Boolean] Report if the medium expectation pass or fail?
12
+ def pass?
13
+ super || exam.exception.nil?
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,174 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Spectus
4
+ # Namespace for the results.
5
+ module Result
6
+ # Common collection of methods for Result's classes.
7
+ module Common
8
+ # @return [#object_id] Returned value by the challenged subject.
9
+ attr_reader :actual
10
+
11
+ # @return [Exception, nil] Any possible raised exception.
12
+ attr_reader :error
13
+
14
+ # @return [#object_id] The expected value.
15
+ attr_reader :expected
16
+
17
+ # @return [#object_id] The result of the boolean comparison between the
18
+ # actual value and the expected value through the matcher.
19
+ attr_reader :got
20
+
21
+ # @return [#object_id] The matcher.
22
+ attr_reader :matcher
23
+
24
+ # @return [:MUST, :SHOULD, :MAY] The requirement level of the expectation.
25
+ attr_reader :level
26
+
27
+ # Common initialize method.
28
+ #
29
+ # @param actual [#object_id] Returned value by the challenged subject.
30
+ # @param error [Exception, nil] Any possible raised exception.
31
+ # @param expected [#object_id] The expected value.
32
+ # @param got [Boolean, nil] The result of the boolean comparison
33
+ # between the actual value and the expected value through the matcher.
34
+ # @param negate [Boolean] Evaluated to a negative assertion?
35
+ # @param valid [Boolean] Report if the test was true or false?
36
+ # @param matcher [Symbol] The matcher.
37
+ # @param level [:MUST, :SHOULD, :MAY] The requirement level.
38
+ def initialize(actual:, error:, expected:, got:, negate:, valid:,
39
+ matcher:, level:)
40
+
41
+ @actual = actual
42
+ @error = error
43
+ @expected = expected
44
+ @got = got
45
+ @negate = negate
46
+ @valid = valid
47
+ @matcher = matcher
48
+ @level = level
49
+
50
+ super(to_s) if failed?
51
+ end
52
+
53
+ # The value of the negate instance variable.
54
+ #
55
+ # @return [Boolean] Evaluated to a negative assertion?
56
+ def negate?
57
+ @negate
58
+ end
59
+
60
+ # The state of error.
61
+ #
62
+ # @return [Boolean] The test raised an error?
63
+ def error?
64
+ !error.nil?
65
+ end
66
+
67
+ # The state of success.
68
+ #
69
+ # @return [Boolean] The test was a success?
70
+ def success?
71
+ got.equal?(true)
72
+ end
73
+
74
+ # The value of the boolean comparison between the actual value and the
75
+ # expected value.
76
+ #
77
+ # @return [Boolean] The test was true or false?
78
+ def valid?
79
+ @valid
80
+ end
81
+
82
+ # A string containing a human-readable representation of the result.
83
+ #
84
+ # @return [String] The human-readable representation of the result.
85
+ def inspect
86
+ "#{self.class}(actual: #{actual.inspect}, " \
87
+ "error: #{error.inspect}, " \
88
+ "expected: #{expected.inspect}, " \
89
+ "got: #{got.inspect}, " \
90
+ "matcher: #{matcher.inspect}, " \
91
+ "negate: #{negate?.inspect}, " \
92
+ "level: #{level.inspect}, " \
93
+ "valid: #{valid?.inspect})" \
94
+ end
95
+
96
+ # The readable definition.
97
+ #
98
+ # @return [String] A readable string of the definition.
99
+ def definition
100
+ [matcher, expected&.inspect].compact.join(' ')
101
+ end
102
+
103
+ # The negation, if any.
104
+ #
105
+ # @return [String] The negation, or an empty string.
106
+ def maybe_negate
107
+ negate? ? ' not' : ''
108
+ end
109
+
110
+ # The summary of the result.
111
+ #
112
+ # @return [String] A string representing the summary of the result.
113
+ def summary
114
+ if error?
115
+ error.message
116
+ elsif actual.is_a?(::Exception)
117
+ actual.message
118
+ elsif actual == expected
119
+ "expected#{maybe_negate} to #{definition}"
120
+ else
121
+ "expected #{actual.inspect}#{maybe_negate} to #{definition}"
122
+ end
123
+ end
124
+
125
+ # Express the result with one colored char.
126
+ #
127
+ # @return [String] The colored char that identify the result.
128
+ def colored_char
129
+ color(char)
130
+ end
131
+
132
+ # The colored string representation of the result.
133
+ #
134
+ # @return [String] A string representing the result.
135
+ def colored_string
136
+ color(to_s)
137
+ end
138
+
139
+ # The representation of the result.
140
+ #
141
+ # @return [String] A string representing the result.
142
+ def to_s
143
+ "#{titre}: #{summary}."
144
+ end
145
+
146
+ # Titre for the result.
147
+ #
148
+ # @return [String] A string representing the titre.
149
+ def titre
150
+ if error?
151
+ error.class.name
152
+ else
153
+ to_sym.to_s.capitalize
154
+ end
155
+ end
156
+
157
+ protected
158
+
159
+ def color(str)
160
+ if success?
161
+ "\e[32m#{str}\e[0m"
162
+ elsif info?
163
+ "\e[36m#{str}\e[0m"
164
+ elsif warning?
165
+ "\e[33m#{str}\e[0m"
166
+ elsif failure?
167
+ "\e[35m#{str}\e[0m"
168
+ else
169
+ "\e[31m#{str}\e[0m"
170
+ end
171
+ end
172
+ end
173
+ end
174
+ end
@@ -1,32 +1,46 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'base'
3
+ require_relative 'common'
4
4
 
5
5
  module Spectus
6
6
  module Result
7
7
  # The class that is responsible for reporting that the expectation is false.
8
8
  class Fail < ::StandardError
9
- include Base
9
+ include Common
10
10
 
11
- # The value of the expectation of the spec.
11
+ # Did the test fail?
12
12
  #
13
- # @return [Boolean] The spec was false.
14
- def result?
15
- false
13
+ # @return [Boolean] The spec passed or failed?
14
+ def failed?
15
+ true
16
+ end
17
+
18
+ # Did the test pass?
19
+ #
20
+ # @return [Boolean] The spec passed or failed?
21
+ def passed?
22
+ !failed?
16
23
  end
17
24
 
18
25
  # The state of failure.
19
26
  #
20
- # @return [Boolean] The test was a failure.
27
+ # @return [Boolean] The test was a failure?
21
28
  def failure?
22
- error.nil?
29
+ !error?
30
+ end
31
+
32
+ # The state of info.
33
+ #
34
+ # @return [Boolean] The test was an info?
35
+ def info?
36
+ false
23
37
  end
24
38
 
25
- # The state of error.
39
+ # The state of warning.
26
40
  #
27
- # @return [Boolean] The test was an error.
28
- def error?
29
- !failure?
41
+ # @return [Boolean] The test was a warning?
42
+ def warning?
43
+ false
30
44
  end
31
45
 
32
46
  # Identify the state of the result.
@@ -38,14 +52,23 @@ module Spectus
38
52
 
39
53
  # Express the result with one char.
40
54
  #
41
- # @param color [Boolean] Enable the color.
42
- #
43
55
  # @return [String] The char that identify the result.
44
- def to_char(color = false)
56
+ def char
57
+ if failure?
58
+ 'F'
59
+ else
60
+ 'E'
61
+ end
62
+ end
63
+
64
+ # Express the result with one emoji.
65
+ #
66
+ # @return [String] The emoji that identify the result.
67
+ def emoji
45
68
  if failure?
46
- color ? "\e[35mF\e[0m" : 'F'
69
+ ''
47
70
  else
48
- color ? "\e[31mE\e[0m" : 'E'
71
+ '💥'
49
72
  end
50
73
  end
51
74
  end