#
# execute runit tests in T/
#

require 'runit/testsuite'
require 'runit/testcase'
require 'runit/cui/testrunner'


def main
  unless File.basename(Dir.pwd) == 'T' then
    Dir.chdir 'T'
  end

  if ARGV.empty? then
    suite = load_all_test_cases()
  else
    suite = load_selected_test_cases(ARGV)
  end

  RUNIT::CUI::TestRunner.quiet_mode = true
  writing_result {|f|
      RUNIT::CUI::TestRunner.new(f).run suite
  }
end

def writing_result
  if File.exist? '/dev/tty' then
    File.open('/dev/tty', 'w') {|f|
        f.sync = true
        yield f
    }
  else
    yield $stderr
  end
end


def load_all_test_cases
  suite = RUNIT::TestSuite.new
  Dir.glob('test*.rb').each do |fname|
    load_test_classes(fname).each do |c|
      suite.add_test c.suite
    end
  end
  suite
end


def load_selected_test_cases( specs )
  suite = RUNIT::TestSuite.new
  specs.each do |spec|
    suite.add_test get_suite( * parse_target_spec(spec) )
  end
  suite
end

def parse_target_spec( arg )
  unless m = %r<\A(\w+)(?:/(\w+)(?:\.(\w+(?:,\w+)*))?)?>.match(arg) then
    $stderr.puts "wrong arg format: #{arg}"
    exit 1
  end
  return m[1], m[2], m[3] && m[3].split(/,/)
end

def get_suite( t_file, t_class, t_methods )
  suite = RUNIT::TestSuite.new
  
  classes = load_test_classes("test#{t_file}.rb")
  if t_class then
    targclass = find_target_class(t_file, classes, t_class)
    if t_methods then
      t_methods.each do |m|
        suite.add_test targclass.new('test_' + m)
      end
    else
      suite.add_test targclass.suite
    end
  else
    classes.each do |c|
      suite.add_test c.suite
    end
  end

  suite
end

def find_target_class( t_file, classes, target )
  ret = classes.find {|c|
            c.name.split(/::/)[-1].downcase == target.downcase + 'tester'
        }
  unless ret then
    $stderr.puts "no such class in test#{t_file}.rb: #{target}Tester"
    exit 1
  end
  ret
end


$loaded_test_classes = []

def load_test_classes( fname )
  unless File.file? fname then
    $stderr.puts "test script '#{fname}' not exist"
    exit 1
  end
  require './' + fname
  if $loaded_test_classes.empty? then
    $stderr.puts "Error: #{fname} does not contains any test classes; abort."
    exit 1
  end
  ret, $loaded_test_classes = $loaded_test_classes, []
  ret
end

class Class
  def testme!
    unless RUNIT::TestCase > self then
      $stderr.puts "class '#{self.name}' is not a RUNIT::TestCase"
      exit 1
    end
    $loaded_test_classes.push self
  end
end


main
