class VoltaBox500Hz(VoltaBox): """ VoltaBox500Hz - works with plain-text 500hz box, grabs data and stores data to queue Attributes: source (string): path to data source, should be able to be opened by resource manager may be url, e.g. 'http://myhost.tld/path/to/file' may be device, e.g. '/dev/cu.wchusbserial1420' may be path to file, e.g. '/home/users/netort/path/to/file.data' sample_rate (int): volta box sample rate - depends on software and which type of volta box you use chop_ratio (int): chop ratio for incoming data, 1 means 1 second (500 for sample_rate 500) baud_rate (int): baud rate for device if device specified in source grab_timeout (int): timeout for grabber """ def __init__(self, config): VoltaBox.__init__(self, config) self.source = config.get_option('volta', 'source') self.chop_ratio = config.get_option('volta', 'chop_ratio') self.sample_rate = config.get_option('volta', 'sample_rate', 500) self.baud_rate = config.get_option('volta', 'baud_rate', 115200) self.grab_timeout = config.get_option('volta', 'grab_timeout') # initialize data source try: self.source_opener = resource.get_opener(self.source) except: raise RuntimeError( 'Device %s not found. Please check VoltaBox USB connection', self.source) self.source_opener.baud_rate = self.baud_rate self.source_opener.read_timeout = self.grab_timeout self.data_source = self.source_opener() logger.debug('Data source initialized: %s', self.data_source) self.pipeline = None self.grabber_q = None self.process_currents = None def start_test(self, results): """ Grab stage - starts grabber thread and puts data to results queue +clean up dirty buffer pipeline read source data -> chop by samplerate w/ ratio -> make pandas DataFrame -> drain DataFrame to queue `results` """ self.grabber_q = results # clean up dirty buffer for _ in range(self.sample_rate): self.data_source.readline() self.reader = BoxPlainTextReader(self.data_source, self.sample_rate) self.pipeline = Drain( TimeChopper(self.reader, self.sample_rate, self.chop_ratio), self.grabber_q) logger.info('Starting grab thread...') self.pipeline.start() logger.debug('Waiting grabber thread finish...') def end_test(self): self.reader.close() self.pipeline.close() self.pipeline.join(10) self.data_source.close() def get_info(self): data = {} if self.pipeline: data['grabber_alive'] = self.pipeline.isAlive() if self.grabber_q: data['grabber_queue_size'] = self.grabber_q.qsize() return data
class VoltaBoxBinary(VoltaBox): """ VoltaBoxBinary - works with binary box, grabs data and stores data to queue Attributes: source (string): path to data source, should be able to be opened by resource manager may be url, e.g. 'http://myhost.tld/path/to/file' may be device, e.g. '/dev/cu.wchusbserial1420' may be path to file, e.g. '/home/users/netort/path/to/file.data' sample_rate (int): volta box sample rate - depends on software and which type of volta box you use chop_ratio (int): chop ratio for incoming data, 1 means 1 second (500 for sample_rate 500) baud_rate (int): baud rate for device if device specified in source grab_timeout (int): timeout for grabber """ def __init__(self, config): VoltaBox.__init__(self, config) self.source = config.get_option('volta', 'source') self.sample_rate = config.get_option('volta', 'sample_rate', 10000) self.chop_ratio = config.get_option('volta', 'chop_ratio') self.baud_rate = config.get_option('volta', 'baud_rate', 230400) self.grab_timeout = config.get_option('volta', 'grab_timeout') self.slope = config.get_option('volta', 'slope') self.offset = config.get_option('volta', 'offset') self.power_voltage = config.get_option('volta', 'power_voltage') # initialize data source self.source_opener = resource.get_opener(self.source) self.source_opener.baud_rate = self.baud_rate self.source_opener.read_timeout = self.grab_timeout self.data_source = self.source_opener() logger.debug('Data source initialized: %s', self.data_source) self.pipeline = None self.grabber_q = None self.process_currents = None def start_test(self, results): """ Grab stage - starts grabber thread and puts data to results queue + handshake w/ device, get samplerate pipeline read source data -> chop by samplerate w/ ratio -> make pandas DataFrame -> drain DataFrame to queue `results` Args: results: object answers to put() and get() methods """ self.grabber_q = results # handshake while self.data_source.readline() != "VOLTAHELLO\n": pass volta_spec = json.loads(self.data_source.readline()) self.sample_rate = volta_spec["sps"] logger.info('Sample rate handshake success: %s', self.sample_rate) while self.data_source.readline() != "DATASTART\n": pass self.reader = BoxBinaryReader(self.data_source, self.sample_rate, self.slope, self.offset) self.pipeline = Drain( TimeChopper(self.reader, self.sample_rate, self.chop_ratio), self.grabber_q) logger.info('Starting grab thread...') self.pipeline.start() logger.debug('Waiting grabber thread finish...') def end_test(self): self.reader.close() self.pipeline.close() self.pipeline.join(10) self.data_source.close() def get_info(self): data = {} if self.pipeline: data['grabber_alive'] = self.pipeline.isAlive() if self.grabber_q: data['grabber_queue_size'] = self.grabber_q.qsize() return data
class AndroidPhone(Phone): """ Android phone worker class - work w/ phone, read phone logs, run test apps and store data Attributes: source (string): path to data source, phone id (adb devices) lightning_apk_path (string, optional): path to lightning app may be url, e.g. 'http://myhost.tld/path/to/file' may be path to file, e.g. '/home/users/netort/path/to/file.apk' lightning_apk_class (string, optional): lightning class test_apps (list, optional): list of apps to be installed to device for test test_class (string, optional): app class to be started during test execution test_package (string, optional): app package to be started during test execution test_runner (string, optional): app runner to be started during test execution """ def __init__(self, config): """ Args: config (VoltaConfig): module configuration data """ Phone.__init__(self, config) self.logcat_stdout_reader = None self.logcat_stderr_reader = None # mandatory options self.source = config.get_option('phone', 'source') # lightning app configuration self.lightning_apk_path = config.get_option( 'phone', 'lightning', pkg_resources.resource_filename('volta.providers.phones', 'binary/lightning-new3.apk')) self.lightning_apk_class = config.get_option('phone', 'lightning_class') self.lightning_apk_fname = None # test app configuration self.test_apps = config.get_option('phone', 'test_apps') self.test_class = config.get_option('phone', 'test_class') self.test_package = config.get_option('phone', 'test_package') self.test_runner = config.get_option('phone', 'test_runner') self.regexp = config.get_option('phone', 'event_regexp', event_regexp) try: self.compiled_regexp = re.compile(self.regexp, re.VERBOSE | re.IGNORECASE) except: logger.debug('Unable to parse specified regexp', exc_info=True) raise RuntimeError("Unable to parse specified regexp") self.drain_logcat_stdout = None self.test_performer = None def prepare(self): """ Phone preparements stage: install apps etc pipeline: install lightning install apks clean log """ # install lightning self.lightning_apk_fname = resource.get_opener( self.lightning_apk_path).get_filename logger.info('Installing lightning apk...') execute("adb -s {device_id} install -r -d -t {apk}".format( device_id=self.source, apk=self.lightning_apk_fname)) # install apks for apk in self.test_apps: apk_fname = resource.get_opener(apk).get_filename execute("adb -s {device_id} install -r -d -t {apk}".format( device_id=self.source, apk=apk_fname)) # clean logcat execute("adb -s {device_id} logcat -c".format(device_id=self.source)) def start(self, results): """ Grab stage: starts log reader, make sync w/ flashlight Args: results (queue-like object): Phone should put there dataframes, format: ['sys_uts', 'message'] """ self.phone_q = results self.__start_async_logcat() # start flashes app execute( "adb -s {device_id} shell am start -n {package}/{runner}.MainActivity" .format(device_id=self.source, package=self.lightning_apk_class, runner=self.lightning_apk_class)) return def __start_async_logcat(self): """ Start logcat read in subprocess and make threads to read its stdout/stderr to queues """ cmd = "adb -s {device_id} logcat".format(device_id=self.source) logger.debug("Execute : %s", cmd) self.logcat_process = popen(cmd) self.logcat_reader_stdout = LogReader(self.logcat_process.stdout, self.compiled_regexp) self.drain_logcat_stdout = Drain(self.logcat_reader_stdout, self.phone_q) self.drain_logcat_stdout.start() self.phone_q_err = q.Queue() self.logcat_reader_stderr = LogReader(self.logcat_process.stderr, self.compiled_regexp) self.drain_logcat_stderr = Drain(self.logcat_reader_stderr, self.phone_q_err) self.drain_logcat_stderr.start() def run_test(self): """ App stage: run app/phone tests """ if self.test_package: command = "adb -s {device_id} shell am instrument -w -e class {test_class} {test_package}/{test_runner}".format( test_class=self.test_class, device_id=self.source, test_package=self.test_package, test_runner=self.test_runner) else: logger.info( 'Infinite loop for volta because there are no tests specified, waiting for SIGINT' ) command = 'while [ 1 ]; do sleep 1; done' self.test_performer = PhoneTestPerformer(command) self.test_performer.start() return def end(self): """ Stop test and grabbers """ if self.test_performer: self.test_performer.close() self.logcat_reader_stdout.close() self.logcat_reader_stderr.close() self.logcat_process.kill() self.drain_logcat_stdout.close() self.drain_logcat_stderr.close() return def get_info(self): data = {} if self.drain_logcat_stdout: data['grabber_alive'] = self.drain_logcat_stdout.isAlive() if self.phone_q: data['grabber_queue_size'] = self.phone_q.qsize() if self.test_performer: data['test_performer_alive'] = self.test_performer.isAlive() return data
class AndroidOldPhone(Phone): """ Android Old phone worker class - work w/ phone, read phone logs, run test apps and store data Attributes: source (string): path to data source, phone id (adb devices) unplug_type (string): type of test execution `auto`: disable battery charge (by software) or use special USB cord limiting charge over USB `manual`: disable phone from USB by your own hands during test exection and click your test lightning_apk_path (string, optional): path to lightning app may be url, e.g. 'http://myhost.tld/path/to/file' may be path to file, e.g. '/home/users/netort/path/to/file.apk' lightning_apk_class (string, optional): lightning class test_apps (list, optional): list of apps to be installed to device for test test_class (string, optional): app class to be started during test execution test_package (string, optional): app package to be started during test execution test_runner (string, optional): app runner to be started during test execution Todo: unplug_type manual - remove raw_input() """ def __init__(self, config): """ Args: config (VoltaConfig): module configuration data """ Phone.__init__(self, config) self.logcat_stdout_reader = None self.logcat_stderr_reader = None # mandatory options self.source = config.get_option('phone', 'source') #self.unplug_type = config.get('unplug_type', 'auto') # lightning app configuration self.lightning_apk_path = config.get_option( 'phone', 'lightning', pkg_resources.resource_filename('volta.providers.phones', 'binary/lightning-new3.apk')) self.lightning_apk_class = config.get_option('phone', 'lightning_class') self.lightning_apk_fname = None # test app configuration self.test_apps = config.get_option('phone', 'test_apps') self.test_class = config.get_option('phone', 'test_class') self.test_package = config.get_option('phone', 'test_package') self.test_runner = config.get_option('phone', 'test_runner') self.cleanup_apps = config.get_option('phone', 'cleanup_apps') self.regexp = config.get_option('phone', 'event_regexp', event_regexp) try: self.compiled_regexp = re.compile(self.regexp, re.VERBOSE | re.IGNORECASE) except: logger.debug('Unable to parse specified regexp', exc_info=True) raise RuntimeError("Unable to parse specified regexp") self.test_performer = None def prepare(self): """ Phone preparements stage: install apps etc pipeline: install lightning install apks clean log """ # apps cleanup for apk in self.cleanup_apps: execute("adb -s {device_id} uninstall {app}".format( device_id=self.source, app=apk)) # install lightning self.lightning_apk_fname = resource.get_opener( self.lightning_apk_path).get_filename logger.info('Installing lightning apk...') execute("adb -s {device_id} install -r -d -t {apk}".format( device_id=self.source, apk=self.lightning_apk_fname)) # install apks for apk in self.test_apps: apk_fname = resource.get_opener(apk).get_filename execute("adb -s {device_id} install -r -d -t {apk}".format( device_id=self.source, apk=apk_fname)) # clean logcat execute("adb -s {device_id} logcat -c".format(device_id=self.source)) # unplug device or start logcat #if self.unplug_type == 'manual': # logger.info('Detach the phone %s from USB and press enter to continue...', self.source) # # TODO make API and remove this # raw_input() def start(self, results): """ Grab stage: starts log reader, make sync w/ flashlight pipeline: if uplug_type is manual: remind user to start flashlight app if unplug_type is auto: start async logcat reader start lightning flashes Args: results (queue-like object): Phone should put there dataframes, format: ['sys_uts', 'message'] """ self.phone_q = results #if self.unplug_type == 'manual': # logger.info("It's time to start flashlight app!") # return #if self.unplug_type == 'auto': self.__start_async_logcat() # start flashes app execute( "adb -s {device_id} shell am start -n {package}/{runner}.MainActivity" .format(device_id=self.source, package=self.lightning_apk_class, runner=self.lightning_apk_class)) logger.info('Waiting 15 seconds till flashlight app end its work...') time.sleep(15) return def __start_async_logcat(self): """ Start logcat read in subprocess and make threads to read its stdout/stderr to queues """ cmd = "adb -s {device_id} logcat -v time".format(device_id=self.source) logger.debug("Execute : %s", cmd) self.logcat_process = popen(cmd) self.logcat_reader_stdout = LogReader(self.logcat_process.stdout, self.compiled_regexp) self.drain_logcat_stdout = Drain(self.logcat_reader_stdout, self.phone_q) self.drain_logcat_stdout.start() self.phone_q_err = q.Queue() self.logcat_reader_stderr = LogReader(self.logcat_process.stderr, self.compiled_regexp) self.drain_logcat_stderr = Drain(self.logcat_reader_stderr, self.phone_q_err) self.drain_logcat_stderr.start() def run_test(self): """ App stage: run app/phone tests """ if self.test_package: command = "adb -s {device_id} shell am instrument -w -e class {test_class} {test_package}/{test_runner}".format( test_class=self.test_class, device_id=self.source, test_package=self.test_package, test_runner=self.test_runner) else: logger.info( 'Infinite loop for volta because there are no tests specified, waiting for SIGINT' ) command = 'while [ 1 ]; do sleep 1; done' self.test_performer = PhoneTestPerformer(command) self.test_performer.start() return def end(self): """ Stop test and grabbers """ if self.test_performer: self.test_performer.close() self.logcat_reader_stdout.close() self.logcat_reader_stderr.close() self.logcat_process.kill() self.drain_logcat_stdout.close() self.drain_logcat_stderr.close() # apps cleanup for apk in self.cleanup_apps: execute("adb -s {device_id} uninstall {app}".format( device_id=self.source, app=apk)) return def get_info(self): data = {} if self.drain_logcat_stdout: data['grabber_alive'] = self.drain_logcat_stdout.isAlive() if self.phone_q: data['grabber_queue_size'] = self.phone_q.qsize() if self.test_performer: data['test_performer_alive'] = self.test_performer.isAlive() return data
class iPhone(Phone): """ iPhone worker class - work w/ phone, read phone logs, store data Attributes: source (string): path to data source, cfgutil id for iphones unplug_type (string, optional): type of test execution - NOT available at the moment for now `auto`: disable battery charge (by software) or use special USB cord limiting charge over USB path_to_util (string, optional): path to Apple Configurators' cfgutil Todo: unlug_type manual """ def __init__(self, config): """ Args: config (dict): module configuration data """ Phone.__init__(self, config) self.log_stdout_reader = None self.log_stderr_reader = None self.drain_log_stdout = None self.path_to_util = config.get_option('phone', 'util') self.source = config.get_option('phone', 'source') # self.test_performer = None def prepare(self): """ this method skipped by iphone - instruments do the thing """ return def start(self, results): """ Grab stage: starts log reader pipeline: start async logcat reader Args: results (queue-like object): Phone should put there dataframes, format: ['sys_uts', 'message'] """ self.phone_q = results self.__start_async_log() def run_test(self): """ App stage: run app/phone tests """ pass # logger.info('Infinite loop for volta because there are no tests specified, waiting for SIGINT') # command = 'while [ 1 ]; do sleep 1; done' # self.test_performer = PhoneTestPerformer(command) # self.test_performer.start() def end(self): """ pipeline: stop async log process, readers and queues """ # if self.test_performer: # self.test_performer.close() self.log_reader_stdout.close() self.log_reader_stderr.close() self.log_process.kill() self.drain_log_stdout.close() self.drain_log_stderr.close() def __start_async_log(self): """ Start logcat read in subprocess and make threads to read its stdout/stderr to queues """ cmd = "{path}cfgutil -e {device_id} syslog".format( path=self.path_to_util, device_id=self.source) logger.debug("Execute : %s", cmd) self.log_process = popen(cmd) self.log_reader_stdout = LogReader(self.log_process.stdout, iphone_logevent_re) self.drain_log_stdout = Drain(self.log_reader_stdout, self.phone_q) self.drain_log_stdout.start() self.phone_q_err = q.Queue() self.log_reader_stderr = LogReader(self.log_process.stderr, iphone_logevent_re) self.drain_log_stderr = Drain(self.log_reader_stderr, self.phone_q_err) self.drain_log_stderr.start() def get_info(self): data = {} if self.drain_log_stdout: data['grabber_alive'] = self.drain_log_stdout.isAlive() if self.phone_q: data['grabber_queue_size'] = self.phone_q.qsize() # if self.test_performer: # data['test_performer_alive'] = self.test_performer.isAlive() return data