class UIAutoMonkey::MonkeyRunner

Constants

RESULT_BASE_PATH
RESULT_DETAIL_EVENT_NUM
TIME_LIMIT_SEC
TRACE_TEMPLATE

Public Instance Methods

all_tests_ok?(result_list) click to toggle source
# File lib/crash_monkey/monkey_runner.rb, line 119
def all_tests_ok?(result_list)
  result_list.select {|r| !r[:ok]}.empty?
end
app_name() click to toggle source
# File lib/crash_monkey/monkey_runner.rb, line 163
def app_name
  File.basename(app_path).gsub(/\.app$/, '')
end
app_path() click to toggle source
# File lib/crash_monkey/monkey_runner.rb, line 159
def app_path
  @app_path ||= find_app_path(@options)
end
config_json_path() click to toggle source
# File lib/crash_monkey/monkey_runner.rb, line 250
def config_json_path
  @options[:config_path] || template_path('config.json')
end
console_log_path() click to toggle source
# File lib/crash_monkey/monkey_runner.rb, line 230
def console_log_path
  "#{result_dir}/console.log"
end
copy_html_resources() click to toggle source
# File lib/crash_monkey/monkey_runner.rb, line 113
def copy_html_resources
  bootstrap_dir = File.expand_path('../../bootstrap', __FILE__)
  FileUtils.copy("#{bootstrap_dir}/css/bootstrap.css", result_base_dir)
  FileUtils.copy("#{bootstrap_dir}/js/bootstrap.js", result_base_dir)
end
crash_report_dir() click to toggle source
# File lib/crash_monkey/monkey_runner.rb, line 218
def crash_report_dir
  "#{ENV['HOME']}/Library/Logs/DiagnosticReports"
end
crash_report_list() click to toggle source
# File lib/crash_monkey/monkey_runner.rb, line 222
def crash_report_list
  `ls -t #{crash_report_dir}/'#{app_name}'_*.crash`.strip.split(/\n/)
end
create_index_html(result_hash) click to toggle source
# File lib/crash_monkey/monkey_runner.rb, line 104
def create_index_html(result_hash)
  er = Erubis::Eruby.new(File.read(template_path('index.html.erb')))
  result_hash[:test_count] = result_hash[:result_list].size
  result_hash[:ok_count] = result_hash[:result_list].select {|r| r[:ok]}.size
  result_hash[:ng_count] = result_hash[:test_count] - result_hash[:ok_count]
  open("#{result_base_dir}/index.html", 'w') {|f| f.write er.result(result_hash)}
  copy_html_resources
end
create_result_html(log_list) click to toggle source
# File lib/crash_monkey/monkey_runner.rb, line 284
def create_result_html(log_list)
  latest_list = LogDecoder.new(log_list).decode_latest(RESULT_DETAIL_EVENT_NUM)
  hash = {}
  hash[:log_list] = latest_list.reverse
  hash[:log_list_json] = JSON.dump(hash[:log_list])
  crash_report = Dir.glob("#{result_dir}/*.crash")[0]
  hash[:crash_report] = crash_report ? File.basename(crash_report) : nil
  hash[:crashed] = @crashed

  er = Erubis::Eruby.new(File.read(template_path('result.html.erb')))
  open("#{result_dir}/result.html", 'w') do |f|
    f.write(er.result(hash))
  end
  FileUtils.copy(template_path('result_view.js'), "#{result_dir}/result_view.js")
end
device() click to toggle source
# File lib/crash_monkey/monkey_runner.rb, line 155
def device
  @options[:device] || devices[0]
end
devices() click to toggle source
# File lib/crash_monkey/monkey_runner.rb, line 171
def devices
  `"instruments" -s devices`.strip.split(/\n/).drop(2)
end
find_app_path(opts) click to toggle source
# File lib/crash_monkey/monkey_runner.rb, line 175
def find_app_path(opts)
  app_path = nil
  if opts[:app_path].include?('/')
    app_path = File.expand_path(opts[:app_path])
  elsif opts[:app_path] =~ /\.app$/
    apps = find_apps(opts[:app_path])
    app_path = apps[0]
    log "#{apps.size} apps are found, USE NEWEST APP: #{app_path}" if apps.size > 1
  end
  unless app_path
    raise 'Invalid AppName'
  end
  app_path
end
find_apps(app) click to toggle source
# File lib/crash_monkey/monkey_runner.rb, line 167
def find_apps(app)
  `"ls" -dt #{ENV['HOME']}/Library/Developer/Xcode/DerivedData/*/Build/Products/*/'#{app}'`.strip.split(/\n/)
end
finish_running(result) click to toggle source
# File lib/crash_monkey/monkey_runner.rb, line 98
def finish_running(result)
  FileUtils.remove_dir(result_history_dir(@times), true)
  FileUtils.move(result_dir, result_history_dir(@times))
  kill_all('iPhone Simulator')
end
generate_ui_auto_monkey() click to toggle source
# File lib/crash_monkey/monkey_runner.rb, line 238
def generate_ui_auto_monkey
  extend_javascript_flag, extend_javascript_path =  show_extend_javascript
  orig = File.read(ui_auto_monkey_original_path)
  config = JSON.parse(File.read(config_json_path))
  replace_str = "    this.config = #{JSON.pretty_generate(config, :indent => ' '*6)}; \n"
  js = replace_text(orig, replace_str, '__UIAutoMonkey Configuration Begin__', '__UIAutoMonkey Configuration End__')
  if extend_javascript_flag
    js = File.read(extend_javascript_path) + "\n" + js
  end
  File.open(ui_auto_monkey_path, 'w') {|f| f.write(js)}
end
grep_syslog() click to toggle source
# File lib/crash_monkey/monkey_runner.rb, line 226
def grep_syslog
  'tail -n 0 -f /var/log/system.log'
end
list_app() click to toggle source
# File lib/crash_monkey/monkey_runner.rb, line 134
def list_app
  puts find_apps('*.app').map{|n| File.basename n}.uniq.sort.join("\n")
end
list_devices() click to toggle source
# File lib/crash_monkey/monkey_runner.rb, line 138
def list_devices
  puts devices.join("\n")
end
log(msg) click to toggle source
# File lib/crash_monkey/monkey_runner.rb, line 142
def log(msg)
  puts msg
end
parse_results() click to toggle source
# File lib/crash_monkey/monkey_runner.rb, line 270
def parse_results
  filename = "#{result_dir}/Automation Results.plist"
  log_list = []
  if File.exists?(filename)
    doc = REXML::Document.new(open(filename))
    doc.elements.each('plist/dict/array/dict') do |record|
      ary = record.elements.to_a.map{|a| a.text}
      log_list << Hash[*ary]
    end
    @crashed = true if log_list[-1][LOG_TYPE] == 'Fail'
  end
  log_list
end
replace_text(orig, replace_str, marker_begin_line, marker_end_line) click to toggle source
# File lib/crash_monkey/monkey_runner.rb, line 254
def replace_text(orig, replace_str, marker_begin_line, marker_end_line)
  results = []
  status = 1
  orig.each_line do |line|
    if status == 1 && line =~ /#{marker_begin_line}/
      status = 2
      results << line
      results << replace_str
    elsif status == 2 && line =~/#{marker_end_line}/
      status = 3
    end
    results << line unless status == 2
  end
  results.join('')
end
reset_iphone_simulator() click to toggle source
# File lib/crash_monkey/monkey_runner.rb, line 146
def reset_iphone_simulator
  FileUtils.rm_rf("#{Dir.home}/Library/Application\ Support/iPhone\ Simulator/")
  puts 'reset iPhone Simulator successful'
end
result_base_dir() click to toggle source
# File lib/crash_monkey/monkey_runner.rb, line 206
def result_base_dir
  @options[:result_base_dir] || RESULT_BASE_PATH
end
result_dir() click to toggle source
# File lib/crash_monkey/monkey_runner.rb, line 210
def result_dir
  "#{result_base_dir}/Run 1"
end
result_history_dir(times) click to toggle source
# File lib/crash_monkey/monkey_runner.rb, line 214
def result_history_dir(times)
  "#{result_base_dir}/result_#{sprintf('%03d', times)}"
end
run(opts) click to toggle source
# File lib/crash_monkey/monkey_runner.rb, line 18
def run(opts)
  @options = opts
  if @options[:show_config]
    show_config
    return true
  elsif @options[:list_app]
    list_app
    return true
  elsif @options[:list_devices]
    list_devices
    return true
  elsif @options[:reset_iphone_simulator]
    reset_iphone_simulator
    return true
  end
  ###########
  log @options.inspect
  FileUtils.remove_dir(result_base_dir, true)
  FileUtils.makedirs(result_base_dir)
  generate_ui_auto_monkey
  ###########
  start_time = Time.now
  result_list = []
  total_test_count.times do |times|
    @times = times
    setup_running
    result = run_a_case
    finish_running(result)
    result_list << result
  end
  #
  create_index_html({
      :start_time => start_time,
      :end_time => Time.now,
      :result_list => result_list,
  })
  all_tests_ok?(result_list)
end
run_a_case() click to toggle source
# File lib/crash_monkey/monkey_runner.rb, line 64
def run_a_case
  log "=================================== Start Test (#{@times+1}/#{total_test_count}) ======================================="
  cr_list = crash_report_list
  start_time = Time.now
  watch_syslog do
    begin
      Timeout.timeout(time_limit_sec + 5) do
        run_process(%W(instruments -w #{device} -l #{time_limit} -t #{TRACE_TEMPLATE} #{app_path} -e UIASCRIPT #{ui_auto_monkey_path} -e UIARESULTSPATH #{result_base_dir}))
      end
    rescue Timeout::Error
      log 'killall -9 instruments'
      kill_all('instruments', '9')
    end
  end
  new_cr_list = crash_report_list
  # increase crash report?
  unless cr_list[0] == new_cr_list[0]
    @crashed = true
    log "Find new crash report: #{new_cr_list[0]}"
    FileUtils.copy(new_cr_list[0], result_dir)
  end
  # output result
  create_result_html(parse_results)

  {
    :start_time => start_time,
    :end_time => Time.now,
    :times => @times,
    :ok => !@crashed,
    :result_dir => File.basename(result_history_dir(@times)),
    :message => nil
  }
end
setup_running() click to toggle source
# File lib/crash_monkey/monkey_runner.rb, line 57
def setup_running
  # kill_all('iPhone Simulator')
  FileUtils.remove_dir(result_dir, true)
  ENV['UIARESULTSPATH'] = result_dir
  @crashed = false
end
show_config() click to toggle source
# File lib/crash_monkey/monkey_runner.rb, line 123
def show_config
  puts File.read(config_json_path)
end
show_extend_javascript() click to toggle source
# File lib/crash_monkey/monkey_runner.rb, line 127
def show_extend_javascript
  if @options[:extend_javascript_path]
    filename = @options[:extend_javascript_path]
    return File.exist?(filename), filename
  end
end
template_path(name) click to toggle source
# File lib/crash_monkey/monkey_runner.rb, line 234
def template_path(name)
  File.expand_path("../templates/#{name}", __FILE__)
end
time_limit() click to toggle source
# File lib/crash_monkey/monkey_runner.rb, line 190
def time_limit
  time_limit_sec * 1000
end
time_limit_sec() click to toggle source
# File lib/crash_monkey/monkey_runner.rb, line 194
def time_limit_sec
  (@options[:time_limit_sec] || TIME_LIMIT_SEC).to_i
end
total_test_count() click to toggle source
# File lib/crash_monkey/monkey_runner.rb, line 151
def total_test_count
  (@options[:run_count] || 2)
end
ui_auto_monkey_original_path() click to toggle source
# File lib/crash_monkey/monkey_runner.rb, line 198
def ui_auto_monkey_original_path
  File.expand_path('../../ui-auto-monkey/UIAutoMonkey.js', __FILE__)
end
ui_auto_monkey_path() click to toggle source
# File lib/crash_monkey/monkey_runner.rb, line 202
def ui_auto_monkey_path
  "#{result_base_dir}/UIAutoMonkey.js"
end
watch_syslog() { || ... } click to toggle source
# File lib/crash_monkey/monkey_runner.rb, line 300
def watch_syslog
  STDOUT.sync = true
  stdin, stdout, stderr = Open3.popen3(grep_syslog)
  log_filename = "#{result_base_dir}/console.log"
  thread = Thread.new do
    File.open(log_filename, 'a') do |output|
      begin
        while true
          line = stdout.readline
          output.write(line) if line.include?(app_name)
        end
      rescue IOError
        log 'tail finished: system.log'
      end
    end
  end
  yield
  sleep 3
  stdout.close; stderr.close; stdin.close
  thread.join
  FileUtils.makedirs(result_dir) unless File.exists?(result_dir)
  if File.exists?(log_filename)
    FileUtils.move(log_filename, console_log_path)
  end
end