class M::Executor

def execute

def execute
  # Locate tests to run that may be inside of this line. There could be more than one!
  tests_to_run = tests.within testable.lines
  # If we found any tests,
  if tests_to_run.size.positive?
    # assemble the regexp to run these tests,
    test_names = tests_to_run.map { |test| Regexp.escape(test.name) }.join("|")
    # set up the args needed for the runner
    test_arguments = ["-n", "/^(#{test_names})$/"]
    # directly run the tests from here and exit with the status of the tests passing or failing
    runner.run test_arguments + testable.passthrough_options
  elsif tests.size.positive?
    # Otherwise we found no tests on this line, so you need to pick one.
    message = "No tests found on line #{testable.lines.join ", "}. Valid tests to run:\n\n"
    # For every test ordered by line number,
    # spit out the test name and line number where it starts,
    tests.by_line_number do |test|
      message << "#{format "%0#{tests.column_size}s", test.name}: m #{testable.file}:#{test.start_line}\n"
    end
    # Spit out helpful message and bail
    warn message
    false
  else
    # There were no tests at all
    message = "There were no tests found.\n\n"
    warn message
    false
  end
end

def initialize testable

def initialize testable
  @testable = testable
end

def runner

def runner
  @runner ||= M::Frameworks.framework_runner
end

def suites

Finds all test suites in this test file, with test methods included.
def suites
  # Since we're not using `ruby -Itest -Ilib` to run the tests, we need to add this directory to the `LOAD_PATH`
  $LOAD_PATH.unshift "./test", "./spec", "./lib"
  begin
    # Fire up this Ruby file. Let's hope it actually has tests.
    require "./#{testable.file}"
  rescue LoadError => e
    # Fail with a happier error message instead of spitting out a backtrace from this gem
    warn "Failed loading test file:\n#{e.message}"
    return []
  end
  suites = runner.suites
  # Use some janky internal APIs to group test methods by test suite.
  suites.each_with_object({}) do |suite_class, test_suites|
    # End up with a hash of suite class name to an array of test methods, so we can later find them and ignore empty test suites
    test_suites[suite_class] = runner.test_methods(suite_class) if runner.test_methods(suite_class).any?
  end
end

def tests

Memoize it since it's unnecessary to do this more than one for a given file.
Shoves tests together in our custom container and collection classes.
def tests
  @tests ||= begin
    require "m/test_collection"
    require "m/test_method"
    # With each suite and array of tests,
    # and with each test method present in this test file,
    # shove a new test method into this collection.
    suites.each_with_object TestCollection.new do |(suite_class, test_methods), collection|
      test_methods.each do |test_method|
        collection << TestMethod.create(suite_class, test_method)
      end
    end
  end
end