Beispiel #1
0
    def configure(self, options, conf):
        """
        Called after the command  line has been parsed, with the parsed options and the config container. Here, implement any config storage or changes to state or operation that are set by command line options. DO NOT return a value from this method unless you want to stop all other plugins from being configured.
        """
        super(ReporterPlugin, self).configure(options, conf)
        if not self.enabled: return

        self.write_hashes = conf.verbosity == 2
        self.conf = conf
        self.opt = options
        if self.opt.icycle and not self.__counter:
            self.__counter = TestCounter(cycles=self.opt.icycle)
        elif not self.__counter:
            self.__counter = TestCounter()

        if self.opt.duration and not self.__timer:
            self.__timer = Timer(self.opt.duration)

        if not self.__configuration:
            if self.opt.reportserver:
                if not exists(options.server_config):
                    raise Exception(
                        'exit due to unable to find server configuration file: "%s"'
                        % options.server_config)
            if not exists(options.device_config):
                raise Exception(
                    'exit due to unable to find device configuration file: "%s"'
                    % options.device_config)
            self.__configuration.update(
                _getServerConfiguration(options.server_config))
            self.__configuration.update(
                _getDeviceConfiguration(options.device_config))
            self.__configuration.update(
                {'planname': os.path.basename(self.conf.options.plan_file)})

        self.result_properties = {'payload': None, 'extras': None}
        #if disable report server
        if self.opt.reportserver and not self.__report_client:
            server_need = {
                'username': None,
                'password': None,
                'auth': None,
                'session_create': None,
                'session_update': None,
                'case_create': None,
                'case_update': None,
                'file_upload': None
            }
            self.__report_client = ReportClient(**self.__configuration)
            self.token = self.__report_client.regist()
            if not self.token:
                raise Exception('exit due to unable to get token from server!')
    def configure(self, options, conf):
        """
        Called after the command  line has been parsed, with the parsed options and the config container. Here, implement any config storage or changes to state or operation that are set by command line options. DO NOT return a value from this method unless you want to stop all other plugins from being configured.
        """
        super(ReporterPlugin, self).configure(options, conf)
        if not self.enabled: return

        self.write_hashes = conf.verbosity == 2
        self.conf = conf
        self.opt = options

        if self.opt.icycle and not self.__counter:
            self.__counter = TestCounter(cycles=self.opt.icycle)
        elif not self.__counter:
            self.__counter = TestCounter()

        if self.opt.duration and not self.__timer:
            self.__timer = Timer(self.opt.duration)

        self.result_properties = {'payload': None, 'extras': None}
        #if disable report server
        if self.opt.reportserver and not self.__report_client:
            server_need = {'username':None, 'password':None, 'auth':None, 'session_create':None,
                           'session_update':None, 'case_create':None, 'case_update':None, 'file_upload':None}
            if not exists(options.server_config):
                raise Exception('exit due to unable to find server config file!')
            self.__report_client =  ReportClient(config=options.server_config)
            self.token = self.__report_client.regist()
            if not self.token:
                raise Exception('exit due to unable to get token from server!')
Beispiel #3
0
class ReporterPlugin(nose.plugins.Plugin):
    """
    Write test result to $WORKSPACE/result.txt or ./result.txt
    """
    name = 'reporter'
    #score = 200

    def __init__(self, counter=None, report_client=None):
        super(ReporterPlugin, self).__init__()
        self.__counter = counter if counter else TestCounter()
        self.__report_client = report_client if report_client else None

    def options(self, parser, env):
        """ 
        Register commandline options.
        Called to allow plugin to register command line options with the parser. DO NOT return a value from this method unless you want to stop all other plugins from setting their options.        
        """
        super(ReporterPlugin, self).options(parser, env)

        ###local ouput result config###
        parser.add_option('--file-name', 
                          dest='file_name', default='result.txt',
                          help="save output file to this directory")

        parser.add_option('--directory', action='store_true',
                          dest='directory', default=self.getDefault(),
                          help="save output file to this directory. default is current nose worspace")

        parser.add_option('--size', action='store',  metavar="FILE",
                          dest='server_config', default=4096,
                          help="file size limit")

        ###report server config###
        parser.add_option('--reportserver', action='store_true',
                          dest='reportserver', default=False,
                          help="switcher of uploading result to server. default is enable the feature")

        parser.add_option('--server-config', action='store',  metavar="FILE",
                          dest='server_config', default='server.config',
                          help="specify the server config file path")

    def getDefault(self):
        workspace = os.getcwd()
        if 'WORKSPACE' in os.environ:
            ws = os.environ['WORKSPACE']
            workspace = _mkdir(ws)
        return workspace

    def configure(self, options, conf):
        """
        Called after the command  line has been parsed, with the parsed options and the config container. Here, implement any config storage or changes to state or operation that are set by command line options. DO NOT return a value from this method unless you want to stop all other plugins from being configured.
        """
        super(ReporterPlugin, self).configure(options, conf)
        if not self.enabled: return
        self.write_hashes = conf.verbosity == 2
        self.conf = conf
        self.opt = options
        self.result_properties = {'payload': None, 'extras': None}
        #if disable report server
        if self.opt.reportserver and not self.__report_client:
            server_need = {'username':None, 'password':None, 'auth':None, 'session_create':None,
                           'session_update':None, 'case_create':None, 'case_update':None, 'file_upload':None}
            if not exists(options.server_config):
                raise Exception('exit due to unable to find server config file!')
            self.__report_client =  ReportClient(config=options.server_config)
            self.token = self.__report_client.regist()
            if not self.token:
                raise Exception('exit due to unable to get token from server!')

        #used to add local report if need
        #self.result_file = join(_mkdir(self.opt.directory), self.opt.file_name)

    def setOutputStream(self, stream):
        """
        Get handle on output stream so the plugin can print id #n
        """
        self.stream = stream

    def _write(self, output):
        '''
        Write output as content
        '''
        try:
            self.stream.write(output)
        except:
            pass

    def begin(self):
        self.session_id = self.__counter.sid
        self.test_start_time = getattr(self, 'test_start_time', None)
        if not self.test_start_time:
            self.test_start_time = reporttime()
        self._report_path = _mkdir(join(join(self.opt.directory, 'report'), str(self.test_start_time).replace(' ', '_')))
        self._all_report_path = _mkdir(join(self._report_path, 'all'))
        self._fail_report_path = _mkdir(join(self._report_path, 'fail'))
        self._error_report_path = _mkdir(join(self._report_path, 'error'))
        self._timeout_report_path = _mkdir(join(self._report_path, 'timeout'))
        session_properties = {'sid': self.session_id,
                              'starttime': self.test_start_time
                              }
        if self.opt.reportserver and not self.__report_client.created:
            self.__report_client.createSession(**session_properties)

    def setTestCaseContext(self, test):
        module_name, class_name, method_name = test.id().split('.')[-3:]
        ctx = TestCaseContext(self._fail_report_path, self._error_report_path)
        ctx.case_dir_name = '%s%s%s' % (class_name, '.', method_name)
        setattr(test.context, 'contexts', ctx)


    def getTestCaseContext(self, test):
        return getattr(test.context, 'contexts')

    def prepareTestCase(self, test):
        #sys.stderr.write('------------------------------prepareTestCase-------------\n')
        self.setTestCaseContext(test)

    def startTest(self, test):
        """
        startTest: called after beforeTest(*)
        """
        self.tid = self.__counter.next()
        ctx = self.getTestCaseContext(test)
        ctx.case_start_time = reporttime()
        ctx.user_log_dir = join(ctx.case_report_tmp_dir, 'logs')
        if self.write_hashes:
            self._write('#%s %s ' % (str(self.tid), str(ctx.case_start_time)))

    def stopTest(self, test):
        """
        stopTest: called before afterTest(*)
        """
        pass

    def handleFailure(self, test, err):
        '''
        Called on addFailure. To handle the failure yourself and prevent normal failure processing, return a true value.
        '''
        self.result_properties.clear()
        exctype, value, tb = err

        ctx = self.getTestCaseContext(test)
        #common log output
        grabLog(ctx.user_log_dir)
        shutil.move(ctx.case_report_tmp_dir, self._fail_report_path)

        self.result_properties.update({'extras': {'screenshot_at_failure': ctx.screenshot_at_failure,
                                                  'log': ctx.log,
                                                  'expect': ctx.expect,
                                                  }
                                      })

    def handleError(self, test, err):
        '''
        Called on addError. To handle the failure yourself and prevent normal error processing, return a true value.
        '''
        self.result_properties.clear()
        
        ctx = self.getTestCaseContext(test)
        #last step snapshot
        grabLog(ctx.user_log_dir)
        shutil.move(ctx.case_report_tmp_dir, self._error_report_path)

        self.result_properties.update({'extras': {'screenshot_at_failure': ctx.screenshot_at_failure,
                                                  'log': ctx.log,
                                                  'expect': ctx.expect,
                                                 }
                                       })

    def addFailure(self, test, err, capt=None, tbinfo=None):
        ctx = self.getTestCaseContext(test)
        ctx.case_end_time = reporttime()
        #payload data
        self.result_properties.update({'payload': {'tid': self.tid,
                                                  'casename': ctx.case_dir_name,
                                                  'starttime': ctx.case_start_time,
                                                  'endtime': ctx.case_end_time,
                                                  'result': 'fail',
                                                  'trace':formatOutput(ctx.case_dir_name, 'fail', err)
                                                  }
                                       })
        if self.opt.reportserver:
            self.__report_client.updateTestCase(**self.result_properties)

    #remote upload
    def addError(self, test, err, capt=None):
        ctx = self.getTestCaseContext(test)
        ctx.case_end_time = reporttime()

        self.result_properties.update({'payload': {'tid': self.tid,
                                                  'casename': ctx.case_dir_name,
                                                  'starttime': ctx.case_start_time,
                                                  'endtime': ctx.case_end_time,
                                                  'result': 'error',
                                                  'trace':formatOutput(ctx.case_dir_name, 'error', err)
                                                  }
                                       })

        if self.opt.reportserver:
            self.__report_client.updateTestCase(**self.result_properties)

    #remote upload
    def addSuccess(self, test, capt=None):
        ctx = self.getTestCaseContext(test)
        ctx.case_end_time = reporttime()
        self.result_properties.clear()
        self.result_properties.update({'payload': {'tid': self.tid,
                                                   'casename': ctx.case_dir_name,
                                                   'starttime': ctx.case_start_time,
                                                   'endtime': ctx.case_end_time,
                                                   'result': 'pass'
                                                  }
                                      })        
        if self.opt.reportserver:
            self.__report_client.updateTestCase(**self.result_properties)
Beispiel #4
0
class ReporterPlugin(nose.plugins.Plugin):
    """
    handle test output.
    """
    name = 'reporter'

    #score = 200

    def __init__(self, counter=None, report_client=None, timer=None):
        super(ReporterPlugin, self).__init__()
        self.__report_client = report_client if report_client else None
        self.__counter = counter
        self.__timer = timer
        self.__log_handler = None
        self.__configuration = {}

    def options(self, parser, env):
        """ 
        Register commandline options.
        Called to allow plugin to register command line options with the parser. DO NOT return a value from this method unless you want to stop all other plugins from setting their options.        
        """
        super(ReporterPlugin, self).options(parser, env)

        ###local ouput result config###
        parser.add_option('--file-name',
                          dest='file_name',
                          default='result.txt',
                          help="save output file to this directory")

        parser.add_option(
            '--directory',
            action='store_true',
            dest='directory',
            default=self.__getDefault(),
            help=
            "save output file to this directory. default is current nose worspace"
        )

        parser.add_option('--icycle',
                          action='store',
                          type='string',
                          metavar="STRING",
                          dest='icycle',
                          default=None,
                          help="total cycle flag")

        ###report server config###
        parser.add_option(
            '--reportserver',
            action='store_true',
            dest='reportserver',
            default=False,
            help=
            "switcher of uploading result to server. default is enable the feature"
        )

        parser.add_option('--server-config',
                          action='store',
                          metavar="FILE",
                          dest='server_config',
                          default='server.config',
                          help="specify the server config file path")

        parser.add_option('--device-config',
                          action='store',
                          metavar="FILE",
                          dest='device_config',
                          default='device.config',
                          help="specify the device config file path")

        parser.add_option(
            '--duration',
            dest='duration',
            type='string',
            metavar="STRING",
            action='callback',
            callback=self.__validate_duration,
            help='The minumum test duration before ending the test.\
                                  Here format must follow next format: xxDxxHxxMxxS.\
                                  e.g. --duration=2D09H30M12S, which means 2 days, 09 hours, 30 minutes and 12 seconds'
        )

    def __validate_duration(self, option, opt, value, parser):
        '''
        '''
        value = string.lower(value)
        begin = 0
        days = hours = minutes = seconds = 0
        for i, v in enumerate(value):
            if v == 'd':
                days = int(value[begin:i])
                begin = i + 1
            elif v == 'h':
                hours = int(value[begin:i])
                begin = i + 1
            elif v == 'm':
                minutes = int(value[begin:i])
                begin = i + 1
            elif v == 's':
                seconds = int(value[begin:i])
                begin = i + 1
        if begin == 0:
            parser.error('%s: duration format error' % value)
        times = datetime.timedelta(days=days,
                                   hours=hours,
                                   minutes=minutes,
                                   seconds=seconds)
        setattr(parser.values, option.dest, times)

    def __getDefault(self):
        workspace = os.getcwd()
        if 'WORKSPACE' in os.environ:
            ws = os.environ['WORKSPACE']
            workspace = _mkdir(ws)
        return workspace

    def configure(self, options, conf):
        """
        Called after the command  line has been parsed, with the parsed options and the config container. Here, implement any config storage or changes to state or operation that are set by command line options. DO NOT return a value from this method unless you want to stop all other plugins from being configured.
        """
        super(ReporterPlugin, self).configure(options, conf)
        if not self.enabled: return

        self.write_hashes = conf.verbosity == 2
        self.conf = conf
        self.opt = options
        if self.opt.icycle and not self.__counter:
            self.__counter = TestCounter(cycles=self.opt.icycle)
        elif not self.__counter:
            self.__counter = TestCounter()

        if self.opt.duration and not self.__timer:
            self.__timer = Timer(self.opt.duration)

        if not self.__configuration:
            if self.opt.reportserver:
                if not exists(options.server_config):
                    raise Exception(
                        'exit due to unable to find server configuration file: "%s"'
                        % options.server_config)
            if not exists(options.device_config):
                raise Exception(
                    'exit due to unable to find device configuration file: "%s"'
                    % options.device_config)
            self.__configuration.update(
                _getServerConfiguration(options.server_config))
            self.__configuration.update(
                _getDeviceConfiguration(options.device_config))
            self.__configuration.update(
                {'planname': os.path.basename(self.conf.options.plan_file)})

        self.result_properties = {'payload': None, 'extras': None}
        #if disable report server
        if self.opt.reportserver and not self.__report_client:
            server_need = {
                'username': None,
                'password': None,
                'auth': None,
                'session_create': None,
                'session_update': None,
                'case_create': None,
                'case_update': None,
                'file_upload': None
            }
            self.__report_client = ReportClient(**self.__configuration)
            self.token = self.__report_client.regist()
            if not self.token:
                raise Exception('exit due to unable to get token from server!')

        #used to add local report if need
        #self.result_file = join(_mkdir(self.opt.directory), self.opt.file_name)

    def setOutputStream(self, stream):
        """
        Get handle on output stream so the plugin can print id #n
        """
        self.stream = stream

    def __write(self, output):
        '''
        Write output as content
        '''
        try:
            self.stream.write(output)
        except:
            pass

    def prepareTest(self, test):
        '''
        enable log handler.
        '''
        if not self.__log_handler:
            self.__log_handler = LogHandler(
                serial=self.__configuration['deviceid'])
        if not self.__log_handler.available():
            self.__log_handler.start()

    def begin(self):
        self.session_id = self.__counter.sid
        self.test_start_time = getattr(self, 'test_start_time', None)
        if not self.test_start_time:
            self.test_start_time = _reportTime()
        self.opt.directory = self.conf.workingDir
        self._report_path = _mkdir(
            join(join(self.opt.directory, 'report'),
                 str(self.test_start_time).replace(' ', '_')))
        self._pass_report_path = _mkdir(join(self._report_path, 'pass'))
        self._fail_report_path = _mkdir(join(self._report_path, 'fail'))
        self._error_report_path = _mkdir(join(self._report_path, 'error'))
        self._timeout_report_path = _mkdir(join(self._report_path, 'timeout'))
        session_properties = {
            'sid': self.session_id,
            'starttime': self.test_start_time
        }
        self.cid = self.__counter.next_cid()
        if self.write_hashes:
            sys.stderr.write('begin cycle: %s \n' % (self.cid))
        if self.opt.reportserver and not self.__report_client.created:
            self.__report_client.createSession(**session_properties)

    def __setTestCaseContext(self, test):
        module_name, class_name, method_name = test.id().split('.')[-3:]
        ctx = TestCaseContext(self._fail_report_path, self._error_report_path)
        ctx.case_dir_name = '%s%s%s' % (class_name, '.', method_name)
        ctx.device_config = self.__configuration
        setattr(test.context, 'contexts', ctx)

    def __getTestCaseContext(self, test):
        return getattr(test.context, 'contexts')

    def prepareTestCase(self, test):
        self.__setTestCaseContext(test)

    def startTest(self, test):
        """
        startTest: called after beforeTest(*)
        """
        self.tid = self.__counter.next_tid()
        ctx = self.__getTestCaseContext(test)
        ctx.case_start_time = _reportTime()
        ctx.user_log_dir = join(ctx.case_report_tmp_dir, 'logs')
        path = _mkdir(ctx.user_log_dir)

        if self.write_hashes:
            self.__write('#%s %s ' % (str(self.tid), str(ctx.case_start_time)))

    def handleFailure(self, test, err):
        '''
        Called on addFailure. To handle the failure yourself and prevent normal failure processing, return a true value.
        '''
        self.result_properties.clear()
        exctype, value, tb = err

        try:
            ctx = self.__getTestCaseContext(test)
            log_file = join(ctx.user_log_dir, LOG_FILE_NAME)
            self.__log_handler.save(log_file)
            #makeLog(ctx.user_log_dir)
            #shutil.move(ctx.case_report_tmp_dir, self._fail_report_path)
        except:
            pass

        self.result_properties.update({
            'extras': {
                'screenshot_at_failure': ctx.fail_screenshot_at_failure,
                'log': ctx.fail_log,
                'expect': ctx.expect,
            }
        })

    def handleError(self, test, err):
        '''
        Called on addError. To handle the failure yourself and prevent normal error processing, return a true value.
        '''
        self.result_properties.clear()

        try:
            ctx = self.__getTestCaseContext(test)
            log_file = join(ctx.user_log_dir, LOG_FILE_NAME)
            self.__log_handler.save(log_file)
            #makeLog(ctx.user_log_dir)
        except:
            pass

        self.result_properties.update({
            'extras': {
                'screenshot_at_failure': ctx.error_screenshot_at_failure,
                'log': ctx.error_log,
                'expect': ctx.expect,
            }
        })

    def addFailure(self, test, err, capt=None, tbinfo=None):
        ctx = self.__getTestCaseContext(test)
        ctx.case_end_time = _reportTime()
        #payload data
        self.result_properties.update({
            'payload': {
                'tid': self.tid,
                'casename': ctx.case_dir_name,
                'starttime': ctx.case_start_time,
                'endtime': ctx.case_end_time,
                'result': 'fail',
                'trace': _formatOutput(ctx.case_dir_name, 'fail', err)
            }
        })
        trace_log_path = join(ctx.user_log_dir, 'trace.txt')
        with open(trace_log_path, 'w+') as f:
            f.write(str(self.result_properties['payload']['trace']))
        _makeLog(path=ctx.user_log_dir,
                 serial=self.__configuration['deviceid'])
        try:
            shutil.move(ctx.case_report_tmp_dir, self._fail_report_path)
        except:
            pass
        if self.__timer and not self.__timer.alive():
            self.conf.stopOnError = True
        if self.opt.reportserver:
            self.__report_client.updateTestCase(**self.result_properties)

    #remote upload
    def addError(self, test, err, capt=None):
        ctx = self.__getTestCaseContext(test)
        ctx.case_end_time = _reportTime()

        self.result_properties.update({
            'payload': {
                'tid': self.tid,
                'casename': ctx.case_dir_name,
                'starttime': ctx.case_start_time,
                'endtime': ctx.case_end_time,
                'result': 'error',
                'trace': _formatOutput(ctx.case_dir_name, 'error', err)
            }
        })
        trace_log_path = join(ctx.user_log_dir, 'trace.txt')
        with open(trace_log_path, 'w+') as f:
            f.write(str(self.result_properties['payload']['trace']))
        _makeLog(path=ctx.user_log_dir,
                 serial=self.__configuration['deviceid'])
        try:
            shutil.move(ctx.case_report_tmp_dir, self._error_report_path)
        except:
            pass
        if self.__timer and not self.__timer.alive():
            self.conf.stopOnError = True

        if self.opt.reportserver:
            self.__report_client.updateTestCase(**self.result_properties)

    #remote upload
    def addSuccess(self, test, capt=None):
        ctx = self.__getTestCaseContext(test)
        ctx.case_end_time = _reportTime()
        self.result_properties.clear()
        self.result_properties.update({
            'payload': {
                'tid': self.tid,
                'casename': ctx.case_dir_name,
                'starttime': ctx.case_start_time,
                'endtime': ctx.case_end_time,
                'result': 'pass'
            }
        })
        #self.__log_handler.drop()
        log_file = join(ctx.user_log_dir, LOG_FILE_NAME)
        self.__log_handler.save(log_file)
        trace_log_path = join(ctx.user_log_dir, 'trace.txt')
        with open(trace_log_path, 'w+') as f:
            f.write(str(self.result_properties['payload']['result']))
        _makeLog(path=ctx.user_log_dir,
                 serial=self.__configuration['deviceid'],
                 result='pass')
        try:
            shutil.move(ctx.case_report_tmp_dir, self._pass_report_path)
        except:
            pass
        if self.__timer and not self.__timer.alive():
            self.conf.stopOnError = True
        if self.opt.reportserver:
            self.__report_client.updateTestCase(**self.result_properties)

    def report(self, stream):
        session_properties = {'sid': self.session_id}
        if self.opt.duration and self.__timer:
            session_properties.update({'status': self.__timer.progress()})
        elif self.opt.icycle and self.__counter:
            session_properties.update({'status': self.__counter.progress()})
        if self.opt.reportserver:
            self.__report_client.updateSession(**session_properties)
        return None

    def finalize(self, result):
        if self.write_hashes:
            self.__write('end cycle: %s \n' % (self.cid))
        session_properties = {'sid': self.session_id}
        if self.opt.icycle and not self.__counter.alive(
        ) and self.opt.reportserver:
            session_properties.update({'endtime': _reportTime()})
            self.__report_client.updateSession(**session_properties)
        if self.conf.stopOnError and self.opt.reportserver:
            session_properties.update({'endtime': _reportTime()})
            self.__report_client.updateSession(**session_properties)
            sys.exit(1)
        return None
Beispiel #5
0
class ReporterPlugin(nose.plugins.Plugin):
    """
    handle test output.
    """
    name = 'reporter'
    #score = 200

    def __init__(self, counter=None, report_client=None, timer=None):
        super(ReporterPlugin, self).__init__()
        self.__report_client = report_client if report_client else None
        self.__counter = counter
        self.__timer = timer
        self.__log_handler = None
        self.__configuration = {}

    def options(self, parser, env):
        """ 
        Register commandline options.
        Called to allow plugin to register command line options with the parser. DO NOT return a value from this method unless you want to stop all other plugins from setting their options.        
        """
        super(ReporterPlugin, self).options(parser, env)

        ###local ouput result config###
        parser.add_option('--file-name', 
                          dest='file_name', default='result.txt',
                          help="save output file to this directory")

        parser.add_option('--directory', action='store_true',
                          dest='directory', default=self.__getDefault(),
                          help="save output file to this directory. default is current nose worspace")

        parser.add_option('--icycle', action='store', type='string',metavar="STRING",
                          dest='icycle', default=None, help="total cycle flag")

        ###report server config###
        parser.add_option('--reportserver', action='store_true',
                          dest='reportserver', default=False,
                          help="switcher of uploading result to server. default is enable the feature")

        parser.add_option('--server-config', action='store',  metavar="FILE",
                          dest='server_config', default='server.config',
                          help="specify the server config file path")

        parser.add_option('--device-config', action='store',  metavar="FILE",
                          dest='device_config', default='device.config',
                          help="specify the device config file path")

        parser.add_option('--duration', dest='duration', type='string',metavar="STRING",
                          action='callback', callback=self.__validate_duration, 
                          help='The minumum test duration before ending the test.\
                                  Here format must follow next format: xxDxxHxxMxxS.\
                                  e.g. --duration=2D09H30M12S, which means 2 days, 09 hours, 30 minutes and 12 seconds')

    def __validate_duration(self, option, opt, value, parser):
        '''
        '''
        value = string.lower(value)
        begin = 0
        days = hours = minutes = seconds = 0
        for i, v in enumerate(value):
            if v == 'd':
                days = int(value[begin:i])
                begin = i + 1
            elif v == 'h':
                hours = int(value[begin:i])
                begin = i + 1
            elif v == 'm':
                minutes = int(value[begin:i])
                begin = i + 1
            elif v == 's':
                seconds = int(value[begin:i])
                begin = i + 1
        if begin == 0:
            parser.error('%s: duration format error' % value) 
        times = datetime.timedelta(days=days, hours=hours, minutes=minutes, seconds=seconds)      
        setattr(parser.values, option.dest, times)

    def __getDefault(self):
        workspace = os.getcwd()
        if 'WORKSPACE' in os.environ:
            ws = os.environ['WORKSPACE']
            workspace = _mkdir(ws)
        return workspace

    def configure(self, options, conf):
        """
        Called after the command  line has been parsed, with the parsed options and the config container. Here, implement any config storage or changes to state or operation that are set by command line options. DO NOT return a value from this method unless you want to stop all other plugins from being configured.
        """
        super(ReporterPlugin, self).configure(options, conf)
        if not self.enabled: return

        self.write_hashes = conf.verbosity == 2
        self.conf = conf
        self.opt = options
        if self.opt.icycle and not self.__counter:
            self.__counter = TestCounter(cycles=self.opt.icycle)
        elif not self.__counter:
            self.__counter = TestCounter()

        if self.opt.duration and not self.__timer:
            self.__timer = Timer(self.opt.duration)

        if not self.__configuration:
            if self.opt.reportserver:
                if not exists(options.server_config):
                    raise Exception('exit due to unable to find server configuration file: "%s"' % options.server_config)
            if not exists(options.device_config):
                raise Exception('exit due to unable to find device configuration file: "%s"' % options.device_config)
            self.__configuration.update(_getServerConfiguration(options.server_config))
            self.__configuration.update(_getDeviceConfiguration(options.device_config))
            self.__configuration.update({'planname': os.path.basename(self.conf.options.plan_file)})

        self.result_properties = {'payload': None, 'extras': None}
        #if disable report server
        if self.opt.reportserver and not self.__report_client:
            server_need = {'username':None, 'password':None, 'auth':None, 'session_create':None,
                           'session_update':None, 'case_create':None, 'case_update':None, 'file_upload':None}
            self.__report_client =  ReportClient(**self.__configuration)
            self.token = self.__report_client.regist()
            if not self.token:
                raise Exception('exit due to unable to get token from server!')

        #used to add local report if need
        #self.result_file = join(_mkdir(self.opt.directory), self.opt.file_name)

    def setOutputStream(self, stream):
        """
        Get handle on output stream so the plugin can print id #n
        """
        self.stream = stream

    def __write(self, output):
        '''
        Write output as content
        '''
        try:
            self.stream.write(output)
        except:
            pass

    def prepareTest(self, test):
        '''
        enable log handler.
        '''
        if not self.__log_handler:
            self.__log_handler = LogHandler(serial=self.__configuration['deviceid'])
        if not self.__log_handler.available():
            self.__log_handler.start()

    def begin(self):
        self.session_id = self.__counter.sid  
        self.test_start_time = getattr(self, 'test_start_time', None)
        if not self.test_start_time:
            self.test_start_time = _reportTime()
        self.opt.directory = self.conf.workingDir
        self._report_path = _mkdir(join(join(self.opt.directory, 'report'), str(self.test_start_time).replace(' ', '_')))
        self._pass_report_path = _mkdir(join(self._report_path, 'pass'))
        self._fail_report_path = _mkdir(join(self._report_path, 'fail'))
        self._error_report_path = _mkdir(join(self._report_path, 'error'))
        self._timeout_report_path = _mkdir(join(self._report_path, 'timeout'))
        session_properties = {'sid': self.session_id,
                              'starttime': self.test_start_time
                              }
        self.cid = self.__counter.next_cid()
        if self.write_hashes:
            sys.stderr.write('begin cycle: %s \n' % (self.cid))                          
        if self.opt.reportserver and not self.__report_client.created:
            self.__report_client.createSession(**session_properties)

    def __setTestCaseContext(self, test):
        module_name, class_name, method_name = test.id().split('.')[-3:]
        ctx = TestCaseContext(self._fail_report_path, self._error_report_path)
        ctx.case_dir_name = '%s%s%s' % (class_name, '.', method_name)
        ctx.device_config = self.__configuration
        setattr(test.context, 'contexts', ctx)

    def __getTestCaseContext(self, test):
        return getattr(test.context, 'contexts')

    def prepareTestCase(self, test):
        self.__setTestCaseContext(test)

    def startTest(self, test):
        """
        startTest: called after beforeTest(*)
        """
        self.tid = self.__counter.next_tid()
        ctx = self.__getTestCaseContext(test)
        ctx.case_start_time = _reportTime()
        ctx.user_log_dir = join(ctx.case_report_tmp_dir, 'logs')
        path = _mkdir(ctx.user_log_dir)

        if self.write_hashes:
            self.__write('#%s %s ' % (str(self.tid), str(ctx.case_start_time)))


    def handleFailure(self, test, err):
        '''
        Called on addFailure. To handle the failure yourself and prevent normal failure processing, return a true value.
        '''
        self.result_properties.clear()
        exctype, value, tb = err

        try:
            ctx = self.__getTestCaseContext(test)
            log_file = join(ctx.user_log_dir, LOG_FILE_NAME)
            self.__log_handler.save(log_file)
            #makeLog(ctx.user_log_dir)
            #shutil.move(ctx.case_report_tmp_dir, self._fail_report_path)
        except:
            pass

        self.result_properties.update({'extras': {'screenshot_at_failure': ctx.fail_screenshot_at_failure,
                                                  'log': ctx.fail_log,
                                                  'expect': ctx.expect,
                                                  }
                                      })

    def handleError(self, test, err):
        '''
        Called on addError. To handle the failure yourself and prevent normal error processing, return a true value.
        '''
        self.result_properties.clear()
        
        try:
            ctx = self.__getTestCaseContext(test)
            log_file = join(ctx.user_log_dir, LOG_FILE_NAME)
            self.__log_handler.save(log_file)
            #makeLog(ctx.user_log_dir)
        except:
            pass

        self.result_properties.update({'extras': {'screenshot_at_failure': ctx.error_screenshot_at_failure,
                                                  'log': ctx.error_log,
                                                  'expect': ctx.expect,
                                                 }
                                       })

    def addFailure(self, test, err, capt=None, tbinfo=None):
        ctx = self.__getTestCaseContext(test)
        ctx.case_end_time = _reportTime()
        #payload data
        self.result_properties.update({'payload': {'tid': self.tid,
                                                  'casename': ctx.case_dir_name,
                                                  'starttime': ctx.case_start_time,
                                                  'endtime': ctx.case_end_time,
                                                  'result': 'fail',
                                                  'trace':_formatOutput(ctx.case_dir_name, 'fail', err)
                                                  }
                                       })
        trace_log_path = join(ctx.user_log_dir, 'trace.txt')
        with open(trace_log_path, 'w+') as f:
            f.write(str(self.result_properties['payload']['trace']))
        _makeLog(path=ctx.user_log_dir, serial=self.__configuration['deviceid'])
        try:
            shutil.move(ctx.case_report_tmp_dir, self._fail_report_path)
        except:
            pass
        if self.__timer and not self.__timer.alive():
            self.conf.stopOnError = True
        if self.opt.reportserver:
            self.__report_client.updateTestCase(**self.result_properties)

    #remote upload
    def addError(self, test, err, capt=None):
        ctx = self.__getTestCaseContext(test)
        ctx.case_end_time = _reportTime()

        self.result_properties.update({'payload': {'tid': self.tid,
                                                  'casename': ctx.case_dir_name,
                                                  'starttime': ctx.case_start_time,
                                                  'endtime': ctx.case_end_time,
                                                  'result': 'error',
                                                  'trace':_formatOutput(ctx.case_dir_name, 'error', err)
                                                  }
                                       })
        trace_log_path = join(ctx.user_log_dir, 'trace.txt')
        with open(trace_log_path, 'w+') as f:
            f.write(str(self.result_properties['payload']['trace']))
        _makeLog(path=ctx.user_log_dir, serial=self.__configuration['deviceid'])
        try:
            shutil.move(ctx.case_report_tmp_dir, self._error_report_path)
        except:
            pass
        if self.__timer and not self.__timer.alive():
            self.conf.stopOnError = True

        if self.opt.reportserver:
            self.__report_client.updateTestCase(**self.result_properties)

    #remote upload
    def addSuccess(self, test, capt=None):
        ctx = self.__getTestCaseContext(test)
        ctx.case_end_time = _reportTime()
        self.result_properties.clear()
        self.result_properties.update({'payload': {'tid': self.tid,
                                                   'casename': ctx.case_dir_name,
                                                   'starttime': ctx.case_start_time,
                                                   'endtime': ctx.case_end_time,
                                                   'result': 'pass'
                                                  }
                                      })
        #self.__log_handler.drop()
        log_file = join(ctx.user_log_dir, LOG_FILE_NAME)
        self.__log_handler.save(log_file)
        trace_log_path = join(ctx.user_log_dir, 'trace.txt')
        with open(trace_log_path, 'w+') as f:
            f.write(str(self.result_properties['payload']['result']))
        _makeLog(path=ctx.user_log_dir, serial=self.__configuration['deviceid'], result='pass')
        try:
            shutil.move(ctx.case_report_tmp_dir, self._pass_report_path)
        except:
            pass
        if self.__timer and not self.__timer.alive():
            self.conf.stopOnError = True
        if self.opt.reportserver:
            self.__report_client.updateTestCase(**self.result_properties)

    def report(self, stream):
        session_properties = {'sid': self.session_id}
        if self.opt.duration and self.__timer:
            session_properties.update({'status': self.__timer.progress()})
        elif self.opt.icycle and self.__counter:
            session_properties.update({'status': self.__counter.progress()})
        if self.opt.reportserver:
            self.__report_client.updateSession(**session_properties)                            
        return None

    def finalize(self, result):
        if self.write_hashes:
            self.__write('end cycle: %s \n' % (self.cid)) 
        session_properties = {'sid': self.session_id}
        if self.opt.icycle and not self.__counter.alive() and self.opt.reportserver:
            session_properties.update({'endtime': _reportTime()})
            self.__report_client.updateSession(**session_properties)
        if self.conf.stopOnError and self.opt.reportserver:
            session_properties.update({'endtime': _reportTime()})
            self.__report_client.updateSession(**session_properties)
            sys.exit(1)
        return None