tryouts 3.7.1 → 4.0.0.pre1
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 +3 -2
- data/lib/tryouts/cli/formatters/agent.rb +2 -0
- data/lib/tryouts/cli/formatters/base.rb +2 -0
- data/lib/tryouts/cli/formatters/compact.rb +2 -0
- data/lib/tryouts/cli/formatters/factory.rb +2 -0
- data/lib/tryouts/cli/formatters/live_status_manager.rb +2 -0
- data/lib/tryouts/cli/formatters/output_manager.rb +2 -0
- data/lib/tryouts/cli/formatters/quiet.rb +2 -0
- data/lib/tryouts/cli/formatters/test_run_state.rb +2 -0
- data/lib/tryouts/cli/formatters/token_budget.rb +2 -0
- data/lib/tryouts/cli/formatters/tty_status_display.rb +2 -0
- data/lib/tryouts/cli/formatters/verbose.rb +2 -0
- data/lib/tryouts/cli/formatters.rb +2 -0
- data/lib/tryouts/cli/line_spec_parser.rb +2 -0
- data/lib/tryouts/cli/modes/generate.rb +2 -0
- data/lib/tryouts/cli/modes/inspect.rb +15 -3
- data/lib/tryouts/cli/opts.rb +2 -1
- data/lib/tryouts/cli/tty_detector.rb +2 -0
- data/lib/tryouts/cli.rb +2 -0
- data/lib/tryouts/console.rb +2 -0
- data/lib/tryouts/expectation_evaluators/base.rb +2 -0
- data/lib/tryouts/expectation_evaluators/boolean.rb +2 -0
- data/lib/tryouts/expectation_evaluators/exception.rb +2 -0
- data/lib/tryouts/expectation_evaluators/expectation_result.rb +2 -0
- data/lib/tryouts/expectation_evaluators/false.rb +2 -0
- data/lib/tryouts/expectation_evaluators/intentional_failure.rb +2 -0
- data/lib/tryouts/expectation_evaluators/non_nil.rb +2 -0
- data/lib/tryouts/expectation_evaluators/output.rb +2 -0
- data/lib/tryouts/expectation_evaluators/performance_time.rb +2 -0
- data/lib/tryouts/expectation_evaluators/regex_match.rb +2 -0
- data/lib/tryouts/expectation_evaluators/registry.rb +2 -0
- data/lib/tryouts/expectation_evaluators/regular.rb +2 -0
- data/lib/tryouts/expectation_evaluators/result_type.rb +2 -0
- data/lib/tryouts/expectation_evaluators/true.rb +2 -0
- data/lib/tryouts/failure_collector.rb +2 -0
- data/lib/tryouts/file_processor.rb +3 -4
- data/lib/tryouts/parser_warning.rb +2 -0
- data/lib/tryouts/parsers/base_parser.rb +3 -3
- data/lib/tryouts/parsers/enhanced_parser.rb +57 -3
- data/lib/tryouts/parsers/shared_methods.rb +67 -17
- data/lib/tryouts/test_batch.rb +155 -43
- data/lib/tryouts/test_case.rb +54 -6
- data/lib/tryouts/test_executor.rb +2 -0
- data/lib/tryouts/test_result_aggregator.rb +2 -0
- data/lib/tryouts/test_runner.rb +2 -1
- data/lib/tryouts/translators/minitest_translator.rb +36 -12
- data/lib/tryouts/translators/rspec_translator.rb +19 -0
- data/lib/tryouts/version.rb +3 -1
- data/lib/tryouts.rb +2 -1
- metadata +1 -2
- data/lib/tryouts/parsers/legacy_parser.rb +0 -254
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b43c7d018d4bb7bd546bb516a584903d7922d87a616dbec87d2e4b6d00e06652
|
|
4
|
+
data.tar.gz: d4068b53bbd8428f88bf1a4d4af62aa7db0ff4ecccfecca0c0b268f1d8fb0381
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c11a368db40e8335a3d9eae238581ad6e0b4ea8b56862bb82f10adbb9746626762db1a89247be6e8eb2473aff90506f151a5b18452ab625d2a6e2f2753968f2d
|
|
7
|
+
data.tar.gz: 7ebec1c8923c423cd3ec2117d78e3a4a95fec48139bb1af2ecee48ebdfea7d02d1ceccfa8608fb5914cb21c762dfafc04a1f81835fc18723c3fbe79d495540db
|
data/README.md
CHANGED
|
@@ -42,7 +42,7 @@ try try/core/basic_syntax_try.rb
|
|
|
42
42
|
|
|
43
43
|
## Writing Tests
|
|
44
44
|
|
|
45
|
-
Tryouts use a comment-based syntax for
|
|
45
|
+
Tryouts use a comment-based syntax for expectations:
|
|
46
46
|
|
|
47
47
|
```ruby
|
|
48
48
|
# Setup code runs before all tests
|
|
@@ -98,9 +98,10 @@ Each test file is made up of three sections:
|
|
|
98
98
|
| `#=:>` | Must match result type | `#=:> String` | result, _ |
|
|
99
99
|
| `#=~>` | Must match regex pattern | `#=~> /^[^@]+@[^@]+\.[^@]+$/` | result, _ |
|
|
100
100
|
| `#=%>` | Must complete within time | `#=%> 2000 # in milliseconds` | result, _ |
|
|
101
|
+
| `#=*>` | Must be non-nil | `#=*> result` | result, _ |
|
|
101
102
|
| `#=1>` | Match content in STDOUT | `#=1> "You have great success"` | result, _ |
|
|
102
103
|
| `#=2>` | Match content in STDERR | `#=2> /[a-zA-Z0-9]+-?[0-9]{1,5}` | result, _ |
|
|
103
|
-
| `#=<>` | Fails on purpose |
|
|
104
|
+
| `#=<>` | Fails on purpose | `#=<> result.include?(4)` | result, _ |
|
|
104
105
|
|
|
105
106
|
|
|
106
107
|
### Using other test runners
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
# lib/tryouts/cli/modes/inspect.rb
|
|
2
|
+
#
|
|
3
|
+
# frozen_string_literal: true
|
|
2
4
|
|
|
3
5
|
class Tryouts
|
|
4
6
|
class CLI
|
|
@@ -13,14 +15,24 @@ class Tryouts
|
|
|
13
15
|
|
|
14
16
|
def handle
|
|
15
17
|
@output_manager.raw("Inspecting: #{@file}")
|
|
16
|
-
@output_manager.
|
|
18
|
+
@output_manager.raw('=' * 60)
|
|
17
19
|
@output_manager.raw("Found #{@testrun.total_tests} test cases")
|
|
18
20
|
@output_manager.raw("Setup code: #{@testrun.setup.empty? ? 'None' : 'Present'}")
|
|
19
21
|
@output_manager.raw("Teardown code: #{@testrun.teardown.empty? ? 'None' : 'Present'}")
|
|
20
22
|
@output_manager.raw('')
|
|
21
23
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
+
test_number = 0
|
|
25
|
+
@testrun.test_cases.each do |tc|
|
|
26
|
+
if tc.is_a?(Tryouts::OrphanBlock)
|
|
27
|
+
@output_manager.raw("Orphan code block (no description, no expectations)")
|
|
28
|
+
@output_manager.raw(" Code lines: #{tc.code.lines.count}")
|
|
29
|
+
@output_manager.raw(" Range: #{tc.line_range}")
|
|
30
|
+
@output_manager.raw('')
|
|
31
|
+
next
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
test_number += 1
|
|
35
|
+
@output_manager.raw("Test #{test_number}: #{tc.description}")
|
|
24
36
|
@output_manager.raw(" Code lines: #{tc.code.lines.count}")
|
|
25
37
|
@output_manager.raw(" Expectations: #{tc.expectations.size}")
|
|
26
38
|
@output_manager.raw(" Range: #{tc.line_range}")
|
data/lib/tryouts/cli/opts.rb
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
# lib/tryouts/cli/opts.rb
|
|
2
|
+
#
|
|
3
|
+
# frozen_string_literal: true
|
|
2
4
|
|
|
3
5
|
class Tryouts
|
|
4
6
|
class CLI
|
|
@@ -161,7 +163,6 @@ class Tryouts
|
|
|
161
163
|
|
|
162
164
|
opts.separator "\nParser Options:"
|
|
163
165
|
opts.on('--enhanced-parser', 'Use enhanced parser with inhouse comment extraction (default)') { options[:parser] = :enhanced }
|
|
164
|
-
opts.on('--legacy-parser', 'Use legacy prism parser') { options[:parser] = :prism }
|
|
165
166
|
|
|
166
167
|
opts.separator "\nInspection Options:"
|
|
167
168
|
opts.on('-i', '--inspect', 'Inspect file structure without running tests') { options[:inspect] = true }
|
data/lib/tryouts/cli.rb
CHANGED
data/lib/tryouts/console.rb
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# lib/tryouts/file_processor.rb
|
|
2
|
+
#
|
|
3
|
+
# frozen_string_literal: true
|
|
2
4
|
|
|
3
|
-
require_relative 'parsers/legacy_parser'
|
|
4
5
|
require_relative 'parsers/enhanced_parser'
|
|
5
6
|
require_relative 'test_executor'
|
|
6
7
|
require_relative 'cli/modes/inspect'
|
|
@@ -9,7 +10,7 @@ require_relative 'cli/modes/generate'
|
|
|
9
10
|
class Tryouts
|
|
10
11
|
class FileProcessor
|
|
11
12
|
# Supported parser types for validation and documentation
|
|
12
|
-
PARSER_TYPES = [:enhanced
|
|
13
|
+
PARSER_TYPES = [:enhanced].freeze
|
|
13
14
|
def initialize(file:, options:, output_manager:, translator:, global_tally:)
|
|
14
15
|
@file = file
|
|
15
16
|
@options = options
|
|
@@ -52,8 +53,6 @@ class Tryouts
|
|
|
52
53
|
case parser_type
|
|
53
54
|
when :enhanced
|
|
54
55
|
EnhancedParser.new(file, options)
|
|
55
|
-
when :prism
|
|
56
|
-
LegacyParser.new(file, options)
|
|
57
56
|
end
|
|
58
57
|
end
|
|
59
58
|
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
# lib/tryouts/parsers/base_parser.rb
|
|
2
|
+
#
|
|
3
|
+
# frozen_string_literal: true
|
|
2
4
|
|
|
3
5
|
require 'prism'
|
|
4
6
|
|
|
@@ -11,8 +13,7 @@ class Tryouts
|
|
|
11
13
|
#
|
|
12
14
|
# BaseParser establishes the foundation for parsing tryout files by handling
|
|
13
15
|
# file loading, Prism integration, and providing shared parsing infrastructure.
|
|
14
|
-
# All concrete parser implementations (EnhancedParser
|
|
15
|
-
# from this class.
|
|
16
|
+
# All concrete parser implementations (EnhancedParser) inherit from this class.
|
|
16
17
|
#
|
|
17
18
|
# @abstract Subclass and implement {#parse} to create a concrete parser
|
|
18
19
|
# @example Implementing a custom parser
|
|
@@ -75,7 +76,6 @@ class Tryouts
|
|
|
75
76
|
# 4. Provide a unique `parser_type` identifier
|
|
76
77
|
#
|
|
77
78
|
# @see EnhancedParser For Prism-based comment extraction
|
|
78
|
-
# @see LegacyParser For line-by-line parsing approach
|
|
79
79
|
# @see SharedMethods For common parsing utilities
|
|
80
80
|
# @since 3.0.0
|
|
81
81
|
class BaseParser
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
# lib/tryouts/parsers/enhanced_parser.rb
|
|
2
|
+
#
|
|
3
|
+
# frozen_string_literal: true
|
|
2
4
|
|
|
3
5
|
require_relative '../test_case'
|
|
4
6
|
require_relative 'base_parser'
|
|
@@ -31,7 +33,7 @@ class Tryouts
|
|
|
31
33
|
# @!attribute [r] parser_type
|
|
32
34
|
# @return [Symbol] Returns :enhanced to identify parser type
|
|
33
35
|
#
|
|
34
|
-
# ## Key Benefits
|
|
36
|
+
# ## Key Benefits
|
|
35
37
|
#
|
|
36
38
|
# ### 1. HEREDOC Safety
|
|
37
39
|
# - Uses Prism's `parse_comments()` to extract only actual Ruby comments
|
|
@@ -56,7 +58,6 @@ class Tryouts
|
|
|
56
58
|
# Uses Ruby 3.4+ pattern matching (`case/in`) for token classification,
|
|
57
59
|
# providing clean, modern syntax for expectation type detection.
|
|
58
60
|
#
|
|
59
|
-
# @see LegacyParser For simpler regex-based parsing (legacy compatibility)
|
|
60
61
|
# @see BaseParser For shared parsing functionality
|
|
61
62
|
# @since 3.2.0
|
|
62
63
|
class EnhancedParser < Tryouts::Parsers::BaseParser
|
|
@@ -72,6 +73,12 @@ class Tryouts
|
|
|
72
73
|
def parse
|
|
73
74
|
return handle_syntax_errors if @prism_result.failure?
|
|
74
75
|
|
|
76
|
+
# Line ranges of multi-line statements, computed once from the whole-file
|
|
77
|
+
# parse. A column-0 comment strictly inside one of these spans (e.g. a
|
|
78
|
+
# #=>-shaped line inside an unclosed array literal) cannot be a real
|
|
79
|
+
# block boundary or expectation, no matter what it looks like lexically.
|
|
80
|
+
@multiline_statement_ranges = multiline_statement_ranges
|
|
81
|
+
|
|
75
82
|
# Use inhouse comment extraction instead of line-by-line regex parsing
|
|
76
83
|
# This automatically excludes HEREDOC content!
|
|
77
84
|
tokens = tokenize_content_with_inhouse_extraction
|
|
@@ -104,22 +111,41 @@ class Tryouts
|
|
|
104
111
|
comments = Prism.parse_comments(@source)
|
|
105
112
|
comment_by_line = comments.group_by { |comment| comment.location.start_line }
|
|
106
113
|
|
|
114
|
+
# A =begin/=end block comment is one Prism comment node spanning multiple
|
|
115
|
+
# lines, but comment_by_line only keys its start line. Its interior lines
|
|
116
|
+
# must be skipped entirely: re-emitting them as :code duplicates them, and
|
|
117
|
+
# running the marker regex cascade against the raw multi-line slice lets an
|
|
118
|
+
# interior #=>-shaped line match via ^/$ line anchors.
|
|
119
|
+
multiline_comment_spans = comments.filter_map do |comment|
|
|
120
|
+
loc = comment.location
|
|
121
|
+
((loc.start_line + 1)..loc.end_line) if loc.end_line > loc.start_line
|
|
122
|
+
end
|
|
123
|
+
|
|
107
124
|
# Process each line, handling multiple comments per line
|
|
108
125
|
@lines.each_with_index do |line, index|
|
|
109
126
|
line_number = index + 1
|
|
110
127
|
|
|
128
|
+
next if multiline_comment_spans.any? { |span| span.cover?(line_number) }
|
|
129
|
+
|
|
111
130
|
if (comments_for_line = comment_by_line[line_number]) && !comments_for_line.empty?
|
|
112
131
|
emitted_code = false
|
|
113
132
|
comments_for_line.sort_by! { |c| c.location.start_column }
|
|
114
133
|
comments_for_line.each do |comment|
|
|
115
134
|
comment_content = comment.slice.strip
|
|
116
|
-
if comment.location.
|
|
135
|
+
if comment.location.end_line > comment.location.start_line
|
|
136
|
+
# Whole =begin/=end block: one plain comment token for the span
|
|
137
|
+
tokens << { type: :comment, content: comment_content, line: index }
|
|
138
|
+
elsif comment.location.start_column > 0
|
|
117
139
|
unless emitted_code
|
|
118
140
|
tokens << { type: :code, content: line, line: index, ast: parse_ruby_line(line) }
|
|
119
141
|
emitted_code = true
|
|
120
142
|
end
|
|
121
143
|
# Inline comment (after code) - treat as regular comment, not expectation
|
|
122
144
|
tokens << { type: :comment, content: comment_content.sub(/^#\s*/, ''), line: line_number - 1 }
|
|
145
|
+
elsif strictly_inside_multiline_statement?(index)
|
|
146
|
+
# Column-0 comment inside a multi-line statement (unclosed
|
|
147
|
+
# array/hash/call/...) - force plain :comment regardless of shape
|
|
148
|
+
tokens << { type: :comment, content: comment_content.sub(/^#\s*/, ''), line: line_number - 1 }
|
|
123
149
|
else
|
|
124
150
|
tokens << classify_comment_inhousely(comment_content, line_number)
|
|
125
151
|
end
|
|
@@ -140,6 +166,34 @@ class Tryouts
|
|
|
140
166
|
tokens
|
|
141
167
|
end
|
|
142
168
|
|
|
169
|
+
# Collect 0-based, inclusive line ranges for every AST node that spans more
|
|
170
|
+
# than one physical line, walking the existing whole-file @prism_result.
|
|
171
|
+
# Built once per parse; no second Prism.parse call.
|
|
172
|
+
def multiline_statement_ranges
|
|
173
|
+
ranges = []
|
|
174
|
+
root = @prism_result.value
|
|
175
|
+
return ranges if root.nil?
|
|
176
|
+
|
|
177
|
+
visit = lambda do |node|
|
|
178
|
+
next if node.nil?
|
|
179
|
+
|
|
180
|
+
loc = node.location
|
|
181
|
+
ranges << ((loc.start_line - 1)..(loc.end_line - 1)) if loc.start_line != loc.end_line
|
|
182
|
+
node.compact_child_nodes.each { |child| visit.call(child) }
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
root.statements.body.each { |stmt| visit.call(stmt) }
|
|
186
|
+
ranges
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
# True if the 0-based line index falls STRICTLY inside a multi-line
|
|
190
|
+
# statement span. A comment on the same line as the opening or closing
|
|
191
|
+
# delimiter is still eligible to be a real boundary; one on a line fully
|
|
192
|
+
# swallowed by the statement is not.
|
|
193
|
+
def strictly_inside_multiline_statement?(line_idx)
|
|
194
|
+
@multiline_statement_ranges.any? { |range| line_idx > range.first && line_idx < range.last }
|
|
195
|
+
end
|
|
196
|
+
|
|
143
197
|
# Classify comment content into specific token types using pattern matching
|
|
144
198
|
#
|
|
145
199
|
# Takes a raw comment string and determines what type of tryout token it
|