"
19 |
20 |
21 | def add_cell_separator():
22 | return "
"
23 |
24 |
25 | def add_cell_subseparator():
26 | return "
"
27 |
28 |
29 | def add_summary_table_results_header(text):
30 | return "".format(text)
31 |
32 |
33 | def add_summary_table_results_cell(text):
34 | return "
{}
".format(text)
35 |
36 |
37 | def add_summary_table_results_passed_left(text):
38 | return "
{}
".format(text)
39 |
40 |
41 | def add_summary_table_results_passed_right(text):
42 | return "
{}
".format(text)
43 |
44 |
45 | def add_summary_table_results_failed_left(text):
46 | return "
{}
".format(text)
47 |
48 |
49 | def add_summary_table_results_failed_right(text):
50 | return "
{}
".format(text)
51 |
52 |
53 | def add_summary_table_general_header(text):
54 | return "".format(text)
55 |
56 |
57 | def add_summary_table_general_cell_left(text):
58 | return "
{}
".format(text)
59 |
60 |
61 | def add_summary_table_general_cell_right(text):
62 | return "
{}
".format(text)
63 |
64 |
65 | def add_summary_table_apk_header(text):
66 | return "".format(text)
67 |
68 |
69 | def add_summary_table_apk_subheader(text):
70 | return "".format(text)
71 |
72 |
73 | def add_summary_table_apk_cell_left(text):
74 | return "
{}
".format(text)
75 |
76 |
77 | def add_summary_table_apk_cell_right(text):
78 | return "
{}
".format(text)
79 |
80 |
81 | def add_summary_table_devices_header(text):
82 | return "".format(text)
83 |
84 |
85 | def add_summary_table_devices_subheader(text):
86 | return "".format(text)
87 |
88 |
89 | def add_summary_table_devices_cell_left(text):
90 | return "
{}
".format(text)
91 |
92 |
93 | def add_summary_table_devices_cell_right(text):
94 | return "
{}
".format(text)
95 |
96 |
97 | def start_summary_failed_test_list_table():
98 | return "
"
99 |
100 |
101 | def start_summary_failed_test_list_subtable():
102 | return "
"
103 |
104 |
105 | def start_summary_failed_test_row():
106 | return "
"
107 |
108 |
109 | def add_summary_failed_test_row_cell_light(text):
110 | return "
{}
".format(text)
111 |
112 |
113 | def add_summary_failed_test_row_cell_dark(text):
114 | return "
{}
".format(text)
115 |
116 |
117 | def end_row():
118 | return "
"
119 |
120 |
121 | def end_table():
122 | return "
"
123 |
124 |
125 | def end_wrapper():
126 | return "
"
127 |
--------------------------------------------------------------------------------
/log_generator/utils/HtmlUtils.py:
--------------------------------------------------------------------------------
1 | def start_html():
2 | return ""
3 |
4 |
5 | def end_html():
6 | return ""
7 |
8 |
9 | def start_head():
10 | return ""
11 |
12 |
13 | def end_head():
14 | return ""
15 |
16 |
17 | def link_css(css_dir):
18 | return "
".format(css_dir)
19 |
20 |
21 | def start_body():
22 | return ""
23 |
24 |
25 | def end_body():
26 | return ""
27 |
28 |
29 | def create_link_to_file(link_destination, text):
30 | return "
{} ".format(link_destination, text)
31 |
32 |
33 | def start_script():
34 | return ""
39 |
40 |
41 | def add_toggle_visibility_function_for_package_with_fails():
42 | return """
43 | function toggle_visibility_for_package_with_fails(id) {
44 | var e = document.getElementById(id);
45 |
46 | if (e.style.display === 'none')
47 | e.style.display = 'block';
48 | else
49 | e.style.display = 'none';
50 | }
51 | """
52 |
53 |
54 | def add_toggle_visibility_function_for_clean_package():
55 | return """
56 | function toggle_visibility_for_clean_package(id) {
57 | var e = document.getElementById(id);
58 |
59 | if (e.style.display === 'block')
60 | e.style.display = 'none';
61 | else
62 | e.style.display = 'block';
63 | }
64 | """
65 |
66 |
67 | def wrap_in_toggle_visibility_on_click_for_package_with_fails(object_to_be_wrapped, click_id):
68 | return ("
{} ").format(click_id, object_to_be_wrapped)
70 |
71 |
72 | def wrap_in_toggle_visibility_on_click_for_clean_package(object_to_be_wrapped, click_id):
73 | return ("
{"
74 | + "} ").format(click_id, object_to_be_wrapped)
75 |
76 |
--------------------------------------------------------------------------------
/session/SessionDataStores.py:
--------------------------------------------------------------------------------
1 | import copy
2 | import re
3 | import os
4 |
5 | from settings import GlobalConfig
6 |
7 | from session.SessionModels import (
8 | OutsideSessionVirtualDevice,
9 | OutsideSessionDevice,
10 | SessionVirtualDevice,
11 | ApkCandidate,
12 | )
13 |
14 | from system.file import FileUtils
15 | from system.port import PortManager
16 | from system.console import (
17 | Printer,
18 | Color
19 | )
20 |
21 |
22 | class ApkStore:
23 | TAG = "ApkStore:"
24 |
25 | def __init__(self, aapt_controller):
26 | self.aapt_controller = aapt_controller
27 | self.apk_candidates = list()
28 | self.usable_apk_candidate = None
29 | self._create_apk_dir_if_not_exists()
30 |
31 | def _create_apk_dir_if_not_exists(self):
32 | if FileUtils.dir_exists(GlobalConfig.APK_DIR):
33 | Printer.system_message(self.TAG, "Directory " + Color.GREEN + GlobalConfig.APK_DIR + Color.BLUE
34 | + " was found.")
35 | else:
36 | Printer.system_message(self.TAG, "Directory " + Color.GREEN + GlobalConfig.APK_DIR + Color.BLUE
37 | + " not found. Creating...")
38 | FileUtils.create_dir(GlobalConfig.APK_DIR)
39 |
40 | def provide_apk(self, test_set):
41 | self._find_candidates(test_set)
42 | self.usable_apk_candidate = self._get_usable_apk_candidate_for_latest_version()
43 | return self.usable_apk_candidate
44 |
45 | def _find_candidates(self, test_set):
46 | name_part = test_set.apk_name_part.replace(".apk", "")
47 | Printer.system_message(self.TAG,
48 | "Checking " + Color.GREEN + GlobalConfig.APK_DIR + Color.BLUE + " directory for .*apk" +
49 | " list with names containing " + Color.GREEN + name_part + Color.BLUE + ":")
50 |
51 | app_apk_list = self.get_list_with_application_apk(name_part, GlobalConfig.APK_DIR)
52 | test_apk_list = self.get_list_with_test_apk(name_part, GlobalConfig.APK_DIR)
53 |
54 | path = 0
55 | name = 1
56 | if app_apk_list:
57 | for apk in app_apk_list:
58 | apk_filename = apk[name]
59 | apk_filepath = FileUtils.clean_folder_only_dir(
60 | FileUtils.add_ending_slash(apk[path])) + apk[name]
61 |
62 | apk_test_filename = ""
63 | apk_test_filepath = ""
64 | for test_apk in test_apk_list:
65 | if apk_filename.replace(".apk", "") in test_apk[name] and "-androidTest" in test_apk[name]:
66 | apk_test_filename = test_apk[name]
67 | apk_test_filepath = FileUtils.clean_folder_only_dir(
68 | FileUtils.add_ending_slash(test_apk[path])) + test_apk[name]
69 |
70 | dump = self.aapt_controller.dump_badging(apk_filepath)
71 | version_code = re.findall("versionCode='(.+?)'", dump)
72 | version_code = int(version_code[0])
73 |
74 | self.apk_candidates.append(ApkCandidate(apk_filename,
75 | apk_filepath,
76 | apk_test_filename,
77 | apk_test_filepath,
78 | version_code))
79 | else:
80 | Printer.system_message(self.TAG, " * No .apk* files found.")
81 |
82 | def display_candidates(self):
83 | candidate_no = 0
84 | for apk_info in self.apk_candidates:
85 | candidate_no += 1
86 | Printer.system_message(self.TAG, " * Candidate no." + str(candidate_no) + " "
87 | + (Color.GREEN + "('can be used in test')" if apk_info.is_usable()
88 | else Color.RED + "('cannot be used in test - missing fields')"))
89 | Printer.system_message(self.TAG,
90 | " Apk candidate name: " + Color.GREEN + str(apk_info.apk_name) + Color.END)
91 | Printer.system_message(self.TAG,
92 | " Apk candidate path: " + Color.GREEN + str(apk_info.apk_path) + Color.END)
93 | Printer.system_message(self.TAG,
94 | " Related test apk: " + Color.GREEN + str(apk_info.test_apk_name) + Color.END)
95 | Printer.system_message(self.TAG, " Related test apk path: " + Color.GREEN + str(apk_info.test_apk_path)
96 | + Color.END)
97 | Printer.system_message(self.TAG, " Version: " + Color.GREEN + str(apk_info.apk_version) + Color.END)
98 |
99 | def _get_usable_apk_candidate_for_latest_version(self):
100 | latest_apk_info = None
101 | latest_ver = -1
102 | for apk_info in self.apk_candidates:
103 | if apk_info.is_usable() and apk_info.apk_version > latest_ver:
104 | latest_apk_info = apk_info
105 | latest_ver = apk_info.apk_version
106 | return latest_apk_info
107 |
108 | @staticmethod
109 | def get_list_with_application_apk(apk_name_part_cleaned, apk_dir):
110 | app_apk_files = list()
111 | for path, subdirs, files in os.walk(apk_dir):
112 | for filename in files:
113 | if apk_name_part_cleaned in filename and ".apk" in filename and "androidTest" not in filename:
114 | app_apk_files.append((path, filename))
115 |
116 | return app_apk_files
117 |
118 | @staticmethod
119 | def get_list_with_test_apk(apk_name_part_cleaned, apk_dir):
120 | test_apk_files = list()
121 | for path, subdirs, files in os.walk(apk_dir):
122 | for filename in files:
123 | if apk_name_part_cleaned in filename and ".apk" in filename and "androidTest" in filename:
124 | test_apk_files.append((path, filename))
125 |
126 | return test_apk_files
127 |
128 |
129 | class DeviceStore:
130 | TAG = "DeviceStore:"
131 |
132 | def __init__(self,
133 | adb_controller,
134 | adb_package_manager_controller,
135 | adb_settings_controller,
136 | avdmanager_controller,
137 | emulator_controller):
138 |
139 | self.adb_controller = adb_controller
140 | self.adb_package_manager_controller = adb_package_manager_controller
141 | self.adb_settings_controller = adb_settings_controller
142 | self.avdmanager_controller = avdmanager_controller
143 | self.emulator_controller = emulator_controller
144 |
145 | self.outside_session_virtual_devices = list()
146 | self.outside_session_devices = list()
147 | self.session_devices = list()
148 |
149 | def prepare_outside_session_devices(self):
150 | currently_visible_devices = self._get_visible_devices()
151 |
152 | for device_name, status in currently_visible_devices.items():
153 | if "emulator" not in device_name:
154 | outside_session_device = OutsideSessionDevice(device_name,
155 | status,
156 | self.adb_controller,
157 | self.adb_package_manager_controller,
158 | self.adb_settings_controller)
159 | Printer.system_message(self.TAG, "Android Device model representing device with name "
160 | + Color.GREEN + device_name + Color.BLUE + " was added to test run.")
161 | self.outside_session_devices.append(outside_session_device)
162 |
163 | if not any(isinstance(device, OutsideSessionDevice) for device in self.outside_session_devices):
164 | Printer.system_message(self.TAG, "No Android Devices connected to PC were found.")
165 |
166 | def prepare_outside_session_virtual_devices(self):
167 | currently_visible_devices = self._get_visible_devices()
168 |
169 | for device_name, status in currently_visible_devices.items():
170 | if "emulator" in device_name:
171 | outside_session_virtual_device = OutsideSessionVirtualDevice(device_name,
172 | status,
173 | self.adb_controller,
174 | self.adb_package_manager_controller,
175 | self.adb_settings_controller)
176 | Printer.system_message(self.TAG, "AVD model representing device with name "
177 | + Color.GREEN + device_name + Color.BLUE + " was added to test run.")
178 | self.outside_session_virtual_devices.append(outside_session_virtual_device)
179 |
180 | if not any(isinstance(device, OutsideSessionVirtualDevice) for device in self.outside_session_virtual_devices):
181 | Printer.system_message(self.TAG, "No currently launched AVD were found.")
182 |
183 | def prepare_session_devices(self, avd_set, avd_schemas):
184 | avd_ports = PortManager.get_open_ports(avd_set)
185 | for avd in avd_set.avd_list:
186 | instances_of_schema = avd.instances
187 | for i in range(instances_of_schema):
188 | avd_schema = copy.deepcopy(avd_schemas[avd.avd_name])
189 | avd_schema.avd_name = avd_schema.avd_name + "-" + str(i)
190 | port = avd_ports.pop(0)
191 |
192 | log_file = FileUtils.clean_path(GlobalConfig.OUTPUT_AVD_LOG_DIR + avd_schema.avd_name + ".txt")
193 | FileUtils.create_file(GlobalConfig.OUTPUT_AVD_LOG_DIR, avd_schema.avd_name, "txt")
194 | Printer.system_message(self.TAG, "Created file " + Color.GREEN + log_file + Color.BLUE + ".")
195 |
196 | session_device = SessionVirtualDevice(avd_schema,
197 | port,
198 | log_file,
199 | self.avdmanager_controller,
200 | self.emulator_controller,
201 | self.adb_controller,
202 | self.adb_package_manager_controller,
203 | self.adb_settings_controller)
204 | self.session_devices.append(session_device)
205 | Printer.system_message(self.TAG, "Android Virtual Device model was created according to schema "
206 | + Color.GREEN + avd_schema.avd_name + Color.BLUE +
207 | ". Instance number: " + str(i) + ". Assigned to port: " + str(port) + ".")
208 |
209 | def _get_visible_devices(self):
210 | currently_visible_devices = dict()
211 |
212 | adb_devices_output = self.adb_controller.devices()
213 | adb_devices_lines = [line for line in adb_devices_output.splitlines() if len(line.split()) == 2]
214 |
215 | for line in adb_devices_lines:
216 | device_name = line.split()[0]
217 | device_status = line.split()[1]
218 |
219 | # edge case
220 | if device_name == "*":
221 | continue
222 |
223 | currently_visible_devices.update({device_name: device_status})
224 |
225 | return currently_visible_devices
226 |
227 | def get_devices(self):
228 | return self.session_devices + self.outside_session_devices + self.outside_session_virtual_devices
229 |
230 | def update_model_statuses(self):
231 | currently_visible_devices = self._get_visible_devices()
232 |
233 | for session_device in self.session_devices:
234 | session_device.status = "not-launched"
235 |
236 | for outside_session_device in self.outside_session_devices:
237 | outside_session_device.status = "not-launched"
238 |
239 | for outside_session_virtual_device in self.outside_session_virtual_devices:
240 | outside_session_virtual_device.status = "not-launched"
241 |
242 | for device_name, status in currently_visible_devices.items():
243 | for session_device in self.session_devices:
244 | if session_device.adb_name == device_name:
245 | session_device.status = status
246 |
247 | for outside_session_device in self.outside_session_devices:
248 | if outside_session_device.adb_name == device_name:
249 | outside_session_device.status = status
250 |
251 | for outside_session_virtual_device in self.outside_session_virtual_devices:
252 | if outside_session_virtual_device.adb_name == device_name:
253 | outside_session_virtual_device.status = status
254 |
255 | def clear_outside_session_virtual_device_models(self):
256 | self.outside_session_virtual_devices.clear()
257 |
258 | def clear_outside_session_device_models(self):
259 | self.outside_session_devices.clear()
260 |
261 | def clear_session_avd_models(self):
262 | self.session_devices.clear()
263 |
264 | def remove_device_from_session(self, device):
265 | if device in self.outside_session_virtual_devices:
266 | self.outside_session_virtual_devices.remove(device)
267 | elif device in self.outside_session_devices:
268 | self.outside_session_devices.remove(device)
269 | elif device in self.session_devices:
270 | self.session_devices.remove(device)
271 | Printer.system_message(self.TAG, "Device with name "
272 | + Color.GREEN + device.adb_name + Color.BLUE + " was removed from session.")
273 |
274 |
275 | class TestStore:
276 | TAG = "TestStore:"
277 |
278 | def __init__(self):
279 | self.packages_to_run = list()
280 | self.test_statuses = list()
281 | self.test_logcats = list()
282 |
283 | # TODO split into init and getter
284 | def get_packages(self, test_set, test_list):
285 | for package_name in test_set.set_package_names:
286 | for test_package in test_list[package_name].test_packages:
287 | if test_package not in self.packages_to_run:
288 | self.packages_to_run.append(test_package)
289 | return self.packages_to_run
290 |
291 | def store_test_status(self, test_statuses):
292 | for status in test_statuses:
293 | self.test_statuses.append(status)
294 |
295 | def store_test_logcat(self, test_logcats):
296 | for logcat in test_logcats:
297 | self.test_logcats.append(logcat)
298 |
299 | def test_contain_count(self, test_name):
300 | return len([t for t in self.get_test_statuses() if t.test_name == test_name])
301 |
302 | def test_logcat_contain_count(self, test_name):
303 | return len([t for t in self.get_test_logcats() if t.test_name == test_name])
304 |
305 | def get_test_statuses(self):
306 | return self.test_statuses
307 |
308 | def get_test_logcats(self):
309 | return self.test_logcats
310 |
--------------------------------------------------------------------------------
/session/SessionModels.py:
--------------------------------------------------------------------------------
1 | from system.console import Color
2 |
3 | from error.Exceptions import LauncherFlowInterruptedException
4 |
5 |
6 | class _BasicDevice:
7 | def __init__(self, adb_name, status, adb_controller, adb_package_manager_controller, adb_settings_controller):
8 | self.adb_controller = adb_controller
9 | self.adb_package_manager_controller = adb_package_manager_controller
10 | self.adb_settings_controller = adb_settings_controller
11 | self.adb_name = adb_name
12 | self.android_id = None
13 | self.status = status
14 |
15 | def install_apk(self, apk_file):
16 | return self.adb_controller.install_apk(self.adb_name, apk_file)
17 |
18 | def list_packages(self):
19 | return self.adb_package_manager_controller
20 |
21 | def get_installed_packages(self):
22 | return self.adb_package_manager_controller.get_installed_packages(self.adb_name)
23 |
24 | def uninstall_package(self, package_name):
25 | return self.adb_package_manager_controller.uninstall_package(self.adb_name, package_name)
26 |
27 | def get_android_id(self):
28 | if self.android_id is None:
29 | self.android_id = self.adb_settings_controller.get_device_android_id(self.adb_name).strip()
30 | return self.android_id
31 |
32 |
33 | class _BasicVirtualDevice(_BasicDevice):
34 | def __init__(self, adb_name, status, adb_controller, adb_package_manager_controller, adb_settings_controller):
35 | super().__init__(adb_name, status, adb_controller, adb_package_manager_controller, adb_settings_controller)
36 |
37 | def get_property(self, device_property):
38 | return self.adb_controller.get_property(self.adb_name, device_property)
39 |
40 | def kill(self):
41 | return self.adb_controller.kill_device(self.adb_name)
42 |
43 |
44 | class OutsideSessionDevice(_BasicDevice):
45 | def __init__(self, adb_name, status, adb_controller, adb_package_manager_controller, adb_settings_controller):
46 | super().__init__(adb_name, status, adb_controller, adb_package_manager_controller, adb_settings_controller)
47 |
48 |
49 | class OutsideSessionVirtualDevice(_BasicVirtualDevice):
50 | def __init__(self, adb_name, status, adb_controller, adb_package_manager_controller, adb_settings_controller):
51 | super().__init__(adb_name, status, adb_controller, adb_package_manager_controller, adb_settings_controller)
52 |
53 |
54 | class SessionVirtualDevice(_BasicVirtualDevice):
55 | TAG = "SessionVirtualDevice:"
56 |
57 | def __init__(self,
58 | avd_schema, port, log_file, avdmanager_controller, emulator_controller, adb_controller,
59 | adb_package_manager_controller, adb_settings_controller):
60 |
61 | super().__init__("emulator-" + str(port), "not-launched", adb_controller, adb_package_manager_controller,
62 | adb_settings_controller)
63 |
64 | self.avdmanager_controller = avdmanager_controller
65 | self.emulator_controller = emulator_controller
66 | self.avd_schema = avd_schema
67 | self._check_avd_schema()
68 |
69 | self.port = port
70 | self.log_file = log_file
71 |
72 | def _check_avd_schema(self):
73 | if self.avd_schema.avd_name == "":
74 | message = "One AVD schema doesn't have name set."
75 | raise LauncherFlowInterruptedException(self.TAG, message)
76 |
77 | if self.avd_schema.create_avd_package == "":
78 | message = "Parameter 'create_avd_package' in AVD schema {} cannot be empty."
79 | message = message.format(self.avd_schema.avd_name)
80 | raise LauncherFlowInterruptedException(self.TAG, message)
81 |
82 | if self.avd_schema.launch_avd_launch_binary_name == "":
83 | message = "Parameter 'launch_avd_launch_binary_name' in AVD schema {} cannot be empty."
84 | message = message.format(self.avd_schema.avd_name)
85 | raise LauncherFlowInterruptedException(self.TAG, message)
86 |
87 | def create(self):
88 | return self.avdmanager_controller.create_avd(self.avd_schema)
89 |
90 | def delete(self):
91 | return self.avdmanager_controller.delete_avd(self.avd_schema)
92 |
93 | def launch(self):
94 | return self.emulator_controller.launch_avd(self.avd_schema, self.port, self.log_file)
95 |
96 | def apply_config_ini(self):
97 | return self.emulator_controller.apply_config_to_avd(self.avd_schema)
98 |
99 |
100 | class ApkCandidate:
101 | MISSING_VALUE = Color.RED + "missing" + Color.END
102 |
103 | def __init__(self, apk_name, apk_path, test_apk_name, test_apk_path, apk_version):
104 | self.apk_name = self._set_field(apk_name)
105 | self.apk_path = self._set_field(apk_path)
106 | self.test_apk_name = self._set_field(test_apk_name)
107 | self.test_apk_path = self._set_field(test_apk_path)
108 | self.apk_version = apk_version
109 |
110 | def is_usable(self):
111 | return self._is_field_filled(self.apk_name) \
112 | and self._is_field_filled(self.test_apk_path) \
113 | and self._is_field_filled(self.test_apk_name) \
114 | and self._is_field_filled(self.test_apk_path) \
115 | and self.apk_version != -1
116 |
117 | def __str__(self):
118 | return "Apk('apk_name: " + self.apk_name + "', " \
119 | + "'apk_path: " + self.apk_path + "', " \
120 | + "'test_apk_name: " + self.test_apk_name + "', " \
121 | + "'test_apk_path: " + self.test_apk_path + "', " \
122 | + "'version_code: " + str(self.apk_version) + "')"
123 |
124 | def _is_field_filled(self, field):
125 | return field is not None and field != self.MISSING_VALUE
126 |
127 | def _set_field(self, field):
128 | return field if field is not None and field != "" else self.MISSING_VALUE
129 |
130 |
131 | class SessionSummary:
132 | def __init__(self):
133 | self.device_summaries = None
134 | self.time_summary = None
135 | self.apk_summary = None
136 | self.test_summary = None
137 |
138 |
139 | class SessionDeviceSummary:
140 | def __init__(self):
141 | self.device_name = None
142 |
143 | self.creation_start_time = None
144 | self.creation_end_time = None
145 | self.creation_time = None
146 |
147 | self.launch_start_time = None
148 | self.launch_end_time = None
149 | self.launch_time = None
150 |
151 | self.apk_install_start_time = None
152 | self.apk_install_end_time = None
153 | self.apk_install_time = None
154 |
155 | self.test_apk_install_start_time = None
156 | self.test_apk_install_end_time = None
157 | self.test_apk_install_time = None
158 |
159 |
160 | class SessionTimeSummary:
161 | def __init__(self):
162 | self.total_device_creation_start_time = None
163 | self.total_device_creation_end_time = None
164 | self.total_device_creation_time = None
165 |
166 | self.total_device_launch_start_time = None
167 | self.total_device_launch_end_time = None
168 | self.total_device_launch_time = None
169 |
170 | self.total_apk_build_start_time = None
171 | self.total_apk_build_end_time = None
172 | self.total_apk_build_time = None
173 |
174 | self.total_apk_install_start_time = None
175 | self.total_apk_install_end_time = None
176 | self.total_apk_install_time = None
177 |
178 | self.total_test_start_time = None
179 | self.total_test_end_time = None
180 | self.total_test_time = None
181 |
182 | self.total_rerun_start_time = None
183 | self.total_rerun_end_time = None
184 | self.total_rerun_time = None
185 |
186 | self.total_session_start_time = None
187 | self.total_session_end_time = None
188 | self.total_session_time = None
189 |
190 |
191 | class SessionApkSummary:
192 | def __init__(self):
193 | self.apk = None
194 | self.test_apk = None
195 | self.version_code = None
196 |
197 | self.apk_build_start_time = None
198 | self.apk_build_end_time = None
199 | self.apk_build_time = None
200 |
201 | self.test_apk_build_start_time = None
202 | self.test_apk_build_end_time = None
203 | self.test_apk_build_time = None
204 |
205 |
206 | class SessionTestSummary:
207 | def __init__(self):
208 | self.test_number = None
209 | self.test_passed = None
210 | self.test_failed = None
211 | self.health_rate = None
212 |
213 |
214 | class SessionFlakinessCheckSummary:
215 | def __init__(self):
216 | self.suspects = dict()
217 |
218 |
219 | class TestSummary:
220 | def __init__(self):
221 | self.test_name = None
222 | self.test_container = None
223 | self.test_full_package = None
224 | self.test_status = None
225 | self.device = None
226 | self.test_start_time = None
227 | self.test_end_time = None
228 | self.rerun_count = 0
229 | self.error_messages = list()
230 |
231 |
232 | class TestLogCat:
233 | def __init__(self):
234 | self.test_name = None
235 | self.test_container = None
236 | self.test_full_package = None
237 | self.rerun_count = 0
238 | self.lines = list()
239 |
240 |
241 | class TestLogCatLine:
242 | def __init__(self):
243 | self.date = None
244 | self.time = None
245 | self.level = None
246 | self.tag = None
247 | self.text = None
248 |
--------------------------------------------------------------------------------
/session/SessionThreads.py:
--------------------------------------------------------------------------------
1 | import time
2 | import re
3 | import os
4 | import subprocess
5 | import threading
6 |
7 | from error.Exceptions import LauncherFlowInterruptedException
8 |
9 | from settings import GlobalConfig
10 |
11 | from session.SessionModels import (
12 | TestLogCat,
13 | TestLogCatLine,
14 | TestSummary
15 | )
16 |
17 | from system.file import FileUtils
18 | from system.console import ShellHelper
19 | from system.console import (
20 | Printer,
21 | Color
22 | )
23 |
24 |
25 | class TestSummarySavingThread(threading.Thread):
26 | TAG = "TestSummarySavingThread"
27 |
28 | TEST_SUMMARY_APPENDIX = "test_summary.json"
29 |
30 | def __init__(self, device, test_summary_list):
31 | super().__init__()
32 | self.device = device
33 | self.test_summary_list = test_summary_list
34 | self.created_files = None
35 |
36 | def run(self):
37 | self.created_files = list()
38 |
39 | for test_summary in self.test_summary_list:
40 | test_summary_json_dict = vars(test_summary)
41 |
42 | filename = test_summary_json_dict["test_name"] + "_" + self.device.adb_name + "_" \
43 | + self.TEST_SUMMARY_APPENDIX
44 |
45 | if test_summary.rerun_count > 0:
46 | filename = filename.replace(".json", "_rerun_no_{}.json".format(test_summary.rerun_count))
47 |
48 | created_file_path = FileUtils.save_json_dict_to_json(
49 | GlobalConfig.OUTPUT_TEST_LOG_DIR, test_summary_json_dict, filename)
50 |
51 | self.created_files.append(created_file_path)
52 |
53 | def is_finished(self):
54 | return self.created_files is not None and len(self.created_files) == len(self.test_summary_list) \
55 | and all(os.path.isfile(file) for file in self.created_files)
56 |
57 |
58 | class TestLogcatSavingThread(threading.Thread):
59 | TAG = "TestLogcatSavingThread"
60 |
61 | TEST_LOGCAT_APPENDIX = "logcat.json"
62 |
63 | def __init__(self, device, test_logcat_list):
64 | super().__init__()
65 | self.device = device
66 | self.test_logcat_list = test_logcat_list
67 | self.created_files = None
68 |
69 | def run(self):
70 | self.created_files = list()
71 |
72 | for logcat in self.test_logcat_list:
73 | logcat_lines_json_dict = list()
74 | for logcat_line in logcat.lines:
75 | logcat_lines_json_dict.append(vars(logcat_line))
76 | logcat.lines = logcat_lines_json_dict
77 |
78 | logcat_json_dict = vars(logcat)
79 |
80 | filename = logcat_json_dict["test_name"] + "_" + self.device.adb_name + "_" + self.TEST_LOGCAT_APPENDIX
81 |
82 | if logcat.rerun_count > 0:
83 | filename = filename.replace(".json", "_rerun_no_{}.json".format(logcat.rerun_count))
84 |
85 | created_file_path = FileUtils.save_json_dict_to_json(
86 | GlobalConfig.OUTPUT_TEST_LOGCAT_DIR, logcat_json_dict, filename)
87 |
88 | self.created_files.append(created_file_path)
89 |
90 | def is_finished(self):
91 | return self.created_files is not None and len(self.created_files) == len(self.test_logcat_list) \
92 | and all(os.path.isfile(file) for file in self.created_files)
93 |
94 |
95 | class TestRecordingSavingThread(threading.Thread):
96 | TAG = "TestRecordingSavingThread"
97 |
98 | def __init__(self, device):
99 | super().__init__()
100 | self.device = device
101 |
102 | self.recordings = list()
103 | self.recording_pull_cmds = dict()
104 | self.recording_clear_cmds = dict()
105 |
106 | self.should_finish = False
107 |
108 | def run(self):
109 | while True:
110 | if self.recordings and self._all_recordings_has_commands():
111 | recording = self.recordings.pop()
112 |
113 | # TODO Implement proper synchronisation/wait for .mp4 to finish being written to
114 | time.sleep(10)
115 |
116 | ShellHelper.execute_shell(self.recording_pull_cmds.get(recording), False, False)
117 | ShellHelper.execute_shell(self.recording_clear_cmds.get(recording), False, False)
118 |
119 | if self.should_finish:
120 | break
121 |
122 | def _all_recordings_has_commands(self):
123 | all_recordings_have_commands = True
124 | for recording in self.recordings:
125 | pull_cmd = self.recording_pull_cmds.get(recording)
126 | remove_cmd = self.recording_clear_cmds.get(recording)
127 |
128 | if pull_cmd is None or remove_cmd is None:
129 | all_recordings_have_commands = False
130 | break
131 |
132 | return all_recordings_have_commands
133 |
134 | def add_recordings(self, recording_list):
135 | self.recordings.extend(recording_list)
136 |
137 | def add_pull_recording_cmds(self, pull_recording_cmds):
138 | for cmd in pull_recording_cmds:
139 | for recording_name in self.recordings:
140 | if recording_name in cmd:
141 | self.recording_pull_cmds.update({recording_name: cmd})
142 |
143 | def add_clear_recordings_cmd(self, clear_recording_cmds):
144 | for cmd in clear_recording_cmds:
145 | for recording_name in self.recordings:
146 | if recording_name in cmd:
147 | self.recording_clear_cmds.update({recording_name: cmd})
148 |
149 | def kill_processes(self):
150 | self.should_finish = True
151 |
152 |
153 | class TestRecordingThread(threading.Thread):
154 | TAG = "TestRecordingThread
:"
155 |
156 | def __init__(self, start_recording_cmd, recording_name, device):
157 | super().__init__()
158 | self.start_recording_cmd = start_recording_cmd
159 | self.recording_name = recording_name
160 |
161 | self.device = device
162 | self.TAG = self.TAG.replace("device_adb_name", device.adb_name)
163 |
164 | self.device = list()
165 | self.process = None
166 |
167 | def run(self):
168 | with subprocess.Popen(self.start_recording_cmd + self.recording_name, shell=True, stdout=subprocess.PIPE,
169 | bufsize=1, universal_newlines=True) as p:
170 | self.process = p
171 |
172 | def kill_processes(self):
173 | if self.process is not None and hasattr(self.process, "kill"):
174 | self.process.kill()
175 |
176 |
177 | class TestLogCatMonitorThread(threading.Thread):
178 | TAG = "TestLogCatMonitorThread:"
179 |
180 | TEST_STARTED = "TestRunner: started:"
181 | TEST_FINISHED = "TestRunner: finished:"
182 |
183 | LOG_LEVELS = ["D", "I", "W", "V", "E"]
184 |
185 | DATE_INDEX = 0
186 | TIME_INDEX = 1
187 | PID_INDEX = 2
188 | LEVEL_INDEX = 4
189 | TAG_INDEX = 5
190 |
191 | def __init__(self, device, device_commands_dict, should_record_screen):
192 | super().__init__()
193 | self.monitor_logcat_cmd = device_commands_dict["monitor_logcat_cmd"]
194 | self.flush_logcat_cmd = device_commands_dict["flush_logcat_cmd"]
195 | self.record_screen_cmd = device_commands_dict["record_screen_cmd"]
196 |
197 | self.device = device
198 | self.TAG = self.TAG.replace("device_adb_name", device.adb_name)
199 | self.should_record_screen = should_record_screen
200 |
201 | self.logs = list()
202 | self.recordings = list()
203 |
204 | self.logcat_process = None
205 | self.screen_recording_thread = None
206 |
207 | def run(self):
208 | ShellHelper.execute_shell(self.flush_logcat_cmd, False, False)
209 | with subprocess.Popen(self.monitor_logcat_cmd, shell=True, stdout=subprocess.PIPE, bufsize=1,
210 | universal_newlines=True, encoding="utf-8", errors="ignore") as p:
211 | self.logcat_process = p
212 |
213 | current_log = None
214 | current_process_pid = None
215 | current_recording_name = None
216 |
217 | for line in p.stdout:
218 | line_cleaned = line.encode("utf-8", "ignore").decode("utf-8").strip()
219 | line_parts = line_cleaned.split()
220 |
221 | if len(line_parts) <= 5:
222 | continue
223 |
224 | if self.TEST_STARTED in line and current_log is None and current_process_pid is None:
225 | current_log = TestLogCat()
226 |
227 | current_process_pid = line_parts[self.PID_INDEX]
228 |
229 | test_name = re.findall("TestRunner: started:(.+)\(", line_cleaned)
230 | current_log.test_name = test_name[0].strip()
231 |
232 | full_test_package = re.findall("\((.+)\)", line_cleaned)
233 | package_parts = full_test_package[0].split(".")
234 | current_log.test_container = package_parts.pop().strip()
235 | current_log.test_full_package = full_test_package[0].strip() + "." + test_name[0].strip()
236 | if self.should_record_screen:
237 | self._restart_recording(current_log.test_name)
238 | if current_recording_name is not None:
239 | self.recordings.append(current_recording_name)
240 | current_recording_name = self.screen_recording_thread.recording_name
241 |
242 | if current_log is not None:
243 | if line_parts[self.PID_INDEX] == current_process_pid:
244 | logcat_line = TestLogCatLine()
245 |
246 | date = line_parts[self.DATE_INDEX]
247 | logcat_line.date = date
248 |
249 | time_hour = line_parts[self.TIME_INDEX]
250 | logcat_line.time = time_hour
251 |
252 | level = line_parts[self.LEVEL_INDEX]
253 | logcat_line.level = level
254 |
255 | tag = line_parts[self.TAG_INDEX]
256 | if len(tag) > 0 and tag[len(tag) - 1] == ":":
257 | tag = tag[:-1]
258 | logcat_line.tag = tag
259 |
260 | string_pos = line_cleaned.find(tag)
261 | length_tag = len(tag)
262 | text = line_cleaned[(string_pos + length_tag):].strip()
263 | if text.startswith(":"):
264 | text = text[1:]
265 | text = text.strip()
266 | logcat_line.text = text
267 |
268 | current_log.lines.append(logcat_line)
269 |
270 | if self.TEST_FINISHED in line:
271 | self.logs.append(current_log)
272 |
273 | if self.should_record_screen:
274 | self._stop_recording()
275 | self.recordings.append(current_recording_name)
276 |
277 | current_log = None
278 | current_process_pid = None
279 | current_recording_name = None
280 |
281 | def _stop_recording(self):
282 | if self.screen_recording_thread is not None:
283 | self.screen_recording_thread.kill_processes()
284 | self.screen_recording_thread = None
285 |
286 | def _restart_recording(self, test_name):
287 | if self.screen_recording_thread is None or not self.screen_recording_thread.is_alive():
288 | recording_name = test_name + "-" + str(int(round(time.time() * 1000))) + ".mp4"
289 | self.screen_recording_thread = TestRecordingThread(self.record_screen_cmd, recording_name, self.device)
290 | self.screen_recording_thread.start()
291 |
292 | def kill_processes(self):
293 | if self.screen_recording_thread is not None:
294 | self.screen_recording_thread.kill_processes()
295 |
296 | if self.logcat_process is not None and hasattr(self.logcat_process, "kill"):
297 | self.logcat_process.kill()
298 |
299 |
300 | class TestThread(threading.Thread):
301 | TAG = "TestThread:"
302 |
303 | TEST_STATUS_SUCCESS = "success"
304 | TEST_STATUS_FAILURE = "failure"
305 |
306 | TEST_ENDED_WITH_SUCCESS_0 = "INSTRUMENTATION_STATUS_CODE: 0"
307 | TEST_ENDED_WITH_FAILURE = "INSTRUMENTATION_STATUS_CODE: -2"
308 |
309 | TEST_NAME = "INSTRUMENTATION_STATUS: test="
310 | TEST_PACKAGE = "INSTRUMENTATION_STATUS: class="
311 | TEST_OUTPUT_STACK_STARTED = "INSTRUMENTATION_STATUS: stack="
312 | TEST_CURRENT_TEST_NUMBER = "INSTRUMENTATION_STATUS: current"
313 |
314 | def __init__(self, launch_tests_cmd, device):
315 | super().__init__()
316 | self.launch_tests_cmd = launch_tests_cmd
317 |
318 | self.device = device
319 | self.TAG = self.TAG.replace("device_adb_name", device.adb_name)
320 |
321 | self.logs = list()
322 |
323 | self.test_process = None
324 |
325 | def run(self):
326 | Printer.console_highlighted(self.TAG, "Executing shell command: ", self.launch_tests_cmd)
327 | with subprocess.Popen(self.launch_tests_cmd, shell=True, stdout=subprocess.PIPE, bufsize=1,
328 | universal_newlines=True, encoding="utf-8", errors="ignore") as p:
329 | self.test_process = p
330 |
331 | is_using_post_api27_flow = None
332 | reading_stack_in_progress = False
333 | current_log = None
334 | stack = None
335 |
336 | for line in p.stdout:
337 | line_cleaned = line.encode("utf-8", "ignore").decode("utf-8")
338 |
339 | if self.TEST_NAME in line_cleaned and is_using_post_api27_flow is None:
340 | is_using_post_api27_flow = False
341 | elif self.TEST_PACKAGE in line_cleaned and is_using_post_api27_flow is None:
342 | is_using_post_api27_flow = True
343 | elif is_using_post_api27_flow is None:
344 | continue
345 |
346 | if is_using_post_api27_flow:
347 | current_log, stack, reading_stack_in_progress = self._post_api27_logging_flow(
348 | line_cleaned, current_log, stack, reading_stack_in_progress)
349 | else:
350 | current_log, stack, reading_stack_in_progress = self._pre_api27_logging_flow(
351 | line_cleaned, current_log, stack, reading_stack_in_progress)
352 |
353 | def _post_api27_logging_flow(self, line_cleaned, current_log, stack, reading_stack_in_progress):
354 | if self.TEST_PACKAGE in line_cleaned and current_log is None:
355 | current_log = TestSummary()
356 |
357 | line_cleaned = line_cleaned.replace(self.TEST_PACKAGE, "").strip()
358 | package_parts = line_cleaned.split(".")
359 | current_log.test_container = package_parts.pop()
360 | current_log.test_full_package = line_cleaned + "."
361 |
362 | if self.TEST_OUTPUT_STACK_STARTED in line_cleaned:
363 | stack = ""
364 | reading_stack_in_progress = True
365 |
366 | if self.TEST_CURRENT_TEST_NUMBER in line_cleaned:
367 | if stack is not None:
368 | current_log.error_messages.append(stack)
369 | reading_stack_in_progress = False
370 | stack = None
371 |
372 | if self.TEST_ENDED_WITH_SUCCESS_0 in line_cleaned:
373 | current_log.test_end_time = int(round(time.time() * 1000))
374 | current_log.test_status = self.TEST_STATUS_SUCCESS
375 | self.logs.append(current_log)
376 | current_log = None
377 |
378 | if self.TEST_ENDED_WITH_FAILURE in line_cleaned:
379 | current_log.test_end_time = int(round(time.time() * 1000))
380 | current_log.test_status = self.TEST_STATUS_FAILURE
381 | self.logs.append(current_log)
382 | current_log = None
383 |
384 | if self.TEST_NAME in line_cleaned:
385 | current_log.test_name = line_cleaned.replace(self.TEST_NAME, "").strip()
386 | current_log.test_start_time = int(round(time.time() * 1000))
387 | current_log.device = self.device.adb_name
388 |
389 | if current_log.test_name not in current_log.test_full_package:
390 | current_log.test_full_package += current_log.test_name
391 |
392 | Printer.console(self.TAG + " Test " + current_log.test_name + "\n", end='')
393 |
394 | if reading_stack_in_progress:
395 | if self.TEST_OUTPUT_STACK_STARTED in line_cleaned:
396 | test_error_info = self.TAG + " Test " + current_log.test_name + " - FAILED\n"
397 | Printer.console(test_error_info, end="")
398 | line_cleaned = line_cleaned.replace(self.TEST_OUTPUT_STACK_STARTED, "")
399 | stack += line_cleaned
400 | Printer.console(line_cleaned, end="")
401 |
402 | return current_log, stack, reading_stack_in_progress
403 |
404 | def _pre_api27_logging_flow(self, line_cleaned, current_log, stack, reading_stack_in_progress):
405 | if self.TEST_NAME in line_cleaned and current_log is None:
406 | current_log = TestSummary()
407 | current_log.test_name = line_cleaned.replace(self.TEST_NAME, "").strip()
408 | current_log.test_start_time = int(round(time.time() * 1000))
409 | current_log.device = self.device.adb_name
410 |
411 | Printer.console(self.TAG + " Test " + current_log.test_name + "\n", end='')
412 |
413 | if self.TEST_PACKAGE in line_cleaned:
414 | line_cleaned = line_cleaned.replace(self.TEST_PACKAGE, "").strip()
415 | package_parts = line_cleaned.split(".")
416 | current_log.test_container = package_parts.pop()
417 | current_log.test_full_package = line_cleaned + "." + current_log.test_name
418 |
419 | if self.TEST_OUTPUT_STACK_STARTED in line_cleaned:
420 | stack = ""
421 | reading_stack_in_progress = True
422 |
423 | if self.TEST_CURRENT_TEST_NUMBER in line_cleaned:
424 | if stack is not None:
425 | current_log.error_messages.append(stack)
426 | reading_stack_in_progress = False
427 | stack = None
428 |
429 | if self.TEST_ENDED_WITH_SUCCESS_0 in line_cleaned:
430 | current_log.test_end_time = int(round(time.time() * 1000))
431 | current_log.test_status = "success"
432 | self.logs.append(current_log)
433 | current_log = None
434 |
435 | if self.TEST_ENDED_WITH_FAILURE in line_cleaned:
436 | current_log.test_end_time = int(round(time.time() * 1000))
437 | current_log.test_status = "failure"
438 | self.logs.append(current_log)
439 | current_log = None
440 |
441 | if reading_stack_in_progress:
442 | if self.TEST_OUTPUT_STACK_STARTED in line_cleaned:
443 | test_error_info = self.TAG + " Test " + current_log.test_name + " - FAILED\n"
444 | Printer.console(test_error_info, end="")
445 | line_cleaned = line_cleaned.replace(self.TEST_OUTPUT_STACK_STARTED, "")
446 | stack += line_cleaned
447 | Printer.console(line_cleaned, end="")
448 |
449 | return current_log, stack, reading_stack_in_progress
450 |
451 | def kill_processes(self):
452 | if self.test_process is not None and hasattr(self.test_process, "kill"):
453 | self.test_process.kill()
454 |
455 |
456 | class ApkInstallThread(threading.Thread):
457 | TAG = "ApkInstallThread:"
458 |
459 | def __init__(self, dump_badging_cmd, device, apk_name, apk_path):
460 | super().__init__()
461 | self.dump_badging_cmd = dump_badging_cmd
462 |
463 | self.device = device
464 | self.TAG = self.TAG.replace("device_adb_name", device.adb_name)
465 |
466 | self.apk_name = apk_name
467 | self.apk_path = apk_path
468 |
469 | self.install_time = 0
470 | self.note = None
471 | self.is_finished = False
472 |
473 | def run(self):
474 | start_time = int(round(time.time() * 1000))
475 |
476 | package = self._get_apk_package()
477 | installed_packages_str = self.device.get_installed_packages()
478 |
479 | if package in installed_packages_str:
480 | Printer.system_message(self.TAG, "Package " + Color.GREEN + package + Color.BLUE +
481 | " is currently installed on device " + Color.GREEN + self.device.adb_name
482 | + Color.BLUE + ". Removing from device...")
483 | self.device.uninstall_package(package)
484 | else:
485 | Printer.system_message(self.TAG, "Package " + Color.GREEN + package + Color.BLUE +
486 | " was not found on device " + Color.GREEN + self.device.adb_name + Color.BLUE + ".")
487 |
488 | Printer.system_message(self.TAG, "Installing .*apk file...")
489 | self.device.install_apk(self.apk_path)
490 |
491 | end_time = int(round(time.time() * 1000))
492 | self.install_time = (end_time - start_time) / 1000
493 |
494 | Printer.system_message(self.TAG, ".*apk " + Color.GREEN + self.apk_path + Color.BLUE
495 | + " was successfully installed on device " + Color.GREEN + self.device.adb_name
496 | + Color.BLUE + ". It took " + Color.GREEN + str(self.install_time) + Color.BLUE
497 | + " seconds.")
498 | self.is_finished = True
499 |
500 | def _get_apk_package(self):
501 | dump = ShellHelper.execute_shell(self.dump_badging_cmd, False, False)
502 | regex_result = re.findall("package: name='(.+?)'", dump)
503 | if regex_result:
504 | package = str(regex_result[0])
505 | if ".test" in package:
506 | GlobalConfig.APP_TEST_PACKAGE = package
507 | else:
508 | GlobalConfig.APP_PACKAGE = package
509 | Printer.system_message(self.TAG, "Package that is about to be installed: " + Color.GREEN + package
510 | + Color.BLUE + ".")
511 | else:
512 | message = "Unable to find package of .*apk file: " + self.apk_name
513 | raise LauncherFlowInterruptedException(self.TAG, message)
514 | return package
515 |
--------------------------------------------------------------------------------
/settings/GlobalConfig.py:
--------------------------------------------------------------------------------
1 | SHOULD_RESTART_ADB = None
2 | SHOULD_BUILD_NEW_APK = None
3 | SHOULD_RECREATE_EXISTING_AVD = None
4 | SHOULD_LAUNCH_AVD_SEQUENTIALLY = None
5 | SHOULD_USE_ONLY_DEVICES_SPAWNED_IN_SESSION = None
6 | SHOULD_RECORD_TESTS = None
7 | SHOULD_RERUN_FAILED_TESTS = None
8 |
9 | AVD_ADB_BOOT_TIMEOUT = None
10 | AVD_SYSTEM_BOOT_TIMEOUT = None
11 |
12 | IGNORED_DEVICE_LIST = []
13 |
14 | ADB_SCAN_INTERVAL = None
15 | ADB_CALL_BUFFER_SIZE = None
16 | ADB_CALL_BUFFER_DELAY_BETWEEN_CMD = None
17 |
18 | FLAKINESS_RERUN_COUNT = None
19 |
20 | SDK_DIR = ""
21 | AVD_DIR = ""
22 | PROJECT_ROOT_DIR = ""
23 | APK_DIR = ""
24 | OUTPUT_DIR = ""
25 |
26 | OUTPUT_SUMMARY_LOG_DIR = ""
27 | OUTPUT_AVD_LOG_DIR = ""
28 | OUTPUT_TEST_LOG_DIR = ""
29 | OUTPUT_TEST_LOGCAT_DIR = ""
30 | OUTPUT_TEST_RECORDINGS_DIR = ""
31 | OUTPUT_STYLES_FOLDER_DIR = ""
32 | OUTPUT_LOGCAT_HTML_DIR = ""
33 | OUTPUT_INDEX_HTML_DIR = ""
34 |
35 | DEVICE_VIDEO_STORAGE_DIR = ""
36 | LOG_GENERATOR_DIR = ""
37 |
38 | INSTRUMENTATION_RUNNER = ""
39 | APP_PACKAGE = ""
40 | APP_TEST_PACKAGE = ""
41 |
--------------------------------------------------------------------------------
/settings/Version.py:
--------------------------------------------------------------------------------
1 | import sys
2 |
3 | from error.Exceptions import LauncherFlowInterruptedException
4 |
5 | from system.console import (
6 | Printer,
7 | Color
8 | )
9 |
10 | NUMBER = "1.1-beta"
11 | MIN_PYTHON_VER = (3, 6)
12 |
13 |
14 | def info():
15 | Printer.system_message("", "AutomationTestSupervisor version: " + Color.GREEN + NUMBER + Color.BLUE + ".")
16 |
17 |
18 | def python_check():
19 | if sys.version_info >= MIN_PYTHON_VER:
20 | Printer.system_message("", "Minimum Python version requirement met! Your version: " + Color.GREEN
21 | + str(sys.version_info) + Color.BLUE + ".")
22 |
23 | else:
24 | message = ("Invalid Python version. Please use at least Python " + str(MIN_PYTHON_VER[0]) + "."
25 | + str(MIN_PYTHON_VER[1]) + ".")
26 | raise LauncherFlowInterruptedException("", message)
27 |
--------------------------------------------------------------------------------
/settings/loader/ArgLoader.py:
--------------------------------------------------------------------------------
1 | import argparse
2 | import os
3 |
4 | from system.file.FileUtils import (
5 | clean_path,
6 | get_project_root,
7 | load_json,
8 | add_ending_slash
9 | )
10 |
11 | TAG = "ArgLoader:"
12 |
13 | CONFIG_FILES_DIR_DEFAULT_DIR = clean_path(add_ending_slash(get_project_root()) + "config_files_dir.json")
14 |
15 | LAUNCH_MANIFEST_DIR_KEY = "launch_manifest_path"
16 | TEST_MANIFEST_DIR_KEY = "test_manifest_path"
17 | AVD_MANIFEST_DIR_KEY = "avd_manifest_path"
18 | PATH_MANIFEST_DIR_KEY = "path_manifest_path"
19 |
20 | config_files_dir = load_json(CONFIG_FILES_DIR_DEFAULT_DIR)
21 |
22 |
23 | def get_manifest_dir(key):
24 | if config_files_dir is None:
25 | return None
26 | else:
27 | return config_files_dir[key]
28 |
29 |
30 | LAUNCH_PLAN_DEFAULT = "default"
31 | TEST_SET_DEFAULT = "default"
32 | AVD_SET_DEFAULT = "default"
33 | PATH_SET_DEFAULT = "default"
34 |
35 | LAUNCH_PLAN_PREFIX = "-lplan"
36 | TEST_SET_PREFIX = "-tset"
37 | AVD_SET_PREFIX = "-aset"
38 | PATH_SET_PREFIX = "-pset"
39 |
40 | parser = argparse.ArgumentParser()
41 | parser.add_argument(LAUNCH_PLAN_PREFIX,
42 | type=str,
43 | default=LAUNCH_PLAN_DEFAULT,
44 | help="Name of launch plan specified in LaunchManifest.json.")
45 |
46 | parser.add_argument(TEST_SET_PREFIX,
47 | nargs="+",
48 | default=[TEST_SET_DEFAULT],
49 | help="Name of test set specified in TestManifest.json.")
50 |
51 | parser.add_argument(AVD_SET_PREFIX,
52 | type=str,
53 | default=AVD_SET_DEFAULT,
54 | help="Name of AVD set specified in AvdManifest.json.")
55 |
56 | parser.add_argument(PATH_SET_PREFIX,
57 | type=str,
58 | default=PATH_SET_DEFAULT,
59 | help="Name of path set set specified in PathManifest.json.")
60 |
61 | parser_args = parser.parse_args()
62 |
63 |
64 | def get_arg_loaded_by(param):
65 | if param == LAUNCH_PLAN_PREFIX:
66 | return parser_args.lplan
67 | if param == TEST_SET_PREFIX:
68 | return parser_args.tset
69 | if param == AVD_SET_PREFIX:
70 | return parser_args.aset
71 | if param == PATH_SET_PREFIX:
72 | return parser_args.pset
73 | return None
74 |
--------------------------------------------------------------------------------
/settings/loader/AvdSetLoader.py:
--------------------------------------------------------------------------------
1 | from error.Exceptions import LauncherFlowInterruptedException
2 |
3 | from settings.loader import ArgLoader
4 | from settings.manifest.models.AvdManifestModels import AvdManifest
5 |
6 | from system.console import (
7 | Printer,
8 | Color
9 | )
10 |
11 | from system.file.FileUtils import (
12 | make_path_absolute
13 | )
14 |
15 | TAG = "AvdSetLoader:"
16 |
17 |
18 | def init_avd_settings():
19 | avd_set_name = _load_avd_set_name()
20 | avd_manifest = _load_avd_manifest()
21 |
22 | avd_set = None
23 | avd_schema_dict = None
24 |
25 | if avd_set_name != ArgLoader.AVD_SET_DEFAULT:
26 | avd_set = _load_avd_set(avd_manifest, avd_set_name)
27 | avd_schema_dict = _load_avd_schema(avd_manifest, avd_set, avd_set_name)
28 |
29 | return avd_set, avd_schema_dict
30 |
31 |
32 | def _load_avd_set_name():
33 | avd_set_name = ArgLoader.get_arg_loaded_by(ArgLoader.AVD_SET_PREFIX)
34 |
35 | if avd_set_name is None:
36 | Printer.system_message(TAG, "No AVD set selected. ""Currently available real devices will be used in test "
37 | "session.")
38 | else:
39 | Printer.system_message(TAG, "Selected avd set: " + Color.GREEN + avd_set_name + Color.BLUE + ".")
40 | return avd_set_name
41 |
42 |
43 | def _load_avd_manifest():
44 | avd_manifest_dir = make_path_absolute(ArgLoader.get_manifest_dir(ArgLoader.AVD_MANIFEST_DIR_KEY))
45 |
46 | if avd_manifest_dir is None:
47 | message = ("AvdManifest file directory was not found. Check if config_files_dir.json exists in root of"
48 | + "project. Otherwise check if it's linking to existing file.")
49 | raise LauncherFlowInterruptedException(TAG, message)
50 | else:
51 | avd_manifest = AvdManifest(avd_manifest_dir)
52 | Printer.system_message(TAG, "Created AvdManifest from file: " + Color.GREEN + avd_manifest_dir + Color.BLUE
53 | + ".")
54 | return avd_manifest
55 |
56 |
57 | def _load_avd_set(avd_manifest, avd_set_name):
58 | if avd_manifest.contains_set(avd_set_name):
59 | Printer.system_message(TAG, "AVD set " + Color.GREEN + avd_set_name + Color.BLUE + " was found in AvdManifest.")
60 |
61 | avd_set = avd_manifest.get_set(avd_set_name)
62 | Printer.system_message(TAG, "Requested AVD in set:")
63 | for requested_avd_schema in avd_set.avd_list:
64 | Printer.system_message(TAG, " * " + Color.GREEN + requested_avd_schema.avd_name + Color.BLUE
65 | + " - instances num: " + Color.GREEN + str(requested_avd_schema.instances)
66 | + Color.BLUE + ".")
67 | else:
68 | message = "Invalid AVD set. Set '{}' does not exist in AvdManifest!"
69 | message = message.format(avd_set_name)
70 | raise LauncherFlowInterruptedException(TAG, message)
71 |
72 | return avd_manifest.get_set(avd_set_name)
73 |
74 |
75 | def _load_avd_schema(avd_manifest, avd_set, avd_set_name):
76 | avd_schema_dict = avd_manifest.avd_schema_dict
77 |
78 | for avd in avd_set.avd_list:
79 | if avd_manifest.contains_schema(avd.avd_name):
80 | Printer.system_message(TAG, "AVD schema " + Color.GREEN + avd.avd_name + Color.BLUE
81 | + " was found in AvdManifest.")
82 | else:
83 | message = "Set '{}' requests usage of AVD schema with name '{}' which doesn't exists in AVD schema list."
84 | message = message.format(avd_set_name, avd.avd_name)
85 | raise LauncherFlowInterruptedException(TAG, message)
86 |
87 | return avd_schema_dict
88 |
--------------------------------------------------------------------------------
/settings/loader/LaunchPlanLoader.py:
--------------------------------------------------------------------------------
1 | from error.Exceptions import LauncherFlowInterruptedException
2 | from settings import GlobalConfig
3 | from settings.loader import ArgLoader
4 | from settings.manifest.models.LaunchManifestModels import LaunchManifest
5 | from system.console import Color
6 | from system.console import Printer
7 |
8 | from system.file.FileUtils import (
9 | make_path_absolute
10 | )
11 |
12 | TAG = "LaunchPlanLoader:"
13 |
14 |
15 | def init_launch_plan():
16 | launch_plan_name = _load_launch_plan_name()
17 | launch_manifest = _load_launch_plan_manifest()
18 | launch_plan = _load_launch_plan(launch_manifest, launch_plan_name)
19 |
20 | _load_launch_plan_to_global_settings(launch_plan)
21 |
22 |
23 | def _load_launch_plan_to_global_settings(launch_plan):
24 | avd_set = ArgLoader.get_arg_loaded_by(ArgLoader.AVD_SET_PREFIX)
25 | is_avd_session_requested = avd_set is not None and avd_set != ArgLoader.AVD_SET_DEFAULT
26 |
27 | Printer.system_message(TAG, "General:")
28 | general_settings = launch_plan.general
29 |
30 | if is_avd_session_requested:
31 | GlobalConfig.SHOULD_USE_ONLY_DEVICES_SPAWNED_IN_SESSION = True
32 | else:
33 | GlobalConfig.SHOULD_USE_ONLY_DEVICES_SPAWNED_IN_SESSION = False
34 |
35 | GlobalConfig.ADB_CALL_BUFFER_SIZE = general_settings.adb_call_buffer_size
36 | if GlobalConfig.ADB_CALL_BUFFER_SIZE > 0:
37 | Printer.system_message(TAG, " * ADB call buffer size set to: " + Color.GREEN +
38 | str(GlobalConfig.ADB_CALL_BUFFER_SIZE) + " slot(s)" + Color.BLUE + ".")
39 | else:
40 | message = "ADB_CALL_BUFFER_SIZE cannot be smaller than 1. Launcher will quit."
41 | raise LauncherFlowInterruptedException(TAG, message)
42 |
43 | GlobalConfig.ADB_CALL_BUFFER_DELAY_BETWEEN_CMD = general_settings.adb_call_buffer_delay_between_cmd
44 | if GlobalConfig.ADB_CALL_BUFFER_DELAY_BETWEEN_CMD >= 0:
45 | if GlobalConfig.ADB_CALL_BUFFER_DELAY_BETWEEN_CMD == 0:
46 | Printer.system_message(TAG, " * ADB call buffer is disabled. ADB_CALL_BUFFER_DELAY_BETWEEN_CMD"
47 | " param set to: " + + Color.GREEN + "0 second(s)" + + Color.BLUE + ".")
48 | else:
49 | Printer.system_message(TAG, " * ADB call buffer will clear slots after " + Color.GREEN +
50 | str(GlobalConfig.ADB_CALL_BUFFER_DELAY_BETWEEN_CMD / 1000) + " second(s)" +
51 | Color.BLUE + " from ADB call.")
52 | else:
53 | message = "ADB_CALL_BUFFER_DELAY_BETWEEN_CMD cannot be negative! Launcher will quit."
54 | raise LauncherFlowInterruptedException(TAG, message)
55 |
56 | if is_avd_session_requested:
57 | Printer.system_message(TAG, "Device preparation phase settings:")
58 | device_prep_settings = launch_plan.device_preparation_phase
59 |
60 | GlobalConfig.SHOULD_RECREATE_EXISTING_AVD = device_prep_settings.avd_should_recreate_existing
61 | if GlobalConfig.SHOULD_RECREATE_EXISTING_AVD:
62 | Printer.system_message(TAG, " * If requested AVD already exists - it will be" + Color.GREEN +
63 | " recreated from scratch" + Color.BLUE + ".")
64 | else:
65 | Printer.system_message(TAG, " * If requested AVD already exists - it will be" + Color.GREEN +
66 | " reused" + Color.BLUE + ".")
67 |
68 | Printer.system_message(TAG, "Device launching phase settings:")
69 | device_launch_settings = launch_plan.device_launching_phase
70 |
71 | GlobalConfig.IGNORED_DEVICE_LIST = device_launch_settings.device_android_id_to_ignore
72 | Printer.system_message(TAG, " * Devices with following Android-IDs will be ignored: " + Color.GREEN +
73 | str(GlobalConfig.IGNORED_DEVICE_LIST) + Color.BLUE + ".")
74 |
75 | if is_avd_session_requested:
76 | GlobalConfig.SHOULD_LAUNCH_AVD_SEQUENTIALLY = device_launch_settings.avd_launch_sequentially
77 | if GlobalConfig.SHOULD_LAUNCH_AVD_SEQUENTIALLY:
78 | Printer.system_message(TAG,
79 | " * AVD will be launched " + Color.GREEN + "one by one" + Color.BLUE +
80 | ". Wait for start will be performed for each AVD separately and it will take more"
81 | " time.")
82 | else:
83 | Printer.system_message(TAG, " * AVD will be launched " + Color.GREEN + "all at once" + Color.BLUE + ".")
84 | Printer.error(TAG, "Warning: when launching AVD simultaneously ADB is unaware of the amount of memory"
85 | " that specific AVD will use. If there is not enough memory in the system and you launch"
86 | " too many AVD at the same time your PC might turn off due to lack of RAM memory.")
87 |
88 | GlobalConfig.ADB_SCAN_INTERVAL = device_launch_settings.avd_status_scan_interval_millis
89 | if GlobalConfig.ADB_SCAN_INTERVAL is "":
90 | message = " * ADB_SCAN_INTERVAL not specified in LaunchManifest. Launcher will quit."
91 | raise LauncherFlowInterruptedException(TAG, message)
92 | else:
93 | Printer.system_message(TAG, " * ADB will be scanned with interval of " + Color.GREEN +
94 | str(GlobalConfig.ADB_SCAN_INTERVAL / 1000) + " second(s)" + Color.BLUE + ".")
95 |
96 | GlobalConfig.AVD_ADB_BOOT_TIMEOUT = device_launch_settings.avd_wait_for_adb_boot_timeout_millis
97 | if GlobalConfig.AVD_ADB_BOOT_TIMEOUT is "":
98 | message = " * AVD_ADB_BOOT_TIMEOUT not specified in LaunchManifest. Launcher will quit."
99 | raise LauncherFlowInterruptedException(TAG, message)
100 | else:
101 | Printer.system_message(TAG, " * AVD - ADB boot timeout set to " + Color.GREEN +
102 | str(GlobalConfig.AVD_ADB_BOOT_TIMEOUT / 1000) + " second(s)" + Color.BLUE + ".")
103 |
104 | GlobalConfig.AVD_SYSTEM_BOOT_TIMEOUT = device_launch_settings.avd_wait_for_system_boot_timeout_millis
105 | if GlobalConfig.AVD_SYSTEM_BOOT_TIMEOUT is "":
106 | message = " * AVD_SYSTEM_BOOT_TIMEOUT not specified in LaunchManifest. Launcher will quit."
107 | raise LauncherFlowInterruptedException(TAG, message)
108 | else:
109 | Printer.system_message(TAG, " * AVD - ADB system boot timeout set to " + Color.GREEN +
110 | str(GlobalConfig.AVD_SYSTEM_BOOT_TIMEOUT / 1000) + " second(s)" + Color.BLUE + ".")
111 |
112 | GlobalConfig.SHOULD_RESTART_ADB = device_launch_settings.device_before_launching_restart_adb
113 | if GlobalConfig.SHOULD_RESTART_ADB:
114 | Printer.system_message(TAG, " * " + Color.GREEN + "ADB will be restarted" + Color.BLUE
115 | + " before launching tests.")
116 |
117 | Printer.system_message(TAG, "Apk preparation phase settings:")
118 | apk_preparation_settings = launch_plan.apk_preparation_phase
119 |
120 | GlobalConfig.SHOULD_BUILD_NEW_APK = apk_preparation_settings.build_new_apk
121 | if GlobalConfig.SHOULD_BUILD_NEW_APK:
122 | Printer.system_message(TAG, " * Launcher will " + Color.GREEN + "build .*apk" + Color.BLUE
123 | + " for tests with commands specified in test set.")
124 | else:
125 | Printer.system_message(TAG, " * Launcher will " + Color.GREEN + "look for existing .*apk" + Color.BLUE
126 | + " look for existing .*apk for tests and try to build only if nothing was found.")
127 |
128 | Printer.system_message(TAG, "Test run phase settings:")
129 | testing_phase = launch_plan.testing_phase
130 |
131 | GlobalConfig.SHOULD_RECORD_TESTS = testing_phase.record_tests
132 | if GlobalConfig.SHOULD_RECORD_TESTS:
133 | Printer.system_message(TAG, " * Launcher will " + Color.GREEN + "record device screens" + Color.BLUE
134 | + " during test session.")
135 | else:
136 | Printer.system_message(TAG, " * Launcher test " + Color.GREEN + "recording is turned off"
137 | + Color.BLUE + ".")
138 |
139 | Printer.system_message(TAG, "Flakiness check phase settings:")
140 | flakiness_check_phase = launch_plan.flakiness_check_phase
141 |
142 | GlobalConfig.SHOULD_RERUN_FAILED_TESTS = flakiness_check_phase.should_rerun_failed_tests
143 | GlobalConfig.FLAKINESS_RERUN_COUNT = flakiness_check_phase.rerun_count
144 | if GlobalConfig.SHOULD_RERUN_FAILED_TESTS:
145 | Printer.system_message(TAG, " * Launcher will " + Color.GREEN + "re-run failed tests {} times".format(
146 | GlobalConfig.FLAKINESS_RERUN_COUNT) + Color.BLUE + " after test session ends.")
147 | else:
148 | Printer.system_message(TAG, " * Launcher test " + Color.GREEN + "re-running option is turned off"
149 | + Color.BLUE + ".")
150 |
151 |
152 | def _load_launch_plan_name():
153 | launch_plan_name = ArgLoader.get_arg_loaded_by(ArgLoader.LAUNCH_PLAN_PREFIX)
154 |
155 | if launch_plan_name is None:
156 | message = "No launch plan selected. Launcher will quit."
157 | raise LauncherFlowInterruptedException(TAG, message)
158 | else:
159 | Printer.system_message(TAG, "Selected launch plan: " + Color.GREEN + launch_plan_name + Color.BLUE + ".")
160 | return launch_plan_name
161 |
162 |
163 | def _load_launch_plan_manifest():
164 | launch_manifest_dir = make_path_absolute(ArgLoader.get_manifest_dir(ArgLoader.LAUNCH_MANIFEST_DIR_KEY))
165 |
166 | if launch_manifest_dir is None:
167 | message = ("LaunchManifest file directory was not found. Check if config_files_dir.json exists in root "
168 | "of project. Otherwise check if it's linking to existing file.")
169 | raise LauncherFlowInterruptedException(TAG, message)
170 | else:
171 | launch_manifest = LaunchManifest(launch_manifest_dir)
172 | Printer.system_message(TAG, "Created LaunchManifest from file: " + Color.GREEN + launch_manifest_dir
173 | + Color.BLUE + ".")
174 | return launch_manifest
175 |
176 |
177 | def _load_launch_plan(launch_manifest, launch_plan_name):
178 | if launch_manifest.contains_plan(launch_plan_name):
179 | Printer.system_message(TAG, "Launch plan " + Color.GREEN + launch_plan_name + Color.BLUE
180 | + " was found in LaunchManifest.")
181 | return launch_manifest.get_plan(launch_plan_name)
182 | else:
183 | message = "Invalid launch plan with name '{}' does not exist in LaunchManifest!"
184 | message = message.format(launch_plan_name)
185 | raise LauncherFlowInterruptedException(TAG, message)
186 |
--------------------------------------------------------------------------------
/settings/loader/PathsLoader.py:
--------------------------------------------------------------------------------
1 | import os
2 |
3 | from error.Exceptions import LauncherFlowInterruptedException
4 | from settings import GlobalConfig
5 | from settings.loader import ArgLoader
6 | from settings.manifest.models.PathManifestModels import PathManifest
7 | from system.console import (
8 | Printer,
9 | Color
10 | )
11 | from system.file.FileUtils import (
12 | clean_folder_only_dir,
13 | make_path_absolute
14 | )
15 |
16 | HOME = "~/"
17 |
18 | ANDROID_HOME_ENV = os.getenv('ANDROID_HOME')
19 | ANDROID_SDK_HOME_ENV = os.getenv('ANDROID_SDK_HOME')
20 |
21 | OUTPUT_DIR_DEFAULT = os.path.abspath(os.path.dirname(__name__)) + "/output/"
22 | OUTPUT_SUMMARY_LOG_FOLDER_DEFAULT = "/summary/"
23 | OUTPUT_AVD_LOG_FOLDER_DEFAULT = "/avd_logs/"
24 | OUTPUT_TEST_LOG_FOLDER_DEFAULT = "/test_results/"
25 | OUTPUT_TEST_LOGCAT_FOLDER_DEFAULT = "/test_logcats/"
26 | OUTPUT_TEST_VIDEO_FOLDER_DEFAULT = "/recordings/"
27 | OUTPUT_TEST_LOGCAT_HTML_FOLDER_DEFAULT = "/test_logcats_html/"
28 | OUTPUT_HTML_STYLES_FOLDER_DEFAULT = "/styles"
29 | OUTPUT_HTML_INDEX_FILE_NAME = "index.html"
30 |
31 | LOG_GENERATOR_DIR_DEFAULT = os.path.abspath(os.path.dirname(__name__)) + "/log_generator/"
32 |
33 | DEVICE_VIDEO_STORAGE_FOLDER_DEFAULT = "/sdcard/test_automation_recordings/"
34 |
35 | TAG = "PathsLoader:"
36 |
37 |
38 | def init_paths():
39 | _display_manifest_source_info()
40 |
41 | path_set_name = _load_path_set_name()
42 | path_manifest = _load_path_manifest()
43 | path_set = _load_path_set(path_manifest, path_set_name)
44 |
45 | _load_paths_to_global_settings(path_set)
46 |
47 |
48 | def _display_manifest_source_info():
49 | Printer.system_message(TAG,
50 | "File used for locating manifest files: " + Color.GREEN
51 | + ArgLoader.CONFIG_FILES_DIR_DEFAULT_DIR + Color.BLUE + ".")
52 |
53 |
54 | def _load_path_set_name():
55 | path_set_name = ArgLoader.get_arg_loaded_by(ArgLoader.PATH_SET_PREFIX)
56 | if path_set_name is None:
57 | message = "No path set was selected. Launcher will quit."
58 | raise LauncherFlowInterruptedException(TAG, message)
59 | else:
60 | Printer.system_message(TAG, "Selected path set: " + Color.GREEN + path_set_name + Color.BLUE + ".")
61 | return path_set_name
62 |
63 |
64 | def _load_path_manifest():
65 | path_manifest_dir = make_path_absolute(ArgLoader.get_manifest_dir(ArgLoader.PATH_MANIFEST_DIR_KEY))
66 |
67 | if path_manifest_dir is None:
68 | message = ("PathManifest file directory was not found. Check if config_files_dir.json exists in root "
69 | "of project. Otherwise check if it's linking to existing file.")
70 | raise LauncherFlowInterruptedException(TAG, message)
71 | else:
72 | path_manifest = PathManifest(path_manifest_dir)
73 | Printer.system_message(TAG, "Created PathManifest from file: " + Color.GREEN + path_manifest_dir + Color.BLUE
74 | + ".")
75 | return path_manifest
76 |
77 |
78 | def _load_path_set(path_manifest, path_set_name):
79 | if path_manifest.contains_set(path_set_name):
80 | Printer.system_message(TAG, "Path set " + Color.GREEN + path_set_name + Color.BLUE
81 | + " was found in PathManifest.")
82 | return path_manifest.get_set(path_set_name)
83 | else:
84 | message = "Invalid path set with name '{}' does not exist in PathManifest!"
85 | message = message.format(path_set_name)
86 | raise LauncherFlowInterruptedException(TAG, message)
87 |
88 |
89 | def _load_paths_to_global_settings(path_set):
90 | GlobalConfig.SDK_DIR = clean_folder_only_dir((path_set.paths["sdk_dir"]).path_value)
91 | if GlobalConfig.SDK_DIR == "":
92 | Printer.system_message(TAG, "SDK path not set in PathManifest. Will use path set in env variable "
93 | + Color.GREEN + "ANDROID_HOME" + Color.BLUE + ".")
94 | if ANDROID_HOME_ENV is None:
95 | message = "Env variable 'ANDROID_HOME' is not set. Launcher will quit."
96 | raise LauncherFlowInterruptedException(TAG, message)
97 | else:
98 | GlobalConfig.SDK_DIR = clean_folder_only_dir(make_path_absolute(ANDROID_HOME_ENV))
99 | Printer.system_message(TAG, "Launcher will look for SDK in dir: " + Color.GREEN + GlobalConfig.SDK_DIR
100 | + Color.BLUE + ".")
101 |
102 | GlobalConfig.AVD_DIR = clean_folder_only_dir((path_set.paths["avd_dir"]).path_value)
103 | if GlobalConfig.AVD_DIR == "":
104 | Printer.system_message(TAG, "AVD path not set in PathManifest. "
105 | "Will use path set in env variable 'ANDROID_SDK_HOME'.")
106 | if ANDROID_SDK_HOME_ENV is None:
107 | Printer.system_message(TAG, "Env variable 'ANDROID_SDK_HOME' is not set. "
108 | "Trying to recreate default path from user root.")
109 | GlobalConfig.AVD_DIR = clean_folder_only_dir(make_path_absolute(HOME)) + ".android"
110 | Printer.system_message(TAG, "Launcher will look for AVD images in dir: " + Color.GREEN + GlobalConfig.AVD_DIR
111 | + Color.BLUE + ".")
112 |
113 | GlobalConfig.PROJECT_ROOT_DIR = clean_folder_only_dir(
114 | make_path_absolute((path_set.paths["project_root_dir"]).path_value))
115 | if GlobalConfig.PROJECT_ROOT_DIR == "":
116 | Printer.system_message(TAG, "Project root was not specified. This field is not obligatory.")
117 | Printer.error(TAG, "Warning: Without project root directory launcher will quit if no "
118 | ".*apk files will be found in directory loaded from 'apk_dir' field of PathManifest.")
119 | else:
120 | Printer.system_message(TAG, "Android project root dir set to: " + Color.GREEN + GlobalConfig.PROJECT_ROOT_DIR
121 | + Color.BLUE + ".")
122 |
123 | GlobalConfig.APK_DIR = clean_folder_only_dir(make_path_absolute((path_set.paths["apk_dir"]).path_value))
124 | if GlobalConfig.APK_DIR == "":
125 | message = "Directory with .*apk files was not specified. Launcher will quit."
126 | raise LauncherFlowInterruptedException(TAG, message)
127 | Printer.system_message(TAG, "Launcher will look for .*apk files in dir: " + Color.GREEN + GlobalConfig.APK_DIR
128 | + Color.BLUE + ".")
129 |
130 | GlobalConfig.OUTPUT_DIR = clean_folder_only_dir((path_set.paths["output_dir"]).path_value)
131 | if GlobalConfig.OUTPUT_DIR == "":
132 | Printer.system_message(TAG, "Output path not set in PathManifest. Default value will be used.")
133 | GlobalConfig.OUTPUT_DIR = OUTPUT_DIR_DEFAULT
134 | if not os.path.isabs(GlobalConfig.OUTPUT_DIR):
135 | message = "Path " + GlobalConfig.OUTPUT_DIR + " needs to be absolute!"
136 | raise LauncherFlowInterruptedException(TAG, message)
137 | Printer.system_message(TAG, "Launcher will generate log from tests in dir: " + Color.GREEN +
138 | GlobalConfig.OUTPUT_DIR + Color.BLUE + ".")
139 |
140 | GlobalConfig.OUTPUT_SUMMARY_LOG_DIR = clean_folder_only_dir(
141 | GlobalConfig.OUTPUT_DIR + OUTPUT_SUMMARY_LOG_FOLDER_DEFAULT)
142 | if not os.path.isabs(GlobalConfig.OUTPUT_SUMMARY_LOG_DIR):
143 | message = "Path " + GlobalConfig.OUTPUT_SUMMARY_LOG_DIR + " needs to be absolute!"
144 | raise LauncherFlowInterruptedException(TAG, message)
145 | Printer.system_message(TAG,
146 | "Summary log will be stored in dir: " + Color.GREEN + GlobalConfig.OUTPUT_SUMMARY_LOG_DIR
147 | + Color.BLUE + ".")
148 |
149 | GlobalConfig.OUTPUT_AVD_LOG_DIR = clean_folder_only_dir(GlobalConfig.OUTPUT_DIR + OUTPUT_AVD_LOG_FOLDER_DEFAULT)
150 | if not os.path.isabs(GlobalConfig.OUTPUT_AVD_LOG_DIR):
151 | message = "Path " + GlobalConfig.OUTPUT_AVD_LOG_DIR + " needs to be absolute!"
152 | raise LauncherFlowInterruptedException(TAG, message)
153 | Printer.system_message(TAG, "Logs from AVD will be stored in dir: " + Color.GREEN + GlobalConfig.OUTPUT_AVD_LOG_DIR
154 | + Color.BLUE + ".")
155 |
156 | GlobalConfig.OUTPUT_TEST_LOG_DIR = clean_folder_only_dir(GlobalConfig.OUTPUT_DIR + OUTPUT_TEST_LOG_FOLDER_DEFAULT)
157 | if not os.path.isabs(GlobalConfig.OUTPUT_TEST_LOG_DIR):
158 | message = "Path " + GlobalConfig.OUTPUT_TEST_LOG_DIR + " needs to be absolute!"
159 | raise LauncherFlowInterruptedException(TAG, message)
160 | Printer.system_message(TAG, "Logs from tests will be stored in dir: " + Color.GREEN +
161 | GlobalConfig.OUTPUT_TEST_LOG_DIR + Color.BLUE + ".")
162 |
163 | GlobalConfig.OUTPUT_TEST_LOGCAT_DIR = clean_folder_only_dir(
164 | GlobalConfig.OUTPUT_DIR + OUTPUT_TEST_LOGCAT_FOLDER_DEFAULT)
165 | if not os.path.isabs(GlobalConfig.OUTPUT_TEST_LOGCAT_DIR):
166 | message = "Path " + GlobalConfig.OUTPUT_TEST_LOGCAT_DIR + " needs to be absolute!"
167 | raise LauncherFlowInterruptedException(TAG, message)
168 | Printer.system_message(TAG, "Logcat logs from tests will be stored in dir: " + Color.GREEN +
169 | GlobalConfig.OUTPUT_TEST_LOGCAT_DIR + Color.BLUE + ".")
170 |
171 | GlobalConfig.DEVICE_VIDEO_STORAGE_DIR = clean_folder_only_dir(DEVICE_VIDEO_STORAGE_FOLDER_DEFAULT)
172 | Printer.system_message(TAG, "Firstly recordings will be saved in root directory of test device storage in " +
173 | Color.GREEN + GlobalConfig.DEVICE_VIDEO_STORAGE_DIR + Color.BLUE + " folder.")
174 |
175 | GlobalConfig.OUTPUT_TEST_RECORDINGS_DIR = clean_folder_only_dir(
176 | GlobalConfig.OUTPUT_DIR + OUTPUT_TEST_VIDEO_FOLDER_DEFAULT)
177 | if not os.path.isabs(GlobalConfig.OUTPUT_TEST_RECORDINGS_DIR):
178 | message = "Path " + GlobalConfig.OUTPUT_TEST_RECORDINGS_DIR + " needs to be absolute!"
179 | raise LauncherFlowInterruptedException(TAG, message)
180 | Printer.system_message(TAG, "Secondly recordings from tests will be pulled from each device to dir: " + Color.GREEN
181 | + GlobalConfig.OUTPUT_TEST_RECORDINGS_DIR + Color.BLUE + ".")
182 |
183 | GlobalConfig.OUTPUT_STYLES_FOLDER_DIR = clean_folder_only_dir(
184 | GlobalConfig.OUTPUT_DIR + OUTPUT_HTML_STYLES_FOLDER_DEFAULT)
185 | if not os.path.isabs(GlobalConfig.OUTPUT_STYLES_FOLDER_DIR):
186 | message = "Path " + GlobalConfig.OUTPUT_STYLES_FOLDER_DIR + " needs to be absolute!"
187 | raise LauncherFlowInterruptedException(TAG, message)
188 | Printer.system_message(TAG, "Styles for all html logs will be stored in dir: " + Color.GREEN
189 | + GlobalConfig.OUTPUT_STYLES_FOLDER_DIR + Color.BLUE + ".")
190 |
191 | GlobalConfig.OUTPUT_LOGCAT_HTML_DIR = clean_folder_only_dir(
192 | GlobalConfig.OUTPUT_DIR + OUTPUT_TEST_LOGCAT_HTML_FOLDER_DEFAULT)
193 | if not os.path.isabs(GlobalConfig.OUTPUT_LOGCAT_HTML_DIR):
194 | message = "Path " + GlobalConfig.OUTPUT_TEST_RECORDINGS_DIR + " needs to be absolute!"
195 | raise LauncherFlowInterruptedException(TAG, message)
196 | Printer.system_message(TAG, "Html logs presenting logcats from devices will be stored in dir: " + Color.GREEN
197 | + GlobalConfig.OUTPUT_LOGCAT_HTML_DIR + Color.BLUE + ".")
198 |
199 | GlobalConfig.OUTPUT_INDEX_HTML_DIR = clean_folder_only_dir(GlobalConfig.OUTPUT_DIR) + OUTPUT_HTML_INDEX_FILE_NAME
200 | if not os.path.isabs(GlobalConfig.OUTPUT_INDEX_HTML_DIR):
201 | message = "Path " + GlobalConfig.OUTPUT_TEST_RECORDINGS_DIR + " needs to be absolute!"
202 | raise LauncherFlowInterruptedException(TAG, message)
203 | Printer.system_message(TAG, "All html logs containing results from tests, logcats and videos can be accessed from "
204 | + Color.GREEN + GlobalConfig.OUTPUT_INDEX_HTML_DIR + Color.BLUE + " file generated after "
205 | + "session ends.")
206 |
207 | GlobalConfig.LOG_GENERATOR_DIR = clean_folder_only_dir(LOG_GENERATOR_DIR_DEFAULT)
208 | if not os.path.isabs(GlobalConfig.LOG_GENERATOR_DIR):
209 | message = "Path " + GlobalConfig.LOG_GENERATOR_DIR + " needs to be absolute!"
210 | raise LauncherFlowInterruptedException(TAG, message)
211 | Printer.system_message(TAG, "Logs will be generated with usage of " + Color.GREEN + "LogGenerator.py" + Color.BLUE +
212 | " file stored in dir: " + Color.GREEN + GlobalConfig.LOG_GENERATOR_DIR + Color.BLUE + ".")
213 |
--------------------------------------------------------------------------------
/settings/loader/TestSetLoader.py:
--------------------------------------------------------------------------------
1 | from error.Exceptions import LauncherFlowInterruptedException
2 |
3 | from settings.loader import ArgLoader
4 | from settings.manifest.models.TestManifestModels import TestManifest, TestSet
5 |
6 | from system.console import (
7 | Printer,
8 | Color
9 | )
10 |
11 | from system.file.FileUtils import (
12 | make_path_absolute
13 | )
14 |
15 | TAG = "TestSetLoader:"
16 |
17 |
18 | def init_test_settings():
19 | test_set_names = _load_test_set_name()
20 | test_manifest = _load_manifest()
21 | test_list = _load_test_list(test_manifest)
22 | test_set = _load_test_set(test_manifest, test_set_names)
23 |
24 | return test_set, test_list
25 |
26 |
27 | def _load_test_set_name():
28 | test_set_names = ArgLoader.get_arg_loaded_by(ArgLoader.TEST_SET_PREFIX)
29 | if test_set_names is None or len(test_set_names) == 0:
30 | message = "No test set inserted. Launcher will quit."
31 | raise LauncherFlowInterruptedException(TAG, message)
32 | else:
33 | Printer.system_message(TAG, "Selected test sets: ")
34 | for t_set_name in test_set_names:
35 | Printer.system_message(TAG, " * " + Color.GREEN + t_set_name + Color.BLUE)
36 | return test_set_names
37 |
38 |
39 | def _load_manifest():
40 | test_manifest_dir = make_path_absolute(ArgLoader.get_manifest_dir(ArgLoader.TEST_MANIFEST_DIR_KEY))
41 |
42 | if test_manifest_dir is None:
43 | message = ("TestManifest file directory was not found. Check if config_files_dir.json exists in root "
44 | "of project. Otherwise check if it's linking to existing file.")
45 | raise LauncherFlowInterruptedException(TAG, message)
46 | else:
47 | test_manifest = TestManifest(test_manifest_dir)
48 | Printer.system_message(TAG, "Created TestManifest from file: " + Color.GREEN + test_manifest_dir + Color.BLUE
49 | + ".")
50 | return test_manifest
51 |
52 |
53 | def _load_test_list(test_manifest):
54 | if test_manifest.test_package_list:
55 | return test_manifest.test_package_list
56 | else:
57 | message = "There are no tests specified in TestManifest! Launcher will quit."
58 | raise LauncherFlowInterruptedException(TAG, message)
59 |
60 |
61 | def _check_if_packages_exists(test_manifest, test_set_names):
62 | found_all_packages = True
63 | errors = ""
64 | for test_set_name in test_set_names:
65 | if test_manifest.contains_set(test_set_name):
66 | Printer.system_message(TAG, "Test set " + Color.GREEN + test_set_name + Color.BLUE
67 | + " was found in TestManifest. Contains following package names:")
68 |
69 | test_set = test_manifest.get_set(test_set_name)
70 | for package_name in test_set.set_package_names:
71 | Printer.system_message(TAG, " * " + Color.GREEN + package_name + Color.BLUE)
72 |
73 | for package_name in test_set.set_package_names:
74 | if not test_manifest.contains_package(package_name):
75 | found_all_packages = False
76 | errors += "\n - Test package '" + package_name + "' was not found in TestManifest!"
77 | else:
78 | message = "Test set '{}' not found in TestManifest. Launcher will quit."
79 | message = message.format(test_set_name)
80 | raise LauncherFlowInterruptedException(TAG, message)
81 | if found_all_packages:
82 | Printer.system_message(TAG, "All test packages were found in TestManifest.")
83 | else:
84 | raise LauncherFlowInterruptedException(TAG, errors)
85 |
86 |
87 | def _pick_test_set(test_manifest, test_set_names):
88 | if len(test_set_names) > 1:
89 | Printer.system_message(TAG, "There were " + Color.GREEN + "{}".format(len(test_set_names)) + Color.BLUE
90 | + " test sets passed. Merge will occur now.")
91 |
92 | test_set_dict = dict()
93 | test_set_dict["set_name"] = "merged"
94 | test_set_dict["apk_name_part"] = None
95 | test_set_dict["application_apk_assemble_task"] = None
96 | test_set_dict["test_apk_assemble_task"] = None
97 | test_set_dict["gradle_build_params"] = None
98 | test_set_dict["shard"] = None
99 | test_set_dict["set_package_names"] = set()
100 |
101 | config_compatible = True
102 | errors_tuples = set()
103 |
104 | for test_set_name in test_set_names:
105 | test_set = test_manifest.get_set(test_set_name)
106 | for other_test_set_name in test_set_names:
107 | other_test_set = test_manifest.get_set(other_test_set_name)
108 |
109 | if test_set.apk_name_part != other_test_set.apk_name_part:
110 | config_compatible = False
111 | errors_tuples.add((test_set_name, other_test_set_name, "apk_name_part"))
112 | else:
113 | test_set_dict["apk_name_part"] = test_set.apk_name_part
114 |
115 | if test_set.application_apk_assemble_task != other_test_set.application_apk_assemble_task:
116 | config_compatible = False
117 | errors_tuples.add((test_set_name, other_test_set_name, "application_apk_assemble_task"))
118 | else:
119 | test_set_dict["application_apk_assemble_task"] = test_set.application_apk_assemble_task
120 |
121 | if test_set.test_apk_assemble_task != other_test_set.test_apk_assemble_task:
122 | config_compatible = False
123 | errors_tuples.add((test_set_name, other_test_set_name, "test_apk_assemble_task"))
124 | else:
125 | test_set_dict["test_apk_assemble_task"] = test_set.test_apk_assemble_task
126 |
127 | if test_set.gradle_build_params != other_test_set.gradle_build_params:
128 | config_compatible = False
129 | errors_tuples.add((test_set_name, other_test_set_name, "gradle_build_params"))
130 | else:
131 | test_set_dict["gradle_build_params"] = test_set.gradle_build_params
132 |
133 | if test_set.shard != other_test_set.shard:
134 | config_compatible = False
135 | errors_tuples.add((test_set_name, other_test_set_name, "shard"))
136 | else:
137 | test_set_dict["shard"] = test_set.shard
138 |
139 | for package in test_set.set_package_names:
140 | test_set_dict["set_package_names"].add(package)
141 |
142 | if config_compatible:
143 | Printer.system_message(TAG,
144 | "All tests sets are compatible and were successfully merged. Including packages:")
145 |
146 | merged_test_set = TestSet(test_set_dict)
147 | for package_name in merged_test_set.set_package_names:
148 | Printer.system_message(TAG, " * " + Color.GREEN + package_name + Color.BLUE)
149 |
150 | return merged_test_set
151 | else:
152 | error = ""
153 | for tset1, tset2, parameter_name in errors_tuples:
154 | error += "\n - Test set '{}' and test set '{}' have incompatible ".format(tset1, tset2) \
155 | + "config (on parameter: {}) and cannot be merged.".format(parameter_name)
156 | raise LauncherFlowInterruptedException(TAG, error)
157 | else:
158 | return test_manifest.get_set(test_set_names[0])
159 |
160 | def _load_test_set(test_manifest, test_set_names):
161 | _check_if_packages_exists(test_manifest, test_set_names)
162 | return _pick_test_set(test_manifest, test_set_names)
163 |
--------------------------------------------------------------------------------
/settings/manifest/models/AvdManifestModels.py:
--------------------------------------------------------------------------------
1 | import copy
2 |
3 | from system.file import FileUtils
4 |
5 |
6 | class AvdManifest:
7 | TAG = "AvdManifest:"
8 |
9 | def __init__(self, manifest_dir):
10 | self.avd_manifest_source = FileUtils.load_json(manifest_dir)
11 | self.avd_schema_dict = dict()
12 | self.avd_set_dict = dict()
13 |
14 | for avd_schema in self.avd_manifest_source["avd_schema_list"]:
15 | self.avd_schema_dict.update({avd_schema["avd_name"]: AvdSchema(avd_schema)})
16 |
17 | for avd_set in self.avd_manifest_source["avd_set_list"]:
18 | self.avd_set_dict.update({avd_set["set_name"]: AvdSet(avd_set)})
19 |
20 | def contains_set(self, set_name):
21 | set_with_name_found = False
22 | for key in self.avd_set_dict.keys():
23 | if key == set_name:
24 | set_with_name_found = True
25 | break
26 | return set_with_name_found
27 |
28 | def get_set(self, set_name):
29 | avd_set = None
30 | for key in self.avd_set_dict.keys():
31 | if key == set_name:
32 | avd_set = self.avd_set_dict[key]
33 | break
34 | return copy.deepcopy(avd_set)
35 |
36 | def contains_schema(self, schema_name):
37 | schema_with_name_found = False
38 | for key in self.avd_schema_dict.keys():
39 | if key == schema_name:
40 | schema_with_name_found = True
41 | break
42 | return schema_with_name_found
43 |
44 | def get_schema(self, schema_name):
45 | avd_schema = None
46 | for key in self.avd_schema_dict.keys():
47 | if key == schema_name:
48 | avd_schema = self.avd_schema_dict[key]
49 | break
50 | return copy.deepcopy(avd_schema)
51 |
52 |
53 | class AvdSchema:
54 | def __init__(self, avd_schema_dict):
55 | self.avd_name = avd_schema_dict["avd_name"]
56 | self.create_avd_package = avd_schema_dict["create_avd_package"]
57 | self.create_avd_device = avd_schema_dict["create_device"]
58 | self.create_avd_tag = avd_schema_dict["create_avd_tag"]
59 | self.create_avd_abi = avd_schema_dict["create_avd_abi"]
60 | self.create_avd_hardware_config_filepath = avd_schema_dict["create_avd_hardware_config_filepath"]
61 | self.create_avd_additional_options = avd_schema_dict["create_avd_additional_options"]
62 | self.launch_avd_snapshot_filepath = avd_schema_dict["launch_avd_snapshot_filepath"]
63 | self.launch_avd_launch_binary_name = avd_schema_dict["launch_avd_launch_binary_name"]
64 | self.launch_avd_additional_options = avd_schema_dict["launch_avd_additional_options"]
65 |
66 |
67 | class AvdSet:
68 | def __init__(self, avd_set_dict):
69 | self.set_name = avd_set_dict["set_name"]
70 | self.avd_port_rules = AvdPortRules(avd_set_dict["avd_port_rules"])
71 | self.avd_list = list()
72 |
73 | for avd in avd_set_dict["avd_list"]:
74 | self.avd_list.append(AvdInSet(avd))
75 |
76 |
77 | class AvdPortRules:
78 | def __init__(self, avd_port_rules_dict):
79 | self.assign_missing_ports = avd_port_rules_dict["assign_missing_ports"]
80 | self.search_range_min = avd_port_rules_dict["search_range_min"]
81 | self.search_range_max = avd_port_rules_dict["search_range_max"]
82 | self.ports_to_ignore = avd_port_rules_dict["ports_to_ignore"]
83 | self.ports_to_use = avd_port_rules_dict["ports_to_use"]
84 |
85 |
86 | class AvdInSet:
87 | def __init__(self, avd_in_set_dict):
88 | self.avd_name = avd_in_set_dict["avd_name"]
89 | self.instances = avd_in_set_dict["instances"]
90 |
--------------------------------------------------------------------------------
/settings/manifest/models/LaunchManifestModels.py:
--------------------------------------------------------------------------------
1 | import copy
2 |
3 | from system.file import FileUtils
4 |
5 |
6 | class LaunchManifest:
7 | TAG = "LaunchManifest:"
8 |
9 | def __init__(self, manifest_dir):
10 | self.launch_manifest_source = FileUtils.load_json(manifest_dir)
11 | self.launch_plan_dict = dict()
12 |
13 | for launch_plan in self.launch_manifest_source["launch_plan_list"]:
14 | self.launch_plan_dict.update({launch_plan["plan_name"]: LaunchPlan(launch_plan)})
15 |
16 | def contains_plan(self, plan_name):
17 | plan_with_name_found = False
18 | for key in self.launch_plan_dict.keys():
19 | if key == plan_name:
20 | plan_with_name_found = True
21 | break
22 | return plan_with_name_found
23 |
24 | def get_plan(self, plan_name):
25 | launch_plan = None
26 | for key in self.launch_plan_dict.keys():
27 | if key == plan_name:
28 | launch_plan = self.launch_plan_dict[key]
29 | break
30 | return copy.deepcopy(launch_plan)
31 |
32 |
33 | class LaunchPlan:
34 | def __init__(self, launch_plan_dict):
35 | self.plan_name = launch_plan_dict["plan_name"]
36 | self.general = LaunchGeneralOptions(launch_plan_dict["general"])
37 | self.device_preparation_phase = DevicePreparationPhaseOptions(launch_plan_dict["device_preparation_phase"])
38 | self.device_launching_phase = DeviceLaunchingPhaseOptions(launch_plan_dict["device_launching_phase"])
39 | self.apk_preparation_phase = ApkPreparationPhaseOptions(launch_plan_dict["apk_preparation_phase"])
40 | self.testing_phase = TestingPhaseOptions(launch_plan_dict["testing_phase"])
41 | self.flakiness_check_phase = FlakinessCheckPhaseOptions(launch_plan_dict["flakiness_check_phase"])
42 |
43 |
44 | class LaunchGeneralOptions:
45 | def __init__(self, general_options_dict):
46 | self.adb_call_buffer_size = general_options_dict["adb_call_buffer_size"]
47 | self.adb_call_buffer_delay_between_cmd = general_options_dict["adb_call_buffer_delay_between_cmd"]
48 |
49 |
50 | class DevicePreparationPhaseOptions:
51 | def __init__(self, device_preparation_phase_dict):
52 | self.avd_should_recreate_existing = device_preparation_phase_dict["avd_should_recreate_existing"]
53 |
54 |
55 | class DeviceLaunchingPhaseOptions:
56 | def __init__(self, launching_phase_dict):
57 | self.device_android_id_to_ignore = launching_phase_dict["device_android_id_to_ignore"]
58 | self.avd_launch_sequentially = launching_phase_dict["avd_launch_sequentially"]
59 | self.avd_status_scan_interval_millis = launching_phase_dict["avd_status_scan_interval_millis"]
60 | self.avd_wait_for_adb_boot_timeout_millis = launching_phase_dict["avd_wait_for_adb_boot_timeout_millis"]
61 | self.avd_wait_for_system_boot_timeout_millis = launching_phase_dict["avd_wait_for_system_boot_timeout_millis"]
62 | self.device_before_launching_restart_adb = launching_phase_dict["device_before_launching_restart_adb"]
63 |
64 |
65 | class ApkPreparationPhaseOptions:
66 | def __init__(self, apk_preparation_phase_dict):
67 | self.build_new_apk = apk_preparation_phase_dict["build_new_apk"]
68 |
69 |
70 | class TestingPhaseOptions:
71 | def __init__(self, testing_phase_dict):
72 | self.record_tests = testing_phase_dict["record_tests"]
73 |
74 |
75 | class FlakinessCheckPhaseOptions:
76 | def __init__(self, flakiness_check_phase_dict):
77 | self.should_rerun_failed_tests = flakiness_check_phase_dict["should_rerun_failed_tests"]
78 | self.rerun_count = flakiness_check_phase_dict["rerun_count"]
79 |
--------------------------------------------------------------------------------
/settings/manifest/models/PathManifestModels.py:
--------------------------------------------------------------------------------
1 | import copy
2 |
3 | from system.file import FileUtils
4 |
5 |
6 | class PathManifest:
7 | TAG = "PathManifest:"
8 |
9 | def __init__(self, manifest_dir):
10 | self.path_manifest_source = FileUtils.load_json(manifest_dir)
11 | self.path_set_list = dict()
12 | for path_set in self.path_manifest_source["path_set_list"]:
13 | self.path_set_list.update({path_set["set_name"]: PathSet(path_set)})
14 |
15 | def contains_set(self, set_name):
16 | set_with_name_found = False
17 | for key in self.path_set_list.keys():
18 | if key == set_name:
19 | set_with_name_found = True
20 | break
21 | return set_with_name_found
22 |
23 | def get_set(self, set_name):
24 | path_set = None
25 | for key in self.path_set_list.keys():
26 | if key == set_name:
27 | path_set = self.path_set_list[key]
28 | break
29 | return copy.deepcopy(path_set)
30 |
31 |
32 | class PathSet:
33 | def __init__(self, path_set_dict):
34 | self.set_name = path_set_dict["set_name"]
35 | self.paths = dict()
36 |
37 | for path in path_set_dict["paths"]:
38 | self.paths.update({path["path_name"]: Path(path)})
39 |
40 |
41 | class Path:
42 | def __init__(self, path_dict):
43 | self.path_name = path_dict["path_name"]
44 | self.path_value = path_dict["path_value"]
45 | self.path_description = path_dict["path_description"]
46 |
--------------------------------------------------------------------------------
/settings/manifest/models/TestManifestModels.py:
--------------------------------------------------------------------------------
1 | import copy
2 |
3 | from system.file import FileUtils
4 |
5 |
6 | class TestManifest:
7 | TAG = "TestManifest:"
8 |
9 | def __init__(self, manifest_dir):
10 | self.path_manifest_source = FileUtils.load_json(manifest_dir)
11 | self.test_package_list = dict()
12 | self.test_set_list = dict()
13 |
14 | for test_package_dict in self.path_manifest_source["test_list"]:
15 | self.test_package_list.update({test_package_dict["test_package_name"]: TestPackage(test_package_dict)})
16 |
17 | for test_set_dict in self.path_manifest_source["test_set_list"]:
18 | self.test_set_list.update({test_set_dict["set_name"]: TestSet(test_set_dict)})
19 |
20 | def contains_package(self, package_name):
21 | package_with_name_found = False
22 | for key in self.test_package_list.keys():
23 | if key == package_name:
24 | package_with_name_found = True
25 | break
26 | return package_with_name_found
27 |
28 | def contains_set(self, set_name):
29 | set_with_name_found = False
30 | for key in self.test_set_list.keys():
31 | if key == set_name:
32 | set_with_name_found = True
33 | break
34 | return set_with_name_found
35 |
36 | def get_package(self, package_name):
37 | test_package = None
38 | for key in self.test_package_list.keys():
39 | if key == package_name:
40 | test_package = self.test_package_list[key]
41 | break
42 | return copy.deepcopy(test_package)
43 |
44 | def get_set(self, set_name):
45 | test_set = None
46 | for key in self.test_set_list.keys():
47 | if key == set_name:
48 | test_set = self.test_set_list[key]
49 | break
50 | return copy.deepcopy(test_set)
51 |
52 |
53 | class TestPackage:
54 | def __init__(self, test_package_dict):
55 | self.test_package_name = test_package_dict["test_package_name"]
56 | self.test_packages = test_package_dict["test_packages"]
57 | self.test_classes = test_package_dict["test_classes"]
58 | self.test_cases = test_package_dict["test_cases"]
59 |
60 |
61 | class TestSet:
62 | def __init__(self, test_set_dict):
63 | self.set_name = test_set_dict["set_name"]
64 | self.apk_name_part = test_set_dict["apk_name_part"]
65 | self.application_apk_assemble_task = test_set_dict["application_apk_assemble_task"]
66 | self.test_apk_assemble_task = test_set_dict["test_apk_assemble_task"]
67 | self.set_package_names = test_set_dict["set_package_names"]
68 | self.gradle_build_params = test_set_dict["gradle_build_params"]
69 | self.shard = test_set_dict["shard"]
70 |
--------------------------------------------------------------------------------
/settings/manifest/templates/avdManifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "avd_schema_list": [
3 | {
4 | "avd_name": "",
5 | "create_avd_package": "",
6 | "create_device": "",
7 | "create_avd_tag": "",
8 | "create_avd_abi": "",
9 | "create_avd_additional_options": "",
10 | "create_avd_hardware_config_filepath": "",
11 | "launch_avd_snapshot_filepath": "",
12 | "launch_avd_launch_binary_name": "",
13 | "launch_avd_additional_options": ""
14 | }
15 | ],
16 | "avd_set_list": [
17 | {
18 | "set_name": "default",
19 | "avd_list": [
20 | {
21 | "avd_name": "",
22 | "instances": -1
23 | }
24 | ],
25 | "avd_port_rules": {
26 | "assign_missing_ports": true,
27 | "search_range_min": 5556,
28 | "search_range_max": 5580,
29 | "ports_to_ignore": [],
30 | "ports_to_use": []
31 | }
32 | }
33 | ]
34 | }
--------------------------------------------------------------------------------
/settings/manifest/templates/launchManifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "launch_plan_list": [
3 | {
4 | "plan_name": "default",
5 | "general": {
6 | "adb_call_buffer_size": 3,
7 | "adb_call_buffer_delay_between_cmd": 2000
8 | },
9 | "device_preparation_phase": {
10 | "avd_should_recreate_existing": true
11 | },
12 | "apk_preparation_phase": {
13 | "build_new_apk": true
14 | },
15 | "device_launching_phase": {
16 | "device_android_id_to_ignore": [],
17 | "avd_launch_sequentially": true,
18 | "avd_status_scan_interval_millis": 10000,
19 | "avd_wait_for_adb_boot_timeout_millis": 240000,
20 | "avd_wait_for_system_boot_timeout_millis": 120000,
21 | "device_before_launching_restart_adb": true
22 | },
23 | "testing_phase": {
24 | "record_tests": true
25 | },
26 | "flakiness_check_phase": {
27 | "should_rerun_failed_tests": true,
28 | "rerun_count": 3
29 | }
30 | }
31 | ]
32 | }
--------------------------------------------------------------------------------
/settings/manifest/templates/pathManifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "path_set_list": [
3 | {
4 | "set_name": "default",
5 | "paths": [
6 | {
7 | "path_name": "sdk_dir",
8 | "path_value": "",
9 | "path_description": "Path to Android Software Development kit. By default stored in ANDROID_HOME env."
10 | },
11 | {
12 | "path_name": "avd_dir",
13 | "path_value": "",
14 | "path_description": "Location to .android folder where AVD images are stored. By default stored in ANDROID_SDK_HOME env."
15 | },
16 | {
17 | "path_name": "output_dir",
18 | "path_value": "",
19 | "path_description": "Path to where test process logs will be saved. By default ./output/."
20 | },
21 | {
22 | "path_name": "project_root_dir",
23 | "path_value": "",
24 | "path_description": "Path to root of application under test. (optional)"
25 | },
26 | {
27 | "path_name": "apk_dir",
28 | "path_value": "",
29 | "path_description": "Directory where .apk files of application under test are stored."
30 | }
31 | ]
32 | }
33 | ]
34 | }
--------------------------------------------------------------------------------
/settings/manifest/templates/testManifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "test_list": [
3 | {
4 | "test_package_name": "",
5 | "test_packages": [],
6 | "test_classes": [],
7 | "test_cases": []
8 | }
9 | ],
10 | "test_set_list": [
11 | {
12 | "set_name": "default",
13 | "apk_name_part": "",
14 | "application_apk_assemble_task": "",
15 | "test_apk_assemble_task": "",
16 | "gradle_build_params": "",
17 | "shard": false,
18 | "set_package_names": []
19 | }
20 | ]
21 | }
22 |
--------------------------------------------------------------------------------
/system/bin/AndroidBinaryFileControllers.py:
--------------------------------------------------------------------------------
1 | import os
2 | import re
3 |
4 | from error.Exceptions import LauncherFlowInterruptedException
5 |
6 | from settings import GlobalConfig
7 |
8 | from system.bin.AndroidShellCommandAssemblers import (
9 | AaptCommandAssembler,
10 | AdbCommandAssembler,
11 | AdbShellCommandAssembler,
12 | AdbPackageManagerCommandAssembler,
13 | AdbSettingsCommandAssembler,
14 | AdbLogCatCommandAssembler,
15 | AvdManagerCommandAssembler,
16 | EmulatorCommandAssembler,
17 | GradleCommandAssembler,
18 | InstrumentationRunnerCommandAssembler
19 |
20 | )
21 | from system.console import (
22 | Printer,
23 | ShellHelper,
24 | Color
25 | )
26 | from system.file.FileUtils import (
27 | clean_path,
28 | add_ending_slash,
29 | list_files_in_dir
30 | )
31 |
32 |
33 | class AaptController:
34 | TAG = "AaptController:"
35 |
36 | aapt_bin = None
37 |
38 | def __init__(self):
39 | build_tools = self._find_latest_build_tools()
40 | self.aapt_bin = clean_path(add_ending_slash(GlobalConfig.SDK_DIR) + "build-tools/" + build_tools + "/aapt")
41 | self._assert_bin_directory_exists()
42 | self.aapt_command_assembler = AaptCommandAssembler()
43 |
44 | def _find_latest_build_tools(self):
45 | build_tools = list_files_in_dir(clean_path(add_ending_slash(GlobalConfig.SDK_DIR) + "build-tools"))
46 | build_tools = [build_tool for build_tool in build_tools if build_tool[0].isdigit()]
47 | build_tools_folder_with_highest_ver = None
48 | Printer.system_message(self.TAG, "Available Android SDK Build-Tools versions: " + str(build_tools))
49 | for build_tools_folder in build_tools:
50 | if build_tools_folder_with_highest_ver is None:
51 | build_tools_folder_with_highest_ver = build_tools_folder
52 | continue
53 |
54 | ver = int(re.sub("[^0-9]", "", build_tools_folder))
55 | highest_ver = int(re.sub("[^0-9]", "", build_tools_folder_with_highest_ver))
56 |
57 | if ver > highest_ver:
58 | build_tools_folder_with_highest_ver = build_tools_folder
59 | if build_tools_folder_with_highest_ver is None:
60 | message = "Android SDK Build-Tools not found. Launcher will quit."
61 | raise LauncherFlowInterruptedException(self.TAG, message)
62 |
63 | else:
64 | Printer.system_message(self.TAG, "Android SDK Build-Tools with latest version were selected: "
65 | + Color.GREEN + str(build_tools_folder_with_highest_ver) + Color.BLUE + ".")
66 | return build_tools_folder_with_highest_ver
67 |
68 | def _assert_bin_directory_exists(self):
69 | if os.path.isfile(self.aapt_bin):
70 | Printer.system_message(self.TAG, "Aapt binary file found at: " + Color.GREEN + self.aapt_bin
71 | + Color.BLUE + ".")
72 | else:
73 | message = "Unable to find Aapt binary at '{}'."
74 | message = message.format(self.aapt_bin)
75 | raise LauncherFlowInterruptedException(self.TAG, message)
76 |
77 | def dump_badging(self, apk_filepath):
78 | cmd = self.aapt_command_assembler.assemble_dump_badging_cmd(self.aapt_bin, apk_filepath)
79 | return ShellHelper.execute_shell(cmd, False, False)
80 |
81 | def list_resources(self, apk_filepath):
82 | cmd = self.aapt_command_assembler.assemble_list_all_cmd(self.aapt_bin, apk_filepath)
83 | return ShellHelper.execute_shell(cmd, False, False)
84 |
85 |
86 | class AdbController:
87 | TAG = "AdbController:"
88 |
89 | adb_bin = None
90 |
91 | def __init__(self):
92 | self.adb_bin = clean_path(add_ending_slash(GlobalConfig.SDK_DIR) + "platform-tools/adb")
93 | self._assert_bin_directory_exists()
94 | self.adb_command_assembler = AdbCommandAssembler()
95 |
96 | def _assert_bin_directory_exists(self):
97 | if os.path.isfile(self.adb_bin):
98 | Printer.system_message(self.TAG, "ADB binary file found at " + Color.GREEN + self.adb_bin + Color.BLUE
99 | + ".")
100 | else:
101 | message = "Unable to find ADB binary at '{}'."
102 | message = message.format(self.adb_bin)
103 | raise LauncherFlowInterruptedException(self.TAG, message)
104 |
105 | def start_server(self):
106 | cmd = self.adb_command_assembler.assemble_start_server_cmd(self.adb_bin)
107 | return ShellHelper.execute_shell(cmd, True, True)
108 |
109 | def kill_server(self):
110 | cmd = self.adb_command_assembler.assemble_kill_server_cmd(self.adb_bin)
111 | return ShellHelper.execute_shell(cmd, True, True)
112 |
113 | def devices(self):
114 | cmd = self.adb_command_assembler.assemble_devices_cmd(self.adb_bin)
115 | return ShellHelper.execute_shell(cmd, False, False)
116 |
117 | def wait_for_device(self):
118 | cmd = self.adb_command_assembler.assemble_wait_for_device_cmd(self.adb_bin)
119 | return ShellHelper.execute_shell(cmd, False, False)
120 |
121 | def kill_device(self, device_adb_name):
122 | cmd = self.adb_command_assembler.assemble_kill_device_cmd(self.adb_bin, device_adb_name)
123 | return ShellHelper.execute_shell(cmd, True, False)
124 |
125 | def install_apk(self, device_adb_name, apk_name):
126 | cmd = self.adb_command_assembler.assemble_install_apk_cmd(self.adb_bin, device_adb_name, apk_name)
127 | return ShellHelper.execute_shell(cmd, True, False)
128 |
129 | def pull(self, device_adb_name, file_dir, file_destination):
130 | cmd = self.adb_command_assembler.assemble_pull_file_cmd(self.adb_bin,
131 | device_adb_name,
132 | file_dir,
133 | file_destination)
134 | return ShellHelper.execute_shell(cmd, False, False)
135 |
136 |
137 | class AdbShellController:
138 | TAG = "AdbShellController:"
139 |
140 | adb_bin = None
141 |
142 | def __init__(self):
143 | self.adb_bin = clean_path(add_ending_slash(GlobalConfig.SDK_DIR) + "platform-tools/adb")
144 | self._assert_bin_directory_exists()
145 | self.adb_shell_command_assembler = AdbShellCommandAssembler()
146 |
147 | def _assert_bin_directory_exists(self):
148 | if os.path.isfile(self.adb_bin):
149 | Printer.system_message(self.TAG, "ADB binary file found at " + Color.GREEN + self.adb_bin + Color.BLUE
150 | + ".")
151 | else:
152 | message = "Unable to find ADB binary at '{}'."
153 | message = message.format(self.adb_bin)
154 | raise LauncherFlowInterruptedException(self.TAG, message)
155 |
156 | def get_property(self, device_adb_name, device_property):
157 | cmd = self.adb_shell_command_assembler.assemble_get_property_cmd(self.adb_bin, device_adb_name, device_property)
158 | return ShellHelper.execute_shell(cmd, False, False)
159 |
160 | def record_screen(self, device_adb_name, file_dir):
161 | cmd = self.adb_shell_command_assembler.assemble_record_screen_cmd(self.adb_bin, device_adb_name, file_dir)
162 | return ShellHelper.execute_shell(cmd, False, False)
163 |
164 | def remove_file(self, device_adb_name, file_dir):
165 | cmd = self.adb_shell_command_assembler.assemble_remove_file_cmd(self.adb_bin, device_adb_name, file_dir)
166 | return ShellHelper.execute_shell(cmd, True, True)
167 |
168 | def remove_files_in_dir(self, device_adb_name, file_dir):
169 | cmd = self.adb_shell_command_assembler.assemble_remove_files_in_dir_cmd(self.adb_bin, device_adb_name, file_dir)
170 | return ShellHelper.execute_shell(cmd, True, True)
171 |
172 | def create_dir(self, device_adb_name, file_dir):
173 | cmd = self.adb_shell_command_assembler.assemble_create_dir_cmd(self.adb_bin, device_adb_name, file_dir)
174 | return ShellHelper.execute_shell(cmd, True, True)
175 |
176 | def check_for_directory(self, device_adb_name, directory):
177 | cmd = self.adb_shell_command_assembler.assemble_check_if_dir_exists_cmd(self.adb_bin,
178 | device_adb_name,
179 | directory)
180 | return ShellHelper.execute_shell(cmd, True, True)
181 |
182 |
183 | class AdbPackageManagerController:
184 | TAG = "AdbPackageManagerController:"
185 |
186 | adb_bin = None
187 |
188 | def __init__(self):
189 | self.adb_bin = clean_path(add_ending_slash(GlobalConfig.SDK_DIR) + "platform-tools/adb")
190 | self._assert_bin_directory_exists()
191 | self.adb_package_manager_command_assembler = AdbPackageManagerCommandAssembler()
192 |
193 | def _assert_bin_directory_exists(self):
194 | if os.path.isfile(self.adb_bin):
195 | Printer.system_message(self.TAG, "ADB binary file found at " + Color.GREEN + self.adb_bin + Color.BLUE
196 | + ".")
197 | else:
198 | message = "Unable to find ADB binary at '{}'."
199 | message = message.format(self.adb_bin)
200 | raise LauncherFlowInterruptedException(self.TAG, message)
201 |
202 | def get_installed_packages(self, device_adb_name):
203 | cmd = self.adb_package_manager_command_assembler.assemble_list_installed_packages_cmd(self.adb_bin,
204 | device_adb_name)
205 | return ShellHelper.execute_shell(cmd, False, False)
206 |
207 | def uninstall_package(self, device_adb_name, package_name):
208 | cmd = self.adb_package_manager_command_assembler.assemble_uninstall_package_cmd(self.adb_bin,
209 | device_adb_name,
210 | package_name)
211 | return ShellHelper.execute_shell(cmd, True, True)
212 |
213 | def clear_package_cache(self, device_adb_name, package_name):
214 | cmd = self.adb_package_manager_command_assembler.assemble_clear_package_cache_cmd(self.adb_bin,
215 | device_adb_name,
216 | package_name)
217 | return ShellHelper.execute_shell(cmd, True, True)
218 |
219 |
220 | class AdbSettingsController:
221 | TAG = "AdbSettingsController:"
222 |
223 | adb_bin = None
224 |
225 | def __init__(self):
226 | self.adb_bin = clean_path(add_ending_slash(GlobalConfig.SDK_DIR) + "platform-tools/adb")
227 | self._assert_bin_directory_exists()
228 | self.adb_settings_command_assembler = AdbSettingsCommandAssembler()
229 |
230 | def _assert_bin_directory_exists(self):
231 | if os.path.isfile(self.adb_bin):
232 | Printer.system_message(self.TAG, "ADB binary file found at " + Color.GREEN + self.adb_bin + Color.BLUE
233 | + ".")
234 | else:
235 | message = "Unable to find ADB binary at '{}'."
236 | message = message.format(self.adb_bin)
237 | raise LauncherFlowInterruptedException(self.TAG, message)
238 |
239 | def get_device_android_id(self, device_adb_name):
240 | cmd = self.adb_settings_command_assembler.assemble_get_device_android_id_cmd(self.adb_bin,
241 | device_adb_name)
242 | return ShellHelper.execute_shell(cmd, False, False)
243 |
244 |
245 | class AdbLogCatController:
246 | TAG = "AdbLogCatController:"
247 |
248 | def __init__(self):
249 | self.adb_bin = clean_path(add_ending_slash(GlobalConfig.SDK_DIR) + "platform-tools/adb")
250 | self._assert_bin_directory_exists()
251 | self.adb_logcat_command_assembler = AdbLogCatCommandAssembler()
252 |
253 | def _assert_bin_directory_exists(self):
254 | if os.path.isfile(self.adb_bin):
255 | Printer.system_message(self.TAG, "ADB binary file found at " + Color.GREEN + self.adb_bin + Color.BLUE
256 | + ".")
257 | else:
258 | message = "Unable to find ADB binary at '{}'."
259 | message = message.format(self.adb_bin)
260 | raise LauncherFlowInterruptedException(self.TAG, message)
261 |
262 | def flush_logcat(self, device_adb_name):
263 | cmd = self.adb_logcat_command_assembler.assemble_flush_logcat_cmd(self.adb_bin, device_adb_name)
264 |
265 | return ShellHelper.execute_shell(cmd, False, False)
266 |
267 | def read_logcat(self, device_adb_name):
268 | cmd = self.adb_logcat_command_assembler.assemble_dump_logcat_cmd(self.adb_bin, device_adb_name)
269 |
270 | return ShellHelper.execute_shell(cmd, False, False)
271 |
272 | def monitor_logcat(self, device_adb_name):
273 | cmd = self.adb_logcat_command_assembler.monitor_logcat_schema(self.adb_bin, device_adb_name)
274 |
275 | return ShellHelper.execute_shell(cmd, False, False)
276 |
277 |
278 | class AvdManagerController:
279 | TAG = "AvdManagerController:"
280 |
281 | avdmanager_bin = None
282 |
283 | def __init__(self):
284 | self.avdmanager_bin = clean_path(add_ending_slash(GlobalConfig.SDK_DIR) + "tools/bin/avdmanager")
285 | self._assert_bin_directory_exists()
286 | self.avdmanager_command_assembler = AvdManagerCommandAssembler()
287 |
288 | def _assert_bin_directory_exists(self):
289 | if os.path.isfile(self.avdmanager_bin):
290 | Printer.system_message(self.TAG, "AvdManager binary file found at " + Color.GREEN + self.avdmanager_bin
291 | + Color.BLUE + ".")
292 | else:
293 | message = "Unable to find ADB binary at '{}'."
294 | message = message.format(self.avdmanager_bin)
295 | raise LauncherFlowInterruptedException(self.TAG, message)
296 |
297 | def list_avd(self):
298 | cmd = self.avdmanager_command_assembler.assemble_list_avd_cmd(self.avdmanager_bin)
299 | return ShellHelper.execute_shell(cmd, False, False)
300 |
301 | def delete_avd(self, avd_schema):
302 | cmd = self.avdmanager_command_assembler.assemble_delete_avd_cmd(self.avdmanager_bin, avd_schema)
303 | return ShellHelper.execute_shell(cmd, True, True)
304 |
305 | def create_avd(self, avd_schema):
306 | cmd = self.avdmanager_command_assembler.assemble_create_avd_cmd(self.avdmanager_bin, avd_schema)
307 | return ShellHelper.execute_shell(cmd, True, True)
308 |
309 |
310 | class EmulatorController:
311 | TAG = "EmulatorController:"
312 |
313 | emulator_bin_dict = dict()
314 |
315 | def __init__(self):
316 | self.emulator_bin_dict = self._display_emulator_binaries()
317 | self.emulator_command_assembler = EmulatorCommandAssembler()
318 |
319 | def _display_emulator_binaries(self):
320 | emulator_binaries = dict()
321 |
322 | emulator_dir = clean_path(add_ending_slash(str(GlobalConfig.SDK_DIR)) + "emulator/")
323 | try:
324 | for the_file in list_files_in_dir(emulator_dir):
325 | file_path = os.path.join(emulator_dir, the_file)
326 | if os.path.isfile(file_path) and "emulator" in file_path:
327 | binary_name = re.findall("emulator\/(emulator*.+)", file_path)
328 |
329 | if binary_name:
330 | emulator_binaries[str(binary_name[0])] = file_path
331 | finally:
332 | if len(emulator_binaries) == 0:
333 | message = "Unable to find emulator binary files in direction '{}' of Android SDK."
334 | message = message.format(str(emulator_dir))
335 | raise LauncherFlowInterruptedException(self.TAG, message)
336 |
337 | else:
338 | Printer.system_message(self.TAG, "Emulator related binary files found in Android SDK:")
339 | for path in emulator_binaries.values():
340 | Printer.system_message(self.TAG, " * " + Color.GREEN + path + Color.BLUE)
341 |
342 | return emulator_binaries
343 |
344 | def launch_avd(self, avd_schema, port, log_file):
345 | emulator_binary = self.emulator_bin_dict[avd_schema.launch_avd_launch_binary_name]
346 | cmd = self.emulator_command_assembler.assemble_launch_avd_cmd(emulator_binary, avd_schema, port, log_file)
347 | return ShellHelper.execute_shell(cmd, True, False)
348 |
349 | def apply_config_to_avd(self, avd_schema):
350 | config_ini_to_apply_filepath = clean_path(avd_schema.create_avd_hardware_config_filepath)
351 |
352 | real_config_ini_file_path = clean_path(
353 | add_ending_slash(GlobalConfig.AVD_DIR) + add_ending_slash("avd") + add_ending_slash(
354 | avd_schema.avd_name + ".avd") + "config.ini")
355 |
356 | real_config_ini_file = None
357 | config_ini_to_apply_file = None
358 | try:
359 | if os.path.isfile(config_ini_to_apply_filepath):
360 | config_ini_to_apply_file = open(config_ini_to_apply_filepath, "r")
361 | real_config_ini_file = open(real_config_ini_file_path, "w")
362 | real_config_ini_file.seek(0)
363 | real_config_ini_file.truncate()
364 |
365 | for config_line in config_ini_to_apply_file.readlines():
366 | temp_lane = config_line
367 | if "AvdId=" in config_line:
368 | temp_lane = ("AvdId=" + str(avd_schema.avd_name) + "\n")
369 | if "avd.ini.displayname=" in config_line:
370 | temp_lane = ("avd.ini.displayname=" + str(avd_schema.avd_name) + "\n")
371 | real_config_ini_file.write(temp_lane)
372 | else:
373 | message = "Filepath '" + config_ini_to_apply_filepath + "' not found!"
374 | raise LauncherFlowInterruptedException(self.TAG, message)
375 | finally:
376 | if real_config_ini_file is not None:
377 | real_config_ini_file.close()
378 | if config_ini_to_apply_file is not None:
379 | config_ini_to_apply_file.close()
380 |
381 | return "Config.ini successfully applied"
382 |
383 |
384 | class GradleController:
385 | TAG = "GradleController:"
386 |
387 | gradle_bin = None
388 |
389 | project_root_found = False
390 | gradlew_found = False
391 |
392 | def __init__(self):
393 | self.gradle_bin = GlobalConfig.PROJECT_ROOT_DIR + "gradlew"
394 | self._check_if_gradle_binary_was_found()
395 | self.gradle_command_assembler = GradleCommandAssembler()
396 |
397 | def _check_if_gradle_binary_was_found(self):
398 | self.project_root_found = GlobalConfig.PROJECT_ROOT_DIR != "" and os.path.isdir(GlobalConfig.PROJECT_ROOT_DIR)
399 | self.gradlew_found = os.path.isfile(self.gradle_bin)
400 | if self.project_root_found:
401 | Printer.system_message(self.TAG, "Project root dir " + Color.GREEN + GlobalConfig.PROJECT_ROOT_DIR
402 | + Color.BLUE + " was found! Building new .*apk is possible.")
403 | if self.gradlew_found:
404 | Printer.system_message(self.TAG, "gradlew binary found at " + Color.GREEN + str(self.gradle_bin)
405 | + Color.BLUE + ".")
406 |
407 | def build_application_apk(self, test_set):
408 | assemble_task = test_set.application_apk_assemble_task
409 | self._check_if_build_is_possible(assemble_task)
410 |
411 | cmd = self.gradle_command_assembler.assemble_build_application_apk_cmd(self.gradle_bin,
412 | test_set.gradle_build_params,
413 | assemble_task,
414 | GlobalConfig.PROJECT_ROOT_DIR)
415 | ShellHelper.execute_shell(cmd, True, True)
416 |
417 | def build_test_apk(self, test_set):
418 | assemble_task = test_set.test_apk_assemble_task
419 | self._check_if_build_is_possible(assemble_task)
420 |
421 | cmd = self.gradle_command_assembler.assemble_build_test_apk_cmd(self.gradle_bin,
422 | test_set.gradle_build_params,
423 | assemble_task,
424 | GlobalConfig.PROJECT_ROOT_DIR)
425 | ShellHelper.execute_shell(cmd, True, True)
426 |
427 | def _check_if_build_is_possible(self, cmd):
428 | if cmd == "":
429 | message = "Gradle assemble task (for building .*apk) was not specified in TestManifest. Launcher will quit."
430 | raise LauncherFlowInterruptedException(self.TAG, message)
431 |
432 | if not self.project_root_found:
433 | message = "Unable to build new .*apk. Project root not found. Launcher will quit."
434 | raise LauncherFlowInterruptedException(self.TAG, message)
435 |
436 | if not self.gradlew_found:
437 | message = "Unable to build new .*apk. File 'gradlew' not found in dir '{}'. Launcher will quit."
438 | message = message.format(GlobalConfig.PROJECT_ROOT_DIR)
439 | raise LauncherFlowInterruptedException(self.TAG, message)
440 |
441 |
442 | class InstrumentationRunnerController:
443 | TAG = "InstrumentationRunnerController:"
444 |
445 | adb_bin = None
446 |
447 | def __init__(self):
448 | self.adb_bin = clean_path(add_ending_slash(GlobalConfig.SDK_DIR) + "platform-tools/adb")
449 | self._assert_bin_directory_exists()
450 | self.instrumentation_runner_command_assembler = InstrumentationRunnerCommandAssembler()
451 |
452 | def _assert_bin_directory_exists(self):
453 | if os.path.isfile(self.adb_bin):
454 | Printer.system_message(self.TAG, "ADB binary file found at " + Color.GREEN + self.adb_bin + Color.BLUE
455 | + ".")
456 | else:
457 | message = "Unable to find ADB binary at '{}'."
458 | message = message.format(self.adb_bin)
459 | raise LauncherFlowInterruptedException(self.TAG, message)
460 |
461 |
462 |
--------------------------------------------------------------------------------
/system/bin/AndroidShellCommandAssemblers.py:
--------------------------------------------------------------------------------
1 | from system.bin.AndroidShellCommands import (
2 | AaptCommand,
3 | AdbCommand,
4 | AdbShellCommand,
5 | AdbActivityManagerCommand,
6 | AdbPackageManagerCommand,
7 | AdbSettingsCommand,
8 | AdbLogCatCommand,
9 | AvdManagerCommand,
10 | EmulatorCommand,
11 | GradleCommand,
12 | InstrumentationRunnerCommand
13 | )
14 | from system.bin.SystemShellCommands import GeneralCommand
15 |
16 |
17 | class AaptCommandAssembler:
18 | dump_badging_schema = "{} {}"
19 | list_all_schema = "{} {}"
20 |
21 | def assemble_dump_badging_cmd(self, aapt_bin, apk_filepath):
22 | return self.dump_badging_schema.format(aapt_bin,
23 | AaptCommand.DUMP_BADGING.format(apk_filepath))
24 |
25 | def assemble_list_all_cmd(self, aapt_bin, apk_filepath):
26 | return self.list_all_schema.format(aapt_bin,
27 | AaptCommand.LIST_ALL.format(apk_filepath))
28 |
29 |
30 | class AdbCommandAssembler:
31 | start_server_schema = "{} {}"
32 | kill_server_schema = "{} {}"
33 | devices_schema = "{} {}"
34 | waif_for_device_schema = "{} {}"
35 | kill_device_schema = "{} {} {}"
36 | install_apk_schema = "{} {} {} {}"
37 | pull_file_schema = "{} {} {}"
38 |
39 | def assemble_start_server_cmd(self, adb_bin):
40 | return self.start_server_schema.format(adb_bin,
41 | AdbCommand.START_SERVER)
42 |
43 | def assemble_kill_server_cmd(self, adb_bin):
44 | return self.kill_server_schema.format(adb_bin,
45 | AdbCommand.KILL_SERVER)
46 |
47 | def assemble_devices_cmd(self, adb_bin):
48 | return self.devices_schema.format(adb_bin,
49 | AdbCommand.DEVICES)
50 |
51 | def assemble_wait_for_device_cmd(self, adb_bin):
52 | return self.waif_for_device_schema.format(adb_bin,
53 | AdbCommand.WAIT_FOR_DEVICE)
54 |
55 | def assemble_kill_device_cmd(self, adb_bin, device_adb_name):
56 | return self.kill_device_schema.format(adb_bin,
57 | AdbCommand.SPECIFIC_DEVICE.format(device_adb_name),
58 | AdbCommand.KILL_DEVICE)
59 |
60 | def assemble_install_apk_cmd(self, adb_bin, device_adb_name, apk_name):
61 | return self.install_apk_schema.format(adb_bin,
62 | AdbCommand.SPECIFIC_DEVICE.format(device_adb_name),
63 | AdbCommand.INSTALL_APK.format(apk_name),
64 | GeneralCommand.CHANGE_THREAD)
65 |
66 | def assemble_pull_file_cmd(self, adb_bin, device_adb_name, file_dir, file_destination_dir):
67 | return self.pull_file_schema.format(adb_bin,
68 | AdbCommand.SPECIFIC_DEVICE.format(device_adb_name),
69 | AdbCommand.PULL.format(file_dir, file_destination_dir))
70 |
71 |
72 | class AdbShellCommandAssembler:
73 | get_property_schema = "{} {} {} {}"
74 | record_screen = "{} {} {} {}"
75 | remove_file = "{} {} {} {}"
76 | remove_files_in_dir = "{} {} {} {}"
77 | create_dir = "{} {} {} {}"
78 | check_if_dir_exists = "{} {} {} {}"
79 |
80 | def assemble_get_property_cmd(self, adb_bin, device_adb_name, device_property):
81 | return self.get_property_schema.format(adb_bin,
82 | AdbCommand.SPECIFIC_DEVICE.format(device_adb_name),
83 | AdbShellCommand.SHELL,
84 | AdbShellCommand.GET_PROPERTY.format(device_property))
85 |
86 | def assemble_record_screen_cmd(self, adb_bin, device_adb_name, file_dir):
87 | return self.record_screen.format(adb_bin,
88 | AdbCommand.SPECIFIC_DEVICE.format(device_adb_name),
89 | AdbShellCommand.SHELL,
90 | AdbShellCommand.RECORD.format(file_dir))
91 |
92 | def assemble_remove_file_cmd(self, adb_bin, device_adb_name, file_dir):
93 | return self.remove_file.format(adb_bin,
94 | AdbCommand.SPECIFIC_DEVICE.format(device_adb_name),
95 | AdbShellCommand.SHELL,
96 | AdbShellCommand.REMOVE_FILE.format(file_dir))
97 |
98 | def assemble_remove_files_in_dir_cmd(self, adb_bin, device_adb_name, file_dir):
99 | return self.remove_files_in_dir.format(adb_bin,
100 | AdbCommand.SPECIFIC_DEVICE.format(device_adb_name),
101 | AdbShellCommand.SHELL,
102 | AdbShellCommand.REMOVE_FILES_IN_DIR.format(file_dir))
103 |
104 | def assemble_create_dir_cmd(self, adb_bin, device_adb_name, file_dir):
105 | return self.create_dir.format(adb_bin,
106 | AdbCommand.SPECIFIC_DEVICE.format(device_adb_name),
107 | AdbShellCommand.SHELL,
108 | AdbShellCommand.CREATE_DIR.format(file_dir))
109 |
110 | def assemble_check_if_dir_exists_cmd(self, adb_bin, device_adb_name, directory):
111 | return self.check_if_dir_exists.format(adb_bin,
112 | AdbCommand.SPECIFIC_DEVICE.format(device_adb_name),
113 | AdbShellCommand.SHELL,
114 | GeneralCommand.CHECK_IF_DIR_EXISTS.format(directory))
115 |
116 |
117 | class AdbPackageManagerCommandAssembler:
118 | list_installed_packages_schema = "{} {} {} {} {}"
119 | uninstall_package_schema = "{} {} {} {} {}"
120 | clear_package_cache_schema = "{} {} {} {} {}"
121 |
122 | def assemble_list_installed_packages_cmd(self, adb_bin, device_adb_name):
123 | return self.list_installed_packages_schema.format(adb_bin,
124 | AdbCommand.SPECIFIC_DEVICE.format(device_adb_name),
125 | AdbShellCommand.SHELL,
126 | AdbPackageManagerCommand.PACKAGE_MANAGER,
127 | AdbPackageManagerCommand.LIST_SERVICES)
128 |
129 | def assemble_uninstall_package_cmd(self, adb_bin, device_adb_name, package_name):
130 | return self.uninstall_package_schema.format(adb_bin,
131 | AdbCommand.SPECIFIC_DEVICE.format(device_adb_name),
132 | AdbShellCommand.SHELL,
133 | AdbPackageManagerCommand.PACKAGE_MANAGER,
134 | AdbPackageManagerCommand.UNINSTALL_PACKAGE.format(package_name))
135 |
136 | def assemble_clear_package_cache_cmd(self, adb_bin, device_adb_name, package_name):
137 | return self.clear_package_cache_schema.format(adb_bin,
138 | AdbCommand.SPECIFIC_DEVICE.format(device_adb_name),
139 | AdbShellCommand.SHELL,
140 | AdbPackageManagerCommand.PACKAGE_MANAGER,
141 | AdbPackageManagerCommand.CLEAR_CACHE.format(package_name))
142 |
143 |
144 | class AdbSettingsCommandAssembler:
145 | get_device_android_id_schema = "{} {} {} {} {}"
146 |
147 | def assemble_get_device_android_id_cmd(self, adb_bin, device_adb_name):
148 | return self.get_device_android_id_schema.format(adb_bin,
149 | AdbCommand.SPECIFIC_DEVICE.format(device_adb_name),
150 | AdbShellCommand.SHELL,
151 | AdbSettingsCommand.SETTINGS,
152 | AdbSettingsCommand.GET_DEVICE_ANDROID_ID)
153 |
154 |
155 | class AdbLogCatCommandAssembler:
156 | flush_logcat_schema = "{} {} {} {}"
157 | dump_logcat_schema = "{} {} {} {}"
158 | monitor_logcat_schema = "{} {} {}"
159 |
160 | def assemble_monitor_logcat_cmd(self, adb_bin, device_adb_name):
161 | return self.monitor_logcat_schema.format(adb_bin,
162 | AdbCommand.SPECIFIC_DEVICE.format(device_adb_name),
163 | AdbLogCatCommand.LOG_CAT)
164 |
165 | def assemble_flush_logcat_cmd(self, adb_bin, device_adb_name):
166 | return self.flush_logcat_schema.format(adb_bin,
167 | AdbCommand.SPECIFIC_DEVICE.format(device_adb_name),
168 | AdbLogCatCommand.LOG_CAT,
169 | AdbLogCatCommand.FLUSH)
170 |
171 | def assemble_dump_logcat_cmd(self, adb_bin, device_adb_name):
172 | return self.dump_logcat_schema.format(adb_bin,
173 | AdbCommand.SPECIFIC_DEVICE.format(device_adb_name),
174 | AdbLogCatCommand.LOG_CAT,
175 | AdbLogCatCommand.DUMP)
176 |
177 |
178 | class AvdManagerCommandAssembler:
179 | list_avd_schema = "{} {}"
180 | delete_avd_schema = "{} {}"
181 | create_avd_schema = "{} {} {} {} {} {} {} {} {}"
182 |
183 | def assemble_list_avd_cmd(self, avdmanager_bin):
184 | return self.list_avd_schema.format(avdmanager_bin,
185 | AvdManagerCommand.LIST_AVD)
186 |
187 | def assemble_delete_avd_cmd(self, avdmanager_bin, avd_schema):
188 | return self.delete_avd_schema.format(avdmanager_bin,
189 | AvdManagerCommand.DELETE_AVD.format(avd_schema.avd_name))
190 |
191 | def assemble_create_avd_cmd(self, avdmanager_bin, avd_schema):
192 | part_answer_no = GeneralCommand.ANSWER_NO
193 | part_create_avd = AvdManagerCommand.CreateAvdCommandPart.AVD_CREATE
194 | part_name = AvdManagerCommand.CreateAvdCommandPart.AVD_NAME.format(avd_schema.avd_name)
195 | part_package = AvdManagerCommand.CreateAvdCommandPart.AVD_PACKAGE.format(avd_schema.create_avd_package)
196 |
197 | if avd_schema.create_avd_device == "":
198 | part_device = avd_schema.create_avd_device
199 | else:
200 | part_device = AvdManagerCommand.CreateAvdCommandPart.AVD_DEVICE.format(avd_schema.create_avd_device)
201 |
202 | if avd_schema.create_avd_tag == "":
203 | part_tag = avd_schema.create_avd_tag
204 | else:
205 | part_tag = AvdManagerCommand.CreateAvdCommandPart.AVD_TAG.format(avd_schema.create_avd_tag)
206 |
207 | if avd_schema.create_avd_abi == "":
208 | part_abi = avd_schema.create_avd_abi
209 | else:
210 | part_abi = AvdManagerCommand.CreateAvdCommandPart.AVD_ABI.format(avd_schema.create_avd_abi)
211 |
212 | part_avd_additional_options = AvdManagerCommand.CreateAvdCommandPart.AVD_ADDITIONAL_OPTIONS.format(
213 | avd_schema.create_avd_additional_options)
214 |
215 | return self.create_avd_schema.format(part_answer_no,
216 | avdmanager_bin,
217 | part_create_avd,
218 | part_name,
219 | part_package,
220 | part_device,
221 | part_tag,
222 | part_abi,
223 | part_avd_additional_options)
224 |
225 |
226 | class EmulatorCommandAssembler:
227 | launch_avd_schema = "{} {} {} {} {} {}"
228 | output_file_schema = "{} {}"
229 |
230 | def assemble_launch_avd_cmd(self, emulator_bin, avd_schema, port, log_file):
231 | part_emulator_binary = emulator_bin
232 | part_port = EmulatorCommand.LaunchAvdCommandPart.AVD_PORT.format(port)
233 | part_name = EmulatorCommand.LaunchAvdCommandPart.AVD_NAME.format(avd_schema.avd_name)
234 |
235 | part_snapshot = ""
236 | if avd_schema.launch_avd_snapshot_filepath != "":
237 | part_snapshot = EmulatorCommand.LaunchAvdCommandPart.AVD_SNAPSHOT.format(
238 | avd_schema.launch_avd_snapshot_filepath)
239 |
240 | part_additional_options = EmulatorCommand.LaunchAvdCommandPart.AVD_ADDITIONAL_OPTIONS.format(
241 | avd_schema.launch_avd_additional_options)
242 |
243 | part_output_file = self.output_file_schema.format(GeneralCommand.DELEGATE_OUTPUT_TO_FILE.format(log_file),
244 | GeneralCommand.CHANGE_THREAD)
245 |
246 | return self.launch_avd_schema.format(part_emulator_binary,
247 | part_name,
248 | part_port,
249 | part_snapshot,
250 | part_additional_options,
251 | part_output_file)
252 |
253 |
254 | class GradleCommandAssembler:
255 | gradle_command_schema = "{} {}"
256 |
257 | def assemble_build_application_apk_cmd(self, gradle_bin, gradle_params, assemble_task, project_root_dir):
258 | gradle_cmd = GradleCommand.RUN_TASK_IN_OTHER_DIRECTORY.format(gradle_bin,
259 | project_root_dir,
260 | assemble_task)
261 | return self.gradle_command_schema.format(gradle_cmd, gradle_params)
262 |
263 | def assemble_build_test_apk_cmd(self, gradle_bin, gradle_params, assemble_task, project_root_dir):
264 | gradle_cmd = GradleCommand.RUN_TASK_IN_OTHER_DIRECTORY.format(gradle_bin,
265 | project_root_dir,
266 | assemble_task)
267 | return self.gradle_command_schema.format(gradle_cmd, gradle_params)
268 |
269 |
270 | class InstrumentationRunnerCommandAssembler:
271 | test_command_schema = "{} {} {} {} {} {} {} {}"
272 | single_test_command_schema = "{} {} {} {} {} {} {}{} {}"
273 |
274 | def assemble_run_test_package_cmd(self, adb_binary, device_adb_name, params, instrumentation_runner):
275 | parameters = self._assemble_params(params)
276 | return self.test_command_schema.format(adb_binary,
277 | AdbCommand.SPECIFIC_DEVICE.format(device_adb_name),
278 | AdbShellCommand.SHELL,
279 | AdbActivityManagerCommand.ACTIVITY_MANAGER,
280 | InstrumentationRunnerCommand.INSTRUMENT_PROCESS,
281 | InstrumentationRunnerCommand.DISPLAY_RAW_MESSAGE,
282 | parameters,
283 | InstrumentationRunnerCommand.INSTRUMENTATION_RUNNER.format(
284 | instrumentation_runner)
285 | )
286 |
287 | def assemble_run_single_test_cmd(self, adb_binary, device_adb_name, test_class, test_name, instrumentation_runner):
288 | return self.single_test_command_schema.format(adb_binary,
289 | AdbCommand.SPECIFIC_DEVICE.format(device_adb_name),
290 | AdbShellCommand.SHELL,
291 | AdbActivityManagerCommand.ACTIVITY_MANAGER,
292 | InstrumentationRunnerCommand.INSTRUMENT_PROCESS,
293 | InstrumentationRunnerCommand.DISPLAY_RAW_MESSAGE,
294 | InstrumentationRunnerCommand.CLASS.format(test_class),
295 | InstrumentationRunnerCommand.TEST_CASE.format(test_name),
296 | InstrumentationRunnerCommand.INSTRUMENTATION_RUNNER.format(
297 | instrumentation_runner)
298 | )
299 |
300 | @staticmethod
301 | def _assemble_params(params):
302 | parameters = ""
303 | for param, value in params.items():
304 | if param == "package":
305 | parameters += (" " + InstrumentationRunnerCommand.PACKAGE.format(value))
306 | if param == "numShards":
307 | parameters += (" " + InstrumentationRunnerCommand.NUM_SHARD.format(value))
308 | if param == "shardIndex":
309 | parameters += (" " + InstrumentationRunnerCommand.SHARD_INDEX.format(value))
310 | return parameters
311 |
--------------------------------------------------------------------------------
/system/bin/AndroidShellCommands.py:
--------------------------------------------------------------------------------
1 | class AaptCommand:
2 | DUMP_BADGING = "dump badging {}"
3 | LIST_ALL = "l -a {}"
4 |
5 |
6 | class AdbCommand:
7 | KILL_SERVER = "kill-server"
8 | START_SERVER = "start-server"
9 | DEVICES = "devices"
10 | WAIT_FOR_DEVICE = "wait-for-device"
11 | LIST_AVD = "list avd"
12 | KILL_DEVICE = "emu kill"
13 | SPECIFIC_DEVICE = "-s {}"
14 | INSTALL_APK = "install {}"
15 | UNINSTALL_PACKAGE = "uninstall {}"
16 | PULL = "pull {} {}"
17 |
18 |
19 | class AdbShellCommand:
20 | SHELL = "shell"
21 | CREATE_DIR = "mkdir {}"
22 | REMOVE_FILE = "rm -f {}"
23 | REMOVE_FILES_IN_DIR = "rm -r {}"
24 | RECORD = "screenrecord --bit-rate 2000000 {}"
25 | GET_PROPERTY = "getprop {}"
26 |
27 |
28 | class AdbPackageManagerCommand:
29 | PACKAGE_MANAGER = "pm"
30 | CLEAR_CACHE = "clear {}"
31 | LIST_SERVICES = "list packages"
32 | UNINSTALL_PACKAGE = "uninstall {}"
33 |
34 |
35 | class AdbActivityManagerCommand:
36 | ACTIVITY_MANAGER = "am"
37 |
38 |
39 | class AdbSettingsCommand:
40 | SETTINGS = "settings"
41 | GET_DEVICE_ANDROID_ID = "get secure android_id"
42 |
43 |
44 | class AdbLogCatCommand:
45 | LOG_CAT = "logcat"
46 | FLUSH = "-b all -c"
47 | DUMP = "-d"
48 |
49 |
50 | class InstrumentationRunnerCommand:
51 | INSTRUMENT_PROCESS = "instrument -w"
52 | NUM_SHARD = "-e numShards {}"
53 | SHARD_INDEX = "-e shardIndex {}"
54 | PACKAGE = "-e package {}"
55 | DISPLAY_RAW_MESSAGE = "-r"
56 | INSTRUMENTATION_RUNNER = "{}"
57 | CLASS = "-e class {}"
58 | TEST_CASE = "#{}"
59 |
60 | class AvdManagerCommand:
61 | class CreateAvdCommandPart:
62 | AVD_CREATE = "create avd"
63 | AVD_NAME = "--name \"{}\""
64 | AVD_PACKAGE = "--package \"{}\""
65 | AVD_TAG = "--tag \"{}\""
66 | AVD_ABI = "--abi {}"
67 | AVD_DEVICE = "--device \"{}\""
68 | AVD_ADDITIONAL_OPTIONS = "{}"
69 |
70 | LIST_AVD = "list avd"
71 | DELETE_AVD = "delete avd -n {}"
72 |
73 |
74 | class EmulatorCommand:
75 | class LaunchAvdCommandPart:
76 | AVD_NAME = "-avd \"{}\""
77 | AVD_PORT = "-port {}"
78 | AVD_SNAPSHOT = "-wipe-data -initdata {}"
79 | AVD_ADDITIONAL_OPTIONS = "{}"
80 | AVD_OUTPUT = "{}"
81 |
82 |
83 | class GradleCommand:
84 | RUN_TASK_IN_OTHER_DIRECTORY = "{} -p {} {}"
85 |
--------------------------------------------------------------------------------
/system/bin/SystemShellCommands.py:
--------------------------------------------------------------------------------
1 | class GeneralCommand:
2 | ANSWER_NO = "echo \"no\" |"
3 | CHECK_PORT = "lsof -i:{}"
4 | COPY_FROM_TO = "cat {} > {}"
5 | DELEGATE_OUTPUT_TO_FILE = "&> {}"
6 | DELEGATE_OUTPUT_TO_NULL = "&>/dev/null"
7 | CHANGE_THREAD = "&"
8 | CHECK_IF_DIR_EXISTS = "ls -d {}"
9 |
--------------------------------------------------------------------------------
/system/buffer/AdbCallBuffer.py:
--------------------------------------------------------------------------------
1 | import time
2 | import threading
3 |
4 | from settings import GlobalConfig
5 |
6 | _adb_call_buffer = list()
7 |
8 |
9 | def is_adb_cmd(cmd):
10 | adb_bin_found = False
11 |
12 | cmd_parts = cmd.split(" ")
13 | if cmd_parts:
14 | bin_part = cmd_parts[0]
15 |
16 | if bin_part is not None and len(bin_part) >= 3:
17 | last_three_letters = bin_part[-3:]
18 | adb_bin_found = last_three_letters.lower() == "adb"
19 | return adb_bin_found
20 |
21 |
22 | def report_adb_call():
23 | if _is_full():
24 | _wait_for_empty_slot()
25 | _report_adb_call()
26 |
27 |
28 | def _is_full():
29 | return len(_adb_call_buffer) >= GlobalConfig.ADB_CALL_BUFFER_SIZE
30 |
31 |
32 | def _wait_for_empty_slot():
33 | while True:
34 | if not _is_full():
35 | break
36 | time.sleep(0.1)
37 |
38 |
39 | def _report_adb_call():
40 | cmd_call_time = int(round(time.time() * 1000))
41 | _adb_call_buffer.append(cmd_call_time)
42 |
43 | remove_slot_thread = _ReleaseBufferSlotThread(_adb_call_buffer, cmd_call_time)
44 | remove_slot_thread.start()
45 |
46 |
47 | class _ReleaseBufferSlotThread(threading.Thread):
48 | TAG = "_ReleaseBufferSlotThread"
49 |
50 | def __init__(self, buffer, cmd_call_time):
51 | super().__init__()
52 |
53 | self.buffer = buffer
54 | self.cmd_call_time = cmd_call_time
55 |
56 | def run(self):
57 | while (self._get_current_time_millis() - self.cmd_call_time) < GlobalConfig.ADB_CALL_BUFFER_DELAY_BETWEEN_CMD:
58 | time.sleep(0.1)
59 | self.buffer.remove(self.cmd_call_time)
60 |
61 | @staticmethod
62 | def _get_current_time_millis():
63 | return int(round(time.time() * 1000))
64 |
--------------------------------------------------------------------------------
/system/console/Color.py:
--------------------------------------------------------------------------------
1 | RED = '\033[91m'
2 | BLUE = '\033[94m'
3 | GREEN = '\033[92m'
4 | YELLOW = '\033[93m'
5 | GOLD = '\033[33m'
6 | DARK_PURPLE = '\033[35m'
7 | PURPLE = '\033[95m'
8 | PURPLE_UNDERLINE = '\033[95;4m'
9 | GRAY = '\033[90m'
10 | END = '\033[0m'
11 |
--------------------------------------------------------------------------------
/system/console/Printer.py:
--------------------------------------------------------------------------------
1 | import datetime
2 |
3 | from system.console import Color
4 |
5 |
6 | def phase(message):
7 | print("\n" + Color.PURPLE + "[{:%H:%M:%S}]".format(
8 | datetime.datetime.now()) + " " + Color.PURPLE_UNDERLINE + message + " PHASE" + Color.END)
9 |
10 |
11 | def error(tag, message):
12 | message_assembly = ""
13 |
14 | message_assembly += Color.RED + "[{:%H:%M:%S}] - ".format(datetime.datetime.now())
15 | if tag != "":
16 | message_assembly += tag + " "
17 | message_assembly += message + Color.END
18 |
19 | print(message_assembly)
20 |
21 |
22 | def step(message):
23 | print(Color.YELLOW + "[{:%H:%M:%S}] ".format(datetime.datetime.now()) + message + Color.END)
24 |
25 |
26 | def system_message(tag, message):
27 | message_assembly = ""
28 |
29 | message_assembly += Color.BLUE + "[{:%H:%M:%S}] - ".format(datetime.datetime.now())
30 | if tag != "":
31 | message_assembly += tag + " "
32 | message_assembly += message + Color.END
33 |
34 | print(message_assembly)
35 |
36 |
37 | def console_highlighted(tag, message, cmd):
38 | print(Color.BLUE + "[{:%H:%M:%S}] - ".format(datetime.datetime.now())
39 | + tag + " " + message + Color.GOLD + cmd + Color.END)
40 |
41 |
42 | def console(message, end):
43 | print(Color.DARK_PURPLE + "[{:%H:%M:%S}] > ".format(datetime.datetime.now()) + message + Color.END, end=end)
44 |
--------------------------------------------------------------------------------
/system/console/ShellHelper.py:
--------------------------------------------------------------------------------
1 | import subprocess
2 |
3 | from system.console import Printer
4 | from system.buffer import AdbCallBuffer
5 |
6 | TAG = "ShellHelper:"
7 |
8 |
9 | def execute_shell(cmd, show_cmd, monitor_output):
10 | if AdbCallBuffer.is_adb_cmd(cmd):
11 | AdbCallBuffer.report_adb_call()
12 |
13 | if show_cmd:
14 | Printer.console_highlighted(TAG, "Executing shell command: ", cmd)
15 | with subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, bufsize=1, universal_newlines=True) as p:
16 | output = ""
17 | for line in p.stdout:
18 | if monitor_output:
19 | Printer.console(line, end='')
20 | output += line
21 |
22 | if p.returncode != 0:
23 | raise Exception(p.returncode, p.args)
24 |
25 | return output
26 |
--------------------------------------------------------------------------------
/system/file/FileUtils.py:
--------------------------------------------------------------------------------
1 | import os
2 | import shutil
3 | import distutils.dir_util
4 | import codecs
5 | import json
6 |
7 | from error.Exceptions import LauncherFlowInterruptedException
8 |
9 | TAG = "FileUtils:"
10 |
11 |
12 | def copy(from_dir, to_dir):
13 | distutils.dir_util.copy_tree(from_dir, to_dir)
14 |
15 |
16 | def dir_exists(directory):
17 | return os.path.exists(directory)
18 |
19 |
20 | def file_exists(file):
21 | return os.path.isfile(file)
22 |
23 |
24 | def list_files_in_dir(directory):
25 | return os.listdir(directory)
26 |
27 |
28 | def create_dir(directory):
29 | dir_path = clean_path(directory)
30 |
31 | try:
32 | os.makedirs(directory)
33 | except Exception as e:
34 | message = "Unable to create directory '{}'. Error message: {}"
35 | message = message.format(dir_path, str(e))
36 | raise LauncherFlowInterruptedException(TAG, message)
37 |
38 | return True
39 |
40 |
41 | def clear_dir(directory):
42 | if list_files_in_dir(directory):
43 | for the_file in list_files_in_dir(directory):
44 | file_path = os.path.join(directory, the_file)
45 | try:
46 | if file_exists(file_path):
47 | os.unlink(file_path)
48 | elif file_exists(file_path):
49 | shutil.rmtree(file_path)
50 | except Exception as e:
51 | message = "Unable to delete files in directory '{}'. Error message: {}"
52 | message = message.format(directory, str(e))
53 | raise LauncherFlowInterruptedException(TAG, message)
54 |
55 | return True
56 |
57 |
58 | def create_file(directory, file_name, extension):
59 | file_path = clean_path(directory + str(file_name) + "." + extension)
60 |
61 | try:
62 | if not dir_exists(directory):
63 | os.makedirs(directory)
64 | open(file_path, "w", encoding="utf-8")
65 | except Exception as e:
66 | message = "Unable to create file '{}.{}'. Error message: {}"
67 | message = message.format(file_path, extension, str(e))
68 | raise LauncherFlowInterruptedException(TAG, message)
69 |
70 | return True
71 |
72 |
73 | def load_json(json_dir):
74 | json_dir = clean_path(json_dir)
75 | with open(json_dir, "r", encoding="utf-8") as json_file:
76 | json_data = json_file.read()
77 |
78 | try:
79 | json_dict = json.loads(json_data)
80 | except Exception as e:
81 | message = "Unable to parse JSON file '{}/{}'. Error message: {}"
82 | message = message.format(os.getcwd(), json_dir, str(e))
83 | raise LauncherFlowInterruptedException(TAG, message)
84 |
85 | return json_dict
86 |
87 |
88 | def save_json_dict_to_json(directory, json_dict, file_name):
89 | extension = ".json"
90 | if extension not in file_name:
91 | file_name = file_name + extension
92 |
93 | file_path = clean_path(directory + str(file_name))
94 | if not dir_exists(directory):
95 | os.makedirs(directory)
96 |
97 | with open(file_path, 'w', encoding="utf-8") as f:
98 | f.write(json.dumps(json_dict, indent=4, ensure_ascii=False))
99 |
100 | return os.path.abspath(file_path)
101 |
102 |
103 | def clean_folder_only_dir(path):
104 | return remove_slash_pairs(add_ending_slash(clean_path(path)))
105 |
106 |
107 | def clean_path(path):
108 | return codecs.getdecoder("unicode_escape")(os.path.expanduser(path))[0]
109 |
110 |
111 | def add_starting_slash(path):
112 | if path != "" and path[0] != "/":
113 | return "/" + path
114 | else:
115 | return path
116 |
117 |
118 | def add_ending_slash(path):
119 | if path != "" and path[len(path) - 1] != "/":
120 | return path + "/"
121 | else:
122 | return path
123 |
124 |
125 | def remove_slash_pairs(path):
126 | return path.replace("//", "/")
127 |
128 |
129 | def get_project_root():
130 | return os.path.abspath(os.path.dirname(__name__))
131 |
132 |
133 | def make_path_absolute(path):
134 | return os.path.abspath(clean_path(path))
135 |
--------------------------------------------------------------------------------
/system/port/PortManager.py:
--------------------------------------------------------------------------------
1 | from error.Exceptions import LauncherFlowInterruptedException
2 |
3 | from system.bin.SystemShellCommands import GeneralCommand
4 | from system.console import (
5 | ShellHelper,
6 | Printer
7 | )
8 |
9 | TAG = "PortManager:"
10 |
11 |
12 | def get_open_ports(avd_set):
13 | _check_port_rules(avd_set)
14 |
15 | ports_to_use = avd_set.avd_port_rules.ports_to_use
16 | if len(ports_to_use) > 0:
17 | message = "Requested ports:"
18 | for port in ports_to_use:
19 | message += (" '" + str(port) + "'")
20 | message += "."
21 | message(TAG, message)
22 |
23 | ports_to_ignore = avd_set.avd_port_rules.ports_to_ignore
24 | if len(ports_to_ignore) > 0:
25 | message = "Ignoring ports:"
26 | for port in ports_to_ignore:
27 | message += (" '" + str(port) + "'")
28 | message += "."
29 | Printer.system_message(TAG, message)
30 |
31 | should_assign_missing_ports = avd_set.avd_port_rules.assign_missing_ports
32 | if should_assign_missing_ports:
33 | Printer.system_message(TAG, "Not requested ports will be automatically assigned.")
34 | else:
35 | Printer.system_message(TAG, "Port auto assignment is turned off. Only specified ports will be used.")
36 |
37 | avd_instances = 0
38 | for avd in avd_set.avd_list:
39 | avd_instances += avd.instances
40 |
41 | range_min = avd_set.avd_port_rules.search_range_min
42 | range_max = avd_set.avd_port_rules.search_range_max
43 | Printer.system_message(TAG, "Checking for " + str(avd_instances) + " open ports in range <" + str(
44 | range_min) + ", " + str(range_max) + ">.")
45 |
46 | available_ports = list()
47 |
48 | for port in ports_to_use:
49 | try:
50 | ShellHelper.execute_shell(GeneralCommand.CHECK_PORT.format(port), False, False)
51 | port_open = False
52 | except:
53 | port_open = True
54 |
55 | if port_open:
56 | available_ports.append(port)
57 | else:
58 | message = "Port {} was requested but is currently used."
59 | message = message.format(str(port))
60 | raise LauncherFlowInterruptedException(TAG, message)
61 |
62 | if should_assign_missing_ports:
63 | temp_port = range_min
64 | for i in range(avd_instances - len(available_ports)):
65 | while temp_port < range_max and len(available_ports) != avd_instances:
66 | try:
67 | ShellHelper.execute_shell(GeneralCommand.CHECK_PORT.format(temp_port), False, False)
68 | port_open = False
69 | except:
70 | port_open = True
71 |
72 | if port_open:
73 | if temp_port not in available_ports and temp_port not in ports_to_ignore:
74 | available_ports.append(temp_port)
75 | else:
76 | Printer.error(TAG, "Port " + str(temp_port) + " is currently used and will be omitted.")
77 |
78 | temp_port += 2
79 |
80 | if len(available_ports) != avd_instances:
81 | message = "There are only {} open ports available in range <{}, {}>. Requested amount: {}."
82 | message = message.format(str(len(available_ports)), str(range_min), str(range_max), str(avd_instances))
83 | raise LauncherFlowInterruptedException(TAG, message)
84 |
85 | return available_ports
86 |
87 |
88 | def _check_port_rules(avd_set):
89 | avd_instances = 0
90 | avd_rules = avd_set.avd_port_rules
91 |
92 | for avd in avd_set.avd_list:
93 | avd_instances += avd.instances
94 |
95 | if len(avd_rules.ports_to_use) != len(set(avd_rules.ports_to_use)):
96 | message = "'Ports to use' list contains duplicates."
97 | raise LauncherFlowInterruptedException(TAG, message)
98 |
99 | if len(avd_rules.ports_to_ignore) != len(set(avd_rules.ports_to_ignore)):
100 | message = "'Ports to ignore' list contains duplicates."
101 | raise LauncherFlowInterruptedException(TAG, message)
102 |
103 | for port_to_be_used in avd_rules.ports_to_use:
104 | if port_to_be_used % 2 == 1:
105 | message = "Port numbers has to be even."
106 | raise LauncherFlowInterruptedException(TAG, message)
107 |
108 | for port_to_be_used in avd_rules.ports_to_use:
109 | if port_to_be_used < avd_rules.search_range_min or port_to_be_used > avd_rules.search_range_max:
110 | message = "Requested to use port {} is out of range <{}, {}>."
111 | message = message.format(str(port_to_be_used), str(avd_rules.search_range_min),
112 | str(avd_rules.search_range_max))
113 | raise LauncherFlowInterruptedException(TAG, message)
114 |
115 | for port_to_be_ignored in avd_rules.ports_to_ignore:
116 | for port_to_be_used in avd_rules.ports_to_use:
117 | if port_to_be_used == port_to_be_ignored:
118 | message = "Port {} is set to be used and ignored at the same time."
119 | message = message.format(str(port_to_be_used))
120 | raise LauncherFlowInterruptedException(TAG, message)
121 |
122 | if not avd_rules.assign_missing_ports:
123 | requested_ports_to_use_num = len(avd_rules.ports_to_use)
124 | if requested_ports_to_use_num != avd_instances:
125 | message = ("There are {} AVD instances about to be created according to set, but there were only {} ports "
126 | "requested to use. Set 'assign_missing_ports' to True or add more ports to list.")
127 | message = message.format(str(avd_instances), str(requested_ports_to_use_num))
128 | raise LauncherFlowInterruptedException(TAG, message)
129 |
--------------------------------------------------------------------------------