class Minitest::TestTask

def self.create name = :test, &block

def self.create name = :test, &block
  task = new name
  task.instance_eval(&block) if block
  task.process_env
  task.define
  task
end

def define # :nodoc:

:nodoc:
def define # :nodoc:
  desc "Run the test suite. Use N, X, A, and TESTOPTS to add flags/args."
  task name do
    ruby make_test_cmd, verbose:verbose
  end
  desc "Print out the test command. Good for profiling and other tools."
  task "#{name}:cmd" do
    puts "ruby #{make_test_cmd}"
  end
  desc "Show which test files fail when run in isolation."
  task "#{name}:isolated" do
    tests = Dir[*self.test_globs].uniq
    # 3 seems to be the magic number... (tho not by that much)
    bad, good, n = {}, [], (ENV.delete("K") || 3).to_i
    file = ENV.delete("F")
    times = {}
    tt0 = Time.now
    n.threads_do tests.sort do |path|
      t0 = Time.now
      output = `#{Gem.ruby} #{make_test_cmd path} 2>&1`
      t1 = Time.now - t0
      times[path] = t1
      if $?.success?
        $stderr.print "."
        good << path
      else
        $stderr.print "x"
        bad[path] = output
      end
    end
    puts "done"
    puts "Ran in %.2f seconds" % [ Time.now - tt0 ]
    if file then
      require "json"
      File.open file, "w" do |io|
        io.puts JSON.pretty_generate times
      end
    end
    unless good.empty?
      puts
      puts "# Good tests:"
      puts
      good.sort.each do |path|
        puts "%.2fs: %s" % [times[path], path]
      end
    end
    unless bad.empty?
      puts
      puts "# Bad tests:"
      puts
      bad.keys.sort.each do |path|
        puts "%.2fs: %s" % [times[path], path]
      end
      puts
      puts "# Bad Test Output:"
      puts
      bad.sort.each do |path, output|
        puts
        puts "# #{path}:"
        puts output
      end
      exit 1
    end
  end
  task "#{name}:deps" => "#{name}:isolated" # now just an alias
  desc "Show bottom 25 tests wrt time."
  task "#{name}:slow" do
    sh ["rake #{name} A=-v",
        "egrep '#test_.* s = .'",
        "sort -n -k2 -t=",
        "tail -25"].join " | "
  end
end

def initialize name = :test # :nodoc:

:nodoc:
def initialize name = :test # :nodoc:
  self.extra_args   = []
  self.framework    = %(require "minitest/autorun")
  self.libs         = %w[lib test .]
  self.name         = name
  self.test_globs   = ["test/**/test_*.rb",
                       "test/**/*_test.rb"]
  self.test_prelude = nil
  self.verbose      = Rake.application.options.trace
  self.warning      = true
end

def make_test_cmd globs = test_globs

def make_test_cmd globs = test_globs
  tests = []
  tests.concat Dir[*globs].sort.shuffle # TODO: SEED -> srand first?
  tests.map! { |f| %(require "#{f}") }
  runner = []
  runner << test_prelude if test_prelude
  runner << framework
  runner.concat tests
  runner = runner.join "; "
  args  = []
  args << "-I#{libs.join(File::PATH_SEPARATOR)}" unless libs.empty?
  args << "-w" if warning
  args << '-e'
  args << "'#{runner}'"
  args << '--'
  args << extra_args.map(&:shellescape)
  args.join " "
end

def process_env

def process_env
  warn "TESTOPTS is deprecated in Minitest::TestTask. Use A instead" if
    ENV["TESTOPTS"]
  warn "FILTER is deprecated in Minitest::TestTask. Use A instead" if
    ENV["FILTER"]
  warn "N is deprecated in Minitest::TestTask. Use MT_CPU instead" if
    ENV["N"] && ENV["N"].to_i > 0
  lib_extras = (ENV["MT_LIB_EXTRAS"] || "").split File::PATH_SEPARATOR
  self.libs[0,0] = lib_extras
  extra_args << "-n" << ENV["N"]                      if ENV["N"]
  extra_args << "-e" << ENV["X"]                      if ENV["X"]
  extra_args.concat Shellwords.split(ENV["TESTOPTS"]) if ENV["TESTOPTS"]
  extra_args.concat Shellwords.split(ENV["FILTER"])   if ENV["FILTER"]
  extra_args.concat Shellwords.split(ENV["A"])        if ENV["A"]
  ENV.delete "N" if ENV["N"]
  # TODO? RUBY_DEBUG = ENV["RUBY_DEBUG"]
  # TODO? ENV["RUBY_FLAGS"]
  extra_args.compact!
end