tryouts 3.1.0 → 3.1.2
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/exe/try +3 -3
- data/lib/tryouts/cli/formatters/base.rb +0 -2
- data/lib/tryouts/cli/formatters/compact.rb +5 -4
- data/lib/tryouts/cli/formatters/quiet.rb +1 -1
- data/lib/tryouts/cli/formatters/verbose.rb +9 -7
- data/lib/tryouts/console.rb +1 -1
- data/lib/tryouts/expectation_evaluators/boolean.rb +1 -1
- data/lib/tryouts/expectation_evaluators/exception.rb +2 -2
- data/lib/tryouts/expectation_evaluators/expectation_result.rb +3 -3
- data/lib/tryouts/expectation_evaluators/false.rb +1 -1
- data/lib/tryouts/expectation_evaluators/intentional_failure.rb +3 -3
- data/lib/tryouts/expectation_evaluators/output.rb +6 -6
- data/lib/tryouts/expectation_evaluators/performance_time.rb +3 -3
- data/lib/tryouts/expectation_evaluators/regex_match.rb +2 -2
- data/lib/tryouts/expectation_evaluators/regular.rb +1 -1
- data/lib/tryouts/expectation_evaluators/result_type.rb +1 -1
- data/lib/tryouts/expectation_evaluators/true.rb +2 -2
- data/lib/tryouts/prism_parser.rb +118 -14
- data/lib/tryouts/{testbatch.rb → test_batch.rb} +85 -33
- data/lib/tryouts/{testcase.rb → test_case.rb} +5 -5
- data/lib/tryouts/test_executor.rb +1 -1
- data/lib/tryouts/test_runner.rb +2 -2
- data/lib/tryouts/translators/minitest_translator.rb +38 -0
- data/lib/tryouts/translators/rspec_translator.rb +38 -0
- data/lib/tryouts/version.rb +1 -1
- data/lib/tryouts.rb +1 -10
- metadata +3 -23
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d2dea4f111f12c35d204df54313a62045bd9e8841710b19323454e5a626ac4fd
|
4
|
+
data.tar.gz: 275b234c7ce6ca2debd28e9d861053bc041e825443683078fee710de2b45e0cf
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6c704d75d9bed36875b843b6014508df36292247e9c03bbc68892f1d7318db0cfd8ff9907c9c45d71d45441e9512dd5550e4c47b84a9075285ccf8980474c4b9
|
7
|
+
data.tar.gz: 0230e782d3a214bb92e647d2beb38e3b3ce1d3bbc129f6628ca14b0b7cef1430fee94ee7746bda55e78a8236da5df1fbf7b6270a8f0a186646f5a01662d0d667
|
data/exe/try
CHANGED
@@ -46,8 +46,8 @@ begin
|
|
46
46
|
expanded_files = []
|
47
47
|
files.each do |file_or_dir|
|
48
48
|
if File.directory?(file_or_dir)
|
49
|
-
# If it's a directory, find all *_try.rb files within it
|
50
|
-
dir_files = Dir.glob('**/*_try.rb', base: file_or_dir)
|
49
|
+
# If it's a directory, find all *_try.rb and *.try.rb files within it
|
50
|
+
dir_files = Dir.glob(['**/*_try.rb', '**/*.try.rb'], base: file_or_dir)
|
51
51
|
expanded_files.concat(dir_files.map { |f| File.join(file_or_dir, f) })
|
52
52
|
else
|
53
53
|
# If it's a file, add it as-is
|
@@ -59,7 +59,7 @@ begin
|
|
59
59
|
# Default file discovery if no files specified
|
60
60
|
if files.empty?
|
61
61
|
raw_files = Dir.glob(
|
62
|
-
['{app,apps,lib,try,tryouts}/**/*_try.rb', './*_try.rb'],
|
62
|
+
['{app,apps,lib,try,tryouts}/**/*_try.rb', './*_try.rb', '{app,apps,lib,try,tryouts}/**/*.try.rb', './*.try.rb'],
|
63
63
|
base: Dir.pwd,
|
64
64
|
)
|
65
65
|
|
@@ -7,7 +7,6 @@ class Tryouts
|
|
7
7
|
class CLI
|
8
8
|
# Enhanced interface for all test output formatting
|
9
9
|
module FormatterInterface
|
10
|
-
|
11
10
|
attr_reader :current_indent
|
12
11
|
|
13
12
|
# Phase-level output (major sections)
|
@@ -115,6 +114,5 @@ class Tryouts
|
|
115
114
|
@current_indent = old_indent
|
116
115
|
end
|
117
116
|
end
|
118
|
-
|
119
117
|
end
|
120
118
|
end
|
@@ -22,7 +22,7 @@ class Tryouts
|
|
22
22
|
io.puts text
|
23
23
|
when 1
|
24
24
|
# Skip execution phase headers - they create unwanted empty lines
|
25
|
-
|
25
|
+
nil
|
26
26
|
else
|
27
27
|
# Other phase headers with minimal formatting
|
28
28
|
io.puts indent_text(message, level - 1)
|
@@ -112,8 +112,8 @@ class Tryouts
|
|
112
112
|
return if result_packet.passed? && !@show_passed
|
113
113
|
|
114
114
|
test_case = result_packet.test_case
|
115
|
-
desc
|
116
|
-
desc
|
115
|
+
desc = test_case.description.to_s
|
116
|
+
desc = 'unnamed test' if desc.empty?
|
117
117
|
|
118
118
|
case result_packet.status
|
119
119
|
when :passed
|
@@ -133,6 +133,7 @@ class Tryouts
|
|
133
133
|
if test_case.source_lines && test_case.source_lines.size <= 3
|
134
134
|
test_case.source_lines.each do |line|
|
135
135
|
next if line.strip.empty? || line.strip.start_with?('#')
|
136
|
+
|
136
137
|
io.puts indent_text(" #{line.strip}", 1)
|
137
138
|
break # Only show first relevant line
|
138
139
|
end
|
@@ -209,7 +210,7 @@ class Tryouts
|
|
209
210
|
|
210
211
|
time_str = format_timing(elapsed_time)
|
211
212
|
|
212
|
-
io.puts "Total: #{result}
|
213
|
+
io.puts "Total: #{result} #{time_str}"
|
213
214
|
io.puts "Files: #{successful_files} of #{total_files} successful"
|
214
215
|
end
|
215
216
|
|
@@ -112,7 +112,7 @@ class Tryouts
|
|
112
112
|
|
113
113
|
issues_count = failed_count + error_count
|
114
114
|
if issues_count > 0
|
115
|
-
passed
|
115
|
+
passed = [total_tests - issues_count, 0].max # Ensure passed never goes negative
|
116
116
|
details = []
|
117
117
|
details << "#{failed_count} failed" if failed_count > 0
|
118
118
|
details << "#{error_count} errors" if error_count > 0
|
@@ -80,7 +80,7 @@ class Tryouts
|
|
80
80
|
details = [
|
81
81
|
"#{passed_count} passed",
|
82
82
|
]
|
83
|
-
|
83
|
+
puts
|
84
84
|
if issues_count > 0
|
85
85
|
details << "#{failed_count} failed" if failed_count > 0
|
86
86
|
details << "#{error_count} errors" if error_count > 0
|
@@ -134,7 +134,7 @@ class Tryouts
|
|
134
134
|
end
|
135
135
|
|
136
136
|
test_case = result_packet.test_case
|
137
|
-
location
|
137
|
+
location = "#{Console.pretty_path(test_case.path)}:#{test_case.first_expectation_line + 1}"
|
138
138
|
puts
|
139
139
|
puts indent_text("#{status_line} @ #{location}", 2)
|
140
140
|
|
@@ -181,6 +181,7 @@ class Tryouts
|
|
181
181
|
def teardown_start(line_range)
|
182
182
|
message = "Executing teardown (lines #{line_range.first}..#{line_range.last})"
|
183
183
|
puts indent_text(Console.color(:cyan, message), 2)
|
184
|
+
puts
|
184
185
|
end
|
185
186
|
|
186
187
|
def teardown_output(output_text)
|
@@ -278,7 +279,7 @@ class Tryouts
|
|
278
279
|
puts indent_text('Exception Details:', 4)
|
279
280
|
|
280
281
|
actual_results.each_with_index do |actual, idx|
|
281
|
-
expected
|
282
|
+
expected = expected_results[idx] if expected_results && idx < expected_results.length
|
282
283
|
expectation = test_case.expectations[idx] if test_case.expectations
|
283
284
|
|
284
285
|
if expectation&.type == :exception
|
@@ -299,7 +300,7 @@ class Tryouts
|
|
299
300
|
line_display = format('%3d: %s', line_num + 1, line_content)
|
300
301
|
|
301
302
|
# Highlight expectation lines by checking if this line contains any expectation syntax
|
302
|
-
if line_content.match?(
|
303
|
+
if line_content.match?(%r{^\s*#\s*=(!|<|=|/=|\||:|~|%|\d+)?>\s*})
|
303
304
|
line_display = Console.color(:yellow, line_display)
|
304
305
|
end
|
305
306
|
|
@@ -319,12 +320,13 @@ class Tryouts
|
|
319
320
|
# Use the evaluated expected value from the evaluator
|
320
321
|
puts indent_text("Expected: #{Console.color(:green, expected.inspect)}", 4)
|
321
322
|
puts indent_text("Actual: #{Console.color(:red, actual.inspect)}", 4)
|
322
|
-
elsif expected_line
|
323
|
-
#
|
323
|
+
elsif expected_line && !expected_results.empty?
|
324
|
+
# Only show raw expectation content if we have expected_results (non-error case)
|
324
325
|
puts indent_text("Expected: #{Console.color(:green, expected_line.content)}", 4)
|
325
326
|
puts indent_text("Actual: #{Console.color(:red, actual.inspect)}", 4)
|
326
327
|
else
|
327
|
-
|
328
|
+
# For error cases (empty expected_results), just show the error
|
329
|
+
puts indent_text("Error: #{Console.color(:red, actual.inspect)}", 4)
|
328
330
|
end
|
329
331
|
|
330
332
|
# Show difference if both are strings
|
data/lib/tryouts/console.rb
CHANGED
@@ -143,7 +143,7 @@ class Tryouts
|
|
143
143
|
|
144
144
|
# Enable colors if neither appears redirected
|
145
145
|
return "\e[%sm" % att.join(';') unless stdout_redirected || stderr_redirected
|
146
|
-
rescue
|
146
|
+
rescue StandardError
|
147
147
|
# If stat fails, fall back to enabling colors with TERM set
|
148
148
|
return "\e[%sm" % att.join(';')
|
149
149
|
end
|
@@ -45,7 +45,7 @@ class Tryouts
|
|
45
45
|
|
46
46
|
def evaluate(actual_result = nil)
|
47
47
|
expectation_result = ExpectationResult.from_result(actual_result)
|
48
|
-
expression_result
|
48
|
+
expression_result = eval_expectation_content(@expectation.content, expectation_result)
|
49
49
|
|
50
50
|
build_result(
|
51
51
|
passed: [true, false].include?(expression_result),
|
@@ -22,7 +22,7 @@ class Tryouts
|
|
22
22
|
|
23
23
|
# Create result packet for evaluation to show what was expected
|
24
24
|
expectation_result = ExpectationResult.from_result(nil)
|
25
|
-
expected_value
|
25
|
+
expected_value = eval_expectation_content(@expectation.content, expectation_result)
|
26
26
|
|
27
27
|
build_result(
|
28
28
|
passed: false,
|
@@ -41,7 +41,7 @@ class Tryouts
|
|
41
41
|
@context.define_singleton_method(:error) { caught_error }
|
42
42
|
|
43
43
|
expectation_result = ExpectationResult.from_result(caught_error)
|
44
|
-
expected_value
|
44
|
+
expected_value = eval_expectation_content(@expectation.content, expectation_result)
|
45
45
|
|
46
46
|
build_result(
|
47
47
|
passed: !!expected_value,
|
@@ -32,7 +32,7 @@ class Tryouts
|
|
32
32
|
start_time_ns: nil,
|
33
33
|
end_time_ns: nil,
|
34
34
|
stdout_content: nil,
|
35
|
-
stderr_content: nil
|
35
|
+
stderr_content: nil,
|
36
36
|
)
|
37
37
|
end
|
38
38
|
|
@@ -46,7 +46,7 @@ class Tryouts
|
|
46
46
|
start_time_ns: start_time_ns,
|
47
47
|
end_time_ns: end_time_ns,
|
48
48
|
stdout_content: nil,
|
49
|
-
stderr_content: nil
|
49
|
+
stderr_content: nil,
|
50
50
|
)
|
51
51
|
end
|
52
52
|
|
@@ -59,7 +59,7 @@ class Tryouts
|
|
59
59
|
start_time_ns: nil,
|
60
60
|
end_time_ns: nil,
|
61
61
|
stdout_content: stdout_content,
|
62
|
-
stderr_content: stderr_content
|
62
|
+
stderr_content: stderr_content,
|
63
63
|
)
|
64
64
|
end
|
65
65
|
end
|
@@ -45,7 +45,7 @@ class Tryouts
|
|
45
45
|
|
46
46
|
def evaluate(actual_result = nil)
|
47
47
|
expectation_result = ExpectationResult.from_result(actual_result)
|
48
|
-
expression_result
|
48
|
+
expression_result = eval_expectation_content(@expectation.content, expectation_result)
|
49
49
|
|
50
50
|
build_result(
|
51
51
|
passed: expression_result == false,
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
require_relative 'base'
|
4
4
|
require_relative 'regular'
|
5
|
-
require_relative '../
|
5
|
+
require_relative '../test_case'
|
6
6
|
|
7
7
|
class Tryouts
|
8
8
|
module ExpectationEvaluators
|
@@ -56,14 +56,14 @@ class Tryouts
|
|
56
56
|
|
57
57
|
# Delegate to regular evaluator
|
58
58
|
regular_evaluator = Regular.new(regular_expectation, @test_case, @context)
|
59
|
-
regular_result
|
59
|
+
regular_result = regular_evaluator.evaluate(actual_result)
|
60
60
|
|
61
61
|
# Invert the result while preserving metadata
|
62
62
|
build_result(
|
63
63
|
passed: !regular_result[:passed],
|
64
64
|
actual: regular_result[:actual],
|
65
65
|
expected: "NOT #{regular_result[:expected]} (intentional failure)",
|
66
|
-
expectation: @expectation.content
|
66
|
+
expectation: @expectation.content,
|
67
67
|
)
|
68
68
|
rescue StandardError => ex
|
69
69
|
# If evaluation itself fails (not the expectation), that's a real error
|
@@ -56,9 +56,9 @@ class Tryouts
|
|
56
56
|
|
57
57
|
# Get the appropriate captured content
|
58
58
|
captured_content = case pipe_number
|
59
|
-
when 1 then stdout_content ||
|
60
|
-
when 2 then stderr_content ||
|
61
|
-
else
|
59
|
+
when 1 then stdout_content || ''
|
60
|
+
when 2 then stderr_content || ''
|
61
|
+
else ''
|
62
62
|
end
|
63
63
|
|
64
64
|
# Create result packet for expression evaluation
|
@@ -82,8 +82,8 @@ class Tryouts
|
|
82
82
|
|
83
83
|
# Build result with appropriate pipe description
|
84
84
|
pipe_name = case pipe_number
|
85
|
-
when 1 then
|
86
|
-
when 2 then
|
85
|
+
when 1 then 'stdout'
|
86
|
+
when 2 then 'stderr'
|
87
87
|
else "pipe#{pipe_number}"
|
88
88
|
end
|
89
89
|
|
@@ -91,7 +91,7 @@ class Tryouts
|
|
91
91
|
passed: matched,
|
92
92
|
actual: "#{pipe_name}: #{captured_content.inspect}",
|
93
93
|
expected: expected_pattern.inspect,
|
94
|
-
expectation: @expectation.content
|
94
|
+
expectation: @expectation.content,
|
95
95
|
)
|
96
96
|
rescue StandardError => ex
|
97
97
|
handle_evaluation_error(ex, actual_result)
|
@@ -54,18 +54,18 @@ class Tryouts
|
|
54
54
|
passed: false,
|
55
55
|
actual: 'No timing data available',
|
56
56
|
expected: 'Performance measurement',
|
57
|
-
error: 'Performance expectations require execution timing data'
|
57
|
+
error: 'Performance expectations require execution timing data',
|
58
58
|
)
|
59
59
|
end
|
60
60
|
|
61
61
|
# Create result packet with timing data available to expectation
|
62
62
|
expectation_result = ExpectationResult.from_timing(actual_result, execution_time_ns)
|
63
|
-
expected_limit_ms
|
63
|
+
expected_limit_ms = eval_expectation_content(@expectation.content, expectation_result)
|
64
64
|
|
65
65
|
actual_time_ms = expectation_result.execution_time_ms
|
66
66
|
|
67
67
|
# Performance tolerance: actual <= expected + 10% (not strict window)
|
68
|
-
max_allowed_ms
|
68
|
+
max_allowed_ms = expected_limit_ms * 1.1
|
69
69
|
within_tolerance = actual_time_ms <= max_allowed_ms
|
70
70
|
|
71
71
|
build_result(
|
@@ -38,11 +38,11 @@ class Tryouts
|
|
38
38
|
|
39
39
|
def evaluate(actual_result = nil)
|
40
40
|
expectation_result = ExpectationResult.from_result(actual_result)
|
41
|
-
pattern
|
41
|
+
pattern = eval_expectation_content(@expectation.content, expectation_result)
|
42
42
|
|
43
43
|
# Convert actual_result to string for regex matching
|
44
44
|
string_result = actual_result.to_s
|
45
|
-
match_result
|
45
|
+
match_result = string_result =~ pattern
|
46
46
|
|
47
47
|
build_result(
|
48
48
|
passed: !match_result.nil?,
|
@@ -52,7 +52,7 @@ class Tryouts
|
|
52
52
|
|
53
53
|
def evaluate(actual_result = nil)
|
54
54
|
expectation_result = ExpectationResult.from_result(actual_result)
|
55
|
-
expected_value
|
55
|
+
expected_value = eval_expectation_content(@expectation.content, expectation_result)
|
56
56
|
|
57
57
|
build_result(
|
58
58
|
passed: actual_result == expected_value,
|
@@ -36,7 +36,7 @@ class Tryouts
|
|
36
36
|
|
37
37
|
def evaluate(actual_result = nil)
|
38
38
|
expectation_result = ExpectationResult.from_result(actual_result)
|
39
|
-
expected_class
|
39
|
+
expected_class = eval_expectation_content(@expectation.content, expectation_result)
|
40
40
|
|
41
41
|
build_result(
|
42
42
|
passed: actual_result.is_a?(expected_class),
|
@@ -42,8 +42,8 @@ class Tryouts
|
|
42
42
|
end
|
43
43
|
|
44
44
|
def evaluate(actual_result = nil)
|
45
|
-
expectation_result
|
46
|
-
expression_result
|
45
|
+
expectation_result = ExpectationResult.from_result(actual_result)
|
46
|
+
expression_result = eval_expectation_content(@expectation.content, expectation_result)
|
47
47
|
|
48
48
|
build_result(
|
49
49
|
passed: expression_result == true,
|
data/lib/tryouts/prism_parser.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# Modern Ruby 3.4+ solution for the teardown bug
|
2
2
|
|
3
3
|
require 'prism'
|
4
|
-
require_relative '
|
4
|
+
require_relative 'test_case'
|
5
5
|
|
6
6
|
class Tryouts
|
7
7
|
# Fixed PrismParser with pattern matching for robust token filtering
|
@@ -16,8 +16,10 @@ class Tryouts
|
|
16
16
|
def parse
|
17
17
|
return handle_syntax_errors if @prism_result.failure?
|
18
18
|
|
19
|
-
tokens
|
20
|
-
|
19
|
+
tokens = tokenize_content
|
20
|
+
test_boundaries = find_test_case_boundaries(tokens)
|
21
|
+
tokens = classify_potential_descriptions_with_boundaries(tokens, test_boundaries)
|
22
|
+
test_blocks = group_into_test_blocks(tokens)
|
21
23
|
process_test_blocks(test_blocks)
|
22
24
|
end
|
23
25
|
|
@@ -66,8 +68,106 @@ class Tryouts
|
|
66
68
|
tokens << token
|
67
69
|
end
|
68
70
|
|
69
|
-
#
|
70
|
-
|
71
|
+
# Return tokens with potential_descriptions - they'll be classified later with test boundaries
|
72
|
+
tokens
|
73
|
+
end
|
74
|
+
|
75
|
+
# Find actual test case boundaries by looking for ## descriptions or # TEST: patterns
|
76
|
+
# followed by code and expectations
|
77
|
+
def find_test_case_boundaries(tokens)
|
78
|
+
boundaries = []
|
79
|
+
|
80
|
+
tokens.each_with_index do |token, index|
|
81
|
+
# Look for explicit test descriptions (## or # TEST:)
|
82
|
+
if token[:type] == :description
|
83
|
+
# Find the end of this test case by looking for the last expectation
|
84
|
+
# before the next description or end of file
|
85
|
+
start_line = token[:line]
|
86
|
+
end_line = find_test_case_end(tokens, index)
|
87
|
+
|
88
|
+
boundaries << { start: start_line, end: end_line } if end_line
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
boundaries
|
93
|
+
end
|
94
|
+
|
95
|
+
# Find where a test case ends by looking for the last expectation
|
96
|
+
# before the next test description or end of tokens
|
97
|
+
def find_test_case_end(tokens, start_index)
|
98
|
+
last_expectation_line = nil
|
99
|
+
|
100
|
+
# Look forward from the description for expectations
|
101
|
+
(start_index + 1).upto(tokens.length - 1) do |i|
|
102
|
+
token = tokens[i]
|
103
|
+
|
104
|
+
# Stop if we hit another test description
|
105
|
+
break if token[:type] == :description
|
106
|
+
|
107
|
+
# Track the last expectation we see
|
108
|
+
if is_expectation_type?(token[:type])
|
109
|
+
last_expectation_line = token[:line]
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
last_expectation_line
|
114
|
+
end
|
115
|
+
|
116
|
+
# Convert potential_descriptions to descriptions or comments using test case boundaries
|
117
|
+
def classify_potential_descriptions_with_boundaries(tokens, test_boundaries)
|
118
|
+
tokens.map.with_index do |token, index|
|
119
|
+
if token[:type] == :potential_description
|
120
|
+
# Check if this comment falls within any test case boundary
|
121
|
+
line_num = token[:line]
|
122
|
+
within_test_case = test_boundaries.any? do |boundary|
|
123
|
+
line_num >= boundary[:start] && line_num <= boundary[:end]
|
124
|
+
end
|
125
|
+
|
126
|
+
if within_test_case
|
127
|
+
# This comment is within a test case, treat as regular comment
|
128
|
+
token.merge(type: :comment)
|
129
|
+
else
|
130
|
+
# For comments outside test boundaries, be more conservative
|
131
|
+
# Only treat as description if it immediately precedes a test pattern AND
|
132
|
+
# looks like a test description
|
133
|
+
content = token[:content].strip
|
134
|
+
|
135
|
+
# Check if this looks like a test description based on content
|
136
|
+
looks_like_test_description = content.match?(/test|example|demonstrate|show|should|when|given/i) &&
|
137
|
+
content.length > 10
|
138
|
+
|
139
|
+
# Check if there's code immediately before this (suggesting it's mid-test)
|
140
|
+
prev_token = index > 0 ? tokens[index - 1] : nil
|
141
|
+
has_code_before = prev_token && prev_token[:type] == :code
|
142
|
+
|
143
|
+
if has_code_before || !looks_like_test_description
|
144
|
+
# Treat as regular comment
|
145
|
+
token.merge(type: :comment)
|
146
|
+
else
|
147
|
+
# Look ahead for IMMEDIATE test pattern (stricter than before)
|
148
|
+
following_tokens = tokens[(index + 1)..]
|
149
|
+
|
150
|
+
# Skip blanks and comments to find meaningful content
|
151
|
+
meaningful_following = following_tokens.reject { |t| [:blank, :comment].include?(t[:type]) }
|
152
|
+
|
153
|
+
# Look for test pattern within next 5 tokens (more restrictive)
|
154
|
+
test_window = meaningful_following.first(5)
|
155
|
+
has_code = test_window.any? { |t| t[:type] == :code }
|
156
|
+
has_expectation = test_window.any? { |t| is_expectation_type?(t[:type]) }
|
157
|
+
|
158
|
+
# Only promote to description if BOTH code and expectation are found nearby
|
159
|
+
# AND it looks like a test description
|
160
|
+
if has_code && has_expectation && looks_like_test_description
|
161
|
+
token.merge(type: :description)
|
162
|
+
else
|
163
|
+
token.merge(type: :comment)
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
else
|
168
|
+
token
|
169
|
+
end
|
170
|
+
end
|
71
171
|
end
|
72
172
|
|
73
173
|
# Convert potential_descriptions to descriptions or comments based on context
|
@@ -80,11 +180,11 @@ class Tryouts
|
|
80
180
|
# Skip if it's clearly just a regular comment (short, lowercase, etc.)
|
81
181
|
# Test descriptions are typically longer and more descriptive
|
82
182
|
looks_like_regular_comment = content.length < 20 &&
|
83
|
-
|
84
|
-
|
183
|
+
content.downcase == content &&
|
184
|
+
!content.match?(/test|example|demonstrate|show/i)
|
85
185
|
|
86
186
|
# Check if there's code immediately before this (suggesting it's mid-test)
|
87
|
-
prev_token
|
187
|
+
prev_token = index > 0 ? tokens[index - 1] : nil
|
88
188
|
has_code_before = prev_token && prev_token[:type] == :code
|
89
189
|
|
90
190
|
if looks_like_regular_comment || has_code_before
|
@@ -99,8 +199,8 @@ class Tryouts
|
|
99
199
|
|
100
200
|
# Look for test pattern: at least one code token followed by at least one expectation
|
101
201
|
# within the next 10 meaningful tokens (to avoid matching setup/teardown)
|
102
|
-
test_window
|
103
|
-
has_code
|
202
|
+
test_window = meaningful_following.first(10)
|
203
|
+
has_code = test_window.any? { |t| t[:type] == :code }
|
104
204
|
has_expectation = test_window.any? { |t| is_expectation_type?(t[:type]) }
|
105
205
|
|
106
206
|
if has_code && has_expectation
|
@@ -360,15 +460,18 @@ class Tryouts
|
|
360
460
|
# Extract source lines from the original source during parsing
|
361
461
|
source_lines = @lines[start_line..end_line]
|
362
462
|
|
463
|
+
# Find the first expectation line for better error reporting
|
464
|
+
first_expectation_line = exp_tokens.empty? ? start_line : exp_tokens.first[:line]
|
465
|
+
|
363
466
|
TestCase.new(
|
364
467
|
description: desc,
|
365
468
|
code: extract_code_content(code_tokens),
|
366
|
-
expectations: exp_tokens.map
|
469
|
+
expectations: exp_tokens.map do |token|
|
367
470
|
type = case token[:type]
|
368
471
|
when :exception_expectation then :exception
|
369
472
|
when :intentional_failure_expectation then :intentional_failure
|
370
|
-
when :true_expectation then :true
|
371
|
-
when :false_expectation then :false
|
473
|
+
when :true_expectation then :true # rubocop:disable Lint/BooleanSymbol
|
474
|
+
when :false_expectation then :false # rubocop:disable Lint/BooleanSymbol
|
372
475
|
when :boolean_expectation then :boolean
|
373
476
|
when :result_type_expectation then :result_type
|
374
477
|
when :regex_match_expectation then :regex_match
|
@@ -383,10 +486,11 @@ class Tryouts
|
|
383
486
|
else
|
384
487
|
Expectation.new(content: token[:content], type: type)
|
385
488
|
end
|
386
|
-
|
489
|
+
end,
|
387
490
|
line_range: start_line..end_line,
|
388
491
|
path: @source_path,
|
389
492
|
source_lines: source_lines,
|
493
|
+
first_expectation_line: first_expectation_line,
|
390
494
|
)
|
391
495
|
else
|
392
496
|
raise "Invalid test block structure: #{block}"
|
@@ -38,10 +38,13 @@ class Tryouts
|
|
38
38
|
@test_case_count = 0
|
39
39
|
@setup_failed = false
|
40
40
|
|
41
|
+
# Setup container for fresh context mode - preserves @instance_variables from setup
|
42
|
+
@setup_container = nil
|
43
|
+
|
41
44
|
# Circuit breaker for batch-level failure protection
|
42
|
-
@consecutive_failures
|
45
|
+
@consecutive_failures = 0
|
43
46
|
@max_consecutive_failures = options[:max_consecutive_failures] || 10
|
44
|
-
@circuit_breaker_active
|
47
|
+
@circuit_breaker_active = false
|
45
48
|
|
46
49
|
# Expose context objects for testing - different strategies for each mode
|
47
50
|
@shared_context = if options[:shared_context]
|
@@ -68,7 +71,19 @@ class Tryouts
|
|
68
71
|
|
69
72
|
# Stop execution if setup failed
|
70
73
|
if @setup_failed
|
71
|
-
@output_manager&.error(
|
74
|
+
@output_manager&.error('Stopping batch execution due to setup failure')
|
75
|
+
@status = :failed
|
76
|
+
finalize_results([])
|
77
|
+
return false
|
78
|
+
end
|
79
|
+
else
|
80
|
+
# Fresh context mode: execute setup once to establish shared @instance_variables
|
81
|
+
@output_manager&.info('Running setup for fresh context...', 2)
|
82
|
+
execute_fresh_context_setup
|
83
|
+
|
84
|
+
# Stop execution if setup failed
|
85
|
+
if @setup_failed
|
86
|
+
@output_manager&.error('Stopping batch execution due to setup failure')
|
72
87
|
@status = :failed
|
73
88
|
finalize_results([])
|
74
89
|
return false
|
@@ -94,10 +109,10 @@ class Tryouts
|
|
94
109
|
update_circuit_breaker(result)
|
95
110
|
|
96
111
|
result
|
97
|
-
rescue StandardError =>
|
98
|
-
@output_manager&.test_end(test_case, idx, @test_case_count, status: :failed, error:
|
112
|
+
rescue StandardError => ex
|
113
|
+
@output_manager&.test_end(test_case, idx, @test_case_count, status: :failed, error: ex)
|
99
114
|
# Create error result packet to maintain consistent data flow
|
100
|
-
error_result = build_error_result(test_case,
|
115
|
+
error_result = build_error_result(test_case, ex)
|
101
116
|
process_test_result(error_result)
|
102
117
|
|
103
118
|
# Update circuit breaker for exception cases
|
@@ -174,7 +189,7 @@ class Tryouts
|
|
174
189
|
error: result.error,
|
175
190
|
captured_output: captured_output,
|
176
191
|
elapsed_time: result.elapsed_time,
|
177
|
-
metadata: result.metadata
|
192
|
+
metadata: result.metadata,
|
178
193
|
)
|
179
194
|
end
|
180
195
|
|
@@ -188,7 +203,7 @@ class Tryouts
|
|
188
203
|
execute_test_case_with_container(test_case, @container)
|
189
204
|
end
|
190
205
|
|
191
|
-
# Fresh context execution -
|
206
|
+
# Fresh context execution - tests run in isolated state but inherit setup @instance_variables
|
192
207
|
def execute_with_fresh_context(test_case)
|
193
208
|
fresh_container = if @shared_context.is_a?(FreshContextFactory)
|
194
209
|
@shared_context.create_container
|
@@ -196,10 +211,12 @@ class Tryouts
|
|
196
211
|
Object.new # Fallback for backwards compatibility
|
197
212
|
end
|
198
213
|
|
199
|
-
#
|
200
|
-
|
201
|
-
|
202
|
-
|
214
|
+
# Copy @instance_variables from setup container to fresh container
|
215
|
+
if @setup_container
|
216
|
+
@setup_container.instance_variables.each do |var|
|
217
|
+
value = @setup_container.instance_variable_get(var)
|
218
|
+
fresh_container.instance_variable_set(var, value)
|
219
|
+
end
|
203
220
|
end
|
204
221
|
|
205
222
|
execute_test_case_with_container(test_case, fresh_container)
|
@@ -225,7 +242,7 @@ class Tryouts
|
|
225
242
|
# Check if we need output capture for any expectations
|
226
243
|
needs_output_capture = test_case.expectations.any?(&:output?)
|
227
244
|
|
228
|
-
result_value,
|
245
|
+
result_value, _, _, _, expectations_result =
|
229
246
|
execute_with_timeout(test_timeout, test_case) do
|
230
247
|
if needs_output_capture
|
231
248
|
# Execute with output capture using Fiber-local isolation
|
@@ -239,9 +256,9 @@ class Tryouts
|
|
239
256
|
else
|
240
257
|
# Regular execution with timing capture only
|
241
258
|
execution_start_ns = Process.clock_gettime(Process::CLOCK_MONOTONIC, :nanosecond)
|
242
|
-
result_value
|
243
|
-
execution_end_ns
|
244
|
-
execution_time_ns
|
259
|
+
result_value = container.instance_eval(code, path, range.first + 1)
|
260
|
+
execution_end_ns = Process.clock_gettime(Process::CLOCK_MONOTONIC, :nanosecond)
|
261
|
+
execution_time_ns = execution_end_ns - execution_start_ns
|
245
262
|
|
246
263
|
expectations_result = evaluate_expectations(test_case, result_value, container, execution_time_ns)
|
247
264
|
[result_value, execution_time_ns, nil, nil, expectations_result]
|
@@ -276,9 +293,9 @@ class Tryouts
|
|
276
293
|
|
277
294
|
# Execute with timing capture
|
278
295
|
execution_start_ns = Process.clock_gettime(Process::CLOCK_MONOTONIC, :nanosecond)
|
279
|
-
result_value
|
280
|
-
execution_end_ns
|
281
|
-
execution_time_ns
|
296
|
+
result_value = container.instance_eval(code, path, range.first + 1)
|
297
|
+
execution_end_ns = Process.clock_gettime(Process::CLOCK_MONOTONIC, :nanosecond)
|
298
|
+
execution_time_ns = execution_end_ns - execution_start_ns
|
282
299
|
|
283
300
|
[result_value, execution_time_ns]
|
284
301
|
end.resume.tap do |result_value, execution_time_ns|
|
@@ -317,7 +334,7 @@ class Tryouts
|
|
317
334
|
{
|
318
335
|
passed: evaluation_results.all? { |r| r[:passed] },
|
319
336
|
actual_results: evaluation_results.map { |r| r[:actual] },
|
320
|
-
expected_results: evaluation_results.map { |r| r[:expected] }
|
337
|
+
expected_results: evaluation_results.map { |r| r[:expected] },
|
321
338
|
}
|
322
339
|
end
|
323
340
|
|
@@ -328,14 +345,14 @@ class Tryouts
|
|
328
345
|
test_case,
|
329
346
|
result_value,
|
330
347
|
expectations_result[:actual_results],
|
331
|
-
expectations_result[:expected_results]
|
348
|
+
expectations_result[:expected_results],
|
332
349
|
)
|
333
350
|
else
|
334
351
|
TestCaseResultPacket.from_failure(
|
335
352
|
test_case,
|
336
353
|
result_value,
|
337
354
|
expectations_result[:actual_results],
|
338
|
-
expectations_result[:expected_results]
|
355
|
+
expectations_result[:expected_results],
|
339
356
|
)
|
340
357
|
end
|
341
358
|
end
|
@@ -375,7 +392,7 @@ class Tryouts
|
|
375
392
|
@output_manager&.setup_output(captured_output) if captured_output && !captured_output.empty?
|
376
393
|
end
|
377
394
|
rescue StandardError => ex
|
378
|
-
@setup_failed
|
395
|
+
@setup_failed = true
|
379
396
|
@global_tally[:total_errors] += 1 if @global_tally
|
380
397
|
|
381
398
|
# Classify error and handle appropriately
|
@@ -394,6 +411,43 @@ class Tryouts
|
|
394
411
|
raise "Global setup failed (#{ex.class}): #{ex.message}"
|
395
412
|
end
|
396
413
|
|
414
|
+
# Setup execution for fresh context mode - creates @setup_container with @instance_variables
|
415
|
+
def execute_fresh_context_setup
|
416
|
+
setup = @testrun.setup
|
417
|
+
|
418
|
+
if setup && !setup.code.empty? && !@options[:shared_context]
|
419
|
+
@output_manager&.setup_start(setup.line_range)
|
420
|
+
|
421
|
+
# Create setup container to hold @instance_variables
|
422
|
+
@setup_container = Object.new
|
423
|
+
|
424
|
+
# Capture setup output instead of letting it print directly
|
425
|
+
captured_output = capture_output do
|
426
|
+
@setup_container.instance_eval(setup.code, setup.path, setup.line_range.first + 1)
|
427
|
+
end
|
428
|
+
|
429
|
+
@output_manager&.setup_output(captured_output) if captured_output && !captured_output.empty?
|
430
|
+
end
|
431
|
+
rescue StandardError => ex
|
432
|
+
@setup_failed = true
|
433
|
+
@global_tally[:total_errors] += 1 if @global_tally
|
434
|
+
|
435
|
+
# Classify error and handle appropriately
|
436
|
+
error_type = Tryouts.classify_error(ex)
|
437
|
+
|
438
|
+
Tryouts.debug "Setup failed with #{error_type} error: (#{ex.class}): #{ex.message}"
|
439
|
+
Tryouts.trace ex.backtrace
|
440
|
+
|
441
|
+
# For non-catastrophic errors, we still stop batch execution
|
442
|
+
unless Tryouts.batch_stopping_error?(ex)
|
443
|
+
@output_manager&.error("Fresh context setup failed: #{ex.message}")
|
444
|
+
return
|
445
|
+
end
|
446
|
+
|
447
|
+
# For catastrophic errors, still raise to stop execution
|
448
|
+
raise "Fresh context setup failed (#{ex.class}): #{ex.message}"
|
449
|
+
end
|
450
|
+
|
397
451
|
# Global teardown execution
|
398
452
|
def execute_global_teardown
|
399
453
|
teardown = @testrun.teardown
|
@@ -420,11 +474,11 @@ class Tryouts
|
|
420
474
|
@output_manager&.error("Teardown failed: #{ex.message}")
|
421
475
|
|
422
476
|
# Teardown failures are generally non-fatal - log and continue
|
423
|
-
|
424
|
-
@output_manager&.error("Continuing despite teardown failure")
|
425
|
-
else
|
477
|
+
if Tryouts.batch_stopping_error?(ex)
|
426
478
|
# Only catastrophic errors should potentially affect batch completion
|
427
|
-
@output_manager&.error(
|
479
|
+
@output_manager&.error('Teardown failure may affect subsequent operations')
|
480
|
+
else
|
481
|
+
@output_manager&.error('Continuing despite teardown failure')
|
428
482
|
end
|
429
483
|
end
|
430
484
|
|
@@ -477,11 +531,9 @@ class Tryouts
|
|
477
531
|
end
|
478
532
|
|
479
533
|
# Timeout protection for individual test execution
|
480
|
-
def execute_with_timeout(timeout_seconds, test_case)
|
481
|
-
Timeout.timeout(timeout_seconds)
|
482
|
-
|
483
|
-
end
|
484
|
-
rescue Timeout::Error => e
|
534
|
+
def execute_with_timeout(timeout_seconds, test_case, &)
|
535
|
+
Timeout.timeout(timeout_seconds, &)
|
536
|
+
rescue Timeout::Error
|
485
537
|
Tryouts.debug "Test timeout after #{timeout_seconds}s: #{test_case.description}"
|
486
538
|
raise StandardError.new("Test execution timeout (#{timeout_seconds}s)")
|
487
539
|
end
|
@@ -496,7 +548,7 @@ class Tryouts
|
|
496
548
|
end
|
497
549
|
else
|
498
550
|
# Reset on success
|
499
|
-
@consecutive_failures
|
551
|
+
@consecutive_failures = 0
|
500
552
|
@circuit_breaker_active = false
|
501
553
|
end
|
502
554
|
end
|
@@ -3,7 +3,7 @@
|
|
3
3
|
# Modern data structures using Ruby 3.2+ Data classes
|
4
4
|
class Tryouts
|
5
5
|
# Core data structures
|
6
|
-
TestCase = Data.define(:description, :code, :expectations, :line_range, :path, :source_lines) do
|
6
|
+
TestCase = Data.define(:description, :code, :expectations, :line_range, :path, :source_lines, :first_expectation_line) do
|
7
7
|
def empty?
|
8
8
|
code.empty?
|
9
9
|
end
|
@@ -89,7 +89,7 @@ class Tryouts
|
|
89
89
|
:error, # Exception object (if any)
|
90
90
|
:captured_output, # Captured stdout/stderr content
|
91
91
|
:elapsed_time, # Execution timing (future use)
|
92
|
-
:metadata # Hash for future extensibility
|
92
|
+
:metadata, # Hash for future extensibility
|
93
93
|
) do
|
94
94
|
def passed?
|
95
95
|
status == :passed
|
@@ -131,7 +131,7 @@ class Tryouts
|
|
131
131
|
error: nil,
|
132
132
|
captured_output: captured_output,
|
133
133
|
elapsed_time: elapsed_time,
|
134
|
-
metadata: metadata
|
134
|
+
metadata: metadata,
|
135
135
|
)
|
136
136
|
end
|
137
137
|
|
@@ -146,7 +146,7 @@ class Tryouts
|
|
146
146
|
error: nil,
|
147
147
|
captured_output: captured_output,
|
148
148
|
elapsed_time: elapsed_time,
|
149
|
-
metadata: metadata
|
149
|
+
metadata: metadata,
|
150
150
|
)
|
151
151
|
end
|
152
152
|
|
@@ -171,7 +171,7 @@ class Tryouts
|
|
171
171
|
error: error,
|
172
172
|
captured_output: captured_output,
|
173
173
|
elapsed_time: elapsed_time,
|
174
|
-
metadata: metadata
|
174
|
+
metadata: metadata,
|
175
175
|
)
|
176
176
|
end
|
177
177
|
end
|
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.each_with_index do |file,
|
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'
|
@@ -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'
|
data/lib/tryouts/version.rb
CHANGED
data/lib/tryouts.rb
CHANGED
@@ -1,14 +1,12 @@
|
|
1
1
|
# lib/tryouts.rb
|
2
2
|
|
3
|
-
|
4
|
-
|
5
3
|
require 'stringio'
|
6
4
|
require 'timeout'
|
7
5
|
|
8
6
|
TRYOUTS_LIB_HOME = __dir__ unless defined?(TRYOUTS_LIB_HOME)
|
9
7
|
|
10
8
|
require_relative 'tryouts/console'
|
11
|
-
require_relative 'tryouts/
|
9
|
+
require_relative 'tryouts/test_batch'
|
12
10
|
require_relative 'tryouts/version'
|
13
11
|
require_relative 'tryouts/prism_parser'
|
14
12
|
require_relative 'tryouts/cli'
|
@@ -20,7 +18,6 @@ class Tryouts
|
|
20
18
|
@fails = false
|
21
19
|
@container = Class.new
|
22
20
|
@cases = [] # rubocop:disable ThreadSafety/MutableClassInstanceVariable
|
23
|
-
@sysinfo = nil
|
24
21
|
@testcase_io = StringIO.new
|
25
22
|
|
26
23
|
module ClassMethods
|
@@ -28,12 +25,6 @@ class Tryouts
|
|
28
25
|
attr_writer :debug
|
29
26
|
attr_reader :cases, :testcase_io
|
30
27
|
|
31
|
-
def sysinfo
|
32
|
-
require 'sysinfo'
|
33
|
-
@sysinfo ||= SysInfo.new
|
34
|
-
@sysinfo
|
35
|
-
end
|
36
|
-
|
37
28
|
def debug?
|
38
29
|
@debug == true
|
39
30
|
end
|
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.1.
|
4
|
+
version: 3.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Delano Mandelbaum
|
@@ -37,26 +37,6 @@ dependencies:
|
|
37
37
|
- - "~>"
|
38
38
|
- !ruby/object:Gem::Version
|
39
39
|
version: '3.0'
|
40
|
-
- !ruby/object:Gem::Dependency
|
41
|
-
name: sysinfo
|
42
|
-
requirement: !ruby/object:Gem::Requirement
|
43
|
-
requirements:
|
44
|
-
- - ">="
|
45
|
-
- !ruby/object:Gem::Version
|
46
|
-
version: '0.8'
|
47
|
-
- - "<"
|
48
|
-
- !ruby/object:Gem::Version
|
49
|
-
version: '1.0'
|
50
|
-
type: :runtime
|
51
|
-
prerelease: false
|
52
|
-
version_requirements: !ruby/object:Gem::Requirement
|
53
|
-
requirements:
|
54
|
-
- - ">="
|
55
|
-
- !ruby/object:Gem::Version
|
56
|
-
version: '0.8'
|
57
|
-
- - "<"
|
58
|
-
- !ruby/object:Gem::Version
|
59
|
-
version: '1.0'
|
60
40
|
description: A simple test framework for Ruby code that uses introspection to allow
|
61
41
|
defining checks in comments.
|
62
42
|
email: gems@solutious.com
|
@@ -98,10 +78,10 @@ files:
|
|
98
78
|
- lib/tryouts/expectation_evaluators/true.rb
|
99
79
|
- lib/tryouts/file_processor.rb
|
100
80
|
- lib/tryouts/prism_parser.rb
|
81
|
+
- lib/tryouts/test_batch.rb
|
82
|
+
- lib/tryouts/test_case.rb
|
101
83
|
- lib/tryouts/test_executor.rb
|
102
84
|
- lib/tryouts/test_runner.rb
|
103
|
-
- lib/tryouts/testbatch.rb
|
104
|
-
- lib/tryouts/testcase.rb
|
105
85
|
- lib/tryouts/translators/minitest_translator.rb
|
106
86
|
- lib/tryouts/translators/rspec_translator.rb
|
107
87
|
- lib/tryouts/version.rb
|