Class: Flaky::Appium

Inherits:
Object
  • Object
show all
Includes:
POSIX::Spawn
Defined in:
lib/flaky/appium.rb

Overview

noinspection RubyResolve

Constant Summary collapse

@@thread =
nil

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(opts = {}) ⇒ Appium

android: true to activate Android mode



54
55
56
57
58
59
60
61
62
63
64
# File 'lib/flaky/appium.rb', line 54

def initialize opts={}
  @ready = false
  @pid, @in, @out, @err = nil
  @log = ''
  @buffer = ''
  @android = opts.fetch(:android, false)
  if @android
    @droid = Flaky::Android.new
    @logcat = Flaky::Logcat.new
  end
end

Instance Attribute Details

#errObject (readonly)

logcat is read & stopped by run.execute



26
27
28
# File 'lib/flaky/appium.rb', line 26

def err
  @err
end

#inObject (readonly)

logcat is read & stopped by run.execute



26
27
28
# File 'lib/flaky/appium.rb', line 26

def in
  @in
end

#logObject (readonly)

logcat is read & stopped by run.execute



26
27
28
# File 'lib/flaky/appium.rb', line 26

def log
  @log
end

#logcatObject (readonly)

logcat is read & stopped by run.execute



26
27
28
# File 'lib/flaky/appium.rb', line 26

def logcat
  @logcat
end

#outObject (readonly)

logcat is read & stopped by run.execute



26
27
28
# File 'lib/flaky/appium.rb', line 26

def out
  @out
end

#pidObject (readonly)

logcat is read & stopped by run.execute



26
27
28
# File 'lib/flaky/appium.rb', line 26

def pid
  @pid
end

#readyObject (readonly)

logcat is read & stopped by run.execute



26
27
28
# File 'lib/flaky/appium.rb', line 26

def ready
  @ready
end

Class Method Details

.kill_all(process_name) ⇒ Object



42
43
44
45
46
47
48
49
50
51
# File 'lib/flaky/appium.rb', line 42

def self.kill_all process_name
  _pid, _in, _out, _err = POSIX::Spawn::popen4('killall', '-9', process_name)
  raise "Unable to kill #{process_name}" unless _pid
  _in.close
  _out.read
  _err.read
ensure
  [_in, _out, _err].each { |io| io.close unless io.nil? || io.closed? }
  Process::waitpid(_pid) if _pid
end

.remove_ios_appsObject



29
30
31
32
33
34
35
36
37
38
39
40
# File 'lib/flaky/appium.rb', line 29

def self.remove_ios_apps
  user = ENV['USER']
  raise 'User must be defined' unless user

  # Must kill iPhone simulator or strange install errors will occur.
  self.kill_all 'iPhone Simulator'

  app_glob = "/Users/#{user}/Library/Application Support/iPhone Simulator/**/Applications/*"
  Dir.glob(app_glob) do |ios_app_folder|
    FileUtils.rm_rf ios_app_folder
  end
end

Instance Method Details

#end_all_instrumentsObject



141
142
143
# File 'lib/flaky/appium.rb', line 141

def end_all_instruments
  self.class.kill_all 'instruments'
end

#end_all_nodesObject



137
138
139
# File 'lib/flaky/appium.rb', line 137

def end_all_nodes
  self.class.kill_all 'node'
end

#flush_bufferObject



101
102
103
104
105
106
107
108
# File 'lib/flaky/appium.rb', line 101

def flush_buffer
  return if @buffer.nil? || @buffer.empty?
  File.open(@log, 'a') do |f|
    f.write EscapeUtils.escape_html @buffer
  end
  @buffer = ''
  @log
end

#launchObject

Invoked inside a thread by ‘self.go`



146
147
148
149
150
151
152
153
154
155
156
157
# File 'lib/flaky/appium.rb', line 146

def launch
  self.end_all_nodes
  @ready = false
  appium_home = ENV['APPIUM_HOME']
  raise "ENV['APPIUM_HOME'] must be set!" if appium_home.nil? || appium_home.empty?
  contains_appium = File.exists?(File.join(ENV['APPIUM_HOME'], 'bin', 'appium.js'))
  raise "Appium home `#{appium_home}` doesn't contain bin/appium.js!" unless contains_appium
  cmd = %Q(cd "#{appium_home}"; node .)
  @pid, @in, @out, @err = popen4 cmd
  @in.close
  self # used to chain `launch.wait`
end

#startObject



66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/flaky/appium.rb', line 66

def start
  self.stop # stop existing process
  @log = "/tmp/flaky/tmp_log_#{Random.rand(10**4..10**5-1)}"
  if @android
    @droid.reset
    @logcat.start
  else
    self.class.remove_ios_apps
  end

  @@thread.exit if @@thread
  @@thread = Thread.new do
    Thread.current.abort_on_exception = true
    self.launch.wait
  end

  while !self.ready
    sleep 0.5
  end

  # -e = -A = include other user's processes
  # -a = include your own processes
  # -x = include processes without a controlling terminal
  # ps -eax | grep "tail"
  # http://askubuntu.com/questions/157075/why-does-ps-aux-grep-x-give-better-results-than-pgrep-x
end

#stopObject



159
160
161
162
163
164
165
166
167
168
# File 'lib/flaky/appium.rb', line 159

def stop
  # https://github.com/tmm1/pygments.rb/blob/master/lib/pygments/popen.rb
  begin
    Process.kill 'KILL', @pid
  rescue
  end unless @pid.nil?
  @pid = nil
  self.end_all_nodes
  self.end_all_instruments unless @android
end

#update_buffer(data) ⇒ Object



93
94
95
96
97
98
99
# File 'lib/flaky/appium.rb', line 93

def update_buffer data
  @buffer += data

  if @buffer.length >= 32_000
    self.flush_buffer
  end
end

#waitObject

Internal methods



113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
# File 'lib/flaky/appium.rb', line 113

def wait
  out_err = [@out, @err]

  # https://github.com/rtomayko/posix-spawn/blob/1d498232660763ff0db6a2f0ab5c1c47fe593896/lib/posix/spawn/child.rb
  while out_err.any?
    io_array = IO.select out_err, [], out_err
    raise 'Appium never spawned' if io_array.nil?

    ready_for_reading = io_array[0]

    ready_for_reading.each do |stream|
      begin
        capture = stream.readpartial 999_999
        update_buffer(capture) if capture
        @ready = true if !@ready && capture.include?('Appium REST http interface listener started')
      rescue EOFError
        out_err.delete stream
        stream.close
      end
    end
  end
end