#!/usr/bin/env ruby
# Author::    The Uttk Team.
# Copyright:: Copyright (c) 2004, 2005 Uttk team. All rights reserved.
# License::   LGPL
# $Id: /w/fey/uttk/trunk/bin/uttk 22095 2006-02-21T17:17:56.386215Z pouillar  $

begin
  require 'pathname'

  ME = Pathname.new($0).basename unless defined? ME
  ME_DIR = Pathname.new(__FILE__).dirname unless defined? ME_DIR

  $PROGRAME_NAME = ME
  $PROGRAME_NAME.freeze
  $VERBOSE = true           # you can use 'export RUBYOPT="-w"' too

  Uttk_BIN = true

  require Pathname.new(__FILE__).dirname.parent + 'lib/uttk' unless defined? Uttk

  module Uttk
    SPEC_YML = ME_DIR.parent + 'SPEC.yml'
    SPEC = YAML::load(SPEC_YML.read)
    VERSION = SPEC['version']
    VERSION_PATH = ME_DIR.parent + 'VERSION'
    AUTHORS_PATH = ME_DIR.parent + 'AUTHORS'
    require 'erb'
    VERSION_TEXT = ERB.new(VERSION_PATH.read, nil, '<%>').result(binding)
  end

  require "#{ME_DIR}/getopts/uttk"

  argv = ARGV.dup
  opts = Uttk::Getopts::Uttk.parse(argv)

  require 'profile' if opts[:profile]

  if opts[:cache_dir] and opts[:cache_dir] != TempPath.tmpdir
    TempPath.tmpdir.rmdir
    opts[:cache_dir].mv(TempPath.tmpdir)
    opts[:cache_dir] = TempPath.tmpdir
    opts[:cache_mode] ||= true
  end

  symtbl = Uttk.default_symtbl(opts).new_child
  log = symtbl[:log]
  at_exit { log.close }

  t = argv.uttk_testify(symtbl)

  # Load the cache file if cache_dir is set.
  if cache_dir = opts[:cache_dir]
    cache = cache_dir + 'cache.yml'
    t.symtbl[:use_cache] = YAML.parse(cache.read).symbol_safe_transform
  end

  # Disable the automatic cleaning of TempPath for the cache mode.
  if opts[:cache_mode]
    TempPath.auto_clean = false
  end

  # Run the root test.
  status = t.run


  # Generate the cache file.
  if opts[:cache_mode]
    cache_file = TempPath.tmpdir + 'cache.yml'
    cache = t.symtbl[:cache]
    t.symtbl.local.delete :cache
    cache.each do |name, test|
      todel = [:use_cache]
      test[:symtbl].each do |k, v|
        if [SynFlow, SynFlowFactory].any? { |klass| v.is_a? klass }
          todel << k
        end
      end
      todel.each { |k| test[:symtbl].delete(k) }
    end
    cache_file.open('w') { |f| f.puts cache.to_yaml }
  end

  relaunch = TempPath.tmpdir + 'relaunch'

  if opts[:cache_mode]

    # Update the argument for the cache mode
    ARGV.delete '-C'
    if ARGV.include? '--cache-dir'
      ind = ARGV.index('--cache-dir') + 1
      ARGV[ind] = TempPath.tmpdir.to_s
    else
      ARGV << '--cache-dir' << TempPath.tmpdir.to_s
    end

  end

  if (opts[:cache_mode] or opts[:cache_dir]) and not status.pass?
    raise 'HighLine is unavailable' unless defined? HighLine
    h = HighLine.new
    # Ask the user what to do.
    if h.agree 'Would you relaunch the test suite now (use the cache) (y/n)', true
      pid = Kernel.fork do
        Kernel.exec Config::CONFIG['RUBY_INSTALL_PATH'], $0, *ARGV
      end
      Process.waitpid pid
      exit
    else
      if h.agree 'Would you remove the temporary dir (y/n)', true
        TempPath.auto_clean = true
      else
        TempPath.auto_clean = false
        # Generate a little helper script to use the cache mode.
        unless relaunch.exist?
          relaunch.open('w') do |f|
            f.puts '#!/usr/bin/env ruby'
            f.puts "
              |Kernel.exec #{Config::CONFIG['RUBY_INSTALL_PATH'].inspect},
              |            #{$0.inspect}, *#{ARGV.inspect}
              |".head_cut!
          end
          relaunch.chmod(0500)
        end

        STDERR.puts "
          |
          |--- |
          |
          | /------------------------------------------------------------------.
          | | Uttk was running in cache mode to re-run the same one just type: |
          | |   #{relaunch.to_s.ljust(63)}|
          | `------------------------------------------------------------------/
          |".head_cut!
      end
    end
  else
    TempPath.auto_clean = true
  end

  # Handle the --dump-status option.
  STDOUT.puts status.to_yaml if opts[:dump_status]

  unless status.pass?

    if not opts[:dump_status] # FIXME and filter contains Default
      STDERR.puts "
        |
        |--- |
        | /-----------------------------------------------------------------------------.
        | |                         *** Some tests failed ***                           |
        | | The last status was not PASS but something like FAIL(42%) (just FAIL stands |
        | | for FAIL(0%)). This means that 42% of tests *pass* but 100% was expected.   |
        | | To investigate results these different outputs are available:               |
        | |   - the standard output is the shortest one (like a progression bar).       |
        | |   - log.html contains an HTML/JavaScript page nice to display big outputs.  |
        | |   - log.yml contains more information in the very readable YAML format.     |
        | |   - log.xml contains the output as an XML document.                         |
        | `-----------------------------------------------------------------------------/
        |".head_cut!
    end
    exit(2)
  end

rescue SystemExit
  raise
rescue Exception => ex

  TempPath.auto_clean = true if defined? TempPath
  Thread.critical = true if defined? Thread
  raise ex if ex.is_a? SystemExit
  if defined? OptionParser and ex.is_a? OptionParser::ParseError
    Uttk::Getopts::Uttk.usage
    puts
    if log.nil?
      STDERR.puts "ERROR:\n  #{ex.to_s} (OptionParser)"
    else
      log.error { ex }
    end
  elsif log.nil?
    raise ex
  else
    log.error { ex }
  end
  exit(1)

end

exit(0)