Exemple #1
0
    def teardown_method(self):
        super().teardown_method()
        if self.video_config["record"]:
            video = self._driver.stop_recording_screen()
            video_folder_path = os.path.join(os.getcwd(), "projects",
                                             LoadConfig.load_config("project"),
                                             "test_reports", "videos")
            if not os.path.exists(video_folder_path):
                os.mkdir(video_folder_path)
            video_file_path = os.path.join(
                video_folder_path,
                "%s.mp4" % datetime.datetime.now().strftime("%Y%m%d%H%M%S%f"))
            with open(video_file_path, "wb") as f:
                f.write(base64.b64decode(video))
            self.log.info("video captured")

            if self.video_config["only_record_failed"]:
                stat_file = os.path.join(os.getcwd(), "projects",
                                         LoadConfig.load_config("project"),
                                         "test_reports", "stat.json")
                with open(stat_file, "r") as f:
                    current_stat = json.load(f)
                current_test = os.environ.get('PYTEST_CURRENT_TEST').split(
                    '::')
                current_test_file = current_test[0].split(
                    os.sep)[-1].split(".")[0]
                current_test_name = current_test[-1].split(" ")[0]
                if current_stat["Details"][current_test_file][
                        current_test_name].lower() == "pass":
                    os.remove(video_file_path)
                    self.log.info("remove video because current test pass")
Exemple #2
0
 def start_app(cls):
     mobile = LoadConfig.load_config()["mobile"]
     device = LoadConfig.load_config()["device"]
     project = LoadConfig.load_config()["project"]
     if device.lower() == "stf":
         cls.remote_device = StfDevices(
             LoadConfig.load_config()["env"]["stf_host"])
         cls.device_serial = cls.remote_device.get_single_device()["serial"]
         cls.log.info("device serial: %s" % str(cls.device_serial))
         cls.log.info(
             cls.remote_device.rent_single_device(cls.device_serial).text)
         cls.remote_connect_url = cls.remote_device.get_user_device_remote_connect_url(
             cls.device_serial)
         cls.log.info("connect url: %s" % str(cls.remote_connect_url))
         os.system("adb connect " + cls.remote_connect_url)
         stf_caps = YamlHelper.load_yaml(
             os.path.join(os.getcwd(), "projects", project, "conf",
                          "device_caps.yaml"))["STF"]
         device_cap = None
         for device in stf_caps.values():
             if device["deviceName"] == cls.device_serial:
                 device_cap = device
                 break
         cls._driver = AppiumHelper.get_driver(mobile, device_cap)
     else:
         device_cap = YamlHelper.load_yaml(
             os.path.join(os.getcwd(), "projects", project, "conf",
                          "device_caps.yaml"))[device]
         cls._driver = AppiumHelper.get_driver(mobile, device_cap)
Exemple #3
0
 def browser_home_page(self):
     if "home_page" in LoadConfig.load_config()["env"].keys():
         home_page = LoadConfig.load_config()["env"]["home_page"]
         self.log.info(f"Go to home page: {home_page}")
         self.browse_page(home_page)
     else:
         self.log.info("no home page value, stay current page")
Exemple #4
0
 def start_app(cls):
     project = LoadConfig.load_config()["project"]
     executable = LoadConfig.load_config()["env"]["executable"]
     win_caps_file = os.path.join(os.getcwd(), "projects", project, "conf", "win_caps.yaml")
     if os.path.exists(win_caps_file):
         win_caps = YamlHelper.load_yaml(win_caps_file)
         cls._driver = WinautoHelper.get_driver(executable, win_caps)
     else:
         cls._driver = WinautoHelper.get_driver(executable)
Exemple #5
0
 def setup_method(self):
     super().setup_method()
     self.video_config = LoadConfig.load_config("video")
     if self.video_config["record"]:
         video_folder_path = os.path.join(os.getcwd(), "projects", LoadConfig.load_config("project"), "test_reports", "videos")
         if not os.path.exists(video_folder_path):
             os.mkdir(video_folder_path)
         self.video_file_path = os.path.join(video_folder_path, "%s.mp4" % datetime.datetime.now().strftime("%Y%m%d%H%M%S%f"))
         self.vh = VideoHelper(out_file=self.video_file_path)
         self.vh.start_recording_screen()
Exemple #6
0
    def initialize_logger(cls, logger):
        logging.basicConfig(level=LoadConfig.load_config()["log_level"],
                            format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
                            filename=os.path.join(os.getcwd(), "projects", LoadConfig.load_config()["project"], "log", "AutoTest.log"),
                            filemode='a')

        if len(logger.handlers) == 0:
            cls.add_handler(logger, cls.get_handler(level=LoadConfig.load_config()["log_level"]))
        else:
            logger.setLevel(LoadConfig.load_config()["log_level"])
        return logger
Exemple #7
0
 def click(self, locator, *args):
     by, value = self.format_locator(locator, *args)
     self.log.info("Click the element found by %s: %s" % (by, value))
     if by != "image":
         try:
             element = self.wait_until_element_to_be_clickable(
                 locator, *args)
         except:
             element = self.find_element(locator, *args)
         element.click()
     else:
         test_data_path = os.path.join(os.getcwd(), "projects",
                                       LoadConfig.load_config()["project"],
                                       "test_data")
         screenshot_path = os.path.join(test_data_path, "screenshot.png")
         element_img_path = os.path.join(test_data_path, value)
         self._driver.save_screenshot(screenshot_path)
         time.sleep(1)
         if os.path.exists(screenshot_path):
             scale_x, scale_y = CVHelper.flann_generate_matched_points_center(
                 element_img_path, screenshot_path)
             window_size = self._driver.get_window_size()
             x = round(scale_x * window_size["width"], 2)
             y = round(scale_y * window_size["height"], 2)
             self.log.info(f"click on the coordinates: {x}, {y}")
             self._driver.tap([(x, y)])
         else:
             self.log.info("unable to find the screenshot file")
 def click(self, locator, *args):
     by, value = self.format_locator(locator, *args)
     self.log.info("Click the element found by %s: %s" % (by, value))
     if by != "image":
         try:
             element = self.wait_until_element_to_be_clickable(locator, *args)
             # element.location_once_scrolled_into_view
             self.scroll_into_view_of_element(element)
         except:
             element = self.find_element(locator, *args)
         element.click()
     else:
         test_data_path = os.path.join(os.getcwd(), "projects", LoadConfig.load_config()["project"], "test_data")
         screenshot_path = os.path.join(test_data_path, "screenshot.png")
         element_img_path = os.path.join(test_data_path, value)
         self._driver.save_screenshot(screenshot_path)
         time.sleep(1)
         if os.path.exists(screenshot_path):
             scale_x, scale_y = CVHelper.flann_generate_matched_points_center(element_img_path, screenshot_path)
             window_size = self._driver.get_window_size()
             x = round(scale_x * window_size["width"], 2)
             y = round(scale_y * window_size["height"], 2)
             self.log.info(f"click on the coordinates: {x}, {y}")
             whole_page = self.find_element({"by": "xpath", "value": "//html/body"})
             ActionChains(self._driver).move_to_element_with_offset(whole_page, 1, 1).perform()
             ActionChains(self._driver).move_by_offset(x, y).click().perform()
         else:
             self.log.info("unable to find the screenshot file")
 def __init__(self, api_name):
     self.api_name = api_name
     self.current_class = os.environ.get('PYTEST_CURRENT_TEST').split('::')[-2].split(' ')[0]
     self.log = logging.getLogger(self.current_class)
     self.config = LoadConfig.load_config()
     self.url = "/".join([self.config["env"]["base_url"], self.config["env"]["api_list"][self.api_name]])
     self.new_url = None
Exemple #10
0
 def teardown_class(cls):
     super().teardown_class()
     AppiumHelper.close_driver()
     device = LoadConfig.load_config()["device"]
     if device.lower() == "stf":
         os.system("adb disconnect " + cls.remote_connect_url)
         cls.log.info(
             cls.remote_device.return_rented_device(cls.device_serial).text)
    def flann_generate_matched_points_center(cls, target, source="screenshot.png"):
        base_path = os.path.join(os.getcwd(), "projects", LoadConfig.load_config()["project"], "test_data")
        img_target = cv2.imread(os.path.join(base_path, target), 0)
        img_source = cv2.imread(os.path.join(base_path, source), 0)
        print('target image size: height, width: {}, {}'.format(img_target.shape[0], img_target.shape[1]))
        print('source image size: height, width: {}, {}'.format(img_source.shape[0], img_source.shape[1]))

        sift = cv2.xfeatures2d.SIFT_create()
        kp1, des1 = sift.detectAndCompute(img_target, None)
        kp2, des2 = sift.detectAndCompute(img_source, None)
        FLANN_INDEX_KDTREE = 0
        index_params = dict(algorithm=FLANN_INDEX_KDTREE, trees=5)
        search_params = dict(checks=50)
        flann = cv2.FlannBasedMatcher(index_params, search_params)
        matches = flann.knnMatch(des1, des2, k=2)

        points = []
        for m, n in matches:
            if m.distance < 0.75 * n.distance:
                points.append(m)
        h1, w1 = img_target.shape[:2]
        h2, w2 = img_source.shape[:2]
        view = np.zeros((max(h1, h2), w1 + w2, 3), np.uint8)
        view[:h1, :w1, 0] = img_target
        view[:h2, w1:, 0] = img_source
        view[:, :, 1] = view[:, :, 0]
        view[:, :, 2] = view[:, :, 0]

        coordinate_x = []
        coordinate_y = []
        for p in points:
            coordinate_x.append(int(kp2[p.trainIdx].pt[0]))
            coordinate_y.append(int(kp2[p.trainIdx].pt[1]))
        scale_x = round(sum(coordinate_x) / len(points) / img_source.shape[1], 2)
        scale_y = round(sum(coordinate_y) / len(points) / img_source.shape[0], 2)
        return scale_x, scale_y
        # temp = []
        # for m, n in matches:
        #     if m.distance < 0.6 * n.distance:
        #         temp.append(m)
        # src_pts = np.float32([kp1[m.queryIdx].pt for m in temp]).reshape(-1, 1, 2)
        # dst_pts = np.float32([kp2[m.trainIdx].pt for m in temp]).reshape(-1, 1, 2)
        # M, mask = cv2.findHomography(src_pts, dst_pts, cv2.LMEDS, 5.0)
        # matches_mask = mask.ravel().tolist()
        #
        # h, w = img_target.shape
        # pts = np.float32([[0, 0], [0, h - 1], [w - 1, h - 1], [w - 1, 0]]).reshape(-1, 1, 2)
        #
        # dst = cv2.perspectiveTransform(pts, M)
        # img_source_new = cv2.polylines(img_source, [np.int32(dst)], True, 255, 3, cv2.LINE_AA)
        #
        # area = [np.int32(dst)]
        #
        # # compute object center coordinate in the search image
        # x = (area[0][2][0][0] - area[0][0][0][0]) / 2 + area[0][0][0][0]
        # y = (area[0][1][0][1] - area[0][0][0][1]) / 2 + area[0][0][0][1]
        # return x, y
Exemple #12
0
 def __init__(self, driver, logger):
     self._driver = driver
     self.log = logger
     self._config = LoadConfig.load_config()
     self.behavior = self.__init_behavior_by_project_type()
     element_file = os.path.join(os.getcwd(), "projects", self._config["project"], "pages", "elements", "%s.yaml" % self.__module__.split(".")[-1])
     if os.path.exists(element_file):
         self._elements = YamlHelper.load_yaml(element_file)
     else:
         self.log.critical("no element definition file found!!: %s" % element_file)
Exemple #13
0
 def start_browser(cls):
     browser = LoadConfig.load_config()["browser"]
     if browser.lower() in ["chrome", "firefox", "ie", "edge", "safari"]:
         cls.log.info("Start to launch browser - %s" % browser)
         cls._driver = SeleniumHelper.get_driver(browser)
     else:
         device = LoadConfig.load_config()["device"]
         cls.log.info("Start to launch browser - %s with device name %s" % (browser, device))
         cls._driver = SeleniumHelper.get_driver(browser, device_name=device)
     try:
         if browser.lower() == "chrome":
             width, height = cls._driver.execute_script("return [window.screen.availWidth, window.screen.availHeight];")
             cls.log.info(f"set window size to width: {width} and height: {height}")
             cls._driver.set_window_size(width, height)
         else:
             cls.log.info("maximize browser window")
             cls._driver.maximize_window()
     except WebDriverException as e:
         cls.log.warning("fail to set window size: %s" % str(e))
Exemple #14
0
 def input_texts_into_edit_then_save(self, texts, file_name):
     self.log.info("Input texts into edit area")
     self.element("edit_input").input_value(texts)
     main_window = self.element_info("main_window")
     self.behavior.select_menu(main_window, ["File", "Save As..."])
     file_path = os.path.join(os.getcwd(), "projects",
                              LoadConfig.load_config()["project"],
                              "test_data", file_name)
     self.element("save_file_input").input_value(file_path)
     self.element("save_button").click()
Exemple #15
0
 def teardown_method(self):
     super().teardown_method()
     if self.video_config["record"]:
         self.vh.stop_recording_screen()
         self.log.info("video captured")
         if self.video_config["only_record_failed"]:
             stat_file = os.path.join(os.getcwd(), "projects", LoadConfig.load_config("project"), "test_reports", "stat.json")
             with open(stat_file, "r") as f:
                 current_stat = json.load(f)
             current_test = os.environ.get('PYTEST_CURRENT_TEST').split('::')
             current_test_file = current_test[0].split("/")[-1].split(".")[0]
             current_test_name = current_test[-1].split(" ")[0]
             if current_stat["Details"][current_test_file][current_test_name].lower() == "pass":
                 os.remove(self.video_file_path)
                 self.log.info("remove video because current test pass")
    def sift_show_points_matched(cls, target, source="screenshot.png"):
        base_path = os.path.join(os.getcwd(), "projects", LoadConfig.load_config()["project"], "test_data")
        img_target = cv2.imread(os.path.join(base_path, target), 0)
        img_source = cv2.imread(os.path.join(base_path, source), 0)
        print('target image size: height, width: {}, {}'.format(img_target.shape[0], img_target.shape[1]))
        print('source image size: height, width: {}, {}'.format(img_source.shape[0], img_source.shape[1]))

        sift = cv2.xfeatures2d.SIFT_create()
        kp1, des1 = sift.detectAndCompute(img_target, None)
        kp2, des2 = sift.detectAndCompute(img_source, None)

        bf = cv2.BFMatcher()
        points = []
        matches = bf.knnMatch(des1, des2, k=2)
        for m, n in matches:
            if m.distance < 0.75 * n.distance:
                points.append([m])
        img_result = cv2.drawMatchesKnn(img_target, kp1, img_source, kp2, points, None, flags=2)
        plt.imshow(img_result)
        plt.show()
 def click(self, locator, *args):
     by, value = self.format_locator(locator, *args)
     self.log.info("Click the element found by %s: %s" % (by, value))
     if by != "image":
         element = self.wait_until_element_to_be_clickable(locator, *args)
         element.click()
     else:
         test_data_path = os.path.join(os.getcwd(), "projects", LoadConfig.load_config()["project"], "test_data")
         screenshot_path = os.path.join(test_data_path, "screenshot.png")
         element_img_path = os.path.join(test_data_path, value)
         width, height = Win32Helper.capture_screen(screenshot_path)
         time.sleep(1)
         if os.path.exists(screenshot_path):
             scale_x, scale_y = CVHelper.flann_generate_matched_points_center(element_img_path, screenshot_path)
             x = round(scale_x * width, 2)
             y = round(scale_y * height, 2)
             self.log.info(f"click on the coordinates: {x}, {y}")
             Mouse.click("left", (x, y))
         else:
             self.log.info("unable to find the screenshot file")
Exemple #18
0
 def swipe(self, direction, duration=1000):
     self.log.info("Swipe to direction: %s" % direction)
     if LoadConfig.load_config()["device"].lower().startswith("ip"):
         self._driver.execute_script("mobile: swipe",
                                     {"direction": direction})
     else:
         screen_size = self._driver.get_window_size()
         directions = {
             "up": {
                 "start_x": screen_size.width / 2,
                 "start_y": screen_size.height * 3 / 4,
                 "end_x": screen_size.width / 2,
                 "end_y": screen_size.height / 4,
                 duration: duration
             },
             "down": {
                 "start_x": screen_size.width / 2,
                 "start_y": screen_size.height / 4,
                 "end_x": screen_size.width / 2,
                 "end_y": screen_size.height * 3 / 4,
                 duration: duration
             },
             "left": {
                 "start_x": screen_size.width * 4 / 5,
                 "start_y": screen_size.height / 2,
                 "end_x": screen_size.width / 5,
                 "end_y": screen_size.height / 2,
                 duration: duration
             },
             "right": {
                 "start_x": screen_size.width / 5,
                 "start_y": screen_size.height / 2,
                 "end_x": screen_size.width * 4 / 5,
                 "end_y": screen_size.height / 2,
                 duration: duration
             }
         }
         self._driver.swipe(directions[direction]["start_x"],
                            directions[direction]["start_y"],
                            directions[direction]["end_x"],
                            directions[direction]["end_y"], duration)
class TestSample(LoggedTestCase):
    def test_answer1(self):
        """TEMP-1: this is description of test answer1"""
        assert 2 + 3 == 5

    @pytest.mark.error
    def test_answer2(self):
        """TEMP-2: this is description of test answer2"""
        assert 2 + 4 == 5

    @pytest.mark.sample
    @pytest.mark.temp
    def test_answer3(self):
        """TEMP-3: this is description of test answer3"""
        assert 2 + 3 == 5

    @pytest.mark.skipif(LoadConfig.load_config()["tag"].lower()
                        in ["staging", "prod", "all"],
                        reason="not ready")
    def test_answer4(self):
        """TEMP-4: this is description of test answer4"""
        assert 2 + 4 == 5
Exemple #20
0
def pytest_configure(config):
    if hasattr(config, '_metadata'):
        run_config_data = LoadConfig.load_config()
        if "Python" in config._metadata.keys():
            del config._metadata["Python"]
        if "Packages" in config._metadata.keys():
            del config._metadata["Packages"]
        if "Plugins" in config._metadata.keys():
            del config._metadata["Plugins"]
        if "JAVA_HOME" in config._metadata.keys():
            del config._metadata["JAVA_HOME"]
        config._metadata["Project"] = run_config_data["project"]
        config._metadata["Environment"] = run_config_data["environment"]
        if run_config_data["report"]["ui_test"]:
            if run_config_data["report"]["app_test"]:
                config._metadata["Mobile"] = run_config_data["mobile"]
                config._metadata["Device"] = run_config_data["device"]
            elif run_config_data["report"]["win_test"]:
                config._metadata["Type"] = "Windows Software"
            else:
                config._metadata["Browser"] = run_config_data["browser"]
        else:
            config._metadata["Type"] = "API"
Exemple #21
0
 def get_project_config_data():
     return LoadConfig.load_config()["env"]
Exemple #22
0
 def go_to_home_page(self):
     home_page = LoadConfig.load_config()["env"]["home_page"]
     self.log.info(f"Go to home page: {home_page}")
     self.behavior.go_to_page(home_page)
     self.element("search_button").wait_clickable()
Exemple #23
0
 def setup_method(self):
     super().setup_method()
     self.video_config = LoadConfig.load_config("video")
     if self.video_config["record"]:
         self._driver.start_recording_screen()
 def __init__(self):
     self.env_config = LoadConfig.load_config()["env"]
Exemple #25
0
 def notepad_is_able_to_save_content_to_file(self, file_name):
     file_path = os.path.join(os.getcwd(), "projects",
                              LoadConfig.load_config()["project"],
                              "test_data", file_name)
     assert self.check_file_exists_in_test_data(
         file_path), "not find the file: %s" % str(file_path)
Exemple #26
0
 def remove_file_if_exists_in_test_data(self, file_name):
     file_path = os.path.join(os.getcwd(), "projects",
                              LoadConfig.load_config()["project"],
                              "test_data", file_name)
     if self.check_file_exists_in_test_data(file_path):
         os.remove(file_path)
Exemple #27
0
 def _load_config(self):
     self._config = LoadConfig.load_config()
Exemple #28
0
def pytest_runtest_makereport(item, call):
    # stat_file = os.path.join(os.getcwd(), "projects", LoadConfig.load_config()["project"], "test_reports", "stat.json")
    global total_sum
    global pass_sum
    global fail_sum
    global skip_sum
    global module_case
    global failures
    global start_time
    global browser_name
    global browser_version
    global mobile_platform_name
    global mobile_platform_version
    outcome = yield
    rep = outcome.get_result()
    test_file = item.function.__module__.split(".")[-1]
    if test_file not in module_case.keys():
        module_case[test_file] = {}
    if test_file not in failures.keys():
        failures[test_file] = {}
    rep.description = str(item.function.__doc__)
    setattr(item, "rep_" + rep.when, rep)
    if rep.when == "setup" and rep.skipped:
        test_method = rep.nodeid.split("::")[-1]
        if test_method not in module_case[test_file]:
            total_sum += 1
            skip_sum += 1
        module_case[test_file][test_method] = "skip"
    if rep.when == "call":
        print("%s: Case Duration: %ss ...%s" %
              (rep.nodeid.split("::")[-1], str(round(rep.duration,
                                                     2)), rep.outcome))
        config = LoadConfig.load_config()
        if config["report"]["ui_test"]:
            if config["report"]["app_test"]:
                driver = AppiumHelper.get_current_driver()
                cap = driver.capabilities
                mobile_platform_name = cap["platformName"]
                mobile_platform_version = cap["platformVersion"]
            elif not config["report"]["win_test"]:
                driver = SeleniumHelper.get_current_driver()
                cap = driver.capabilities
                browser_name = cap["browserName"]
                browser_version = cap["browserVersion"]
        test_method = rep.nodeid.split("::")[-1]
        if rep.passed:
            if test_method not in module_case[test_file]:
                total_sum += 1
                pass_sum += 1
            else:
                pass_sum += 1
                fail_sum -= 1
            module_case[test_file][test_method] = "pass"
            if test_method in failures[test_file]:
                del failures[test_file][test_method]
        elif rep.failed:
            if test_method not in module_case[test_file]:
                total_sum += 1
                fail_sum += 1
            module_case[test_file][test_method] = "fail"
            failures[test_file][test_method] = {
                "error_message": rep.longreprtext
            }
            config = LoadConfig.load_config()
            if config["report"]["ui_test"]:
                screen_folder_path = os.path.join(os.getcwd(), "projects",
                                                  config["project"],
                                                  "test_reports",
                                                  "screenshots")
                if not os.path.exists(screen_folder_path):
                    os.mkdir(screen_folder_path)
                if config["report"]["app_test"]:
                    driver = AppiumHelper.get_current_driver()
                elif not config["report"]["win_test"]:
                    driver = SeleniumHelper.get_current_driver()
                screenshot_file_path = os.path.join(
                    screen_folder_path, "%s.png" %
                    datetime.datetime.now().strftime("%Y%m%d%H%M%S%f"))
                if config["report"]["win_test"]:
                    from utils.win32_helper import Win32Helper
                    Win32Helper.capture_screen(screenshot_file_path)
                else:
                    driver.save_screenshot(screenshot_file_path)
                failures[test_file][test_method][
                    "screenshot"] = screenshot_file_path.split(os.getcwd())[-1]
                html = '<div><img src="%s" alt="screenshot" style="width:304px;height:228px;" ' \
                       'onclick="window.open(this.src)" align="right"/></div>' % screenshot_file_path
                extra = getattr(rep, 'extra', [])
                extra.append(
                    item.config.pluginmanager.getplugin('html').extras.html(
                        html))
                rep.extra = extra
        elif rep.skipped:
            total_sum += 1
            skip_sum += 1
            module_case[test_file][test_method] = "skip"
    print("Run %d cases, Current Status: Pass - %d, Fail - %d, Skip - %d" %
          (total_sum, pass_sum, fail_sum, skip_sum))
Exemple #29
0
def pytest_sessionfinish(session, exitstatus):
    global start_time
    global browser_name
    global browser_version
    global mobile_platform_name
    global mobile_platform_version
    global failures
    stat_file = os.path.join(os.getcwd(), "projects",
                             LoadConfig.load_config()["project"],
                             "test_reports", "stat.json")
    session_pass_sum = session_fail_sum = session_skip_sum = 0
    session_module_case = {}
    session_case_names = {}
    session_class_doc = {}
    reporter = session.config.pluginmanager.get_plugin('terminalreporter')
    expect_types = ["passed", "failed", "skipped"]
    alias = {"passed": "pass", "failed": "fail", "skipped": "skip"}
    actual_types = reporter.stats.keys()
    for rt in expect_types:
        if rt in actual_types:
            for item in reporter.stats[rt]:
                test_file = item.nodeid.split("tests/")[-1].split(
                    "::")[0].split(".")[0]
                test_file_path, test_class_name, _ = item.nodeid.split("::")
                if test_class_name not in session_class_doc.keys():
                    with open(os.path.join(os.getcwd(), test_file_path)) as fd:
                        tree = ast.parse(fd.read())
                        for node in ast.walk(tree):
                            if isinstance(node, ast.ClassDef
                                          ) and node.name == test_class_name:
                                docstring = ast.get_docstring(node)
                                session_class_doc[test_file] = yaml.load(
                                    docstring, Loader=yaml.BaseLoader)
                test_method = item.nodeid.split("::")[-1]
                test_result = item.outcome
                test_case_name = item.description
                unique_test_method = test_method.split("[")[0]
                if test_result == "passed":
                    session_pass_sum += 1
                elif test_result == "failed":
                    session_fail_sum += 1
                elif test_result == "skipped":
                    session_skip_sum += 1
                if test_file not in session_module_case.keys():
                    session_module_case[test_file] = {
                        test_method: alias[test_result]
                    }
                    session_case_names[test_file] = {
                        unique_test_method: test_case_name
                    }
                else:
                    session_module_case[test_file][test_method] = alias[
                        test_result]
                    if unique_test_method not in session_case_names[test_file]:
                        session_case_names[test_file][
                            unique_test_method] = test_case_name
    session_total_sum = session_pass_sum + session_fail_sum + session_skip_sum
    current_result = {
        "Total": session_total_sum,
        "Pass": session_pass_sum,
        "Fail": session_fail_sum,
        "Skip": session_skip_sum,
        "Start_Time": start_time,
        "End_Time": datetime.datetime.now().strftime("%Y-%m-%d-%H:%M:%S.%f"),
        "Brief": session_class_doc,
        "Details": session_module_case,
        "Cases": session_case_names,
        "Failures": failures
    }
    config = LoadConfig.load_config()
    if config["report"]["ui_test"]:
        if config["report"]["app_test"]:
            current_result["mobile_platform_name"] = mobile_platform_name
            current_result["mobile_platform_version"] = mobile_platform_version
        elif not config["report"]["win_test"]:
            current_result["browser_name"] = browser_name
            current_result["browser_version"] = browser_version
    with open(stat_file, "w") as f:
        json.dump(current_result, f)
    def run(self):
        self.start_time = datetime.datetime.now()
        self._load_config()
        # print(self._config)
        report_folder_path = os.path.join(os.getcwd(), "projects",
                                          self._config["project"],
                                          "test_reports")
        log_folder_path = os.path.join(os.getcwd(), "projects",
                                       self._config["project"], "log")
        data_folder_path = os.path.join(os.getcwd(), "projects",
                                        self._config["project"], "test_data")
        for folder in [report_folder_path, log_folder_path, data_folder_path]:
            if not os.path.exists(folder):
                os.mkdir(folder)
        if self._config["report"]["ui_test"]:
            if self._config["report"]["app_test"]:
                report_suffix = self._config["mobile"]
            elif self._config["report"]["win_test"]:
                report_suffix = "WindowsSoftware"
            else:
                report_suffix = self._config["browser"]
        else:
            report_suffix = "API"
        report_path = os.path.join(
            report_folder_path, "AutoTest-%s-%s-%s-%s.html" %
            (self._config["project"], self._config["environment"],
             report_suffix, datetime.datetime.now().strftime("%Y%m%d%H%M%S")))

        # -v shows the result of each def; -q only shows the overall status; -s shows the print function in test def
        command_list = [
            "--html", report_path, "--self-contained-html", "-v", "--tb=short"
        ]
        # if has include_test, then run these cases; else check and run exclude_test
        if self._config["test"]:
            for in_test in self._config["test"].split(","):
                command_list.append(
                    os.path.join("projects", self._config["project"], "tests",
                                 in_test.strip()))
        elif not self._config["test"]:
            command_list.append(
                os.path.join("projects", self._config["project"], "tests"))
        elif self._config["exclude_test"]:
            for ex_test in self._config["exclude_test"].split(","):
                command_list.append("--deselect")
                command_list.append(
                    os.path.join("projects", self._config["project"], "tests",
                                 ex_test.strip()))
        # run tests which name match keyword, example: keyword, not keyword, key1 or key2, key1 and key2
        if self._config["keyword"]:
            command_list.append("-k")
            command_list.append(self._config["keyword"].strip())
        # run tests which name match marker, example: marker, not marker, marker1 or marker2, marker1 and marker2
        if self._config["marker"]:
            command_list.append("-m")
            command_list.append(self._config["marker"].strip())
        # rerun failures
        if self._config["rerun"]["rerun_times"]:
            command_list.append("--reruns")
            command_list.append(str(self._config["rerun"]["rerun_times"]))
            if self._config["rerun"]["rerun_delay"]:
                command_list.append("--reruns-delay")
                command_list.append(str(self._config["rerun"]["rerun_delay"]))
        # log
        #command_list.append('--log-format="%(asctime)s %(levelname)s %(message)s"')
        #command_list.append('--log-date-format="%Y-%m-%d %H:%M:%S"')
        #command_list.append("--show-capture=no")
        command_list.append("--log-file=%s" % os.path.join(
            os.getcwd(), "projects",
            LoadConfig.load_config()["project"], "log", "AutoTest-%s.log" %
            datetime.datetime.now().strftime("%Y%m%d%H%M%S")))
        # multi-process
        try:
            if int(self._config["process"]) > 1:
                command_list.append("-n")
                command_list.append(self._config["process"])
        except (ValueError, KeyError):
            pass
        pytest.main(command_list)
        # print(command_list)
        self.exit_test()
        self.end_time = datetime.datetime.now()
        result_statistics = self._generate_result_statistics(
            report_folder_path)
        if self._config["email_sender"]["send_report"]:
            self._send_test_result(result_statistics, report_path)
        print(
            "Overall Start Time: %s\nOverall End Time: %s\nOverall Duraion: %s\nReport file in: %s"
            % (str(self.start_time), str(self.end_time),
               str(self.end_time - self.start_time), report_path))