fastlane-plugin-automated_test_emulator_run 0.5.16 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +56 -18
- data/lib/fastlane/plugin/automated_test_emulator_run.rb +3 -4
- data/lib/fastlane/plugin/automated_test_emulator_run/actions/automated_test_emulator_run_action.rb +182 -218
- data/lib/fastlane/plugin/automated_test_emulator_run/factory/adb_controller_factory.rb +55 -0
- data/lib/fastlane/plugin/automated_test_emulator_run/factory/avd_controller_factory.rb +109 -0
- data/lib/fastlane/plugin/automated_test_emulator_run/provider/avd_setup_provider.rb +167 -0
- data/lib/fastlane/plugin/automated_test_emulator_run/version.rb +1 -1
- metadata +8 -6
- data/lib/fastlane/plugin/automated_test_emulator_run/helper/automated_test_emulator_run_helper.rb +0 -12
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5719e5040624f349907cc2c01d6c7030c3827d01
|
4
|
+
data.tar.gz: 061d66711e351773c997e350f7c3b53c7326a0bc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 115cbc0163872401a1c40a69d2a1d17a8b96f5c9933cfee62015a4d3c9cb1273cb8ac4bf6f45115b0d0a546d99b87d72d0428de98021c1fa81a4e91df177a17c
|
7
|
+
data.tar.gz: a7fb87f8726d0abd1ff80deb01403227894953eee782efd78e64ed4ee38d0c89933f69c7f9fa04b53d394e643837204dea70ede1e5c6b6c543676b9d75a76382
|
data/README.md
CHANGED
@@ -2,39 +2,77 @@
|
|
2
2
|
|
3
3
|
[![fastlane Plugin Badge](https://rawcdn.githack.com/fastlane/fastlane/master/fastlane/assets/plugin-badge.svg)](https://rubygems.org/gems/fastlane-plugin-automated_test_emulator_run)
|
4
4
|
|
5
|
-
##
|
5
|
+
## About automated_test_emulator_run
|
6
6
|
|
7
|
-
|
7
|
+
Starts any number of AVDs. AVDs are created and configured automatically according to user liking before instrumentation test process starts (started either via shell command or from gradle) and killed/deleted after test process finishes.
|
8
8
|
|
9
|
-
|
10
|
-
fastlane add_plugin automated_test_emulator_run
|
11
|
-
```
|
9
|
+
## Getting Started
|
12
10
|
|
13
|
-
|
11
|
+
This project is a [fastlane](https://github.com/fastlane/fastlane) plugin.
|
14
12
|
|
15
|
-
|
13
|
+
1. To get started with `fastlane-plugin-automated_test_emulator_run`, add it to your project by running:
|
16
14
|
|
17
|
-
|
15
|
+
```bash
|
16
|
+
fastlane add_plugin automated_test_emulator_run
|
17
|
+
```
|
18
|
+
2. Create your \*.JSON config file to create AVD launch plan according to schema below/provided example.
|
18
19
|
|
19
|
-
|
20
|
+
3. Wrap your test launch command with plugin and provide link to \*.JSON config.
|
20
21
|
|
21
|
-
|
22
|
+
## Example of Fastfile
|
22
23
|
|
23
|
-
|
24
|
+
Check out the [example `Fastfile`](fastlane/Fastfile) to see how to use this plugin. Try it by cloning the repo, running `fastlane install_plugins` and `bundle exec fastlane test`.
|
24
25
|
|
25
|
-
##
|
26
|
+
## JSON config
|
26
27
|
|
27
|
-
|
28
|
+
What is JSON config?
|
28
29
|
|
29
|
-
|
30
|
-
rake
|
31
|
-
```
|
30
|
+
It is a core of this plugin. User can specify any number of AVD devices in JSON file. Each AVD can be configured separately. Plugin will read JSON file and create fresh, new, untouched AVDs on host - use them in tests - and then delete them after test process finishes.
|
32
31
|
|
33
|
-
|
32
|
+
JSON file scheme:
|
34
33
|
```
|
35
|
-
|
34
|
+
{
|
35
|
+
"avd_list":
|
36
|
+
[
|
37
|
+
{
|
38
|
+
"avd_name": "",
|
39
|
+
|
40
|
+
"create_avd_target": "",
|
41
|
+
"create_avd_abi": "",
|
42
|
+
"create_avd_hardware_config_filepath": "",
|
43
|
+
"create_avd_additional_options": "",
|
44
|
+
|
45
|
+
"launch_avd_snapshot_filepath": "",
|
46
|
+
"launch_avd_launch_binary_name": "",
|
47
|
+
"launch_avd_port": "",
|
48
|
+
"launch_avd_additional_options": ""
|
49
|
+
}
|
50
|
+
]
|
51
|
+
}
|
36
52
|
```
|
37
53
|
|
54
|
+
Parameters:
|
55
|
+
- `avd_name` - name of your AVD, avoid using spaces, this file is necessary
|
56
|
+
- `create_avd_target` - Android target api level (https://developer.android.com/guide/topics/manifest/uses-sdk-element.html)
|
57
|
+
- `create_avd_abi` - CPU architecture used by AVD (https://developer.android.com/ndk/guides/abis.html)
|
58
|
+
- `create_avd_hardware_config_filepath` - path to config.ini file containing custom config for your AVD device. After AVD is created this file will be copied into AVD location before it launches.
|
59
|
+
- `create_avd_additional_options` - if you think that you need something more you can just add your create parameters here (e.g. "--sdcard 128M", https://developer.android.com/studio/tools/help/android.html)
|
60
|
+
- `launch_avd_snapshot_filepath` - plugin will delete and re-create AVD before test start. That means all your permissions and settings will be lost on each emulator run. If you want to apply qemu image with saved AVD state you can put path to it in this field. It will be applied by using "-wipe-data -initdata <path to your file>"
|
61
|
+
- `launch_avd_launch_binary_name` - depending on your CPU architecture you need to choose binary file which should launch your AVD (e.g. "emulator", "emulator64-arm", "emulator64-x86")
|
62
|
+
- `launch_avd_port` - port on which you wish your AVD should be launched, if you leave this field empty it will be assigned automatically
|
63
|
+
- `launch_avd_additional_options` - if you need more customization add your parameters here (e.g. "-gpu on -no-boot-anim -no-window", https://developer.android.com/studio/run/emulator-commandline.html)
|
64
|
+
|
65
|
+
Hints:
|
66
|
+
|
67
|
+
- all fields need to be present in JSON, if you don't need any of the parameters just leave it empty
|
68
|
+
- pick even ports for your AVDs
|
69
|
+
- if you can't launch more than 2 AVDs be sure to check how much memory is your HAXM allowed to use (by default it is 2GB and that will allow you to launch around 2 AVDs) If you face any problems with freezing AVDs then be sure to reinstall your HAXM and allow it to use more of RAM (https://software.intel.com/en-us/android/articles/intel-hardware-accelerated-execution-manager)
|
70
|
+
- make sure you have all targets/abis installed on your PC if you want to use them (type in terminal: `android list targets`)
|
71
|
+
|
72
|
+
Example:
|
73
|
+
|
74
|
+
[Example of complete JSON file can be found here.](fastlane/examples/AVD_setup.json)
|
75
|
+
|
38
76
|
## Issues and Feedback
|
39
77
|
|
40
78
|
For any other issues and feedback about this plugin, please submit it to this repository.
|
@@ -2,15 +2,14 @@ require 'fastlane/plugin/automated_test_emulator_run/version'
|
|
2
2
|
|
3
3
|
module Fastlane
|
4
4
|
module AutomatedTestEmulatorRun
|
5
|
-
# Return all .rb files inside the "actions" and "helper" directory
|
6
5
|
def self.all_classes
|
7
|
-
Dir[File.expand_path('**/{actions,
|
6
|
+
Dir[File.expand_path('**/{actions,factory,provider}/*.rb', File.dirname(__FILE__))]
|
8
7
|
end
|
9
8
|
end
|
10
9
|
end
|
11
10
|
|
12
|
-
# By default we want to import all available actions and helpers
|
13
|
-
# A plugin can contain any number of actions and plugins
|
14
11
|
Fastlane::AutomatedTestEmulatorRun.all_classes.each do |current|
|
15
12
|
require current
|
16
13
|
end
|
14
|
+
|
15
|
+
|
data/lib/fastlane/plugin/automated_test_emulator_run/actions/automated_test_emulator_run_action.rb
CHANGED
@@ -1,268 +1,232 @@
|
|
1
1
|
require 'open3'
|
2
|
-
require '
|
2
|
+
require 'json'
|
3
3
|
|
4
4
|
module Fastlane
|
5
5
|
module Actions
|
6
|
-
module SharedValues
|
7
|
-
end
|
8
6
|
|
9
|
-
|
7
|
+
class AutomatedTestEmulatorRunAction < Action
|
8
|
+
def self.run(params)
|
9
|
+
UI.message("The automated_test_emulator_run plugin is working!")
|
10
10
|
|
11
|
-
|
12
|
-
|
11
|
+
# Parse JSON with AVD launch confing to array of AVD_scheme objects
|
12
|
+
avd_schemes = Provider::AvdSchemeProvider.get_avd_schemes(params)
|
13
13
|
|
14
|
-
|
14
|
+
# ADB, AVD helper classes
|
15
|
+
adb_controller = Factory::AdbControllerFactory.get_adb_controller(params)
|
16
|
+
avd_controllers = []
|
15
17
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
else
|
22
|
-
port = "#{params[:avd_port]}"
|
23
|
-
UI.message(["Port set by user to:", port].join(" ").yellow)
|
24
|
-
end
|
25
|
-
|
26
|
-
# Set up params
|
27
|
-
UI.message("Preparing parameters...".yellow)
|
28
|
-
|
29
|
-
# AVD general params
|
30
|
-
sdkRoot = "#{params[:sdk_path]}"
|
31
|
-
avd_name = "--name \"#{params[:avd_name]}\""
|
32
|
-
|
33
|
-
# AVD create params
|
34
|
-
target_id = "--target \"#{params[:target_id]}\""
|
35
|
-
avd_abi = "--abi #{params[:avd_abi]}" unless params[:avd_abi].nil?
|
36
|
-
avd_tag = "--tag #{params[:avd_tag]}" unless params[:avd_tag].nil?
|
37
|
-
avd_create_options = params[:avd_create_options] unless params[:avd_create_options].nil?
|
38
|
-
|
39
|
-
# AVD start params
|
40
|
-
emulator_binary = "#{params[:emulator_binary]}"
|
41
|
-
avd_initdata = "-wipe-data -initdata #{params[:initdata_snapshot_path]}" unless params[:initdata_snapshot_path].nil?
|
42
|
-
avd_port = ["-port", port].join(" ")
|
43
|
-
avd_start_options = params[:avd_start_options] unless params[:avd_start_options].nil?
|
44
|
-
|
45
|
-
# Set up commands
|
46
|
-
UI.message("Setting up run commands".yellow)
|
47
|
-
create_avd_command = ["echo \"no\" |", sdkRoot + "/tools/android", "create avd", avd_name, target_id, avd_abi, avd_tag, avd_create_options].join(" ")
|
48
|
-
get_devices_command = sdkRoot + "/tools/android list avd".chomp
|
49
|
-
start_avd_command = [sdkRoot + "/tools/" + emulator_binary, avd_port, "-avd #{params[:avd_name]}", avd_initdata, avd_start_options, "&>/dev/null &"].join(" ")
|
50
|
-
shell_command = "#{params[:shell_command]}" unless params[:shell_command].nil?
|
51
|
-
gradle_task = "#{params[:gradle_task]}" unless params[:gradle_task].nil?
|
52
|
-
|
53
|
-
# Recreating AVD
|
54
|
-
UI.message("Creating AVD...".yellow)
|
55
|
-
createEmulator(get_devices_command, create_avd_command, params, sdkRoot)
|
56
|
-
|
57
|
-
# Starting AVD
|
58
|
-
UI.message("Starting AVD....".yellow)
|
59
|
-
launchEmulator(start_avd_command, get_devices_command, create_avd_command, sdkRoot, port, params)
|
60
|
-
|
61
|
-
UI.message("Starting tests".green)
|
62
|
-
begin
|
63
|
-
unless shell_command.nil?
|
64
|
-
UI.message("Using shell command".green)
|
65
|
-
Action.sh(shell_command)
|
18
|
+
# Create AVD_Controller class for each AVD_scheme
|
19
|
+
for i in 0..(avd_schemes.length - 1)
|
20
|
+
avd_scheme = avd_schemes[i]
|
21
|
+
avd_controller = Factory::AvdControllerFactory.get_avd_controller(params, avd_scheme)
|
22
|
+
avd_controllers << avd_controller
|
66
23
|
end
|
67
24
|
|
68
|
-
|
69
|
-
UI.message("Using gradle task".green)
|
70
|
-
gradle.trigger(task: params[:gradle_task], flags: params[:gradle_flags], serial: nil)
|
71
|
-
end
|
72
|
-
end
|
25
|
+
all_avd_launched = false
|
73
26
|
|
74
|
-
|
75
|
-
end
|
27
|
+
while(!all_avd_launched)
|
76
28
|
|
77
|
-
|
78
|
-
|
79
|
-
|
29
|
+
# Preparation
|
30
|
+
UI.message("Configuring environment in order to launch emulators: ".yellow)
|
31
|
+
|
32
|
+
UI.message("Getting avaliable AVDs".yellow)
|
33
|
+
devices = Action.sh(adb_controller.command_get_avds)
|
34
|
+
|
35
|
+
for i in 0..(avd_schemes.length - 1)
|
36
|
+
avd_scheme = avd_schemes[i]
|
37
|
+
avd_controller = avd_controllers[i]
|
38
|
+
|
39
|
+
# Delete existing AVDs
|
40
|
+
if !devices.match(avd_scheme.avd_name).nil?
|
41
|
+
UI.message(["Deleting existing AVD with name:", avd_scheme.avd_name].join(" ").yellow)
|
42
|
+
Action.sh(avd_controller.command_delete_avd)
|
43
|
+
end
|
80
44
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
end
|
86
|
-
port +=1
|
87
|
-
end
|
88
|
-
return port
|
89
|
-
end
|
45
|
+
# Re-create deleted AVDs
|
46
|
+
UI.message("Creating new AVD".yellow)
|
47
|
+
Action.sh(avd_controller.command_create_avd)
|
48
|
+
end
|
90
49
|
|
91
|
-
|
92
|
-
|
93
|
-
|
50
|
+
# Restart ADB
|
51
|
+
UI.message("Restarting adb".yellow)
|
52
|
+
Action.sh(adb_controller.command_stop)
|
53
|
+
Action.sh(adb_controller.command_start)
|
54
|
+
|
55
|
+
# Applying custom configs (it's not done directly after create because 'cat' operation seems to fail overwrite)
|
56
|
+
for i in 0..(avd_schemes.length - 1)
|
57
|
+
avd_scheme = avd_schemes[i]
|
58
|
+
avd_controller = avd_controllers[i]
|
59
|
+
|
60
|
+
UI.message(["Attemting to apply custom config to ", avd_scheme.avd_name].join("").yellow)
|
61
|
+
if avd_controller.command_apply_config_avd.eql? ""
|
62
|
+
UI.message(["No config file found for AVD '", avd_scheme.avd_name, "'. AVD won't have config.ini applied."].join("").yellow)
|
63
|
+
else
|
64
|
+
UI.message(["Config file found! Applying custom config to: ", avd_scheme.avd_name].join("").yellow)
|
65
|
+
Action.sh(avd_controller.command_apply_config_avd)
|
66
|
+
end
|
67
|
+
end
|
94
68
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
69
|
+
# Launching AVDs
|
70
|
+
UI.message("Launching all AVDs at the same time".yellow)
|
71
|
+
for i in 0..(avd_schemes.length - 1)
|
72
|
+
avd_scheme = avd_schemes[i]
|
73
|
+
avd_controller = avd_controllers[i]
|
99
74
|
|
100
|
-
|
101
|
-
|
75
|
+
Action.sh(avd_controller.command_start_avd)
|
76
|
+
end
|
102
77
|
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
78
|
+
# Wait for AVDs finish booting
|
79
|
+
UI.message("Waiting for AVDs to finish booting".yellow)
|
80
|
+
boot_status = []
|
81
|
+
for i in 0..(avd_schemes.length - 1)
|
82
|
+
boot_status << false
|
83
|
+
end
|
108
84
|
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
return true
|
117
|
-
else
|
118
|
-
timeoutInSeconds= 150.0
|
119
|
-
startTime = Time.now
|
120
|
-
loop do
|
121
|
-
dev_bootcomplete, _stdeerr, _status = Open3.capture3(sdkRoot + ["/platform-tools/adb -s emulator-", port].join("") + " shell getprop dev.bootcomplete")
|
122
|
-
sys_boot_completed, _stdeerr, _status = Open3.capture3(sdkRoot + ["/platform-tools/adb -s emulator-", port].join("") + " shell getprop sys.boot_completed")
|
123
|
-
bootanim, _stdeerr, _status = Open3.capture3(sdkRoot + ["/platform-tools/adb -s emulator-", port].join("") + " shell getprop init.svc.bootanim")
|
124
|
-
currentTime = Time.now
|
125
|
-
|
126
|
-
if (currentTime - startTime) >= timeoutInSeconds
|
127
|
-
UI.message("Emulator loading took more than 2 minutes 30 seconds. Restarting emulator launch until.".yellow)
|
128
|
-
adb_devices_result = Action.sh(sdkRoot + "/platform-tools/adb devices")
|
129
|
-
if adb_devices_result.include? "offline"
|
130
|
-
kill_emulator(sdkRoot, port, params)
|
131
|
-
|
132
|
-
delayInSeconds = 20.0
|
133
|
-
sleep(delayInSeconds)
|
134
|
-
|
135
|
-
delete_emulator(sdkRoot, port, params)
|
136
|
-
|
137
|
-
createEmulator(get_devices_command, create_avd_command, params, sdkRoot)
|
138
|
-
|
139
|
-
launchEmulator(start_avd_command, get_devices_command, create_avd_command, sdkRoot, port, params)
|
85
|
+
UI.message("Performing wait for params: dev.bootcomplete, sys.boot_completed, init.svc.bootanim".yellow)
|
86
|
+
for i in 0..(avd_schemes.length - 1)
|
87
|
+
avd_controller = avd_controllers[i]
|
88
|
+
status = wait_for_emulator_boot(adb_controller, avd_controller)
|
89
|
+
|
90
|
+
if (!status)
|
91
|
+
all_avd_launched = false
|
140
92
|
break
|
141
93
|
end
|
142
|
-
|
94
|
+
all_avd_launched = true
|
143
95
|
end
|
144
96
|
|
145
|
-
|
146
|
-
|
147
|
-
|
97
|
+
# Deciding if AVD launch should be restarted
|
98
|
+
devices = Action.sh(adb_controller.command_get_devices)
|
99
|
+
if all_avd_launched
|
100
|
+
UI.message("AVDs Booted!".green)
|
101
|
+
else
|
102
|
+
for i in 0..(avd_schemes.length - 1)
|
103
|
+
avd_scheme = avd_schemes[i]
|
104
|
+
avd_controller = avd_controllers[i]
|
105
|
+
|
106
|
+
# Kill all emulators
|
107
|
+
if !devices.match(["emulator-", avd_scheme.launch_avd_port].join("")).nil?
|
108
|
+
Action.sh(avd_controller.command_kill_device)
|
109
|
+
end
|
110
|
+
end
|
148
111
|
end
|
112
|
+
|
149
113
|
end
|
150
|
-
end
|
151
|
-
end
|
152
114
|
|
153
|
-
|
154
|
-
|
155
|
-
Action.sh(sdkRoot + "/platform-tools/adb kill-server")
|
156
|
-
Action.sh(sdkRoot + "/platform-tools/adb start-server")
|
157
|
-
end
|
115
|
+
# Launching tests
|
116
|
+
gradle = Helper::GradleHelper.new(gradle_path: Dir["./gradlew"].last)
|
158
117
|
|
159
|
-
|
160
|
-
|
161
|
-
Action.sh(sdkRoot + "/platform-tools/adb -s " + ["emulator-", port].join("") + " emu kill &>/dev/null")
|
162
|
-
end
|
118
|
+
shell_task = "#{params[:shell_task]}" unless params[:shell_task].nil?
|
119
|
+
gradle_task = "#{params[:gradle_task]}" unless params[:gradle_task].nil?
|
163
120
|
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
121
|
+
UI.message("Starting tests".green)
|
122
|
+
begin
|
123
|
+
unless shell_task.nil?
|
124
|
+
UI.message("Using shell task".green)
|
125
|
+
Action.sh(shell_task)
|
126
|
+
end
|
168
127
|
|
169
|
-
|
170
|
-
|
128
|
+
unless gradle_task.nil?
|
129
|
+
UI.message("Using gradle task".green)
|
130
|
+
gradle.trigger(task: params[:gradle_task], flags: params[:gradle_flags], serial: nil)
|
131
|
+
end
|
132
|
+
ensure
|
133
|
+
# Clean up
|
134
|
+
for i in 0..(avd_schemes.length - 1)
|
135
|
+
avd_scheme = avd_schemes[i]
|
136
|
+
avd_controller = avd_controllers[i]
|
137
|
+
|
138
|
+
# Kill all emulators
|
139
|
+
if !devices.match(["emulator-", avd_scheme.launch_avd_port].join("")).nil?
|
140
|
+
Action.sh(avd_controller.command_kill_device)
|
141
|
+
end
|
171
142
|
|
172
|
-
|
173
|
-
|
143
|
+
# Delete AVDs
|
144
|
+
Action.sh(avd_controller.command_delete_avd)
|
145
|
+
end
|
146
|
+
end
|
174
147
|
|
175
|
-
|
176
|
-
|
148
|
+
end
|
149
|
+
|
150
|
+
def self.wait_for_emulator_boot(adb_controller, avd_controller)
|
151
|
+
timeoutInSeconds= 240.0
|
152
|
+
startTime = Time.now
|
177
153
|
|
178
|
-
|
154
|
+
launch_status = false
|
155
|
+
loop do
|
156
|
+
dev_bootcomplete, _stdeerr, _status = Open3.capture3([avd_controller.command_get_property, "dev.bootcomplete"].join(" "))
|
157
|
+
sys_boot_completed, _stdeerr, _status = Open3.capture3([avd_controller.command_get_property, "sys.boot_completed"].join(" "))
|
158
|
+
bootanim, _stdeerr, _status = Open3.capture3([avd_controller.command_get_property, "init.svc.bootanim"].join(" "))
|
159
|
+
currentTime = Time.now
|
160
|
+
|
161
|
+
if (currentTime - startTime) >= timeoutInSeconds
|
162
|
+
UI.message("AVD loading took more than 4 minutes. Restarting launch".red)
|
163
|
+
launch_status = false
|
164
|
+
break
|
165
|
+
end
|
166
|
+
|
167
|
+
if (dev_bootcomplete.strip == "1" && sys_boot_completed.strip == "1" && bootanim.strip == "stopped")
|
168
|
+
launch_status = true
|
169
|
+
break
|
170
|
+
end
|
171
|
+
end
|
172
|
+
return launch_status
|
173
|
+
end
|
174
|
+
|
175
|
+
def self.available_options
|
179
176
|
[
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
optional: false),
|
185
|
-
FastlaneCore::ConfigItem.new(key: :target_id,
|
186
|
-
env_name: "TARGET_ID",
|
187
|
-
description: "Target id of the avd to be created, get list of installed target by running command 'android list targets'",
|
188
|
-
is_string: true,
|
189
|
-
optional: false),
|
190
|
-
FastlaneCore::ConfigItem.new(key: :avd_create_options,
|
191
|
-
env_name: "AVD_CREATE_OPTIONS",
|
192
|
-
description: "Other avd options, used during avd creation, in the form of a <option>=<value> list, i.e \"--scale 96dpi --dpi-device 160\"",
|
193
|
-
is_string: true,
|
194
|
-
optional: true),
|
195
|
-
FastlaneCore::ConfigItem.new(key: :avd_abi,
|
196
|
-
env_name: "AVD_ABI",
|
197
|
-
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",
|
177
|
+
#paths
|
178
|
+
FastlaneCore::ConfigItem.new(key: :AVD_path,
|
179
|
+
env_name: "AVD_PATH",
|
180
|
+
description: "The path to your android AVD directory (root). HOME/.android/avd by default",
|
198
181
|
is_string: true,
|
182
|
+
default_value: ENV['HOME'] + '/.android/avd',
|
199
183
|
optional: true),
|
200
|
-
FastlaneCore::ConfigItem.new(key: :
|
201
|
-
env_name: "
|
202
|
-
description: "
|
184
|
+
FastlaneCore::ConfigItem.new(key: :AVD_setup_path,
|
185
|
+
env_name: "AVD_SETUP_PATH",
|
186
|
+
description: "Location to AVD_setup.json file which contains info about how many AVD should be launched and their configs",
|
203
187
|
is_string: true,
|
204
|
-
optional:
|
205
|
-
FastlaneCore::ConfigItem.new(key: :
|
188
|
+
optional: false),
|
189
|
+
FastlaneCore::ConfigItem.new(key: :SDK_path,
|
206
190
|
env_name: "SDK_PATH",
|
207
191
|
description: "The path to your android sdk directory (root). ANDROID_HOME by default",
|
208
192
|
is_string: true,
|
209
193
|
default_value: ENV['ANDROID_HOME'],
|
210
194
|
optional: true),
|
211
195
|
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
196
|
+
|
197
|
+
#launch commands
|
198
|
+
FastlaneCore::ConfigItem.new(key: :shell_task,
|
199
|
+
env_name: "SHELL_TASK",
|
200
|
+
description: "The shell command you want to execute",
|
201
|
+
is_string: true,
|
202
|
+
conflicting_options: [:gradle_task],
|
203
|
+
optional: true),
|
218
204
|
FastlaneCore::ConfigItem.new(key: :gradle_task,
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
205
|
+
env_name: "GRADLE_TASK",
|
206
|
+
description: "The gradle task you want to execute",
|
207
|
+
is_string: true,
|
208
|
+
conflicting_options: [:shell_command],
|
209
|
+
optional: true),
|
224
210
|
FastlaneCore::ConfigItem.new(key: :gradle_flags,
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
FastlaneCore::ConfigItem.new(key: :emulator_binary,
|
232
|
-
env_name: "EMULATOR_BINARY",
|
233
|
-
description: "Emulator binary file you would like to use in order to start emulator",
|
234
|
-
is_string: true,
|
235
|
-
default_value: "emulator",
|
236
|
-
optional: true),
|
237
|
-
FastlaneCore::ConfigItem.new(key: :avd_port,
|
238
|
-
env_name: "AVD_PORT",
|
239
|
-
description: "Possible to specify port on which emulator should run",
|
240
|
-
is_string: true,
|
241
|
-
optional: true),
|
242
|
-
FastlaneCore::ConfigItem.new(key: :avd_start_options,
|
243
|
-
env_name: "AVD_START_OPTIONS",
|
244
|
-
description: "Additonal run parameters e.g. gpu, audio, boot animation",
|
245
|
-
is_string: true,
|
246
|
-
optional: true),
|
247
|
-
FastlaneCore::ConfigItem.new(key: :initdata_snapshot_path,
|
248
|
-
env_name: "INIT_DATA_PATH",
|
249
|
-
description: "The path to userdata-qemu which will be used to initialize AVD status",
|
250
|
-
is_string: true,
|
251
|
-
optional: true),
|
211
|
+
env_name: "GRADLE_FLAGS",
|
212
|
+
description: "All parameter flags you want to pass to the gradle command, e.g. `--exitcode --xml file.xml`",
|
213
|
+
optional: true,
|
214
|
+
conflicting_options: [:shell_command],
|
215
|
+
is_string: true),
|
252
216
|
]
|
253
|
-
|
217
|
+
end
|
254
218
|
|
255
|
-
|
256
|
-
|
257
|
-
|
219
|
+
def self.description
|
220
|
+
"Starts AVD, based on AVD_setup.json file, before test launch and kills it after testing is done."
|
221
|
+
end
|
258
222
|
|
259
|
-
|
260
|
-
|
261
|
-
|
223
|
+
def self.authors
|
224
|
+
["F1sherKK"]
|
225
|
+
end
|
262
226
|
|
263
|
-
|
264
|
-
|
227
|
+
def self.is_supported?(platform)
|
228
|
+
platform == :android
|
229
|
+
end
|
265
230
|
end
|
266
|
-
end
|
267
231
|
end
|
268
232
|
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module Fastlane
|
2
|
+
module Factory
|
3
|
+
|
4
|
+
class ADB_Controller
|
5
|
+
attr_accessor :command_stop, :command_start, :command_get_devices, :command_wait_for_device, :command_get_avds
|
6
|
+
end
|
7
|
+
|
8
|
+
class AdbControllerFactory
|
9
|
+
|
10
|
+
def self.get_adb_controller(params)
|
11
|
+
UI.message(["Preparing commands for Android ADB"].join(" ").yellow)
|
12
|
+
|
13
|
+
# Get paths
|
14
|
+
path_sdk = "#{params[:SDK_path]}"
|
15
|
+
path_android_binary = path_sdk + "/tools/android"
|
16
|
+
path_adb = path_sdk + "/platform-tools/adb"
|
17
|
+
|
18
|
+
# ADB shell command parts
|
19
|
+
sh_stop_adb = "kill-server"
|
20
|
+
sh_start_adb = "start-server"
|
21
|
+
sh_devices_adb = "devices"
|
22
|
+
sh_wait_for_device_adb = "wait-for-device"
|
23
|
+
sh_list_avd_adb = "list avd"
|
24
|
+
|
25
|
+
# Assemble ADB controller
|
26
|
+
adb_controller = ADB_Controller.new
|
27
|
+
adb_controller.command_stop = [
|
28
|
+
path_adb,
|
29
|
+
sh_stop_adb
|
30
|
+
].join(" ")
|
31
|
+
|
32
|
+
adb_controller.command_start = [
|
33
|
+
path_adb,
|
34
|
+
sh_start_adb
|
35
|
+
].join(" ")
|
36
|
+
|
37
|
+
adb_controller.command_get_devices = [
|
38
|
+
path_adb,
|
39
|
+
sh_devices_adb
|
40
|
+
].join(" ")
|
41
|
+
|
42
|
+
adb_controller.command_wait_for_device = [
|
43
|
+
path_adb,
|
44
|
+
sh_wait_for_device_adb
|
45
|
+
].join(" ")
|
46
|
+
|
47
|
+
adb_controller.command_get_avds = [
|
48
|
+
path_android_binary,
|
49
|
+
sh_list_avd_adb].join(" ").chomp
|
50
|
+
|
51
|
+
return adb_controller
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
module Fastlane
|
2
|
+
module Factory
|
3
|
+
|
4
|
+
class AVD_Controller
|
5
|
+
attr_accessor :command_create_avd, :command_start_avd, :command_delete_avd, :command_apply_config_avd, :command_get_property, :command_kill_device
|
6
|
+
end
|
7
|
+
|
8
|
+
class AvdControllerFactory
|
9
|
+
|
10
|
+
def self.get_avd_controller(params, avd_scheme)
|
11
|
+
UI.message(["Preparing parameters and commands for emulator:", avd_scheme.avd_name].join(" ").yellow)
|
12
|
+
|
13
|
+
# Get paths
|
14
|
+
path_sdk = "#{params[:SDK_path]}"
|
15
|
+
path_android_binary = path_sdk + "/tools/android"
|
16
|
+
path_adb = path_sdk + "/platform-tools/adb"
|
17
|
+
path_avd = "#{params[:AVD_path]}"
|
18
|
+
|
19
|
+
# Create AVD shell command parts
|
20
|
+
sh_create_answer_no = "echo \"no\" |"
|
21
|
+
sh_create_avd = "create avd"
|
22
|
+
sh_create_avd_name = ["--name \"", avd_scheme.avd_name, "\""].join("")
|
23
|
+
sh_create_avd_additional_options = avd_scheme.create_avd_additional_options
|
24
|
+
sh_create_config_loc = "#{path_avd}/#{avd_scheme.avd_name}.avd/config.ini"
|
25
|
+
|
26
|
+
if avd_scheme.create_avd_target.eql? ""
|
27
|
+
sh_create_avd_target = ""
|
28
|
+
else
|
29
|
+
sh_create_avd_target = ["--target \"", avd_scheme.create_avd_target, "\""].join("")
|
30
|
+
end
|
31
|
+
|
32
|
+
if avd_scheme.create_avd_abi.eql? ""
|
33
|
+
sh_create_avd_abi = ""
|
34
|
+
else
|
35
|
+
sh_create_avd_abi = ["--abi ", avd_scheme.create_avd_abi].join("")
|
36
|
+
end
|
37
|
+
|
38
|
+
# Launch AVD shell command parts
|
39
|
+
sh_launch_emulator_binray = [path_sdk, "/tools/", avd_scheme.launch_avd_launch_binary_name].join("")
|
40
|
+
sh_launch_avd_name = ["-avd ", avd_scheme.avd_name].join("")
|
41
|
+
sh_launch_avd_additional_options = avd_scheme.launch_avd_additional_options
|
42
|
+
sh_launch_avd_port = ["-port", avd_scheme.launch_avd_port].join(" ")
|
43
|
+
|
44
|
+
if avd_scheme.launch_avd_snapshot_filepath.eql? ""
|
45
|
+
sh_launch_avd_snapshot = ""
|
46
|
+
else
|
47
|
+
sh_launch_avd_snapshot = ["-wipe-data -initdata ", avd_scheme.launch_avd_snapshot_filepath].join("")
|
48
|
+
end
|
49
|
+
|
50
|
+
# Re-create AVD shell command parts
|
51
|
+
sh_delete_avd = ["delete avd -n ", avd_scheme.avd_name].join("")
|
52
|
+
|
53
|
+
# ADB related shell command parts
|
54
|
+
sh_specific_device = "-s"
|
55
|
+
sh_device_name_adb = ["emulator-", avd_scheme.launch_avd_port].join("")
|
56
|
+
sh_get_property = "shell getprop"
|
57
|
+
sh_kill_device = "emu kill"
|
58
|
+
|
59
|
+
# Assemble AVD controller
|
60
|
+
avd_controller = AVD_Controller.new
|
61
|
+
avd_controller.command_create_avd = [
|
62
|
+
sh_create_answer_no,
|
63
|
+
path_android_binary,
|
64
|
+
sh_create_avd,
|
65
|
+
sh_create_avd_name,
|
66
|
+
sh_create_avd_target,
|
67
|
+
sh_create_avd_abi,
|
68
|
+
sh_create_avd_additional_options].join(" ")
|
69
|
+
|
70
|
+
avd_controller.command_start_avd = [
|
71
|
+
sh_launch_emulator_binray,
|
72
|
+
sh_launch_avd_port,
|
73
|
+
sh_launch_avd_name,
|
74
|
+
sh_launch_avd_snapshot,
|
75
|
+
sh_launch_avd_additional_options,
|
76
|
+
"&>/dev/null &"].join(" ")
|
77
|
+
|
78
|
+
avd_controller.command_delete_avd = [
|
79
|
+
path_android_binary,
|
80
|
+
sh_delete_avd].join(" ")
|
81
|
+
|
82
|
+
if path_avd.nil? || (avd_scheme.create_avd_hardware_config_filepath.eql? "")
|
83
|
+
avd_controller.command_apply_config_avd = ""
|
84
|
+
else
|
85
|
+
avd_controller.command_apply_config_avd = [
|
86
|
+
"cat",
|
87
|
+
avd_scheme.create_avd_hardware_config_filepath,
|
88
|
+
">",
|
89
|
+
sh_create_config_loc].join(" ")
|
90
|
+
end
|
91
|
+
|
92
|
+
avd_controller.command_get_property = [
|
93
|
+
path_adb,
|
94
|
+
sh_specific_device,
|
95
|
+
sh_device_name_adb,
|
96
|
+
sh_get_property].join(" ")
|
97
|
+
|
98
|
+
avd_controller.command_kill_device = [
|
99
|
+
path_adb,
|
100
|
+
sh_specific_device,
|
101
|
+
sh_device_name_adb,
|
102
|
+
sh_kill_device,
|
103
|
+
"&>/dev/null"].join(" ")
|
104
|
+
|
105
|
+
return avd_controller
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1,167 @@
|
|
1
|
+
module Fastlane
|
2
|
+
module Provider
|
3
|
+
|
4
|
+
class AVD_scheme
|
5
|
+
attr_accessor :avd_name, :create_avd_target, :create_avd_abi, :create_avd_hardware_config_filepath, :create_avd_additional_options,
|
6
|
+
:launch_avd_port, :launch_avd_launch_binary_name, :launch_avd_additional_options, :launch_avd_snapshot_filepath
|
7
|
+
end
|
8
|
+
|
9
|
+
class AvdSchemeProvider
|
10
|
+
|
11
|
+
def self.get_avd_schemes(params)
|
12
|
+
|
13
|
+
# Read JSON into string variable
|
14
|
+
avd_setup_json = read_avd_setup(params)
|
15
|
+
if avd_setup_json.nil?
|
16
|
+
throw_error("Unable to read AVD_setup.json. Check JSON file structure or file path.")
|
17
|
+
end
|
18
|
+
|
19
|
+
# Read JSON into Hash
|
20
|
+
avd_setup = JSON.parse(avd_setup_json)
|
21
|
+
avd_hash_list = avd_setup['avd_list']
|
22
|
+
|
23
|
+
# Create AVD_scheme objects and fill them with data
|
24
|
+
avd_scheme_list = []
|
25
|
+
for i in 0..(avd_hash_list.length - 1)
|
26
|
+
avd_hash = avd_hash_list[i]
|
27
|
+
|
28
|
+
avd_scheme = AVD_scheme.new
|
29
|
+
avd_scheme.avd_name = avd_hash['avd_name']
|
30
|
+
avd_scheme.create_avd_target = avd_hash['create_avd_target']
|
31
|
+
avd_scheme.create_avd_abi = avd_hash['create_avd_abi']
|
32
|
+
avd_scheme.create_avd_hardware_config_filepath = avd_hash['create_avd_hardware_config_filepath']
|
33
|
+
avd_scheme.create_avd_additional_options = avd_hash['create_avd_additional_options']
|
34
|
+
avd_scheme.launch_avd_port = avd_hash['launch_avd_port']
|
35
|
+
avd_scheme.launch_avd_launch_binary_name = avd_hash['launch_avd_launch_binary_name']
|
36
|
+
avd_scheme.launch_avd_additional_options = avd_hash['launch_avd_additional_options']
|
37
|
+
avd_scheme.launch_avd_snapshot_filepath = avd_hash['launch_avd_snapshot_filepath']
|
38
|
+
|
39
|
+
errors = check_avd_fields(avd_scheme)
|
40
|
+
unless errors.empty?
|
41
|
+
error_log = "Error! Fields not found in JSON: \n"
|
42
|
+
errors.each { |error| error_log += error + "\n" }
|
43
|
+
throw_error(error_log)
|
44
|
+
end
|
45
|
+
|
46
|
+
avd_scheme_list << avd_scheme
|
47
|
+
end
|
48
|
+
|
49
|
+
# Prepare list of open ports for AVD_schemes without ports set in JSON
|
50
|
+
avaliable_ports = get_unused_even_tcp_ports(5556, 5586, avd_scheme_list)
|
51
|
+
|
52
|
+
# Fill empty AVD_schemes with open ports
|
53
|
+
for i in 0..(avd_scheme_list.length - 1)
|
54
|
+
avd_scheme = avd_scheme_list[i]
|
55
|
+
if avd_scheme.launch_avd_port.eql? ""
|
56
|
+
avd_scheme.launch_avd_port = avaliable_ports[0]
|
57
|
+
avaliable_ports.delete(avaliable_ports[0])
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
return avd_scheme_list
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.get_unused_even_tcp_ports(min_port, max_port, avd_scheme_list)
|
65
|
+
if min_port % 2 != 0
|
66
|
+
min_port += 1
|
67
|
+
end
|
68
|
+
|
69
|
+
if max_port % 2 != 0
|
70
|
+
max_port += 1
|
71
|
+
end
|
72
|
+
|
73
|
+
avaliable_ports = []
|
74
|
+
reserved_ports = []
|
75
|
+
|
76
|
+
# Gather ports requested in JSON config
|
77
|
+
for i in 0..(avd_scheme_list.length - 1)
|
78
|
+
avd_scheme = avd_scheme_list[i]
|
79
|
+
unless avd_scheme.launch_avd_port.eql? ""
|
80
|
+
reserved_ports << avd_scheme.launch_avd_port
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# Find next open port which wasn't reserved in JSON config
|
85
|
+
port = min_port
|
86
|
+
for i in 0..(avd_scheme_list.length - 1)
|
87
|
+
|
88
|
+
while port < max_port do
|
89
|
+
if !system("lsof -i:#{port}", out: '/dev/null')
|
90
|
+
|
91
|
+
is_port_reserved = false
|
92
|
+
for j in 0..(reserved_ports.length - 1)
|
93
|
+
if reserved_ports[j].eql?(port.to_s)
|
94
|
+
is_port_reserved = true
|
95
|
+
break
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
if is_port_reserved
|
100
|
+
port = port + 2
|
101
|
+
break
|
102
|
+
end
|
103
|
+
|
104
|
+
avaliable_ports << port
|
105
|
+
port = port + 2
|
106
|
+
break
|
107
|
+
else
|
108
|
+
port = port + 2
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
return avaliable_ports
|
114
|
+
end
|
115
|
+
|
116
|
+
def self.read_avd_setup(params)
|
117
|
+
if File.exists?(File.expand_path("#{params[:AVD_setup_path]}"))
|
118
|
+
file = File.open(File.expand_path("#{params[:AVD_setup_path]}"), "rb")
|
119
|
+
json = file.read
|
120
|
+
file.close
|
121
|
+
return json
|
122
|
+
else
|
123
|
+
return nil
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def self.check_avd_fields(avd_scheme)
|
128
|
+
errors = []
|
129
|
+
|
130
|
+
if avd_scheme.avd_name.nil?
|
131
|
+
errors.push("avd_name not found")
|
132
|
+
end
|
133
|
+
if avd_scheme.create_avd_target.nil?
|
134
|
+
errors.push("create_avd_target not found")
|
135
|
+
end
|
136
|
+
if avd_scheme.create_avd_abi.nil?
|
137
|
+
errors.push("create_avd_abi not found")
|
138
|
+
end
|
139
|
+
if avd_scheme.create_avd_hardware_config_filepath.nil?
|
140
|
+
errors.push("create_avd_hardware_config_filepath not found")
|
141
|
+
end
|
142
|
+
if avd_scheme.create_avd_additional_options.nil?
|
143
|
+
errors.push("create_avd_additional_options not found")
|
144
|
+
end
|
145
|
+
if avd_scheme.launch_avd_snapshot_filepath.nil?
|
146
|
+
errors.push("launch_avd_snapshot_filepath not found")
|
147
|
+
end
|
148
|
+
if avd_scheme.launch_avd_launch_binary_name.nil?
|
149
|
+
errors.push("launch_avd_launch_binary_name not found")
|
150
|
+
end
|
151
|
+
if avd_scheme.launch_avd_port.nil?
|
152
|
+
errors.push("launch_avd_port not found")
|
153
|
+
end
|
154
|
+
if avd_scheme.launch_avd_additional_options.nil?
|
155
|
+
errors.push("launch_avd_additional_options not found")
|
156
|
+
end
|
157
|
+
|
158
|
+
return errors
|
159
|
+
end
|
160
|
+
|
161
|
+
def self.throw_error(message)
|
162
|
+
UI.message("Error: ".red + message.red)
|
163
|
+
raise Exception, "Lane was stopped by plugin"
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fastlane-plugin-automated_test_emulator_run
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kamil Krzyk
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-11-
|
11
|
+
date: 2016-11-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: pry
|
@@ -104,7 +104,9 @@ files:
|
|
104
104
|
- README.md
|
105
105
|
- lib/fastlane/plugin/automated_test_emulator_run.rb
|
106
106
|
- lib/fastlane/plugin/automated_test_emulator_run/actions/automated_test_emulator_run_action.rb
|
107
|
-
- lib/fastlane/plugin/automated_test_emulator_run/
|
107
|
+
- lib/fastlane/plugin/automated_test_emulator_run/factory/adb_controller_factory.rb
|
108
|
+
- lib/fastlane/plugin/automated_test_emulator_run/factory/avd_controller_factory.rb
|
109
|
+
- lib/fastlane/plugin/automated_test_emulator_run/provider/avd_setup_provider.rb
|
108
110
|
- lib/fastlane/plugin/automated_test_emulator_run/version.rb
|
109
111
|
homepage: https://github.com/AzimoLabs/fastlane-plugin-automated-test-emulator-run
|
110
112
|
licenses:
|
@@ -129,7 +131,7 @@ rubyforge_project:
|
|
129
131
|
rubygems_version: 2.5.1
|
130
132
|
signing_key:
|
131
133
|
specification_version: 4
|
132
|
-
summary:
|
133
|
-
|
134
|
-
|
134
|
+
summary: Starts n AVDs based on JSON file config. AVDs are created and configured
|
135
|
+
according to user liking before instrumentation test process (started either via
|
136
|
+
shell command or gradle) and killed/deleted after test process finishes.
|
135
137
|
test_files: []
|
data/lib/fastlane/plugin/automated_test_emulator_run/helper/automated_test_emulator_run_helper.rb
DELETED
@@ -1,12 +0,0 @@
|
|
1
|
-
module Fastlane
|
2
|
-
module Helper
|
3
|
-
class AutomatedTestEmulatorRunHelper
|
4
|
-
# class methods that you define here become available in your action
|
5
|
-
# as `Helper::AutomatedTestEmulatorRunHelper.your_method`
|
6
|
-
#
|
7
|
-
def self.show_message
|
8
|
-
UI.message("Hello from the automated_test_emulator_run plugin helper!")
|
9
|
-
end
|
10
|
-
end
|
11
|
-
end
|
12
|
-
end
|