app自动化

from django.contrib.auth.decorators import login_required
from django.http import JsonResponse
from django.shortcuts import render
from request.app_ui_zhh.model.app_ui_model import AppUiTestCase, AppPackageInfo, AppDeviceInfo, AppReport
from request.app_ui_zhh.service.common import up_app_ui_case, add_device_info, add_package_info, delete_package_info, delete_device_info
from request.app_ui_zhh.app_ui_test_main import execute_test_process
from request.app_ui_zhh.common.logger import CLogger
from request.app_ui_zhh.config.config import LOG_LEVEL


log = CLogger(LOG_LEVEL)


@login_required
def app_execute_ui_task(request):
    # 执行测试用例的入口
    platform = request.POST.get("platform")
    project = request.POST.get("project")
    product = request.POST.get("product")
    device_name = request.POST.get("device_name")
    app_name = request.POST.get("app_name")
    if platform is None or platform == "" or project is None or project == "" or product is None or product == "" or device_name is None or device_name == "" or app_name is None or app_name == "":
        log.error("获取数据错误,platform={}, project={}, product={}, device_name={}, app_name={}!".format(platform, project, product, device_name, app_name))
        return JsonResponse({'result': 'false'})
    if execute_test_process(platform=platform, project=project, product=product, device_name=device_name, app_name=app_name) is False:
        return JsonResponse({'result': 'false'})
    return JsonResponse({'result': 'ok'})


@login_required
def app_add_device_info(request):
    device_sn = request.POST.get("device_sn")
    device_name = request.POST.get("device_name")
    platform_name = request.POST.get("platform_name")
    platform_version = request.POST.get("platform_version")
    version = request.POST.get("version")
    server = request.POST.get("server")
    remarks = request.POST.get("remarks")
    if device_sn is None or device_sn == "" or device_name is None or device_name == "" or platform_name is None or platform_name == "" or server is None or server == "":
        log.error("获取数据错误,device_sn={}, device_name={}, platform_name={}, server={}!".format(device_sn, device_name, platform_name, server))
        return JsonResponse({'result': 'false'})
    add_device_info(device_sn, device_name, platform_name, platform_version, version, server, remarks)
    return JsonResponse({'result': 'ok'})


@login_required
def app_add_package_info(request):
    app_name = request.POST.get("app_name")
    platform = request.POST.get("platform")
    package_name = request.POST.get("package_name")
    activity_name = request.POST.get("activity_name")
    remarks = request.POST.get("remarks")
    if platform == "Android":
        if app_name is None or app_name == "" or package_name is None or package_name == "" or activity_name is None or activity_name == "":
            log.error("获取数据错误,app_name={}, platform={}, package_name={}, activity_name={}!".format(app_name, platform, package_name, activity_name))
            return JsonResponse({'result': 'false'})
    elif platform == "IOS":
        if app_name is None or app_name == "" or package_name is None or package_name == "":
            log.error("获取数据错误,app_name={}, platform={}, package_name={}!".format(app_name, platform, package_name))
            return JsonResponse({'result': 'false'})
    else:
        return JsonResponse({'result': 'false'})
    add_package_info(app_name, platform, package_name, activity_name, remarks)
    return JsonResponse({'result': 'ok'})


@login_required
def app_delete_package_info(request):
    id = request.POST.get("id")
    if id is None or id == "":
        log.error("获取数据错误,id={}!".format(id))
        return JsonResponse({'result': 'false'})
    delete_package_info(id)
    return JsonResponse({'result': 'ok'})


@login_required
def app_delete_device_info(request):
    id = request.POST.get("id")
    if id is None or id == "":
        log.error("获取数据错误,id={}!".format(id))
        return JsonResponse({'result': 'false'})
    delete_device_info(id)
    return JsonResponse({'result': 'ok'})


@login_required
def app_get_ui_case_html(request):
    # 跳转到app测试用例页面
    platform = request.GET.get("platform")
    return render(request, "app_ui_case.html", {"platform": platform})


@login_required
def app_get_ui_case(request):
    # 查询用例展示在页面的接口
    platform = request.POST.get("platform")
    project = request.POST.get("project")
    product = request.POST.get("product")
    if platform is None or platform == "" or project is None or project == "" or product is None or product == "":
        return JsonResponse({"tasks": [], "platform": platform, "project": project, "product": product})
    else:
        tasks = list(AppUiTestCase.objects.all().filter(platform=platform, project=project, product=product).values())
        return JsonResponse({"tasks": tasks, "platform": platform, "project": project, "product": product})


@login_required
def app_up_ui_case(request):
    # 上传Excel测试用例到写入数据库
    try:
        if request.method == "POST":
            file_contents = request.FILES.get('fileContent')
            if up_app_ui_case(file_contents) is False:
                raise ImportError("提交失败")
            return JsonResponse({'result': 'ok'})
    except Exception:
        raise ImportError("提交失败")


@login_required
def app_get_device_type(request):
    book_list = AppUiTestCase.objects.values("platform").distinct()
    results = []
    for temp in book_list:
        results.append(temp["platform"])
    data = {'data': results}
    return JsonResponse(data)


@login_required
def app_get_up_ui(request):
    return render(request, "app_ui_case_up.html")


@login_required
def app_get_app_info_html(request):
    return render(request, "app_package_info.html")


@login_required
def app_get_app_info(request):
    # 查询app信息展示在页面
    if request.method == "GET":
        info_data = list(AppPackageInfo.objects.all().values())
    elif request.method == "POST":
        platform = request.POST.get("platform")
        info_data = list(AppPackageInfo.objects.all().filter(platform=platform).values())
    else:
        return JsonResponse({"info_data": []})
    return JsonResponse({"info_data": info_data})


@login_required
def app_get_device_info_html(request):
    return render(request, "app_device_info.html")


@login_required
def app_get_device_info(request):
    # 查询设备信息展示在页面
    if request.method == "GET":
        info_data = list(AppDeviceInfo.objects.all().values())
    elif request.method == "POST":
        platform = request.POST.get("platform")
        info_data = list(AppDeviceInfo.objects.all().filter(platform_name=platform).values())
    else:
        return JsonResponse({"info_data": []})
    return JsonResponse({"info_data": info_data})


@login_required
def app_delete_ui_case(request):
    platform = request.POST.get("platform")
    project = request.POST.get("project")
    product = request.POST.get("product")
    if platform is None or platform == "" or project is None or project == "" or product is None or product == "":
        return JsonResponse({'result': 'false'})
    try:
        AppUiTestCase.objects.filter(platform=platform, project=project, product=product).delete()
    except Exception:
        raise ImportError("删除失败")
    return JsonResponse({'result': 'ok'})


@login_required
def app_get_report_html(request):
    return render(request, "app_report.html")


@login_required
def app_get_report_data(request):
    # 查询app信息展示在页面
    platform_name = request.GET.get("platform_name")
    if platform_name is not None and platform_name != "":
        report_data = list(AppReport.objects.all().filter(platform_name=platform_name).order_by("-id").values())
    else:
        report_data = list(AppReport.objects.all().order_by("-id").values())

    return JsonResponse({"report_data": report_data})
"""
Excel的读写方法
"""

import xlrd
import xlwt
from xlutils.copy import copy
import os


def write_excel(file_name, sheet_name, row, col, data):
    """
    写一个单元格数据。
    :param file_name: 需要写入的文件名称,如不存在会新建一个,字符串
    :param sheet_name: 需要写入的sheet页名称,如不存在会新建一个,字符串
    :param row: 需要写入的行,unsignedint
    :param col: 需要写入的列,unsignedint
    :param data: 需要写入的数据,字符串
    :return:
    """
    if os.access(file_name, os.R_OK):
        old_wb = xlrd.open_workbook(file_name)          # 打开已存在的表
        sheets = old_wb.sheet_names()
        new_wb = copy(old_wb)                           # 复制
        if sheet_name not in sheets:
            new_ws = new_wb.add_sheet(sheet_name, cell_overwrite_ok=True)
        else:
            new_ws = new_wb.get_sheet(sheets.index(sheet_name))
        new_ws.write(row, col, data)                    # 写入单元格数据
        new_wb.save(file_name)                          # 保存
    else:
        f, sheet = add_excel_sheet(sheet_name)
        my_style = set_style('Times New Roman', 220)
        sheet.write(row, col, data, my_style)
        excel_close(f, file_name)


def set_style(name, height, bold=False):
    """
    设置表格字体样式。
    :param name: 字体名称
    :param height: 字体大小
    :param bold:  是否加粗
    :return:  返回字体格式类。
    """
    style = xlwt.XFStyle()
    font = xlwt.Font()
    font.name = name
    font.bold = bold
    font.color_index = 4
    font.height = height
    style.font = font
    return style


def add_excel_sheet(sheet_name):
    """
    在Excel中创建新sheet。
    :param sheet_name: sheet名称。
    :return: 返回Excel和sheet的对象
    """
    f = xlwt.Workbook()
    sheet = f.add_sheet(sheet_name, cell_overwrite_ok=True)

    return f, sheet


def write_new_excel(sheet, row_num, row):
    """
    写一个sheet页面,如果sheet页面中有内容,会将原有内容覆盖。
    :param sheet: sheet对象
    :param row_num: 从第几行开始写,第一行为0
    :param row: 写的内容,是一个数组,按顺序写入单元格,[(),()...]
    :return:
    """
    i = 0
    j = 0
    my_style = set_style('Times New Roman', 220)
    while i < len(row):
        while j < len(row[i]):
            sheet.write(row_num + i, j, row[i][j], my_style)
            j = j + 1
        i = i + 1


def excel_close(f, file_name):
    """
    保存并关闭Excel。
    :param f: Excel的对象
    :param file_name: 文件名称。
    :return: 无
    """
    f.save(file_name)


def read_excel(file_name, sheet_name, row=-1, col=-1):
    """
    读取Excel文件内容。
    :param file_name: 需要读取的Excel文件名称,字符串
    :param sheet_name: 需要读取的Excel中的sheet名称,字符串
    :param row: 需要读取的行,row与col都为-1表示读取整个sheet,row为-1且col为正数表示读取整列数据,row为正数且col为-1表示读取整行数据
    :param col: 参考row
    :return: 返回读取的内容,列表
    """
    if os.access(file_name, os.R_OK):
        wb = xlrd.open_workbook(filename=file_name)
    else:
        print('Exception occurred: ' + "文件不存在!")
        return False
    try:
        sheet = wb.sheet_by_name(sheet_name)
    except xlrd.XLRDError:
        print('Exception occurred: ' + "sheet_name不存在!")
        return False
    rows = sheet.nrows
    cols = sheet.ncols
    data_values = []
    if rows < 1 or cols < 1:
        print('Exception occurred: ' + "sheet中无数据!")
        return False
    if row >= rows or col >= cols:
        print('Exception occurred: ' + "获取的内容超过sheet页中的内容行数或列数")
        return False
    if row == -1 and col == -1:                         # 获取整个sheet页的内容
        for i in range(rows):
            data_values.append(sheet.row_values(i))
    elif row == -1 and col > -1:                        # 获取sheet页的某列数据
        data_values = sheet.col_values(col)
    elif row > -1 and col == -1:                        # 获取sheet页的某行数据
        data_values = sheet.row_values(row)
    elif row > -1 and col > -1:                         # 获取sheet页的某个单元格数据
        data_values = sheet.cell_value(row, col)
    else:
        return False

    return data_values


def get_excel_sheet(file_name):
    """
    读取Excel中的所有sheet名称。
    :param file_name: 需要读取的Excel文件名称,字符串
    :return: 返回获取的sheet名称
    """
    wb = xlrd.open_workbook(filename=file_name)
    return wb.sheet_names()


def read_excel_from_contents(file_contents=None):
    """
    读取Excel。
    """
    data = xlrd.open_workbook(filename=None, file_contents=file_contents)
    return data
"""
定义全局变量,使用方法:
引用:import global_env
设置:global_env.set_global_value('env_data', 0)
获取:global_env.get_global_value('env_data')
删除变量:global_env.get_global_value('del_global_env')
如果变量是字典,删除字典的某个键值对:global_env.get_global_value('del_global_env', "key")
"""

_global_dict = {}


def global_init():
    global _global_dict


def set_global_value(key, value):
    _global_dict[key] = value
    return True


def get_global_value(key):
    if key in _global_dict:
        return _global_dict[key]
    else:
        return None


def del_global_env(key, value=None):
    if isinstance(key, dict) and value is not None:
        if value in _global_dict[key]:
            del _global_dict[key][value]
    else:
        del _global_dict[key]

    return True
import cv2
from shutil import copy
import os


def img_resize(image, ratio):
    image_path = os.path.join(os.path.dirname(image), "image_origin")
    copy(image, image_path)
    img = cv2.imread(image)
    height, width = img.shape[0], img.shape[1]
    width_new = int(width * ratio)
    height_new = int(height * ratio)
    img_new = cv2.resize(img, (width_new, height_new))
    cv2.imwrite(image, img_new)
import os
import sys
import time
import logging
from logging.handlers import TimedRotatingFileHandler
from request.app_ui_zhh.common.singleton import Singleton
import datetime
from request.app_ui_zhh.config.config import LOG_CONSOLE


@Singleton
class CLogger:
    def __init__(self, set_level="Info",
                 name=os.path.split(os.path.splitext(sys.argv[0])[0])[-1],
                 log_name=time.strftime("%Y-%m-%d.log", time.localtime()),
                 log_path=os.path.join(os.path.dirname(os.path.dirname(__file__)), "log"),
                 use_console=True):

        """
        :param set_level: 日志级别["NOTSET"|"DEBUG"|"INFO"|"WARNING"|"ERROR"|"CRITICAL"],默认为INFO
        :param name: 日志中打印的name,默认为运行程序的name
        :param log_name: 日志文件的名字,默认为当前时间(年-月-日 时:分.log)
        :param log_path: 日志文件夹的路径,默认为logger.py同级目录中的log文件夹
        :param use_console: 是否在控制台打印,默认为True
        """

        if not set_level:
            set_level = self._exec_type()
        self.__logger = logging.getLogger(name)
        self.setLevel(getattr(logging, set_level.upper()) if hasattr(logging, set_level.upper()) else logging.INFO)         # 设置日志级别
        if not os.path.exists(log_path):
            os.makedirs(log_path)
        formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")                               # 设置日志格式
        handler_list = list()
        logfile_name = os.path.join(log_path, "app_ui_test.log")                                                           # 设置日志名称
        log_handler = logging.handlers.TimedRotatingFileHandler(logfile_name, when='midnight', interval=1, backupCount=0, encoding="UTF-8",    # backupCount为0表示不限制文件个数
                                                                atTime=datetime.datetime(2022, 1, 1, hour=0, minute=0, second=0, microsecond=0))
        handler_list.append(log_handler)                        # 日志根据时间分文件,不限制文件个数,根据atTime的0点整进行分文件,atTime中的年月日是忽略掉的,只需设置时分秒毫秒
        if use_console and LOG_CONSOLE:                         # 如果是使用终端输出,在文件输出的同时会输出到控制台
            handler_list.append(logging.StreamHandler())
        for handler in handler_list:
            handler.setFormatter(formatter)
            self.addHandler(handler)

    @staticmethod
    def _exec_type():
        return "DEBUG" if os.environ.get("IPYTHONENABLE") else "INFO"

    def __getattr__(self, item):
        return getattr(self.logger, item)

    @property
    def logger(self):
        return self.__logger

    @logger.setter
    def logger(self, func):
        self.__logger = func
class Singleton:
    """
    单例装饰器。
    """
    __cls = dict()

    def __init__(self, cls):
        self.__key = cls

    def __call__(self, *args, **kwargs):
        if self.__key not in self.cls:
            self[self.__key] = self.__key(*args, **kwargs)
        return self[self.__key]

    def __setitem__(self, key, value):
        self.cls[key] = value

    def __getitem__(self, item):
        return self.cls[item]

    @property
    def cls(self):
        return self.__cls

    @cls.setter
    def cls(self, cls):
        self.__cls = cls
"""
本模块是Yaml文件的读写方法。
"""
import yaml


class CYaml(object):
    def __init__(self, yaml_path):
        """
        :param yaml_path: Yaml文件的路径,字符串
        """
        self.yaml_path = yaml_path

    def read_yaml(self):
        """
        读Yaml文件的数据。
        :return: 返回读取的数据,字典
        """
        with open(self.yaml_path, 'r', encoding='utf-8') as fp_yaml:
            yaml_data_str = fp_yaml.read()
            yaml_data_dict = yaml.safe_load(yaml_data_str)
        return yaml_data_dict

    def write_yaml(self, yaml_write_data):
        """
        写Yaml文件。
        :param yaml_write_data: 需要写入的数据,字典
        :return: 无
        """
        with open(self.yaml_path, 'w') as fp_yaml:
            yaml.dump(yaml_write_data, fp_yaml, default_flow_style=False)
from request.app_ui_zhh.common import yml_rw
import os


def get_sys_config_from_yml():
    """
    获取yml配置。
    :return: 配置信息,字典
    """
    pro_base_dir = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
    config_yml_path = os.path.join(pro_base_dir, "config/config.yml")

    config_obj = yml_rw.CYaml(config_yml_path)
    config_data = config_obj.read_yaml()

    log_console = True
    log_level = "info"
    report_type = 0
    data_type = 1
    report_save_path = ""
    airtest_threshold = 0.7
    airtest_find_timeout = 20
    if"SYSTEM" in config_data:
        if "LOG_CONSOLE" in config_data["SYSTEM"]:
            log_console = config_data["SYSTEM"]["LOG_CONSOLE"]
        if "LOG_LEVEL" in config_data["SYSTEM"]:
            log_level = config_data["SYSTEM"]["LOG_LEVEL"]
        if "REPORT_TYPE" in config_data["SYSTEM"]:
            report_type = config_data["SYSTEM"]["REPORT_TYPE"]
        if "DATA_TYPE" in config_data["SYSTEM"]:
            data_type = config_data["SYSTEM"]["DATA_TYPE"]
        if "REPORT_SAVE_PATH" in config_data["SYSTEM"]:
            report_save_path = config_data["SYSTEM"]["REPORT_SAVE_PATH"]
        if "AIRTEST_THRESHOLD" in config_data["SYSTEM"]:
            airtest_threshold = config_data["SYSTEM"]["AIRTEST_THRESHOLD"]
        if "AIRTEST_FIND_TIMEOUT" in config_data["SYSTEM"]:
            airtest_find_timeout = config_data["SYSTEM"]["AIRTEST_FIND_TIMEOUT"]

    return log_console, log_level, report_type, data_type, report_save_path, airtest_threshold, airtest_find_timeout


def get_test_config_from_yml():
    """
    获取yml配置。
    :return: 配置信息,字典
    """
    pro_base_dir = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
    config_yml_path = os.path.join(pro_base_dir, "config/config.yml")

    config_obj = yml_rw.CYaml(config_yml_path)
    config_data = config_obj.read_yaml()

    image_ratio = None
    if"TEST_ENV" in config_data:
        if "IMAGE_RATIO" in config_data["TEST_ENV"]:
            image_ratio = config_data["TEST_ENV"]["IMAGE_RATIO"]

    return image_ratio


LOG_CONSOLE, LOG_LEVEL, REPORT_TYPE, DATA_TYPE, REPORT_SAVE_PATH, AIRTEST_THRESHOLD, AIRTEST_FIND_TIMEOUT = get_sys_config_from_yml()
IMAGE_RATIO = get_test_config_from_yml()


if __name__ == "__main__":
    get_sys_config_from_yml()
SYSTEM:
  LOG_CONSOLE: True    # 是否在控制台打印日志信息
  LOG_LEVEL: "info"    # 日志等级,包括warning/info/debug
  REPORT_TYPE: 1       # 0-报告类型为allure,1-报告类型为python-html
  DATA_TYPE: 0         # 0-数据源为数据库,1-数据源为Excel
  REPORT_SAVE_PATH: "\\\\10.99.10.251/sf_local/data/测试报告/消费级BP/3bp go app/APP_UI_Test_Report/"   # 报告存放的路径
  AIRTEST_THRESHOLD: 0.7 #[0,1] 图像识别的阈值,当识别结果的可信度大于阈值时,才认为找到了匹配结果
  AIRTEST_FIND_TIMEOUT: 20 # 查找元素的超时时间,单位秒
TEST_ENV:
  IMAGE_RATIO: 0.5     # 截图缩放比率,小于1,缩放后减少存储空间
from request.app_ui_zhh.common.logger import CLogger
from request.app_ui_zhh.config.config import LOG_LEVEL, AIRTEST_THRESHOLD, AIRTEST_FIND_TIMEOUT
from airtest.core.api import *
from airtest.core.settings import Settings as ST
import os


class AirtestBaseAction(object):
    def __init__(self):
        self.log = CLogger(LOG_LEVEL)
        self.platform_name = None
        pro_base_dir = os.path.dirname(os.path.dirname(__file__))
        self.airtest_img_path = os.path.join(pro_base_dir, "data/airtest_img/")

    def device_connect(self, device_app_info):
        ST.THRESHOLD = AIRTEST_THRESHOLD
        ST.FIND_TIMEOUT = AIRTEST_FIND_TIMEOUT
        ST.SAVE_IMAGE = False
        pro_base_dir = os.path.dirname(os.path.dirname(__file__))
        log_path = os.path.join(pro_base_dir, "log/airtest_log")
        if "platformName" in device_app_info:
            self.platform_name = device_app_info["platformName"]
        else:
            self.log.error("平台类型获取失败!")
            return False
        if "deviceName" in device_app_info:
            if self.platform_name == "Android":
                auto_setup(__file__, logdir=log_path, devices=["android://127.0.0.1:5037/" + device_app_info["deviceName"]])
            elif self.platform_name == "IOS":
                auto_setup(__file__, logdir=log_path, devices=["ios:http+usbmux://" + device_app_info["deviceName"]])
            else:
                self.log.error("平台类型错误,当前只支持Android和IOS!")
                return False
        else:
            self.log.error("设备名称获取失败!")
            return False

        return True

    def device_disconnect(self):
        device().disconnect()
        self.log.info("停止Airtest相关服务!")

    def start_app(self, package_name, activity=None):
        start_app(package_name, activity)
        self.log.error("启动APP:{},{}".format(package_name, activity))
        return True

    def stop_app(self, package_name):
        stop_app(package_name)
        self.log.error("停止APP:{}".format(package_name))
        return True

    def sleep_wait(self, value):
        self.log.info('睡眠{}秒'.format(int(value)))
        time.sleep(value)

    def touch_img_one(self, image_name, duration=0.5, threshold=AIRTEST_THRESHOLD):
        try:
            touch(Template(self.airtest_img_path + image_name, target_pos=5, threshold=threshold), times=1, duration=duration)
        except Exception as e:
            self.log.error("点击图片实例失败:{}", e)
            return False
        else:
            return True

    def is_exists_img(self, image_name):
        try:
            result = exists(Template(self.airtest_img_path + image_name))
        except Exception as e:
            self.log.error("查找图片实例失败:{}", e)
            return False
        else:
            if result is False:
                return False
            return True
import time
from selenium.webdriver.support.wait import WebDriverWait
from request.app_ui_zhh.common.logger import CLogger
from request.app_ui_zhh.config.config import LOG_LEVEL
from request.app_ui_zhh.drivers.check_devices import is_devices_link, is_app_installed
from appium import webdriver
from selenium.common.exceptions import WebDriverException
from urllib3.exceptions import MaxRetryError
from request.app_ui_zhh.drivers.config import AndroidKeyCode


class AppiumBaseAction(object):
    def __init__(self):
        self.log = CLogger(LOG_LEVEL)
        self.driver = None
        self.app_package_name = None
        self.platform_name = None

    def device_driver(self, device_app_info):
        if "appPackage" in device_app_info:
            self.app_package_name = device_app_info["appPackage"]
        else:
            self.log.error("APP包名/bundleId获取失败!")
            return False
        if "platformName" in device_app_info:
            self.platform_name = device_app_info["platformName"]
        else:
            self.log.error("平台类型获取失败!")
            return False

        if self.platform_name == "Android":
            if "server" in device_app_info:
                server_ip = device_app_info["server"].split("//")[1].split(":")[0]
                if server_ip == "localhost" or server_ip == "127.0.0.1":
                    if is_devices_link(device_type=self.platform_name) is False:
                        self.log.error("未找到设备!")
                        return False
                    if is_app_installed(device_name=device_app_info["deviceName"], app_package_name=self.app_package_name, device_type=self.platform_name) is False:
                        self.log.error("APP未安装!")
                        return False
            else:
                self.log.error("设备连接地址获取失败!")
                return False

        try:
            server = device_app_info["server"]
            if device_app_info["platformName"] == "Android":
                desired_capabilities = {
                    'platformName': device_app_info["platformName"],
                    'deviceName': device_app_info["deviceName"],
                    'appPackage': device_app_info["appPackage"],
                    'appActivity': device_app_info["appActivity"],
                    'noReset': device_app_info["noReset"],
                    'fullReset': device_app_info["fullReset"]
                }
            elif device_app_info["platformName"] == "IOS":
                wda_ip = server.split("//")[1].split(":")[0]
                desired_capabilities = {
                    "platformName": device_app_info["platformName"],
                    "appium:deviceName": device_app_info["device_sn"],
                    "appium:platformVersion": device_app_info["platform_version"],
                    "appium:udid": device_app_info["deviceName"],
                    "appium:bundleId": device_app_info["appPackage"],
                    "appium:webDriverAgentUrl": "http://" + wda_ip + ":8100",
                    "appium:automationName": device_app_info["automationName"],
                    "appium:usePrebuiltWDA": device_app_info["usePrebuiltWDA"],
                    "appium:useXctestrunFile": device_app_info["useXctestrunFile"],
                    "appium:skipLogCapture": device_app_info["skipLogCapture"]
                }
            else:
                self.log.error("平台类型错误,当前只支持Android和IOS!")
                return False
            self.driver = webdriver.Remote(server, desired_capabilities)
        except MaxRetryError:
            self.log.error("连接设备失败,请检查appium server是否启动!")
            return False
        except WebDriverException:
            self.log.error("连接设备失败,请检查连接设备的参数,比如APP包名、启动名等")
            return False
        else:
            return True

    def app_close(self):
        try:
            self.driver.terminate_app(self.app_package_name)
        except Exception as e:
            self.log.error("APP关闭失败:{}", e)
            return False
        else:
            return True

    def find_element(self, by, value, timeout=20.0, time_p=0.5):
        try:
            ele = WebDriverWait(self.driver, timeout, time_p).until(lambda x: x.find_element(by, value))
            self.log.info("找到元素:{},{}".format(by, value))
        except TimeoutError:
            self.log.error("没有找到元素:{},{},或者寻找超时".format(by, value))
            return False
        else:
            return ele

    def find_elements(self, by, value, timeout=20.0, time_p=0.5):
        try:
            ele = WebDriverWait(self.driver, timeout, time_p).until(lambda x: x.find_elements(by, value))
            self.log.info("找到元素:{},{}".format(by, value))
        except TimeoutError:
            self.log.error("没有找到元素:{},{},或者寻找超时".format(by, value))
            return False
        else:
            return ele

    def is_element_present(self, by, value):
        """判断元素是否存在"""
        try:
            self.driver.find_element(by=by, value=value)
        except Exception as e:
            self.log.error("没有找到元素:{},{},或者寻找超时,错误:{}".format(by, value, e))
            return False
        else:
            return True

    def click(self, by, value):
        try:
            self.find_element(by, value).click()
            self.log.info("click元素")
        except Exception as e:
            self.log.error("click元素失败:{},{},错误:{}".format(by, value, e))
            return False
        else:
            return True

    def click_s(self, by, value, index):
        try:
            self.find_elements(by, value)[index].click()
            self.log.info("click元素{}".format(index))
        except Exception as e:
            self.log.error("click元素失败:{},{},错误:{}".format(by, value, e))
            return False
        else:
            return True

    def sleep_wait(self, value):
        self.log.info('睡眠{}秒'.format(int(value)))
        time.sleep(int(value))

    def input_text(self, by, value, text):
        try:
            self.find_element(by, value).send_keys(text)
            self.log.info("输入文本:{}".format(text))
        except Exception as e:
            self.log.error("元素文本输入失败:{},{},错误:{}".format(by, value, e))
            return False
        else:
            return True

    def click_back_key(self):
        try:
            self.driver.press_keycode(AndroidKeyCode["KEYCODE_BACK"])
            self.log.info("click返回键")
        except Exception as e:
            self.log.error("返回失败,错误:{}".format(e))
            return False
        else:
            return True

    def device_quit(self):
        try:
            self.driver.quit()
            self.log.info("关闭设备连接")
        except Exception as e:
            self.log.error("关闭设备连接失败,错误:{}".format(e))
            return False
        else:
            return True

    def get_screenshot_png(self, file_name):
        try:
            self.driver.get_screenshot_as_file(file_name)
            self.log.info("获取设备截图")
        except Exception as e:
            self.log.error("获取设备截图失败,错误:{}".format(e))
            return False
        else:
            return True

    def wait_ele_num(self, by, value, num, time_out):
        time_start = int(time.time())
        while True:
            now = int(time.time())
            if now - time_start > int(time_out):
                break
            ele = self.find_elements(self, by, value, time_out)
            if isinstance(ele, list) is True:
                if len(ele) == int(num):
                    self.log.error('等待元素数量正确{},数量{}'.format((by, value), num))
                    return True
        self.log.error('等待元素数量错误{},数量{}'.format((by, value), num))
        return False

    def swipe(self, start_x, start_y, end_x, end_y, duration=1000):
        try:
            size = self.driver.get_window_size()
            if start_x == 0 or start_y == 0 or end_x == 0 or end_y == 0:
                self.log.info('不能选取边界值{},{},{},{}'.format(start_x, start_y, end_x, end_y))
            if start_x == size["width"] or start_y == size["height"] or end_x == size["width"] or end_y == size["height"]:
                self.log.info('不能选取边界值{},{},{},{}'.format(start_x, start_y, end_x, end_y))
            self.driver.swipe(start_x, start_y, end_x, end_y, duration)
        except Exception as e:
            self.log.error('滑动屏幕出现错误,错误{}'.format(e))
            return False
        else:
            return True

    def swipe_left_to_right(self):
        try:
            size = self.driver.get_window_size()
            start_x = int(size["width"] / 10)
            start_y = int(size["height"] / 2)
            end_x = int(size["width"] * 3 / 4)
            end_y = int(size["height"] / 2)
            self.driver.swipe(start_x, start_y, end_x, end_y, 500)
        except Exception as e:
            self.log.error('滑动屏幕出现错误,错误{}'.format(e))
            return False
        else:
            return True

    def swipe_down_to_up(self):
        try:
            size = self.driver.get_window_size()
            start_x = int(size["width"] / 2)
            start_y = int(size["height"] * 3 / 4)
            end_x = int(size["width"] / 2)
            end_y = int(size["height"] / 10)
            self.driver.swipe(start_x, start_y, end_x, end_y, 500)
        except Exception as e:
            self.log.error('滑动屏幕出现错误,错误{}'.format(e))
            return False
        else:
            return True

    def swipe_screen_back(self):
        return self.swipe_left_to_right()

    def swipe_screen_ele(self, by, value, direction, px=None):
        """
        在元素范围内滑动
        :param by:
        :param value:
        :param direction: 1-从左至右,2-从右至左,3-从下到上,4-从上到下
        :param px: 滑动的点数
        :return:
        """
        try:
            ele = self.find_element(by, value)
            ele_location = ele.location
            ele_size = ele.size
            if direction == 1:
                start_x = int(ele_location["x"] + (ele_size["width"] / 10))
                start_y = int(ele_location["y"] + (ele_size["height"] / 2))
                end_x = int(ele_location["x"] + (ele_size["width"] * 3 / 4))
                end_y = int(ele_location["y"] + (ele_size["height"] / 2))
                if px is not None and (start_x + px) < (ele_location["x"] + ele_size["width"]):
                    end_x = start_x + px
                self.driver.swipe(start_x, start_y, end_x, end_y, 500)
            elif direction == 2:
                start_x = int(ele_location["x"] + (ele_size["width"] * 3 / 4))
                start_y = int(ele_location["y"] + (ele_size["height"] / 2))
                end_x = int(ele_location["x"] + (ele_size["width"] / 10))
                end_y = int(ele_location["y"] + (ele_size["height"] / 2))
                if px is not None and (start_x - px) > ele_location["x"]:
                    end_x = start_x - px
                self.driver.swipe(start_x, start_y, end_x, end_y, 500)
            elif direction == 3:
                start_x = int(ele_location["x"] + (ele_size["width"] / 2))
                start_y = int(ele_location["y"] + (ele_size["height"] * 3 / 4))
                end_x = int(ele_location["x"] + (ele_size["width"] / 2))
                end_y = int(ele_location["y"] + (ele_size["height"] / 10))
                if px is not None and (start_y - px) > ele_location["y"]:
                    end_y = start_y - px
                self.driver.swipe(start_x, start_y, end_x, end_y, 500)
            elif direction == 4:
                start_x = int(ele_location["x"] + (ele_size["width"] / 2))
                start_y = int(ele_location["y"] + (ele_size["height"] / 10))
                end_x = int(ele_location["x"] + (ele_size["width"] / 2))
                end_y = int(ele_location["y"] + (ele_size["height"] * 3 / 4))
                if px is not None and (start_y + px) < (ele_location["y"] + ele_size["height"]):
                    end_y = start_y + px
                self.driver.swipe(start_x, start_y, end_x, end_y, 500)
            else:
                self.log.info('direction值错误,错误{}'.format(direction))
                return False
        except Exception as e:
            self.log.error('元素范围内滑动出现错误,错误{}'.format(e))
            return False
        else:
            return True

    def get_ele_attribute(self, by, value, attribute):
        """ 获取元素的属性 """
        try:
            ele = self.find_element(by, value)
            attribute_value = ele.get_attribute(attribute)
        except Exception as e:
            self.log.error('获取元素属性出现错误,错误{}'.format(e))
            return False
        else:
            return attribute_value

    def swipe_ele_to_expect_attribute(self, by, value, attribute, attribute_value, direction, px=None, time_out=30):
        time_start = int(time.time())
        while True:
            now = int(time.time())
            if now - time_start > int(time_out):
                break
            ele = self.find_element(self, by, value, time_out)
            if ele is False:
                break
            else:
                while ele.get_attribute(attribute) == attribute_value:
                    return True
                self.swipe_screen_ele(by, value, direction, px)
        self.log.error('滑动元素等待元素属性符合预期出现错误{}'.format(by, value))
        return False

    def swipe_to_page_bottom(self, time_out=20):
        time_start = int(time.time())
        while True:
            now = int(time.time())
            if now - time_start > int(time_out):
                break
            before_swipe = self.driver.page_resource
            if self.swipe_down_to_up() is False:
                return False
            else:
                after_swipe = self.driver.page_resource
                if before_swipe == after_swipe:
                    return True
        self.log.error('滑动到页面底部出现错误')
        return False

    def is_expect_ele_attribute(self, by, value, attribute, expect):
        """ 判断元素的属性 """
        attribute_value = self.get_ele_attribute(by, value, attribute)
        if attribute_value == expect:
            return True
        else:
            return False
import os
from request.app_ui_zhh.common.logger import CLogger
from request.app_ui_zhh.config.config import LOG_LEVEL


log = CLogger(LOG_LEVEL)


def is_devices_link(device_type):
    """
    检查是否有设备连接PC,有则返回True
    :param device_type: 设备类型,Android-安卓,IOS-ios
    :return:
    """
    devices_list_finally = []
    if device_type == "Android":
        devices_list_start = []
        devices_cmd = os.popen('adb devices').readlines()
        devices_list_start_count = len(devices_cmd)
        devices_list_start_count = devices_list_start_count - 2
        if devices_list_start_count >= 1:
            log.info('find devices linked')
            for devices_num in range(devices_list_start_count):
                devices_list_start.append(devices_cmd[devices_num + 1])
                device_list_pers = devices_list_start[devices_num].index('\t')
                devices_list_finally.append(devices_list_start[devices_num][:device_list_pers])
                log.info('devices list :' + '%d  ' % (devices_num + 1) + '%s' % devices_list_finally[devices_num])
            return devices_list_finally
        else:
            log.error("无法连接到手机,试试重新插拔手机")
            return False
    else:
        log.warning("检测安卓外的其它设备类型是否在线暂未实现")
        return False


def is_app_installed(device_name, app_package_name, device_type):
    """
    判断手机是否安装了待测试APP,安装则返回True
    :param app_package_name: APP包名称,字符串
    :param device_type: 设备类型,Android-安卓,IOS-ios
    :param device_name: 设备名称
    :return:
    """
    if device_type == "Android":
        app_package_name = "package:" + app_package_name + "\n"
        all_packages = list(os.popen("adb -s " + device_name + " shell pm list package"))
        if app_package_name in all_packages:
            log.info("设备已安装指定APP:" + app_package_name)
            return True
        else:
            log.warning("未安装指定APP:" + app_package_name)
            return False
    else:
        log.warning("安卓外的其它设备类型检测安装包暂未实现")
        return False


if __name__ == '__main__':
    is_devices_link("Android")
    is_app_installed("ACMFUT2519008078", "com.heygears.blueprintgo", "Android")
AndroidKeyCode = {
    "KEYCODE_HOME": 33,
    "KEYCODE_BACK": 4,
    "KEYCODE_MENU": 82
}
import time
from request.app_ui_zhh.common.logger import CLogger
from request.app_ui_zhh.config.config import LOG_LEVEL
import os

log = CLogger(LOG_LEVEL)


def tidevice_wda_server_start(udid, bundle_id="com.heygears.WebDriverAgentRunner.xctrunner", port="8100"):
    val = os.popen("tasklist | findstr /s /i tidevice.exe").read()
    if "tidevice.exe" in val:
        os.popen("taskkill /im tidevice.exe /t /f").read()
        time.sleep(5)
    os.popen("tidevice -u " + udid + " wdaproxy -B " + bundle_id + " --port " + port)
    time.sleep(15)
    val = os.popen("tasklist | findstr /s /i tidevice.exe").read()
    if "tidevice.exe" in val:
        log.info("Tidevice启动成功!")
        return True

    log.error("Tidevice启动失败!")
    return False


def tidevice_wda_server_stop():
    val = os.popen("tasklist | findstr /s /i tidevice.exe").read()
    if "tidevice.exe" in val:
        os.popen("taskkill /im tidevice.exe /t /f").read()
        time.sleep(5)
    log.info("Tidevice进程关闭成功!")
    return True
from django.db import models


class AppDeviceInfo(models.Model):
    id = models.AutoField(primary_key=True, max_length=200, unique=True)
    device_sn = models.CharField('设备编号', max_length=100)
    device_name = models.CharField('设备名称', max_length=100)
    platform_name = models.CharField('平台类型', max_length=100)
    platform_version = models.CharField('平台版本', max_length=100)
    version = models.CharField('系统版本', max_length=100)
    server = models.CharField('连接到的服务链接', max_length=100)
    create_time = models.DateTimeField(auto_now=True)
    remarks = models.CharField('备注', max_length=100, null=True)

    def __str__(self):
        return self.device_name

    class Meta:
        managed = False
        db_table = "app_device_info"


class AppPackageInfo(models.Model):
    id = models.AutoField(primary_key=True, max_length=200, unique=True)
    app_name = models.CharField('app名称', max_length=100)
    platform = models.CharField('平台类型', max_length=100)
    package_name = models.CharField('包名称', max_length=100)
    activity_name = models.CharField('启动activity名称', max_length=100)
    create_time = models.DateTimeField(auto_now=True)
    remarks = models.CharField('备注', max_length=100, null=True)

    def __str__(self):
        return self.app_name

    class Meta:
        managed = False
        db_table = "app_package_info"


class AppUiTestCase(models.Model):
    id = models.AutoField(primary_key=True, max_length=200, unique=True)
    platform = models.CharField('平台类型', max_length=100)
    project = models.CharField('项目', max_length=100)
    product = models.CharField('产品', max_length=100)
    module = models.CharField('模块', max_length=100)
    level = models.CharField('用例等级', max_length=20)
    lib = models.CharField('库', max_length=100)
    method = models.CharField('方法', max_length=100)
    parameter = models.CharField('参数', max_length=200)
    expect = models.CharField('参数', max_length=200)
    label = models.CharField('参数', max_length=20)
    create_time = models.DateTimeField(auto_now=True)
    remarks = models.CharField('备注', max_length=100, null=True)

    def __str__(self):
        return self.method

    class Meta:
        managed = False
        db_table = "app_ui_test_case"


class AppReport(models.Model):
    id = models.AutoField(primary_key=True, max_length=200, unique=True)
    platform_name = models.CharField('平台类型', max_length=100)
    product_name = models.CharField('产品', max_length=100)
    result = models.CharField('结果', max_length=100)
    path = models.CharField('报告路径', max_length=100)
    report_type = models.CharField('报告类型', max_length=100)
    create_time = models.DateTimeField(auto_now=True)
    remarks = models.CharField('备注', max_length=100, null=True)

    def __str__(self):
        return self.product_name

    class Meta:
        managed = False
        db_table = "app_report"
#service common.py
import allure
from request.app_ui_zhh.common.image_processing import img_resize
import json
from request.app_ui_zhh.common.logger import CLogger
from request.app_ui_zhh.config.config import LOG_LEVEL, REPORT_TYPE, REPORT_SAVE_PATH, DATA_TYPE
import os
from shutil import copy, copytree
from request.app_ui_zhh.common.excel_rw import read_excel, read_excel_from_contents
from request.app_ui_zhh.model.app_ui_model import AppUiTestCase, AppDeviceInfo, AppPackageInfo, AppReport
from request.app_ui_zhh.common.global_env import set_global_value, get_global_value
import time


log = CLogger(LOG_LEVEL)


def init_global_env(platform, project, product, device_name, app_name):
    device_app_info = {
            'server': 'http://localhost:' + '4723' + '/wd/hub',
            # 'device_sn': "ANY-AN00",
            'platformName': platform,
            'device_sn': "iPhone Pro 13",
            'deviceName': device_name,                               # iPhone的udid也用这个字段
            'appPackage': "com.heygears.blueprintgo",                # ISO目标APP的bundleId也用该字段
            'appActivity': "com.heygears.blueprintgo.MainActivity",
            'noReset': True,
            'fullReset': False,
            'platform_version': "12",
            'version': "Magic UI 6.1",
            'webDriverAgentUrl': "http://localhost:8100",            # 连接IOS需要,WDA地址,暂时开放成变量修改IP和端口
            "usePrebuiltWDA": False,
            "useXctestrunFile": False,
            "skipLogCapture": True,
            "automationName": "XCUITest",
            "AppName": app_name
        }

    set_global_value("screenshot_file_png", "")
    set_global_value("test_case_remarks", "")
    set_global_value("test_stop", 0)
    set_global_value("test_result", "succ")
    set_global_value("exist_airtest_case", False)

    pro_base_dir = os.path.dirname(os.path.dirname(__file__))
    test_case_excel_file = os.path.join(pro_base_dir, "data/testcase/blueprint_go_ui_test_case.xlsx")

    if DATA_TYPE == 1:
        # 调试脚本,测试数据从Excel中获取
        test_case_data = get_test_case_from_excel(test_case_excel_file, device_app_info["platformName"])
    else:
        test_device_app_info = get_test_device_app_info_from_db(platform, device_name, app_name)
        if test_device_app_info is False or len(test_device_app_info) < 1:
            return False
        device_app_info.update(test_device_app_info)

        test_case_data = []
        test_case_data_temp = get_test_case_from_db(platform=platform, project=project, product=product)
        if len(test_case_data_temp) < 1:
            return False
        for data in test_case_data_temp:
            test_case_data.append([value for value in data.values()])

    set_global_value("test_case_data", test_case_data)
    set_global_value("test_device_app_info", device_app_info)

    for data in test_case_data:
        if data[5] == "airtest":
            set_global_value("exist_airtest_case", True)
            break

    return True


def save_screenshot(driver, path, file_name, ratio=None):
    file = path + file_name
    driver.get_screenshot_png(file)
    if isinstance(ratio, (float, int)) and ratio < 1:
        img_resize(file, ratio)
    with open(file, mode='rb') as f:
        file_data = f.read()
    if REPORT_TYPE == 0:
        allure.attach(file_data, "test", allure.attachment_type.PNG)

    return file


def allure_trend_data_processing(history_trend_file, new_trend_file):
    # allure趋势图处理方法
    history_trend_data = []
    build_order = None
    if os.path.exists(history_trend_file):
        # 获取历史文件数据
        with open(history_trend_file) as f:
            try:
                history_trend_data = json.load(f)
                if "history-trend.json" == new_trend_file.split("/")[-1]:
                    build_order = int(history_trend_data[0]['buildOrder'])
            except Exception as e:
                log.error("文件内容读取错误:{}".format(history_trend_file))
                log.error("错误信息:{}".format(e))
    # 获取新文件数据
    with open(new_trend_file) as f:
        try:
            new_trend_data = json.load(f)
            if "history-trend.json" == new_trend_file.split("/")[-1]:
                if build_order is not None:
                    new_trend_data[0]['buildOrder'] = build_order + 1
                else:
                    new_trend_data[0]['buildOrder'] = 1
            history_trend_data.insert(0, new_trend_data[0])
        except Exception as e:
            log.error("文件内容读取错误:{}".format(new_trend_file))
            log.error("错误信息:{}".format(e))
    # 写入新文件
    with open(new_trend_file, "w") as f:
        json.dump(history_trend_data, f)
    # 将新文件写入save_history文件夹
    copy(new_trend_file, history_trend_file)


def get_test_case_from_excel(file_name, sheet_name):
    data = read_excel(file_name, sheet_name, row=-1, col=-1)
    if len(data) > 1:
        del data[0]
    return data


def get_test_case_from_db(platform, project, product):
    data = list(AppUiTestCase.objects.filter(platform=platform, project=project, product=product).values('id', 'project', 'product', 'module', 'level', 'lib', 'method', 'parameter', 'expect', 'label', 'remarks'))
    return data


def get_test_device_app_info_from_db(platform, device_name, app_name):
    # 从数据库获取测试设备和APP的信息
    device_info_data = AppDeviceInfo.objects.filter(platform_name=platform, device_name=device_name).values_list()
    if len(device_info_data) != 1:
        log.error("获取设备信息出错,未识别到唯一设备:{}".format(device_name))
        return False
    app_info_data = AppPackageInfo.objects.filter(platform=platform, app_name=app_name).values_list()
    if len(app_info_data) != 1:
        log.error("获取APP信息出错:{}".format(app_name))
        return False

    test_device_app_info = {}
    for data in device_info_data:
        if len(data) < 7:
            log.error("获取设备信息出错,未识别到唯一设备:{}".format(device_name))
            return False
        test_device_app_info["server"] = data[6]
        test_device_app_info["device_sn"] = data[1]
        test_device_app_info["deviceName"] = data[2]
        test_device_app_info["platformName"] = data[3]
        test_device_app_info["platform_version"] = data[4]
        test_device_app_info["version"] = data[5]
    for data in app_info_data:
        if len(data) < 4:
            log.error("获取APP信息出错:{}".format(app_name))
            return False
        test_device_app_info["appPackage"] = data[3]
        test_device_app_info["appActivity"] = data[4]

    return test_device_app_info


def up_app_ui_case(file_contents):
    data = read_excel_from_contents(file_contents=file_contents.read())
    sheets = data.sheets()
    if sheets is not None:
        for sheet in sheets:
            if sheet.name == "Android" or sheet.name == "IOS":
                test_case_data = []
                times = time.strftime('%y-%m-%d', time.localtime())
                num = 0
                for row in range(sheet.nrows):
                    platform = sheet.name
                    project = sheet.cell_value(row, 1)
                    product = sheet.cell_value(row, 2)
                    module = sheet.cell_value(row, 3)
                    level = sheet.cell_value(row, 4)
                    lib = sheet.cell_value(row, 5)
                    method = sheet.cell_value(row, 6)
                    parameter = sheet.cell_value(row, 7)
                    expect = sheet.cell_value(row, 8)
                    label = sheet.cell_value(row, 9)
                    create_time = times
                    remarks = sheet.cell_value(row, 10)
                    if num == 0:
                        num = num + 1
                        continue
                    test_case_data.append(AppUiTestCase(platform=str(platform), project=str(project), product=str(product), module=str(module),
                                                    level=str(level), lib=str(lib), method=str(method), parameter=str(parameter), expect=str(expect),
                                                    label=str(label), create_time=create_time, remarks=remarks))
                AppUiTestCase.objects.bulk_create(test_case_data)
        return True
    else:
        return False


def add_device_info(device_sn, device_name, platform_name, platform_version, version, server, remarks):
    AppDeviceInfo.objects.create(device_sn=str(device_sn), device_name=str(device_name), platform_name=str(platform_name), platform_version=str(platform_version),
                                 version=str(version), server=str(server), create_time=time.strftime('%y-%m-%d', time.localtime()), remarks=str(remarks))


def add_package_info(app_name, platform, package_name, activity_name, remarks):
    AppPackageInfo.objects.create(app_name=str(app_name), platform=str(platform), package_name=str(package_name), activity_name=str(activity_name),
                                  create_time=time.strftime('%y-%m-%d', time.localtime()), remarks=str(remarks))


def delete_device_info(id_num):
    AppDeviceInfo.objects.filter(id=id_num).delete()


def delete_package_info(id_num):
    AppPackageInfo.objects.filter(id=id_num).delete()


def save_report(platform_name, product_name, report_file_name):
    start_check_time = int(time.time())
    report_path_dist = ""
    report_type = ""
    test_result = get_global_value("test_result")
    while int(time.time()) - start_check_time < 30:
        if REPORT_TYPE == 1:
            # 检测报告是否存在
            report_type = "pytest-html"
            pro_base_dir = os.path.dirname(os.path.dirname(__file__))
            report_path_src = os.path.join(pro_base_dir, "report/pytest_html/" + report_file_name)
            report_path_dist = REPORT_SAVE_PATH + "pytest-html/" + report_file_name
            if os.path.exists(report_path_src):
                copy(report_path_src, report_path_dist)
                break
        else:
            # allure报告
            report_type = "allure"
            pro_base_dir = os.path.dirname(os.path.dirname(__file__))
            report_path_src = os.path.join(pro_base_dir, "report/" + report_file_name)
            report_path_dist = REPORT_SAVE_PATH + "allure/" + report_file_name
            if not os.path.exists(report_path_dist) and os.path.isdir(report_path_src):
                copytree(report_path_src, report_path_dist)
                break

        time.sleep(5)

    AppReport.objects.create(platform_name=str(platform_name), product_name=str(product_name), result=str(test_result), path=str(report_path_dist), report_type=str(report_type),
                             create_time=time.strftime('%y-%m-%d', time.localtime()), remarks=str(""))
from request.app_ui_zhh.common.global_env import set_global_value, get_global_value
from pytest_assume.plugin import assume
from request.app_ui_zhh.common.logger import CLogger
from request.app_ui_zhh.config.config import LOG_LEVEL


log = CLogger(LOG_LEVEL)


def result_assert(driver, expect):
    if expect != "":
        expect_value_list = expect.split(";")
        if len(expect_value_list) > 0:
            method = expect_value_list[0]
            parameters = []
            if len(expect_value_list) > 1:
                i = 1
                while i < len(expect_value_list):
                    parameters.append(expect_value_list[i])
                    i = i + 1
            try:
                if parameters is []:
                    result = getattr(driver, method)()
                else:
                    result = getattr(driver, method)(*parameters)
            except Exception as e:
                log.info("断言过程出现错误:{}".format(e))
                set_global_value("test_stop", 1)
                set_global_value("test_result", "Fail")
            else:
                if result is False:
                    log.info("断言失败:{}".format(expect))
                    set_global_value("test_stop", 1)
                    set_global_value("test_result", "Fail")
                    assume(False)
    else:
        assume(True)
import time
import allure
from pytest_assume.plugin import assume
import pytest
from request.app_ui_zhh.common.logger import CLogger
from request.app_ui_zhh.config.config import LOG_LEVEL, IMAGE_RATIO
from request.app_ui_zhh.service.common import save_screenshot
from request.app_ui_zhh.service.result_assert import result_assert
from request.app_ui_zhh.drivers.appium_driver import AppiumBaseAction
from request.app_ui_zhh.drivers.airtest_driver import AirtestBaseAction
from request.app_ui_zhh.drivers.server_manager import tidevice_wda_server_start, tidevice_wda_server_stop
import os
from request.app_ui_zhh.common.global_env import set_global_value, get_global_value


pro_base_dir = os.path.dirname(os.path.dirname(__file__))
image_path = os.path.join(pro_base_dir, "report/image/")

log = CLogger(LOG_LEVEL)

exist_airtest_case = get_global_value("exist_airtest_case")
device_app_info = get_global_value("test_device_app_info")
test_case_data = get_global_value("test_case_data")
test_case_num = 0

device_driver = AppiumBaseAction()
airtest_driver = AirtestBaseAction()


def setup_module():
    global device_driver, device_app_info, exist_airtest_case
    # 连接IOS设备需要先启动tidevice
    if device_app_info["platformName"] == "IOS" and ("localhost" in device_app_info["service"] or "127.0.0.1" in device_app_info["service"]):
        if tidevice_wda_server_start(device_app_info["deviceName"]) is False:
            pytest.exit("Tidevice启动失败,退出测试!")
    # 如果用例存在airtest库方法,需要airtest连接到设备
    if exist_airtest_case is True:
        if airtest_driver.device_connect(device_app_info) is False:
            pytest.exit("Airtest连接设备失败,退出测试!")
    # appium连接设备
    if device_driver.device_driver(device_app_info) is False:
        pytest.exit("Appium连接设备失败,退出测试!")
    log.info("测试开始!")


def teardown_module():
    global device_driver, exist_airtest_case
    device_driver.device_quit()
    if exist_airtest_case is True:
        airtest_driver.device_disconnect()
    if device_app_info["platformName"] == "IOS" and ("localhost" in device_app_info["service"] or "127.0.0.1" in device_app_info["service"]):
        tidevice_wda_server_stop()
    log.info("测试完成,退出测试!")


@allure.feature("测试安卓APP应用BluePrintGo")
class TestCase(object):
    @staticmethod
    def setup_method(self):
        global test_case_num
        test_stop = get_global_value("test_stop")
        if test_stop == 1 and test_case_data[test_case_num][5] != "not_skip":
            pytest.exit("测试进程主动终止,可能是执行过程遇到了错误,请检查!")
        test_case_num = test_case_num + 1

    @pytest.fixture()
    def test_case_dict(self, request):
        test_case_data_temp = request.param
        test_case_dict = {
            "case_id": test_case_data_temp[0],
            "project": test_case_data_temp[1],
            "product": test_case_data_temp[2],
            "module": test_case_data_temp[3],
            "level": test_case_data_temp[4],
            "lib": test_case_data_temp[5],
            "method": test_case_data_temp[6],
            "parameter": test_case_data_temp[7],
            "expect": test_case_data_temp[8],
            "label": test_case_data_temp[9],
            "remarks": test_case_data_temp[10]
        }
        return test_case_dict

    @allure.story(device_app_info["platformName"])
    @allure.title("{test_case_dict[remarks]}")
    @pytest.mark.android_all_case
    @pytest.mark.parametrize('test_case_dict', test_case_data, indirect=True)
    def test_app_ui(self, test_case_dict):
        log.info("正在执行用例:{}".format(test_case_dict['remarks']))
        allure.dynamic.severity(test_case_dict['level'])
        set_global_value("test_case_remarks", test_case_dict['remarks'])
        parameters = test_case_dict["parameter"].split(";")
        try:
            if test_case_dict["lib"] == "appium":
                lib_driver = device_driver
            elif test_case_dict["lib"] == "airtest":
                lib_driver = airtest_driver
            else:
                raise Exception("用例中lib库错误!")
            if test_case_dict["parameter"] == "":
                res = getattr(lib_driver, test_case_dict["method"])()
            else:
                res = getattr(lib_driver, test_case_dict["method"])(*parameters)
            if res is not True:
                raise Exception("执行APP自动化操作出现异常!")
        except Exception as e:
            log.info("执行测试出现错误:{}".format(e))
            set_global_value("test_stop", 1)
            set_global_value("test_result", "Fail")
            assume(False)
        time.sleep(1)
        screenshot_file = save_screenshot(device_driver, image_path, device_app_info["platformName"] + " Test " + str(int(test_case_dict["case_id"])) + ".png", IMAGE_RATIO)
        set_global_value("screenshot_file_png", screenshot_file)
        result_assert(device_driver, test_case_dict["expect"])
import os
import pytest
from shutil import copy
import time
import django
from request.app_ui_zhh.config.config import REPORT_TYPE
from request.app_ui_zhh.common.logger import CLogger
from request.app_ui_zhh.config.config import LOG_LEVEL
import multiprocessing


os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'AutoTest.settings')
django.setup()
from request.app_ui_zhh.service.common import allure_trend_data_processing, save_report, init_global_env

log = CLogger(LOG_LEVEL)
process_execute_state = multiprocessing.Value("i", 0)


def execute_test_process(platform, project, product, device_name, app_name):
    global process_execute_state
    if process_execute_state.value == 1:
        return False
    else:
        process_execute_state.value = 1
        p = multiprocessing.Process(target=execute_test, args=(platform, project, product, device_name, app_name, process_execute_state))
        log.info("启动测试进程")
        p.start()

    return True


def execute_test(platform, project, product, device_name, app_name, process_state):
    os.chdir(os.path.dirname(__file__))
    if init_global_env(platform, project, product, device_name, app_name) is False:
        log.error("初始化数据失败!未执行任何测试!")
        process_state.value = 0
        return False
    if REPORT_TYPE == 0:
        # allure报告执行代码
        pro_base_dir = os.path.dirname(__file__)
        report_path = os.path.join(pro_base_dir, "report/")
        save_history_path = os.path.join(report_path, "save_history/")
        report_xml_dir = os.path.join(report_path, "xml")
        report_file_name = "html_" + str(int(time.time()))
        report_html_dir = os.path.join(report_path, report_file_name)
        widgets_dir = os.path.join(report_html_dir, "widgets/")
        # pytest.main(['-s', '-q', '--clean-alluredir', '--alluredir', report_xml_dir, '-m', 'android_all_case'])
        pytest.main(['-s', '-q', '--alluredir', report_xml_dir, '-m', 'android_all_case'])
        copy(os.path.join(report_path, "environment.properties"), report_xml_dir)
        copy(os.path.join(report_path, "categories.json"), report_xml_dir)
        os.system('allure generate ' + report_xml_dir + ' -o ' + report_html_dir + ' --clean')
        copy(os.path.join(report_path, "executors.json"), widgets_dir)
        allure_trend_data_processing(save_history_path + "history-trend.json", widgets_dir + "history-trend.json")
        allure_trend_data_processing(save_history_path + "duration-trend.json", widgets_dir + "duration-trend.json")
        allure_trend_data_processing(save_history_path + "retry-trend.json", widgets_dir + "retry-trend.json")
        allure_trend_data_processing(save_history_path + "categories-trend.json", widgets_dir + "categories-trend.json")
        save_report(platform, product, report_file_name)
    elif REPORT_TYPE == 1:
        # pytest-html执行代码
        report_file_name = 'report_' + str(int(time.time())) + '.html'
        pytest.main(['-q', '--html=./report/pytest_html/' + report_file_name, '--self-contained-html', '--capture=tee-sys', '-m', 'android_all_case'])
        save_report(platform, product, report_file_name)
    else:
        log.error("报告类型设置错误!未执行任何测试!")
        process_state.value = 0
        return False

    process_state.value = 0

    return True


if __name__ == '__main__':
    execute_test_process("Android", "消费级", "Blueprint Go", "ACMFUT2519008078", "Blueprint Go")
    # execute_test_process("IOS", "消费级", "Blueprint Go", "00008110-000A20D014F9801E", "Blueprint Go")
import base64
import time
from time import strftime
from py.xml import html
import pytest
from request.app_ui_zhh.common.global_env import get_global_value
from request.app_ui_zhh.config.config import DATA_TYPE
import cv2
import logging


report_time = []


# 编辑报告标题
def pytest_html_report_title(report):
    report.title = "Blueprint Go APP Test Report"


# 运行测试后修改环境信息
@pytest.hookimpl(tryfirst=True)
def pytest_sessionfinish(session, exitstatus):
    device_app_info = get_global_value("test_device_app_info")
    session.config._metadata["Platform"] = device_app_info["platformName"]
    session.config._metadata["System"] = device_app_info["version"]
    session.config._metadata["DeviceName"] = device_app_info["deviceName"]
    session.config._metadata["Version"] = device_app_info["platform_version"]
    session.config._metadata["DeviceSn"] = device_app_info["device_sn"]
    session.config._metadata['Start Time'] = strftime('%Y-%m-%d %H:%M:%S')
    session.config._metadata['AppName'] = device_app_info["AppName"]
    if DATA_TYPE == 1:
        session.config._metadata['Testcase From'] = "Excel"
    else:
        session.config._metadata['Testcase From'] = "DB"
    session.config._metadata.pop("JAVA_HOME")
    session.config._metadata.pop("Plugins")
    session.config._metadata.pop("Packages")


# 测试结果表格
def pytest_html_results_table_header(cells):
    cells.insert(2, html.th("Description", class_="sortable desc", col="desc"))
    cells.insert(3, html.th('Time', class_='sortable time', col='time'))
    cells.pop()


def pytest_html_results_table_row(report, cells):
    global report_time
    cells.insert(2, html.th(report.description))
    cells.insert(3, html.th(time.strftime('%Y.%m.%d %H:%M:%S', time.localtime(report_time[0])), class_="col-time"))
    del report_time[0]
    cells.pop()


@pytest.hookimpl(hookwrapper=True)
def pytest_runtest_makereport(item, call):
    screenshot_file_png = get_global_value("screenshot_file_png")
    test_case_remarks = get_global_value("test_case_remarks")
    pytest_html = item.config.pluginmanager.getplugin("html")
    outcome = yield
    report = outcome.get_result()
    report.description = test_case_remarks
    setattr(report, "duration_formatter", "%H:%M:%S.%f")
    extra = getattr(report, "extra", [])
    global report_time
    if report.when == "setup":
        report_time.append(call.start)
    if report.when == "call":
        image = cv2.imread(screenshot_file_png)
        width_height_ratio = float(image.shape[1] / image.shape[0])
        with open(screenshot_file_png, 'rb') as f:
            image_base64 = str(base64.b64encode(f.read()), encoding='utf-8')
            f.close()
        if image_base64 is not None:
            height = 484
            html_script = '<div><img src="data:image/png;base64,{}" alt="screenshot" style="width:{}px;height:{}px;"" align="right"/></div>'\
                .format(image_base64, str(int(width_height_ratio*height)), height)
            extra.append(pytest_html.extras.html(html_script))
        report.extra = extra


def pytest_sessionfinish():
    # pytest's capsys is enabled by default, see
    # https://docs.pytest.org/en/7.1.x/how-to/capture-stdout-stderr.html.
    # but pytest closes its internal stream for capturing the stdout and
    # stderr, so we are not able to write the logger anymore once the test
    # session finishes. see https://github.com/pytest-dev/pytest/issues/5577,
    # to silence the warnings, let's just prevent logging from raising
    # exceptions.
    logging.raiseExceptions = False

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值