<commit_before>require 'rubygems'
require 'fileutils'
require 'find'
require 'pathname'
require 'json'

require File.join(File.expand_path(File.dirname(__FILE__)), 'InstrumentsRunner.rb')
require File.join(File.expand_path(File.dirname(__FILE__)), 'JavascriptRunner.rb')
require File.join(File.expand_path(File.dirname(__FILE__)), 'HostUtils.rb')
require File.join(File.expand_path(File.dirname(__FILE__)), 'XcodeUtils.rb')
require File.join(File.expand_path(File.dirname(__FILE__)), 'BuildArtifacts.rb')
require File.join(File.expand_path(File.dirname(__FILE__)), 'TestSuite.rb')
require File.join(File.expand_path(File.dirname(__FILE__)), 'TestDefinitions.rb')

require File.join(File.expand_path(File.dirname(__FILE__)), 'listeners/PrettyOutput.rb')
require File.join(File.expand_path(File.dirname(__FILE__)), 'listeners/FullOutput.rb')
require File.join(File.expand_path(File.dirname(__FILE__)), 'listeners/ConsoleLogger.rb')
require File.join(File.expand_path(File.dirname(__FILE__)), 'listeners/TestListener.rb')
require File.join(File.expand_path(File.dirname(__FILE__)), 'listeners/SaltinelAgent.rb')

####################################################################################################
# runner
####################################################################################################


# responsibilities:
#  - apply options to relevant components
#  - prepare javascript config, and start instruments
#  - process any crashes
#  - run coverage
class AutomationRunner
  include SaltinelAgentEventSink
  include TestListenerEventSink

  attr_accessor :appName
  attr_accessor :workspace
  attr_accessor :appLocation

  attr_reader :instrumentsRunner
  attr_reader :javascriptRunner

  def initialize
    @testDefs          = nil
    @testSuite         = nil
    @currentTest       = nil
    @restartedTests    = nil
    @stackTraceLines   = nil
    @stackTraceRecord  = false
    @appCrashed        = false
    @javascriptRunner  = JavascriptRunner.new
    @instrumentsRunner = InstrumentsRunner.new

    @instrumentsRunner.addListener("consolelogger", ConsoleLogger.new)
  end


  def cleanup
    # start a list of what to remove
    dirsToRemove = []

    # FIXME: this should probably get moved to instrument runner
    # keys to the methods of the BuildArtifacts singleton that we want to remove
    buildArtifactKeys = [:crashReports, :instruments, :objectFiles, :coverageReportFile,
                         :junitReportFile, :illuminatorJsRunner, :illuminatorJsEnvironment, :illuminatorConfigFile]
    # get the directories without creating them (the 'true' arg), add them to our list
    buildArtifactKeys.each do |key|
      dir = BuildArtifacts.instance.method(key).call(true)
      dirsToRemove << dir
    end

    # remove directories in the list
    dirsToRemove.each do |d|
      dir = HostUtils.realpath d
      puts "AutomationRunner cleanup: removing #{dir}"
      FileUtils.rmtree dir
    end

    # run cleanups for variables we own
    @instrumentsRunner.cleanup
    # TODO: @javascriptRunner cleanup?

  end


  def saltinelAgentGotScenarioDefinitions jsonPath
    return unless @testDefs.nil?
    @testDefs = TestDefinitions.new jsonPath
  end


  def saltinelAgentGotScenarioList jsonPath
    return unless @testSuite.nil?
    @restartedTests = {}
    rawList = JSON.parse( IO.read(jsonPath) )

    # create a test suite, and add test cases to it.  look up class names from test defs
    @testSuite = TestSuite.new(@implementation)
    rawList["scenarioNames"].each do |n|
      test = @testDefs.byName(n)
      testFileName = test["inFile"]
      testFnName   = test["definedBy"]
      className    = testFileName.sub(".", "_") + "." + testFnName
      @testSuite.addTestCase(className, n)
    end
    self.saveJunitTestReport
  end

  def saltinelAgentGotRestartRequest
    puts "ILLUMINATOR FAILURE TO ORGANIZE".red if @testSuite.nil?
    puts "ILLUMINATOR FAILURE TO ORGANIZE 2".red if @currentTest.nil?
    if @restartedTests[@currentTest]
      puts "Denying restart request for previously-restarted scenario '#{@currentTest}'".yellow
    else
      @testSuite[@currentTest].reset!
      @restartedTests[@currentTest] = true
      @currentTest = nil
      @instrumentsRunner.forceStop "Got restart request"
    end
  end

  def saltinelAgentGotStacktraceHint
    @stackTraceRecord = true
  end


  def testListenerGotTestStart name
    @testSuite[@currentTest].error "ILLUMINATOR FAILURE TO LISTEN" unless @currentTest.nil?
    @testSuite[name].start!
    @currentTest = name
    @stackTraceRecord = false
    @stackTraceLines = Array.new
  end

  def testListenerGotTestPass name
    puts "ILLUMINATOR FAILURE TO SORT TESTS".red unless name == @currentTest
    @testSuite[name].pass!
    @currentTest = nil
    self.saveJunitTestReport
  end

  def testListenerGotTestFail message
    if @testSuite.nil?
      puts "Failure before test suite was received: #{message}".red
      return
    elsif @currentTest.nil?
      puts "Failure outside of a test: #{message}".red
    elsif message == "The target application appears to have died"
      @testSuite[@currentTest].error message
      @appCrashed = true
      # The test runner loop will take it from here
    else
      @testSuite[@currentTest].fail message<commit_msg>Prevent tests from staying marked as failed after being rerun<commit_after>require 'rubygems'
require 'fileutils'
require 'find'
require 'pathname'
require 'json'

require File.join(File.expand_path(File.dirname(__FILE__)), 'InstrumentsRunner.rb')
require File.join(File.expand_path(File.dirname(__FILE__)), 'JavascriptRunner.rb')
require File.join(File.expand_path(File.dirname(__FILE__)), 'HostUtils.rb')
require File.join(File.expand_path(File.dirname(__FILE__)), 'XcodeUtils.rb')
require File.join(File.expand_path(File.dirname(__FILE__)), 'BuildArtifacts.rb')
require File.join(File.expand_path(File.dirname(__FILE__)), 'TestSuite.rb')
require File.join(File.expand_path(File.dirname(__FILE__)), 'TestDefinitions.rb')

require File.join(File.expand_path(File.dirname(__FILE__)), 'listeners/PrettyOutput.rb')
require File.join(File.expand_path(File.dirname(__FILE__)), 'listeners/FullOutput.rb')
require File.join(File.expand_path(File.dirname(__FILE__)), 'listeners/ConsoleLogger.rb')
require File.join(File.expand_path(File.dirname(__FILE__)), 'listeners/TestListener.rb')
require File.join(File.expand_path(File.dirname(__FILE__)), 'listeners/SaltinelAgent.rb')

####################################################################################################
# runner
####################################################################################################


# responsibilities:
#  - apply options to relevant components
#  - prepare javascript config, and start instruments
#  - process any crashes
#  - run coverage
class AutomationRunner
  include SaltinelAgentEventSink
  include TestListenerEventSink

  attr_accessor :appName
  attr_accessor :workspace
  attr_accessor :appLocation

  attr_reader :instrumentsRunner
  attr_reader :javascriptRunner

  def initialize
    @testDefs          = nil
    @testSuite         = nil
    @currentTest       = nil
    @restartedTests    = nil
    @stackTraceLines   = nil
    @stackTraceRecord  = false
    @appCrashed        = false
    @javascriptRunner  = JavascriptRunner.new
    @instrumentsRunner = InstrumentsRunner.new

    @instrumentsRunner.addListener("consolelogger", ConsoleLogger.new)
  end


  def cleanup
    # start a list of what to remove
    dirsToRemove = []

    # FIXME: this should probably get moved to instrument runner
    # keys to the methods of the BuildArtifacts singleton that we want to remove
    buildArtifactKeys = [:crashReports, :instruments, :objectFiles, :coverageReportFile,
                         :junitReportFile, :illuminatorJsRunner, :illuminatorJsEnvironment, :illuminatorConfigFile]
    # get the directories without creating them (the 'true' arg), add them to our list
    buildArtifactKeys.each do |key|
      dir = BuildArtifacts.instance.method(key).call(true)
      dirsToRemove << dir
    end

    # remove directories in the list
    dirsToRemove.each do |d|
      dir = HostUtils.realpath d
      puts "AutomationRunner cleanup: removing #{dir}"
      FileUtils.rmtree dir
    end

    # run cleanups for variables we own
    @instrumentsRunner.cleanup
    # TODO: @javascriptRunner cleanup?

  end


  def saltinelAgentGotScenarioDefinitions jsonPath
    return unless @testDefs.nil?
    @testDefs = TestDefinitions.new jsonPath
  end


  def saltinelAgentGotScenarioList jsonPath
    return unless @testSuite.nil?
    @restartedTests = {}
    rawList = JSON.parse( IO.read(jsonPath) )

    # create a test suite, and add test cases to it.  look up class names from test defs
    @testSuite = TestSuite.new(@implementation)
    rawList["scenarioNames"].each do |n|
      test = @testDefs.byName(n)
      testFileName = test["inFile"]
      testFnName   = test["definedBy"]
      className    = testFileName.sub(".", "_") + "." + testFnName
      @testSuite.addTestCase(className, n)
    end
    self.saveJunitTestReport
  end

  def saltinelAgentGotRestartRequest
    puts "ILLUMINATOR FAILURE TO ORGANIZE".red if @testSuite.nil?
    puts "ILLUMINATOR FAILURE TO ORGANIZE 2".red if @currentTest.nil?
    if @restartedTests[@currentTest]
      puts "Denying restart request for previously-restarted scenario '#{@currentTest}'".yellow
    else
      @testSuite[@currentTest].reset!
      @restartedTests[@currentTest] = true
      @currentTest = nil
      @instrumentsRunner.forceStop "Got restart request"
    end
  end

  def saltinelAgentGotStacktraceHint
    @stackTraceRecord = true
  end


  def testListenerGotTestStart name
    @testSuite[@currentTest].error "ILLUMINATOR FAILURE TO LISTEN" unless @currentTest.nil?
    @testSuite[name].reset!
    @testSuite[name].start!
    @currentTest = name
    @stackTraceRecord = false
    @stackTraceLines = Array.new
  end

  def testListenerGotTestPass name
    puts "ILLUMINATOR FAILURE TO SORT TESTS".red unless name == @currentTest
    @testSuite[name].pass!
    @currentTest = nil
    self.saveJunitTestReport
  end

  def testListenerGotTestFail message
    if @testSuite.nil?
      puts "Failure before test suite was received: #{message}".red
      return
    elsif @currentTest.nil?
      puts "Failure outside of a test: #{message}".red
    elsif message == "The target application appears to have died"
      @testSuite[@currentTest].error message
      @appCrashed = true
      # The test runner loop will take it from here
    else
      @testSuite[@currentTest].fail message
{'fraction_non_alphanumeric': 0.12685446009389673, 'fraction_numerical': 0.00018779342723004695, 'mean_word_length': 4.120673076923077, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 3, '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': '110714', 'n_tokens_mistral': 3143, 'n_tokens_neox': 3017, 'n_words': 767}