mirror of
https://gitee.com/openharmony/testfwk_xdevice
synced 2024-11-22 23:10:15 +00:00
update openharmony 1.0.1
This commit is contained in:
parent
9d2c809306
commit
c2c469f6ec
@ -1,13 +0,0 @@
|
||||
### 该问题是怎么引起的?
|
||||
|
||||
|
||||
|
||||
### 重现步骤
|
||||
|
||||
|
||||
|
||||
### 报错信息
|
||||
|
||||
|
||||
|
||||
|
@ -1,15 +0,0 @@
|
||||
### 相关的Issue
|
||||
|
||||
|
||||
### 原因(目的、解决的问题等)
|
||||
|
||||
|
||||
### 描述(做了什么,变更了什么)
|
||||
|
||||
|
||||
### 测试用例(新增、改动、可能影响的功能)
|
||||
|
||||
|
||||
|
||||
|
||||
|
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
*.pyc
|
||||
/extension/
|
35
BUILD.gn
Executable file → Normal file
35
BUILD.gn
Executable file → Normal file
@ -1,18 +1,17 @@
|
||||
# Copyright (c) 2020 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("//test/xts/tools/build/suite_lite.gni")
|
||||
|
||||
deploy_suite("xdevice"){
|
||||
suite_name = "acts"
|
||||
|
||||
}
|
||||
# Copyright (c) 2020 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("//test/xts/tools/lite/build/suite_lite.gni")
|
||||
|
||||
deploy_suite("xdevice") {
|
||||
suite_name = "acts,hits,ssts"
|
||||
}
|
||||
|
227
README.md
Executable file
227
README.md
Executable file
@ -0,0 +1,227 @@
|
||||
# XDevice<a name="EN-US_TOPIC_0000001083129731"></a>
|
||||
|
||||
- [Introduction](#section15701932113019)
|
||||
- [Directory Structure](#section1791423143211)
|
||||
- [Constraints](#section118067583303)
|
||||
- [Usage](#section2036431583)
|
||||
- [Repositories Involved](#section260848241)
|
||||
|
||||
## Introduction<a name="section15701932113019"></a>
|
||||
|
||||
XDevice, a core module of the OpenHarmony test framework, provides services on which test case execution depends.
|
||||
|
||||
XDevice consists of the following sub-modules:
|
||||
|
||||
- **command**: enables command-based interactions between users and the test platform. It parses and processes user commands.
|
||||
- **config**: sets test framework configurations and provides different configuration options for the serial port connection and USB connection modes.
|
||||
- **driver**: functions as a test case executor, which defines main test steps, such as test case distribution, execution, and result collection.
|
||||
- **report**: parses test results and generates test reports.
|
||||
- **scheduler**: schedules various test case executors in the test framework.
|
||||
- **environment**: configures the test framework environment, enabling device discovery and device management.
|
||||
- **testkit**: provides test tools to implement JSON parsing, network file mounting, etc.
|
||||
- **resource**: provides the device connection configuration file and report template definitions.
|
||||
- **adapter**: adapts the test framework to open-source software.
|
||||
|
||||
## Directory Structure<a name="section1791423143211"></a>
|
||||
|
||||
```
|
||||
xdevice
|
||||
├── config # XDevice configuration
|
||||
│ ├── user_config.xml # XDevice environment configuration
|
||||
├── resource # XDevice resources
|
||||
│ ├── tools # Burning tools
|
||||
├── src # Source code
|
||||
│ ├── xdevice
|
||||
├── extension # XDevice extension
|
||||
│ ├── src # Source code of the extension
|
||||
│ └── setup.py # Installation script of the extension
|
||||
```
|
||||
|
||||
## Constraints<a name="section118067583303"></a>
|
||||
|
||||
The environment requirements for using this module are as follows:
|
||||
|
||||
- Python version: 3.7.5 or later
|
||||
- pySerial version: 3.3 or later
|
||||
- Paramiko version: 2.7.1 or later
|
||||
- RSA version: 4.0 or later
|
||||
|
||||
## Usage<a name="section2036431583"></a>
|
||||
|
||||
- **Installing XDevice**
|
||||
1. Go to the installation directory of XDevice.
|
||||
2. Open the console window and run the following command:
|
||||
|
||||
```
|
||||
python setup.py install
|
||||
```
|
||||
|
||||
|
||||
- **Installing the extension**
|
||||
1. Go to the installation directory of the XDevice extension.
|
||||
2. Open the console and run the following command:
|
||||
|
||||
```
|
||||
python setup.py install
|
||||
```
|
||||
|
||||
|
||||
- **Modifying the user\_config.xml file**
|
||||
|
||||
Configure information about your environment in the **user\_config.xml** file.
|
||||
|
||||
**1. Configure the environment.**
|
||||
|
||||
- For devices that support hdc connection, refer to the following note to configure the environment.
|
||||
|
||||
>![](figures/icon-note.gif) **NOTE:**
|
||||
>**ip/port**: IP address and port of a remote device. By default, the parameter is left blank, indicating that the local device \(IP address: 127.0.0.1; port: the one used for hdc startup\) is used as the test device.
|
||||
>**sn**: SN of the test devices specified for command execution. If this parameter is set to **SN1**, only device SN1 can execute the subsequent **run** commands. In this case, other devices are set as **Ignored** and not involved in the command execution. You can run the **list devices** command and check the value of **Allocation** to view the **sn** values. You can set multiple SNs and separate each two of them with a semicolon \(;\).
|
||||
|
||||
- For devices that support serial port connection, refer to the following note to configure the environment.
|
||||
|
||||
>![](figures/icon-note.gif) **NOTE:**
|
||||
>**type**: device connection mode. The **com** mode indicates that the device is connected through the serial port.
|
||||
>**label**: device type, for example, **wifiiot**
|
||||
>**serial**: serial port
|
||||
>- **serial/com**: serial port for local connection, for example, **COM20**
|
||||
>- **serial/type**: serial port type. The value can be **cmd** \(serial port for test case execution\) or **deploy** \(serial port for system upgrade\).
|
||||
> For the open-source project, the **cmd** and **deploy** serial ports are the same, and their **com** values are the same too.
|
||||
>**serial/baud\_rate, data\_bits, stop\_bits** and **timeout**: serial port parameters. You can use the default values.
|
||||
|
||||
|
||||
**2. Set the test case directory.**
|
||||
|
||||
**dir**: test case directory
|
||||
|
||||
**3. Mount the NFS.**
|
||||
|
||||
>![](figures/icon-note.gif) **NOTE:**
|
||||
>**server**: NFS mounting configuration. Set the value to **NfsServer**.
|
||||
>**server/ip**: IP address of the mounting environment
|
||||
>**server/port**: port number of the mounting environment
|
||||
>**server/username**: user name for logging in to the server
|
||||
>**server/password**: password for logging in to the server
|
||||
>**server/dir**: external mount path
|
||||
>**server/remote**: whether the NFS server and the XDevice executor are deployed on different devices. If yes, set this parameter to **true**. Otherwise, set it to **false**.
|
||||
|
||||
- **Specify the task type.**
|
||||
- **Start the test framework.**
|
||||
- **Execute test commands.**
|
||||
|
||||
Test framework commands can be classified into three groups: **help**, **list**, and **run**. Among them, **run** commands are most commonly used in the instruction sequence.
|
||||
|
||||
**help**
|
||||
|
||||
Queries help information about test framework commands.
|
||||
|
||||
```
|
||||
help:
|
||||
Use help to get information.
|
||||
usage:
|
||||
run: Display a list of supported run commands.
|
||||
list: Display a list of supported devices and task records.
|
||||
Examples:
|
||||
help run
|
||||
help list
|
||||
```
|
||||
|
||||
>![](figures/icon-note.gif) **NOTE:**
|
||||
>**help run**: displays the description of **run** commands.
|
||||
>**help list**: displays the description of **list** commands.
|
||||
|
||||
**list**
|
||||
|
||||
Displays device information and related task information.
|
||||
|
||||
```
|
||||
list:
|
||||
Display device list and task records.
|
||||
usage:
|
||||
list
|
||||
list history
|
||||
list <id>
|
||||
Introduction:
|
||||
list: Display the device list.
|
||||
list history: Display historical records of a series of tasks.
|
||||
list <id>: Display historical records of tasks with the specified IDs.
|
||||
Examples:
|
||||
list
|
||||
list history
|
||||
list 6e****90
|
||||
```
|
||||
|
||||
>![](figures/icon-note.gif) **NOTE:**
|
||||
>**list**: displays device information.
|
||||
>**list history**: displays historical task information.
|
||||
>**list <id\>**: displays historical information about tasks with specified IDs.
|
||||
|
||||
**run**
|
||||
|
||||
Executes test tasks.
|
||||
|
||||
```
|
||||
run:
|
||||
Execute the selected test cases.
|
||||
The command execution process includes use case compilation, execution, and result collection.
|
||||
usage: run [-l TESTLIST [TESTLIST ...] | -tf TESTFILE
|
||||
[TESTFILE ...]] [-tc TESTCASE] [-c CONFIG] [-sn DEVICE_SN]
|
||||
[-rp REPORT_PATH [REPORT_PATH ...]]
|
||||
[-respath RESOURCE_PATH [RESOURCE_PATH ...]]
|
||||
[-tcpath TESTCASES_PATH [TESTCASES_PATH ...]]
|
||||
[-ta TESTARGS [TESTARGS ...]] [-pt]
|
||||
[-env TEST_ENVIRONMENT [TEST_ENVIRONMENT ...]]
|
||||
[-e EXECTYPE] [-t [TESTTYPE [TESTTYPE ...]]]
|
||||
[-td TESTDRIVER] [-tl TESTLEVEL] [-bv BUILD_VARIANT]
|
||||
[-cov COVERAGE] [--retry RETRY] [--session SESSION]
|
||||
[--dryrun] [--reboot-per-module] [--check-device]
|
||||
[--repeat REPEAT]
|
||||
action task
|
||||
Specify tests to run.
|
||||
positional arguments:
|
||||
action Specify the action to do.
|
||||
task Specify the task name, such as ssts, acts, and hits.
|
||||
```
|
||||
|
||||
>![](figures/icon-note.gif) **NOTE:**
|
||||
>The structure of a basic **run** command is as follows:
|
||||
>```
|
||||
>run [task name] -l module1;moudle2
|
||||
>```
|
||||
>**task name**: task type. This parameter is optional. Generally, the value is **ssts**, **acts**, or **hits**.
|
||||
>**-l**: test cases to execute. Use semicolons \(;\) to separate each two test cases.
|
||||
>**module**: module to test. Generally, there is a **.json** file of the module in the **testcases** directory.
|
||||
>In addition, other parameters can be attached to this command as constraints. Common parameters are as follows:
|
||||
>**-sn**: specifies the devices for test case execution. If this parameter is set to **SN1**, only device SN1 executes the test cases.
|
||||
>**-c**: specifies a new **user\_config.xml** file.
|
||||
>**-rp**: indicates the path where the report is generated. The default directory is **xxx/xdevice/reports**. Priority of a specified directory is higher than that of the default one.
|
||||
>**-tcpath**: indicates the environment directory, which is **xxx/xdevice/testcases** by default. Priority of a specified directory is higher than that of the default one.
|
||||
>**-respath**: indicates the test suite directory, which is **xxx/xdevice/resource** by default. Priority of a specified directory is higher than that of the default one.
|
||||
>**--reboot-per-module**: restarts the device before test case execution.
|
||||
|
||||
- **View the execution result.**
|
||||
|
||||
After executing the **run** commands, the test framework displays the corresponding logs on the console, and generates the execution report in the directory specified by the **-rp** parameter. If the parameter is not set, the report will be generated in the default directory.
|
||||
|
||||
```
|
||||
Structure of the report directory (the default or the specified one)
|
||||
├── result # Test case execution results of the module
|
||||
│ ├── module name.xml
|
||||
│ ├── ...
|
||||
│
|
||||
├── log # Running logs of devices and tasks
|
||||
│ ├── device 1.log
|
||||
│ ├── ...
|
||||
│ ├── task.log
|
||||
├── summary_report.html # Visual report
|
||||
├── summary_report.html # Statistical report
|
||||
└── ...
|
||||
```
|
||||
|
||||
|
||||
## Repositories Involved<a name="section260848241"></a>
|
||||
|
||||
test\_xdevice
|
||||
|
||||
test\_xdevice\_extension
|
||||
|
225
README_zh.md
Executable file
225
README_zh.md
Executable file
@ -0,0 +1,225 @@
|
||||
# xdevice组件<a name="ZH-CN_TOPIC_0000001083129731"></a>
|
||||
|
||||
- [简介](#section15701932113019)
|
||||
- [目录](#section1791423143211)
|
||||
- [约束](#section118067583303)
|
||||
- [使用](#section2036431583)
|
||||
- [相关仓](#section260848241)
|
||||
|
||||
## 简介<a name="section15701932113019"></a>
|
||||
|
||||
xdevice是OpenHarmony中为测试框架的核心组件,提供用例执行所依赖的相关服务。
|
||||
|
||||
xdevice主要包括以下几个主要模块:
|
||||
|
||||
- command,用户与测试平台命令行交互模块,提供用户输入命令解析,命令处理。
|
||||
- config,测试框架配置模块,提供测试平台串口连接方式和USB连接方式的不同配置选项。
|
||||
- driver,测试用例执行器,提供测试用例分发,执行,结果收集等主要测试步骤定义。
|
||||
- report,测试报告模块,提供测试结果解析和测试报告生成。
|
||||
- scheduler,测试框架调度模块,提供不同类型的测试执行器调度的调度功能。
|
||||
- environment,测试框架的环境配置模块,提供设备发现,设备管理的功能。
|
||||
- testkit,测试框架工具模块,提供json解析,网络文件挂载等操作。
|
||||
- resource,测试框架资源模块,提供设备连接配置文件和报告模板定义。
|
||||
- adapter,测试框架适配开源软件的模块。
|
||||
|
||||
## 目录<a name="section1791423143211"></a>
|
||||
|
||||
```
|
||||
xdevice
|
||||
├── config # xdevice组件配置
|
||||
│ ├── user_config.xml # xdevice环境配置
|
||||
├── resource # xdevice组件资源
|
||||
│ ├── tools # 版本烧录工具
|
||||
├── src # 组件源码目录
|
||||
│ ├── xdevice
|
||||
├── extension # xdevice扩展模块
|
||||
│ ├── src # 扩展模块源码
|
||||
│ └── setup.py # xdevice扩展模块安装脚本
|
||||
```
|
||||
|
||||
## 约束<a name="section118067583303"></a>
|
||||
|
||||
运行环境要求:
|
||||
|
||||
- python版本\>=3.7.5
|
||||
- pyserial\>=3.3
|
||||
- paramiko\>=2.7.1
|
||||
- rsa\>=4.0
|
||||
|
||||
## 使用<a name="section2036431583"></a>
|
||||
|
||||
- **安装xdevice**
|
||||
1. 打开xdevice安装目录。
|
||||
2. 打开控制台,执行如下命令:
|
||||
|
||||
```
|
||||
python setup.py install
|
||||
```
|
||||
|
||||
|
||||
- **安装extension**
|
||||
1. 打开extension安装目录。
|
||||
2. 打开控制台,执行如下命令:
|
||||
|
||||
```
|
||||
python setup.py install
|
||||
```
|
||||
|
||||
|
||||
- **修改user\_config.xml**
|
||||
|
||||
user\_config.xml是框架提供的用户配置文件,用户可以根据自身环境信息配置相关内容,具体介绍如下:
|
||||
|
||||
**1、environment环境相关配置:**
|
||||
|
||||
- 设备类型一
|
||||
|
||||
>![](figures/icon-note.gif) **说明:**
|
||||
>ip/port: 表示远程设备地址,默认情况为空,表示使用本地设备,ip地址为127.0.0.1,port为本机hdc启动端口号;
|
||||
>sn: 过滤执行测试设备,若设置为SN1,则表示只有设备SN1能够支持后续run命令执行,其他设备分配状态设置为Ignored,不参与命令执行,可通过list devices命令中Allocation字段来查看sn设置,可配置多个sn,中间以;隔开;
|
||||
|
||||
- 设备类型二
|
||||
|
||||
>![](figures/icon-note.gif) **说明:**
|
||||
>type: 设备连接方式,com表示连接方式是串口
|
||||
>label: 表示设备种类,如wifiiot
|
||||
>serial: 表示一个串口定义
|
||||
>serial/com 表示本地连接的串口,如COM20 serial/type 表示串口类型,cmd是命令串口,deploy是刷机串口,社区版本cmd和deploy使用同一个串口,com值相同
|
||||
>serial/baud\_rate、data\_bits、stop\_bits、timeout: 为串口波特率等串口参数 ,一般采用默认值即可。
|
||||
|
||||
|
||||
**2、测试用例目录设置**
|
||||
|
||||
dir: 指定测试用例目录。
|
||||
|
||||
**3、nfs挂载**
|
||||
|
||||
>![](figures/icon-note.gif) **说明:**
|
||||
>server: nfs挂载配置,label取值为NfsServer。
|
||||
>server/ip: 挂载环境IP地址。
|
||||
>server/port: 挂载环境端口。
|
||||
>server/username: 登录用户名。
|
||||
>server/password: 登录用户密码。
|
||||
>server/dir: 对应挂载的外部路径。
|
||||
>server/remote: nfs服务器与xDevice执行机不在同一台机器时,remote配置为true,否则为false。
|
||||
|
||||
- **选定任务类型**
|
||||
- **启动框架**
|
||||
- **执行指令**
|
||||
|
||||
框架指令可以分为三组:help、list、run。在指令序列中,以run为最常用的执行指令。
|
||||
|
||||
**1、help**
|
||||
|
||||
输入help指令可以查询框架指令帮助信息。
|
||||
|
||||
```
|
||||
help:
|
||||
use help to get information.
|
||||
usage:
|
||||
run: Display a list of supported run command.
|
||||
list: Display a list of supported device and task record.
|
||||
Examples:
|
||||
help run
|
||||
help list
|
||||
```
|
||||
|
||||
>![](figures/icon-note.gif) **说明:**
|
||||
>help run:展示run指令相关说明
|
||||
>help list:展示 list指令相关说明
|
||||
|
||||
**2、list**
|
||||
|
||||
list指令用来展示设备和相关的任务信息
|
||||
|
||||
```
|
||||
list:
|
||||
This command is used to display device list and task record.
|
||||
usage:
|
||||
list
|
||||
list history
|
||||
list <id>
|
||||
Introduction:
|
||||
list: display device list
|
||||
list history: display history record of a serial of tasks
|
||||
list <id>: display history record about task what contains specific id
|
||||
Examples:
|
||||
list
|
||||
list history
|
||||
list 6e****90
|
||||
```
|
||||
|
||||
>![](figures/icon-note.gif) **说明:**
|
||||
>list: 展示设备信息
|
||||
>list history: 展示任务历史信息
|
||||
>list <id\>: 展示特定id的任务其历史信息
|
||||
|
||||
**3、run**
|
||||
|
||||
run指令主要用于执行测试任务
|
||||
|
||||
```
|
||||
run:
|
||||
This command is used to execute the selected testcases.
|
||||
It includes a series of processes such as use case compilation, execution, and result collection.
|
||||
usage: run [-l TESTLIST [TESTLIST ...] | -tf TESTFILE
|
||||
[TESTFILE ...]] [-tc TESTCASE] [-c CONFIG] [-sn DEVICE_SN]
|
||||
[-rp REPORT_PATH [REPORT_PATH ...]]
|
||||
[-respath RESOURCE_PATH [RESOURCE_PATH ...]]
|
||||
[-tcpath TESTCASES_PATH [TESTCASES_PATH ...]]
|
||||
[-ta TESTARGS [TESTARGS ...]] [-pt]
|
||||
[-env TEST_ENVIRONMENT [TEST_ENVIRONMENT ...]]
|
||||
[-e EXECTYPE] [-t [TESTTYPE [TESTTYPE ...]]]
|
||||
[-td TESTDRIVER] [-tl TESTLEVEL] [-bv BUILD_VARIANT]
|
||||
[-cov COVERAGE] [--retry RETRY] [--session SESSION]
|
||||
[--dryrun] [--reboot-per-module] [--check-device]
|
||||
[--repeat REPEAT]
|
||||
action task
|
||||
Specify tests to run.
|
||||
positional arguments:
|
||||
action Specify action
|
||||
task Specify task name,such as "ssts", "acts", "hits"
|
||||
```
|
||||
|
||||
>![](figures/icon-note.gif) **说明:**
|
||||
>一个基本的run指令结构如下:
|
||||
>```
|
||||
>run [task name] -l module1;moudle2
|
||||
>```
|
||||
>task name:任务类型。一般为ssts、acts、hits。非必选项
|
||||
>-l :指定执行测试用例,多个测试用例,中间用;隔开
|
||||
>module:被测试的模块。一般在testcases目录下存在对应的\\.json文件
|
||||
>此外,其他参数可以作为约束条件,附加到这个基本指令之上使用。常用的如:
|
||||
>-sn: 过滤执行测试设备,若设置为SN1,则表示只有设备SN1执行用例
|
||||
>-c: 重新指定user\_config.xml。
|
||||
>-rp: 报告生成路径。默认为xxx/xdevice/reports目录。指定目录后,优先级:指定目录\>xxx/xdevice/reports目录。
|
||||
>-tcpath:环境目录,默认为xxx/xdevice/testcases目录。指定目录后,优先级:指定目录\>xxx/xdevice/testcases目录
|
||||
>-respath:测试套目录,默认为xxx/xdevice/resource目录。指定目录后,优先级:指定目录\>xxx/xdevice/resource目录
|
||||
>--reboot-per-module: 执行前先重启设备
|
||||
|
||||
- **查看执行结果**
|
||||
|
||||
框架执行run指令,控制台会输出对应的log打印,还会生成对应的执行结果报告。如果使用了-rp参数指定报告路径,那么报告就会生成在指定的路径下。否则报告会存放在默认目录。
|
||||
|
||||
```
|
||||
当前报告目录(默认目录/指定目录)
|
||||
├── result(模块执行结果存放目录)
|
||||
│ ├── <模块名>.xml
|
||||
│ ├── ... ...
|
||||
│
|
||||
├── log (设备和任务运行log存放目录)
|
||||
│ ├── <设备1>.log
|
||||
│ ├── ... ...
|
||||
│ ├── <任务>.log
|
||||
├── summary_report.html(测试任务可视化报告)
|
||||
├── summary_report.html(测试任务数据化报告)
|
||||
└── ... ...
|
||||
```
|
||||
|
||||
|
||||
## 相关仓<a name="section260848241"></a>
|
||||
|
||||
test\_xdevice
|
||||
|
||||
test\_xdevice\_extension
|
||||
|
24
adapter/LICENSE
Executable file
24
adapter/LICENSE
Executable file
@ -0,0 +1,24 @@
|
||||
Copyright (c) 2021 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.
|
||||
***************************************************************
|
||||
Copyright (C) 2011 The Android Open Source Project
|
||||
|
||||
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
|
||||
|
||||
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.
|
17
adapter/xdevice_adapter/__init__.py
Executable file
17
adapter/xdevice_adapter/__init__.py
Executable file
@ -0,0 +1,17 @@
|
||||
#!/usr/bin/env python3
|
||||
# coding=utf-8
|
||||
|
||||
#
|
||||
# Copyright (c) 2021 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.
|
||||
#
|
44
adapter/xdevice_adapter/constants.py
Executable file
44
adapter/xdevice_adapter/constants.py
Executable file
@ -0,0 +1,44 @@
|
||||
#!/usr/bin/env python3
|
||||
# coding=utf-8
|
||||
|
||||
#
|
||||
# Copyright (c) 2021 Huawei Device Co., Ltd.
|
||||
# Copyright (c) 2013 The Android Open Source Project
|
||||
# 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.
|
||||
#
|
||||
|
||||
|
||||
from dataclasses import dataclass
|
||||
|
||||
__all__ = ["UsbConst", "AppConst"]
|
||||
|
||||
|
||||
@dataclass
|
||||
class UsbConst:
|
||||
connector = "adb"
|
||||
connector_type = "usb-adb"
|
||||
push = "adb push"
|
||||
pull = "adb pull"
|
||||
shell = "adb shell"
|
||||
server_port = "ANDROID_ADB_SERVER_PORT"
|
||||
kill_server = "adb kill-server"
|
||||
start_server = "adb start-server"
|
||||
reboot = "adb reboot"
|
||||
|
||||
|
||||
@dataclass
|
||||
class AppConst:
|
||||
entry_app = "Entry.apk"
|
||||
app_ext = ".apk"
|
||||
app_name = "APK"
|
||||
wifi_app = "xDeviceService-wifi.apk"
|
38
config/acts.json
Executable file → Normal file
38
config/acts.json
Executable file → Normal file
@ -1,16 +1,22 @@
|
||||
{
|
||||
"description": "Config for acts test suites",
|
||||
"kits": [
|
||||
{
|
||||
"type": "QueryKit",
|
||||
"server": "NfsServer",
|
||||
"mount": [
|
||||
{
|
||||
"source": "resource/tools/query.bin",
|
||||
"target": "/test_root/tools"
|
||||
}
|
||||
],
|
||||
"query" : "/test_root/tools/query.bin"
|
||||
}
|
||||
]
|
||||
}
|
||||
{
|
||||
"description": "Config for acts test suites",
|
||||
"kits": [
|
||||
{
|
||||
"type": "QueryKit",
|
||||
"server": "NfsServer",
|
||||
"mount": [
|
||||
{
|
||||
"source": "resource/tools/query.bin",
|
||||
"target": "/test_root/tools"
|
||||
}
|
||||
],
|
||||
"query" : "/test_root/tools/query.bin"
|
||||
},
|
||||
{
|
||||
"type": "RootFsKit",
|
||||
"command": "./bin/checksum /bin",
|
||||
"hash_file_name": "checksum.hash",
|
||||
"device_label": "ipcamera"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
16
config/hits.json
Normal file
16
config/hits.json
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
"description": "Config for hits test suites",
|
||||
"kits": [
|
||||
{
|
||||
"type": "QueryKit",
|
||||
"server": "NfsServer",
|
||||
"mount": [
|
||||
{
|
||||
"source": "resource/tools/query.bin",
|
||||
"target": "/test_root/tools"
|
||||
}
|
||||
],
|
||||
"query" : "/test_root/tools/query.bin"
|
||||
}
|
||||
]
|
||||
}
|
20
config/ssts.json
Normal file
20
config/ssts.json
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"description": "Runs a STS plan from a pre-existing STS installation",
|
||||
"kits": [
|
||||
{
|
||||
"type": "QueryKit",
|
||||
"server": "NfsServer",
|
||||
"mount": [
|
||||
{
|
||||
"source": "resource/tools/query.bin",
|
||||
"target": "/test_root/tools"
|
||||
}
|
||||
],
|
||||
"query" : "/test_root/tools/query.bin",
|
||||
"properties": {
|
||||
"version": "",
|
||||
"spt": ""
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
@ -1,65 +1,66 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Copyright (c) 2020 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.
|
||||
-->
|
||||
<user_config>
|
||||
<environment>
|
||||
<support_device>
|
||||
<device_lite>true</device_lite>
|
||||
</support_device>
|
||||
<device type="com" label="wifiiot">
|
||||
<serial>
|
||||
<com></com>
|
||||
<type>cmd</type>
|
||||
<baund_rate>115200</baund_rate>
|
||||
<data_bits>8</data_bits>
|
||||
<stop_bits>1</stop_bits>
|
||||
<timeout>20</timeout>
|
||||
</serial>
|
||||
<serial>
|
||||
<com></com>
|
||||
<type>deploy</type>
|
||||
<baund_rate>115200</baund_rate>
|
||||
</serial>
|
||||
</device>
|
||||
<device type="com" label="ipcamera">
|
||||
<serial>
|
||||
<com></com>
|
||||
<type>cmd</type>
|
||||
<baund_rate>115200</baund_rate>
|
||||
<data_bits>8</data_bits>
|
||||
<stop_bits>1</stop_bits>
|
||||
<timeout>1</timeout>
|
||||
</serial>
|
||||
</device>
|
||||
<device type="com" label="ipcamera">
|
||||
<ip></ip>
|
||||
<port></port>
|
||||
</device>
|
||||
</environment>
|
||||
<testcases>
|
||||
<dir></dir>
|
||||
<server label="NfsServer">
|
||||
<ip></ip>
|
||||
<port></port>
|
||||
<dir></dir>
|
||||
<username></username>
|
||||
<password></password>
|
||||
<remote></remote>
|
||||
</server>
|
||||
</testcases>
|
||||
<resource>
|
||||
<dir></dir>
|
||||
</resource>
|
||||
</user_config>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (c) 2020 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.
|
||||
-->
|
||||
<user_config>
|
||||
<environment>
|
||||
<device type="com"
|
||||
label="wifiiot">
|
||||
<serial>
|
||||
<com></com>
|
||||
<type>cmd</type>
|
||||
<baud_rate>115200</baud_rate>
|
||||
<data_bits>8</data_bits>
|
||||
<stop_bits>1</stop_bits>
|
||||
<timeout>20</timeout>
|
||||
</serial>
|
||||
<serial>
|
||||
<com></com>
|
||||
<type>deploy</type>
|
||||
<baud_rate>115200</baud_rate>
|
||||
</serial>
|
||||
</device>
|
||||
<device type="com"
|
||||
label="ipcamera">
|
||||
<serial>
|
||||
<com></com>
|
||||
<type>cmd</type>
|
||||
<baud_rate>115200</baud_rate>
|
||||
<data_bits>8</data_bits>
|
||||
<stop_bits>1</stop_bits>
|
||||
<timeout>1</timeout>
|
||||
</serial>
|
||||
</device>
|
||||
<device type="com"
|
||||
label="ipcamera">
|
||||
<ip></ip>
|
||||
<port></port>
|
||||
</device>
|
||||
</environment>
|
||||
<testcases>
|
||||
<dir></dir>
|
||||
<server label="NfsServer">
|
||||
<ip></ip>
|
||||
<port></port>
|
||||
<dir></dir>
|
||||
<username></username>
|
||||
<password></password>
|
||||
<remote></remote>
|
||||
</server>
|
||||
</testcases>
|
||||
<resource>
|
||||
<dir></dir>
|
||||
</resource>
|
||||
<loglevel>INFO</loglevel>
|
||||
</user_config>
|
||||
|
BIN
figures/icon-caution.gif
Executable file
BIN
figures/icon-caution.gif
Executable file
Binary file not shown.
After Width: | Height: | Size: 580 B |
BIN
figures/icon-danger.gif
Executable file
BIN
figures/icon-danger.gif
Executable file
Binary file not shown.
After Width: | Height: | Size: 580 B |
BIN
figures/icon-note.gif
Executable file
BIN
figures/icon-note.gif
Executable file
Binary file not shown.
After Width: | Height: | Size: 394 B |
BIN
figures/icon-notice.gif
Executable file
BIN
figures/icon-notice.gif
Executable file
Binary file not shown.
After Width: | Height: | Size: 406 B |
BIN
figures/icon-tip.gif
Executable file
BIN
figures/icon-tip.gif
Executable file
Binary file not shown.
After Width: | Height: | Size: 253 B |
BIN
figures/icon-warning.gif
Executable file
BIN
figures/icon-warning.gif
Executable file
Binary file not shown.
After Width: | Height: | Size: 580 B |
17
lite/BUILD.gn
Normal file
17
lite/BUILD.gn
Normal file
@ -0,0 +1,17 @@
|
||||
# Copyright (c) 2020 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("//test/xts/tools/lite/build/suite_lite.gni")
|
||||
|
||||
deploy_suite("xdevice") {
|
||||
suite_name = "acts"
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
详见:https://gitee.com/openharmony/docs/blob/master/readme/测试子系统README.md
|
||||
|
||||
see: https://gitee.com/openharmony/docs/blob/master/docs-en/readme/testing-subsystem.md
|
21
run.bat
21
run.bat
@ -1,3 +1,16 @@
|
||||
@rem Copyright (c) 2020-2021 Huawei Device Co., Ltd.
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem You may obtain a copy of the License at
|
||||
@rem
|
||||
@rem http://www.apache.org/licenses/LICENSE-2.0
|
||||
@rem
|
||||
@rem Unless required by applicable law or agreed to in writing, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
|
||||
@echo off
|
||||
set BASE_DIR=%~dp0
|
||||
set PYTHON=python
|
||||
@ -6,12 +19,14 @@ cd /d %BASE_DIR%
|
||||
|
||||
(where %PYTHON% | findstr %PYTHON%) >nul 2>&1 || (
|
||||
@echo "Python3.7 or higher version required!"
|
||||
pause
|
||||
goto:eof
|
||||
)
|
||||
|
||||
python -c "import sys; exit(1) if sys.version_info.major < 3 or sys.version_info.minor < 7 else exit(0)"
|
||||
@if errorlevel 1 (
|
||||
@echo "Python3.7 or higher version required!"
|
||||
pause
|
||||
goto:eof
|
||||
)
|
||||
|
||||
@ -32,4 +47,10 @@ for %%a in (%TOOLS%/*.egg) do (
|
||||
@echo "Error occurs to install %%a!"
|
||||
)
|
||||
)
|
||||
for %%a in (%TOOLS%/*.tar.gz) do (
|
||||
python -m easy_install --user %TOOLS%/%%a
|
||||
@if errorlevel 1 (
|
||||
@echo "Error occurs to install %%a!"
|
||||
)
|
||||
)
|
||||
python -m xdevice %*
|
||||
|
23
run.sh
23
run.sh
@ -1,5 +1,18 @@
|
||||
#!/usr/bin/env sh
|
||||
# Copyright (c) Huawei Technologies Co., Ltd. 2019. All rights reserved.
|
||||
#
|
||||
# Copyright (c) 2020-2021 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.
|
||||
#
|
||||
|
||||
error()
|
||||
{
|
||||
@ -31,4 +44,12 @@ do
|
||||
$PYTHON -m easy_install --user "$f" || echo "Error occurs to install $f!"
|
||||
done
|
||||
|
||||
for f in "$TOOLS"/*.tar.gz
|
||||
do
|
||||
if [ ! -e "$f" ]; then
|
||||
error "Can not find xdevice package!"
|
||||
fi
|
||||
$PYTHON -m easy_install --user "$f" || echo "Error occurs to install $f!"
|
||||
done
|
||||
|
||||
$PYTHON -m xdevice "$@"
|
||||
|
8
setup.py
8
setup.py
@ -18,12 +18,14 @@
|
||||
|
||||
from setuptools import setup
|
||||
|
||||
INSTALL_REQUIRES = []
|
||||
|
||||
|
||||
def main():
|
||||
setup(name='xdevice',
|
||||
description='xdevice test framework',
|
||||
url='',
|
||||
package_dir={'': 'src'},
|
||||
package_dir={'': 'src', 'adapter': 'adapter'},
|
||||
packages=['xdevice',
|
||||
'xdevice._core',
|
||||
'xdevice._core.build',
|
||||
@ -33,7 +35,8 @@ def main():
|
||||
'xdevice._core.environment',
|
||||
'xdevice._core.executor',
|
||||
'xdevice._core.report',
|
||||
'xdevice._core.testkit'
|
||||
'xdevice._core.testkit',
|
||||
'adapter.xdevice_adapter',
|
||||
],
|
||||
package_data={
|
||||
'xdevice._core': [
|
||||
@ -49,6 +52,7 @@ def main():
|
||||
]
|
||||
},
|
||||
zip_safe=False,
|
||||
install_requires=INSTALL_REQUIRES,
|
||||
)
|
||||
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
# coding=utf-8
|
||||
|
||||
#
|
||||
# Copyright (c) 2020 Huawei Device Co., Ltd.
|
||||
# Copyright (c) 2020-2021 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
|
||||
@ -30,6 +30,7 @@ from _core.interface import LifeCycle
|
||||
from _core.interface import IShellReceiver
|
||||
from _core.interface import ITestKit
|
||||
from _core.interface import IListener
|
||||
from _core.interface import IReporter
|
||||
from _core.exception import ParamError
|
||||
from _core.exception import DeviceError
|
||||
from _core.exception import LiteDeviceError
|
||||
@ -40,7 +41,9 @@ from _core.constants import DeviceLabelType
|
||||
from _core.constants import ManagerType
|
||||
from _core.constants import DeviceOsType
|
||||
from _core.constants import ProductForm
|
||||
from _core.constants import TestType
|
||||
from _core.constants import CKit
|
||||
from _core.constants import ConfigConst
|
||||
from _core.config.config_manager import UserConfigManager
|
||||
from _core.config.resource_manager import ResourceManager
|
||||
from _core.executor.listener import CaseResult
|
||||
@ -57,11 +60,17 @@ from _core.utils import get_device_log_file
|
||||
from _core.utils import get_kit_instances
|
||||
from _core.utils import get_config_value
|
||||
from _core.utils import exec_cmd
|
||||
from _core.utils import check_device_name
|
||||
from _core.utils import do_module_kit_setup
|
||||
from _core.utils import do_module_kit_teardown
|
||||
from _core.environment.manager_env import DeviceSelectionOption
|
||||
from _core.environment.manager_env import EnvironmentManager
|
||||
from _core.executor.scheduler import Scheduler
|
||||
from _core.report.suite_reporter import SuiteReporter
|
||||
from _core.report.suite_reporter import ResultCode
|
||||
from _core.report.reporter_helper import ExecInfo
|
||||
from _core.report.result_reporter import ResultReporter
|
||||
from _core.report.__main__ import main_report
|
||||
from _core.command.console import Console
|
||||
|
||||
__all__ = [
|
||||
@ -78,6 +87,7 @@ __all__ = [
|
||||
"IShellReceiver",
|
||||
"ITestKit",
|
||||
"IListener",
|
||||
"IReporter",
|
||||
"ParamError",
|
||||
"DeviceError",
|
||||
"LiteDeviceError",
|
||||
@ -88,7 +98,9 @@ __all__ = [
|
||||
"ManagerType",
|
||||
"DeviceOsType",
|
||||
"ProductForm",
|
||||
"TestType",
|
||||
"CKit",
|
||||
"ConfigConst",
|
||||
"UserConfigManager",
|
||||
"ResourceManager",
|
||||
"CaseResult",
|
||||
@ -109,13 +121,20 @@ __all__ = [
|
||||
"get_device_log_file",
|
||||
"get_kit_instances",
|
||||
"get_config_value",
|
||||
"exec_cmd"
|
||||
"exec_cmd",
|
||||
"check_device_name",
|
||||
"do_module_kit_setup",
|
||||
"do_module_kit_teardown",
|
||||
"ExecInfo",
|
||||
"ResultReporter",
|
||||
"main_report"
|
||||
]
|
||||
|
||||
|
||||
def _load_external_plugins():
|
||||
plugins = [Plugin.SCHEDULER, Plugin.DRIVER, Plugin.DEVICE, Plugin.LOG,
|
||||
Plugin.PARSER, Plugin.LISTENER, Plugin.TEST_KIT, Plugin.MANAGER]
|
||||
Plugin.PARSER, Plugin.LISTENER, Plugin.TEST_KIT, Plugin.MANAGER,
|
||||
Plugin.REPORTER]
|
||||
for plugin_group in plugins:
|
||||
for entry_point in pkg_resources.iter_entry_points(group=plugin_group):
|
||||
entry_point.load()
|
||||
|
@ -2,7 +2,7 @@
|
||||
# coding=utf-8
|
||||
|
||||
#
|
||||
# Copyright (c) 2020 Huawei Device Co., Ltd.
|
||||
# Copyright (c) 2020-2021 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
|
||||
|
0
src/xdevice/_core/__init__.py
Executable file → Normal file
0
src/xdevice/_core/__init__.py
Executable file → Normal file
0
src/xdevice/_core/build/__init__.py
Executable file → Normal file
0
src/xdevice/_core/build/__init__.py
Executable file → Normal file
0
src/xdevice/_core/command/__init__.py
Executable file → Normal file
0
src/xdevice/_core/command/__init__.py
Executable file → Normal file
@ -2,7 +2,7 @@
|
||||
# coding=utf-8
|
||||
|
||||
#
|
||||
# Copyright (c) 2020 Huawei Device Co., Ltd.
|
||||
# Copyright (c) 2020-2021 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
|
||||
@ -25,6 +25,8 @@ import threading
|
||||
|
||||
from _core.config.config_manager import UserConfigManager
|
||||
from _core.constants import SchedulerType
|
||||
from _core.constants import ConfigConst
|
||||
from _core.constants import ModeType
|
||||
from _core.constants import ToolCommandType
|
||||
from _core.environment.manager_env import EnvironmentManager
|
||||
from _core.exception import ParamError
|
||||
@ -36,6 +38,7 @@ from _core.plugin import Plugin
|
||||
from _core.plugin import get_plugin
|
||||
from _core.utils import SplicingAction
|
||||
from _core.utils import get_instance_name
|
||||
from _core.report.result_reporter import ResultReporter
|
||||
|
||||
__all__ = ["Console"]
|
||||
|
||||
@ -43,7 +46,7 @@ LOG = platform_logger("Console")
|
||||
try:
|
||||
if platform.system() != 'Windows':
|
||||
import readline
|
||||
except ModuleNotFoundError:
|
||||
except (ModuleNotFoundError, ImportError):
|
||||
LOG.warning("readline module is not exist.")
|
||||
|
||||
|
||||
@ -111,7 +114,7 @@ class Console(object):
|
||||
|
||||
except SystemExit:
|
||||
LOG.info("Program exit normally!")
|
||||
return
|
||||
break
|
||||
except ExecuteTerminate:
|
||||
LOG.info("execution terminated")
|
||||
except (IOError, EOFError, KeyboardInterrupt) as error:
|
||||
@ -138,84 +141,109 @@ class Console(object):
|
||||
type=str,
|
||||
default=None,
|
||||
help="Specify task name")
|
||||
parser.add_argument("-sn", "--device_sn",
|
||||
action="store",
|
||||
type=str,
|
||||
dest="device_sn",
|
||||
default="",
|
||||
help="Specify device serial number"
|
||||
)
|
||||
group.add_argument("-l", "--testlist",
|
||||
action=SplicingAction,
|
||||
type=str,
|
||||
nargs='+',
|
||||
dest=ConfigConst.testlist,
|
||||
default="",
|
||||
help="Specify test list"
|
||||
)
|
||||
group.add_argument("-tf", "--testfile",
|
||||
action=SplicingAction,
|
||||
type=str,
|
||||
nargs='+',
|
||||
dest="testfile",
|
||||
dest=ConfigConst.testfile,
|
||||
default="",
|
||||
help="Specify test list file"
|
||||
)
|
||||
parser.add_argument("-c", "--config",
|
||||
parser.add_argument("-tc", "--testcase",
|
||||
action="store",
|
||||
type=str,
|
||||
dest="config",
|
||||
dest=ConfigConst.testcase,
|
||||
default="",
|
||||
help="Specify test config file"
|
||||
help="Specify test case"
|
||||
)
|
||||
parser.add_argument("-c", "--config",
|
||||
action=SplicingAction,
|
||||
type=str,
|
||||
nargs='+',
|
||||
dest=ConfigConst.configfile,
|
||||
default="",
|
||||
help="Specify config file path"
|
||||
)
|
||||
parser.add_argument("-sn", "--device_sn",
|
||||
action="store",
|
||||
type=str,
|
||||
dest=ConfigConst.device_sn,
|
||||
default="",
|
||||
help="Specify device serial number"
|
||||
)
|
||||
parser.add_argument("-rp", "--reportpath",
|
||||
action=SplicingAction,
|
||||
type=str,
|
||||
nargs='+',
|
||||
dest="reportpath",
|
||||
dest=ConfigConst.report_path,
|
||||
default="",
|
||||
help="Specify test report path"
|
||||
)
|
||||
parser.add_argument("-respath", "--resourcepath",
|
||||
action=SplicingAction,
|
||||
type=str,
|
||||
nargs='+',
|
||||
dest=ConfigConst.resource_path,
|
||||
default="",
|
||||
help="Specify test resource path"
|
||||
)
|
||||
parser.add_argument("-tcpath", "--testcasespath",
|
||||
action=SplicingAction,
|
||||
type=str,
|
||||
nargs='+',
|
||||
dest=ConfigConst.testcases_path,
|
||||
default="",
|
||||
help="Specify testcases path"
|
||||
)
|
||||
parser.add_argument("-ta", "--testargs",
|
||||
action=SplicingAction,
|
||||
type=str,
|
||||
nargs='+',
|
||||
dest=ConfigConst.testargs,
|
||||
default={},
|
||||
help="Specify test arguments"
|
||||
)
|
||||
parser.add_argument("-pt", "--passthrough",
|
||||
action="store_true",
|
||||
dest=ConfigConst.pass_through,
|
||||
help="Pass through test arguments"
|
||||
)
|
||||
parser.add_argument("-env", "--environment",
|
||||
action=SplicingAction,
|
||||
type=str,
|
||||
nargs='+',
|
||||
dest=ConfigConst.test_environment,
|
||||
default="",
|
||||
help="Specify test environment"
|
||||
)
|
||||
parser.add_argument("-e", "--exectype",
|
||||
action="store",
|
||||
type=str,
|
||||
dest="exectype",
|
||||
dest=ConfigConst.exectype,
|
||||
default="device",
|
||||
help="Specify test execute type"
|
||||
)
|
||||
parser.add_argument("-p", "--productform",
|
||||
action="store",
|
||||
type=str,
|
||||
dest="productform",
|
||||
default="phone",
|
||||
help="Specified product form"
|
||||
)
|
||||
parser.add_argument("-t", "--testtype",
|
||||
nargs='*',
|
||||
dest="testtype",
|
||||
default=["UT"],
|
||||
dest=ConfigConst.testtype,
|
||||
default=[],
|
||||
help="Specify test type" +
|
||||
"(UT,MST,ST,PERF,SEC,RELI,DST,ALL)"
|
||||
)
|
||||
parser.add_argument("-ss", "--subsystem",
|
||||
parser.add_argument("-td", "--testdriver",
|
||||
action="store",
|
||||
type=str,
|
||||
dest="subsystem",
|
||||
dest=ConfigConst.testdriver,
|
||||
default="",
|
||||
help="Specify test subsystem"
|
||||
)
|
||||
parser.add_argument("-tm", "--testmodule",
|
||||
action="store",
|
||||
type=str,
|
||||
dest="testmodule",
|
||||
default="",
|
||||
help="Specified test module"
|
||||
)
|
||||
parser.add_argument("-ts", "--testsuit",
|
||||
action="store",
|
||||
type=str,
|
||||
dest="testsuit",
|
||||
default="",
|
||||
help="Specify test suit"
|
||||
)
|
||||
parser.add_argument("-tc", "--testcase",
|
||||
action="store",
|
||||
type=str,
|
||||
dest="testcase",
|
||||
default="",
|
||||
help="Specify test case"
|
||||
help="Specify test driver id"
|
||||
)
|
||||
parser.add_argument("-tl", "--testlevel",
|
||||
action="store",
|
||||
@ -224,13 +252,6 @@ class Console(object):
|
||||
default="",
|
||||
help="Specify test level"
|
||||
)
|
||||
parser.add_argument("-td", "--testdriver",
|
||||
action="store",
|
||||
type=str,
|
||||
dest="testdriver",
|
||||
default="",
|
||||
help="Specify test driver id"
|
||||
)
|
||||
parser.add_argument("-bv", "--build_variant",
|
||||
action="store",
|
||||
type=str,
|
||||
@ -238,13 +259,6 @@ class Console(object):
|
||||
default="release",
|
||||
help="Specify build variant(release,debug)"
|
||||
)
|
||||
parser.add_argument("-b", "--build",
|
||||
nargs='*',
|
||||
dest="build",
|
||||
default=[],
|
||||
help="Specify build values"
|
||||
"(version,testcase,example)"
|
||||
)
|
||||
parser.add_argument("-cov", "--coverage",
|
||||
action="store",
|
||||
type=str,
|
||||
@ -252,58 +266,37 @@ class Console(object):
|
||||
default="",
|
||||
help="Specify coverage"
|
||||
)
|
||||
parser.add_argument("-respath", "--resourcepath",
|
||||
action=SplicingAction,
|
||||
type=str,
|
||||
nargs='+',
|
||||
dest="resource_path",
|
||||
default="",
|
||||
help="Specify test resource path"
|
||||
)
|
||||
parser.add_argument("-tcpath", "--testcasespath",
|
||||
action=SplicingAction,
|
||||
type=str,
|
||||
nargs='+',
|
||||
dest="testcases_path",
|
||||
default="",
|
||||
help="Specify testcases path"
|
||||
)
|
||||
parser.add_argument("-ta", "--testargs",
|
||||
action=SplicingAction,
|
||||
type=str,
|
||||
nargs='+',
|
||||
dest="testargs",
|
||||
default={},
|
||||
help="Specify test arguments"
|
||||
)
|
||||
group.add_argument("-l", "--testlist",
|
||||
action=SplicingAction,
|
||||
type=str,
|
||||
nargs='+',
|
||||
dest="testlist",
|
||||
default="",
|
||||
help="Specify test list"
|
||||
)
|
||||
parser.add_argument("-env", "--environment",
|
||||
action=SplicingAction,
|
||||
type=str,
|
||||
nargs='+',
|
||||
dest="test_environment",
|
||||
default="",
|
||||
help="Specify test environment"
|
||||
)
|
||||
parser.add_argument("--retry",
|
||||
action="store",
|
||||
type=str,
|
||||
dest="retry",
|
||||
dest=ConfigConst.retry,
|
||||
default="",
|
||||
help="Specify retry command"
|
||||
)
|
||||
parser.add_argument("--pass_through",
|
||||
parser.add_argument("--session",
|
||||
action="store",
|
||||
dest=ConfigConst.session,
|
||||
help="retry task by session id")
|
||||
parser.add_argument("--dryrun",
|
||||
action="store_true",
|
||||
dest="pass_through",
|
||||
help="Pass through test arguments"
|
||||
)
|
||||
dest=ConfigConst.dry_run,
|
||||
help="show retry test case list")
|
||||
parser.add_argument("--reboot-per-module",
|
||||
action="store_true",
|
||||
dest=ConfigConst.reboot_per_module,
|
||||
help="reboot devices before executing each "
|
||||
"module")
|
||||
parser.add_argument("--check-device",
|
||||
action="store_true",
|
||||
dest=ConfigConst.check_device,
|
||||
help="check the test device meets the "
|
||||
"requirements")
|
||||
parser.add_argument("--repeat",
|
||||
type=int,
|
||||
default=0,
|
||||
dest=ConfigConst.repeat,
|
||||
help="number of times that a task is executed"
|
||||
" repeatedly")
|
||||
self._params_pre_processing(para_list)
|
||||
(options, unparsed) = parser.parse_known_args(para_list)
|
||||
if unparsed:
|
||||
@ -333,18 +326,24 @@ class Console(object):
|
||||
def _params_post_processing(self, options):
|
||||
# params post-processing
|
||||
if options.task == Task.EMPTY_TASK:
|
||||
setattr(options, "task", "")
|
||||
if options.testargs and not options.pass_through:
|
||||
test_args = self._parse_combination_param(options.testargs)
|
||||
setattr(options, "testargs", test_args)
|
||||
setattr(options, ConfigConst.task, "")
|
||||
if options.testargs:
|
||||
if not options.pass_through:
|
||||
test_args = self._parse_combination_param(options.testargs)
|
||||
setattr(options, ConfigConst.testargs, test_args)
|
||||
else:
|
||||
setattr(options, ConfigConst.testargs, {
|
||||
ConfigConst.pass_through: options.testargs})
|
||||
if not options.resource_path:
|
||||
resource_path = UserConfigManager(env=options.test_environment).\
|
||||
resource_path = UserConfigManager(
|
||||
config_file=options.config, env=options.test_environment).\
|
||||
get_resource_path()
|
||||
setattr(options, "resource_path", resource_path)
|
||||
setattr(options, ConfigConst.resource_path, resource_path)
|
||||
if not options.testcases_path:
|
||||
testcases_path = UserConfigManager(env=options.test_environment). \
|
||||
testcases_path = UserConfigManager(
|
||||
config_file=options.config, env=options.test_environment).\
|
||||
get_testcases_dir()
|
||||
setattr(options, "testcases_path", testcases_path)
|
||||
setattr(options, ConfigConst.testcases_path, testcases_path)
|
||||
|
||||
def command_parser(self, args):
|
||||
try:
|
||||
@ -359,6 +358,11 @@ class Console(object):
|
||||
if options.action == ToolCommandType.toolcmd_key_run and \
|
||||
options.retry:
|
||||
options = self._get_retry_options(options)
|
||||
if options.dry_run:
|
||||
history_report_path = getattr(options,
|
||||
"history_report_path", "")
|
||||
self._list_retry_case(history_report_path)
|
||||
return
|
||||
else:
|
||||
from xdevice import SuiteReporter
|
||||
SuiteReporter.clear_failed_case_list()
|
||||
@ -372,8 +376,9 @@ class Console(object):
|
||||
self._process_command(command, options, para_list, parser)
|
||||
except (ParamError, ValueError, TypeError, SyntaxError,
|
||||
AttributeError) as exception:
|
||||
error_no = getattr(exception, "error_no", "00000")
|
||||
LOG.exception("%s: %s" % (get_instance_name(exception), exception),
|
||||
exc_info=False)
|
||||
exc_info=False, error_no=error_no)
|
||||
if Scheduler.upload_address:
|
||||
Scheduler.upload_unavailable_result(str(exception.args))
|
||||
Scheduler.upload_report_end()
|
||||
@ -393,65 +398,71 @@ class Console(object):
|
||||
elif command.startswith(ToolCommandType.toolcmd_key_list):
|
||||
self._process_command_list(command, para_list)
|
||||
else:
|
||||
LOG.error("unsupported command action: %s" % command)
|
||||
LOG.error("unsupported command action", error_no="00100",
|
||||
action=command)
|
||||
|
||||
def _get_retry_options(self, options):
|
||||
input_report_path = options.reportpath
|
||||
|
||||
# get history_command, history_report_path
|
||||
if options.retry == "retry_previous_command":
|
||||
if len(Scheduler.command_queue) < 2:
|
||||
raise ParamError("no previous command executed")
|
||||
_, history_command, history_report_path = \
|
||||
Scheduler.command_queue[-2]
|
||||
else:
|
||||
history_command, history_report_path = "", ""
|
||||
for command_tuple in Scheduler.command_queue[:-1]:
|
||||
if command_tuple[0] != options.retry:
|
||||
continue
|
||||
history_command, history_report_path = \
|
||||
command_tuple[1], command_tuple[2]
|
||||
break
|
||||
if not history_command:
|
||||
raise ParamError("wrong task id input: %s" % options.retry)
|
||||
|
||||
# get history command, history report path
|
||||
history_command, history_report_path = self._parse_retry_option(
|
||||
options)
|
||||
input_report_path = options.report_path
|
||||
LOG.info("History command: %s", history_command)
|
||||
# parse history_command, set history_report_path
|
||||
if not os.path.exists(history_report_path) and \
|
||||
Scheduler.mode != "decc":
|
||||
Scheduler.mode != ModeType.decc:
|
||||
raise ParamError(
|
||||
"history report path %s not exists" % history_report_path)
|
||||
(options, _, _, _) = self.argument_parser(
|
||||
history_command.split())
|
||||
|
||||
# parse history command, set history report path
|
||||
is_dry_run = True if options.dry_run else False
|
||||
|
||||
# clear the content about repeat count in history command
|
||||
if "--repeat" in history_command:
|
||||
split_list = list(history_command.split())
|
||||
if "--repeat" in split_list:
|
||||
pos = split_list.index("--repeat")
|
||||
split_list = split_list[:pos] + split_list[pos+2:]
|
||||
history_command = " ".join(split_list)
|
||||
|
||||
(options, _, _, _) = self.argument_parser(history_command.split())
|
||||
options.dry_run = is_dry_run
|
||||
setattr(options, "history_report_path", history_report_path)
|
||||
|
||||
# modify history_command -rp param
|
||||
if options.reportpath:
|
||||
if input_report_path:
|
||||
history_command = history_command.replace(
|
||||
options.reportpath, input_report_path)
|
||||
setattr(options, "reportpath", input_report_path)
|
||||
else:
|
||||
history_command = history_command.replace(
|
||||
options.reportpath, "").replace("-rp", "").replace(
|
||||
"--reportpath", "")
|
||||
setattr(options, "reportpath", "")
|
||||
else:
|
||||
if input_report_path:
|
||||
history_command = "{}{}".format(history_command,
|
||||
" -rp %s" % input_report_path)
|
||||
setattr(options, "reportpath", input_report_path)
|
||||
history_command = history_command.strip()
|
||||
history_command = self._parse_rp_option(
|
||||
history_command, input_report_path, options)
|
||||
|
||||
# add history command to Scheduler.command_queue
|
||||
LOG.info("Retry command: %s", history_command)
|
||||
Scheduler.command_queue[-1] = history_command
|
||||
return options
|
||||
|
||||
@classmethod
|
||||
def _parse_rp_option(cls, history_command, input_report_path,
|
||||
options):
|
||||
if options.report_path:
|
||||
if input_report_path:
|
||||
history_command = history_command.replace(
|
||||
options.report_path, input_report_path)
|
||||
setattr(options, "report_path", input_report_path)
|
||||
else:
|
||||
history_command = history_command.replace(
|
||||
options.report_path, "").replace("-rp", "").replace(
|
||||
"--reportpath", "")
|
||||
setattr(options, "report_path", "")
|
||||
else:
|
||||
if input_report_path:
|
||||
history_command = "{}{}".format(history_command,
|
||||
" -rp %s" % input_report_path)
|
||||
setattr(options, "report_path", input_report_path)
|
||||
return history_command.strip()
|
||||
|
||||
@classmethod
|
||||
def _process_command_help(cls, parser, para_list):
|
||||
if para_list[0] == ToolCommandType.toolcmd_key_help:
|
||||
parser.print_help()
|
||||
if len(para_list) == 2:
|
||||
cls.display_help_command_info(para_list[1])
|
||||
else:
|
||||
parser.print_help()
|
||||
else:
|
||||
LOG.error("Wrong help command. Use 'help' to print help")
|
||||
return
|
||||
@ -545,3 +556,256 @@ class Console(object):
|
||||
print("{0:<16}{1:<100}".format("TaskId:", task_id))
|
||||
print("{0:<16}{1:<100}".format("Command:", command))
|
||||
print("{0:<16}{1:<100}".format("ReportPath:", report_path))
|
||||
|
||||
@classmethod
|
||||
def _list_retry_case(cls, history_path):
|
||||
params = ResultReporter.get_task_info_params(history_path)
|
||||
if not params:
|
||||
raise ParamError("no retry case exists")
|
||||
session_id, command, report_path, failed_list = \
|
||||
params[0], params[1], params[2], \
|
||||
[(module, failed) for module, case_list in params[3].items()
|
||||
for failed in case_list]
|
||||
if Scheduler.mode == ModeType.decc:
|
||||
from xdevice import SuiteReporter
|
||||
SuiteReporter.failed_case_list = failed_list
|
||||
return
|
||||
|
||||
# draw tables in console
|
||||
left, middle, right = 23, 49, 49
|
||||
two_segments = "{0:-<%s}{1:-<%s}+" % (left, middle + right)
|
||||
two_rows = "|{0:^%s}|{1:^%s}|" % (left - 1, middle + right - 1)
|
||||
|
||||
three_segments = "{0:-<%s}{1:-<%s}{2:-<%s}+" % (left, middle, right)
|
||||
three_rows = "|{0:^%s}|{1:^%s}|{2:^%s}|" % \
|
||||
(left - 1, middle - 1, right - 1)
|
||||
if len(session_id) > middle + right - 1:
|
||||
session_id = "%s..." % session_id[:middle + right - 4]
|
||||
if len(command) > middle + right - 1:
|
||||
command = "%s..." % command[:middle + right - 4]
|
||||
if len(report_path) > middle + right - 1:
|
||||
report_path = "%s..." % report_path[:middle + right - 4]
|
||||
|
||||
print(two_segments.format("+", '+'))
|
||||
print(two_rows.format("SessionId", session_id))
|
||||
print(two_rows.format("Command", command))
|
||||
print(two_rows.format("ReportPath", report_path))
|
||||
|
||||
print(three_segments.format("+", '+', '+'))
|
||||
print(three_rows.format("Module", "Testsuite", "Testcase"))
|
||||
print(three_segments.format("+", '+', '+'))
|
||||
for module, failed in failed_list:
|
||||
# all module is failed
|
||||
if "#" not in failed:
|
||||
class_name = "-"
|
||||
test = "-"
|
||||
# others, get failed cases info
|
||||
else:
|
||||
pos = failed.rfind("#")
|
||||
class_name = failed[:pos]
|
||||
test = failed[pos + 1:]
|
||||
if len(module) > left - 1:
|
||||
module = "%s..." % module[:left - 4]
|
||||
if len(class_name) > middle - 1:
|
||||
class_name = "%s..." % class_name[:middle - 4]
|
||||
if len(test) > right - 1:
|
||||
test = "%s..." % test[:right - 4]
|
||||
print(three_rows.format(module, class_name, test))
|
||||
print(three_segments.format("+", '+', '+'))
|
||||
|
||||
@classmethod
|
||||
def _find_history_path(cls, session):
|
||||
from xdevice import Variables
|
||||
if os.path.isdir(session):
|
||||
return session
|
||||
|
||||
target_path = os.path.join(
|
||||
Variables.exec_dir, Variables.report_vars.report_dir, session)
|
||||
if not os.path.isdir(target_path):
|
||||
raise ParamError("session '%s' is invalid!" % session)
|
||||
|
||||
return target_path
|
||||
|
||||
def _parse_retry_option(self, options):
|
||||
if Scheduler.mode == ModeType.decc:
|
||||
if len(Scheduler.command_queue) < 2:
|
||||
raise ParamError("no previous command executed")
|
||||
_, history_command, history_report_path = \
|
||||
Scheduler.command_queue[-2]
|
||||
return history_command, history_report_path
|
||||
|
||||
# get history_command, history_report_path
|
||||
if options.retry == "retry_previous_command":
|
||||
from xdevice import Variables
|
||||
history_path = os.path.join(
|
||||
Variables.exec_dir, Variables.report_vars.report_dir, "latest")
|
||||
if options.session:
|
||||
history_path = self._find_history_path(options.session)
|
||||
|
||||
params = ResultReporter.get_task_info_params(history_path)
|
||||
if not params:
|
||||
error_msg = "no previous command executed" if not \
|
||||
options.session else "'%s' has no command executed" % \
|
||||
options.session
|
||||
raise ParamError(error_msg)
|
||||
history_command, history_report_path = params[1], params[2]
|
||||
else:
|
||||
history_command, history_report_path = "", ""
|
||||
for command_tuple in Scheduler.command_queue[:-1]:
|
||||
if command_tuple[0] != options.retry:
|
||||
continue
|
||||
history_command, history_report_path = \
|
||||
command_tuple[1], command_tuple[2]
|
||||
break
|
||||
if not history_command:
|
||||
raise ParamError("wrong task id input: %s" % options.retry)
|
||||
return history_command, history_report_path
|
||||
|
||||
@classmethod
|
||||
def display_help_command_info(cls, command):
|
||||
if command == ToolCommandType.toolcmd_key_run:
|
||||
print(RUN_INFORMATION)
|
||||
elif command == ToolCommandType.toolcmd_key_list:
|
||||
print(LIST_INFORMATION)
|
||||
elif command == "empty":
|
||||
print(GUIDE_INFORMATION)
|
||||
else:
|
||||
print("'%s' command no help information." % command)
|
||||
|
||||
|
||||
RUN_INFORMATION = """run:
|
||||
This command is used to execute the selected testcases.
|
||||
It includes a series of processes such as use case compilation, \
|
||||
execution, and result collection.
|
||||
|
||||
usage: run [-l TESTLIST [TESTLIST ...] | -tf TESTFILE
|
||||
[TESTFILE ...]] [-tc TESTCASE] [-c CONFIG] [-sn DEVICE_SN]
|
||||
[-rp REPORT_PATH [REPORT_PATH ...]]
|
||||
[-respath RESOURCE_PATH [RESOURCE_PATH ...]]
|
||||
[-tcpath TESTCASES_PATH [TESTCASES_PATH ...]]
|
||||
[-ta TESTARGS [TESTARGS ...]] [-pt]
|
||||
[-env TEST_ENVIRONMENT [TEST_ENVIRONMENT ...]]
|
||||
[-e EXECTYPE] [-t [TESTTYPE [TESTTYPE ...]]]
|
||||
[-td TESTDRIVER] [-tl TESTLEVEL] [-bv BUILD_VARIANT]
|
||||
[-cov COVERAGE] [--retry RETRY] [--session SESSION]
|
||||
[--dryrun] [--reboot-per-module] [--check-device]
|
||||
[--repeat REPEAT]
|
||||
action task
|
||||
|
||||
Specify tests to run.
|
||||
|
||||
positional arguments:
|
||||
action Specify action
|
||||
task Specify task name,such as "ssts", "acts", "hits"
|
||||
|
||||
optional arguments:
|
||||
-h, --help show this help message and exit
|
||||
-l TESTLIST [TESTLIST ...], --testlist TESTLIST [TESTLIST ...]
|
||||
Specify test list
|
||||
-tf TESTFILE [TESTFILE ...], --testfile TESTFILE [TESTFILE ...]
|
||||
Specify test list file
|
||||
-tc TESTCASE, --testcase TESTCASE
|
||||
Specify test case
|
||||
-c CONFIG, --config CONFIG
|
||||
Specify config file path
|
||||
-sn DEVICE_SN, --device_sn DEVICE_SN
|
||||
Specify device serial number
|
||||
-rp REPORT_PATH [REPORT_PATH ...], --reportpath REPORT_PATH [REPORT_PATH \
|
||||
...]
|
||||
Specify test report path
|
||||
-respath RESOURCE_PATH [RESOURCE_PATH ...], --resourcepath RESOURCE_PATH \
|
||||
[RESOURCE_PATH ...]
|
||||
Specify test resource path
|
||||
-tcpath TESTCASES_PATH [TESTCASES_PATH ...], --testcasespath \
|
||||
TESTCASES_PATH [TESTCASES_PATH ...]
|
||||
Specify testcases path
|
||||
-ta TESTARGS [TESTARGS ...], --testargs TESTARGS [TESTARGS ...]
|
||||
Specify test arguments
|
||||
-pt, --passthrough Pass through test arguments
|
||||
-env TEST_ENVIRONMENT [TEST_ENVIRONMENT ...], --environment \
|
||||
TEST_ENVIRONMENT [TEST_ENVIRONMENT ...]
|
||||
Specify test environment
|
||||
-e EXECTYPE, --exectype EXECTYPE
|
||||
Specify test execute type
|
||||
-t [TESTTYPE [TESTTYPE ...]], --testtype [TESTTYPE [TESTTYPE ...]]
|
||||
Specify test type(UT,MST,ST,PERF,SEC,RELI,DST,ALL)
|
||||
-td TESTDRIVER, --testdriver TESTDRIVER
|
||||
Specify test driver id
|
||||
-tl TESTLEVEL, --testlevel TESTLEVEL
|
||||
Specify test level
|
||||
-bv BUILD_VARIANT, --build_variant BUILD_VARIANT
|
||||
Specify build variant(release,debug)
|
||||
-cov COVERAGE, --coverage COVERAGE
|
||||
Specify coverage
|
||||
--retry RETRY Specify retry command
|
||||
--session SESSION retry task by session id
|
||||
--dryrun show retry test case list
|
||||
--reboot-per-module reboot devices before executing each module
|
||||
--check-device check the test device meets the requirements
|
||||
--repeat REPEAT number of times that a task is executed repeatedly
|
||||
|
||||
Examples:
|
||||
run -l <module name>;<module name>
|
||||
run -tf test/resource/<test file name>.txt
|
||||
|
||||
run –l <module name> -sn <device serial number>;<device serial number>
|
||||
run –l <module name> -respath <path of resource>
|
||||
run –l <module name> -ta size:large
|
||||
run –l <module name> –ta class:<package>#<class>#<method>
|
||||
run –l <module name> -ta size:large -pt
|
||||
run –l <module name> –env <the content string of user_config.xml>
|
||||
run –l <module name> –e device
|
||||
run –l <module name> –t ALL
|
||||
run –l <module name> –td CppTest
|
||||
run –l <module name> -tcpath resource/testcases
|
||||
|
||||
run ssts
|
||||
run ssts –tc <python script name>;<python script name>
|
||||
run ssts -sn <device serial number>;<device serial number>
|
||||
run ssts -respath <path of resource>
|
||||
... ...
|
||||
|
||||
run acts
|
||||
run acts –tc <python script name>;<python script name>
|
||||
run acts -sn <device serial number>;<device serial number>
|
||||
run acts -respath <path of resource>
|
||||
... ...
|
||||
|
||||
run hits
|
||||
... ...
|
||||
|
||||
run --retry
|
||||
run --retry --session <report folder name>
|
||||
run --retry --dryrun
|
||||
"""
|
||||
|
||||
LIST_INFORMATION = "list:" + """
|
||||
This command is used to display device list and task record.\n
|
||||
usage:
|
||||
list
|
||||
list history
|
||||
list <id>
|
||||
|
||||
Introduction:
|
||||
list: display device list
|
||||
list history: display history record of a serial of tasks
|
||||
list <id>: display history record about task what contains specific id
|
||||
|
||||
Examples:
|
||||
list
|
||||
list history
|
||||
list 6e****90
|
||||
"""
|
||||
|
||||
|
||||
GUIDE_INFORMATION = """help:
|
||||
use help to get information.
|
||||
|
||||
usage:
|
||||
run: Display a list of supported run command.
|
||||
list: Display a list of supported device and task record.
|
||||
|
||||
Examples:
|
||||
help run
|
||||
help list
|
||||
"""
|
||||
|
0
src/xdevice/_core/common.py
Executable file → Normal file
0
src/xdevice/_core/common.py
Executable file → Normal file
0
src/xdevice/_core/config/__init__.py
Executable file → Normal file
0
src/xdevice/_core/config/__init__.py
Executable file → Normal file
@ -2,7 +2,7 @@
|
||||
# coding=utf-8
|
||||
|
||||
#
|
||||
# Copyright (c) 2020 Huawei Device Co., Ltd.
|
||||
# Copyright (c) 2020-2021 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
|
||||
@ -22,6 +22,9 @@ from dataclasses import dataclass
|
||||
|
||||
from _core.exception import ParamError
|
||||
from _core.logger import platform_logger
|
||||
from _core.utils import get_local_ip
|
||||
|
||||
from xdevice_adapter.constants import UsbConst
|
||||
|
||||
__all__ = ["UserConfigManager"]
|
||||
LOG = platform_logger("ConfigManager")
|
||||
@ -35,37 +38,42 @@ class ConfigFileConst(object):
|
||||
class UserConfigManager(object):
|
||||
def __init__(self, config_file="", env=""):
|
||||
from xdevice import Variables
|
||||
if config_file:
|
||||
pass
|
||||
try:
|
||||
if env:
|
||||
self.config_content = ET.fromstring(env)
|
||||
else:
|
||||
user_path = os.path.join(Variables.exec_dir, "config")
|
||||
top_user_path = os.path.join(Variables.top_dir, "config")
|
||||
config_path = os.path.join(Variables.res_dir, "config")
|
||||
paths = [user_path, top_user_path, config_path]
|
||||
if config_file:
|
||||
self.file_path = config_file
|
||||
else:
|
||||
user_path = os.path.join(Variables.exec_dir, "config")
|
||||
top_user_path = os.path.join(Variables.top_dir, "config")
|
||||
config_path = os.path.join(Variables.res_dir, "config")
|
||||
paths = [user_path, top_user_path, config_path]
|
||||
|
||||
for path in paths:
|
||||
if os.path.exists(os.path.abspath(os.path.join(
|
||||
path, ConfigFileConst.userconfig_filepath))):
|
||||
self.file_path = os.path.abspath(os.path.join(
|
||||
path, ConfigFileConst.userconfig_filepath))
|
||||
break
|
||||
for path in paths:
|
||||
if os.path.exists(os.path.abspath(os.path.join(
|
||||
path, ConfigFileConst.userconfig_filepath))):
|
||||
self.file_path = os.path.abspath(os.path.join(
|
||||
path, ConfigFileConst.userconfig_filepath))
|
||||
break
|
||||
|
||||
LOG.debug("user config path: %s" % self.file_path)
|
||||
if os.path.exists(self.file_path):
|
||||
tree = ET.parse(self.file_path)
|
||||
self.config_content = tree.getroot()
|
||||
else:
|
||||
raise ParamError("config file not found")
|
||||
raise ParamError("%s not found" % self.file_path,
|
||||
error_no="00115")
|
||||
|
||||
except SyntaxError as error:
|
||||
if env:
|
||||
raise ParamError(
|
||||
"Parse environment parameter fail! Error: %s" % error.args)
|
||||
"Parse environment parameter fail! Error: %s" % error.args,
|
||||
error_no="00115")
|
||||
else:
|
||||
raise ParamError(
|
||||
"Parse %s fail! Error: %s" % (self.file_path, error.args))
|
||||
"Parse %s fail! Error: %s" % (self.file_path, error.args),
|
||||
error_no="00115")
|
||||
|
||||
def get_user_config_list(self, tag_name):
|
||||
data_dic = {}
|
||||
@ -94,18 +102,10 @@ class UserConfigManager(object):
|
||||
return []
|
||||
return config_list
|
||||
|
||||
def get_sn_list(self):
|
||||
def get_sn_list(self, input_string):
|
||||
sn_select_list = []
|
||||
data_dic = {}
|
||||
for node in self.config_content.findall("environment/device"):
|
||||
if node.attrib["type"] != "usb-hdc":
|
||||
continue
|
||||
for sub in node:
|
||||
data_dic[sub.tag] = sub.text if sub.text else ""
|
||||
sn_config = data_dic.get("sn", "")
|
||||
if sn_config:
|
||||
sn_select_list = self._handle_str(sn_config)
|
||||
break
|
||||
if input_string:
|
||||
sn_select_list = self._handle_str(input_string)
|
||||
return sn_select_list
|
||||
|
||||
def get_remote_config(self):
|
||||
@ -114,17 +114,18 @@ class UserConfigManager(object):
|
||||
|
||||
if "ip" in data_dic.keys() and "port" in data_dic.keys():
|
||||
remote_ip = data_dic.get("ip", "")
|
||||
remote_adb_port = data_dic.get("port", "")
|
||||
remote_port = data_dic.get("port", "")
|
||||
else:
|
||||
remote_ip = ""
|
||||
remote_adb_port = ""
|
||||
remote_port = ""
|
||||
|
||||
if (not remote_ip) or (not remote_adb_port):
|
||||
if (not remote_ip) or (not remote_port):
|
||||
remote_ip = ""
|
||||
remote_adb_port = ""
|
||||
|
||||
remote_port = ""
|
||||
if remote_ip == get_local_ip():
|
||||
remote_ip = "127.0.0.1"
|
||||
remote_dic["ip"] = remote_ip
|
||||
remote_dic["port"] = remote_adb_port
|
||||
remote_dic["port"] = remote_port
|
||||
return remote_dic
|
||||
|
||||
def get_testcases_dir_config(self):
|
||||
@ -161,7 +162,7 @@ class UserConfigManager(object):
|
||||
devices = []
|
||||
|
||||
for node in self.config_content.findall(target_name):
|
||||
if node.attrib["type"] != "com":
|
||||
if node.attrib["type"] != "com" and node.attrib["type"] != "agent":
|
||||
continue
|
||||
|
||||
device = [node.attrib]
|
||||
@ -172,6 +173,8 @@ class UserConfigManager(object):
|
||||
if sub.text is not None and sub.tag != "serial":
|
||||
data_dic[sub.tag] = sub.text
|
||||
if data_dic:
|
||||
if data_dic.get("ip", "") == get_local_ip():
|
||||
data_dic["ip"] = "127.0.0.1"
|
||||
device.append(data_dic)
|
||||
devices.append(device)
|
||||
continue
|
||||
@ -189,18 +192,21 @@ class UserConfigManager(object):
|
||||
return devices
|
||||
|
||||
def get_device(self, target_name):
|
||||
data_dic = {}
|
||||
|
||||
for node in self.config_content.findall(target_name):
|
||||
if node.attrib["type"] != "usb-hdc":
|
||||
data_dic = {}
|
||||
if node.attrib["type"] != "usb-hdc" and \
|
||||
node.attrib["type"] != UsbConst.connector_type:
|
||||
continue
|
||||
data_dic["usb_type"] = node.attrib["type"]
|
||||
for sub in node:
|
||||
if sub.text is None:
|
||||
data_dic[sub.tag] = ""
|
||||
else:
|
||||
data_dic[sub.tag] = sub.text
|
||||
break
|
||||
return data_dic
|
||||
if data_dic.get("ip", "") == get_local_ip():
|
||||
data_dic["ip"] = "127.0.0.1"
|
||||
return data_dic
|
||||
return None
|
||||
|
||||
def get_testcases_dir(self):
|
||||
from xdevice import Variables
|
||||
@ -226,3 +232,15 @@ class UserConfigManager(object):
|
||||
|
||||
return os.path.abspath(
|
||||
os.path.join(Variables.exec_dir, "resource"))
|
||||
|
||||
def get_log_level(self):
|
||||
data_dic = {}
|
||||
node = self.config_content.find("loglevel")
|
||||
if node is not None:
|
||||
if node.find("console") is None and node.find("file") is None:
|
||||
# neither loglevel/console nor loglevel/file exists
|
||||
data_dic.update({"console": str(node.text).strip()})
|
||||
else:
|
||||
for child in node:
|
||||
data_dic.update({child.tag: str(child.text).strip()})
|
||||
return data_dic
|
||||
|
@ -2,7 +2,7 @@
|
||||
# coding=utf-8
|
||||
|
||||
#
|
||||
# Copyright (c) 2020 Huawei Device Co., Ltd.
|
||||
# Copyright (c) 2020-2021 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
|
||||
@ -110,9 +110,7 @@ class ResourceManager(object):
|
||||
src = os.path.join(resource_dir, push_value[0:pos].strip())
|
||||
dst = push_value[pos + len(find_key):len(push_value)].strip()
|
||||
|
||||
LOG.info("create_dir: dst = %s" % (dst))
|
||||
device.execute_shell_command("mkdir -p %s" % dst)
|
||||
LOG.info("push_file: src = %s, dst = %s" % (src, dst))
|
||||
device.push_file(src, dst)
|
||||
elif item["name"] == "pull":
|
||||
push_value = item["value"]
|
||||
@ -122,16 +120,13 @@ class ResourceManager(object):
|
||||
src = os.path.join(resource_dir, push_value[0:pos].strip())
|
||||
dst = push_value[pos + len(find_key):len(push_value)].strip()
|
||||
|
||||
LOG.info("pull_file: src = %s, dst = %s" % (src, dst))
|
||||
device.pull_file(src, dst)
|
||||
elif item["name"] == "shell":
|
||||
command = item["value"].strip()
|
||||
LOG.info("shell = %s", command)
|
||||
device.execute_shell_command(command)
|
||||
else:
|
||||
command = "".join((item["name"], " ", item["value"]))
|
||||
command = command.strip()
|
||||
LOG.info("others = %s", command)
|
||||
device.execute_command(command)
|
||||
|
||||
@staticmethod
|
||||
|
@ -2,7 +2,7 @@
|
||||
# coding=utf-8
|
||||
|
||||
#
|
||||
# Copyright (c) 2020 Huawei Device Co., Ltd.
|
||||
# Copyright (c) 2020-2021 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
|
||||
@ -22,7 +22,8 @@ __all__ = ["DeviceOsType", "ProductForm", "TestType", "TestExecType",
|
||||
"DeviceTestType", "HostTestType", "HostDrivenTestType",
|
||||
"SchedulerType", "ListenerType", "ToolCommandType",
|
||||
"TEST_DRIVER_SET", "LogType", "ParserType", "CKit", "ComType",
|
||||
"DeviceLabelType", "DeviceLiteKernel", "GTestConst", "ManagerType"]
|
||||
"DeviceLabelType", "DeviceLiteKernel", "GTestConst", "ManagerType",
|
||||
"ModeType", "ConfigConst"]
|
||||
|
||||
|
||||
@dataclass
|
||||
@ -40,9 +41,10 @@ class ProductForm(object):
|
||||
ProductForm enumeration
|
||||
"""
|
||||
phone = "phone"
|
||||
car = "car"
|
||||
car = "ivi"
|
||||
television = "tv"
|
||||
watch = "watch"
|
||||
tablet = 'tablet'
|
||||
|
||||
|
||||
@dataclass
|
||||
@ -57,6 +59,7 @@ class TestType(object):
|
||||
sec = "security"
|
||||
reli = "reliability"
|
||||
dst = "distributedtest"
|
||||
benchmark = "benchmark"
|
||||
all = "ALL"
|
||||
|
||||
|
||||
@ -76,8 +79,9 @@ class DeviceLabelType(object):
|
||||
"""
|
||||
wifiiot = "wifiiot"
|
||||
ipcamera = "ipcamera"
|
||||
watch = "watch"
|
||||
watch_gt = "watchGT"
|
||||
phone = "phone"
|
||||
watch = "watch"
|
||||
|
||||
|
||||
@dataclass
|
||||
@ -124,10 +128,12 @@ class DeviceTestType(object):
|
||||
hap_test = "HapTest"
|
||||
junit_test = "JUnitTest"
|
||||
jsunit_test = "JSUnitTest"
|
||||
jsunit_test_lite = "JSUnitTestLite"
|
||||
ctest_lite = "CTestLite"
|
||||
cpp_test_lite = "CppTestLite"
|
||||
lite_cpp_test = "LiteUnitTest"
|
||||
open_source_test = "OpenSourceTest"
|
||||
build_only_test = "BuildOnlyTestLite"
|
||||
|
||||
|
||||
@dataclass
|
||||
@ -190,6 +196,8 @@ class ParserType:
|
||||
cpp_test_lite = "CppTestLite"
|
||||
cpp_test_list_lite = "CppTestListLite"
|
||||
open_source_test = "OpenSourceTest"
|
||||
build_only_test = "BuildOnlyTestLite"
|
||||
jsuit_test_lite = "JSUnitTestLite"
|
||||
|
||||
|
||||
@dataclass
|
||||
@ -210,7 +218,7 @@ class ToolCommandType(object):
|
||||
@dataclass
|
||||
class CKit:
|
||||
push = "PushKit"
|
||||
install = "ApkInstallKit"
|
||||
liteinstall = "LiteAppInstallKit"
|
||||
command = "CommandKit"
|
||||
config = "ConfigKit"
|
||||
wifi = "WIFIKit"
|
||||
@ -222,9 +230,55 @@ class CKit:
|
||||
liteuikit = 'LiteUiKit'
|
||||
rootfs = "RootFsKit"
|
||||
query = "QueryKit"
|
||||
liteshell = "LiteShellKit"
|
||||
app_install = "AppInstallKit"
|
||||
|
||||
|
||||
@dataclass
|
||||
class GTestConst(object):
|
||||
exec_para_filter = "--gtest_filter"
|
||||
exec_para_level = "--gtest_testsize"
|
||||
|
||||
|
||||
@dataclass
|
||||
class ModeType(object):
|
||||
decc = "decc"
|
||||
factory = "factory"
|
||||
developer = "developer"
|
||||
|
||||
|
||||
@dataclass
|
||||
class ConfigConst(object):
|
||||
action = "action"
|
||||
task = "task"
|
||||
testlist = "testlist"
|
||||
testfile = "testfile"
|
||||
testcase = "testcase"
|
||||
device_sn = "device_sn"
|
||||
report_path = "report_path"
|
||||
resource_path = "resource_path"
|
||||
testcases_path = "testcases_path"
|
||||
testargs = "testargs"
|
||||
pass_through = "pass_through"
|
||||
test_environment = "test_environment"
|
||||
exectype = "exectype"
|
||||
testtype = "testtype"
|
||||
testdriver = "testdriver"
|
||||
retry = "retry"
|
||||
session = "session"
|
||||
dry_run = "dry_run"
|
||||
reboot_per_module = "reboot_per_module"
|
||||
check_device = "check_device"
|
||||
configfile = "config"
|
||||
repeat = "repeat"
|
||||
|
||||
# Runtime Constant
|
||||
history_report_path = "history_report_path"
|
||||
product_info = "product_info"
|
||||
task_state = "task_state"
|
||||
recover_state = "recover_state"
|
||||
need_kit_setup = "need_kit_setup"
|
||||
task_kits = "task_kits"
|
||||
module_kits = "module_kits"
|
||||
spt = "spt"
|
||||
version = "version"
|
||||
|
0
src/xdevice/_core/driver/__init__.py
Executable file → Normal file
0
src/xdevice/_core/driver/__init__.py
Executable file → Normal file
@ -2,7 +2,7 @@
|
||||
# coding=utf-8
|
||||
|
||||
#
|
||||
# Copyright (c) 2020 Huawei Device Co., Ltd.
|
||||
# Copyright (c) 2020-2021 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
|
||||
@ -21,9 +21,12 @@ import os
|
||||
import sys
|
||||
|
||||
from _core.constants import HostDrivenTestType
|
||||
from _core.constants import TestExecType
|
||||
from _core.constants import ModeType
|
||||
from _core.constants import DeviceLabelType
|
||||
from _core.driver.drivers_lite import init_remote_server
|
||||
from _core.exception import DeviceError
|
||||
from _core.exception import LiteDeviceError
|
||||
from _core.exception import ParamError
|
||||
from _core.exception import ReportException
|
||||
from _core.exception import ExecuteTerminate
|
||||
@ -32,64 +35,18 @@ from _core.logger import platform_logger
|
||||
from _core.plugin import Plugin
|
||||
from _core.testkit.json_parser import JsonParser
|
||||
from _core.utils import get_config_value
|
||||
from _core.utils import do_module_kit_setup
|
||||
from _core.utils import do_module_kit_teardown
|
||||
from _core.utils import get_filename_extension
|
||||
from _core.utils import get_file_absolute_path
|
||||
from _core.utils import get_kit_instances
|
||||
from _core.utils import check_result_report
|
||||
from _core.utils import check_mode
|
||||
from _core.report.suite_reporter import SuiteReporter
|
||||
|
||||
LOG = platform_logger("DeviceTest")
|
||||
|
||||
|
||||
def start_task(test_file, configs, device, logger):
|
||||
from xdevice import Variables
|
||||
# insert& devicetest path for loading devicetest module
|
||||
devicetest_module = os.path.join(Variables.modules_dir, "_devicetest")
|
||||
if os.path.exists(devicetest_module):
|
||||
sys.path.insert(1, devicetest_module)
|
||||
|
||||
if configs["testcases_path"]:
|
||||
sys.path.insert(1, configs["testcases_path"])
|
||||
|
||||
from _devicetest.devicetest.main import DeviceTest
|
||||
device_test = DeviceTest(test_list=test_file, configs=configs,
|
||||
devices=[device], log=logger)
|
||||
device_test.run()
|
||||
|
||||
|
||||
def set_up_env(request, source):
|
||||
if not source.endswith(".json"):
|
||||
source = "%s.json" % source
|
||||
json_config = JsonParser(source)
|
||||
test_args = get_config_value('xml-output', json_config.get_driver())
|
||||
|
||||
kits = get_kit_instances(json_config, request.config.resource_path,
|
||||
request.config.testcases_path)
|
||||
kits_copy = copy.deepcopy(kits)
|
||||
from xdevice import Scheduler
|
||||
for kit in kits:
|
||||
if not Scheduler.is_execute:
|
||||
raise ExecuteTerminate()
|
||||
kit.__setup__(request.config.device, request=request)
|
||||
return test_args, kits, kits_copy
|
||||
|
||||
|
||||
def get_file_list(module_path):
|
||||
file_list = []
|
||||
|
||||
if not file_list:
|
||||
for test_file in os.listdir(module_path):
|
||||
if test_file.endswith(".py") or test_file.endswith(".pyd"):
|
||||
file_list.append(os.path.join(
|
||||
module_path, test_file))
|
||||
return file_list
|
||||
else:
|
||||
filter_file_list = []
|
||||
for test_file in file_list:
|
||||
if (test_file.endswith(".pyd") or test_file.endswith(".py")) and \
|
||||
os.path.exists(os.path.join(module_path, test_file)):
|
||||
filter_file_list.append(os.path.join(
|
||||
module_path, test_file))
|
||||
|
||||
return filter_file_list
|
||||
PY_SUFFIX = ".py"
|
||||
PYD_SUFFIX = ".pyd"
|
||||
|
||||
|
||||
@Plugin(type=Plugin.DRIVER, id=HostDrivenTestType.device_test)
|
||||
@ -111,125 +68,197 @@ class DeviceTestDriver(IDriver):
|
||||
pass
|
||||
|
||||
def __check_config__(self, config=None):
|
||||
del config
|
||||
self.py_file = ""
|
||||
pass
|
||||
|
||||
def __init_nfs_server__(self, request=None):
|
||||
return init_remote_server(self, request)
|
||||
|
||||
def __execute__(self, request):
|
||||
kits, source = self._parse_request(request)
|
||||
configs = dict()
|
||||
configs["testargs"] = self.config.testargs or ""
|
||||
configs["testcases_path"] = self.config.testcases_path or ""
|
||||
configs["request"] = request
|
||||
|
||||
try:
|
||||
if self.config.device and self.config.device.label == \
|
||||
DeviceLabelType.ipcamera:
|
||||
self.__init_nfs_server__(request=request)
|
||||
_, kits, kits_copy = set_up_env(request, source)
|
||||
configs["linux_host"] = self.linux_host
|
||||
configs["linux_directory"] = self.linux_directory
|
||||
configs["kits"] = kits_copy
|
||||
self.run_module(request, configs, source)
|
||||
elif self.config.device and self.config.device.label == \
|
||||
DeviceLabelType.watch:
|
||||
self.run_module(request, configs, source)
|
||||
elif self.config.device and self.config.device.label == \
|
||||
DeviceLabelType.phone:
|
||||
self.run_module(request, configs, source)
|
||||
# set self.config
|
||||
self.config = request.config
|
||||
self.config.tmp_id = str(request.uuid)
|
||||
self.config.tmp_folder = os.path.join(self.config.report_path,
|
||||
"temp")
|
||||
self.config.devices = request.get_devices()
|
||||
if request.get("exectype") == TestExecType.device_test and \
|
||||
not self.config.devices:
|
||||
LOG.error("no device", error_no="00104")
|
||||
raise ParamError("Load Error[00104]", error_no="00104")
|
||||
|
||||
# get source, json config and kits
|
||||
if request.get_config_file():
|
||||
source = request.get_config_file()
|
||||
LOG.debug("Test config file path: %s" % source)
|
||||
else:
|
||||
source = request.get_source_string()
|
||||
LOG.debug("Test String: %s" % source)
|
||||
|
||||
if not source:
|
||||
LOG.error("no config file found for '%s'" %
|
||||
request.get_source_file(), error_no="00102")
|
||||
raise ParamError("Load Error(00102)", error_no="00102")
|
||||
|
||||
json_config = JsonParser(source)
|
||||
kits = get_kit_instances(json_config, request.config.resource_path,
|
||||
request.config.testcases_path)
|
||||
|
||||
# create tmp folder
|
||||
test_name = request.get_module_name()
|
||||
tmp_sub_folder = self._create_tmp_folder(request)
|
||||
self.result = "%s.xml" % os.path.join(tmp_sub_folder, test_name)
|
||||
|
||||
# set configs keys
|
||||
configs = self._set_configs(json_config, kits, request,
|
||||
tmp_sub_folder)
|
||||
|
||||
# get test list
|
||||
test_list = self._get_test_list(json_config, request, source)
|
||||
if not test_list:
|
||||
raise ParamError("no test list to run")
|
||||
|
||||
self._run_devicetest(configs, test_list)
|
||||
except (ReportException, ModuleNotFoundError, ExecuteTerminate,
|
||||
SyntaxError, ValueError, AttributeError, TypeError,
|
||||
KeyboardInterrupt, ParamError) as exception:
|
||||
LOG.exception(exception)
|
||||
KeyboardInterrupt, ParamError, DeviceError, LiteDeviceError) \
|
||||
as exception:
|
||||
error_no = getattr(exception, "error_no", "00000")
|
||||
LOG.exception(exception, exc_info=False, error_no=error_no)
|
||||
self.error_message = exception
|
||||
|
||||
finally:
|
||||
self._handle_finally(kits, request)
|
||||
self._handle_finally(request)
|
||||
|
||||
def _handle_finally(self, kits, request):
|
||||
def _get_test_list(self, json_config, request, source):
|
||||
test_list = get_config_value('py_file', json_config.get_driver(),
|
||||
is_list=True)
|
||||
if str(request.root.source.source_file).endswith(PYD_SUFFIX) or \
|
||||
str(request.root.source.source_file).endswith(PY_SUFFIX):
|
||||
test_list = [request.root.source.source_file]
|
||||
|
||||
if not test_list and os.path.exists(source):
|
||||
test_list = _get_dict_test_list(os.path.dirname(source))
|
||||
|
||||
# check test list
|
||||
testcase = request.get("testcase")
|
||||
testcase_list = []
|
||||
if testcase:
|
||||
testcase_list = str(testcase).split(";")
|
||||
|
||||
checked_test_list = []
|
||||
for index, test in enumerate(test_list):
|
||||
if not os.path.exists(test):
|
||||
try:
|
||||
absolute_file = get_file_absolute_path(test, [
|
||||
self.config.resource_path, self.config.testcases_path])
|
||||
except ParamError as error:
|
||||
LOG.error(error, error_no=error.error_no)
|
||||
continue
|
||||
else:
|
||||
absolute_file = test
|
||||
|
||||
file_name = get_filename_extension(absolute_file)[0]
|
||||
if not testcase_list or file_name in testcase_list:
|
||||
checked_test_list.append(absolute_file)
|
||||
else:
|
||||
LOG.info("test '%s' is ignored", absolute_file)
|
||||
if checked_test_list:
|
||||
LOG.info("test list: {}".format(checked_test_list))
|
||||
else:
|
||||
LOG.error("no test list found", error_no="00109")
|
||||
raise ParamError("Load Error(00109)", error_no="00109")
|
||||
return checked_test_list
|
||||
|
||||
def _set_configs(self, json_config, kits, request, tmp_sub_folder):
|
||||
configs = dict()
|
||||
configs["testargs"] = self.config.testargs or {}
|
||||
configs["testcases_path"] = self.config.testcases_path or ""
|
||||
configs["request"] = request
|
||||
configs["test_name"] = request.get_module_name()
|
||||
configs["report_path"] = tmp_sub_folder
|
||||
configs["execute"] = get_config_value(
|
||||
'execute', json_config.get_driver(), False)
|
||||
|
||||
for device in self.config.devices:
|
||||
do_module_kit_setup(request, kits)
|
||||
if device.label == DeviceLabelType.ipcamera:
|
||||
# add extra keys to configs for ipcamera device
|
||||
self.__init_nfs_server__(request=request)
|
||||
configs["linux_host"] = self.linux_host
|
||||
configs["linux_directory"] = self.linux_directory
|
||||
configs["kits"] = kits
|
||||
|
||||
return configs
|
||||
|
||||
def _handle_finally(self, request):
|
||||
from xdevice import Scheduler
|
||||
for kit in kits:
|
||||
kit.__teardown__(request.config.device)
|
||||
if self.config.device.label == \
|
||||
DeviceLabelType.ipcamera or self.config.device.label == \
|
||||
DeviceLabelType.watch:
|
||||
self.config.device.close()
|
||||
|
||||
# do kit teardown
|
||||
do_module_kit_teardown(request)
|
||||
|
||||
# close device connect
|
||||
for device in self.config.devices:
|
||||
if device.label == DeviceLabelType.ipcamera or device.label == \
|
||||
DeviceLabelType.watch_gt:
|
||||
device.close()
|
||||
if device.label == DeviceLabelType.phone:
|
||||
device.close()
|
||||
|
||||
# check result report
|
||||
report_name = request.root.source.test_name if \
|
||||
not request.root.source.test_name.startswith("{") \
|
||||
else "report"
|
||||
if Scheduler.mode != "decc":
|
||||
module_name = request.get_module_name()
|
||||
if Scheduler.mode != ModeType.decc:
|
||||
self.result = check_result_report(
|
||||
request.config.report_path, self.result, self.error_message,
|
||||
report_name)
|
||||
report_name, module_name)
|
||||
else:
|
||||
tmp_list = copy.copy(SuiteReporter.get_report_result())
|
||||
if os.path.dirname(self.result) not in \
|
||||
[report_path for report_path, _ in tmp_list]:
|
||||
if self.result not in [report_path for report_path, _ in tmp_list]:
|
||||
if not self.error_message:
|
||||
self.error_message = "An unknown exception occurred " \
|
||||
"during the execution case"
|
||||
self.error_message = "Case not execute[01205]"
|
||||
self.result = check_result_report(
|
||||
request.config.report_path, self.result,
|
||||
self.error_message, report_name)
|
||||
self.error_message, report_name, module_name)
|
||||
|
||||
def _parse_request(self, request):
|
||||
from xdevice import Variables
|
||||
kits = []
|
||||
self.config = request.config
|
||||
self.config.device = request.config.environment.devices[0]
|
||||
if not self.config.device:
|
||||
raise DeviceError("no device..........................")
|
||||
current_dir = request.config.resource_path if \
|
||||
request.config.resource_path else Variables.exec_dir
|
||||
if request.root.source.source_file.strip():
|
||||
source = os.path.join(current_dir,
|
||||
request.root.source.source_file.strip())
|
||||
LOG.debug("Testfile FilePath: %s" % source)
|
||||
else:
|
||||
source = request.root.source.source_string.strip()
|
||||
self.config.tmp_folder = os.path.join(self.config.report_path,
|
||||
"temp")
|
||||
self.config.tmp_id = str(request.uuid)
|
||||
return kits, source
|
||||
|
||||
def run_module(self, request, configs, source):
|
||||
json_config = JsonParser(source)
|
||||
def _create_tmp_folder(self, request):
|
||||
if request.root.source.source_file.strip():
|
||||
folder_name = "task_%s_%s" % (self.config.tmp_id,
|
||||
request.root.source.test_name)
|
||||
|
||||
tmp_sub_folder = os.path.join(self.config.tmp_folder, folder_name)
|
||||
os.makedirs(tmp_sub_folder, exist_ok=True)
|
||||
|
||||
configs["report_path"] = tmp_sub_folder
|
||||
|
||||
self.result = "%s.xml" % os.path.join(tmp_sub_folder, "report")
|
||||
|
||||
module_path = os.path.dirname(source)
|
||||
file_list = get_config_value('py_file', json_config.get_driver(),
|
||||
is_list=True)
|
||||
if not file_list:
|
||||
file_list = get_file_list(module_path)
|
||||
else:
|
||||
folder_name = "task_%s_report" % self.config.tmp_id
|
||||
tmp_sub_folder = os.path.join(self.config.tmp_folder,
|
||||
folder_name)
|
||||
self.result = "%s.xml" % os.path.join(tmp_sub_folder, "report")
|
||||
|
||||
json_config = JsonParser(source)
|
||||
file_list = get_config_value('py_file', json_config.get_driver(),
|
||||
is_list=True)
|
||||
configs["test_name"] = request.root.source.test_name
|
||||
configs["execute"] = get_config_value('execute',
|
||||
json_config.get_driver(), False)
|
||||
|
||||
tmp_sub_folder = os.path.join(self.config.tmp_folder, folder_name)
|
||||
os.makedirs(tmp_sub_folder, exist_ok=True)
|
||||
self._run_devicetest(configs, file_list)
|
||||
return tmp_sub_folder
|
||||
|
||||
def _run_devicetest(self, configs, test_file):
|
||||
start_task(test_file, configs, self.config.device, LOG)
|
||||
def _run_devicetest(self, configs, test_list):
|
||||
from xdevice import Variables
|
||||
|
||||
# insert paths for loading _devicetest module and testcases
|
||||
devicetest_module = os.path.join(Variables.modules_dir, "_devicetest")
|
||||
if os.path.exists(devicetest_module):
|
||||
sys.path.insert(1, devicetest_module)
|
||||
if configs["testcases_path"]:
|
||||
sys.path.insert(1, configs["testcases_path"])
|
||||
|
||||
# run devicetest
|
||||
from _devicetest.devicetest.main import DeviceTest
|
||||
device_test = DeviceTest(test_list=test_list, configs=configs,
|
||||
devices=self.config.devices, log=LOG)
|
||||
device_test.run()
|
||||
|
||||
def __result__(self):
|
||||
if check_mode(ModeType.decc):
|
||||
return self.result
|
||||
return self.result if os.path.exists(self.result) else ""
|
||||
|
||||
|
||||
def _get_dict_test_list(module_path):
|
||||
test_list = []
|
||||
for root, _, files in os.walk(module_path):
|
||||
for _file in files:
|
||||
if _file.endswith(".py") or _file.endswith(".pyd"):
|
||||
test_list.append(os.path.join(root, _file))
|
||||
return test_list
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -2,7 +2,7 @@
|
||||
# coding=utf-8
|
||||
|
||||
#
|
||||
# Copyright (c) 2020 Huawei Device Co., Ltd.
|
||||
# Copyright (c) 2020-2021 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
|
||||
@ -19,6 +19,7 @@
|
||||
import copy
|
||||
import re
|
||||
import time
|
||||
import datetime
|
||||
from queue import Queue
|
||||
|
||||
from _core.interface import IParser
|
||||
@ -52,8 +53,7 @@ _CTEST_SUITE_TIME_RUN_TAG = "Run test suite "
|
||||
_CTEST_SETUP_TAG = "setup"
|
||||
_CTEST_RUN_TAG = "-----------------------"
|
||||
|
||||
_TEST_PASSED_LOWER = "test pass"
|
||||
_TESTS_PASSED_LOWER = "tests pass"
|
||||
_TEST_PASSED_LOWER = "pass"
|
||||
|
||||
_COMPILE_PASSED = "compile PASSED"
|
||||
_COMPILE_PARA = r"(.* compile .*)"
|
||||
@ -62,6 +62,14 @@ _PRODUCT_PARA = r"(.*The .* is .*)"
|
||||
_PRODUCT_PARA_START = r"To Obtain Product Params Start"
|
||||
_PRODUCT_PARA_END = r"To Obtain Product Params End"
|
||||
|
||||
_START_JSUNIT_RUN_MARKER = "[start] start run suites"
|
||||
_START_JSUNIT_SUITE_RUN_MARKER = "[suite start]"
|
||||
_START_JSUNIT_SUITE_END_MARKER = "[suite end]"
|
||||
_END_JSUNIT_RUN_MARKER = "[end] run suites end"
|
||||
_PASS_JSUNIT_MARKER = "[%s]" % "pass"
|
||||
_FAIL_JSUNIT_MARKER = "[fail]"
|
||||
_ACE_LOG_MARKER = "[Console Info]"
|
||||
|
||||
LOG = platform_logger("ParserLite")
|
||||
|
||||
|
||||
@ -71,7 +79,7 @@ class CppTestParserLite(IParser):
|
||||
self.state_machine = StateRecorder()
|
||||
self.suite_name = ""
|
||||
self.listeners = []
|
||||
self.product_params = {}
|
||||
self.product_info = {}
|
||||
self.is_params = False
|
||||
|
||||
def get_suite_name(self):
|
||||
@ -103,24 +111,24 @@ class CppTestParserLite(IParser):
|
||||
suites = copy.copy(suites_result)
|
||||
listener.__ended__(LifeCycle.TestSuites, test_result=suites,
|
||||
suites_name=suites.suites_name,
|
||||
product_params=suites.product_params)
|
||||
product_info=suites.product_info)
|
||||
self.state_machine.current_suites = None
|
||||
|
||||
@staticmethod
|
||||
def _is_test_run(line):
|
||||
return True if line.startswith(_TEST_RUN_TAG) else False
|
||||
return True if _TEST_RUN_TAG in line else False
|
||||
|
||||
@staticmethod
|
||||
def _is_test_start_run(line):
|
||||
return True if line.startswith(_TEST_START_RUN_TAG) else False
|
||||
return True if _TEST_START_RUN_TAG in line else False
|
||||
|
||||
@staticmethod
|
||||
def _is_informational_start(line):
|
||||
return True if line.startswith(_INFORMATIONAL_START) else False
|
||||
return True if _INFORMATIONAL_START in line else False
|
||||
|
||||
@staticmethod
|
||||
def _is_test_start(line):
|
||||
return True if line.startswith(_TEST_START_TAG) else False
|
||||
return True if _TEST_START_TAG in line else False
|
||||
|
||||
def _process_informational_line(self, line):
|
||||
pattern = r"(.*) (\(\d+ ms total\))"
|
||||
@ -142,18 +150,18 @@ class CppTestParserLite(IParser):
|
||||
elif _PRODUCT_PARA_END in line:
|
||||
self.is_params = False
|
||||
if re.match(_PRODUCT_PARA, line) and self.is_params:
|
||||
handle_product_params(line, self.product_params)
|
||||
handle_product_info(line, self.product_info)
|
||||
|
||||
if self.state_machine.suites_is_started() or self._is_test_run(line):
|
||||
if self._is_test_start_run(line):
|
||||
message = line[len(_TEST_RUN_TAG):].strip()
|
||||
self.handle_suites_started_tag(message)
|
||||
self.handle_suites_started_tag(line)
|
||||
elif self._is_informational_start(line):
|
||||
self._process_informational_line(line)
|
||||
elif self._is_test_run(line):
|
||||
self._process_test_run_line(line)
|
||||
elif self._is_test_start(line):
|
||||
message = line[len(_TEST_START_TAG):].strip()
|
||||
message = line[line.index(_TEST_START_TAG) +
|
||||
len(_TEST_START_TAG):].strip()
|
||||
self.handle_test_started_tag(message)
|
||||
else:
|
||||
self.process_test(line)
|
||||
@ -165,7 +173,7 @@ class CppTestParserLite(IParser):
|
||||
if not self.state_machine.test_is_running():
|
||||
LOG.error(
|
||||
"Found {} without {} before, wrong GTest log format".
|
||||
format(line, _TEST_START_TAG))
|
||||
format(line, _TEST_START_TAG), error_no="00405")
|
||||
return
|
||||
self.handle_test_ended_tag(message, ResultCode.SKIPPED)
|
||||
elif _TEST_OK_TAG in line:
|
||||
@ -174,7 +182,7 @@ class CppTestParserLite(IParser):
|
||||
if not self.state_machine.test_is_running():
|
||||
LOG.error(
|
||||
"Found {} without {} before, wrong GTest log format".
|
||||
format(line, _TEST_START_TAG))
|
||||
format(line, _TEST_START_TAG), error_no="00405")
|
||||
return
|
||||
self.handle_test_ended_tag(message, ResultCode.PASSED)
|
||||
elif _ALT_OK_TAG in line:
|
||||
@ -210,13 +218,13 @@ class CppTestParserLite(IParser):
|
||||
@classmethod
|
||||
def parse_test_description(cls, message):
|
||||
run_time = 0
|
||||
matcher = re.match(r'(.*) \((\d+) ms\)', message)
|
||||
matcher = re.match(r'(.*) \((\d+) ms\)(.*)', message)
|
||||
if matcher:
|
||||
test_class, test_name = matcher.group(1).rsplit(".", 1)
|
||||
run_time = int(matcher.group(2))
|
||||
else:
|
||||
test_class, test_name = message.rsplit(".", 1)
|
||||
return test_class, test_name, run_time
|
||||
return test_class.split(" ")[-1], test_name.split(" ")[0], run_time
|
||||
|
||||
def handle_test_ended_tag(self, message, test_status):
|
||||
test_class, test_name, run_time = self.parse_test_description(
|
||||
@ -226,18 +234,21 @@ class CppTestParserLite(IParser):
|
||||
test_result.code = test_status.value
|
||||
if not test_result.is_running():
|
||||
LOG.error(
|
||||
"Test has no start tag when trying to end test: %s", message)
|
||||
"Test has no start tag when trying to end test: %s", message,
|
||||
error_no="00405")
|
||||
return
|
||||
found_unexpected_test = False
|
||||
if test_result.test_class != test_class:
|
||||
LOG.error(
|
||||
"Expected class: {} but got:{} ".format(test_result.test_class,
|
||||
test_class))
|
||||
test_class),
|
||||
error_no="00405")
|
||||
found_unexpected_test = True
|
||||
if test_result.test_name != test_name:
|
||||
LOG.error(
|
||||
"Expected test: {} but got: {}".format(test_result.test_name,
|
||||
test_name))
|
||||
test_name),
|
||||
error_no="00405")
|
||||
found_unexpected_test = True
|
||||
test_result.current = self.state_machine.running_test_index + 1
|
||||
self.state_machine.test().is_completed = True
|
||||
@ -255,13 +266,13 @@ class CppTestParserLite(IParser):
|
||||
|
||||
def handle_suites_started_tag(self, message):
|
||||
self.state_machine.get_suites(reset=True)
|
||||
matcher = re.match(r'Running (\d+) test[s]? from .*', message)
|
||||
matcher = re.match(r'.* Running (\d+) test[s]? from .*', message)
|
||||
expected_test_num = int(matcher.group(1)) if matcher else -1
|
||||
if expected_test_num >= 0:
|
||||
test_suites = self.state_machine.get_suites()
|
||||
test_suites.suites_name = self.get_suite_name()
|
||||
test_suites.test_num = expected_test_num
|
||||
test_suites.product_params = self.product_params
|
||||
test_suites.product_info = self.product_info
|
||||
for listener in self.get_listeners():
|
||||
suite_report = copy.copy(test_suites)
|
||||
listener.__started__(LifeCycle.TestSuites, suite_report)
|
||||
@ -299,7 +310,7 @@ class CppTestParserLite(IParser):
|
||||
copy_suites = copy.copy(suites)
|
||||
listener.__ended__(LifeCycle.TestSuites, test_result=copy_suites,
|
||||
suites_name=suites.suites_name,
|
||||
product_params=suites.product_params)
|
||||
product_info=suites.product_info)
|
||||
|
||||
def append_test_output(self, message):
|
||||
if self.state_machine.test().stacktrace:
|
||||
@ -409,7 +420,7 @@ class CppTestListParserLite(IParser):
|
||||
if not self.last_test_class_name:
|
||||
LOG.error(
|
||||
"parsed new test case name %s but no test class"
|
||||
" name has been set" % line)
|
||||
" name has been set" % line, error_no="00405")
|
||||
else:
|
||||
test = TestDescription(self.last_test_class_name,
|
||||
self.method_result.group(1))
|
||||
@ -521,7 +532,7 @@ class CTestParser(IParser):
|
||||
self.state_machine = StateRecorder()
|
||||
self.suites_name = ""
|
||||
self.listeners = []
|
||||
self.product_params = {}
|
||||
self.product_info = {}
|
||||
self.is_params = False
|
||||
|
||||
def get_suite_name(self):
|
||||
@ -543,7 +554,7 @@ class CTestParser(IParser):
|
||||
for listener in self.get_listeners():
|
||||
listener.__ended__(LifeCycle.TestSuites, test_result=suites,
|
||||
suites_name=suites.suites_name,
|
||||
product_params=suites.product_params)
|
||||
product_info=suites.product_info)
|
||||
self.state_machine.current_suites = None
|
||||
|
||||
@staticmethod
|
||||
@ -556,7 +567,7 @@ class CTestParser(IParser):
|
||||
|
||||
@staticmethod
|
||||
def _is_ctest_run(line):
|
||||
return True if line.endswith(_CTEST_RUN_TAG) else False
|
||||
return re.match(r".*(\d+ Tests \d+ Failures \d+ Ignored).*", line)
|
||||
|
||||
def _is_ctest_suite_test_run(self, line):
|
||||
return re.match("{}{}".format(self.pattern, _CTEST_SUITE_TEST_RUN_TAG),
|
||||
@ -578,13 +589,13 @@ class CTestParser(IParser):
|
||||
r"(.*" + "\\.c:" + "\\d+:.*:(PASS|FAIL|OK|IGNORE"")\\.*)",
|
||||
line.strip())
|
||||
|
||||
@staticmethod
|
||||
def _is_result_line(line):
|
||||
return line.find("PASS") != -1 or line.find("FAIL") != -1 or line.find(
|
||||
"IGNORE") != -1
|
||||
|
||||
def parse(self, line):
|
||||
if _PRODUCT_PARA_START in line:
|
||||
self.is_params = True
|
||||
elif _PRODUCT_PARA_END in line:
|
||||
self.is_params = False
|
||||
if self.is_params and re.match(_PRODUCT_PARA, line):
|
||||
handle_product_params(line, self.product_params)
|
||||
self._parse_product_info(line)
|
||||
|
||||
if self.state_machine.suites_is_started() or \
|
||||
self._is_ctest_start_test_run(line):
|
||||
@ -594,20 +605,56 @@ class CTestParser(IParser):
|
||||
elif self._is_ctest_end_test_run(line):
|
||||
self.process_suites_ended_tag()
|
||||
elif self._is_ctest_run(line):
|
||||
self.handle_suite_ended_tag()
|
||||
self.handle_suite_ended_tag(line)
|
||||
elif self._is_ctest_suite_test_run(line) and \
|
||||
not self.state_machine.suite_is_running():
|
||||
self._process_ctest_suite_test_run_line(line)
|
||||
elif self.is_ctest_suite_time_run(line) and \
|
||||
not self.state_machine.suite_is_running():
|
||||
self.handle_suite_started_tag(line)
|
||||
elif line.find(":") != -1 and line.count(":") >= 3:
|
||||
if self._is_execute_result_line(line):
|
||||
self.handle_one_test_tag(line.strip())
|
||||
elif self._is_result_line(line) and \
|
||||
self.state_machine.suite_is_running():
|
||||
if line.find(":") != -1 and line.count(
|
||||
":") >= 3 and self._is_execute_result_line(line):
|
||||
self.handle_one_test_tag(line.strip(), False)
|
||||
else:
|
||||
self.handle_one_test_tag(line.strip(), True)
|
||||
except AttributeError:
|
||||
LOG.error("parsing log: %s failed" % (line.strip()))
|
||||
LOG.error("parsing log: %s failed" % (line.strip()),
|
||||
error_no="00405")
|
||||
self.last_line = line
|
||||
|
||||
def _parse_product_info(self, line):
|
||||
if _PRODUCT_PARA_START in line:
|
||||
self.is_params = True
|
||||
elif _PRODUCT_PARA_END in line:
|
||||
self.is_params = False
|
||||
if self.is_params and re.match(_PRODUCT_PARA, line):
|
||||
handle_product_info(line, self.product_info)
|
||||
|
||||
def parse_error_test_description(self, message):
|
||||
end_time = re.match(self.pattern, message).group().strip()
|
||||
start_time = re.match(self.pattern,
|
||||
self.last_line.strip()).group().strip()
|
||||
start_timestamp = int(time.mktime(
|
||||
time.strptime(start_time, "%Y-%m-%d %H:%M:%S.%f"))) * 1000 + int(
|
||||
start_time.split(".")[-1])
|
||||
end_timestamp = int(time.mktime(
|
||||
time.strptime(end_time, "%Y-%m-%d %H:%M:%S.%f"))) * 1000 + int(
|
||||
end_time.split(".")[-1])
|
||||
run_time = end_timestamp - start_timestamp
|
||||
status_dict = {"PASS": ResultCode.PASSED, "FAIL": ResultCode.FAILED,
|
||||
"IGNORE": ResultCode.SKIPPED}
|
||||
status = ""
|
||||
if message.find("PASS") != -1:
|
||||
status = "PASS"
|
||||
elif message.find("FAIL") != -1:
|
||||
status = "FAIL"
|
||||
elif message.find("IGNORE") != -1:
|
||||
status = "IGNORE"
|
||||
status = status_dict.get(status)
|
||||
return "", "", status, run_time
|
||||
|
||||
def parse_test_description(self, message):
|
||||
|
||||
test_class = message.split(".c:")[0].split(" ")[-1].split("/")[-1]
|
||||
@ -629,9 +676,13 @@ class CTestParser(IParser):
|
||||
status = status_dict.get(status)
|
||||
return test_class, test_name, status, run_time
|
||||
|
||||
def handle_one_test_tag(self, message):
|
||||
test_class, test_name, status, run_time = \
|
||||
self.parse_test_description(message)
|
||||
def handle_one_test_tag(self, message, is_error):
|
||||
if is_error:
|
||||
test_class, test_name, status, run_time = \
|
||||
self.parse_error_test_description(message)
|
||||
else:
|
||||
test_class, test_name, status, run_time = \
|
||||
self.parse_test_description(message)
|
||||
test_result = self.state_machine.test(reset=True)
|
||||
test_result.test_class = test_class
|
||||
test_result.test_name = test_name
|
||||
@ -660,7 +711,7 @@ class CTestParser(IParser):
|
||||
elif ResultCode.SKIPPED == status:
|
||||
for listener in self.get_listeners():
|
||||
result = copy.copy(test_result)
|
||||
listener.__skipped__(LifeCycle.TestCase, result)
|
||||
listener.__failed__(LifeCycle.TestCase, result)
|
||||
|
||||
self.state_machine.test().is_completed = True
|
||||
test_suite.test_num += 1
|
||||
@ -674,7 +725,7 @@ class CTestParser(IParser):
|
||||
self.state_machine.get_suites(reset=True)
|
||||
test_suites = self.state_machine.get_suites()
|
||||
test_suites.suites_name = self.suites_name
|
||||
test_suites.product_params = self.product_params
|
||||
test_suites.product_info = self.product_info
|
||||
test_suites.test_num = 0
|
||||
for listener in self.get_listeners():
|
||||
suite_report = copy.copy(test_suites)
|
||||
@ -692,7 +743,7 @@ class CTestParser(IParser):
|
||||
suite_report = copy.copy(test_suite)
|
||||
listener.__started__(LifeCycle.TestSuite, suite_report)
|
||||
|
||||
def handle_suite_ended_tag(self):
|
||||
def handle_suite_ended_tag(self, line):
|
||||
suite_result = self.state_machine.suite()
|
||||
suites = self.state_machine.get_suites()
|
||||
suite_result.run_time = suite_result.run_time
|
||||
@ -711,7 +762,7 @@ class CTestParser(IParser):
|
||||
for listener in self.get_listeners():
|
||||
listener.__ended__(LifeCycle.TestSuites, test_result=suites,
|
||||
suites_name=suites.suites_name,
|
||||
product_params=suites.product_params)
|
||||
product_info=suites.product_info)
|
||||
|
||||
def append_test_output(self, message):
|
||||
if self.state_machine.test().stacktrace:
|
||||
@ -731,6 +782,7 @@ class OpenSourceParser(IParser):
|
||||
self.listeners = []
|
||||
self.output = ""
|
||||
self.lines = []
|
||||
self.start_time = None
|
||||
|
||||
def get_suite_name(self):
|
||||
return self.suite_name
|
||||
@ -739,6 +791,8 @@ class OpenSourceParser(IParser):
|
||||
return self.listeners
|
||||
|
||||
def __process__(self, lines):
|
||||
if not self.start_time:
|
||||
self.start_time = datetime.datetime.now()
|
||||
self.lines.extend(lines)
|
||||
|
||||
def __done__(self):
|
||||
@ -758,15 +812,23 @@ class OpenSourceParser(IParser):
|
||||
listener.__started__(LifeCycle.TestCase, result)
|
||||
for line in self.lines:
|
||||
self.output = "{}{}".format(self.output, line)
|
||||
if _TEST_PASSED_LOWER in line.lower() or \
|
||||
_TESTS_PASSED_LOWER in line.lower():
|
||||
if _TEST_PASSED_LOWER in line.lower():
|
||||
test_result.code = ResultCode.PASSED.value
|
||||
if self.start_time:
|
||||
end_time = datetime.datetime.now()
|
||||
run_time = (end_time - self.start_time).total_seconds()
|
||||
test_result.run_time = int(run_time * 1000)
|
||||
for listener in self.get_listeners():
|
||||
result = copy.copy(test_result)
|
||||
listener.__ended__(LifeCycle.TestCase, result)
|
||||
break
|
||||
else:
|
||||
test_result.code = ResultCode.FAILED.value
|
||||
test_result.stacktrace = "\\n".join(self.lines)
|
||||
if self.start_time:
|
||||
end_time = datetime.datetime.now()
|
||||
run_time = (end_time - self.start_time).total_seconds()
|
||||
test_result.run_time = int(run_time * 1000)
|
||||
for listener in self.get_listeners():
|
||||
result = copy.copy(test_result)
|
||||
listener.__ended__(LifeCycle.TestCase, result)
|
||||
@ -791,6 +853,221 @@ class OpenSourceParser(IParser):
|
||||
suite_report=True)
|
||||
|
||||
|
||||
@Plugin(type=Plugin.PARSER, id=ParserType.build_only_test)
|
||||
class BuildOnlyParser(IParser):
|
||||
def __init__(self):
|
||||
self.state_machine = StateRecorder()
|
||||
self.suite_name = ""
|
||||
self.test_name = ""
|
||||
self.test_num = 0
|
||||
self.listeners = []
|
||||
self.output = ""
|
||||
|
||||
def get_suite_name(self):
|
||||
return self.suite_name
|
||||
|
||||
def get_listeners(self):
|
||||
return self.listeners
|
||||
|
||||
def __process__(self, lines):
|
||||
if not self.state_machine.suites_is_started():
|
||||
self.state_machine.trace_logs.extend(lines)
|
||||
self.handle_suite_started_tag(self.test_num)
|
||||
|
||||
self.state_machine.running_test_index = \
|
||||
self.state_machine.running_test_index + 1
|
||||
|
||||
for line in lines:
|
||||
if re.match(_COMPILE_PARA, line):
|
||||
self.test_name = str(line).split('compile')[0].strip()
|
||||
test_result = self.state_machine.test(reset=True)
|
||||
test_result.run_time = 0
|
||||
test_result.test_class = self.suite_name
|
||||
test_result.test_name = self.test_name
|
||||
for listener in self.get_listeners():
|
||||
result = copy.copy(test_result)
|
||||
listener.__started__(LifeCycle.TestCase, result)
|
||||
if _COMPILE_PASSED in line:
|
||||
test_result.code = ResultCode.PASSED.value
|
||||
for listener in self.get_listeners():
|
||||
result = copy.copy(test_result)
|
||||
listener.__ended__(LifeCycle.TestCase, result)
|
||||
else:
|
||||
test_result.code = ResultCode.FAILED.value
|
||||
for listener in self.get_listeners():
|
||||
result = copy.copy(test_result)
|
||||
listener.__failed__(LifeCycle.TestCase, result)
|
||||
self.state_machine.test().is_completed = True
|
||||
|
||||
def __done__(self):
|
||||
self.handle_suite_ended_tag()
|
||||
|
||||
def handle_suite_started_tag(self, test_num):
|
||||
test_suite = self.state_machine.suite()
|
||||
if test_num >= 0:
|
||||
test_suite.suite_name = self.suite_name
|
||||
test_suite.test_num = test_num
|
||||
for listener in self.get_listeners():
|
||||
suite_report = copy.copy(test_suite)
|
||||
listener.__started__(LifeCycle.TestSuite, suite_report)
|
||||
|
||||
def handle_suite_ended_tag(self):
|
||||
suite_result = self.state_machine.suite()
|
||||
for listener in self.get_listeners():
|
||||
suite = copy.copy(suite_result)
|
||||
listener.__ended__(LifeCycle.TestSuite, suite,
|
||||
suite_report=True)
|
||||
|
||||
|
||||
@Plugin(type=Plugin.PARSER, id=ParserType.jsuit_test_lite)
|
||||
class JSUnitParserLite(IParser):
|
||||
last_line = ""
|
||||
pattern = r"(\d{1,2}-\d{1,2}\s\d{1,2}:\d{1,2}:\d{1,2}\.\d{3}) "
|
||||
|
||||
def __init__(self):
|
||||
self.state_machine = StateRecorder()
|
||||
self.suites_name = ""
|
||||
self.listeners = []
|
||||
|
||||
def get_listeners(self):
|
||||
return self.listeners
|
||||
|
||||
def __process__(self, lines):
|
||||
if not self.state_machine.suites_is_started():
|
||||
self.state_machine.trace_logs.extend(lines)
|
||||
for line in lines:
|
||||
self.parse(line)
|
||||
|
||||
def __done__(self):
|
||||
pass
|
||||
|
||||
def parse(self, line):
|
||||
if (self.state_machine.suites_is_started() or
|
||||
line.find(_START_JSUNIT_RUN_MARKER) != -1) and \
|
||||
line.find(_ACE_LOG_MARKER) != -1:
|
||||
if line.find(_START_JSUNIT_RUN_MARKER) != -1:
|
||||
self.handle_suites_started_tag()
|
||||
elif line.endswith(_END_JSUNIT_RUN_MARKER):
|
||||
self.handle_suites_ended_tag()
|
||||
elif line.find(_START_JSUNIT_SUITE_RUN_MARKER) != -1:
|
||||
self.handle_suite_started_tag(line.strip())
|
||||
elif line.endswith(_START_JSUNIT_SUITE_END_MARKER):
|
||||
self.handle_suite_ended_tag()
|
||||
elif _PASS_JSUNIT_MARKER in line or _FAIL_JSUNIT_MARKER \
|
||||
in line:
|
||||
self.handle_one_test_tag(line.strip())
|
||||
self.last_line = line
|
||||
|
||||
def parse_test_description(self, message):
|
||||
pattern = r"\[(pass|fail)\]"
|
||||
year = time.strftime("%Y")
|
||||
filter_message = message.split("[Console Info]")[1].strip()
|
||||
end_time = "%s-%s" % \
|
||||
(year, re.match(self.pattern, message).group().strip())
|
||||
start_time = "%s-%s" % \
|
||||
(year, re.match(self.pattern,
|
||||
self.last_line.strip()).group().strip())
|
||||
start_timestamp = int(time.mktime(
|
||||
time.strptime(start_time, "%Y-%m-%d %H:%M:%S.%f"))) * 1000 + int(
|
||||
start_time.split(".")[-1])
|
||||
end_timestamp = int(time.mktime(
|
||||
time.strptime(end_time, "%Y-%m-%d %H:%M:%S.%f"))) * 1000 + int(
|
||||
end_time.split(".")[-1])
|
||||
run_time = end_timestamp - start_timestamp
|
||||
_, status_end_index = re.match(pattern, filter_message).span()
|
||||
status = filter_message[:status_end_index]
|
||||
test_name = filter_message[status_end_index:]
|
||||
status_dict = {"pass": ResultCode.PASSED, "fail": ResultCode.FAILED,
|
||||
"ignore": ResultCode.SKIPPED}
|
||||
status = status_dict.get(status[1:-1])
|
||||
return test_name, status, run_time
|
||||
|
||||
def handle_suites_started_tag(self):
|
||||
self.state_machine.get_suites(reset=True)
|
||||
test_suites = self.state_machine.get_suites()
|
||||
test_suites.suites_name = self.suites_name
|
||||
test_suites.test_num = 0
|
||||
for listener in self.get_listeners():
|
||||
suite_report = copy.copy(test_suites)
|
||||
listener.__started__(LifeCycle.TestSuites, suite_report)
|
||||
|
||||
def handle_suites_ended_tag(self):
|
||||
suites = self.state_machine.get_suites()
|
||||
suites.is_completed = True
|
||||
|
||||
for listener in self.get_listeners():
|
||||
listener.__ended__(LifeCycle.TestSuites, test_result=suites,
|
||||
suites_name=suites.suites_name)
|
||||
|
||||
def handle_one_test_tag(self, message):
|
||||
test_name, status, run_time = \
|
||||
self.parse_test_description(message)
|
||||
test_result = self.state_machine.test(reset=True)
|
||||
test_suite = self.state_machine.suite()
|
||||
test_result.test_class = test_suite.suite_name
|
||||
test_result.test_name = test_name
|
||||
test_result.run_time = run_time
|
||||
test_result.code = status.value
|
||||
test_result.current = self.state_machine.running_test_index + 1
|
||||
self.state_machine.suite().run_time += run_time
|
||||
for listener in self.get_listeners():
|
||||
test_result = copy.copy(test_result)
|
||||
listener.__started__(LifeCycle.TestCase, test_result)
|
||||
|
||||
test_suites = self.state_machine.get_suites()
|
||||
found_unexpected_test = False
|
||||
|
||||
if found_unexpected_test or ResultCode.FAILED == status:
|
||||
for listener in self.get_listeners():
|
||||
result = copy.copy(test_result)
|
||||
listener.__failed__(LifeCycle.TestCase, result)
|
||||
elif ResultCode.SKIPPED == status:
|
||||
for listener in self.get_listeners():
|
||||
result = copy.copy(test_result)
|
||||
listener.__skipped__(LifeCycle.TestCase, result)
|
||||
|
||||
self.state_machine.test().is_completed = True
|
||||
test_suite.test_num += 1
|
||||
test_suites.test_num += 1
|
||||
for listener in self.get_listeners():
|
||||
result = copy.copy(test_result)
|
||||
listener.__ended__(LifeCycle.TestCase, result)
|
||||
self.state_machine.running_test_index += 1
|
||||
|
||||
def fake_run_marker(self, message):
|
||||
fake_marker = re.compile(" +").split(message)
|
||||
self.processTestStartedTag(fake_marker)
|
||||
|
||||
def handle_suite_started_tag(self, message):
|
||||
self.state_machine.suite(reset=True)
|
||||
test_suite = self.state_machine.suite()
|
||||
if re.match(r".*\[suite start\].*", message):
|
||||
_, index = re.match(r".*\[suite start\]", message).span()
|
||||
test_suite.suite_name = message[index:]
|
||||
test_suite.test_num = 0
|
||||
for listener in self.get_listeners():
|
||||
suite_report = copy.copy(test_suite)
|
||||
listener.__started__(LifeCycle.TestSuite, suite_report)
|
||||
|
||||
def handle_suite_ended_tag(self):
|
||||
suite_result = self.state_machine.suite()
|
||||
suites = self.state_machine.get_suites()
|
||||
suite_result.run_time = suite_result.run_time
|
||||
suites.run_time += suite_result.run_time
|
||||
suite_result.is_completed = True
|
||||
|
||||
for listener in self.get_listeners():
|
||||
suite = copy.copy(suite_result)
|
||||
listener.__ended__(LifeCycle.TestSuite, suite, is_clear=True)
|
||||
|
||||
def append_test_output(self, message):
|
||||
if self.state_machine.test().stacktrace:
|
||||
self.state_machine.test().stacktrace = \
|
||||
"%s\r\n" % self.state_machine.test().stacktrace
|
||||
self.state_machine.test().stacktrace = \
|
||||
''.join((self.state_machine.test().stacktrace, message))
|
||||
|
||||
|
||||
class ShellHandler:
|
||||
def __init__(self, parsers):
|
||||
self.parsers = []
|
||||
@ -846,8 +1123,8 @@ class ShellHandler:
|
||||
parser.__done__()
|
||||
|
||||
|
||||
def handle_product_params(message, product_params):
|
||||
def handle_product_info(message, product_info):
|
||||
message = message[message.index("The"):]
|
||||
items = message[len("The "):].split(" is ")
|
||||
product_params.setdefault(items[0].strip(),
|
||||
items[1].strip().strip("[").strip("]"))
|
||||
product_info.setdefault(items[0].strip(),
|
||||
items[1].strip().strip("[").strip("]"))
|
||||
|
0
src/xdevice/_core/environment/__init__.py
Executable file → Normal file
0
src/xdevice/_core/environment/__init__.py
Executable file → Normal file
@ -2,7 +2,7 @@
|
||||
# coding=utf-8
|
||||
|
||||
#
|
||||
# Copyright (c) 2020 Huawei Device Co., Ltd.
|
||||
# Copyright (c) 2020-2021 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
|
||||
@ -20,23 +20,29 @@ import re
|
||||
import telnetlib
|
||||
import time
|
||||
import os
|
||||
import threading
|
||||
|
||||
from _core.constants import DeviceOsType
|
||||
from _core.constants import ConfigConst
|
||||
from _core.constants import ComType
|
||||
from _core.constants import DeviceLabelType
|
||||
from _core.constants import ModeType
|
||||
from _core.environment.dmlib_lite import LiteHelper
|
||||
from _core.exception import LiteDeviceConnectError
|
||||
from _core.exception import ParamError
|
||||
from _core.exception import DeviceError
|
||||
from _core.exception import LiteDeviceTimeout
|
||||
from _core.exception import LiteParamError
|
||||
from _core.interface import IDevice
|
||||
from _core.exception import ExecuteTerminate
|
||||
from _core.logger import platform_logger
|
||||
from _core.environment.manager_env import DeviceAllocationState
|
||||
from _core.plugin import Plugin
|
||||
from _core.utils import exec_cmd
|
||||
from _core.utils import convert_serial
|
||||
from _core.utils import convert_ip
|
||||
from _core.utils import check_mode
|
||||
|
||||
LOG = platform_logger("DeviceLite")
|
||||
TIMEOUT = 90
|
||||
RETRY_ATTEMPTS = 0
|
||||
HDC = "litehdc.exe"
|
||||
|
||||
|
||||
@ -58,7 +64,7 @@ def get_hdc_path():
|
||||
if os.path.exists(file_path):
|
||||
return file_path
|
||||
else:
|
||||
raise ParamError("config file not found")
|
||||
raise LiteParamError("litehdc.exe not found", error_no="00108")
|
||||
|
||||
|
||||
def parse_available_com(com_str):
|
||||
@ -69,6 +75,33 @@ def parse_available_com(com_str):
|
||||
return com_list
|
||||
|
||||
|
||||
def perform_device_action(func):
|
||||
def device_action(self, *args, **kwargs):
|
||||
if not self.get_recover_state():
|
||||
LOG.debug("device %s %s is false" % (self.device_sn,
|
||||
ConfigConst.recover_state))
|
||||
return "", "", ""
|
||||
|
||||
tmp = int(kwargs.get("retry", RETRY_ATTEMPTS))
|
||||
retry = tmp + 1 if tmp > 0 else 1
|
||||
exception = None
|
||||
for num in range(retry):
|
||||
try:
|
||||
result = func(self, *args, **kwargs)
|
||||
return result
|
||||
except LiteDeviceTimeout as error:
|
||||
LOG.error(error)
|
||||
exception = error
|
||||
if num:
|
||||
self.recover_device()
|
||||
except Exception as error:
|
||||
LOG.error(error)
|
||||
exception = error
|
||||
raise exception
|
||||
|
||||
return device_action
|
||||
|
||||
|
||||
@Plugin(type=Plugin.DEVICE, id=DeviceOsType.lite)
|
||||
class DeviceLite(IDevice):
|
||||
"""
|
||||
@ -83,42 +116,52 @@ class DeviceLite(IDevice):
|
||||
device_allocation_state = DeviceAllocationState.available
|
||||
|
||||
def __init__(self):
|
||||
self.error_message = ""
|
||||
self.device_sn = ""
|
||||
self.label = ""
|
||||
self.device_connect_type = ""
|
||||
self.device_kernel = ""
|
||||
self.remote_device = None
|
||||
self.local_device = None
|
||||
self.device = None
|
||||
self.ifconfig = None
|
||||
self.extend_value = {}
|
||||
self.device_lock = threading.RLock()
|
||||
|
||||
def __set_serial__(self, device=None):
|
||||
for item in device:
|
||||
if "ip" in item.keys():
|
||||
self.device_sn = "remote_%s" % item.get("ip")
|
||||
if "ip" in item.keys() and "port" in item.keys():
|
||||
self.device_sn = "remote_%s_%s" % \
|
||||
(item.get("ip"), item.get("port"))
|
||||
break
|
||||
elif "type" in item.keys() and ComType.deploy_com == item.get(
|
||||
"type"):
|
||||
elif "type" in item.keys() and "com" in item.keys():
|
||||
self.device_sn = "local_%s" % item.get("com")
|
||||
break
|
||||
|
||||
def __get_serial__(self):
|
||||
return self.device_sn
|
||||
|
||||
def get(self, key=None, default=None):
|
||||
if not key:
|
||||
return default
|
||||
value = getattr(self, key, None)
|
||||
if value:
|
||||
return value
|
||||
else:
|
||||
return self.extend_value.get(key, default)
|
||||
|
||||
def __set_device_kernel__(self, kernel_type=""):
|
||||
self.device_kernel = kernel_type
|
||||
|
||||
def __get_device_kernel__(self):
|
||||
return self.device_kernel
|
||||
|
||||
def __check_watch__(self, device):
|
||||
@staticmethod
|
||||
def _check_watchgt(device):
|
||||
for item in device:
|
||||
if "label" not in item.keys():
|
||||
if "com" not in item.keys() or ("com" in item.keys() and
|
||||
not item.get("com")):
|
||||
self.error_message = "watch local com cannot be " \
|
||||
"empty, please check"
|
||||
LOG.error(self.error_message)
|
||||
raise ParamError(self.error_message)
|
||||
error_message = "watchGT local com cannot be " \
|
||||
"empty, please check"
|
||||
raise LiteParamError(error_message, error_no="00108")
|
||||
else:
|
||||
hdc = get_hdc_path()
|
||||
result = exec_cmd([hdc])
|
||||
@ -126,72 +169,89 @@ class DeviceLite(IDevice):
|
||||
if item.get("com").upper() in com_list:
|
||||
return True
|
||||
else:
|
||||
self.error_message = "watch local com does not exist"
|
||||
LOG.error(self.error_message)
|
||||
raise ParamError(self.error_message)
|
||||
error_message = "watchGT local com does not exist"
|
||||
raise LiteParamError(error_message, error_no="00108")
|
||||
|
||||
def __check_wifiiot_config__(self, device):
|
||||
@staticmethod
|
||||
def _check_wifiiot_config(device):
|
||||
com_type_set = set()
|
||||
for item in device:
|
||||
if "label" not in item.keys():
|
||||
if "com" not in item.keys() or ("com" in item.keys() and
|
||||
not item.get("com")):
|
||||
self.error_message = "wifiiot local com cannot be " \
|
||||
"empty, please check"
|
||||
LOG.error(self.error_message)
|
||||
raise ParamError(self.error_message)
|
||||
error_message = "wifiiot local com cannot be " \
|
||||
"empty, please check"
|
||||
raise LiteParamError(error_message, error_no="00108")
|
||||
|
||||
if "type" not in item.keys() or ("type" not in item.keys() and
|
||||
not item.get("type")):
|
||||
self.error_message = "wifiiot com type cannot be " \
|
||||
"empty, please check"
|
||||
LOG.error(self.error_message)
|
||||
raise ParamError(self.error_message)
|
||||
error_message = "wifiiot com type cannot be " \
|
||||
"empty, please check"
|
||||
raise LiteParamError(error_message, error_no="00108")
|
||||
else:
|
||||
com_type_set.add(item.get("type"))
|
||||
if len(com_type_set) < 2:
|
||||
self.error_message = "wifiiot need cmd com and deploy com" \
|
||||
" at the same time, please check"
|
||||
LOG.error(self.error_message)
|
||||
raise ParamError(self.error_message)
|
||||
error_message = "wifiiot need cmd com and deploy com" \
|
||||
" at the same time, please check"
|
||||
raise LiteParamError(error_message, error_no="00108")
|
||||
|
||||
def __check_ipcamera_local__(self, device):
|
||||
@staticmethod
|
||||
def _check_ipcamera_local(device):
|
||||
for item in device:
|
||||
if "label" not in item.keys():
|
||||
if "com" not in item.keys() or ("com" in item.keys() and
|
||||
not item.get("com")):
|
||||
self.error_message = "ipcamera local com cannot be " \
|
||||
"empty, please check"
|
||||
LOG.error(self.error_message)
|
||||
raise ParamError(self.error_message)
|
||||
error_message = "ipcamera local com cannot be " \
|
||||
"empty, please check"
|
||||
raise LiteParamError(error_message, error_no="00108")
|
||||
|
||||
def __check_ipcamera_remote__(self, device=None):
|
||||
@staticmethod
|
||||
def _check_ipcamera_remote(device=None):
|
||||
for item in device:
|
||||
if "label" not in item.keys():
|
||||
if "port" in item.keys() and item.get("port") and not item.get(
|
||||
"port").isnumeric():
|
||||
self.error_message = "ipcamera remote port should be " \
|
||||
"a number, please check"
|
||||
LOG.error(self.error_message)
|
||||
raise ParamError(self.error_message)
|
||||
error_message = "ipcamera remote port should be " \
|
||||
"a number, please check"
|
||||
raise LiteParamError(error_message, error_no="00108")
|
||||
elif "port" not in item.keys():
|
||||
self.error_message = "ipcamera remote port cannot be" \
|
||||
" empty, please check"
|
||||
LOG.error(self.error_message)
|
||||
raise ParamError(self.error_message)
|
||||
error_message = "ipcamera remote port cannot be" \
|
||||
" empty, please check"
|
||||
raise LiteParamError(error_message, error_no="00108")
|
||||
|
||||
@staticmethod
|
||||
def _check_agent(device=None):
|
||||
for item in device:
|
||||
if "label" not in item.keys():
|
||||
if "port" in item.keys() and item.get("port") and not item.get(
|
||||
"port").isnumeric():
|
||||
error_message = "agent port should be " \
|
||||
"a number, please check"
|
||||
raise LiteParamError(error_message, error_no="00108")
|
||||
elif "port" not in item.keys():
|
||||
error_message = "ipcamera agent port cannot be" \
|
||||
" empty, please check"
|
||||
raise LiteParamError(error_message, error_no="00108")
|
||||
|
||||
def __check_config__(self, device=None):
|
||||
self.set_connect_type(device)
|
||||
if "agent" == device[0].get("type"):
|
||||
self.device_connect_type = "agent"
|
||||
self.label = device[0].get("label")
|
||||
else:
|
||||
self.set_connect_type(device)
|
||||
if self.label == DeviceLabelType.wifiiot:
|
||||
self.__check_wifiiot_config__(device)
|
||||
self._check_wifiiot_config(device)
|
||||
elif self.label == DeviceLabelType.ipcamera and \
|
||||
self.device_connect_type == "local":
|
||||
self.__check_ipcamera_local__(device)
|
||||
self._check_ipcamera_local(device)
|
||||
elif self.label == DeviceLabelType.ipcamera and \
|
||||
self.device_connect_type == "remote":
|
||||
self.__check_ipcamera_remote__(device)
|
||||
elif self.label == DeviceLabelType.watch:
|
||||
self.__check_watch__(device)
|
||||
self._check_ipcamera_remote(device)
|
||||
elif self.label == DeviceLabelType.watch_gt:
|
||||
self._check_watchgt(device)
|
||||
elif self.label == DeviceLabelType.ipcamera and \
|
||||
self.device_connect_type == "agent":
|
||||
self._check_agent(device)
|
||||
|
||||
def set_connect_type(self, device):
|
||||
pattern = r'^((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[' \
|
||||
@ -205,95 +265,133 @@ class DeviceLite(IDevice):
|
||||
if re.match(pattern, item.get("ip")):
|
||||
self.device_connect_type = "remote"
|
||||
else:
|
||||
self.error_message = "Remote device ip not in right" \
|
||||
error_message = "Remote device ip not in right" \
|
||||
"format, please check user_config.xml"
|
||||
LOG.error(self.error_message)
|
||||
raise ParamError(self.error_message)
|
||||
raise LiteParamError(error_message, error_no="00108")
|
||||
if not self.label:
|
||||
self.error_message = "device label cannot be empty, " \
|
||||
"please check"
|
||||
LOG.error(self.error_message)
|
||||
raise ParamError(self.error_message)
|
||||
error_message = "device label cannot be empty, " \
|
||||
"please check"
|
||||
raise LiteParamError(error_message, error_no="00108")
|
||||
else:
|
||||
if self.label != DeviceLabelType.wifiiot and self.label != \
|
||||
DeviceLabelType.ipcamera and self.label != \
|
||||
DeviceLabelType.watch:
|
||||
self.error_message = "device label should be ipcamera or" \
|
||||
if self.label != DeviceLabelType.wifiiot and \
|
||||
self.label != DeviceLabelType.ipcamera and \
|
||||
self.label != DeviceLabelType.watch_gt:
|
||||
error_message = "device label should be ipcamera or" \
|
||||
" wifiiot, please check"
|
||||
LOG.error(self.error_message)
|
||||
raise ParamError(self.error_message)
|
||||
raise LiteParamError(error_message, error_no="00108")
|
||||
if not self.device_connect_type:
|
||||
self.error_message = "device com or ip cannot be empty, " \
|
||||
error_message = "device com or ip cannot be empty, " \
|
||||
"please check"
|
||||
LOG.error(self.error_message)
|
||||
raise ParamError(self.error_message)
|
||||
|
||||
def __init_device__(self, device=None):
|
||||
if not device:
|
||||
LOG.error(
|
||||
"no available device, please config it in lite_config.xml.")
|
||||
raise DeviceError(
|
||||
"no available device, please config it in lite_config.xml.")
|
||||
raise LiteParamError(error_message, error_no="00108")
|
||||
|
||||
def __init_device__(self, device):
|
||||
self.__check_config__(device)
|
||||
self.__set_serial__(device)
|
||||
if self.device_connect_type == "remote":
|
||||
self.remote_device = RemoteController(device)
|
||||
self.device = CONTROLLER_DICT.get("remote")(device)
|
||||
elif self.device_connect_type == "agent":
|
||||
self.device = CONTROLLER_DICT.get("agent")(device)
|
||||
else:
|
||||
self.local_device = LocalController(device)
|
||||
self.device = CONTROLLER_DICT.get("local")(device)
|
||||
|
||||
self.ifconfig = device[1].get("ifconfig")
|
||||
|
||||
def connect(self):
|
||||
"""
|
||||
connect the device
|
||||
|
||||
"""
|
||||
if self.device_connect_type == "remote":
|
||||
self.remote_device.connect()
|
||||
else:
|
||||
self.local_device.connect()
|
||||
try:
|
||||
self.device.connect()
|
||||
except LiteDeviceConnectError:
|
||||
if check_mode(ModeType.decc):
|
||||
LOG.debug("set device %s recover state to false" %
|
||||
self.device_sn)
|
||||
self.device_allocation_state = DeviceAllocationState.unusable
|
||||
self.set_recover_state(False)
|
||||
raise
|
||||
|
||||
@perform_device_action
|
||||
def execute_command_with_timeout(self, command="", case_type="",
|
||||
timeout=TIMEOUT, receiver=None):
|
||||
"""
|
||||
Executes command on the device.
|
||||
timeout=TIMEOUT, **kwargs):
|
||||
"""Executes command on the device.
|
||||
|
||||
Parameters:
|
||||
Args:
|
||||
command: the command to execute
|
||||
case_type: CTest or CppTest
|
||||
receiver: parser handler
|
||||
timeout: timeout for read result
|
||||
**kwargs: receiver - parser handler input
|
||||
|
||||
Returns:
|
||||
(filter_result, status, error_message)
|
||||
|
||||
filter_result: command execution result
|
||||
status: true or false
|
||||
error_message: command execution error message
|
||||
"""
|
||||
try:
|
||||
if self.device_connect_type == "remote":
|
||||
filter_result, status, error_message = \
|
||||
self.remote_device.execute_command_with_timeout(
|
||||
command=command,
|
||||
timeout=timeout,
|
||||
receiver=receiver)
|
||||
else:
|
||||
filter_result, status, error_message = \
|
||||
self.local_device.execute_command_with_timeout(
|
||||
command=command,
|
||||
case_type=case_type,
|
||||
timeout=timeout,
|
||||
receiver=receiver)
|
||||
receiver = kwargs.get("receiver", None)
|
||||
if self.device_connect_type == "remote":
|
||||
LOG.info("%s execute command shell %s with timeout %ss" %
|
||||
(convert_serial(self.__get_serial__()), command,
|
||||
str(timeout)))
|
||||
filter_result, status, error_message = \
|
||||
self.device.execute_command_with_timeout(
|
||||
command=command,
|
||||
timeout=timeout,
|
||||
receiver=receiver)
|
||||
elif self.device_connect_type == "agent":
|
||||
filter_result, status, error_message = \
|
||||
self.device.execute_command_with_timeout(
|
||||
command=command,
|
||||
case_type=case_type,
|
||||
timeout=timeout,
|
||||
receiver=receiver, type="cmd")
|
||||
else:
|
||||
filter_result, status, error_message = \
|
||||
self.device.execute_command_with_timeout(
|
||||
command=command,
|
||||
case_type=case_type,
|
||||
timeout=timeout,
|
||||
receiver=receiver)
|
||||
if not receiver:
|
||||
LOG.debug("execute result:%s", filter_result)
|
||||
if not status:
|
||||
LOG.debug("error_message:%s", error_message)
|
||||
return filter_result, status, error_message
|
||||
except ExecuteTerminate:
|
||||
error_message = "Execute terminate."
|
||||
return "", False, error_message
|
||||
if not status:
|
||||
LOG.debug("error_message:%s", error_message)
|
||||
return filter_result, status, error_message
|
||||
|
||||
def recover_device(self):
|
||||
self.reboot()
|
||||
|
||||
def reboot(self):
|
||||
self.connect()
|
||||
filter_result, status, error_message = self. \
|
||||
execute_command_with_timeout(command="reset", timeout=30)
|
||||
if not filter_result:
|
||||
if check_mode(ModeType.decc):
|
||||
LOG.debug("set device %s recover state to false" %
|
||||
self.device_sn)
|
||||
self.device_allocation_state = DeviceAllocationState.unusable
|
||||
self.set_recover_state(False)
|
||||
|
||||
if self.ifconfig:
|
||||
self.execute_command_with_timeout(command=self.ifconfig,
|
||||
timeout=60)
|
||||
|
||||
def close(self):
|
||||
"""
|
||||
Unmount the testcase from device server and close the telnet connection
|
||||
with device server or close the local serial
|
||||
Close the telnet connection with device server or close the local
|
||||
serial
|
||||
"""
|
||||
if self.device_connect_type == "remote":
|
||||
return self.remote_device.close()
|
||||
else:
|
||||
return self.local_device.close()
|
||||
self.device.close()
|
||||
|
||||
def set_recover_state(self, state):
|
||||
with self.device_lock:
|
||||
setattr(self, ConfigConst.recover_state, state)
|
||||
|
||||
def get_recover_state(self, default_state=True):
|
||||
with self.device_lock:
|
||||
state = getattr(self, ConfigConst.recover_state, default_state)
|
||||
return state
|
||||
|
||||
|
||||
class RemoteController:
|
||||
@ -306,7 +404,6 @@ class RemoteController:
|
||||
def __init__(self, device):
|
||||
self.host = device[1].get("ip")
|
||||
self.port = int(device[1].get("port"))
|
||||
self.directory = device[1].get("dir")
|
||||
self.telnet = None
|
||||
|
||||
def connect(self):
|
||||
@ -314,17 +411,16 @@ class RemoteController:
|
||||
connect the device server
|
||||
|
||||
"""
|
||||
now_time = time.strftime('%Y-%m-%d %H:%M:%S',
|
||||
time.localtime(time.time()))
|
||||
try:
|
||||
if self.telnet:
|
||||
return self.telnet
|
||||
self.telnet = telnetlib.Telnet(self.host, self.port,
|
||||
timeout=TIMEOUT)
|
||||
except Exception as err_msgs:
|
||||
LOG.error('TIME: %s IP: %s STATUS: telnet failed\n error:%s' % (
|
||||
now_time, self.host, str(err_msgs)))
|
||||
raise LiteDeviceConnectError("connect lite_device failed")
|
||||
error_message = "Connect remote lite device failed, host is %s, " \
|
||||
"port is %s, error is %s" % \
|
||||
(convert_ip(self.host), self.port, str(err_msgs))
|
||||
raise LiteDeviceConnectError(error_message, error_no="00401")
|
||||
time.sleep(2)
|
||||
self.telnet.set_debuglevel(0)
|
||||
return self.telnet
|
||||
@ -344,10 +440,8 @@ class RemoteController:
|
||||
|
||||
def close(self):
|
||||
"""
|
||||
Unmount the testcase directory from device server and close the telnet
|
||||
connection with device server
|
||||
Close the telnet connection with device server
|
||||
"""
|
||||
error_message = ""
|
||||
try:
|
||||
if not self.telnet:
|
||||
return
|
||||
@ -355,8 +449,7 @@ class RemoteController:
|
||||
self.telnet = None
|
||||
except (ConnectionError, Exception):
|
||||
error_message = "Remote device is disconnected abnormally"
|
||||
LOG.error(error_message)
|
||||
return error_message
|
||||
LOG.error(error_message, error_no="00401")
|
||||
|
||||
|
||||
class LocalController:
|
||||
@ -380,13 +473,7 @@ class LocalController:
|
||||
"""
|
||||
Open serial.
|
||||
"""
|
||||
try:
|
||||
self.com_dict.get(key).connect()
|
||||
except Exception:
|
||||
raise LiteDeviceConnectError("connect serial failed")
|
||||
|
||||
def flush_input(self, key=ComType.cmd_com):
|
||||
self.com_dict.get(key).flush_input()
|
||||
self.com_dict.get(key).connect()
|
||||
|
||||
def close(self, key=ComType.cmd_com):
|
||||
"""
|
||||
@ -417,14 +504,13 @@ class ComController:
|
||||
Parameters:
|
||||
device: local com
|
||||
"""
|
||||
self.serial_port = device.get("com") if device.get("com") else None
|
||||
self.baund_rate = int(device.get("baund_rate")) if device.get(
|
||||
"baund_rate") else 115200
|
||||
self.is_open = False
|
||||
self.com = None
|
||||
self.serial_port = device.get("com") if device.get("com") else None
|
||||
self.baud_rate = int(device.get("baud_rate")) if device.get(
|
||||
"baud_rate") else 115200
|
||||
self.timeout = int(device.get("timeout")) if device.get(
|
||||
"timeout") else TIMEOUT
|
||||
self.directory = device.get("dir") if device.get("dir") else None
|
||||
self.com = None
|
||||
|
||||
def connect(self):
|
||||
"""
|
||||
@ -434,23 +520,19 @@ class ComController:
|
||||
if not self.is_open:
|
||||
import serial
|
||||
self.com = serial.Serial(self.serial_port,
|
||||
baudrate=self.baund_rate,
|
||||
baudrate=self.baud_rate,
|
||||
timeout=self.timeout)
|
||||
self.is_open = True
|
||||
except Exception:
|
||||
LOG.error(
|
||||
"connect %s serial failed, please make sure this port is not "
|
||||
"occupied." % self.serial_port)
|
||||
raise LiteDeviceConnectError("connect serial failed")
|
||||
|
||||
def flush_input(self):
|
||||
self.com.flushInput()
|
||||
except Exception as error_msg:
|
||||
error = "connect %s serial failed, please make sure this port is" \
|
||||
" not occupied, error is %s[00401]" % \
|
||||
(self.serial_port, str(error_msg))
|
||||
raise LiteDeviceConnectError(error, error_no="00401")
|
||||
|
||||
def close(self):
|
||||
"""
|
||||
Close serial.
|
||||
"""
|
||||
error_message = ""
|
||||
try:
|
||||
if not self.com:
|
||||
return
|
||||
@ -459,8 +541,7 @@ class ComController:
|
||||
self.is_open = False
|
||||
except (ConnectionError, Exception):
|
||||
error_message = "Local device is disconnected abnormally"
|
||||
LOG.error(error_message)
|
||||
return error_message
|
||||
LOG.error(error_message, error_no="00401")
|
||||
|
||||
def execute_command_with_timeout(self, **kwargs):
|
||||
"""
|
||||
@ -473,3 +554,51 @@ class ComController:
|
||||
Execute command on the serial and read all the output from the serial.
|
||||
"""
|
||||
LiteHelper.execute_local_command(self.com, command)
|
||||
|
||||
|
||||
class AgentController:
|
||||
def __init__(self, device):
|
||||
"""
|
||||
Init serial.
|
||||
Parameters:
|
||||
device: local com
|
||||
"""
|
||||
LOG.info("AgentController begin")
|
||||
self.com_dict = {}
|
||||
self.host = device[1].get("ip")
|
||||
self.port = str(device[1].get("port"))
|
||||
self.headers = {'Content-Type': 'application/json'}
|
||||
self.local_source_dir = ""
|
||||
self.target_save_path = ""
|
||||
self.base_url = "http" + "://" + self.host + ":" + self.port
|
||||
|
||||
def connect(self):
|
||||
"""
|
||||
Open serial.
|
||||
"""
|
||||
|
||||
def close(self):
|
||||
"""
|
||||
Close serial.
|
||||
"""
|
||||
pass
|
||||
|
||||
def execute_command_with_timeout(self, command, **kwargs):
|
||||
"""
|
||||
Executes command on the device.
|
||||
|
||||
Parameters:
|
||||
command: the command to execute
|
||||
timeout: timeout for read result
|
||||
receiver: parser handler
|
||||
"""
|
||||
return LiteHelper.execute_agent_cmd_with_timeout(self,
|
||||
command,
|
||||
**kwargs)
|
||||
|
||||
|
||||
CONTROLLER_DICT = {
|
||||
"local": LocalController,
|
||||
"remote": RemoteController,
|
||||
"agent": AgentController
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
# coding=utf-8
|
||||
|
||||
#
|
||||
# Copyright (c) 2020 Huawei Device Co., Ltd.
|
||||
# Copyright (c) 2020-2021 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
|
||||
@ -16,47 +16,74 @@
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
import json
|
||||
import time
|
||||
import re
|
||||
|
||||
from _core.constants import DeviceTestType
|
||||
from _core.exception import ExecuteTerminate
|
||||
from _core.exception import LiteDeviceTimeout
|
||||
from _core.exception import LiteDeviceConnectError
|
||||
from _core.exception import LiteDeviceExecuteCommandError
|
||||
from _core.logger import platform_logger
|
||||
|
||||
from _core.utils import convert_port
|
||||
|
||||
__all__ = ["generate_report", "LiteHelper"]
|
||||
|
||||
CPP_TEST_STANDARD_SIGN = "[==========]"
|
||||
CPP_TEST_END_SIGN = "Global test environment tear-down"
|
||||
CPP_SYS_STANDARD_SIGN = " #"
|
||||
CPP_TEST_END_SIGN = "Gtest xml output finished"
|
||||
CPP_SYS_STANDARD_SIGN = "OHOS #"
|
||||
CPP_ERR_MESSAGE = "[ERR]No such file or directory: "
|
||||
|
||||
TIMEOUT = 90
|
||||
CTEST_STANDARD_SIGN = "Start to run test suite"
|
||||
AT_CMD_ENDS = "OK"
|
||||
CTEST_END_SIGN = "Framework finished."
|
||||
CTEST_END_SIGN = "All the test suites finished"
|
||||
|
||||
_START_JSUNIT_RUN_MARKER = "[start] start run suites"
|
||||
_END_JSUNIT_RUN_MARKER = "[end] run suites end"
|
||||
INSTALL_END_MARKER = "resultMessage is install success !"
|
||||
|
||||
PATTERN = re.compile(r'\x1B(\[([0-9]{1,2}(;[0-9]{1,2})*)?m)*')
|
||||
TIMEOUT = 90
|
||||
LOG = platform_logger("DmlibLite")
|
||||
|
||||
|
||||
def check_open_source_test(result_output):
|
||||
if result_output.find(CPP_TEST_STANDARD_SIGN) == -1 and \
|
||||
("test pass" in result_output.lower() or
|
||||
"test fail" in result_output.lower() or
|
||||
"tests pass" in result_output.lower() or
|
||||
"tests fail" in result_output.lower()):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def check_read_test_end(result=None, input_command=None):
|
||||
result_output = result.replace(input_command, "")
|
||||
temp_result = result.replace("\n", "")
|
||||
if input_command not in temp_result:
|
||||
return False
|
||||
index = result.find(input_command) + len(input_command)
|
||||
result_output = result[index:]
|
||||
if input_command.startswith("./"):
|
||||
if result_output.find(CPP_TEST_STANDARD_SIGN) != -1:
|
||||
if result_output.count(CPP_TEST_STANDARD_SIGN) == 2 or \
|
||||
result_output.find(CPP_TEST_END_SIGN) != -1:
|
||||
return True
|
||||
if result_output.find(CPP_TEST_STANDARD_SIGN) == -1 and \
|
||||
("test pass" in result_output.lower() or
|
||||
"test fail" in result_output.lower()):
|
||||
if check_open_source_test(result_output):
|
||||
return True
|
||||
if result_output.find(_START_JSUNIT_RUN_MARKER) >= 1 and \
|
||||
result_output.find(_END_JSUNIT_RUN_MARKER) >= 1:
|
||||
return True
|
||||
|
||||
if result_output.find(INSTALL_END_MARKER) != -1:
|
||||
return True
|
||||
|
||||
if "%s%s" % (CPP_ERR_MESSAGE, input_command[2:]) in result_output:
|
||||
LOG.error("execute file not exist, result is %s" % result_output)
|
||||
raise LiteDeviceExecuteCommandError("execute file not exist")
|
||||
LOG.error("execute file not exist, result is %s" % result_output,
|
||||
error_no="00402")
|
||||
raise LiteDeviceExecuteCommandError("execute file not exist",
|
||||
error_no="00402")
|
||||
else:
|
||||
if " #" in result_output:
|
||||
if "OHOS #" in result_output or "Linux" in result_output:
|
||||
if input_command == "reboot" or input_command == "reset":
|
||||
return False
|
||||
if input_command.startswith("mount"):
|
||||
@ -100,37 +127,43 @@ class LiteHelper:
|
||||
status = True
|
||||
error_message = ""
|
||||
result = ""
|
||||
LOG.info("execute command shell %s with timeout %s" %
|
||||
(command, str(timeout)))
|
||||
if not telnet:
|
||||
raise LiteDeviceConnectError("remote device is not connected.")
|
||||
raise LiteDeviceConnectError("remote device is not connected.",
|
||||
error_no="00402")
|
||||
|
||||
telnet.write(command.encode('ascii') + b"\n")
|
||||
while time.time() - start_time < timeout:
|
||||
data = telnet.read_until(bytes(command, encoding="utf8"),
|
||||
timeout=1)
|
||||
data = PATTERN.sub('', data.decode('gbk', 'ignore')).replace(
|
||||
"\r", "")
|
||||
result = "{}{}".format(result, data)
|
||||
if command in result:
|
||||
break
|
||||
|
||||
expect_result = [bytes(CPP_TEST_STANDARD_SIGN, encoding="utf8"),
|
||||
bytes(CPP_SYS_STANDARD_SIGN, encoding="utf8"),
|
||||
bytes(CPP_TEST_END_SIGN, encoding="utf8")]
|
||||
while time.time()-start_time < timeout:
|
||||
while time.time() - start_time < timeout:
|
||||
if not Scheduler.is_execute:
|
||||
raise ExecuteTerminate()
|
||||
raise ExecuteTerminate("Execute terminate", error_no="00300")
|
||||
_, _, data = telnet.expect(expect_result, timeout=1)
|
||||
data = PATTERN.sub('', data.decode('gbk', 'ignore'))
|
||||
result = result + data.replace("\r", "")
|
||||
|
||||
data = PATTERN.sub('', data.decode('gbk', 'ignore')).replace(
|
||||
"\r", "")
|
||||
result = "{}{}".format(result, data)
|
||||
if receiver and data:
|
||||
receiver.__read__(data)
|
||||
if check_read_test_end(result, command):
|
||||
break
|
||||
else:
|
||||
error_message = "execute command with timeout=%s " % command
|
||||
LOG.debug("error_message=%s" % error_message)
|
||||
error_message = "execute %s timed out %s " % (command, timeout)
|
||||
status = False
|
||||
|
||||
if receiver:
|
||||
if command in result:
|
||||
index = result.find(command) + len(command)
|
||||
result_output = result[index:]
|
||||
else:
|
||||
result_output = result
|
||||
generate_report(receiver, result_output)
|
||||
receiver.__done__()
|
||||
|
||||
if not status and command.startswith("uname"):
|
||||
raise LiteDeviceTimeout("Execute command time out:%s" % command)
|
||||
|
||||
return result, status, error_message
|
||||
|
||||
@ -138,34 +171,43 @@ class LiteHelper:
|
||||
def read_local_output_test(com=None, command=None, timeout=TIMEOUT,
|
||||
receiver=None):
|
||||
input_command = command
|
||||
linux_end_command = ""
|
||||
if "--gtest_output=" in command:
|
||||
linux_end_command = input_command.split(":")[1].split(
|
||||
"reports")[0].rstrip("/") + " #"
|
||||
error_message = ""
|
||||
start_time = time.time()
|
||||
result = ""
|
||||
status = True
|
||||
from xdevice import Scheduler
|
||||
|
||||
while time.time()-start_time < timeout:
|
||||
if not Scheduler.is_execute:
|
||||
raise ExecuteTerminate()
|
||||
while time.time() - start_time < timeout:
|
||||
data = com.readline().decode('gbk', errors='ignore')
|
||||
data = PATTERN.sub('', data)
|
||||
result = "{}{}".format(result, data.replace("\r", ""))
|
||||
data = PATTERN.sub('', data).replace("\r", "")
|
||||
result = "{}{}".format(result, data)
|
||||
if command in result or linux_end_command in result:
|
||||
break
|
||||
|
||||
while time.time() - start_time < timeout:
|
||||
if not Scheduler.is_execute:
|
||||
raise ExecuteTerminate("Execute terminate", error_no="00300")
|
||||
data = com.readline().decode('gbk', errors='ignore')
|
||||
data = PATTERN.sub('', data).replace("\r", "")
|
||||
result = "{}{}".format(result, data)
|
||||
if receiver and data:
|
||||
receiver.__read__(data)
|
||||
if check_read_test_end(result, input_command):
|
||||
break
|
||||
else:
|
||||
error_message = "execute command with timeout=%s " % command
|
||||
LOG.debug("error_message:%s" % error_message)
|
||||
error_message = "execute %s timed out %s " % (command, timeout)
|
||||
status = False
|
||||
|
||||
if receiver:
|
||||
if command in result:
|
||||
index = result.find(command) + len(command)
|
||||
result_output = result[index:]
|
||||
else:
|
||||
result_output = result
|
||||
generate_report(receiver, result_output)
|
||||
receiver.__done__()
|
||||
|
||||
if not status and command.startswith("uname"):
|
||||
raise LiteDeviceTimeout("Execute command time out:%s" % command)
|
||||
|
||||
LOG.info('Info: execute command success')
|
||||
return result, status, error_message
|
||||
|
||||
@staticmethod
|
||||
@ -178,17 +220,21 @@ class LiteHelper:
|
||||
from xdevice import Scheduler
|
||||
while True:
|
||||
if not Scheduler.is_execute:
|
||||
raise ExecuteTerminate()
|
||||
raise ExecuteTerminate("Execute terminate", error_no="00300")
|
||||
data = com.readline().decode('gbk', errors='ignore')
|
||||
data = PATTERN.sub('', data)
|
||||
if isinstance(input_command, list):
|
||||
data = "{} {}".format(get_current_time(), data)
|
||||
if data and receiver:
|
||||
receiver.__read__(data.replace("\r", ""))
|
||||
result = "{}{}".format(result, data.replace("\r", ""))
|
||||
if re.search(r"\d+\s+Tests\s+\d+\s+Failures\s+\d+\s+"
|
||||
r"Ignored", data):
|
||||
start = time.time()
|
||||
if (int(time.time()) - int(start)) > timeout:
|
||||
break
|
||||
if CTEST_END_SIGN in data:
|
||||
break
|
||||
else:
|
||||
result = "{}{}".format(
|
||||
result, data.replace("\r", "").replace("\n", "").strip())
|
||||
@ -197,11 +243,8 @@ class LiteHelper:
|
||||
if (int(time.time()) - int(start)) > timeout:
|
||||
return result, False, ""
|
||||
|
||||
result = PATTERN.sub('', result)
|
||||
if receiver:
|
||||
result = "{}{}".format(
|
||||
result, "{} {}".format(get_current_time(), CTEST_END_SIGN))
|
||||
generate_report(receiver, result)
|
||||
receiver.__done__()
|
||||
LOG.info('Info: execute command success')
|
||||
return result, True, ""
|
||||
|
||||
@ -227,10 +270,12 @@ class LiteHelper:
|
||||
timeout = args.get("timeout", TIMEOUT)
|
||||
receiver = args.get("receiver", None)
|
||||
if not com:
|
||||
raise LiteDeviceConnectError("local device is not connected.")
|
||||
raise LiteDeviceConnectError("local device is not connected.",
|
||||
error_no="00402")
|
||||
|
||||
LOG.info("local_%s execute command shell %s with timeout %ss" %
|
||||
(convert_port(com.port), command, str(timeout)))
|
||||
|
||||
LOG.info("%s \n execute command shell %s with timeout %s" %
|
||||
(com, command, str(timeout)))
|
||||
if isinstance(command, str):
|
||||
command = command.encode("utf-8")
|
||||
if command[-2:] != b"\r\n":
|
||||
@ -248,11 +293,92 @@ class LiteHelper:
|
||||
Execute command on the serial and read all the output from the serial.
|
||||
"""
|
||||
if not com:
|
||||
raise LiteDeviceConnectError("local device is not connected.")
|
||||
raise LiteDeviceConnectError("local device is not connected.",
|
||||
error_no="00402")
|
||||
|
||||
LOG.info(
|
||||
"%s \n execute command shell %s" % (com, command))
|
||||
"local_%s execute command shell %s" % (convert_port(com.port),
|
||||
command))
|
||||
command = command.encode("utf-8")
|
||||
if command[-2:] != b"\r\n":
|
||||
command = command.rstrip() + b'\r\n'
|
||||
com.write(command)
|
||||
|
||||
@staticmethod
|
||||
def execute_agent_cmd_with_timeout(self, command,
|
||||
**kwargs):
|
||||
import requests
|
||||
|
||||
base_url = self.base_url
|
||||
headers = self.headers
|
||||
local_source_dir = self.local_source_dir
|
||||
target_save_path = self.target_save_path
|
||||
args = kwargs
|
||||
command_type = args.get("type", None)
|
||||
sync = args.get("sync", 1)
|
||||
response = None
|
||||
try:
|
||||
if "cmd" == command_type:
|
||||
url = base_url + "/_processes/"
|
||||
data = {
|
||||
'cmd': command,
|
||||
'sync': sync,
|
||||
'stdoutRedirect': 'file',
|
||||
'stderrRedirect': 'file'
|
||||
}
|
||||
response = requests.post(url=url, headers=headers,
|
||||
data=json.dumps(data))
|
||||
if response.status_code == 200:
|
||||
LOG.info("connect OK")
|
||||
else:
|
||||
LOG.info("connect failed")
|
||||
response.close()
|
||||
|
||||
elif "put" == command_type:
|
||||
url = base_url + target_save_path + command
|
||||
data = open(local_source_dir + command, "rb")
|
||||
response = requests.put(url=url, headers=headers, data=data)
|
||||
if response.status_code == 200:
|
||||
LOG.info("{} upload file to {} "
|
||||
"success".format(local_source_dir,
|
||||
target_save_path))
|
||||
else:
|
||||
LOG.info(
|
||||
"{} upload file to {} fail".format(local_source_dir,
|
||||
target_save_path))
|
||||
response.close()
|
||||
elif "get" == command_type:
|
||||
url = base_url + target_save_path + command
|
||||
response = requests.get(url=url, headers=headers, stream=True)
|
||||
if response.status_code == 200:
|
||||
file_name = open(local_source_dir + command + "bak", "wb")
|
||||
for file_data in response.iter_content(chunk_size=512):
|
||||
file_name.write(file_data)
|
||||
file_name.close()
|
||||
LOG.info("from {} download file to {} success".format(
|
||||
target_save_path + command,
|
||||
local_source_dir))
|
||||
else:
|
||||
LOG.info("{} download file to {} fail".format(
|
||||
target_save_path + command,
|
||||
command,
|
||||
local_source_dir))
|
||||
elif "delete" == command_type:
|
||||
url = base_url + target_save_path + command
|
||||
LOG.info(url)
|
||||
response = requests.delete(url)
|
||||
if response.status_code == 200:
|
||||
LOG.info("delete {} file success".format(
|
||||
target_save_path + command))
|
||||
else:
|
||||
LOG.info("delete {} file fail".format(
|
||||
target_save_path + command))
|
||||
else:
|
||||
LOG.info("type error")
|
||||
except Exception as error_message:
|
||||
LOG.info("error_message:{}".format(error_message))
|
||||
finally:
|
||||
if response:
|
||||
response.close()
|
||||
|
||||
return "", True, ""
|
||||
|
@ -2,7 +2,7 @@
|
||||
# coding=utf-8
|
||||
|
||||
#
|
||||
# Copyright (c) 2020 Huawei Device Co., Ltd.
|
||||
# Copyright (c) 2020-2021 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
|
||||
@ -16,19 +16,17 @@
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
import sys
|
||||
from dataclasses import dataclass
|
||||
|
||||
from _core.config.config_manager import UserConfigManager
|
||||
from _core.exception import DeviceError
|
||||
from _core.exception import ParamError
|
||||
from _core.logger import platform_logger
|
||||
from _core.logger import change_logger_level
|
||||
from _core.plugin import Plugin
|
||||
from _core.plugin import get_plugin
|
||||
from _core.utils import convert_serial
|
||||
|
||||
__all__ = ["EnvironmentManager", "DeviceSelectionOption",
|
||||
"DeviceAllocationState"]
|
||||
"DeviceAllocationState", "Environment"]
|
||||
|
||||
LOG = platform_logger("ManagerEnv")
|
||||
|
||||
@ -61,7 +59,7 @@ class Environment(object):
|
||||
def add_device(self, device):
|
||||
if device.label == "phone":
|
||||
self.phone += 1
|
||||
device.device_id = "phone%s" % str(self.phone)
|
||||
device.device_id = "Phone%s" % str(self.phone)
|
||||
self.devices.append(device)
|
||||
|
||||
|
||||
@ -82,44 +80,39 @@ class EnvironmentManager(object):
|
||||
cls.__instance = super(EnvironmentManager, cls).__new__(cls)
|
||||
return cls.__instance
|
||||
|
||||
def __init__(self, environment=""):
|
||||
def __init__(self, environment="", user_config_file=""):
|
||||
if EnvironmentManager.__init_flag:
|
||||
return
|
||||
self.manager_device = None
|
||||
self.manager_lite = None
|
||||
self._get_device_manager(environment)
|
||||
self.managers = {}
|
||||
self.env_start(environment, user_config_file)
|
||||
EnvironmentManager.__init_flag = True
|
||||
|
||||
def _get_device_manager(self, environment=""):
|
||||
try:
|
||||
result = UserConfigManager(env=environment).get_user_config(
|
||||
"environment/support_device")
|
||||
except ParamError as error:
|
||||
LOG.exception("ParamError: %s" % error.args, exc_info=False)
|
||||
if not environment:
|
||||
sys.exit(0)
|
||||
else:
|
||||
raise error
|
||||
if result.get("device") == "true":
|
||||
managers_device = get_plugin(plugin_type=Plugin.MANAGER,
|
||||
plugin_id="device")
|
||||
if managers_device:
|
||||
self.manager_device = managers_device[0]
|
||||
self.manager_device.init_environment(environment)
|
||||
if result.get("device_lite") == "true":
|
||||
managers_lite = get_plugin(plugin_type=Plugin.MANAGER,
|
||||
plugin_id="device_lite")
|
||||
if managers_lite:
|
||||
self.manager_lite = managers_lite[0]
|
||||
self.manager_lite.init_environment(environment)
|
||||
def env_start(self, environment="", user_config_file=""):
|
||||
|
||||
log_level_dict = UserConfigManager(
|
||||
config_file=user_config_file, env=environment).get_log_level()
|
||||
|
||||
if log_level_dict:
|
||||
# change log level when load or reset EnvironmentManager object
|
||||
change_logger_level(log_level_dict)
|
||||
|
||||
manager_plugins = get_plugin(Plugin.MANAGER)
|
||||
for manager_plugin in manager_plugins:
|
||||
try:
|
||||
manager_instance = manager_plugin.__class__()
|
||||
manager_instance.init_environment(environment,
|
||||
user_config_file)
|
||||
self.managers[manager_instance.__class__.__name__] = \
|
||||
manager_instance
|
||||
except Exception as error:
|
||||
LOG.debug(error)
|
||||
|
||||
def env_stop(self):
|
||||
if self.manager_device:
|
||||
self.manager_device.env_stop()
|
||||
self.manager_device = None
|
||||
if self.manager_lite:
|
||||
self.manager_lite.devices_list = []
|
||||
self.manager_lite = None
|
||||
for manager in self.managers.values():
|
||||
manager.env_stop()
|
||||
manager.devices_list = []
|
||||
self.managers = {}
|
||||
|
||||
EnvironmentManager.__init_flag = False
|
||||
|
||||
def apply_environment(self, device_options):
|
||||
@ -128,61 +121,72 @@ class EnvironmentManager(object):
|
||||
device = self.apply_device(device_option)
|
||||
if device is not None:
|
||||
environment.add_device(device)
|
||||
device.extend_value = device_option.extend_value
|
||||
LOG.debug("device %s: extend value: %s", device.device_sn,
|
||||
device.extend_value)
|
||||
|
||||
return environment
|
||||
|
||||
def release_environment(self, environment):
|
||||
for device in environment.devices:
|
||||
device.extend_value = {}
|
||||
self.release_device(device)
|
||||
|
||||
def apply_device(self, device_option, timeout=10):
|
||||
|
||||
if not device_option.label or device_option.label == "phone":
|
||||
device_manager = self.manager_device
|
||||
for manager_type, manager in self.managers.items():
|
||||
support_labels = getattr(manager, "support_labels", [])
|
||||
if not support_labels:
|
||||
continue
|
||||
if device_option.label is None:
|
||||
if manager_type != "ManagerDevice":
|
||||
continue
|
||||
else:
|
||||
if support_labels and \
|
||||
device_option.label not in support_labels:
|
||||
continue
|
||||
device = manager.apply_device(device_option, timeout)
|
||||
if device:
|
||||
return device
|
||||
else:
|
||||
device_manager = self.manager_lite
|
||||
|
||||
if device_manager:
|
||||
return device_manager.apply_device(device_option, timeout)
|
||||
else:
|
||||
raise DeviceError(
|
||||
"%s is not supported, please check %s and "
|
||||
"environment config user_config.xml" %
|
||||
(str(device_option.test_driver), device_option.source_file))
|
||||
return None
|
||||
|
||||
def check_device_exist(self, device_options):
|
||||
"""
|
||||
Check if there are matched devices which can be allocated or available.
|
||||
"""
|
||||
devices = []
|
||||
for device_option in device_options:
|
||||
device_exist = False
|
||||
if self.manager_device:
|
||||
for device in self.manager_device.devices_list:
|
||||
for manager_type, manager in self.managers.items():
|
||||
support_labels = getattr(manager, "support_labels", [])
|
||||
if device_option.label is None:
|
||||
if manager_type != "ManagerDevice":
|
||||
continue
|
||||
else:
|
||||
if support_labels and \
|
||||
device_option.label not in support_labels:
|
||||
continue
|
||||
for device in manager.devices_list:
|
||||
if device.device_sn in devices:
|
||||
continue
|
||||
if device_option.matches(device, False):
|
||||
device_exist = True
|
||||
if self.manager_lite:
|
||||
for device in self.manager_lite.devices_list:
|
||||
if device_option.matches(device, False):
|
||||
device_exist = True
|
||||
if not device_exist:
|
||||
devices.append(device.device_sn)
|
||||
break
|
||||
else:
|
||||
continue
|
||||
break
|
||||
else:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def release_device(self, device):
|
||||
if self.manager_device and \
|
||||
device in self.manager_device.devices_list:
|
||||
self.manager_device.release_device(device)
|
||||
elif self.manager_lite and \
|
||||
device in self.manager_lite.devices_list:
|
||||
self.manager_lite.release_device(device)
|
||||
for manager in self.managers.values():
|
||||
if device in manager.devices_list:
|
||||
manager.release_device(device)
|
||||
|
||||
def list_devices(self):
|
||||
LOG.info("list devices.")
|
||||
if self.manager_device:
|
||||
self.manager_device.list_devices()
|
||||
if self.manager_lite:
|
||||
self.manager_lite.list_devices()
|
||||
for manager in self.managers.values():
|
||||
manager.list_devices()
|
||||
|
||||
|
||||
class DeviceSelectionOption(object):
|
||||
@ -195,18 +199,24 @@ class DeviceSelectionOption(object):
|
||||
self.label = label
|
||||
self.test_driver = test_source.test_type
|
||||
self.source_file = ""
|
||||
self.extend_value = {}
|
||||
|
||||
def get_label(self):
|
||||
return self.label
|
||||
|
||||
def matches(self, device, allocate=True):
|
||||
if not getattr(device, "task_state", True):
|
||||
return False
|
||||
if allocate and device.device_allocation_state != \
|
||||
DeviceAllocationState.available:
|
||||
return False
|
||||
|
||||
if not allocate and device.device_allocation_state == \
|
||||
DeviceAllocationState.ignored:
|
||||
return False
|
||||
if not allocate:
|
||||
if device.device_allocation_state != \
|
||||
DeviceAllocationState.available and \
|
||||
device.device_allocation_state != \
|
||||
DeviceAllocationState.allocated:
|
||||
return False
|
||||
|
||||
if len(self.device_sn) != 0 and device.device_sn not in self.device_sn:
|
||||
return False
|
||||
@ -222,3 +232,4 @@ class DeviceAllocationState:
|
||||
ignored = "Ignored"
|
||||
available = "Available"
|
||||
allocated = "Allocated"
|
||||
unusable = "Unusable"
|
||||
|
@ -2,7 +2,7 @@
|
||||
# coding=utf-8
|
||||
|
||||
#
|
||||
# Copyright (c) 2020 Huawei Device Co., Ltd.
|
||||
# Copyright (c) 2020-2021 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
|
||||
@ -17,17 +17,19 @@
|
||||
#
|
||||
|
||||
import time
|
||||
import threading
|
||||
|
||||
from _core.config.config_manager import UserConfigManager
|
||||
from _core.constants import DeviceOsType
|
||||
from _core.constants import ManagerType
|
||||
from _core.environment.manager_env import DeviceAllocationState
|
||||
from _core.exception import LiteDeviceConnectError
|
||||
from _core.exception import ParamError
|
||||
from _core.exception import LiteDeviceError
|
||||
from _core.plugin import Plugin
|
||||
from _core.plugin import get_plugin
|
||||
from _core.interface import IDeviceManager
|
||||
from _core.logger import platform_logger
|
||||
from _core.utils import convert_ip
|
||||
from _core.utils import convert_port
|
||||
|
||||
__all__ = ["ManagerLite"]
|
||||
|
||||
@ -43,12 +45,15 @@ class ManagerLite(IDeviceManager):
|
||||
|
||||
def __init__(self):
|
||||
self.devices_list = []
|
||||
self.list_con = threading.Condition()
|
||||
self.support_labels = ["ipcamera", "wifiiot", "watchGT"]
|
||||
|
||||
def init_environment(self, environment=""):
|
||||
def init_environment(self, environment="", user_config_file=""):
|
||||
device_lite = get_plugin(plugin_type=Plugin.DEVICE,
|
||||
plugin_id=DeviceOsType.lite)[0]
|
||||
|
||||
devices = UserConfigManager(env=environment).get_com_device(
|
||||
devices = UserConfigManager(
|
||||
config_file=user_config_file, env=environment).get_com_device(
|
||||
"environment/device")
|
||||
|
||||
for device in devices:
|
||||
@ -57,33 +62,50 @@ class ManagerLite(IDeviceManager):
|
||||
device_lite_instance.__init_device__(device)
|
||||
device_lite_instance.device_allocation_state = \
|
||||
DeviceAllocationState.available
|
||||
except (LiteDeviceConnectError, ParamError):
|
||||
except LiteDeviceError as exception:
|
||||
LOG.warning(exception)
|
||||
continue
|
||||
|
||||
self.devices_list.append(device_lite_instance)
|
||||
|
||||
def env_stop(self):
|
||||
pass
|
||||
|
||||
def apply_device(self, device_option, timeout=10):
|
||||
"""
|
||||
Request a device for testing that meets certain criteria.
|
||||
"""
|
||||
del timeout
|
||||
allocated_device = None
|
||||
LOG.debug("lite apply_device: apply lock")
|
||||
self.list_con.acquire()
|
||||
try:
|
||||
allocated_device = None
|
||||
for device in self.devices_list:
|
||||
if device_option.matches(device):
|
||||
device.device_allocation_state = \
|
||||
DeviceAllocationState.allocated
|
||||
LOG.debug("allocate device sn: %s, type: %s" % (
|
||||
device.__get_serial__(), device.__class__))
|
||||
return device
|
||||
time.sleep(10)
|
||||
return allocated_device
|
||||
finally:
|
||||
LOG.debug("lite apply_device: release lock")
|
||||
self.list_con.release()
|
||||
|
||||
for device in self.devices_list:
|
||||
if device_option.matches(device):
|
||||
def release_device(self, device):
|
||||
LOG.debug("lite release_device: apply lock")
|
||||
self.list_con.acquire()
|
||||
try:
|
||||
if device.device_allocation_state == \
|
||||
DeviceAllocationState.allocated:
|
||||
device.device_allocation_state = \
|
||||
DeviceAllocationState.allocated
|
||||
LOG.debug("allocate device sn: %s, type: %s" % (
|
||||
device.__get_serial__(), device.__class__))
|
||||
return device
|
||||
time.sleep(10)
|
||||
return allocated_device
|
||||
|
||||
@staticmethod
|
||||
def release_device(device):
|
||||
device.device_allocation_state = DeviceAllocationState.available
|
||||
LOG.debug("free device sn: %s, type: %s" % (
|
||||
device.__get_serial__(), device.__class__))
|
||||
DeviceAllocationState.available
|
||||
LOG.debug("free device sn: %s, type: %s" % (
|
||||
device.__get_serial__(), device.__class__))
|
||||
finally:
|
||||
LOG.debug("lite release_device: release lock")
|
||||
self.list_con.release()
|
||||
|
||||
def list_devices(self):
|
||||
print("lite devices:")
|
||||
@ -91,21 +113,22 @@ class ManagerLite(IDeviceManager):
|
||||
format("SerialPort/IP", "Baudrate/Port", "OsType", "Allocation",
|
||||
"Product", "ConnectType", "ComType"))
|
||||
for device in self.devices_list:
|
||||
if device.device_connect_type == "remote":
|
||||
if device.device_connect_type == "remote" or \
|
||||
device.device_connect_type == "agent":
|
||||
print("{0:<20}{1:<16}{2:<16}{3:<16}{4:<16}{5:<16}".format(
|
||||
device.remote_device.host,
|
||||
device.remote_device.port,
|
||||
convert_ip(device.device.host),
|
||||
convert_port(device.device.port),
|
||||
device.device_os_type,
|
||||
device.device_allocation_state,
|
||||
device.label,
|
||||
device.device_connect_type))
|
||||
else:
|
||||
for com_controller in device.local_device.com_dict:
|
||||
for com_controller in device.device.com_dict:
|
||||
print("{0:<20}{1:<16}{2:<16}{3:<16}{4:<16}{5:<16}{6:<16}".
|
||||
format(device.local_device.com_dict[
|
||||
com_controller].serial_port,
|
||||
device.local_device.com_dict[
|
||||
com_controller].baund_rate,
|
||||
format(convert_port(device.device.com_dict[
|
||||
com_controller].serial_port),
|
||||
device.device.com_dict[
|
||||
com_controller].baud_rate,
|
||||
device.device_os_type,
|
||||
device.device_allocation_state,
|
||||
device.label,
|
||||
|
@ -2,7 +2,7 @@
|
||||
# coding=utf-8
|
||||
|
||||
#
|
||||
# Copyright (c) 2020 Huawei Device Co., Ltd.
|
||||
# Copyright (c) 2020-2021 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
|
||||
@ -18,36 +18,40 @@
|
||||
|
||||
|
||||
class ParamError(Exception):
|
||||
def __init__(self, error_msg):
|
||||
super(ParamError, self).__init__(error_msg)
|
||||
def __init__(self, error_msg, error_no=""):
|
||||
super(ParamError, self).__init__(error_msg, error_no)
|
||||
self.error_msg = error_msg
|
||||
self.error_no = error_no
|
||||
|
||||
def __str__(self):
|
||||
return str(self.error_msg)
|
||||
|
||||
|
||||
class LiteDeviceError(Exception):
|
||||
def __init__(self, error_msg):
|
||||
super(LiteDeviceError, self).__init__(error_msg)
|
||||
def __init__(self, error_msg, error_no=""):
|
||||
super(LiteDeviceError, self).__init__(error_msg, error_no)
|
||||
self.error_msg = error_msg
|
||||
self.error_no = error_no
|
||||
|
||||
def __str__(self):
|
||||
return str(self.error_msg)
|
||||
|
||||
|
||||
class DeviceError(Exception):
|
||||
def __init__(self, error_msg):
|
||||
super(DeviceError, self).__init__(error_msg)
|
||||
def __init__(self, error_msg, error_no=""):
|
||||
super(DeviceError, self).__init__(error_msg, error_no)
|
||||
self.error_msg = error_msg
|
||||
self.error_no = error_no
|
||||
|
||||
def __str__(self):
|
||||
return str(self.error_msg)
|
||||
|
||||
|
||||
class ExecuteTerminate(Exception):
|
||||
def __init__(self, error_msg="ExecuteTerminate"):
|
||||
super(ExecuteTerminate, self).__init__(error_msg)
|
||||
def __init__(self, error_msg="ExecuteTerminate", error_no=""):
|
||||
super(ExecuteTerminate, self).__init__(error_msg, error_no)
|
||||
self.error_msg = error_msg
|
||||
self.error_no = error_no
|
||||
|
||||
def __str__(self):
|
||||
return str(self.error_msg)
|
||||
@ -58,115 +62,72 @@ class ReportException(Exception):
|
||||
Exception thrown when a shell command executed on a device takes too long
|
||||
to send its output.
|
||||
"""
|
||||
def __init__(self, error_msg="ReportException"):
|
||||
super(ReportException, self).__init__(error_msg)
|
||||
def __init__(self, error_msg="ReportException", error_no=""):
|
||||
super(ReportException, self).__init__(error_msg, error_no)
|
||||
self.error_msg = error_msg
|
||||
self.error_no = error_no
|
||||
|
||||
def __str__(self):
|
||||
return str(self.error_msg)
|
||||
|
||||
|
||||
class LiteParamError(LiteDeviceError):
|
||||
def __init__(self, error_msg, error_no=""):
|
||||
super(LiteParamError, self).__init__(error_msg, error_no)
|
||||
self.error_msg = error_msg
|
||||
self.error_no = error_no
|
||||
|
||||
def __str__(self):
|
||||
return str(self.error_msg)
|
||||
|
||||
|
||||
class LiteDeviceConnectError(LiteDeviceError):
|
||||
def __init__(self, error_msg):
|
||||
super(LiteDeviceConnectError, self).__init__(error_msg)
|
||||
def __init__(self, error_msg, error_no=""):
|
||||
super(LiteDeviceConnectError, self).__init__(error_msg, error_no)
|
||||
self.error_msg = error_msg
|
||||
self.error_no = error_no
|
||||
|
||||
def __str__(self):
|
||||
return str(self.error_msg)
|
||||
|
||||
|
||||
class LiteDeviceMountError(LiteDeviceError):
|
||||
def __init__(self, error_msg):
|
||||
super(LiteDeviceMountError, self).__init__(error_msg)
|
||||
def __init__(self, error_msg, error_no=""):
|
||||
super(LiteDeviceMountError, self).__init__(error_msg, error_no)
|
||||
self.error_msg = error_msg
|
||||
self.error_no = error_no
|
||||
|
||||
def __str__(self):
|
||||
return str(self.error_msg)
|
||||
|
||||
|
||||
class LiteDeviceReadOutputError(LiteDeviceError):
|
||||
def __init__(self, error_msg):
|
||||
super(LiteDeviceReadOutputError, self).__init__(error_msg)
|
||||
def __init__(self, error_msg, error_no=""):
|
||||
super(LiteDeviceReadOutputError, self).__init__(error_msg, error_no)
|
||||
self.error_msg = error_msg
|
||||
self.error_no = error_no
|
||||
|
||||
def __str__(self):
|
||||
return str(self.error_msg)
|
||||
|
||||
|
||||
class LiteDeviceExecuteCommandError(LiteDeviceError):
|
||||
def __init__(self, error_msg):
|
||||
super(LiteDeviceExecuteCommandError, self).__init__(error_msg)
|
||||
def __init__(self, error_msg, error_no=""):
|
||||
super(LiteDeviceExecuteCommandError, self).__init__(
|
||||
error_msg, error_no)
|
||||
self.error_msg = error_msg
|
||||
self.error_no = error_no
|
||||
|
||||
def __str__(self):
|
||||
return str(self.error_msg)
|
||||
|
||||
|
||||
class HdcCommandRejectedException(DeviceError):
|
||||
"""
|
||||
Exception thrown when hdc refuses a command.
|
||||
"""
|
||||
|
||||
def __init__(self, error_msg):
|
||||
super(HdcCommandRejectedException, self).__init__(error_msg)
|
||||
class LiteDeviceTimeout(LiteDeviceError):
|
||||
def __init__(self, error_msg, error_no=""):
|
||||
super(LiteDeviceTimeout, self).__init__(
|
||||
error_msg, error_no)
|
||||
self.error_msg = error_msg
|
||||
self.error_no = error_no
|
||||
|
||||
def __str__(self):
|
||||
return str(self.error_msg)
|
||||
|
||||
|
||||
class ShellCommandUnresponsiveException(DeviceError):
|
||||
"""
|
||||
Exception thrown when a shell command executed on a device takes too long
|
||||
to send its output.
|
||||
"""
|
||||
def __init__(self, error_msg="ShellCommandUnresponsiveException"):
|
||||
super(ShellCommandUnresponsiveException, self).__init__(error_msg)
|
||||
self.error_msg = error_msg
|
||||
|
||||
def __str__(self):
|
||||
return str(self.error_msg)
|
||||
|
||||
|
||||
class DeviceUnresponsiveException(DeviceError):
|
||||
"""
|
||||
Exception thrown when a shell command executed on a device takes too long
|
||||
to send its output.
|
||||
"""
|
||||
def __init__(self, error_msg="DeviceUnresponsiveException"):
|
||||
super(DeviceUnresponsiveException, self).__init__(error_msg)
|
||||
self.error_msg = error_msg
|
||||
|
||||
def __str__(self):
|
||||
return str(self.error_msg)
|
||||
|
||||
|
||||
class HdcError(DeviceError):
|
||||
"""
|
||||
Raised when there is an error in hdc operations.
|
||||
"""
|
||||
|
||||
def __init__(self, error_msg):
|
||||
super(HdcError, self).__init__(error_msg)
|
||||
self.error_msg = error_msg
|
||||
|
||||
def __str__(self):
|
||||
return str(self.error_msg)
|
||||
|
||||
|
||||
class ApkInstallError(DeviceError):
|
||||
def __init__(self, error_msg):
|
||||
super(ApkInstallError, self).__init__(error_msg)
|
||||
self.error_msg = error_msg
|
||||
|
||||
def __str__(self):
|
||||
return str(self.error_msg)
|
||||
|
||||
|
||||
class HapNotSupportTest(DeviceError):
|
||||
def __init__(self, error_msg):
|
||||
super(HapNotSupportTest, self).__init__(error_msg)
|
||||
self.error_msg = error_msg
|
||||
|
||||
def __str__(self):
|
||||
return str(self.error_msg)
|
||||
return str(self.error_msg)
|
0
src/xdevice/_core/executor/__init__.py
Executable file → Normal file
0
src/xdevice/_core/executor/__init__.py
Executable file → Normal file
@ -2,7 +2,7 @@
|
||||
# coding=utf-8
|
||||
|
||||
#
|
||||
# Copyright (c) 2020 Huawei Device Co., Ltd.
|
||||
# Copyright (c) 2020-2021 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
|
||||
@ -16,6 +16,7 @@
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
import copy
|
||||
import os
|
||||
import shutil
|
||||
import threading
|
||||
@ -23,10 +24,23 @@ import time
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
from concurrent.futures import wait
|
||||
|
||||
from _core.constants import ModeType
|
||||
from _core.constants import ConfigConst
|
||||
from _core.executor.request import Request
|
||||
from _core.logger import platform_logger
|
||||
from _core.plugin import Config
|
||||
from _core.utils import get_instance_name
|
||||
from _core.utils import get_filename_extension
|
||||
from _core.utils import check_mode
|
||||
from _core.exception import ParamError
|
||||
from _core.exception import ExecuteTerminate
|
||||
from _core.exception import DeviceError
|
||||
from _core.exception import LiteDeviceError
|
||||
from _core.report.reporter_helper import VisionHelper
|
||||
from _core.report.reporter_helper import ReportConstant
|
||||
from _core.report.reporter_helper import DataHelper
|
||||
from _core.report.reporter_helper import Suite
|
||||
from _core.report.reporter_helper import Case
|
||||
|
||||
LOG = platform_logger("Concurrent")
|
||||
|
||||
@ -73,6 +87,9 @@ class DriversThread(threading.Thread):
|
||||
|
||||
def set_listeners(self, listeners):
|
||||
self.listeners = listeners
|
||||
if self.environment is None:
|
||||
return
|
||||
|
||||
for listener in listeners:
|
||||
listener.device_sn = self.environment.devices[0].device_sn
|
||||
|
||||
@ -82,6 +99,7 @@ class DriversThread(threading.Thread):
|
||||
def run(self):
|
||||
from xdevice import Scheduler
|
||||
LOG.debug("thread id: %s start" % self.thread_id)
|
||||
start_time = time.time()
|
||||
execute_message = ExecuteMessage('', self.environment,
|
||||
self.test_driver, self.thread_id)
|
||||
driver, test = None, None
|
||||
@ -98,135 +116,165 @@ class DriversThread(threading.Thread):
|
||||
self._do_task_setup(driver_request)
|
||||
|
||||
# driver execute
|
||||
driver.__check_config__(driver_request.config)
|
||||
self.reset_device(driver_request.config)
|
||||
driver.__execute__(driver_request)
|
||||
|
||||
except Exception as exception:
|
||||
LOG.exception(
|
||||
"device: %s, exception: %s" % (
|
||||
self.environment.__get_serial__(), exception.args))
|
||||
error_no = getattr(exception, "error_no", "00000")
|
||||
if self.environment is None:
|
||||
LOG.exception("exception: %s", exception, exc_info=False,
|
||||
error_no=error_no)
|
||||
else:
|
||||
LOG.exception(
|
||||
"device: %s, exception: %s" % (
|
||||
self.environment.__get_serial__(), exception),
|
||||
exc_info=False, error_no=error_no)
|
||||
self.error_message = "{}: {}".format(
|
||||
get_instance_name(exception), str(exception.args))
|
||||
get_instance_name(exception), str(exception))
|
||||
|
||||
finally:
|
||||
if driver and test:
|
||||
execute_result = driver.__result__()
|
||||
if getattr(self.task.config, "history_report_path", ""):
|
||||
execute_result = self._inherit_execute_result(
|
||||
execute_result, test)
|
||||
execute_message.set_result(execute_result)
|
||||
self._handle_finally(driver, execute_message, start_time, test)
|
||||
|
||||
if self.error_message:
|
||||
execute_message.set_state(ExecuteMessage.DEVICE_ERROR)
|
||||
else:
|
||||
execute_message.set_state(ExecuteMessage.DEVICE_FINISH)
|
||||
LOG.debug("put thread %s result", self.thread_id)
|
||||
self.message_queue.put(execute_message)
|
||||
@staticmethod
|
||||
def reset_device(config):
|
||||
if getattr(config, "reboot_per_module", False):
|
||||
for device in config.environment.devices:
|
||||
device.reboot()
|
||||
|
||||
def _handle_finally(self, driver, execute_message, start_time, test):
|
||||
from xdevice import Scheduler
|
||||
# output execute time
|
||||
end_time = time.time()
|
||||
execute_time = VisionHelper.get_execute_time(int(
|
||||
end_time - start_time))
|
||||
source_content = self.test_driver[1].source.source_file or \
|
||||
self.test_driver[1].source.source_string
|
||||
LOG.info("Executed: %s, Execution Time: %s" % (
|
||||
source_content, execute_time))
|
||||
|
||||
# inherit history report under retry mode
|
||||
if driver and test:
|
||||
execute_result = driver.__result__()
|
||||
LOG.debug("execute_result:%s" % execute_result)
|
||||
if getattr(self.task.config, "history_report_path", ""):
|
||||
execute_result = self._inherit_execute_result(
|
||||
execute_result, test)
|
||||
execute_message.set_result(execute_result)
|
||||
|
||||
# set execute state
|
||||
if self.error_message:
|
||||
execute_message.set_state(ExecuteMessage.DEVICE_ERROR)
|
||||
else:
|
||||
execute_message.set_state(ExecuteMessage.DEVICE_FINISH)
|
||||
|
||||
# free environment
|
||||
if self.environment:
|
||||
LOG.debug("thread %s free environment",
|
||||
execute_message.get_thread_id())
|
||||
Scheduler.__free_environment__(
|
||||
execute_message.get_environment())
|
||||
LOG.info("")
|
||||
Scheduler.__free_environment__(execute_message.get_environment())
|
||||
|
||||
LOG.debug("put thread %s result", self.thread_id)
|
||||
self.message_queue.put(execute_message)
|
||||
LOG.info("")
|
||||
|
||||
def _do_task_setup(self, driver_request):
|
||||
if check_mode(ModeType.decc) or getattr(
|
||||
driver_request.config, ConfigConst.check_device, False):
|
||||
return
|
||||
|
||||
if self.environment is None:
|
||||
return
|
||||
|
||||
from xdevice import Scheduler
|
||||
for device in self.environment.devices:
|
||||
if getattr(device, "need_kit_setup", True):
|
||||
for kit in self.task.config.kits:
|
||||
if not Scheduler.is_execute:
|
||||
break
|
||||
kit.__setup__(device, request=driver_request)
|
||||
setattr(device, "need_kit_setup", False)
|
||||
if not getattr(device, ConfigConst.need_kit_setup, True):
|
||||
LOG.debug("device %s need kit setup is false" % device)
|
||||
continue
|
||||
|
||||
if getattr(driver_request, "product_params", "") and not getattr(
|
||||
self.task, "product_params", ""):
|
||||
product_params = getattr(driver_request, "product_params")
|
||||
if not isinstance(product_params, dict):
|
||||
LOG.warning("product params should be dict, %s",
|
||||
product_params)
|
||||
# do task setup for device
|
||||
kits_copy = copy.deepcopy(self.task.config.kits)
|
||||
setattr(device, ConfigConst.task_kits, kits_copy)
|
||||
for kit in getattr(device, ConfigConst.task_kits, []):
|
||||
if not Scheduler.is_execute:
|
||||
break
|
||||
try:
|
||||
kit.__setup__(device, request=driver_request)
|
||||
except (ParamError, ExecuteTerminate, DeviceError,
|
||||
LiteDeviceError, ValueError, TypeError,
|
||||
SyntaxError, AttributeError) as exception:
|
||||
error_no = getattr(exception, "error_no", "00000")
|
||||
LOG.exception(
|
||||
"task setup device: %s, exception: %s" % (
|
||||
self.environment.__get_serial__(),
|
||||
exception), exc_info=False, error_no=error_no)
|
||||
LOG.debug("set device %s need kit setup to false" % device)
|
||||
setattr(device, ConfigConst.need_kit_setup, False)
|
||||
|
||||
# set product_info to self.task
|
||||
if getattr(driver_request, ConfigConst.product_info, "") and not \
|
||||
getattr(self.task, ConfigConst.product_info, ""):
|
||||
product_info = getattr(driver_request, ConfigConst.product_info)
|
||||
if not isinstance(product_info, dict):
|
||||
LOG.warning("product info should be dict, %s",
|
||||
product_info)
|
||||
return
|
||||
setattr(self.task, "product_params", product_params)
|
||||
setattr(self.task, ConfigConst.product_info, product_info)
|
||||
|
||||
def _get_driver_request(self, root_desc, execute_message):
|
||||
config = Config()
|
||||
config.update(self.task.config.__dict__)
|
||||
config.update(copy.deepcopy(self.task.config).__dict__)
|
||||
config.environment = self.environment
|
||||
if getattr(config, "history_report_path", ""):
|
||||
# modify config.testargs
|
||||
history_report_path = getattr(config, "history_report_path", "")
|
||||
module_name = root_desc.source.module_name
|
||||
unpassed_test_params = self._get_unpassed_test_params(
|
||||
history_report_path, root_desc)
|
||||
history_report_path, module_name)
|
||||
if not unpassed_test_params:
|
||||
LOG.info("%s all test cases are passed, no need retry",
|
||||
root_desc.source.test_name)
|
||||
module_name)
|
||||
driver_request = Request(self.thread_id, root_desc,
|
||||
self.listeners, config)
|
||||
execute_message.set_request(driver_request)
|
||||
return None
|
||||
test_args = getattr(config, "testargs", {})
|
||||
test_params = []
|
||||
for unpassed_test_param in unpassed_test_params:
|
||||
if unpassed_test_param not in test_params:
|
||||
test_params.append(unpassed_test_param)
|
||||
test_args["test"] = test_params
|
||||
if "class" in test_args.keys():
|
||||
test_args.pop("class")
|
||||
setattr(config, "testargs", test_args)
|
||||
if unpassed_test_params[0] != module_name and \
|
||||
unpassed_test_params[0] != str(module_name).split(".")[0]:
|
||||
test_args = getattr(config, "testargs", {})
|
||||
test_params = []
|
||||
for unpassed_test_param in unpassed_test_params:
|
||||
if unpassed_test_param not in test_params:
|
||||
test_params.append(unpassed_test_param)
|
||||
test_args["test"] = test_params
|
||||
if "class" in test_args.keys():
|
||||
test_args.pop("class")
|
||||
setattr(config, "testargs", test_args)
|
||||
|
||||
for listener in self.listeners:
|
||||
LOG.debug("thread id %s, listener %s" % (
|
||||
self.thread_id, listener))
|
||||
LOG.debug("thread id %s, listener %s" % (self.thread_id, listener))
|
||||
driver_request = Request(self.thread_id, root_desc, self.listeners,
|
||||
config)
|
||||
execute_message.set_request(driver_request)
|
||||
return driver_request
|
||||
|
||||
def _get_unpassed_test_params(self, history_report_path, root_desc):
|
||||
from xdevice import Scheduler
|
||||
if Scheduler.mode == "decc":
|
||||
return self._get_decc_unpassed_params(root_desc)
|
||||
|
||||
@classmethod
|
||||
def _get_unpassed_test_params(cls, history_report_path, module_name):
|
||||
unpassed_test_params = []
|
||||
# get target_report_file
|
||||
history_report_file = ""
|
||||
report_file_suffix = "%s.xml" % root_desc.source.test_name
|
||||
for root_dir, _, files in os.walk(history_report_path):
|
||||
for report_file in files:
|
||||
if report_file.endswith(report_file_suffix):
|
||||
history_report_file = os.path.abspath(
|
||||
os.path.join(root_dir, report_file))
|
||||
break
|
||||
if not history_report_file:
|
||||
from _core.report.result_reporter import ResultReporter
|
||||
params = ResultReporter.get_task_info_params(history_report_path)
|
||||
if not params:
|
||||
return unpassed_test_params
|
||||
|
||||
# append unpassed_test_param
|
||||
self._append_unpassed_test_param(history_report_file,
|
||||
unpassed_test_params)
|
||||
failed_list = params[3].get(module_name, [])
|
||||
if not failed_list:
|
||||
failed_list = params[3].get(str(module_name).split(".")[0], [])
|
||||
unpassed_test_params.extend(failed_list)
|
||||
LOG.debug("get unpassed test params %s", unpassed_test_params)
|
||||
return unpassed_test_params
|
||||
|
||||
@classmethod
|
||||
def _get_decc_unpassed_params(cls, root_desc):
|
||||
from xdevice import SuiteReporter
|
||||
from _core.report.reporter_helper import DataHelper
|
||||
from _core.report.reporter_helper import ReportConstant
|
||||
|
||||
if not SuiteReporter.get_report_result():
|
||||
LOG.warning("decc retry command no previous result")
|
||||
return []
|
||||
summary_result = SuiteReporter.get_report_result()[0][1]
|
||||
summary_element = DataHelper.parse_data_report(summary_result)
|
||||
for child in summary_element:
|
||||
if child.get(ReportConstant.module) == root_desc.source.test_name:
|
||||
return []
|
||||
return [root_desc.source.test_name]
|
||||
|
||||
@classmethod
|
||||
def _append_unpassed_test_param(cls, history_report_file,
|
||||
unpassed_test_params):
|
||||
from _core.report.reporter_helper import DataHelper
|
||||
from _core.report.reporter_helper import Suite
|
||||
|
||||
testsuites_element = DataHelper.parse_data_report(history_report_file)
|
||||
for testsuite_element in testsuites_element:
|
||||
suite_name = testsuite_element.get("name", "")
|
||||
@ -240,12 +288,8 @@ class DriversThread(threading.Thread):
|
||||
unpassed_test_params.append(unpassed_test_param)
|
||||
|
||||
def _inherit_execute_result(self, execute_result, root_desc):
|
||||
from xdevice import Scheduler
|
||||
if Scheduler.mode == "decc":
|
||||
return
|
||||
|
||||
# get history execute result
|
||||
execute_result_name = "%s.xml" % root_desc.source.test_name
|
||||
module_name = root_desc.source.module_name
|
||||
execute_result_name = "%s.xml" % module_name
|
||||
history_execute_result = self._get_history_execute_result(
|
||||
execute_result_name)
|
||||
if not history_execute_result:
|
||||
@ -253,30 +297,56 @@ class DriversThread(threading.Thread):
|
||||
execute_result_name)
|
||||
return execute_result
|
||||
|
||||
if not os.path.exists(execute_result):
|
||||
result_dir = os.path.join(self.task.config.report_path, "result")
|
||||
os.makedirs(result_dir, exist_ok=True)
|
||||
target_execute_result = os.path.join(result_dir,
|
||||
execute_result_name)
|
||||
shutil.copyfile(history_execute_result, target_execute_result)
|
||||
LOG.info("copy %s to %s" % (history_execute_result,
|
||||
target_execute_result))
|
||||
return target_execute_result
|
||||
if not check_mode(ModeType.decc):
|
||||
if not os.path.exists(execute_result):
|
||||
result_dir = \
|
||||
os.path.join(self.task.config.report_path, "result")
|
||||
os.makedirs(result_dir, exist_ok=True)
|
||||
target_execute_result = os.path.join(result_dir,
|
||||
execute_result_name)
|
||||
shutil.copyfile(history_execute_result, target_execute_result)
|
||||
LOG.info("copy %s to %s" % (history_execute_result,
|
||||
target_execute_result))
|
||||
return target_execute_result
|
||||
|
||||
real_execute_result = self._get_real_execute_result(execute_result)
|
||||
|
||||
# inherit history execute result
|
||||
from _core.report.reporter_helper import DataHelper
|
||||
LOG.info("inherit history execute result: %s", history_execute_result)
|
||||
testsuites_element = DataHelper.parse_data_report(real_execute_result)
|
||||
if self._is_empty_report(testsuites_element):
|
||||
if check_mode(ModeType.decc):
|
||||
LOG.info("empty report no need to inherit history execute"
|
||||
" result")
|
||||
else:
|
||||
LOG.info("empty report '%s' no need to inherit history execute"
|
||||
" result", history_execute_result)
|
||||
return execute_result
|
||||
|
||||
real_history_execute_result = self._get_real_history_execute_result(
|
||||
history_execute_result, module_name)
|
||||
|
||||
history_testsuites_element = DataHelper.parse_data_report(
|
||||
history_execute_result)
|
||||
testsuites_element = DataHelper.parse_data_report(execute_result)
|
||||
real_history_execute_result)
|
||||
if self._is_empty_report(history_testsuites_element):
|
||||
LOG.info("history report '%s' is empty", history_execute_result)
|
||||
return execute_result
|
||||
if check_mode(ModeType.decc):
|
||||
LOG.info("inherit history execute result")
|
||||
else:
|
||||
LOG.info("inherit history execute result: %s",
|
||||
history_execute_result)
|
||||
self._inherit_element(history_testsuites_element, testsuites_element)
|
||||
|
||||
# generate inherit execute result
|
||||
DataHelper.generate_report(testsuites_element, execute_result)
|
||||
if check_mode(ModeType.decc):
|
||||
from xdevice import SuiteReporter
|
||||
SuiteReporter.append_report_result(
|
||||
(execute_result, DataHelper.to_string(testsuites_element)))
|
||||
else:
|
||||
# generate inherit execute result
|
||||
DataHelper.generate_report(testsuites_element, execute_result)
|
||||
return execute_result
|
||||
|
||||
def _inherit_element(self, history_testsuites_element, testsuites_element):
|
||||
from _core.report.reporter_helper import ReportConstant
|
||||
for history_testsuite_element in history_testsuites_element:
|
||||
history_testsuite_name = history_testsuite_element.get("name", "")
|
||||
target_testsuite_element = None
|
||||
@ -309,7 +379,12 @@ class DriversThread(threading.Thread):
|
||||
testsuites_element.set(ReportConstant.tests, str(inherited_test))
|
||||
|
||||
def _get_history_execute_result(self, execute_result_name):
|
||||
history_execute_result = ""
|
||||
if execute_result_name.endswith(".xml"):
|
||||
execute_result_name = execute_result_name[:-4]
|
||||
history_execute_result = \
|
||||
self._get_data_report_from_record(execute_result_name)
|
||||
if history_execute_result:
|
||||
return history_execute_result
|
||||
for root_dir, _, files in os.walk(
|
||||
self.task.config.history_report_path):
|
||||
for result_file in files:
|
||||
@ -320,8 +395,6 @@ class DriversThread(threading.Thread):
|
||||
|
||||
@classmethod
|
||||
def _check_testcase_pass(cls, history_testcase_element):
|
||||
from _core.report.reporter_helper import ReportConstant
|
||||
from _core.report.reporter_helper import Case
|
||||
case = Case()
|
||||
case.result = history_testcase_element.get(ReportConstant.result, "")
|
||||
case.status = history_testcase_element.get(ReportConstant.status, "")
|
||||
@ -334,6 +407,61 @@ class DriversThread(threading.Thread):
|
||||
|
||||
return case.is_passed()
|
||||
|
||||
@classmethod
|
||||
def _is_empty_report(cls, testsuites_element):
|
||||
if len(testsuites_element) < 1:
|
||||
return True
|
||||
if len(testsuites_element) >= 2:
|
||||
return False
|
||||
|
||||
if int(testsuites_element[0].get(ReportConstant.unavailable, 0)) > 0:
|
||||
return True
|
||||
return False
|
||||
|
||||
def _get_data_report_from_record(self, execute_result_name):
|
||||
history_report_path = \
|
||||
getattr(self.task.config, "history_report_path", "")
|
||||
if history_report_path:
|
||||
from _core.report.result_reporter import ResultReporter
|
||||
params = ResultReporter.get_task_info_params(history_report_path)
|
||||
if params:
|
||||
report_data_dict = dict(params[4])
|
||||
if execute_result_name in report_data_dict.keys():
|
||||
return report_data_dict.get(execute_result_name)
|
||||
elif execute_result_name.split(".")[0] in \
|
||||
report_data_dict.keys():
|
||||
return report_data_dict.get(
|
||||
execute_result_name.split(".")[0])
|
||||
return ""
|
||||
|
||||
@classmethod
|
||||
def _get_real_execute_result(cls, execute_result):
|
||||
from xdevice import SuiteReporter
|
||||
LOG.debug("get_real_execute_result length is: %s" %
|
||||
len(SuiteReporter.get_report_result()))
|
||||
if check_mode(ModeType.decc):
|
||||
for suite_report, report_result in \
|
||||
SuiteReporter.get_report_result():
|
||||
if os.path.splitext(suite_report)[0] == \
|
||||
os.path.splitext(execute_result)[0]:
|
||||
return report_result
|
||||
return ""
|
||||
else:
|
||||
return execute_result
|
||||
|
||||
@classmethod
|
||||
def _get_real_history_execute_result(cls, history_execute_result,
|
||||
module_name):
|
||||
from xdevice import SuiteReporter
|
||||
LOG.debug("get_real_history_execute_result: %s" %
|
||||
SuiteReporter.history_report_result)
|
||||
if check_mode(ModeType.decc):
|
||||
virtual_report_path, report_result = SuiteReporter. \
|
||||
get_history_result_by_module(module_name)
|
||||
return report_result
|
||||
else:
|
||||
return history_execute_result
|
||||
|
||||
|
||||
class QueueMonitorThread(threading.Thread):
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
# coding=utf-8
|
||||
|
||||
#
|
||||
# Copyright (c) 2020 Huawei Device Co., Ltd.
|
||||
# Copyright (c) 2020-2021 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
|
||||
@ -21,11 +21,12 @@ import uuid
|
||||
from dataclasses import dataclass
|
||||
|
||||
from _core.plugin import Plugin
|
||||
from _core.plugin import get_plugin
|
||||
from _core.constants import ListenerType
|
||||
from _core.constants import TestType
|
||||
from _core.interface import LifeCycle
|
||||
from _core.interface import IListener
|
||||
from _core.logger import platform_logger
|
||||
from _core.report.result_reporter import ResultReporter
|
||||
from _core.report.suite_reporter import SuiteReporter
|
||||
from _core.report.suite_reporter import ResultCode
|
||||
from _core.report.encrypt import check_pub_key_exist
|
||||
@ -76,7 +77,7 @@ class SuitesResult:
|
||||
stacktrace = ""
|
||||
run_time = 0
|
||||
is_completed = False
|
||||
product_params = {}
|
||||
product_info = {}
|
||||
|
||||
|
||||
@dataclass
|
||||
@ -245,6 +246,10 @@ class ReportListener(IListener):
|
||||
else:
|
||||
return self.tests.get(self.current_test_id)
|
||||
|
||||
def _remove_current_test_result(self):
|
||||
if self.current_test_id in self.tests:
|
||||
del self.tests[self.current_test_id]
|
||||
|
||||
def __started__(self, lifecycle, test_result):
|
||||
if lifecycle == LifeCycle.TestSuites:
|
||||
suites = self._get_suite_result(test_result=test_result,
|
||||
@ -300,10 +305,10 @@ class ReportListener(IListener):
|
||||
result_dir = os.path.join(self.report_path, "result")
|
||||
os.makedirs(result_dir, exist_ok=True)
|
||||
suites_name = kwargs.get("suites_name", "")
|
||||
product_params = kwargs.get("product_params", "")
|
||||
product_info = kwargs.get("product_info", "")
|
||||
suite_report = SuiteReporter(self.result, suites_name,
|
||||
result_dir,
|
||||
product_params=product_params)
|
||||
product_info=product_info)
|
||||
suite_report.generate_data_report()
|
||||
elif lifecycle == LifeCycle.TestCase:
|
||||
test = self._get_test_result(test_result=test_result, create=False)
|
||||
@ -311,18 +316,21 @@ class ReportListener(IListener):
|
||||
test.stacktrace = test_result.stacktrace
|
||||
test.code = test_result.code
|
||||
elif lifecycle == LifeCycle.TestTask:
|
||||
result_report = ResultReporter(report_path=self.report_path,
|
||||
task_info=test_result)
|
||||
result_report.generate_reports()
|
||||
test_type = str(kwargs.get("test_type", TestType.all))
|
||||
reporter = get_plugin(plugin_type=Plugin.REPORTER,
|
||||
plugin_id=test_type)
|
||||
if not reporter:
|
||||
reporter = get_plugin(plugin_type=Plugin.REPORTER,
|
||||
plugin_id=TestType.all)[0]
|
||||
else:
|
||||
reporter = reporter[0]
|
||||
reporter.__generate_reports__(self.report_path,
|
||||
task_info=test_result)
|
||||
|
||||
def __skipped__(self, lifecycle, test_result):
|
||||
if lifecycle == LifeCycle.TestSuite:
|
||||
suite = self._get_suite_result(test_result=test_result,
|
||||
create=False)
|
||||
suite.code = ResultCode.SKIPPED.value
|
||||
elif lifecycle == LifeCycle.TestCase:
|
||||
test = self._get_test_result(test_result=test_result, create=False)
|
||||
test.code = ResultCode.SKIPPED.value
|
||||
del test_result
|
||||
if lifecycle == LifeCycle.TestCase:
|
||||
self._remove_current_test_result()
|
||||
|
||||
def __failed__(self, lifecycle, test_result):
|
||||
if lifecycle == LifeCycle.TestSuite:
|
||||
|
@ -2,7 +2,7 @@
|
||||
# coding=utf-8
|
||||
|
||||
#
|
||||
# Copyright (c) 2020 Huawei Device Co., Ltd.
|
||||
# Copyright (c) 2020-2021 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
|
||||
@ -19,6 +19,8 @@
|
||||
import datetime
|
||||
import os
|
||||
|
||||
from _core.constants import ModeType
|
||||
from _core.constants import ConfigConst
|
||||
from _core.exception import ParamError
|
||||
from _core.executor.source import TestSource
|
||||
from _core.logger import platform_logger
|
||||
@ -68,20 +70,20 @@ class Task:
|
||||
|
||||
def init(self, config):
|
||||
from xdevice import Variables
|
||||
from xdevice import Scheduler
|
||||
start_time = datetime.datetime.now()
|
||||
LOG.debug("StartTime=%s" % start_time.strftime("%Y-%m-%d %H:%M:%S"))
|
||||
|
||||
self.config.update(config.__dict__)
|
||||
if config.reportpath == "":
|
||||
if getattr(config, ConfigConst.report_path, "") == "":
|
||||
Variables.task_name = start_time.strftime('%Y-%m-%d-%H-%M-%S')
|
||||
else:
|
||||
Variables.task_name = config.reportpath
|
||||
Variables.task_name = config.report_path
|
||||
|
||||
# create a report folder to store test report
|
||||
report_path = os.path.join(Variables.exec_dir,
|
||||
Variables.report_vars.report_dir,
|
||||
Variables.task_name)
|
||||
LOG.info("Report path: %s", report_path)
|
||||
os.makedirs(report_path, exist_ok=True)
|
||||
self._check_report_path(report_path)
|
||||
|
||||
@ -99,6 +101,9 @@ class Task:
|
||||
self.config.report_path = report_path
|
||||
self.config.log_path = log_path
|
||||
self.config.start_time = start_time.strftime("%Y-%m-%d %H:%M:%S")
|
||||
Scheduler.start_task_log(self.config.log_path)
|
||||
Scheduler.start_encrypt_log(self.config.log_path)
|
||||
LOG.info("Report path: %s", report_path)
|
||||
|
||||
def _get_task_dir(self, task_file):
|
||||
from xdevice import Variables
|
||||
@ -108,15 +113,16 @@ class Task:
|
||||
if os.path.normcase(Variables.exec_dir) == \
|
||||
os.path.normcase(Variables.top_dir):
|
||||
raise ParamError("task file %s not exists, please add task "
|
||||
"file to '%s'" % (
|
||||
task_file, exec_task_dir))
|
||||
"file to '%s'" % (task_file, exec_task_dir),
|
||||
error_no="00101")
|
||||
|
||||
top_task_dir = os.path.abspath(
|
||||
os.path.join(Variables.top_dir, self.TASK_CONFIG_DIR))
|
||||
if not os.path.exists(os.path.join(top_task_dir, task_file)):
|
||||
raise ParamError("task file %s not exists, please add task "
|
||||
"file to '%s' or '%s'" % (
|
||||
task_file, exec_task_dir, top_task_dir))
|
||||
task_file, exec_task_dir, top_task_dir),
|
||||
error_no="00101")
|
||||
else:
|
||||
return top_task_dir
|
||||
else:
|
||||
@ -125,7 +131,8 @@ class Task:
|
||||
def _load_task(self, task_dir, file_name):
|
||||
task_file = os.path.join(task_dir, file_name)
|
||||
if not os.path.exists(task_file):
|
||||
raise ParamError("task file %s not exists" % task_file)
|
||||
raise ParamError("task file %s not exists" % task_file,
|
||||
error_no="00101")
|
||||
|
||||
# add kits to self.config
|
||||
json_config = JsonParser(task_file)
|
||||
@ -140,9 +147,11 @@ class Task:
|
||||
self.root = root
|
||||
self._init_driver(root)
|
||||
if not self.test_drivers:
|
||||
raise ParamError("no test driver to execute")
|
||||
LOG.error("no test driver to execute", error_no="00106")
|
||||
|
||||
def _init_driver(self, test_descriptor):
|
||||
from xdevice import Scheduler
|
||||
|
||||
plugin_id = None
|
||||
source = test_descriptor.source
|
||||
|
||||
@ -150,15 +159,20 @@ class Task:
|
||||
if source.test_type is not None:
|
||||
plugin_id = source.test_type
|
||||
else:
|
||||
LOG.error("'%s' no test driver specified" % source.test_name)
|
||||
LOG.error("'%s' no test driver specified" % source.test_name,
|
||||
error_no="00106")
|
||||
|
||||
drivers = get_plugin(plugin_type=Plugin.DRIVER, plugin_id=plugin_id)
|
||||
if plugin_id is not None:
|
||||
if len(drivers) == 0:
|
||||
LOG.error("'%s' can not find test driver '%s'" %
|
||||
(source.test_name, plugin_id))
|
||||
error_message = "'%s' can not find test driver '%s'" % (
|
||||
source.test_name, plugin_id)
|
||||
LOG.error(error_message, error_no="00106")
|
||||
if Scheduler.mode == ModeType.decc:
|
||||
error_message = "Load Error[00106]"
|
||||
Scheduler.report_not_executed(self.config.report_path, [
|
||||
("", test_descriptor)], error_message)
|
||||
else:
|
||||
from xdevice import Scheduler
|
||||
check_result = False
|
||||
for driver in drivers:
|
||||
driver_instance = driver.__class__()
|
||||
@ -172,7 +186,7 @@ class Task:
|
||||
break
|
||||
if check_result is False:
|
||||
LOG.error("'%s' can not find suitable test driver '%s'" %
|
||||
(source.test_name, plugin_id))
|
||||
(source.test_name, plugin_id), error_no="00106")
|
||||
|
||||
for desc in test_descriptor.children:
|
||||
self._init_driver(desc)
|
||||
@ -182,7 +196,8 @@ class Task:
|
||||
for _, _, files in os.walk(report_path):
|
||||
for _file in files:
|
||||
if _file.endswith(".xml"):
|
||||
raise ParamError("xml file exists in '%s'" % report_path)
|
||||
raise ParamError("xml file exists in '%s'" % report_path,
|
||||
error_no="00105")
|
||||
|
||||
|
||||
class Request:
|
||||
@ -201,3 +216,49 @@ class Request:
|
||||
|
||||
def get_config(self):
|
||||
return self.config
|
||||
|
||||
def get(self, key=None, default=""):
|
||||
# get value from self.config
|
||||
if not key:
|
||||
return default
|
||||
return getattr(self.config, key, default)
|
||||
|
||||
def get_devices(self):
|
||||
if self.config is None:
|
||||
return []
|
||||
if not hasattr(self.config, "environment"):
|
||||
return []
|
||||
if not hasattr(self.config.environment, "devices"):
|
||||
return []
|
||||
return getattr(self.config.environment, "devices", [])
|
||||
|
||||
def get_config_file(self):
|
||||
return self._get_source_value("config_file")
|
||||
|
||||
def get_source_file(self):
|
||||
return self._get_source_value("source_file")
|
||||
|
||||
def get_test_name(self):
|
||||
return self._get_source_value("test_name")
|
||||
|
||||
def get_source_string(self):
|
||||
return self._get_source_value("source_string")
|
||||
|
||||
def get_test_type(self):
|
||||
return self._get_source_value("test_type")
|
||||
|
||||
def get_module_name(self):
|
||||
return self._get_source_value("module_name")
|
||||
|
||||
def _get_source(self):
|
||||
if not hasattr(self.root, "source"):
|
||||
return ""
|
||||
return getattr(self.root, "source", "")
|
||||
|
||||
def _get_source_value(self, key=None, default=""):
|
||||
if not key:
|
||||
return default
|
||||
source = self._get_source()
|
||||
if not source:
|
||||
return default
|
||||
return getattr(source, key, default)
|
||||
|
@ -2,7 +2,7 @@
|
||||
# coding=utf-8
|
||||
|
||||
#
|
||||
# Copyright (c) 2020 Huawei Device Co., Ltd.
|
||||
# Copyright (c) 2020-2021 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
|
||||
@ -16,6 +16,7 @@
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
import copy
|
||||
import datetime
|
||||
import os
|
||||
import queue
|
||||
@ -24,6 +25,8 @@ import uuid
|
||||
from xml.etree import ElementTree
|
||||
|
||||
from _core.utils import unique_id
|
||||
from _core.utils import check_mode
|
||||
from _core.utils import get_sub_path
|
||||
from _core.utils import get_filename_extension
|
||||
from _core.utils import convert_serial
|
||||
from _core.utils import get_instance_name
|
||||
@ -32,6 +35,8 @@ from _core.utils import check_result_report
|
||||
from _core.environment.manager_env import EnvironmentManager
|
||||
from _core.environment.manager_env import DeviceSelectionOption
|
||||
from _core.exception import ParamError
|
||||
from _core.exception import ExecuteTerminate
|
||||
from _core.exception import LiteDeviceError
|
||||
from _core.exception import DeviceError
|
||||
from _core.interface import LifeCycle
|
||||
from _core.executor.request import Request
|
||||
@ -45,11 +50,12 @@ from _core.report.reporter_helper import ReportConstant
|
||||
from _core.report.reporter_helper import Case
|
||||
from _core.report.reporter_helper import DataHelper
|
||||
from _core.constants import TestExecType
|
||||
from _core.constants import CKit
|
||||
from _core.constants import ModeType
|
||||
from _core.constants import DeviceLabelType
|
||||
from _core.constants import SchedulerType
|
||||
from _core.constants import HostDrivenTestType
|
||||
from _core.constants import ListenerType
|
||||
from _core.executor.concurrent import Concurrent
|
||||
from _core.constants import ConfigConst
|
||||
from _core.executor.concurrent import DriversThread
|
||||
from _core.executor.concurrent import QueueMonitorThread
|
||||
from _core.executor.source import TestSetSource
|
||||
@ -58,6 +64,8 @@ from _core.executor.source import find_testdict_descriptors
|
||||
from _core.logger import platform_logger
|
||||
from _core.logger import add_task_file_handler
|
||||
from _core.logger import remove_task_file_handler
|
||||
from _core.logger import add_encrypt_file_handler
|
||||
from _core.logger import remove_encrypt_file_handler
|
||||
|
||||
__all__ = ["Scheduler"]
|
||||
LOG = platform_logger("Scheduler")
|
||||
@ -95,8 +103,8 @@ class Scheduler(object):
|
||||
return task
|
||||
|
||||
def __execute__(self, task):
|
||||
error_message = ""
|
||||
try:
|
||||
self._start_task_logcat(task.config.log_path)
|
||||
Scheduler.is_execute = True
|
||||
if Scheduler.command_queue:
|
||||
LOG.debug("Run command: %s" % Scheduler.command_queue[-1])
|
||||
@ -107,6 +115,22 @@ class Scheduler(object):
|
||||
if len(Scheduler.command_queue) > self.max_command_num:
|
||||
Scheduler.command_queue.pop(0)
|
||||
|
||||
if getattr(task.config, ConfigConst.test_environment, ""):
|
||||
self._reset_environment(task.config.get(
|
||||
ConfigConst.test_environment, ""))
|
||||
elif getattr(task.config, ConfigConst.configfile, ""):
|
||||
self._reset_environment(config_file=task.config.get(
|
||||
ConfigConst.configfile, ""))
|
||||
|
||||
# do with the count of repeat about a task
|
||||
if getattr(task.config, ConfigConst.repeat, 0) > 0:
|
||||
drivers_list = list()
|
||||
for repeat_index in range(task.config.repeat):
|
||||
for driver_index in range(len(task.test_drivers)):
|
||||
drivers_list.append(
|
||||
copy.deepcopy(task.test_drivers[driver_index]))
|
||||
task.test_drivers = drivers_list
|
||||
|
||||
self.test_number = len(task.test_drivers)
|
||||
|
||||
if task.config.exectype == TestExecType.device_test:
|
||||
@ -116,11 +140,22 @@ class Scheduler(object):
|
||||
else:
|
||||
LOG.info("exec type %s is bypassed" % task.config.exectype)
|
||||
|
||||
if Scheduler.upload_address:
|
||||
Scheduler.upload_task_result(task)
|
||||
Scheduler.upload_report_end()
|
||||
except (ParamError, ValueError, TypeError, SyntaxError, AttributeError,
|
||||
DeviceError, LiteDeviceError, ExecuteTerminate) as exception:
|
||||
error_no = getattr(exception, "error_no", "")
|
||||
error_message = "%s[%s]" % (str(exception), error_no) \
|
||||
if error_no else str(exception)
|
||||
error_no = error_no if error_no else "00000"
|
||||
LOG.exception(exception, exc_info=False, error_no=error_no)
|
||||
|
||||
finally:
|
||||
self._stop_task_logcat()
|
||||
if getattr(task.config, ConfigConst.test_environment, "") or\
|
||||
getattr(task.config, ConfigConst.configfile, ""):
|
||||
self._restore_environment()
|
||||
|
||||
if Scheduler.upload_address:
|
||||
Scheduler.upload_task_result(task, error_message)
|
||||
Scheduler.upload_report_end()
|
||||
|
||||
def _device_test_execute(self, task):
|
||||
used_devices = {}
|
||||
@ -132,30 +167,56 @@ class Scheduler(object):
|
||||
|
||||
def _host_test_execute(self, task):
|
||||
"""execute host test"""
|
||||
test_drivers = task.test_drivers
|
||||
if not test_drivers:
|
||||
raise ParamError("test driver not found")
|
||||
|
||||
params_list = []
|
||||
for test_driver in task.test_drivers:
|
||||
listeners = self._create_listeners(task)
|
||||
params_list.append((listeners, task, [test_driver]))
|
||||
try:
|
||||
Concurrent.concurrent_execute(self._execute_test_drivers,
|
||||
params_list, 2)
|
||||
# initial params
|
||||
current_driver_threads = {}
|
||||
test_drivers = task.test_drivers
|
||||
message_queue = queue.Queue()
|
||||
|
||||
# execute test drivers
|
||||
queue_monitor_thread = self._start_queue_monitor(
|
||||
message_queue, test_drivers, current_driver_threads)
|
||||
while test_drivers:
|
||||
if len(current_driver_threads) > 5:
|
||||
time.sleep(3)
|
||||
continue
|
||||
|
||||
# clear remaining test drivers when scheduler is terminated
|
||||
if not Scheduler.is_execute:
|
||||
LOG.info("clear test drivers")
|
||||
self._clear_not_executed(task, test_drivers)
|
||||
break
|
||||
|
||||
# get test driver and device
|
||||
test_driver = test_drivers[0]
|
||||
|
||||
# display executing progress
|
||||
self._display_executing_process(None, test_driver,
|
||||
test_drivers)
|
||||
|
||||
# start driver thread
|
||||
self._start_driver_thread(current_driver_threads, (
|
||||
None, message_queue, task, test_driver))
|
||||
test_drivers.pop(0)
|
||||
|
||||
# wait for all drivers threads finished and do kit teardown
|
||||
while True:
|
||||
if not queue_monitor_thread.is_alive():
|
||||
break
|
||||
time.sleep(3)
|
||||
|
||||
finally:
|
||||
# generate reports
|
||||
self._generate_task_report(task)
|
||||
|
||||
def _generate_task_report(self, task, used_devices=None):
|
||||
task_info = ExecInfo()
|
||||
test_type = getattr(task, "testtype", [])
|
||||
test_type = getattr(task.config, "testtype", [])
|
||||
task_name = getattr(task.config, "task", "")
|
||||
if task_name:
|
||||
task_info.test_type = str(task_name).upper()
|
||||
else:
|
||||
task_info.test_type = ",".join(
|
||||
test_type) if test_type else "Test"
|
||||
task_info.test_type = ",".join(test_type) if test_type else "Test"
|
||||
if used_devices:
|
||||
serials = []
|
||||
platforms = []
|
||||
@ -170,11 +231,12 @@ class Scheduler(object):
|
||||
task_info.device_name = "None"
|
||||
task_info.platform = "None"
|
||||
task_info.test_time = task.config.start_time
|
||||
task_info.product_params = getattr(task, "product_params", "")
|
||||
task_info.product_info = getattr(task, "product_info", "")
|
||||
|
||||
listeners = self._create_listeners(task)
|
||||
for listener in listeners:
|
||||
listener.__ended__(LifeCycle.TestTask, task_info)
|
||||
listener.__ended__(LifeCycle.TestTask, task_info,
|
||||
test_type=task_info.test_type)
|
||||
|
||||
@classmethod
|
||||
def _create_listeners(cls, task):
|
||||
@ -206,6 +268,9 @@ class Scheduler(object):
|
||||
if not label:
|
||||
continue
|
||||
device_option = DeviceSelectionOption(options, label, test_source)
|
||||
device_dict.pop("type", None)
|
||||
device_dict.pop("label", None)
|
||||
device_option.extend_value = device_dict
|
||||
device_option.source_file = test_source.config_file or \
|
||||
test_source.source_string
|
||||
devices_option.append(device_option)
|
||||
@ -230,7 +295,8 @@ class Scheduler(object):
|
||||
continue
|
||||
else:
|
||||
raise DeviceError("The '%s' required device does not exist"
|
||||
% test_driver[1].source.source_file)
|
||||
% test_driver[1].source.source_file,
|
||||
error_no="00104")
|
||||
|
||||
return environment
|
||||
|
||||
@ -267,13 +333,99 @@ class Scheduler(object):
|
||||
env_manager = EnvironmentManager()
|
||||
env_manager.release_environment(environment)
|
||||
|
||||
def _dynamic_concurrent_execute(self, task, used_devices):
|
||||
from xdevice import SuiteReporter
|
||||
def _check_device_spt(self, kit, driver_request, device):
|
||||
kit_version = self._parse_version_id(driver_request, kit)
|
||||
kit_spt = kit.properties.get(ConfigConst.spt, None)
|
||||
if not kit_spt or not kit_version:
|
||||
setattr(device, ConfigConst.task_state, False)
|
||||
LOG.error("spt or version is empty", error_no="00108")
|
||||
return
|
||||
if getattr(driver_request, ConfigConst.product_info, ""):
|
||||
product_info = getattr(driver_request,
|
||||
ConfigConst.product_info)
|
||||
if not isinstance(product_info, dict):
|
||||
LOG.warning("product info should be dict, %s",
|
||||
product_info)
|
||||
setattr(device, ConfigConst.task_state, False)
|
||||
return
|
||||
device_spt = product_info.get("Security Patch", None)
|
||||
device_version = product_info.get("VersionID", None)
|
||||
if not device_version or not device_version == kit_version:
|
||||
LOG.error("The device %s VersionID is %s, "
|
||||
"and the test case version is %s, "
|
||||
"which does not meet the requirements" %
|
||||
(device.device_sn, device_version, kit_version),
|
||||
error_no="00116")
|
||||
setattr(device, ConfigConst.task_state, False)
|
||||
return
|
||||
|
||||
if not device_spt or not \
|
||||
Scheduler.compare_spt_time(kit_spt, device_spt):
|
||||
LOG.error("The device %s spt is %s, "
|
||||
"and the test case spt is %s, "
|
||||
"which does not meet the requirements" %
|
||||
(device.device_sn, device_spt, kit_spt),
|
||||
error_no="00116")
|
||||
|
||||
setattr(device, ConfigConst.task_state, False)
|
||||
return
|
||||
|
||||
def _decc_task_setup(self, environment, task):
|
||||
config = Config()
|
||||
config.update(task.config.__dict__)
|
||||
config.environment = environment
|
||||
driver_request = Request(config=config)
|
||||
|
||||
if environment is None:
|
||||
return False
|
||||
|
||||
for device in environment.devices:
|
||||
if not getattr(device, ConfigConst.need_kit_setup, True):
|
||||
LOG.debug("device %s need kit setup is false" % device)
|
||||
continue
|
||||
|
||||
# do task setup for device
|
||||
kits_copy = copy.deepcopy(task.config.kits)
|
||||
setattr(device, ConfigConst.task_kits, kits_copy)
|
||||
for kit in getattr(device, ConfigConst.task_kits, []):
|
||||
if not Scheduler.is_execute:
|
||||
break
|
||||
try:
|
||||
kit.__setup__(device, request=driver_request)
|
||||
except (ParamError, ExecuteTerminate, DeviceError,
|
||||
LiteDeviceError, ValueError, TypeError,
|
||||
SyntaxError, AttributeError) as exception:
|
||||
error_no = getattr(exception, "error_no", "00000")
|
||||
LOG.exception(
|
||||
"task setup device: %s, exception: %s" % (
|
||||
environment.__get_serial__(),
|
||||
exception), exc_info=False, error_no=error_no)
|
||||
if kit.__class__.__name__ == CKit.query:
|
||||
self._check_device_spt(kit, driver_request, device)
|
||||
LOG.debug("set device %s need kit setup to false" % device)
|
||||
setattr(device, ConfigConst.need_kit_setup, False)
|
||||
|
||||
for device in environment.devices:
|
||||
if not getattr(device, ConfigConst.task_state, True):
|
||||
return False
|
||||
|
||||
# set product_info to self.task
|
||||
if getattr(driver_request, ConfigConst.product_info, "") and \
|
||||
not getattr(task, ConfigConst.product_info, ""):
|
||||
product_info = getattr(driver_request, ConfigConst.product_info)
|
||||
if not isinstance(product_info, dict):
|
||||
LOG.warning("product info should be dict, %s",
|
||||
product_info)
|
||||
else:
|
||||
setattr(task, ConfigConst.product_info, product_info)
|
||||
return True
|
||||
|
||||
def _dynamic_concurrent_execute(self, task, used_devices):
|
||||
# initial params
|
||||
current_driver_threads = {}
|
||||
test_drivers = task.test_drivers
|
||||
message_queue = queue.Queue()
|
||||
task_unused_env = []
|
||||
|
||||
# execute test drivers
|
||||
queue_monitor_thread = self._start_queue_monitor(
|
||||
@ -282,25 +434,22 @@ class Scheduler(object):
|
||||
# clear remaining test drivers when scheduler is terminated
|
||||
if not Scheduler.is_execute:
|
||||
LOG.info("clear test drivers")
|
||||
self._clear_not_executed(test_drivers, task)
|
||||
self._clear_not_executed(task, test_drivers)
|
||||
break
|
||||
|
||||
# get test driver and device
|
||||
test_driver = test_drivers[0]
|
||||
|
||||
if getattr(task.config, "history_report_path", "") and \
|
||||
Scheduler.mode == "decc":
|
||||
if test_driver[1].source.test_name not in \
|
||||
SuiteReporter.get_failed_case_list():
|
||||
if getattr(task.config, ConfigConst.history_report_path, ""):
|
||||
module_name = test_driver[1].source.module_name
|
||||
if not self.is_module_need_retry(task, module_name):
|
||||
self._display_executing_process(None, test_driver,
|
||||
test_drivers)
|
||||
LOG.info("%s are passed, no need to retry" % module_name)
|
||||
self._append_history_result(task, module_name)
|
||||
LOG.info("")
|
||||
test_drivers.pop(0)
|
||||
self.test_number = self.test_number - 1
|
||||
LOG.info("%s has been passed, test number changes to %s",
|
||||
test_driver[1].source.test_name, self.test_number)
|
||||
continue
|
||||
else:
|
||||
SuiteReporter.get_failed_case_list().remove(
|
||||
test_driver[1].source.test_name)
|
||||
|
||||
# get environment
|
||||
try:
|
||||
environment = self.__allocate_environment__(
|
||||
@ -314,6 +463,24 @@ class Scheduler(object):
|
||||
Scheduler.__free_environment__(environment)
|
||||
continue
|
||||
|
||||
if check_mode(ModeType.decc) or getattr(
|
||||
task.config, ConfigConst.check_device, False):
|
||||
LOG.info("start to check environment: %s" %
|
||||
environment.__get_serial__())
|
||||
status = self._decc_task_setup(environment, task)
|
||||
if not status:
|
||||
Scheduler.__free_environment__(environment)
|
||||
task_unused_env.append(environment)
|
||||
error_message = "Load Error[00116]"
|
||||
self.report_not_executed(task.config.report_path,
|
||||
[test_drivers[0]],
|
||||
error_message, task)
|
||||
test_drivers.pop(0)
|
||||
continue
|
||||
else:
|
||||
LOG.info("environment %s check success",
|
||||
environment.__get_serial__())
|
||||
|
||||
# display executing progress
|
||||
self._display_executing_process(environment, test_driver,
|
||||
test_drivers)
|
||||
@ -331,45 +498,109 @@ class Scheduler(object):
|
||||
if not queue_monitor_thread.is_alive():
|
||||
break
|
||||
time.sleep(3)
|
||||
self._do_taskkit_teardown(task, used_devices)
|
||||
|
||||
self._do_taskkit_teardown(used_devices, task_unused_env)
|
||||
|
||||
@classmethod
|
||||
def _append_history_result(cls, task, module_name):
|
||||
history_report_path = getattr(
|
||||
task.config, ConfigConst.history_report_path, "")
|
||||
from _core.report.result_reporter import ResultReporter
|
||||
params = ResultReporter.get_task_info_params(
|
||||
history_report_path)
|
||||
|
||||
if not params or not params[4]:
|
||||
LOG.debug("task info record data reports is empty")
|
||||
return
|
||||
|
||||
report_data_dict = dict(params[4])
|
||||
if module_name not in report_data_dict.keys():
|
||||
module_name_ = str(module_name).split(".")[0]
|
||||
if module_name_ not in report_data_dict.keys():
|
||||
LOG.error("%s not in data reports" % module_name)
|
||||
return
|
||||
module_name = module_name_
|
||||
|
||||
from xdevice import SuiteReporter
|
||||
if check_mode(ModeType.decc):
|
||||
virtual_report_path, report_result = SuiteReporter. \
|
||||
get_history_result_by_module(module_name)
|
||||
LOG.debug("append history result: (%s, %s)" % (
|
||||
virtual_report_path, report_result))
|
||||
SuiteReporter.append_report_result(
|
||||
(virtual_report_path, report_result))
|
||||
else:
|
||||
import shutil
|
||||
history_execute_result = report_data_dict.get(module_name, "")
|
||||
LOG.info("start copy %s" % history_execute_result)
|
||||
file_name = get_filename_extension(history_execute_result)[0]
|
||||
if os.path.exists(history_execute_result):
|
||||
result_dir = \
|
||||
os.path.join(task.config.report_path, "result")
|
||||
os.makedirs(result_dir, exist_ok=True)
|
||||
target_execute_result = "%s.xml" % os.path.join(
|
||||
task.config.report_path, "result", file_name)
|
||||
shutil.copyfile(history_execute_result, target_execute_result)
|
||||
LOG.info("copy %s to %s" % (
|
||||
history_execute_result, target_execute_result))
|
||||
else:
|
||||
error_msg = "copy failed! %s not exists!" %\
|
||||
history_execute_result
|
||||
raise ParamError(error_msg)
|
||||
|
||||
def _handle_device_error(self, exception, task, test_drivers):
|
||||
self._display_executing_process(None, test_drivers[0], test_drivers)
|
||||
error_message = "%s: %s" % \
|
||||
(get_instance_name(exception), exception)
|
||||
LOG.exception(error_message, exc_info=False)
|
||||
# under "decc" mode ,response device error
|
||||
if Scheduler.mode == "decc":
|
||||
self._report_not_executed(
|
||||
task, test_drivers[1], error_message)
|
||||
LOG.exception(error_message, exc_info=False,
|
||||
error_no=exception.error_no)
|
||||
if check_mode(ModeType.decc):
|
||||
error_message = "Load Error[00104]"
|
||||
self.report_not_executed(task.config.report_path, [test_drivers[0]],
|
||||
error_message, task)
|
||||
|
||||
LOG.info("")
|
||||
test_drivers.pop(0)
|
||||
self.test_number = self.test_number - 1
|
||||
LOG.info("test number changes to %s", self.test_number)
|
||||
|
||||
@classmethod
|
||||
def _clear_not_executed(cls, task, test_drivers):
|
||||
if Scheduler.mode != "decc":
|
||||
if Scheduler.mode != ModeType.decc:
|
||||
# clear all
|
||||
test_drivers.clear()
|
||||
return
|
||||
# The result is reported only in DECC mode, and also clear all.
|
||||
error_message = "task execution terminated!"
|
||||
cls._report_not_executed(task, test_drivers, error_message)
|
||||
LOG.error("case no run: task execution terminated!", error_no="00300")
|
||||
error_message = "Execute Terminate[00300]"
|
||||
cls.report_not_executed(task.config.report_path, test_drivers,
|
||||
error_message)
|
||||
test_drivers.clear()
|
||||
|
||||
@classmethod
|
||||
def _report_not_executed(cls, task, test_drivers, error_message):
|
||||
def report_not_executed(cls, report_path, test_drivers, error_message,
|
||||
task=None):
|
||||
# traversing list to get remained elements
|
||||
for test_driver in test_drivers:
|
||||
report_path = task.config.report_path
|
||||
report_file = os.path.join(
|
||||
report_path, "result",
|
||||
"%s.xml" % test_driver[1].source.test_name)
|
||||
# get report file
|
||||
if task and getattr(task.config, "testdict", ""):
|
||||
report_file = os.path.join(get_sub_path(
|
||||
test_driver[1].source.source_file),
|
||||
"%s.xml" % test_driver[1].source.test_name)
|
||||
else:
|
||||
report_file = os.path.join(
|
||||
report_path, "result",
|
||||
"%s.xml" % test_driver[1].source.module_name)
|
||||
|
||||
# get report name
|
||||
report_name = test_driver[1].source.test_name if \
|
||||
not test_driver[1].source.test_name.startswith("{") \
|
||||
else "report"
|
||||
|
||||
# get module name
|
||||
module_name = test_driver[1].source.module_name
|
||||
|
||||
# here, normally create empty report and then upload result
|
||||
check_result_report(report_path, report_file, error_message,
|
||||
report_name)
|
||||
report_name, module_name)
|
||||
|
||||
def _start_driver_thread(self, current_driver_threads, thread_params):
|
||||
environment, message_queue, task, test_driver = thread_params
|
||||
@ -383,18 +614,35 @@ class Scheduler(object):
|
||||
current_driver_threads.setdefault(thread_id, driver_thread)
|
||||
|
||||
@classmethod
|
||||
def _do_taskkit_teardown(cls, task, used_devices):
|
||||
def _do_taskkit_teardown(cls, used_devices, task_unused_env):
|
||||
for device in used_devices.values():
|
||||
if getattr(device, "need_kit_setup", True):
|
||||
if getattr(device, ConfigConst.need_kit_setup, True):
|
||||
continue
|
||||
for kit in task.config.kits:
|
||||
kit.__teardown__(device)
|
||||
setattr(device, "need_kit_setup", True)
|
||||
|
||||
for kit in getattr(device, ConfigConst.task_kits, []):
|
||||
try:
|
||||
kit.__teardown__(device)
|
||||
except Exception as error:
|
||||
LOG.debug("do taskkit teardown: %s" % error)
|
||||
setattr(device, ConfigConst.task_kits, [])
|
||||
setattr(device, ConfigConst.need_kit_setup, True)
|
||||
|
||||
for environment in task_unused_env:
|
||||
for device in environment.devices:
|
||||
setattr(device, ConfigConst.task_state, True)
|
||||
setattr(device, ConfigConst.need_kit_setup, True)
|
||||
|
||||
def _display_executing_process(self, environment, test_driver,
|
||||
test_drivers):
|
||||
source_content = test_driver[1].source.source_file or \
|
||||
test_driver[1].source.source_string
|
||||
if environment is None:
|
||||
LOG.info("[%d / %d] Executing: %s, Driver: %s" %
|
||||
(self.test_number - len(test_drivers) + 1,
|
||||
self.test_number, source_content,
|
||||
test_driver[1].source.test_type))
|
||||
return
|
||||
|
||||
LOG.info("[%d / %d] Executing: %s, Device: %s, Driver: %s" %
|
||||
(self.test_number - len(test_drivers) + 1,
|
||||
self.test_number, source_content,
|
||||
@ -416,7 +664,6 @@ class Scheduler(object):
|
||||
device_serial = device.__get_serial__() if device else "None"
|
||||
if device_serial and device_serial not in used_devices.keys():
|
||||
used_devices[device_serial] = device
|
||||
setattr(device, "need_kit_setup", True)
|
||||
|
||||
@staticmethod
|
||||
def _start_queue_monitor(message_queue, test_drivers,
|
||||
@ -428,39 +675,20 @@ class Scheduler(object):
|
||||
queue_monitor_thread.start()
|
||||
return queue_monitor_thread
|
||||
|
||||
@classmethod
|
||||
def _execute_test_drivers(cls, listeners, task, allocated_test_drivers):
|
||||
LOG.debug('========= test_drivers count = %s =========' % len(
|
||||
allocated_test_drivers))
|
||||
for listener in listeners:
|
||||
LOG.debug("listener %s" % listener)
|
||||
for driver, test in allocated_test_drivers:
|
||||
config = Config()
|
||||
config.update(task.config.__dict__)
|
||||
driver_request = Request(test, listeners, config)
|
||||
driver.__execute__(driver_request)
|
||||
|
||||
def exec_command(self, command, options):
|
||||
"""
|
||||
Directly executes a command without adding it to the command queue.
|
||||
"""
|
||||
if command != "run":
|
||||
LOG.error("exec_command can only deal with run command")
|
||||
raise SystemError("exec_command can only deal with run command")
|
||||
|
||||
try:
|
||||
if getattr(options, "test_environment", ""):
|
||||
self._reset_environment(options.test_environment)
|
||||
|
||||
exec_type = options.exectype
|
||||
if exec_type in [TestExecType.device_test, TestExecType.host_test,
|
||||
TestExecType.host_driven_test]:
|
||||
self._exec_task(options)
|
||||
else:
|
||||
LOG.error("unsupported execution type '%s'" % exec_type)
|
||||
finally:
|
||||
if getattr(options, "test_environment", ""):
|
||||
self._restore_environment()
|
||||
raise ParamError("unsupported command action: %s" % command,
|
||||
error_no="00100")
|
||||
exec_type = options.exectype
|
||||
if exec_type in [TestExecType.device_test, TestExecType.host_test,
|
||||
TestExecType.host_driven_test]:
|
||||
self._exec_task(options)
|
||||
else:
|
||||
LOG.error("unsupported execution type '%s'" % exec_type,
|
||||
error_no="00100")
|
||||
|
||||
return
|
||||
|
||||
@ -468,14 +696,26 @@ class Scheduler(object):
|
||||
"""
|
||||
Directly allocates a device and execute a device test.
|
||||
"""
|
||||
task = self.__discover__(options.__dict__)
|
||||
self.__execute__(task)
|
||||
try:
|
||||
task = self.__discover__(options.__dict__)
|
||||
self.__execute__(task)
|
||||
except (ParamError, ValueError, TypeError, SyntaxError,
|
||||
AttributeError) as exception:
|
||||
error_no = getattr(exception, "error_no", "00000")
|
||||
LOG.exception("%s: %s" % (get_instance_name(exception), exception),
|
||||
exc_info=False, error_no=error_no)
|
||||
if Scheduler.upload_address:
|
||||
Scheduler.upload_unavailable_result(str(exception.args))
|
||||
Scheduler.upload_report_end()
|
||||
finally:
|
||||
self.stop_task_log()
|
||||
self.stop_encrypt_log()
|
||||
|
||||
@classmethod
|
||||
def _reset_environment(cls, environment):
|
||||
def _reset_environment(cls, environment="", config_file=""):
|
||||
env_manager = EnvironmentManager()
|
||||
env_manager.env_stop()
|
||||
EnvironmentManager(environment)
|
||||
EnvironmentManager(environment, config_file)
|
||||
|
||||
@classmethod
|
||||
def _restore_environment(cls):
|
||||
@ -484,37 +724,53 @@ class Scheduler(object):
|
||||
EnvironmentManager()
|
||||
|
||||
@classmethod
|
||||
def _start_task_logcat(cls, log_path):
|
||||
def start_task_log(cls, log_path):
|
||||
tool_file_name = "task_log.log"
|
||||
tool_log_file = os.path.join(log_path, tool_file_name)
|
||||
add_task_file_handler(tool_log_file)
|
||||
|
||||
@classmethod
|
||||
def _stop_task_logcat(cls):
|
||||
def start_encrypt_log(cls, log_path):
|
||||
from _core.report.encrypt import check_pub_key_exist
|
||||
if check_pub_key_exist():
|
||||
encrypt_file_name = "task_log.ept"
|
||||
encrypt_log_file = os.path.join(log_path, encrypt_file_name)
|
||||
add_encrypt_file_handler(encrypt_log_file)
|
||||
|
||||
@classmethod
|
||||
def stop_task_log(cls):
|
||||
remove_task_file_handler()
|
||||
|
||||
@classmethod
|
||||
def stop_encrypt_log(cls):
|
||||
remove_encrypt_file_handler()
|
||||
|
||||
@staticmethod
|
||||
def _find_test_root_descriptor(config):
|
||||
# read test list from testfile, testlist or task
|
||||
if getattr(config, "testfile", "") or getattr(config, "testlist", "") \
|
||||
or getattr(config, "task", ""):
|
||||
test_set = config.testfile or config.testlist or config.task
|
||||
fname, _ = get_filename_extension(test_set)
|
||||
uid = unique_id("Scheduler", fname)
|
||||
root = Descriptor(uuid=uid, name=fname,
|
||||
source=TestSetSource(test_set), container=True)
|
||||
root.children = find_test_descriptors(config)
|
||||
return root
|
||||
# read test list from testdict
|
||||
elif getattr(config, "testdict", "") != "":
|
||||
if getattr(config, "testdict", "") != "" and getattr(
|
||||
config, "testfile", "") == "":
|
||||
uid = unique_id("Scheduler", "testdict")
|
||||
root = Descriptor(uuid=uid, name="testdict",
|
||||
source=TestSetSource(config.testdict),
|
||||
container=True)
|
||||
root.children = find_testdict_descriptors(config)
|
||||
return root
|
||||
|
||||
# read test list from testfile, testlist or task
|
||||
test_set = getattr(config, "testfile", "") or getattr(
|
||||
config, "testlist", "") or getattr(config, "task", "") or getattr(
|
||||
config, "testcase")
|
||||
if test_set:
|
||||
fname, _ = get_filename_extension(test_set)
|
||||
uid = unique_id("Scheduler", fname)
|
||||
root = Descriptor(uuid=uid, name=fname,
|
||||
source=TestSetSource(test_set), container=True)
|
||||
root.children = find_test_descriptors(config)
|
||||
return root
|
||||
else:
|
||||
raise ParamError("no test file, list, dict or task found")
|
||||
raise ParamError("no test file, list, dict, case or task found",
|
||||
error_no="00102")
|
||||
|
||||
@classmethod
|
||||
def terminate_cmd_exec(cls):
|
||||
@ -546,43 +802,59 @@ class Scheduler(object):
|
||||
|
||||
test_name = request.root.source.test_name
|
||||
if not result_file or not os.path.exists(result_file):
|
||||
LOG.error("%s result not exists", test_name)
|
||||
LOG.error("%s result not exists", test_name, error_no="00200")
|
||||
return
|
||||
|
||||
test_type = request.root.source.test_type
|
||||
LOG.info("need upload result: %s, test type: %s, upload address: %s" %
|
||||
(result_file, test_type, Scheduler.upload_address))
|
||||
upload_params, _, _ = \
|
||||
cls._get_upload_params(result_file, test_type)
|
||||
LOG.info("need upload result: %s, test type: %s" %
|
||||
(result_file, test_type))
|
||||
upload_params, _, _ = cls._get_upload_params(result_file, request)
|
||||
if not upload_params:
|
||||
LOG.error("%s no test case result to upload" % result_file)
|
||||
LOG.error("%s no test case result to upload" % result_file,
|
||||
error_no="00201")
|
||||
return
|
||||
LOG.info("need upload %s case" % len(upload_params))
|
||||
from agent.factory import upload_batch
|
||||
upload_suite = []
|
||||
for upload_param in upload_params:
|
||||
cls.upload_case_result(upload_param)
|
||||
case_id, result, error, start_time, end_time, report_path = \
|
||||
upload_param
|
||||
case = {"caseid": case_id, "result": result, "error": error,
|
||||
"start": start_time, "end": end_time,
|
||||
"report": report_path}
|
||||
LOG.info("case info: %s", case)
|
||||
upload_suite.append(case)
|
||||
upload_batch(upload_suite)
|
||||
|
||||
@classmethod
|
||||
def _get_upload_params(cls, result_file, test_type):
|
||||
def _get_upload_params(cls, result_file, request):
|
||||
upload_params = []
|
||||
|
||||
report_path = result_file
|
||||
testsuite_element = DataHelper.parse_data_report(report_path)
|
||||
start_time, end_time = cls._get_time(testsuite_element)
|
||||
task_log_path = os.path.join(request.config.report_path, "log",
|
||||
"task_log.log")
|
||||
testsuites_element = DataHelper.parse_data_report(report_path)
|
||||
start_time, end_time = cls._get_time(testsuites_element)
|
||||
|
||||
if test_type == HostDrivenTestType.device_test:
|
||||
for model_element in testsuite_element:
|
||||
case_id = model_element.get(ReportConstant.name, "")
|
||||
result, error = cls.get_script_result(model_element)
|
||||
upload_params.append((case_id, result, error, start_time,
|
||||
end_time, report_path))
|
||||
else:
|
||||
for model_element in testsuite_element:
|
||||
model_name = model_element.get(ReportConstant.name, "none")
|
||||
for case_element in model_element:
|
||||
case_id = cls._get_case_id(case_element, model_name)
|
||||
case_result, error = cls._get_case_result(case_element)
|
||||
upload_params.append(
|
||||
(case_id, case_result, error, start_time,
|
||||
end_time, report_path))
|
||||
for testsuite_element in testsuites_element:
|
||||
if check_mode(ModeType.developer):
|
||||
module_name = str(get_filename_extension(
|
||||
report_path)[0]).split(".")[0]
|
||||
else:
|
||||
module_name = testsuite_element.get(ReportConstant.name,
|
||||
"none")
|
||||
for case_element in testsuite_element:
|
||||
case_id = cls._get_case_id(case_element, module_name)
|
||||
case_result, error = cls._get_case_result(case_element)
|
||||
if error and len(error) > 150:
|
||||
error = "%s..." % error[:150]
|
||||
if case_result == "Ignored":
|
||||
LOG.info("get upload params: %s result is ignored",
|
||||
case_id)
|
||||
continue
|
||||
upload_params.append(
|
||||
(case_id, case_result, error, start_time,
|
||||
end_time, task_log_path))
|
||||
return upload_params, start_time, end_time
|
||||
|
||||
@classmethod
|
||||
@ -604,7 +876,7 @@ class Scheduler(object):
|
||||
|
||||
if result == "Passed":
|
||||
return result, ""
|
||||
if Scheduler.mode == "decc":
|
||||
if Scheduler.mode == ModeType.decc:
|
||||
result = "Failed"
|
||||
|
||||
error_msg = model_element.get(ReportConstant.message, "")
|
||||
@ -615,11 +887,11 @@ class Scheduler(object):
|
||||
return result, error_msg
|
||||
|
||||
@classmethod
|
||||
def _get_case_id(cls, case_element, model_name):
|
||||
package_name = case_element.get(ReportConstant.class_name, "none")
|
||||
def _get_case_id(cls, case_element, package_name):
|
||||
class_name = case_element.get(ReportConstant.class_name, "none")
|
||||
method_name = case_element.get(ReportConstant.name, "none")
|
||||
case_id = "{}#{}#{}#{}".format(Scheduler.task_name, model_name,
|
||||
package_name, method_name)
|
||||
case_id = "{}#{}#{}#{}".format(Scheduler.task_name, package_name,
|
||||
class_name, method_name)
|
||||
return case_id
|
||||
|
||||
@classmethod
|
||||
@ -640,6 +912,8 @@ class Scheduler(object):
|
||||
result = "Failed"
|
||||
elif case.is_blocked():
|
||||
result = "Blocked"
|
||||
elif case.is_ignored():
|
||||
result = "Ignored"
|
||||
else:
|
||||
result = "Unavailable"
|
||||
return result, case.message
|
||||
@ -648,26 +922,35 @@ class Scheduler(object):
|
||||
def _get_time(cls, testsuite_element):
|
||||
start_time = testsuite_element.get(ReportConstant.start_time, "")
|
||||
end_time = testsuite_element.get(ReportConstant.end_time, "")
|
||||
if start_time and end_time:
|
||||
start_time = int(time.mktime(time.strptime(
|
||||
start_time, ReportConstant.time_format)) * 1000)
|
||||
end_time = int(time.mktime(time.strptime(
|
||||
end_time, ReportConstant.time_format)) * 1000)
|
||||
else:
|
||||
timestamp = str(testsuite_element.get(
|
||||
ReportConstant.time_stamp, "")).replace("T", " ")
|
||||
cost_time = testsuite_element.get(ReportConstant.time, "")
|
||||
if timestamp and cost_time:
|
||||
try:
|
||||
if start_time and end_time:
|
||||
start_time = int(time.mktime(time.strptime(
|
||||
start_time, ReportConstant.time_format)) * 1000)
|
||||
end_time = int(time.mktime(time.strptime(
|
||||
timestamp, ReportConstant.time_format)) * 1000)
|
||||
start_time = int(end_time - float(cost_time) * 1000)
|
||||
end_time, ReportConstant.time_format)) * 1000)
|
||||
else:
|
||||
current_time = int(time.time() * 1000)
|
||||
start_time, end_time = current_time, current_time
|
||||
timestamp = str(testsuite_element.get(
|
||||
ReportConstant.time_stamp, "")).replace("T", " ")
|
||||
cost_time = testsuite_element.get(ReportConstant.time, "")
|
||||
if timestamp and cost_time:
|
||||
try:
|
||||
end_time = int(time.mktime(time.strptime(
|
||||
timestamp, ReportConstant.time_format)) * 1000)
|
||||
except ArithmeticError as error:
|
||||
LOG.error("get time error %s" % error)
|
||||
end_time = int(time.time() * 1000)
|
||||
start_time = int(end_time - float(cost_time) * 1000)
|
||||
else:
|
||||
current_time = int(time.time() * 1000)
|
||||
start_time, end_time = current_time, current_time
|
||||
except ArithmeticError as error:
|
||||
LOG.error("get time error %s" % error)
|
||||
current_time = int(time.time() * 1000)
|
||||
start_time, end_time = current_time, current_time
|
||||
return start_time, end_time
|
||||
|
||||
@classmethod
|
||||
def upload_task_result(cls, task):
|
||||
def upload_task_result(cls, task, error_message=""):
|
||||
if not Scheduler.task_name:
|
||||
LOG.info("no need upload summary report")
|
||||
return
|
||||
@ -677,12 +960,20 @@ class Scheduler(object):
|
||||
summary_vision_report = os.path.join(
|
||||
task.config.report_path, ReportConstant.summary_vision_report)
|
||||
if not os.path.exists(summary_data_report):
|
||||
Scheduler.upload_unavailable_result("summary report not exists")
|
||||
task_log_path = os.path.join(task.config.report_path, "log",
|
||||
"task_log.log")
|
||||
if not os.path.exists(task_log_path):
|
||||
task_log_path = ""
|
||||
Scheduler.upload_unavailable_result(str(
|
||||
error_message) or "summary report not exists", task_log_path)
|
||||
return
|
||||
|
||||
task_element = ElementTree.parse(summary_data_report).getroot()
|
||||
start_time, end_time = cls._get_time(task_element)
|
||||
task_result = cls._get_task_result(task_element)
|
||||
if task_result == "Unavailable":
|
||||
summary_vision_report = os.path.join(task.config.report_path,
|
||||
"log", "task_log.log")
|
||||
error_msg = ""
|
||||
for child in task_element:
|
||||
if child.get(ReportConstant.message, ""):
|
||||
@ -711,17 +1002,75 @@ class Scheduler(object):
|
||||
return task_result
|
||||
|
||||
@classmethod
|
||||
def upload_unavailable_result(cls, error_msg):
|
||||
def upload_unavailable_result(cls, error_msg, report_path=""):
|
||||
start_time = int(time.time() * 1000)
|
||||
LOG.info(
|
||||
"get exception upload params: %s, %s, %s, %s, %s, %s" % (
|
||||
Scheduler.task_name, "Unavailable",
|
||||
error_msg, start_time, start_time, ""))
|
||||
Scheduler.upload_case_result((Scheduler.task_name, "Unavailable",
|
||||
error_msg, start_time, start_time, ""))
|
||||
error_msg, start_time, start_time,
|
||||
report_path))
|
||||
|
||||
@classmethod
|
||||
def upload_report_end(cls):
|
||||
from agent.factory import report_end
|
||||
LOG.info("upload report end")
|
||||
report_end()
|
||||
|
||||
@classmethod
|
||||
def is_module_need_retry(cls, task, module_name):
|
||||
failed_flag = False
|
||||
if check_mode(ModeType.decc):
|
||||
from xdevice import SuiteReporter
|
||||
for module, failed in SuiteReporter.get_failed_case_list():
|
||||
if module_name == module or str(module_name).split(
|
||||
".")[0] == module:
|
||||
failed_flag = True
|
||||
break
|
||||
else:
|
||||
from xdevice import ResultReporter
|
||||
history_report_path = \
|
||||
getattr(task.config, ConfigConst.history_report_path, "")
|
||||
params = ResultReporter.get_task_info_params(history_report_path)
|
||||
if params and params[3]:
|
||||
if dict(params[3]).get(module_name, []):
|
||||
failed_flag = True
|
||||
elif dict(params[3]).get(str(module_name).split(".")[0], []):
|
||||
failed_flag = True
|
||||
return failed_flag
|
||||
|
||||
@classmethod
|
||||
def compare_spt_time(cls, kit_spt, device_spt):
|
||||
if not kit_spt or not device_spt:
|
||||
return False
|
||||
try:
|
||||
kit_time = str(kit_spt).split("-")[:2]
|
||||
device_time = str(device_spt).split("-")[:2]
|
||||
k_spt = datetime.datetime.strptime(
|
||||
"-".join(kit_time), "%Y-%m")
|
||||
d_spt = datetime.datetime.strptime("-".join(device_time), "%Y-%m")
|
||||
except ValueError as value_error:
|
||||
LOG.debug("date format is error, %s" % value_error.args)
|
||||
return False
|
||||
month_interval = int(k_spt.month) - int(d_spt.month)
|
||||
year_interval = int(k_spt.year) - int(d_spt.year)
|
||||
LOG.debug("kit spt (year=%s, month=%s), device spt (year=%s, month=%s)"
|
||||
% (k_spt.year, k_spt.month, d_spt.year, d_spt.month))
|
||||
if year_interval == 0 and month_interval in (0, 1, 2):
|
||||
return True
|
||||
if year_interval == 1 and month_interval + 12 in (1, 2):
|
||||
return True
|
||||
|
||||
@classmethod
|
||||
def _parse_version_id(cls, driver_request, kit):
|
||||
test_args = copy.deepcopy(
|
||||
driver_request.config.get(ConfigConst.testargs, dict()))
|
||||
version_id = ""
|
||||
if ConfigConst.pass_through in test_args.keys():
|
||||
import json
|
||||
pt_dict = json.loads(test_args.get(ConfigConst.pass_through, ""))
|
||||
version_id = pt_dict.get("VersionID", None)
|
||||
elif "VersionID" in test_args.keys():
|
||||
version_id = test_args.get("VersionID", None)
|
||||
if version_id:
|
||||
kit_version = version_id
|
||||
else:
|
||||
kit_version = kit.properties.get(ConfigConst.version, None)
|
||||
return kit_version
|
||||
|
@ -2,7 +2,7 @@
|
||||
# coding=utf-8
|
||||
|
||||
#
|
||||
# Copyright (c) 2020 Huawei Device Co., Ltd.
|
||||
# Copyright (c) 2020-2021 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
|
||||
@ -20,6 +20,7 @@ import os
|
||||
from collections import namedtuple
|
||||
|
||||
from _core.constants import DeviceTestType
|
||||
from _core.constants import ModeType
|
||||
from _core.constants import HostDrivenTestType
|
||||
from _core.exception import ParamError
|
||||
from _core.logger import platform_logger
|
||||
@ -27,33 +28,38 @@ from _core.utils import get_filename_extension
|
||||
from _core.utils import is_config_str
|
||||
from _core.utils import unique_id
|
||||
|
||||
from xdevice_adapter.constants import AppConst
|
||||
|
||||
__all__ = ["TestSetSource", "TestSource", "find_test_descriptors",
|
||||
"find_testdict_descriptors"]
|
||||
|
||||
TestSetSource = namedtuple('TestSetSource', 'set')
|
||||
TestSource = namedtuple('TestSource', 'source_file source_string config_file '
|
||||
'test_name test_type')
|
||||
'test_name test_type module_name')
|
||||
|
||||
TEST_TYPE_DICT = {"DEX": DeviceTestType.dex_test,
|
||||
"HAP": DeviceTestType.hap_test,
|
||||
"APK": DeviceTestType.hap_test,
|
||||
AppConst.app_name: DeviceTestType.hap_test,
|
||||
"PYT": HostDrivenTestType.device_test,
|
||||
"JST": DeviceTestType.jsunit_test,
|
||||
"CXX": DeviceTestType.cpp_test,
|
||||
"BIN": DeviceTestType.lite_cpp_test}
|
||||
EXT_TYPE_DICT = {".dex": DeviceTestType.dex_test,
|
||||
".hap": DeviceTestType.hap_test,
|
||||
".apk": DeviceTestType.hap_test,
|
||||
AppConst.app_ext: DeviceTestType.hap_test,
|
||||
".py": HostDrivenTestType.device_test,
|
||||
".js": DeviceTestType.jsunit_test,
|
||||
".bin": DeviceTestType.lite_cpp_test,
|
||||
"default": DeviceTestType.cpp_test}
|
||||
PY_SUFFIX = ".py"
|
||||
PYD_SUFFIX = ".pyd"
|
||||
MODULE_CONFIG_SUFFIX = ".json"
|
||||
LOG = platform_logger("TestSource")
|
||||
|
||||
|
||||
def find_test_descriptors(config):
|
||||
if config.testfile == "" and config.testlist == "" and config.task == "":
|
||||
if not config.testfile and not config.testlist and not config.task and \
|
||||
not config.testcase:
|
||||
return None
|
||||
|
||||
# get test sources
|
||||
@ -62,7 +68,8 @@ def find_test_descriptors(config):
|
||||
LOG.debug("test sources: %s", test_sources)
|
||||
|
||||
# normalize test sources
|
||||
test_sources = _normalize_test_sources(testcases_dirs, test_sources)
|
||||
test_sources = _normalize_test_sources(testcases_dirs, test_sources,
|
||||
config)
|
||||
|
||||
# make test descriptors
|
||||
test_descriptors = _make_test_descriptors_from_testsources(test_sources,
|
||||
@ -119,7 +126,7 @@ def find_testdict_descriptors(config):
|
||||
if desc is not None:
|
||||
test_descriptors.append(desc)
|
||||
if not test_descriptors:
|
||||
raise ParamError("test source is none")
|
||||
raise ParamError("test source is none", error_no="00110")
|
||||
return test_descriptors
|
||||
|
||||
|
||||
@ -127,7 +134,8 @@ def _get_test_sources(config, testcases_dirs):
|
||||
test_sources = []
|
||||
|
||||
# get test sources from testcases_dirs
|
||||
if not config.testfile and not config.testlist and config.task:
|
||||
if not config.testfile and not config.testlist and not config.testcase \
|
||||
and config.task:
|
||||
for testcases_dir in testcases_dirs:
|
||||
_append_module_test_source(testcases_dir, test_sources)
|
||||
return test_sources
|
||||
@ -140,11 +148,22 @@ def _get_test_sources(config, testcases_dirs):
|
||||
return test_sources
|
||||
|
||||
# get test sources from config.testfile
|
||||
test_file = _get_test_file(config, testcases_dirs)
|
||||
with open(test_file, "r") as file_content:
|
||||
for line in file_content:
|
||||
if line.strip():
|
||||
test_sources.append(line.strip())
|
||||
if getattr(config, "testfile", ""):
|
||||
test_file = _get_test_file(config, testcases_dirs)
|
||||
import stat
|
||||
flags = os.O_RDONLY
|
||||
modes = stat.S_IWUSR | stat.S_IRUSR
|
||||
with os.fdopen(os.open(test_file, flags, modes), "r") as file_content:
|
||||
for line in file_content:
|
||||
if line.strip():
|
||||
test_sources.append(line.strip())
|
||||
|
||||
# get test sources from config.testcase
|
||||
if getattr(config, "testcase", ""):
|
||||
for test_source in config.testcase.split(";"):
|
||||
if test_source.strip():
|
||||
test_sources.append(test_source.strip())
|
||||
return test_sources
|
||||
return test_sources
|
||||
|
||||
|
||||
@ -163,7 +182,8 @@ def _get_test_file(config, testcases_dirs):
|
||||
if os.path.exists(config.testfile):
|
||||
return config.testfile
|
||||
else:
|
||||
raise ParamError("test file '%s' not exists" % config.testfile)
|
||||
raise ParamError("test file '%s' not exists" % config.testfile,
|
||||
error_no="00110")
|
||||
|
||||
for testcases_dir in testcases_dirs:
|
||||
test_file = os.path.join(testcases_dir, config.testfile)
|
||||
@ -173,16 +193,18 @@ def _get_test_file(config, testcases_dirs):
|
||||
raise ParamError("test file '%s' not exists" % config.testfile)
|
||||
|
||||
|
||||
def _normalize_test_sources(testcases_dirs, test_sources):
|
||||
def _normalize_test_sources(testcases_dirs, test_sources, config):
|
||||
norm_test_sources = []
|
||||
for test_source in test_sources:
|
||||
append_result = False
|
||||
for testcases_dir in testcases_dirs:
|
||||
# append test source absolute path
|
||||
append_result = _append_norm_test_source(
|
||||
norm_test_sources, test_source, testcases_dir)
|
||||
norm_test_sources, test_source, testcases_dir, config)
|
||||
if append_result:
|
||||
break
|
||||
|
||||
# append test source if no corresponding file founded
|
||||
if not append_result:
|
||||
norm_test_sources.append(test_source)
|
||||
if not norm_test_sources:
|
||||
@ -190,18 +212,33 @@ def _normalize_test_sources(testcases_dirs, test_sources):
|
||||
return norm_test_sources
|
||||
|
||||
|
||||
def _append_norm_test_source(norm_test_sources, test_source, testcases_dir):
|
||||
def _append_norm_test_source(norm_test_sources, test_source, testcases_dir,
|
||||
config):
|
||||
# get norm_test_source
|
||||
norm_test_source = test_source
|
||||
if not os.path.isabs(test_source):
|
||||
norm_test_source = os.path.abspath(
|
||||
os.path.join(testcases_dir, test_source))
|
||||
|
||||
# find py or pyd for test case input
|
||||
if config.testcase and not config.testlist:
|
||||
if os.path.isfile("%s%s" % (norm_test_source, PY_SUFFIX)):
|
||||
norm_test_sources.append(
|
||||
"%s%s" % (norm_test_source, PY_SUFFIX))
|
||||
return True
|
||||
elif os.path.isfile("%s%s" % (norm_test_source, PYD_SUFFIX)):
|
||||
norm_test_sources.append(
|
||||
"%s%s" % (norm_test_source, PYD_SUFFIX))
|
||||
return True
|
||||
return False
|
||||
|
||||
# append to norm_test_sources
|
||||
if os.path.isfile(norm_test_source):
|
||||
norm_test_sources.append(norm_test_source)
|
||||
return True
|
||||
elif os.path.isfile(norm_test_source + MODULE_CONFIG_SUFFIX):
|
||||
norm_test_sources.append(norm_test_source + MODULE_CONFIG_SUFFIX)
|
||||
elif os.path.isfile("%s%s" % (norm_test_source, MODULE_CONFIG_SUFFIX)):
|
||||
norm_test_sources.append("%s%s" % (norm_test_source,
|
||||
MODULE_CONFIG_SUFFIX))
|
||||
return True
|
||||
return False
|
||||
|
||||
@ -218,10 +255,11 @@ def _make_test_descriptor(file_path, test_type_key):
|
||||
config_file = _get_config_file(
|
||||
os.path.join(os.path.dirname(file_path), filename))
|
||||
|
||||
module_name = _parse_module_name(config_file, filename)
|
||||
# make test descriptor
|
||||
desc = Descriptor(uuid=uid, name=filename,
|
||||
source=TestSource(file_path, "", config_file, filename,
|
||||
test_type))
|
||||
test_type, module_name))
|
||||
return desc
|
||||
|
||||
|
||||
@ -231,7 +269,7 @@ def _get_test_driver(test_source):
|
||||
json_config = JsonParser(test_source)
|
||||
return json_config.get_driver_type()
|
||||
except ParamError as error:
|
||||
LOG.error(error)
|
||||
LOG.error(error, error_no=error.error_no)
|
||||
return ""
|
||||
|
||||
|
||||
@ -249,63 +287,116 @@ def _make_test_descriptors_from_testsources(test_sources, config):
|
||||
|
||||
# get params
|
||||
config_file = _get_config_file(
|
||||
os.path.join(os.path.dirname(test_source), filename))
|
||||
os.path.join(os.path.dirname(test_source), filename), ext, config)
|
||||
test_type = _get_test_type(config_file, test_driver, ext)
|
||||
if not test_type:
|
||||
LOG.error("no driver to execute '%s'" % test_source)
|
||||
continue
|
||||
|
||||
desc = _create_descriptor(config_file, filename, test_source,
|
||||
test_type)
|
||||
test_descriptors.append(desc)
|
||||
test_type, config)
|
||||
if desc:
|
||||
test_descriptors.append(desc)
|
||||
|
||||
return test_descriptors
|
||||
|
||||
|
||||
def _create_descriptor(config_file, filename, test_source, test_type):
|
||||
def _create_descriptor(config_file, filename, test_source, test_type, config):
|
||||
from xdevice import Scheduler
|
||||
from _core.executor.request import Descriptor
|
||||
|
||||
uid = unique_id("TestSource", filename)
|
||||
error_message = ""
|
||||
if not test_type:
|
||||
error_message = "no driver to execute '%s'" % test_source
|
||||
LOG.error(error_message, error_no="00112")
|
||||
if Scheduler.mode != ModeType.decc:
|
||||
return None
|
||||
|
||||
# create Descriptor
|
||||
if os.path.isfile(test_source):
|
||||
desc = Descriptor(uuid=uid, name=filename,
|
||||
source=TestSource(test_source, "", config_file,
|
||||
filename, test_type))
|
||||
elif is_config_str(test_source):
|
||||
desc = Descriptor(uuid=uid, name=filename,
|
||||
source=TestSource("", test_source, config_file,
|
||||
filename, test_type))
|
||||
else:
|
||||
raise ParamError("test source '%s' or '%s' not exists" % (
|
||||
test_source, "%s%s" % (test_source, ".json")))
|
||||
uid = unique_id("TestSource", filename)
|
||||
module_name = _parse_module_name(config_file, filename)
|
||||
desc = Descriptor(uuid=uid, name=filename,
|
||||
source=TestSource(test_source, "", config_file,
|
||||
filename, test_type, module_name))
|
||||
if not os.path.isfile(test_source):
|
||||
if is_config_str(test_source):
|
||||
desc = Descriptor(uuid=uid, name=filename,
|
||||
source=TestSource("", test_source, config_file,
|
||||
filename, test_type,
|
||||
module_name))
|
||||
else:
|
||||
if config.testcase and not config.testlist:
|
||||
error_message = "test case '%s' or '%s' not exists" % (
|
||||
"%s%s" % (test_source, PY_SUFFIX), "%s%s" % (
|
||||
test_source, PYD_SUFFIX))
|
||||
error_no = "00103"
|
||||
else:
|
||||
error_message = "test source '%s' or '%s' not exists" % (
|
||||
test_source, "%s%s" % (test_source, MODULE_CONFIG_SUFFIX))
|
||||
error_no = "00102"
|
||||
if Scheduler.mode != ModeType.decc:
|
||||
raise ParamError(error_message, error_no=error_no)
|
||||
|
||||
if Scheduler.mode == ModeType.decc and error_message:
|
||||
Scheduler.report_not_executed(config.report_path, [("", desc)],
|
||||
error_message)
|
||||
return None
|
||||
|
||||
return desc
|
||||
|
||||
|
||||
def _get_config_file(filename):
|
||||
def _get_config_file(filename, ext=None, config=None):
|
||||
config_file = None
|
||||
if os.path.exists(filename + MODULE_CONFIG_SUFFIX):
|
||||
config_file = filename + MODULE_CONFIG_SUFFIX
|
||||
if os.path.exists("%s%s" % (filename, MODULE_CONFIG_SUFFIX)):
|
||||
config_file = "%s%s" % (filename, MODULE_CONFIG_SUFFIX)
|
||||
return config_file
|
||||
if ext and os.path.exists("%s%s%s" % (filename, ext,
|
||||
MODULE_CONFIG_SUFFIX)):
|
||||
config_file = "%s%s%s" % (filename, ext, MODULE_CONFIG_SUFFIX)
|
||||
return config_file
|
||||
if config and getattr(config, "testcase", "") and not getattr(
|
||||
config, "testlist"):
|
||||
return _get_testcase_config_file(filename)
|
||||
|
||||
return config_file
|
||||
|
||||
|
||||
def _get_testcase_config_file(filename):
|
||||
depth = 1
|
||||
dirname = os.path.dirname(filename)
|
||||
while dirname and depth < 6:
|
||||
for item in os.listdir(dirname):
|
||||
item_path = os.path.join(dirname, item)
|
||||
if os.path.isfile(item_path) and item.endswith(
|
||||
MODULE_CONFIG_SUFFIX):
|
||||
return item_path
|
||||
depth += 1
|
||||
dirname = os.path.dirname(dirname)
|
||||
return None
|
||||
|
||||
|
||||
def _get_test_type(config_file, test_driver, ext):
|
||||
if test_driver:
|
||||
return test_driver
|
||||
|
||||
if config_file:
|
||||
if not os.path.exists(config_file):
|
||||
LOG.error("config file '%s' not exists" % config_file)
|
||||
LOG.error("config file '%s' not exists" % config_file,
|
||||
error_no="00110")
|
||||
return ""
|
||||
return _get_test_driver(config_file)
|
||||
|
||||
# .py .js .hap, .dex, .bin
|
||||
if ext in [".py", ".js", ".dex", ".hap", ".bin"] \
|
||||
and ext in EXT_TYPE_DICT.keys():
|
||||
test_type = EXT_TYPE_DICT[ext]
|
||||
# .apk
|
||||
elif ext in [".apk"] and ext in EXT_TYPE_DICT.keys():
|
||||
elif ext in [AppConst.app_ext] and ext in EXT_TYPE_DICT.keys():
|
||||
test_type = DeviceTestType.hap_test
|
||||
# cxx
|
||||
else:
|
||||
test_type = DeviceTestType.cpp_test
|
||||
return test_type
|
||||
|
||||
|
||||
def _parse_module_name(config_file, file_name):
|
||||
if config_file:
|
||||
return get_filename_extension(config_file)[0]
|
||||
else:
|
||||
if "{" in file_name:
|
||||
return "report"
|
||||
return file_name
|
||||
|
@ -2,7 +2,7 @@
|
||||
# coding=utf-8
|
||||
|
||||
#
|
||||
# Copyright (c) 2020 Huawei Device Co., Ltd.
|
||||
# Copyright (c) 2020-2021 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
|
||||
@ -21,7 +21,7 @@ from abc import abstractmethod
|
||||
from enum import Enum
|
||||
|
||||
__all__ = ["LifeCycle", "IDevice", "IDriver", "IListener", "IShellReceiver",
|
||||
"IParser", "ITestKit", "IScheduler", "IDeviceManager"]
|
||||
"IParser", "ITestKit", "IScheduler", "IDeviceManager", "IReporter"]
|
||||
|
||||
|
||||
class LifeCycle(Enum):
|
||||
@ -49,6 +49,7 @@ class IDeviceManager(ABC):
|
||||
Class managing the set of different types of devices for testing
|
||||
"""
|
||||
__slots__ = ()
|
||||
support_labels = []
|
||||
|
||||
@abstractmethod
|
||||
def apply_device(self, device_option, timeout=10):
|
||||
@ -64,6 +65,18 @@ class IDeviceManager(ABC):
|
||||
return _check_methods(class_info, "__serial__")
|
||||
return NotImplemented
|
||||
|
||||
@abstractmethod
|
||||
def init_environment(self, environment, user_config_file):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def env_stop(self):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def list_devices(self):
|
||||
pass
|
||||
|
||||
|
||||
class IDevice(ABC):
|
||||
"""
|
||||
@ -71,6 +84,7 @@ class IDevice(ABC):
|
||||
devices
|
||||
"""
|
||||
__slots__ = ()
|
||||
extend_value = {}
|
||||
|
||||
@abstractmethod
|
||||
def __set_serial__(self, device_sn=""):
|
||||
@ -86,6 +100,16 @@ class IDevice(ABC):
|
||||
return _check_methods(class_info, "__serial__")
|
||||
return NotImplemented
|
||||
|
||||
@abstractmethod
|
||||
def get(self, key=None, default=None):
|
||||
if not key:
|
||||
return default
|
||||
value = getattr(self, key, None)
|
||||
if value:
|
||||
return value
|
||||
else:
|
||||
return self.extend_value.get(key, default)
|
||||
|
||||
|
||||
class IDriver(ABC):
|
||||
"""
|
||||
@ -306,3 +330,20 @@ class ITestKit(ABC):
|
||||
return _check_methods(class_info, "__check_config__", "__setup__",
|
||||
"__teardown__")
|
||||
return NotImplemented
|
||||
|
||||
|
||||
class IReporter(ABC):
|
||||
"""
|
||||
A reporter to generate reports
|
||||
"""
|
||||
__slots__ = ()
|
||||
|
||||
@abstractmethod
|
||||
def __generate_reports__(self, report_path, **kwargs):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def __subclasshook__(cls, class_info):
|
||||
if cls is IReporter:
|
||||
return _check_methods(class_info, "__generate_reports__")
|
||||
return NotImplemented
|
||||
|
@ -2,7 +2,7 @@
|
||||
# coding=utf-8
|
||||
|
||||
#
|
||||
# Copyright (c) 2020 Huawei Device Co., Ltd.
|
||||
# Copyright (c) 2020-2021 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
|
||||
@ -18,18 +18,24 @@
|
||||
|
||||
import logging
|
||||
import sys
|
||||
import time
|
||||
from logging.handlers import RotatingFileHandler
|
||||
|
||||
from _core.constants import LogType
|
||||
from _core.plugin import Plugin
|
||||
from _core.plugin import get_plugin
|
||||
from _core.exception import ParamError
|
||||
|
||||
|
||||
__all__ = ["Log", "platform_logger", "device_logger", "shutdown",
|
||||
"add_task_file_handler", "remove_task_file_handler"]
|
||||
"add_task_file_handler", "remove_task_file_handler",
|
||||
"change_logger_level", "add_encrypt_file_handler",
|
||||
"remove_encrypt_file_handler"]
|
||||
|
||||
_HANDLERS = []
|
||||
_LOGGERS = []
|
||||
MAX_LOG_LENGTH = 10 * 1024 * 1024
|
||||
MAX_ENCRYPT_LOG_LENGTH = 5 * 1024 * 1024
|
||||
|
||||
|
||||
class Log:
|
||||
@ -40,6 +46,7 @@ class Log:
|
||||
self.handlers = []
|
||||
self.loggers = {}
|
||||
self.task_file_handler = None
|
||||
self.encrypt_file_handler = None
|
||||
|
||||
def __initial__(self, log_handler_flag, log_file=None, level=None,
|
||||
log_format=None):
|
||||
@ -64,6 +71,9 @@ class Log:
|
||||
self.task_file_handler = None
|
||||
_HANDLERS.extend(self.handlers)
|
||||
|
||||
def set_level(self, level):
|
||||
self.level = level
|
||||
|
||||
def __logger__(self, name=None):
|
||||
if not name:
|
||||
return _init_global_logger(name)
|
||||
@ -98,6 +108,27 @@ class Log:
|
||||
self.task_file_handler.close()
|
||||
self.task_file_handler = None
|
||||
|
||||
def add_encrypt_file_handler(self, log_file):
|
||||
from xdevice import Variables
|
||||
|
||||
file_handler = \
|
||||
EncryptFileHandler(log_file, mode="ab",
|
||||
max_bytes=MAX_ENCRYPT_LOG_LENGTH,
|
||||
backup_count=5, encoding="utf-8")
|
||||
file_handler.setFormatter(logging.Formatter(
|
||||
Variables.report_vars.log_format))
|
||||
self.encrypt_file_handler = file_handler
|
||||
for _, log in self.loggers.items():
|
||||
log.add_encrypt_log(self.encrypt_file_handler)
|
||||
|
||||
def remove_encrypt_file_handler(self):
|
||||
if self.encrypt_file_handler is None:
|
||||
return
|
||||
for _, log in self.loggers.items():
|
||||
log.remove_encrypt_log(self.encrypt_file_handler)
|
||||
self.encrypt_file_handler.close()
|
||||
self.encrypt_file_handler = None
|
||||
|
||||
|
||||
class FrameworkLog:
|
||||
|
||||
@ -105,12 +136,23 @@ class FrameworkLog:
|
||||
self.name = name
|
||||
self.platform_log = logging.Logger(name)
|
||||
self.task_log = None
|
||||
self.encrypt_log = None
|
||||
|
||||
def set_level(self, level):
|
||||
# apply to dynamic change logger level, and
|
||||
# only change the level of platform log
|
||||
cache = getattr(self.platform_log, "_cache", None)
|
||||
if cache and isinstance(cache, dict):
|
||||
cache.clear()
|
||||
self.platform_log.setLevel(level)
|
||||
|
||||
def add_task_log(self, handler):
|
||||
if self.task_log:
|
||||
return
|
||||
self.task_log = logging.Logger(self.name)
|
||||
self.task_log.setLevel(logging.DEBUG)
|
||||
log_level = getattr(sys, "log_level", logging.INFO) if hasattr(
|
||||
sys, "log_level") else logging.DEBUG
|
||||
self.task_log.setLevel(log_level)
|
||||
self.task_log.addHandler(handler)
|
||||
|
||||
def remove_task_log(self, handler):
|
||||
@ -119,30 +161,105 @@ class FrameworkLog:
|
||||
self.task_log.removeHandler(handler)
|
||||
self.task_log = None
|
||||
|
||||
def add_encrypt_log(self, handler):
|
||||
if self.encrypt_log:
|
||||
return
|
||||
self.encrypt_log = logging.Logger(self.name)
|
||||
log_level = getattr(sys, "log_level", logging.INFO) if hasattr(
|
||||
sys, "log_level") else logging.DEBUG
|
||||
self.encrypt_log.setLevel(log_level)
|
||||
self.encrypt_log.addHandler(handler)
|
||||
|
||||
def remove_encrypt_log(self, handler):
|
||||
if not self.encrypt_log:
|
||||
return
|
||||
self.encrypt_log.removeHandler(handler)
|
||||
self.encrypt_log = None
|
||||
|
||||
def info(self, msg, *args, **kwargs):
|
||||
self.platform_log.info(msg, *args, **kwargs)
|
||||
additional_output = self._get_additional_output(**kwargs)
|
||||
updated_msg = self._update_msg(additional_output, msg)
|
||||
self.platform_log.info(updated_msg, *args)
|
||||
if self.task_log:
|
||||
self.task_log.info(msg, *args, **kwargs)
|
||||
self.task_log.info(updated_msg, *args)
|
||||
if self.encrypt_log:
|
||||
self.encrypt_log.info(updated_msg, *args)
|
||||
|
||||
def debug(self, msg, *args, **kwargs):
|
||||
self.platform_log.debug(msg, *args, **kwargs)
|
||||
if self.task_log:
|
||||
self.task_log.debug(msg, *args, **kwargs)
|
||||
additional_output = self._get_additional_output(**kwargs)
|
||||
updated_msg = self._update_msg(additional_output, msg)
|
||||
from _core.report.encrypt import check_pub_key_exist
|
||||
if not check_pub_key_exist():
|
||||
self.platform_log.debug(updated_msg, *args)
|
||||
if self.task_log:
|
||||
self.task_log.debug(updated_msg, *args)
|
||||
else:
|
||||
if self.encrypt_log:
|
||||
self.encrypt_log.debug(updated_msg, *args)
|
||||
|
||||
def error(self, msg, *args, **kwargs):
|
||||
self.platform_log.error(msg, *args, **kwargs)
|
||||
error_no = kwargs.get("error_no", "00000")
|
||||
additional_output = self._get_additional_output(error_no, **kwargs)
|
||||
updated_msg = self._update_msg(additional_output, msg)
|
||||
|
||||
self.platform_log.error(updated_msg, *args)
|
||||
if self.task_log:
|
||||
self.task_log.error(msg, *args, **kwargs)
|
||||
self.task_log.error(updated_msg, *args)
|
||||
if self.encrypt_log:
|
||||
self.encrypt_log.error(updated_msg, *args)
|
||||
|
||||
def warning(self, msg, *args, **kwargs):
|
||||
self.platform_log.warning(msg, *args, **kwargs)
|
||||
if self.task_log:
|
||||
self.task_log.warning(msg, *args, **kwargs)
|
||||
additional_output = self._get_additional_output(**kwargs)
|
||||
updated_msg = self._update_msg(additional_output, msg)
|
||||
|
||||
def exception(self, msg, exc_info=True, **kwargs):
|
||||
self.platform_log.exception(msg, exc_info=exc_info, **kwargs)
|
||||
self.platform_log.warning(updated_msg, *args)
|
||||
if self.task_log:
|
||||
self.task_log.exception(msg, exc_info=exc_info, **kwargs)
|
||||
self.task_log.warning(updated_msg, *args)
|
||||
if self.encrypt_log:
|
||||
self.encrypt_log.warning(updated_msg, *args)
|
||||
|
||||
def exception(self, msg, *args, **kwargs):
|
||||
error_no = kwargs.get("error_no", "00000")
|
||||
exc_info = kwargs.get("exc_info", True)
|
||||
if exc_info is not True and exc_info is not False:
|
||||
exc_info = True
|
||||
additional_output = self._get_additional_output(error_no, **kwargs)
|
||||
updated_msg = self._update_msg(additional_output, msg)
|
||||
|
||||
self.platform_log.exception(updated_msg, exc_info=exc_info, *args)
|
||||
if self.task_log:
|
||||
self.task_log.exception(updated_msg, exc_info=exc_info, *args)
|
||||
if self.encrypt_log:
|
||||
self.encrypt_log.exception(updated_msg, exc_info=exc_info, *args)
|
||||
|
||||
@classmethod
|
||||
def _update_msg(cls, additional_output, msg):
|
||||
msg = "[%s]" % msg if msg else msg
|
||||
if msg and additional_output:
|
||||
msg = "%s [%s]" % (msg, additional_output)
|
||||
return msg
|
||||
|
||||
def _get_additional_output(self, error_number=None, **kwargs):
|
||||
dict_str = self._get_dict_str(**kwargs)
|
||||
if error_number:
|
||||
additional_output = "ErrorNo=%s" % error_number
|
||||
else:
|
||||
return dict_str
|
||||
|
||||
if dict_str:
|
||||
additional_output = "%s, %s" % (additional_output, dict_str)
|
||||
return additional_output
|
||||
|
||||
@classmethod
|
||||
def _get_dict_str(cls, **kwargs):
|
||||
dict_str = ""
|
||||
for key, value in kwargs.items():
|
||||
if key in ["error_no", "exc_info"]:
|
||||
continue
|
||||
dict_str = "%s%s=%s, " % (dict_str, key, value)
|
||||
if dict_str:
|
||||
dict_str = dict_str[:-2]
|
||||
return dict_str
|
||||
|
||||
|
||||
def platform_logger(name=None):
|
||||
@ -189,11 +306,131 @@ def remove_task_file_handler():
|
||||
log_plugin.remove_task_file_handler()
|
||||
|
||||
|
||||
def add_encrypt_file_handler(log_file=None):
|
||||
if log_file is None:
|
||||
return
|
||||
plugins = get_plugin(Plugin.LOG, LogType.tool)
|
||||
for log_plugin in plugins:
|
||||
if log_plugin.get_plugin_config().enabled:
|
||||
log_plugin.add_encrypt_file_handler(log_file)
|
||||
|
||||
|
||||
def remove_encrypt_file_handler():
|
||||
plugins = get_plugin(Plugin.LOG, LogType.tool)
|
||||
for log_plugin in plugins:
|
||||
if log_plugin.get_plugin_config().enabled:
|
||||
log_plugin.remove_encrypt_file_handler()
|
||||
|
||||
|
||||
def _init_global_logger(name=None):
|
||||
handler = logging.StreamHandler(sys.stdout)
|
||||
log_format = "%(asctime)s %(name)-15s %(levelname)-8s %(message)s"
|
||||
log_format = "[%(asctime)s] [%(name)s] [%(levelname)s] [%(message)s]"
|
||||
handler.setFormatter(logging.Formatter(log_format))
|
||||
log = logging.Logger(name)
|
||||
log.setLevel(logging.INFO)
|
||||
log.addHandler(handler)
|
||||
log = FrameworkLog(name)
|
||||
log.platform_log.setLevel(logging.INFO)
|
||||
log.platform_log.addHandler(handler)
|
||||
return log
|
||||
|
||||
|
||||
def change_logger_level(leve_dict):
|
||||
level_map = {"debug": logging.DEBUG, "info": logging.INFO}
|
||||
if "console" in leve_dict.keys():
|
||||
level = leve_dict["console"]
|
||||
if not level:
|
||||
return
|
||||
if str(level).lower() in level_map.keys():
|
||||
logger_level = level_map.get(str(level).lower(), logging.INFO)
|
||||
|
||||
# change level of loggers which will to be instantiated.
|
||||
# Actually, it changes the level attribute in ToolLog,
|
||||
# which will be used when instantiating the FrameLog object.
|
||||
plugins = get_plugin(Plugin.LOG, LogType.tool)
|
||||
for log_plugin in plugins:
|
||||
log_plugin.set_level(logger_level)
|
||||
# change level of loggers which have instantiated
|
||||
for logger in _LOGGERS:
|
||||
if getattr(logger, "setLevel", None):
|
||||
logger.setLevel(logger_level)
|
||||
elif getattr(logger, "set_level", None):
|
||||
logger.set_level(logger_level)
|
||||
|
||||
if "file" in leve_dict.keys():
|
||||
level = leve_dict["file"]
|
||||
if not level:
|
||||
return
|
||||
if str(level).lower() in level_map.keys():
|
||||
logger_level = level_map.get(str(level).lower(), logging.INFO)
|
||||
setattr(sys, "log_level", logger_level)
|
||||
|
||||
|
||||
class EncryptFileHandler(RotatingFileHandler):
|
||||
|
||||
def __init__(self, filename, mode='ab', max_bytes=0, backup_count=0,
|
||||
encoding=None, delay=False):
|
||||
RotatingFileHandler.__init__(self, filename, mode, max_bytes,
|
||||
backup_count, encoding, delay)
|
||||
self.mode = mode
|
||||
self.encrypt_error = None
|
||||
|
||||
def _open(self):
|
||||
if not self.mode == "ab":
|
||||
self.mode = "ab"
|
||||
|
||||
# baseFilename is the attribute in FileHandler
|
||||
base_file_name = getattr(self, "baseFilename", None)
|
||||
return open(base_file_name, self.mode)
|
||||
|
||||
def emit(self, record):
|
||||
try:
|
||||
if not self._encrypt_valid():
|
||||
return
|
||||
|
||||
# shouldRoller and doRoller is the method in RotatingFileHandler
|
||||
should_rollover = getattr(self, "shouldRollover", None)
|
||||
if callable(should_rollover) and should_rollover(record):
|
||||
self.doRollover()
|
||||
|
||||
# stream is the attribute in StreamHandler
|
||||
if not getattr(self, "stream", None):
|
||||
setattr(self, "stream", self._open())
|
||||
msg = self.format(record)
|
||||
stream = getattr(self, "stream", self._open())
|
||||
stream.write(msg)
|
||||
self.flush()
|
||||
except RecursionError:
|
||||
raise
|
||||
|
||||
def _encrypt_valid(self):
|
||||
from _core.report.encrypt import check_pub_key_exist
|
||||
if check_pub_key_exist() and not self.encrypt_error:
|
||||
return True
|
||||
|
||||
def format(self, record):
|
||||
"""
|
||||
Customize the implementation method. If the log format of the
|
||||
framework changes, update the return value format of the method
|
||||
in a timely manner.
|
||||
:param record: logging.LogRecord
|
||||
:return: bytes
|
||||
"""
|
||||
from _core.report.encrypt import do_rsa_encrypt
|
||||
create_time = "{},{}".format(
|
||||
time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(record.created)),
|
||||
"{:0>3d}".format(int("%d" % record.msecs)))
|
||||
name = record.name
|
||||
level_name = record.levelname
|
||||
msg = record.msg
|
||||
if msg and "%s" in msg:
|
||||
msg = msg % record.args
|
||||
info = "[%s] [%s] [%s] %s%s" \
|
||||
% (create_time, name, level_name, msg, "\n")
|
||||
|
||||
try:
|
||||
return do_rsa_encrypt(info)
|
||||
except ParamError as error:
|
||||
error_no_str = \
|
||||
"ErrorNo={}".format(getattr(error, "error_no", "00113"))
|
||||
info = "[%s] [%s] [%s] [%s] [%s]\n" % (
|
||||
create_time, name, "ERROR", error, error_no_str)
|
||||
self.encrypt_error = bytes(info, "utf-8")
|
||||
return self.encrypt_error
|
||||
|
@ -2,7 +2,7 @@
|
||||
# coding=utf-8
|
||||
|
||||
#
|
||||
# Copyright (c) 2020 Huawei Device Co., Ltd.
|
||||
# Copyright (c) 2020-2021 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
|
||||
@ -24,6 +24,8 @@ from _core.interface import IListener
|
||||
from _core.interface import IScheduler
|
||||
from _core.interface import IDevice
|
||||
from _core.interface import ITestKit
|
||||
from _core.interface import IDeviceManager
|
||||
from _core.interface import IReporter
|
||||
|
||||
__all__ = ["Config", "Plugin", "get_plugin", "set_plugin_params",
|
||||
"get_all_plugins", "clear_plugin_cache"]
|
||||
@ -53,6 +55,12 @@ class Config:
|
||||
def update(self, params):
|
||||
self.__dict__.update(params)
|
||||
|
||||
def get(self, key, default=""):
|
||||
return self.__dict__.get(key, default)
|
||||
|
||||
def set(self, key, value):
|
||||
self.__dict__[key] = value
|
||||
|
||||
|
||||
class Plugin(object):
|
||||
"""
|
||||
@ -68,6 +76,7 @@ class Plugin(object):
|
||||
LISTENER = "listener"
|
||||
TEST_KIT = "testkit"
|
||||
MANAGER = "manager"
|
||||
REPORTER = "reporter"
|
||||
|
||||
_builtin_plugin = dict({
|
||||
SCHEDULER: [IScheduler],
|
||||
@ -75,7 +84,9 @@ class Plugin(object):
|
||||
DEVICE: [IDevice],
|
||||
PARSER: [IParser],
|
||||
LISTENER: [IListener],
|
||||
TEST_KIT: [ITestKit]
|
||||
TEST_KIT: [ITestKit],
|
||||
MANAGER: [IDeviceManager],
|
||||
REPORTER: [IReporter]
|
||||
})
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
@ -129,8 +140,12 @@ class Plugin(object):
|
||||
"{} plugin must implement {} interface.".format(
|
||||
cls.__name__, interface))
|
||||
|
||||
_PLUGINS.setdefault((self.plugin_type, self.plugin_id), []).insert(
|
||||
0, instance)
|
||||
if "xdevice" in str(instance.__class__).lower():
|
||||
_PLUGINS.setdefault((self.plugin_type, self.plugin_id), []).append(
|
||||
instance)
|
||||
else:
|
||||
_PLUGINS.setdefault((self.plugin_type, self.plugin_id), []).insert(
|
||||
0, instance)
|
||||
|
||||
return cls
|
||||
|
||||
@ -149,8 +164,20 @@ def get_plugin(plugin_type, plugin_id=None):
|
||||
:return: the instance list of plugin
|
||||
"""
|
||||
if plugin_id is None:
|
||||
plugin_id = plugin_type
|
||||
return _PLUGINS.get((plugin_type, plugin_id), [])
|
||||
plugins = []
|
||||
for key in _PLUGINS:
|
||||
if key[0] != plugin_type:
|
||||
continue
|
||||
if not _PLUGINS.get(key):
|
||||
continue
|
||||
if key[1] == plugin_type:
|
||||
plugins.insert(0, _PLUGINS.get(key)[0])
|
||||
else:
|
||||
plugins.append(_PLUGINS.get(key)[0])
|
||||
return plugins
|
||||
|
||||
else:
|
||||
return _PLUGINS.get((plugin_type, plugin_id), [])
|
||||
|
||||
|
||||
def set_plugin_params(plugin_type, plugin_id=None, **kwargs):
|
||||
|
0
src/xdevice/_core/report/__init__.py
Executable file → Normal file
0
src/xdevice/_core/report/__init__.py
Executable file → Normal file
@ -2,7 +2,7 @@
|
||||
# coding=utf-8
|
||||
|
||||
#
|
||||
# Copyright (c) 2020 Huawei Device Co., Ltd.
|
||||
# Copyright (c) 2020-2021 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
|
||||
@ -55,8 +55,8 @@ def main_report():
|
||||
task_info.device_name = "None"
|
||||
task_info.test_time = time.strftime(ReportConstant.time_format,
|
||||
time.localtime())
|
||||
result_report = ResultReporter(report_path, task_info)
|
||||
result_report.generate_reports()
|
||||
result_report = ResultReporter()
|
||||
result_report.__generate_reports__(report_path, task_info=task_info)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
@ -2,7 +2,7 @@
|
||||
# coding=utf-8
|
||||
|
||||
#
|
||||
# Copyright (c) 2020 Huawei Device Co., Ltd.
|
||||
# Copyright (c) 2020-2021 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
|
||||
@ -20,16 +20,21 @@ import os
|
||||
import hashlib
|
||||
|
||||
from _core.logger import platform_logger
|
||||
from _core.exception import ParamError
|
||||
|
||||
__all__ = ["check_pub_key_exist", "do_rsa_encrypt", "do_rsa_decrypt",
|
||||
"generate_key_file", "get_file_summary"]
|
||||
|
||||
PUBLIC_KEY_FILE = "config/pub.key"
|
||||
PRIVATE_KEY_FILE = "config/pri.key"
|
||||
LOG = platform_logger("Encrypt")
|
||||
|
||||
|
||||
def check_pub_key_exist():
|
||||
from xdevice import Variables
|
||||
if Variables.report_vars.pub_key_string:
|
||||
return Variables.report_vars.pub_key_string
|
||||
|
||||
if Variables.report_vars.pub_key_file is not None:
|
||||
if Variables.report_vars.pub_key_file == "":
|
||||
return False
|
||||
@ -62,27 +67,31 @@ def do_rsa_encrypt(content):
|
||||
|
||||
import rsa
|
||||
from xdevice import Variables
|
||||
with open(Variables.report_vars.pub_key_file, 'rb') as key_content:
|
||||
# get params
|
||||
public_key = rsa.PublicKey.load_pkcs1(key_content.read())
|
||||
max_encrypt_len = int(public_key.n.bit_length() / 8) - 11
|
||||
if not Variables.report_vars.pub_key_string:
|
||||
with open(Variables.report_vars.pub_key_file,
|
||||
'rb') as key_content:
|
||||
Variables.report_vars.pub_key_string = key_content.read()
|
||||
|
||||
try:
|
||||
# encrypt
|
||||
cipher_text = b""
|
||||
for frag in _get_frags(plain_text, max_encrypt_len):
|
||||
cipher_text_frag = rsa.encrypt(frag, public_key)
|
||||
cipher_text += cipher_text_frag
|
||||
return cipher_text
|
||||
except rsa.pkcs1.CryptoError as error:
|
||||
error_msg = "rsa encryption error occurs, %s" % error.args[0]
|
||||
LOG.error(error_msg)
|
||||
return bytes(error_msg, 'utf-8')
|
||||
if isinstance(Variables.report_vars.pub_key_string, str):
|
||||
Variables.report_vars.pub_key_string =\
|
||||
bytes(Variables.report_vars.pub_key_string, "utf-8")
|
||||
|
||||
except (ModuleNotFoundError, ValueError, TypeError, UnicodeError) as error:
|
||||
public_key = rsa.PublicKey.load_pkcs1_openssl_pem(
|
||||
Variables.report_vars.pub_key_string)
|
||||
|
||||
max_encrypt_len = int(public_key.n.bit_length() / 8) - 11
|
||||
|
||||
# encrypt
|
||||
cipher_text = b""
|
||||
for frag in _get_frags(plain_text, max_encrypt_len):
|
||||
cipher_text_frag = rsa.encrypt(frag, public_key)
|
||||
cipher_text += cipher_text_frag
|
||||
return cipher_text
|
||||
|
||||
except (ModuleNotFoundError, ValueError, TypeError, UnicodeError,
|
||||
Exception) as error:
|
||||
error_msg = "rsa encryption error occurs, %s" % error.args[0]
|
||||
LOG.error(error_msg)
|
||||
return bytes(error_msg, 'utf-8')
|
||||
raise ParamError(error_msg, error_no="00113")
|
||||
|
||||
|
||||
def do_rsa_decrypt(content):
|
||||
@ -96,8 +105,11 @@ def do_rsa_decrypt(content):
|
||||
|
||||
import rsa
|
||||
from xdevice import Variables
|
||||
pri_key_file = os.path.join(os.path.dirname(
|
||||
Variables.report_vars.pub_key_file), "pri.key")
|
||||
pri_key_path = os.path.join(Variables.exec_dir, PRIVATE_KEY_FILE)
|
||||
if os.path.exists(pri_key_path):
|
||||
pri_key_file = pri_key_path
|
||||
else:
|
||||
pri_key_file = os.path.join(Variables.top_dir, PRIVATE_KEY_FILE)
|
||||
if not os.path.exists(pri_key_file):
|
||||
return content
|
||||
with open(pri_key_file, "rb") as key_content:
|
||||
@ -114,12 +126,12 @@ def do_rsa_decrypt(content):
|
||||
return plain_text.decode(encoding='utf-8')
|
||||
except rsa.pkcs1.CryptoError as error:
|
||||
error_msg = "rsa decryption error occurs, %s" % error.args[0]
|
||||
LOG.error(error_msg)
|
||||
LOG.error(error_msg, error_no="00114")
|
||||
return error_msg
|
||||
|
||||
except (ModuleNotFoundError, ValueError, TypeError, UnicodeError) as error:
|
||||
error_msg = "rsa decryption error occurs, %s" % error.args[0]
|
||||
LOG.error(error_msg)
|
||||
LOG.error(error_msg, error_no="00114")
|
||||
return error_msg
|
||||
|
||||
|
||||
@ -134,10 +146,17 @@ def generate_key_file(length=2048):
|
||||
pub_key, pri_key = key.newkeys(int(length))
|
||||
pub_key_pem = pub_key.save_pkcs1().decode()
|
||||
pri_key_pem = pri_key.save_pkcs1().decode()
|
||||
with open("pri.key", "w") as file_pri:
|
||||
|
||||
file_pri_open = os.open("pri.key", os.O_WRONLY | os.O_CREAT |
|
||||
os.O_APPEND, 0o755)
|
||||
file_pub_open = os.open("pub.key", os.O_WRONLY | os.O_CREAT |
|
||||
os.O_APPEND, 0o755)
|
||||
with os.fdopen(file_pri_open, "w") as file_pri, \
|
||||
os.fdopen(file_pub_open, "w") as file_pub:
|
||||
file_pri.write(pri_key_pem)
|
||||
with open("pub.key", "w") as file_pub:
|
||||
file_pri.flush()
|
||||
file_pub.write(pub_key_pem)
|
||||
file_pub.flush()
|
||||
except ModuleNotFoundError:
|
||||
return
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
# coding=utf-8
|
||||
|
||||
#
|
||||
# Copyright (c) 2020 Huawei Device Co., Ltd.
|
||||
# Copyright (c) 2020-2021 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
|
||||
@ -26,6 +26,7 @@ from xml.etree import ElementTree
|
||||
from _core.logger import platform_logger
|
||||
from _core.report.encrypt import check_pub_key_exist
|
||||
from _core.report.encrypt import do_rsa_encrypt
|
||||
from _core.exception import ParamError
|
||||
|
||||
LOG = platform_logger("ReporterHelper")
|
||||
|
||||
@ -37,13 +38,13 @@ class ReportConstant:
|
||||
summary_vision_report = "summary_report.html"
|
||||
details_vision_report = "details_report.html"
|
||||
failures_vision_report = "failures_report.html"
|
||||
task_info_record = "task_info.record"
|
||||
summary_ini = "summary.ini"
|
||||
summary_report_hash = "summary_report.hash"
|
||||
title_name = "title_name"
|
||||
summary_title = "Summary Report"
|
||||
details_title = "Details Report"
|
||||
failures_title = "Failures Report"
|
||||
decc_mode = "decc"
|
||||
|
||||
# exec_info constants
|
||||
platform = "platform"
|
||||
@ -56,9 +57,11 @@ class ReportConstant:
|
||||
execute_time = "execute_time"
|
||||
|
||||
# summary constants
|
||||
product_params = "product_params"
|
||||
product_info = "productinfo"
|
||||
product_info_ = "product_info"
|
||||
modules = "modules"
|
||||
modules_done = "modules_done"
|
||||
run_modules = "runmodules"
|
||||
run_modules_ = "run_modules"
|
||||
name = "name"
|
||||
time = "time"
|
||||
total = "total"
|
||||
@ -73,7 +76,8 @@ class ReportConstant:
|
||||
message = "message"
|
||||
|
||||
# case result constants
|
||||
module = "module"
|
||||
module_name = "modulename"
|
||||
module_name_ = "module_name"
|
||||
result = "result"
|
||||
status = "status"
|
||||
run = "run"
|
||||
@ -87,8 +91,8 @@ class ReportConstant:
|
||||
|
||||
# time constants
|
||||
time_stamp = "timestamp"
|
||||
start_time = "start_time"
|
||||
end_time = "end_time"
|
||||
start_time = "starttime"
|
||||
end_time = "endtime"
|
||||
time_format = "%Y-%m-%d %H:%M:%S"
|
||||
|
||||
# xml tag constants
|
||||
@ -117,14 +121,17 @@ class DataHelper:
|
||||
|
||||
@staticmethod
|
||||
def parse_data_report(data_report):
|
||||
if os.path.exists(data_report):
|
||||
with open(data_report, 'r', encoding='UTF-8') as file_content:
|
||||
if "<" not in data_report and os.path.exists(data_report):
|
||||
with open(data_report, 'r', encoding='UTF-8', errors="ignore") as \
|
||||
file_content:
|
||||
data_str = file_content.read()
|
||||
else:
|
||||
data_str = data_report
|
||||
|
||||
data_str = data_str.replace(chr(2), "").replace(chr(15), "") \
|
||||
.replace(chr(16), "").replace(chr(1), "")
|
||||
for char_index in range(32):
|
||||
if char_index in [10, 13]: # chr(10): LF, chr(13): CR
|
||||
continue
|
||||
data_str = data_str.replace(chr(char_index), "")
|
||||
try:
|
||||
return ElementTree.fromstring(data_str)
|
||||
except SyntaxError as error:
|
||||
@ -157,9 +164,22 @@ class DataHelper:
|
||||
self.LINE_BREAK_INDENT + self.INDENT, "")
|
||||
|
||||
@classmethod
|
||||
def get_summary_result(cls, report_path, file_name, key=None,
|
||||
reverse=False):
|
||||
data_reports = cls._get_data_reports(report_path)
|
||||
def update_suite_result(cls, suite, case):
|
||||
update_time = round(float(suite.get(
|
||||
ReportConstant.time, 0)) + float(
|
||||
case.get(ReportConstant.time, 0)), 3)
|
||||
suite.set(ReportConstant.time, str(update_time))
|
||||
update_tests = str(int(suite.get(ReportConstant.tests, 0))+1)
|
||||
suite.set(ReportConstant.tests, update_tests)
|
||||
if case.findall('failure'):
|
||||
update_failures = str(int(suite.get(ReportConstant.failures, 0))+1)
|
||||
suite.set(ReportConstant.failures, update_failures)
|
||||
|
||||
@classmethod
|
||||
def get_summary_result(cls, report_path, file_name, key=None, **kwargs):
|
||||
reverse = kwargs.get("reverse", False)
|
||||
file_prefix = kwargs.get("file_prefix", None)
|
||||
data_reports = cls._get_data_reports(report_path, file_prefix)
|
||||
if not data_reports:
|
||||
return
|
||||
if key:
|
||||
@ -171,24 +191,39 @@ class DataHelper:
|
||||
ReportConstant.unavailable]
|
||||
for data_report in data_reports:
|
||||
data_report_element = cls.parse_data_report(data_report)
|
||||
if not len(list(data_report_element)):
|
||||
continue
|
||||
if not summary_result:
|
||||
summary_result = data_report_element
|
||||
continue
|
||||
summary_testsuite = summary_result[0]
|
||||
data_testsuite = data_report_element[0]
|
||||
cls._update_attributes(summary_testsuite, data_testsuite,
|
||||
need_update_attributes)
|
||||
for element in data_testsuite:
|
||||
summary_testsuite.append(element)
|
||||
|
||||
need_update_attributes.append(ReportConstant.time)
|
||||
for attribute in need_update_attributes:
|
||||
summary_result.set(attribute, summary_result[0].get(attribute))
|
||||
|
||||
cls.generate_report(summary_result, file_name)
|
||||
if not summary_result or not data_report_element:
|
||||
continue
|
||||
for data_suite in data_report_element:
|
||||
for summary_suite in summary_result:
|
||||
if data_suite.get("name", None) == \
|
||||
summary_suite.get("name", None):
|
||||
for data_case in data_suite:
|
||||
for summary_case in summary_suite:
|
||||
if data_case.get("name", None) == \
|
||||
summary_case.get("name", None):
|
||||
break
|
||||
else:
|
||||
summary_suite.append(data_case)
|
||||
DataHelper.update_suite_result(summary_result,
|
||||
data_case)
|
||||
DataHelper.update_suite_result(summary_suite,
|
||||
data_case)
|
||||
break
|
||||
else:
|
||||
summary_result.append(data_suite)
|
||||
DataHelper._update_attributes(summary_result, data_suite,
|
||||
need_update_attributes)
|
||||
if summary_result:
|
||||
cls.generate_report(summary_result, file_name)
|
||||
return summary_result
|
||||
|
||||
@classmethod
|
||||
def _get_data_reports(cls, report_path):
|
||||
def _get_data_reports(cls, report_path, file_prefix=None):
|
||||
if not os.path.isdir(report_path):
|
||||
return []
|
||||
data_reports = []
|
||||
@ -196,6 +231,8 @@ class DataHelper:
|
||||
for file_name in files:
|
||||
if not file_name.endswith(cls.DATA_REPORT_SUFFIX):
|
||||
continue
|
||||
if file_prefix and not file_name.startswith(file_prefix):
|
||||
continue
|
||||
data_reports.append(os.path.join(root, file_name))
|
||||
return data_reports
|
||||
|
||||
@ -209,16 +246,26 @@ class DataHelper:
|
||||
# update time
|
||||
updated_time = round(float(summary_element.get(
|
||||
ReportConstant.time, 0)) + float(
|
||||
summary_element.get(ReportConstant.time, 0)), 3)
|
||||
data_element.get(ReportConstant.time, 0)), 3)
|
||||
summary_element.set(ReportConstant.time, str(updated_time))
|
||||
|
||||
@staticmethod
|
||||
def generate_report(element, file_name):
|
||||
if check_pub_key_exist():
|
||||
plain_text = DataHelper.to_string(element)
|
||||
cipher_text = do_rsa_encrypt(plain_text)
|
||||
with open(file_name, "wb") as file_handler:
|
||||
try:
|
||||
cipher_text = do_rsa_encrypt(plain_text)
|
||||
except ParamError as error:
|
||||
LOG.error(error, error_no=error.error_no)
|
||||
cipher_text = b""
|
||||
if platform.system() == "Windows":
|
||||
flags = os.O_WRONLY | os.O_CREAT | os.O_APPEND | os.O_BINARY
|
||||
else:
|
||||
flags = os.O_WRONLY | os.O_CREAT | os.O_APPEND
|
||||
file_name_open = os.open(file_name, flags, 0o755)
|
||||
with os.fdopen(file_name_open, "wb") as file_handler:
|
||||
file_handler.write(cipher_text)
|
||||
file_handler.flush()
|
||||
else:
|
||||
tree = ElementTree.ElementTree(element)
|
||||
tree.write(file_name, encoding="UTF-8", xml_declaration=True,
|
||||
@ -244,7 +291,7 @@ class ExecInfo:
|
||||
log_path = ""
|
||||
platform = ""
|
||||
execute_time = ""
|
||||
product_params = dict()
|
||||
product_info = dict()
|
||||
|
||||
|
||||
class Result:
|
||||
@ -268,12 +315,12 @@ class Summary:
|
||||
keys = [ReportConstant.modules, ReportConstant.total,
|
||||
ReportConstant.passed, ReportConstant.failed,
|
||||
ReportConstant.blocked, ReportConstant.unavailable,
|
||||
ReportConstant.ignored, ReportConstant.modules_done]
|
||||
ReportConstant.ignored, ReportConstant.run_modules_]
|
||||
|
||||
def __init__(self):
|
||||
self.result = Result()
|
||||
self.modules = None
|
||||
self.modules_done = 0
|
||||
self.run_modules = 0
|
||||
|
||||
def get_result(self):
|
||||
return self.result
|
||||
@ -283,11 +330,11 @@ class Summary:
|
||||
|
||||
|
||||
class Suite:
|
||||
keys = [ReportConstant.module, ReportConstant.name, ReportConstant.total,
|
||||
ReportConstant.passed, ReportConstant.failed,
|
||||
ReportConstant.blocked, ReportConstant.ignored,
|
||||
ReportConstant.time]
|
||||
module = ReportConstant.empty_name
|
||||
keys = [ReportConstant.module_name_, ReportConstant.name,
|
||||
ReportConstant.total, ReportConstant.passed,
|
||||
ReportConstant.failed, ReportConstant.blocked,
|
||||
ReportConstant.ignored, ReportConstant.time]
|
||||
module_name = ReportConstant.empty_name
|
||||
name = ""
|
||||
time = ""
|
||||
|
||||
@ -301,14 +348,14 @@ class Suite:
|
||||
|
||||
def set_cases(self, element):
|
||||
if len(element) == 0:
|
||||
LOG.warning("%s has no testcase",
|
||||
element.get(ReportConstant.name, ""))
|
||||
LOG.debug("%s has no testcase",
|
||||
element.get(ReportConstant.name, ""))
|
||||
return
|
||||
|
||||
# get case context and add to self.cases
|
||||
for child in element:
|
||||
case = Case()
|
||||
case.module = self.module
|
||||
case.module_name = self.module_name
|
||||
for key, value in child.items():
|
||||
setattr(case, key, value)
|
||||
if len(child) > 0:
|
||||
@ -325,7 +372,7 @@ class Suite:
|
||||
|
||||
|
||||
class Case:
|
||||
module = ReportConstant.empty_name
|
||||
module_name = ReportConstant.empty_name
|
||||
name = ReportConstant.empty_name
|
||||
classname = ReportConstant.empty_name
|
||||
status = ""
|
||||
@ -405,27 +452,31 @@ class VisionHelper:
|
||||
"None")
|
||||
exec_info.host_info = platform.platform()
|
||||
start_time = self.summary_element.get(ReportConstant.start_time, "")
|
||||
if not start_time:
|
||||
start_time = self.summary_element.get("start_time", "")
|
||||
end_time = self.summary_element.get(ReportConstant.end_time, "")
|
||||
if not end_time:
|
||||
end_time = self.summary_element.get("end_time", "")
|
||||
exec_info.test_time = "%s/ %s" % (start_time, end_time)
|
||||
start_time = time.mktime(time.strptime(
|
||||
start_time, ReportConstant.time_format))
|
||||
end_time = time.mktime(time.strptime(
|
||||
end_time, ReportConstant.time_format))
|
||||
exec_info.execute_time = self._get_execute_time(round(
|
||||
exec_info.execute_time = self.get_execute_time(round(
|
||||
end_time - start_time, 3))
|
||||
exec_info.log_path = os.path.abspath(os.path.join(report_path, "log"))
|
||||
|
||||
try:
|
||||
product_params = self.summary_element.get(
|
||||
ReportConstant.product_params, "")
|
||||
if product_params:
|
||||
exec_info.product_params = literal_eval(str(product_params))
|
||||
product_info = self.summary_element.get(
|
||||
ReportConstant.product_info, "")
|
||||
if product_info:
|
||||
exec_info.product_info = literal_eval(str(product_info))
|
||||
except SyntaxError as error:
|
||||
LOG.error("summary report error: %s", error.args)
|
||||
return exec_info
|
||||
|
||||
@classmethod
|
||||
def _get_execute_time(cls, second_time):
|
||||
def get_execute_time(cls, second_time):
|
||||
hour, day = 0, 0
|
||||
second, minute = second_time % 60, second_time // 60
|
||||
if minute > 0:
|
||||
@ -445,8 +496,8 @@ class VisionHelper:
|
||||
summary = Summary()
|
||||
summary.modules = self.summary_element.get(
|
||||
ReportConstant.modules, 0)
|
||||
summary.modules_done = self.summary_element.get(
|
||||
ReportConstant.modules_done, 0)
|
||||
summary.run_modules = self.summary_element.get(
|
||||
ReportConstant.run_modules, 0)
|
||||
summary.result.total = int(self.summary_element.get(
|
||||
ReportConstant.tests, 0))
|
||||
summary.result.failed = int(
|
||||
@ -466,8 +517,8 @@ class VisionHelper:
|
||||
suites = []
|
||||
for child in self.summary_element:
|
||||
suite = Suite()
|
||||
suite.module = child.get(ReportConstant.module,
|
||||
ReportConstant.empty_name)
|
||||
suite.module_name = child.get(ReportConstant.module_name,
|
||||
ReportConstant.empty_name)
|
||||
suite.name = child.get(ReportConstant.name, "")
|
||||
suite.message = child.get(ReportConstant.message, "")
|
||||
suite.result.total = int(child.get(ReportConstant.tests)) if \
|
||||
@ -526,34 +577,39 @@ class VisionHelper:
|
||||
value = self._get_hidden_style_value(getattr(
|
||||
exec_info, key, "None"))
|
||||
file_context = self._render_key(prefix, key, value, file_context)
|
||||
file_context = self._render_product_params(exec_info, file_context,
|
||||
prefix)
|
||||
file_context = self._render_product_info(exec_info, file_context,
|
||||
prefix)
|
||||
return file_context
|
||||
|
||||
def _render_product_params(self, exec_info, file_context, prefix):
|
||||
"""construct product params context and render it to file context
|
||||
product params sample:
|
||||
def _render_product_info(self, exec_info, file_context, prefix):
|
||||
"""construct product info context and render it to file context
|
||||
|
||||
rendered product info sample:
|
||||
<tr>
|
||||
<td class="normal first">key:</td>
|
||||
<td class="normal second">value</td>
|
||||
<td class="normal third">key:</td>
|
||||
<td class="normal fourth">value</td>
|
||||
</tr>
|
||||
|
||||
Args:
|
||||
exec_info: dict that used to update file_content
|
||||
file_context: exist html content
|
||||
prefix: target replace prefix key
|
||||
|
||||
Returns:
|
||||
updated file context that includes rendered product info
|
||||
"""
|
||||
row_start = True
|
||||
try:
|
||||
keys = list(exec_info.product_params.keys())
|
||||
keys = list(exec_info.product_info.keys())
|
||||
except AttributeError:
|
||||
LOG.error("product params error %s", exec_info.product_params)
|
||||
LOG.error("product info error %s", exec_info.product_info)
|
||||
keys = []
|
||||
if ReportConstant.log_path_title not in keys:
|
||||
keys.append(ReportConstant.log_path_title)
|
||||
exec_info.product_params[ReportConstant.log_path_title] = \
|
||||
exec_info.log_path
|
||||
|
||||
render_value = ""
|
||||
for key in keys:
|
||||
value = exec_info.product_params[key]
|
||||
value = exec_info.product_info[key]
|
||||
if row_start:
|
||||
render_value = "%s<tr>\n" % render_value
|
||||
render_value = "{}{}".format(
|
||||
@ -563,9 +619,8 @@ class VisionHelper:
|
||||
row_start = not row_start
|
||||
if not row_start:
|
||||
render_value = "%s</tr>\n" % render_value
|
||||
file_context = self._render_key(prefix, ReportConstant.product_params,
|
||||
file_context = self._render_key(prefix, ReportConstant.product_info_,
|
||||
render_value, file_context)
|
||||
exec_info.product_params.pop(ReportConstant.log_path_title)
|
||||
return file_context
|
||||
|
||||
def _get_exec_info_td(self, key, value, row_start):
|
||||
@ -646,7 +701,7 @@ class VisionHelper:
|
||||
<th class="normal operate">Operate</th>
|
||||
</tr>
|
||||
<tr [class="background-color"]>
|
||||
<td class="normal module">base</td>
|
||||
<td class="normal module">{suite.module_name}</td>
|
||||
<td class="normal test-suite">{suite.name}</td>
|
||||
<td class="normal total">{suite.result.total}</td>
|
||||
<td class="normal passed">{suite.result.passed}</td>
|
||||
@ -735,14 +790,14 @@ class VisionHelper:
|
||||
<tr>
|
||||
<th class="normal module">Module</th>
|
||||
<th class="normal test-suite">Testsuite</th>
|
||||
<th class="normal test">Test</th>
|
||||
<th class="normal test">Testcase</th>
|
||||
<th class="normal time">Time</th>
|
||||
<th class="normal status"><div class="circle-normal
|
||||
circle-white"></div></th>
|
||||
<th class="normal result">Result</th>
|
||||
</tr>
|
||||
<tr [class="background-color"]>
|
||||
<td class="normal module">{case.module}</td>
|
||||
<td class="normal module">{case.module_name}</td>
|
||||
<td class="normal test-suite">{case.classname}</td>
|
||||
<td class="normal test">{case.name}</td>
|
||||
<td class="normal time">{case.time}</td>
|
||||
@ -794,8 +849,8 @@ class VisionHelper:
|
||||
" <td class='normal status'>"
|
||||
"<div class='circle-normal circle-%s'></div></td>\n"
|
||||
" <td class='normal result'>%s</td>\n"
|
||||
"</tr>\n" % (case.module, case.classname, case.name, case.time,
|
||||
result, rendered_result))
|
||||
"</tr>\n" % (case.module_name, case.classname, case.name,
|
||||
case.time, result, rendered_result))
|
||||
return case_td_context
|
||||
|
||||
@classmethod
|
||||
@ -811,7 +866,7 @@ class VisionHelper:
|
||||
"<tr>\n" \
|
||||
" <th class='normal module'>Module</th>\n" \
|
||||
" <th class='normal test-suite'>Testsuite</th>\n" \
|
||||
" <th class='normal test'>Test</th>\n" \
|
||||
" <th class='normal test'>Testcase</th>\n" \
|
||||
" <th class='normal time'>Time</th>\n" \
|
||||
" <th class='normal status'><div class='circle-normal " \
|
||||
"circle-white'></div></th>\n" \
|
||||
@ -841,10 +896,10 @@ class VisionHelper:
|
||||
</tr>
|
||||
<tr [class="background-color"]>
|
||||
<td class="normal test" id="{suite.name}">
|
||||
{suite.module}#{suite.name}</td>
|
||||
{suite.module_name}#{suite.name}</td>
|
||||
or
|
||||
<td class="normal test" id="{suite.name}.{case.name}">
|
||||
{case.module}#{case.classname}#{case.name}</td>
|
||||
{case.module_name}#{case.classname}#{case.name}</td>
|
||||
<td class="normal status"><div class="circle-normal
|
||||
circle-{case.result/status}"></div></td>
|
||||
<td class="normal result">{case.result/status}</td>
|
||||
@ -896,17 +951,18 @@ class VisionHelper:
|
||||
failure_case_td_context = "<tr>\n" if index % 2 == 0 else \
|
||||
"<tr class='background-color'>\n"
|
||||
if result == ReportConstant.unavailable:
|
||||
test_context = "%s#%s" % (case.module, case.name)
|
||||
test_context = "%s#%s" % (case.module_name, case.name)
|
||||
href_id = suite_name
|
||||
else:
|
||||
test_context = \
|
||||
"%s#%s#%s" % (case.module, case.classname, case.name)
|
||||
"%s#%s#%s" % (case.module_name, case.classname, case.name)
|
||||
href_id = "%s.%s" % (suite_name, case.name)
|
||||
details_context = case.message
|
||||
if details_context:
|
||||
details_context = str(details_context).replace("<", "<"). \
|
||||
replace(">", ">").replace("\\r\\n", "<br/>"). \
|
||||
replace("\\n", "<br/>").replace("\n", "<br/>")
|
||||
replace("\\n", "<br/>").replace("\n", "<br/>"). \
|
||||
replace(" ", " ")
|
||||
failure_case_td_context = "{}{}".format(
|
||||
failure_case_td_context,
|
||||
" <td class='normal test' id='%s'>%s</td>\n"
|
||||
@ -943,11 +999,21 @@ class VisionHelper:
|
||||
|
||||
@staticmethod
|
||||
def generate_report(summary_vision_path, report_context):
|
||||
vision_file = open(summary_vision_path, "wb")
|
||||
if platform.system() == "Windows":
|
||||
flags = os.O_WRONLY | os.O_CREAT | os.O_APPEND | os.O_BINARY
|
||||
else:
|
||||
flags = os.O_WRONLY | os.O_CREAT | os.O_APPEND
|
||||
vision_file_open = os.open(summary_vision_path, flags, 0o755)
|
||||
vision_file = os.fdopen(vision_file_open, "wb")
|
||||
if check_pub_key_exist():
|
||||
cipher_text = do_rsa_encrypt(report_context)
|
||||
try:
|
||||
cipher_text = do_rsa_encrypt(report_context)
|
||||
except ParamError as error:
|
||||
LOG.error(error, error_no=error.error_no)
|
||||
cipher_text = b""
|
||||
vision_file.write(cipher_text)
|
||||
else:
|
||||
vision_file.write(bytes(report_context, "utf-8"))
|
||||
vision_file.write(bytes(report_context, "utf-8", "ignore"))
|
||||
vision_file.flush()
|
||||
vision_file.close()
|
||||
LOG.info("generate vision report: %s", summary_vision_path)
|
||||
|
@ -2,7 +2,7 @@
|
||||
# coding=utf-8
|
||||
|
||||
#
|
||||
# Copyright (c) 2020 Huawei Device Co., Ltd.
|
||||
# Copyright (c) 2020-2021 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
|
||||
@ -17,54 +17,69 @@
|
||||
#
|
||||
|
||||
import os
|
||||
import platform
|
||||
import shutil
|
||||
import time
|
||||
import zipfile
|
||||
from ast import literal_eval
|
||||
|
||||
from _core.interface import IReporter
|
||||
from _core.plugin import Plugin
|
||||
from _core.constants import ModeType
|
||||
from _core.constants import TestType
|
||||
from _core.logger import platform_logger
|
||||
from _core.exception import ParamError
|
||||
from _core.utils import get_filename_extension
|
||||
from _core.report.encrypt import check_pub_key_exist
|
||||
from _core.report.encrypt import do_rsa_encrypt
|
||||
from _core.report.encrypt import get_file_summary
|
||||
from _core.report.reporter_helper import DataHelper
|
||||
from _core.report.reporter_helper import ExecInfo
|
||||
from _core.report.reporter_helper import VisionHelper
|
||||
from _core.report.reporter_helper import ReportConstant
|
||||
|
||||
LOG = platform_logger("ResultReporter")
|
||||
|
||||
|
||||
class ResultReporter:
|
||||
@Plugin(type=Plugin.REPORTER, id=TestType.all)
|
||||
class ResultReporter(IReporter):
|
||||
summary_report_result = []
|
||||
|
||||
def __init__(self, report_path, task_info):
|
||||
self.report_path = report_path
|
||||
self.task_info = task_info
|
||||
self.data_helper = DataHelper()
|
||||
self.vision_helper = VisionHelper()
|
||||
self.summary_data_path = os.path.join(
|
||||
self.report_path, ReportConstant.summary_data_report)
|
||||
self.exec_info = task_info
|
||||
def __init__(self):
|
||||
self.report_path = None
|
||||
self.task_info = None
|
||||
self.summary_data_path = None
|
||||
self.summary_data_str = ""
|
||||
self.exec_info = None
|
||||
self.parsed_data = None
|
||||
self.data_helper = None
|
||||
self.vision_helper = None
|
||||
|
||||
def generate_reports(self):
|
||||
def __generate_reports__(self, report_path, **kwargs):
|
||||
LOG.info("")
|
||||
LOG.info("**************************************************")
|
||||
LOG.info("************** Start generate reports ************")
|
||||
LOG.info("**************************************************")
|
||||
LOG.info("")
|
||||
|
||||
# generate data report
|
||||
self._generate_data_report()
|
||||
if self._check_params(report_path, **kwargs):
|
||||
# generate data report
|
||||
self._generate_data_report()
|
||||
|
||||
# generate vision reports
|
||||
self._generate_vision_reports()
|
||||
# generate vision reports
|
||||
self._generate_vision_reports()
|
||||
|
||||
# generate summary ini
|
||||
self._generate_summary()
|
||||
# generate task info record
|
||||
self._generate_task_info_record()
|
||||
|
||||
# copy reports to reports/latest folder
|
||||
self._copy_report()
|
||||
# generate summary ini
|
||||
self._generate_summary()
|
||||
|
||||
# compress report folder
|
||||
self._compress_report_folder()
|
||||
# copy reports to reports/latest folder
|
||||
self._copy_report()
|
||||
|
||||
# compress report folder
|
||||
self._compress_report_folder()
|
||||
|
||||
LOG.info("")
|
||||
LOG.info("**************************************************")
|
||||
@ -72,6 +87,27 @@ class ResultReporter:
|
||||
LOG.info("**************************************************")
|
||||
LOG.info("")
|
||||
|
||||
def _check_params(self, report_path, **kwargs):
|
||||
task_info = kwargs.get("task_info", "")
|
||||
if not report_path:
|
||||
LOG.error("report path is wrong", error_no="00440",
|
||||
ReportPath=report_path)
|
||||
return False
|
||||
if not task_info or not isinstance(task_info, ExecInfo):
|
||||
LOG.error("task info is wrong", error_no="00441",
|
||||
TaskInfo=task_info)
|
||||
return False
|
||||
|
||||
os.makedirs(report_path, exist_ok=True)
|
||||
self.report_path = report_path
|
||||
self.task_info = task_info
|
||||
self.summary_data_path = os.path.join(
|
||||
self.report_path, ReportConstant.summary_data_report)
|
||||
self.exec_info = task_info
|
||||
self.data_helper = DataHelper()
|
||||
self.vision_helper = VisionHelper()
|
||||
return True
|
||||
|
||||
def _generate_data_report(self):
|
||||
# initial element
|
||||
test_suites_element = self.data_helper.initial_suites_element()
|
||||
@ -82,32 +118,24 @@ class ResultReporter:
|
||||
return
|
||||
|
||||
# generate report
|
||||
if not self._check_mode(ReportConstant.decc_mode):
|
||||
if not self._check_mode(ModeType.decc):
|
||||
self.data_helper.generate_report(test_suites_element,
|
||||
self.summary_data_path)
|
||||
|
||||
# set SuiteReporter.suite_report_result
|
||||
if not check_pub_key_exist() and not self._check_mode(
|
||||
ReportConstant.decc_mode):
|
||||
ModeType.decc):
|
||||
return
|
||||
from xdevice import SuiteReporter
|
||||
SuiteReporter.suite_report_result = [(
|
||||
self.summary_data_path, DataHelper.to_string(
|
||||
test_suites_element))]
|
||||
self.set_summary_report_result(
|
||||
self.summary_data_path, DataHelper.to_string(test_suites_element))
|
||||
|
||||
if self._check_mode(ReportConstant.decc_mode):
|
||||
if self._check_mode(ModeType.decc):
|
||||
try:
|
||||
from devicetest.agent.auth_server import Handler
|
||||
from agent.decc import Handler
|
||||
from xdevice import Scheduler
|
||||
LOG.info("upload task summary result to decc")
|
||||
Handler.upload_task_summary_results(
|
||||
SuiteReporter.suite_report_result[0][1])
|
||||
tmp_element = self.data_helper.initial_suites_element()
|
||||
for child in test_suites_element:
|
||||
result, _ = Scheduler.get_script_result(child)
|
||||
if result == "Passed":
|
||||
tmp_element.append(child)
|
||||
SuiteReporter.suite_report_result = [(
|
||||
self.summary_data_path, DataHelper.to_string(tmp_element))]
|
||||
self.get_result_of_summary_report())
|
||||
except ModuleNotFoundError as error:
|
||||
LOG.error("module not found %s", error.args)
|
||||
|
||||
@ -128,12 +156,13 @@ class ResultReporter:
|
||||
total = int(root.get(ReportConstant.tests, 0))
|
||||
modules[module_name] = modules.get(module_name, 0) + total
|
||||
|
||||
self._append_product_params(test_suites_attributes, root)
|
||||
self._append_product_info(test_suites_attributes, root)
|
||||
for child in root:
|
||||
child.tail = self.data_helper.LINE_BREAK_INDENT
|
||||
if not child.get(ReportConstant.module) or child.get(
|
||||
ReportConstant.module) == ReportConstant.empty_name:
|
||||
child.set(ReportConstant.module, module_name)
|
||||
if not child.get(ReportConstant.module_name) or child.get(
|
||||
ReportConstant.module_name) == \
|
||||
ReportConstant.empty_name:
|
||||
child.set(ReportConstant.module_name, module_name)
|
||||
self._check_tests_and_unavailable(child)
|
||||
test_suite_elements.append(child)
|
||||
for update_attribute in need_update_attributes:
|
||||
@ -156,7 +185,7 @@ class ResultReporter:
|
||||
if modules_zero:
|
||||
LOG.info("the total tests of %s module is 0", ",".join(
|
||||
modules_zero))
|
||||
test_suites_attributes[ReportConstant.modules_done] = \
|
||||
test_suites_attributes[ReportConstant.run_modules] = \
|
||||
len(modules) - len(modules_zero)
|
||||
test_suites_attributes[ReportConstant.modules] = len(modules)
|
||||
self.data_helper.set_element_attributes(test_suites_element,
|
||||
@ -175,37 +204,36 @@ class ResultReporter:
|
||||
ReportConstant.name), total, unavailable)
|
||||
|
||||
@classmethod
|
||||
def _append_product_params(cls, test_suites_attributes, root):
|
||||
product_params = root.get(ReportConstant.product_params, "")
|
||||
if not product_params:
|
||||
def _append_product_info(cls, test_suites_attributes, root):
|
||||
product_info = root.get(ReportConstant.product_info, "")
|
||||
if not product_info:
|
||||
return
|
||||
try:
|
||||
product_params = literal_eval(str(product_params))
|
||||
product_info = literal_eval(str(product_info))
|
||||
except SyntaxError as error:
|
||||
LOG.error("%s %s", root.get(ReportConstant.name, ""), error.args)
|
||||
return
|
||||
product_info = {}
|
||||
|
||||
if not test_suites_attributes[ReportConstant.product_params]:
|
||||
test_suites_attributes[ReportConstant.product_params] = \
|
||||
product_params
|
||||
if not test_suites_attributes[ReportConstant.product_info]:
|
||||
test_suites_attributes[ReportConstant.product_info] = \
|
||||
product_info
|
||||
return
|
||||
for key, value in product_params.items():
|
||||
for key, value in product_info.items():
|
||||
exist_value = test_suites_attributes[
|
||||
ReportConstant.product_params].get(key, "")
|
||||
ReportConstant.product_info].get(key, "")
|
||||
|
||||
if not exist_value:
|
||||
test_suites_attributes[
|
||||
ReportConstant.product_params][key] = value
|
||||
ReportConstant.product_info][key] = value
|
||||
continue
|
||||
if value in exist_value:
|
||||
continue
|
||||
test_suites_attributes[ReportConstant.product_params][key] = \
|
||||
test_suites_attributes[ReportConstant.product_info][key] = \
|
||||
"%s,%s" % (exist_value, value)
|
||||
|
||||
@classmethod
|
||||
def _get_module_name(cls, data_report, root):
|
||||
# get module name from data report
|
||||
from _core.utils import get_filename_extension
|
||||
module_name = get_filename_extension(data_report)[0]
|
||||
if "report" in module_name or "summary" in module_name or \
|
||||
"<" in data_report or ">" in data_report:
|
||||
@ -225,8 +253,8 @@ class ResultReporter:
|
||||
ReportConstant.errors: 0, ReportConstant.disabled: 0,
|
||||
ReportConstant.failures: 0, ReportConstant.tests: 0,
|
||||
ReportConstant.ignored: 0, ReportConstant.unavailable: 0,
|
||||
ReportConstant.product_params: self.task_info.product_params,
|
||||
ReportConstant.modules: 0, ReportConstant.modules_done: 0}
|
||||
ReportConstant.product_info: self.task_info.product_info,
|
||||
ReportConstant.modules: 0, ReportConstant.run_modules: 0}
|
||||
need_update_attributes = [ReportConstant.tests, ReportConstant.ignored,
|
||||
ReportConstant.failures,
|
||||
ReportConstant.disabled,
|
||||
@ -235,31 +263,43 @@ class ResultReporter:
|
||||
return test_suites_attributes, need_update_attributes
|
||||
|
||||
def _generate_vision_reports(self):
|
||||
if not self.summary_data_report_exist:
|
||||
if not self._check_mode(ModeType.decc) and not \
|
||||
self.summary_data_report_exist:
|
||||
LOG.error("summary data report not exists")
|
||||
return
|
||||
|
||||
if check_pub_key_exist():
|
||||
from xdevice import SuiteReporter
|
||||
if not SuiteReporter.get_report_result():
|
||||
if check_pub_key_exist() or self._check_mode(ModeType.decc):
|
||||
if not self.summary_report_result_exists():
|
||||
LOG.error("summary data report not exists")
|
||||
return
|
||||
self.summary_data_path = SuiteReporter.get_report_result()[0][1]
|
||||
SuiteReporter.clear_report_result()
|
||||
self.summary_data_str = \
|
||||
self.get_result_of_summary_report()
|
||||
if check_pub_key_exist():
|
||||
from xdevice import SuiteReporter
|
||||
SuiteReporter.clear_report_result()
|
||||
|
||||
# parse data
|
||||
summary_element_tree = self.data_helper.parse_data_report(
|
||||
self.summary_data_path)
|
||||
if self.summary_data_str:
|
||||
# only in decc mode and pub key, self.summary_data_str is not empty
|
||||
summary_element_tree = self.data_helper.parse_data_report(
|
||||
self.summary_data_str)
|
||||
else:
|
||||
summary_element_tree = self.data_helper.parse_data_report(
|
||||
self.summary_data_path)
|
||||
parsed_data = self.vision_helper.parse_element_data(
|
||||
summary_element_tree, self.report_path, self.task_info)
|
||||
self.parsed_data = parsed_data
|
||||
self.exec_info, summary, _ = parsed_data
|
||||
if not check_pub_key_exist():
|
||||
LOG.info("Summary result: modules: %s, modules done: %s, total: "
|
||||
"%s, passed: %s, failed: %s, blocked: %s, ignored: %s, "
|
||||
"unavailable: %s", summary.modules, summary.modules_done,
|
||||
summary.result.total, summary.result.passed,
|
||||
summary.result.failed, summary.result.blocked,
|
||||
summary.result.ignored, summary.result.unavailable)
|
||||
|
||||
if self._check_mode(ModeType.decc):
|
||||
return
|
||||
|
||||
LOG.info("Summary result: modules: %s, run modules: %s, total: "
|
||||
"%s, passed: %s, failed: %s, blocked: %s, ignored: %s, "
|
||||
"unavailable: %s", summary.modules, summary.run_modules,
|
||||
summary.result.total, summary.result.passed,
|
||||
summary.result.failed, summary.result.blocked,
|
||||
summary.result.ignored, summary.result.unavailable)
|
||||
LOG.info("Log path: %s", self.exec_info.log_path)
|
||||
|
||||
# generate summary vision report
|
||||
@ -298,18 +338,23 @@ class ResultReporter:
|
||||
|
||||
@property
|
||||
def summary_data_report_exist(self):
|
||||
if self._check_mode(ReportConstant.decc_mode):
|
||||
return False
|
||||
return os.path.exists(self.summary_data_path) or (
|
||||
"<" in self.summary_data_path and ">" in self.summary_data_path)
|
||||
return "<" in self.summary_data_str or \
|
||||
os.path.exists(self.summary_data_path)
|
||||
|
||||
@property
|
||||
def data_reports(self):
|
||||
if check_pub_key_exist() or self._check_mode(ReportConstant.decc_mode):
|
||||
if check_pub_key_exist() or self._check_mode(ModeType.decc):
|
||||
from xdevice import SuiteReporter
|
||||
suite_reports = SuiteReporter.get_report_result()
|
||||
data_reports = [(suite_report[1], ReportConstant.empty_name) for
|
||||
suite_report in suite_reports]
|
||||
if self._check_mode(ModeType.decc):
|
||||
LOG.debug("handle history result, data_reports length:{}".
|
||||
format(len(suite_reports)))
|
||||
SuiteReporter.clear_history_result()
|
||||
SuiteReporter.append_history_result(suite_reports)
|
||||
data_reports = []
|
||||
for report_path, report_result in suite_reports:
|
||||
module_name = get_filename_extension(report_path)[0]
|
||||
data_reports.append((report_result, module_name))
|
||||
SuiteReporter.clear_report_result()
|
||||
return data_reports
|
||||
|
||||
@ -344,7 +389,8 @@ class ResultReporter:
|
||||
return module_name
|
||||
|
||||
def _generate_summary(self):
|
||||
if not self.summary_data_report_exist:
|
||||
if not self.summary_data_report_exist or \
|
||||
self._check_mode(ModeType.decc):
|
||||
return
|
||||
summary_ini_content = \
|
||||
"[default]\n" \
|
||||
@ -357,27 +403,41 @@ class ResultReporter:
|
||||
self.exec_info.platform, self.exec_info.test_type,
|
||||
self.exec_info.device_name, self.exec_info.host_info,
|
||||
self.exec_info.test_time, self.exec_info.execute_time)
|
||||
if self.exec_info.product_params:
|
||||
for key, value in self.exec_info.product_params.items():
|
||||
if self.exec_info.product_info:
|
||||
for key, value in self.exec_info.product_info.items():
|
||||
summary_ini_content = "{}{}".format(
|
||||
summary_ini_content, "%s=%s\n" % (key, value))
|
||||
summary_ini_content = "{}{}".format(
|
||||
summary_ini_content, "Log Path=%s\n" % self.exec_info.log_path)
|
||||
|
||||
if not self._check_mode(ModeType.factory):
|
||||
summary_ini_content = "{}{}".format(
|
||||
summary_ini_content, "Log Path=%s\n" % self.exec_info.log_path)
|
||||
|
||||
# write summary_ini_content
|
||||
summary_filepath = os.path.join(self.report_path,
|
||||
ReportConstant.summary_ini)
|
||||
with open(summary_filepath, 'wb') as file_handler:
|
||||
|
||||
if platform.system() == "Windows":
|
||||
flags = os.O_WRONLY | os.O_CREAT | os.O_APPEND | os.O_BINARY
|
||||
else:
|
||||
flags = os.O_WRONLY | os.O_CREAT | os.O_APPEND
|
||||
summary_filepath_open = os.open(summary_filepath, flags, 0o755)
|
||||
|
||||
with os.fdopen(summary_filepath_open, "wb") as file_handler:
|
||||
if check_pub_key_exist():
|
||||
cipher_text = do_rsa_encrypt(summary_ini_content)
|
||||
try:
|
||||
cipher_text = do_rsa_encrypt(summary_ini_content)
|
||||
except ParamError as error:
|
||||
LOG.error(error, error_no=error.error_no)
|
||||
cipher_text = b""
|
||||
file_handler.write(cipher_text)
|
||||
else:
|
||||
file_handler.write(bytes(summary_ini_content, 'utf-8'))
|
||||
file_handler.flush()
|
||||
LOG.info("generate summary ini: %s", summary_filepath)
|
||||
|
||||
def _copy_report(self):
|
||||
from xdevice import Scheduler
|
||||
if Scheduler.upload_address:
|
||||
if Scheduler.upload_address or self._check_mode(ModeType.decc):
|
||||
return
|
||||
|
||||
from xdevice import Variables
|
||||
@ -398,11 +458,12 @@ class ResultReporter:
|
||||
return
|
||||
|
||||
def _compress_report_folder(self):
|
||||
if self._check_mode(ModeType.decc):
|
||||
return
|
||||
|
||||
if not os.path.isdir(self.report_path):
|
||||
LOG.error("'%s' is not folder!" % self.report_path)
|
||||
return
|
||||
if self._check_mode(ReportConstant.decc_mode):
|
||||
return
|
||||
|
||||
# get file path list
|
||||
file_path_list = []
|
||||
@ -431,12 +492,149 @@ class ResultReporter:
|
||||
# generate hex digest, then save it to summary_report.hash
|
||||
hash_file = os.path.abspath(os.path.join(
|
||||
self.report_path, ReportConstant.summary_report_hash))
|
||||
with open(hash_file, "w") as hash_file_handler:
|
||||
hash_file_open = os.open(hash_file,
|
||||
os.O_WRONLY | os.O_CREAT | os.O_APPEND, 0o755)
|
||||
with os.fdopen(hash_file_open, "w") as hash_file_handler:
|
||||
hash_file_handler.write(get_file_summary(zipped_file))
|
||||
LOG.info("generate hash file: %s", hash_file)
|
||||
hash_file_handler.flush()
|
||||
return zipped_file
|
||||
|
||||
@classmethod
|
||||
def _check_mode(cls, mode):
|
||||
from xdevice import Scheduler
|
||||
return Scheduler.mode == mode
|
||||
|
||||
def _generate_task_info_record(self):
|
||||
# under encryption status, don't handle anything directly
|
||||
if check_pub_key_exist() and not self._check_mode(ModeType.decc):
|
||||
return
|
||||
|
||||
# get info from command_queue
|
||||
from xdevice import Scheduler
|
||||
if not Scheduler.command_queue:
|
||||
return
|
||||
_, command, report_path = Scheduler.command_queue[-1]
|
||||
|
||||
# handle parsed data
|
||||
record = self._parse_record_from_data(command, report_path)
|
||||
|
||||
def encode(content):
|
||||
# inner function to encode
|
||||
return ' '.join([bin(ord(c)).replace('0b', '') for c in content])
|
||||
|
||||
# write into file
|
||||
import json
|
||||
record_file = os.path.join(self.report_path,
|
||||
ReportConstant.task_info_record)
|
||||
_record_json = json.dumps(record, indent=2)
|
||||
|
||||
with open(file=record_file, mode="wb") as file:
|
||||
if Scheduler.mode == ModeType.decc:
|
||||
# under decc, write in encoded text
|
||||
file.write(bytes(encode(_record_json), encoding="utf-8"))
|
||||
else:
|
||||
# others, write in plain text
|
||||
file.write(bytes(_record_json, encoding="utf-8"))
|
||||
|
||||
LOG.info("generate record file: %s", record_file)
|
||||
|
||||
def _parse_record_from_data(self, command, report_path):
|
||||
record = dict()
|
||||
if self.parsed_data:
|
||||
_, _, suites = self.parsed_data
|
||||
unsuccessful = dict()
|
||||
module_set = set()
|
||||
for suite in suites:
|
||||
module_set.add(suite.module_name)
|
||||
|
||||
failed = unsuccessful.get(suite.module_name, [])
|
||||
# because suite not contains case's some attribute,
|
||||
# for example, 'module', 'classname', 'name' . so
|
||||
# if unavailable, only add module's name into list.
|
||||
if int(suite.result.unavailable) > 0:
|
||||
failed.append(suite.module_name)
|
||||
else:
|
||||
# others, get key attributes join string
|
||||
for case in suite.get_cases():
|
||||
if not case.is_passed():
|
||||
failed.append(
|
||||
"{}#{}".format(case.classname, case.name))
|
||||
unsuccessful.update({suite.module_name: failed})
|
||||
data_reports = self._get_data_reports(module_set)
|
||||
record = {"command": command,
|
||||
"session_id": os.path.split(report_path)[-1],
|
||||
"report_path": report_path,
|
||||
"unsuccessful_params": unsuccessful,
|
||||
"data_reports": data_reports
|
||||
}
|
||||
return record
|
||||
|
||||
def _get_data_reports(self, module_set):
|
||||
data_reports = dict()
|
||||
if self._check_mode(ModeType.decc):
|
||||
from xdevice import SuiteReporter
|
||||
for module_name, report_path, report_result in \
|
||||
SuiteReporter.get_history_result_list():
|
||||
if module_name in module_set:
|
||||
data_reports.update({module_name: report_path})
|
||||
else:
|
||||
for report_path, module_name in self.data_reports:
|
||||
if module_name == ReportConstant.empty_name:
|
||||
root = self.data_helper.parse_data_report(report_path)
|
||||
module_name = self._get_module_name(report_path, root)
|
||||
if module_name in module_set:
|
||||
data_reports.update({module_name: report_path})
|
||||
|
||||
return data_reports
|
||||
|
||||
@classmethod
|
||||
def get_task_info_params(cls, history_path):
|
||||
# under encryption status, don't handle anything directly
|
||||
if check_pub_key_exist() and not cls._check_mode(ModeType.decc):
|
||||
return ()
|
||||
|
||||
def decode(content):
|
||||
return ''.join([chr(i) for i in [int(b, 2) for b in
|
||||
content.split(' ')]])
|
||||
|
||||
record_path = os.path.join(history_path,
|
||||
ReportConstant.task_info_record)
|
||||
if not os.path.exists(record_path):
|
||||
LOG.error("%s not exists!", ReportConstant.task_info_record)
|
||||
return ()
|
||||
|
||||
import json
|
||||
from xdevice import Scheduler
|
||||
with open(record_path, mode="rb") as file:
|
||||
if Scheduler.mode == ModeType.decc:
|
||||
# under decc, read from encoded text
|
||||
result = json.loads(decode(file.read().decode("utf-8")))
|
||||
else:
|
||||
# others, read from plain text
|
||||
result = json.loads(file.read())
|
||||
if not len(result.keys()) == 5:
|
||||
LOG.error("%s error!", ReportConstant.task_info_record)
|
||||
return ()
|
||||
|
||||
return result["session_id"], result["command"], result["report_path"],\
|
||||
result["unsuccessful_params"], result["data_reports"]
|
||||
|
||||
@classmethod
|
||||
def set_summary_report_result(cls, summary_data_path, result_xml):
|
||||
cls.summary_report_result.clear()
|
||||
cls.summary_report_result.append((summary_data_path, result_xml))
|
||||
|
||||
@classmethod
|
||||
def get_result_of_summary_report(cls):
|
||||
if cls.summary_report_result:
|
||||
return cls.summary_report_result[0][1]
|
||||
|
||||
@classmethod
|
||||
def summary_report_result_exists(cls):
|
||||
return True if cls.summary_report_result else False
|
||||
|
||||
@classmethod
|
||||
def get_path_of_summary_report(cls):
|
||||
if cls.summary_report_result:
|
||||
return cls.summary_report_result[0][0]
|
||||
|
@ -2,7 +2,7 @@
|
||||
# coding=utf-8
|
||||
|
||||
#
|
||||
# Copyright (c) 2020 Huawei Device Co., Ltd.
|
||||
# Copyright (c) 2020-2021 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
|
||||
@ -21,6 +21,7 @@ import time
|
||||
from enum import Enum
|
||||
from threading import RLock
|
||||
|
||||
from _core.constants import ModeType
|
||||
from _core.logger import platform_logger
|
||||
from _core.report.encrypt import check_pub_key_exist
|
||||
from _core.report.reporter_helper import DataHelper
|
||||
@ -32,6 +33,7 @@ SUITE_REPORTER_LOCK = RLock()
|
||||
|
||||
class ResultCode(Enum):
|
||||
UNKNOWN = -1010
|
||||
BLOCKED = -1
|
||||
PASSED = 0
|
||||
FAILED = 1
|
||||
SKIPPED = 2
|
||||
@ -41,6 +43,7 @@ class SuiteReporter:
|
||||
suite_list = []
|
||||
suite_report_result = []
|
||||
failed_case_list = []
|
||||
history_report_result = []
|
||||
|
||||
def __init__(self, results, report_name, report_path=None, **kwargs):
|
||||
"""
|
||||
@ -55,11 +58,11 @@ class SuiteReporter:
|
||||
self.report_name = report_name
|
||||
self.report_path = report_path
|
||||
self.suite_data_path = os.path.join(
|
||||
self.report_path, report_name +
|
||||
self.data_helper.DATA_REPORT_SUFFIX)
|
||||
self.report_path, "%s%s" % (
|
||||
report_name, self.data_helper.DATA_REPORT_SUFFIX))
|
||||
self.args = kwargs
|
||||
from xdevice import Scheduler
|
||||
if not check_pub_key_exist() and Scheduler.mode != "decc":
|
||||
if not check_pub_key_exist() and Scheduler.mode != ModeType.decc:
|
||||
SuiteReporter.suite_report_result.clear()
|
||||
|
||||
def create_empty_report(self):
|
||||
@ -83,6 +86,10 @@ class SuiteReporter:
|
||||
"", self.data_helper.LINE_BREAK
|
||||
test_suite_attributes[ReportConstant.unavailable] = 1
|
||||
test_suite_attributes[ReportConstant.message] = suite_result.stacktrace
|
||||
|
||||
from xdevice import Scheduler
|
||||
if Scheduler.mode == ModeType.decc:
|
||||
test_suite_attributes[ReportConstant.result] = ReportConstant.false
|
||||
self.data_helper.set_element_attributes(test_suite_element,
|
||||
test_suite_attributes)
|
||||
|
||||
@ -92,7 +99,7 @@ class SuiteReporter:
|
||||
# generate report
|
||||
if test_suites_element:
|
||||
from xdevice import Scheduler
|
||||
if Scheduler.mode != "decc":
|
||||
if Scheduler.mode != ModeType.decc:
|
||||
self.data_helper.generate_report(test_suites_element,
|
||||
self.suite_data_path)
|
||||
SuiteReporter.append_report_result((
|
||||
@ -155,8 +162,11 @@ class SuiteReporter:
|
||||
ReportConstant.tests: 0,
|
||||
ReportConstant.ignored: 0,
|
||||
ReportConstant.unavailable: 0,
|
||||
ReportConstant.product_params: self.args.get(
|
||||
ReportConstant.product_params, "")}
|
||||
ReportConstant.product_info: self.args.get(
|
||||
ReportConstant.product_info_, "")}
|
||||
if self.args.get(ReportConstant.module_name, ""):
|
||||
test_suites_attributes[ReportConstant.name] = self.args.get(
|
||||
ReportConstant.module_name, "")
|
||||
need_update_attributes = [ReportConstant.time, ReportConstant.errors,
|
||||
ReportConstant.tests, ReportConstant.ignored,
|
||||
ReportConstant.disabled,
|
||||
@ -192,7 +202,7 @@ class SuiteReporter:
|
||||
child = test_case_elements[-1]
|
||||
child.tail = self.data_helper.LINE_BREAK_INDENT
|
||||
else:
|
||||
LOG.error("no case executed")
|
||||
LOG.debug("no case executed")
|
||||
test_suite_element.extend(test_case_elements)
|
||||
|
||||
# set test suite attributes
|
||||
@ -237,12 +247,18 @@ class SuiteReporter:
|
||||
ReportConstant.message:
|
||||
suite_result.stacktrace
|
||||
}
|
||||
if self.args.get(ReportConstant.module_name, ""):
|
||||
test_suite_attributes[ReportConstant.module_name] = self.args.get(
|
||||
ReportConstant.module_name, "")
|
||||
return test_suite_element, test_suite_attributes
|
||||
|
||||
def _initial_test_case(self, case_result):
|
||||
test_case_element = self.data_helper.initial_case_element()
|
||||
case_stacktrace = str(case_result.stacktrace).replace(chr(2), "")\
|
||||
.replace(chr(15), "").replace(chr(16), "")
|
||||
case_stacktrace = str(case_result.stacktrace)
|
||||
for char_index in range(32):
|
||||
if char_index in [10, 13]: # chr(10): LF, chr(13): CR
|
||||
continue
|
||||
case_stacktrace = case_stacktrace.replace(chr(char_index), "")
|
||||
test_case_attributes = {ReportConstant.name: case_result.test_name,
|
||||
ReportConstant.status: "",
|
||||
ReportConstant.time: float(
|
||||
@ -257,11 +273,13 @@ class SuiteReporter:
|
||||
@classmethod
|
||||
def clear_report_result(cls):
|
||||
with SUITE_REPORTER_LOCK:
|
||||
LOG.debug("clear_report_result")
|
||||
cls.suite_report_result.clear()
|
||||
|
||||
@classmethod
|
||||
def clear_failed_case_list(cls):
|
||||
with SUITE_REPORTER_LOCK:
|
||||
LOG.debug("clear_failed_case_list")
|
||||
cls.failed_case_list.clear()
|
||||
|
||||
@classmethod
|
||||
@ -282,36 +300,81 @@ class SuiteReporter:
|
||||
@classmethod
|
||||
def _upload_case_result(cls, result_str):
|
||||
from xdevice import Scheduler
|
||||
if Scheduler.mode != ReportConstant.decc_mode:
|
||||
if Scheduler.mode != ModeType.decc:
|
||||
return
|
||||
element = DataHelper.parse_data_report(result_str)
|
||||
if len(element) == 0:
|
||||
LOG.debug("%s is error", result_str)
|
||||
return
|
||||
element = element[0]
|
||||
result, error_msg = Scheduler.get_script_result(element)
|
||||
case_name = element.get(ReportConstant.name, "")
|
||||
if result != "Passed":
|
||||
cls.failed_case_list.append(case_name)
|
||||
try:
|
||||
from devicetest.agent.auth_server import Handler
|
||||
from agent.decc import Handler
|
||||
LOG.info("upload case result to decc")
|
||||
Handler.upload_case_result(case_name, result, error_msg)
|
||||
except ModuleNotFoundError as error:
|
||||
from xdevice import Scheduler
|
||||
if Scheduler.mode == ReportConstant.decc_mode:
|
||||
if Scheduler.mode == ModeType.decc:
|
||||
LOG.error("module not found %s", error.args)
|
||||
|
||||
@classmethod
|
||||
def get_report_result(cls):
|
||||
with SUITE_REPORTER_LOCK:
|
||||
LOG.debug("get_report_result, length is {}".
|
||||
format(len(cls.suite_report_result)))
|
||||
return SuiteReporter.suite_report_result
|
||||
|
||||
@classmethod
|
||||
def set_suite_list(cls, suite_list):
|
||||
LOG.debug("set_suite_list, length is {}".format(len(suite_list)))
|
||||
cls.suite_list = suite_list
|
||||
|
||||
@classmethod
|
||||
def get_suite_list(cls):
|
||||
with SUITE_REPORTER_LOCK:
|
||||
LOG.debug("get_suite_list, length is {}".
|
||||
format(len(cls.suite_list)))
|
||||
return SuiteReporter.suite_list
|
||||
|
||||
@classmethod
|
||||
def get_failed_case_list(cls):
|
||||
with SUITE_REPORTER_LOCK:
|
||||
LOG.debug("get_failed_case_list, length is {}".
|
||||
format(len(cls.failed_case_list)))
|
||||
return SuiteReporter.failed_case_list
|
||||
|
||||
@classmethod
|
||||
def append_history_result(cls, suite_reports):
|
||||
from _core.utils import get_filename_extension
|
||||
with SUITE_REPORTER_LOCK:
|
||||
LOG.debug("append_history_result,suite_reports length is {}".
|
||||
format(len(suite_reports)))
|
||||
for report_path, report_result in suite_reports:
|
||||
module_name = get_filename_extension(report_path)[0]
|
||||
cls.history_report_result. \
|
||||
append((module_name, report_path, report_result))
|
||||
|
||||
@classmethod
|
||||
def clear_history_result(cls):
|
||||
with SUITE_REPORTER_LOCK:
|
||||
LOG.debug("clear_history_result")
|
||||
cls.history_report_result.clear()
|
||||
|
||||
@classmethod
|
||||
def get_history_result_by_module(cls, name):
|
||||
with SUITE_REPORTER_LOCK:
|
||||
LOG.debug("get_history_result_by_module,module_name:{}".
|
||||
format(name))
|
||||
for module_name, report_path, report_result in \
|
||||
cls.history_report_result:
|
||||
if name == module_name:
|
||||
return report_path, report_result
|
||||
return "", ""
|
||||
|
||||
@classmethod
|
||||
def get_history_result_list(cls):
|
||||
with SUITE_REPORTER_LOCK:
|
||||
LOG.debug("get_history_result_list,length is {}".
|
||||
format(len(cls.history_report_result)))
|
||||
return cls.history_report_result
|
||||
|
@ -1,65 +1,66 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Copyright (c) 2020 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.
|
||||
-->
|
||||
<user_config>
|
||||
<environment>
|
||||
<support_device>
|
||||
<device_lite>true</device_lite>
|
||||
</support_device>
|
||||
<device type="com" label="wifiiot">
|
||||
<serial>
|
||||
<com></com>
|
||||
<type>cmd</type>
|
||||
<baund_rate>115200</baund_rate>
|
||||
<data_bits>8</data_bits>
|
||||
<stop_bits>1</stop_bits>
|
||||
<timeout>20</timeout>
|
||||
</serial>
|
||||
<serial>
|
||||
<com></com>
|
||||
<type>deploy</type>
|
||||
<baund_rate>115200</baund_rate>
|
||||
</serial>
|
||||
</device>
|
||||
<device type="com" label="ipcamera">
|
||||
<serial>
|
||||
<com></com>
|
||||
<type>cmd</type>
|
||||
<baund_rate>115200</baund_rate>
|
||||
<data_bits>8</data_bits>
|
||||
<stop_bits>1</stop_bits>
|
||||
<timeout>1</timeout>
|
||||
</serial>
|
||||
</device>
|
||||
<device type="com" label="ipcamera">
|
||||
<ip></ip>
|
||||
<port></port>
|
||||
</device>
|
||||
</environment>
|
||||
<testcases>
|
||||
<dir></dir>
|
||||
<server label="NfsServer">
|
||||
<ip></ip>
|
||||
<port></port>
|
||||
<dir></dir>
|
||||
<username></username>
|
||||
<password></password>
|
||||
<remote></remote>
|
||||
</server>
|
||||
</testcases>
|
||||
<resource>
|
||||
<dir></dir>
|
||||
</resource>
|
||||
</user_config>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (c) 2020 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.
|
||||
-->
|
||||
<user_config>
|
||||
<environment>
|
||||
<device type="com"
|
||||
label="wifiiot">
|
||||
<serial>
|
||||
<com></com>
|
||||
<type>cmd</type>
|
||||
<baud_rate>115200</baud_rate>
|
||||
<data_bits>8</data_bits>
|
||||
<stop_bits>1</stop_bits>
|
||||
<timeout>20</timeout>
|
||||
</serial>
|
||||
<serial>
|
||||
<com></com>
|
||||
<type>deploy</type>
|
||||
<baud_rate>115200</baud_rate>
|
||||
</serial>
|
||||
</device>
|
||||
<device type="com"
|
||||
label="ipcamera">
|
||||
<serial>
|
||||
<com></com>
|
||||
<type>cmd</type>
|
||||
<baud_rate>115200</baud_rate>
|
||||
<data_bits>8</data_bits>
|
||||
<stop_bits>1</stop_bits>
|
||||
<timeout>1</timeout>
|
||||
</serial>
|
||||
</device>
|
||||
<device type="com"
|
||||
label="ipcamera">
|
||||
<ip></ip>
|
||||
<port></port>
|
||||
</device>
|
||||
</environment>
|
||||
<testcases>
|
||||
<dir></dir>
|
||||
<server label="NfsServer">
|
||||
<ip></ip>
|
||||
<port></port>
|
||||
<dir></dir>
|
||||
<username></username>
|
||||
<password></password>
|
||||
<remote></remote>
|
||||
</server>
|
||||
</testcases>
|
||||
<resource>
|
||||
<dir></dir>
|
||||
</resource>
|
||||
<loglevel>INFO</loglevel>
|
||||
</user_config>
|
||||
|
22
src/xdevice/_core/resource/template/report.html
Executable file → Normal file
22
src/xdevice/_core/resource/template/report.html
Executable file → Normal file
@ -149,7 +149,7 @@
|
||||
table.summary th.modules {
|
||||
padding: 20px 35px 0 34px;
|
||||
}
|
||||
table.summary th.modules-done, table.summary th.total-tests, table.summary th.passed {
|
||||
table.summary th.run-modules, table.summary th.total-tests, table.summary th.passed {
|
||||
padding: 20px 35px 0 0;
|
||||
}
|
||||
table.summary th.failed, table.summary th.blocked, table.summary th.ignored {
|
||||
@ -161,7 +161,7 @@
|
||||
table.summary td.modules {
|
||||
padding: 5px 35px 20px 34px;
|
||||
}
|
||||
table.summary td.modules-done, table.summary td.total-tests, table.summary td.passed {
|
||||
table.summary td.run-modules, table.summary td.total-tests, table.summary td.passed {
|
||||
padding: 5px 35px 20px 0;
|
||||
}
|
||||
table.summary td.failed, table.summary td.blocked, table.summary td.ignored {
|
||||
@ -378,20 +378,24 @@
|
||||
}
|
||||
|
||||
table.failure-test td.test {
|
||||
vertical-align: top;
|
||||
width: 569px;
|
||||
padding: 0 0 0 20px;
|
||||
padding: 2px 0 0 20px;
|
||||
}
|
||||
table.failure-test td.status {
|
||||
vertical-align: top;
|
||||
width: 11px;
|
||||
padding: 0 0 0 0;
|
||||
padding: 8px 0 0 0;
|
||||
}
|
||||
table.failure-test td.result {
|
||||
vertical-align: top;
|
||||
width: 80px;
|
||||
padding: 0 0 0 0;
|
||||
padding: 2px 0 0 0;
|
||||
}
|
||||
table.failure-test td.details {
|
||||
vertical-align: top;
|
||||
width: 480px;
|
||||
padding: 0 0 0 0;
|
||||
padding: 2px 0 0 0;
|
||||
}
|
||||
|
||||
div.hidden {
|
||||
@ -433,13 +437,13 @@
|
||||
<td class="normal third">Execution Time:</td>
|
||||
<td class="normal fourth"><!--{exec_info.execute_time}--></td>
|
||||
</tr>
|
||||
<!--{exec_info.product_params}-->
|
||||
<!--{exec_info.product_info}-->
|
||||
</table>
|
||||
|
||||
<table class="summary">
|
||||
<tr>
|
||||
<th class="normal modules color-normal"><!--{summary.modules}--></th>
|
||||
<th class="normal modules-done color-passed"><!--{summary.modules_done}--></th>
|
||||
<th class="normal run-modules color-passed"><!--{summary.run_modules}--></th>
|
||||
<th class="normal total-tests color-normal"><!--{summary.total}--></th>
|
||||
<th class="normal passed color-passed"><!--{summary.passed}--></th>
|
||||
<th class="normal failed <!--{color_type.failed}-->"><!--{summary.failed}--></th>
|
||||
@ -449,7 +453,7 @@
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="normal modules">Modules</td>
|
||||
<td class="normal modules-done">Modules Done</td>
|
||||
<td class="normal run-modules">Run Modules</td>
|
||||
<td class="normal total-tests">Total Tests</td>
|
||||
<td class="normal passed">Passed</td>
|
||||
<td class="normal failed">Failed</td>
|
||||
|
0
src/xdevice/_core/testkit/__init__.py
Executable file → Normal file
0
src/xdevice/_core/testkit/__init__.py
Executable file → Normal file
@ -2,7 +2,7 @@
|
||||
# coding=utf-8
|
||||
|
||||
#
|
||||
# Copyright (c) 2020 Huawei Device Co., Ltd.
|
||||
# Copyright (c) 2020-2021 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
|
||||
@ -18,7 +18,7 @@
|
||||
|
||||
import json
|
||||
import os
|
||||
|
||||
import stat
|
||||
from _core.exception import ParamError
|
||||
from _core.logger import platform_logger
|
||||
from _core.plugin import Config
|
||||
@ -68,11 +68,16 @@ class JsonParser:
|
||||
else:
|
||||
if not os.path.exists(path_or_content):
|
||||
raise ParamError("The json file {} does not exist".format(
|
||||
path_or_content))
|
||||
with open(path_or_content, encoding="utf-8") as file_content:
|
||||
path_or_content), error_no="00110")
|
||||
|
||||
flags = os.O_RDONLY
|
||||
modes = stat.S_IWUSR | stat.S_IRUSR
|
||||
with os.fdopen(os.open(path_or_content, flags, modes),
|
||||
"r") as file_content:
|
||||
json_content = json.load(file_content)
|
||||
except (TypeError, ValueError, AttributeError) as error:
|
||||
raise ParamError("%s %s" % (path_or_content, error))
|
||||
raise ParamError("json file error: %s %s" % (
|
||||
path_or_content, error), error_no="00111")
|
||||
self._check_config(json_content)
|
||||
|
||||
# set self.config
|
||||
|
@ -2,7 +2,7 @@
|
||||
# coding=utf-8
|
||||
|
||||
#
|
||||
# Copyright (c) 2020 Huawei Device Co., Ltd.
|
||||
# Copyright (c) 2020-2021 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
|
||||
@ -23,16 +23,19 @@ import string
|
||||
import subprocess
|
||||
import shutil
|
||||
import platform
|
||||
import glob
|
||||
|
||||
from _core.logger import platform_logger
|
||||
from _core.plugin import Plugin
|
||||
from _core.config.config_manager import UserConfigManager
|
||||
from _core.constants import CKit
|
||||
from _core.constants import ConfigConst
|
||||
from _core.constants import ComType
|
||||
from _core.constants import DeviceLiteKernel
|
||||
from _core.constants import DeviceTestType
|
||||
from _core.exception import LiteDeviceMountError
|
||||
from _core.exception import ParamError
|
||||
from _core.exception import LiteDeviceError
|
||||
from _core.interface import ITestKit
|
||||
from _core.utils import get_config_value
|
||||
from _core.utils import get_file_absolute_path
|
||||
@ -40,9 +43,11 @@ from _core.utils import get_local_ip
|
||||
from _core.utils import get_test_component_version
|
||||
from _core.exception import LiteDeviceConnectError
|
||||
from _core.constants import DeviceLabelType
|
||||
from _core.environment.manager_env import DeviceAllocationState
|
||||
|
||||
|
||||
__all__ = ["DeployKit", "MountKit", "RootFsKit", "QueryKit"]
|
||||
__all__ = ["DeployKit", "MountKit", "RootFsKit", "QueryKit", "LiteShellKit",
|
||||
"LiteAppInstallKit"]
|
||||
LOG = platform_logger("KitLite")
|
||||
|
||||
RESET_CMD = "0xEF, 0xBE, 0xAD, 0xDE, 0x0C, 0x00, 0x87, 0x78, 0x00, 0x00, " \
|
||||
@ -58,68 +63,53 @@ class DeployKit(ITestKit):
|
||||
self.paths = ""
|
||||
|
||||
def __check_config__(self, config):
|
||||
self.timeout = str(int(get_config_value('timeout', config,
|
||||
is_list=False)) * 1000)
|
||||
self.timeout = str(int(get_config_value(
|
||||
'timeout', config, is_list=False, default=0)) * 1000)
|
||||
self.burn_file = get_config_value('burn_file', config, is_list=False)
|
||||
burn_command = get_config_value('burn_command', config, is_list=False,
|
||||
default=RESET_CMD)
|
||||
self.burn_command = burn_command.replace(" ", "").split(",")
|
||||
self.paths = get_config_value('paths', config)
|
||||
if not self.timeout or not self.burn_file or not self.burn_command:
|
||||
msg = "The config for deploy kit is invalid with timeout:{} " \
|
||||
"burn_file:{} burn_command:{}".format(self.timeout,
|
||||
self.burn_file,
|
||||
self.burn_command)
|
||||
LOG.error(msg)
|
||||
raise TypeError(msg)
|
||||
if self.timeout == "0" or not self.burn_file:
|
||||
msg = "The config for deploy kit is invalid with timeout:{}, " \
|
||||
"burn_file:{}".format(self.timeout, self.burn_file)
|
||||
raise ParamError(msg, error_no="00108")
|
||||
|
||||
def __setup__(self, device, **kwargs):
|
||||
"""
|
||||
Execute reset command on the device by cmd serial port and then upload
|
||||
patch file by deploy tool.
|
||||
Parameters:
|
||||
device: the instance of LocalController with one or more
|
||||
ComController
|
||||
"""
|
||||
del kwargs
|
||||
LOG.debug(
|
||||
"Deploy kit params:{}".format(self.get_plugin_config().__dict__))
|
||||
cmd_com = device.com_dict.get(ComType.cmd_com)
|
||||
LOG.info("The cmd com is {}".format(cmd_com.serial_port))
|
||||
def _reset(self, device):
|
||||
cmd_com = device.device.com_dict.get(ComType.cmd_com)
|
||||
try:
|
||||
cmd_com.connect()
|
||||
cmd_com.execute_command(
|
||||
command='AT+RST={}'.format(self.timeout))
|
||||
cmd_com.close()
|
||||
except (LiteDeviceConnectError, IOError) as error:
|
||||
device.device_allocation_state = DeviceAllocationState.unusable
|
||||
LOG.error(
|
||||
"The exception {} happened in deploy kit running".format(
|
||||
error))
|
||||
return False
|
||||
error), error_no=getattr(error, "error_no",
|
||||
"00000"))
|
||||
raise LiteDeviceError("%s port set_up wifiiot failed" %
|
||||
cmd_com.serial_port,
|
||||
error_no=getattr(error, "error_no",
|
||||
"00000"))
|
||||
finally:
|
||||
if cmd_com:
|
||||
cmd_com.close()
|
||||
|
||||
def _send_file(self, device):
|
||||
burn_tool_name = "HiBurn.exe" if os.name == "nt" else "HiBurn"
|
||||
burn_tool_path = get_file_absolute_path(
|
||||
os.path.join("tools", burn_tool_name), self.paths)
|
||||
if not os.path.exists(burn_tool_path):
|
||||
LOG.error('The burn tool {} does not exist, please check!'.format(
|
||||
burn_tool_path))
|
||||
return False
|
||||
patch_file = get_file_absolute_path(self.burn_file, self.paths)
|
||||
if not os.path.exists(patch_file):
|
||||
LOG.error('The patch file {} does not exist, please check!'.format(
|
||||
patch_file))
|
||||
return False
|
||||
deploy_serial_port = device.com_dict.get(
|
||||
deploy_serial_port = device.device.com_dict.get(
|
||||
ComType.deploy_com).serial_port
|
||||
deploy_baudrate = device.com_dict.get(ComType.deploy_com).baund_rate
|
||||
deploy_baudrate = device.device.com_dict.\
|
||||
get(ComType.deploy_com).baud_rate
|
||||
port_number = re.findall(r'\d+$', deploy_serial_port)
|
||||
if not port_number:
|
||||
LOG.error(
|
||||
"The config of serial port {} to deploy is invalid".format(
|
||||
deploy_serial_port))
|
||||
return False
|
||||
raise LiteDeviceError("The config of serial port {} to deploy is "
|
||||
"invalid".format(deploy_serial_port),
|
||||
error_no="00108")
|
||||
new_temp_tool_path = copy_file_as_temp(burn_tool_path, 10)
|
||||
cmd = '{} -com:{} -bin:{} -signalbaud:{}' \
|
||||
.format(new_temp_tool_path, port_number[0], patch_file,
|
||||
@ -131,9 +121,22 @@ class DeployKit(ITestKit):
|
||||
'Deploy kit to execute burn tool finished with return_code: {} '
|
||||
'output: {}'.format(return_code, out))
|
||||
os.remove(new_temp_tool_path)
|
||||
if 0 == return_code:
|
||||
return True
|
||||
return False
|
||||
if 0 != return_code:
|
||||
device.device_allocation_state = DeviceAllocationState.unusable
|
||||
raise LiteDeviceError("%s port set_up wifiiot failed" %
|
||||
deploy_serial_port, error_no="00402")
|
||||
|
||||
def __setup__(self, device, **kwargs):
|
||||
"""
|
||||
Execute reset command on the device by cmd serial port and then upload
|
||||
patch file by deploy tool.
|
||||
Parameters:
|
||||
device: the instance of LocalController with one or more
|
||||
ComController
|
||||
"""
|
||||
del kwargs
|
||||
self._reset(device)
|
||||
self._send_file(device)
|
||||
|
||||
def __teardown__(self, device):
|
||||
pass
|
||||
@ -159,8 +162,8 @@ class MountKit(ITestKit):
|
||||
if not self.mount_list:
|
||||
msg = "The config for mount kit is invalid with mount:{}" \
|
||||
.format(self.mount_list)
|
||||
LOG.error(msg)
|
||||
raise TypeError(msg)
|
||||
LOG.error(msg, error_no="00108")
|
||||
raise TypeError("Load Error[00108]")
|
||||
|
||||
def mount_on_board(self, device=None, remote_info=None, case_type=""):
|
||||
"""
|
||||
@ -180,7 +183,8 @@ class MountKit(ITestKit):
|
||||
True or False, represent init Failed or success
|
||||
"""
|
||||
if not remote_info:
|
||||
raise ParamError("failed to get server environment")
|
||||
raise ParamError("failed to get server environment",
|
||||
error_no="00108")
|
||||
|
||||
linux_host = remote_info.get("ip", "")
|
||||
linux_directory = remote_info.get("dir", "")
|
||||
@ -188,40 +192,47 @@ class MountKit(ITestKit):
|
||||
liteos_commands = ["cd /", "umount device_directory",
|
||||
"mount nfs_ip:nfs_directory device"
|
||||
"_directory nfs"]
|
||||
linux_commands = ["cd /", "umount device_directory",
|
||||
"mount -t nfs -o nolock -o tcp nfs_ip:nfs_directory"
|
||||
"device_directory", "chmod 755 -R device_directory"]
|
||||
linux_commands = ["cd /%s" % "storage",
|
||||
"fuser -k /%s/%s" % ("storage", "device_directory"),
|
||||
"umount -f /%s/%s" % ("storage", "device_directory"),
|
||||
"mount -t nfs -o nolock -o tcp nfs_ip:nfs_directory "
|
||||
"/%s/%s" % ("storage", "device_directory"),
|
||||
"chmod 755 -R /%s/%s" % (
|
||||
"storage", "device_directory")]
|
||||
if not linux_host or not linux_directory:
|
||||
raise LiteDeviceMountError("nfs server miss ip or directory")
|
||||
if device.device_connect_type == "local":
|
||||
device.local_device.flush_input()
|
||||
raise LiteDeviceMountError(
|
||||
"nfs server miss ip or directory[00108]", error_no="00108")
|
||||
|
||||
commands = []
|
||||
if device.label == "ipcamera":
|
||||
env_result, status, _ = device.execute_command_with_timeout(
|
||||
command="uname", timeout=1)
|
||||
command="uname", timeout=1, retry=2)
|
||||
if status:
|
||||
if env_result.find(DeviceLiteKernel.linux_kernel) != -1:
|
||||
if env_result.find(DeviceLiteKernel.linux_kernel) != -1 or \
|
||||
env_result.find("Linux") != -1:
|
||||
commands = linux_commands
|
||||
device.__set_device_kernel__(DeviceLiteKernel.linux_kernel)
|
||||
else:
|
||||
commands = liteos_commands
|
||||
device.__set_device_kernel__(DeviceLiteKernel.lite_kernel)
|
||||
else:
|
||||
raise LiteDeviceMountError("failed to get device env")
|
||||
raise LiteDeviceMountError("failed to get device env[00402]",
|
||||
error_no="00402")
|
||||
|
||||
for mount_file in self.mount_list:
|
||||
target = mount_file.get("target", "/test_root")
|
||||
if target in self.mounted_dir:
|
||||
LOG.debug("%s is mounted" % target)
|
||||
continue
|
||||
mkdir_on_board(device, target)
|
||||
|
||||
# local nfs server need use alias of dir to mount
|
||||
if is_remote.lower() == "false":
|
||||
linux_directory = get_mount_dir(linux_directory)
|
||||
for command in commands:
|
||||
command = command.replace("nfs_ip", linux_host). \
|
||||
replace("nfs_directory", linux_directory).replace(
|
||||
"device_directory", target)
|
||||
"device_directory", target).replace("//", "/")
|
||||
timeout = 15 if command.startswith("mount") else 1
|
||||
result, status, _ = device.execute_command_with_timeout(
|
||||
command=command, case_type=case_type, timeout=timeout)
|
||||
@ -233,12 +244,17 @@ class MountKit(ITestKit):
|
||||
"""
|
||||
Mount the file to the board by the nfs server.
|
||||
"""
|
||||
LOG.debug("start mount kit setup")
|
||||
|
||||
request = kwargs.get("request", None)
|
||||
if not request:
|
||||
raise ParamError("MountKit setup request is None")
|
||||
raise ParamError("MountKit setup request is None",
|
||||
error_no="02401")
|
||||
device.connect()
|
||||
|
||||
config_manager = UserConfigManager(env=request.config.test_environment)
|
||||
config_manager = UserConfigManager(
|
||||
config_file=request.get(ConfigConst.configfile, ""),
|
||||
env=request.get(ConfigConst.test_environment, ""))
|
||||
remote_info = config_manager.get_user_config("testcases/server",
|
||||
filter_name=self.server)
|
||||
|
||||
@ -271,7 +287,9 @@ class MountKit(ITestKit):
|
||||
else:
|
||||
file_local_paths.append(file_path)
|
||||
|
||||
config_manager = UserConfigManager(env=request.config.test_environment)
|
||||
config_manager = UserConfigManager(
|
||||
config_file=request.get(ConfigConst.configfile, ""),
|
||||
env=request.get(ConfigConst.test_environment, ""))
|
||||
remote_info = config_manager.get_user_config("testcases/server",
|
||||
filter_name=self.server)
|
||||
self.remote_info = remote_info
|
||||
@ -279,7 +297,7 @@ class MountKit(ITestKit):
|
||||
if not remote_info:
|
||||
err_msg = "The name of remote device {} does not match". \
|
||||
format(self.remote)
|
||||
LOG.error(err_msg)
|
||||
LOG.error(err_msg, error_no="00403")
|
||||
raise TypeError(err_msg)
|
||||
is_remote = remote_info.get("remote", "false")
|
||||
if (str(get_local_ip()) == linux_host) and (
|
||||
@ -303,47 +321,53 @@ class MountKit(ITestKit):
|
||||
except (OSError, Exception) as exception:
|
||||
msg = "copy file to nfs server failed with error {}" \
|
||||
.format(exception)
|
||||
LOG.error(msg)
|
||||
raise LiteDeviceMountError(exception)
|
||||
LOG.error(msg, error_no="00403")
|
||||
# local copy
|
||||
else:
|
||||
shutil.copy(_file, remote_info.get("dir"))
|
||||
for count in range(1, 4):
|
||||
shutil.copy(_file, remote_info.get("dir"))
|
||||
if check_server_file(_file, remote_info.get("dir")):
|
||||
break
|
||||
else:
|
||||
LOG.info(
|
||||
"Trying to copy the file from {} to nfs "
|
||||
"server {} times".format(_file, count))
|
||||
if count == 3:
|
||||
msg = "copy {} to nfs server " \
|
||||
"failed {} times".format(
|
||||
os.path.basename(_file), count)
|
||||
LOG.error(msg, error_no="00403")
|
||||
LOG.debug("Nfs server:{}".format(glob.glob(
|
||||
os.path.join(remote_info.get("dir"), '*.*'))))
|
||||
|
||||
self.file_name_list.append(os.path.basename(_file))
|
||||
|
||||
return self.file_name_list
|
||||
|
||||
def __teardown__(self, device):
|
||||
device.execute_command_with_timeout(command="cd /", timeout=1)
|
||||
for mounted_dir in self.mounted_dir:
|
||||
device.execute_command_with_timeout(command="umount {}".
|
||||
format(mounted_dir),
|
||||
timeout=2)
|
||||
device.execute_command_with_timeout(command="rm -r {}".
|
||||
format(mounted_dir),
|
||||
if device.__get_device_kernel__() == DeviceLiteKernel.linux_kernel:
|
||||
device.execute_command_with_timeout(command="cd /storage",
|
||||
timeout=1)
|
||||
|
||||
is_remote = self.remote_info.get("remote", "false")
|
||||
for _file in self.file_name_list:
|
||||
LOG.info("Trying to delete the file from nfs server".
|
||||
format(_file))
|
||||
try:
|
||||
if is_remote.lower() == "true":
|
||||
import paramiko
|
||||
client = paramiko.Transport(
|
||||
(self.remote_info.get("ip"),
|
||||
int(self.remote_info.get("port"))))
|
||||
client.connect(
|
||||
username=self.remote_info.get("username"),
|
||||
password=self.remote_info.get("password"))
|
||||
sftp = paramiko.SFTPClient.from_transport(client)
|
||||
file_path = "{}{}".format(
|
||||
self.remote_info.get("dir"), _file)
|
||||
sftp.remove(file_path)
|
||||
client.close()
|
||||
else:
|
||||
os.remove(os.path.join(self.remote_info.get("dir"), _file))
|
||||
except FileNotFoundError:
|
||||
LOG.debug("delete file %s failed" % _file)
|
||||
for mounted_dir in self.mounted_dir:
|
||||
device.execute_command_with_timeout(command="fuser -k {}".
|
||||
format(mounted_dir),
|
||||
timeout=2)
|
||||
device.execute_command_with_timeout(command="umount -f "
|
||||
"/storage{}".
|
||||
format(mounted_dir),
|
||||
timeout=2)
|
||||
device.execute_command_with_timeout(command="rm -r /storage{}".
|
||||
format(mounted_dir),
|
||||
timeout=1)
|
||||
else:
|
||||
device.execute_command_with_timeout(command="cd /", timeout=1)
|
||||
for mounted_dir in self.mounted_dir:
|
||||
device.execute_command_with_timeout(command="umount {}".
|
||||
format(mounted_dir),
|
||||
timeout=2)
|
||||
device.execute_command_with_timeout(command="rm -r {}".
|
||||
format(mounted_dir),
|
||||
timeout=1)
|
||||
|
||||
|
||||
def copy_file_as_temp(original_file, str_length):
|
||||
@ -369,7 +393,10 @@ def mkdir_on_board(device, dir_path):
|
||||
device : the L1 board
|
||||
dir_path: the dir path to make
|
||||
"""
|
||||
device.execute_command_with_timeout(command="cd /", timeout=1)
|
||||
if device.__get_device_kernel__() == DeviceLiteKernel.linux_kernel:
|
||||
device.execute_command_with_timeout(command="cd /storage", timeout=1)
|
||||
else:
|
||||
device.execute_command_with_timeout(command="cd /", timeout=1)
|
||||
for sub_dir in dir_path.split("/"):
|
||||
if sub_dir in ["", "/"]:
|
||||
continue
|
||||
@ -377,7 +404,10 @@ def mkdir_on_board(device, dir_path):
|
||||
timeout=1)
|
||||
device.execute_command_with_timeout(command="cd {}".format(sub_dir),
|
||||
timeout=1)
|
||||
device.execute_command_with_timeout(command="cd /", timeout=1)
|
||||
if device.__get_device_kernel__() == DeviceLiteKernel.linux_kernel:
|
||||
device.execute_command_with_timeout(command="cd /storage", timeout=1)
|
||||
else:
|
||||
device.execute_command_with_timeout(command="cd /", timeout=1)
|
||||
|
||||
|
||||
def get_mount_dir(mount_dir):
|
||||
@ -400,6 +430,13 @@ def get_mount_dir(mount_dir):
|
||||
return mount_dir
|
||||
|
||||
|
||||
def check_server_file(local_file, target_path):
|
||||
for file_list in glob.glob(os.path.join(target_path, '*.*')):
|
||||
if os.path.basename(local_file) in file_list:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
@Plugin(type=Plugin.TEST_KIT, id=CKit.rootfs)
|
||||
class RootFsKit(ITestKit):
|
||||
def __init__(self):
|
||||
@ -420,7 +457,7 @@ class RootFsKit(ITestKit):
|
||||
" hash_file_name:{} device_label:{}" \
|
||||
.format(self.checksum_command, self.hash_file_name,
|
||||
self.device_label)
|
||||
LOG.error(msg)
|
||||
LOG.error(msg, error_no="00108")
|
||||
return TypeError(msg)
|
||||
|
||||
def __setup__(self, device, **kwargs):
|
||||
@ -428,7 +465,8 @@ class RootFsKit(ITestKit):
|
||||
|
||||
# check device label
|
||||
if not device.label == self.device_label:
|
||||
LOG.error("device label is not match '%s '" % "demo_label")
|
||||
LOG.error("device label is not match '%s '" % "demo_label",
|
||||
error_no="00108")
|
||||
return False
|
||||
else:
|
||||
report_path = self._get_report_dir()
|
||||
@ -436,6 +474,8 @@ class RootFsKit(ITestKit):
|
||||
|
||||
# execute command of checksum
|
||||
device.connect()
|
||||
device.execute_command_with_timeout(
|
||||
command="cd /", case_type=DeviceTestType.cpp_test_lite)
|
||||
result, _, _ = device.execute_command_with_timeout(
|
||||
command=self.checksum_command,
|
||||
case_type=DeviceTestType.cpp_test_lite)
|
||||
@ -451,13 +491,16 @@ class RootFsKit(ITestKit):
|
||||
hash_file_name = "".join((self.hash_file_name, serial))
|
||||
hash_file_path = os.path.join(report_path, hash_file_name)
|
||||
# write result to file
|
||||
with open(hash_file_path, mode="w", encoding="utf-8") \
|
||||
as hash_file:
|
||||
hash_file_path_open = os.open(hash_file_path, os.O_WRONLY |
|
||||
os.O_CREAT | os.O_APPEND, 0o755)
|
||||
|
||||
with os.fdopen(hash_file_path_open, mode="w") as hash_file:
|
||||
hash_file.write(result)
|
||||
hash_file.flush()
|
||||
else:
|
||||
msg = "RootFsKit teardown, log path [%s] not exists!" \
|
||||
% report_path
|
||||
LOG.error(msg)
|
||||
LOG.error(msg, error_no="00440")
|
||||
return False
|
||||
return True
|
||||
|
||||
@ -484,28 +527,37 @@ class QueryKit(ITestKit):
|
||||
setattr(self.mount_kit, "server", get_config_value(
|
||||
'server', config, is_list=False, default="NfsServer"))
|
||||
self.query = get_config_value('query', config, is_list=False)
|
||||
self.properties = get_config_value('properties', config, is_list=False)
|
||||
|
||||
if not self.query:
|
||||
msg = "The config for query kit is invalid with query:{}" \
|
||||
.format(self.query)
|
||||
LOG.error(msg)
|
||||
LOG.error(msg, error_no="00108")
|
||||
raise TypeError(msg)
|
||||
|
||||
def __setup__(self, device, **kwargs):
|
||||
LOG.debug("start query kit setup")
|
||||
if device.label != DeviceLabelType.ipcamera:
|
||||
return
|
||||
request = kwargs.get("request", None)
|
||||
if not request:
|
||||
raise ParamError("the request of queryKit is None")
|
||||
raise ParamError("the request of queryKit is None",
|
||||
error_no="02401")
|
||||
self.mount_kit.__setup__(device, request=request)
|
||||
device.execute_command_with_timeout(command="cd /", timeout=0.2)
|
||||
output, _, _ = device.execute_command_with_timeout(
|
||||
command=".{}".format(self.query), timeout=5)
|
||||
product_param = {}
|
||||
if device.__get_device_kernel__() == DeviceLiteKernel.linux_kernel:
|
||||
device.execute_command_with_timeout(command="cd /storage",
|
||||
timeout=0.2)
|
||||
output, _, _ = device.execute_command_with_timeout(
|
||||
command=".{}{}".format("/storage", self.query), timeout=5)
|
||||
else:
|
||||
device.execute_command_with_timeout(command="cd /", timeout=0.2)
|
||||
output, _, _ = device.execute_command_with_timeout(
|
||||
command=".{}".format(self.query), timeout=5)
|
||||
product_info = {}
|
||||
for line in output.split("\n"):
|
||||
process_product_params(line, product_param)
|
||||
product_param["version"] = get_test_component_version(request.config)
|
||||
request.product_params = product_param
|
||||
process_product_info(line, product_info)
|
||||
product_info["version"] = get_test_component_version(request.config)
|
||||
request.product_info = product_info
|
||||
|
||||
def __teardown__(self, device):
|
||||
if device.label != DeviceLabelType.ipcamera:
|
||||
@ -515,9 +567,95 @@ class QueryKit(ITestKit):
|
||||
device.close()
|
||||
|
||||
|
||||
def process_product_params(message, product_params):
|
||||
@Plugin(type=Plugin.TEST_KIT, id=CKit.liteshell)
|
||||
class LiteShellKit(ITestKit):
|
||||
def __init__(self):
|
||||
self.command_list = []
|
||||
self.tear_down_command = []
|
||||
self.paths = None
|
||||
|
||||
def __check_config__(self, config):
|
||||
self.command_list = get_config_value('run-command', config)
|
||||
self.tear_down_command = get_config_value('teardown-command', config)
|
||||
|
||||
def __setup__(self, device, **kwargs):
|
||||
del kwargs
|
||||
LOG.debug("LiteShellKit setup, device:{}".format(device.device_sn))
|
||||
if len(self.command_list) == 0:
|
||||
LOG.info("No setup_command to run, skipping!")
|
||||
return
|
||||
for command in self.command_list:
|
||||
run_command(device, command)
|
||||
|
||||
def __teardown__(self, device):
|
||||
LOG.debug("LiteShellKit teardown: device:{}".format(device.device_sn))
|
||||
if len(self.tear_down_command) == 0:
|
||||
LOG.info("No teardown_command to run, skipping!")
|
||||
return
|
||||
for command in self.tear_down_command:
|
||||
run_command(device, command)
|
||||
|
||||
|
||||
def run_command(device, command):
|
||||
LOG.debug("The command:{} is running".format(command))
|
||||
if command.strip() == "reset":
|
||||
device.reboot()
|
||||
else:
|
||||
device.execute_shell_command(command)
|
||||
|
||||
|
||||
@Plugin(type=Plugin.TEST_KIT, id=CKit.liteinstall)
|
||||
class LiteAppInstallKit(ITestKit):
|
||||
def __init__(self):
|
||||
self.app_list = ""
|
||||
self.is_clean = ""
|
||||
self.alt_dir = ""
|
||||
self.bundle_name = None
|
||||
self.paths = ""
|
||||
self.signature = False
|
||||
|
||||
def __check_config__(self, options):
|
||||
self.app_list = get_config_value('test-file-name', options)
|
||||
self.is_clean = get_config_value('cleanup-apps', options, False)
|
||||
self.signature = get_config_value('signature', options, False)
|
||||
self.alt_dir = get_config_value('alt-dir', options, False)
|
||||
if self.alt_dir and self.alt_dir.startswith("resource/"):
|
||||
self.alt_dir = self.alt_dir[len("resource/"):]
|
||||
self.paths = get_config_value('paths', options)
|
||||
|
||||
def __setup__(self, device, **kwargs):
|
||||
del kwargs
|
||||
LOG.debug("LiteAppInstallKit setup, device:{}".
|
||||
format(device.device_sn))
|
||||
if len(self.app_list) == 0:
|
||||
LOG.info("No app to install, skipping!")
|
||||
return
|
||||
|
||||
for app in self.app_list:
|
||||
if app.endswith(".hap"):
|
||||
device.execute_command_with_timeout("cd /", timeout=1)
|
||||
if self.signature:
|
||||
device.execute_command_with_timeout(
|
||||
command="./bin/bm set -d enable", timeout=10)
|
||||
else:
|
||||
device.execute_command_with_timeout(
|
||||
command="./bin/bm set -s disable", timeout=10)
|
||||
|
||||
device.execute_command_with_timeout(
|
||||
"./bin/bm install -p %s" % app, timeout=60)
|
||||
|
||||
def __teardown__(self, device):
|
||||
LOG.debug("LiteAppInstallKit teardown: device:{}".format(
|
||||
device.device_sn))
|
||||
if self.is_clean and str(self.is_clean).lower() == "true" \
|
||||
and self.bundle_name:
|
||||
device.execute_command_with_timeout(
|
||||
"./bin/bm uninstall -n %s" % self.bundle_name, timeout=90)
|
||||
|
||||
|
||||
def process_product_info(message, product_info):
|
||||
if "The" in message:
|
||||
message = message[message.index("The"):]
|
||||
items = message[len("The "):].split(" is ")
|
||||
product_params.setdefault(items[0].strip(),
|
||||
items[1].strip().strip("[").strip("]"))
|
||||
product_info.setdefault(items[0].strip(),
|
||||
items[1].strip().strip("[").strip("]"))
|
||||
|
@ -1,476 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# coding=utf-8
|
||||
|
||||
#
|
||||
# Copyright (c) 2020 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 datetime
|
||||
import platform
|
||||
import signal
|
||||
import os
|
||||
import json
|
||||
import time
|
||||
import subprocess
|
||||
import re
|
||||
from dataclasses import dataclass
|
||||
|
||||
from json import JSONDecodeError
|
||||
from shutil import copyfile
|
||||
from threading import Timer
|
||||
from _core.constants import CKit
|
||||
from _core.exception import ExecuteTerminate
|
||||
from _core.exception import ParamError
|
||||
from _core.logger import platform_logger
|
||||
from _core.plugin import Plugin
|
||||
|
||||
TIMEOUT = 90
|
||||
MAX_VALID_POSITION = 454
|
||||
LOG = platform_logger("UiKitLite")
|
||||
|
||||
|
||||
def timeout_callback(proc, timeout):
|
||||
try:
|
||||
timeout.is_timeout = True
|
||||
LOG.error("Error: execute command timeout.")
|
||||
LOG.error(proc.pid)
|
||||
if platform.system() != "Windows":
|
||||
os.killpg(proc.pid, signal.SIGKILL)
|
||||
else:
|
||||
subprocess.call(
|
||||
["C:\\Windows\\System32\\taskkill", "/F", "/T", "/PID",
|
||||
str(proc.pid)],
|
||||
shell=False)
|
||||
except (FileNotFoundError, KeyboardInterrupt, AttributeError) as error:
|
||||
LOG.exception("timeout callback exception: %s" % error)
|
||||
|
||||
|
||||
def get_dump_str(dump_file):
|
||||
dump_str = ""
|
||||
if os.path.exists(dump_file):
|
||||
with open(dump_file, "r") as json_content:
|
||||
dump_str = json_content.read()
|
||||
return dump_str
|
||||
|
||||
|
||||
def get_center_position(parsed_dict, attr_name, attr_value):
|
||||
if attr_value == parsed_dict.get(attr_name, ""):
|
||||
return _calculate_center_position(parsed_dict)
|
||||
|
||||
child_dicts = parsed_dict.get("child", [])
|
||||
for child_dict in child_dicts:
|
||||
x_center, y_center = get_center_position(child_dict, attr_name,
|
||||
attr_value)
|
||||
if check_position(x_center, y_center):
|
||||
return x_center, y_center
|
||||
|
||||
return -1, -1
|
||||
|
||||
|
||||
def check_position(x_position, y_position):
|
||||
return (0 <= x_position <= MAX_VALID_POSITION) and \
|
||||
(0 <= y_position <= MAX_VALID_POSITION)
|
||||
|
||||
|
||||
def copy_file(destination_path, screen_file_name):
|
||||
from xdevice import Variables
|
||||
abs_paths = [Variables.exec_dir, Variables.top_dir, Variables.modules_dir]
|
||||
for path in abs_paths:
|
||||
if path:
|
||||
for file_name in os.listdir(path):
|
||||
if file_name.endswith(".bin") and os.path.exists(os.path.join(
|
||||
path, file_name)):
|
||||
dst_file = "%s%s" % (os.path.join(
|
||||
destination_path, screen_file_name), ".bin")
|
||||
copyfile(os.path.join(path, file_name), dst_file)
|
||||
os.remove(os.path.join(path, file_name))
|
||||
break
|
||||
|
||||
|
||||
def _calculate_center_position(parsed_dict):
|
||||
x_value = parsed_dict.get("x", -1)
|
||||
y_value = parsed_dict.get("y", -1)
|
||||
if not check_position(x_value, y_value):
|
||||
LOG.info("error (x, y) value, (%s, %s)" % (x_value, y_value))
|
||||
return -1, -1
|
||||
width = parsed_dict.get("width", -1)
|
||||
height = parsed_dict.get("height", -1)
|
||||
if not check_position(width, height):
|
||||
LOG.info("error (width, height) value, (%s, %s)" % (width, height))
|
||||
return -1, -1
|
||||
return min((x_value + width / 2), 454), min((y_value + height / 2), 454)
|
||||
|
||||
|
||||
def get_center(dump_str, attr_name, attr_value):
|
||||
"""execute hd.click method
|
||||
|
||||
Parameters:
|
||||
dump_str: json string
|
||||
attr_name: target attribute name like 'id' or 'text'
|
||||
attr_value: target attribute value
|
||||
|
||||
Return:
|
||||
0: success 1: fail
|
||||
"""
|
||||
try:
|
||||
parsed_dict = json.loads(dump_str, encoding="utf-8")
|
||||
except JSONDecodeError as error:
|
||||
LOG.info("format error, %s" % error.args)
|
||||
return 1
|
||||
x_center, y_center = get_center_position(parsed_dict, attr_name,
|
||||
attr_value)
|
||||
LOG.info("(x_center, y_center) value, (%s, %s)" % (x_center, y_center))
|
||||
if check_position(x_center, y_center):
|
||||
return True, (x_center, y_center)
|
||||
else:
|
||||
LOG.info("no valid center position (%s, %s)" % (x_center, y_center))
|
||||
return False, None
|
||||
|
||||
|
||||
def get_hdc_command(command, shell=True):
|
||||
if shell:
|
||||
return " ".join(["hdc", "shell", command])
|
||||
else:
|
||||
return " ".join(["hdc", command])
|
||||
|
||||
|
||||
def filter_json(result_str=""):
|
||||
result = "no such node"
|
||||
if result_str.find("{") != -1:
|
||||
result_str = result_str.replace("\n", "").strip()
|
||||
pattern = r"(.*)(\{.*\})(.*)"
|
||||
matcher = re.match(pattern, result_str)
|
||||
if matcher:
|
||||
return matcher.group(2)
|
||||
return result
|
||||
|
||||
|
||||
@dataclass
|
||||
class Timeout:
|
||||
is_timeout = False
|
||||
|
||||
|
||||
@Plugin(type=Plugin.TEST_KIT, id=CKit.liteuikit)
|
||||
class LiteUiKit:
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def __set_device__(self, device):
|
||||
pass
|
||||
|
||||
def __set_connect_type__(self, device):
|
||||
pass
|
||||
|
||||
def __check_config__(self, config):
|
||||
pass
|
||||
|
||||
def __setup__(self, device):
|
||||
pass
|
||||
|
||||
def __teardown__(self, device):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def execute_hdc_cmd_with_timeout(device, command, timeout=800,
|
||||
result_print=True):
|
||||
"""
|
||||
Executes a command on the device with timeout.
|
||||
|
||||
Parameters:
|
||||
device: device
|
||||
command: the command to execute
|
||||
timeout: time out value
|
||||
result_print:
|
||||
"""
|
||||
from _core.environment.device_lite import get_hdc_path
|
||||
cmd = [get_hdc_path(), "-p", device.serial_port.upper().replace(
|
||||
"COM", "").strip()] + command.split(" ")
|
||||
ret_message = ""
|
||||
LOG.info("execute command: %s" % " ".join(cmd))
|
||||
|
||||
start_time = datetime.datetime.now()
|
||||
LOG.info("starttime=%s with timeout=%s" % (
|
||||
start_time.strftime("%Y-%m-%d %H:%M:%S"), str(timeout)))
|
||||
|
||||
proc = subprocess.Popen(
|
||||
cmd, stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE, shell=False,
|
||||
preexec_fn=os.setsid if platform.system() != 'Windows' else None)
|
||||
is_timeout = Timeout()
|
||||
proc_timer = Timer(timeout, timeout_callback, [proc, is_timeout])
|
||||
proc_timer.start()
|
||||
|
||||
try:
|
||||
ret_message = LiteUiKit._result_hdc_out_process(
|
||||
proc, is_timeout, result_print)
|
||||
except (ExecuteTerminate, ValueError) as exception:
|
||||
LOG.exception("exception: %s", str(exception))
|
||||
finally:
|
||||
ret_message = "{}{}".format(
|
||||
ret_message, LiteUiKit._print_hdc_stdout(proc, result_print))
|
||||
error_message = LiteUiKit._print_hdc_stderr(proc, result_print)
|
||||
|
||||
proc_timer.cancel()
|
||||
proc.stdout.close()
|
||||
proc.stderr.close()
|
||||
|
||||
LOG.info("end time=%s delta=%s" % (
|
||||
datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"), str(
|
||||
(datetime.datetime.now() - start_time).seconds)))
|
||||
|
||||
if proc.returncode == 0:
|
||||
LOG.info('Info: execute command success. command=%s', command)
|
||||
return_code = 0
|
||||
else:
|
||||
LOG.error('Error: execute command failed. command=%s', command)
|
||||
return_code = 1
|
||||
return ResultValue(return_code, error_message, ret_message)
|
||||
|
||||
@staticmethod
|
||||
def _result_hdc_out_process(proc, timeout, result_print):
|
||||
result = ""
|
||||
from xdevice import Scheduler
|
||||
while proc.poll() is None:
|
||||
if not Scheduler.is_execute:
|
||||
raise ExecuteTerminate()
|
||||
line = proc.stdout.readline()
|
||||
line = line.strip()
|
||||
if isinstance(line, bytes):
|
||||
line = line.decode('utf-8', 'ignore').strip()
|
||||
if line != "":
|
||||
result = "%s%s%s" % (result, line, "\n")
|
||||
if result_print:
|
||||
LOG.info(line)
|
||||
if timeout.is_timeout:
|
||||
LOG.info("timeout flag is True")
|
||||
timeout.is_timeout = False
|
||||
break
|
||||
return result
|
||||
|
||||
@classmethod
|
||||
def _print_hdc_stdout(cls, proc, result_print=True):
|
||||
data = proc.stdout.read()
|
||||
if isinstance(data, bytes):
|
||||
data = data.decode('utf-8', 'ignore')
|
||||
if data != "":
|
||||
if not result_print:
|
||||
return data
|
||||
for line in data.rstrip("\n").split("\n"):
|
||||
LOG.info(line)
|
||||
return data.rstrip("\n")
|
||||
return ""
|
||||
|
||||
@classmethod
|
||||
def _print_hdc_stderr(cls, proc, result_print=True):
|
||||
data = proc.stderr.read()
|
||||
if isinstance(data, bytes):
|
||||
data = data.decode('utf-8', 'ignore')
|
||||
if data != "":
|
||||
if not result_print:
|
||||
return data
|
||||
LOG.error("----------stderr info start------------")
|
||||
for error_message in data.rstrip("\n").split("\n"):
|
||||
LOG.error(error_message)
|
||||
LOG.error("----------stderr info ended------------")
|
||||
return data.rstrip("\n")
|
||||
return ""
|
||||
|
||||
@staticmethod
|
||||
def click(device, x_coordinate, y_coordinate, timeout=800):
|
||||
press_command = "%s%s%s%s%s" % ("uievent ", str(x_coordinate), str(
|
||||
y_coordinate), " ", " PRESSDOWN")
|
||||
release_command = "%s%s%s%s%s" % ("uievent ", str(x_coordinate), " ",
|
||||
str(y_coordinate), " RELEASE")
|
||||
press_result = LiteUiKit.execute_hdc_cmd_with_timeout(
|
||||
device, get_hdc_command(press_command, True), timeout, True)
|
||||
if press_result.return_code == 0:
|
||||
time.sleep(timeout / 1000)
|
||||
release_result = LiteUiKit.execute_hdc_cmd_with_timeout(
|
||||
device, get_hdc_command(release_command, True), timeout, True)
|
||||
if release_result.return_code == 0:
|
||||
LOG.info("click success.")
|
||||
return True
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def click_id(device, node_id, timeout=800):
|
||||
if not node_id:
|
||||
raise ParamError("miss node id")
|
||||
status, point = LiteUiKit.ui_dump_id(
|
||||
device, node_id=node_id, timeout=timeout)
|
||||
if status:
|
||||
LiteUiKit.click(device, point[0], point[1], timeout)
|
||||
else:
|
||||
LOG.info("click failed")
|
||||
|
||||
@staticmethod
|
||||
def click_text(device, text="", timeout=800):
|
||||
status = False
|
||||
if not text:
|
||||
raise ParamError("miss node text")
|
||||
dump_str = LiteUiKit.ui_dump_tree(
|
||||
device, timeout=timeout)
|
||||
if dump_str and text:
|
||||
status, point = get_center(dump_str, "text", text)
|
||||
if status:
|
||||
LiteUiKit.click(point[0], point[1], timeout)
|
||||
else:
|
||||
LOG.info("click failed")
|
||||
|
||||
@staticmethod
|
||||
def screen_shot(device, screen_path, file_name, timeout=800):
|
||||
result_value = LiteUiKit.execute_hdc_cmd_with_timeout(
|
||||
device, get_hdc_command("screenshot"), timeout, True)
|
||||
if result_value.return_code == 0:
|
||||
retry_times = 2
|
||||
while retry_times > 0:
|
||||
command = "rfile user/log/screenshot.bin"
|
||||
if LiteUiKit.download_file(device, command, timeout=1400):
|
||||
img_dir = os.path.join(screen_path, "img")
|
||||
os.makedirs(img_dir, exist_ok=True)
|
||||
copy_file(img_dir, file_name)
|
||||
break
|
||||
retry_times -= 1
|
||||
LOG.info("screen shot success.")
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def ui_dump_id(device, **kwargs):
|
||||
args = kwargs
|
||||
node_id = args.get("node_id", "")
|
||||
timeout = args.get("timeout", "")
|
||||
dump_node_command = "%s%s" % ("uidump node ", str(node_id))
|
||||
dump_times = 5
|
||||
while dump_times > 0:
|
||||
dump_node_value = LiteUiKit.execute_hdc_cmd_with_timeout(
|
||||
device, get_hdc_command(dump_node_command), timeout, True)
|
||||
if dump_node_value.return_code == 0:
|
||||
result = filter_json(dump_node_value.return_message)
|
||||
if result != "no such node":
|
||||
return get_center(result, "id", node_id)
|
||||
dump_times -= 1
|
||||
LOG.error("dump fail, please check node id")
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def ui_dump_tree(device, **kwargs):
|
||||
args = kwargs
|
||||
node_id = args.get("node_id", "")
|
||||
timeout = args.get("timeout", "")
|
||||
if node_id:
|
||||
dump_tree_command = "%s%s" % ("uidump tree ", node_id)
|
||||
else:
|
||||
dump_tree_command = "uidump tree"
|
||||
dump_tree_value = LiteUiKit.execute_hdc_cmd_with_timeout(
|
||||
device, get_hdc_command(dump_tree_command), timeout, True)
|
||||
if dump_tree_value.return_code == 0:
|
||||
dump_str = ""
|
||||
from _core.environment.device_lite import get_hdc_path
|
||||
local_path = get_hdc_path()
|
||||
retry_times = 2
|
||||
while retry_times > 0:
|
||||
command = "rfile user/log/dump_dom_tree.json"
|
||||
if LiteUiKit.download_file(device, command, timeout=1400):
|
||||
with open(local_path, "r") as file_stream:
|
||||
dump_str = file_stream.read()
|
||||
break
|
||||
retry_times -= 1
|
||||
return dump_str
|
||||
else:
|
||||
LOG.error("dump failed")
|
||||
raise ParamError("dump failed")
|
||||
|
||||
@staticmethod
|
||||
def swipe(device, start_point, end_point, timeout=800):
|
||||
if not isinstance(start_point, tuple) and not isinstance(
|
||||
end_point, tuple):
|
||||
raise ParamError(
|
||||
"The coordinates of the sliding point should be tuple")
|
||||
start_x, start_y = start_point
|
||||
end_x, end_y = end_point
|
||||
press_first_command = "%s%s%s%s%s" % ("uievent ", str(start_x), " ",
|
||||
str(start_y), " PRESSDOWN")
|
||||
press_end_command = "%s%s%s%s" % ("uievent ", str(end_x), str(end_y),
|
||||
" PRESSDOWN")
|
||||
release_end_command = "%s%s%s%s%s" % ("uievent ", str(end_x), " ",
|
||||
str(end_y), " RELEASE")
|
||||
press_first_point_result = LiteUiKit.execute_hdc_cmd_with_timeout(
|
||||
device, get_hdc_command(press_first_command, True), timeout)
|
||||
if press_first_point_result.return_code == 0:
|
||||
time.sleep(timeout / 1000)
|
||||
press_medium_point_result = \
|
||||
LiteUiKit.execute_hdc_cmd_with_timeout(
|
||||
device, get_hdc_command(press_end_command, True),
|
||||
timeout, True)
|
||||
if press_medium_point_result.return_code == 0:
|
||||
time.sleep(timeout / 1000)
|
||||
press_end_point_result = \
|
||||
LiteUiKit.execute_hdc_cmd_with_timeout(
|
||||
device, get_hdc_command(release_end_command, True),
|
||||
timeout, True)
|
||||
if press_end_point_result.return_code == 0:
|
||||
LOG.info("swipe success.")
|
||||
return True
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def long_press(device, x_coordinate, y_coordinate, timeout=1200):
|
||||
press_command = "%s%s%s%s%s" % ("uievent ", str(x_coordinate), " ",
|
||||
str(y_coordinate), " PRESSDOWN")
|
||||
release_command = "%s%s%s%s%s" % ("uievent ", str(x_coordinate), " ",
|
||||
str(y_coordinate), " RELEASE")
|
||||
press_result = LiteUiKit.execute_hdc_cmd_with_timeout(
|
||||
device, get_hdc_command(press_command, True), timeout, True)
|
||||
if press_result.return_code == 0:
|
||||
time.sleep(timeout / 1000)
|
||||
release_result = LiteUiKit.execute_hdc_cmd_with_timeout(
|
||||
device, get_hdc_command(release_command, True), timeout, True)
|
||||
if release_result.return_code == 0:
|
||||
LOG.info("press success.")
|
||||
return True
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def download_file(device, command, timeout=1200):
|
||||
press_result = LiteUiKit.execute_hdc_cmd_with_timeout(
|
||||
device, command, timeout, True)
|
||||
if press_result.return_code == 0:
|
||||
LOG.info("download file success.")
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
class ResultValue(object):
|
||||
def __init__(self, return_code, error_message="", return_message=""):
|
||||
self.return_code = return_code
|
||||
self.error_message = error_message
|
||||
self.return_message = return_message
|
||||
|
||||
def set_return_code(self, ret_code):
|
||||
self.return_code = ret_code
|
||||
|
||||
def set_error_message(self, error_message):
|
||||
self.error_message = error_message
|
||||
|
||||
def set_return_message(self, return_message):
|
||||
self.return_message = return_message
|
||||
|
||||
def get_return_code(self):
|
||||
return self.return_code
|
||||
|
||||
def get_error_message(self):
|
||||
return self.error_message
|
||||
|
||||
def get_return_message(self):
|
||||
return self.return_message
|
@ -2,7 +2,7 @@
|
||||
# coding=utf-8
|
||||
|
||||
#
|
||||
# Copyright (c) 2020 Huawei Device Co., Ltd.
|
||||
# Copyright (c) 2020-2021 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
|
||||
@ -16,6 +16,7 @@
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
import copy
|
||||
import os
|
||||
import socket
|
||||
import time
|
||||
@ -25,15 +26,19 @@ import subprocess
|
||||
import signal
|
||||
import uuid
|
||||
import json
|
||||
import stat
|
||||
from tempfile import NamedTemporaryFile
|
||||
|
||||
from _core.executor.listener import SuiteResult
|
||||
from _core.driver.parser_lite import ShellHandler
|
||||
from _core.exception import ParamError
|
||||
from _core.exception import ExecuteTerminate
|
||||
from _core.logger import platform_logger
|
||||
from _core.report.suite_reporter import SuiteReporter
|
||||
from _core.plugin import get_plugin
|
||||
from _core.plugin import Plugin
|
||||
from _core.constants import ModeType
|
||||
from _core.constants import ConfigConst
|
||||
|
||||
LOG = platform_logger("Utils")
|
||||
|
||||
@ -95,31 +100,27 @@ def stop_standing_subprocess(process):
|
||||
|
||||
|
||||
def get_decode(stream):
|
||||
if not isinstance(stream, str) and not isinstance(stream, bytes):
|
||||
if isinstance(stream, str):
|
||||
return stream
|
||||
|
||||
if not isinstance(stream, bytes):
|
||||
return str(stream)
|
||||
|
||||
try:
|
||||
ret = stream.decode("utf-8", errors="ignore")
|
||||
except (ValueError, AttributeError, TypeError):
|
||||
ret = str(stream)
|
||||
else:
|
||||
try:
|
||||
ret = stream.decode("utf-8", errors="ignore")
|
||||
except (ValueError, AttributeError, TypeError):
|
||||
ret = str(stream)
|
||||
return ret
|
||||
|
||||
|
||||
def is_proc_running(pid, name=None):
|
||||
if platform.system() == "Windows":
|
||||
proc_sub = subprocess.Popen(["C:\\Windows\\System32\\tasklist"],
|
||||
stdout=subprocess.PIPE,
|
||||
shell=False)
|
||||
proc = subprocess.Popen(["C:\\Windows\\System32\\findstr", "%s" % pid],
|
||||
stdin=proc_sub.stdout,
|
||||
stdout=subprocess.PIPE, shell=False)
|
||||
list_command = ["C:\\Windows\\System32\\tasklist"]
|
||||
find_command = ["C:\\Windows\\System32\\findstr", "%s" % pid]
|
||||
else:
|
||||
proc_sub = subprocess.Popen(["/bin/ps", "-ef"],
|
||||
stdout=subprocess.PIPE,
|
||||
shell=False)
|
||||
proc = subprocess.Popen(["/bin/grep", "%s" % pid],
|
||||
stdin=proc_sub.stdout,
|
||||
stdout=subprocess.PIPE, shell=False)
|
||||
list_command = ["/bin/ps", "-ef"]
|
||||
find_command = ["/bin/grep", "%s" % pid]
|
||||
proc = _get_find_proc(find_command, list_command)
|
||||
(out, _) = proc.communicate()
|
||||
out = get_decode(out).strip()
|
||||
if out == "":
|
||||
@ -128,7 +129,15 @@ def is_proc_running(pid, name=None):
|
||||
return True if name is None else out.find(name) != -1
|
||||
|
||||
|
||||
def exec_cmd(cmd, timeout=5 * 60, error_print=True):
|
||||
def _get_find_proc(find_command, list_command):
|
||||
proc_sub = subprocess.Popen(list_command, stdout=subprocess.PIPE,
|
||||
shell=False)
|
||||
proc = subprocess.Popen(find_command, stdin=proc_sub.stdout,
|
||||
stdout=subprocess.PIPE, shell=False)
|
||||
return proc
|
||||
|
||||
|
||||
def exec_cmd(cmd, timeout=5 * 60, error_print=True, join_result=False):
|
||||
"""
|
||||
Executes commands in a new shell. Directing stderr to PIPE.
|
||||
|
||||
@ -139,35 +148,38 @@ def exec_cmd(cmd, timeout=5 * 60, error_print=True):
|
||||
cmd: A sequence of commands and arguments.
|
||||
timeout: timeout for exe cmd.
|
||||
error_print: print error output or not.
|
||||
|
||||
join_result: join error and out
|
||||
Returns:
|
||||
The output of the command run.
|
||||
"""
|
||||
|
||||
sys_type = platform.system()
|
||||
if sys_type == "Windows":
|
||||
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE, shell=False)
|
||||
elif sys_type == "Linux" or sys_type == "Darwin":
|
||||
if sys_type == "Linux" or sys_type == "Darwin":
|
||||
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE, shell=False,
|
||||
preexec_fn=os.setsid) # @UndefinedVariable
|
||||
preexec_fn=os.setsid)
|
||||
else:
|
||||
return
|
||||
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE, shell=False)
|
||||
try:
|
||||
(out, err) = proc.communicate(timeout=timeout)
|
||||
err = get_decode(err).strip()
|
||||
out = get_decode(out).strip()
|
||||
if err and error_print:
|
||||
LOG.exception(err)
|
||||
return err if err else out
|
||||
LOG.exception(err, exc_info=False)
|
||||
if join_result:
|
||||
return "%s\n %s" % (out, err) if err else out
|
||||
else:
|
||||
return err if err else out
|
||||
|
||||
except (TimeoutError, KeyboardInterrupt, AttributeError, ValueError):
|
||||
except (TimeoutError, KeyboardInterrupt, AttributeError, ValueError,
|
||||
EOFError, IOError):
|
||||
sys_type = platform.system()
|
||||
if sys_type == "Windows":
|
||||
os.kill(proc.pid, signal.SIGINT)
|
||||
elif sys_type == "Linux" or sys_type == "Darwin":
|
||||
if sys_type == "Linux" or sys_type == "Darwin":
|
||||
os.killpg(proc.pid, signal.SIGTERM)
|
||||
else:
|
||||
os.kill(proc.pid, signal.SIGINT)
|
||||
raise
|
||||
|
||||
|
||||
def create_dir(path):
|
||||
@ -182,15 +194,26 @@ def create_dir(path):
|
||||
|
||||
|
||||
def get_config_value(key, config_dict, is_list=True, default=None):
|
||||
"""get corresponding values for key in config_dict
|
||||
|
||||
Args:
|
||||
key: target key in config_dict
|
||||
config_dict: dictionary that store values
|
||||
is_list: decide return values is list type or not
|
||||
default: if key not in config_dict, default value will be returned
|
||||
|
||||
Returns:
|
||||
corresponding values for key
|
||||
"""
|
||||
if not isinstance(config_dict, dict):
|
||||
return default
|
||||
|
||||
value = config_dict.get(key, "")
|
||||
value = config_dict.get(key, None)
|
||||
if isinstance(value, bool):
|
||||
return value
|
||||
|
||||
if not value:
|
||||
if default:
|
||||
if value is None:
|
||||
if default is not None:
|
||||
return default
|
||||
return [] if is_list else ""
|
||||
|
||||
@ -200,28 +223,48 @@ def get_config_value(key, config_dict, is_list=True, default=None):
|
||||
|
||||
|
||||
def get_file_absolute_path(input_name, paths=None, alt_dir=None):
|
||||
"""find absolute path for input_name
|
||||
|
||||
Args:
|
||||
input_name: the target file to search
|
||||
paths: path list for searching input_name
|
||||
alt_dir: extra dir that appended to paths
|
||||
|
||||
Returns:
|
||||
absolute path for input_name
|
||||
"""
|
||||
input_name = str(input_name)
|
||||
abs_paths = set(paths) if paths else set()
|
||||
_update_paths(abs_paths)
|
||||
|
||||
for path in abs_paths:
|
||||
if alt_dir:
|
||||
file_path = os.path.join(path, alt_dir, input_name)
|
||||
_inputs = [input_name]
|
||||
if input_name.startswith("resource/"):
|
||||
_inputs.append(input_name.replace("resource/", "", 1))
|
||||
elif input_name.startswith("testcases/"):
|
||||
_inputs.append(input_name.replace("testcases/", "", 1))
|
||||
|
||||
for _input in _inputs:
|
||||
for path in abs_paths:
|
||||
if alt_dir:
|
||||
file_path = os.path.join(path, alt_dir, _input)
|
||||
if os.path.exists(file_path):
|
||||
return os.path.abspath(file_path)
|
||||
|
||||
file_path = os.path.join(path, _input)
|
||||
if os.path.exists(file_path):
|
||||
return os.path.abspath(file_path)
|
||||
|
||||
file_path = os.path.join(path, input_name)
|
||||
if os.path.exists(file_path):
|
||||
return os.path.abspath(file_path)
|
||||
|
||||
err_msg = "The file {} does not exist".format(input_name)
|
||||
LOG.error(err_msg)
|
||||
if check_mode(ModeType.decc):
|
||||
LOG.error(err_msg, error_no="00109")
|
||||
err_msg = "Load Error[00109]"
|
||||
|
||||
if alt_dir:
|
||||
LOG.debug("alt_dir is %s" % alt_dir)
|
||||
LOG.debug("paths is:")
|
||||
for path in abs_paths:
|
||||
LOG.debug(path)
|
||||
raise ParamError(err_msg)
|
||||
raise ParamError(err_msg, error_no="00109")
|
||||
|
||||
|
||||
def _update_paths(paths):
|
||||
@ -267,7 +310,9 @@ def modify_props(device, local_prop_file, target_prop_file, new_props):
|
||||
old_props = {}
|
||||
changed_prop_key = []
|
||||
lines = []
|
||||
with open(local_prop_file, 'r') as old_file:
|
||||
flags = os.O_RDONLY
|
||||
modes = stat.S_IWUSR | stat.S_IRUSR
|
||||
with os.fdopen(os.open(local_prop_file, flags, modes), "r") as old_file:
|
||||
lines = old_file.readlines()
|
||||
if lines:
|
||||
lines[-1] = lines[-1] + '\n'
|
||||
@ -304,25 +349,42 @@ def modify_props(device, local_prop_file, target_prop_file, new_props):
|
||||
return is_changed
|
||||
|
||||
|
||||
def get_device_log_file(report_path, serial=None, log_name="device_log"):
|
||||
def get_device_log_file(report_path, serial=None, log_name="device_log",
|
||||
device_name=""):
|
||||
from xdevice import Variables
|
||||
log_path = os.path.join(report_path, Variables.report_vars.log_dir)
|
||||
os.makedirs(log_path, exist_ok=True)
|
||||
|
||||
serial = serial or time.time_ns()
|
||||
device_file_name = "{}_{}.log".format(log_name, serial)
|
||||
if device_name:
|
||||
serial = "%s_%s" % (device_name, serial)
|
||||
device_file_name = "{}_{}.log".format(log_name, str(serial).replace(
|
||||
":", "_"))
|
||||
device_log_file = os.path.join(log_path, device_file_name)
|
||||
LOG.info("generate device log file: %s", device_log_file)
|
||||
return device_log_file
|
||||
|
||||
|
||||
def check_result_report(report_path, report_file, error_message="",
|
||||
report_name=""):
|
||||
def check_result_report(report_root_dir, report_file, error_message="",
|
||||
report_name="", module_name=""):
|
||||
"""
|
||||
check whether report_file exits or not. if report_file is not exist,
|
||||
create empty report with error_message under report_root_dir
|
||||
"""
|
||||
|
||||
if os.path.exists(report_file):
|
||||
return report_file
|
||||
result_dir = os.path.join(report_path, "result")
|
||||
report_dir = os.path.dirname(report_file)
|
||||
if os.path.isabs(report_dir):
|
||||
result_dir = report_dir
|
||||
else:
|
||||
result_dir = os.path.join(report_root_dir, "result", report_dir)
|
||||
os.makedirs(result_dir, exist_ok=True)
|
||||
LOG.error("report %s not exist, create empty report under %s" % (
|
||||
report_file, result_dir))
|
||||
if check_mode(ModeType.decc):
|
||||
LOG.error("report not exist, create empty report")
|
||||
else:
|
||||
LOG.error("report %s not exist, create empty report under %s" % (
|
||||
report_file, result_dir))
|
||||
|
||||
suite_name = report_name
|
||||
if not suite_name:
|
||||
@ -330,12 +392,28 @@ def check_result_report(report_path, report_file, error_message="",
|
||||
suite_result = SuiteResult()
|
||||
suite_result.suite_name = suite_name
|
||||
suite_result.stacktrace = error_message
|
||||
if module_name:
|
||||
suite_name = module_name
|
||||
suite_reporter = SuiteReporter([(suite_result, [])], suite_name,
|
||||
result_dir)
|
||||
result_dir, modulename=module_name)
|
||||
suite_reporter.create_empty_report()
|
||||
return "%s.xml" % os.path.join(result_dir, suite_name)
|
||||
|
||||
|
||||
def get_sub_path(test_suite_path):
|
||||
pattern = "%stests%s" % (os.sep, os.sep)
|
||||
file_dir = os.path.dirname(test_suite_path)
|
||||
pos = file_dir.find(pattern)
|
||||
if -1 == pos:
|
||||
return ""
|
||||
|
||||
sub_path = file_dir[pos + len(pattern):]
|
||||
pos = sub_path.find(os.sep)
|
||||
if -1 == pos:
|
||||
return ""
|
||||
return sub_path[pos + len(os.sep):]
|
||||
|
||||
|
||||
def is_config_str(content):
|
||||
return True if "{" in content and "}" in content else False
|
||||
|
||||
@ -346,8 +424,9 @@ def get_version():
|
||||
ver_file_path = os.path.join(Variables.res_dir, 'version.txt')
|
||||
if not os.path.isfile(ver_file_path):
|
||||
return ver
|
||||
|
||||
with open(ver_file_path, mode='r', encoding='UTF-8') as ver_file:
|
||||
flags = os.O_RDONLY
|
||||
modes = stat.S_IWUSR | stat.S_IRUSR
|
||||
with os.fdopen(os.open(ver_file_path, flags, modes), "r") as ver_file:
|
||||
line = ver_file.readline()
|
||||
if '-v' in line:
|
||||
ver = line.strip().split('-')[1]
|
||||
@ -370,17 +449,19 @@ def convert_ip(origin_ip):
|
||||
|
||||
|
||||
def convert_port(port):
|
||||
if len(port) >= 2:
|
||||
return "{}**{}".format(port[0], port[-1])
|
||||
_port = str(port)
|
||||
if len(_port) >= 2:
|
||||
return "{}{}{}".format(_port[0], "*" * (len(_port) - 2), _port[-1])
|
||||
else:
|
||||
return "{}**".format(port)
|
||||
return "*{}".format(_port[-1])
|
||||
|
||||
|
||||
def convert_serial(serial):
|
||||
if serial.startswith("local_"):
|
||||
return "local_{}".format('*'*(len(serial)-6))
|
||||
return serial
|
||||
elif serial.startswith("remote_"):
|
||||
return "remote_{}".format(convert_ip(serial.split("_")[1]))
|
||||
return "remote_{}_{}".format(convert_ip(serial.split("_")[1]),
|
||||
convert_port(serial.split("_")[-1]))
|
||||
else:
|
||||
length = len(serial)//3
|
||||
return "{}{}{}".format(
|
||||
@ -402,25 +483,45 @@ def get_shell_handler(request, parser_type):
|
||||
return handler
|
||||
|
||||
|
||||
def get_kit_instances(json_config, resource_path, testcases_path):
|
||||
def get_kit_instances(json_config, resource_path="", testcases_path=""):
|
||||
from _core.testkit.json_parser import JsonParser
|
||||
kit_instances = []
|
||||
|
||||
# check input param
|
||||
if not isinstance(json_config, JsonParser):
|
||||
return kit_instances
|
||||
|
||||
# get kit instances
|
||||
for kit in json_config.config.kits:
|
||||
kit["paths"] = [resource_path, testcases_path]
|
||||
kit_type = kit.get("type", "")
|
||||
device_name = kit.get("device_name", None)
|
||||
if get_plugin(plugin_type=Plugin.TEST_KIT, plugin_id=kit_type):
|
||||
test_kit = \
|
||||
get_plugin(plugin_type=Plugin.TEST_KIT, plugin_id=kit_type)[0]
|
||||
test_kit_instance = test_kit.__class__()
|
||||
test_kit_instance.__check_config__(kit)
|
||||
setattr(test_kit_instance, "device_name", device_name)
|
||||
kit_instances.append(test_kit_instance)
|
||||
else:
|
||||
raise ParamError("kit %s not exists" % kit_type)
|
||||
raise ParamError("kit %s not exists" % kit_type, error_no="00107")
|
||||
return kit_instances
|
||||
|
||||
|
||||
def check_device_name(device, kit, step="setup"):
|
||||
kit_device_name = getattr(kit, "device_name", None)
|
||||
device_name = device.get("name")
|
||||
if kit_device_name and device_name and \
|
||||
kit_device_name != device_name:
|
||||
return False
|
||||
if kit_device_name and device_name:
|
||||
LOG.debug("do kit:%s %s for device:%s",
|
||||
kit.__class__.__name__, step, device_name)
|
||||
else:
|
||||
LOG.debug("do kit:%s %s", kit.__class__.__name__, step)
|
||||
return True
|
||||
|
||||
|
||||
def check_path_legal(path):
|
||||
if path and " " in path:
|
||||
return "\"%s\"" % path
|
||||
@ -458,6 +559,8 @@ def get_local_ip():
|
||||
else:
|
||||
local_ip = "127.0.0.1"
|
||||
return local_ip
|
||||
else:
|
||||
return "127.0.0.1"
|
||||
|
||||
|
||||
class SplicingAction(argparse.Action):
|
||||
@ -466,16 +569,55 @@ class SplicingAction(argparse.Action):
|
||||
|
||||
|
||||
def get_test_component_version(config):
|
||||
if check_mode(ModeType.decc):
|
||||
return ""
|
||||
|
||||
try:
|
||||
paths = [config.resource_path, config.testcases_path]
|
||||
test_file = get_file_absolute_path("test_component.json", paths)
|
||||
|
||||
with open(test_file, encoding="utf-8") as file_content:
|
||||
flags = os.O_RDONLY
|
||||
modes = stat.S_IWUSR | stat.S_IRUSR
|
||||
with os.fdopen(os.open(test_file, flags, modes), "r") as file_content:
|
||||
json_content = json.load(file_content)
|
||||
version = json_content.get("version", "")
|
||||
return version
|
||||
except (ParamError, ValueError) as error:
|
||||
LOG.error(
|
||||
"The exception {} happened when get version".format(
|
||||
error))
|
||||
LOG.error("The exception {} happened when get version".format(error))
|
||||
return ""
|
||||
|
||||
|
||||
def check_mode(mode):
|
||||
from xdevice import Scheduler
|
||||
return Scheduler.mode == mode
|
||||
|
||||
|
||||
def do_module_kit_setup(request, kits):
|
||||
for device in request.get_devices():
|
||||
setattr(device, ConfigConst.module_kits, [])
|
||||
|
||||
from xdevice import Scheduler
|
||||
for kit in kits:
|
||||
run_flag = False
|
||||
for device in request.get_devices():
|
||||
if not Scheduler.is_execute:
|
||||
raise ExecuteTerminate()
|
||||
if check_device_name(device, kit):
|
||||
run_flag = True
|
||||
kit_copy = copy.deepcopy(kit)
|
||||
module_kits = getattr(device, ConfigConst.module_kits)
|
||||
module_kits.append(kit_copy)
|
||||
kit_copy.__setup__(device, request=request)
|
||||
if not run_flag:
|
||||
kit_device_name = getattr(kit, "device_name", None)
|
||||
error_msg = "device name '%s' of '%s' not exist" % (
|
||||
kit_device_name, kit.__class__.__name__)
|
||||
LOG.error(error_msg, error_no="00108")
|
||||
raise ParamError(error_msg, error_no="00108")
|
||||
|
||||
|
||||
def do_module_kit_teardown(request):
|
||||
for device in request.get_devices():
|
||||
for kit in getattr(device, ConfigConst.module_kits, []):
|
||||
if check_device_name(device, kit, step="teardown"):
|
||||
kit.__teardown__(device)
|
||||
setattr(device, ConfigConst.module_kits, [])
|
||||
|
@ -2,7 +2,7 @@
|
||||
# coding=utf-8
|
||||
|
||||
#
|
||||
# Copyright (c) 2020 Huawei Device Co., Ltd.
|
||||
# Copyright (c) 2020-2021 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
|
||||
@ -23,12 +23,16 @@ from dataclasses import dataclass
|
||||
__all__ = ["Variables"]
|
||||
|
||||
SRC_DIR = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
|
||||
SRC_ADAPTER_DIR = os.path.abspath(os.path.join(SRC_DIR, "adapter"))
|
||||
MODULES_DIR = os.path.abspath(os.path.dirname(__file__))
|
||||
TOP_DIR = os.path.abspath(
|
||||
os.path.dirname(os.path.dirname(os.path.dirname(__file__))))
|
||||
TOP_ADAPTER_DIR = os.path.abspath(os.path.join(TOP_DIR, "adapter"))
|
||||
sys.path.insert(0, SRC_DIR)
|
||||
sys.path.insert(1, MODULES_DIR)
|
||||
sys.path.insert(2, TOP_DIR)
|
||||
sys.path.insert(3, SRC_ADAPTER_DIR)
|
||||
sys.path.insert(4, TOP_ADAPTER_DIR)
|
||||
|
||||
|
||||
@dataclass
|
||||
@ -39,6 +43,7 @@ class ReportVariables:
|
||||
log_level = ""
|
||||
log_handler = ""
|
||||
pub_key_file = None
|
||||
pub_key_string = ""
|
||||
|
||||
|
||||
@dataclass
|
||||
@ -65,8 +70,8 @@ def _init_global_config():
|
||||
# set report variables
|
||||
Variables.report_vars.log_dir = "log"
|
||||
Variables.report_vars.report_dir = "reports"
|
||||
Variables.report_vars.log_format = "%(asctime)s %(name)-15s " \
|
||||
"%(levelname)-8s %(message)s"
|
||||
Variables.report_vars.log_format = "[%(asctime)s] [%(name)s] " \
|
||||
"[%(levelname)s] %(message)s"
|
||||
Variables.report_vars.log_level = logging.INFO
|
||||
Variables.report_vars.log_handler = "console, file"
|
||||
|
||||
@ -111,7 +116,8 @@ def _init_logger():
|
||||
|
||||
tool_log_file = None
|
||||
if Variables.exec_dir and os.path.normcase(
|
||||
Variables.exec_dir) == os.path.normcase(Variables.top_dir):
|
||||
Variables.exec_dir) == os.path.normcase(Variables.top_dir) and \
|
||||
not hasattr(sys, "decc_mode"):
|
||||
host_log_path = os.path.join(Variables.exec_dir,
|
||||
Variables.report_vars.report_dir,
|
||||
Variables.report_vars.log_dir)
|
||||
|
Loading…
Reference in New Issue
Block a user