Class: Fastlane::Actions::InstrumentedTestsAction

Inherits:
Action
  • Object
show all
Defined in:
lib/fastlane/plugin/instrumented_tests/actions/instrumented_tests_action.rb

Class Method Summary collapse

Class Method Details

.authorsObject



230
231
232
# File 'lib/fastlane/plugin/instrumented_tests/actions/instrumented_tests_action.rb', line 230

def self.authors
  ["joshrlesch", "lexxdark"]
end

.available_optionsObject



151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
# File 'lib/fastlane/plugin/instrumented_tests/actions/instrumented_tests_action.rb', line 151

def self.available_options
  [
    FastlaneCore::ConfigItem.new(key: :avd_name,
                                 env_name: "AVD_NAME",
                                 description: "Name of the avd to be created",
                                 is_string: true,
                                 optional: false),
    FastlaneCore::ConfigItem.new(key: :target_id,
                                 env_name: "TARGET_ID",
                                 description: "Target id of the avd to be created, get list of installed target by running command 'android list targets'",
                                 is_string: true,
                                 optional: false),
    FastlaneCore::ConfigItem.new(key: :avd_options,
                                 env_name: "AVD_OPTIONS",
                                 description: "Other avd command line options passed to 'android create avd ...'. i.e. \"--scale 96dpi --dpi-device 160\"",
                                 is_string: true,
                                 optional: true),
    FastlaneCore::ConfigItem.new(key: :avd_abi,
                                 env_name: "AVD_ABI",
                                 description: "The ABI to use for the AVD. The default is to auto-select the ABI if the platform has only one ABI for its system images",
                                 is_string: true,
                                 optional: true),
    FastlaneCore::ConfigItem.new(key: :avd_tag,
                                 env_name: "AVD_TAG",
                                 description: "The sys-img tag to use for the AVD. The default is to auto-select if the platform has only one tag for its system images",
                                 is_string: true,
                                 optional: true),
    FastlaneCore::ConfigItem.new(key: :avd_port,
                                 env_name: "AVD_PORT",
                                 description: "The port used for communication with the emulator. If not set it is randomly selected",
                                 is_string: false,
                                 optional: true),
    FastlaneCore::ConfigItem.new(key: :boot_timeout,
                                 env_name: "BOOT_TIMEOUT",
                                 description: "Number of seconds to wait for the emulator to boot",
                                 is_string: false,
                                 optional: true,
                                 default_value: 500),
    FastlaneCore::ConfigItem.new(key: :avd_hide,
                                 env_name: "AVD_HIDE",
                                 description: "Hide the avd interface, required for CI. Default true if on CI, false if not on CI",
                                 is_string: false,
                                 optional: true),
    FastlaneCore::ConfigItem.new(key: :avd_emulator_options,
                                 env_name: "AVD_EMULATOR_OPTIONS",
                                 description: "Other options passed to the emulator command ('emulator -avd AVD_NAME ...')." +
                                     "Defaults are '-gpu on' when AVD_HIDE is false and '-no-window' otherwise. " +
                                     "For macs running the CI you might want to use '-no-audio -no-window'",
                                 is_string: true,
                                 optional: true),
    FastlaneCore::ConfigItem.new(key: :sdk_path,
                                 env_name: "ANDROID_HOME",
                                 description: "The path to your android sdk directory",
                                 is_string: true,
                                 default_value: ENV['ANDROID_HOME'],
                                 optional: true),
    FastlaneCore::ConfigItem.new(key: :flags,
                                 env_name: "FL_GRADLE_FLAGS",
                                 description: "All parameter flags you want to pass to the gradle command, e.g. `--exitcode --xml file.xml`",
                                 optional: true,
                                 is_string: true),
    FastlaneCore::ConfigItem.new(key: :task,
                                 env_name: "FL_GRADLE_TASK",
                                 description: "The gradle task you want to execute",
                                 is_string: true,
                                 optional: true,
                                 default_value: "connectedCheck"),
    FastlaneCore::ConfigItem.new(key: :project_dir,
                                 env_name: 'FL_GRADLE_PROJECT_DIR',
                                 description: 'The root directory of the gradle project. Defaults to `.`',
                                 default_value: '.',
                                 is_string: true)
  ]
end

.close_emulator_streams(params) ⇒ Object



130
131
132
# File 'lib/fastlane/plugin/instrumented_tests/actions/instrumented_tests_action.rb', line 130

def self.close_emulator_streams(params)
  @emulator_output.close
end

.create_emulator(params) ⇒ Object



52
53
54
55
56
57
58
59
60
61
# File 'lib/fastlane/plugin/instrumented_tests/actions/instrumented_tests_action.rb', line 52

def self.create_emulator(params)
  avd_name = "--name \"#{params[:avd_name]}\""
  target_id = "--target #{params[:target_id]}"
  avd_options = params[:avd_options] unless params[:avd_options].nil?
  avd_abi = "--abi #{params[:avd_abi]}" unless params[:avd_abi].nil?
  avd_tag = "--tag #{params[:avd_tag]}" unless params[:avd_tag].nil?
  create_avd = ["#{params[:sdk_path]}/tools/android", "create avd", avd_name, target_id, avd_abi, avd_tag, avd_options].join(" ")
  UI.important("Creating AVD...")
  Action.sh(create_avd)
end

.delete_old_emulators(params) ⇒ Object



44
45
46
47
48
49
50
# File 'lib/fastlane/plugin/instrumented_tests/actions/instrumented_tests_action.rb', line 44

def self.delete_old_emulators(params)
  devices = `#{params[:sdk_path]}/tools/android list avd`.chomp

  unless devices.match(/#{params[:avd_name]}/).nil?
    Action.sh("#{params[:sdk_path]}/tools/android delete avd -n #{params[:avd_name]}")
  end
end

.descriptionObject



139
140
141
# File 'lib/fastlane/plugin/instrumented_tests/actions/instrumented_tests_action.rb', line 139

def self.description
  "Run android instrumented tests via a gradle command againts a newly created avd"
end

.detailsObject



143
144
145
146
147
148
149
# File 'lib/fastlane/plugin/instrumented_tests/actions/instrumented_tests_action.rb', line 143

def self.details
  [
    "Instrumented tests need a emulator or real device to execute against.",
    "This action will check for a specific avd and created, wait for full boot,",
    "run gradle command, then deleted that avd on each run."
  ].join("\n")
end

.execute_gradle(params) ⇒ Object



134
135
136
137
# File 'lib/fastlane/plugin/instrumented_tests/actions/instrumented_tests_action.rb', line 134

def self.execute_gradle(params)
  Fastlane::Actions::GradleAction.run(task: params[:task], flags: params[:flags], project_dir: params[:project_dir],
                                      serial: @android_serial, print_command: true, print_command_output: true)
end

.is_supported?(platform) ⇒ Boolean

Returns:

  • (Boolean)


234
235
236
# File 'lib/fastlane/plugin/instrumented_tests/actions/instrumented_tests_action.rb', line 234

def self.is_supported?(platform)
  platform == :android
end


123
124
125
126
127
128
# File 'lib/fastlane/plugin/instrumented_tests/actions/instrumented_tests_action.rb', line 123

def self.print_emulator_output(params)
  UI.error("Error while trying to execute instrumentation tests. Output from emulator:")
  @emulator_output.readlines.each do |line|
    UI.error(line.gsub(/\r|\n/, " "))
  end
end

.return_valueObject



226
227
228
# File 'lib/fastlane/plugin/instrumented_tests/actions/instrumented_tests_action.rb', line 226

def self.return_value
  "The output from the test execution."
end

.run(params) ⇒ Object



10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# File 'lib/fastlane/plugin/instrumented_tests/actions/instrumented_tests_action.rb', line 10

def self.run(params)
  setup_parameters(params)
  delete_old_emulators(params)
  begin
    begin
      create_emulator(params)
      start_emulator(params)

      wait_emulator_boot(params)
      execute_gradle(params)
    ensure
      stop_emulator(params)
    end

  rescue Exception => e
    print_emulator_output(params)
    raise e
  ensure
    close_emulator_streams(params)
  end
end

.setup_parameters(params) ⇒ Object



32
33
34
35
36
37
38
39
40
41
42
# File 'lib/fastlane/plugin/instrumented_tests/actions/instrumented_tests_action.rb', line 32

def self.setup_parameters(params)
  # port must be an even integer number between 5554 and 5680
  params[:avd_port]=Random.rand(15)*2+5554 if params[:avd_port].nil?
  raise ":avd_port must be at least 5554" if params[:avd_port]<5554
  raise ":avd_port must be lower than 5584" if params[:avd_port]>5584
  raise ":avd_port must be an even number" if params[:avd_port]%2 != 0

  params[:avd_hide]=Helper.is_ci? if params[:avd_hide].nil?

  @android_serial="emulator-#{params[:avd_port]}"
end

.start_emulator(params) ⇒ Object



63
64
65
66
67
68
69
70
71
72
73
# File 'lib/fastlane/plugin/instrumented_tests/actions/instrumented_tests_action.rb', line 63

def self.start_emulator(params)
  UI.important("Starting AVD...")
  ui_args="-gpu on"
  ui_args="-no-window" if params[:avd_hide]
  ui_args=params[:avd_emulator_options] if params[:avd_emulator_options] != nil
  start_avd = ["#{params[:sdk_path]}/tools/emulator", "-avd #{params[:avd_name]}", "#{ui_args}", "-port #{params[:avd_port]}" ].join(" ")

  UI.command(start_avd)
  stdin, @emulator_output, @emulator_thread = Open3.popen2e(start_avd)
  stdin.close
end

.stop_emulator(params) ⇒ Object



103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# File 'lib/fastlane/plugin/instrumented_tests/actions/instrumented_tests_action.rb', line 103

def self.stop_emulator(params)
  begin
    UI.important("Shutting down emulator...")
    adb = Helper::AdbHelper.new(adb_path: "#{params[:sdk_path]}/platform-tools/adb")
    adb.trigger(command: "emu kill", serial: @android_serial)
  rescue
    UI.message("Emulator is not listening for our commands...")
    UI.message("Current status of emulator process is: #{@emulator_thread.status}")

    if @emulator_thread != nil && @emulator_thread.status != true && @emulator_thread.status != false
      UI.important("Emulator still running... Killing PID #{@emulator_thread.pid}!")
      Process.kill("KILL", @emulator_thread.pid)
    end

  end

  UI.important("Deleting emulator...")
  Action.sh("#{params[:sdk_path]}/tools/android delete avd -n #{params[:avd_name]}")
end

.wait_emulator_boot(params) ⇒ Object



75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/fastlane/plugin/instrumented_tests/actions/instrumented_tests_action.rb', line 75

def self.wait_emulator_boot(params)
  timeout = Time.now + params[:boot_timeout]
  UI.important("Waiting for emulator to finish booting... May take a few minutes...")

  adb_path = "#{params[:sdk_path]}/platform-tools/adb"
  raise "Unable to find adb in #{adb_path}" unless File.file?(adb_path)
  loop do
    boot_complete_cmd = "ANDROID_SERIAL=#{@android_serial} #{adb_path} shell getprop sys.boot_completed" 
    stdout, _stdeerr, _status = Open3.capture3(boot_complete_cmd)

    if @emulator_thread != nil && (@emulator_thread.status == false || @emulator_thread.status == true)
      UI.error("Emulator unexpectedly quit!")
      raise "Emulator unexpectedly quit"
    end

    if (Time.now > timeout)
      UI.error("Waited #{params[:boot_timeout]} seconds for emulator to boot without success")
      raise "Emulator didn't boot"
    end

    if stdout.strip == "1"
      UI.success("Emulator Booted!")
      break
    end
    sleep(1)
  end
end