diff --git a/function/ui_compare/README.md b/function/ui_compare/README.md new file mode 100644 index 0000000000000000000000000000000000000000..36064d4182c8c1a984052488b46f1b49801fbf03 --- /dev/null +++ b/function/ui_compare/README.md @@ -0,0 +1,126 @@ +# UI对比测试开发指南 + +## 文本用例(需求) + + +| 用例_名称 | 用例_编号 | 创建版本 |预置条件| 测试步骤|预期结果|自动化类型| +|-----|------|-------|------|------|------|------| +| 文本样式fontColor:Color类型 | SUB_ACE_UI_ATTRIBUTES_FONT_INTERFACE_0010 | OpenHarmony V400R001 |新建eTS页面|1、添加文本组件2、设置fontColor为Color.Blue 3、编译测试demo|3、编译通过,文本显示蓝色|UI对比| + +## 编写工程和代码 + +### 新建或导入已有工程,创建目录和页面 +在testability目录下创建页面,命名规则,用例编号除数字外,相同的用例在pages下创建一个同名子目录,每个编号的用例创建一个ets文件,文件名采用大驼峰如下: + +![image](figures/1.png) + +### 创建测试用例目录和文件 + +在test目录下创建用例目录,目录名称和2\.1中的页面相同并在后面加Test,用例文件和页面ets文件相同,后缀多了\.test,如下图: + +![image](figures/2.png) + +### 编写页面 + +按用例要求编写页面,创建文本组件,并设置颜色为Blue。 + +结构名称和文件名相同,组件需要设置id的,使用本文件名\_001按递增使用,避免和别的文件中的id重名。 + +![image](figures/3.png) + +### 编写用例 + +修改类名,注释,测试套名称,用例名称,和测试用例文件及用例文档中对应,修改调用的页面文件路由,和页面路径对应: + +![image](figures/4.png) + +![image](figures/5.png) + +### 在List.test.ets中添加测试套和页面路由 +注意:这里的页面是写在ohosTest模块下,不要写到main模块里。 + +![image](figures/6.png) + +![image](figures/7.png) + +## 执行 +### 编译 +在IDE的file -> Project Structure 中自动签名: + +![image](figures/8.png) + +Build-> Rebuild 编译hap: + +![image](figures/9.png) + +### 执行用例 +右键xxxxx.test.ets 文件执行用例: + +![image](figures/10.png) + +观察设备是否正常显示测试页面,显示了蓝色的文字: + +![image](figures/11.png) + +测试结果: + +![image](figures/12.png) + +### 检查截屏图片: + +![image](figures/13.png) + +用hdc从设备拉取截屏图片: + +![image](figures/14.png) + +打开检查截图是否正确: + +![image](figures/15.png) + +用例开发完成,且调试成功。 + +## 对比图片 + +使用对比脚本前需要安装python环境,安装依赖包:需安装三个包:openpyxl、numpy、pillow。 + +hdc配置到系统环境变量。 + +### 将编译的hap包拷贝到ui对比工具的hap目录下 + +![image](figures/16.png) + +![image](figures/17.png) + +### 在excel表中添加测试用例 + +excel文件名和hap名必须相同,一个excel对应一个hap。 + +![image](figures/18.png) + +![image](figures/19.png) + +### 生成基线图片: + +![image](figures/20.png) + +![image](figures/21.png) + +### 对此测试 + +指定基线图片目录和上中路径一致: + +![image](figures/22.png) + +测试显示用例执行通过: + +![image](figures/23.png) + +### 测试报告 + +![image](figures/24.png) + +![image](figures/25.png) + + + diff --git a/function/ui_compare/figures/1.png b/function/ui_compare/figures/1.png new file mode 100644 index 0000000000000000000000000000000000000000..5424b7902c69330ffe5f6dd97aadefda3091164f Binary files /dev/null and b/function/ui_compare/figures/1.png differ diff --git a/function/ui_compare/figures/10.png b/function/ui_compare/figures/10.png new file mode 100644 index 0000000000000000000000000000000000000000..a3e91a1b1164e13c1a4032e9ec4619944a533e5f Binary files /dev/null and b/function/ui_compare/figures/10.png differ diff --git a/function/ui_compare/figures/11.png b/function/ui_compare/figures/11.png new file mode 100644 index 0000000000000000000000000000000000000000..7dbbbb8680139cc82151b17593d7fd2bd5e42402 Binary files /dev/null and b/function/ui_compare/figures/11.png differ diff --git a/function/ui_compare/figures/12.png b/function/ui_compare/figures/12.png new file mode 100644 index 0000000000000000000000000000000000000000..4ccf7d44417f7d93f94ddda5b18be9517348269f Binary files /dev/null and b/function/ui_compare/figures/12.png differ diff --git a/function/ui_compare/figures/13.png b/function/ui_compare/figures/13.png new file mode 100644 index 0000000000000000000000000000000000000000..2024a8115867c326dee5efaa621ce962521f434a Binary files /dev/null and b/function/ui_compare/figures/13.png differ diff --git a/function/ui_compare/figures/14.png b/function/ui_compare/figures/14.png new file mode 100644 index 0000000000000000000000000000000000000000..633a80c80bc310cd66deb08f2473918db9ba2eb7 Binary files /dev/null and b/function/ui_compare/figures/14.png differ diff --git a/function/ui_compare/figures/15.png b/function/ui_compare/figures/15.png new file mode 100644 index 0000000000000000000000000000000000000000..08c41aa20d552bfaddd2a40e90ea4eafc0fbadc3 Binary files /dev/null and b/function/ui_compare/figures/15.png differ diff --git a/function/ui_compare/figures/16.png b/function/ui_compare/figures/16.png new file mode 100644 index 0000000000000000000000000000000000000000..61a4c79840404a664181de7eaab0637b38c9f9b2 Binary files /dev/null and b/function/ui_compare/figures/16.png differ diff --git a/function/ui_compare/figures/17.png b/function/ui_compare/figures/17.png new file mode 100644 index 0000000000000000000000000000000000000000..b3c346c1220ee4529f0b60752b9996e16485617d Binary files /dev/null and b/function/ui_compare/figures/17.png differ diff --git a/function/ui_compare/figures/18.png b/function/ui_compare/figures/18.png new file mode 100644 index 0000000000000000000000000000000000000000..68d8881e20e301488b1cd85fb33bf3b106dad5c7 Binary files /dev/null and b/function/ui_compare/figures/18.png differ diff --git a/function/ui_compare/figures/19.png b/function/ui_compare/figures/19.png new file mode 100644 index 0000000000000000000000000000000000000000..ac58da7cc49e6f0957fbadc50a1927becfc4e2c9 Binary files /dev/null and b/function/ui_compare/figures/19.png differ diff --git a/function/ui_compare/figures/2.png b/function/ui_compare/figures/2.png new file mode 100644 index 0000000000000000000000000000000000000000..6c3dde1577ddb12f63061e4ddfd380d1f32d167a Binary files /dev/null and b/function/ui_compare/figures/2.png differ diff --git a/function/ui_compare/figures/20.png b/function/ui_compare/figures/20.png new file mode 100644 index 0000000000000000000000000000000000000000..f1168f94ca1a07da6b8d4573040f440e19a37811 Binary files /dev/null and b/function/ui_compare/figures/20.png differ diff --git a/function/ui_compare/figures/21.png b/function/ui_compare/figures/21.png new file mode 100644 index 0000000000000000000000000000000000000000..62c5318d417bd4616d52857c7661c12a768f840e Binary files /dev/null and b/function/ui_compare/figures/21.png differ diff --git a/function/ui_compare/figures/22.png b/function/ui_compare/figures/22.png new file mode 100644 index 0000000000000000000000000000000000000000..03ce77ab89e45415079f6239d53cde66e1bbc37a Binary files /dev/null and b/function/ui_compare/figures/22.png differ diff --git a/function/ui_compare/figures/23.png b/function/ui_compare/figures/23.png new file mode 100644 index 0000000000000000000000000000000000000000..8efdb1a39de83730abec3a4b7f0f5fb740c2389a Binary files /dev/null and b/function/ui_compare/figures/23.png differ diff --git a/function/ui_compare/figures/24.png b/function/ui_compare/figures/24.png new file mode 100644 index 0000000000000000000000000000000000000000..ba25f3a4b3ba91274b7eccf5051a158261b23a55 Binary files /dev/null and b/function/ui_compare/figures/24.png differ diff --git a/function/ui_compare/figures/25.png b/function/ui_compare/figures/25.png new file mode 100644 index 0000000000000000000000000000000000000000..5277cf722abb4f1d7943c2deffaf90e4d33dab39 Binary files /dev/null and b/function/ui_compare/figures/25.png differ diff --git a/function/ui_compare/figures/3.png b/function/ui_compare/figures/3.png new file mode 100644 index 0000000000000000000000000000000000000000..f67097e20179919a3ff91dd8c6c209773538a1cb Binary files /dev/null and b/function/ui_compare/figures/3.png differ diff --git a/function/ui_compare/figures/4.png b/function/ui_compare/figures/4.png new file mode 100644 index 0000000000000000000000000000000000000000..fb62a3cf06c8afe812c7caebef4d2283a3e46e13 Binary files /dev/null and b/function/ui_compare/figures/4.png differ diff --git a/function/ui_compare/figures/5.png b/function/ui_compare/figures/5.png new file mode 100644 index 0000000000000000000000000000000000000000..b728087c47ad5e2f7ef28dc1de3566165b1604ce Binary files /dev/null and b/function/ui_compare/figures/5.png differ diff --git a/function/ui_compare/figures/6.png b/function/ui_compare/figures/6.png new file mode 100644 index 0000000000000000000000000000000000000000..ba08042c823ef71f49e76a4b3e1ea812d5c8432f Binary files /dev/null and b/function/ui_compare/figures/6.png differ diff --git a/function/ui_compare/figures/7.png b/function/ui_compare/figures/7.png new file mode 100644 index 0000000000000000000000000000000000000000..276823adc1fa505f57d4059d768357654fc9d992 Binary files /dev/null and b/function/ui_compare/figures/7.png differ diff --git a/function/ui_compare/figures/8.png b/function/ui_compare/figures/8.png new file mode 100644 index 0000000000000000000000000000000000000000..93d2547bf903a0b37a8b8f6706789558b95bcea2 Binary files /dev/null and b/function/ui_compare/figures/8.png differ diff --git a/function/ui_compare/figures/9.png b/function/ui_compare/figures/9.png new file mode 100644 index 0000000000000000000000000000000000000000..5ab1b3c96fb2d320093889235d364fc651e2238b Binary files /dev/null and b/function/ui_compare/figures/9.png differ diff --git a/function/ui_compare/uicompare/.gitignore b/function/ui_compare/uicompare/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..fbabf771011fe78f9919db0b1195ab6cadffc2b0 --- /dev/null +++ b/function/ui_compare/uicompare/.gitignore @@ -0,0 +1,11 @@ +/node_modules +/oh_modules +/local.properties +/.idea +**/build +/.hvigor +.cxx +/.clangd +/.clang-format +/.clang-tidy +**/.test \ No newline at end of file diff --git a/function/ui_compare/uicompare/AppScope/app.json5 b/function/ui_compare/uicompare/AppScope/app.json5 new file mode 100644 index 0000000000000000000000000000000000000000..eabf71557eadee20c8a9913b10b8e25e6e5347a4 --- /dev/null +++ b/function/ui_compare/uicompare/AppScope/app.json5 @@ -0,0 +1,10 @@ +{ + "app": { + "bundleName": "com.example.uicompare", + "vendor": "example", + "versionCode": 1000000, + "versionName": "1.0.0", + "icon": "$media:app_icon", + "label": "$string:app_name" + } +} diff --git a/function/ui_compare/uicompare/AppScope/resources/base/element/string.json b/function/ui_compare/uicompare/AppScope/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..c102b07cf38bd502d548f44266affbd1267ade12 --- /dev/null +++ b/function/ui_compare/uicompare/AppScope/resources/base/element/string.json @@ -0,0 +1,8 @@ +{ + "string": [ + { + "name": "app_name", + "value": "uicompare" + } + ] +} diff --git a/function/ui_compare/uicompare/AppScope/resources/base/media/app_icon.png b/function/ui_compare/uicompare/AppScope/resources/base/media/app_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..ce307a8827bd75456441ceb57d530e4c8d45d36c Binary files /dev/null and b/function/ui_compare/uicompare/AppScope/resources/base/media/app_icon.png differ diff --git a/function/ui_compare/uicompare/build-profile.json5 b/function/ui_compare/uicompare/build-profile.json5 new file mode 100644 index 0000000000000000000000000000000000000000..14a6f7b04a68dce636782a96a86be987dd5bfab5 --- /dev/null +++ b/function/ui_compare/uicompare/build-profile.json5 @@ -0,0 +1,40 @@ +{ + "app": { + "signingConfigs": [ + { + "name": "default", + "material": { + "certpath": "C:/Users/Administrator/.ohos/config/openharmony/default_uicompare_JATV3sai8Vu453IAuyPAlrcBCITyeH85AVd3jtrOqX8=.cer", + "storePassword": "0000001B5800C5862EC1C131F62392DCF0CF0C6ADA36F77E974C9AABB8F83303F7B9F2403B658750DE79B2", + "keyAlias": "debugKey", + "keyPassword": "0000001BECE219BF83EBDBA9905E2D724889CD8A7609B551E92AE7521165E20E0E0AAE435E0577D72C5420", + "profile": "C:/Users/Administrator/.ohos/config/openharmony/default_uicompare_JATV3sai8Vu453IAuyPAlrcBCITyeH85AVd3jtrOqX8=.p7b", + "signAlg": "SHA256withECDSA", + "storeFile": "C:/Users/Administrator/.ohos/config/openharmony/default_uicompare_JATV3sai8Vu453IAuyPAlrcBCITyeH85AVd3jtrOqX8=.p12" + } + } + ], + "products": [ + { + "name": "default", + "signingConfig": "default", + "compileSdkVersion": 12, + "compatibleSdkVersion": 12 + } + ] + }, + "modules": [ + { + "name": "entry", + "srcPath": "./entry", + "targets": [ + { + "name": "default", + "applyToProducts": [ + "default" + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/function/ui_compare/uicompare/entry/.gitignore b/function/ui_compare/uicompare/entry/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..e2713a2779c5a3e0eb879efe6115455592caeea5 --- /dev/null +++ b/function/ui_compare/uicompare/entry/.gitignore @@ -0,0 +1,6 @@ +/node_modules +/oh_modules +/.preview +/build +/.cxx +/.test \ No newline at end of file diff --git a/function/ui_compare/uicompare/entry/build-profile.json5 b/function/ui_compare/uicompare/entry/build-profile.json5 new file mode 100644 index 0000000000000000000000000000000000000000..2c53bb59beda0250e90c1e054f04edbaa4d8eb12 --- /dev/null +++ b/function/ui_compare/uicompare/entry/build-profile.json5 @@ -0,0 +1,14 @@ +{ + "apiType": 'stageMode', + "buildOption": { + }, + "targets": [ + { + "name": "default", + "runtimeOS": "OpenHarmony" + }, + { + "name": "ohosTest", + } + ] +} \ No newline at end of file diff --git a/function/ui_compare/uicompare/entry/hvigorfile.ts b/function/ui_compare/uicompare/entry/hvigorfile.ts new file mode 100644 index 0000000000000000000000000000000000000000..80e4ec5b81689f238c34614b167a0b9e9c83e8d9 --- /dev/null +++ b/function/ui_compare/uicompare/entry/hvigorfile.ts @@ -0,0 +1,2 @@ +// Script for compiling build behavior. It is built in the build plug-in and cannot be modified currently. +export { hapTasks } from '@ohos/hvigor-ohos-plugin'; diff --git a/function/ui_compare/uicompare/entry/oh-package.json5 b/function/ui_compare/uicompare/entry/oh-package.json5 new file mode 100644 index 0000000000000000000000000000000000000000..225946cb11a2c405c8dc81eea89c22f923556638 --- /dev/null +++ b/function/ui_compare/uicompare/entry/oh-package.json5 @@ -0,0 +1,10 @@ +{ + "license": "", + "devDependencies": {}, + "author": "", + "name": "entry", + "description": "Please describe the basic information.", + "main": "", + "version": "1.0.0", + "dependencies": {} +} diff --git a/function/ui_compare/uicompare/entry/src/main/ets/entryability/EntryAbility.ts b/function/ui_compare/uicompare/entry/src/main/ets/entryability/EntryAbility.ts new file mode 100644 index 0000000000000000000000000000000000000000..40da48be6a41164ab47bb9c4fab8f1beb6341f61 --- /dev/null +++ b/function/ui_compare/uicompare/entry/src/main/ets/entryability/EntryAbility.ts @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2024 Shenzhen Kaihong Digital Industry Development Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import UIAbility from '@ohos.app.ability.UIAbility'; +import hilog from '@ohos.hilog'; +import window from '@ohos.window'; + +export default class EntryAbility extends UIAbility { + onCreate(want, launchParam) { + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate'); + } + + onDestroy() { + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onDestroy'); + } + + onWindowStageCreate(windowStage: window.WindowStage) { + // Main window is created, set main page for this ability + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate'); + + windowStage.loadContent('pages/Index', (err, data) => { + if (err.code) { + hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? ''); + return; + } + hilog.info(0x0000, 'testTag', 'Succeeded in loading the content. Data: %{public}s', JSON.stringify(data) ?? ''); + }); + } + + onWindowStageDestroy() { + // Main window is destroyed, release UI related resources + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageDestroy'); + } + + onForeground() { + // Ability has brought to foreground + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onForeground'); + } + + onBackground() { + // Ability has back to background + hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onBackground'); + } +} diff --git a/function/ui_compare/uicompare/entry/src/main/ets/pages/Index.ets b/function/ui_compare/uicompare/entry/src/main/ets/pages/Index.ets new file mode 100644 index 0000000000000000000000000000000000000000..b4f3ac24e9a85e41f0e5eae382a813cbdb5c2173 --- /dev/null +++ b/function/ui_compare/uicompare/entry/src/main/ets/pages/Index.ets @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2024 Shenzhen Kaihong Digital Industry Development Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@Entry +@Component +struct Index { + @State message: string = 'Hello World' + + build() { + Row() { + Column() { + Text(this.message) + .fontSize(50) + .fontWeight(FontWeight.Bold) + } + .width('100%') + } + .height('100%') + } +} \ No newline at end of file diff --git a/function/ui_compare/uicompare/entry/src/main/module.json5 b/function/ui_compare/uicompare/entry/src/main/module.json5 new file mode 100644 index 0000000000000000000000000000000000000000..9b07af70a189c0165e30b9f4bedc3bfd22b53cda --- /dev/null +++ b/function/ui_compare/uicompare/entry/src/main/module.json5 @@ -0,0 +1,37 @@ +{ + "module": { + "name": "entry", + "type": "entry", + "description": "$string:module_desc", + "mainElement": "EntryAbility", + "deviceTypes": [ + "default", + "tablet" + ], + "deliveryWithInstall": true, + "installationFree": false, + "pages": "$profile:main_pages", + "abilities": [ + { + "name": "EntryAbility", + "srcEntry": "./ets/entryability/EntryAbility.ts", + "description": "$string:EntryAbility_desc", + "icon": "$media:icon", + "label": "$string:EntryAbility_label", + "startWindowIcon": "$media:icon", + "startWindowBackground": "$color:start_window_background", + "exported": true, + "skills": [ + { + "entities": [ + "entity.system.home" + ], + "actions": [ + "action.system.home" + ] + } + ] + } + ] + } +} \ No newline at end of file diff --git a/function/ui_compare/uicompare/entry/src/main/resources/base/element/color.json b/function/ui_compare/uicompare/entry/src/main/resources/base/element/color.json new file mode 100644 index 0000000000000000000000000000000000000000..3c712962da3c2751c2b9ddb53559afcbd2b54a02 --- /dev/null +++ b/function/ui_compare/uicompare/entry/src/main/resources/base/element/color.json @@ -0,0 +1,8 @@ +{ + "color": [ + { + "name": "start_window_background", + "value": "#FFFFFF" + } + ] +} \ No newline at end of file diff --git a/function/ui_compare/uicompare/entry/src/main/resources/base/element/string.json b/function/ui_compare/uicompare/entry/src/main/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..f94595515a99e0c828807e243494f57f09251930 --- /dev/null +++ b/function/ui_compare/uicompare/entry/src/main/resources/base/element/string.json @@ -0,0 +1,16 @@ +{ + "string": [ + { + "name": "module_desc", + "value": "module description" + }, + { + "name": "EntryAbility_desc", + "value": "description" + }, + { + "name": "EntryAbility_label", + "value": "label" + } + ] +} \ No newline at end of file diff --git a/function/ui_compare/uicompare/entry/src/main/resources/base/media/icon.png b/function/ui_compare/uicompare/entry/src/main/resources/base/media/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..ce307a8827bd75456441ceb57d530e4c8d45d36c Binary files /dev/null and b/function/ui_compare/uicompare/entry/src/main/resources/base/media/icon.png differ diff --git a/function/ui_compare/uicompare/entry/src/main/resources/base/profile/main_pages.json b/function/ui_compare/uicompare/entry/src/main/resources/base/profile/main_pages.json new file mode 100644 index 0000000000000000000000000000000000000000..1898d94f58d6128ab712be2c68acc7c98e9ab9ce --- /dev/null +++ b/function/ui_compare/uicompare/entry/src/main/resources/base/profile/main_pages.json @@ -0,0 +1,5 @@ +{ + "src": [ + "pages/Index" + ] +} diff --git a/function/ui_compare/uicompare/entry/src/main/resources/en_US/element/string.json b/function/ui_compare/uicompare/entry/src/main/resources/en_US/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..f94595515a99e0c828807e243494f57f09251930 --- /dev/null +++ b/function/ui_compare/uicompare/entry/src/main/resources/en_US/element/string.json @@ -0,0 +1,16 @@ +{ + "string": [ + { + "name": "module_desc", + "value": "module description" + }, + { + "name": "EntryAbility_desc", + "value": "description" + }, + { + "name": "EntryAbility_label", + "value": "label" + } + ] +} \ No newline at end of file diff --git a/function/ui_compare/uicompare/entry/src/main/resources/zh_CN/element/string.json b/function/ui_compare/uicompare/entry/src/main/resources/zh_CN/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..597ecf95e61d7e30367c22fe2f8638008361b044 --- /dev/null +++ b/function/ui_compare/uicompare/entry/src/main/resources/zh_CN/element/string.json @@ -0,0 +1,16 @@ +{ + "string": [ + { + "name": "module_desc", + "value": "模块描述" + }, + { + "name": "EntryAbility_desc", + "value": "description" + }, + { + "name": "EntryAbility_label", + "value": "label" + } + ] +} \ No newline at end of file diff --git a/function/ui_compare/uicompare/entry/src/ohosTest/ets/test/Ability.test.ets b/function/ui_compare/uicompare/entry/src/ohosTest/ets/test/Ability.test.ets new file mode 100644 index 0000000000000000000000000000000000000000..1469eb907f54a26b57dd65cb43f4d90d8eab6d42 --- /dev/null +++ b/function/ui_compare/uicompare/entry/src/ohosTest/ets/test/Ability.test.ets @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2024 Shenzhen Kaihong Digital Industry Development Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import hilog from '@ohos.hilog'; +import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium' + +export default function abilityTest() { + describe('ActsAbilityTest', function () { + // Defines a test suite. Two parameters are supported: test suite name and test suite function. + beforeAll(function () { + // Presets an action, which is performed only once before all test cases of the test suite start. + // This API supports only one parameter: preset action function. + }) + beforeEach(function () { + // Presets an action, which is performed before each unit test case starts. + // The number of execution times is the same as the number of test cases defined by **it**. + // This API supports only one parameter: preset action function. + }) + afterEach(function () { + // Presets a clear action, which is performed after each unit test case ends. + // The number of execution times is the same as the number of test cases defined by **it**. + // This API supports only one parameter: clear action function. + }) + afterAll(function () { + // Presets a clear action, which is performed after all test cases of the test suite end. + // This API supports only one parameter: clear action function. + }) + it('assertContain',0, function () { + // Defines a test case. This API supports three parameters: test case name, filter parameter, and test case function. + hilog.info(0x0000, 'testTag', '%{public}s', 'it begin'); + let a = 'abc' + let b = 'b' + // Defines a variety of assertion methods, which are used to declare expected boolean conditions. + expect(a).assertContain(b) + expect(a).assertEqual(a) + }) + }) +} \ No newline at end of file diff --git a/function/ui_compare/uicompare/entry/src/ohosTest/ets/test/Demo.test.ets b/function/ui_compare/uicompare/entry/src/ohosTest/ets/test/Demo.test.ets new file mode 100644 index 0000000000000000000000000000000000000000..da3467775392556dd8884cdc33f1cba4ec0a5545 --- /dev/null +++ b/function/ui_compare/uicompare/entry/src/ohosTest/ets/test/Demo.test.ets @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2024 Shenzhen Kaihong Digital Industry Development Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium' +import Settings from './model/Settings' +import windowSnap from './model/snapShot' +import Logger from './model/Logger' +import Utils from './model/Utils' +import { + UiComponent, + UiDriver, + Component, + Driver, + UiWindow, + ON, + BY, + MatchPattern, + DisplayRotation, + ResizeDirection, + WindowMode, + PointerMatrix +} from '@ohos.UiTest'; + +/* + * demoTest_001:更改page路由 + * demoTest_002:更改page路由,像素密度 + * demoTest_003:更改page路由,窗口大小,位置,像素密度 + * demoTest_004:更改page路由,页面内元素属性 + * demoTest_005:更改page路由,注入点击事件 + * + * demoTest_004:更改属性需要配合页面路由配置globalThis.value.message + * demoTest_005:注入事件,引用'@ohos.UiTest',findComponent需要根据页面路由内组件配置情况更改 + * + * Settings.createWindow(config.url): + * 创建窗口,更改窗口基本配置,更改方式详见model/Settings createWindow方法 + * + * windowSnap.snapShot(globalThis.context): + * 窗口截屏&图片文件保存,存储在设备端 + * 存储文件固定,单挑用例执行后覆盖,用于自动化UI对比 + * 支持调试更改文件名为时间戳格式,更改model/snapShot createAndGetFile方法 注释L35,放开L32,L33 + * + * Logger日志使用方法: + * import Logger form './model/Logger' + * Logger.info(TAG,`config = ${config}, err = ${JSON.stringify(exception)}`) + * */ + + +export default function demoTest() { + + + describe('demoTest', () => { + + afterEach(async (done: Function) => { + + if (Settings.windowClass == undefined) { + return + } + + Settings.windowClass.destroyWindow((err) => { + if (err.code) { + Logger.error('TEST', `Failed to destroy the window. Cause : ${JSON.stringify(err)}`) + return; + } + Logger.info('TEST', `Succeeded in destroy the window.`); + }) + await Utils.sleep(1000); + done() + }) + + it('demoTest_001', 0, async (done: Function) => { + //更改page路由 + + Settings.createWindow("testability/pages/demo") + await Utils.sleep(1000) + windowSnap.snapShot() + await Utils.sleep(1000) + + done() + }) + + + }) +} diff --git a/function/ui_compare/uicompare/entry/src/ohosTest/ets/test/List.test.ets b/function/ui_compare/uicompare/entry/src/ohosTest/ets/test/List.test.ets new file mode 100644 index 0000000000000000000000000000000000000000..79216c3a6f9270785abac2818e5fa16f43f7ff96 --- /dev/null +++ b/function/ui_compare/uicompare/entry/src/ohosTest/ets/test/List.test.ets @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2024 Shenzhen Kaihong Digital Industry Development Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import demoTest from './Demo.test' +import UIAttributeFontInterface001 from './UIAttributeFontInterfaceTest/UIAttributeFontInterface001.test' + +export default function testsuite() { + demoTest(); + UIAttributeFontInterface001(); +} \ No newline at end of file diff --git a/function/ui_compare/uicompare/entry/src/ohosTest/ets/test/UIAttributeFontInterfaceTest/UIAttributeFontInterface001.test.ets b/function/ui_compare/uicompare/entry/src/ohosTest/ets/test/UIAttributeFontInterfaceTest/UIAttributeFontInterface001.test.ets new file mode 100644 index 0000000000000000000000000000000000000000..2e013f9751bef1a24c64cdea80164c8d972a968d --- /dev/null +++ b/function/ui_compare/uicompare/entry/src/ohosTest/ets/test/UIAttributeFontInterfaceTest/UIAttributeFontInterface001.test.ets @@ -0,0 +1,52 @@ +import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium' +import Settings from '../model/Settings' +import windowSnap from '../model/snapShot' +import Logger from '../model/Logger' +import Utils from '../model/Utils' + +/* + * SUB_ACE_UI_ATTRIBUTES_FONT_INTERFACE_0010:设置文本颜色 + * + * Settings.createWindow(config.url): + * 创建窗口,更改窗口基本配置,更改方式详见model/Settings createWindow方法 + * + * windowSnap.snapShot(globalThis.context): + * 窗口截屏&图片文件保存,存储在设备端 + * 存储文件固定,单挑用例执行后覆盖,用于自动化UI对比 + * 支持调试更改文件名为时间戳格式,更改model/snapShot createAndGetFile方法 注释L35,放开L32,L33 + * + * Logger日志使用方法: + * import Logger form './model/Logger' + * Logger.info(TAG,`config = ${config}, err = ${JSON.stringify(exception)}`) + * */ + +export default function UIAttributeFontInterface001() { + + describe('UIAttributeFontInterface001', () => { + afterEach(async (done: Function) => { + if (Settings.windowClass == undefined) { + return + } + + Settings.windowClass.destroyWindow((err) => { + if (err.code) { + Logger.error('TEST', `Failed to destroy the window. Cause : ${JSON.stringify(err)}`) + return; + } + Logger.info('TEST', `Succeeded in destroy the window.`); + }) + await Utils.sleep(1000); + done() + }) + + it('SUB_ACE_UI_ATTRIBUTES_FONT_INTERFACE_0010', 0, async (done: Function) => { + Logger.info('TEST', `SUB_ACE_UI_ATTRIBUTES_FONT_INTERFACE_0010 start.`); + Settings.createWindow("testability/pages/UIAttributeFontInterface/UIAttributeFontInterface001") + await Utils.sleep(1000) + windowSnap.snapShot() + await Utils.sleep(1000) + Logger.info('TEST', `SUB_ACE_UI_ATTRIBUTES_FONT_INTERFACE_0010 finish.`); + done() + }) + }) +} diff --git a/function/ui_compare/uicompare/entry/src/ohosTest/ets/test/model/Logger.ts b/function/ui_compare/uicompare/entry/src/ohosTest/ets/test/model/Logger.ts new file mode 100644 index 0000000000000000000000000000000000000000..60f2ccef32824dc49a575852fb7cffa09e7a3d54 --- /dev/null +++ b/function/ui_compare/uicompare/entry/src/ohosTest/ets/test/model/Logger.ts @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import hilog from '@ohos.hilog'; + +class Logger { + private domain: number; + private prefix: string; + private format: string = "%{public}s, %{public}s"; + + constructor(prefix: string) { + this.prefix = prefix; + this.domain = 0xFF00; + } + + debug(...args: any[]) { + hilog.debug(this.domain, this.prefix, this.format, args); + } + + info(...args: any[]) { + hilog.info(this.domain, this.prefix, this.format, args); + } + + warn(...args: any[]) { + hilog.warn(this.domain, this.prefix, this.format, args); + } + + error(...args: any[]) { + hilog.error(this.domain, this.prefix, this.format, args); + } +} + +export default new Logger('[Screenshot]'); \ No newline at end of file diff --git a/function/ui_compare/uicompare/entry/src/ohosTest/ets/test/model/Settings.ets b/function/ui_compare/uicompare/entry/src/ohosTest/ets/test/model/Settings.ets new file mode 100644 index 0000000000000000000000000000000000000000..f64d2304cd079532ed1c0c80fbf29af9ef301ea9 --- /dev/null +++ b/function/ui_compare/uicompare/entry/src/ohosTest/ets/test/model/Settings.ets @@ -0,0 +1,221 @@ +/* + * Copyright (c) 2024 Shenzhen Kaihong Digital Industry Development Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import screen from '@ohos.screen'; +import window from '@ohos.window'; +import Logger from './Logger'; +import { GlobalThis } from './globalThis' +import { BusinessError } from '@ohos.base' + +const TAG: string = '[TEST]'; + +async function sleep(time: number): Promise { + const result: string = await new Promise((resolve: Function) => { + setTimeout(() => { + resolve('sleep ok') + }, time) + }); + console.info(result); +} + +class Settings { + windowClass: window.Window | undefined = undefined + windowCreateType: string = "changeWindow" + + changeWindowPosition(windowClass: window.Window, X: number | undefined, Y: number | undefined) { + //为悬浮窗设置位置 + windowClass.moveWindowTo(X, Y, (err) => { + if (err.code) { + Logger.error(TAG, `Failed to move the window. Cause : ${JSON.stringify(err)}`) + return; + } + Logger.info(TAG, 'Succeed moveWindowTo.'); + }) + } + + changeWindowSize(windowClass: window.Window, width: number | undefined, height: number | undefined) { + //为悬浮窗设置大小 + windowClass.resize(width, height, (err) => { + if (err.code) { + Logger.error(TAG, `Failed to change the window size. Cause : ${JSON.stringify(err)}`) + return; + } + Logger.info(TAG, 'Succeed changeWindowSize'); + }) + } + + loadContent(windowClass: window.Window, pageURI: string) { + //为悬浮窗加载对应的目标页面 + windowClass.setUIContent(pageURI, (err) => { + if (err.code) { + Logger.error(TAG, `Failed to load the window. Cause : ${JSON.stringify(err)}`) + return; + } + Logger.info(TAG, `Succeeded in loading the content.`); + + //显示悬浮窗 + windowClass.showWindow((err) => { + if (err.code) { + Logger.error(TAG, `Failed to show the window. Cause : ${JSON.stringify(err)}`) + return; + } + Logger.info(TAG, `Succeeded in showing the window.`); + }) + }) + } + + loadContentChange(windowClass: window.Window, pageURI: string) { + //为悬浮窗加载对应的目标页面 + windowClass.setUIContent(pageURI, (err) => { + if (err.code) { + Logger.error(TAG, `Failed to load the window. Cause : ${JSON.stringify(err)}`) + return; + } + Logger.info(TAG, `Succeeded in loading the content.`); + + }) + } + + changeDpi(dpi: number) { + let screenClass: screen.Screen; + screen.getAllScreens((err: BusinessError, data: Array) => { + if (err.code) { + Logger.error(TAG, `Failed to get all screens. Cause : ${JSON.stringify(err)}`); + return; + } + Logger.info(TAG, `Succeeded in getting all screens. Data:${JSON.stringify(data)}`); + + screenClass = data[0]; + //设置设备dpi + screenClass.setDensityDpi(dpi, (err: BusinessError) => { + if (err.code) { + Logger.error(TAG, `Failed to set the pixel density. Code : ${JSON.stringify(err)}`) + return; + } + Logger.info(TAG, `Succeeded in setting the pixel density`); + }) + }) + } + + setWindowSystemBarEnable(windowClass: window.Window, names: Array<'status' | 'navigation'>) { + windowClass.setWindowSystemBarEnable(names, (err: BusinessError) => { + const errCode: number = err.code; + if (errCode) { + Logger.error(TAG, 'Failed to set the system bar to be invisible. Cause:' + JSON.stringify(err)) + return; + } + Logger.info(TAG, `Succeeded in set the system bar to be invisible`); + }); + } + + destroyWindow(windowClass: window.Window) { + //销毁窗口 + windowClass.destroyWindow((err) => { + if (err.code) { + Logger.error(TAG, `Failed to destroy the window. Cause : ${JSON.stringify(err)}`) + return; + } + Logger.info(TAG, `Succeeded in destroy the window.`); + }) + } + + /* + * 窗口页面更新 + * Setting.createWindow(pageURI: String, {X,Y,width,height,dpi}?:{X?:number,Y?:number,width?:number,height?:number,dpi?:number}):void + * 必填: + * pageURI:页面路由 + * 选填: + * X,Y:窗口相对于左上角的位置,默认X=100,Y=100 单位为px + * width,height:窗口宽高,默认width=600,height=1250 可配置范围:[0,2560],单位为vp + * dpi:像素密度,默认dpi=330,可配置范围:80-640 + * */ + + createWindow(pageURI: string, X?: number | undefined, Y?: number | undefined, width?: number | undefined, height?: number | undefined, dpi?: number | undefined) { + + this.windowCreateType = "createWindow" + if (X == undefined) { + X = 10; + } + if (Y == undefined) { + Y = 200; + } + if (width == undefined) { + width = 1000; + } + if (height == undefined) { + height = 1000; + } + if (dpi == undefined) { + dpi = 400; + } + Logger.info(TAG, `params,pageURI=${pageURI},X=${X},Y=${Y},width=${width},height=${height},dpi=${dpi}`) + this.changeDpi(dpi) + sleep(1000) + let config: window.Configuration = { + name: "floatWindow", + windowType: window.WindowType.TYPE_FLOAT, + ctx: GlobalThis.getInstance().getContext('context') + }; + + window.createWindow(config, (err, data) => { + if (err.code) { + Logger.error(TAG, `Failed to create the floatWindow. Cause : ${JSON.stringify(err)}`) + return; + } + Logger.info(TAG, `Succeeded in creating the floatWindow. Data : ${JSON.stringify(err)}`); + this.windowClass = data; + this.changeWindowPosition(this.windowClass, X, Y); + this.changeWindowSize(this.windowClass, width, height); + this.loadContent(this.windowClass, pageURI) + }) + } + + async changeWindow(pageURI: string, width?: number | undefined, height?: number | undefined, dpi?: number | undefined) { + + this.windowCreateType = "changeWindow" + if (width == undefined) { + width = 1000; + } + if (height == undefined) { + height = 1000; + } + if (dpi == undefined) { + dpi = 400; + } + Logger.info(TAG, `params,pageURI=${pageURI},width=${width},height=${height},dpi=${dpi}`) + this.changeDpi(dpi) + await sleep(500) + + window.getLastWindow(GlobalThis.getInstance().getContext('context'), async (err: BusinessError, data) => { + const errCode: number = err.code; + if (errCode) { + Logger.error(TAG, 'Failed to obtain the top Window. Cause : ' + JSON.stringify(err)); + return; + } + Logger.info(TAG, 'succeed to obtain the top Window. Cause : ' + JSON.stringify(err)); + this.windowClass = data; + await sleep(200) + this.loadContent(this.windowClass, pageURI) + await sleep(200) + this.changeWindowSize(this.windowClass, width, height); + await sleep(200) + this.setWindowSystemBarEnable(this.windowClass, []); + }); + + } + +} + +export default new Settings() \ No newline at end of file diff --git a/function/ui_compare/uicompare/entry/src/ohosTest/ets/test/model/Utils.ets b/function/ui_compare/uicompare/entry/src/ohosTest/ets/test/model/Utils.ets new file mode 100644 index 0000000000000000000000000000000000000000..1493fe2f1f579cc654348cc704e7ad81d2b78813 --- /dev/null +++ b/function/ui_compare/uicompare/entry/src/ohosTest/ets/test/model/Utils.ets @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2024 Shenzhen Kaihong Digital Industry Development Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import {Driver , ON} from '@ohos.UiTest' + +export default class Utils { + + static async sleep(time : number) :Promise { + const result:string = await new Promise((resolve:Function)=>{ + setTimeout(()=> { + resolve('sleep ok') + },time) + }); + console.info(result); + } + static async waitAsync (ms:number):Promise{ + return new Promise((resolve)=>{ + setTimeout(resolve,ms) + }) + + } + + static async clickComponentByKey (componentKey :string){ + let driver = Driver.create(); + let component = await driver.findComponent(ON.id(componentKey)) + await component.click(); + + } + + +} + + + + diff --git a/function/ui_compare/uicompare/entry/src/ohosTest/ets/test/model/globalThis.ets b/function/ui_compare/uicompare/entry/src/ohosTest/ets/test/model/globalThis.ets new file mode 100644 index 0000000000000000000000000000000000000000..43f44611f00071f6ca9c98a989893c713629cd98 --- /dev/null +++ b/function/ui_compare/uicompare/entry/src/ohosTest/ets/test/model/globalThis.ets @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2024 Shenzhen Kaihong Digital Industry Development Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import common from '@ohos.app.ability.common' + +// 构造单例对象 + +export class GlobalThis { + + private constructor() { + } + + private static instance:GlobalThis; + + private _uiContexts = new Map (); + + public static getInstance():GlobalThis { + + if (!GlobalThis.instance) { + GlobalThis.instance = new GlobalThis(); + + } + + return GlobalThis.instance + + } + getContext(key:string):common.UIAbilityContext | undefined { + + return this._uiContexts.get(key); + } + + setContext(key:string, value:common.UIAbilityContext) :void { + this._uiContexts.set(key,value); + } + + // 其他需要传递的类依次扩展 +} \ No newline at end of file diff --git a/function/ui_compare/uicompare/entry/src/ohosTest/ets/test/model/snapShot.ets b/function/ui_compare/uicompare/entry/src/ohosTest/ets/test/model/snapShot.ets new file mode 100644 index 0000000000000000000000000000000000000000..88d046b2d4e862c4a5e0f9deb7614ff8ec10ff9a --- /dev/null +++ b/function/ui_compare/uicompare/entry/src/ohosTest/ets/test/model/snapShot.ets @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2024 Shenzhen Kaihong Digital Industry Development Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import window from '@ohos.window'; +import Logger from './Logger'; +import image from '@ohos.multimedia.image'; +import mediaLibrary from '@ohos.multimedia.mediaLibrary'; +import fs from '@ohos.file.fs'; +import common from '@ohos.app.ability.common' +import { GlobalThis } from './globalThis' +import screenshot from '@ohos.screenshot' +import { BusinessError } from '@ohos.base' +import Settings from './Settings'; + +const TAG: string = '[TEST]'; + + +class windowSnap { + filePath(pageName ?: string) { + let context: common.UIAbilityContext | undefined = GlobalThis.getInstance().getContext('context') + Logger.info(TAG, 'deleteFile start'); + let pathDir = context ? context.filesDir : "/" + Logger.info(TAG, `deleteFile pathDir = ${pathDir}`) + let name = pageName ? pageName : "test" + let displayName = `IMG_${name}.webp` + let filePath: string = pathDir + "/" + displayName; + Logger.info(TAG, 'createFile filePath = ${filePath}') + return filePath; + + } + + async deleteFile(filePath: string) { + let exists = await fs.access(filePath) + Logger.info(TAG, `oldFile exists = ${exists}`) + if (exists) { + fs.unlinkSync(filePath) + Logger.info(TAG, `remove file succeed`) + } + } + + createFile(filePath: string) { + return fs.openSync(filePath, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE) + } + + async save(filePath: string, data: image.PixelMap) { + let packOpts: image.PackingOption = { + format: "image/webp", quality: 100 + } + let imagePackerApi = image.createImagePacker() + let arrayBuffer = await imagePackerApi.packing(data, packOpts) + let file: fs.File | null = null; + while (file == null) { + file = this.createFile(filePath) + } + let exists = await fs.access(file.path) + Logger.info(TAG, `createFile exists = ${exists}`) + let fd = file.fd + Logger.info(TAG, `createFile fd = ${fd}`) + imagePackerApi.release() + try { + await fs.write(fd, arrayBuffer) + } catch (err) { + Logger.error(TAG, `write failed code is ${err.code},message is ${err.message}`) + } + fs.closeSync(file) + Logger.info(TAG, `write picture done`) + } + + /* + * 获取窗口结合&文件保存 + * 入参必填 ability的context + + * snapShot + * save:设备端保存路径:/data/app/el2/100/base/{bundle_name}/haps/entry_test/files/IMG_test.webp + * 查看命令:hdc_st shell ,cd + **/ + async snapShot(pageName?: string) { + //获取窗口 + Logger.info(TAG, 'start snapshot') + if (Settings.windowClass == undefined) { + Logger.info(TAG, 'getWindowClass error') + return; + } + Logger.info(TAG, 'find window success') + let filePath = this.filePath(pageName) + this.deleteFile(filePath); + + //截屏 + Settings.windowClass.snapshot((err, data: image.PixelMap) => { + if (err.code) { + Logger.info('failed to snap window .cause : ' + JSON.stringify(err)) + return; + } + Logger.info(TAG, 'Succeed in saving screenshot. Pixel bytes number: ' + data.getPixelBytesNumber()) + this.save(filePath, data) + + data.release(); + }); + } + + /* + * 获取窗口结合&文件保存 + * 入参必填 ability的context + * screenShot + * save:设备端保存路径:/data/app/el2/100/base/{bundle_name}/haps/entry_test/files/IMG_test.webp + * 查看命令:hdc_st shell ,cd + **/ + + async screenShot(pageName?: string) { + Logger.info(TAG, 'start screen shot') + if (Settings.windowClass == undefined) { + Logger.info(TAG, 'getWindowClass error') + return; + } + Logger.info(TAG, 'find window success') + let filePath = this.filePath(pageName) + this.deleteFile(filePath) + + // 获取窗口属性 + try { + let properties = Settings.windowClass.getWindowProperties(); + Logger.info(TAG, 'properties.windowRect = ' + JSON.stringify(properties)) + + let screenshotOptions: screenshot.ScreenshotOptions = { + "screenRect": properties.windowRect, + "imageSize": { + "width": properties.windowRect.width, + "height": properties.windowRect.height + } + }; + Logger.info(TAG, 'screenshotOptions: ' + JSON.stringify(screenshotOptions)); + try { + screenshot.save(screenshotOptions, (err: BusinessError, pixelMap: image.PixelMap) => { + if (err) { + Logger.info('failed to save screenshot .Code : ' + JSON.stringify(err)) + return; + } + Logger.info(TAG, 'Succeed in saving screenshot. Pixel bytes number: ' + pixelMap.getPixelBytesNumber()) + this.save(filePath, pixelMap) //pixelMap 保存生成文件 + pixelMap.release(); + }); + } catch (exception) { + Logger.error(TAG, 'failed to : save screenshot. code: ' + JSON.stringify(exception)); + } + + } catch (exception) { + Logger.error(TAG, 'failed to obtain the window properties .Cause : ' + JSON.stringify(exception)) + } + } +} + +export default new windowSnap() \ No newline at end of file diff --git a/function/ui_compare/uicompare/entry/src/ohosTest/ets/testability/TestAbility.ets b/function/ui_compare/uicompare/entry/src/ohosTest/ets/testability/TestAbility.ets new file mode 100644 index 0000000000000000000000000000000000000000..d8e4f9389ae2a75c4dec35ec6a4eecb63deddae2 --- /dev/null +++ b/function/ui_compare/uicompare/entry/src/ohosTest/ets/testability/TestAbility.ets @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2024 Shenzhen Kaihong Digital Industry Development Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import bundleManager from '@ohos.bundle.bundleManager'; +import abilityAccessCtrl from '@ohos.abilityAccessCtrl'; +import UIAbility from '@ohos.app.ability.UIAbility'; +import AbilityDelegatorRegistry from '@ohos.app.ability.abilityDelegatorRegistry'; +import hilog from '@ohos.hilog'; +import { Hypium } from '@ohos/hypium'; +import testsuite from '../test/List.test'; +import window from '@ohos.window'; +import Logger from '../test/model/Logger' +import { GlobalThis } from '../test/model/globalThis' +import Want from '@ohos.app.ability.Want' +import AbilityConstant from '@ohos.app.ability.AbilityConstant'; + + +const TAG: string = '[TEST]' + +async function sleep(time: number): Promise { + const result: string = await new Promise((resolve: Function) => { + setTimeout(() => { + resolve('sleep ok') + }, time) + }); + console.info(result); +} + +export default class TestAbility extends UIAbility { + onCreate(want: Want, launchParam: AbilityConstant.LaunchParam) { + hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onCreate'); + hilog.info(0x0000, 'testTag', '%{public}s', 'want param:' + JSON.stringify(want) ?? ''); + hilog.info(0x0000, 'testTag', '%{public}s', 'launchParam:' + JSON.stringify(launchParam) ?? ''); + let abilityDelegator: AbilityDelegatorRegistry.AbilityDelegator + abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator() + let abilityDelegatorArguments: AbilityDelegatorRegistry.AbilityDelegatorArgs + abilityDelegatorArguments = AbilityDelegatorRegistry.getArguments() + hilog.info(0x0000, 'testTag', '%{public}s', 'start run testcase!!!'); + Hypium.hypiumTest(abilityDelegator, abilityDelegatorArguments, testsuite) + } + + onDestroy() { + hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onDestroy'); + } + + onWindowStageCreate(windowStage: window.WindowStage) { + hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onWindowStageCreate'); + GlobalThis.getInstance().setContext("context", this.context) + + // + let atManager = abilityAccessCtrl.createAtManager(); + let appFlags = bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION; + let permissionFlags = 0; + bundleManager.getBundleInfoForSelf(appFlags, (err, data) => { + if (err) { + Logger.error(TAG, 'getAllApplicationInfo failed: %{public}s', err.message); + console.error("") + } + let tokenID = data.appInfo.accessTokenId; //系统应用可以通过bundleManager.getApplicationInfo获取,普通应用可以通过bundleManager.getBundleInfoForSelf获取 + atManager.grantUserGrantedPermission(tokenID, 'ohos.permission.READ_MEDIA', permissionFlags, (err, data) => { + if (err) { + Logger.info(TAG, `grantUserGrantedPermission fail, err->${JSON.stringify(err)}`) + } + }) + atManager.grantUserGrantedPermission(tokenID, 'ohos.permission.WRITE_MEDIA', permissionFlags, (err, data) => { + if (err) { + Logger.info(TAG, `grantUserGrantedPermission fail, err->${JSON.stringify(err)}`) + } + }) + }) + + } + + onWindowStageDestroy() { + hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onWindowStageDestroy'); + } + + onForeground() { + hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onForeground'); + } + + onBackground() { + hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onBackground'); + } +} \ No newline at end of file diff --git a/function/ui_compare/uicompare/entry/src/ohosTest/ets/testability/pages/Index.ets b/function/ui_compare/uicompare/entry/src/ohosTest/ets/testability/pages/Index.ets new file mode 100644 index 0000000000000000000000000000000000000000..7670cdf55fd7e4e287418119800be585b7a0f32b --- /dev/null +++ b/function/ui_compare/uicompare/entry/src/ohosTest/ets/testability/pages/Index.ets @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2024 Shenzhen Kaihong Digital Industry Development Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import hilog from '@ohos.hilog'; + +@Entry +@Component +struct Index { + aboutToAppear() { + hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility index aboutToAppear'); + } + @State message: string = 'Hello World' + + build() { + Row() { + Column() { + Text(this.message) + .fontSize(50) + .fontWeight(FontWeight.Bold) + Button() { + Text('next page') + .fontSize(20) + .fontWeight(FontWeight.Bold) + } + .type(ButtonType.Capsule) + .margin({ + top: 20 + }) + .backgroundColor('#0D9FFB') + .width('35%') + .height('5%') + .onClick(()=>{ + }) + } + .width('100%') + } + .height('100%') + } + } \ No newline at end of file diff --git a/function/ui_compare/uicompare/entry/src/ohosTest/ets/testability/pages/UIAttributeFontInterface/UIAttributeFontInterface001.ets b/function/ui_compare/uicompare/entry/src/ohosTest/ets/testability/pages/UIAttributeFontInterface/UIAttributeFontInterface001.ets new file mode 100644 index 0000000000000000000000000000000000000000..2978c8d02758ffdb07b525ebdde3de0bf8f51ad9 --- /dev/null +++ b/function/ui_compare/uicompare/entry/src/ohosTest/ets/testability/pages/UIAttributeFontInterface/UIAttributeFontInterface001.ets @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2024 Shenzhen Kaihong Digital Industry Development Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +@Entry +@Component +struct UIAttributeFontInterface001 { + @State message: string = 'Hello' + + build() { + Row() { + Column() { + Text(this.message) + .fontSize(20) + .fontColor(Color.Blue) + .margin({ top: 20 }) + .key('UIAttributeFontInterface001_001') + } + .width('100%') + .height('100%') + } + } +} \ No newline at end of file diff --git a/function/ui_compare/uicompare/entry/src/ohosTest/ets/testability/pages/demo.ets b/function/ui_compare/uicompare/entry/src/ohosTest/ets/testability/pages/demo.ets new file mode 100644 index 0000000000000000000000000000000000000000..a4c08adac6aa17b7c2d11a62f56f5afb61c9d6ab --- /dev/null +++ b/function/ui_compare/uicompare/entry/src/ohosTest/ets/testability/pages/demo.ets @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2024 Shenzhen Kaihong Digital Industry Development Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +@Entry +@Component +struct Index { + @State message: string = 'Hello' + @State fontColor: Color = Color.Brown + + onPageShow() { + console.info('NavDestination onPageShow') + + } + + build() { + Row() { + Column() { + Text(this.message) + .fontSize(20) + .fontWeight(FontWeight.Bold) + .fontColor(Color.Green) + .margin({ top: 20 }) + .key('text_demo') + + Button('click') + .fontSize(16) + .fontWeight(FontWeight.Bold) + .backgroundColor(this.fontColor) + .margin({ top: 20 }) + .key('button_demo') + .onClick(() => { + this.message = 'Hi All' + }) + } + .width('100%') + .height('100%') + .backgroundColor(Color.Orange) + + } + .defaultFocus(true) + } +} \ No newline at end of file diff --git a/function/ui_compare/uicompare/entry/src/ohosTest/ets/testrunner/OpenHarmonyTestRunner.ts b/function/ui_compare/uicompare/entry/src/ohosTest/ets/testrunner/OpenHarmonyTestRunner.ts new file mode 100644 index 0000000000000000000000000000000000000000..2739e9c4d7144254ee0b9066576db398477e409d --- /dev/null +++ b/function/ui_compare/uicompare/entry/src/ohosTest/ets/testrunner/OpenHarmonyTestRunner.ts @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2024 Shenzhen Kaihong Digital Industry Development Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import hilog from '@ohos.hilog'; +import TestRunner from '@ohos.application.testRunner'; +import AbilityDelegatorRegistry from '@ohos.app.ability.abilityDelegatorRegistry'; + +var abilityDelegator = undefined +var abilityDelegatorArguments = undefined + +async function onAbilityCreateCallback() { + hilog.info(0x0000, 'testTag', '%{public}s', 'onAbilityCreateCallback'); +} + +async function addAbilityMonitorCallback(err: any) { + hilog.info(0x0000, 'testTag', 'addAbilityMonitorCallback : %{public}s', JSON.stringify(err) ?? ''); +} + +export default class OpenHarmonyTestRunner implements TestRunner { + constructor() { + } + + onPrepare() { + hilog.info(0x0000, 'testTag', '%{public}s', 'OpenHarmonyTestRunner OnPrepare '); + } + + async onRun() { + hilog.info(0x0000, 'testTag', '%{public}s', 'OpenHarmonyTestRunner onRun run'); + abilityDelegatorArguments = AbilityDelegatorRegistry.getArguments() + abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator() + var testAbilityName = abilityDelegatorArguments.bundleName + '.TestAbility' + let lMonitor = { + abilityName: testAbilityName, + onAbilityCreate: onAbilityCreateCallback, + }; + abilityDelegator.addAbilityMonitor(lMonitor, addAbilityMonitorCallback) + var cmd = 'aa start -d 0 -a TestAbility' + ' -b ' + abilityDelegatorArguments.bundleName + var debug = abilityDelegatorArguments.parameters['-D'] + if (debug == 'true') + { + cmd += ' -D' + } + hilog.info(0x0000, 'testTag', 'cmd : %{public}s', cmd); + abilityDelegator.executeShellCommand(cmd, + (err: any, d: any) => { + hilog.info(0x0000, 'testTag', 'executeShellCommand : err : %{public}s', JSON.stringify(err) ?? ''); + hilog.info(0x0000, 'testTag', 'executeShellCommand : data : %{public}s', d.stdResult ?? ''); + hilog.info(0x0000, 'testTag', 'executeShellCommand : data : %{public}s', d.exitCode ?? ''); + }) + hilog.info(0x0000, 'testTag', '%{public}s', 'OpenHarmonyTestRunner onRun end'); + } +} \ No newline at end of file diff --git a/function/ui_compare/uicompare/entry/src/ohosTest/module.json5 b/function/ui_compare/uicompare/entry/src/ohosTest/module.json5 new file mode 100644 index 0000000000000000000000000000000000000000..4a90b7bc30d814d34e29a24bd0191cb420fcb74d --- /dev/null +++ b/function/ui_compare/uicompare/entry/src/ohosTest/module.json5 @@ -0,0 +1,60 @@ +{ + "module": { + "name": "entry_test", + "type": "feature", + "description": "$string:module_test_desc", + "mainElement": "TestAbility", + "deviceTypes": [ + "default", + "tablet" + ], + "deliveryWithInstall": true, + "installationFree": false, + "pages": "$profile:test_pages", + "requestPermissions": [ + { + "name": "ohos.permission.SYSTEM_FLOAT_WINDOW", + "usedScene": { + "abilities":[ + "EntryAbility" + ], + "when": "inuse" + } + }, + { + "name": "ohos.permission.GRANT_SENSITIVE_PERMISSIONS" + }, + { + "name": "ohos.permission.WRITE_MEDIA" + }, + { + "name": "ohos.permission.READ_MEDIA" + }, + { + "name":"ohos.permission.CAPTURE_SCREEN" + } + ], + "abilities": [ + { + "name": "TestAbility", + "srcEntry": "./ets/testability/TestAbility.ets", + "description": "$string:TestAbility_desc", + "icon": "$media:icon", + "label": "$string:TestAbility_label", + "exported": true, + "startWindowIcon": "$media:icon", + "startWindowBackground": "$color:start_window_background", + "skills": [ + { + "actions": [ + "action.system.home" + ], + "entities": [ + "entity.system.home" + ] + } + ] + } + ] + } +} diff --git a/function/ui_compare/uicompare/entry/src/ohosTest/resources/base/element/color.json b/function/ui_compare/uicompare/entry/src/ohosTest/resources/base/element/color.json new file mode 100644 index 0000000000000000000000000000000000000000..3c712962da3c2751c2b9ddb53559afcbd2b54a02 --- /dev/null +++ b/function/ui_compare/uicompare/entry/src/ohosTest/resources/base/element/color.json @@ -0,0 +1,8 @@ +{ + "color": [ + { + "name": "start_window_background", + "value": "#FFFFFF" + } + ] +} \ No newline at end of file diff --git a/function/ui_compare/uicompare/entry/src/ohosTest/resources/base/element/string.json b/function/ui_compare/uicompare/entry/src/ohosTest/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..65d8fa5a7cf54aa3943dcd0214f58d1771bc1f6c --- /dev/null +++ b/function/ui_compare/uicompare/entry/src/ohosTest/resources/base/element/string.json @@ -0,0 +1,16 @@ +{ + "string": [ + { + "name": "module_test_desc", + "value": "test ability description" + }, + { + "name": "TestAbility_desc", + "value": "the test ability" + }, + { + "name": "TestAbility_label", + "value": "test label" + } + ] +} \ No newline at end of file diff --git a/function/ui_compare/uicompare/entry/src/ohosTest/resources/base/media/icon.png b/function/ui_compare/uicompare/entry/src/ohosTest/resources/base/media/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..ce307a8827bd75456441ceb57d530e4c8d45d36c Binary files /dev/null and b/function/ui_compare/uicompare/entry/src/ohosTest/resources/base/media/icon.png differ diff --git a/function/ui_compare/uicompare/entry/src/ohosTest/resources/base/profile/test_pages.json b/function/ui_compare/uicompare/entry/src/ohosTest/resources/base/profile/test_pages.json new file mode 100644 index 0000000000000000000000000000000000000000..05ef251de9c4659b3a57506be934278a1b50f7f8 --- /dev/null +++ b/function/ui_compare/uicompare/entry/src/ohosTest/resources/base/profile/test_pages.json @@ -0,0 +1,7 @@ +{ + "src": [ + "testability/pages/Index", + "testability/pages/demo", + "testability/pages/UIAttributeFontInterface/UIAttributeFontInterface001" + ] +} diff --git a/function/ui_compare/uicompare/hvigor/hvigor-config.json5 b/function/ui_compare/uicompare/hvigor/hvigor-config.json5 new file mode 100644 index 0000000000000000000000000000000000000000..64655e6970c2acdf5708226c4d5a584ca52b6f37 --- /dev/null +++ b/function/ui_compare/uicompare/hvigor/hvigor-config.json5 @@ -0,0 +1,6 @@ +{ + "hvigorVersion": "3.2.4", + "dependencies": { + "@ohos/hvigor-ohos-plugin": "3.2.4" + } +} \ No newline at end of file diff --git a/function/ui_compare/uicompare/hvigor/hvigor-wrapper.js b/function/ui_compare/uicompare/hvigor/hvigor-wrapper.js new file mode 100644 index 0000000000000000000000000000000000000000..372eae8eb4a124095936f9cd78df5c6756746f3f --- /dev/null +++ b/function/ui_compare/uicompare/hvigor/hvigor-wrapper.js @@ -0,0 +1 @@ +"use strict";var u=require("path"),D=require("os"),e=require("fs"),t=require("crypto"),r=require("child_process"),n="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{},i={},C={},F=n&&n.__importDefault||function(u){return u&&u.__esModule?u:{default:u}};Object.defineProperty(C,"__esModule",{value:!0}),C.maxPathLength=C.isMac=C.isLinux=C.isWindows=void 0;const E=F(D),A="Windows_NT",o="Darwin";function a(){return E.default.type()===A}function c(){return E.default.type()===o}C.isWindows=a,C.isLinux=function(){return"Linux"===E.default.type()},C.isMac=c,C.maxPathLength=function(){return c()?1016:a()?259:4095},function(e){var t=n&&n.__createBinding||(Object.create?function(u,D,e,t){void 0===t&&(t=e);var r=Object.getOwnPropertyDescriptor(D,e);r&&!("get"in r?!D.__esModule:r.writable||r.configurable)||(r={enumerable:!0,get:function(){return D[e]}}),Object.defineProperty(u,t,r)}:function(u,D,e,t){void 0===t&&(t=e),u[t]=D[e]}),r=n&&n.__setModuleDefault||(Object.create?function(u,D){Object.defineProperty(u,"default",{enumerable:!0,value:D})}:function(u,D){u.default=D}),i=n&&n.__importStar||function(u){if(u&&u.__esModule)return u;var D={};if(null!=u)for(var e in u)"default"!==e&&Object.prototype.hasOwnProperty.call(u,e)&&t(D,u,e);return r(D,u),D};Object.defineProperty(e,"__esModule",{value:!0}),e.WORK_SPACE=e.HVIGOR_PROJECT_WRAPPER_HOME=e.HVIGOR_PROJECT_ROOT_DIR=e.HVIGOR_PROJECT_CACHES_HOME=e.HVIGOR_PNPM_STORE_PATH=e.HVIGOR_WRAPPER_PNPM_SCRIPT_PATH=e.PROJECT_CACHES=e.HVIGOR_WRAPPER_TOOLS_HOME=e.HVIGOR_USER_HOME=e.DEFAULT_PACKAGE_JSON=e.DEFAULT_HVIGOR_CONFIG_JSON_FILE_NAME=e.PNPM=e.HVIGOR=e.NPM_TOOL=e.PNPM_TOOL=e.HVIGOR_ENGINE_PACKAGE_NAME=void 0;const F=i(D),E=i(u),A=C;e.HVIGOR_ENGINE_PACKAGE_NAME="@ohos/hvigor",e.PNPM_TOOL=(0,A.isWindows)()?"pnpm.cmd":"pnpm",e.NPM_TOOL=(0,A.isWindows)()?"npm.cmd":"npm",e.HVIGOR="hvigor",e.PNPM="pnpm",e.DEFAULT_HVIGOR_CONFIG_JSON_FILE_NAME="hvigor-config.json5",e.DEFAULT_PACKAGE_JSON="package.json",e.HVIGOR_USER_HOME=E.resolve(F.homedir(),".hvigor"),e.HVIGOR_WRAPPER_TOOLS_HOME=E.resolve(e.HVIGOR_USER_HOME,"wrapper","tools"),e.PROJECT_CACHES="project_caches",e.HVIGOR_WRAPPER_PNPM_SCRIPT_PATH=E.resolve(e.HVIGOR_WRAPPER_TOOLS_HOME,"node_modules",".bin",e.PNPM_TOOL),e.HVIGOR_PNPM_STORE_PATH=E.resolve(e.HVIGOR_USER_HOME,"caches"),e.HVIGOR_PROJECT_CACHES_HOME=E.resolve(e.HVIGOR_USER_HOME,e.PROJECT_CACHES),e.HVIGOR_PROJECT_ROOT_DIR=process.cwd(),e.HVIGOR_PROJECT_WRAPPER_HOME=E.resolve(e.HVIGOR_PROJECT_ROOT_DIR,e.HVIGOR),e.WORK_SPACE="workspace"}(i);var s={},l={};Object.defineProperty(l,"__esModule",{value:!0}),l.logInfoPrintConsole=l.logErrorAndExit=void 0,l.logErrorAndExit=function(u){u instanceof Error?console.error(u.message):console.error(u),process.exit(-1)},l.logInfoPrintConsole=function(u){console.log(u)};var B=n&&n.__createBinding||(Object.create?function(u,D,e,t){void 0===t&&(t=e);var r=Object.getOwnPropertyDescriptor(D,e);r&&!("get"in r?!D.__esModule:r.writable||r.configurable)||(r={enumerable:!0,get:function(){return D[e]}}),Object.defineProperty(u,t,r)}:function(u,D,e,t){void 0===t&&(t=e),u[t]=D[e]}),d=n&&n.__setModuleDefault||(Object.create?function(u,D){Object.defineProperty(u,"default",{enumerable:!0,value:D})}:function(u,D){u.default=D}),f=n&&n.__importStar||function(u){if(u&&u.__esModule)return u;var D={};if(null!=u)for(var e in u)"default"!==e&&Object.prototype.hasOwnProperty.call(u,e)&&B(D,u,e);return d(D,u),D};Object.defineProperty(s,"__esModule",{value:!0});var _=s.executeBuild=void 0;const p=f(e),O=f(u),h=l;_=s.executeBuild=function(u){const D=O.resolve(u,"node_modules","@ohos","hvigor","bin","hvigor.js");try{const u=p.realpathSync(D);require(u)}catch(e){(0,h.logErrorAndExit)(`Error: ENOENT: no such file ${D},delete ${u} and retry.`)}};var P={},v={};!function(u){var D=n&&n.__importDefault||function(u){return u&&u.__esModule?u:{default:u}};Object.defineProperty(u,"__esModule",{value:!0}),u.hashFile=u.hash=u.createHash=void 0;const r=D(t),i=D(e);u.createHash=(u="MD5")=>r.default.createHash(u);u.hash=(D,e)=>(0,u.createHash)(e).update(D).digest("hex");u.hashFile=(D,e)=>{if(i.default.existsSync(D))return(0,u.hash)(i.default.readFileSync(D,"utf-8"),e)}}(v);var g={},m={},R={};Object.defineProperty(R,"__esModule",{value:!0}),R.Unicode=void 0;class y{}R.Unicode=y,y.SPACE_SEPARATOR=/[\u1680\u2000-\u200A\u202F\u205F\u3000]/,y.ID_START=/[\xAA\xB5\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u052F\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u0860-\u086A\u08A0-\u08B4\u08B6-\u08BD\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0980\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u09FC\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0AF9\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D\u0C58-\u0C5A\u0C60\u0C61\u0C80\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D54-\u0D56\u0D5F-\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F5\u13F8-\u13FD\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F8\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1877\u1880-\u1884\u1887-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191E\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1C80-\u1C88\u1CE9-\u1CEC\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2160-\u2188\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303C\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312E\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FEA\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA69D\uA6A0-\uA6EF\uA717-\uA71F\uA722-\uA788\uA78B-\uA7AE\uA7B0-\uA7B7\uA7F7-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA8FD\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uA9E0-\uA9E4\uA9E6-\uA9EF\uA9FA-\uA9FE\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA7E-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB65\uAB70-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]|\uD800[\uDC00-\uDC0B\uDC0D-\uDC26\uDC28-\uDC3A\uDC3C\uDC3D\uDC3F-\uDC4D\uDC50-\uDC5D\uDC80-\uDCFA\uDD40-\uDD74\uDE80-\uDE9C\uDEA0-\uDED0\uDF00-\uDF1F\uDF2D-\uDF4A\uDF50-\uDF75\uDF80-\uDF9D\uDFA0-\uDFC3\uDFC8-\uDFCF\uDFD1-\uDFD5]|\uD801[\uDC00-\uDC9D\uDCB0-\uDCD3\uDCD8-\uDCFB\uDD00-\uDD27\uDD30-\uDD63\uDE00-\uDF36\uDF40-\uDF55\uDF60-\uDF67]|\uD802[\uDC00-\uDC05\uDC08\uDC0A-\uDC35\uDC37\uDC38\uDC3C\uDC3F-\uDC55\uDC60-\uDC76\uDC80-\uDC9E\uDCE0-\uDCF2\uDCF4\uDCF5\uDD00-\uDD15\uDD20-\uDD39\uDD80-\uDDB7\uDDBE\uDDBF\uDE00\uDE10-\uDE13\uDE15-\uDE17\uDE19-\uDE33\uDE60-\uDE7C\uDE80-\uDE9C\uDEC0-\uDEC7\uDEC9-\uDEE4\uDF00-\uDF35\uDF40-\uDF55\uDF60-\uDF72\uDF80-\uDF91]|\uD803[\uDC00-\uDC48\uDC80-\uDCB2\uDCC0-\uDCF2]|\uD804[\uDC03-\uDC37\uDC83-\uDCAF\uDCD0-\uDCE8\uDD03-\uDD26\uDD50-\uDD72\uDD76\uDD83-\uDDB2\uDDC1-\uDDC4\uDDDA\uDDDC\uDE00-\uDE11\uDE13-\uDE2B\uDE80-\uDE86\uDE88\uDE8A-\uDE8D\uDE8F-\uDE9D\uDE9F-\uDEA8\uDEB0-\uDEDE\uDF05-\uDF0C\uDF0F\uDF10\uDF13-\uDF28\uDF2A-\uDF30\uDF32\uDF33\uDF35-\uDF39\uDF3D\uDF50\uDF5D-\uDF61]|\uD805[\uDC00-\uDC34\uDC47-\uDC4A\uDC80-\uDCAF\uDCC4\uDCC5\uDCC7\uDD80-\uDDAE\uDDD8-\uDDDB\uDE00-\uDE2F\uDE44\uDE80-\uDEAA\uDF00-\uDF19]|\uD806[\uDCA0-\uDCDF\uDCFF\uDE00\uDE0B-\uDE32\uDE3A\uDE50\uDE5C-\uDE83\uDE86-\uDE89\uDEC0-\uDEF8]|\uD807[\uDC00-\uDC08\uDC0A-\uDC2E\uDC40\uDC72-\uDC8F\uDD00-\uDD06\uDD08\uDD09\uDD0B-\uDD30\uDD46]|\uD808[\uDC00-\uDF99]|\uD809[\uDC00-\uDC6E\uDC80-\uDD43]|[\uD80C\uD81C-\uD820\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872\uD874-\uD879][\uDC00-\uDFFF]|\uD80D[\uDC00-\uDC2E]|\uD811[\uDC00-\uDE46]|\uD81A[\uDC00-\uDE38\uDE40-\uDE5E\uDED0-\uDEED\uDF00-\uDF2F\uDF40-\uDF43\uDF63-\uDF77\uDF7D-\uDF8F]|\uD81B[\uDF00-\uDF44\uDF50\uDF93-\uDF9F\uDFE0\uDFE1]|\uD821[\uDC00-\uDFEC]|\uD822[\uDC00-\uDEF2]|\uD82C[\uDC00-\uDD1E\uDD70-\uDEFB]|\uD82F[\uDC00-\uDC6A\uDC70-\uDC7C\uDC80-\uDC88\uDC90-\uDC99]|\uD835[\uDC00-\uDC54\uDC56-\uDC9C\uDC9E\uDC9F\uDCA2\uDCA5\uDCA6\uDCA9-\uDCAC\uDCAE-\uDCB9\uDCBB\uDCBD-\uDCC3\uDCC5-\uDD05\uDD07-\uDD0A\uDD0D-\uDD14\uDD16-\uDD1C\uDD1E-\uDD39\uDD3B-\uDD3E\uDD40-\uDD44\uDD46\uDD4A-\uDD50\uDD52-\uDEA5\uDEA8-\uDEC0\uDEC2-\uDEDA\uDEDC-\uDEFA\uDEFC-\uDF14\uDF16-\uDF34\uDF36-\uDF4E\uDF50-\uDF6E\uDF70-\uDF88\uDF8A-\uDFA8\uDFAA-\uDFC2\uDFC4-\uDFCB]|\uD83A[\uDC00-\uDCC4\uDD00-\uDD43]|\uD83B[\uDE00-\uDE03\uDE05-\uDE1F\uDE21\uDE22\uDE24\uDE27\uDE29-\uDE32\uDE34-\uDE37\uDE39\uDE3B\uDE42\uDE47\uDE49\uDE4B\uDE4D-\uDE4F\uDE51\uDE52\uDE54\uDE57\uDE59\uDE5B\uDE5D\uDE5F\uDE61\uDE62\uDE64\uDE67-\uDE6A\uDE6C-\uDE72\uDE74-\uDE77\uDE79-\uDE7C\uDE7E\uDE80-\uDE89\uDE8B-\uDE9B\uDEA1-\uDEA3\uDEA5-\uDEA9\uDEAB-\uDEBB]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEA1\uDEB0-\uDFFF]|\uD87A[\uDC00-\uDFE0]|\uD87E[\uDC00-\uDE1D]/,y.ID_CONTINUE=/[\xAA\xB5\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0300-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u0483-\u0487\u048A-\u052F\u0531-\u0556\u0559\u0561-\u0587\u0591-\u05BD\u05BF\u05C1\u05C2\u05C4\u05C5\u05C7\u05D0-\u05EA\u05F0-\u05F2\u0610-\u061A\u0620-\u0669\u066E-\u06D3\u06D5-\u06DC\u06DF-\u06E8\u06EA-\u06FC\u06FF\u0710-\u074A\u074D-\u07B1\u07C0-\u07F5\u07FA\u0800-\u082D\u0840-\u085B\u0860-\u086A\u08A0-\u08B4\u08B6-\u08BD\u08D4-\u08E1\u08E3-\u0963\u0966-\u096F\u0971-\u0983\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BC-\u09C4\u09C7\u09C8\u09CB-\u09CE\u09D7\u09DC\u09DD\u09DF-\u09E3\u09E6-\u09F1\u09FC\u0A01-\u0A03\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A3C\u0A3E-\u0A42\u0A47\u0A48\u0A4B-\u0A4D\u0A51\u0A59-\u0A5C\u0A5E\u0A66-\u0A75\u0A81-\u0A83\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABC-\u0AC5\u0AC7-\u0AC9\u0ACB-\u0ACD\u0AD0\u0AE0-\u0AE3\u0AE6-\u0AEF\u0AF9-\u0AFF\u0B01-\u0B03\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3C-\u0B44\u0B47\u0B48\u0B4B-\u0B4D\u0B56\u0B57\u0B5C\u0B5D\u0B5F-\u0B63\u0B66-\u0B6F\u0B71\u0B82\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BBE-\u0BC2\u0BC6-\u0BC8\u0BCA-\u0BCD\u0BD0\u0BD7\u0BE6-\u0BEF\u0C00-\u0C03\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D-\u0C44\u0C46-\u0C48\u0C4A-\u0C4D\u0C55\u0C56\u0C58-\u0C5A\u0C60-\u0C63\u0C66-\u0C6F\u0C80-\u0C83\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBC-\u0CC4\u0CC6-\u0CC8\u0CCA-\u0CCD\u0CD5\u0CD6\u0CDE\u0CE0-\u0CE3\u0CE6-\u0CEF\u0CF1\u0CF2\u0D00-\u0D03\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D44\u0D46-\u0D48\u0D4A-\u0D4E\u0D54-\u0D57\u0D5F-\u0D63\u0D66-\u0D6F\u0D7A-\u0D7F\u0D82\u0D83\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0DCA\u0DCF-\u0DD4\u0DD6\u0DD8-\u0DDF\u0DE6-\u0DEF\u0DF2\u0DF3\u0E01-\u0E3A\u0E40-\u0E4E\u0E50-\u0E59\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB9\u0EBB-\u0EBD\u0EC0-\u0EC4\u0EC6\u0EC8-\u0ECD\u0ED0-\u0ED9\u0EDC-\u0EDF\u0F00\u0F18\u0F19\u0F20-\u0F29\u0F35\u0F37\u0F39\u0F3E-\u0F47\u0F49-\u0F6C\u0F71-\u0F84\u0F86-\u0F97\u0F99-\u0FBC\u0FC6\u1000-\u1049\u1050-\u109D\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u135D-\u135F\u1380-\u138F\u13A0-\u13F5\u13F8-\u13FD\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F8\u1700-\u170C\u170E-\u1714\u1720-\u1734\u1740-\u1753\u1760-\u176C\u176E-\u1770\u1772\u1773\u1780-\u17D3\u17D7\u17DC\u17DD\u17E0-\u17E9\u180B-\u180D\u1810-\u1819\u1820-\u1877\u1880-\u18AA\u18B0-\u18F5\u1900-\u191E\u1920-\u192B\u1930-\u193B\u1946-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u19D0-\u19D9\u1A00-\u1A1B\u1A20-\u1A5E\u1A60-\u1A7C\u1A7F-\u1A89\u1A90-\u1A99\u1AA7\u1AB0-\u1ABD\u1B00-\u1B4B\u1B50-\u1B59\u1B6B-\u1B73\u1B80-\u1BF3\u1C00-\u1C37\u1C40-\u1C49\u1C4D-\u1C7D\u1C80-\u1C88\u1CD0-\u1CD2\u1CD4-\u1CF9\u1D00-\u1DF9\u1DFB-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u203F\u2040\u2054\u2071\u207F\u2090-\u209C\u20D0-\u20DC\u20E1\u20E5-\u20F0\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2160-\u2188\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D7F-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2DE0-\u2DFF\u2E2F\u3005-\u3007\u3021-\u302F\u3031-\u3035\u3038-\u303C\u3041-\u3096\u3099\u309A\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312E\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FEA\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA62B\uA640-\uA66F\uA674-\uA67D\uA67F-\uA6F1\uA717-\uA71F\uA722-\uA788\uA78B-\uA7AE\uA7B0-\uA7B7\uA7F7-\uA827\uA840-\uA873\uA880-\uA8C5\uA8D0-\uA8D9\uA8E0-\uA8F7\uA8FB\uA8FD\uA900-\uA92D\uA930-\uA953\uA960-\uA97C\uA980-\uA9C0\uA9CF-\uA9D9\uA9E0-\uA9FE\uAA00-\uAA36\uAA40-\uAA4D\uAA50-\uAA59\uAA60-\uAA76\uAA7A-\uAAC2\uAADB-\uAADD\uAAE0-\uAAEF\uAAF2-\uAAF6\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB65\uAB70-\uABEA\uABEC\uABED\uABF0-\uABF9\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE00-\uFE0F\uFE20-\uFE2F\uFE33\uFE34\uFE4D-\uFE4F\uFE70-\uFE74\uFE76-\uFEFC\uFF10-\uFF19\uFF21-\uFF3A\uFF3F\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]|\uD800[\uDC00-\uDC0B\uDC0D-\uDC26\uDC28-\uDC3A\uDC3C\uDC3D\uDC3F-\uDC4D\uDC50-\uDC5D\uDC80-\uDCFA\uDD40-\uDD74\uDDFD\uDE80-\uDE9C\uDEA0-\uDED0\uDEE0\uDF00-\uDF1F\uDF2D-\uDF4A\uDF50-\uDF7A\uDF80-\uDF9D\uDFA0-\uDFC3\uDFC8-\uDFCF\uDFD1-\uDFD5]|\uD801[\uDC00-\uDC9D\uDCA0-\uDCA9\uDCB0-\uDCD3\uDCD8-\uDCFB\uDD00-\uDD27\uDD30-\uDD63\uDE00-\uDF36\uDF40-\uDF55\uDF60-\uDF67]|\uD802[\uDC00-\uDC05\uDC08\uDC0A-\uDC35\uDC37\uDC38\uDC3C\uDC3F-\uDC55\uDC60-\uDC76\uDC80-\uDC9E\uDCE0-\uDCF2\uDCF4\uDCF5\uDD00-\uDD15\uDD20-\uDD39\uDD80-\uDDB7\uDDBE\uDDBF\uDE00-\uDE03\uDE05\uDE06\uDE0C-\uDE13\uDE15-\uDE17\uDE19-\uDE33\uDE38-\uDE3A\uDE3F\uDE60-\uDE7C\uDE80-\uDE9C\uDEC0-\uDEC7\uDEC9-\uDEE6\uDF00-\uDF35\uDF40-\uDF55\uDF60-\uDF72\uDF80-\uDF91]|\uD803[\uDC00-\uDC48\uDC80-\uDCB2\uDCC0-\uDCF2]|\uD804[\uDC00-\uDC46\uDC66-\uDC6F\uDC7F-\uDCBA\uDCD0-\uDCE8\uDCF0-\uDCF9\uDD00-\uDD34\uDD36-\uDD3F\uDD50-\uDD73\uDD76\uDD80-\uDDC4\uDDCA-\uDDCC\uDDD0-\uDDDA\uDDDC\uDE00-\uDE11\uDE13-\uDE37\uDE3E\uDE80-\uDE86\uDE88\uDE8A-\uDE8D\uDE8F-\uDE9D\uDE9F-\uDEA8\uDEB0-\uDEEA\uDEF0-\uDEF9\uDF00-\uDF03\uDF05-\uDF0C\uDF0F\uDF10\uDF13-\uDF28\uDF2A-\uDF30\uDF32\uDF33\uDF35-\uDF39\uDF3C-\uDF44\uDF47\uDF48\uDF4B-\uDF4D\uDF50\uDF57\uDF5D-\uDF63\uDF66-\uDF6C\uDF70-\uDF74]|\uD805[\uDC00-\uDC4A\uDC50-\uDC59\uDC80-\uDCC5\uDCC7\uDCD0-\uDCD9\uDD80-\uDDB5\uDDB8-\uDDC0\uDDD8-\uDDDD\uDE00-\uDE40\uDE44\uDE50-\uDE59\uDE80-\uDEB7\uDEC0-\uDEC9\uDF00-\uDF19\uDF1D-\uDF2B\uDF30-\uDF39]|\uD806[\uDCA0-\uDCE9\uDCFF\uDE00-\uDE3E\uDE47\uDE50-\uDE83\uDE86-\uDE99\uDEC0-\uDEF8]|\uD807[\uDC00-\uDC08\uDC0A-\uDC36\uDC38-\uDC40\uDC50-\uDC59\uDC72-\uDC8F\uDC92-\uDCA7\uDCA9-\uDCB6\uDD00-\uDD06\uDD08\uDD09\uDD0B-\uDD36\uDD3A\uDD3C\uDD3D\uDD3F-\uDD47\uDD50-\uDD59]|\uD808[\uDC00-\uDF99]|\uD809[\uDC00-\uDC6E\uDC80-\uDD43]|[\uD80C\uD81C-\uD820\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872\uD874-\uD879][\uDC00-\uDFFF]|\uD80D[\uDC00-\uDC2E]|\uD811[\uDC00-\uDE46]|\uD81A[\uDC00-\uDE38\uDE40-\uDE5E\uDE60-\uDE69\uDED0-\uDEED\uDEF0-\uDEF4\uDF00-\uDF36\uDF40-\uDF43\uDF50-\uDF59\uDF63-\uDF77\uDF7D-\uDF8F]|\uD81B[\uDF00-\uDF44\uDF50-\uDF7E\uDF8F-\uDF9F\uDFE0\uDFE1]|\uD821[\uDC00-\uDFEC]|\uD822[\uDC00-\uDEF2]|\uD82C[\uDC00-\uDD1E\uDD70-\uDEFB]|\uD82F[\uDC00-\uDC6A\uDC70-\uDC7C\uDC80-\uDC88\uDC90-\uDC99\uDC9D\uDC9E]|\uD834[\uDD65-\uDD69\uDD6D-\uDD72\uDD7B-\uDD82\uDD85-\uDD8B\uDDAA-\uDDAD\uDE42-\uDE44]|\uD835[\uDC00-\uDC54\uDC56-\uDC9C\uDC9E\uDC9F\uDCA2\uDCA5\uDCA6\uDCA9-\uDCAC\uDCAE-\uDCB9\uDCBB\uDCBD-\uDCC3\uDCC5-\uDD05\uDD07-\uDD0A\uDD0D-\uDD14\uDD16-\uDD1C\uDD1E-\uDD39\uDD3B-\uDD3E\uDD40-\uDD44\uDD46\uDD4A-\uDD50\uDD52-\uDEA5\uDEA8-\uDEC0\uDEC2-\uDEDA\uDEDC-\uDEFA\uDEFC-\uDF14\uDF16-\uDF34\uDF36-\uDF4E\uDF50-\uDF6E\uDF70-\uDF88\uDF8A-\uDFA8\uDFAA-\uDFC2\uDFC4-\uDFCB\uDFCE-\uDFFF]|\uD836[\uDE00-\uDE36\uDE3B-\uDE6C\uDE75\uDE84\uDE9B-\uDE9F\uDEA1-\uDEAF]|\uD838[\uDC00-\uDC06\uDC08-\uDC18\uDC1B-\uDC21\uDC23\uDC24\uDC26-\uDC2A]|\uD83A[\uDC00-\uDCC4\uDCD0-\uDCD6\uDD00-\uDD4A\uDD50-\uDD59]|\uD83B[\uDE00-\uDE03\uDE05-\uDE1F\uDE21\uDE22\uDE24\uDE27\uDE29-\uDE32\uDE34-\uDE37\uDE39\uDE3B\uDE42\uDE47\uDE49\uDE4B\uDE4D-\uDE4F\uDE51\uDE52\uDE54\uDE57\uDE59\uDE5B\uDE5D\uDE5F\uDE61\uDE62\uDE64\uDE67-\uDE6A\uDE6C-\uDE72\uDE74-\uDE77\uDE79-\uDE7C\uDE7E\uDE80-\uDE89\uDE8B-\uDE9B\uDEA1-\uDEA3\uDEA5-\uDEA9\uDEAB-\uDEBB]|\uD869[\uDC00-\uDED6\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF34\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEA1\uDEB0-\uDFFF]|\uD87A[\uDC00-\uDFE0]|\uD87E[\uDC00-\uDE1D]|\uDB40[\uDD00-\uDDEF]/,Object.defineProperty(m,"__esModule",{value:!0}),m.JudgeUtil=void 0;const I=R;m.JudgeUtil=class{static isIgnoreChar(u){return"string"==typeof u&&("\t"===u||"\v"===u||"\f"===u||" "===u||" "===u||"\ufeff"===u||"\n"===u||"\r"===u||"\u2028"===u||"\u2029"===u)}static isSpaceSeparator(u){return"string"==typeof u&&I.Unicode.SPACE_SEPARATOR.test(u)}static isIdStartChar(u){return"string"==typeof u&&(u>="a"&&u<="z"||u>="A"&&u<="Z"||"$"===u||"_"===u||I.Unicode.ID_START.test(u))}static isIdContinueChar(u){return"string"==typeof u&&(u>="a"&&u<="z"||u>="A"&&u<="Z"||u>="0"&&u<="9"||"$"===u||"_"===u||"‌"===u||"‍"===u||I.Unicode.ID_CONTINUE.test(u))}static isDigitWithoutZero(u){return/[1-9]/.test(u)}static isDigit(u){return"string"==typeof u&&/[0-9]/.test(u)}static isHexDigit(u){return"string"==typeof u&&/[0-9A-Fa-f]/.test(u)}};var N=n&&n.__importDefault||function(u){return u&&u.__esModule?u:{default:u}};Object.defineProperty(g,"__esModule",{value:!0}),g.parseJsonText=g.parseJsonFile=void 0;const b=N(e),S=N(D),w=N(u),H=m;var x;!function(u){u[u.Char=0]="Char",u[u.EOF=1]="EOF",u[u.Identifier=2]="Identifier"}(x||(x={}));let M,T,V,G,j,J,W="start",U=[],L=0,$=1,k=0,K=!1,z="default",q="'",Z=1;function X(u,D=!1){T=String(u),W="start",U=[],L=0,$=1,k=0,G=void 0,K=D;do{M=Q(),nu[W]()}while("eof"!==M.type);return G}function Q(){for(z="default",j="",q="'",Z=1;;){J=Y();const u=Du[z]();if(u)return u}}function Y(){if(T[L])return String.fromCodePoint(T.codePointAt(L))}function uu(){const u=Y();return"\n"===u?($++,k=0):u?k+=u.length:k++,u&&(L+=u.length),u}g.parseJsonFile=function(u,D=!1,e="utf-8"){const t=b.default.readFileSync(w.default.resolve(u),{encoding:e});try{return X(t,D)}catch(D){if(D instanceof SyntaxError){const e=D.message.split("at");if(2===e.length)throw new Error(`${e[0].trim()}${S.default.EOL}\t at ${u}:${e[1].trim()}`)}throw new Error(`${u} is not in valid JSON/JSON5 format.`)}},g.parseJsonText=X;const Du={default(){switch(J){case"/":return uu(),void(z="comment");case void 0:return uu(),eu("eof")}if(!H.JudgeUtil.isIgnoreChar(J)&&!H.JudgeUtil.isSpaceSeparator(J))return Du[W]();uu()},start(){z="value"},beforePropertyName(){switch(J){case"$":case"_":return j=uu(),void(z="identifierName");case"\\":return uu(),void(z="identifierNameStartEscape");case"}":return eu("punctuator",uu());case'"':case"'":return q=J,uu(),void(z="string")}if(H.JudgeUtil.isIdStartChar(J))return j+=uu(),void(z="identifierName");throw Eu(x.Char,uu())},afterPropertyName(){if(":"===J)return eu("punctuator",uu());throw Eu(x.Char,uu())},beforePropertyValue(){z="value"},afterPropertyValue(){switch(J){case",":case"}":return eu("punctuator",uu())}throw Eu(x.Char,uu())},beforeArrayValue(){if("]"===J)return eu("punctuator",uu());z="value"},afterArrayValue(){switch(J){case",":case"]":return eu("punctuator",uu())}throw Eu(x.Char,uu())},end(){throw Eu(x.Char,uu())},comment(){switch(J){case"*":return uu(),void(z="multiLineComment");case"/":return uu(),void(z="singleLineComment")}throw Eu(x.Char,uu())},multiLineComment(){switch(J){case"*":return uu(),void(z="multiLineCommentAsterisk");case void 0:throw Eu(x.Char,uu())}uu()},multiLineCommentAsterisk(){switch(J){case"*":return void uu();case"/":return uu(),void(z="default");case void 0:throw Eu(x.Char,uu())}uu(),z="multiLineComment"},singleLineComment(){switch(J){case"\n":case"\r":case"\u2028":case"\u2029":return uu(),void(z="default");case void 0:return uu(),eu("eof")}uu()},value(){switch(J){case"{":case"[":return eu("punctuator",uu());case"n":return uu(),tu("ull"),eu("null",null);case"t":return uu(),tu("rue"),eu("boolean",!0);case"f":return uu(),tu("alse"),eu("boolean",!1);case"-":case"+":return"-"===uu()&&(Z=-1),void(z="numerical");case".":case"0":case"I":case"N":return void(z="numerical");case'"':case"'":return q=J,uu(),j="",void(z="string")}if(void 0===J||!H.JudgeUtil.isDigitWithoutZero(J))throw Eu(x.Char,uu());z="numerical"},numerical(){switch(J){case".":return j=uu(),void(z="decimalPointLeading");case"0":return j=uu(),void(z="zero");case"I":return uu(),tu("nfinity"),eu("numeric",Z*(1/0));case"N":return uu(),tu("aN"),eu("numeric",NaN)}if(void 0!==J&&H.JudgeUtil.isDigitWithoutZero(J))return j=uu(),void(z="decimalInteger");throw Eu(x.Char,uu())},zero(){switch(J){case".":case"e":case"E":return void(z="decimal");case"x":case"X":return j+=uu(),void(z="hexadecimal")}return eu("numeric",0)},decimalInteger(){switch(J){case".":case"e":case"E":return void(z="decimal")}if(!H.JudgeUtil.isDigit(J))return eu("numeric",Z*Number(j));j+=uu()},decimal(){switch(J){case".":j+=uu(),z="decimalFraction";break;case"e":case"E":j+=uu(),z="decimalExponent"}},decimalPointLeading(){if(H.JudgeUtil.isDigit(J))return j+=uu(),void(z="decimalFraction");throw Eu(x.Char,uu())},decimalFraction(){switch(J){case"e":case"E":return j+=uu(),void(z="decimalExponent")}if(!H.JudgeUtil.isDigit(J))return eu("numeric",Z*Number(j));j+=uu()},decimalExponent(){switch(J){case"+":case"-":return j+=uu(),void(z="decimalExponentSign")}if(H.JudgeUtil.isDigit(J))return j+=uu(),void(z="decimalExponentInteger");throw Eu(x.Char,uu())},decimalExponentSign(){if(H.JudgeUtil.isDigit(J))return j+=uu(),void(z="decimalExponentInteger");throw Eu(x.Char,uu())},decimalExponentInteger(){if(!H.JudgeUtil.isDigit(J))return eu("numeric",Z*Number(j));j+=uu()},hexadecimal(){if(H.JudgeUtil.isHexDigit(J))return j+=uu(),void(z="hexadecimalInteger");throw Eu(x.Char,uu())},hexadecimalInteger(){if(!H.JudgeUtil.isHexDigit(J))return eu("numeric",Z*Number(j));j+=uu()},identifierNameStartEscape(){if("u"!==J)throw Eu(x.Char,uu());uu();const u=ru();switch(u){case"$":case"_":break;default:if(!H.JudgeUtil.isIdStartChar(u))throw Eu(x.Identifier)}j+=u,z="identifierName"},identifierName(){switch(J){case"$":case"_":case"‌":case"‍":return void(j+=uu());case"\\":return uu(),void(z="identifierNameEscape")}if(!H.JudgeUtil.isIdContinueChar(J))return eu("identifier",j);j+=uu()},identifierNameEscape(){if("u"!==J)throw Eu(x.Char,uu());uu();const u=ru();switch(u){case"$":case"_":case"‌":case"‍":break;default:if(!H.JudgeUtil.isIdContinueChar(u))throw Eu(x.Identifier)}j+=u,z="identifierName"},string(){switch(J){case"\\":return uu(),void(j+=function(){const u=Y(),D=function(){switch(Y()){case"b":return uu(),"\b";case"f":return uu(),"\f";case"n":return uu(),"\n";case"r":return uu(),"\r";case"t":return uu(),"\t";case"v":return uu(),"\v"}return}();if(D)return D;switch(u){case"0":if(uu(),H.JudgeUtil.isDigit(Y()))throw Eu(x.Char,uu());return"\0";case"x":return uu(),function(){let u="",D=Y();if(!H.JudgeUtil.isHexDigit(D))throw Eu(x.Char,uu());if(u+=uu(),D=Y(),!H.JudgeUtil.isHexDigit(D))throw Eu(x.Char,uu());return u+=uu(),String.fromCodePoint(parseInt(u,16))}();case"u":return uu(),ru();case"\n":case"\u2028":case"\u2029":return uu(),"";case"\r":return uu(),"\n"===Y()&&uu(),""}if(void 0===u||H.JudgeUtil.isDigitWithoutZero(u))throw Eu(x.Char,uu());return uu()}());case'"':case"'":if(J===q){const u=eu("string",j);return uu(),u}return void(j+=uu());case"\n":case"\r":case void 0:throw Eu(x.Char,uu());case"\u2028":case"\u2029":!function(u){console.warn(`JSON5: '${Fu(u)}' in strings is not valid ECMAScript; consider escaping.`)}(J)}j+=uu()}};function eu(u,D){return{type:u,value:D,line:$,column:k}}function tu(u){for(const D of u){if(Y()!==D)throw Eu(x.Char,uu());uu()}}function ru(){let u="",D=4;for(;D-- >0;){const D=Y();if(!H.JudgeUtil.isHexDigit(D))throw Eu(x.Char,uu());u+=uu()}return String.fromCodePoint(parseInt(u,16))}const nu={start(){if("eof"===M.type)throw Eu(x.EOF);iu()},beforePropertyName(){switch(M.type){case"identifier":case"string":return V=M.value,void(W="afterPropertyName");case"punctuator":return void Cu();case"eof":throw Eu(x.EOF)}},afterPropertyName(){if("eof"===M.type)throw Eu(x.EOF);W="beforePropertyValue"},beforePropertyValue(){if("eof"===M.type)throw Eu(x.EOF);iu()},afterPropertyValue(){if("eof"===M.type)throw Eu(x.EOF);switch(M.value){case",":return void(W="beforePropertyName");case"}":Cu()}},beforeArrayValue(){if("eof"===M.type)throw Eu(x.EOF);"punctuator"!==M.type||"]"!==M.value?iu():Cu()},afterArrayValue(){if("eof"===M.type)throw Eu(x.EOF);switch(M.value){case",":return void(W="beforeArrayValue");case"]":Cu()}},end(){}};function iu(){const u=function(){let u;switch(M.type){case"punctuator":switch(M.value){case"{":u={};break;case"[":u=[]}break;case"null":case"boolean":case"numeric":case"string":u=M.value}return u}();if(K&&"object"==typeof u&&(u._line=$,u._column=k),void 0===G)G=u;else{const D=U[U.length-1];Array.isArray(D)?K&&"object"!=typeof u?D.push({value:u,_line:$,_column:k}):D.push(u):D[V]=K&&"object"!=typeof u?{value:u,_line:$,_column:k}:u}!function(u){if(u&&"object"==typeof u)U.push(u),W=Array.isArray(u)?"beforeArrayValue":"beforePropertyName";else{const u=U[U.length-1];W=u?Array.isArray(u)?"afterArrayValue":"afterPropertyValue":"end"}}(u)}function Cu(){U.pop();const u=U[U.length-1];W=u?Array.isArray(u)?"afterArrayValue":"afterPropertyValue":"end"}function Fu(u){const D={"'":"\\'",'"':'\\"',"\\":"\\\\","\b":"\\b","\f":"\\f","\n":"\\n","\r":"\\r","\t":"\\t","\v":"\\v","\0":"\\0","\u2028":"\\u2028","\u2029":"\\u2029"};if(D[u])return D[u];if(u<" "){const D=u.charCodeAt(0).toString(16);return`\\x${`00${D}`.substring(D.length)}`}return u}function Eu(u,D){let e="";switch(u){case x.Char:e=void 0===D?`JSON5: invalid end of input at ${$}:${k}`:`JSON5: invalid character '${Fu(D)}' at ${$}:${k}`;break;case x.EOF:e=`JSON5: invalid end of input at ${$}:${k}`;break;case x.Identifier:k-=5,e=`JSON5: invalid identifier character at ${$}:${k}`}const t=new Au(e);return t.lineNumber=$,t.columnNumber=k,t}class Au extends SyntaxError{}var ou={},au=n&&n.__createBinding||(Object.create?function(u,D,e,t){void 0===t&&(t=e);var r=Object.getOwnPropertyDescriptor(D,e);r&&!("get"in r?!D.__esModule:r.writable||r.configurable)||(r={enumerable:!0,get:function(){return D[e]}}),Object.defineProperty(u,t,r)}:function(u,D,e,t){void 0===t&&(t=e),u[t]=D[e]}),cu=n&&n.__setModuleDefault||(Object.create?function(u,D){Object.defineProperty(u,"default",{enumerable:!0,value:D})}:function(u,D){u.default=D}),su=n&&n.__importStar||function(u){if(u&&u.__esModule)return u;var D={};if(null!=u)for(var e in u)"default"!==e&&Object.prototype.hasOwnProperty.call(u,e)&&au(D,u,e);return cu(D,u),D},lu=n&&n.__importDefault||function(u){return u&&u.__esModule?u:{default:u}};Object.defineProperty(ou,"__esModule",{value:!0}),ou.isFileExists=ou.offlinePluginConversion=ou.executeCommand=ou.getNpmPath=ou.hasNpmPackInPaths=void 0;const Bu=r,du=lu(e),fu=su(u),_u=i,pu=l;ou.hasNpmPackInPaths=function(u,D){try{return require.resolve(u,{paths:[...D]}),!0}catch(u){return!1}},ou.getNpmPath=function(){const u=process.execPath;return fu.join(fu.dirname(u),_u.NPM_TOOL)},ou.executeCommand=function(u,D,e){0!==(0,Bu.spawnSync)(u,D,e).status&&(0,pu.logErrorAndExit)(`Error: ${u} ${D} execute failed.See above for details.`)},ou.offlinePluginConversion=function(u,D){return D.startsWith("file:")||D.endsWith(".tgz")?fu.resolve(u,_u.HVIGOR,D.replace("file:","")):D},ou.isFileExists=function(u){return du.default.existsSync(u)&&du.default.statSync(u).isFile()};var Ou=n&&n.__createBinding||(Object.create?function(u,D,e,t){void 0===t&&(t=e);var r=Object.getOwnPropertyDescriptor(D,e);r&&!("get"in r?!D.__esModule:r.writable||r.configurable)||(r={enumerable:!0,get:function(){return D[e]}}),Object.defineProperty(u,t,r)}:function(u,D,e,t){void 0===t&&(t=e),u[t]=D[e]}),hu=n&&n.__setModuleDefault||(Object.create?function(u,D){Object.defineProperty(u,"default",{enumerable:!0,value:D})}:function(u,D){u.default=D}),Pu=n&&n.__importStar||function(u){if(u&&u.__esModule)return u;var D={};if(null!=u)for(var e in u)"default"!==e&&Object.prototype.hasOwnProperty.call(u,e)&&Ou(D,u,e);return hu(D,u),D},vu=n&&n.__importDefault||function(u){return u&&u.__esModule?u:{default:u}};Object.defineProperty(P,"__esModule",{value:!0});var gu=P.initProjectWorkSpace=void 0;const mu=Pu(e),Ru=vu(D),yu=Pu(u),Iu=v,Nu=i,bu=g,Su=l,wu=ou;let Hu,xu,Mu;function Tu(u,D,e){return void 0!==e.dependencies&&(0,wu.offlinePluginConversion)(Nu.HVIGOR_PROJECT_ROOT_DIR,D.dependencies[u])===yu.normalize(e.dependencies[u])}function Vu(){const u=yu.join(Mu,Nu.WORK_SPACE);if((0,Su.logInfoPrintConsole)("Hvigor cleaning..."),!mu.existsSync(u))return;const D=mu.readdirSync(u);if(!D||0===D.length)return;const e=yu.resolve(Mu,"node_modules","@ohos","hvigor","bin","hvigor.js");mu.existsSync(e)&&(0,wu.executeCommand)(process.argv[0],[e,"--stop-daemon"],{});try{D.forEach((D=>{mu.rmSync(yu.resolve(u,D),{recursive:!0})}))}catch(D){(0,Su.logErrorAndExit)(`The hvigor build tool cannot be installed. Please manually clear the workspace directory and synchronize the project again.\n\n Workspace Path: ${u}.`)}}gu=P.initProjectWorkSpace=function(){if(Hu=function(){const u=yu.resolve(Nu.HVIGOR_PROJECT_WRAPPER_HOME,Nu.DEFAULT_HVIGOR_CONFIG_JSON_FILE_NAME);mu.existsSync(u)||(0,Su.logErrorAndExit)(`Error: Hvigor config file ${u} does not exist.`);return(0,bu.parseJsonFile)(u)}(),Mu=function(u){let D;D=function(u){let D=u.hvigorVersion;if(D.startsWith("file:")||D.endsWith(".tgz"))return!1;const e=u.dependencies,t=Object.getOwnPropertyNames(e);for(const u of t){const D=e[u];if(D.startsWith("file:")||D.endsWith(".tgz"))return!1}if(1===t.length&&"@ohos/hvigor-ohos-plugin"===t[0])return D>"2.5.0";return!1}(u)?function(u){let D=`${Nu.HVIGOR_ENGINE_PACKAGE_NAME}@${u.hvigorVersion}`;const e=u.dependencies;if(e){Object.getOwnPropertyNames(e).sort().forEach((u=>{D+=`,${u}@${e[u]}`}))}return(0,Iu.hash)(D)}(u):(0,Iu.hash)(process.cwd());return yu.resolve(Ru.default.homedir(),".hvigor","project_caches",D)}(Hu),xu=function(){const u=yu.resolve(Mu,Nu.WORK_SPACE,Nu.DEFAULT_PACKAGE_JSON);return mu.existsSync(u)?(0,bu.parseJsonFile)(u):{dependencies:{}}}(),!(0,wu.hasNpmPackInPaths)(Nu.HVIGOR_ENGINE_PACKAGE_NAME,[yu.join(Mu,Nu.WORK_SPACE)])||(0,wu.offlinePluginConversion)(Nu.HVIGOR_PROJECT_ROOT_DIR,Hu.hvigorVersion)!==xu.dependencies[Nu.HVIGOR_ENGINE_PACKAGE_NAME]||!function(){function u(u){const D=null==u?void 0:u.dependencies;return void 0===D?0:Object.getOwnPropertyNames(D).length}const D=u(Hu),e=u(xu);if(D+1!==e)return!1;for(const u in null==Hu?void 0:Hu.dependencies)if(!(0,wu.hasNpmPackInPaths)(u,[yu.join(Mu,Nu.WORK_SPACE)])||!Tu(u,Hu,xu))return!1;return!0}()){Vu();try{!function(){(0,Su.logInfoPrintConsole)("Hvigor installing...");for(const u in Hu.dependencies)Hu.dependencies[u]&&(Hu.dependencies[u]=(0,wu.offlinePluginConversion)(Nu.HVIGOR_PROJECT_ROOT_DIR,Hu.dependencies[u]));const u={dependencies:{...Hu.dependencies}};u.dependencies[Nu.HVIGOR_ENGINE_PACKAGE_NAME]=(0,wu.offlinePluginConversion)(Nu.HVIGOR_PROJECT_ROOT_DIR,Hu.hvigorVersion);const D=yu.join(Mu,Nu.WORK_SPACE);try{mu.mkdirSync(D,{recursive:!0});const e=yu.resolve(D,Nu.DEFAULT_PACKAGE_JSON);mu.writeFileSync(e,JSON.stringify(u))}catch(u){(0,Su.logErrorAndExit)(u)}(function(){const u=["config","set","store-dir",Nu.HVIGOR_PNPM_STORE_PATH],D={cwd:yu.join(Mu,Nu.WORK_SPACE),stdio:["inherit","inherit","inherit"]};(0,wu.executeCommand)(Nu.HVIGOR_WRAPPER_PNPM_SCRIPT_PATH,u,D)})(),function(){const u=["install"],D={cwd:yu.join(Mu,Nu.WORK_SPACE),stdio:["inherit","inherit","inherit"]};(0,wu.executeCommand)(Nu.HVIGOR_WRAPPER_PNPM_SCRIPT_PATH,u,D)}(),(0,Su.logInfoPrintConsole)("Hvigor install success.")}()}catch(u){Vu()}}return Mu};var Gu={};!function(t){var C=n&&n.__createBinding||(Object.create?function(u,D,e,t){void 0===t&&(t=e);var r=Object.getOwnPropertyDescriptor(D,e);r&&!("get"in r?!D.__esModule:r.writable||r.configurable)||(r={enumerable:!0,get:function(){return D[e]}}),Object.defineProperty(u,t,r)}:function(u,D,e,t){void 0===t&&(t=e),u[t]=D[e]}),F=n&&n.__setModuleDefault||(Object.create?function(u,D){Object.defineProperty(u,"default",{enumerable:!0,value:D})}:function(u,D){u.default=D}),E=n&&n.__importStar||function(u){if(u&&u.__esModule)return u;var D={};if(null!=u)for(var e in u)"default"!==e&&Object.prototype.hasOwnProperty.call(u,e)&&C(D,u,e);return F(D,u),D},A=n&&n.__importDefault||function(u){return u&&u.__esModule?u:{default:u}};Object.defineProperty(t,"__esModule",{value:!0}),t.executeInstallPnpm=t.isPnpmInstalled=t.environmentHandler=t.checkNpmConifg=t.PNPM_VERSION=void 0;const o=r,a=E(e),c=A(D),s=E(u),B=i,d=l,f=ou;t.PNPM_VERSION="7.30.0",t.checkNpmConifg=function(){const u=s.resolve(B.HVIGOR_PROJECT_ROOT_DIR,".npmrc"),D=s.resolve(c.default.homedir(),".npmrc");if((0,f.isFileExists)(u)||(0,f.isFileExists)(D))return;const e=(0,f.getNpmPath)(),t=(0,o.spawnSync)(e,["config","get","prefix"],{cwd:B.HVIGOR_PROJECT_ROOT_DIR});if(0!==t.status||!t.stdout)return void(0,d.logErrorAndExit)("Error: The hvigor depends on the npmrc file. Configure the npmrc file first.");const r=s.resolve(`${t.stdout}`.replace(/[\r\n]/gi,""),".npmrc");(0,f.isFileExists)(r)||(0,d.logErrorAndExit)("Error: The hvigor depends on the npmrc file. Configure the npmrc file first.")},t.environmentHandler=function(){process.env["npm_config_update-notifier"]="false"},t.isPnpmInstalled=function(){return!!a.existsSync(B.HVIGOR_WRAPPER_PNPM_SCRIPT_PATH)&&(0,f.hasNpmPackInPaths)("pnpm",[B.HVIGOR_WRAPPER_TOOLS_HOME])},t.executeInstallPnpm=function(){(0,d.logInfoPrintConsole)(`Installing pnpm@${t.PNPM_VERSION}...`);const u=(0,f.getNpmPath)();!function(){const u=s.resolve(B.HVIGOR_WRAPPER_TOOLS_HOME,B.DEFAULT_PACKAGE_JSON);try{a.existsSync(B.HVIGOR_WRAPPER_TOOLS_HOME)||a.mkdirSync(B.HVIGOR_WRAPPER_TOOLS_HOME,{recursive:!0});const D={dependencies:{}};D.dependencies[B.PNPM]=t.PNPM_VERSION,a.writeFileSync(u,JSON.stringify(D))}catch(D){(0,d.logErrorAndExit)(`Error: EPERM: operation not permitted,create ${u} failed.`)}}(),(0,f.executeCommand)(u,["install","pnpm"],{cwd:B.HVIGOR_WRAPPER_TOOLS_HOME,stdio:["inherit","inherit","inherit"],env:process.env}),(0,d.logInfoPrintConsole)("Pnpm install success.")}}(Gu),function(){Gu.checkNpmConifg(),Gu.environmentHandler(),Gu.isPnpmInstalled()||Gu.executeInstallPnpm();const D=gu();_(u.join(D,i.WORK_SPACE))}(); \ No newline at end of file diff --git a/function/ui_compare/uicompare/hvigorfile.ts b/function/ui_compare/uicompare/hvigorfile.ts new file mode 100644 index 0000000000000000000000000000000000000000..6478186902c0c1ad7c966a929c7d6b7d8ae7a9f3 --- /dev/null +++ b/function/ui_compare/uicompare/hvigorfile.ts @@ -0,0 +1,2 @@ +// Script for compiling build behavior. It is built in the build plug-in and cannot be modified currently. +export { appTasks } from '@ohos/hvigor-ohos-plugin'; \ No newline at end of file diff --git a/function/ui_compare/uicompare/hvigorw b/function/ui_compare/uicompare/hvigorw new file mode 100644 index 0000000000000000000000000000000000000000..54aadd226b453397860013d328fd01031648fc31 --- /dev/null +++ b/function/ui_compare/uicompare/hvigorw @@ -0,0 +1,48 @@ +#!/bin/bash + +# ---------------------------------------------------------------------------- +# Hvigor startup script, version 1.0.0 +# +# Required ENV vars: +# ------------------ +# NODE_HOME - location of a Node home dir +# or +# Add /usr/local/nodejs/bin to the PATH environment variable +# ---------------------------------------------------------------------------- + +HVIGOR_APP_HOME=$(dirname $(readlink -f $0)) +HVIGOR_WRAPPER_SCRIPT=${HVIGOR_APP_HOME}/hvigor/hvigor-wrapper.js +warn() { + echo "" + echo -e "\033[1;33m`date '+[%Y-%m-%d %H:%M:%S]'`$@\033[0m" +} + +error() { + echo "" + echo -e "\033[1;31m`date '+[%Y-%m-%d %H:%M:%S]'`$@\033[0m" +} + +fail() { + error "$@" + exit 1 +} + +# Determine node to start hvigor wrapper script +if [ -n "${NODE_HOME}" ];then + EXECUTABLE_NODE="${NODE_HOME}/bin/node" + if [ ! -x "$EXECUTABLE_NODE" ];then + fail "ERROR: NODE_HOME is set to an invalid directory,check $NODE_HOME\n\nPlease set NODE_HOME in your environment to the location where your nodejs installed" + fi +else + EXECUTABLE_NODE="node" + which ${EXECUTABLE_NODE} > /dev/null 2>&1 || fail "ERROR: NODE_HOME is not set and not 'node' command found in your path" +fi + +# Check hvigor wrapper script +if [ ! -r "$HVIGOR_WRAPPER_SCRIPT" ];then + fail "ERROR: Couldn't find hvigor/hvigor-wrapper.js in ${HVIGOR_APP_HOME}" +fi + +# start hvigor-wrapper script +exec "${EXECUTABLE_NODE}" \ + "${HVIGOR_WRAPPER_SCRIPT}" "$@" diff --git a/function/ui_compare/uicompare/hvigorw.bat b/function/ui_compare/uicompare/hvigorw.bat new file mode 100644 index 0000000000000000000000000000000000000000..6861293e47dfd0186da380321b73be9033507e24 --- /dev/null +++ b/function/ui_compare/uicompare/hvigorw.bat @@ -0,0 +1,64 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Hvigor startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +set WRAPPER_MODULE_PATH=%APP_HOME%\hvigor\hvigor-wrapper.js +set NODE_EXE=node.exe + +goto start + +:start +@rem Find node.exe +if defined NODE_HOME goto findNodeFromNodeHome + +%NODE_EXE% --version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: NODE_HOME is not set and no 'node' command could be found in your PATH. +echo. +echo Please set the NODE_HOME variable in your environment to match the +echo location of your NodeJs installation. + +goto fail + +:findNodeFromNodeHome +set NODE_HOME=%NODE_HOME:"=% +set NODE_EXE_PATH=%NODE_HOME%/%NODE_EXE% + +if exist "%NODE_EXE_PATH%" goto execute +echo. +echo ERROR: NODE_HOME is not set and no 'node' command could be found in your PATH. +echo. +echo Please set the NODE_HOME variable in your environment to match the +echo location of your NodeJs installation. + +goto fail + +:execute +@rem Execute hvigor +"%NODE_EXE%" %WRAPPER_MODULE_PATH% %* + +if "%ERRORLEVEL%" == "0" goto hvigorwEnd + +:fail +exit /b 1 + +:hvigorwEnd +if "%OS%" == "Windows_NT" endlocal + +:end diff --git a/function/ui_compare/uicompare/oh-package-lock.json5 b/function/ui_compare/uicompare/oh-package-lock.json5 new file mode 100644 index 0000000000000000000000000000000000000000..1faa6b99d48e66baa345e65d2fa943a546f91182 --- /dev/null +++ b/function/ui_compare/uicompare/oh-package-lock.json5 @@ -0,0 +1,15 @@ +{ + "lockfileVersion": 2, + "ATTENTION": "THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.", + "specifiers": { + "@ohos/hypium@1.0.6": "@ohos/hypium@1.0.6" + }, + "packages": { + "@ohos/hypium@1.0.6": { + "resolved": "https://repo.harmonyos.com/ohpm/@ohos/hypium/-/hypium-1.0.6.tgz", + "integrity": "sha512-bb3DWeWhYrFqj9mPFV3yZQpkm36kbcK+YYaeY9g292QKSjOdmhEIQR2ULPvyMsgSR4usOBf5nnYrDmaCCXirgQ==", + "registryType": "ohpm", + "shasum": "3f5fed65372633233264b3447705b0831dfe7ea1" + } + } +} \ No newline at end of file diff --git a/function/ui_compare/uicompare/oh-package.json5 b/function/ui_compare/uicompare/oh-package.json5 new file mode 100644 index 0000000000000000000000000000000000000000..267c2fb1544d43ec9b8ceb0e67c58c1d6a63a7f2 --- /dev/null +++ b/function/ui_compare/uicompare/oh-package.json5 @@ -0,0 +1,12 @@ +{ + "license": "", + "devDependencies": { + "@ohos/hypium": "1.0.6" + }, + "author": "", + "name": "uicompare", + "description": "Please describe the basic information.", + "main": "", + "version": "1.0.0", + "dependencies": {} +} diff --git a/function/ui_compare/uicompare_tools/UiCompareTools_progress.py b/function/ui_compare/uicompare_tools/UiCompareTools_progress.py new file mode 100644 index 0000000000000000000000000000000000000000..94ec622d4d9466e428eac2b5906a3df2745984bb --- /dev/null +++ b/function/ui_compare/uicompare_tools/UiCompareTools_progress.py @@ -0,0 +1,2810 @@ +# -*- coding:utf-8 -*- + +import os + +import sys + +import time + +import json + +import openpyxl + +import threading + +import subprocess + +import filecmp + +import shutil + +from multiprocessing import Manager, Pool, Process + +import numpy as np + +from datetime import datetime + +from PIL import Image + +from PIL import ImageChops + +from itertools import product + + + +import argparse + +parser = argparse.ArgumentParser(description='manual to this script') + +parser.add_argument("--type", type=str) + +parser.add_argument("--excel", type=str) + +parser.add_argument("--dir", type=str) + +parser.add_argument("--hap", type=str) + +parser.add_argument("--json", type=str) + +args = parser.parse_args() + +mode = args.type + +excel_path = args.excel + +base_path = args.dir + +hap_path = args.hap + +json_path = args.json + + + +new_time = time.strftime('%Y%m%d_%H%M%S', time.localtime(time.time())) + +now_time = str(new_time) + +dir_path = os.path.dirname(os.path.abspath(__file__)) + +reports_path = os.path.join(dir_path, "reports") + +html_path = "./mode.html" + +run_json_path = os.path.join(reports_path, "run_json_" + now_time) + +run_report_path = os.path.join(reports_path, "run_report_{}.html".format(now_time)) + +diff_report_path = os.path.join(reports_path, "diff_report_{}.html".format(now_time)) + +device_list = [] + +image_size_diff = [] + + + +def get_file_list(file_path, file_key): + + # 返回指定文件夹file_path下所有的特定文件名列表 + + file_list = [] + + if os.path.isdir(file_path) == False: + + print("当前路径{}非文件夹,请重新输入正确的文件夹路径后重试".format(file_path)) + + return [] + + elif os.path.exists(file_path) == False: + + print("当前路径{}不存在,请重新输入正确的文件夹路径后重试".format(file_path)) + + return [] + + for file in os.listdir(file_path): + + if file.endswith(file_key): + + file_list.append(file) + + return file_list + + + + + +def write_report_html(data, mode="0", image_path=""): + + if os.path.exists(html_path) == False: + + print("模板html不存在,未生成html报告") + + return + + if mode == "0": + + shutil.copy(html_path, run_report_path) + + module_data = "" + + total_data = "" + + print("report_data: ", data) + + for key, value in data.items(): + + if key == "Total_data": + + with open(run_report_path, "a") as f: + + f.write("""Save Image Path: + + + + + + + + + + + + + + + + + +
""" + image_path + """
Test Start/ End Time:""" + value["start_end_time"] + """Execution Time:""" + value["run_time"] + """
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
MOUDULESTOTAL_TESTSPASSCASE_FAILGET_FAILPASS_RATE
""" + value["moudle_count"] + """""" + value["Total"] + """""" + value["Pass"] + """""" + value["Case_fail"] + """""" + value["Get_fail"] + """""" + value["Pass_rate"] + """
+ + + +""") + + else: + + if isinstance(value, int): + + continue + + total_data += """ + + + + """ + key + """ + + """ + str(value["Total"]) + """ + + """ + str(value["Pass"]) + """ + + """ + str(value["Case_fail"]) + """ + + """ + str(value["Get_fail"]) + """ + + """ + value["Pass_rate"] + """ + + + +""" + + title_flag = True + + for case_name, run_result, use_time in value["result_list"]: + + if run_result == "PASS": + + # 仅展示执行失败用例,执行成功的用例不展示 + + continue + + elif title_flag == True: + + module_data += """ + + + + + + + + + +
+ + """ + key + """ + +
+ + + + + + + + + + + + + + + + """ + + title_flag = False + + module_data += """ + + + + + + + + + + """ + + if title_flag == False: + + module_data += """ + +
ModuleTestsuiteTestcaseResult
""" + key + """""" + value["module_name"] + """""" + case_name + """""" + run_result + """
""" + + module_data += """ + + + + + + + +""" + + total_data += "" + + with open(run_report_path, "a") as f: + + f.write(total_data + module_data) + + print("获取图片报告已生成:", run_report_path) + + elif mode == "1": + + shutil.copy(html_path, diff_report_path) + + module_data = "" + + total_data = "" + + for key, value in data.items(): + + if key == "Total_data": + + with open(diff_report_path, "a") as f: + + f.write("""Diff Image Path: + + + + + + + + + + + + + + + + + +
""" + image_path + """
Test Start/ End Time:""" + value["start_end_time"] + """Execution Time:""" + value["run_time"] + """
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
MOUDULESTOTAL_TESTSPASSCASE_FAILGET_FAILBASE_IMAGECOMPARE_FAILPASS_RATE
""" + value["moudle_count"] + """""" + value["Total"] + """""" + value["Pass"] + """""" + value["Case_fail"] + """""" + value["Get_fail"] + """""" + value["Base_image_fail"] + """""" + value["Compare_fail"] + """""" + value["Pass_rate"] + """
+ + """) + + else: + + if isinstance(value, int): + + continue + + total_data += """ + + + + """ + key + """ + + """ + str(value["Total"]) + """ + + """ + str(value["Pass"]) + """ + + """ + str(value["Case_fail"]) + """ + + """ + str(value["Get_fail"]) + """ + + """ + str(value["Base_image_fail"]) + """ + + """ + str(value["Compare_fail"]) + """ + + """ + value["Pass_rate"] + """ + + + +""" + + add_flag = True + + for case_name, run_result, use_time in value["result_list"]: + + if run_result == "PASS": + + # 仅展示对比失败用例,对比图片相同的用例不展示 + + continue + + else: + + if add_flag == True: + + # 仅在有对比失败用例时,才写入标题栏 + + module_data += """ + + + + + + + + + +
+ + """ + key + """ + +
+ + + + + + + + + + + + + + """ + + add_flag = False + + module_data += """ + + + + + + + + """ + + if add_flag == False: + + module_data += """ + +
ModuleTestcaseResult
""" + key + """""" + case_name + """""" + run_result + """
""" + + module_data += """ + + + + + + + + """ + + total_data += "" + + with open(diff_report_path, "a") as f: + + f.write(total_data + module_data) + + print("对比图片报告已生成:", diff_report_path) + + + + + +def run_cmd(command): + + # 执行cmd命令,返回执行日志 + + p = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE) + + out, err = p.communicate() + + # print("out: ", out) + + # print("err: ", err) + + if err is None: + + return out.decode('utf-8') + + return err.decode('utf-8') + + + + + +def checkResult(command, keyword="", case_name=""): + + # 检查设备是否连接成功或用例执行成功 + + start_time = datetime.timestamp(datetime.now()) + + out = run_cmd(command) + + end_time = datetime.timestamp(datetime.now()) + + use_time = round(end_time - start_time, 3) + + if "#" not in command: + + # 批量执行,计算平均耗时 + + use_time = int(use_time / len(out.splitlines())) + + print("{0} run use_time: {1}s".format(case_name, use_time)) + + if "error: get bundle info failed" in out: + + print("【ERROR】测试应用hap未安装,请成功安装后重试\n", out) + + return "测试hap未安装" + + elif keyword in command: + + # 检查设备是否连接成功 + + if "[Empty]" in out: + + # 设备未连接时,显示“[Empty]” + + print("设备未连接,请连接设备或检查线路连接是否正常,UI工具将开始尝试重连") + + return case_name + " Devices connect fail" + + elif out == "": + + # 无hdc.exe + + return "不支持hdc命令, 仅支持hdc_std命令" + + elif len(out) > 15: + + return "设备已连接,可正常使用" + + elif "execute timeout" in out: + + return "timeout" + + elif "Not found entry_test/ets/" in out: + + return True + + elif "stream=Tests run: 0" in out: + + return False + + elif keyword != "list targets": + + image_name_list = [] + + error_case_list = [] + + command_list = [] + + # 获取用例名和失败用例名 + + index_flag = 0 + + for line_data in out.splitlines(): + + # print(index_flag, line_data) + + # 用例执行成功 + + if "OHOS_REPORT_STATUS_CODE: 0" in line_data: + + # 当前用例名 + + case_name = out.splitlines()[index_flag - 1].split("=")[1] + + image_name_list.append([case_name, "PASS", use_time]) + + print(case_name, " run succ !!!\n") + + elif "OHOS_REPORT_STATUS_CODE: -1" in line_data or "TestFinished-ResultCode: -1" in line_data: + + # 用例执行失败 + + if "TestFinished-ResultCode: -1" in line_data and index_flag >= 3: + + # 当前用例名 + + case_name = out.splitlines()[index_flag - 3].split("test=")[1] + + error_info = out.splitlines()[index_flag + 1].split(":")[1] + + print(out.splitlines()[index_flag + 1]) + + image_name_list.append([case_name, "CASE_FAIL: " + error_info, use_time]) + + elif "OHOS_REPORT_STATUS_CODE: -1" in line_data and index_flag >= 2: + + case_name = out.splitlines()[index_flag - 1].split("test=")[1] + + # 执行失败原因 + + error_info = out.splitlines()[index_flag - 2] + + print(case_name, " CASE_FAIL: " + error_info) + + image_name_list.append([case_name, "CASE_FAIL: " + error_info, use_time]) + + if "#" in command: + + # 单条用例 + + error_case_list.append((command, case_name)) + + elif command not in command_list: + + # 多条用例,只添加一次即可(因为多条用例无法执行单用例) + + error_case_list.append((command, case_name)) + + # 仅用来判断多用例执行失败情况 + + command_list.append(command) + + print(case_name, " run fail !!!\n") + + elif keyword in line_data: + + # 解析用例执行总结果 + + print(line_data) + + index_flag += 1 + + # print("image_name_list: ", image_name_list) + + return image_name_list, error_case_list + + else: + + return False + + + + + +def delete_pictures(bundle_name): + + # 删除设备中应用原有截图 + + app_list = run_cmd("hdc shell cd /data/app/el2/100/base/;ls") + + if bundle_name in app_list.split("\r\n"): + + # 删除指定应用文件夹内截图 + + app_path = "/data/app/el2/100/base/{}/haps/entry_test/files/".format(bundle_name) + + print(app_path) + + run_cmd("hdc shell rm -rf {}*".format(app_path)) + + time.sleep(1) + + out = run_cmd("hdc shell cd {};du -sk".format(app_path)).split(".")[0].strip() + + print(out) + + if "/bin/sh" in out: + + print("设备中不存在待删除图片!!!") + + return + + print("当前{0}文件夹内存(KB):{1}".format(bundle_name, out)) + + count = 0 + + while int(out) > 10 and count <= 30: + + out = run_cmd("hdc shell cd {};du -sk".format(app_path)).split(".")[0].strip() + + print("当前{0}文件夹内存(KB):{1}".format(bundle_name, out)) + + count += 1 + + time.sleep(5) + + print("设备中原有用例截图已全部删除!!!") + + else: + + print("设备中不存在待删除图片!!!") + + + + + +class UiCompareTools(): + + """ + + UI对比工具 + + """ + + def run_testcases(self, sn, data, base_path, excel_path, report_path, excel_name, mode='0'): + + # 遍历执行Execl用例,并将设备端webp文件保存至本地 + + # 设备解锁:兼容部分版本执行完用例设备会锁屏 + + # run_cmd('hdc -t {} shell uinput -T -m 1280 800 1280 800 1000'.format(sn)) + + # run_cmd('hdc -t {} shell uinput -T -m 500 2000 500 500 600'.format(sn)) + + print("excel_path: ", excel_path) + + install_flag = False + + bundle_name = "" + + class_name = "" + + result_list = [] + + pass_count = 0 + + case_fail = 0 + + get_fail = 0 + + run_flag = False + + for case in data: + + try: + + class_name = case[0] + + case_name = case[1] + + bundle_name = case[2] + + if case_name is None: + + run_flag = True + + else: + + run_flag = False + + # 先卸载该应用,避免设备上存在已安装同名应用导致测试hap无法安装 + + if install_flag == False: + + # 卸载、安装仅在执行第一条用例前执行一次 + + install_flag = True + + # 卸载hap + + if bundle_name is not None: + + run_cmd("hdc -t {0} uninstall {1}".format(sn, bundle_name)) + + # 安装hap + + hap_install_path = os.path.join(hap_path, excel_name + ".hap") + + print("hap_install_path: ", hap_install_path) + + if os.path.exists(hap_install_path) == False: + + # hap包路径不存在 + + for case in data: + + case_fail += 1 + + result_list.append( + + [case[1], "CASE_FAIL: The HAP path dose not exist: " + hap_install_path, "0"]) + + print("{}组件hap包{}安装路径不存在:{}".format(excel_name, bundle_name, hap_install_path)) + + break + + out = run_cmd("hdc -t {0} install {1}".format(sn, hap_install_path)) + + print("Install result: ", out) + + if "install failed" in out: + + # hap包安装失败 + + for case in data: + + case_fail += 1 + + result_list.append( + + [case[1], "CASE_FAIL: Failed to install the HAP: " + hap_install_path, "0"]) + + print("{}组件hap包{}安装失败: {}".format(excel_name, bundle_name, hap_install_path)) + + break + + + + # 单条执行 + + command = "hdc -t {3} shell aa test -b {0} -m entry_test -s unittest /ets/testrunner/OpenHarmonyTestRunner -s class {1}#{2} -w 180000 -s timeout 15000".format(case[2], case[0], case[1], sn) + + # 批量执行 + + if case_name is None: + + run_flag = True + + # 删除设备中图片 + + delete_pictures(bundle_name) + + command = "hdc -t {2} shell aa test -b {0} -m entry_test -s unittest /ets/testrunner/OpenHarmonyTestRunner -s class {1} -w 180000 -s timeout 15000".format(case[2], case[0], sn) + + result = checkResult(command, "stream=Tests run:", case_name) + + + + if result == True: + + command = command.replace("/ets/testrunner/OpenHarmonyTestRunner", "OpenHarmonyTestRunner") + + print("new command1: ", command) + + result = checkResult(command, "stream=Tests run:", case_name) + + + + if result == True: + + command = command.replace("OpenHarmonyTestRunner", "/ets/Testrunner/OpenHarmonyTestRunner") + + print("new command2: ", command) + + result = checkResult(command, "stream=Tests run:", case_name) + + + + count = 1 + + while result == "timeout" and count < 6: + + # 若用例执行超时,延长超时的时间5秒,最长延长至30秒,若仍超时,则判定用例执行失败 + + command = command.replace(str(15000 + 5000 * (count - 1)), str(15000 + 5000 * count)) + + print("new command3: ", command) + + result = checkResult(command, "stream=Tests run:", case_name) + + count += 1 + + + + # 存在失败用例,重新执行 + + count_error = 1 + + while run_flag == False and count_error <= 5: + + # 若执行失败,重试5次 + + if type(result) == tuple: + + if len(result) == 2 and len(result[1]) >= 1: + + print("重新执行失败用例:", result[1]) + + with open(report_path, "a") as file: + + for error_info in result[1]: + + file.write(class_name + ": " + error_info[1] + " run fail\n") + + # if run_flag == True: + + # # 删除设备中图片 + + # delete_pictures(bundle_name) + + result = checkResult(command, "stream=Tests run:", case_name) + + count_error += 1 + + else: + + break + + else: + + break + + + + if result == True or result == False: + + print("用例执行失败:{}".format(command)) + + result = [[[case_name, "CASE_FAIL: testcase not run", "0"]], []] + + elif result == "测试hap未安装": + + result = [[[case_name, "CASE_FAIL: Failed to install the HAP or The bundle name in the Excel is inconsistent with that in the HAP", "0"]], []] + + + + # 批量循环取图 + + if class_name and (case_name is None): + + row_num = 1 + + data_list = [] + + for case_name1, case_result, run_time in result[0]: + + # 用例执行成功,将设备中webp文件保存至本地 + + if case_result == "PASS": + + # 用例执行成功,才会创建保存图片的文件夹 + + if os.path.exists(base_path) == False: + + os.makedirs(base_path) + + print('base_path: ', base_path) + + webp_path = os.path.join(base_path, "IMG_%s.webp" % case_name1) + + device_webp = "data/app/el2/100/base/{0}/haps/entry_test/files/".format(case[2]) + + device_path = device_webp + "IMG_%s.webp" % case_name1 + + out_content2 = run_cmd("hdc -t %s file recv %s %s" % (sn, device_path, webp_path)) + + print(out_content2) + + if "FileTransfer finish" in out_content2: + + pass_count += 1 + + print("%s.webp文件保存成功\n" % case_name1) + + elif "no such file" in out_content2: + + get_fail += 1 + + file_path = out_content2.split("path:")[1] + + # 取图失败,更新用例执行结果数据 + + result[0][row_num - 1][1] = "GET_FAIL: save image fail: {}".format(file_path) + + print("设备中 %s 文件不存在,保存本地失败\n" % file_path) + + with open(report_path, "a") as file: + + file.write("%s: %s save fail: 设备中该文件不存在\n" % (class_name, case_name1)) + + else: + + count = 0 + + while count <= 30: + + # 增加延时,确保webp文件成功保存至本地 + + time.sleep(1) + + count += 1 + + if "FileTransfer finish" in out_content2: + + break + + else: + + case_fail += 1 + + # 数据存储至列表 + + data_list.append([row_num, class_name, case_name1, case[2], case_result]) + + row_num += 1 + + # 取完图后,删除设备中所有图片 + + delete_pictures(bundle_name) + + try: + + # 将用例执行结果写入Excel + + self.write_excel_data(excel_path, sheet_name=case[0], result_data=data_list) + + except Exception as e: + + print("【ERROR】write_excel_data: %s" % str(e)) + + + + # 执行单条取图 + + elif class_name and case_name: + + # 用例执行成功才从设备中取资源保存至本地 + + # 将用例执行结果写入Excel + + # try: + + # self.write_excel_data(excel_path, result_data=result[0], compare=2) + + # except Exception as e: + + # print("【ERROR】write_excel_data: %s" % str(e)) + + + + run_result = "" + + if type(result) == tuple: + + try: + + run_result = result[0][0][1] + + except: + + result[0][0] = [case_name, "CASE_FAIL: run testcase fail", "0"] + + run_result = result[0][0][1] + + # print("run_result: ", run_result) + + # 若用例执行成功,将设备中webp文件保存至本地 + + if run_result == "PASS": + + # 用例执行成功,才会创建保存图片的文件夹 + + if os.path.exists(base_path) == False: + + os.makedirs(base_path) + + # run_count = 2 + + # while run_count <= 2: + + # txt_path = os.path.join(base_path, case_name + ".txt") + + # remote_txt = "/storage/media/100/local/files/Documents/TXT_test.txt" + + # out_content2 = run_cmd("hdc -t %s file recv %s %s" % (sn, remote_txt, txt_path)) + + webp_path = os.path.join(base_path, case_name + ".webp") + + remote_webp = "data/app/el2/100/base/{0}/haps/entry_test/files/IMG_test.webp".format(case[2]) + + out_content2 = run_cmd("hdc -t %s file recv %s %s" % (sn, remote_webp, webp_path)) + + print(out_content2) + + if "FileTransfer finish" in out_content2: + + # 累加执行用例成功且成功取图的用例个数 + + pass_count += 1 + + print("%s: %s.webp文件保存成功\n" % (sn, case_name)) + + elif "no such file" in out_content2: + + # 取图失败,重新执行用例后再取图三次 + + print("设备中 %s 文件不存在,保存本地失败\n" % remote_webp) + + result = checkResult(command, "stream=Tests run:", case_name) + + if result == "PASS": + + # 用例执行成功再取图片 + + count = 0 + + while count < 3: + + time.sleep(5) + + out_content2 = run_cmd("hdc -t %s file recv %s %s" % (sn, remote_webp, webp_path)) + + print(out_content2) + + if "FileTransfer finish" in out_content2: + + pass_count += 1 + + print("%s: %s.webp文件保存成功\n" % (sn, case_name)) + + break + + count += 1 + + else: + + # 用例执行失败 + + case_fail += 1 + + else: + + count = 0 + + while count <= 30: + + # 增加延时,确保webp文件成功保存至本地 + + time.sleep(1) + + count += 1 + + if "FileTransfer finish" in out_content2: + + pass_count += 1 + + print("%s: %s.webp文件保存成功\n" % (sn, case_name)) + + break + + if "no such file" in out_content2: + + get_fail += 1 + + file_path = out_content2.split("path:")[1] + + # 取图失败,更新用例执行结果数据 + + result[0][0][1] = "GET_FAIL: save image fail: {}".format(file_path) + + print("设备%s中 %s 文件不存在,保存本地失败\n" % (sn, file_path)) + + with open(report_path, "a") as file: + + file.write("%s: %s save fail: 设备%s中该文件不存在\n" % (class_name, case_name, sn)) + + else: + + case_fail += 1 + + if len(result[0]) == 1: + + result_list.append(result[0][0]) + + except Exception as e: + + print("[ERROR] Get Image fail: ", str(e)) + + time.sleep(5) + + # 用例执行结束,卸载hap + + if bundle_name is not None: + + run_cmd("hdc -t %s uninstall %s" % (sn, bundle_name)) + + time.sleep(2) + + if mode == "0": + + print("{}.xlsx 中所有用例均已执行,基线图源均已获取".format(excel_name)) + + elif mode == "1": + + print("{}.xlsx 中所有用例均已执行,对比图源均已获取".format(excel_name)) + + if pass_count != len(result_list) - case_fail - get_fail: + + pass_count = len(result_list) - case_fail - get_fail + + if len(result_list) <= 0: + + total_num = case_fail + get_fail + + else: + + total_num = len(result_list) + + result_data = {"Total": total_num, + + "Pass": pass_count, + + "Case_fail": case_fail, + + "Get_fail": get_fail, + + "Pass_rate": str(int((pass_count/len(data))*100)) + "%", + + "result_list": result_list, + + "module_name": class_name} + + # print("result_data: ", result_data) + + return result_data + + + + def read_excel_data(self, excel_path): + + # 读取Excel表中类名和用例名 + + if os.path.exists(excel_path) == False: + + print("当前Excel路径不存在: %s" % excel_path) + + return False + + else: + + data_list = [] + + try: + + wb = openpyxl.load_workbook(excel_path) + + # 默认取第一张表 + + sheet = wb.worksheets[0] + + rows = sheet.max_row + + + + for row in range(1, rows): + + # 获取第二列的数据:类名 + + class_name = sheet.cell(row + 1, 2).value + + if class_name is None: + + print("Excel表中有效数据行数: ", row - 1) + + break + + # 获取第三列的数据:用例名 + + case_name = sheet.cell(row + 1, 3).value + + # 获取第四列的数据:bundle_name + + bundle_name = sheet.cell(row + 1, 4).value + + data_list.append((class_name, case_name, bundle_name)) + + print('data_list: ', data_list) + + except Exception as e: + + print(str(e)) + + return data_list + + + + def write_excel_data(self, report_path, sheet_name="Sheet1", result_data=[], x=1, compare=0): + + # 将执行报告数据写入Excel表中 + + if os.path.exists(excel_path) == False: + + print("当前Excel路径不存在: %s" % excel_path) + + return False + + print("write_excel_data: ", excel_path) + + # 读取Excel表数据 + + try: + + wb = openpyxl.load_workbook(excel_path) + + if compare == 2: + + # 默认取第一张表(执行单条用例) + + sheet = wb.worksheets[0] + + elif sheet_name in wb.sheetnames: + + sheet = wb[sheet_name] + + else: + + # 若sheet页不存在,创建新的sheet页 + + sheet = wb.create_sheet(sheet_name) + + print("add sheet %s succ" % sheet_name) + + if compare == 0: + + sheet.append(["序号", "类名", "用例名", "bundle name", "生成图片结果"]) + + print("标题写入完成") + + elif compare == 1: + + sheet.append(["用例名", "对比结果", "耗时"]) + + for testcase in result_data: + + sheet.append(testcase) + + print("对比结果写入完成") + + wb.save(excel_path) + + # 写入result数据 + + rows = sheet.max_row + + cols = sheet.max_column + + if compare == 0 or compare == 2: + + if result_data == []: + + print("[ERROR] write excel data error") + + elif len(result_data) == 1: + + # 单条用例 + + for num in range(1, rows + 1): + + # print("num: ", num) + + if sheet.cell(num, 3).value == result_data[0][0]: + + for i in range(5, 100): + + if sheet.cell(num, i).value is None: + + sheet.cell(num, i, result_data[0][1]) + + if num == 2: + + print("单条用例:报告时间写入完成") + + sheet.cell(1, i, now_time) + + break + + break + + else: + + # 多条用例 + + if sheet.cell(x + 1, 1).value is None: + + # 首次写入 + + for data in result_data: + + sheet.append([data[0], data[1], data[2], data[3], data[4]]) + + print("write %s succ" % data[2]) + + else: + + # 非首次写入 + + if sheet.cell(rows, cols + 1).value is None: + + sheet.cell(1, cols + 1, now_time) + + print("多条用例:报告时间写入完成") + + cols += 1 + + for data in result_data: + + sheet.cell(data[0] + 1, cols, data[4]) + + print("add write %s succ" % data[2]) + + wb.save(excel_path) + + if sheet.cell(2, 1).value is None: + + # 删除空行 + + sheet.delete_rows(2) + + # 保存Excel文件 + + wb.save(excel_path) + + print('result_data: %s succ' % result_data) + + except Exception as e: + + with open(report_path, "a") as r1: + + r1.write("write Excel data error: {}\n".format(excel_path)) + + print(str(e), type(e)) + + if "Permission denied" in str(e): + + print("【ERROR】请先关闭本地已打开的Excel表格,再重新执行") + + + + def get_diff_files(self, excel_path, base_path, compare_path, run_result, now_time): + + # 比较两个文件夹中所有的webp文件,返回所有不同的文件名 + + base_path_exists = True + + if os.path.isdir(base_path) == False: + + base_path_exists = False + + print("{}基线图源文件夹路径不存在".format(base_path)) + + report_diff_list = [] + + case_fail_count = 0 + + get_fail_count = 0 + + base_image_fail = 0 + + compare_fail = 0 + + pass_count = 0 + + for case_name, case_result, use_time in run_result["result_list"]: + + if case_result == "PASS": + + baseImage_path = os.path.join(base_path, case_name + ".webp") + + compareImage_path = os.path.join(compare_path, case_name + ".webp") + + print("baseImage_path: ", baseImage_path) + + print("compareImage_path: ", compareImage_path) + + use_time = "0" + + if base_path_exists == False: + + # 基线图源文件夹路径不存在 + + base_image_fail += 1 + + case_result = "BASE_IMAGE: BaseImage moudle path does not exist: {}".format(base_path) + + elif os.path.exists(baseImage_path) == False: + + # 基线图源文件不存在 + + base_image_fail += 1 + + case_result = "BASE_IMAGE: BaseImage does not exist: {}".format(baseImage_path) + + elif os.path.exists(compareImage_path) == False: + + # 对比图片文件不存在 + + get_fail_count += 1 + + case_result = "GET_FAIL: Save image fail: {}".format(compareImage_path) + + else: + + start_time = datetime.timestamp(datetime.now()) + + # 对比图片,不同时会生成差异图 + + if self.compare_files(baseImage_path, compareImage_path) == False: + + case_result = "COMPARE_FAIL" + + compare_fail += 1 + + else: + + case_result = "PASS" + + pass_count += 1 + + end_time = datetime.timestamp(datetime.now()) + + use_time = str(round(end_time - start_time, 3)) + + elif "CASE_FAIL" in case_result: + + case_fail_count += 1 + + elif "GET_FAIL" in case_result: + + get_fail_count += 1 + + # 将用例执行结果数据保存至列表 + + report_diff_list.append([case_name, case_result, use_time]) + + + + # 将对比执行结果写入Excel + + # self.write_excel_data(excel_path, sheet_name="diff_%s" % now_time, result_data=report_diff_list, compare=1) + + case_count = run_result["Total"] + + if case_count == 0: + + pass_rate = 0 + + else: + + # 防止除数为0,导致异常 + + pass_rate = int(pass_count / case_count * 100) + + if pass_count != case_count - case_fail_count - get_fail_count - base_image_fail - compare_fail: + + pass_count = case_count - case_fail_count - get_fail_count - base_image_fail - compare_fail + + result_data = {"Total": case_count, + + "Pass": pass_count, + + "Case_fail": case_fail_count, + + "Get_fail": get_fail_count, + + "Base_image_fail": base_image_fail, + + "Compare_fail": compare_fail, + + "Pass_rate": str(pass_rate) + "%", + + "result_list": report_diff_list} + + print(result_data) + + return result_data + + + + def modify_image_color(self, num, height_unit, width, img_array, dst): + + # 修改图片像素颜色 + + height_1 = num * height_unit + + height_2 = (num + 1) * height_unit + + for h, w in product(range(height_1, height_2), range(width)): + + (b, g, r) = img_array[h, w] + + if (b, g, r) == (0, 0, 0): # 黑色 + + img_array[h, w] = (255, 255, 255) # 白色 + + else: + + img_array[h, w] = (255, 0, 0) # 剩余差异部分红色显示 + + dst[h, w] = img_array[h, w] + + return dst + + + + def compare_files(self, file1, file2): + + # 比较两个webp文件,如果它们相同则返回True,否则返回False + + if os.path.exists(file1) == False: + + print("当前文件路径不存在,请确认后重试:", file1) + + return False + + if os.path.exists(file2) == False: + + print("当前文件路径不存在,请确认后重试:", file2) + + return False + + # if filecmp.cmp(file1, file2) == True: + + if self.compare_file(file1, file2) == True: + + print("两张图片内容:完全相同") + + return True + + else: + + """ + + 比较图片,如果有不同则生成展示不同的图片 + + @参数一: file1: 基线图片的路径 + + @参数二: file2: 对比图片的路径 + + """ + + module_name = file1.split("\\")[-2] + + diff_path = os.path.join(reports_path, "diff_" + now_time, module_name) + + print("diff_path: ", diff_path) + + if os.path.exists(diff_path) == False: + + os.makedirs(diff_path) + + try: + + image_one = Image.open(file1) + + image_two = Image.open(file2) + + except Exception as e: + + print(str(e)) + + return False + + size1 = image_one.size + + size2 = image_two.size + + print("BaseImage size: {}\nCompareImage size: {}".format(size1, size2)) + + if size1 != size2: + + # 两张图片大小不一致, 无需对比,且不会生成差异图 + + if module_name not in image_size_diff: + + image_size_diff.append(module_name) + + path = os.path.join(reports_path, module_name + ".txt") + + with open(path, "a") as file: + + file.write("截图大小不一致组件列表: %s !!!\n" % str(image_size_diff)) + + with open(report_path, "a") as file: + + file.write("截图大小不一致组件列表: %s !!!\n" % str(image_size_diff)) + + return False + + try: + + diff = ImageChops.difference(image_one, image_two) + + if diff.getbbox() is None: + + # 两张图片相同 + + return True + + img_array = np.array(diff) # 把图像转成数组格式img = np.asarray(image) + + shape = img_array.shape + + height = shape[0] + + width = shape[1] + + dst = np.zeros((height, width, 3)) + + # for h, w in product(range(height), range(width)): + + # (b, g, r) = img_array[h, w] + + # if (b, g, r) == (0, 0, 0): # 黑色 + + # img_array[h, w] = (255, 255, 255) # 白色 + + # else: + + # img_array[h, w] = (255, 0, 0) # 剩余差异部分红色显示 + + # dst[h, w] = img_array[h, w] + + black_pixels = np.where( + + (img_array[:, :, 0] == 0) & (img_array[:, :, 1] == 0) & (img_array[:, :, 2] == 0)) + + dst[black_pixels] = [255, 255, 255] + + other_pixels = np.where( + + (img_array[:, :, 0] != 0) | (img_array[:, :, 1] != 0) | (img_array[:, :, 2] != 0)) + + dst[other_pixels] = [255, 0, 0] + + diff_image = Image.fromarray(np.uint8(dst)) + + print("【+】We are the not same!") + + # 保存差异图、基线图和对比图到diff_path下,命名规则:用例名_diff,用例名_base,用例名_compare + + case_name = file1.split("\\")[-1].split(".")[0] + + diff_save_path = os.path.join(diff_path, case_name + '_diff.webp') + + base_image_path = os.path.join(diff_path, case_name + '_base.webp') + + compare_image_path = os.path.join(diff_path, case_name + '_compare.webp') + + diff_image.save(diff_save_path) + + shutil.copy(file1, base_image_path) + + shutil.copy(file2, compare_image_path) + + except ValueError as e: + + text = ("表示图片大小和box对应的宽度不一致,参考API说明:Pastes another image into this image." + + "The box argument is either a 2-tuple giving the upper left corner, a 4-tuple defining the left, upper, " + + "right, and lower pixel coordinate, or None (same as (0, 0)). If a 4-tuple is given, the size of the pasted " + + "image must match the size of the region.使用2纬的box避免上述问题") + + print("【{0}】{1}".format(e, text)) + + print("两张图片内容:存在差异") + + return False + + + + def compare_file(self, file1, file2): + + with open(file1, "rb") as f1: + + data1 = f1.read() + + with open(file2, "rb") as f2: + + data2 = f2.read() + + return data1 == data2 + + + + def generate_report(self, different_files, report_path): + + # 生成报告显示具体哪个webp文件不同 + + if len(different_files) == 0: + + print("两个文件夹中的所有webp文件都相同。") + + return + + + + report = "以下图片在两个文件夹中存在差异:\n" + + for file in different_files: + + report += f"{file}\n" + + with open(report_path, "w") as r1: + + r1.write(report) + + print("webp文件比对完成,已生成报告:", report_path) + + + + + +def connect_device(device_list): + + # 检查设备是否成功连接 + + time.sleep(3) + + result = run_cmd("hdc list targets") + + now_device_list = result.split("\r\n")[-1] + + print("应连接设备 %d 台" % len(device_list)) + + + + # 先循环等待是否全部连接成功 + + count = 0 + + while len(device_list) != len(now_device_list) and count < 5: + + time.sleep(2) + + count += 1 + + + + # 获取未连接设备sn号 + + disconnect_device = [] + + if len(device_list) != len(now_device_list): + + for device in device_list: + + if device not in now_device_list: + + disconnect_device.append(device) + + else: + + print("设备均已成功连接PC") + + + + # 重试拉起未连接设备hdc,再查询 + + if len(disconnect_device) >= 1: + + print("设备连接异常, 开始重连设备") + + for sn in disconnect_device: + + for i in range(1, 16): + + SN = run_cmd("hdc -t {} shell power-shell setmode 602".format(sn)) + + time.sleep(2) + + # print("SN: ", SN) + + if "[Fail]" in SN: + + print("重连第 %d 次" % i) + + continue + + else: + + break + + run_cmd("hdc -t {} shell power-shell display -s 0".format(sn)) + + if len(device_list) == len(run_cmd("hdc list targets").split("\r\n")[-1]): + + print("设备均已成功连接PC") + + else: + + # 更新设备列表 + + device_list = run_cmd("hdc list targets").split("\r\n")[-1] + + return device_list + + + + + +def delete_and_reboot(sn): + + # 跑用例前删包,重启操作 + + run_cmd("hdc -t {} shell mount -o remount,rw /sys_prod".format(sn)) + + run_cmd("hdc -t {} shell rm -rf /sys_prod/app/SystemResourcesOverlay".format(sn)) + + # 重启设备 + + #run_cmd("hdc -t {} shell reboot".format(sn)) + + #print("设备 {} 重启中,请耐心等待50秒......".format(sn)) + + #time.sleep(50) + + # 当前设备类型 + + device = run_cmd('hdc -t {} shell param get const.product.model'.format(sn)) + + if "Fail" in device: + + # 设备断连,尝试重连 + + connect_device(device_list) + + else: + + print("当前设备类型:", device) + + # 解锁设备 + + if "WGR" in device: + + # 若是WGR需点击屏幕中间才可解锁 + + run_cmd('hdc -t {} shell uinput -T -m 1280 800 1280 800 1000'.format(sn)) + + elif "NOH" in device: + + # 手机:滑动界面即可解锁 + + run_cmd('hdc -t {} shell uinput -T -m 500 2000 500 500 600'.format(sn)) + + else: + + # 设备断连,尝试重连 + + connect_device([sn]) + + run_cmd('hdc -t {} shell uinput -T -m 1280 800 1280 800 1000'.format(sn)) + + run_cmd('hdc -t {} shell uinput -T -m 500 2000 500 500 600'.format(sn)) + + # 设置设备常亮 + + out = run_cmd('hdc -t {} shell power-shell setmode 602'.format(sn)) + + if "Mode Success" in out: + + print("当前设备 {} 已设置常亮!!!".format(sn)) + + # 调低亮度,确保有足够电量进行测试 + + run_cmd('hdc -t {} shell power-shell display -s 0'.format(sn)) + + else: + + print("当前设备 {} 连接异常,请成功连接设备后重新执行".format(sn)) + + + + + +def run_get_image(args): + + # 获取基线图 + + # device_dict, excel_path, excel_name, report_path, base_path, now_time, dir_path + + global device_dict + + device_dict = args[0] + + excel_path = args[1] + + excel_name = args[2] + + report_path = args[3] + + now_time = args[5] + + dir_path = args[6] + + print(excel_name, " before run device_dict: ", device_dict) + + if len(device_dict) == 1: + + for key, value in device_dict.items(): + + sn = key + + elif len(device_dict) > 1: + + sn = device_dict.keys()[0] + + # print("sn: ", sn) + + run_flag = False + + if device_dict[sn] == True: + + # 有空闲设备时,占用并更新设备状态不可用(False) + + tmp = device_dict + + tmp[sn] = False + + device_dict = tmp + + print(excel_name, " running device_dict: ", device_dict) + + elif device_dict[sn] == False: + + index = 0 + + # 单个组件执行用例超过50分钟,可能该设备已闲置,启用该设备 + + while run_flag == False and index < 50 * 60: + + for device, status in device_dict.items(): + + # 监测到有空闲设备时 + + if status == True: + + # 有空闲设备时,占用并更新设备状态不可用(False) + + tmp = device_dict + + tmp[device] = False + + device_dict = tmp + + sn = device + + run_flag = True + + print(excel_name, " running device_dict: ", device_dict) + + break + + if run_flag == True: + + # 有空闲设备时,退出循环,开始执行用例 + + break + + # 等待直到出现空闲设备 + + time.sleep(1) + + index += 1 + + global base_path + + base_path = os.path.join(dir_path, "BaseImages_" + now_time) + + print('base_path: ', base_path) + + # 实例化对象 + + uiTools = UiCompareTools() + + data = uiTools.read_excel_data(excel_path) + + result_dict = {} + + if data != False: + + save_image_path = os.path.join(base_path, excel_name) + + try: + + case_result = uiTools.run_testcases(sn, data, save_image_path, excel_path, report_path, excel_name, mode="0") + + result_dict[excel_name] = case_result + + # 将本次执行结果保存至json文件中,确保遇到异常中断,仍可生成总的html报告 + + if os.path.exists(run_json_path) == False: + + os.makedirs(run_json_path) + + json_data_path = os.path.join(run_json_path, excel_name + ".json") + + with open(json_data_path, "w") as f: + + json.dump(result_dict, f) + + # print("成功写入json文件:", result_dict) + + except Exception as e: + + print("[ERROR] get BaseImage fail: ", str(e)) + + print("result_dict: ", result_dict) + + # 用例执行完,更新当前设备状态为可用(True) + + device_dict[sn] = True + + print(excel_name, " after run device_dict: ", device_dict) + + else: + + print("[ERROR] The Excel path does not exist !!!") + + return device_dict, result_dict + + + + + +def run_compare_image(args): + + # 获取对比图,并对比图片 + + # device_dict, excel_path, excel_name, report_path, base_path, now_time, dir_path + + global device_dict + + device_dict = args[0] + + excel_path = args[1] + + excel_name = args[2] + + report_path = args[3] + + base_path = args[4] + + now_time = args[5] + + dir_path = args[6] + + print(excel_name, " before run device_dict: ", device_dict) + + if len(device_dict) == 1: + + for key, value in device_dict.items(): + + sn = key + + elif len(device_dict) > 1: + + sn = device_dict.keys()[0] + + # print("sn: ", sn) + + run_flag = False + + if device_dict[sn] == True: + + # 有空闲设备时,占用并更新设备状态不可用(False) + + tmp = device_dict + + tmp[sn] = False + + device_dict = tmp + + print(excel_name, " running device_dict: ", device_dict) + + elif device_dict[sn] == False: + + index = 0 + + # 单个组件执行用例超过50分钟,可能该设备已闲置,启用该设备 + + while run_flag == False and index < 50*60: + + for device, status in device_dict.items(): + + # 监测到有空闲设备时 + + if status == True: + + # 有空闲设备时,占用并更新设备状态不可用(False) + + tmp = device_dict + + tmp[device] = False + + device_dict = tmp + + sn = device + + run_flag = True + + print(excel_name, " running device_dict: ", device_dict) + + break + + if run_flag == True: + + # 有空闲设备时,退出循环,开始执行用例 + + break + + # 等待直到出现空闲设备 + + time.sleep(1) + + index += 1 + + + + compare_path = os.path.join(dir_path, "CompareImages_" + now_time) + + print('compare_path: ', compare_path) + + count = 0 + + # 校验基线图片路径是否合法 + + while os.path.isdir(base_path) == False or os.path.exists(base_path) == False: + + if os.path.isdir(base_path) == False: + + base_path = input("路径非文件夹,请重新输入基线图源文件夹所在路径:") + + elif os.path.exists(base_path) == False: + + base_path = input("路径不存在,请重新输入基线图源文件夹所在路径:") + + count += 1 + + if os.path.isdir(base_path) == True and os.path.exists(base_path) == True: + + # 路径输入正确,跳出循环继续往下执行 + + break + + if count == 3: + + # 输入三次非法参数,程序退出 + + sys.exit(0) + + + + # 实例化对象 + + uiTools = UiCompareTools() + + data = uiTools.read_excel_data(excel_path) + + diff_dict = {} + + if data != False: + + # 基线图片路径 + + base_image_path = os.path.join(base_path, excel_name) + + # 对比图片路径 + + save_image_path = os.path.join(compare_path, excel_name) + + # 执行生成对比图片用例,并将执行和保存图片的结果写入Excel表中 + + uiTools = UiCompareTools() + + try: + + case_result = uiTools.run_testcases(sn, data, save_image_path, excel_path, report_path, excel_name, mode="1") + + # 用例执行完,更新当前设备状态为可用(True) + + device_dict[sn] = True + + # 开始对比图片,并将对比结果写入Excel表中,有差异时生成差异图保存至reports\diff_XXX\module_name\XXX.webp + + different_files = uiTools.get_diff_files(excel_path, base_image_path, save_image_path, case_result, now_time) + + # 将执行用例的结果、保存图片的结果与对比图片的结果合并,得到最终展示至报告中的结果 + + # 更新该模块用例总数 + + if different_files["Total"] < len(different_files["result_list"]): + + different_files["Total"] = len(different_files["result_list"]) + + # 将该模块数据添加至字典 + + diff_dict[excel_name] = different_files + + # 将本次执行结果保存至json文件中,确保遇到异常中断,仍可生成总的html报告 + + if os.path.exists(run_json_path) == False: + + os.makedirs(run_json_path) + + json_data_path = os.path.join(run_json_path, excel_name + "_diff.json") + + with open(json_data_path, "w") as f: + + json.dump(diff_dict, f) + + print("成功写入json文件:", result_dict) + + + + # 生成对比报告 + + report_diff_path = os.path.join(reports_path, "report_diff_{0}.txt".format(now_time)) + + uiTools.generate_report(different_files["result_list"], report_diff_path) + + except Exception as e: + + print("[ERROR] run Compare fail: ", str(e)) + + print("{}.xlsx 中所有用例图片均已对比完成".format(excel_name)) + + print("diff_dict: ", diff_dict) + + else: + + print("[ERROR] The Excel path does not exist !!!") + + print(excel_name, " after run device_dict: ", device_dict) + + return device_dict, diff_dict + + + + + +if __name__ == '__main__': + + # 程序入口 + + # 1小时后开始生成基线图 + + # time.sleep(1 * 60 * 60) + + start_time = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time())) + + start_time_second = datetime.timestamp(datetime.now()) + + # 创建reports文件夹 + + if os.path.exists(reports_path) == False: + + os.makedirs(reports_path) + + result_list = [] + + if mode == "0" or mode == "1": + + # 判断用例数据的Excel所在文件夹内是否包含有效数据(必须为xlsx格式的Excel) + + count = 0 + + while os.path.exists(excel_path) == False: + + excel_path = input("路径不存在,请重新输入Excel所在文件夹路径:") + + count += 1 + + if os.path.exists(excel_path) == False and count == 3: + + # 尝试三次错误输入后,退出程序 + + sys.exit(0) + + + + # 获取所有Excel表的全路径 + + excel_name_list = [] + + excel_path_list = [] + + if os.path.exists(excel_path) == True: + + excel_name_list = get_file_list(excel_path, ".xlsx") + + count = 0 + + # 路径无效时,尝试三次输入 + + while len(excel_name_list) == 0: + + excel_path = input("路径中不存在xlsx格式的Excel文件,请检查后重新输入Excel所在文件夹路径:") + + excel_name_list = get_file_list(excel_path, ".xlsx") + + count += 1 + + if len(excel_name_list) == 0 and count == 3: + + # 尝试三次错误输入后,退出程序 + + sys.exit(0) + + # 路径有效时,获取全路径列表 + + for excel_name in excel_name_list: + + excel_full_path = os.path.join(excel_path, excel_name) + + excel_name = excel_name.split(".")[0] + + excel_path_list.append((excel_full_path, excel_name)) + + + + flag = 0 + + func_name = "" + + while flag < 3: + + if mode == "0": + + # 获取基线图 + + func_name = run_get_image + + break + + elif mode == "1": + + # 对比图片 + + func_name = run_compare_image + + break + + else: + + mode = input("您输入有误,请重新输入(输入0可获取基线图源,输入1可对比图片):") + + flag += 1 + + if flag == 3: + + # 错输三次,退出程序 + + sys.exit(0) + + report_path = os.path.join(reports_path, "reports_run_%s.txt" % now_time) + + + + # 兼容本地无hdc.exe的情况 + + content = run_cmd("hdc list targets") + + print("当前设备SN号: \n", content) + + if content == "": + + position = run_cmd("where hdc_std") + + print("本地hdc_std.exe所在路径: ", position) + + position = position.splitlines()[0] + + print("本地hdc_std.exe所在第一个路径: ", position) + + shutil.copy(position, position.replace("hdc_std.exe", "hdc.exe")) + + content = run_cmd("hdc list targets") + + + + device_list = content.split("\r\n")[:-1] + + print("device_list: ", device_list) + + # 最大进程数,即连接设备数 + + process_num = len(device_list) + + # 跑用例前删包,重启操作 + + if process_num == 1: + + # 单台设备 + + delete_and_reboot(device_list[0]) + + print("单设备执行") + + elif process_num > 1: + + # 多设备、多进程 + + print("多设备、多进程执行") + + with Pool(process_num) as p: + + outputs = p.map(delete_and_reboot, device_list) + + # 关闭进程池 + + p.terminate() + + # 检查设备是否成功连接,若连接异常尝试重连 + + connect_device(device_list) + + + + # 利用进程池Pool自动帮我们管理子进程 + + excel_num = len(excel_path_list) + + device_num = len(device_list) + + print("excel_path_list: ", excel_path_list) + + dir_path = os.path.join(dir_path) + + + + if mode == "0": + + base_path = "" + + + + if device_num == 1: + + # 仅连接单台设备时 + + device_dict = {device_list[0]: True} + + for excel_path, excel_name in excel_path_list: + + result_data = [] + + if mode == "0": + + result_data = run_get_image((device_dict, excel_path, excel_name, report_path, base_path, now_time, dir_path)) + + elif mode == "1": + + result_data = run_compare_image( + + (device_dict, excel_path, excel_name, report_path, base_path, now_time, dir_path)) + + else: + + print("您输入的type有误,请您重新输入!") + + sys.exit(0) + + result_list.append(result_data) + + elif device_num > 1: + + # 连接多台设备时 + + # 未执行用例前,所有设备均可用(True) + + device_dict = Manager().dict() + + for device in device_list: + + device_dict[device] = True + + # 最大进程数:PC已连接的设备数 + + p = Pool(device_num) + + res_l = [] + + excel_index = 0 + + for excel_path, excel_name in excel_path_list: + + res = p.apply_async(func_name, ((device_dict, excel_path, excel_name, report_path, base_path, now_time, dir_path),)) + + res_l.append(res) + + excel_index += 1 + + p.close() + + p.join() + + + + for res in res_l: + + # 汇总用例执行结果 + + result_list.append(res.get()) + + # 关闭进程池 + + p.terminate() + + if mode == "2": + + # 获取所有的json路径 + + all_json_path = [] + + json_name_list = get_file_list(json_path, ".json") + + if "_diff.json" in json_name_list[0]: + + mode = "1" + + else: + + mode = "0" + + base_path = "" + + for json_name in json_name_list: + + all_json_path.append(os.path.join(json_path, json_name)) + + print("all_json_path: ", all_json_path) + + for json_data_path in all_json_path: + + with open(json_data_path, "r") as f: + + data = json.loads(f.read()) + + # print("解析后json文件:", data) + + result_list.append(({}, data)) + + + + end_time = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time())) + + end_time_second = datetime.timestamp(datetime.now()) + + run_time = end_time_second - start_time_second + + moudle_count = len(result_list) + + total_count = 0 + + pass_count = 0 + + case_fail_count = 0 + + get_fail_count = 0 + + + + if mode == "0": + + # 获取基线图 + + result_dict = {} + + for run_dict in result_list: + + for key, value in run_dict[1].items(): + + result_dict[key] = value + + total_count += value["Total"] + + pass_count += value["Pass"] + + case_fail_count += value["Case_fail"] + + get_fail_count += value["Get_fail"] + + if total_count == 0: + + pass_rate = 0 + + else: + + pass_rate = int(pass_count / total_count * 100) + + # if pass_count != total_count - case_fail_count - get_fail_count: + + # pass_count = total_count - case_fail_count - get_fail_count + + result_dict["Total_data"] = {"moudle_count": str(moudle_count), + + "Total": str(total_count), + + "Pass": str(pass_count), + + "Case_fail": str(case_fail_count), + + "Get_fail": str(get_fail_count), + + "Pass_rate": str(pass_rate) + "%", + + "start_end_time": str(start_time) + "/ " + str(end_time), + + "run_time": str(time.strftime("%H:%M:%S", time.gmtime(run_time))) + + } + + # 生成获取基线图的html报告 + + write_report_html(result_dict, mode="0", image_path=base_path) + + print("获取图片结束,result_dict: ", result_dict) + + elif mode == "1": + + # 与基线图对比,并生成差异图和报告 + + base_image_fail_count = 0 + + compare_fail_count = 0 + + diff_dict = {} + + for run_dict in result_list: + + for key, value in run_dict[1].items(): + + diff_dict[key] = value + + # 累加获取各状态总数 + + total_count += value["Total"] + + pass_count += value["Pass"] + + case_fail_count += value["Case_fail"] + + get_fail_count += value["Get_fail"] + + base_image_fail_count += value["Base_image_fail"] + + compare_fail_count += value["Compare_fail"] + + if total_count == 0: + + pass_rate = 0 + + else: + + pass_rate = int(pass_count / total_count * 100) + + diff_dict["Total_data"] = {"moudle_count": str(moudle_count), + + "Total": str(total_count), + + "Pass": str(pass_count), + + "Case_fail": str(case_fail_count), + + "Get_fail": str(get_fail_count), + + "Base_image_fail": str(base_image_fail_count), + + "Compare_fail": str(compare_fail_count), + + "Pass_rate": str(pass_rate) + "%", + + "start_end_time": str(start_time) + "/ " + str(end_time), + + "run_time": str(time.strftime("%H:%M:%S", time.gmtime(run_time)))} + + diff_path = os.path.join(reports_path, "diff_" + str(now_time)) + + # 生成对比图片后的html报告 + + write_report_html(diff_dict, mode="1", image_path=diff_path) + + print("对比图片结束,diff_dict: ", diff_dict) + diff --git a/function/ui_compare/uicompare_tools/excel/actionSheet.xlsx b/function/ui_compare/uicompare_tools/excel/actionSheet.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..d8c3be79f5bf717b930196cf91fe131ad3c18ad6 Binary files /dev/null and b/function/ui_compare/uicompare_tools/excel/actionSheet.xlsx differ diff --git a/function/ui_compare/uicompare_tools/excel/uicompare.xlsx b/function/ui_compare/uicompare_tools/excel/uicompare.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..8d0255136e1831e0437d42d19471ca5fdf3733c4 Binary files /dev/null and b/function/ui_compare/uicompare_tools/excel/uicompare.xlsx differ diff --git a/function/ui_compare/uicompare_tools/mode.html b/function/ui_compare/uicompare_tools/mode.html new file mode 100644 index 0000000000000000000000000000000000000000..c56f5b11a876b0ac7afc98f4b4895e32baacc4a9 --- /dev/null +++ b/function/ui_compare/uicompare_tools/mode.html @@ -0,0 +1,432 @@ + + + + + <!--{title_name}--> + + + + + + + + +
+ + + + + + + + + + + + + +
Test Summary
Platform:PhoneTest Type:Test
Device Name:7001005458*************e1c8933900 + \ No newline at end of file diff --git a/function/ui_compare/uicompare_tools/run.bat b/function/ui_compare/uicompare_tools/run.bat new file mode 100644 index 0000000000000000000000000000000000000000..900108445dfae615d0d224a0c3a637091f06ccc9 --- /dev/null +++ b/function/ui_compare/uicompare_tools/run.bat @@ -0,0 +1,22 @@ + +@ECHO OFF +reg add HKEY_CURRENT_USER\Console /v QuickEdit /t REG_DWORD /d 00000000 /f +echo "======使用说明======" +echo "--type: 输入0:获取基线图,输入1:对比生成差异图,输入2:生成总html报告" +echo "--excel: 用例数据的Excel所在文件夹路径(Excel仅支持xlsx格式)" +echo "--dir: 基础图源文件夹所在路径(type=1时必填)" +echo "--hap: 用例hap文件夹所在路径(必填)" +echo "--json: 执行用例生成的 json文件所在文件夹路径(type=2时必填)" +echo "入参范例:--type0--excel D:\UI\All_Excel\--hap D:\UI\hap" +echo "入参范例:--type1--excel D:\UI\All_Excel\--dir D:\UI\BaseImages_20230504_174004 --hap D:\UI\hap" +echo "入参范例:--type2--json D:\UI\run_json_20230810_163907\" +echo "温馨提示:执行用例前,请先确保已关闭上述路径中所有的Excel表,否则将会导致用例结果写入Excel后保存失败" +echo "======使用说明======" + + +set /p param= 输入入参: + + +python UiCompareTools_progress.py %param% + +pause \ No newline at end of file