update openharmony 1.0.1

This commit is contained in:
mamingshuai 2021-03-11 18:42:50 +08:00
parent 9d2c809306
commit c2c469f6ec
69 changed files with 5378 additions and 2493 deletions

View File

@ -1,13 +0,0 @@
### 该问题是怎么引起的?
### 重现步骤
### 报错信息

View File

@ -1,15 +0,0 @@
### 相关的Issue
### 原因(目的、解决的问题等)
### 描述(做了什么,变更了什么)
### 测试用例(新增、改动、可能影响的功能)

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*.pyc
/extension/

35
BUILD.gn Executable file → Normal file
View 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"
}

0
LICENSE Executable file → Normal file
View File

227
README.md Executable file
View 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
View 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.1port为本机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
View 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.

View 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.
#

View 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
View 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
View 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
View 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": ""
}
}
]
}

View File

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 580 B

BIN
figures/icon-danger.gif Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 580 B

BIN
figures/icon-note.gif Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 394 B

BIN
figures/icon-notice.gif Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 406 B

BIN
figures/icon-tip.gif Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 253 B

BIN
figures/icon-warning.gif Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 580 B

17
lite/BUILD.gn Normal file
View 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"
}

View File

@ -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
View File

@ -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
View File

@ -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 "$@"

View File

@ -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,
)

View 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
@ -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()

View 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

0
src/xdevice/_core/__init__.py Executable file → Normal file
View File

0
src/xdevice/_core/build/__init__.py Executable file → Normal file
View File

0
src/xdevice/_core/command/__init__.py Executable file → Normal file
View File

View 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
View File

0
src/xdevice/_core/config/__init__.py Executable file → Normal file
View File

View 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

View 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
@ -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

View 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,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
View File

View 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

View 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
@ -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
View File

View 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
}

View 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,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, ""

View 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,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"

View 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
@ -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,

View 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,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
View File

View 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):

View 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,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:

View 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
@ -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)

View 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 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

View 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,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

View 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,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

View 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,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

View 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
@ -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
View File

View 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__":

View 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,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

View 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
@ -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("<", "&lt;"). \
replace(">", "&gt;").replace("\\r\\n", "<br/>"). \
replace("\\n", "<br/>").replace("\n", "<br/>")
replace("\\n", "<br/>").replace("\n", "<br/>"). \
replace(" ", "&nbsp;")
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)

View 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
@ -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]

View 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,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

View File

@ -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
View 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
View File

View 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

View 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
@ -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("]"))

View File

@ -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

View 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 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, [])

View 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
@ -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)