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