fix 0.21 → 1.0.0.beta1

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.
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