spectus 3.0.10 → 3.1.4

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