Пример #1
0
 def __read_file(name):
     try:
         with open(file=name, mode="r", encoding='utf-8') as f:
             content = f.read()
             # solve problem caused by BOM head
             if content.startswith(u'\ufeff'):
                 content = content.encode('utf-8')[3:].decode('utf-8')
             return content
     except IOError:
         logger.error("Failed to open file: {}".format(name))
         sys.exit(-1)
Пример #2
0
    def source_to_case(self,
                       source,
                       target="ParrotProject",
                       include=None,
                       exclude=None,
                       validate_include=None,
                       validate_exclude=None,
                       auto_extract=False):
        """
        :param source: source file or direcotry
        :param target: target directory for case output
        :param include: list, not matched url would be ignored in recording
        :param exclude: list, matched url would be ignored in recording
        :param validate_include: list, not matched response would be ignored in validating
        :param validate_exclude: list, matched response would be ignored in validating
        :param auto_extract: bool, for automatic identification of interface dependencies
        :return suite dict
        """
        source = format(source).strip()
        if not (source and os.path.exists(source)):
            logger.error(
                "Source file or directory does not exist: {}".format(source))
            sys.exit(-1)

        if source.endswith("/") or source.endswith("\\"):
            suite_name = get_file_name(get_file_path(source))
        else:
            suite_name = get_file_name(source)

        if os.path.isdir(source):
            files = get_dir_files(source)
        else:
            files = [
                source,
            ]

        suite_dict = copy.deepcopy(self.suite_tpl)
        suite_dict['config']['name'] = suite_name

        logger.info(
            "Start to parse cases from source files: {}".format(source))

        for _file in files:
            if _file.lower().endswith('.har'):
                one_case = self.har_to_case(_file, target, include, exclude,
                                            validate_include, validate_exclude,
                                            auto_extract, suite_name)
            # elif _file.lower().endswith('.trace'):
            #     self.charles_trace_to_case()
            # elif _file.lower().endswith('.txt'):
            #     self.fiddler_txt_to_case()
            else:
                logger.warning(
                    "Unsupported file extension: {}, ignore".format(_file))
                continue

            # add case into suite
            suite_dict['test_cases'].append(one_case)
            logger.info("Parse finished.")

        self.__generate_case(suite_dict, target)
        return suite_dict
Пример #3
0
    def har_to_case(self,
                    source,
                    target="ParrotProject",
                    include=None,
                    exclude=None,
                    validate_include=None,
                    validate_exclude=None,
                    auto_extract=False,
                    suite_name=None):
        """parse source har file and generate test cases
        :param source: source file
        :param target: target directory for case output
        :param include: list, not matched url would be ignored in recording
        :param exclude: list, matched url would be ignored in recording
        :param validate_include: list, not matched response would be ignored in validating
        :param validate_exclude: list, matched response would be ignored in validating
        :param auto_extract: bool, for automatic identification of interface dependencies
        :param suite_name: specified suite, new a suite as default
        :return suite dict
        """
        if not (source and os.path.exists(source)):
            logger.error("Source file does not exist: {}".format(source))
            return False
        if not source.lower().endswith('.har'):
            logger.error("The source is not a har file: {}".format(source))
            return False
        logger.info("Start to parse source file: {}".format(source))

        content = self.__read_file(source)
        try:
            har_dict = json.loads(content)['log']['entries']
        except TypeError:
            logger.error("HAR file content error: {}".format(source))
        except KeyError:
            logger.error("HAR file content error: {}".format(source))
            return False

        case_dict = copy.deepcopy(self.case_tpl)
        case_dict['config']['name'] = get_file_name(file=source)
        for entry_dict in har_dict:
            step_dict = copy.deepcopy(self.step_tpl)
            self.__har_times(entry=entry_dict, step_dict=step_dict)
            if not self.__har_request(entry=entry_dict,
                                      step_dict=step_dict,
                                      include=include,
                                      exclude=exclude,
                                      auto_extract=auto_extract):
                continue
            if not self.__har_response(entry=entry_dict,
                                       step_dict=step_dict,
                                       include=validate_include,
                                       exclude=validate_exclude,
                                       auto_extract=auto_extract):
                continue
            logger.debug("test_step: {}".format(
                json.dumps(step_dict, ensure_ascii=False)))

            # add step into case
            case_dict['test_steps'].append(step_dict)
        if suite_name:
            return case_dict
        else:
            suite_dict = copy.deepcopy(self.suite_tpl)
            # add case into suite
            suite_dict['test_cases'].append(case_dict)
            suite_dict['config']['name'] = get_file_name(file=source)
            logger.info("Parse finished.")

            self.__generate_case(suite_dict, target)
            return suite_dict
Пример #4
0
    def run_cases(self, suite_or_case, environment=None, interval='ms', reset_after_case=False,
                  fail_stop=False, retry_times=0, retry_interval=100, output='.'):
        """
        :param suite_or_case: file or directory of test suites / cases / steps
        :param environment: environment flag defined in test data, 'None' - only load 'global' data
        :param interval: interval time(ms) between each step, use the recorded interval as default
        :param reset_after_case: reset runtime environment after each case or not, 'no' as default
        :param fail_stop: stop or not when a test step failed on validation, False as default
        :param retry_times: max retry times when a test step failed on validation, 0 as default
        :param retry_interval: retry interval(ms) when a test step failed on validation, 100 as default
        :param output: output path for report, '.' as default
        :return:
        """
        try:
            interval = float(interval)
        except ValueError:
            interval = 'ms'
        try:
            retry_times = int(retry_times)
        except ValueError:
            retry_times = 0
        try:
            retry_interval = int(retry_interval)
        except ValueError:
            retry_interval = 100

        # parse specified cases into dict
        items = self.parser.load_test_case(suite_or_case=suite_or_case, environment=environment)
        self.report['title'] = suite_or_case
        self.report['detail'] = items
        self.report['time']['start'] = now_ms()
        if not items:
            logger.error("Parsed {}, but get nothing.".format(suite_or_case))
            return -1
        for _sid, _suite in enumerate(items):
            self.report['summary']['suite']['total'] += 1
            _suite['_report_'] = {
                'id': _sid,
                'name': _suite['config']['name'],
                'status': True,
                'cases': {
                    'total': 0,
                    'pass': 0,
                    'fail': 0
                }
            }
            logger.info("Run test suite: {}".format(json.dumps(_suite, ensure_ascii=False)))

            # do hook actions before a suite
            logger.info(" - Do setup hook actions of the suite: {}".format(_suite['setup_hooks']))
            self.do_hook_actions(_suite['setup_hooks'])

            for _cid, _case in enumerate(_suite['test_cases']):
                self.report['summary']['case']['total'] += 1
                _suite['_report_']['cases']['total'] += 1
                _case['_report_'] = {
                    'id': _cid,
                    'name': _case['config']['name'],
                    'status': True,
                    'steps': {
                        'total': 0,
                        'pass': 0,
                        'fail': 0
                    }
                }
                logger.info("Run test case: {}".format(json.dumps(_case, ensure_ascii=False)))

                # do hook actions before a case
                logger.info(" - Do setup hook actions of the case: {}".format(_case['setup_hooks']))
                self.do_hook_actions(_case['setup_hooks'])

                for _tid, _step in enumerate(_case['test_steps']):
                    self.report['summary']['step']['total'] += 1
                    _case['_report_']['steps']['total'] += 1
                    _step['_report_'] = {
                        'id': _tid,
                        'name': _step['config']['name'],
                        'status': True
                    }
                    logger.info("Run test step: {}".format(json.dumps(_step, ensure_ascii=False)))

                    # do hook actions before a request
                    logger.info(" - Do setup hook actions of the step: {}".format(_step['setup_hooks']))
                    self.do_hook_actions(_step['setup_hooks'])

                    # handle variables, priority: suite > case > step
                    self.__set_variables(_step['config']['variables'])
                    self.__set_variables(_case['config']['variables'])
                    self.__set_variables(_suite['config']['variables'])
                    logger.info(" - Config variables of the step: {}".format(json.dumps(self.variables, ensure_ascii=False)))

                    # handle request interval
                    if not isinstance(interval, (int, float)):
                        if 'time.start' in _step['request']:  # use the recorded interval
                            _span = now_timestamp_ms() - int(_step['request']['time.start'])
                            if not self.req_span:
                                self.req_span = _span
                            _sleep = self.req_span - _span if self.req_span > _span else MINOR_INTERVAL_MS
                            # higher than MAX, treat it as request of another batch
                            if _sleep > MAX_INTERVAL_MS:
                                _sleep = MINOR_INTERVAL_MS
                                self.req_span = _span  # reset span
                        else:  # no recorded interval, use default
                            _sleep = MINOR_INTERVAL_MS
                    else:  # use specified interval
                        _sleep = interval
                    if _sleep != MINOR_INTERVAL_MS:
                        logger.info(" - Break time, sleep for {} ms.".format(_sleep))
                    time.sleep(_sleep/1000.0)

                    try_flag = True
                    while try_flag:
                        # run this request
                        response = self.run_one_request(_step['request'])
                        _step['_report_']['request'] = response['request']
                        _step['_report_']['response'] = response['response']
                        _step['_report_']['time'] = response['time']
                        response['response']['time'] = response['time']

                        # extract specified variables
                        if 'extract' in _step['response'] and _step['response']['extract']:
                            logger.info(" - Extract variables: {}".format(_step['response']['extract']))
                            self.__extract_variable(extract=_step['response']['extract'], response=response['response'])
                            logger.debug(" - Variables after extract: {}".format(json.dumps(self.variables, ensure_ascii=False)))

                        # do response validation
                        _validate = self.do_validation(response=response['response'], rules=_step['validations'])
                        _step['_report_']['validation'] = _validate
                        if not _validate['status']:  # failed
                            logger.info(" - Test step validation failed")
                            if fail_stop:
                                try_flag = False
                                break
                            elif retry_times:
                                logger.info("Sleep {} ms and Run this test step again..".format(retry_interval))
                                retry_times -= 1
                                time.sleep(retry_interval*1.0/1000)
                            else:
                                break
                        else:
                            break
                    if _step['_report_']['validation']['status']:  # step pass
                        self.report['summary']['step']['pass'] += 1
                        _case['_report_']['steps']['pass'] += 1
                    else:
                        self.report['summary']['step']['fail'] += 1
                        _case['_report_']['steps']['fail'] += 1
                        _suite['_report_']['status'] = _case['_report_']['status'] = _step['_report_']['status'] = False

                    if not try_flag:  # need to stop
                        _suite['_report_']['cases']['fail'] += 1
                        self.report['summary']['case']['fail'] += 1
                        self.report['summary']['case']['pass'] = self.report['summary']['case']['total'] - \
                                                                 self.report['summary']['case']['fail']
                        self.report['summary']['suite']['fail'] += 1
                        self.report['summary']['suite']['pass'] = self.report['summary']['suite']['total'] - \
                                                                  self.report['summary']['suite']['fail']
                        self.report['time']['end'] = now_ms()
                        logger.info("Stop according to your --fail-stop argument")
                        return self.generate_report(output=output)

                    # do hook actions after a request
                    logger.info(" - Do teardown hook actions of the step: {}".format(_step['teardown_hooks']))
                    self.do_hook_actions(_step['teardown_hooks'])

                # do hook actions after a case
                logger.info(" - Do teardown hook actions of the case: {}".format(_case['teardown_hooks']))
                self.do_hook_actions(_case['teardown_hooks'])

                if reset_after_case:  # reset runtime environment after each case
                    logger.info("Reset runtime environment after the case")
                    self.__reset_env()

                if _case['_report_']['status']:  # case pass
                    _suite['_report_']['cases']['pass'] += 1
                    self.report['summary']['case']['pass'] += 1
                else:
                    _suite['_report_']['cases']['fail'] += 1
                    self.report['summary']['case']['fail'] += 1
                    _suite['_report_']['status'] = False

            # do hook actions after a suite
            logger.info(" - Do teardown hook actions of the suite: {}".format(_suite['teardown_hooks']))
            self.do_hook_actions(_suite['teardown_hooks'])

            # reset runtime environment after each suite
            logger.info("Reset runtime environment after the suite")
            self.__reset_env()

            if _suite['_report_']['status']:  # suite pass
                self.report['summary']['suite']['pass'] += 1
            else:
                self.report['summary']['suite']['fail'] += 1
            self.report['time']['end'] = now_ms()

        # generate report
        self.generate_report(output=output)