tryouts 3.0.0 → 3.1.1
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/README.md +51 -115
- data/exe/try +25 -4
- data/lib/tryouts/cli/formatters/base.rb +33 -21
- data/lib/tryouts/cli/formatters/compact.rb +122 -84
- data/lib/tryouts/cli/formatters/factory.rb +1 -1
- data/lib/tryouts/cli/formatters/output_manager.rb +13 -2
- data/lib/tryouts/cli/formatters/quiet.rb +22 -16
- data/lib/tryouts/cli/formatters/verbose.rb +102 -61
- data/lib/tryouts/console.rb +53 -17
- data/lib/tryouts/expectation_evaluators/base.rb +101 -0
- data/lib/tryouts/expectation_evaluators/boolean.rb +60 -0
- data/lib/tryouts/expectation_evaluators/exception.rb +61 -0
- data/lib/tryouts/expectation_evaluators/expectation_result.rb +67 -0
- data/lib/tryouts/expectation_evaluators/false.rb +60 -0
- data/lib/tryouts/expectation_evaluators/intentional_failure.rb +74 -0
- data/lib/tryouts/expectation_evaluators/output.rb +101 -0
- data/lib/tryouts/expectation_evaluators/performance_time.rb +81 -0
- data/lib/tryouts/expectation_evaluators/regex_match.rb +57 -0
- data/lib/tryouts/expectation_evaluators/registry.rb +66 -0
- data/lib/tryouts/expectation_evaluators/regular.rb +67 -0
- data/lib/tryouts/expectation_evaluators/result_type.rb +51 -0
- data/lib/tryouts/expectation_evaluators/true.rb +58 -0
- data/lib/tryouts/prism_parser.rb +221 -20
- data/lib/tryouts/test_batch.rb +556 -0
- data/lib/tryouts/test_case.rb +192 -0
- data/lib/tryouts/test_executor.rb +7 -5
- data/lib/tryouts/test_runner.rb +2 -2
- data/lib/tryouts/translators/minitest_translator.rb +78 -11
- data/lib/tryouts/translators/rspec_translator.rb +85 -12
- data/lib/tryouts/version.rb +1 -1
- data/lib/tryouts.rb +43 -1
- metadata +18 -5
- data/lib/tryouts/testbatch.rb +0 -314
- data/lib/tryouts/testcase.rb +0 -51
@@ -0,0 +1,192 @@
|
|
1
|
+
# lib/tryouts/testcase.rb
|
2
|
+
|
3
|
+
# Modern data structures using Ruby 3.2+ Data classes
|
4
|
+
class Tryouts
|
5
|
+
# Core data structures
|
6
|
+
TestCase = Data.define(:description, :code, :expectations, :line_range, :path, :source_lines, :first_expectation_line) do
|
7
|
+
def empty?
|
8
|
+
code.empty?
|
9
|
+
end
|
10
|
+
|
11
|
+
def expectations?
|
12
|
+
!expectations.empty?
|
13
|
+
end
|
14
|
+
|
15
|
+
def exception_expectations?
|
16
|
+
expectations.any?(&:exception?)
|
17
|
+
end
|
18
|
+
|
19
|
+
def regular_expectations
|
20
|
+
expectations.filter(&:regular?)
|
21
|
+
end
|
22
|
+
|
23
|
+
def exception_expectations
|
24
|
+
expectations.filter(&:exception?)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
Expectation = Data.define(:content, :type) do
|
29
|
+
def regular? = type == :regular
|
30
|
+
def exception? = type == :exception
|
31
|
+
def boolean? = type == :boolean
|
32
|
+
def true? = type == :true
|
33
|
+
def false? = type == :false
|
34
|
+
def result_type? = type == :result_type
|
35
|
+
def regex_match? = type == :regex_match
|
36
|
+
def performance_time? = type == :performance_time
|
37
|
+
def intentional_failure? = type == :intentional_failure
|
38
|
+
def output? = type == :output
|
39
|
+
end
|
40
|
+
|
41
|
+
# Special expectation type for output capturing with pipe information
|
42
|
+
OutputExpectation = Data.define(:content, :type, :pipe) do
|
43
|
+
def regular? = type == :regular
|
44
|
+
def exception? = type == :exception
|
45
|
+
def boolean? = type == :boolean
|
46
|
+
def true? = type == :true
|
47
|
+
def false? = type == :false
|
48
|
+
def result_type? = type == :result_type
|
49
|
+
def regex_match? = type == :regex_match
|
50
|
+
def performance_time? = type == :performance_time
|
51
|
+
def intentional_failure? = type == :intentional_failure
|
52
|
+
def output? = type == :output
|
53
|
+
|
54
|
+
def stdout? = pipe == 1
|
55
|
+
def stderr? = pipe == 2
|
56
|
+
end
|
57
|
+
|
58
|
+
Setup = Data.define(:code, :line_range, :path) do
|
59
|
+
def empty?
|
60
|
+
code.empty?
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
Teardown = Data.define(:code, :line_range, :path) do
|
65
|
+
def empty?
|
66
|
+
code.empty?
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
Testrun = Data.define(:setup, :test_cases, :teardown, :source_file, :metadata) do
|
71
|
+
def total_tests
|
72
|
+
test_cases.size
|
73
|
+
end
|
74
|
+
|
75
|
+
def empty?
|
76
|
+
test_cases.empty?
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
# Test case result packet for formatters
|
81
|
+
# Replaces the simple Hash aggregation with a rich, immutable data structure
|
82
|
+
# containing all execution context and results needed by formatters
|
83
|
+
TestCaseResultPacket = Data.define(
|
84
|
+
:test_case, # TestCase object
|
85
|
+
:status, # :passed, :failed, :error
|
86
|
+
:result_value, # Actual execution result
|
87
|
+
:actual_results, # Array of actual values from expectations
|
88
|
+
:expected_results, # Array of expected values from expectations
|
89
|
+
:error, # Exception object (if any)
|
90
|
+
:captured_output, # Captured stdout/stderr content
|
91
|
+
:elapsed_time, # Execution timing (future use)
|
92
|
+
:metadata # Hash for future extensibility
|
93
|
+
) do
|
94
|
+
def passed?
|
95
|
+
status == :passed
|
96
|
+
end
|
97
|
+
|
98
|
+
def failed?
|
99
|
+
status == :failed
|
100
|
+
end
|
101
|
+
|
102
|
+
def error?
|
103
|
+
status == :error
|
104
|
+
end
|
105
|
+
|
106
|
+
def has_output?
|
107
|
+
captured_output && !captured_output.empty?
|
108
|
+
end
|
109
|
+
|
110
|
+
def has_error?
|
111
|
+
!error.nil?
|
112
|
+
end
|
113
|
+
|
114
|
+
# Helper for formatter access to first actual/expected values
|
115
|
+
def first_actual
|
116
|
+
actual_results&.first
|
117
|
+
end
|
118
|
+
|
119
|
+
def first_expected
|
120
|
+
expected_results&.first
|
121
|
+
end
|
122
|
+
|
123
|
+
# Create a basic result packet for successful tests
|
124
|
+
def self.from_success(test_case, result_value, actual_results, expected_results, captured_output: nil, elapsed_time: nil, metadata: {})
|
125
|
+
new(
|
126
|
+
test_case: test_case,
|
127
|
+
status: :passed,
|
128
|
+
result_value: result_value,
|
129
|
+
actual_results: actual_results,
|
130
|
+
expected_results: expected_results,
|
131
|
+
error: nil,
|
132
|
+
captured_output: captured_output,
|
133
|
+
elapsed_time: elapsed_time,
|
134
|
+
metadata: metadata
|
135
|
+
)
|
136
|
+
end
|
137
|
+
|
138
|
+
# Create a result packet for failed tests
|
139
|
+
def self.from_failure(test_case, result_value, actual_results, expected_results, captured_output: nil, elapsed_time: nil, metadata: {})
|
140
|
+
new(
|
141
|
+
test_case: test_case,
|
142
|
+
status: :failed,
|
143
|
+
result_value: result_value,
|
144
|
+
actual_results: actual_results,
|
145
|
+
expected_results: expected_results,
|
146
|
+
error: nil,
|
147
|
+
captured_output: captured_output,
|
148
|
+
elapsed_time: elapsed_time,
|
149
|
+
metadata: metadata
|
150
|
+
)
|
151
|
+
end
|
152
|
+
|
153
|
+
# Create a result packet for error cases
|
154
|
+
def self.from_error(test_case, error, captured_output: nil, elapsed_time: nil, metadata: {})
|
155
|
+
error_message = error ? error.message : '<exception is nil>'
|
156
|
+
|
157
|
+
# Include backtrace in error message when in debug/verbose mode
|
158
|
+
error_display = if error && Tryouts.debug?
|
159
|
+
backtrace_preview = error.backtrace&.first(3)&.join("\n ")
|
160
|
+
"(#{error.class}) #{error_message}\n #{backtrace_preview}"
|
161
|
+
else
|
162
|
+
"(#{error.class}) #{error_message}"
|
163
|
+
end
|
164
|
+
|
165
|
+
new(
|
166
|
+
test_case: test_case,
|
167
|
+
status: :error,
|
168
|
+
result_value: nil,
|
169
|
+
actual_results: [error_display],
|
170
|
+
expected_results: [],
|
171
|
+
error: error,
|
172
|
+
captured_output: captured_output,
|
173
|
+
elapsed_time: elapsed_time,
|
174
|
+
metadata: metadata
|
175
|
+
)
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
# Enhanced error with context
|
180
|
+
class TryoutSyntaxError < StandardError
|
181
|
+
attr_reader :line_number, :context, :source_file
|
182
|
+
|
183
|
+
def initialize(message, line_number:, context:, source_file: nil)
|
184
|
+
@line_number = line_number
|
185
|
+
@context = context
|
186
|
+
@source_file = source_file
|
187
|
+
|
188
|
+
location = source_file ? "#{source_file}:#{line_number}" : "line #{line_number}"
|
189
|
+
super("#{message} at #{location}: #{context}")
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# lib/tryouts/test_executor.rb
|
2
2
|
|
3
|
-
require_relative '
|
3
|
+
require_relative 'test_batch'
|
4
4
|
|
5
5
|
class Tryouts
|
6
6
|
class TestExecutor
|
@@ -38,6 +38,7 @@ class Tryouts
|
|
38
38
|
global_tally: @global_tally,
|
39
39
|
)
|
40
40
|
|
41
|
+
# TestBatch handles file output, so don't duplicate it here
|
41
42
|
unless @options[:verbose]
|
42
43
|
context_mode = @options[:shared_context] ? 'shared' : 'fresh'
|
43
44
|
@output_manager.file_execution_start(@file, @testrun.total_tests, context_mode)
|
@@ -49,15 +50,16 @@ class Tryouts
|
|
49
50
|
test_results << last_result if last_result
|
50
51
|
end
|
51
52
|
|
52
|
-
file_failed_count = test_results.count { |r| r
|
53
|
-
file_error_count = test_results.count { |r| r
|
54
|
-
|
53
|
+
file_failed_count = test_results.count { |r| r.failed? }
|
54
|
+
file_error_count = test_results.count { |r| r.error? }
|
55
|
+
executed_test_count = test_results.size
|
56
|
+
@global_tally[:total_tests] += executed_test_count
|
55
57
|
@global_tally[:total_failed] += file_failed_count
|
56
58
|
@global_tally[:total_errors] += file_error_count
|
57
59
|
@global_tally[:successful_files] += 1 if success
|
58
60
|
|
59
61
|
duration = Time.now.to_f - @file_start.to_f
|
60
|
-
@output_manager.file_success(@file,
|
62
|
+
@output_manager.file_success(@file, executed_test_count, file_failed_count, file_error_count, duration)
|
61
63
|
|
62
64
|
# Combine failures and errors to determine the exit code.
|
63
65
|
success ? 0 : (file_failed_count + file_error_count)
|
data/lib/tryouts/test_runner.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# lib/tryouts/test_runner.rb
|
2
2
|
|
3
3
|
require_relative 'prism_parser'
|
4
|
-
require_relative '
|
4
|
+
require_relative 'test_batch'
|
5
5
|
require_relative 'translators/rspec_translator'
|
6
6
|
require_relative 'translators/minitest_translator'
|
7
7
|
require_relative 'file_processor'
|
@@ -79,7 +79,7 @@ class Tryouts
|
|
79
79
|
def process_files
|
80
80
|
failure_count = 0
|
81
81
|
|
82
|
-
@files.
|
82
|
+
@files.each_with_index do |file, idx|
|
83
83
|
result = process_file(file)
|
84
84
|
failure_count += result unless result.zero?
|
85
85
|
status = result.zero? ? Console.color(:green, 'PASS') : Console.color(:red, 'FAIL')
|
@@ -2,6 +2,44 @@
|
|
2
2
|
|
3
3
|
class Tryouts
|
4
4
|
module Translators
|
5
|
+
# Translates Tryouts test files to Minitest format
|
6
|
+
#
|
7
|
+
# IMPORTANT: Context Mode Differences
|
8
|
+
# ==================================
|
9
|
+
#
|
10
|
+
# Tryouts supports two context modes that behave differently than Minitest:
|
11
|
+
#
|
12
|
+
# 1. Tryouts Shared Context (default):
|
13
|
+
# - Setup runs once, all tests share the same context object
|
14
|
+
# - Tests can modify variables/state and affect subsequent tests
|
15
|
+
# - Behaves like a Ruby script executing top-to-bottom
|
16
|
+
# - Designed for documentation-style tests where examples build on each other
|
17
|
+
#
|
18
|
+
# 2. Tryouts Fresh Context (--no-shared-context):
|
19
|
+
# - Setup @instance_variables are copied to each test's fresh context
|
20
|
+
# - Tests are isolated but inherit setup state
|
21
|
+
# - Similar to Minitest's setup method but with setup state inheritance
|
22
|
+
#
|
23
|
+
# Minitest Translation Behavior:
|
24
|
+
# ==============================
|
25
|
+
# - Uses setup method which runs before each test (Minitest standard)
|
26
|
+
# - Each test method gets fresh context (Minitest standard)
|
27
|
+
# - Tests that rely on shared state between test cases WILL FAIL
|
28
|
+
# - This is intentional and reveals inappropriate test dependencies
|
29
|
+
#
|
30
|
+
# Example that works in Tryouts shared mode but fails in Minitest:
|
31
|
+
# ## TEST 1
|
32
|
+
# @counter = 1
|
33
|
+
# @counter
|
34
|
+
# #=> 1
|
35
|
+
#
|
36
|
+
# ## TEST 2
|
37
|
+
# @counter += 1 # Will be reset to 1 by setup, then fail
|
38
|
+
# @counter
|
39
|
+
# #=> 2
|
40
|
+
#
|
41
|
+
# Recommendation: Write tryouts tests that work in fresh context mode
|
42
|
+
# if you plan to use Minitest translation.
|
5
43
|
class MinitestTranslator
|
6
44
|
def initialize
|
7
45
|
require 'minitest/test'
|
@@ -27,11 +65,24 @@ class Tryouts
|
|
27
65
|
|
28
66
|
method_name = "test_#{index.to_s.rjust(3, '0')}_#{parameterize(test_case.description)}"
|
29
67
|
define_method(method_name) do
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
68
|
+
if test_case.exception_expectations?
|
69
|
+
# Handle exception expectations
|
70
|
+
assert_raises(StandardError) do
|
71
|
+
instance_eval(test_case.code) unless test_case.code.strip.empty?
|
72
|
+
end
|
73
|
+
|
74
|
+
test_case.exception_expectations.each do |expectation|
|
75
|
+
result = instance_eval(expectation.content)
|
76
|
+
assert result, "Exception expectation failed: #{expectation.content}"
|
77
|
+
end
|
78
|
+
else
|
79
|
+
# Handle regular expectations
|
80
|
+
result = instance_eval(test_case.code) unless test_case.code.strip.empty?
|
81
|
+
|
82
|
+
test_case.regular_expectations.each do |expectation|
|
83
|
+
expected_value = instance_eval(expectation.content)
|
84
|
+
assert_equal expected_value, result
|
85
|
+
end
|
35
86
|
end
|
36
87
|
end
|
37
88
|
end
|
@@ -72,15 +123,31 @@ class Tryouts
|
|
72
123
|
|
73
124
|
method_name = "test_#{index.to_s.rjust(3, '0')}_#{parameterize(test_case.description)}"
|
74
125
|
lines << " def #{method_name}"
|
75
|
-
|
76
|
-
|
77
|
-
|
126
|
+
|
127
|
+
if test_case.exception_expectations?
|
128
|
+
# Handle exception expectations
|
129
|
+
lines << ' error = assert_raises(StandardError) do'
|
130
|
+
unless test_case.code.strip.empty?
|
131
|
+
test_case.code.lines.each { |line| lines << " #{line.chomp}" }
|
132
|
+
end
|
78
133
|
lines << ' end'
|
79
|
-
end
|
80
134
|
|
81
|
-
|
82
|
-
|
135
|
+
test_case.exception_expectations.each do |expectation|
|
136
|
+
lines << " assert #{expectation}, \"Exception expectation failed: #{expectation}\""
|
137
|
+
end
|
138
|
+
else
|
139
|
+
# Handle regular expectations
|
140
|
+
unless test_case.code.strip.empty?
|
141
|
+
lines << ' result = begin'
|
142
|
+
test_case.code.lines.each { |line| lines << " #{line.chomp}" }
|
143
|
+
lines << ' end'
|
144
|
+
end
|
145
|
+
|
146
|
+
test_case.expectations.each do |expectation|
|
147
|
+
lines << " assert_equal #{expectation}, result"
|
148
|
+
end
|
83
149
|
end
|
150
|
+
|
84
151
|
lines << ' end'
|
85
152
|
lines << ''
|
86
153
|
end
|
@@ -2,6 +2,44 @@
|
|
2
2
|
|
3
3
|
class Tryouts
|
4
4
|
module Translators
|
5
|
+
# Translates Tryouts test files to RSpec format
|
6
|
+
#
|
7
|
+
# IMPORTANT: Context Mode Differences
|
8
|
+
# ==================================
|
9
|
+
#
|
10
|
+
# Tryouts supports two context modes that behave differently than RSpec:
|
11
|
+
#
|
12
|
+
# 1. Tryouts Shared Context (default):
|
13
|
+
# - Setup runs once, all tests share the same context object
|
14
|
+
# - Tests can modify variables/state and affect subsequent tests
|
15
|
+
# - Behaves like a Ruby script executing top-to-bottom
|
16
|
+
# - Designed for documentation-style tests where examples build on each other
|
17
|
+
#
|
18
|
+
# 2. Tryouts Fresh Context (--no-shared-context):
|
19
|
+
# - Setup @instance_variables are copied to each test's fresh context
|
20
|
+
# - Tests are isolated but inherit setup state
|
21
|
+
# - Similar to RSpec's before(:each) but with setup state inheritance
|
22
|
+
#
|
23
|
+
# RSpec Translation Behavior:
|
24
|
+
# ===========================
|
25
|
+
# - Uses before(:all) for setup code (closest equivalent to shared context)
|
26
|
+
# - Each 'it' block gets fresh context (RSpec standard)
|
27
|
+
# - Tests that rely on shared state between test cases WILL FAIL
|
28
|
+
# - This is intentional and reveals inappropriate test dependencies
|
29
|
+
#
|
30
|
+
# Example that works in Tryouts shared mode but fails in RSpec:
|
31
|
+
# ## TEST 1
|
32
|
+
# @counter = 1
|
33
|
+
# @counter
|
34
|
+
# #=> 1
|
35
|
+
#
|
36
|
+
# ## TEST 2
|
37
|
+
# @counter += 1 # Will be nil in RSpec, causing failure
|
38
|
+
# @counter
|
39
|
+
# #=> 2
|
40
|
+
#
|
41
|
+
# Recommendation: Write tryouts tests that work in fresh context mode
|
42
|
+
# if you plan to use RSpec translation.
|
5
43
|
class RSpecTranslator
|
6
44
|
def initialize
|
7
45
|
require 'rspec/core'
|
@@ -16,7 +54,7 @@ class Tryouts
|
|
16
54
|
# Setup before all tests
|
17
55
|
if testrun.setup && !testrun.setup.empty?
|
18
56
|
before(:all) do
|
19
|
-
instance_eval(testrun.setup.code)
|
57
|
+
instance_eval(testrun.setup.code, testrun.source_file)
|
20
58
|
end
|
21
59
|
end
|
22
60
|
|
@@ -25,11 +63,27 @@ class Tryouts
|
|
25
63
|
next if test_case.empty? || !test_case.expectations?
|
26
64
|
|
27
65
|
it test_case.description do
|
28
|
-
|
66
|
+
if test_case.exception_expectations?
|
67
|
+
# Handle exception expectations
|
68
|
+
error = nil
|
69
|
+
expect do
|
70
|
+
instance_eval(test_case.code, testrun.source_file) unless test_case.code.strip.empty?
|
71
|
+
end.to raise_error do |caught_error|
|
72
|
+
error = caught_error
|
73
|
+
end
|
29
74
|
|
30
|
-
|
31
|
-
|
32
|
-
|
75
|
+
test_case.exception_expectations.each do |expectation|
|
76
|
+
expected_value = instance_eval(expectation.content, testrun.source_file)
|
77
|
+
expect(expected_value).to be_truthy
|
78
|
+
end
|
79
|
+
else
|
80
|
+
# Handle regular expectations
|
81
|
+
result = instance_eval(test_case.code, testrun.source_file) unless test_case.code.strip.empty?
|
82
|
+
|
83
|
+
test_case.regular_expectations.each do |expectation|
|
84
|
+
expected_value = instance_eval(expectation.content, testrun.source_file)
|
85
|
+
expect(result).to eq(expected_value)
|
86
|
+
end
|
33
87
|
end
|
34
88
|
end
|
35
89
|
end
|
@@ -37,7 +91,7 @@ class Tryouts
|
|
37
91
|
# Teardown after all tests
|
38
92
|
if testrun.teardown && !testrun.teardown.empty?
|
39
93
|
after(:all) do
|
40
|
-
instance_eval(testrun.teardown.code)
|
94
|
+
instance_eval(testrun.teardown.code, testrun.source_file)
|
41
95
|
end
|
42
96
|
end
|
43
97
|
end
|
@@ -61,15 +115,34 @@ class Tryouts
|
|
61
115
|
next if test_case.empty? || !test_case.expectations?
|
62
116
|
|
63
117
|
lines << " it '#{test_case.description}' do"
|
64
|
-
|
65
|
-
|
66
|
-
|
118
|
+
|
119
|
+
if test_case.exception_expectations?
|
120
|
+
# Handle exception expectations
|
121
|
+
lines << ' error = nil'
|
122
|
+
lines << ' expect {'
|
123
|
+
unless test_case.code.strip.empty?
|
124
|
+
test_case.code.lines.each { |line| lines << " #{line.chomp}" }
|
125
|
+
end
|
126
|
+
lines << ' }.to raise_error do |caught_error|'
|
127
|
+
lines << ' error = caught_error'
|
67
128
|
lines << ' end'
|
68
|
-
end
|
69
129
|
|
70
|
-
|
71
|
-
|
130
|
+
test_case.exception_expectations.each do |expectation|
|
131
|
+
lines << " expect(#{expectation.content}).to be_truthy"
|
132
|
+
end
|
133
|
+
else
|
134
|
+
# Handle regular expectations
|
135
|
+
unless test_case.code.strip.empty?
|
136
|
+
lines << ' result = begin'
|
137
|
+
test_case.code.lines.each { |line| lines << " #{line.chomp}" }
|
138
|
+
lines << ' end'
|
139
|
+
end
|
140
|
+
|
141
|
+
test_case.regular_expectations.each do |expectation|
|
142
|
+
lines << " expect(result).to eq(#{expectation.content})"
|
143
|
+
end
|
72
144
|
end
|
145
|
+
|
73
146
|
lines << ' end'
|
74
147
|
lines << ''
|
75
148
|
end
|
data/lib/tryouts/version.rb
CHANGED
data/lib/tryouts.rb
CHANGED
@@ -3,11 +3,12 @@
|
|
3
3
|
|
4
4
|
|
5
5
|
require 'stringio'
|
6
|
+
require 'timeout'
|
6
7
|
|
7
8
|
TRYOUTS_LIB_HOME = __dir__ unless defined?(TRYOUTS_LIB_HOME)
|
8
9
|
|
9
10
|
require_relative 'tryouts/console'
|
10
|
-
require_relative 'tryouts/
|
11
|
+
require_relative 'tryouts/test_batch'
|
11
12
|
require_relative 'tryouts/version'
|
12
13
|
require_relative 'tryouts/prism_parser'
|
13
14
|
require_relative 'tryouts/cli'
|
@@ -47,6 +48,47 @@ class Tryouts
|
|
47
48
|
prefix = (' ' * indent) + Console.color(:dim, 'TRACE')
|
48
49
|
warn "#{prefix} #{msg}"
|
49
50
|
end
|
51
|
+
|
52
|
+
def debug(msg, indent: 0)
|
53
|
+
return unless debug?
|
54
|
+
|
55
|
+
prefix = (' ' * indent) + Console.color(:cyan, 'DEBUG')
|
56
|
+
warn "#{prefix} #{msg}"
|
57
|
+
end
|
58
|
+
|
59
|
+
# Error classification for resilient error handling
|
60
|
+
def classify_error(exception)
|
61
|
+
case exception
|
62
|
+
when SystemExit, SignalException
|
63
|
+
:non_recoverable_exit
|
64
|
+
when Timeout::Error
|
65
|
+
:transient
|
66
|
+
when Errno::ENOENT, Errno::EACCES, Errno::EPERM
|
67
|
+
:file_system
|
68
|
+
when LoadError, NameError, NoMethodError
|
69
|
+
:code_structure
|
70
|
+
when SecurityError, NoMemoryError, SystemStackError
|
71
|
+
:system_resource
|
72
|
+
when SyntaxError, TryoutSyntaxError
|
73
|
+
:syntax
|
74
|
+
when StandardError
|
75
|
+
:recoverable
|
76
|
+
else
|
77
|
+
:unknown
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
# Determine if an error should stop batch execution
|
82
|
+
def batch_stopping_error?(exception)
|
83
|
+
classification = classify_error(exception)
|
84
|
+
[:non_recoverable_exit, :system_resource, :syntax].include?(classification)
|
85
|
+
end
|
86
|
+
|
87
|
+
# Determine if an error should stop individual test execution
|
88
|
+
def test_stopping_error?(exception)
|
89
|
+
classification = classify_error(exception)
|
90
|
+
[:non_recoverable_exit, :system_resource].include?(classification)
|
91
|
+
end
|
50
92
|
end
|
51
93
|
|
52
94
|
extend ClassMethods
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tryouts
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.
|
4
|
+
version: 3.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Delano Mandelbaum
|
@@ -83,12 +83,25 @@ files:
|
|
83
83
|
- lib/tryouts/cli/modes/inspect.rb
|
84
84
|
- lib/tryouts/cli/opts.rb
|
85
85
|
- lib/tryouts/console.rb
|
86
|
+
- lib/tryouts/expectation_evaluators/base.rb
|
87
|
+
- lib/tryouts/expectation_evaluators/boolean.rb
|
88
|
+
- lib/tryouts/expectation_evaluators/exception.rb
|
89
|
+
- lib/tryouts/expectation_evaluators/expectation_result.rb
|
90
|
+
- lib/tryouts/expectation_evaluators/false.rb
|
91
|
+
- lib/tryouts/expectation_evaluators/intentional_failure.rb
|
92
|
+
- lib/tryouts/expectation_evaluators/output.rb
|
93
|
+
- lib/tryouts/expectation_evaluators/performance_time.rb
|
94
|
+
- lib/tryouts/expectation_evaluators/regex_match.rb
|
95
|
+
- lib/tryouts/expectation_evaluators/registry.rb
|
96
|
+
- lib/tryouts/expectation_evaluators/regular.rb
|
97
|
+
- lib/tryouts/expectation_evaluators/result_type.rb
|
98
|
+
- lib/tryouts/expectation_evaluators/true.rb
|
86
99
|
- lib/tryouts/file_processor.rb
|
87
100
|
- lib/tryouts/prism_parser.rb
|
101
|
+
- lib/tryouts/test_batch.rb
|
102
|
+
- lib/tryouts/test_case.rb
|
88
103
|
- lib/tryouts/test_executor.rb
|
89
104
|
- lib/tryouts/test_runner.rb
|
90
|
-
- lib/tryouts/testbatch.rb
|
91
|
-
- lib/tryouts/testcase.rb
|
92
105
|
- lib/tryouts/translators/minitest_translator.rb
|
93
106
|
- lib/tryouts/translators/rspec_translator.rb
|
94
107
|
- lib/tryouts/version.rb
|
@@ -104,14 +117,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
104
117
|
requirements:
|
105
118
|
- - ">="
|
106
119
|
- !ruby/object:Gem::Version
|
107
|
-
version: '3.
|
120
|
+
version: '3.2'
|
108
121
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
109
122
|
requirements:
|
110
123
|
- - ">="
|
111
124
|
- !ruby/object:Gem::Version
|
112
125
|
version: '0'
|
113
126
|
requirements: []
|
114
|
-
rubygems_version: 3.6.
|
127
|
+
rubygems_version: 3.6.9
|
115
128
|
specification_version: 4
|
116
129
|
summary: Ruby tests that read like documentation.
|
117
130
|
test_files: []
|