fix 0.21 → 1.0.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/LICENSE.md +2 -2
- data/README.md +64 -397
- data/lib/fix/context.rb +147 -0
- data/lib/fix/expectation_result_not_found_error.rb +5 -0
- data/lib/fix/it.rb +31 -0
- data/lib/fix/suspicious_success_error.rb +5 -0
- data/lib/fix.rb +15 -169
- data/lib/kernel.rb +2 -96
- metadata +94 -39
- data/lib/fix/doc.rb +0 -118
- data/lib/fix/dsl.rb +0 -139
- data/lib/fix/error/invalid_specification_name.rb +0 -12
- data/lib/fix/error/missing_specification_block.rb +0 -14
- data/lib/fix/error/missing_subject_block.rb +0 -15
- data/lib/fix/error/specification_not_found.rb +0 -12
- data/lib/fix/matcher.rb +0 -76
- data/lib/fix/requirement.rb +0 -119
- data/lib/fix/run.rb +0 -88
- data/lib/fix/set.rb +0 -224
data/lib/fix/set.rb
DELETED
@@ -1,224 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative "doc"
|
4
|
-
require_relative "dsl"
|
5
|
-
require_relative "run"
|
6
|
-
require_relative "error/missing_subject_block"
|
7
|
-
|
8
|
-
module Fix
|
9
|
-
# A Set represents a collection of test specifications that can be executed as a test suite.
|
10
|
-
# It manages the lifecycle of specifications, including:
|
11
|
-
# - Building and loading specifications from contexts
|
12
|
-
# - Executing specifications in isolation using process forking
|
13
|
-
# - Reporting test results
|
14
|
-
# - Managing test execution flow and exit status
|
15
|
-
#
|
16
|
-
# @example Creating and running a simple test set
|
17
|
-
# set = Fix::Set.build(:Calculator) do
|
18
|
-
# on(:add, 2, 3) do
|
19
|
-
# it MUST eq 5
|
20
|
-
# end
|
21
|
-
# end
|
22
|
-
# set.test { Calculator.new }
|
23
|
-
#
|
24
|
-
# @example Loading and running a registered test set
|
25
|
-
# set = Fix::Set.load(:Calculator)
|
26
|
-
# set.match? { Calculator.new } #=> true
|
27
|
-
#
|
28
|
-
# @api private
|
29
|
-
class Set
|
30
|
-
# Builds a new Set from a specification block.
|
31
|
-
#
|
32
|
-
# This method:
|
33
|
-
# 1. Creates a new DSL class for the specification
|
34
|
-
# 2. Evaluates the specification block in this context
|
35
|
-
# 3. Optionally registers the specification under a name
|
36
|
-
# 4. Returns a Set instance ready for testing
|
37
|
-
#
|
38
|
-
# @param name [Symbol, nil] Optional name to register the specification under
|
39
|
-
# @yield Block containing the specification definition using Fix DSL
|
40
|
-
# @return [Fix::Set] A new specification set ready for testing
|
41
|
-
#
|
42
|
-
# @example Building a named specification
|
43
|
-
# Fix::Set.build(:Calculator) do
|
44
|
-
# on(:add, 2, 3) { it MUST eq 5 }
|
45
|
-
# end
|
46
|
-
#
|
47
|
-
# @example Building an anonymous specification
|
48
|
-
# Fix::Set.build(nil) do
|
49
|
-
# it MUST be_positive
|
50
|
-
# end
|
51
|
-
#
|
52
|
-
# @api private
|
53
|
-
def self.build(name, &block)
|
54
|
-
klass = ::Class.new(Dsl)
|
55
|
-
klass.const_set(:CONTEXTS, [klass])
|
56
|
-
klass.instance_eval(&block)
|
57
|
-
Doc.const_set(name, klass) unless name.nil?
|
58
|
-
new(*klass.const_get(:CONTEXTS))
|
59
|
-
end
|
60
|
-
|
61
|
-
# Loads a previously registered specification set by name.
|
62
|
-
#
|
63
|
-
# @param name [Symbol] The name of the registered specification
|
64
|
-
# @return [Fix::Set] The loaded specification set
|
65
|
-
# @raise [NameError] If the specification name is not found
|
66
|
-
#
|
67
|
-
# @example Loading a registered specification
|
68
|
-
# Fix::Set.load(:Calculator) #=> #<Fix::Set:...>
|
69
|
-
#
|
70
|
-
# @api private
|
71
|
-
def self.load(name)
|
72
|
-
new(*Doc.fetch(name))
|
73
|
-
end
|
74
|
-
|
75
|
-
# Initializes a new Set with given test contexts.
|
76
|
-
#
|
77
|
-
# The contexts are processed to extract test specifications and
|
78
|
-
# randomized to ensure test isolation and catch order dependencies.
|
79
|
-
#
|
80
|
-
# @param contexts [Array<Fix::Dsl>] List of specification contexts to include
|
81
|
-
#
|
82
|
-
# @example Creating a set with multiple contexts
|
83
|
-
# Fix::Set.new(base_context, admin_context, guest_context)
|
84
|
-
def initialize(*contexts)
|
85
|
-
@expected = Doc.extract_specifications(*contexts).shuffle
|
86
|
-
end
|
87
|
-
|
88
|
-
# Verifies if a subject matches all specifications without exiting.
|
89
|
-
#
|
90
|
-
# This method is useful for:
|
91
|
-
# - Conditional testing where exit on failure is not desired
|
92
|
-
# - Integration into larger test suites
|
93
|
-
# - Programmatic test result handling
|
94
|
-
#
|
95
|
-
# @yield Block that returns the subject to test
|
96
|
-
# @yieldreturn [Object] The subject to test against specifications
|
97
|
-
# @return [Boolean] true if all tests pass, false otherwise
|
98
|
-
# @raise [Error::MissingSubjectBlock] If no subject block is provided
|
99
|
-
#
|
100
|
-
# @example Basic matching
|
101
|
-
# set.match? { Calculator.new } #=> true
|
102
|
-
#
|
103
|
-
# @example Conditional testing
|
104
|
-
# if set.match? { user_input }
|
105
|
-
# process_valid_input(user_input)
|
106
|
-
# else
|
107
|
-
# handle_invalid_input
|
108
|
-
# end
|
109
|
-
#
|
110
|
-
# @api public
|
111
|
-
def match?(&subject)
|
112
|
-
raise Error::MissingSubjectBlock unless subject
|
113
|
-
|
114
|
-
expected.all? { |spec| run_spec(*spec, &subject) }
|
115
|
-
end
|
116
|
-
|
117
|
-
# Executes the complete test suite against a subject.
|
118
|
-
#
|
119
|
-
# This method provides a comprehensive test run that:
|
120
|
-
# - Executes all specifications in random order
|
121
|
-
# - Runs each test in isolation via process forking
|
122
|
-
# - Reports results for each specification
|
123
|
-
# - Exits with appropriate status code
|
124
|
-
#
|
125
|
-
# @yield Block that returns the subject to test
|
126
|
-
# @yieldreturn [Object] The subject to test against specifications
|
127
|
-
# @return [Boolean] true if all tests pass
|
128
|
-
# @raise [SystemExit] Exits with status 1 if any test fails
|
129
|
-
# @raise [Error::MissingSubjectBlock] If no subject block is provided
|
130
|
-
#
|
131
|
-
# @example Basic test execution
|
132
|
-
# set.test { Calculator.new }
|
133
|
-
#
|
134
|
-
# @example Testing with dependencies
|
135
|
-
# set.test {
|
136
|
-
# calc = Calculator.new
|
137
|
-
# calc.precision = :high
|
138
|
-
# calc
|
139
|
-
# }
|
140
|
-
#
|
141
|
-
# @api public
|
142
|
-
def test(&subject)
|
143
|
-
match?(&subject) || exit_with_failure
|
144
|
-
end
|
145
|
-
|
146
|
-
# Returns a string representation of the test set.
|
147
|
-
#
|
148
|
-
# @return [String] Human-readable description of the test set
|
149
|
-
#
|
150
|
-
# @example
|
151
|
-
# set.to_s #=> "fix [<specification list>]"
|
152
|
-
#
|
153
|
-
# @api public
|
154
|
-
def to_s
|
155
|
-
"fix #{expected.inspect}"
|
156
|
-
end
|
157
|
-
|
158
|
-
private
|
159
|
-
|
160
|
-
# List of specifications to be tested.
|
161
|
-
# Each specification is an array containing:
|
162
|
-
# - [0] environment: Fix::Dsl instance for test context
|
163
|
-
# - [1] location: String indicating source file and line
|
164
|
-
# - [2] requirement: Test requirement (MUST, SHOULD, or MAY)
|
165
|
-
# - [3] challenges: Array of test challenges to execute
|
166
|
-
#
|
167
|
-
# @return [Array<Array>] List of specification arrays
|
168
|
-
attr_reader :expected
|
169
|
-
|
170
|
-
# Executes a single specification in an isolated process.
|
171
|
-
#
|
172
|
-
# @param env [Fix::Dsl] Test environment instance
|
173
|
-
# @param location [String] Source location (file:line)
|
174
|
-
# @param requirement [Object] Test requirement
|
175
|
-
# @param challenges [Array] Test challenges
|
176
|
-
# @yield Block returning the subject to test
|
177
|
-
# @return [Boolean] true if specification passed
|
178
|
-
def run_spec(env, location, requirement, challenges, &subject)
|
179
|
-
child_pid = ::Process.fork { execute_spec(env, location, requirement, challenges, &subject) }
|
180
|
-
_pid, process_status = ::Process.wait2(child_pid)
|
181
|
-
process_status.success?
|
182
|
-
end
|
183
|
-
|
184
|
-
# Executes a specification in the current process.
|
185
|
-
#
|
186
|
-
# @param env [Fix::Dsl] Test environment instance
|
187
|
-
# @param location [String] Source location (file:line)
|
188
|
-
# @param requirement [Object] Test requirement
|
189
|
-
# @param challenges [Array] Test challenges
|
190
|
-
# @yield Block returning the subject to test
|
191
|
-
# @return [void]
|
192
|
-
def execute_spec(env, location, requirement, challenges, &subject)
|
193
|
-
result = Run.new(env, requirement, *challenges).test(&subject)
|
194
|
-
report_result(location, result)
|
195
|
-
exit_with_status(result.passed?)
|
196
|
-
end
|
197
|
-
|
198
|
-
# Reports the result of a specification execution.
|
199
|
-
#
|
200
|
-
# @param location [String] Source location (file:line)
|
201
|
-
# @param result [Object] Test execution result
|
202
|
-
# @return [void]
|
203
|
-
def report_result(location, result)
|
204
|
-
puts "#{location} #{result.colored_string}"
|
205
|
-
end
|
206
|
-
|
207
|
-
# Exits the process with a failure status.
|
208
|
-
#
|
209
|
-
# @return [void]
|
210
|
-
# @raise [SystemExit] Always exits with status 1
|
211
|
-
def exit_with_failure
|
212
|
-
::Kernel.exit(false)
|
213
|
-
end
|
214
|
-
|
215
|
-
# Exits the process with the given status.
|
216
|
-
#
|
217
|
-
# @param status [Boolean] Exit status to use
|
218
|
-
# @return [void]
|
219
|
-
# @raise [SystemExit] Always exits with provided status
|
220
|
-
def exit_with_status(status)
|
221
|
-
::Kernel.exit(status)
|
222
|
-
end
|
223
|
-
end
|
224
|
-
end
|