# Definitions:
#  MSPEC_FILE:
#    tagged       - runs specs which a MSPEC_FILE
#    all          - runs specs without specifying an MSPEC_FILE
#    !all,!tagged - runs specs with an MSPEC_FILE
#  RUNTIME:
#    interpreted  - -X-C (or OFF)
#    compiled     - JIT w/ threshold 0
#    precompiled  - AOT (or FORCE) 
#  RUBYSPEC:
#    latest       - grab head of rubyspecs
#    stable       - get saved blessed version (see RUBYSPECS_VERSION)

# FIXME: Make Rubyspecs FAILED message appear?

namespace :spec do
  # This set of tasks will run ci and retrieve the proper version of specs.
  # The specs below this section will run specs, but they will not retrieve
  # the specs they run against.  This is so we can run the similiar mspec
  # runs against a stable and head version of the rubyspecs.

  desc "Run simple set of tests over both 1.8 and 1.9 modes"
  task :short => 'spec:ci_interpreted_18_19'

  desc "Run rubyspecs expected to pass (version-frozen)"
  task :ci => ['spec:fetch_stable_specs', 'spec:tagged_18']
  task :ci_18 => :ci

  desc "Run rubyspecs expected to pass (version-frozen)"
  task :ci_19 => ['spec:fetch_stable_specs', 'spec:tagged_19']

  desc "Run rubyspecs expected to pass in interpreted mode (version-frozen)"
  task :ci_interpreted_18 => ['spec:fetch_stable_specs', 'spec:interpreted_18']

  desc "Run rubyspecs expected to pass in interpreted 1.9 mode (version-frozen)"
  task :ci_interpreted_19 => ['spec:fetch_stable_specs', 'spec:interpreted_19']

  desc "Run rubyspecs expected to pass in compiled mode (version-frozen)"
  task :ci_compiled_18 => ['spec:fetch_stable_specs', 'spec:compiled_18']

  desc "Run rubyspecs expected to pass in compiled mode (version-frozen)"
  task :ci_compiled_19 => ['spec:fetch_stable_specs', 'spec:compiled_19']

  desc "Run rubyspecs expected to pass in precompiled mode (version-frozen)"
  task :ci_precompiled_18 => ['spec:fetch_stable_specs', 'spec:precompiled_18']

  desc "Run rubyspecs expected to pass in precompiled mode (version-frozen)"
  task :ci_precompiled_19 => ['spec:fetch_stable_specs', 'spec:precompiled_19']

  desc "Run rubyspecs expected to pass in interpreted mode (version-frozen, both 1.8 and 1.9)"
  task :ci_interpreted_18_19 => ['spec:fetch_stable_specs', 'spec:interpreted_18', 'spec:interpreted_19']

  desc "Run all the specs including failures (version-frozen)"
  task :ci_all => ['spec:fetch_stable_specs', 'spec:all_18']

  desc "Run all the specs including failures (version-frozen)"
  task :ci_all_19 => ['spec:fetch_stable_specs', 'spec:all_19']

  desc "Run all the specs in precompiled mode (version-frozen)"
  task :ci_all_precompiled_18 => ['spec:fetch_stable_specs', 'spec:all_precompiled_18']

  desc "Run rubyspecs expected to pass (against latest rubyspec version)"
  task :ci_latest => ['spec:fast_forward_to_rubyspec_head', 'spec:tagged_18']

  desc "Run rubyspecs expected to pass (against latest rubyspec version)"
  task :ci_latest_19 => ['spec:fast_forward_to_rubyspec_head', 'spec:tagged_19']

  desc "Run optional C API rubyspecs"
  task :ci_cext => ['spec:fast_forward_to_rubyspec_head'] do
    mspec :spec_config => CEXT_MSPEC_FILE
    fail "One or more Ruby spec runs have failed" if spec_run_error
  end

  # Note: For this point below it is your reponsibility to make sure specs
  # are checked out.

  desc "Run 1.8 tagged specs in interpreted, JIT, and pre-compiled modes"
  task :tagged_18 => [:interpreted_18, :compiled_18, :precompiled_18] do
    fail "One or more Ruby spec runs have failed" if spec_run_error
  end

  desc "Run all 1.8 specs in interpreted, JIT, and pre-compiled modes"
  task :all_18 => [:all_interpreted_18, :all_compiled_18, :all_precompiled_18] do
    fail "One or more Ruby spec runs have failed" if spec_run_error
  end

  desc "Run 1.9 tagged specs in interpreted, JIT, and pre-compiled modes"
  task :tagged_19 => [:interpreted_19, :compiled_19, :precompiled_19] do
    fail "One or more Ruby spec runs have failed" if spec_run_error
  end

  desc "Run all 1.9 specs in interpreted, JIT, and pre-compiled modes"
  task :all_19 => [:all_interpreted_19, :all_compiled_19, :all_precompiled_19] do
    fail "One or more Ruby spec runs have failed" if spec_run_error
  end

  desc "Tagged 1.8 specs in interpreted mode only"
  task :interpreted_18 do
    mspec :compile_mode => "OFF", :spec_config => RUBY18_MSPEC_FILE, 
       :compat => "RUBY1_8"
  end

  desc "Tagged 1.8 specs in interpreted (IR) mode only"
  task :interpreted_ir_18 do
    mspec :compile_mode => "OFFIR", :spec_config => RUBY18_MSPEC_FILE, 
       :compat => "RUBY1_8"
  end

  desc "Tagged 1.8 specs in JIT mode only (threshold=0)"
  task :compiled_18 do
    mspec :compile_mode => "JIT", :spec_config => RUBY18_MSPEC_FILE, 
       :jit_threshold => 0, 
       :compat => "RUBY1_8"
  end

  desc "Tagged 1.8 specs in AOT mode only"
  task :precompiled_18 do
    mspec :compile_mode => "FORCE", :spec_config => RUBY18_MSPEC_FILE, 
       :jit_threshold => 0, 
       :compat => "RUBY1_8"
  end

  desc "All 1.8 specs in interpreted mode only"
  task :all_interpreted_18 do
    mspec :compile_mode => "OFF", 
       :compat => "RUBY1_8"
  end

  desc "All 1.8 specs in JIT mode only (threshold=0)"
  task :all_compiled_18 do
    mspec :compile_mode => "JIT", :jit_threshold => 0, 
       :compat => "RUBY1_8"
  end

  desc "All 1.8 specs in AOT mode only"
  task :all_precompiled_18 do
    mspec :compile_mode => "FORCE", :jit_threshold => 0, 
       :compat => "RUBY1_8"
  end

  desc "Tagged 1.9 specs in interpreted mode only"
  task :interpreted_19 do
    mspec :compile_mode => "OFF", :spec_config => RUBY19_MSPEC_FILE, 
       :compat => "RUBY1_9"
  end

  desc "Tagged 1.9 specs in JIT mode only (threshold=0)"
  task :compiled_19 do
    mspec :compile_mode => "JIT", :spec_config => RUBY19_MSPEC_FILE, 
       :jit_threshold => 0, :compat => "RUBY1_9"
  end

  desc "Tagged 1.9 specs in AOT mode only"
  task :precompiled_19 do
    mspec :compile_mode => "FORCE", :spec_config => RUBY19_MSPEC_FILE, 
       :jit_threshold => 0, :compat => "RUBY1_9"
  end

  desc "All 1.9 specs in interpreted mode only"
  task :all_interpreted_19 do
    mspec :compile_mode => "OFF", :compat => "RUBY1_9" 
  end

  desc "All 1.9 specs in JIT mode only (threshold=0)"
  task :all_compiled_19 do
    mspec :compile_mode => "JIT", :jit_threshold => 0, :compat => "RUBY1_9"
  end

  desc "All 1.9 specs in AOT mode only"
  task :all_precompiled_19 do
    mspec :compile_mode => "FORCE", :jit_threshold => 0, :compat => "RUBY1_9"
  end

  # Put Rake on the load path for JI specs without requiring rubygems
  rake_location = File.join(Gem.loaded_specs['rake'].full_gem_path, "lib")
  gem 'rspec'
  require 'rspec/core/rake_task'

  {"" => "--1.8", "_19" => "--1.9"}.each do |version_suffix, version_arg|
    desc "Runs Java Integration Specs"
    RSpec::Core::RakeTask.new("ji#{version_suffix}" => "test:compile") do |t|
      t.ruby_opts = " -I#{rake_location}"
      t.ruby_opts = version_arg
      t.rspec_opts ||= []
      t.rspec_opts << "--options" << "spec/java_integration/spec.opts"
      t.pattern = 'spec/java_integration/**/*_spec.rb'
    end

    desc "Runs Java Integration specs quietly"
    RSpec::Core::RakeTask.new("ji:quiet#{version_suffix}" => "test:compile") do |t|
      t.ruby_opts = " -I#{rake_location}"
      t.ruby_opts = version_arg
      t.rspec_opts ||= []
      t.rspec_opts << "--options" << "spec/java_integration/spec.quiet.opts"
      t.pattern = 'spec/java_integration/**/*_spec.rb'
    end

    desc "Runs Compiler Specs"
    RSpec::Core::RakeTask.new("compiler#{version_suffix}" => "test:compile") do |t|
      t.ruby_opts = version_arg
      t.pattern = 'spec/compiler/**/*_spec.rb'
    end

    desc "Runs Profiler Specs"
    RSpec::Core::RakeTask.new("profiler#{version_suffix}" => "test:compile") do |t|
      t.ruby_opts = "--profile #{version_arg}"
      t.pattern = 'spec/profiler/**/*_spec.rb'
    end

    desc "Runs FFI specs"
    RSpec::Core::RakeTask.new("ffi#{version_suffix}" => "test:compile") do |t|
      t.ruby_opts = version_arg
      t.pattern = 'spec/ffi/**/*_spec.rb'
    end

    desc "Runs Java Signature Parser Specs"
    RSpec::Core::RakeTask.new("java_signature_parser#{version_suffix}") do |t|
      t.ruby_opts = version_arg
      t.rspec_opts ||= []
      #    t.spec_opts << "--options" << "spec/java_integration/spec.quiet.opts"
      t.pattern = 'spec/grammar/**/*_spec.rb'
    end

    desc "Runs Regression Specs"
    RSpec::Core::RakeTask.new("regression#{version_suffix}") do |t|
      t.ruby_opts = version_arg
      t.pattern = 'spec/regression/**/*_spec.rb'
    end

  end

  # Complimentary tasks for running specs

  task :fetch_latest_specs => [:install_build_gems, :fetch_latest_rubyspec_repo, :fetch_latest_mspec_repo]

  task :fetch_stable_specs => :install_build_gems do
    puts "Rolling rubyspec to stable version"
    git_submodule_update('spec/ruby')

    puts "Rolling mspec to stable version"
    git_submodule_update('spec/mspec')
  end
  
  task :fast_forward_to_rubyspec_head => :fetch_latest_specs do
    puts "Rolling to rubyspec to latest version"
    git_checkout('rubyspec', 'origin/HEAD', RUBYSPEC_DIR)
    git_move_to_head_detached('rubyspec', RUBYSPEC_GIT_REPO, RUBYSPEC_DIR)
  end

  desc "Retrieve latest tagged rubyspec git repository"
  task :fetch_latest_rubyspec_repo do
    unless git_repo_exists? RUBYSPEC_DIR
      clean_spec_dirs
      git_clone('rubyspec', RUBYSPEC_GIT_REPO, RUBYSPEC_DIR)
    else
      git_fetch('rubyspec', RUBYSPEC_DIR, ignore_error = true)
    end
  end

  desc "Retrieve latest tagged mspec git repository"
  task :fetch_latest_mspec_repo do
    unless git_repo_exists? MSPEC_DIR
      git_clone('mspec', MSPEC_GIT_REPO, MSPEC_DIR)
    else
      git_fetch('mspec', MSPEC_DIR, ignore_error = true)
    end
  end

  desc "Clean up spec dirs"
  task :clean_specs do
    clean_spec_dirs(true)
  end

  def clean_spec_dirs(wipe_spec_dir = false)
    rm_rf RUBYSPEC_DIR if wipe_spec_dir
    rm_rf MSPEC_DIR
    rm_f RUBYSPEC_TAR_FILE
    rm_f MSPEC_TAR_FILE
    rm_f File.join(SPEC_DIR, "rubyspecs.current.revision")
  end

  def spec_run_error
    # Obtuseriffic - If any previous spec runs were non-zero return we failed
    ['OFF', 'JIT', 'FORCE'].any? {|n| ant.properties["spec.status.#{n}"] != "0"}
  end
end
