lib/tryouts/test_runner.rb
# lib/tryouts/test_runner.rb require_relative 'prism_parser' require_relative 'test_batch' require_relative 'translators/rspec_translator' require_relative 'translators/minitest_translator' require_relative 'file_processor' class Tryouts class TestRunner FRAMEWORKS = { rspec: Translators::RSpecTranslator, minitest: Translators::MinitestTranslator, }.freeze FRAMEWORK_DEFAULTS = { direct: { shared_context: true, generate_only: false }, rspec: { shared_context: false, generate_only: false }, minitest: { shared_context: false, generate_only: false }, }.freeze def initialize(files:, options:, output_manager:) @files = files @options = apply_framework_defaults(options) @output_manager = output_manager @translator = initialize_translator @global_tally = initialize_global_tally end def run log_run_info validate_framework result = process_files show_grand_total if @global_tally[:file_count] > 1 result end private def log_run_info @output_manager.processing_phase(@files.size) @output_manager.info "Framework: #{@options[:framework]}", 1 @output_manager.info "Context: #{@options[:shared_context] ? 'shared' : 'fresh'}", 1 @files.each_with_index do |file, idx| @output_manager.info "#{idx + 1}/#{@files.size}: #{Console.pretty_path(file)}", 1 end end def apply_framework_defaults(options) framework_defaults = FRAMEWORK_DEFAULTS[options[:framework]] || {} framework_defaults.merge(options) end def validate_framework unless @options[:framework] == :direct || FRAMEWORKS.key?(@options[:framework]) raise ArgumentError, "Unknown framework: #{@options[:framework]}. Available: #{FRAMEWORKS.keys.join(', ')}, direct" end end def initialize_translator return nil if @options[:framework] == :direct FRAMEWORKS[@options[:framework]].new end def initialize_global_tally { total_tests: 0, total_failed: 0, total_errors: 0, file_count: 0, start_time: Time.now, successful_files: 0, } end def process_files failure_count = 0 @files.each_with_index do |file, _idx| result = process_file(file) failure_count += result unless result.zero? status = result.zero? ? Console.color(:green, 'PASS') : Console.color(:red, 'FAIL') @output_manager.info "#{status} #{Console.pretty_path(file)} (#{result} failures)", 1 end failure_count end def process_file(file) file = FileProcessor.new( file: file, options: @options, output_manager: @output_manager, translator: @translator, global_tally: @global_tally, ) file.process rescue StandardError => ex handle_file_error(ex) @global_tally[:total_errors] += 1 1 end def show_grand_total elapsed_time = Time.now - @global_tally[:start_time] @output_manager.grand_total( @global_tally[:total_tests], @global_tally[:total_failed], @global_tally[:total_errors], @global_tally[:successful_files], @global_tally[:file_count], elapsed_time, ) end def handle_file_error(exception) @status = :error Tryouts.debug "TestRunner#process_file: An error occurred processing #{file}: #{ex.message}" error_message = "Batch execution failed: #{exception.message}" backtrace = exception.respond_to?(:backtrace) ? exception.backtrace : nil @output_manager&.error(error_message, backtrace) end end end