From 67488a19191c18f53bb6251848c93ef42111e0a4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 8 Dec 2022 14:06:57 +0000 Subject: [PATCH 01/52] build(deps): bump certifi from 2021.10.8 to 2022.12.7 Bumps [certifi](https://github.com/certifi/python-certifi) from 2021.10.8 to 2022.12.7. - [Release notes](https://github.com/certifi/python-certifi/releases) - [Commits](https://github.com/certifi/python-certifi/compare/2021.10.08...2022.12.07) --- updated-dependencies: - dependency-name: certifi dependency-type: indirect ... Signed-off-by: dependabot[bot] --- poetry.lock | 115 ++++++++++++++++++++++++++++++++++------------------ 1 file changed, 75 insertions(+), 40 deletions(-) diff --git a/poetry.lock b/poetry.lock index bf7305fb..ee1c4dd6 100644 --- a/poetry.lock +++ b/poetry.lock @@ -56,10 +56,10 @@ optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [package.extras] -dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit", "cloudpickle"] -docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"] -tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "cloudpickle"] -tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "cloudpickle"] +dev = ["cloudpickle", "coverage[toml] (>=5.0.2)", "furo", "hypothesis", "mypy", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "six", "sphinx", "sphinx-notfound-page", "zope.interface"] +docs = ["furo", "sphinx", "sphinx-notfound-page", "zope.interface"] +tests = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "six", "zope.interface"] +tests-no-zope = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "six"] [package.source] type = "legacy" @@ -109,11 +109,11 @@ reference = "tsinghua" [[package]] name = "certifi" -version = "2021.10.8" +version = "2022.12.7" description = "Python package for providing Mozilla's CA Bundle." category = "main" optional = false -python-versions = "*" +python-versions = ">=3.6" [package.source] type = "legacy" @@ -129,7 +129,7 @@ optional = false python-versions = ">=3.5.0" [package.extras] -unicode_backport = ["unicodedata2"] +unicode-backport = ["unicodedata2"] [package.source] type = "legacy" @@ -214,7 +214,7 @@ optional = true python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*" [package.extras] -docs = ["sphinx"] +docs = ["Sphinx"] [package.source] type = "legacy" @@ -247,9 +247,9 @@ typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""} zipp = ">=0.5" [package.extras] -docs = ["sphinx", "jaraco.packaging (>=9)", "rst.linker (>=1.9)"] +docs = ["jaraco.packaging (>=9)", "rst.linker (>=1.9)", "sphinx"] perf = ["ipython"] -testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "packaging", "pyfakefs", "flufl.flake8", "pytest-perf (>=0.9.2)", "pytest-black (>=0.3.7)", "pytest-mypy (>=0.9.1)", "importlib-resources (>=1.3)"] +testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.0.1)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)"] [package.source] type = "legacy" @@ -314,7 +314,7 @@ colorama = {version = ">=0.3.4", markers = "sys_platform == \"win32\""} win32-setctime = {version = ">=1.0.0", markers = "sys_platform == \"win32\""} [package.extras] -dev = ["codecov (>=2.0.15)", "colorama (>=0.3.4)", "flake8 (>=3.7.7)", "isort (>=4.3.20)", "tox (>=3.9.0)", "tox-travis (>=0.12)", "pytest (>=4.6.2)", "pytest-cov (>=2.7.1)", "Sphinx (>=2.2.1)", "sphinx-autobuild (>=0.7.1)", "sphinx-rtd-theme (>=0.4.3)", "black (>=19.3b0)"] +dev = ["Sphinx (>=2.2.1)", "black (>=19.3b0)", "codecov (>=2.0.15)", "colorama (>=0.3.4)", "flake8 (>=3.7.7)", "isort (>=4.3.20)", "pytest (>=4.6.2)", "pytest-cov (>=2.7.1)", "sphinx-autobuild (>=0.7.1)", "sphinx-rtd-theme (>=0.4.3)", "tox (>=3.9.0)", "tox-travis (>=0.12)"] [package.source] type = "legacy" @@ -385,8 +385,8 @@ optional = false python-versions = ">=3.7" [package.extras] -docs = ["furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx-autodoc-typehints (>=1.12)", "sphinx (>=4)"] -test = ["appdirs (==1.4.4)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)", "pytest (>=6)"] +docs = ["furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx (>=4)", "sphinx-autodoc-typehints (>=1.12)"] +test = ["appdirs (==1.4.4)", "pytest (>=6)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)"] [package.source] type = "legacy" @@ -485,7 +485,7 @@ optional = false python-versions = ">=3.6.8" [package.extras] -diagrams = ["railroad-diagrams", "jinja2"] +diagrams = ["jinja2", "railroad-diagrams"] [package.source] type = "legacy" @@ -581,7 +581,7 @@ urllib3 = ">=1.21.1,<1.27" [package.extras] socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"] -use_chardet_on_py3 = ["chardet (>=3.0.2,<5)"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<5)"] [package.source] type = "legacy" @@ -623,7 +623,7 @@ bottle = ["bottle (>=0.12.13)"] celery = ["celery (>=3)"] django = ["django (>=1.8)"] falcon = ["falcon (>=1.4)"] -flask = ["flask (>=0.11)", "blinker (>=1.1)"] +flask = ["blinker (>=1.1)", "flask (>=0.11)"] pyspark = ["pyspark (>=2.4.4)"] rq = ["rq (>=0.6)"] sanic = ["sanic (>=0.8)"] @@ -661,25 +661,25 @@ greenlet = {version = "!=0.4.17", markers = "python_version >= \"3\" and (platfo importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} [package.extras] -aiomysql = ["greenlet (!=0.4.17)", "aiomysql"] -aiosqlite = ["typing_extensions (!=3.10.0.1)", "greenlet (!=0.4.17)", "aiosqlite"] +aiomysql = ["aiomysql", "greenlet (!=0.4.17)"] +aiosqlite = ["aiosqlite", "greenlet (!=0.4.17)", "typing_extensions (!=3.10.0.1)"] asyncio = ["greenlet (!=0.4.17)"] -asyncmy = ["greenlet (!=0.4.17)", "asyncmy (>=0.2.3,!=0.2.4)"] -mariadb_connector = ["mariadb (>=1.0.1)"] +asyncmy = ["asyncmy (>=0.2.3,!=0.2.4)", "greenlet (!=0.4.17)"] +mariadb-connector = ["mariadb (>=1.0.1)"] mssql = ["pyodbc"] -mssql_pymssql = ["pymssql"] -mssql_pyodbc = ["pyodbc"] -mypy = ["sqlalchemy2-stubs", "mypy (>=0.910)"] -mysql = ["mysqlclient (>=1.4.0,<2)", "mysqlclient (>=1.4.0)"] -mysql_connector = ["mysql-connector-python"] -oracle = ["cx_oracle (>=7,<8)", "cx_oracle (>=7)"] +mssql-pymssql = ["pymssql"] +mssql-pyodbc = ["pyodbc"] +mypy = ["mypy (>=0.910)", "sqlalchemy2-stubs"] +mysql = ["mysqlclient (>=1.4.0)", "mysqlclient (>=1.4.0,<2)"] +mysql-connector = ["mysql-connector-python"] +oracle = ["cx_oracle (>=7)", "cx_oracle (>=7,<8)"] postgresql = ["psycopg2 (>=2.7)"] -postgresql_asyncpg = ["greenlet (!=0.4.17)", "asyncpg"] -postgresql_pg8000 = ["pg8000 (>=1.16.6)"] -postgresql_psycopg2binary = ["psycopg2-binary"] -postgresql_psycopg2cffi = ["psycopg2cffi"] -pymysql = ["pymysql (<1)", "pymysql"] -sqlcipher = ["sqlcipher3-binary"] +postgresql-asyncpg = ["asyncpg", "greenlet (!=0.4.17)"] +postgresql-pg8000 = ["pg8000 (>=1.16.6)"] +postgresql-psycopg2binary = ["psycopg2-binary"] +postgresql-psycopg2cffi = ["psycopg2cffi"] +pymysql = ["pymysql", "pymysql (<1)"] +sqlcipher = ["sqlcipher3_binary"] [package.source] type = "legacy" @@ -719,7 +719,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" ply = ">=3.4,<4.0" [package.extras] -dev = ["cython (>=0.28.4)", "flake8 (>=2.5)", "pytest (>=2.8)", "sphinx-rtd-theme (>=0.1.9)", "sphinx (>=1.3)", "tornado (>=4.0,<6.0)"] +dev = ["cython (>=0.28.4)", "flake8 (>=2.5)", "pytest (>=2.8)", "sphinx (>=1.3)", "sphinx-rtd-theme (>=0.1.9)", "tornado (>=4.0,<6.0)"] tornado = ["tornado (>=4.0,<6.0)"] [package.source] @@ -788,8 +788,8 @@ optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" [package.extras] -brotli = ["brotlicffi (>=0.8.0)", "brotli (>=1.0.9)", "brotlipy (>=0.6.0)"] -secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"] +secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)"] socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] [package.source] @@ -806,7 +806,7 @@ optional = false python-versions = ">=3.5" [package.extras] -dev = ["pytest (>=4.6.2)", "black (>=19.3b0)"] +dev = ["black (>=19.3b0)", "pytest (>=4.6.2)"] [package.source] type = "legacy" @@ -822,8 +822,8 @@ optional = false python-versions = ">=3.7" [package.extras] -docs = ["sphinx", "jaraco.packaging (>=9)", "rst.linker (>=1.9)"] -testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy (>=0.9.1)"] +docs = ["jaraco.packaging (>=9)", "rst.linker (>=1.9)", "sphinx"] +testing = ["func-timeout", "jaraco.itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.0.1)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] [package.source] type = "legacy" @@ -895,8 +895,21 @@ brotli = [ {file = "Brotli-1.0.9-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ee83d3e3a024a9618e5be64648d6d11c37047ac48adff25f12fa4226cf23d1c"}, {file = "Brotli-1.0.9-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:19598ecddd8a212aedb1ffa15763dd52a388518c4550e615aed88dc3753c0f0c"}, {file = "Brotli-1.0.9-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:44bb8ff420c1d19d91d79d8c3574b8954288bdff0273bf788954064d260d7ab0"}, + {file = "Brotli-1.0.9-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e23281b9a08ec338469268f98f194658abfb13658ee98e2b7f85ee9dd06caa91"}, + {file = "Brotli-1.0.9-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:3496fc835370da351d37cada4cf744039616a6db7d13c430035e901443a34daa"}, + {file = "Brotli-1.0.9-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b83bb06a0192cccf1eb8d0a28672a1b79c74c3a8a5f2619625aeb6f28b3a82bb"}, {file = "Brotli-1.0.9-cp310-cp310-win32.whl", hash = "sha256:26d168aac4aaec9a4394221240e8a5436b5634adc3cd1cdf637f6645cecbf181"}, {file = "Brotli-1.0.9-cp310-cp310-win_amd64.whl", hash = "sha256:622a231b08899c864eb87e85f81c75e7b9ce05b001e59bbfbf43d4a71f5f32b2"}, + {file = "Brotli-1.0.9-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:cc0283a406774f465fb45ec7efb66857c09ffefbe49ec20b7882eff6d3c86d3a"}, + {file = "Brotli-1.0.9-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:11d3283d89af7033236fa4e73ec2cbe743d4f6a81d41bd234f24bf63dde979df"}, + {file = "Brotli-1.0.9-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c1306004d49b84bd0c4f90457c6f57ad109f5cc6067a9664e12b7b79a9948ad"}, + {file = "Brotli-1.0.9-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1375b5d17d6145c798661b67e4ae9d5496920d9265e2f00f1c2c0b5ae91fbde"}, + {file = "Brotli-1.0.9-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cab1b5964b39607a66adbba01f1c12df2e55ac36c81ec6ed44f2fca44178bf1a"}, + {file = "Brotli-1.0.9-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:8ed6a5b3d23ecc00ea02e1ed8e0ff9a08f4fc87a1f58a2530e71c0f48adf882f"}, + {file = "Brotli-1.0.9-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:cb02ed34557afde2d2da68194d12f5719ee96cfb2eacc886352cb73e3808fc5d"}, + {file = "Brotli-1.0.9-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:b3523f51818e8f16599613edddb1ff924eeb4b53ab7e7197f85cbc321cdca32f"}, + {file = "Brotli-1.0.9-cp311-cp311-win32.whl", hash = "sha256:ba72d37e2a924717990f4d7482e8ac88e2ef43fb95491eb6e0d124d77d2a150d"}, + {file = "Brotli-1.0.9-cp311-cp311-win_amd64.whl", hash = "sha256:3ffaadcaeafe9d30a7e4e1e97ad727e4f5610b9fa2f7551998471e3736738679"}, {file = "Brotli-1.0.9-cp35-cp35m-macosx_10_6_intel.whl", hash = "sha256:c83aa123d56f2e060644427a882a36b3c12db93727ad7a7b9efd7d7f3e9cc2c4"}, {file = "Brotli-1.0.9-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:6b2ae9f5f67f89aade1fab0f7fd8f2832501311c363a21579d02defa844d9296"}, {file = "Brotli-1.0.9-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:68715970f16b6e92c574c30747c95cf8cf62804569647386ff032195dc89a430"}, @@ -906,12 +919,18 @@ brotli = [ {file = "Brotli-1.0.9-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:40d15c79f42e0a2c72892bf407979febd9cf91f36f495ffb333d1d04cebb34e4"}, {file = "Brotli-1.0.9-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:93130612b837103e15ac3f9cbacb4613f9e348b58b3aad53721d92e57f96d46a"}, {file = "Brotli-1.0.9-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:87fdccbb6bb589095f413b1e05734ba492c962b4a45a13ff3408fa44ffe6479b"}, + {file = "Brotli-1.0.9-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:6d847b14f7ea89f6ad3c9e3901d1bc4835f6b390a9c71df999b0162d9bb1e20f"}, + {file = "Brotli-1.0.9-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:495ba7e49c2db22b046a53b469bbecea802efce200dffb69b93dd47397edc9b6"}, + {file = "Brotli-1.0.9-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:4688c1e42968ba52e57d8670ad2306fe92e0169c6f3af0089be75bbac0c64a3b"}, {file = "Brotli-1.0.9-cp36-cp36m-win32.whl", hash = "sha256:61a7ee1f13ab913897dac7da44a73c6d44d48a4adff42a5701e3239791c96e14"}, {file = "Brotli-1.0.9-cp36-cp36m-win_amd64.whl", hash = "sha256:1c48472a6ba3b113452355b9af0a60da5c2ae60477f8feda8346f8fd48e3e87c"}, {file = "Brotli-1.0.9-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:3b78a24b5fd13c03ee2b7b86290ed20efdc95da75a3557cc06811764d5ad1126"}, {file = "Brotli-1.0.9-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:9d12cf2851759b8de8ca5fde36a59c08210a97ffca0eb94c532ce7b17c6a3d1d"}, {file = "Brotli-1.0.9-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:6c772d6c0a79ac0f414a9f8947cc407e119b8598de7621f39cacadae3cf57d12"}, {file = "Brotli-1.0.9-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29d1d350178e5225397e28ea1b7aca3648fcbab546d20e7475805437bfb0a130"}, + {file = "Brotli-1.0.9-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7bbff90b63328013e1e8cb50650ae0b9bac54ffb4be6104378490193cd60f85a"}, + {file = "Brotli-1.0.9-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:ec1947eabbaf8e0531e8e899fc1d9876c179fc518989461f5d24e2223395a9e3"}, + {file = "Brotli-1.0.9-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:12effe280b8ebfd389022aa65114e30407540ccb89b177d3fbc9a4f177c4bd5d"}, {file = "Brotli-1.0.9-cp37-cp37m-win32.whl", hash = "sha256:f909bbbc433048b499cb9db9e713b5d8d949e8c109a2a548502fb9aa8630f0b1"}, {file = "Brotli-1.0.9-cp37-cp37m-win_amd64.whl", hash = "sha256:97f715cf371b16ac88b8c19da00029804e20e25f30d80203417255d239f228b5"}, {file = "Brotli-1.0.9-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e16eb9541f3dd1a3e92b89005e37b1257b157b7256df0e36bd7b33b50be73bcb"}, @@ -919,6 +938,9 @@ brotli = [ {file = "Brotli-1.0.9-cp38-cp38-manylinux1_i686.whl", hash = "sha256:b663f1e02de5d0573610756398e44c130add0eb9a3fc912a09665332942a2efb"}, {file = "Brotli-1.0.9-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:5b6ef7d9f9c38292df3690fe3e302b5b530999fa90014853dcd0d6902fb59f26"}, {file = "Brotli-1.0.9-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8a674ac10e0a87b683f4fa2b6fa41090edfd686a6524bd8dedbd6138b309175c"}, + {file = "Brotli-1.0.9-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:e2d9e1cbc1b25e22000328702b014227737756f4b5bf5c485ac1d8091ada078b"}, + {file = "Brotli-1.0.9-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:b336c5e9cf03c7be40c47b5fd694c43c9f1358a80ba384a21969e0b4e66a9b17"}, + {file = "Brotli-1.0.9-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:85f7912459c67eaab2fb854ed2bc1cc25772b300545fe7ed2dc03954da638649"}, {file = "Brotli-1.0.9-cp38-cp38-win32.whl", hash = "sha256:35a3edbe18e876e596553c4007a087f8bcfd538f19bc116917b3c7522fca0429"}, {file = "Brotli-1.0.9-cp38-cp38-win_amd64.whl", hash = "sha256:269a5743a393c65db46a7bb982644c67ecba4b8d91b392403ad8a861ba6f495f"}, {file = "Brotli-1.0.9-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:2aad0e0baa04517741c9bb5b07586c642302e5fb3e75319cb62087bd0995ab19"}, @@ -926,15 +948,28 @@ brotli = [ {file = "Brotli-1.0.9-cp39-cp39-manylinux1_i686.whl", hash = "sha256:16d528a45c2e1909c2798f27f7bf0a3feec1dc9e50948e738b961618e38b6a7b"}, {file = "Brotli-1.0.9-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:56d027eace784738457437df7331965473f2c0da2c70e1a1f6fdbae5402e0389"}, {file = "Brotli-1.0.9-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9bf919756d25e4114ace16a8ce91eb340eb57a08e2c6950c3cebcbe3dff2a5e7"}, + {file = "Brotli-1.0.9-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:e4c4e92c14a57c9bd4cb4be678c25369bf7a092d55fd0866f759e425b9660806"}, + {file = "Brotli-1.0.9-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:e48f4234f2469ed012a98f4b7874e7f7e173c167bed4934912a29e03167cf6b1"}, + {file = "Brotli-1.0.9-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9ed4c92a0665002ff8ea852353aeb60d9141eb04109e88928026d3c8a9e5433c"}, {file = "Brotli-1.0.9-cp39-cp39-win32.whl", hash = "sha256:cfc391f4429ee0a9370aa93d812a52e1fee0f37a81861f4fdd1f4fb28e8547c3"}, {file = "Brotli-1.0.9-cp39-cp39-win_amd64.whl", hash = "sha256:854c33dad5ba0fbd6ab69185fec8dab89e13cda6b7d191ba111987df74f38761"}, {file = "Brotli-1.0.9-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:9749a124280a0ada4187a6cfd1ffd35c350fb3af79c706589d98e088c5044267"}, + {file = "Brotli-1.0.9-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:73fd30d4ce0ea48010564ccee1a26bfe39323fde05cb34b5863455629db61dc7"}, + {file = "Brotli-1.0.9-pp37-pypy37_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:02177603aaca36e1fd21b091cb742bb3b305a569e2402f1ca38af471777fb019"}, {file = "Brotli-1.0.9-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:76ffebb907bec09ff511bb3acc077695e2c32bc2142819491579a695f77ffd4d"}, + {file = "Brotli-1.0.9-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:b43775532a5904bc938f9c15b77c613cb6ad6fb30990f3b0afaea82797a402d8"}, + {file = "Brotli-1.0.9-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:5bf37a08493232fbb0f8229f1824b366c2fc1d02d64e7e918af40acd15f3e337"}, + {file = "Brotli-1.0.9-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:330e3f10cd01da535c70d09c4283ba2df5fb78e915bea0a28becad6e2ac010be"}, + {file = "Brotli-1.0.9-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:e1abbeef02962596548382e393f56e4c94acd286bd0c5afba756cffc33670e8a"}, + {file = "Brotli-1.0.9-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:3148362937217b7072cf80a2dcc007f09bb5ecb96dae4617316638194113d5be"}, + {file = "Brotli-1.0.9-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:336b40348269f9b91268378de5ff44dc6fbaa2268194f85177b53463d313842a"}, + {file = "Brotli-1.0.9-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3b8b09a16a1950b9ef495a0f8b9d0a87599a9d1f179e2d4ac014b2ec831f87e7"}, + {file = "Brotli-1.0.9-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:c8e521a0ce7cf690ca84b8cc2272ddaf9d8a50294fd086da67e517439614c755"}, {file = "Brotli-1.0.9.zip", hash = "sha256:4d1b810aa0ed773f81dceda2cc7b403d01057458730e309856356d4ef4188438"}, ] certifi = [ - {file = "certifi-2021.10.8-py2.py3-none-any.whl", hash = "sha256:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569"}, - {file = "certifi-2021.10.8.tar.gz", hash = "sha256:78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872"}, + {file = "certifi-2022.12.7-py3-none-any.whl", hash = "sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18"}, + {file = "certifi-2022.12.7.tar.gz", hash = "sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3"}, ] charset-normalizer = [ {file = "charset-normalizer-2.0.12.tar.gz", hash = "sha256:2857e29ff0d34db842cd7ca3230549d1a697f96ee6d3fb071cfa6c7393832597"}, From 2b120d257161f8d73521b472d6c3773db1e9e6f4 Mon Sep 17 00:00:00 2001 From: cheetah <185541429@qq.com> Date: Mon, 9 Jan 2023 23:48:52 +0800 Subject: [PATCH 02/52] =?UTF-8?q?setup=20hooks=20=E5=85=BC=E5=AE=B9v3?= =?UTF-8?q?=E7=89=88=E6=9C=AC=E5=90=8C=E6=A0=B7=E7=9A=84=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hrp/step_request.go | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/hrp/step_request.go b/hrp/step_request.go index 12a8ffbb..abf9755f 100644 --- a/hrp/step_request.go +++ b/hrp/step_request.go @@ -337,13 +337,34 @@ func runStepRequest(r *SessionRunner, step *TStep) (stepResult *StepResult, err // add request object to step variables, could be used in setup hooks stepVariables["hrp_step_name"] = step.Name stepVariables["hrp_step_request"] = rb.requestMap + stepVariables["request"] = rb.requestMap // deal with setup hooks for _, setupHook := range step.SetupHooks { - _, err = parser.Parse(setupHook, stepVariables) + req, err := parser.Parse(setupHook, stepVariables) if err != nil { return stepResult, errors.Wrap(err, "run setup hooks failed") } + reqMap, ok := req.(map[string]interface{}) + if ok && reqMap != nil { + rb.requestMap = reqMap + stepVariables["request"] = reqMap + } + } + if len(step.SetupHooks) > 0 { + requestBody, ok := rb.requestMap["body"].(map[string]interface{}) + if ok { + body, err := json.Marshal(requestBody) + if err == nil { + rb.req.Body = io.NopCloser(bytes.NewReader(body)) + rb.req.ContentLength = int64(len(body)) + } + } + headers, ok := rb.requestMap["headers"].(map[string]string) + rb.req.Header = map[string][]string{} + for key, value := range headers { + rb.req.Header.Set(key, value) + } } // log & print request @@ -414,13 +435,19 @@ func runStepRequest(r *SessionRunner, step *TStep) (stepResult *StepResult, err // add response object to step variables, could be used in teardown hooks stepVariables["hrp_step_response"] = respObj.respObjMeta + stepVariables["response"] = respObj.respObjMeta // deal with teardown hooks for _, teardownHook := range step.TeardownHooks { - _, err = parser.Parse(teardownHook, stepVariables) + res, err := parser.Parse(teardownHook, stepVariables) if err != nil { return stepResult, errors.Wrap(err, "run teardown hooks failed") } + resMpa, ok := res.(map[string]interface{}) + if ok { + stepVariables["response"] = resMpa + respObj.respObjMeta = resMpa + } } sessionData.ReqResps.Request = rb.requestMap From 8beb88bd5ca0bccc225745fbb0cf442f536a368d Mon Sep 17 00:00:00 2001 From: buyuxiang <347586493@qq.com> Date: Thu, 19 Jan 2023 08:59:17 +0800 Subject: [PATCH 03/52] fix: err wrap and TapFloat --- hrp/pkg/uixt/android_adb_driver.go | 7 +++++++ hrp/runner.go | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/hrp/pkg/uixt/android_adb_driver.go b/hrp/pkg/uixt/android_adb_driver.go index 5c80e1ef..50eed4c7 100644 --- a/hrp/pkg/uixt/android_adb_driver.go +++ b/hrp/pkg/uixt/android_adb_driver.go @@ -180,6 +180,13 @@ func (ad *adbDriver) Tap(x, y int, options ...DataOption) error { } func (ad *adbDriver) TapFloat(x, y float64, options ...DataOption) (err error) { + dataOptions := NewDataOptions(options...) + + if len(dataOptions.Offset) == 2 { + x += float64(dataOptions.Offset[0]) + y += float64(dataOptions.Offset[1]) + } + // adb shell input tap x y _, err = ad.adbClient.RunShellCommand( "input", "tap", fmt.Sprintf("%.1f", x), fmt.Sprintf("%.1f", y)) diff --git a/hrp/runner.go b/hrp/runner.go index 34a50a7b..7db22482 100644 --- a/hrp/runner.go +++ b/hrp/runner.go @@ -559,7 +559,7 @@ func (r *SessionRunner) Start(givenVars map[string]interface{}) error { // check if failfast if r.caseRunner.hrpRunner.failfast { - return errors.New("abort running due to failfast setting") + return errors.Wrap(err, "abort running due to failfast setting") } } From cca4bd7ae311f04224711503b2746491bc376c6f Mon Sep 17 00:00:00 2001 From: debugtalk Date: Mon, 13 Feb 2023 21:47:53 +0800 Subject: [PATCH 04/52] change: update logcat args --- hrp/pkg/uixt/android_adb_driver.go | 7 +++++++ hrp/pkg/uixt/android_device.go | 5 +++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/hrp/pkg/uixt/android_adb_driver.go b/hrp/pkg/uixt/android_adb_driver.go index 50eed4c7..394bf14e 100644 --- a/hrp/pkg/uixt/android_adb_driver.go +++ b/hrp/pkg/uixt/android_adb_driver.go @@ -321,6 +321,13 @@ func (ad *adbDriver) IsHealthy() (healthy bool, err error) { func (ad *adbDriver) StartCaptureLog(identifier ...string) (err error) { log.Info().Msg("start adb log recording") + + // clear logcat + if _, err = ad.adbClient.RunShellCommand("logcat", "--clear"); err != nil { + return err + } + + // start logcat err = ad.logcat.CatchLogcat() if err != nil { err = errors.Wrap(code.AndroidCaptureLogError, diff --git a/hrp/pkg/uixt/android_device.go b/hrp/pkg/uixt/android_device.go index 0df9162d..d70afa54 100644 --- a/hrp/pkg/uixt/android_device.go +++ b/hrp/pkg/uixt/android_device.go @@ -311,12 +311,13 @@ func (l *AdbLogcat) CatchLogcat() (err error) { } // clear logcat - if err = myexec.RunCommand("adb", "-s", l.serial, "logcat", "-c"); err != nil { + if err = myexec.RunCommand("adb", "-s", l.serial, "logcat", "--clear"); err != nil { return } // start logcat - l.cmd = myexec.Command("adb", "-s", l.serial, "logcat", "-v", "time", "-s", "iesqaMonitor:V") + l.cmd = myexec.Command("adb", "-s", l.serial, + "logcat", "--format", "time", "-s", "iesqaMonitor:V") l.cmd.Stderr = l.logBuffer l.cmd.Stdout = l.logBuffer if err = l.cmd.Start(); err != nil { From 8065703fa7be15b4cef4b60119144c42b362f524 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Mon, 13 Feb 2023 22:22:49 +0800 Subject: [PATCH 05/52] feat: sleep random seconds --- hrp/pkg/uixt/ext.go | 16 ++++++++++++++++ hrp/step_mobile_ui.go | 8 ++++++++ 2 files changed, 24 insertions(+) diff --git a/hrp/pkg/uixt/ext.go b/hrp/pkg/uixt/ext.go index d87e7b18..3a75c156 100644 --- a/hrp/pkg/uixt/ext.go +++ b/hrp/pkg/uixt/ext.go @@ -7,6 +7,7 @@ import ( "image" "image/jpeg" "image/png" + "math/rand" "mime" "mime/multipart" "net/http" @@ -32,6 +33,7 @@ const ( AppStop MobileMethod = "app_stop" CtlScreenShot MobileMethod = "screenshot" CtlSleep MobileMethod = "sleep" + CtlSleepRandom MobileMethod = "sleep_random" CtlStartCamera MobileMethod = "camera_start" // alias for app_launch camera CtlStopCamera MobileMethod = "camera_stop" // alias for app_terminate camera RecordStart MobileMethod = "record_start" @@ -315,6 +317,10 @@ func isPathExists(path string) bool { return true } +func init() { + rand.Seed(time.Now().UnixNano()) +} + func (dExt *DriverExt) FindUIRectInUIKit(search string, options ...DataOption) (x, y, width, height float64, err error) { // click on text, using OCR if !isPathExists(search) { @@ -602,6 +608,16 @@ func (dExt *DriverExt) DoAction(action MobileAction) error { return nil } return fmt.Errorf("invalid sleep params: %v(%T)", action.Params, action.Params) + case CtlSleepRandom: + if params, ok := action.Params.([]interface{}); ok && len(params) == 2 { + a := params[0].(float64) + b := params[1].(float64) + n := a + rand.Float64()*(b-a) + log.Info().Float64("duration", n).Msg("sleep random seconds") + time.Sleep(time.Duration(n*1000) * time.Millisecond) + return nil + } + return fmt.Errorf("invalid sleep random params: %v(%T)", action.Params, action.Params) case CtlScreenShot: // take snapshot log.Info().Msg("take snapshot for current screen") diff --git a/hrp/step_mobile_ui.go b/hrp/step_mobile_ui.go index 5d37b203..16dc290b 100644 --- a/hrp/step_mobile_ui.go +++ b/hrp/step_mobile_ui.go @@ -281,6 +281,14 @@ func (s *StepMobile) Sleep(n float64) *StepMobile { return &StepMobile{step: s.step} } +func (s *StepMobile) SleepRandom(a, b float64) *StepMobile { + s.mobileStep().Actions = append(s.mobileStep().Actions, uixt.MobileAction{ + Method: uixt.CtlSleepRandom, + Params: []float64{a, b}, + }) + return &StepMobile{step: s.step} +} + func (s *StepMobile) ScreenShot() *StepMobile { s.mobileStep().Actions = append(s.mobileStep().Actions, uixt.MobileAction{ Method: uixt.CtlScreenShot, From dfa586abbda30d383055124104c028543c4f0200 Mon Sep 17 00:00:00 2001 From: debugtalk Date: Mon, 13 Feb 2023 22:26:49 +0800 Subject: [PATCH 06/52] feat: add random builtin functions --- hrp/internal/builtin/function.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/hrp/internal/builtin/function.go b/hrp/internal/builtin/function.go index 00d01d97..baf94a19 100644 --- a/hrp/internal/builtin/function.go +++ b/hrp/internal/builtin/function.go @@ -24,6 +24,8 @@ var Functions = map[string]interface{}{ "get_timestamp": getTimestamp, // call without arguments "sleep": sleep, // call with one argument "gen_random_string": genRandomString, // call with one argument + "random_int": rand.Intn, // call with one argument + "random_range": random_range, // call with two arguments "max": math.Max, // call with two arguments "md5": MD5, // call with one argument "parameterize": loadFromCSV, @@ -49,6 +51,10 @@ func init() { rand.Seed(time.Now().UnixNano()) } +func random_range(a, b float64) float64 { + return a + rand.Float64()*(b-a) +} + func getTimestamp() int64 { return time.Now().UnixNano() / int64(time.Millisecond) } From 8f45cc270812ea7b1c2a375650551e66f12654bd Mon Sep 17 00:00:00 2001 From: debugtalk Date: Mon, 13 Feb 2023 22:34:45 +0800 Subject: [PATCH 07/52] change: add test for sleep random --- examples/uitest/demo_feed_random_slide.json | 84 +++++++++++++++++++ .../uitest/demo_feed_random_slide_test.go | 50 +++++++++++ 2 files changed, 134 insertions(+) create mode 100644 examples/uitest/demo_feed_random_slide.json create mode 100644 examples/uitest/demo_feed_random_slide_test.go diff --git a/examples/uitest/demo_feed_random_slide.json b/examples/uitest/demo_feed_random_slide.json new file mode 100644 index 00000000..86c62947 --- /dev/null +++ b/examples/uitest/demo_feed_random_slide.json @@ -0,0 +1,84 @@ +{ + "config": { + "name": "点播_抖音_滑动场景_随机间隔_android", + "variables": { + "device": "${ENV(SerialNumber)}" + }, + "android": [ + { + "serial": "$device" + } + ] + }, + "teststeps": [ + { + "name": "启动抖音", + "android": { + "actions": [ + { + "method": "app_terminate", + "params": "com.ss.android.ugc.aweme" + }, + { + "method": "app_launch", + "params": "com.ss.android.ugc.aweme" + }, + { + "method": "sleep", + "params": 10 + } + ] + } + }, + { + "name": "处理青少年弹窗", + "android": { + "actions": [ + { + "method": "tap_ocr", + "params": "我知道了", + "ignore_NotFoundError": true + } + ] + } + }, + { + "name": "滑动 Feed 35 次,随机间隔 0-20s", + "android": { + "actions": [ + { + "method": "swipe", + "params": "up" + }, + { + "method": "sleep_random", + "params": [ + 0, + 20 + ] + } + ] + }, + "loops": 35 + }, + { + "name": "滑动 Feed 15 次,随机间隔 15-50s", + "android": { + "actions": [ + { + "method": "swipe", + "params": "up" + }, + { + "method": "sleep_random", + "params": [ + 15, + 50 + ] + } + ] + }, + "loops": 15 + } + ] +} diff --git a/examples/uitest/demo_feed_random_slide_test.go b/examples/uitest/demo_feed_random_slide_test.go new file mode 100644 index 00000000..cc7c5d95 --- /dev/null +++ b/examples/uitest/demo_feed_random_slide_test.go @@ -0,0 +1,50 @@ +//go:build localtest + +package uitest + +import ( + "testing" + + "github.com/httprunner/httprunner/v4/hrp" + "github.com/httprunner/httprunner/v4/hrp/pkg/uixt" +) + +func TestAndroidDouyinFeedTest(t *testing.T) { + testCase := &hrp.TestCase{ + Config: hrp.NewConfig("点播_抖音_滑动场景_随机间隔_android"). + WithVariables(map[string]interface{}{ + "device": "${ENV(SerialNumber)}", + }). + SetAndroid(uixt.WithSerialNumber("$device")), + TestSteps: []hrp.IStep{ + hrp.NewStep("启动抖音"). + Android(). + AppTerminate("com.ss.android.ugc.aweme"). + AppLaunch("com.ss.android.ugc.aweme"). + Sleep(10), + hrp.NewStep("处理青少年弹窗"). + Android(). + TapByOCR("我知道了", uixt.WithIgnoreNotFoundError(true)), + hrp.NewStep("滑动 Feed 35 次,随机间隔 0-20s"). + Loop(35). + Android(). + SwipeUp(). + SleepRandom(0, 20), + hrp.NewStep("滑动 Feed 15 次,随机间隔 15-50s"). + Loop(15). + Android(). + SwipeUp(). + SleepRandom(15, 50), + }, + } + + if err := testCase.Dump2JSON("demo_feed_random_slide.json"); err != nil { + t.Fatal(err) + } + + runner := hrp.NewRunner(t).SetSaveTests(true) + err := runner.Run(testCase) + if err != nil { + t.Fatal(err) + } +} From bf6032520d33935f26c44205735c13f42a75c0d3 Mon Sep 17 00:00:00 2001 From: "lilong.129" Date: Mon, 13 Feb 2023 23:34:09 +0800 Subject: [PATCH 08/52] fix: stop logcat only when enabled --- hrp/pkg/uixt/android_device.go | 4 ++++ hrp/pkg/uixt/interface.go | 1 + hrp/pkg/uixt/ios_device.go | 4 ++++ hrp/runner.go | 15 +++++++++------ 4 files changed, 18 insertions(+), 6 deletions(-) diff --git a/hrp/pkg/uixt/android_device.go b/hrp/pkg/uixt/android_device.go index d70afa54..1bd43cdd 100644 --- a/hrp/pkg/uixt/android_device.go +++ b/hrp/pkg/uixt/android_device.go @@ -138,6 +138,10 @@ func (dev *AndroidDevice) UUID() string { return dev.SerialNumber } +func (dev *AndroidDevice) LogEnabled() bool { + return dev.LogOn +} + func (dev *AndroidDevice) NewDriver(capabilities Capabilities) (driverExt *DriverExt, err error) { var driver WebDriver if dev.UIA2 { diff --git a/hrp/pkg/uixt/interface.go b/hrp/pkg/uixt/interface.go index 865ed9fb..d9706a17 100644 --- a/hrp/pkg/uixt/interface.go +++ b/hrp/pkg/uixt/interface.go @@ -581,6 +581,7 @@ func NewData(data map[string]interface{}, options ...DataOption) map[string]inte // current implemeted device: IOSDevice, AndroidDevice type Device interface { UUID() string // ios udid or android serial + LogEnabled() bool NewDriver(capabilities Capabilities) (driverExt *DriverExt, err error) StartPerf() error diff --git a/hrp/pkg/uixt/ios_device.go b/hrp/pkg/uixt/ios_device.go index da8a9a33..c7fc3555 100644 --- a/hrp/pkg/uixt/ios_device.go +++ b/hrp/pkg/uixt/ios_device.go @@ -281,6 +281,10 @@ func (dev *IOSDevice) UUID() string { return dev.UDID } +func (dev *IOSDevice) LogEnabled() bool { + return dev.LogOn +} + func (dev *IOSDevice) NewDriver(capabilities Capabilities) (driverExt *DriverExt, err error) { // init WDA driver if capabilities == nil { diff --git a/hrp/runner.go b/hrp/runner.go index 7db22482..d81a3022 100644 --- a/hrp/runner.go +++ b/hrp/runner.go @@ -625,13 +625,16 @@ func (r *SessionRunner) GetSummary() (*TestCaseSummary, error) { for uuid, client := range r.caseRunner.hrpRunner.uiClients { // add WDA/UIA logs to summary - log, err := client.Driver.StopCaptureLog() - if err != nil { - return caseSummary, err - } logs := map[string]interface{}{ - "uuid": uuid, - "content": log, + "uuid": uuid, + } + + if client.Device.LogEnabled() { + log, err := client.Driver.StopCaptureLog() + if err != nil { + return caseSummary, err + } + logs["content"] = log } // stop performance monitor From 4b7bd380be88ef38c6d05bd57bab7e6adbfa6443 Mon Sep 17 00:00:00 2001 From: "lilong.129" Date: Mon, 13 Feb 2023 23:37:54 +0800 Subject: [PATCH 09/52] fix: do not fail case when kill logcat error --- hrp/pkg/uixt/android_device.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hrp/pkg/uixt/android_device.go b/hrp/pkg/uixt/android_device.go index 1bd43cdd..83a46d27 100644 --- a/hrp/pkg/uixt/android_device.go +++ b/hrp/pkg/uixt/android_device.go @@ -330,7 +330,7 @@ func (l *AdbLogcat) CatchLogcat() (err error) { go func() { <-l.stopping if e := myexec.KillProcessesByGpid(l.cmd); e != nil { - l.errs = append(l.errs, fmt.Errorf("kill logcat process err:%v", e)) + log.Error().Err(e).Msg("kill logcat process failed") } l.done <- struct{}{} }() From 8d3d916004fb76131e7f6c25bc0e66fbb4c5b08f Mon Sep 17 00:00:00 2001 From: "lilong.129" Date: Tue, 14 Feb 2023 00:06:19 +0800 Subject: [PATCH 10/52] fix: sleep random arguments adapt to int and float --- hrp/pkg/uixt/ext.go | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/hrp/pkg/uixt/ext.go b/hrp/pkg/uixt/ext.go index 3a75c156..ddc67bbe 100644 --- a/hrp/pkg/uixt/ext.go +++ b/hrp/pkg/uixt/ext.go @@ -610,8 +610,17 @@ func (dExt *DriverExt) DoAction(action MobileAction) error { return fmt.Errorf("invalid sleep params: %v(%T)", action.Params, action.Params) case CtlSleepRandom: if params, ok := action.Params.([]interface{}); ok && len(params) == 2 { - a := params[0].(float64) - b := params[1].(float64) + var a, b float64 + if v, ok := params[0].(float64); ok { + a = v + } else if v, ok := params[0].(int64); ok { + a = float64(v) + } + if v, ok := params[1].(float64); ok { + b = v + } else if v, ok := params[1].(int64); ok { + b = float64(v) + } n := a + rand.Float64()*(b-a) log.Info().Float64("duration", n).Msg("sleep random seconds") time.Sleep(time.Duration(n*1000) * time.Millisecond) From 30db4000ca5a9f9a672fb36aed7ffbeb7391083a Mon Sep 17 00:00:00 2001 From: "lilong.129" Date: Thu, 16 Feb 2023 23:04:36 +0800 Subject: [PATCH 11/52] change: update sponsor info --- README.en.md | 6 ++++++ README.md | 7 +++++++ docs/assets/hogwarts.jpeg | Bin 76446 -> 0 bytes docs/assets/hogwarts.png | Bin 0 -> 158850 bytes 4 files changed, 13 insertions(+) delete mode 100644 docs/assets/hogwarts.jpeg create mode 100644 docs/assets/hogwarts.png diff --git a/README.en.md b/README.en.md index 0a30ce22..45111c35 100644 --- a/README.en.md +++ b/README.en.md @@ -118,6 +118,12 @@ Use "hrp [command] --help" for more information about a command. +## Sponsor + +[霍格沃兹测试开发学社](http://qrcode.testing-studio.com/f?from=HttpRunner&url=https://ceshiren.com/) + +> 霍格沃兹测试开发学社是中国软件测试开发高端教育品牌,产品由国内顶尖软件测试开发技术专家携手打造,为企业与个人提供专业的技能培训与咨询、测试工具与测试平台、测试外包与测试众包服务。领域涵盖 App/Web 自动化测试、接口自动化测试、性能测试、安全测试、持续交付/DevOps、测试左移、测试右移、精准测试、测试平台开发、测试管理等方向。-> [联系我们](http://qrcode.testing-studio.com/f?from=HttpRunner&url=https://ceshiren.com/t/topic/23745) + ## Subscribe 关注 HttpRunner 的微信公众号,第一时间获得最新资讯。 diff --git a/README.md b/README.md index 2ca5dc81..984bc900 100644 --- a/README.md +++ b/README.md @@ -112,6 +112,13 @@ Use "hrp [command] --help" for more information about a command. +## 赞助商 + +[霍格沃兹测试开发学社](http://qrcode.testing-studio.com/f?from=HttpRunner&url=https://ceshiren.com/) + +> 霍格沃兹测试开发学社是中国软件测试开发高端教育品牌,产品由国内顶尖软件测试开发技术专家携手打造,为企业与个人提供专业的技能培训与咨询、测试工具与测试平台、测试外包与测试众包服务。领域涵盖 App/Web 自动化测试、接口自动化测试、性能测试、安全测试、持续交付/DevOps、测试左移、测试右移、精准测试、测试平台开发、测试管理等方向。-> [联系我们](http://qrcode.testing-studio.com/f?from=HttpRunner&url=https://ceshiren.com/t/topic/23745) + + ## Subscribe 关注 HttpRunner 的微信公众号,第一时间获得最新资讯。 diff --git a/docs/assets/hogwarts.jpeg b/docs/assets/hogwarts.jpeg deleted file mode 100644 index 78105f91ac20085c4a5f0fb07a34300ca5e3abc9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 76446 zcmeFYWmH_j)+XFYkl+d4xP; Vj;3+@oywQ+ZMcXxMpx5gbBm*DO;+`I0~duP_n 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 diff --git a/docs/assets/hogwarts.png b/docs/assets/hogwarts.png new file mode 100644 index 0000000000000000000000000000000000000000..ae14fa7f938de1690b1314fed214ab2317307b18 GIT binary patch literal 158850 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 literal 0 HcmV?d00001 From 623ba2a522144e69a67d6eaacf26711916e350f5 Mon Sep 17 00:00:00 2001 From: "lilong.129" Date: Mon, 20 Feb 2023 20:48:47 +0800 Subject: [PATCH 12/52] update --- README.en.md | 4 ++-- README.md | 5 ++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/README.en.md b/README.en.md index 45111c35..e242b461 100644 --- a/README.en.md +++ b/README.en.md @@ -120,9 +120,9 @@ Use "hrp [command] --help" for more information about a command. ## Sponsor -[霍格沃兹测试开发学社](http://qrcode.testing-studio.com/f?from=HttpRunner&url=https://ceshiren.com/) +[霍格沃兹测试开发学社](https://qrcode.testing-studio.com/f?from=HttpRunner&url=https://testing-studio.com/) -> 霍格沃兹测试开发学社是中国软件测试开发高端教育品牌,产品由国内顶尖软件测试开发技术专家携手打造,为企业与个人提供专业的技能培训与咨询、测试工具与测试平台、测试外包与测试众包服务。领域涵盖 App/Web 自动化测试、接口自动化测试、性能测试、安全测试、持续交付/DevOps、测试左移、测试右移、精准测试、测试平台开发、测试管理等方向。-> [联系我们](http://qrcode.testing-studio.com/f?from=HttpRunner&url=https://ceshiren.com/t/topic/23745) +> 霍格沃兹测试开发学社是中国软件测试开发高端教育品牌,产品由国内顶尖软件测试开发技术专家携手打造,为企业与个人提供专业的技能培训与咨询、测试工具与测试平台、测试外包与测试众包服务。领域涵盖 App/Web 自动化测试、接口自动化测试、性能测试、安全测试、持续交付/DevOps、测试左移、测试右移、精准测试、测试平台开发、测试管理等方向。-> [**联系我们**](http://qrcode.testing-studio.com/f?from=HttpRunner&url=https://ceshiren.com/t/topic/23745) ## Subscribe diff --git a/README.md b/README.md index 984bc900..b16276ac 100644 --- a/README.md +++ b/README.md @@ -114,10 +114,9 @@ Use "hrp [command] --help" for more information about a command. ## 赞助商 -[霍格沃兹测试开发学社](http://qrcode.testing-studio.com/f?from=HttpRunner&url=https://ceshiren.com/) - -> 霍格沃兹测试开发学社是中国软件测试开发高端教育品牌,产品由国内顶尖软件测试开发技术专家携手打造,为企业与个人提供专业的技能培训与咨询、测试工具与测试平台、测试外包与测试众包服务。领域涵盖 App/Web 自动化测试、接口自动化测试、性能测试、安全测试、持续交付/DevOps、测试左移、测试右移、精准测试、测试平台开发、测试管理等方向。-> [联系我们](http://qrcode.testing-studio.com/f?from=HttpRunner&url=https://ceshiren.com/t/topic/23745) +[霍格沃兹测试开发学社](https://qrcode.testing-studio.com/f?from=HttpRunner&url=https://testing-studio.com/) +> 霍格沃兹测试开发学社是中国软件测试开发高端教育品牌,产品由国内顶尖软件测试开发技术专家携手打造,为企业与个人提供专业的技能培训与咨询、测试工具与测试平台、测试外包与测试众包服务。领域涵盖 App/Web 自动化测试、接口自动化测试、性能测试、安全测试、持续交付/DevOps、测试左移、测试右移、精准测试、测试平台开发、测试管理等方向。-> [**联系我们**](http://qrcode.testing-studio.com/f?from=HttpRunner&url=https://ceshiren.com/t/topic/23745) ## Subscribe From 0876008078ea1fad37887adab6d62b333d8936b5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 23 Feb 2023 09:02:32 +0000 Subject: [PATCH 13/52] build(deps): bump golang.org/x/text from 0.3.7 to 0.3.8 Bumps [golang.org/x/text](https://github.com/golang/text) from 0.3.7 to 0.3.8. - [Release notes](https://github.com/golang/text/releases) - [Commits](https://github.com/golang/text/compare/v0.3.7...v0.3.8) --- updated-dependencies: - dependency-name: golang.org/x/text dependency-type: indirect ... Signed-off-by: dependabot[bot] --- go.mod | 7 +++---- go.sum | 10 ++++++---- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/go.mod b/go.mod index df99d58c..5a79687c 100644 --- a/go.mod +++ b/go.mod @@ -73,12 +73,11 @@ require ( github.com/tklauser/go-sysconf v0.3.10 // indirect github.com/tklauser/numcpus v0.5.0 // indirect github.com/yusufpapurcu/wmi v1.2.2 // indirect - golang.org/x/mod v0.4.2 // indirect + golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect golang.org/x/sync v0.0.0-20220907140024-f12130a52804 // indirect golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8 // indirect - golang.org/x/text v0.3.7 // indirect - golang.org/x/tools v0.1.7 // indirect - golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f // indirect + golang.org/x/text v0.3.8 // indirect + golang.org/x/tools v0.1.12 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto v0.0.0-20220919141832-68c03719ef51 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect diff --git a/go.sum b/go.sum index b941d7b5..1cc8206d 100644 --- a/go.sum +++ b/go.sum @@ -463,8 +463,9 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/net v0.0.0-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -637,8 +638,9 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -694,15 +696,15 @@ golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.7 h1:6j8CgantCy3yc8JGBqkDLMKWqZ0RDU2g1HVgacojGWQ= golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= +golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= -golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f h1:uF6paiQQebLeSXkrTqHqz0MXhXXS1KgF41eUdBNvxK0= golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= From 14f5fce415b81e7d589ba1d2827d8ee3ba324192 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 25 Feb 2023 02:16:15 +0000 Subject: [PATCH 14/52] build(deps): bump golang.org/x/net Bumps [golang.org/x/net](https://github.com/golang/net) from 0.0.0-20220919232410-f2f64ebce3c1 to 0.7.0. - [Release notes](https://github.com/golang/net/releases) - [Commits](https://github.com/golang/net/commits/v0.7.0) --- updated-dependencies: - dependency-name: golang.org/x/net dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- go.mod | 11 +++++------ go.sum | 18 ++++++++++-------- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/go.mod b/go.mod index df99d58c..593fc47d 100644 --- a/go.mod +++ b/go.mod @@ -28,7 +28,7 @@ require ( github.com/spf13/cobra v1.5.0 github.com/stretchr/testify v1.8.0 gocv.io/x/gocv v0.31.0 - golang.org/x/net v0.0.0-20220919232410-f2f64ebce3c1 + golang.org/x/net v0.7.0 golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1 google.golang.org/grpc v1.49.0 google.golang.org/protobuf v1.28.1 @@ -73,12 +73,11 @@ require ( github.com/tklauser/go-sysconf v0.3.10 // indirect github.com/tklauser/numcpus v0.5.0 // indirect github.com/yusufpapurcu/wmi v1.2.2 // indirect - golang.org/x/mod v0.4.2 // indirect + golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect golang.org/x/sync v0.0.0-20220907140024-f12130a52804 // indirect - golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8 // indirect - golang.org/x/text v0.3.7 // indirect - golang.org/x/tools v0.1.7 // indirect - golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f // indirect + golang.org/x/sys v0.5.0 // indirect + golang.org/x/text v0.7.0 // indirect + golang.org/x/tools v0.1.12 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto v0.0.0-20220919141832-68c03719ef51 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect diff --git a/go.sum b/go.sum index b941d7b5..c40a64ef 100644 --- a/go.sum +++ b/go.sum @@ -463,8 +463,9 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/net v0.0.0-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -512,8 +513,8 @@ golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220919232410-f2f64ebce3c1 h1:TWZxd/th7FbRSMret2MVQdlI8uT49QEtwZdvJrxjEHU= -golang.org/x/net v0.0.0-20220919232410-f2f64ebce3c1/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -625,8 +626,8 @@ golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8 h1:h+EGohizhe9XlX18rfpa8k8RAc5XyaeamM+0VHRd4lc= -golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -637,8 +638,9 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -694,15 +696,15 @@ golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.7 h1:6j8CgantCy3yc8JGBqkDLMKWqZ0RDU2g1HVgacojGWQ= golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= +golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= -golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f h1:uF6paiQQebLeSXkrTqHqz0MXhXXS1KgF41eUdBNvxK0= golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= From 5458dc27c8c74cfcfe9219fd52491ccb9bddde01 Mon Sep 17 00:00:00 2001 From: "lilong.129" Date: Tue, 11 Apr 2023 22:03:14 +0800 Subject: [PATCH 15/52] feat: SleepRandom add weight argument to specify the probability of the time range --- examples/uitest/demo_feed_random_slide.json | 37 ++++++++-- .../uitest/demo_feed_random_slide_test.go | 17 +++-- hrp/pkg/uixt/ext.go | 71 +++++++++++++++---- hrp/step_mobile_ui.go | 8 ++- 4 files changed, 104 insertions(+), 29 deletions(-) diff --git a/examples/uitest/demo_feed_random_slide.json b/examples/uitest/demo_feed_random_slide.json index 86c62947..5a89c1cc 100644 --- a/examples/uitest/demo_feed_random_slide.json +++ b/examples/uitest/demo_feed_random_slide.json @@ -43,7 +43,7 @@ } }, { - "name": "滑动 Feed 35 次,随机间隔 0-20s", + "name": "滑动 Feed 3 次,随机间隔 0-5s", "android": { "actions": [ { @@ -54,15 +54,15 @@ "method": "sleep_random", "params": [ 0, - 20 + 5 ] } ] }, - "loops": 35 + "loops": 3 }, { - "name": "滑动 Feed 15 次,随机间隔 15-50s", + "name": "滑动 Feed 1 次,随机间隔 5-10s", "android": { "actions": [ { @@ -72,13 +72,36 @@ { "method": "sleep_random", "params": [ - 15, - 50 + 5, + 10 ] } ] }, - "loops": 15 + "loops": 1 + }, + { + "name": "滑动 Feed 10 次,70% 随机间隔 0-5s,30% 随机间隔 5-10s", + "android": { + "actions": [ + { + "method": "swipe", + "params": "up" + }, + { + "method": "sleep_random", + "params": [ + 0, + 5, + 0.7, + 5, + 10, + 0.3 + ] + } + ] + }, + "loops": 10 } ] } diff --git a/examples/uitest/demo_feed_random_slide_test.go b/examples/uitest/demo_feed_random_slide_test.go index cc7c5d95..ad892b87 100644 --- a/examples/uitest/demo_feed_random_slide_test.go +++ b/examples/uitest/demo_feed_random_slide_test.go @@ -25,16 +25,21 @@ func TestAndroidDouyinFeedTest(t *testing.T) { hrp.NewStep("处理青少年弹窗"). Android(). TapByOCR("我知道了", uixt.WithIgnoreNotFoundError(true)), - hrp.NewStep("滑动 Feed 35 次,随机间隔 0-20s"). - Loop(35). + hrp.NewStep("滑动 Feed 3 次,随机间隔 0-5s"). + Loop(3). Android(). SwipeUp(). - SleepRandom(0, 20), - hrp.NewStep("滑动 Feed 15 次,随机间隔 15-50s"). - Loop(15). + SleepRandom(0, 5), + hrp.NewStep("滑动 Feed 1 次,随机间隔 5-10s"). + Loop(1). Android(). SwipeUp(). - SleepRandom(15, 50), + SleepRandom(5, 10), + hrp.NewStep("滑动 Feed 10 次,70% 随机间隔 0-5s,30% 随机间隔 5-10s"). + Loop(10). + Android(). + SwipeUp(). + SleepRandom(0, 5, 0.7, 5, 10, 0.3), }, } diff --git a/hrp/pkg/uixt/ext.go b/hrp/pkg/uixt/ext.go index ddc67bbe..5bfe9e09 100644 --- a/hrp/pkg/uixt/ext.go +++ b/hrp/pkg/uixt/ext.go @@ -348,6 +348,19 @@ func (dExt *DriverExt) IsImageExist(text string) bool { var errActionNotImplemented = errors.New("UI action not implemented") +func convertToFloat64(val interface{}) (float64, error) { + switch v := val.(type) { + case float64: + return v, nil + case int: + return float64(v), nil + case int64: + return float64(v), nil + default: + return 0, fmt.Errorf("invalid type for conversion to float64: %T, value: %+v", val, val) + } +} + func (dExt *DriverExt) DoAction(action MobileAction) error { log.Info().Str("method", string(action.Method)).Interface("params", action.Params).Msg("start UI action") @@ -609,24 +622,54 @@ func (dExt *DriverExt) DoAction(action MobileAction) error { } return fmt.Errorf("invalid sleep params: %v(%T)", action.Params, action.Params) case CtlSleepRandom: - if params, ok := action.Params.([]interface{}); ok && len(params) == 2 { - var a, b float64 - if v, ok := params[0].(float64); ok { - a = v - } else if v, ok := params[0].(int64); ok { - a = float64(v) + params, ok := action.Params.([]interface{}) + if !ok { + return fmt.Errorf("invalid sleep random params: %v(%T)", action.Params, action.Params) + } + // append default weight 1 + if len(params) == 2 { + params = append(params, 1.0) + } + + var sections []struct { + min, max, weight float64 + } + totalProb := 0.0 + for i := 0; i+3 <= len(params); i += 3 { + min, err := convertToFloat64(params[i]) + if err != nil { + return errors.Wrapf(err, "invalid minimum time: %v", params[i]) } - if v, ok := params[1].(float64); ok { - b = v - } else if v, ok := params[1].(int64); ok { - b = float64(v) + max, err := convertToFloat64(params[i+1]) + if err != nil { + return errors.Wrapf(err, "invalid maximum time: %v", params[i+1]) } - n := a + rand.Float64()*(b-a) - log.Info().Float64("duration", n).Msg("sleep random seconds") - time.Sleep(time.Duration(n*1000) * time.Millisecond) + weight, err := convertToFloat64(params[i+2]) + if err != nil { + return errors.Wrapf(err, "invalid weight value: %v", params[i+2]) + } + totalProb += weight + sections = append(sections, + struct{ min, max, weight float64 }{min, max, weight}, + ) + } + + if totalProb == 0 { + log.Warn().Msg("total weight is 0, skip sleep") return nil } - return fmt.Errorf("invalid sleep random params: %v(%T)", action.Params, action.Params) + + r := rand.Float64() + accProb := 0.0 + for _, s := range sections { + accProb += s.weight / totalProb + if r < accProb { + n := s.min + rand.Float64()*(s.max-s.min) + log.Info().Float64("duration", n).Msg("sleep random seconds") + time.Sleep(time.Duration(n*1000) * time.Millisecond) + return nil + } + } case CtlScreenShot: // take snapshot log.Info().Msg("take snapshot for current screen") diff --git a/hrp/step_mobile_ui.go b/hrp/step_mobile_ui.go index 16dc290b..a193f0b8 100644 --- a/hrp/step_mobile_ui.go +++ b/hrp/step_mobile_ui.go @@ -281,10 +281,14 @@ func (s *StepMobile) Sleep(n float64) *StepMobile { return &StepMobile{step: s.step} } -func (s *StepMobile) SleepRandom(a, b float64) *StepMobile { +// SleepRandom specify random sleeping seconds after last action +// params have two different kinds: +// 1. [min, max] : min and max are float64 time range boudaries +// 2. [min1, max1, weight1, min2, max2, weight2, ...] : weight is the probability of the time range +func (s *StepMobile) SleepRandom(params ...float64) *StepMobile { s.mobileStep().Actions = append(s.mobileStep().Actions, uixt.MobileAction{ Method: uixt.CtlSleepRandom, - Params: []float64{a, b}, + Params: params, }) return &StepMobile{step: s.step} } From 9ad8410e9bd63e7e65b9c8c7b92b3ff907a39b16 Mon Sep 17 00:00:00 2001 From: "lilong.129" Date: Tue, 11 Apr 2023 22:22:36 +0800 Subject: [PATCH 16/52] change: bump version to 4.3.3 --- docs/CHANGELOG.md | 10 ++++++++++ hrp/internal/version/VERSION | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index cdf53f67..5cf607b5 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -1,5 +1,15 @@ # Release History +## v4.3.3 (2023-04-11) + +**go version** + +- feat: add `sleep_random` to sleep random seconds, with weight for multiple time ranges +- feat: input text with adb +- fix: adb driver for TapFloat +- fix: stop logcat only when enabled +- fix: do not fail case when kill logcat error + ## v4.3.2 (2022-12-26) **go version** diff --git a/hrp/internal/version/VERSION b/hrp/internal/version/VERSION index b0136b0c..c294f2b5 100644 --- a/hrp/internal/version/VERSION +++ b/hrp/internal/version/VERSION @@ -1 +1 @@ -v4.3.2 \ No newline at end of file +v4.3.3 \ No newline at end of file From e35a11a25f79c01651ac5b1333fef9831ef83c44 Mon Sep 17 00:00:00 2001 From: "lilong.129" Date: Thu, 13 Apr 2023 22:41:45 +0800 Subject: [PATCH 17/52] refactor: replace gadb with optimized version authoried by @appl3s --- hrp/cmd/adb/devices.go | 2 +- hrp/pkg/gadb/README.md | 8 +- hrp/pkg/gadb/client.go | 16 +- hrp/pkg/gadb/client_test.go | 20 ++ hrp/pkg/gadb/device.go | 316 ++++++++++++++++++++++++++--- hrp/pkg/gadb/device_test.go | 128 ++++++++++++ hrp/pkg/gadb/features.go | 26 +++ hrp/pkg/gadb/sync_transport.go | 1 + hrp/pkg/gadb/transport.go | 10 + hrp/pkg/gadb/transport_test.go | 1 - hrp/pkg/gadb/utils.go | 9 + hrp/pkg/uixt/android_adb_driver.go | 2 +- hrp/pkg/uixt/android_device.go | 4 +- 13 files changed, 509 insertions(+), 34 deletions(-) create mode 100644 hrp/pkg/gadb/features.go create mode 100644 hrp/pkg/gadb/utils.go diff --git a/hrp/cmd/adb/devices.go b/hrp/cmd/adb/devices.go index 86c93608..28eb8bad 100644 --- a/hrp/cmd/adb/devices.go +++ b/hrp/cmd/adb/devices.go @@ -26,7 +26,7 @@ var listAndroidDevicesCmd = &cobra.Command{ return errors.Wrap(err, "list android devices failed") } - var deviceList []gadb.Device + var deviceList []*gadb.Device // filter by serial for _, d := range devices { if serial != "" && serial != d.Serial() { diff --git a/hrp/pkg/gadb/README.md b/hrp/pkg/gadb/README.md index dc67b810..2027cea5 100644 --- a/hrp/pkg/gadb/README.md +++ b/hrp/pkg/gadb/README.md @@ -1,5 +1,11 @@ # gadb -This module is initially forked from [electricbubble/gadb@v0.0.7]. +This module is initially forked from [electricbubble/gadb@v0.0.7] and optimized by [@appl3s]. + +- feat: add reverse forward command +- feat: add `RunShellCommandV2` which supports running nohup +- feat: add `InstallAPK` with feature judgment +- feat: add `Uninstall` for specified package name [electricbubble/gadb@v0.0.7]: https://github.com/electricbubble/gadb/tree/v0.0.7 +[@appl3s]: https://github.com/appl3s diff --git a/hrp/pkg/gadb/client.go b/hrp/pkg/gadb/client.go index 4543484a..bc0349ff 100644 --- a/hrp/pkg/gadb/client.go +++ b/hrp/pkg/gadb/client.go @@ -38,6 +38,16 @@ func NewClientWith(host string, port ...int) (adbClient Client, err error) { return } +func NewClientWithoutTransport(host string, port ...int) (adbClient Client, err error) { + if len(port) == 0 { + port = []int{AdbServerPort} + } + adbClient.host = host + adbClient.port = port[0] + + return +} + func (c Client) ServerVersion() (version int, err error) { var resp string if resp, err = c.executeCommand("host:version"); err != nil { @@ -73,14 +83,14 @@ func (c Client) DeviceSerialList() (serials []string, err error) { return } -func (c Client) DeviceList() (devices []Device, err error) { +func (c Client) DeviceList() (devices []*Device, err error) { var resp string if resp, err = c.executeCommand("host:devices-l"); err != nil { return } lines := strings.Split(resp, "\n") - devices = make([]Device, 0, len(lines)) + devices = make([]*Device, 0, len(lines)) for i := range lines { line := strings.TrimSpace(lines[i]) @@ -101,7 +111,7 @@ func (c Client) DeviceList() (devices []Device, err error) { key, val := split[0], split[1] mapAttrs[key] = val } - devices = append(devices, Device{adbClient: c, serial: fields[0], attrs: mapAttrs}) + devices = append(devices, &Device{adbClient: c, serial: fields[0], attrs: mapAttrs}) } return diff --git a/hrp/pkg/gadb/client_test.go b/hrp/pkg/gadb/client_test.go index 404af6a8..dff28e38 100644 --- a/hrp/pkg/gadb/client_test.go +++ b/hrp/pkg/gadb/client_test.go @@ -3,6 +3,7 @@ package gadb import ( + "io/ioutil" "testing" ) @@ -127,3 +128,22 @@ func TestClient_KillServer(t *testing.T) { t.Fatal(err) } } + +func TestScreenCap(t *testing.T) { + adbClient, err := NewClient() + if err != nil { + t.Fatal(err) + } + + dl, err := adbClient.DeviceList() + if err != nil { + t.Error(err) + } + d := dl[0] + res, err := d.RunShellCommandV2WithBytes("screencap", "-p") + if err != nil { + t.Error(err) + } + t.Log(len(res)) + ioutil.WriteFile("/tmp/1.png", res, 0o644) +} diff --git a/hrp/pkg/gadb/device.go b/hrp/pkg/gadb/device.go index 59c4d1e6..b9911272 100644 --- a/hrp/pkg/gadb/device.go +++ b/hrp/pkg/gadb/device.go @@ -1,6 +1,8 @@ package gadb import ( + "bytes" + "encoding/binary" "errors" "fmt" "io" @@ -48,9 +50,10 @@ func deviceStateConv(k string) (deviceState DeviceState) { } type DeviceForward struct { - Serial string - Local string - Remote string + Serial string + Local string + Remote string + Reverse bool // LocalProtocol string // RemoteProtocol string } @@ -59,51 +62,92 @@ type Device struct { adbClient Client serial string attrs map[string]string + feat Features } -func (d Device) Product() string { +func (d *Device) HasFeature(name Feature) bool { + feats, err := d.GetFeatures() + if err != nil || len(feats) == 0 { + return false + } + return feats.HasFeature(name) +} + +func (d *Device) GetFeatures() (features Features, err error) { + if len(d.feat) > 0 { + return d.feat, nil + } + return d.features() +} + +func (d *Device) features() (features Features, err error) { + res, err := d.executeCommand("host:features") + if err != nil { + return nil, err + } + if len(res) > 4 { + // stip hash + res = res[4:] + } + fs := strings.Split(string(res), ",") + features = make(Features, len(fs)) + for _, f := range fs { + features[Feature(f)] = struct{}{} + } + d.feat = features + return features, nil +} + +func (d *Device) Product() string { return d.attrs["product"] } -func (d Device) Model() string { +func (d *Device) Model() string { return d.attrs["model"] } -func (d Device) Usb() string { +func (d *Device) Usb() string { return d.attrs["usb"] } -func (d Device) transportId() string { +func (d *Device) transportId() string { return d.attrs["transport_id"] } -func (d Device) DeviceInfo() map[string]string { +func (d *Device) DeviceInfo() map[string]string { return d.attrs } -func (d Device) Serial() string { +func (d *Device) Serial() string { // resp, err := d.adbClient.executeCommand(fmt.Sprintf("host-serial:%s:get-serialno", d.serial)) return d.serial } -func (d Device) IsUsb() bool { +func (d *Device) IsUsb() bool { return d.Usb() != "" } -func (d Device) State() (DeviceState, error) { +func (d *Device) State() (DeviceState, error) { resp, err := d.adbClient.executeCommand(fmt.Sprintf("host-serial:%s:get-state", d.serial)) return deviceStateConv(resp), err } -func (d Device) DevicePath() (string, error) { +func (d *Device) DevicePath() (string, error) { resp, err := d.adbClient.executeCommand(fmt.Sprintf("host-serial:%s:get-devpath", d.serial)) return resp, err } -func (d Device) Forward(localPort, remotePort int, noRebind ...bool) (err error) { +func (d *Device) Forward(localPort int, remoteInterface interface{}, noRebind ...bool) (err error) { command := "" + var remote string local := fmt.Sprintf("tcp:%d", localPort) - remote := fmt.Sprintf("tcp:%d", remotePort) + switch r := remoteInterface.(type) { + // for unix sockets + case string: + remote = r + case int: + remote = fmt.Sprintf("tcp:%d", r) + } if len(noRebind) != 0 && noRebind[0] { command = fmt.Sprintf("host-serial:%s:forward:norebind:%s;%s", d.serial, local, remote) @@ -115,7 +159,7 @@ func (d Device) Forward(localPort, remotePort int, noRebind ...bool) (err error) return } -func (d Device) ForwardList() (deviceForwardList []DeviceForward, err error) { +func (d *Device) ForwardList() (deviceForwardList []DeviceForward, err error) { var forwardList []DeviceForward if forwardList, err = d.adbClient.ForwardList(); err != nil { return nil, err @@ -131,18 +175,81 @@ func (d Device) ForwardList() (deviceForwardList []DeviceForward, err error) { return } -func (d Device) ForwardKill(localPort int) (err error) { +func (d *Device) ForwardKill(localPort int) (err error) { local := fmt.Sprintf("tcp:%d", localPort) _, err = d.adbClient.executeCommand(fmt.Sprintf("host-serial:%s:killforward:%s", d.serial, local), true) return } -func (d Device) RunShellCommand(cmd string, args ...string) (string, error) { +func (d *Device) ReverseForward(localPort int, remoteInterface interface{}, noRebind ...bool) (err error) { + var command string + var remote string + local := fmt.Sprintf("tcp:%d", localPort) + switch r := remoteInterface.(type) { + // for unix sockets + case string: + remote = r + case int: + remote = fmt.Sprintf("tcp:%d", r) + } + + if len(noRebind) != 0 && noRebind[0] { + command = fmt.Sprintf("reverse:forward:norebind:%s;%s", remote, local) + } else { + command = fmt.Sprintf("reverse:forward:%s;%s", remote, local) + } + _, err = d.executeCommand(command, true) + return +} + +func (d *Device) ReverseForwardList() (deviceForwardList []DeviceForward, err error) { + res, err := d.executeCommand("reverse:list-forward") + if err != nil { + return nil, err + } + resStr := string(res) + lines := strings.Split(resStr, "\n") + for _, line := range lines { + groups := strings.Split(line, " ") + if len(groups) == 3 { + deviceForwardList = append(deviceForwardList, DeviceForward{ + Reverse: true, + Serial: d.serial, + Remote: groups[1], + Local: groups[2], + }) + } + } + return +} + +func (d *Device) ReverseForwardKill(remoteInterface interface{}) error { + remote := "" + switch r := remoteInterface.(type) { + case string: + remote = r + case int: + remote = fmt.Sprintf("tcp:%d", r) + } + _, err := d.executeCommand(fmt.Sprintf("reverse:killforward:%s", remote), true) + return err +} + +func (d *Device) ReverseForwardKillAll() error { + _, err := d.executeCommand("reverse:killforward-all") + return err +} + +func (d *Device) RunShellCommand(cmd string, args ...string) (string, error) { raw, err := d.RunShellCommandWithBytes(cmd, args...) return string(raw), err } -func (d Device) RunShellCommandWithBytes(cmd string, args ...string) ([]byte, error) { +func (d *Device) RunShellCommandWithBytes(cmd string, args ...string) ([]byte, error) { + if d.HasFeature(FeatShellV2) { + raw, err := d.RunShellCommandV2WithBytes(cmd, args...) + return raw, err + } if len(args) > 0 { cmd = fmt.Sprintf("%s %s", cmd, strings.Join(args, " ")) } @@ -156,7 +263,86 @@ func (d Device) RunShellCommandWithBytes(cmd string, args ...string) ([]byte, er return raw, err } -func (d Device) EnableAdbOverTCP(port ...int) (err error) { +func (d *Device) RunShellCommandV2(cmd string, args ...string) (string, error) { + raw, err := d.RunShellCommandV2WithBytes(cmd, args...) + return string(raw), err +} + +// RunShellCommandV2WithBytes shell v2, 支持后台运行而不会阻断 +func (d *Device) RunShellCommandV2WithBytes(cmd string, args ...string) ([]byte, error) { + if len(args) > 0 { + cmd = fmt.Sprintf("%s %s", cmd, strings.Join(args, " ")) + } + if strings.TrimSpace(cmd) == "" { + return nil, errors.New("adb shell: command cannot be empty") + } + log.Debug().Str("cmd", + fmt.Sprintf("adb -s %s shell %s", d.serial, cmd)). + Msg("run adb command") + raw, err := d.executeCommand(fmt.Sprintf("shell,v2,raw:%s", cmd)) + if err != nil { + return raw, err + } + return d.parseV2CommandWithBytes(raw) +} + +func (d *Device) parseV2CommandWithBytes(input []byte) (res []byte, err error) { + if len(input) == 0 { + return input, nil + } + reader := bytes.NewReader(input) + sizeBuf := make([]byte, 4) + var ( + resBuf []byte + exitCode int + ) +loop: + for { + msgCode, err := reader.ReadByte() + if err != nil { + return input, err + } + switch msgCode { + case 0x01, 0x02: // STDOUT, STDERR + _, err = io.ReadFull(reader, sizeBuf) + if err != nil { + return input, err + } + size := binary.LittleEndian.Uint32(sizeBuf) + if cap(resBuf) < int(size) { + resBuf = make([]byte, int(size)) + } + _, err = io.ReadFull(reader, resBuf[:size]) + if err != nil { + return input, err + } + res = append(res, resBuf[:size]...) + case 0x03: // EXIT + _, err = io.ReadFull(reader, sizeBuf) + if err != nil { + return input, err + } + size := binary.LittleEndian.Uint32(sizeBuf) + if cap(resBuf) < int(size) { + resBuf = make([]byte, int(size)) + } + ec, err := reader.ReadByte() + if err != nil { + return input, err + } + exitCode = int(ec) + break loop + default: + return input, nil + } + } + if exitCode != 0 { + return nil, errors.New(string(res)) + } + return res, nil +} + +func (d *Device) EnableAdbOverTCP(port ...int) (err error) { if len(port) == 0 { port = []int{AdbDaemonPort} } @@ -168,7 +354,7 @@ func (d Device) EnableAdbOverTCP(port ...int) (err error) { return } -func (d Device) createDeviceTransport() (tp transport, err error) { +func (d *Device) createDeviceTransport() (tp transport, err error) { if tp, err = newTransport(fmt.Sprintf("%s:%d", d.adbClient.host, d.adbClient.port)); err != nil { return transport{}, err } @@ -180,7 +366,7 @@ func (d Device) createDeviceTransport() (tp transport, err error) { return } -func (d Device) executeCommand(command string, onlyVerifyResponse ...bool) (raw []byte, err error) { +func (d *Device) executeCommand(command string, onlyVerifyResponse ...bool) (raw []byte, err error) { if len(onlyVerifyResponse) == 0 { onlyVerifyResponse = []bool{false} } @@ -207,7 +393,7 @@ func (d Device) executeCommand(command string, onlyVerifyResponse ...bool) (raw return } -func (d Device) List(remotePath string) (devFileInfos []DeviceFileInfo, err error) { +func (d *Device) List(remotePath string) (devFileInfos []DeviceFileInfo, err error) { var tp transport if tp, err = d.createDeviceTransport(); err != nil { return nil, err @@ -237,7 +423,7 @@ func (d Device) List(remotePath string) (devFileInfos []DeviceFileInfo, err erro return } -func (d Device) PushFile(local *os.File, remotePath string, modification ...time.Time) (err error) { +func (d *Device) PushFile(local *os.File, remotePath string, modification ...time.Time) (err error) { if len(modification) == 0 { var stat os.FileInfo if stat, err = local.Stat(); err != nil { @@ -249,7 +435,7 @@ func (d Device) PushFile(local *os.File, remotePath string, modification ...time return d.Push(local, remotePath, modification[0], DefaultFileMode) } -func (d Device) Push(source io.Reader, remotePath string, modification time.Time, mode ...os.FileMode) (err error) { +func (d *Device) Push(source io.Reader, remotePath string, modification time.Time, mode ...os.FileMode) (err error) { if len(mode) == 0 { mode = []os.FileMode{DefaultFileMode} } @@ -285,7 +471,7 @@ func (d Device) Push(source io.Reader, remotePath string, modification time.Time return } -func (d Device) Pull(remotePath string, dest io.Writer) (err error) { +func (d *Device) Pull(remotePath string, dest io.Writer) (err error) { var tp transport if tp, err = d.createDeviceTransport(); err != nil { return err @@ -305,3 +491,83 @@ func (d Device) Pull(remotePath string, dest io.Writer) (err error) { err = sync.WriteStream(dest) return } + +func (d *Device) installViaABBExec(apk io.ReadSeeker) (raw []byte, err error) { + var ( + tp transport + filesize int64 + ) + filesize, err = apk.Seek(0, io.SeekEnd) + if err != nil { + return nil, err + } + if tp, err = d.createDeviceTransport(); err != nil { + return nil, err + } + defer func() { _ = tp.Close() }() + if err = tp.Send(fmt.Sprintf("abb_exec:package\x00install\x00-t\x00-S\x00%d", filesize)); err != nil { + return nil, err + } + + if err = tp.VerifyResponse(); err != nil { + return nil, err + } + _, err = apk.Seek(0, io.SeekStart) + if err != nil { + return nil, err + } + _, err = io.Copy(tp.Conn(), apk) + if err != nil { + return nil, err + } + raw, err = tp.ReadBytesAll() + return +} + +func (d *Device) InstallAPK(apk io.ReadSeeker) (string, error) { + haserr := func(ret string) bool { + return strings.Contains(ret, "Failure") + } + if d.HasFeature(FeatAbbExec) { + raw, err := d.installViaABBExec(apk) + if err != nil { + return "", fmt.Errorf("error installing: %v", err) + } + if haserr(string(raw)) { + return "", errors.New(string(raw)) + } + return string(raw), err + } + + remote := fmt.Sprintf("/data/local/tmp/gadb_remote_%d.apk", time.Now().Unix()) + err := d.Push(apk, remote, time.Now()) + if err != nil { + return "", fmt.Errorf("error pushing: %v", err) + } + + res, err := d.RunShellCommand("pm", "install", "-f", remote) + if err != nil { + return "", fmt.Errorf("error installing: %v", err) + } + if haserr(res) { + return "", errors.New(res) + } + + return res, nil +} + +func (d *Device) Uninstall(packageName string, keepData ...bool) (string, error) { + if len(keepData) == 0 { + keepData = []bool{false} + } + packageName = strings.ReplaceAll(packageName, " ", "") + if len(packageName) == 0 { + return "", fmt.Errorf("invalid package name") + } + args := []string{"uninstall"} + if keepData[0] { + args = append(args, "-k") + } + args = append(args, packageName) + return d.RunShellCommandV2("pm", args...) +} diff --git a/hrp/pkg/gadb/device_test.go b/hrp/pkg/gadb/device_test.go index 70ae4889..29f48b4d 100644 --- a/hrp/pkg/gadb/device_test.go +++ b/hrp/pkg/gadb/device_test.go @@ -6,6 +6,7 @@ import ( "bytes" "io/ioutil" "os" + "reflect" "strings" "testing" "time" @@ -145,6 +146,42 @@ func TestDevice_Forward(t *testing.T) { } } +func TestDevice_ReverseForward(t *testing.T) { + adbClient, err := NewClient() + if err != nil { + t.Fatal(err) + } + + devices, err := adbClient.DeviceList() + if err != nil { + t.Fatal(err) + } + + localPort := 5005 + err = devices[0].ReverseForward(localPort, "localabstract:scrcpy") + if err != nil { + t.Fatal(err) + } + err = devices[0].ReverseForward(localPort, "localabstract:scrcpy1") + if err != nil { + t.Fatal(err) + } + + _, err = devices[0].ReverseForwardList() + if err != nil { + t.Fatal(err) + } + + err = devices[0].ReverseForwardKill("localabstract:scrcpy1") + if err != nil { + t.Fatal(err) + } + err = devices[0].ReverseForwardKillAll() + if err != nil { + t.Fatal(err) + } +} + func TestDevice_ForwardList(t *testing.T) { adbClient, err := NewClient() if err != nil { @@ -314,3 +351,94 @@ func TestDevice_Pull(t *testing.T) { t.Fatal(err) } } + +func TestDevice_RunShellCommandBackgroundWithBytes(t *testing.T) { + type fields struct { + adbClient Client + serial string + attrs map[string]string + } + type args struct { + cmd string + args []string + } + tests := []struct { + name string + fields fields + args args + want []byte + wantErr bool + }{ + { + name: "runShellCommandBackground", + fields: fields{ + adbClient: func() Client { + c, _ := NewClient() + return c + }(), + serial: "63c1ee94", + }, + args: args{ + cmd: "nohup sleep 10 2>/dev/null 1>/dev/null &", + // cmd: "sleep 10", + + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + d := Device{ + adbClient: tt.fields.adbClient, + serial: tt.fields.serial, + attrs: tt.fields.attrs, + } + got, err := d.RunShellCommandV2WithBytes(tt.args.cmd, tt.args.args...) + if (err != nil) != tt.wantErr { + t.Errorf("Device.RunShellCommandBackgroundWithBytes() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("Device.RunShellCommandBackgroundWithBytes() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestDevice_InstallAPK(t *testing.T) { + apk, _ := os.Open("test.apk") + adbClient, err := NewClient() + if err != nil { + t.Fatal(err) + } + + devices, err := adbClient.DeviceList() + if err != nil { + t.Fatal(err) + } + + dev := devices[len(devices)-1] + dev = devices[0] + + res, err := dev.InstallAPK(apk) + if err != nil { + t.Fatal(err) + } + t.Log(res) +} + +func TestDevice_HasFeature(t *testing.T) { + adbClient, err := NewClient() + if err != nil { + t.Fatal(err) + } + + devices, err := adbClient.DeviceList() + if err != nil { + t.Fatal(err) + } + + dev := devices[len(devices)-1] + dev = devices[0] + + t.Log(dev.GetFeatures()) +} diff --git a/hrp/pkg/gadb/features.go b/hrp/pkg/gadb/features.go new file mode 100644 index 00000000..806f175c --- /dev/null +++ b/hrp/pkg/gadb/features.go @@ -0,0 +1,26 @@ +package gadb + +type ( + Feature string + Features map[Feature]struct{} +) + +var ( + FeatSendrecvV2Brotli = Feature("sendrecv_v2_brotli") + FeatRemountShell = Feature("remount_shell") + FeatSendrecvV2 = Feature("sendrecv_v2") + FeatAbbExec = Feature("abb_exec") + FeatFixedPushMkdir = Feature("fixed_push_mkdir") + FeatFixedPushSymlinkTimestamp = Feature("fixed_push_symlink_timestamp") + FeatAbb = Feature("abb") + FeatShellV2 = Feature("shell_v2") + FeatCmd = Feature("cmd") + FeatLsV2 = Feature("ls_v2") + FeatApex = Feature("apex") + FeatStatV2 = Feature("stat_v2") +) + +func (fs Features) HasFeature(name Feature) bool { + _, has := fs[name] + return has +} diff --git a/hrp/pkg/gadb/sync_transport.go b/hrp/pkg/gadb/sync_transport.go index 6e55df6b..ff7346a4 100644 --- a/hrp/pkg/gadb/sync_transport.go +++ b/hrp/pkg/gadb/sync_transport.go @@ -250,5 +250,6 @@ func (sync syncTransport) Close() (err error) { if sync.sock == nil { return nil } + _ = DisableTimeWait(sync.sock.(*net.TCPConn)) return sync.sock.Close() } diff --git a/hrp/pkg/gadb/transport.go b/hrp/pkg/gadb/transport.go index ae900429..8c54089e 100644 --- a/hrp/pkg/gadb/transport.go +++ b/hrp/pkg/gadb/transport.go @@ -34,9 +34,18 @@ func newTransport(address string, readTimeout ...time.Duration) (tp transport, e func (t transport) Send(command string) (err error) { msg := fmt.Sprintf("%04x%s", len(command), command) + log.Debug().Str("cmd", command).Msg("run adb command") return _send(t.sock, []byte(msg)) } +func (t transport) SendBytes(b []byte) (err error) { + return _send(t.sock, b) +} + +func (t transport) Conn() net.Conn { + return t.sock +} + func (t transport) VerifyResponse() (err error) { var status string if status, err = t.ReadStringN(4); err != nil { @@ -103,6 +112,7 @@ func (t transport) Close() (err error) { if t.sock == nil { return nil } + _ = DisableTimeWait(t.sock.(*net.TCPConn)) return t.sock.Close() } diff --git a/hrp/pkg/gadb/transport_test.go b/hrp/pkg/gadb/transport_test.go index 143cd438..2610a19e 100644 --- a/hrp/pkg/gadb/transport_test.go +++ b/hrp/pkg/gadb/transport_test.go @@ -13,7 +13,6 @@ func Test_transport_VerifyResponse(t *testing.T) { } defer transport.Close() - // err = transport.Send("host:123version") err = transport.Send("host:version") if err != nil { t.Fatal(err) diff --git a/hrp/pkg/gadb/utils.go b/hrp/pkg/gadb/utils.go new file mode 100644 index 00000000..e6536c37 --- /dev/null +++ b/hrp/pkg/gadb/utils.go @@ -0,0 +1,9 @@ +package gadb + +import ( + "net" +) + +func DisableTimeWait(conn *net.TCPConn) error { + return conn.SetLinger(0) +} diff --git a/hrp/pkg/uixt/android_adb_driver.go b/hrp/pkg/uixt/android_adb_driver.go index 394bf14e..e9a35b38 100644 --- a/hrp/pkg/uixt/android_adb_driver.go +++ b/hrp/pkg/uixt/android_adb_driver.go @@ -17,7 +17,7 @@ import ( type adbDriver struct { Driver - adbClient gadb.Device + adbClient *gadb.Device logcat *AdbLogcat } diff --git a/hrp/pkg/uixt/android_device.go b/hrp/pkg/uixt/android_device.go index 83a46d27..ad80f6e1 100644 --- a/hrp/pkg/uixt/android_device.go +++ b/hrp/pkg/uixt/android_device.go @@ -115,7 +115,7 @@ func NewAndroidDevice(options ...AndroidDeviceOption) (device *AndroidDevice, er fmt.Sprintf("device %s not found", device.SerialNumber)) } -func DeviceList() (devices []gadb.Device, err error) { +func DeviceList() (devices []*gadb.Device, err error) { var adbClient gadb.Client if adbClient, err = gadb.NewClientWith(AdbServerHost, AdbServerPort); err != nil { return nil, errors.Wrap(code.AndroidDeviceConnectionError, err.Error()) @@ -125,7 +125,7 @@ func DeviceList() (devices []gadb.Device, err error) { } type AndroidDevice struct { - d gadb.Device + d *gadb.Device logcat *AdbLogcat SerialNumber string `json:"serial,omitempty" yaml:"serial,omitempty"` UIA2 bool `json:"uia2,omitempty" yaml:"uia2,omitempty"` // use uiautomator2 From 639ae3e4f2b8e167c4879737fb070eef927a27c3 Mon Sep 17 00:00:00 2001 From: "lilong.129" Date: Thu, 13 Apr 2023 22:48:40 +0800 Subject: [PATCH 18/52] faet: add ScreenCap for gadb --- hrp/pkg/gadb/client_test.go | 2 +- hrp/pkg/gadb/device.go | 4 ++++ hrp/pkg/uixt/android_adb_driver.go | 4 +--- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/hrp/pkg/gadb/client_test.go b/hrp/pkg/gadb/client_test.go index dff28e38..56dd1a94 100644 --- a/hrp/pkg/gadb/client_test.go +++ b/hrp/pkg/gadb/client_test.go @@ -140,7 +140,7 @@ func TestScreenCap(t *testing.T) { t.Error(err) } d := dl[0] - res, err := d.RunShellCommandV2WithBytes("screencap", "-p") + res, err := d.ScreenCap() if err != nil { t.Error(err) } diff --git a/hrp/pkg/gadb/device.go b/hrp/pkg/gadb/device.go index b9911272..4b296396 100644 --- a/hrp/pkg/gadb/device.go +++ b/hrp/pkg/gadb/device.go @@ -571,3 +571,7 @@ func (d *Device) Uninstall(packageName string, keepData ...bool) (string, error) args = append(args, packageName) return d.RunShellCommandV2("pm", args...) } + +func (d *Device) ScreenCap() ([]byte, error) { + return d.RunShellCommandV2WithBytes("screencap", "-p") +} diff --git a/hrp/pkg/uixt/android_adb_driver.go b/hrp/pkg/uixt/android_adb_driver.go index e9a35b38..fbf89eb2 100644 --- a/hrp/pkg/uixt/android_adb_driver.go +++ b/hrp/pkg/uixt/android_adb_driver.go @@ -280,9 +280,7 @@ func (ad *adbDriver) SetRotation(rotation Rotation) (err error) { func (ad *adbDriver) Screenshot() (raw *bytes.Buffer, err error) { // adb shell screencap -p - resp, err := ad.adbClient.RunShellCommandWithBytes( - "screencap", "-p", - ) + resp, err := ad.adbClient.ScreenCap() if err == nil { return bytes.NewBuffer(resp), nil } From b0b41b88045ad5c29ec1b458fc3e4818e6ad12fc Mon Sep 17 00:00:00 2001 From: "lilong.129" Date: Thu, 13 Apr 2023 23:08:43 +0800 Subject: [PATCH 19/52] change: remove debug log --- hrp/pkg/gadb/device.go | 5 ++--- hrp/pkg/gadb/transport.go | 1 - 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/hrp/pkg/gadb/device.go b/hrp/pkg/gadb/device.go index 4b296396..97bb08db 100644 --- a/hrp/pkg/gadb/device.go +++ b/hrp/pkg/gadb/device.go @@ -247,8 +247,7 @@ func (d *Device) RunShellCommand(cmd string, args ...string) (string, error) { func (d *Device) RunShellCommandWithBytes(cmd string, args ...string) ([]byte, error) { if d.HasFeature(FeatShellV2) { - raw, err := d.RunShellCommandV2WithBytes(cmd, args...) - return raw, err + return d.RunShellCommandV2WithBytes(cmd, args...) } if len(args) > 0 { cmd = fmt.Sprintf("%s %s", cmd, strings.Join(args, " ")) @@ -278,7 +277,7 @@ func (d *Device) RunShellCommandV2WithBytes(cmd string, args ...string) ([]byte, } log.Debug().Str("cmd", fmt.Sprintf("adb -s %s shell %s", d.serial, cmd)). - Msg("run adb command") + Msg("run adb command in v2") raw, err := d.executeCommand(fmt.Sprintf("shell,v2,raw:%s", cmd)) if err != nil { return raw, err diff --git a/hrp/pkg/gadb/transport.go b/hrp/pkg/gadb/transport.go index 8c54089e..c450d053 100644 --- a/hrp/pkg/gadb/transport.go +++ b/hrp/pkg/gadb/transport.go @@ -34,7 +34,6 @@ func newTransport(address string, readTimeout ...time.Duration) (tp transport, e func (t transport) Send(command string) (err error) { msg := fmt.Sprintf("%04x%s", len(command), command) - log.Debug().Str("cmd", command).Msg("run adb command") return _send(t.sock, []byte(msg)) } From 632f821713806e42eba6f994d191216665f6e4ca Mon Sep 17 00:00:00 2001 From: "lilong.129" Date: Fri, 14 Apr 2023 13:29:22 +0800 Subject: [PATCH 20/52] change: take screenshot after each step --- hrp/step_mobile_ui.go | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/hrp/step_mobile_ui.go b/hrp/step_mobile_ui.go index a193f0b8..56ee9e4f 100644 --- a/hrp/step_mobile_ui.go +++ b/hrp/step_mobile_ui.go @@ -563,6 +563,16 @@ func runStepMobileUI(s *SessionRunner, step *TStep) (stepResult *StepResult, err attachments["error"] = err.Error() } + // take screenshot after each step + screenshotPath, err := uiDriver.ScreenShot( + fmt.Sprintf("step_%d", time.Now().Unix())) + if err != nil { + log.Error().Err(err).Str("step", step.Name).Msg("take screenshot failed") + } else { + log.Info().Str("path", screenshotPath).Msg("take screenshot on step finished") + screenshots = append(screenshots, screenshotPath) + } + // save attachments screenshots = append(screenshots, uiDriver.ScreenShots...) attachments["screenshots"] = screenshots @@ -599,16 +609,6 @@ func runStepMobileUI(s *SessionRunner, step *TStep) (stepResult *StepResult, err } } - // take snapshot - screenshotPath, err := uiDriver.ScreenShot( - fmt.Sprintf("validate_%d", time.Now().Unix())) - if err != nil { - log.Warn().Err(err).Str("step", step.Name).Msg("take screenshot failed") - } else { - log.Info().Str("path", screenshotPath).Msg("take screenshot before validation") - screenshots = append(screenshots, screenshotPath) - } - // validate validateResults, err := validateUI(uiDriver, step.Validators) if err != nil { From 4803d7fbf9b365a64f0a8ca3a90b8f197ada70ba Mon Sep 17 00:00:00 2001 From: "lilong.129" Date: Fri, 14 Apr 2023 15:26:16 +0800 Subject: [PATCH 21/52] refactor: get attached android/ios devices --- hrp/cmd/adb/devices.go | 20 ++--------- hrp/cmd/ios/devices.go | 14 ++------ hrp/cmd/ios/init.go | 9 ++--- hrp/pkg/uixt/android_device.go | 61 +++++++++++++++++++++------------- hrp/pkg/uixt/android_test.go | 2 +- hrp/pkg/uixt/ios_device.go | 43 +++++++++++++----------- 6 files changed, 70 insertions(+), 79 deletions(-) diff --git a/hrp/cmd/adb/devices.go b/hrp/cmd/adb/devices.go index 28eb8bad..3a0de351 100644 --- a/hrp/cmd/adb/devices.go +++ b/hrp/cmd/adb/devices.go @@ -5,10 +5,8 @@ import ( "fmt" "os" - "github.com/pkg/errors" "github.com/spf13/cobra" - "github.com/httprunner/httprunner/v4/hrp/pkg/gadb" "github.com/httprunner/httprunner/v4/hrp/pkg/uixt" ) @@ -21,22 +19,10 @@ var listAndroidDevicesCmd = &cobra.Command{ Use: "devices", Short: "List all Android devices", RunE: func(cmd *cobra.Command, args []string) error { - devices, err := uixt.DeviceList() + deviceList, err := uixt.GetAndroidDevices(serial) if err != nil { - return errors.Wrap(err, "list android devices failed") - } - - var deviceList []*gadb.Device - // filter by serial - for _, d := range devices { - if serial != "" && serial != d.Serial() { - continue - } - deviceList = append(deviceList, d) - } - if serial != "" && len(deviceList) == 0 { - fmt.Printf("no android device found for serial: %s\n", serial) - os.Exit(1) + fmt.Println(err) + os.Exit(0) } for _, d := range deviceList { diff --git a/hrp/cmd/ios/devices.go b/hrp/cmd/ios/devices.go index c1f230d3..ad8fb55c 100644 --- a/hrp/cmd/ios/devices.go +++ b/hrp/cmd/ios/devices.go @@ -70,18 +70,10 @@ var listDevicesCmd = &cobra.Command{ Short: "List all iOS devices", PersistentPreRun: func(cmd *cobra.Command, args []string) {}, RunE: func(cmd *cobra.Command, args []string) error { - devices, err := uixt.IOSDevices(udid) + devices, err := uixt.GetIOSDevices(udid) if err != nil { - return err - } - if len(devices) == 0 { - if udid != "" { - fmt.Printf("no ios device found for udid: %s\n", udid) - os.Exit(1) - } else { - fmt.Println("no ios device found") - os.Exit(0) - } + fmt.Println(err) + os.Exit(0) } for _, d := range devices { diff --git a/hrp/cmd/ios/init.go b/hrp/cmd/ios/init.go index 209846fb..0a285f17 100644 --- a/hrp/cmd/ios/init.go +++ b/hrp/cmd/ios/init.go @@ -2,7 +2,6 @@ package ios import ( "fmt" - "os" "github.com/spf13/cobra" @@ -16,16 +15,12 @@ var iosRootCmd = &cobra.Command{ } func getDevice(udid string) (gidevice.Device, error) { - devices, err := uixt.IOSDevices(udid) + devices, err := uixt.GetIOSDevices(udid) if err != nil { return nil, err } - if len(devices) == 0 { - fmt.Println("no ios device found") - os.Exit(1) - } if len(devices) > 1 { - return nil, fmt.Errorf("multiple devices found, please specify udid") + return nil, fmt.Errorf("multiple devices found, please specify ios udid") } return devices[0], nil } diff --git a/hrp/pkg/uixt/android_device.go b/hrp/pkg/uixt/android_device.go index ad80f6e1..52063250 100644 --- a/hrp/pkg/uixt/android_device.go +++ b/hrp/pkg/uixt/android_device.go @@ -81,15 +81,6 @@ func GetAndroidDeviceOptions(dev *AndroidDevice) (deviceOptions []AndroidDeviceO // uiautomator2 server must be started before // adb shell am instrument -w io.appium.uiautomator2.server.test/androidx.test.runner.AndroidJUnitRunner func NewAndroidDevice(options ...AndroidDeviceOption) (device *AndroidDevice, err error) { - deviceList, err := DeviceList() - if err != nil { - return nil, errors.Wrap(code.AndroidDeviceConnectionError, - fmt.Sprintf("get attached devices failed: %v", err)) - } else if len(deviceList) == 0 { - return nil, errors.Wrap(code.AndroidDeviceConnectionError, - "not attached device found") - } - device = &AndroidDevice{ UIA2IP: UIA2ServerHost, UIA2Port: UIA2ServerPort, @@ -98,30 +89,52 @@ func NewAndroidDevice(options ...AndroidDeviceOption) (device *AndroidDevice, er option(device) } - serialNumber := device.SerialNumber - for _, dev := range deviceList { - // find device by serial number if specified - if serialNumber != "" && dev.Serial() != serialNumber { - continue - } - - device.SerialNumber = dev.Serial() - device.d = dev - device.logcat = NewAdbLogcat(device.SerialNumber) - return device, nil + deviceList, err := GetAndroidDevices(device.SerialNumber) + if err != nil { + return nil, errors.Wrap(code.AndroidDeviceConnectionError, err.Error()) } - return nil, errors.Wrap(code.AndroidDeviceConnectionError, - fmt.Sprintf("device %s not found", device.SerialNumber)) + dev := deviceList[0] + device.SerialNumber = dev.Serial() + device.d = dev + device.logcat = NewAdbLogcat(device.SerialNumber) + + log.Info().Str("serial", device.SerialNumber).Msg("select android device") + return device, nil } -func DeviceList() (devices []*gadb.Device, err error) { +func GetAndroidDevices(serial ...string) (devices []*gadb.Device, err error) { var adbClient gadb.Client if adbClient, err = gadb.NewClientWith(AdbServerHost, AdbServerPort); err != nil { return nil, errors.Wrap(code.AndroidDeviceConnectionError, err.Error()) } - return adbClient.DeviceList() + if devices, err = adbClient.DeviceList(); err != nil { + return nil, errors.Wrap(code.AndroidDeviceConnectionError, + fmt.Sprintf("list android devices failed: %v", err)) + } + + var deviceList []*gadb.Device + // filter by serial + for _, d := range devices { + for _, s := range serial { + if s != "" && s != d.Serial() { + continue + } + deviceList = append(deviceList, d) + } + } + + if len(deviceList) == 0 { + var err error + if serial == nil || (len(serial) == 1 && serial[0] == "") { + err = fmt.Errorf("no android device found") + } else { + err = fmt.Errorf("no android device found for serial %v", serial) + } + return nil, err + } + return deviceList, nil } type AndroidDevice struct { diff --git a/hrp/pkg/uixt/android_test.go b/hrp/pkg/uixt/android_test.go index 44d8d3ac..17d61f97 100644 --- a/hrp/pkg/uixt/android_test.go +++ b/hrp/pkg/uixt/android_test.go @@ -324,7 +324,7 @@ func Test_getFreePort(t *testing.T) { } func TestDeviceList(t *testing.T) { - devices, err := DeviceList() + devices, err := GetAndroidDevices() if err != nil { t.Fatal(err) } diff --git a/hrp/pkg/uixt/ios_device.go b/hrp/pkg/uixt/ios_device.go index c7fc3555..93828a8d 100644 --- a/hrp/pkg/uixt/ios_device.go +++ b/hrp/pkg/uixt/ios_device.go @@ -141,7 +141,7 @@ func WithIOSPcapOptions(options ...gidevice.PcapOption) IOSDeviceOption { } } -func IOSDevices(udid ...string) (devices []gidevice.Device, err error) { +func GetIOSDevices(udid ...string) (devices []gidevice.Device, err error) { var usbmux gidevice.Usbmux if usbmux, err = gidevice.NewUsbmux(); err != nil { return nil, errors.Wrap(code.IOSDeviceConnectionError, @@ -168,6 +168,15 @@ func IOSDevices(udid ...string) (devices []gidevice.Device, err error) { } } + if len(deviceList) == 0 { + var err error + if udid == nil || (len(udid) == 1 && udid[0] == "") { + err = fmt.Errorf("no ios device found") + } else { + err = fmt.Errorf("no ios device found for udid %v", udid) + } + return nil, err + } return deviceList, nil } @@ -223,31 +232,27 @@ func NewIOSDevice(options ...IOSDeviceOption) (device *IOSDevice, err error) { option(device) } - deviceList, err := IOSDevices(device.UDID) + deviceList, err := GetIOSDevices(device.UDID) if err != nil { - return nil, err + return nil, errors.Wrap(code.IOSDeviceConnectionError, err.Error()) } - for _, dev := range deviceList { - udid := dev.Properties().SerialNumber - device.UDID = udid - device.d = dev + dev := deviceList[0] + udid := dev.Properties().SerialNumber + device.UDID = udid + device.d = dev - // run xctest if XCTestBundleID is set - if device.XCTestBundleID != "" { - _, err = device.RunXCTest(device.XCTestBundleID) - if err != nil { - log.Error().Err(err).Str("udid", udid).Msg("failed to init XCTest") - continue - } + // run xctest if XCTestBundleID is set + if device.XCTestBundleID != "" { + _, err = device.RunXCTest(device.XCTestBundleID) + if err != nil { + log.Error().Err(err).Str("udid", udid).Msg("failed to init XCTest") + return } - - log.Info().Str("udid", device.UDID).Msg("select device") - return device, nil } - return nil, errors.Wrap(code.IOSDeviceConnectionError, - fmt.Sprintf("device %s not found", device.UDID)) + log.Info().Str("udid", device.UDID).Msg("select ios device") + return device, nil } type IOSDevice struct { From b5cb8c6ffe3a181051ea0a2a70971720e9c564ef Mon Sep 17 00:00:00 2001 From: "lilong.129" Date: Fri, 14 Apr 2023 16:21:02 +0800 Subject: [PATCH 22/52] feat: add adb screencap sub command --- hrp/cmd/adb/init.go | 20 +++++++++++++++++++- hrp/cmd/adb/screencap.go | 37 +++++++++++++++++++++++++++++++++++++ hrp/cmd/ios/init.go | 2 +- hrp/pkg/uixt/ocr_vedem.go | 2 +- 4 files changed, 58 insertions(+), 3 deletions(-) create mode 100644 hrp/cmd/adb/screencap.go diff --git a/hrp/cmd/adb/init.go b/hrp/cmd/adb/init.go index 9025ef70..e2a86bce 100644 --- a/hrp/cmd/adb/init.go +++ b/hrp/cmd/adb/init.go @@ -1,6 +1,13 @@ package adb -import "github.com/spf13/cobra" +import ( + "fmt" + + "github.com/spf13/cobra" + + "github.com/httprunner/httprunner/v4/hrp/pkg/gadb" + "github.com/httprunner/httprunner/v4/hrp/pkg/uixt" +) var androidRootCmd = &cobra.Command{ Use: "adb", @@ -8,6 +15,17 @@ var androidRootCmd = &cobra.Command{ PersistentPreRun: func(cmd *cobra.Command, args []string) {}, } +func getDevice(serial string) (*gadb.Device, error) { + devices, err := uixt.GetAndroidDevices(serial) + if err != nil { + return nil, err + } + if len(devices) > 1 { + return nil, fmt.Errorf("found multiple attached devices, please specify android serial") + } + return devices[0], nil +} + func Init(rootCmd *cobra.Command) { rootCmd.AddCommand(androidRootCmd) } diff --git a/hrp/cmd/adb/screencap.go b/hrp/cmd/adb/screencap.go new file mode 100644 index 00000000..d9166191 --- /dev/null +++ b/hrp/cmd/adb/screencap.go @@ -0,0 +1,37 @@ +package adb + +import ( + "fmt" + "io/ioutil" + "time" + + "github.com/spf13/cobra" +) + +var screencapAndroidDevicesCmd = &cobra.Command{ + Use: "screencap", + Short: "Start android screen capture", + RunE: func(cmd *cobra.Command, args []string) error { + device, err := getDevice(serial) + if err != nil { + return err + } + + res, err := device.ScreenCap() + if err != nil { + return err + } + + filepath := fmt.Sprintf("screencap_%d.png", time.Now().Unix()) + if err = ioutil.WriteFile(filepath, res, 0o644); err != nil { + return err + } + fmt.Println("screencap saved to", filepath) + return nil + }, +} + +func init() { + screencapAndroidDevicesCmd.Flags().StringVarP(&serial, "serial", "s", "", "filter by device's serial") + androidRootCmd.AddCommand(screencapAndroidDevicesCmd) +} diff --git a/hrp/cmd/ios/init.go b/hrp/cmd/ios/init.go index 0a285f17..8ec31071 100644 --- a/hrp/cmd/ios/init.go +++ b/hrp/cmd/ios/init.go @@ -20,7 +20,7 @@ func getDevice(udid string) (gidevice.Device, error) { return nil, err } if len(devices) > 1 { - return nil, fmt.Errorf("multiple devices found, please specify ios udid") + return nil, fmt.Errorf("found multiple attached devices, please specify ios udid") } return devices[0], nil } diff --git a/hrp/pkg/uixt/ocr_vedem.go b/hrp/pkg/uixt/ocr_vedem.go index 03d8a271..e93fd8f2 100644 --- a/hrp/pkg/uixt/ocr_vedem.go +++ b/hrp/pkg/uixt/ocr_vedem.go @@ -102,7 +102,7 @@ func (s *veDEMOCRService) getOCRResult(imageBuf *bytes.Buffer) ([]OCRResult, err logID = getLogID(resp.Header) } log.Error().Err(err). - Str("logID", logID). + Str("X-TT-LOGID", logID). Int("imageBufSize", size). Msgf("request OCR service failed, retry %d", i) time.Sleep(1 * time.Second) From faf126f046739fdbbb1d43168fa2a8e88e153f5c Mon Sep 17 00:00:00 2001 From: "lilong.129" Date: Fri, 14 Apr 2023 17:42:28 +0800 Subject: [PATCH 23/52] feat: add IsAppInForeground to check if the given package is in fore ground --- hrp/pkg/uixt/android_adb_driver.go | 49 +++++++++++++++++++++++++++--- hrp/pkg/uixt/android_test.go | 28 +++++++++++++++++ hrp/pkg/uixt/client.go | 2 ++ hrp/pkg/uixt/interface.go | 10 ++++-- hrp/pkg/uixt/ios_driver.go | 14 +++++++++ 5 files changed, 95 insertions(+), 8 deletions(-) diff --git a/hrp/pkg/uixt/android_adb_driver.go b/hrp/pkg/uixt/android_adb_driver.go index fbf89eb2..4449a268 100644 --- a/hrp/pkg/uixt/android_adb_driver.go +++ b/hrp/pkg/uixt/android_adb_driver.go @@ -153,11 +153,11 @@ func (ad *adbDriver) PressKeyCode(keyCode KeyCode, metaState KeyMeta) (err error return } -func (ad *adbDriver) AppLaunch(bundleId string) (err error) { +func (ad *adbDriver) AppLaunch(packageName string) (err error) { // 不指定 Activity 名称启动(启动主 Activity) // adb shell monkey -p -c android.intent.category.LAUNCHER 1 sOutput, err := ad.adbClient.RunShellCommand( - "monkey", "-p", bundleId, "-c", "android.intent.category.LAUNCHER", "1", + "monkey", "-p", packageName, "-c", "android.intent.category.LAUNCHER", "1", ) if err != nil { return err @@ -165,14 +165,22 @@ func (ad *adbDriver) AppLaunch(bundleId string) (err error) { if strings.Contains(sOutput, "monkey aborted") { return fmt.Errorf("app launch: %s", strings.TrimSpace(sOutput)) } + ad.lastLaunchedPackageName = packageName return nil } -func (ad *adbDriver) AppTerminate(bundleId string) (successful bool, err error) { +func (ad *adbDriver) AppTerminate(packageName string) (successful bool, err error) { // 强制停止应用,停止 相关的进程 // adb shell am force-stop - _, err = ad.adbClient.RunShellCommand("am", "force-stop", bundleId) - return err == nil, err + _, err = ad.adbClient.RunShellCommand("am", "force-stop", packageName) + if err != nil { + return false, err + } + + if ad.lastLaunchedPackageName == packageName { + ad.lastLaunchedPackageName = "" // reset last launched package name + } + return true, nil } func (ad *adbDriver) Tap(x, y int, options ...DataOption) error { @@ -347,3 +355,34 @@ func (ad *adbDriver) StopCaptureLog() (result interface{}, err error) { content := ad.logcat.logBuffer.String() return ConvertPoints(content), nil } + +func (ad *adbDriver) GetLastLaunchedApp() (packageName string) { + return ad.lastLaunchedPackageName +} + +func (ad *adbDriver) IsAppInForeground(packageName string) (bool, error) { + if packageName == "" { + return false, errors.New("package name is not given") + } + + // adb shell dumpsys activity activities | grep mResumedActivity + output, err := ad.adbClient.RunShellCommand("dumpsys", "activity", "activities") + if err != nil { + return false, err + } + + lines := strings.Split(string(output), "\n") + isInForeground := false + + for _, line := range lines { + trimmedLine := strings.TrimSpace(line) + if strings.HasPrefix(trimmedLine, "mResumedActivity:") { + if strings.Contains(trimmedLine, packageName) { + isInForeground = true + } + break + } + } + + return isInForeground, nil +} diff --git a/hrp/pkg/uixt/android_test.go b/hrp/pkg/uixt/android_test.go index 17d61f97..b2a89037 100644 --- a/hrp/pkg/uixt/android_test.go +++ b/hrp/pkg/uixt/android_test.go @@ -353,6 +353,34 @@ func TestDriver_AppLaunch(t *testing.T) { t.Log(ioutil.WriteFile("s1.png", raw.Bytes(), 0o600)) } +func TestDriver_IsAppInForeground(t *testing.T) { + device, _ := NewAndroidDevice() + driver, err := device.NewDriver(nil) + if err != nil { + t.Fatal(err) + } + + err = driver.Driver.AppLaunch("com.android.settings") + if err != nil { + t.Fatal(err) + } + + yes, err := driver.Driver.IsAppInForeground(driver.Driver.GetLastLaunchedApp()) + if err != nil || !yes { + t.Fatal(err) + } + + _, err = driver.Driver.AppTerminate("com.android.settings") + if err != nil { + t.Fatal(err) + } + + yes, err = driver.Driver.IsAppInForeground("com.android.settings") + if err != nil || yes { + t.Fatal(err) + } +} + func TestDriver_KeepAlive(t *testing.T) { device, _ := NewAndroidDevice() driver, err := device.NewDriver(nil) diff --git a/hrp/pkg/uixt/client.go b/hrp/pkg/uixt/client.go index 5ebd9309..95a8b277 100644 --- a/hrp/pkg/uixt/client.go +++ b/hrp/pkg/uixt/client.go @@ -20,6 +20,8 @@ type Driver struct { urlPrefix *url.URL sessionId string client *http.Client + // cache the last launched package name + lastLaunchedPackageName string } func (wd *Driver) concatURL(u *url.URL, elem ...string) string { diff --git a/hrp/pkg/uixt/interface.go b/hrp/pkg/uixt/interface.go index d9706a17..501e6862 100644 --- a/hrp/pkg/uixt/interface.go +++ b/hrp/pkg/uixt/interface.go @@ -627,10 +627,14 @@ type WebDriver interface { // AppLaunch Launch an application with given bundle identifier in scope of current session. // !This method is only available since Xcode9 SDK - AppLaunch(bundleId string) error - // AppTerminate Terminate an application with the given bundle id. + AppLaunch(packageName string) error + // AppTerminate Terminate an application with the given pacakge name. // Either `true` if the app has been successfully terminated or `false` if it was not running - AppTerminate(bundleId string) (bool, error) + AppTerminate(packageName string) (bool, error) + // GetLastLaunchedApp returns the package name of the last launched app + GetLastLaunchedApp() string + // IsAppInForeground returns true if the given package is in foreground + IsAppInForeground(packageName string) (bool, error) // StartCamera Starts a new camera for recording StartCamera() error diff --git a/hrp/pkg/uixt/ios_driver.go b/hrp/pkg/uixt/ios_driver.go index 53339d8c..33bbe0ed 100644 --- a/hrp/pkg/uixt/ios_driver.go +++ b/hrp/pkg/uixt/ios_driver.go @@ -308,6 +308,9 @@ func (wd *wdaDriver) AppLaunch(bundleId string) (err error) { data := make(map[string]interface{}) data["bundleId"] = bundleId _, err = wd.httpPOST(data, "/session", wd.sessionId, "/wda/apps/launch") + if err == nil { + wd.lastLaunchedPackageName = bundleId + } return } @@ -328,6 +331,9 @@ func (wd *wdaDriver) AppTerminate(bundleId string) (successful bool, err error) if successful, err = rawResp.valueConvertToBool(); err != nil { return false, err } + if wd.lastLaunchedPackageName == bundleId { + wd.lastLaunchedPackageName = "" // reset last launched package name + } return } @@ -348,6 +354,14 @@ func (wd *wdaDriver) AppDeactivate(second float64) (err error) { return } +func (wd *wdaDriver) GetLastLaunchedApp() (packageName string) { + return wd.lastLaunchedPackageName +} + +func (wd *wdaDriver) IsAppInForeground(packageName string) (bool, error) { + return false, errors.New("not implemented") +} + func (wd *wdaDriver) Tap(x, y int, options ...DataOption) error { return wd.TapFloat(float64(x), float64(y), options...) } From 75997b11789eb89726b4bbe5ba5ff35fc188aeef Mon Sep 17 00:00:00 2001 From: "lilong.129" Date: Fri, 14 Apr 2023 18:03:13 +0800 Subject: [PATCH 24/52] change: print ocr service logID when success --- hrp/pkg/uixt/ocr_vedem.go | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/hrp/pkg/uixt/ocr_vedem.go b/hrp/pkg/uixt/ocr_vedem.go index e93fd8f2..67c80b40 100644 --- a/hrp/pkg/uixt/ocr_vedem.go +++ b/hrp/pkg/uixt/ocr_vedem.go @@ -93,14 +93,17 @@ func (s *veDEMOCRService) getOCRResult(imageBuf *bytes.Buffer) ([]OCRResult, err // retry 3 times for i := 1; i <= 3; i++ { resp, err = client.Do(req) - if err == nil { - break - } - var logID string if resp != nil { logID = getLogID(resp.Header) } + if err == nil { + log.Debug(). + Str("X-TT-LOGID", logID). + Int("imageBufSize", size). + Msg("request OCR service success") + break + } log.Error().Err(err). Str("X-TT-LOGID", logID). Int("imageBufSize", size). From 2ce6ce371b3e64e2758c60ba5168b62bfc6ff775 Mon Sep 17 00:00:00 2001 From: "lilong.129" Date: Fri, 14 Apr 2023 18:57:36 +0800 Subject: [PATCH 25/52] feat: check if app is in foreground when step failed --- hrp/internal/code/code.go | 5 +++-- hrp/step_mobile_ui.go | 8 ++++++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/hrp/internal/code/code.go b/hrp/internal/code/code.go index 4687bc96..354ff302 100644 --- a/hrp/internal/code/code.go +++ b/hrp/internal/code/code.go @@ -67,8 +67,9 @@ var ( // UI automation related: [70, 80) var ( - MobileUIDriverError = errors.New("mobile UI driver error") // 70 - MobileUIValidationError = errors.New("mobile UI validation error") // 75 + MobileUIDriverError = errors.New("mobile UI driver error") // 70 + MobileUIValidationError = errors.New("mobile UI validation error") // 75 + MobileUIAppNotInForegroundError = errors.New("mobile UI app not in foreground error") // 76 ) // OCR related: [80, 90) diff --git a/hrp/step_mobile_ui.go b/hrp/step_mobile_ui.go index 56ee9e4f..99014a4c 100644 --- a/hrp/step_mobile_ui.go +++ b/hrp/step_mobile_ui.go @@ -561,6 +561,14 @@ func runStepMobileUI(s *SessionRunner, step *TStep) (stepResult *StepResult, err attachments := make(map[string]interface{}) if err != nil { attachments["error"] = err.Error() + + // check if app is in foreground + packageName := uiDriver.Driver.GetLastLaunchedApp() + yes, err2 := uiDriver.Driver.IsAppInForeground(packageName) + if packageName != "" && (!yes || err2 != nil) { + log.Error().Err(err2).Str("packageName", packageName).Msg("app is not in foreground") + err = errors.Wrap(code.MobileUIAppNotInForegroundError, err.Error()) + } } // take screenshot after each step From 12fdf489d7b80ca8808df75a4beccc839cf1b0dc Mon Sep 17 00:00:00 2001 From: "lilong.129" Date: Fri, 14 Apr 2023 19:02:34 +0800 Subject: [PATCH 26/52] bump version to v4.3.3.2023041419 --- docs/CHANGELOG.md | 6 +++++- hrp/internal/version/VERSION | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 5cf607b5..d7b5f38d 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -1,14 +1,18 @@ # Release History -## v4.3.3 (2023-04-11) +## v4.3.3 (2023-04-14) **go version** - feat: add `sleep_random` to sleep random seconds, with weight for multiple time ranges - feat: input text with adb +- feat: add adb `screencap` sub command +- feat: add `IsAppInForeground` to check if the given package is in foreground +- feat: check if app is in foreground when step failed - fix: adb driver for TapFloat - fix: stop logcat only when enabled - fix: do not fail case when kill logcat error +- fix: take screenshot after each step ## v4.3.2 (2022-12-26) diff --git a/hrp/internal/version/VERSION b/hrp/internal/version/VERSION index c294f2b5..f36ebd00 100644 --- a/hrp/internal/version/VERSION +++ b/hrp/internal/version/VERSION @@ -1 +1 @@ -v4.3.3 \ No newline at end of file +v4.3.3.2023041419 \ No newline at end of file From 7ef29fef60cdbdf4a53e93c519236ba0c763451e Mon Sep 17 00:00:00 2001 From: "lilong.129" Date: Fri, 14 Apr 2023 20:28:04 +0800 Subject: [PATCH 27/52] fix: adb logcat clear argument --- hrp/pkg/uixt/android_adb_driver.go | 2 +- hrp/pkg/uixt/android_device.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/hrp/pkg/uixt/android_adb_driver.go b/hrp/pkg/uixt/android_adb_driver.go index 4449a268..f373f924 100644 --- a/hrp/pkg/uixt/android_adb_driver.go +++ b/hrp/pkg/uixt/android_adb_driver.go @@ -329,7 +329,7 @@ func (ad *adbDriver) StartCaptureLog(identifier ...string) (err error) { log.Info().Msg("start adb log recording") // clear logcat - if _, err = ad.adbClient.RunShellCommand("logcat", "--clear"); err != nil { + if _, err = ad.adbClient.RunShellCommand("logcat", "-c"); err != nil { return err } diff --git a/hrp/pkg/uixt/android_device.go b/hrp/pkg/uixt/android_device.go index 52063250..b511659d 100644 --- a/hrp/pkg/uixt/android_device.go +++ b/hrp/pkg/uixt/android_device.go @@ -328,7 +328,7 @@ func (l *AdbLogcat) CatchLogcat() (err error) { } // clear logcat - if err = myexec.RunCommand("adb", "-s", l.serial, "logcat", "--clear"); err != nil { + if err = myexec.RunCommand("adb", "-s", l.serial, "shell", "logcat", "-c"); err != nil { return } From bae5380f802dd2533b99099ed809ecdfffbdef66 Mon Sep 17 00:00:00 2001 From: "lilong.129" Date: Fri, 14 Apr 2023 21:03:47 +0800 Subject: [PATCH 28/52] fix: ScreenCap call RunShellCommandWithBytes --- hrp/internal/version/VERSION | 2 +- hrp/pkg/gadb/device.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/hrp/internal/version/VERSION b/hrp/internal/version/VERSION index f36ebd00..193b8191 100644 --- a/hrp/internal/version/VERSION +++ b/hrp/internal/version/VERSION @@ -1 +1 @@ -v4.3.3.2023041419 \ No newline at end of file +v4.3.3.2023041421 \ No newline at end of file diff --git a/hrp/pkg/gadb/device.go b/hrp/pkg/gadb/device.go index 97bb08db..87a496ea 100644 --- a/hrp/pkg/gadb/device.go +++ b/hrp/pkg/gadb/device.go @@ -572,5 +572,5 @@ func (d *Device) Uninstall(packageName string, keepData ...bool) (string, error) } func (d *Device) ScreenCap() ([]byte, error) { - return d.RunShellCommandV2WithBytes("screencap", "-p") + return d.RunShellCommandWithBytes("screencap", "-p") } From f5935f0ffd9f4b4a21c397fd929eb0b997a2231a Mon Sep 17 00:00:00 2001 From: "lilong.129" Date: Fri, 14 Apr 2023 21:37:35 +0800 Subject: [PATCH 29/52] fix: support gif image --- hrp/internal/version/VERSION | 2 +- hrp/pkg/uixt/ext.go | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/hrp/internal/version/VERSION b/hrp/internal/version/VERSION index 193b8191..45631758 100644 --- a/hrp/internal/version/VERSION +++ b/hrp/internal/version/VERSION @@ -1 +1 @@ -v4.3.3.2023041421 \ No newline at end of file +v4.3.3.2304142136 \ No newline at end of file diff --git a/hrp/pkg/uixt/ext.go b/hrp/pkg/uixt/ext.go index 5bfe9e09..f110e107 100644 --- a/hrp/pkg/uixt/ext.go +++ b/hrp/pkg/uixt/ext.go @@ -5,6 +5,7 @@ import ( "encoding/json" "fmt" "image" + "image/gif" "image/jpeg" "image/png" "math/rand" @@ -284,6 +285,8 @@ func saveScreenShot(raw *bytes.Buffer, fileName string) (string, error) { err = png.Encode(file, img) case "jpeg": err = jpeg.Encode(file, img, nil) + case "gif": + err = gif.Encode(file, img, nil) default: return "", fmt.Errorf("unsupported image format: %s", format) } From 2d7634f6b04c2876b446d6e98b69dec2935bd36e Mon Sep 17 00:00:00 2001 From: "lilong.129" Date: Fri, 14 Apr 2023 21:50:24 +0800 Subject: [PATCH 30/52] fix: checkout ocr response status code --- hrp/internal/version/VERSION | 2 +- hrp/pkg/uixt/ocr_vedem.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/hrp/internal/version/VERSION b/hrp/internal/version/VERSION index 45631758..45af2229 100644 --- a/hrp/internal/version/VERSION +++ b/hrp/internal/version/VERSION @@ -1 +1 @@ -v4.3.3.2304142136 \ No newline at end of file +v4.3.3.2304142149 \ No newline at end of file diff --git a/hrp/pkg/uixt/ocr_vedem.go b/hrp/pkg/uixt/ocr_vedem.go index 67c80b40..f68964b6 100644 --- a/hrp/pkg/uixt/ocr_vedem.go +++ b/hrp/pkg/uixt/ocr_vedem.go @@ -97,7 +97,7 @@ func (s *veDEMOCRService) getOCRResult(imageBuf *bytes.Buffer) ([]OCRResult, err if resp != nil { logID = getLogID(resp.Header) } - if err == nil { + if err == nil && resp.StatusCode == http.StatusOK { log.Debug(). Str("X-TT-LOGID", logID). Int("imageBufSize", size). From 1c10978facb5f2dc2ede98392714ce57f6ccc19a Mon Sep 17 00:00:00 2001 From: "lilong.129" Date: Fri, 14 Apr 2023 23:50:40 +0800 Subject: [PATCH 31/52] fix: screencap compatibility for shell v1 and v2 --- docs/CHANGELOG.md | 1 + hrp/internal/version/VERSION | 2 +- hrp/pkg/gadb/device.go | 17 ++++++++++++++++- 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index d7b5f38d..35632507 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -13,6 +13,7 @@ - fix: stop logcat only when enabled - fix: do not fail case when kill logcat error - fix: take screenshot after each step +- fix: screencap compatibility for shell v1 and v2 ## v4.3.2 (2022-12-26) diff --git a/hrp/internal/version/VERSION b/hrp/internal/version/VERSION index 45af2229..f907911d 100644 --- a/hrp/internal/version/VERSION +++ b/hrp/internal/version/VERSION @@ -1 +1 @@ -v4.3.3.2304142149 \ No newline at end of file +v4.3.3.2304142356 \ No newline at end of file diff --git a/hrp/pkg/gadb/device.go b/hrp/pkg/gadb/device.go index 87a496ea..181609b0 100644 --- a/hrp/pkg/gadb/device.go +++ b/hrp/pkg/gadb/device.go @@ -572,5 +572,20 @@ func (d *Device) Uninstall(packageName string, keepData ...bool) (string, error) } func (d *Device) ScreenCap() ([]byte, error) { - return d.RunShellCommandWithBytes("screencap", "-p") + if d.HasFeature(FeatShellV2) { + return d.RunShellCommandV2WithBytes("screencap", "-p") + } + + // for shell v1, screenshot buffer maybe truncated + // thus we firstly save it to local file and then pull it + tempPath := fmt.Sprintf("/data/local/tmp/screenshot_%d.png", + time.Now().Unix()) + _, err := d.RunShellCommandWithBytes("screencap", "-p", tempPath) + if err != nil { + return nil, err + } + + buffer := bytes.NewBuffer(nil) + err = d.Pull(tempPath, buffer) + return buffer.Bytes(), err } From 36d9802adbd855cdd15c95384c302a574a518b23 Mon Sep 17 00:00:00 2001 From: "lilong.129" Date: Sun, 16 Apr 2023 12:33:13 +0800 Subject: [PATCH 32/52] fix: MobileUIAppNotInForegroundError exit code --- hrp/internal/code/code.go | 5 +++-- hrp/internal/version/VERSION | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/hrp/internal/code/code.go b/hrp/internal/code/code.go index 354ff302..3479c8b6 100644 --- a/hrp/internal/code/code.go +++ b/hrp/internal/code/code.go @@ -124,8 +124,9 @@ var errorsMap = map[error]int{ AndroidCaptureLogError: 66, // UI automation related - MobileUIDriverError: 70, - MobileUIValidationError: 75, + MobileUIDriverError: 70, + MobileUIValidationError: 75, + MobileUIAppNotInForegroundError: 76, // OCR related OCREnvMissedError: 80, diff --git a/hrp/internal/version/VERSION b/hrp/internal/version/VERSION index f907911d..ff51d667 100644 --- a/hrp/internal/version/VERSION +++ b/hrp/internal/version/VERSION @@ -1 +1 @@ -v4.3.3.2304142356 \ No newline at end of file +v4.3.3.2304161232 \ No newline at end of file From 2249cfab9e60ef42fcc67d5e11e83a36b7a4dcbc Mon Sep 17 00:00:00 2001 From: "lilong.129" Date: Sun, 16 Apr 2023 18:00:36 +0800 Subject: [PATCH 33/52] change: update go mod --- go.mod | 6 +++--- go.sum | 14 ++++++++------ 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index 593fc47d..f6975946 100644 --- a/go.mod +++ b/go.mod @@ -22,7 +22,7 @@ require ( github.com/olekukonko/tablewriter v0.0.5 github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.13.0 - github.com/rs/zerolog v1.28.0 + github.com/rs/zerolog v1.29.1 github.com/satori/go.uuid v1.2.0 github.com/shirou/gopsutil v3.21.11+incompatible github.com/spf13/cobra v1.5.0 @@ -56,7 +56,7 @@ require ( github.com/josharian/intern v1.0.0 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.16 // indirect + github.com/mattn/go-isatty v0.0.18 // indirect github.com/mattn/go-runewidth v0.0.14 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect github.com/mitchellh/go-testing-interface v1.14.1 // indirect @@ -75,7 +75,7 @@ require ( github.com/yusufpapurcu/wmi v1.2.2 // indirect golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect golang.org/x/sync v0.0.0-20220907140024-f12130a52804 // indirect - golang.org/x/sys v0.5.0 // indirect + golang.org/x/sys v0.7.0 // indirect golang.org/x/text v0.7.0 // indirect golang.org/x/tools v0.1.12 // indirect google.golang.org/appengine v1.6.7 // indirect diff --git a/go.sum b/go.sum index c40a64ef..979a82b0 100644 --- a/go.sum +++ b/go.sum @@ -89,7 +89,7 @@ github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWH github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= @@ -301,8 +301,9 @@ github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98= +github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= @@ -370,8 +371,8 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/zerolog v1.26.1/go.mod h1:/wSSJWX7lVrsOwlbyTRSOJvqRlc+WjWlfes+CiJ+tmc= -github.com/rs/zerolog v1.28.0 h1:MirSo27VyNi7RJYP3078AA1+Cyzd2GB66qy3aUHvsWY= -github.com/rs/zerolog v1.28.0/go.mod h1:NILgTygv/Uej1ra5XxGf82ZFSLk58MFGAUS2o6usyD0= +github.com/rs/zerolog v1.29.1 h1:cO+d60CHkknCbvzEWxP0S9K6KqyTjrCNUy1LdQLCGPc= +github.com/rs/zerolog v1.29.1/go.mod h1:Le6ESbR7hc+DP6Lt1THiV8CQSdkkNrd3R0XbEgp3ZBU= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= @@ -626,8 +627,9 @@ golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= +golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= From 2644c24fae1b4132ca161ae70b1c742572b00bc1 Mon Sep 17 00:00:00 2001 From: "lilong.129" Date: Sun, 16 Apr 2023 18:50:21 +0800 Subject: [PATCH 34/52] feat: add validator AssertAppInForeground and AssertAppNotInForeground --- docs/CHANGELOG.md | 3 +- examples/uitest/demo_feed_random_slide.json | 29 ++++++++++++++- .../uitest/demo_feed_random_slide_test.go | 9 ++++- hrp/internal/version/VERSION | 2 +- hrp/pkg/uixt/android_adb_driver.go | 1 + hrp/pkg/uixt/ext.go | 37 ++++++++++++++----- hrp/step_mobile_ui.go | 30 +++++++++++++++ 7 files changed, 97 insertions(+), 14 deletions(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 35632507..723ca0d4 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -1,6 +1,6 @@ # Release History -## v4.3.3 (2023-04-14) +## v4.3.3 (2023-04-16) **go version** @@ -9,6 +9,7 @@ - feat: add adb `screencap` sub command - feat: add `IsAppInForeground` to check if the given package is in foreground - feat: check if app is in foreground when step failed +- feat: add validator AssertAppInForeground and AssertAppNotInForeground - fix: adb driver for TapFloat - fix: stop logcat only when enabled - fix: do not fail case when kill logcat error diff --git a/examples/uitest/demo_feed_random_slide.json b/examples/uitest/demo_feed_random_slide.json index 5a89c1cc..d74f45ae 100644 --- a/examples/uitest/demo_feed_random_slide.json +++ b/examples/uitest/demo_feed_random_slide.json @@ -28,7 +28,15 @@ "params": 10 } ] - } + }, + "validate": [ + { + "check": "ui_foreground_app", + "assert": "equal", + "expect": "com.ss.android.ugc.aweme", + "msg": "app [com.ss.android.ugc.aweme] should be in foreground" + } + ] }, { "name": "处理青少年弹窗", @@ -102,6 +110,25 @@ ] }, "loops": 10 + }, + { + "name": "exit", + "android": { + "actions": [ + { + "method": "app_terminate", + "params": "com.ss.android.ugc.aweme" + } + ] + }, + "validate": [ + { + "check": "ui_foreground_app", + "assert": "not_equal", + "expect": "com.ss.android.ugc.aweme", + "msg": "app [com.ss.android.ugc.aweme] should not be in foreground" + } + ] } ] } diff --git a/examples/uitest/demo_feed_random_slide_test.go b/examples/uitest/demo_feed_random_slide_test.go index ad892b87..98311c69 100644 --- a/examples/uitest/demo_feed_random_slide_test.go +++ b/examples/uitest/demo_feed_random_slide_test.go @@ -21,7 +21,9 @@ func TestAndroidDouyinFeedTest(t *testing.T) { Android(). AppTerminate("com.ss.android.ugc.aweme"). AppLaunch("com.ss.android.ugc.aweme"). - Sleep(10), + Sleep(10). + Validate(). + AssertAppInForeground("com.ss.android.ugc.aweme"), hrp.NewStep("处理青少年弹窗"). Android(). TapByOCR("我知道了", uixt.WithIgnoreNotFoundError(true)), @@ -40,6 +42,11 @@ func TestAndroidDouyinFeedTest(t *testing.T) { Android(). SwipeUp(). SleepRandom(0, 5, 0.7, 5, 10, 0.3), + hrp.NewStep("exit"). + Android(). + AppTerminate("com.ss.android.ugc.aweme"). + Validate(). + AssertAppNotInForeground("com.ss.android.ugc.aweme"), }, } diff --git a/hrp/internal/version/VERSION b/hrp/internal/version/VERSION index ff51d667..e73aa79d 100644 --- a/hrp/internal/version/VERSION +++ b/hrp/internal/version/VERSION @@ -1 +1 @@ -v4.3.3.2304161232 \ No newline at end of file +v4.3.3.2304161855 \ No newline at end of file diff --git a/hrp/pkg/uixt/android_adb_driver.go b/hrp/pkg/uixt/android_adb_driver.go index f373f924..68f8c28b 100644 --- a/hrp/pkg/uixt/android_adb_driver.go +++ b/hrp/pkg/uixt/android_adb_driver.go @@ -368,6 +368,7 @@ func (ad *adbDriver) IsAppInForeground(packageName string) (bool, error) { // adb shell dumpsys activity activities | grep mResumedActivity output, err := ad.adbClient.RunShellCommand("dumpsys", "activity", "activities") if err != nil { + log.Error().Err(err).Msg("failed to dumpsys activities") return false, err } diff --git a/hrp/pkg/uixt/ext.go b/hrp/pkg/uixt/ext.go index f110e107..093cb776 100644 --- a/hrp/pkg/uixt/ext.go +++ b/hrp/pkg/uixt/ext.go @@ -41,10 +41,15 @@ const ( RecordStop MobileMethod = "record_stop" // UI validation - SelectorName string = "ui_name" - SelectorLabel string = "ui_label" - SelectorOCR string = "ui_ocr" - SelectorImage string = "ui_image" + // selectors + SelectorName string = "ui_name" + SelectorLabel string = "ui_label" + SelectorOCR string = "ui_ocr" + SelectorImage string = "ui_image" + SelectorForegroundApp string = "ui_foreground_app" + // assertions + AssertionEqual string = "equal" + AssertionNotEqual string = "not_equal" AssertionExists string = "exists" AssertionNotExists string = "not_exists" @@ -349,6 +354,16 @@ func (dExt *DriverExt) IsImageExist(text string) bool { return err == nil } +func (dExt *DriverExt) IsAppInForeground(packageName string) bool { + // check if app is in foreground + yes, err := dExt.Driver.IsAppInForeground(packageName) + if !yes || err != nil { + log.Info().Str("packageName", packageName).Msg("app is not in foreground") + return false + } + return true +} + var errActionNotImplemented = errors.New("UI action not implemented") func convertToFloat64(val interface{}) (float64, error) { @@ -700,18 +715,20 @@ func (dExt *DriverExt) getAbsScope(x1, y1, x2, y2 float64) (int, int, int, int) } func (dExt *DriverExt) DoValidation(check, assert, expected string, message ...string) bool { - var exists bool - if assert == AssertionExists { - exists = true + var exp bool + if assert == AssertionExists || assert == AssertionEqual { + exp = true } else { - exists = false + exp = false } var result bool switch check { case SelectorOCR: - result = (dExt.IsOCRExist(expected) == exists) + result = (dExt.IsOCRExist(expected) == exp) case SelectorImage: - result = (dExt.IsImageExist(expected) == exists) + result = (dExt.IsImageExist(expected) == exp) + case SelectorForegroundApp: + result = (dExt.IsAppInForeground(expected) == exp) } if !result { diff --git a/hrp/step_mobile_ui.go b/hrp/step_mobile_ui.go index 99014a4c..f5483488 100644 --- a/hrp/step_mobile_ui.go +++ b/hrp/step_mobile_ui.go @@ -468,6 +468,36 @@ func (s *StepMobileUIValidation) AssertImageNotExists(expectedImagePath string, return s } +func (s *StepMobileUIValidation) AssertAppInForeground(packageName string, msg ...string) *StepMobileUIValidation { + v := Validator{ + Check: uixt.SelectorForegroundApp, + Assert: uixt.AssertionEqual, + Expect: packageName, + } + if len(msg) > 0 { + v.Message = msg[0] + } else { + v.Message = fmt.Sprintf("app [%s] should be in foreground", packageName) + } + s.step.Validators = append(s.step.Validators, v) + return s +} + +func (s *StepMobileUIValidation) AssertAppNotInForeground(packageName string, msg ...string) *StepMobileUIValidation { + v := Validator{ + Check: uixt.SelectorForegroundApp, + Assert: uixt.AssertionNotEqual, + Expect: packageName, + } + if len(msg) > 0 { + v.Message = msg[0] + } else { + v.Message = fmt.Sprintf("app [%s] should not be in foreground", packageName) + } + s.step.Validators = append(s.step.Validators, v) + return s +} + func (s *StepMobileUIValidation) Name() string { return s.step.Name } From 0a59ced8240421088c63cf40287da47dbceb4c94 Mon Sep 17 00:00:00 2001 From: "lilong.129" Date: Sun, 16 Apr 2023 22:11:35 +0800 Subject: [PATCH 35/52] refactor: TakeScreenShot saves all screenshots --- examples/worldcup/main.go | 6 +--- hrp/cmd/adb/screencap.go | 5 ++-- hrp/internal/builtin/utils.go | 4 +++ hrp/pkg/gadb/device.go | 4 ++- hrp/pkg/uixt/ext.go | 53 ++++++++++++++++++----------------- hrp/pkg/uixt/interface.go | 12 -------- hrp/pkg/uixt/ocr_vedem.go | 17 ++--------- hrp/pkg/uixt/opencv.go | 6 ++-- hrp/step_mobile_ui.go | 15 ++++------ 9 files changed, 50 insertions(+), 72 deletions(-) diff --git a/examples/worldcup/main.go b/examples/worldcup/main.go index e67f024c..697bcd57 100644 --- a/examples/worldcup/main.go +++ b/examples/worldcup/main.go @@ -150,11 +150,7 @@ func NewWorldCupLive(device uixt.Device, matchName, bundleID string, duration, i func (wc *WorldCupLive) getCurrentLiveTime(utcTime time.Time) error { utcTimeStr := utcTime.Format("15:04:05") - fileName := filepath.Join( - wc.resultDir, "screenshot", utcTimeStr) - ocrTexts, err := wc.driver.GetTextsByOCR( - uixt.WithScreenShot(fileName), - ) + ocrTexts, err := wc.driver.GetTextsByOCR() if err != nil { log.Error().Err(err).Msg("get ocr texts failed") return err diff --git a/hrp/cmd/adb/screencap.go b/hrp/cmd/adb/screencap.go index d9166191..bb1ddf6c 100644 --- a/hrp/cmd/adb/screencap.go +++ b/hrp/cmd/adb/screencap.go @@ -3,9 +3,10 @@ package adb import ( "fmt" "io/ioutil" - "time" "github.com/spf13/cobra" + + "github.com/httprunner/httprunner/v4/hrp/internal/builtin" ) var screencapAndroidDevicesCmd = &cobra.Command{ @@ -22,7 +23,7 @@ var screencapAndroidDevicesCmd = &cobra.Command{ return err } - filepath := fmt.Sprintf("screencap_%d.png", time.Now().Unix()) + filepath := fmt.Sprintf("%s.png", builtin.GenNameWithTimestamp("screencap_")) if err = ioutil.WriteFile(filepath, res, 0o644); err != nil { return err } diff --git a/hrp/internal/builtin/utils.go b/hrp/internal/builtin/utils.go index 63dbfd0f..a27ac366 100644 --- a/hrp/internal/builtin/utils.go +++ b/hrp/internal/builtin/utils.go @@ -443,3 +443,7 @@ func Sign(ver string, ak string, sk string, body []byte) string { signResult := sha256HMAC(signKey, body) return fmt.Sprintf("%v/%v", signKeyInfo, string(signResult)) } + +func GenNameWithTimestamp(prefix string) string { + return fmt.Sprintf("%s%d", prefix, time.Now().Unix()) +} diff --git a/hrp/pkg/gadb/device.go b/hrp/pkg/gadb/device.go index 181609b0..dfac9042 100644 --- a/hrp/pkg/gadb/device.go +++ b/hrp/pkg/gadb/device.go @@ -11,6 +11,8 @@ import ( "time" "github.com/rs/zerolog/log" + + "github.com/httprunner/httprunner/v4/hrp/internal/builtin" ) type DeviceFileInfo struct { @@ -538,7 +540,7 @@ func (d *Device) InstallAPK(apk io.ReadSeeker) (string, error) { return string(raw), err } - remote := fmt.Sprintf("/data/local/tmp/gadb_remote_%d.apk", time.Now().Unix()) + remote := fmt.Sprintf("/data/local/tmp/%s.apk", builtin.GenNameWithTimestamp("gadb_remote_")) err := d.Push(apk, remote, time.Now()) if err != nil { return "", fmt.Errorf("error pushing: %v", err) diff --git a/hrp/pkg/uixt/ext.go b/hrp/pkg/uixt/ext.go index 093cb776..6b495370 100644 --- a/hrp/pkg/uixt/ext.go +++ b/hrp/pkg/uixt/ext.go @@ -217,7 +217,7 @@ type DriverExt struct { doneMjpegStream chan bool scale float64 ocrService OCRService // used to get text from image - ScreenShots []string // save screenshots path + screenShots []string // cache screenshot paths CVArgs } @@ -253,7 +253,9 @@ func NewDriverExt(device Device, driver WebDriver) (dExt *DriverExt, err error) return dExt, nil } -func (dExt *DriverExt) takeScreenShot() (raw *bytes.Buffer, err error) { +// TakeScreenShot takes screenshot and saves image file to $CWD/screenshots/ folder +// if fileName is empty, it will not save image file and only return raw image data +func (dExt *DriverExt) TakeScreenShot(fileName ...string) (raw *bytes.Buffer, err error) { // wait for action done time.Sleep(500 * time.Millisecond) @@ -263,14 +265,27 @@ func (dExt *DriverExt) takeScreenShot() (raw *bytes.Buffer, err error) { return dExt.frame, nil } if raw, err = dExt.Driver.Screenshot(); err != nil { - log.Error().Err(err).Msg("takeScreenShot failed") + log.Error().Err(err).Msg("capture screenshot data failed") return nil, err } + + // save screenshot to file + if len(fileName) > 0 && fileName[0] != "" { + path := filepath.Join(env.ScreenShotsPath, fileName[0]) + path, err := dExt.saveScreenShot(raw, path) + if err != nil { + log.Error().Err(err).Msg("save screenshot file failed") + return nil, err + } + dExt.screenShots = append(dExt.screenShots, path) + log.Info().Str("path", path).Msg("save screenshot file success") + } + return raw, nil } // saveScreenShot saves image file with file name -func saveScreenShot(raw *bytes.Buffer, fileName string) (string, error) { +func (dExt *DriverExt) saveScreenShot(raw *bytes.Buffer, fileName string) (string, error) { img, format, err := image.Decode(raw) if err != nil { return "", errors.Wrap(err, "decode screenshot image failed") @@ -302,19 +317,11 @@ func saveScreenShot(raw *bytes.Buffer, fileName string) (string, error) { return screenshotPath, nil } -// ScreenShot takes screenshot and saves image file to $CWD/screenshots/ folder -func (dExt *DriverExt) ScreenShot(fileName string) (string, error) { - raw, err := dExt.takeScreenShot() - if err != nil { - return "", errors.Wrap(err, "screenshot failed") - } - - fileName = filepath.Join(env.ScreenShotsPath, fileName) - path, err := saveScreenShot(raw, fileName) - if err != nil { - return "", errors.Wrap(err, "save screenshot failed") - } - return path, nil +func (dExt *DriverExt) GetScreenShots() []string { + defer func() { + dExt.screenShots = nil + }() + return dExt.screenShots } // isPathExists returns true if path exists, whether path is file or dir @@ -689,15 +696,9 @@ func (dExt *DriverExt) DoAction(action MobileAction) error { } } case CtlScreenShot: - // take snapshot - log.Info().Msg("take snapshot for current screen") - screenshotPath, err := dExt.ScreenShot(fmt.Sprintf("screenshot_%d", - time.Now().Unix())) - if err != nil { - return errors.Wrap(err, "take screenshot failed") - } - log.Info().Str("path", screenshotPath).Msg("take screenshot") - dExt.ScreenShots = append(dExt.ScreenShots, screenshotPath) + // take screenshot + log.Info().Msg("take screenshot for current screen") + _, err := dExt.TakeScreenShot(builtin.GenNameWithTimestamp("screenshot_")) return err case CtlStartCamera: return dExt.Driver.StartCamera() diff --git a/hrp/pkg/uixt/interface.go b/hrp/pkg/uixt/interface.go index 501e6862..7d6145b3 100644 --- a/hrp/pkg/uixt/interface.go +++ b/hrp/pkg/uixt/interface.go @@ -2,7 +2,6 @@ package uixt import ( "bytes" - "fmt" "math" "strings" "time" @@ -437,7 +436,6 @@ type DataOptions struct { IgnoreNotFoundError bool // ignore error if target element not found MaxRetryTimes int // max retry times if target element not found Interval float64 // interval between retries in seconds - ScreenShotFilename string // turn on screenshot and specify file name } type DataOption func(data *DataOptions) @@ -514,16 +512,6 @@ func WithDataWaitTime(sec float64) DataOption { } } -func WithScreenShot(fileName ...string) DataOption { - return func(data *DataOptions) { - if len(fileName) > 0 { - data.ScreenShotFilename = fileName[0] - } else { - data.ScreenShotFilename = fmt.Sprintf("screenshot_%d", time.Now().Unix()) - } - } -} - func NewDataOptions(options ...DataOption) *DataOptions { dataOptions := &DataOptions{ Data: make(map[string]interface{}), diff --git a/hrp/pkg/uixt/ocr_vedem.go b/hrp/pkg/uixt/ocr_vedem.go index f68964b6..48bb804a 100644 --- a/hrp/pkg/uixt/ocr_vedem.go +++ b/hrp/pkg/uixt/ocr_vedem.go @@ -175,14 +175,6 @@ func (s *veDEMOCRService) GetTexts(imageBuf *bytes.Buffer, options ...DataOption dataOptions := NewDataOptions(options...) - if dataOptions.ScreenShotFilename != "" { - path, err := saveScreenShot(imageBuf, dataOptions.ScreenShotFilename) - if err != nil { - return nil, errors.Wrap(err, "save screenshot failed") - } - log.Debug().Str("path", path).Msg("save screenshot") - } - for _, ocrResult := range ocrResults { rect := image.Rectangle{ // ocrResult.Points 顺序:左上 -> 右上 -> 右下 -> 左下 @@ -313,8 +305,7 @@ type OCRService interface { func (dExt *DriverExt) GetTextsByOCR(options ...DataOption) (texts OCRTexts, err error) { var bufSource *bytes.Buffer - if bufSource, err = dExt.takeScreenShot(); err != nil { - err = fmt.Errorf("takeScreenShot error: %v", err) + if bufSource, err = dExt.TakeScreenShot(builtin.GenNameWithTimestamp("ocr_")); err != nil { return } @@ -329,8 +320,7 @@ func (dExt *DriverExt) GetTextsByOCR(options ...DataOption) (texts OCRTexts, err func (dExt *DriverExt) FindTextByOCR(ocrText string, options ...DataOption) (x, y, width, height float64, err error) { var bufSource *bytes.Buffer - if bufSource, err = dExt.takeScreenShot(); err != nil { - err = fmt.Errorf("takeScreenShot error: %v", err) + if bufSource, err = dExt.TakeScreenShot(builtin.GenNameWithTimestamp("ocr_")); err != nil { return } @@ -348,8 +338,7 @@ func (dExt *DriverExt) FindTextByOCR(ocrText string, options ...DataOption) (x, func (dExt *DriverExt) FindTextsByOCR(ocrTexts []string, options ...DataOption) (points [][]float64, err error) { var bufSource *bytes.Buffer - if bufSource, err = dExt.takeScreenShot(); err != nil { - err = fmt.Errorf("takeScreenShot error: %v", err) + if bufSource, err = dExt.TakeScreenShot(builtin.GenNameWithTimestamp("ocr_")); err != nil { return } diff --git a/hrp/pkg/uixt/opencv.go b/hrp/pkg/uixt/opencv.go index 6a5b3bb8..95e10783 100644 --- a/hrp/pkg/uixt/opencv.go +++ b/hrp/pkg/uixt/opencv.go @@ -14,6 +14,8 @@ import ( "github.com/pkg/errors" "gocv.io/x/gocv" + + "github.com/httprunner/httprunner/v4/hrp/internal/builtin" ) const ( @@ -101,7 +103,7 @@ func (dExt *DriverExt) FindAllImageRect(search string) (rects []image.Rectangle, if bufSearch, err = getBufFromDisk(search); err != nil { return nil, err } - if bufSource, err = dExt.takeScreenShot(); err != nil { + if bufSource, err = dExt.TakeScreenShot(builtin.GenNameWithTimestamp("cv_")); err != nil { return nil, err } @@ -116,7 +118,7 @@ func (dExt *DriverExt) FindImageRectInUIKit(imagePath string, options ...DataOpt if bufSearch, err = getBufFromDisk(imagePath); err != nil { return 0, 0, 0, 0, err } - if bufSource, err = dExt.takeScreenShot(); err != nil { + if bufSource, err = dExt.TakeScreenShot(builtin.GenNameWithTimestamp("cv_")); err != nil { return 0, 0, 0, 0, err } diff --git a/hrp/step_mobile_ui.go b/hrp/step_mobile_ui.go index f5483488..6cfb90d1 100644 --- a/hrp/step_mobile_ui.go +++ b/hrp/step_mobile_ui.go @@ -2,11 +2,11 @@ package hrp import ( "fmt" - "time" "github.com/pkg/errors" "github.com/rs/zerolog/log" + "github.com/httprunner/httprunner/v4/hrp/internal/builtin" "github.com/httprunner/httprunner/v4/hrp/internal/code" "github.com/httprunner/httprunner/v4/hrp/pkg/uixt" ) @@ -572,7 +572,6 @@ func runStepMobileUI(s *SessionRunner, step *TStep) (stepResult *StepResult, err Success: false, ContentSize: 0, } - screenshots := make([]string, 0) // merge step variables with session variables stepVariables, err := s.ParseStepVariables(step.Variables) @@ -602,18 +601,14 @@ func runStepMobileUI(s *SessionRunner, step *TStep) (stepResult *StepResult, err } // take screenshot after each step - screenshotPath, err := uiDriver.ScreenShot( - fmt.Sprintf("step_%d", time.Now().Unix())) + _, err := uiDriver.TakeScreenShot( + builtin.GenNameWithTimestamp("step_") + "_" + step.Name) if err != nil { - log.Error().Err(err).Str("step", step.Name).Msg("take screenshot failed") - } else { - log.Info().Str("path", screenshotPath).Msg("take screenshot on step finished") - screenshots = append(screenshots, screenshotPath) + log.Error().Err(err).Str("step", step.Name).Msg("take screenshot failed on step finished") } // save attachments - screenshots = append(screenshots, uiDriver.ScreenShots...) - attachments["screenshots"] = screenshots + attachments["screenshots"] = uiDriver.GetScreenShots() stepResult.Attachments = attachments }() From a988a2341f4ddbe7881d53a4e6f9ca21b73eb01e Mon Sep 17 00:00:00 2001 From: "lilong.129" Date: Mon, 17 Apr 2023 14:38:07 +0800 Subject: [PATCH 36/52] change: update changelog --- docs/CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 723ca0d4..01e44971 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -10,11 +10,12 @@ - feat: add `IsAppInForeground` to check if the given package is in foreground - feat: check if app is in foreground when step failed - feat: add validator AssertAppInForeground and AssertAppNotInForeground +- feat: save screenshots of all steps including ocr and cv recognition process data - fix: adb driver for TapFloat - fix: stop logcat only when enabled - fix: do not fail case when kill logcat error - fix: take screenshot after each step -- fix: screencap compatibility for shell v1 and v2 +- fix: screencap compatibility for shell v1 and v2 protocol ## v4.3.2 (2022-12-26) From 58f1fec2faacd75383192dfc61a338e004d6a262 Mon Sep 17 00:00:00 2001 From: "lilong.129" Date: Mon, 17 Apr 2023 22:45:58 +0800 Subject: [PATCH 37/52] change: GenNameWithTimestamp --- .../demo_android_feed_random_swipe.json | 134 ++++++++++++++++++ ...=> demo_android_feed_random_swipe_test.go} | 2 +- examples/uitest/demo_feed_random_slide.json | 2 +- ...yin_live.json => demo_ios_live_swipe.json} | 0 ...in_test.go => demo_ios_live_swipe_test.go} | 5 +- hrp/cmd/adb/screencap.go | 2 +- hrp/internal/builtin/utils.go | 7 +- hrp/pkg/gadb/device.go | 2 +- hrp/pkg/uixt/ext.go | 2 +- hrp/pkg/uixt/ocr_vedem.go | 6 +- hrp/pkg/uixt/opencv.go | 4 +- hrp/step_mobile_ui.go | 2 +- 12 files changed, 151 insertions(+), 17 deletions(-) create mode 100644 examples/uitest/demo_android_feed_random_swipe.json rename examples/uitest/{demo_feed_random_slide_test.go => demo_android_feed_random_swipe_test.go} (94%) rename examples/uitest/{demo_douyin_live.json => demo_ios_live_swipe.json} (100%) rename examples/uitest/{demo_douyin_test.go => demo_ios_live_swipe_test.go} (91%) diff --git a/examples/uitest/demo_android_feed_random_swipe.json b/examples/uitest/demo_android_feed_random_swipe.json new file mode 100644 index 00000000..d74f45ae --- /dev/null +++ b/examples/uitest/demo_android_feed_random_swipe.json @@ -0,0 +1,134 @@ +{ + "config": { + "name": "点播_抖音_滑动场景_随机间隔_android", + "variables": { + "device": "${ENV(SerialNumber)}" + }, + "android": [ + { + "serial": "$device" + } + ] + }, + "teststeps": [ + { + "name": "启动抖音", + "android": { + "actions": [ + { + "method": "app_terminate", + "params": "com.ss.android.ugc.aweme" + }, + { + "method": "app_launch", + "params": "com.ss.android.ugc.aweme" + }, + { + "method": "sleep", + "params": 10 + } + ] + }, + "validate": [ + { + "check": "ui_foreground_app", + "assert": "equal", + "expect": "com.ss.android.ugc.aweme", + "msg": "app [com.ss.android.ugc.aweme] should be in foreground" + } + ] + }, + { + "name": "处理青少年弹窗", + "android": { + "actions": [ + { + "method": "tap_ocr", + "params": "我知道了", + "ignore_NotFoundError": true + } + ] + } + }, + { + "name": "滑动 Feed 3 次,随机间隔 0-5s", + "android": { + "actions": [ + { + "method": "swipe", + "params": "up" + }, + { + "method": "sleep_random", + "params": [ + 0, + 5 + ] + } + ] + }, + "loops": 3 + }, + { + "name": "滑动 Feed 1 次,随机间隔 5-10s", + "android": { + "actions": [ + { + "method": "swipe", + "params": "up" + }, + { + "method": "sleep_random", + "params": [ + 5, + 10 + ] + } + ] + }, + "loops": 1 + }, + { + "name": "滑动 Feed 10 次,70% 随机间隔 0-5s,30% 随机间隔 5-10s", + "android": { + "actions": [ + { + "method": "swipe", + "params": "up" + }, + { + "method": "sleep_random", + "params": [ + 0, + 5, + 0.7, + 5, + 10, + 0.3 + ] + } + ] + }, + "loops": 10 + }, + { + "name": "exit", + "android": { + "actions": [ + { + "method": "app_terminate", + "params": "com.ss.android.ugc.aweme" + } + ] + }, + "validate": [ + { + "check": "ui_foreground_app", + "assert": "not_equal", + "expect": "com.ss.android.ugc.aweme", + "msg": "app [com.ss.android.ugc.aweme] should not be in foreground" + } + ] + } + ] +} diff --git a/examples/uitest/demo_feed_random_slide_test.go b/examples/uitest/demo_android_feed_random_swipe_test.go similarity index 94% rename from examples/uitest/demo_feed_random_slide_test.go rename to examples/uitest/demo_android_feed_random_swipe_test.go index 98311c69..a8d7392e 100644 --- a/examples/uitest/demo_feed_random_slide_test.go +++ b/examples/uitest/demo_android_feed_random_swipe_test.go @@ -50,7 +50,7 @@ func TestAndroidDouyinFeedTest(t *testing.T) { }, } - if err := testCase.Dump2JSON("demo_feed_random_slide.json"); err != nil { + if err := testCase.Dump2JSON("demo_android_feed_random_swipe.json"); err != nil { t.Fatal(err) } diff --git a/examples/uitest/demo_feed_random_slide.json b/examples/uitest/demo_feed_random_slide.json index d74f45ae..f256eaf4 100644 --- a/examples/uitest/demo_feed_random_slide.json +++ b/examples/uitest/demo_feed_random_slide.json @@ -109,7 +109,7 @@ } ] }, - "loops": 10 + "loops": 2 }, { "name": "exit", diff --git a/examples/uitest/demo_douyin_live.json b/examples/uitest/demo_ios_live_swipe.json similarity index 100% rename from examples/uitest/demo_douyin_live.json rename to examples/uitest/demo_ios_live_swipe.json diff --git a/examples/uitest/demo_douyin_test.go b/examples/uitest/demo_ios_live_swipe_test.go similarity index 91% rename from examples/uitest/demo_douyin_test.go rename to examples/uitest/demo_ios_live_swipe_test.go index def2e9d3..575edc58 100644 --- a/examples/uitest/demo_douyin_test.go +++ b/examples/uitest/demo_ios_live_swipe_test.go @@ -45,10 +45,7 @@ func TestIOSDouyinLive(t *testing.T) { }, } - if err := testCase.Dump2JSON("demo_douyin_live.json"); err != nil { - t.Fatal(err) - } - if err := testCase.Dump2YAML("demo_douyin_live.yaml"); err != nil { + if err := testCase.Dump2JSON("demo_ios_live_swipe.json"); err != nil { t.Fatal(err) } diff --git a/hrp/cmd/adb/screencap.go b/hrp/cmd/adb/screencap.go index bb1ddf6c..8147e0c4 100644 --- a/hrp/cmd/adb/screencap.go +++ b/hrp/cmd/adb/screencap.go @@ -23,7 +23,7 @@ var screencapAndroidDevicesCmd = &cobra.Command{ return err } - filepath := fmt.Sprintf("%s.png", builtin.GenNameWithTimestamp("screencap_")) + filepath := fmt.Sprintf("%s.png", builtin.GenNameWithTimestamp("screencap_%d")) if err = ioutil.WriteFile(filepath, res, 0o644); err != nil { return err } diff --git a/hrp/internal/builtin/utils.go b/hrp/internal/builtin/utils.go index a27ac366..b88f7902 100644 --- a/hrp/internal/builtin/utils.go +++ b/hrp/internal/builtin/utils.go @@ -444,6 +444,9 @@ func Sign(ver string, ak string, sk string, body []byte) string { return fmt.Sprintf("%v/%v", signKeyInfo, string(signResult)) } -func GenNameWithTimestamp(prefix string) string { - return fmt.Sprintf("%s%d", prefix, time.Now().Unix()) +func GenNameWithTimestamp(tmpl string) string { + if !strings.Contains(tmpl, "%d") { + tmpl = tmpl + "_%d" + } + return fmt.Sprintf(tmpl, time.Now().Unix()) } diff --git a/hrp/pkg/gadb/device.go b/hrp/pkg/gadb/device.go index dfac9042..2067755a 100644 --- a/hrp/pkg/gadb/device.go +++ b/hrp/pkg/gadb/device.go @@ -540,7 +540,7 @@ func (d *Device) InstallAPK(apk io.ReadSeeker) (string, error) { return string(raw), err } - remote := fmt.Sprintf("/data/local/tmp/%s.apk", builtin.GenNameWithTimestamp("gadb_remote_")) + remote := fmt.Sprintf("/data/local/tmp/%s.apk", builtin.GenNameWithTimestamp("gadb_remote_%d")) err := d.Push(apk, remote, time.Now()) if err != nil { return "", fmt.Errorf("error pushing: %v", err) diff --git a/hrp/pkg/uixt/ext.go b/hrp/pkg/uixt/ext.go index 6b495370..0d296577 100644 --- a/hrp/pkg/uixt/ext.go +++ b/hrp/pkg/uixt/ext.go @@ -698,7 +698,7 @@ func (dExt *DriverExt) DoAction(action MobileAction) error { case CtlScreenShot: // take screenshot log.Info().Msg("take screenshot for current screen") - _, err := dExt.TakeScreenShot(builtin.GenNameWithTimestamp("screenshot_")) + _, err := dExt.TakeScreenShot(builtin.GenNameWithTimestamp("screenshot_%d")) return err case CtlStartCamera: return dExt.Driver.StartCamera() diff --git a/hrp/pkg/uixt/ocr_vedem.go b/hrp/pkg/uixt/ocr_vedem.go index 48bb804a..15db742d 100644 --- a/hrp/pkg/uixt/ocr_vedem.go +++ b/hrp/pkg/uixt/ocr_vedem.go @@ -305,7 +305,7 @@ type OCRService interface { func (dExt *DriverExt) GetTextsByOCR(options ...DataOption) (texts OCRTexts, err error) { var bufSource *bytes.Buffer - if bufSource, err = dExt.TakeScreenShot(builtin.GenNameWithTimestamp("ocr_")); err != nil { + if bufSource, err = dExt.TakeScreenShot(builtin.GenNameWithTimestamp("step_%d_ocr")); err != nil { return } @@ -320,7 +320,7 @@ func (dExt *DriverExt) GetTextsByOCR(options ...DataOption) (texts OCRTexts, err func (dExt *DriverExt) FindTextByOCR(ocrText string, options ...DataOption) (x, y, width, height float64, err error) { var bufSource *bytes.Buffer - if bufSource, err = dExt.TakeScreenShot(builtin.GenNameWithTimestamp("ocr_")); err != nil { + if bufSource, err = dExt.TakeScreenShot(builtin.GenNameWithTimestamp("step_%d_ocr")); err != nil { return } @@ -338,7 +338,7 @@ func (dExt *DriverExt) FindTextByOCR(ocrText string, options ...DataOption) (x, func (dExt *DriverExt) FindTextsByOCR(ocrTexts []string, options ...DataOption) (points [][]float64, err error) { var bufSource *bytes.Buffer - if bufSource, err = dExt.TakeScreenShot(builtin.GenNameWithTimestamp("ocr_")); err != nil { + if bufSource, err = dExt.TakeScreenShot(builtin.GenNameWithTimestamp("step_%d_ocr")); err != nil { return } diff --git a/hrp/pkg/uixt/opencv.go b/hrp/pkg/uixt/opencv.go index 95e10783..98f7286b 100644 --- a/hrp/pkg/uixt/opencv.go +++ b/hrp/pkg/uixt/opencv.go @@ -103,7 +103,7 @@ func (dExt *DriverExt) FindAllImageRect(search string) (rects []image.Rectangle, if bufSearch, err = getBufFromDisk(search); err != nil { return nil, err } - if bufSource, err = dExt.TakeScreenShot(builtin.GenNameWithTimestamp("cv_")); err != nil { + if bufSource, err = dExt.TakeScreenShot(builtin.GenNameWithTimestamp("step_%d_cv")); err != nil { return nil, err } @@ -118,7 +118,7 @@ func (dExt *DriverExt) FindImageRectInUIKit(imagePath string, options ...DataOpt if bufSearch, err = getBufFromDisk(imagePath); err != nil { return 0, 0, 0, 0, err } - if bufSource, err = dExt.TakeScreenShot(builtin.GenNameWithTimestamp("cv_")); err != nil { + if bufSource, err = dExt.TakeScreenShot(builtin.GenNameWithTimestamp("step_%d_cv")); err != nil { return 0, 0, 0, 0, err } diff --git a/hrp/step_mobile_ui.go b/hrp/step_mobile_ui.go index 6cfb90d1..ff8b3a2c 100644 --- a/hrp/step_mobile_ui.go +++ b/hrp/step_mobile_ui.go @@ -602,7 +602,7 @@ func runStepMobileUI(s *SessionRunner, step *TStep) (stepResult *StepResult, err // take screenshot after each step _, err := uiDriver.TakeScreenShot( - builtin.GenNameWithTimestamp("step_") + "_" + step.Name) + builtin.GenNameWithTimestamp("step_%d_") + step.Name) if err != nil { log.Error().Err(err).Str("step", step.Name).Msg("take screenshot failed on step finished") } From 1b7ea07611185779a2cb30b0cebada3ad5a6ef48 Mon Sep 17 00:00:00 2001 From: ywb Date: Tue, 18 Apr 2023 16:05:26 +0800 Subject: [PATCH 38/52] fix: ref testcase, generate report failed (#1547) --- hrp/runner.go | 42 +++++++++++++++++++++++++++++++----------- 1 file changed, 31 insertions(+), 11 deletions(-) diff --git a/hrp/runner.go b/hrp/runner.go index d81a3022..374fc1be 100644 --- a/hrp/runner.go +++ b/hrp/runner.go @@ -524,17 +524,7 @@ func (r *SessionRunner) Start(givenVars map[string]interface{}) error { stepResult, err = step.Run(r) stepResult.Name = stepName + loopIndex - // update summary - r.summary.Records = append(r.summary.Records, stepResult) - } - - r.summary.Stat.Total += 1 - if stepResult.Success { - r.summary.Stat.Successes += 1 - } else { - r.summary.Stat.Failures += 1 - // update summary result to failed - r.summary.Success = false + r.updateSummary(stepResult) } // update extracted variables @@ -646,3 +636,33 @@ func (r *SessionRunner) GetSummary() (*TestCaseSummary, error) { return caseSummary, nil } + +// updateSummary updates summary of StepResult. +func (r *SessionRunner) updateSummary(stepResult *StepResult) { + switch stepResult.StepType { + case stepTypeTestCase: + // record requests of testcase step + if records, ok := stepResult.Data.([]*StepResult); ok { + for _, result := range records { + r.addSingleStepResult(result) + } + } else { + r.addSingleStepResult(stepResult) + } + default: + r.addSingleStepResult(stepResult) + } +} + +func (r *SessionRunner) addSingleStepResult(stepResult *StepResult) { + // update summary + r.summary.Records = append(r.summary.Records, stepResult) + r.summary.Stat.Total += 1 + if stepResult.Success { + r.summary.Stat.Successes += 1 + } else { + r.summary.Stat.Failures += 1 + // update summary result to failed + r.summary.Success = false + } +} From 419620fad359114a138a0da18ae0fc042945cb25 Mon Sep 17 00:00:00 2001 From: ywb Date: Tue, 18 Apr 2023 16:23:20 +0800 Subject: [PATCH 39/52] fix: fast fail , not closing the websocket connection --- hrp/boomer.go | 4 ++++ hrp/runner.go | 32 +++++++++++++++++++------------- 2 files changed, 23 insertions(+), 13 deletions(-) diff --git a/hrp/boomer.go b/hrp/boomer.go index 4e3ecef5..0f35e34d 100644 --- a/hrp/boomer.go +++ b/hrp/boomer.go @@ -361,6 +361,10 @@ func (b *HRPBoomer) convertBoomerTask(testcase *TestCase, rendezvousList []*Rend } mutex.Unlock() + defer func() { + sessionRunner.releaseResources() + }() + startTime := time.Now() for _, step := range testcase.TestSteps { // TODO: parse step struct diff --git a/hrp/runner.go b/hrp/runner.go index d81a3022..dbb3b6b3 100644 --- a/hrp/runner.go +++ b/hrp/runner.go @@ -488,6 +488,11 @@ func (r *SessionRunner) Start(givenVars map[string]interface{}) error { // update config variables with given variables r.InitWithParameters(givenVars) + defer func() { + // close session resource after all steps done or fast fail + r.releaseResources() + }() + // run step in sequential order for _, step := range r.caseRunner.testCase.TestSteps { // TODO: parse step struct @@ -563,19 +568,6 @@ func (r *SessionRunner) Start(givenVars map[string]interface{}) error { } } - // close websocket connection after all steps done - defer func() { - for _, wsConn := range r.wsConnMap { - if wsConn != nil { - log.Info().Str("testcase", config.Name).Msg("websocket disconnected") - err := wsConn.Close() - if err != nil { - log.Error().Err(err).Msg("websocket disconnection failed") - } - } - } - }() - log.Info().Str("testcase", config.Name).Msg("run testcase end") return nil } @@ -646,3 +638,17 @@ func (r *SessionRunner) GetSummary() (*TestCaseSummary, error) { return caseSummary, nil } + +// releaseResources releases resources used by session runner +func (r *SessionRunner) releaseResources() { + // close websocket connections + for _, wsConn := range r.wsConnMap { + if wsConn != nil { + log.Info().Str("testcase", r.caseRunner.testCase.Config.Name).Msg("websocket disconnected") + err := wsConn.Close() + if err != nil { + log.Error().Err(err).Msg("websocket disconnection failed") + } + } + } +} From 9d9751ab4df135e789d2237935b6cba0f6c2eafd Mon Sep 17 00:00:00 2001 From: ywb Date: Tue, 18 Apr 2023 17:37:33 +0800 Subject: [PATCH 40/52] fix: report html display the value of the url instead of variables --- hrp/step_request.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hrp/step_request.go b/hrp/step_request.go index 12a8ffbb..5f23db87 100644 --- a/hrp/step_request.go +++ b/hrp/step_request.go @@ -187,6 +187,9 @@ func (r *requestBuilder) prepareUrlParams(stepVariables map[string]interface{}) r.req.URL = u r.req.Host = u.Host + // update url + r.requestMap["url"] = u + return nil } From 409808f297f6e3f643b52a0cf320362aee0a7a1d Mon Sep 17 00:00:00 2001 From: "lilong.129" Date: Tue, 18 Apr 2023 19:33:15 +0800 Subject: [PATCH 41/52] change: update examples --- examples/uitest/demo_android_douyin_test.go | 44 ----------- ...wipe.json => demo_android_feed_swipe.json} | 0 ...est.go => demo_android_feed_swipe_test.go} | 2 +- ...lide.json => demo_android_live_swipe.json} | 78 ++++++++++--------- .../uitest/demo_android_live_swipe_test.go | 59 ++++++++++++++ 5 files changed, 102 insertions(+), 81 deletions(-) delete mode 100644 examples/uitest/demo_android_douyin_test.go rename examples/uitest/{demo_android_feed_random_swipe.json => demo_android_feed_swipe.json} (100%) rename examples/uitest/{demo_android_feed_random_swipe_test.go => demo_android_feed_swipe_test.go} (94%) rename examples/uitest/{demo_feed_random_slide.json => demo_android_live_swipe.json} (73%) create mode 100644 examples/uitest/demo_android_live_swipe_test.go diff --git a/examples/uitest/demo_android_douyin_test.go b/examples/uitest/demo_android_douyin_test.go deleted file mode 100644 index 7d7c94f4..00000000 --- a/examples/uitest/demo_android_douyin_test.go +++ /dev/null @@ -1,44 +0,0 @@ -//go:build localtest - -package uitest - -import ( - "testing" - - "github.com/httprunner/httprunner/v4/hrp" - "github.com/httprunner/httprunner/v4/hrp/pkg/uixt" -) - -func TestAndroidDouYinLive(t *testing.T) { - testCase := &hrp.TestCase{ - Config: hrp.NewConfig("通过 feed 头像进入抖音直播间"). - SetAndroid(uixt.WithUIA2(false), uixt.WithAdbLogOn(true)), - TestSteps: []hrp.IStep{ - hrp.NewStep("启动抖音"). - Android(). - Home(). - AppTerminate("com.ss.android.ugc.aweme"). // 关闭已运行的抖音,确保启动抖音后在「抖音」首页 - SwipeToTapApp("抖音", uixt.WithMaxRetryTimes(5)). - Sleep(10), - hrp.NewStep("处理青少年弹窗"). - Android(). - Tap("推荐"). - TapByOCR("我知道了", uixt.WithIgnoreNotFoundError(true)). - Validate(). - AssertOCRExists("首页", "抖音启动失败,「首页」不存在"), - hrp.NewStep("在推荐页上划,直到出现 feed 头像「直播」"). - Android(). - SwipeToTapText("直播", uixt.WithMaxRetryTimes(10), uixt.WithIdentifier("进入直播间")), - hrp.NewStep("向上滑动,等待 10s"). - Android(). - SwipeUp(uixt.WithIdentifier("第一次上划")).Sleep(10).ScreenShot(). // 上划 1 次,等待 10s,截图保存 - SwipeUp(uixt.WithIdentifier("第二次上划")).Sleep(10).ScreenShot(), // 再上划 1 次,等待 10s,截图保存 - }, - } - - runner := hrp.NewRunner(t).SetSaveTests(true) - err := runner.Run(testCase) - if err != nil { - t.Fatal(err) - } -} diff --git a/examples/uitest/demo_android_feed_random_swipe.json b/examples/uitest/demo_android_feed_swipe.json similarity index 100% rename from examples/uitest/demo_android_feed_random_swipe.json rename to examples/uitest/demo_android_feed_swipe.json diff --git a/examples/uitest/demo_android_feed_random_swipe_test.go b/examples/uitest/demo_android_feed_swipe_test.go similarity index 94% rename from examples/uitest/demo_android_feed_random_swipe_test.go rename to examples/uitest/demo_android_feed_swipe_test.go index a8d7392e..c2a024c3 100644 --- a/examples/uitest/demo_android_feed_random_swipe_test.go +++ b/examples/uitest/demo_android_feed_swipe_test.go @@ -50,7 +50,7 @@ func TestAndroidDouyinFeedTest(t *testing.T) { }, } - if err := testCase.Dump2JSON("demo_android_feed_random_swipe.json"); err != nil { + if err := testCase.Dump2JSON("demo_android_feed_swipe.json"); err != nil { t.Fatal(err) } diff --git a/examples/uitest/demo_feed_random_slide.json b/examples/uitest/demo_android_live_swipe.json similarity index 73% rename from examples/uitest/demo_feed_random_slide.json rename to examples/uitest/demo_android_live_swipe.json index f256eaf4..3912f1ca 100644 --- a/examples/uitest/demo_feed_random_slide.json +++ b/examples/uitest/demo_android_live_swipe.json @@ -25,7 +25,7 @@ }, { "method": "sleep", - "params": 10 + "params": 5 } ] }, @@ -51,45 +51,20 @@ } }, { - "name": "滑动 Feed 3 次,随机间隔 0-5s", + "name": "在推荐页上划,直到出现「点击进入直播间」", "android": { "actions": [ { - "method": "swipe", - "params": "up" - }, - { - "method": "sleep_random", - "params": [ - 0, - 5 - ] + "method": "swipe_to_tap_text", + "params": "点击进入直播间", + "identifier": "进入直播间", + "max_retry_times": 10 } ] - }, - "loops": 3 + } }, { - "name": "滑动 Feed 1 次,随机间隔 5-10s", - "android": { - "actions": [ - { - "method": "swipe", - "params": "up" - }, - { - "method": "sleep_random", - "params": [ - 5, - 10 - ] - } - ] - }, - "loops": 1 - }, - { - "name": "滑动 Feed 10 次,70% 随机间隔 0-5s,30% 随机间隔 5-10s", + "name": "滑动 Feed 5 次,60% 随机间隔 0-5s,40% 随机间隔 5-10s", "android": { "actions": [ { @@ -101,15 +76,46 @@ "params": [ 0, 5, - 0.7, + 0.6, 5, 10, - 0.3 + 0.4 ] } ] }, - "loops": 2 + "loops": 5 + }, + { + "name": "向上滑动,等待 10s", + "android": { + "actions": [ + { + "method": "swipe", + "params": "up", + "identifier": "第一次上划" + }, + { + "method": "sleep", + "params": 10 + }, + { + "method": "screenshot" + }, + { + "method": "swipe", + "params": "up", + "identifier": "第二次上划" + }, + { + "method": "sleep", + "params": 10 + }, + { + "method": "screenshot" + } + ] + } }, { "name": "exit", diff --git a/examples/uitest/demo_android_live_swipe_test.go b/examples/uitest/demo_android_live_swipe_test.go new file mode 100644 index 00000000..500aff1d --- /dev/null +++ b/examples/uitest/demo_android_live_swipe_test.go @@ -0,0 +1,59 @@ +//go:build localtest + +package uitest + +import ( + "testing" + + "github.com/httprunner/httprunner/v4/hrp" + "github.com/httprunner/httprunner/v4/hrp/pkg/uixt" +) + +func TestAndroidLiveSwipeTest(t *testing.T) { + testCase := &hrp.TestCase{ + Config: hrp.NewConfig("点播_抖音_滑动场景_随机间隔_android"). + WithVariables(map[string]interface{}{ + "device": "${ENV(SerialNumber)}", + }). + SetAndroid(uixt.WithSerialNumber("$device")), + TestSteps: []hrp.IStep{ + hrp.NewStep("启动抖音"). + Android(). + AppTerminate("com.ss.android.ugc.aweme"). + AppLaunch("com.ss.android.ugc.aweme"). + Sleep(5). + Validate(). + AssertAppInForeground("com.ss.android.ugc.aweme"), + hrp.NewStep("处理青少年弹窗"). + Android(). + TapByOCR("我知道了", uixt.WithIgnoreNotFoundError(true)), + hrp.NewStep("在推荐页上划,直到出现「点击进入直播间」"). + Android(). + SwipeToTapText("点击进入直播间", uixt.WithMaxRetryTimes(10), uixt.WithIdentifier("进入直播间")), + hrp.NewStep("滑动 Feed 5 次,60% 随机间隔 0-5s,40% 随机间隔 5-10s"). + Loop(5). + Android(). + SwipeUp(). + SleepRandom(0, 5, 0.6, 5, 10, 0.4), + hrp.NewStep("向上滑动,等待 10s"). + Android(). + SwipeUp(uixt.WithIdentifier("第一次上划")).Sleep(10).ScreenShot(). // 上划 1 次,等待 10s,截图保存 + SwipeUp(uixt.WithIdentifier("第二次上划")).Sleep(10).ScreenShot(), // 再上划 1 次,等待 10s,截图保存 + hrp.NewStep("exit"). + Android(). + AppTerminate("com.ss.android.ugc.aweme"). + Validate(). + AssertAppNotInForeground("com.ss.android.ugc.aweme"), + }, + } + + if err := testCase.Dump2JSON("demo_android_live_swipe.json"); err != nil { + t.Fatal(err) + } + + runner := hrp.NewRunner(t).SetSaveTests(true) + err := runner.Run(testCase) + if err != nil { + t.Fatal(err) + } +} From 6c89236941dab45d6b210d52131670213a0d1df1 Mon Sep 17 00:00:00 2001 From: "lilong.129" Date: Tue, 18 Apr 2023 19:59:52 +0800 Subject: [PATCH 42/52] fix: copy screenshot buffer for OCR recognizing --- docs/CHANGELOG.md | 2 +- hrp/internal/version/VERSION | 2 +- hrp/pkg/uixt/ext.go | 8 +++++++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 01e44971..905caa26 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -1,6 +1,6 @@ # Release History -## v4.3.3 (2023-04-16) +## v4.3.3 (2023-04-18) **go version** diff --git a/hrp/internal/version/VERSION b/hrp/internal/version/VERSION index e73aa79d..2167abfb 100644 --- a/hrp/internal/version/VERSION +++ b/hrp/internal/version/VERSION @@ -1 +1 @@ -v4.3.3.2304161855 \ No newline at end of file +v4.3.3.2304181958 \ No newline at end of file diff --git a/hrp/pkg/uixt/ext.go b/hrp/pkg/uixt/ext.go index 0d296577..bff5f541 100644 --- a/hrp/pkg/uixt/ext.go +++ b/hrp/pkg/uixt/ext.go @@ -286,7 +286,13 @@ func (dExt *DriverExt) TakeScreenShot(fileName ...string) (raw *bytes.Buffer, er // saveScreenShot saves image file with file name func (dExt *DriverExt) saveScreenShot(raw *bytes.Buffer, fileName string) (string, error) { - img, format, err := image.Decode(raw) + // notice: screenshot data is a stream, so we need to copy it to a new buffer + copiedBuffer := &bytes.Buffer{} + if _, err := copiedBuffer.Write(raw.Bytes()); err != nil { + log.Error().Err(err).Msg("copy screenshot buffer failed") + } + + img, format, err := image.Decode(copiedBuffer) if err != nil { return "", errors.Wrap(err, "decode screenshot image failed") } From a6baeb1dd62271711a1704258df58b6ad7bbfd0c Mon Sep 17 00:00:00 2001 From: "lilong.129" Date: Tue, 18 Apr 2023 20:14:40 +0800 Subject: [PATCH 43/52] fix: change httpbin.org from http to https --- examples/httpbin/validate.yml | 2 +- examples/httpbin/validate_test.py | 2 +- hrp/boomer_test.go | 2 +- hrp/pkg/convert/from_curl_test.go | 2 +- hrp/runner_test.go | 4 ++-- hrp/step_request_test.go | 8 ++++---- httprunner/client_test.py | 6 +++--- httprunner/ext/uploader/__init__.py | 10 ++++++---- 8 files changed, 19 insertions(+), 17 deletions(-) diff --git a/examples/httpbin/validate.yml b/examples/httpbin/validate.yml index d5769a7b..f18f14e9 100644 --- a/examples/httpbin/validate.yml +++ b/examples/httpbin/validate.yml @@ -1,6 +1,6 @@ config: name: basic test with httpbin - base_url: http://httpbin.org/ + base_url: https://httpbin.org/ teststeps: - diff --git a/examples/httpbin/validate_test.py b/examples/httpbin/validate_test.py index 18f38d2e..44d5a4d2 100644 --- a/examples/httpbin/validate_test.py +++ b/examples/httpbin/validate_test.py @@ -5,7 +5,7 @@ from httprunner import HttpRunner, Config, Step, RunRequest class TestCaseValidate(HttpRunner): - config = Config("basic test with httpbin").base_url("http://httpbin.org/") + config = Config("basic test with httpbin").base_url("https://httpbin.org/") teststeps = [ Step( diff --git a/hrp/boomer_test.go b/hrp/boomer_test.go index 83151b5e..9eadc91a 100644 --- a/hrp/boomer_test.go +++ b/hrp/boomer_test.go @@ -10,7 +10,7 @@ func TestBoomerStandaloneRun(t *testing.T) { defer removeHashicorpGoPlugin() testcase1 := &TestCase{ - Config: NewConfig("TestCase1").SetBaseURL("http://httpbin.org"), + Config: NewConfig("TestCase1").SetBaseURL("https://httpbin.org"), TestSteps: []IStep{ NewStep("headers"). GET("/headers"). diff --git a/hrp/pkg/convert/from_curl_test.go b/hrp/pkg/convert/from_curl_test.go index a9b5f4f2..63c036a5 100644 --- a/hrp/pkg/convert/from_curl_test.go +++ b/hrp/pkg/convert/from_curl_test.go @@ -24,7 +24,7 @@ func TestLoadCurlCase(t *testing.T) { if !assert.EqualValues(t, "GET", tCase.TestSteps[0].Request.Method) { t.Fatal() } - if !assert.Equal(t, "http://httpbin.org", tCase.TestSteps[0].Request.URL) { + if !assert.Equal(t, "https://httpbin.org", tCase.TestSteps[0].Request.URL) { t.Fatal() } diff --git a/hrp/runner_test.go b/hrp/runner_test.go index c07cf1f1..9a0280bb 100644 --- a/hrp/runner_test.go +++ b/hrp/runner_test.go @@ -63,7 +63,7 @@ func assertRunTestCases(t *testing.T) { refCase := TestCasePath(demoTestCaseWithPluginJSONPath) testcase1 := &TestCase{ Config: NewConfig("TestCase1"). - SetBaseURL("http://httpbin.org"), + SetBaseURL("https://httpbin.org"), TestSteps: []IStep{ NewStep("testcase1-step1"). GET("/headers"). @@ -77,7 +77,7 @@ func assertRunTestCases(t *testing.T) { AssertEqual("headers.\"Content-Type\"", "application/json", "check http response Content-Type"), NewStep("testcase1-step3").CallRefCase( &TestCase{ - Config: NewConfig("testcase1-step3-ref-case").SetBaseURL("http://httpbin.org"), + Config: NewConfig("testcase1-step3-ref-case").SetBaseURL("https://httpbin.org"), TestSteps: []IStep{ NewStep("ip"). GET("/ip"). diff --git a/hrp/step_request_test.go b/hrp/step_request_test.go index 3f7e63c5..04970ea7 100644 --- a/hrp/step_request_test.go +++ b/hrp/step_request_test.go @@ -153,7 +153,7 @@ func TestRunRequestStatOn(t *testing.T) { if !assert.Greater(t, stat["Total"], int64(1)) { t.Fatal() } - if !assert.Less(t, stat["Total"]-summary.Records[0].Elapsed, int64(3)) { + if !assert.Less(t, stat["Total"]-summary.Records[0].Elapsed, int64(100)) { t.Fatal() } } @@ -165,7 +165,7 @@ func TestRunCaseWithTimeout(t *testing.T) { testcase1 := &TestCase{ Config: NewConfig("TestCase1"). SetTimeout(2 * time.Second). // set global timeout to 2s - SetBaseURL("http://httpbin.org"), + SetBaseURL("https://httpbin.org"), TestSteps: []IStep{ NewStep("step1"). GET("/delay/1"). @@ -181,7 +181,7 @@ func TestRunCaseWithTimeout(t *testing.T) { testcase2 := &TestCase{ Config: NewConfig("TestCase2"). SetTimeout(2 * time.Second). // set global timeout to 2s - SetBaseURL("http://httpbin.org"), + SetBaseURL("https://httpbin.org"), TestSteps: []IStep{ NewStep("step1"). GET("/delay/3"). @@ -198,7 +198,7 @@ func TestRunCaseWithTimeout(t *testing.T) { testcase3 := &TestCase{ Config: NewConfig("TestCase3"). SetTimeout(2 * time.Second). - SetBaseURL("http://httpbin.org"), + SetBaseURL("https://httpbin.org"), TestSteps: []IStep{ NewStep("step2"). GET("/delay/3"). diff --git a/httprunner/client_test.py b/httprunner/client_test.py index 467d4246..3e3175e0 100644 --- a/httprunner/client_test.py +++ b/httprunner/client_test.py @@ -8,7 +8,7 @@ class TestHttpSession(unittest.TestCase): self.session = HttpSession() def test_request_http(self): - self.session.request("get", "http://httpbin.org/get") + self.session.request("get", "https://httpbin.org/get") address = self.session.data.address self.assertGreater(len(address.server_ip), 0) self.assertEqual(address.server_port, 80) @@ -26,7 +26,7 @@ class TestHttpSession(unittest.TestCase): def test_request_http_allow_redirects(self): self.session.request( "get", - "http://httpbin.org/redirect-to?url=https%3A%2F%2Fgithub.com", + "https://httpbin.org/redirect-to?url=https%3A%2F%2Fgithub.com", allow_redirects=True, ) address = self.session.data.address @@ -50,7 +50,7 @@ class TestHttpSession(unittest.TestCase): def test_request_http_not_allow_redirects(self): self.session.request( "get", - "http://httpbin.org/redirect-to?url=https%3A%2F%2Fgithub.com", + "https://httpbin.org/redirect-to?url=https%3A%2F%2Fgithub.com", allow_redirects=False, ) address = self.session.data.address diff --git a/httprunner/ext/uploader/__init__.py b/httprunner/ext/uploader/__init__.py index 6bbd86cf..a89fce79 100644 --- a/httprunner/ext/uploader/__init__.py +++ b/httprunner/ext/uploader/__init__.py @@ -10,7 +10,7 @@ Then you can write upload test script as below: - test: name: upload file request: - url: http://httpbin.org/upload + url: https://httpbin.org/upload method: POST headers: Cookie: session=AAA-BBB-CCC @@ -31,7 +31,7 @@ For compatibility, you can also write upload test script in old way: field2: "value2" m_encoder: ${multipart_encoder(file=$file, field1=$field1, field2=$field2)} request: - url: http://httpbin.org/upload + url: https://httpbin.org/upload method: POST headers: Content-Type: ${multipart_content_type($m_encoder)} @@ -75,7 +75,9 @@ def ensure_upload_ready(): sys.exit(1) -def prepare_upload_step(step: TStep, step_variables: VariablesMapping, functions: FunctionsMapping): +def prepare_upload_step( + step: TStep, step_variables: VariablesMapping, functions: FunctionsMapping +): """preprocess for upload test replace `upload` info with MultipartEncoder @@ -84,7 +86,7 @@ def prepare_upload_step(step: TStep, step_variables: VariablesMapping, functions { "variables": {}, "request": { - "url": "http://httpbin.org/upload", + "url": "https://httpbin.org/upload", "method": "POST", "headers": { "Cookie": "session=AAA-BBB-CCC" From eb04bee822cd4f27e0912518c6ebe0c9a9a343d7 Mon Sep 17 00:00:00 2001 From: "lilong.129" Date: Tue, 18 Apr 2023 20:42:52 +0800 Subject: [PATCH 44/52] fix #1592: report html display url --- hrp/step_request.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hrp/step_request.go b/hrp/step_request.go index 5f23db87..e4623eb8 100644 --- a/hrp/step_request.go +++ b/hrp/step_request.go @@ -188,7 +188,7 @@ func (r *requestBuilder) prepareUrlParams(stepVariables map[string]interface{}) r.req.Host = u.Host // update url - r.requestMap["url"] = u + r.requestMap["url"] = u.String() return nil } From ac7e12f111efa6925954f1af0bc80cf62c9ae110 Mon Sep 17 00:00:00 2001 From: "lilong.129" Date: Tue, 18 Apr 2023 21:04:38 +0800 Subject: [PATCH 45/52] fix #1549: avoid duplicate creating plugins --- hrp/plugin.go | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/hrp/plugin.go b/hrp/plugin.go index 850d17c4..d12d1777 100644 --- a/hrp/plugin.go +++ b/hrp/plugin.go @@ -29,9 +29,33 @@ const ( const projectInfoFile = "proj.json" // used for ensuring root project -var pluginMap = sync.Map{} // used for reusing plugin instance +var ( + pluginMap sync.Map // used for reusing plugin instance + pluginMutex sync.Mutex +) -func initPlugin(path, venv string, logOn bool) (plugin funplugin.IPlugin, err error) { +func initPlugin(path, venv string, logOn bool) (funplugin.IPlugin, error) { + if plugin, ok := pluginMap.Load(path); ok { + return plugin.(funplugin.IPlugin), nil + } + + pluginMutex.Lock() + defer pluginMutex.Unlock() + + if plugin, ok := pluginMap.Load(path); ok { + return plugin.(funplugin.IPlugin), nil + } + + p, err := initplugin(path, venv, logOn) + if err != nil { + return nil, errors.Wrap(err, "init plugin failed") + } + + pluginMap.Store(path, p) + return p, nil +} + +func initplugin(path, venv string, logOn bool) (plugin funplugin.IPlugin, err error) { // plugin file not found if path == "" { return nil, nil From ebeae0c7772b45b9df099619d841d2e54ec05f2e Mon Sep 17 00:00:00 2001 From: "lilong.129" Date: Tue, 18 Apr 2023 21:08:28 +0800 Subject: [PATCH 46/52] change: update changelog --- docs/CHANGELOG.md | 3 +++ hrp/internal/version/VERSION | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 905caa26..63bc74ce 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -16,6 +16,9 @@ - fix: do not fail case when kill logcat error - fix: take screenshot after each step - fix: screencap compatibility for shell v1 and v2 protocol +- fix: display parsed url in html report +- fix: fast fail not closing the websocket connection +- fix #1549: avoid duplicate creating plugins ## v4.3.2 (2022-12-26) diff --git a/hrp/internal/version/VERSION b/hrp/internal/version/VERSION index 2167abfb..3bafdfb4 100644 --- a/hrp/internal/version/VERSION +++ b/hrp/internal/version/VERSION @@ -1 +1 @@ -v4.3.3.2304181958 \ No newline at end of file +v4.3.3.2304182109 \ No newline at end of file From 1ffee4734eb7ff7a8ea2cedf924bcc61a5013129 Mon Sep 17 00:00:00 2001 From: "lilong.129" Date: Tue, 18 Apr 2023 21:43:30 +0800 Subject: [PATCH 47/52] fix #1549: avoid duplicate creating plugins --- hrp/pkg/convert/from_curl_test.go | 2 +- hrp/plugin.go | 28 +++++----------------------- 2 files changed, 6 insertions(+), 24 deletions(-) diff --git a/hrp/pkg/convert/from_curl_test.go b/hrp/pkg/convert/from_curl_test.go index 63c036a5..a9b5f4f2 100644 --- a/hrp/pkg/convert/from_curl_test.go +++ b/hrp/pkg/convert/from_curl_test.go @@ -24,7 +24,7 @@ func TestLoadCurlCase(t *testing.T) { if !assert.EqualValues(t, "GET", tCase.TestSteps[0].Request.Method) { t.Fatal() } - if !assert.Equal(t, "https://httpbin.org", tCase.TestSteps[0].Request.URL) { + if !assert.Equal(t, "http://httpbin.org", tCase.TestSteps[0].Request.URL) { t.Fatal() } diff --git a/hrp/plugin.go b/hrp/plugin.go index d12d1777..a23c7dd2 100644 --- a/hrp/plugin.go +++ b/hrp/plugin.go @@ -31,31 +31,10 @@ const projectInfoFile = "proj.json" // used for ensuring root project var ( pluginMap sync.Map // used for reusing plugin instance - pluginMutex sync.Mutex + pluginMutex sync.RWMutex ) -func initPlugin(path, venv string, logOn bool) (funplugin.IPlugin, error) { - if plugin, ok := pluginMap.Load(path); ok { - return plugin.(funplugin.IPlugin), nil - } - - pluginMutex.Lock() - defer pluginMutex.Unlock() - - if plugin, ok := pluginMap.Load(path); ok { - return plugin.(funplugin.IPlugin), nil - } - - p, err := initplugin(path, venv, logOn) - if err != nil { - return nil, errors.Wrap(err, "init plugin failed") - } - - pluginMap.Store(path, p) - return p, nil -} - -func initplugin(path, venv string, logOn bool) (plugin funplugin.IPlugin, err error) { +func initPlugin(path, venv string, logOn bool) (plugin funplugin.IPlugin, err error) { // plugin file not found if path == "" { return nil, nil @@ -66,6 +45,9 @@ func initplugin(path, venv string, logOn bool) (plugin funplugin.IPlugin, err er return nil, nil } + pluginMutex.Lock() + defer pluginMutex.Unlock() + // reuse plugin instance if it already initialized if p, ok := pluginMap.Load(pluginPath); ok { return p.(funplugin.IPlugin), nil From 72e1780b1c4372cc63139fa9afddcfbef37494db Mon Sep 17 00:00:00 2001 From: "lilong.129" Date: Tue, 18 Apr 2023 22:08:10 +0800 Subject: [PATCH 48/52] change: fix screenshot filename --- hrp/internal/version/VERSION | 2 +- hrp/pkg/uixt/ext.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/hrp/internal/version/VERSION b/hrp/internal/version/VERSION index 3bafdfb4..92e4a34f 100644 --- a/hrp/internal/version/VERSION +++ b/hrp/internal/version/VERSION @@ -1 +1 @@ -v4.3.3.2304182109 \ No newline at end of file +v4.3.3.2304182207 \ No newline at end of file diff --git a/hrp/pkg/uixt/ext.go b/hrp/pkg/uixt/ext.go index bff5f541..ca09aaed 100644 --- a/hrp/pkg/uixt/ext.go +++ b/hrp/pkg/uixt/ext.go @@ -704,7 +704,7 @@ func (dExt *DriverExt) DoAction(action MobileAction) error { case CtlScreenShot: // take screenshot log.Info().Msg("take screenshot for current screen") - _, err := dExt.TakeScreenShot(builtin.GenNameWithTimestamp("screenshot_%d")) + _, err := dExt.TakeScreenShot(builtin.GenNameWithTimestamp("step_%d_screenshot")) return err case CtlStartCamera: return dExt.Driver.StartCamera() From e2eae18c44596016668b096a55e11ddc2e784d75 Mon Sep 17 00:00:00 2001 From: ywanbing Date: Wed, 19 Apr 2023 11:12:15 +0800 Subject: [PATCH 49/52] =?UTF-8?q?feature:=20=E5=A2=9E=E5=8A=A0ws=E8=BF=9E?= =?UTF-8?q?=E6=8E=A5=E5=8F=AF=E4=BB=A5=E7=BB=A7=E6=89=BF=E5=88=B0=E5=BC=95?= =?UTF-8?q?=E7=94=A8=E7=9A=84testcases=E4=B8=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hrp/runner.go | 28 +++++++++++++++++++++++++--- hrp/step_testcase.go | 2 ++ hrp/step_websocket.go | 15 ++++++++++----- 3 files changed, 37 insertions(+), 8 deletions(-) diff --git a/hrp/runner.go b/hrp/runner.go index dbb3b6b3..363915cc 100644 --- a/hrp/runner.go +++ b/hrp/runner.go @@ -461,6 +461,7 @@ type SessionRunner struct { startTime time.Time // record start time of the testcase summary *TestCaseSummary // record test case summary wsConnMap map[string]*websocket.Conn // save all websocket connections + inheritWsConnMap map[string]*websocket.Conn // inherit all websocket connections pongResponseChan chan string // channel used to receive pong response message closeResponseChan chan *wsCloseRespObject // channel used to receive close response message } @@ -472,19 +473,28 @@ func (r *SessionRunner) resetSession() { r.startTime = time.Now() r.summary = newSummary() r.wsConnMap = make(map[string]*websocket.Conn) + r.inheritWsConnMap = make(map[string]*websocket.Conn) r.pongResponseChan = make(chan string, 1) r.closeResponseChan = make(chan *wsCloseRespObject, 1) } +func (r *SessionRunner) inheritConnection(src *SessionRunner) { + log.Info().Msg("inherit session runner") + r.inheritWsConnMap = make(map[string]*websocket.Conn, len(src.wsConnMap)+len(src.inheritWsConnMap)) + for k, v := range src.wsConnMap { + r.inheritWsConnMap[k] = v + } + for k, v := range src.inheritWsConnMap { + r.inheritWsConnMap[k] = v + } +} + // Start runs the test steps in sequential order. // givenVars is used for data driven func (r *SessionRunner) Start(givenVars map[string]interface{}) error { config := r.caseRunner.testCase.Config log.Info().Str("testcase", config.Name).Msg("run testcase start") - // reset session runner - r.resetSession() - // update config variables with given variables r.InitWithParameters(givenVars) @@ -652,3 +662,15 @@ func (r *SessionRunner) releaseResources() { } } } + +func (r *SessionRunner) getWsClient(url string) *websocket.Conn { + if client, ok := r.wsConnMap[url]; ok { + return client + } + + if client, ok := r.inheritWsConnMap[url]; ok { + return client + } + + return nil +} diff --git a/hrp/step_testcase.go b/hrp/step_testcase.go index 6cfdeeaa..c72fe9e8 100644 --- a/hrp/step_testcase.go +++ b/hrp/step_testcase.go @@ -89,6 +89,8 @@ func (s *StepTestCaseWithOptionalArgs) Run(r *SessionRunner) (stepResult *StepRe return stepResult, err } sessionRunner := caseRunner.NewSession() + // need to inherit some information from current session + sessionRunner.inheritConnection(r) start := time.Now() // run referenced testcase with step variables diff --git a/hrp/step_websocket.go b/hrp/step_websocket.go index fbb5cfd5..ec633ca7 100644 --- a/hrp/step_websocket.go +++ b/hrp/step_websocket.go @@ -314,7 +314,7 @@ func runStepWebSocket(r *SessionRunner, step *TStep) (stepResult *StepResult, er case wsOpen: log.Info().Int64("timeout(ms)", step.WebSocket.GetTimeout()).Str("url", parsedURL).Msg("open websocket connection") // use the current websocket connection if existed - if r.wsConnMap[parsedURL] != nil { + if r.getWsClient(parsedURL) != nil { break } resp, err = openWithTimeout(parsedURL, parsedHeader, r, step) @@ -476,10 +476,15 @@ func openWithTimeout(urlStr string, requestHeader http.Header, r *SessionRunner, conn.SetCloseHandler(func(code int, text string) error { message := websocket.FormatCloseMessage(code, "") conn.WriteControl(websocket.CloseMessage, message, time.Now().Add(defaultWriteWait)) - r.closeResponseChan <- &wsCloseRespObject{ + select { + case r.closeResponseChan <- &wsCloseRespObject{ StatusCode: code, Text: text, + }: + default: + log.Warn().Msg("close response channel is block, drop the response") } + return nil }) r.wsConnMap[urlStr] = conn @@ -499,7 +504,7 @@ func openWithTimeout(urlStr string, requestHeader http.Header, r *SessionRunner, } func readMessageWithTimeout(urlString string, r *SessionRunner, step *TStep) (*wsReadRespObject, error) { - wsConn := r.wsConnMap[urlString] + wsConn := r.getWsClient(urlString) if wsConn == nil { return nil, errors.New("try to use existing connection, but there is no connection") } @@ -529,7 +534,7 @@ func readMessageWithTimeout(urlString string, r *SessionRunner, step *TStep) (*w } func writeWebSocket(urlString string, r *SessionRunner, step *TStep, stepVariables map[string]interface{}) error { - wsConn := r.wsConnMap[urlString] + wsConn := r.getWsClient(urlString) if wsConn == nil { return errors.New("try to use existing connection, but there is no connection") } @@ -595,7 +600,7 @@ func writeWithAction(c *websocket.Conn, step *TStep, messageType int, message [] } func closeWithTimeout(urlString string, r *SessionRunner, step *TStep, stepVariables map[string]interface{}) (*wsCloseRespObject, error) { - wsConn := r.wsConnMap[urlString] + wsConn := r.getWsClient(urlString) if wsConn == nil { return nil, errors.New("no connection needs to be closed") } From 202e6ce463d57c87355947fb51bdd126398f44fc Mon Sep 17 00:00:00 2001 From: "lilong.129" Date: Wed, 19 Apr 2023 15:51:46 +0800 Subject: [PATCH 50/52] fix #1467: failed to parse parameters with plugin functions --- docs/CHANGELOG.md | 3 ++- hrp/internal/version/VERSION | 2 +- hrp/parameters.go | 8 ++++---- hrp/parameters_test.go | 24 ++++++++++++++++-------- hrp/runner.go | 3 +-- 5 files changed, 24 insertions(+), 16 deletions(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 63bc74ce..524aca34 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -1,6 +1,6 @@ # Release History -## v4.3.3 (2023-04-18) +## v4.3.3 (2023-04-19) **go version** @@ -18,6 +18,7 @@ - fix: screencap compatibility for shell v1 and v2 protocol - fix: display parsed url in html report - fix: fast fail not closing the websocket connection +- fix #1467: failed to parse parameters with plugin functions - fix #1549: avoid duplicate creating plugins ## v4.3.2 (2022-12-26) diff --git a/hrp/internal/version/VERSION b/hrp/internal/version/VERSION index 92e4a34f..c294f2b5 100644 --- a/hrp/internal/version/VERSION +++ b/hrp/internal/version/VERSION @@ -1 +1 @@ -v4.3.3.2304182207 \ No newline at end of file +v4.3.3 \ No newline at end of file diff --git a/hrp/parameters.go b/hrp/parameters.go index af54afa0..69a35904 100644 --- a/hrp/parameters.go +++ b/hrp/parameters.go @@ -38,8 +38,8 @@ type iteratorStrategy struct { PickOrder iteratorPickOrder `json:"pick_order,omitempty" yaml:"pick_order,omitempty"` } -func initParametersIterator(cfg *TConfig) (*ParametersIterator, error) { - parameters, err := loadParameters(cfg.Parameters, cfg.Variables) +func (p *Parser) initParametersIterator(cfg *TConfig) (*ParametersIterator, error) { + parameters, err := p.loadParameters(cfg.Parameters, cfg.Variables) if err != nil { return nil, err } @@ -236,7 +236,7 @@ configParameters = { ] } */ -func loadParameters(configParameters map[string]interface{}, variablesMapping map[string]interface{}) ( +func (p *Parser) loadParameters(configParameters map[string]interface{}, variablesMapping map[string]interface{}) ( map[string]Parameters, error) { if len(configParameters) == 0 { @@ -263,7 +263,7 @@ func loadParameters(configParameters map[string]interface{}, variablesMapping ma // => [["test1", "111111"], ["test2", "222222"]] // e.g. "app_version": "${gen_app_version()}" // => ["1.0.0", "1.0.1"] - parsedParameterContent, err := newParser().ParseString(rawValue.String(), variablesMapping) + parsedParameterContent, err := p.ParseString(rawValue.String(), variablesMapping) if err != nil { log.Error().Err(err). Str("parametersRawContent", rawValue.String()). diff --git a/hrp/parameters_test.go b/hrp/parameters_test.go index 02176415..4d2781f2 100644 --- a/hrp/parameters_test.go +++ b/hrp/parameters_test.go @@ -74,8 +74,9 @@ func TestLoadParameters(t *testing.T) { variablesMapping := map[string]interface{}{ "file": "account.csv", } + parser := newParser() for _, data := range testData { - value, err := loadParameters(data.configParameters, variablesMapping) + value, err := parser.loadParameters(data.configParameters, variablesMapping) if !assert.Nil(t, err) { t.Fatal() } @@ -92,21 +93,25 @@ func TestLoadParametersError(t *testing.T) { { map[string]interface{}{ "username_password": fmt.Sprintf("${parameterize(%s/account.csv)}", hrpExamplesDir), - "user_agent": []interface{}{"iOS/10.1", "iOS/10.2"}}, + "user_agent": []interface{}{"iOS/10.1", "iOS/10.2"}, + }, }, { map[string]interface{}{ "username-password": fmt.Sprintf("${parameterize(%s/account.csv)}", hrpExamplesDir), - "user-agent": []interface{}{"iOS/10.1", "iOS/10.2"}}, + "user-agent": []interface{}{"iOS/10.1", "iOS/10.2"}, + }, }, { map[string]interface{}{ "username-password": fmt.Sprintf("${param(%s/account.csv)}", hrpExamplesDir), - "user_agent": []interface{}{"iOS/10.1", "iOS/10.2"}}, + "user_agent": []interface{}{"iOS/10.1", "iOS/10.2"}, + }, }, } + parser := newParser() for _, data := range testData { - _, err := loadParameters(data.configParameters, map[string]interface{}{}) + _, err := parser.loadParameters(data.configParameters, map[string]interface{}{}) if !assert.Error(t, err) { t.Fatal() } @@ -240,8 +245,9 @@ func TestInitParametersIteratorCount(t *testing.T) { 1, }, } + parser := newParser() for _, data := range testData { - iterator, err := initParametersIterator(data.cfg) + iterator, err := parser.initParametersIterator(data.cfg) if !assert.Nil(t, err) { t.Fatal() } @@ -288,8 +294,9 @@ func TestInitParametersIteratorUnlimitedCount(t *testing.T) { }, }, } + parser := newParser() for _, data := range testData { - iterator, err := initParametersIterator(data.cfg) + iterator, err := parser.initParametersIterator(data.cfg) if !assert.Nil(t, err) { t.Fatal() } @@ -370,8 +377,9 @@ func TestInitParametersIteratorContent(t *testing.T) { map[string]interface{}{}, }, } + parser := newParser() for _, data := range testData { - iterator, err := initParametersIterator(data.cfg) + iterator, err := parser.initParametersIterator(data.cfg) if !assert.Nil(t, err) { t.Fatal() } diff --git a/hrp/runner.go b/hrp/runner.go index e3bfab71..d8e67b57 100644 --- a/hrp/runner.go +++ b/hrp/runner.go @@ -386,7 +386,7 @@ func (r *CaseRunner) parseConfig() error { r.parsedConfig.WebSocketSetting.checkWebSocket() // parse testcase config parameters - parametersIterator, err := initParametersIterator(r.parsedConfig) + parametersIterator, err := r.parser.initParametersIterator(r.parsedConfig) if err != nil { log.Error().Err(err). Interface("parameters", r.parsedConfig.Parameters). @@ -629,7 +629,6 @@ func (r *SessionRunner) GetSummary() (*TestCaseSummary, error) { return caseSummary, nil } - // updateSummary updates summary of StepResult. func (r *SessionRunner) updateSummary(stepResult *StepResult) { switch stepResult.StepType { From 60709272ee2276bd6f0c0808830394167ecbe432 Mon Sep 17 00:00:00 2001 From: "lilong.129" Date: Wed, 19 Apr 2023 16:14:19 +0800 Subject: [PATCH 51/52] fix: unittest test_request_http --- examples/data/a_b_c/T1_test.py | 2 +- examples/data/a_b_c/T2_3_test.py | 2 +- examples/postman_echo/request_methods/hardcode_test.py | 2 +- .../postman_echo/request_methods/request_with_functions_test.py | 2 +- .../request_methods/request_with_parameters_test.py | 2 +- .../request_methods/request_with_testcase_reference_test.py | 2 +- .../postman_echo/request_methods/request_with_variables_test.py | 2 +- .../request_methods/validate_with_functions_test.py | 2 +- .../request_methods/validate_with_variables_test.py | 2 +- httprunner/client_test.py | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/examples/data/a_b_c/T1_test.py b/examples/data/a_b_c/T1_test.py index 6caf90b1..5adf2afd 100644 --- a/examples/data/a_b_c/T1_test.py +++ b/examples/data/a_b_c/T1_test.py @@ -1,4 +1,4 @@ -# NOTE: Generated By HttpRunner v4.1.4 +# NOTE: Generated By HttpRunner v4.3.0 # FROM: a-b.c/1.yml from httprunner import HttpRunner, Config, Step, RunRequest diff --git a/examples/data/a_b_c/T2_3_test.py b/examples/data/a_b_c/T2_3_test.py index 3a4bb701..4411bed1 100644 --- a/examples/data/a_b_c/T2_3_test.py +++ b/examples/data/a_b_c/T2_3_test.py @@ -1,4 +1,4 @@ -# NOTE: Generated By HttpRunner v4.1.4 +# NOTE: Generated By HttpRunner v4.3.0 # FROM: a-b.c/2 3.yml from httprunner import HttpRunner, Config, Step, RunRequest from httprunner import RunTestCase diff --git a/examples/postman_echo/request_methods/hardcode_test.py b/examples/postman_echo/request_methods/hardcode_test.py index ba0702a4..a74f4aaa 100644 --- a/examples/postman_echo/request_methods/hardcode_test.py +++ b/examples/postman_echo/request_methods/hardcode_test.py @@ -1,4 +1,4 @@ -# NOTE: Generated By HttpRunner v4.1.4 +# NOTE: Generated By HttpRunner v4.3.0 # FROM: request_methods/hardcode.yml from httprunner import HttpRunner, Config, Step, RunRequest diff --git a/examples/postman_echo/request_methods/request_with_functions_test.py b/examples/postman_echo/request_methods/request_with_functions_test.py index 54afbef1..cba5888f 100644 --- a/examples/postman_echo/request_methods/request_with_functions_test.py +++ b/examples/postman_echo/request_methods/request_with_functions_test.py @@ -1,4 +1,4 @@ -# NOTE: Generated By HttpRunner v4.1.4 +# NOTE: Generated By HttpRunner v4.3.0 # FROM: request_methods/request_with_functions.yml from httprunner import HttpRunner, Config, Step, RunRequest diff --git a/examples/postman_echo/request_methods/request_with_parameters_test.py b/examples/postman_echo/request_methods/request_with_parameters_test.py index 120f8d58..0755d95e 100644 --- a/examples/postman_echo/request_methods/request_with_parameters_test.py +++ b/examples/postman_echo/request_methods/request_with_parameters_test.py @@ -1,4 +1,4 @@ -# NOTE: Generated By HttpRunner v4.1.4 +# NOTE: Generated By HttpRunner v4.3.0 # FROM: request_methods/request_with_parameters.yml import pytest diff --git a/examples/postman_echo/request_methods/request_with_testcase_reference_test.py b/examples/postman_echo/request_methods/request_with_testcase_reference_test.py index 34c406e1..19aebb1d 100644 --- a/examples/postman_echo/request_methods/request_with_testcase_reference_test.py +++ b/examples/postman_echo/request_methods/request_with_testcase_reference_test.py @@ -1,4 +1,4 @@ -# NOTE: Generated By HttpRunner v4.1.4 +# NOTE: Generated By HttpRunner v4.3.0 # FROM: request_methods/request_with_testcase_reference.yml from httprunner import HttpRunner, Config, Step, RunRequest from httprunner import RunTestCase diff --git a/examples/postman_echo/request_methods/request_with_variables_test.py b/examples/postman_echo/request_methods/request_with_variables_test.py index 9834cb4d..89aaef48 100644 --- a/examples/postman_echo/request_methods/request_with_variables_test.py +++ b/examples/postman_echo/request_methods/request_with_variables_test.py @@ -1,4 +1,4 @@ -# NOTE: Generated By HttpRunner v4.1.4 +# NOTE: Generated By HttpRunner v4.3.0 # FROM: request_methods/request_with_variables.yml from httprunner import HttpRunner, Config, Step, RunRequest diff --git a/examples/postman_echo/request_methods/validate_with_functions_test.py b/examples/postman_echo/request_methods/validate_with_functions_test.py index fa906fe4..d75de2fe 100644 --- a/examples/postman_echo/request_methods/validate_with_functions_test.py +++ b/examples/postman_echo/request_methods/validate_with_functions_test.py @@ -1,4 +1,4 @@ -# NOTE: Generated By HttpRunner v4.1.4 +# NOTE: Generated By HttpRunner v4.3.0 # FROM: request_methods/validate_with_functions.yml from httprunner import HttpRunner, Config, Step, RunRequest diff --git a/examples/postman_echo/request_methods/validate_with_variables_test.py b/examples/postman_echo/request_methods/validate_with_variables_test.py index 275be39f..aa06a855 100644 --- a/examples/postman_echo/request_methods/validate_with_variables_test.py +++ b/examples/postman_echo/request_methods/validate_with_variables_test.py @@ -1,4 +1,4 @@ -# NOTE: Generated By HttpRunner v4.1.4 +# NOTE: Generated By HttpRunner v4.3.0 # FROM: request_methods/validate_with_variables.yml from httprunner import HttpRunner, Config, Step, RunRequest diff --git a/httprunner/client_test.py b/httprunner/client_test.py index 3e3175e0..9fd32ca8 100644 --- a/httprunner/client_test.py +++ b/httprunner/client_test.py @@ -11,7 +11,7 @@ class TestHttpSession(unittest.TestCase): self.session.request("get", "https://httpbin.org/get") address = self.session.data.address self.assertGreater(len(address.server_ip), 0) - self.assertEqual(address.server_port, 80) + self.assertEqual(address.server_port, 443) self.assertGreater(len(address.client_ip), 0) self.assertGreater(address.client_port, 10000) From 90cea61c321ec73d9c1c3984897aaa61954cf570 Mon Sep 17 00:00:00 2001 From: "lilong.129" Date: Wed, 19 Apr 2023 16:20:56 +0800 Subject: [PATCH 52/52] fix unittest TestRunCaseWithTimeout --- hrp/step_request_test.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/hrp/step_request_test.go b/hrp/step_request_test.go index 04970ea7..7172bf66 100644 --- a/hrp/step_request_test.go +++ b/hrp/step_request_test.go @@ -164,7 +164,7 @@ func TestRunCaseWithTimeout(t *testing.T) { // global timeout testcase1 := &TestCase{ Config: NewConfig("TestCase1"). - SetTimeout(2 * time.Second). // set global timeout to 2s + SetTimeout(10 * time.Second). // set global timeout to 10s SetBaseURL("https://httpbin.org"), TestSteps: []IStep{ NewStep("step1"). @@ -180,11 +180,11 @@ func TestRunCaseWithTimeout(t *testing.T) { testcase2 := &TestCase{ Config: NewConfig("TestCase2"). - SetTimeout(2 * time.Second). // set global timeout to 2s + SetTimeout(10 * time.Second). // set global timeout to 10s SetBaseURL("https://httpbin.org"), TestSteps: []IStep{ NewStep("step1"). - GET("/delay/3"). + GET("/delay/11"). Validate(). AssertEqual("status_code", 200, "check status code"), }, @@ -197,12 +197,12 @@ func TestRunCaseWithTimeout(t *testing.T) { // step timeout testcase3 := &TestCase{ Config: NewConfig("TestCase3"). - SetTimeout(2 * time.Second). + SetTimeout(10 * time.Second). SetBaseURL("https://httpbin.org"), TestSteps: []IStep{ NewStep("step2"). - GET("/delay/3"). - SetTimeout(4*time.Second). // set step timeout to 4s + GET("/delay/11"). + SetTimeout(15*time.Second). // set step timeout to 4s Validate(). AssertEqual("status_code", 200, "check status code"), },