<commit_before>require File.join(File.dirname(__FILE__), 'spec_helper')

describe 'RightScale::popen3' do

  module RightPopenSpec

    class Runner
      def initialize
        @done           = false
        @output_text    = nil
        @error_text     = nil
        @status         = nil
        @last_exception = nil
        @last_iteration = 0
      end

      attr_reader :output_text, :error_text, :status
      attr_accessor :pid

      def do_right_popen(command, env=nil, input=nil)
        @timeout = EM::Timer.new(2) { puts "\n** Failed to run #{command.inspect}: Timeout"; EM.stop }
        @output_text = ''
        @error_text  = ''
        @status      = nil
        @pid         = nil
        RightScale.popen3(:command        => command,
                          :input          => input,
                          :target         => self,
                          :environment    => env,
                          :stdout_handler => :on_read_stdout,
                          :stderr_handler => :on_read_stderr,
                          :pid_handler    => :on_pid,
                          :exit_handler   => :on_exit)
      end

      def run_right_popen(command, env=nil, input=nil, count=1)
        begin
          @command = command
          @env = env
          @last_iteration = 0
          @count = count
          puts "#{count}>" if count > 1
          EM.run { EM.next_tick { do_right_popen(command, env, input) } }
        rescue Exception => e
          puts "\n** Failed: #{e.message} FROM\n#{e.backtrace.join("\n")}"
          raise e
        end
      end

      def on_read_stdout(data)
        @output_text << data
      end

      def on_read_stderr(data)
        @error_text << data
      end

      def on_pid(pid)
        raise "PID already set!" unless @pid.nil?
        @pid = pid
      end

      def on_exit(status)
        @last_iteration += 1
        @timeout.cancel if @timeout
        if @last_iteration < @count
          EM.next_tick do
            print '+'
            STDOUT.flush
            do_right_popen(@command, @env)
          end
        else
          puts "<" if @count > 1
          EM.stop
        end
        @status = status
      end
    end

  end

  it 'should redirect output' do
    command = "\"#{RUBY_CMD}\" \"#{File.expand_path(File.join(File.dirname(__FILE__), 'produce_output.rb'))}\" \"#{STANDARD_MESSAGE}\" \"#{ERROR_MESSAGE}\""
    runner = RightPopenSpec::Runner.new
    runner.run_right_popen(command)
    runner.status.exitstatus.should == 0
    runner.output_text.should == STANDARD_MESSAGE + "\n"
    runner.error_text.should == ERROR_MESSAGE + "\n"
    runner.pid.should > 0
  end

  it 'should return the right status' do
    command = "\"#{RUBY_CMD}\" \"#{File.expand_path(File.join(File.dirname(__FILE__), 'produce_status.rb'))}\" #{EXIT_STATUS}"
    runner = RightPopenSpec::Runner.new
    runner.run_right_popen(command)
    runner.status.exitstatus.should == EXIT_STATUS
    runner.output_text.should == ''
    runner.error_text.should == ''
    runner.pid.should > 0
  end

  it 'should correctly handle many small processes' do
    pending 'Set environment variable TEST_STRESS to enable' unless ENV['TEST_STRESS']
    1000.times do
      command = "exit 0"
      runner = RightPopenSpec::Runner.new
      runner.run_right_popen(command)
      runner.status.exitstatus.should == 0
      runner.output_text.should == ""
      runner.error_text.should == ''
      runner.pid.should > 0
    end
  end

  it 'should preserve the integrity of stdout when stderr is unavailable' do
    count = LARGE_OUTPUT_COUNTER
    command = "\"#{RUBY_CMD}\" \"#{File.expand_path(File.join(File.dirname(__FILE__), 'produce_stdout_only.rb'))}\" #{count}"
    runner = RightPopenSpec::Runner.new
    runner.run_right_popen(command)<commit_msg>fixed stress test command line for windows<commit_after>require File.join(File.dirname(__FILE__), 'spec_helper')

describe 'RightScale::popen3' do

  module RightPopenSpec

    class Runner
      def initialize
        @done           = false
        @output_text    = nil
        @error_text     = nil
        @status         = nil
        @last_exception = nil
        @last_iteration = 0
      end

      attr_reader :output_text, :error_text, :status
      attr_accessor :pid

      def do_right_popen(command, env=nil, input=nil)
        @timeout = EM::Timer.new(2) { puts "\n** Failed to run #{command.inspect}: Timeout"; EM.stop }
        @output_text = ''
        @error_text  = ''
        @status      = nil
        @pid         = nil
        RightScale.popen3(:command        => command,
                          :input          => input,
                          :target         => self,
                          :environment    => env,
                          :stdout_handler => :on_read_stdout,
                          :stderr_handler => :on_read_stderr,
                          :pid_handler    => :on_pid,
                          :exit_handler   => :on_exit)
      end

      def run_right_popen(command, env=nil, input=nil, count=1)
        begin
          @command = command
          @env = env
          @last_iteration = 0
          @count = count
          puts "#{count}>" if count > 1
          EM.run { EM.next_tick { do_right_popen(command, env, input) } }
        rescue Exception => e
          puts "\n** Failed: #{e.message} FROM\n#{e.backtrace.join("\n")}"
          raise e
        end
      end

      def on_read_stdout(data)
        @output_text << data
      end

      def on_read_stderr(data)
        @error_text << data
      end

      def on_pid(pid)
        raise "PID already set!" unless @pid.nil?
        @pid = pid
      end

      def on_exit(status)
        @last_iteration += 1
        @timeout.cancel if @timeout
        if @last_iteration < @count
          EM.next_tick do
            print '+'
            STDOUT.flush
            do_right_popen(@command, @env)
          end
        else
          puts "<" if @count > 1
          EM.stop
        end
        @status = status
      end
    end

  end

  def is_windows?
    return !!(RUBY_PLATFORM =~ /mswin/)
  end

  it 'should redirect output' do
    command = "\"#{RUBY_CMD}\" \"#{File.expand_path(File.join(File.dirname(__FILE__), 'produce_output.rb'))}\" \"#{STANDARD_MESSAGE}\" \"#{ERROR_MESSAGE}\""
    runner = RightPopenSpec::Runner.new
    runner.run_right_popen(command)
    runner.status.exitstatus.should == 0
    runner.output_text.should == STANDARD_MESSAGE + "\n"
    runner.error_text.should == ERROR_MESSAGE + "\n"
    runner.pid.should > 0
  end

  it 'should return the right status' do
    command = "\"#{RUBY_CMD}\" \"#{File.expand_path(File.join(File.dirname(__FILE__), 'produce_status.rb'))}\" #{EXIT_STATUS}"
    runner = RightPopenSpec::Runner.new
    runner.run_right_popen(command)
    runner.status.exitstatus.should == EXIT_STATUS
    runner.output_text.should == ''
    runner.error_text.should == ''
    runner.pid.should > 0
  end

  it 'should correctly handle many small processes' do
    pending 'Set environment variable TEST_STRESS to enable' unless ENV['TEST_STRESS']
    1000.times do
      command = is_windows? ? "cmd.exe /c exit 0" : "exit 0"
      runner = RightPopenSpec::Runner.new
      runner.run_right_popen(command)
      runner.status.exitstatus.should == 0
      runner.output_text.should == ""
      runner.error_text.should == ''
      runner.pid.should > 0
    end
  end

  it 'should preserve the integrity of stdout when stderr is unavailable' do
    count = LARGE_OUTPUT_COUNTER
    command = "\"#{RUBY_CMD}\" \"#{File.expand_path(File.join(File.dirname(__FILE__), 'produce_stdout_only.rb'))}\" #{count}"
    runner = RightPopenSpec::Runner.new
    runner.run_right_popen(command)
{'fraction_non_alphanumeric': 0.11448417802932853, 'fraction_numerical': 0.005016722408026756, 'mean_word_length': 1.9926866820631255, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 33, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5362150', 'n_tokens_mistral': 2425, 'n_tokens_neox': 2371, 'n_words': 528}