# encoding: UTF-8

require 'benchmark'
require 'spec'
require 'spec/runner/formatter/base_text_formatter'
require 'spec/spec_helper.rb'

# Load this stuff so it only has to be loaded once for the entire suite
require 'spec/mocks'
require 'spec/mocks/extensions'
require 'spec/runner/formatter/specdoc_formatter'
require 'base64'
require 'nkf'
require 'kconv'
require 'rack'
require 'fileutils'

Merb::Dispatcher

module Merb
  class Counter

    attr_accessor :time
    def initialize
      @examples, @failures, @errors, @pending, @total_time = 0, 0, 0, 0, 0
      @err = ""
      @mutex = Mutex.new
    end

    def failed?
      @failures > 0
    end

    def add(spec, out, err)
      @mutex.synchronize do
        puts
        puts "Running #{spec}."
        STDOUT.puts out
        STDOUT.flush
        match = out.match(/(\d+) examples?, (\d+) failures?(?:, (\d+) errors?)?(?:, (\d+) pending?)?/m)
        time = out.match(/Finished in (\d+\.\d+) seconds/)
        @total_time += time[1].to_f if time
        if match
          e, f, errors, pending = match[1..-1]
          @examples += e.to_i
          @failures += f.to_i
          @errors += errors.to_i
          @pending += pending.to_i
        end
        unless err.chomp.empty?
          @err << err.chomp << "\n"
        end
      end
    end

    def report
      i = 0
      @err.gsub!(/^\d*\)\s*/) do
        "#{i += 1})\n"
      end

      puts @err
      puts
      if @failures != 0 || @errors != 0
        print "\e[31m" # Red
      elsif @pending != 0
        print "\e[33m" # Yellow
      else
        print "\e[32m" # Green
      end
      puts "#{@examples} examples, #{@failures} failures, #{@errors} errors, #{@pending} pending, #{sprintf("suite run in %3.3f seconds", @time.real)}"
      # TODO: we need to report pending examples all together
       puts "\e[0m"
    end
  end
end

require File.dirname(__FILE__) / "run_spec"

# Runs specs in all files matching the file pattern.
#
# @param [String, Array<String>] globs File patterns to look for.
# @param [#to_s]  spec_cmd The spec command.
# @param [String] run_opts
#   Options to pass to spec commands, for instance, if you want to use profiling
#   formatter.
# @param [Array<String>] except File paths to skip.
def run_specs(globs, spec_cmd='spec', run_opts = "-c", except = [])
  require "optparse"
  require "spec"
  globs = globs.is_a?(Array) ? globs : [globs]

  forking = (ENV["FORK"] ? ENV["FORK"] == "1" : Merb.forking_environment?)
  base_dir = File.expand_path(File.dirname(__FILE__) / ".." / ".." / "..")

  counter = Merb::Counter.new
  forks   = 0
  failure = false

  FileUtils.rm_rf(base_dir / "results")
  FileUtils.mkdir_p(base_dir / "results")

  time = Benchmark.measure do
    files = {}
    globs.each do |glob|
      Dir[glob].each do |spec|
        if forking
          Kernel.fork do
            run_spec(spec, base_dir, run_opts)
          end
          Process.wait
        else
          `NOW=1 #{Gem.ruby} #{File.dirname(__FILE__) / "run_spec.rb"} \"#{spec}\"`
        end
        begin
          out = File.read(base_dir / "results" / "#{File.basename(spec)}_out")
          err = File.read(base_dir / "results" / "#{File.basename(spec)}_err")
          counter.add(spec, out, err)
        rescue Errno::ENOENT => e
          STDOUT.puts e.message
        end
      end
    end
  end

  Process.waitall

  counter.time = time
  counter.report
  FileUtils.rm_rf(base_dir / "results")
  exit!(counter.failed? ? -1 : 0)
end
