tryouts 3.6.1 → 3.7.0
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 +1 -1
- data/exe/try +9 -0
- data/lib/tryouts/cli/formatters/agent.rb +46 -2
- data/lib/tryouts/cli/formatters/verbose.rb +45 -8
- data/lib/tryouts/cli/opts.rb +15 -0
- data/lib/tryouts/expectation_evaluators/exception.rb +25 -8
- data/lib/tryouts/expectation_evaluators/regex_match.rb +1 -2
- data/lib/tryouts/file_processor.rb +3 -34
- data/lib/tryouts/test_batch.rb +69 -7
- data/lib/tryouts/test_executor.rb +1 -0
- data/lib/tryouts/version.rb +1 -1
- metadata +27 -21
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7178f4f14ad2015fb3e5a795bbdd7813b4a184b56413535d7ddfff569badca9d
|
4
|
+
data.tar.gz: ff451b947073ebfbaece457e3b94cb090410ff86aa8ca09e8fdc8c998e04118c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d15d3afd82d4cef85ffe9eb481b5bfd68971c2cbc4247f8f6b46fbf53e7292ae6d22d396b1b2070481f0034c12079f8183ff66903a05f6a36dc754a7b33aef55
|
7
|
+
data.tar.gz: 936ecb57906f16befe499de544032e9f1cd25ed50f757652556f357ac80b6ff7d14dbb3787c8ff2eb2d93460f3c59a224eecbc2b491a936038394013976a3b42
|
data/README.md
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
**Ruby tests that read like documentation.**
|
4
4
|
|
5
|
-
A modern
|
5
|
+
A modern Ruby testing framework that uses simple comments to define what should happen. With Tryouts, your tests double as documentation, allowing you to write straightforward Ruby code that feels as natural as everyday coding.
|
6
6
|
|
7
7
|
> [!NOTE]
|
8
8
|
> **Agent-Optimized Output**: Tryouts includes specialized output modes for LLM consumption with `--agent` flag, providing structured, token-efficient test results that are 60-80% smaller than traditional output while preserving debugging context.
|
data/exe/try
CHANGED
@@ -48,6 +48,9 @@ begin
|
|
48
48
|
# Add original command to options for agent formatter
|
49
49
|
options[:original_command] = original_command
|
50
50
|
|
51
|
+
# Track whether user explicitly provided paths
|
52
|
+
explicit_paths_given = !files.empty?
|
53
|
+
|
51
54
|
# Expand files if directories are given, preserving line specs
|
52
55
|
expanded_files = []
|
53
56
|
files.each do |file_or_dir|
|
@@ -65,6 +68,12 @@ begin
|
|
65
68
|
end
|
66
69
|
files = expanded_files
|
67
70
|
|
71
|
+
# If user provided explicit paths but nothing matched, exit with error
|
72
|
+
if explicit_paths_given && files.empty?
|
73
|
+
warn 'Error: No test files found matching the given paths'
|
74
|
+
exit 1
|
75
|
+
end
|
76
|
+
|
68
77
|
# Default file discovery if no files specified
|
69
78
|
if files.empty?
|
70
79
|
raw_files = Dir.glob(
|
@@ -412,10 +412,10 @@ class Tryouts
|
|
412
412
|
output << summary
|
413
413
|
output << ""
|
414
414
|
|
415
|
-
# Show files with issues only
|
415
|
+
# Show files with issues only (unless suppressed)
|
416
416
|
files_with_issues = @collected_files.select { |f| f[:failures].any? || f[:errors].any? }
|
417
417
|
|
418
|
-
if files_with_issues.any?
|
418
|
+
if files_with_issues.any? && !@options[:agent_no_failures]
|
419
419
|
files_with_issues.each do |file_data|
|
420
420
|
break unless @budget.has_budget?
|
421
421
|
|
@@ -652,6 +652,18 @@ class Tryouts
|
|
652
652
|
end
|
653
653
|
end
|
654
654
|
|
655
|
+
# Add framework tips if requested
|
656
|
+
if @options[:agent_tips]
|
657
|
+
context_lines << ""
|
658
|
+
context_lines << render_framework_tips
|
659
|
+
end
|
660
|
+
|
661
|
+
# Add re-run command if requested and there are failures
|
662
|
+
if @options[:agent_command] && has_failures?
|
663
|
+
context_lines << ""
|
664
|
+
context_lines << render_rerun_command
|
665
|
+
end
|
666
|
+
|
655
667
|
context_lines.join("\n")
|
656
668
|
end
|
657
669
|
|
@@ -749,6 +761,38 @@ class Tryouts
|
|
749
761
|
# Return empty hash on any error (timeout, permission, etc.)
|
750
762
|
{}
|
751
763
|
end
|
764
|
+
|
765
|
+
# Render framework tips and reminders for LLM context
|
766
|
+
def render_framework_tips
|
767
|
+
tips = []
|
768
|
+
tips << "Framework Tips:"
|
769
|
+
tips << " • Instance variables (@var) persist across testcases; local variables do not"
|
770
|
+
tips << " • Each testcase can have multiple expectations (multiple #=> lines)"
|
771
|
+
tips << " • Exception testing: Use #=!> to signal expected exception; 'error' variable becomes available"
|
772
|
+
tips << " • In exception tests, #=~> automatically refers to error.message for pattern matching"
|
773
|
+
tips.join("\n")
|
774
|
+
end
|
775
|
+
|
776
|
+
# Render copy-paste command for re-running failures with verbose-fails-stack
|
777
|
+
def render_rerun_command
|
778
|
+
files_with_issues = @collected_files.select { |f| f[:failures].any? || f[:errors].any? }
|
779
|
+
return "" if files_with_issues.empty?
|
780
|
+
|
781
|
+
cmd = []
|
782
|
+
cmd << "Re-run Command (verbose-fails-stack):"
|
783
|
+
|
784
|
+
# Build command parts
|
785
|
+
base_cmd = @options[:original_command]&.first || "try"
|
786
|
+
file_paths = files_with_issues.map { |f| f[:path] }.join(" ")
|
787
|
+
|
788
|
+
cmd << " #{base_cmd} -vfs #{file_paths}"
|
789
|
+
cmd.join("\n")
|
790
|
+
end
|
791
|
+
|
792
|
+
# Check if there are any failures or errors
|
793
|
+
def has_failures?
|
794
|
+
@collected_files.any? { |f| f[:failures].any? || f[:errors].any? }
|
795
|
+
end
|
752
796
|
end
|
753
797
|
end
|
754
798
|
end
|
@@ -156,10 +156,10 @@ class Tryouts
|
|
156
156
|
|
157
157
|
# Show failure details for failed tests
|
158
158
|
if result_packet.failed? || result_packet.error?
|
159
|
-
show_failure_details(test_case, result_packet.actual_results, result_packet.expected_results)
|
159
|
+
show_failure_details(test_case, result_packet.actual_results, result_packet.expected_results, result_packet.result_value)
|
160
160
|
# Show exception details for passed exception expectations
|
161
161
|
elsif result_packet.passed? && has_exception_expectations?(test_case)
|
162
|
-
show_exception_details(test_case, result_packet.actual_results, result_packet.expected_results)
|
162
|
+
show_exception_details(test_case, result_packet.actual_results, result_packet.expected_results, result_packet.result_value)
|
163
163
|
end
|
164
164
|
end
|
165
165
|
|
@@ -273,7 +273,7 @@ class Tryouts
|
|
273
273
|
test_case.expectations.any? { |exp| exp.type == :exception }
|
274
274
|
end
|
275
275
|
|
276
|
-
def show_exception_details(test_case, actual_results, expected_results = [])
|
276
|
+
def show_exception_details(test_case, actual_results, expected_results = [], exception_object = nil)
|
277
277
|
return if actual_results.empty?
|
278
278
|
|
279
279
|
puts indent_text('Exception Details:', 2)
|
@@ -288,6 +288,19 @@ class Tryouts
|
|
288
288
|
puts indent_text("Result: #{Console.color(:green, expected.inspect)}", 3) if expected
|
289
289
|
end
|
290
290
|
end
|
291
|
+
|
292
|
+
# Show backtrace when --stack flag is used
|
293
|
+
if @show_stack_traces && exception_object.is_a?(Exception) && exception_object.backtrace
|
294
|
+
puts
|
295
|
+
puts indent_text('Backtrace:', 2)
|
296
|
+
Console.pretty_backtrace(exception_object.backtrace, limit: 15).each do |line|
|
297
|
+
puts indent_text(Console.color(:dim, line), 3)
|
298
|
+
end
|
299
|
+
if exception_object.backtrace.length > 15
|
300
|
+
puts indent_text(Console.color(:dim, "... (#{exception_object.backtrace.length - 15} more lines)"), 3)
|
301
|
+
end
|
302
|
+
end
|
303
|
+
|
291
304
|
puts
|
292
305
|
end
|
293
306
|
|
@@ -309,7 +322,7 @@ class Tryouts
|
|
309
322
|
puts
|
310
323
|
end
|
311
324
|
|
312
|
-
def show_failure_details(test_case, actual_results, expected_results = [])
|
325
|
+
def show_failure_details(test_case, actual_results, expected_results = [], exception_object = nil)
|
313
326
|
return if actual_results.empty?
|
314
327
|
|
315
328
|
actual_results.each_with_index do |actual, idx|
|
@@ -325,8 +338,19 @@ class Tryouts
|
|
325
338
|
puts indent_text("Expected: #{Console.color(:green, expected_line.content)}", 2)
|
326
339
|
puts indent_text("Actual: #{Console.color(:red, actual.inspect)}", 2)
|
327
340
|
else
|
328
|
-
# For error cases (empty expected_results),
|
329
|
-
|
341
|
+
# For error cases (empty expected_results), show formatted error with stack trace
|
342
|
+
if actual.is_a?(String) && actual.include?("\n")
|
343
|
+
# Multi-line error (with stack trace) - format properly
|
344
|
+
lines = actual.split("\n")
|
345
|
+
puts indent_text("Error: #{Console.color(:red, lines.first)}", 2)
|
346
|
+
# Show remaining lines (stack trace) with proper indentation
|
347
|
+
lines[1..].each do |line|
|
348
|
+
puts indent_text(" #{Console.color(:red, line)}", 2)
|
349
|
+
end
|
350
|
+
else
|
351
|
+
# Single-line error
|
352
|
+
puts indent_text("Error: #{Console.color(:red, actual.inspect)}", 2)
|
353
|
+
end
|
330
354
|
end
|
331
355
|
|
332
356
|
# Show difference if both are strings
|
@@ -336,6 +360,18 @@ class Tryouts
|
|
336
360
|
|
337
361
|
puts
|
338
362
|
end
|
363
|
+
|
364
|
+
# Show backtrace for exception expectation failures when --stack flag is used
|
365
|
+
if @show_stack_traces && exception_object.is_a?(Exception) && exception_object.backtrace
|
366
|
+
puts indent_text('Backtrace:', 2)
|
367
|
+
Console.pretty_backtrace(exception_object.backtrace, limit: 15).each do |line|
|
368
|
+
puts indent_text(Console.color(:dim, line), 3)
|
369
|
+
end
|
370
|
+
if exception_object.backtrace.length > 15
|
371
|
+
puts indent_text(Console.color(:dim, "... (#{exception_object.backtrace.length - 15} more lines)"), 3)
|
372
|
+
end
|
373
|
+
puts
|
374
|
+
end
|
339
375
|
end
|
340
376
|
|
341
377
|
def show_string_diff(expected, actual)
|
@@ -384,9 +420,10 @@ class Tryouts
|
|
384
420
|
# No output in fails mode
|
385
421
|
end
|
386
422
|
|
387
|
-
#
|
423
|
+
# Show file result summaries in fails-only mode (matching compact behavior)
|
388
424
|
def file_result(_file_path, total_tests:, failed_count:, error_count:, elapsed_time: nil)
|
389
|
-
#
|
425
|
+
# Always show summary - this matches compact formatter behavior
|
426
|
+
super
|
390
427
|
end
|
391
428
|
|
392
429
|
def live_status_capabilities
|
data/lib/tryouts/cli/opts.rb
CHANGED
@@ -24,6 +24,9 @@ class Tryouts
|
|
24
24
|
--agent-focus first-failure # Show first failure per file
|
25
25
|
--agent-focus critical # Show errors/exceptions only
|
26
26
|
--agent-limit 1000 # Limit output to 1000 tokens
|
27
|
+
--agent-tips # Include framework tips for LLMs
|
28
|
+
--agent-command # Include copy-paste re-run command
|
29
|
+
--agent-no-failures # Suppress failure details (summary only)
|
27
30
|
|
28
31
|
File Naming & Organization:
|
29
32
|
Files must end with '_try.rb' or '.try.rb' (e.g., auth_service_try.rb, user_model.try.rb)
|
@@ -142,6 +145,18 @@ class Tryouts
|
|
142
145
|
options[:agent] = true
|
143
146
|
options[:agent_focus] = focus.to_sym
|
144
147
|
end
|
148
|
+
opts.on('--agent-tips', 'Include tryouts framework tips and reminders in agent output') do
|
149
|
+
options[:agent] = true
|
150
|
+
options[:agent_tips] = true
|
151
|
+
end
|
152
|
+
opts.on('--agent-command', 'Include copy-paste command for re-running failures with -vfs') do
|
153
|
+
options[:agent] = true
|
154
|
+
options[:agent_command] = true
|
155
|
+
end
|
156
|
+
opts.on('--agent-no-failures', 'Suppress detailed failure list (show summary/command only)') do
|
157
|
+
options[:agent] = true
|
158
|
+
options[:agent_no_failures] = true
|
159
|
+
end
|
145
160
|
|
146
161
|
opts.separator "\nParser Options:"
|
147
162
|
opts.on('--enhanced-parser', 'Use enhanced parser with inhouse comment extraction (default)') { options[:parser] = :enhanced }
|
@@ -44,20 +44,37 @@ class Tryouts
|
|
44
44
|
end
|
45
45
|
|
46
46
|
def evaluate_exception_condition(caught_error)
|
47
|
-
|
47
|
+
# Note: error variable is already available in context (set in evaluate_expectations)
|
48
48
|
|
49
49
|
expectation_result = ExpectationResult.from_result(caught_error)
|
50
|
-
expected_value
|
50
|
+
expected_value = eval_expectation_content(@expectation.content, expectation_result)
|
51
51
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
52
|
+
# Support two syntaxes:
|
53
|
+
# 1. Class constant: #=!> StandardError (checks exception class)
|
54
|
+
# 2. Boolean expression: #=!> error.message == "test" (checks truthy value)
|
55
|
+
if expected_value.is_a?(Class)
|
56
|
+
# Class-based exception checking (new behavior)
|
57
|
+
# Check if caught exception is instance of expected class (or subclass)
|
58
|
+
exception_matches = caught_error.is_a?(expected_value)
|
59
|
+
|
60
|
+
build_result(
|
61
|
+
passed: exception_matches,
|
62
|
+
actual: caught_error.class.name,
|
63
|
+
expected: expected_value.name,
|
64
|
+
)
|
65
|
+
else
|
66
|
+
# Boolean/truthy expression checking (legacy behavior)
|
67
|
+
# This supports expressions like: error.message == "test"
|
68
|
+
build_result(
|
69
|
+
passed: !!expected_value,
|
70
|
+
actual: caught_error.message,
|
71
|
+
expected: expected_value,
|
72
|
+
)
|
73
|
+
end
|
57
74
|
rescue StandardError => ex
|
58
75
|
build_result(
|
59
76
|
passed: false,
|
60
|
-
actual: caught_error.
|
77
|
+
actual: caught_error.class.name,
|
61
78
|
expected: "EXPECTED: #{ex.message}",
|
62
79
|
error: ex,
|
63
80
|
)
|
@@ -42,9 +42,8 @@ class Tryouts
|
|
42
42
|
|
43
43
|
# Auto-detect exceptions and use message for regex matching
|
44
44
|
# This allows #=~> /pattern/ to work naturally with exception messages
|
45
|
+
# Note: error variable is already available in context (set in evaluate_expectations)
|
45
46
|
string_result = if actual_result.is_a?(Exception)
|
46
|
-
# Make error available in context for manual access if needed
|
47
|
-
@context.define_singleton_method(:error) { actual_result }
|
48
47
|
actual_result.message # Match against error message
|
49
48
|
else
|
50
49
|
actual_result.to_s # Normal case: convert to string
|
@@ -19,13 +19,10 @@ class Tryouts
|
|
19
19
|
end
|
20
20
|
|
21
21
|
def process
|
22
|
-
testrun
|
23
|
-
|
24
|
-
# Apply line spec filtering before reporting test counts
|
25
|
-
if @options[:line_spec]
|
26
|
-
testrun = filter_testrun_by_line_spec(testrun)
|
27
|
-
end
|
22
|
+
testrun = create_parser(@file, @options).parse
|
28
23
|
|
24
|
+
# Report total test count (before filtering for display)
|
25
|
+
# Line spec filtering affects output only, not execution
|
29
26
|
@global_tally[:aggregator].increment_total_files
|
30
27
|
@output_manager.file_parsed(@file, testrun.total_tests)
|
31
28
|
@output_manager.parser_warnings(@file, warnings: testrun.warnings)
|
@@ -45,34 +42,6 @@ class Tryouts
|
|
45
42
|
|
46
43
|
private
|
47
44
|
|
48
|
-
def filter_testrun_by_line_spec(testrun)
|
49
|
-
require_relative 'cli/line_spec_parser'
|
50
|
-
|
51
|
-
line_spec = @options[:line_spec]
|
52
|
-
|
53
|
-
# Filter test cases to only those that match the line spec
|
54
|
-
filtered_cases = testrun.test_cases.select do |test_case|
|
55
|
-
Tryouts::CLI::LineSpecParser.matches?(test_case, line_spec)
|
56
|
-
end
|
57
|
-
|
58
|
-
# Check if any tests matched the line specification
|
59
|
-
if filtered_cases.empty?
|
60
|
-
@output_manager.file_failure(@file, "No test cases found matching line specification: #{line_spec}")
|
61
|
-
return testrun # Return original testrun to avoid breaking the pipeline
|
62
|
-
end
|
63
|
-
|
64
|
-
# Create a new testrun with filtered cases
|
65
|
-
# We need to preserve the setup and teardown but only include matching tests
|
66
|
-
testrun.class.new(
|
67
|
-
setup: testrun.setup,
|
68
|
-
test_cases: filtered_cases,
|
69
|
-
teardown: testrun.teardown,
|
70
|
-
source_file: testrun.source_file,
|
71
|
-
metadata: testrun.metadata,
|
72
|
-
warnings: testrun.warnings
|
73
|
-
)
|
74
|
-
end
|
75
|
-
|
76
45
|
def create_parser(file, options)
|
77
46
|
parser_type = options[:parser] || :enhanced # enhanced parser is now the default
|
78
47
|
|
data/lib/tryouts/test_batch.rb
CHANGED
@@ -37,6 +37,7 @@ class Tryouts
|
|
37
37
|
@start_time = nil
|
38
38
|
@test_case_count = 0
|
39
39
|
@setup_failed = false
|
40
|
+
@line_spec = options[:line_spec] # For output filtering only
|
40
41
|
|
41
42
|
# Setup container for fresh context mode - preserves @instance_variables from setup
|
42
43
|
@setup_container = nil
|
@@ -101,16 +102,26 @@ class Tryouts
|
|
101
102
|
break
|
102
103
|
end
|
103
104
|
|
104
|
-
|
105
|
+
# Apply line_spec filter for output notifications (test_start/test_end)
|
106
|
+
# but still execute ALL tests regardless of filter
|
107
|
+
if should_display_test_result?(test_case)
|
108
|
+
@output_manager&.test_start(test_case, idx, @test_case_count)
|
109
|
+
end
|
110
|
+
|
105
111
|
result = execute_single_test(test_case, before_test_hook, &) # runs the test code
|
106
|
-
|
112
|
+
|
113
|
+
if should_display_test_result?(test_case)
|
114
|
+
@output_manager&.test_end(test_case, idx, @test_case_count)
|
115
|
+
end
|
107
116
|
|
108
117
|
# Update circuit breaker state based on result
|
109
118
|
update_circuit_breaker(result)
|
110
119
|
|
111
120
|
result
|
112
121
|
rescue StandardError => ex
|
113
|
-
|
122
|
+
if should_display_test_result?(test_case)
|
123
|
+
@output_manager&.test_end(test_case, idx, @test_case_count)
|
124
|
+
end
|
114
125
|
# Create error result packet to maintain consistent data flow
|
115
126
|
error_result = build_error_result(test_case, ex)
|
116
127
|
process_test_result(error_result)
|
@@ -333,6 +344,12 @@ class Tryouts
|
|
333
344
|
def evaluate_expectations(test_case, actual_result, context, execution_time_ns = nil, stdout_content = nil, stderr_content = nil, caught_exception = nil)
|
334
345
|
return { passed: true, actual_results: [], expected_results: [] } if test_case.expectations.empty?
|
335
346
|
|
347
|
+
# Make error variable available to ALL expectations in exception test cases
|
348
|
+
# This allows #=/=> error.message.nil?, #=~> /pattern/ (auto-targets error.message), etc.
|
349
|
+
if caught_exception
|
350
|
+
context.define_singleton_method(:error) { caught_exception }
|
351
|
+
end
|
352
|
+
|
336
353
|
evaluation_results = test_case.expectations.map do |expectation|
|
337
354
|
evaluator = ExpectationEvaluators::Registry.evaluator_for(expectation, test_case, context)
|
338
355
|
|
@@ -398,11 +415,14 @@ class Tryouts
|
|
398
415
|
@failed_count += 1
|
399
416
|
end
|
400
417
|
|
401
|
-
|
418
|
+
# Apply line_spec filter for output only - test was still executed
|
419
|
+
if should_display_test_result?(result.test_case)
|
420
|
+
show_test_result(result)
|
402
421
|
|
403
|
-
|
404
|
-
|
405
|
-
|
422
|
+
# Show captured output if any exists
|
423
|
+
if result.has_output?
|
424
|
+
@output_manager&.test_output(result.test_case, result.captured_output, result)
|
425
|
+
end
|
406
426
|
end
|
407
427
|
end
|
408
428
|
|
@@ -442,6 +462,17 @@ class Tryouts
|
|
442
462
|
|
443
463
|
# For catastrophic errors, still raise to stop execution
|
444
464
|
raise "Global setup failed (#{ex.class}): #{ex.message}"
|
465
|
+
rescue SystemExit, SignalException => ex
|
466
|
+
# Handle process control exceptions (exit, abort, signals) gracefully
|
467
|
+
@setup_failed = true
|
468
|
+
if @global_tally && @global_tally[:aggregator]
|
469
|
+
@global_tally[:aggregator].add_infrastructure_failure(
|
470
|
+
:setup, @testrun.source_file, "Setup terminated by #{ex.class}: #{ex.message}", ex
|
471
|
+
)
|
472
|
+
end
|
473
|
+
|
474
|
+
Tryouts.debug "Setup received #{ex.class}: #{ex.message}"
|
475
|
+
@output_manager&.error("Setup terminated by #{ex.class}: #{ex.message}")
|
445
476
|
end
|
446
477
|
|
447
478
|
# Setup execution for fresh context mode - creates @setup_container with @instance_variables
|
@@ -483,6 +514,17 @@ class Tryouts
|
|
483
514
|
|
484
515
|
# For catastrophic errors, still raise to stop execution
|
485
516
|
raise "Fresh context setup failed (#{ex.class}): #{ex.message}"
|
517
|
+
rescue SystemExit, SignalException => ex
|
518
|
+
# Handle process control exceptions (exit, abort, signals) gracefully
|
519
|
+
@setup_failed = true
|
520
|
+
if @global_tally && @global_tally[:aggregator]
|
521
|
+
@global_tally[:aggregator].add_infrastructure_failure(
|
522
|
+
:setup, @testrun.source_file, "Setup terminated by #{ex.class}: #{ex.message}", ex
|
523
|
+
)
|
524
|
+
end
|
525
|
+
|
526
|
+
Tryouts.debug "Setup received #{ex.class}: #{ex.message}"
|
527
|
+
@output_manager&.error("Setup terminated by #{ex.class}: #{ex.message}")
|
486
528
|
end
|
487
529
|
|
488
530
|
# Global teardown execution
|
@@ -521,6 +563,17 @@ class Tryouts
|
|
521
563
|
else
|
522
564
|
@output_manager&.error('Continuing despite teardown failure')
|
523
565
|
end
|
566
|
+
rescue SystemExit, SignalException => ex
|
567
|
+
# Handle process control exceptions (exit, abort, signals) gracefully
|
568
|
+
if @global_tally && @global_tally[:aggregator]
|
569
|
+
@global_tally[:aggregator].add_infrastructure_failure(
|
570
|
+
:teardown, @testrun.source_file, "Teardown terminated by #{ex.class}: #{ex.message}", ex
|
571
|
+
)
|
572
|
+
end
|
573
|
+
|
574
|
+
Tryouts.debug "Teardown received #{ex.class}: #{ex.message}"
|
575
|
+
@output_manager&.error("Teardown terminated by #{ex.class}: #{ex.message}")
|
576
|
+
@output_manager&.error('Continuing despite teardown termination')
|
524
577
|
end
|
525
578
|
|
526
579
|
# Result finalization and summary display
|
@@ -550,6 +603,15 @@ class Tryouts
|
|
550
603
|
test_case.expectations.any? { |exp| exp.result_type? || exp.regex_match? }
|
551
604
|
end
|
552
605
|
|
606
|
+
# Check if test result should be displayed based on line_spec filter
|
607
|
+
# All tests are EXECUTED regardless of line_spec, but only matching tests are DISPLAYED
|
608
|
+
def should_display_test_result?(test_case)
|
609
|
+
return true unless @line_spec
|
610
|
+
|
611
|
+
require_relative 'cli/line_spec_parser'
|
612
|
+
Tryouts::CLI::LineSpecParser.matches?(test_case, @line_spec)
|
613
|
+
end
|
614
|
+
|
553
615
|
def capture_output
|
554
616
|
old_stdout = $stdout
|
555
617
|
old_stderr = $stderr
|
@@ -34,6 +34,7 @@ class Tryouts
|
|
34
34
|
shared_context: @options[:shared_context],
|
35
35
|
verbose: @options[:verbose],
|
36
36
|
fails_only: @options[:fails_only],
|
37
|
+
line_spec: @options[:line_spec], # Pass line_spec for output filtering
|
37
38
|
output_manager: @output_manager,
|
38
39
|
global_tally: @global_tally,
|
39
40
|
)
|
data/lib/tryouts/version.rb
CHANGED
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.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Delano Mandelbaum
|
@@ -9,6 +9,26 @@ bindir: exe
|
|
9
9
|
cert_chain: []
|
10
10
|
date: 1980-01-02 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
|
+
- !ruby/object:Gem::Dependency
|
13
|
+
name: concurrent-ruby
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
15
|
+
requirements:
|
16
|
+
- - "~>"
|
17
|
+
- !ruby/object:Gem::Version
|
18
|
+
version: '1.0'
|
19
|
+
- - "<"
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '2'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
requirements:
|
26
|
+
- - "~>"
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
version: '1.0'
|
29
|
+
- - "<"
|
30
|
+
- !ruby/object:Gem::Version
|
31
|
+
version: '2'
|
12
32
|
- !ruby/object:Gem::Dependency
|
13
33
|
name: irb
|
14
34
|
requirement: !ruby/object:Gem::Requirement
|
@@ -38,33 +58,33 @@ dependencies:
|
|
38
58
|
- !ruby/object:Gem::Version
|
39
59
|
version: '1.0'
|
40
60
|
- !ruby/object:Gem::Dependency
|
41
|
-
name:
|
61
|
+
name: minitest
|
42
62
|
requirement: !ruby/object:Gem::Requirement
|
43
63
|
requirements:
|
44
64
|
- - "~>"
|
45
65
|
- !ruby/object:Gem::Version
|
46
|
-
version: '
|
66
|
+
version: '5.0'
|
47
67
|
type: :runtime
|
48
68
|
prerelease: false
|
49
69
|
version_requirements: !ruby/object:Gem::Requirement
|
50
70
|
requirements:
|
51
71
|
- - "~>"
|
52
72
|
- !ruby/object:Gem::Version
|
53
|
-
version: '
|
73
|
+
version: '5.0'
|
54
74
|
- !ruby/object:Gem::Dependency
|
55
|
-
name:
|
75
|
+
name: pastel
|
56
76
|
requirement: !ruby/object:Gem::Requirement
|
57
77
|
requirements:
|
58
78
|
- - "~>"
|
59
79
|
- !ruby/object:Gem::Version
|
60
|
-
version: '
|
80
|
+
version: '0.8'
|
61
81
|
type: :runtime
|
62
82
|
prerelease: false
|
63
83
|
version_requirements: !ruby/object:Gem::Requirement
|
64
84
|
requirements:
|
65
85
|
- - "~>"
|
66
86
|
- !ruby/object:Gem::Version
|
67
|
-
version: '
|
87
|
+
version: '0.8'
|
68
88
|
- !ruby/object:Gem::Dependency
|
69
89
|
name: rspec
|
70
90
|
requirement: !ruby/object:Gem::Requirement
|
@@ -85,20 +105,6 @@ dependencies:
|
|
85
105
|
- - "<"
|
86
106
|
- !ruby/object:Gem::Version
|
87
107
|
version: '5.0'
|
88
|
-
- !ruby/object:Gem::Dependency
|
89
|
-
name: pastel
|
90
|
-
requirement: !ruby/object:Gem::Requirement
|
91
|
-
requirements:
|
92
|
-
- - "~>"
|
93
|
-
- !ruby/object:Gem::Version
|
94
|
-
version: '0.8'
|
95
|
-
type: :runtime
|
96
|
-
prerelease: false
|
97
|
-
version_requirements: !ruby/object:Gem::Requirement
|
98
|
-
requirements:
|
99
|
-
- - "~>"
|
100
|
-
- !ruby/object:Gem::Version
|
101
|
-
version: '0.8'
|
102
108
|
- !ruby/object:Gem::Dependency
|
103
109
|
name: tty-cursor
|
104
110
|
requirement: !ruby/object:Gem::Requirement
|