#!/usr/bin/env ruby
# encoding: utf-8

require 'rake'
require 'rake/testtask'
require 'fileutils'

module RubyProf

  # Define a task library for profiling unit tests with ruby-prof.
  #
  # All of the options provided by
  # the Rake:TestTask are supported except the loader
  # which is set to ruby-prof.  For detailed information
  # please refer to the Rake:TestTask documentation.
  #
  # ruby-prof specific options include:
  #
  #   output_dir - For each file specified an output
  #                file with profile information will be
  #                written to the output directory.
  #                By default, the output directory is
  #                called "profile" and is created underneath
  #                the current working directory.
  #
  #   printer - Specifies the output printer.  Valid values include
  #             :flat, :graph, :graph_html and :call_tree.
  #
  #   min_percent - Methods that take less than the specified percent
  #                 will not be written out.
  #
  # Example:
  #
  #   require 'ruby-prof/task'
  #
  #   RubyProf::ProfileTask.new do |t|
  #     t.test_files = FileList['test/test*.rb']
  #     t.output_dir = "c:/temp"
  #     t.printer = :graph
  #     t.min_percent = 10
  #   end
  #
  # If rake is invoked with a "TEST=filename" command line option,
  # then the list of test files will be overridden to include only the
  # filename specified on the command line.  This provides an easy way
  # to run just one test.
  #
  # If rake is invoked with a "TESTOPTS=options" command line option,
  # then the given options are passed to the test process after a
  # '--'.  This allows Test::Unit options to be passed to the test
  # suite.
  #
  # Examples:
  #
  #   rake profile                           # run tests normally
  #   rake profile TEST=just_one_file.rb     # run just one test file.
  #   rake profile TESTOPTS="-v"             # run in verbose mode
  #   rake profile TESTOPTS="--runner=fox"   # use the fox test runner

  class ProfileTask < Rake::TestTask
    attr_accessor :output_dir
    attr_accessor :min_percent
    attr_accessor :printer

    def initialize(name = :profile)
      super(name)
    end

    # Create the tasks defined by this task lib.
    def define
      lib_path = @libs.join(File::PATH_SEPARATOR)
      desc "Profile" + (@name==:profile ? "" : " for #{@name}")

      task @name do
        create_output_directory

        @ruby_opts.unshift( "-I#{lib_path}" )
        @ruby_opts.unshift( "-w" ) if @warning
        @ruby_opts.push("-S ruby-prof")
        @ruby_opts.push("--printer #{@printer}")
        @ruby_opts.push("--min_percent #{@min_percent}")

        file_list.each do |file_path|
          run_script(file_path)
        end
      end
      self
    end

    # Run script
    def run_script(script_path)
      run_code = ''
      RakeFileUtils.verbose(@verbose) do
        file_name = File.basename(script_path, File.extname(script_path))
        case @printer
          when :flat, :graph, :call_tree
            file_name += ".txt"
          when :graph_html
            file_name += ".html"
          else
            file_name += ".txt"
        end

        output_file_path = File.join(output_directory, file_name)

        command_line = @ruby_opts.join(" ") +
                      " --file=" + output_file_path +
                      " " + script_path

        puts "ruby " + command_line
        # We have to catch the exeption to continue on.  However,
        # the error message will have been output to STDERR
        # already by the time we get here so we don't have to
        # do that again
        begin
          ruby command_line
        rescue => e
          STDOUT << e << "\n"
          STDOUT.flush
        end
        puts ""
        puts ""
      end
    end

    def output_directory
      File.expand_path(@output_dir)
    end

    def create_output_directory
      if not File.exist?(output_directory)
        Dir.mkdir(output_directory)
      end
    end

    def clean_output_directory
      if File.exist?(output_directory)
        files = Dir.glob(output_directory + '/*')
        FileUtils.rm(files)
      end
    end

    def option_list # :nodoc:
      ENV['OPTIONS'] || @options.join(" ") || ""
    end
  end
end
