class Tryouts::CLI::TestRunState

Immutable state tracking for test runs using modern Ruby Data.define

def self.empty

def self.empty
  new(
    total_tests: 0,
    passed: 0,
    failed: 0,
    errors: 0,
    files_completed: 0,
    total_files: 0,
    current_file: nil,
    current_test: nil,
    start_time: nil,
  )
end

def self.initial(total_files: 0)

def self.initial(total_files: 0)
  empty.with(
    total_files: total_files,
    start_time: Time.now,
  )
end

def completion_percentage

def completion_percentage
  return 0 if total_files == 0
  (files_completed.to_f / total_files * 100).round(1)
end

def elapsed_time

Computed properties
def elapsed_time
  start_time ? Time.now - start_time : 0
end

def files_remaining

def files_remaining
  total_files - files_completed
end

def has_issues?

def has_issues?
  issues_count > 0
end

def issues_count

def issues_count
  failed + errors
end

def passed_count

def passed_count
  total_tests - issues_count
end

def tests_run?

def tests_run?
  total_tests > 0
end

def update_from_event(event_type, *args, **_kwargs)

Update state based on formatter events using pattern matching
def update_from_event(event_type, *args, **_kwargs)
  case event_type
  in :phase_header
    _, file_count, level = args
    if level == 0 && file_count
      with(total_files: file_count, start_time: Time.now)
    else
      self
    end
  in :file_start
    file_path   = args[0]
    pretty_path = Console.pretty_path(file_path)
    with(current_file: pretty_path)
  in :file_end
    with(
      files_completed: files_completed + 1,
      current_file: nil,
    )
  in :test_start
    test_case = args[0]
    desc      = test_case.description.to_s
    desc      = "test #{args[1]}" if desc.empty?
    with(current_test: desc)
  in :test_end
    with(current_test: nil)
  in :test_result
    result_packet = args[0]
    updated_state = with(total_tests: total_tests + 1)
    case result_packet.status
    when :passed
      updated_state.with(passed: passed + 1)
    when :failed
      updated_state.with(failed: failed + 1)
    when :error
      updated_state.with(errors: errors + 1)
    else
      updated_state
    end
  else
    # Unknown event, return unchanged state
    self
  end
end