class Tryouts

def cformat(*args)

def cformat(*args)
  Console.bright '%d of %d %s' % args
end

def comment? str

def comment? str
  !str.strip.match(/^\#+/).nil? && !expectation?(str)
end

def debug *msg

def debug *msg
  STDERR.puts *msg if @debug
end

def debug?() @debug == true end

def debug?() @debug == true end

def err *msg

def err *msg
  msg.each do |line|
    STDERR.puts Console.color :red, line
  end
end

def eval(str, path, line)

def eval(str, path, line)
  begin
    Kernel.eval str, @container.send(:binding), path, line
  rescue SyntaxError, LoadError => ex
    Tryouts.err Console.color(:red, ex.message),
                Console.color(:red, ex.backtrace.first)
    nil
  end
end

def expectation? str

def expectation? str
  !ignore?(str) && str.strip.match(/\A\#+\s*=>/)
end

def ignore? str

def ignore? str
  str.to_s.strip.chomp.empty?
end

def msg *msg

def msg *msg
  STDOUT.puts *msg unless Tryouts.quiet
end

def parse path

def parse path
  #debug "Loading #{path}"
  lines = File.readlines path
  skip_ahead = 0
  batch = TestBatch.new path, lines
  lines.size.times do |idx|
    skip_ahead -= 1 and next if skip_ahead > 0
    line = lines[idx].chomp
    #debug('%-4d %s' % [idx, line])
    if expectation? line
      offset = 0
      exps = Section.new(path, idx+1)
      exps << line.chomp
      while (idx+offset < lines.size)
        offset += 1
        this_line = lines[idx+offset]
        break if ignore?(this_line)
        if expectation?(this_line)
          exps << this_line.chomp
          skip_ahead += 1
        end
        exps.last += 1
      end
      offset = 0
      buffer, desc = Section.new(path), Section.new(path)
      test = Section.new(path, idx)  # test start the line before the exp.
      blank_buffer = Section.new(path)
      while (idx-offset >= 0)
        offset += 1
        this_line = lines[idx-offset].chomp
        buffer.unshift this_line if ignore?(this_line)
        if comment?(this_line)
          buffer.unshift this_line
        end
        if test?(this_line)
          test.unshift(*buffer) && buffer.clear
          test.unshift this_line
        end
        if test_begin?(this_line)
          while test_begin?(lines[idx-(offset+1)].chomp)
            offset += 1
            buffer.unshift lines[idx-offset].chomp
          end
        end
        if test_begin?(this_line) || idx-offset == 0 || expectation?(this_line)
          adjust = expectation?(this_line) ? 2 : 1
          test.first = idx-offset+buffer.size+adjust
          desc.unshift *buffer
          desc.last = test.first-1
          desc.first = desc.last-desc.size+1
          # remove empty lines between the description
          # and the previous expectation
          while !desc.empty? && desc[0].empty?
            desc.shift
            desc.first += 1
          end
          break
        end
      end
      batch << TestCase.new(desc, test, exps)
    end
  end
  batch
end

def print str

def print str
  return if Tryouts.quiet
  STDOUT.print str
  STDOUT.flush
end

def run path

def run path
  batch = parse path
  batch.run
  batch
end

def run_all *paths

def run_all *paths
  batches = paths.collect do |path|
    parse path
  end
  all, skipped_tests, failed_tests = 0, 0, 0
  skipped_batches, failed_batches = 0, 0
  msg 'Ruby %s @ %-40s' % [RUBY_VERSION, Time.now], $/
  if Tryouts.debug?
    Tryouts.debug "Found #{paths.size} files:"
    paths.each { |path| Tryouts.debug "  #{path}" }
    Tryouts.debug
  end
  batches.each do |batch|
    path = batch.path.gsub(/#{Dir.pwd}\/?/, '')
    vmsg '%-60s %s' % [path, '']
    before_handler = Proc.new do |t|
      if Tryouts.noisy && !Tryouts.fails
        vmsg Console.reverse(' %-58s ' % [t.desc.to_s])
        vmsg t.test.inspect, t.exps.inspect
      end
    end
    batch.run(before_handler) do |t|
      if t.failed?
        failed_tests += 1
        if Tryouts.noisy && Tryouts.fails
          vmsg Console.color(:red, t.failed.join($/)), $/
        else
          msg ' %s (%s:%s)' % [Console.color(:red, "FAIL"), path, t.exps.first]
        end
      elsif (t.skipped? || !t.run?) && !Tryouts.fails
        skipped_tests += 1
        if Tryouts.noisy
          vmsg Console.bright(t.skipped.join($/)), $/
        else
          msg ' SKIP (%s:%s)' % [path, t.exps.first]
        end
      elsif !Tryouts.fails
        if Tryouts.noisy
          vmsg Console.color(:green, t.passed.join($/)), $/
        else
          msg ' %s' % [Console.color(:green, 'PASS')]
        end
      end
      all += 1
    end
  end
  msg
  if all > 0
    suffix = 'tests passed'
    suffix << " (and #{skipped_tests} skipped)" if skipped_tests > 0
    msg cformat(all-failed_tests-skipped_tests, all-skipped_tests, suffix) if all-skipped_tests > 0
  end
  if batches.size > 1
    if batches.size-skipped_batches > 0
      suffix = "batches passed"
      suffix << " (and #{skipped_batches} skipped)" if skipped_batches > 0
      msg cformat(batches.size-skipped_batches-failed_batches, batches.size-skipped_batches, suffix)
    end
  end
  failed_tests # 0 means success
end

def sysinfo

def sysinfo
  require 'sysinfo'
  @sysinfo ||= SysInfo.new
  @sysinfo
end

def test? str

def test? str
  !ignore?(str) && !expectation?(str) && !comment?(str)
end

def test_begin? str

def test_begin? str
  ret = !str.strip.match(/\#+\s*TEST/i).nil? ||
  !str.strip.match(/\A\#\#+[\s\w]+/i).nil?
  ret
end

def vmsg *msg

def vmsg *msg
  STDOUT.puts *msg if !Tryouts.quiet && Tryouts.noisy
end