diff --git a/conftest.py b/conftest.py index b1617a7..6aefa8e 100644 --- a/conftest.py +++ b/conftest.py @@ -48,6 +48,9 @@ log_setting.CLASS_NAME_STARTSWITH = GlobalConfig.CLASS_NAME_STARTSWITH log_setting.CLASS_NAME_ENDSWITH = GlobalConfig.CLASS_NAME_ENDSWITH log_setting.CLASS_NAME_CONTAIN = GlobalConfig.CLASS_NAME_CONTAIN +from letmego import write_testcase_running_status +from letmego import read_testcase_running_status + from setting import skipif from setting.globalconfig import ConfStr from setting.globalconfig import FixedCsvTitle @@ -158,13 +161,16 @@ def pytest_addoption(parser): "--duringfail", action="store_true", dest="duringfail", default=False, help="出现错误时立即显示" ) parser.addoption( - '--repeat', action='store', default=1, type=int, help='用例重复执行的次数' + '--repeat', action='store', default=1, type=int, help="用例重复执行的次数" ) parser.addoption( - '--exportcsv', action='store', default="", help='导出测试用例文件' + '--exportcsv', action='store', default="", help="导出测试用例文件" ) parser.addoption( - '--line', action='store', default="", help='业务线(CI)' + '--line', action='store', default="", help="业务线(CI)" + ) + parser.addoption( + '--autostart', action='store', default="", help="用例执行程序注册到开机自启服务" ) @@ -457,6 +463,13 @@ def pytest_collection_modifyitems(session): # 批量执行时,不执行没有ID的用例。 logger.error(f"<{item.name}> csv文件中未标记,强制跳过") session.items.remove(item) + + if session.config.option.autostart: + for item in session.items[::-1]: + _letmego = read_testcase_running_status(item) + if _letmego: + session.items.remove(item) + if (suite_id or task_id) and session.items: print("\n即将执行的用例:") for item in session.items: @@ -614,6 +627,9 @@ def pytest_runtest_makereport(item, call): if write_json(item.session): # 只要是需要数据回填(无论是自动还是手动),都需要写json结果. write_case_result(item, report) + + if item.config.option.autostart: + write_testcase_running_status(item) try: if item.execution_count >= (int(item.config.option.record_failed_case) + 1): if report.when == "call": # 存放录屏当次测试结果 diff --git a/env.sh b/env.sh index 52e37a0..734df96 100644 --- a/env.sh +++ b/env.sh @@ -133,6 +133,7 @@ pip_array=( pdocr-rpc image-center allure-custom + letmego ) if [ "${ENV_CUT_FLAG}" = "cut" ]; then diff --git a/env_dev.sh b/env_dev.sh index f2a1242..d62d5af 100755 --- a/env_dev.sh +++ b/env_dev.sh @@ -72,6 +72,7 @@ pip_array=( allure-custom funnylog image-center + letmego ) # 裁剪基础环境 if [ "${ENV_CUT_FLAG}" = "cut" ]; then diff --git a/manage.py b/manage.py index f4c4e1f..e587115 100644 --- a/manage.py +++ b/manage.py @@ -77,6 +77,7 @@ class Manage: build_env=None, client_password=None, parallel=None, + autostart=None, ): self.default_app = app self.default_keywords = keywords @@ -114,6 +115,7 @@ class Manage: self.default_build_env = build_env self.default_client_password = client_password self.default_parallel = parallel + self.default_autostart = autostart say(GlobalConfig.PROJECT_NAME) version_font = "slick" @@ -137,10 +139,10 @@ class Manage: sub_parser_export_csv = subparsers.add_parser(SubCmd.exportcsv.value) help_tip = ( - f"\033[0;32mmanage.py\033[0m 支持 \033[0;32m{[i.value for i in SubCmd]}\033[0m 命令, " - "\n您需要传入一个命令,可以使用 \033[0;32m-h\033[0m或\033[0;32m--help\033[0m 查看每个命令参数的详细使用说明," - "\n比如: \033[0;32myouqu manage.py run -h\033[0m \n" - ) + f"\033[0;32mmanage.py\033[0m 支持 \033[0;32m{[i.value for i in SubCmd]}\033[0m 命令, " + "\n您需要传入一个命令,可以使用 \033[0;32m-h\033[0m或\033[0;32m--help\033[0m 查看每个命令参数的详细使用说明," + "\n比如: \033[0;32myouqu manage.py run -h\033[0m \n" + ) if not cmd_args: print(help_tip) sys.exit(1) @@ -306,6 +308,9 @@ class Manage: sub_parser_run.add_argument( "--line", default="", help="执行的业务线(写入json文件)" ) + sub_parser_run.add_argument( + "--autostart", default="", help="用例执行程序注册到开机自启服务" + ) args = parser.parse_args() local_kwargs = { Args.app_name.value: args.app or self.default_app, @@ -339,6 +344,7 @@ class Manage: Args.project_name.value: args.project_name or self.default_project_name, Args.build_location.value: args.build_location or self.default_build_location, Args.line.value: args.line or self.default_line, + Args.autostart.value: args.autostart or self.default_autostart, } return local_kwargs, args diff --git a/pytest.ini b/pytest.ini index d7735c0..477e0e3 100644 --- a/pytest.ini +++ b/pytest.ini @@ -2,7 +2,7 @@ addopts = -s -vv - --durations=10 + --durations=3 --no-header --verbosity=2 --show-capture=no diff --git a/setting/globalconfig.ini b/setting/globalconfig.ini index 82cfc30..507b7f4 100644 --- a/setting/globalconfig.ini +++ b/setting/globalconfig.ini @@ -78,6 +78,9 @@ REPEAT = ;yes, 测试过程中立即显示报错 DURING_FAIL = no +;注册自启服务 +AUTOSTART = + ;=============================== REPORT CONFIG =================================== [report] ;测试报告的title diff --git a/setting/globalconfig.py b/setting/globalconfig.py index 836ac4b..6d0944f 100644 --- a/setting/globalconfig.py +++ b/setting/globalconfig.py @@ -85,6 +85,7 @@ class _GlobalConfig: NOSKIP = runner_cfg.get_bool("NOSKIP", default=False) IFIXED = runner_cfg.get_bool("IFIXED", default=False) DURING_FAIL = runner_cfg.get_bool("DURING_FAIL", default=False) + AUTOSTART = runner_cfg.get_bool("AUTOSTART", default=False) TOP = runner_cfg.get("TOP", default="") REPEAT = runner_cfg.get("REPEAT", default="") DEB_PATH = runner_cfg.get("DEB_PATH", default="~/Downloads/") diff --git a/src/depends/pyautogui/_pyautogui_wayland.py b/src/depends/pyautogui/_pyautogui_wayland.py index 8362c3e..e50f611 100644 --- a/src/depends/pyautogui/_pyautogui_wayland.py +++ b/src/depends/pyautogui/_pyautogui_wayland.py @@ -18,7 +18,6 @@ if sys.version_info[0] == 2 or sys.version_info[0:2] in ((3, 1), (3, 7)): # the mouse. MINIMUM_DURATION = 0.1 # If sleep_amount is less than MINIMUM_DURATION, sleep() will be a no-op and the mouse cursor moves there instantly. -# TODO: This value should vary with the platform. http://stackoverflow.com/q/1133857 MINIMUM_SLEEP = 0.05 STEP_SLEEP = 10 @@ -377,7 +376,7 @@ def size(): return Size(posx, posy) -def moveTo(x=None, y=None, duration=0.0, tween=linear, logScreenshot=False, _pause=True): +def moveTo(x=None, y=None, duration=0.0, tween=linear, _pause=True): startx, starty = position() x = int(x) if x is not None else startx @@ -399,13 +398,10 @@ def moveTo(x=None, y=None, duration=0.0, tween=linear, logScreenshot=False, _pau if sleep_amount < MINIMUM_SLEEP: num_steps = int(duration / MINIMUM_SLEEP) sleep_amount = duration / num_steps - # print(str(startx) + "," + str(starty)) - # print(str(x) + "," + str(y)) steps = [getPointOnLine(startx, starty, x, y, tween(n / num_steps)) for n in range(num_steps)] # Making sure the last position is the actual destination. # print(steps) steps.append((x, y)) - # print(steps) for tweenX, tweenY in steps: if len(steps) > 1: # A single step does not require tweening. @@ -417,30 +413,49 @@ def moveTo(x=None, y=None, duration=0.0, tween=linear, logScreenshot=False, _pau f"{dbus_cmd}.moveTo int32:{str(tweenX)} int32:{str(tweenY)}" ) +def moveRel( + xOffset=0, yOffset=0, duration=0.0, tween=linear, _pause=True, +): + if xOffset is None: + xOffset = 0 + if yOffset is None: + yOffset = 0 -def mouseDown(x=None, y=None, button=PRIMARY, duration=0.0, tween=linear, logScreenshot=None, _pause=True): + if type(xOffset) in (tuple, list): + xOffset, yOffset = xOffset[0], xOffset[1] + + if xOffset == 0 and yOffset == 0: + return # no-op case + + mousex, mousey = position() + mousex = mousex + xOffset + mousey = mousey + yOffset + moveTo(mousex, mousey, duration, tween) + +move = moveRel + +def mouseDown(x=None, y=None, button=PRIMARY, duration=0.0, tween=linear, _pause=True): x, y = _normalizeXYArgs(x, y) - moveTo(x, y, duration, tween, logScreenshot, _pause) + moveTo(x, y, duration, tween, _pause) system( f"{dbus_cmd}.mouseDown string:{button}" ) sleep(0.3) -def mouseUp(x=None, y=None, button=PRIMARY, duration=0.0, tween=linear, logScreenshot=None, _pause=True): +def mouseUp(x=None, y=None, button=PRIMARY, duration=0.0, tween=linear, _pause=True): x, y = _normalizeXYArgs(x, y) - moveTo(x, y, duration, tween, logScreenshot, _pause) + moveTo(x, y, duration, tween, _pause) system( f"{dbus_cmd}.mouseUp string:{button}" ) def click( - x=None, y=None, clicks=1, interval=0.0, button=PRIMARY, duration=0.0, tween=linear, logScreenshot=None, - _pause=True + x=None, y=None, clicks=1, interval=0.0, button=PRIMARY, duration=0.0, tween=linear, _pause=True ): x, y = _normalizeXYArgs(x, y) - moveTo(x, y, duration, tween, logScreenshot, _pause) + moveTo(x, y, duration, tween, _pause) for i in range(clicks): if button in (LEFT, MIDDLE, RIGHT): system( @@ -449,35 +464,35 @@ def click( sleep(interval) -def leftClick(x=None, y=None, interval=0.0, duration=0.0, tween=linear, logScreenshot=None, _pause=True): - click(x, y, 1, interval, LEFT, duration, tween, logScreenshot, _pause=_pause) +def leftClick(x=None, y=None, interval=0.0, duration=0.0, tween=linear, _pause=True): + click(x, y, 1, interval, LEFT, duration, tween, _pause=_pause) -def rightClick(x=None, y=None, interval=0.0, duration=0.0, tween=linear, logScreenshot=None, _pause=True): - click(x, y, 1, interval, RIGHT, duration, tween, logScreenshot, _pause=_pause) +def rightClick(x=None, y=None, interval=0.0, duration=0.0, tween=linear, _pause=True): + click(x, y, 1, interval, RIGHT, duration, tween, _pause=_pause) -def middleClick(x=None, y=None, interval=0.0, duration=0.0, tween=linear, logScreenshot=None, _pause=True): - click(x, y, 1, interval, MIDDLE, duration, tween, logScreenshot, _pause=_pause) +def middleClick(x=None, y=None, interval=0.0, duration=0.0, tween=linear, _pause=True): + click(x, y, 1, interval, MIDDLE, duration, tween, _pause=_pause) -def doubleClick(x=None, y=None, interval=0.0, button=LEFT, duration=0.0, tween=linear, logScreenshot=None, _pause=True): - click(x, y, 2, interval, button, duration, tween, logScreenshot, _pause=False) +def doubleClick(x=None, y=None, interval=0.0, button=LEFT, duration=0.0, tween=linear, _pause=True): + click(x, y, 2, interval, button, duration, tween, _pause=False) -def tripleClick(x=None, y=None, interval=0.0, button=LEFT, duration=0.0, tween=linear, logScreenshot=None, _pause=True): - click(x, y, 3, interval, button, duration, tween, logScreenshot, _pause=False) +def tripleClick(x=None, y=None, interval=0.0, button=LEFT, duration=0.0, tween=linear, _pause=True): + click(x, y, 3, interval, button, duration, tween, _pause=False) -def scroll(clicks, x=None, y=None, logScreenshot=None, _pause=True): +def scroll(clicks, x=None, y=None, _pause=True): if type(x) in (tuple, list): x, y = x[0], x[1] x, y = position(x, y) moveTo(x, y) - vscroll(clicks, x, y, logScreenshot, _pause) + vscroll(clicks, x, y, _pause) -def hscroll(clicks, x=None, y=None, logScreenshot=None, _pause=True): +def hscroll(clicks, x=None, y=None, _pause=True): if type(x) in (tuple, list): x, y = x[0], x[1] x, y = position(x, y) @@ -490,7 +505,7 @@ def hscroll(clicks, x=None, y=None, logScreenshot=None, _pause=True): sleep(0.05) -def vscroll(clicks, x=None, y=None, logScreenshot=None, _pause=True): +def vscroll(clicks, x=None, y=None, _pause=True): if type(x) in (tuple, list): x, y = x[0], x[1] x, y = position(x, y) @@ -504,18 +519,18 @@ def vscroll(clicks, x=None, y=None, logScreenshot=None, _pause=True): def dragTo( - x=None, y=None, duration=0.0, tween=linear, button=PRIMARY, logScreenshot=None, _pause=True, mouseDownUp=True + x=None, y=None, duration=0.0, tween=linear, button=PRIMARY, _pause=True, mouseDownUp=True ): x, y = _normalizeXYArgs(x, y) if mouseDownUp: - mouseDown(button=button, logScreenshot=False, _pause=False) + mouseDown(button=button, _pause=False) moveTo(x, y, duration, tween) if mouseDownUp: - mouseUp(button=button, logScreenshot=False, _pause=False) + mouseUp(button=button, _pause=False) def dragRel( - xOffset=0, yOffset=0, duration=0.0, tween=linear, button=PRIMARY, logScreenshot=None, _pause=True, + xOffset=0, yOffset=0, duration=0.0, tween=linear, button=PRIMARY, _pause=True, mouseDownUp=True ): if xOffset is None: @@ -531,15 +546,15 @@ def dragRel( mousex, mousey = position() mousex = mousex + xOffset - mousey = mousey + mousey + mousey = mousey + yOffset if mouseDownUp: - mouseDown(button=button, logScreenshot=False, _pause=False) + mouseDown(button=button, _pause=False) moveTo(mousex, mousey, duration, tween) if mouseDownUp: - mouseUp(button=button, logScreenshot=False, _pause=False) + mouseUp(button=button, _pause=False) -def keyDown(key, logScreenshot=None, _pause=True): +def keyDown(key, _pause=True): global bshift try: if len(key) > 1: @@ -555,7 +570,7 @@ def keyDown(key, logScreenshot=None, _pause=True): pass -def keyUp(key, logScreenshot=None, _pause=True): +def keyUp(key, _pause=True): global bshift try: if len(key) > 1: @@ -573,7 +588,7 @@ def keyUp(key, logScreenshot=None, _pause=True): pass -def press(keys, presses=1, interval=0.0, logScreenshot=None, _pause=True): +def press(keys, presses=1, interval=0.0, _pause=True): if type(keys) == str: if len(keys) > 1: keys = keys.lower() @@ -594,7 +609,7 @@ def press(keys, presses=1, interval=0.0, logScreenshot=None, _pause=True): sleep(interval) -def typewrite(message, interval=0.0, logScreenshot=None, _pause=True): +def typewrite(message, interval=0.0, _pause=True): interval = float(interval) # TODO - this should be taken out. for c in message: @@ -645,5 +660,7 @@ def screenshot(): if image_path != "": image = Image.open(image_path) return image - else: - return None + return None + +if __name__ == '__main__': + moveRel(10, 10) \ No newline at end of file diff --git a/src/mouse_key.py b/src/mouse_key.py index 3b2c820..620e0c9 100644 --- a/src/mouse_key.py +++ b/src/mouse_key.py @@ -359,6 +359,17 @@ class MouseKey: cls.press_key_up(c) sleep(0.03) + @classmethod + def move_to_and_click(cls, _x, _y): + """ + 移动到某个位置点击 + :param _x: 移动到的位置 x + :param _y: 移动到的位置 y + :return: + """ + cls.move_to(_x, _y) + cls.click() + @classmethod def move_to_and_right_click(cls, _x, _y): """ @@ -371,15 +382,15 @@ class MouseKey: cls.right_click() @classmethod - def move_to_and_click(cls, _x, _y): + def move_to_and_double_click(cls, _x, _y): """ - 移动到某个位置点击左键 - :param _x: 移动到的位置x - :param _y: 移动到的位置y + 移动到某个位置点击双击 + :param _x: 移动到的位置 x + :param _y: 移动到的位置 y :return: """ - cls.move_to(int(_x), int(_y)) - cls.click() + cls.move_to(_x, _y) + cls.double_click() @classmethod def move_on_and_drag_to(cls, start: tuple, end: tuple): diff --git a/src/plugins/allure_report_extend.py b/src/plugins/allure_report_extend.py index 8e81eed..df41e70 100644 --- a/src/plugins/allure_report_extend.py +++ b/src/plugins/allure_report_extend.py @@ -11,6 +11,10 @@ from setting.globalconfig import GlobalConfig from src.dbus_utils import DbusUtils +def wf(f, t): + f.write(t.encode("unicode_escape").decode() + "\n") + + class AllureReportExtend: """AllureReportExtend""" @@ -23,32 +27,39 @@ class AllureReportExtend: return if not allure_path: return - allure_fspath_path = os.path.join(session.config.invocation_dir, allure_path) - with open( - allure_fspath_path + "/environment.properties", "w+", encoding="utf-8" - ) as _f: - _f.write( - f"网络地址={GlobalConfig.HOST_IP}".encode("unicode_escape").decode() + "\n" - ) - _f.write( - f"系统信息={GlobalConfig.PRODUCT_INFO}".encode("unicode_escape").decode() - + "\n" - ) - _f.write( - f"镜像版本={GlobalConfig.VERSION}".encode("unicode_escape").decode() + "\n" - ) + allure_fspath_path = os.path.join( + session.config.invocation_dir, + allure_path, + "environment.properties" + ) + with open(allure_fspath_path, "w+", encoding="utf-8") as _f: + wf(_f, f"网络地址={GlobalConfig.USERNAME}@{GlobalConfig.HOST_IP}") + wf(_f, f"系统信息={GlobalConfig.PRODUCT_INFO}") + wf(_f, f"镜像版本={GlobalConfig.VERSION}") + screen = Tk() - x = screen.winfo_screenwidth() - y = screen.winfo_screenheight() - _f.write(f"分辨率={x}x{y}".encode("unicode_escape").decode() + "\n") - language_code = DbusUtils( - "com.deepin.daemon.LangSelector", - "/com/deepin/daemon/LangSelector", - "com.deepin.daemon.LangSelector", - ).get_session_properties_value("CurrentLocale") - _f.write( - # pylint: disable=line-too-long - f"系统语言={GlobalConfig.LANGUAGE_INI.get(language_code, default=language_code)}".encode( - "unicode_escape" - ).decode() - ) + wf(_f, f"分辨率={screen.winfo_screenwidth()}x{screen.winfo_screenheight()}") + + try: + language_code = DbusUtils( + "com.deepin.daemon.LangSelector", + "/com/deepin/daemon/LangSelector", + "com.deepin.daemon.LangSelector", + ).get_session_properties_value("CurrentLocale") + wf(_f, f"系统语言={GlobalConfig.LANGUAGE_INI.get(language_code, default=language_code)}") + except Exception: + pass + + _display = GlobalConfig.DisplayServer.wayland if GlobalConfig.IS_WAYLAND else GlobalConfig.DisplayServer.x11 + wf(_f, f"显示协议={_display.title()}") + + os_info = os.popen("uname -a").read() + wf(_f, f"内核信息={os_info}") + + cpu_info = os.popen("cat /proc/cpuinfo | grep name | cut -f2 -d: | uniq -c").read().replace(" ", "") + if not cpu_info: + cpu_info = os.popen("cat /proc/cpuinfo | grep Hardware").read() + wf(_f, f"CPU信息={cpu_info}") + + mem_info = os.popen("cat /proc/meminfo | grep MemTotal").read() + wf(_f, f"内存信息={mem_info}") diff --git a/src/rtk/_base.py b/src/rtk/_base.py index 0b13893..17b467c 100644 --- a/src/rtk/_base.py +++ b/src/rtk/_base.py @@ -61,6 +61,7 @@ class Args(Enum): build_env = "build_env" client_password = "client_password" parallel = "parallel" + autostart = "autostart" def transform_app_name(real_app_name): diff --git a/src/rtk/local_runner.py b/src/rtk/local_runner.py index 4cb2dce..7ae31b0 100644 --- a/src/rtk/local_runner.py +++ b/src/rtk/local_runner.py @@ -22,14 +22,20 @@ from tkinter import Tk import pytest from allure_custom import AllureCustom -from allure_custom.conf import setting +from allure_custom.conf import setting as al_setting from setting.globalconfig import GetCfg from setting.globalconfig import GlobalConfig -setting.html_title = GlobalConfig.REPORT_TITLE -setting.report_name = GlobalConfig.REPORT_NAME -setting.report_language = GlobalConfig.REPORT_LANGUAGE +al_setting.html_title = GlobalConfig.REPORT_TITLE +al_setting.report_name = GlobalConfig.REPORT_NAME +al_setting.report_language = GlobalConfig.REPORT_LANGUAGE + +from letmego import register_autostart_service +from letmego.conf import setting as letmego_setting + +letmego_setting.RUNNING_MAN_FILE = f"{GlobalConfig.REPORT_PATH}/_running_man.txt" +letmego_setting.PROJECT_NAME = GlobalConfig.PASSWORD from src import logger from src.rtk._base import Args @@ -77,6 +83,7 @@ class LocalRunner: build_location=None, line=None, exportcsv=None, + autostart=None, **kwargs, ): logger("INFO") @@ -112,6 +119,7 @@ class LocalRunner: if not pms_info_file else None, Args.pms_info_file.value: pms_info_file, + Args.autostart.value: autostart or GlobalConfig.AUTOSTART, } self.lastfailed = lastfailed self.project_name = project_name @@ -258,6 +266,8 @@ class LocalRunner: cmd.append("--duringfail") if default.get(Args.top.value): cmd.extend(["--top", default.get(Args.top.value)]) + if default.get(Args.autostart.value): + cmd.extend(["--autostart", default.get(Args.autostart.value)]) if default.get(Args.repeat.value): cmd.extend(["--repeat", default.get(Args.repeat.value)]) if self.line: @@ -335,6 +345,14 @@ class LocalRunner: system(f"mv {allure_report_path}/* {allure_report_back_path}/") run_test_cmd = " ".join(run_test_cmd_list) + + if self.default.get(Args.autostart.value): + register_autostart_service( + user=GlobalConfig.USERNAME, + working_directory=GlobalConfig.ROOT_DIR, + cmd=run_test_cmd + ) + if not self.default.get(Args.pms_info_file.value): print(f"Running: \n{run_test_cmd}") if self.default.get(Args.debug.value): diff --git a/src/startproject.py b/src/startproject.py index 7e4e339..aa6e201 100644 --- a/src/startproject.py +++ b/src/startproject.py @@ -38,7 +38,7 @@ def cli(): youqu_version = conf.get("current", "tag") except NoSectionError: youqu_version = None - print(f"The project: [\033[0;32m{project_name}\033[0m],has been created by youqu{f'-{youqu_version}' if youqu_version else ''}.") + print(f"The project: [\033[0;32m{project_name}\033[0m],has been created by youqu{f'-{youqu_version}' if youqu_version else ''}") if __name__ == '__main__': diff --git a/src/wayland_wininfo.py b/src/wayland_wininfo.py index cbb98ad..5317bff 100644 --- a/src/wayland_wininfo.py +++ b/src/wayland_wininfo.py @@ -72,7 +72,3 @@ class WaylandWindowINfo: window_info.height ), } - - -if __name__ == '__main__': - WaylandWindowINfo().window_info()