Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
S2-group
GitHub Repository: S2-group/android-runner
Path: blob/master/AndroidRunner/Experiment.py
629 views
1
import psutil
2
import logging
3
import multiprocessing as mp
4
import os.path as op
5
import time
6
from os import remove, rmdir, walk
7
from threading import Thread
8
from AndroidRunner.USBHandler import USBHandler
9
from . import Tests
10
from . import Adb
11
import paths
12
from .Devices import Devices
13
from .Profilers import Profilers
14
from .Scripts import Scripts
15
from .util import ConfigError, makedirs, slugify_dir
16
from AndroidRunner.PrematureStoppableRun import PrematureStoppableRun
17
import multiprocessing as mp
18
# noinspection PyUnusedLocal
19
class Experiment(object):
20
def __init__(self, config, progress, restart):
21
self.logger = logging.getLogger(self.__class__.__name__)
22
self.progress = progress
23
self.basedir = None
24
self.random = config.get('randomization', False)
25
26
Tests.is_valid_option(self.random, valid_options=[True, False])
27
self.clear_cache = config.get('clear_cache', False)
28
Tests.is_valid_option(self.clear_cache, valid_options=[True, False])
29
30
if 'devices' not in config:
31
raise ConfigError('"device" is required in the configuration')
32
adb_path = config.get('adb_path', 'adb')
33
34
self.devices = Devices(config['devices'], adb_path=adb_path, devices_spec=config.get('devices_spec'))
35
self.repetitions = Tests.is_integer(config.get('repetitions', 1))
36
self.paths = config.get('paths', [])
37
self.profilers = Profilers(config.get('profilers', {}))
38
self.scripts = Scripts(config.get('scripts', {}))
39
self.reset_adb_among_runs = config.get('reset_adb_among_runs', False)
40
Tests.is_valid_option(self.reset_adb_among_runs, valid_options=[True, False])
41
self.time_between_run = Tests.is_integer(config.get('time_between_run', 0))
42
Tests.check_dependencies(self.devices, self.profilers.dependencies())
43
self.output_root = paths.OUTPUT_DIR
44
self.result_file_structure = None
45
46
self.usb_handler_config = config.get("usb_handler", None)
47
self.usb_handler = USBHandler(self.usb_handler_config)
48
49
self.run_stopping_condition_config = config.get("run_stopping_condition", None)
50
self.queue = mp.Queue()
51
52
if restart:
53
for device in self.devices:
54
self.prepare_device(device, restart=True)
55
56
def prepare_device(self, device, restart=False):
57
"""Prepare the device for experiment"""
58
self.logger.info('Device: %s' % device)
59
self.profilers.load(device)
60
device.unplug(restart)
61
62
def cleanup(self, device):
63
"""Cleans up the changes on the devices"""
64
self.usb_handler.enable_usb()
65
device.plug()
66
self.profilers.stop_profiling(device)
67
self.profilers.unload(device)
68
69
def get_progress_xml_file(self):
70
return self.progress.progress_xml_file
71
def update_progress(self):
72
self.progress.write_progress_to_file()
73
result_data_path = op.join(paths.BASE_OUTPUT_DIR, 'data')
74
self.result_file_structure = self.walk_to_list(walk(result_data_path))
75
76
def start(self):
77
try:
78
result_data_path = op.join(paths.BASE_OUTPUT_DIR, 'data')
79
self.result_file_structure = self.walk_to_list(walk(result_data_path))
80
while not self.progress.experiment_finished_check():
81
current_run = self.get_experiment()
82
self.run_experiment(current_run)
83
self.save_progress()
84
except Exception as e:
85
import traceback
86
print((traceback.format_exc()))
87
self.logger.error('%s: %s' % (e.__class__.__name__, str(e)))
88
self.finish_experiment(True, False)
89
raise e
90
except KeyboardInterrupt:
91
self.finish_experiment(False, True)
92
raise KeyboardInterrupt
93
else:
94
self.finish_experiment(False, False)
95
96
def finish_experiment(self, error, interrupted):
97
self.check_result_files(self.result_file_structure)
98
for device in self.devices:
99
try:
100
self.cleanup(device)
101
except Exception:
102
continue
103
if not error and not interrupted:
104
self.aggregate_end()
105
106
def run_experiment(self, current_run):
107
self.prepare_run(current_run)
108
self.run_run(current_run)
109
self.finish_run(current_run)
110
111
def prepare_run(self, current_run):
112
self.prepare_output_dir(current_run)
113
self.first_run_device(current_run)
114
self.before_every_run_subject(current_run)
115
116
def run_run(self, current_run):
117
if 'browser' in current_run:
118
self.run(self.devices.get_device(current_run['device']), current_run['path'],
119
int(current_run['runCount']), current_run['browser'])
120
else:
121
self.run(self.devices.get_device(current_run['device']), current_run['path'],
122
int(current_run['runCount']), None)
123
124
def finish_run(self, current_run):
125
self.progress.run_finished(current_run['runId'])
126
self.last_run_subject(current_run)
127
self.last_run_device(current_run)
128
129
def save_progress(self):
130
a = Thread(target=self.update_progress)
131
a.start()
132
a.join()
133
134
def check_result_files(self, correct_file_list):
135
result_data_path = op.join(paths.BASE_OUTPUT_DIR, 'data')
136
current_file_structure = walk(result_data_path)
137
current_file_list = self.walk_to_list(current_file_structure)
138
for path in current_file_list:
139
if path not in correct_file_list:
140
if op.isfile(path):
141
remove(path)
142
else:
143
rmdir(path)
144
145
@staticmethod
146
def walk_to_list(walk_result):
147
walk_list = list()
148
for (path, dirs, files) in walk_result:
149
for dr in dirs:
150
walk_list.append(op.join(path, dr))
151
for fl in files:
152
walk_list.append(op.join(path, fl))
153
walk_list.reverse()
154
return walk_list
155
156
def get_experiment(self):
157
if self.random:
158
return self.progress.get_random_run()
159
else:
160
return self.progress.get_next_run()
161
162
def first_run_device(self, current_run):
163
device = self.devices.get_device(current_run['device'])
164
if self.progress.device_first(current_run['device']):
165
self.prepare_device(device)
166
self.before_experiment(device)
167
168
def before_every_run_subject(self, current_run):
169
self.before_run_subject(self.devices.get_device(current_run['device']), current_run['path'])
170
171
def last_run_device(self, current_run):
172
if self.progress.device_finished(current_run['device']):
173
self.after_experiment(self.devices.get_device(current_run['device']))
174
175
def last_run_subject(self, current_run):
176
if self.progress.subject_finished(current_run['device'], current_run['path']):
177
self.after_last_run(self.devices.get_device(current_run['device']), current_run['path'])
178
self.aggregate_subject()
179
180
def prepare_output_dir(self, current_run):
181
paths.OUTPUT_DIR = op.join(paths.BASE_OUTPUT_DIR, 'data/', current_run['device'],
182
slugify_dir(current_run['path']))
183
makedirs(paths.OUTPUT_DIR)
184
185
def stop_run(self):
186
"""
187
Stops the current run. Can only be called when using the run_stopping_condition option.
188
"""
189
if not self.run_stopping_condition_config:
190
raise ConfigError("Experiment.stop_run() can only be called when a valid run_stopping_condition value is set in the config.")
191
self.queue.put(PrematureStoppableRun.STOPPING_MECHANISM_FUNCTION_CALL)
192
193
def run(self, device, path, run, dummy):
194
self.before_run(device, path, run)
195
196
self.usb_handler.disable_usb()
197
self.start_profiling(device, path, run)
198
199
if self.run_stopping_condition_config:
200
self.queue = mp.Queue()
201
premature_stoppable_run = PrematureStoppableRun(self.run_stopping_condition_config, self.queue, self.interaction, device, path, run)
202
premature_stoppable_run.run()
203
else:
204
self.interaction(device, path, run)
205
206
self.stop_profiling(device, path, run)
207
self.usb_handler.enable_usb()
208
209
self.after_run(device, path, run)
210
211
def before_experiment(self, device, *args, **kwargs):
212
"""Hook executed before the first run of a device in current experiment"""
213
self.scripts.run('before_experiment', device, *args, **kwargs)
214
215
def before_run_subject(self, device, path, *args, **kwargs):
216
"""Hook executed before the first run for a subject"""
217
pass
218
219
def before_run(self, device, path, run, *args, **kwargs):
220
"""Hook executed before a run"""
221
self.profilers.set_output()
222
self.logger.info('Run %s/%s of subject "%s" on %s' % (run, self.repetitions, path, device.name))
223
device.shell('logcat -c')
224
self.logger.info('Logcat cleared')
225
self.scripts.run('before_run', device, *args, **kwargs)
226
227
def after_launch(self, device, path, run, *args, **kwargs):
228
self.scripts.run('after_launch', device, device.id, device.current_activity(), *args, **kwargs)
229
230
def start_profiling(self, device, path, run, *args, **kwargs):
231
#FIXME: handle *args
232
self.profilers.start_profiling(device, **kwargs)
233
234
def interaction(self, device, path, run, *args, **kwargs):
235
"""Interactions on the device to be profiled"""
236
self.scripts.run('interaction', device, self, *args, **kwargs)
237
238
def stop_profiling(self, device, path, run, *args, **kwargs):
239
# FIXME: handle *args
240
self.profilers.stop_profiling(device, **kwargs)
241
242
def before_close(self, device, path, run, *args, **kwargs):
243
self.scripts.run('before_close', device, device.id, device.current_activity(), *args, **kwargs)
244
245
def after_run(self, device, path, run, *args, **kwargs):
246
"""Hook executed after a run"""
247
self.scripts.run('after_run', device, *args, **kwargs)
248
self.profilers.collect_results(device)
249
Adb.reset(self.reset_adb_among_runs)
250
self.logger.info('Sleeping for %s milliseconds' % self.time_between_run)
251
time.sleep(self.time_between_run / 1000.0)
252
253
def after_last_run(self, device, path, *args, **kwargs):
254
"""Hook executed after the last run of a subject"""
255
pass
256
257
def after_experiment(self, device, *args, **kwargs):
258
"""Hook executed after the last run for device of experiment"""
259
self.logger.info('Experiment completed, start cleanup')
260
self.scripts.run('after_experiment', device, *args, **kwargs)
261
262
def aggregate_subject(self):
263
self.profilers.aggregate_subject()
264
265
def aggregate_end(self):
266
self.profilers.aggregate_end(self.output_root)
267
268