fix 0.21 → 1.0.0.beta1

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