从零开始玩转PerfDogService---------简易web结合篇

背景
随着测试工具的丰富,可执行程序文件越来越多,对于测试的工具管理来说也不是很友好;而且有我们后期会利用PerfDogService搭建自动化平台,所以今天先来一个简易版的demo;
做完之后是这个样子:
在这里插入图片描述
执行完毕后会显示
在这里插入图片描述
选择存储会同步上传到云端
在这里插入图片描述

当前web前端表现比较丑 比较素颜,没有花花绿绿的CSS样式,样式打算后期优化,先把主要功能搞出来;

一.结构组成

python web框架: Tornado
前端组件库: element-ui,VUE
核心: PerfDogService

整体思路其实很简单,首先获取本地的机型信息显示到前端,前端发送指令到Server,Server开始选中相应的机型开始测试。

在这里插入图片描述
web框架选型理由: Tornado使非阻塞式的服务器,速度相当快。这得益于其非阻塞的方式和对epoll的运用。Tornado每秒可以处理数以千计的连接,对于实时Web服务来说Tornado确实是一个理想的Web框架;简单理解就是相比于其他python web框架, Tornado的性能非常好,可以承载很高的并发访问量,不过缺点就是Tornado走的是少而精的方向,适合定制化,缺少一些库,比如数据库支持什么的, 需要自己写或者找三方的;
前端: 其实你也可以用其他的,适合自己的就好

二.核心结构讲解

在这里插入图片描述

目录结构是这个样子。我是用的Pycharm建立的虚拟环境开发,所以会有一个虚拟环境venv文件夹;

Main.py WebServer的主服务.
demo.py 调用PerfDogService的Handler存放文件夹
Template index.html存放的文件夹
Static html调用的一些js或者css存放的文件夹
perfdog_pb2.py,perfdog_pb2_grpc.py PerfDogService运行所依赖的重要文件.

三.核心方法讲解

在index.html中主要有两个核心方法:

1.getdevicesinfo(),主要是用于当用户在打开网页的时候从服务器获取当前本地的连接测试手机;对应的是(r"/devices", get_devicesHandler)路由,主要会调用demowe.py的Get_deviceList()方法

2.save_test()方法主要用于将当前网页上的数据发送给服务器;对应 (r"/save_test/([^/]+)", get_test_data)路由,加了通配符是因为这里以后可以传一个当前使用用户的id用于存储是谁提出了测试请求;主要会调用demo.py的run_test_data();其实主要还是运行perfdog的测试程序,对应demo.py的run(DEVICES_UID=None,APP_NAME=None,TEST_TIMER=None, CASENAME=None,SAVEDATA_TYPE=None,IS_SAVE=None,OTHER_GETDATA=None)

四.完整代码

代码较烂,轻喷,主要是还没优化,初版功能,后面会有较大改动
Index.html

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<meta http-equiv="X-UA-Compatible" content="ie=edge">
	<link rel="shortcut icon" href="/static/images/icon/logo.png" />
	<title>{% block title %}{% end %}</title>
	<!-- Tell the browser to be responsive to screen width -->
	<meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">
	<!-- Bootstrap 3.3.6 -->
	<link rel="stylesheet" href="/static/element-ui/lib/theme-chalk/index.css">
	<script src="/static/vue.min.js"></script>
	<script src="/static/element-ui/lib/index.js"></script>
	<script src="/static/jquery-3.3.1.min.js"></script>
	<!-- Theme style -->
</head>


<body>



	<div id="app" class="text">
		<el-form :model="ruleForm" :rules="rules" ref="ruleForm" label-width="100px" class="demo-ruleForm" > 
			<el-form-item label="测试名称" prop="name">
				<el-input v-model="ruleForm.name"  ></el-input>
			</el-form-item>
			<el-form-item label="测试时间(s)" prop="test_time">
				<el-input v-model="ruleForm.test_time"></el-input>
			</el-form-item>
			<el-form-item label="测试APP名字" prop="app_name">
				<el-input v-model="form.app_name"></el-input>
			</el-form-item>
			<el-form-item label="是否保存到本地">
				<el-switch v-model="form.save_flag"></el-switch>
			</el-form-item>

			<el-form-item label="选择设备">
				<el-select v-model="form.devices" placeholder="请选择设备" style="width: 140px" size="small" alue-key="id" @change="currentSel">
					<el-option
					v-for="item in devices"
					:key="item"
					:label="item"
					:value="item">
				</el-option>
			</el-select>
		</el-form-item>
		<el-form-item label="额外捕获数据">
			<el-checkbox-group v-model="form.type">
				<el-checkbox label="CPU Core Usage" name="type"></el-checkbox>
			</el-checkbox-group>
		</el-form-item>
		<el-form-item label="导出数据类型">
			<el-radio-group v-model="form.resource">
				<el-radio label="json"></el-radio>
				<el-radio label="Xls"></el-radio>
			</el-radio-group>
		</el-form-item>
		<el-form-item>
			<el-button  :disabled = "form.btnDisabled"  type="primary" @click="save_test('ruleForm')">立即创建</el-button>
			<el-button @click="close()">取消</el-button>
		</el-form-item>	

	</el-form>
</div>
</body>
<script type="text/javascript">
	var id = "{{id}}"
	var app = new Vue({
		el: '#app',
		data() {
			return {  
				devices: ["",""],
				   //多条件查询是否性别下拉框

		form: { //变量存储集合
			name: '',
			devices:'',
			app_name:'',
			date1: '',
			date2: '',
			save_flag: false,
			selected_device:'',
			type: [],
			resource: '',
			btnDisabled:false,

		},    
				ruleForm: {//如果有必要创建字典类型变量可以这样使用
					id:id,
					name: '',
					test_time: '',
					
				},
				rules: {
					name: [
					{ required: true, message: '请输入本次测试名称', trigger: 'blur' },
					{ min: 1, max:500 , message: '长度在 1 到 500 个字符', trigger: 'blur' }
					],

				}

			}
		},

		methods: {

			save_test() {
				test_time=this.ruleForm.test_time
				if(test_time.length<1){
					alert("名称不能为空")
					return
				}
				params={"test_name": this.ruleForm.name,"test_time": this.ruleForm.test_time, "is_save":this.form.save_flag,"other_data": this.form.type,"selected_device":this.selected_device,"save_type":this.form.resource,"app_name":this.form.app_name}
				this.form.btnDisabled=true;
				$.ajax({
					method:"post",
					url:"/save_test/"+id,
					data: JSON.stringify(params),
					dataType:"json",
				}).done((res) => {

					if(res["success"]==false){
						alert(res["error"])
					}
					else{
						alert("测试已完成")
					}
					this.form.btnDisabled=false;       
				})
			},

			getdevicesinfo(){
				$.ajax({
					method:"get",
					url:"/devices",
					dataType:"json",
				}).done((res) => {
					this.devices=res["devices"]

				})
			},
			currentSel(selVal) {
				this.selected_device = selVal
				console.log("选择的name为:" + this.selected_device);
				console.log(selVal);
			},
			close(){

				//do someting 
			}
		},
		created() {
			this.getdevicesinfo()


		},
		mounted() {

		}
	})


</script>
</html>

Main.py

import tornado.ioloop
import tornado.web
import os
from demo import *
import json
class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.render("index.html")
        # self.write("Hello, world")



class get_devicesHandler(tornado.web.RequestHandler):
    def get(self):
        devices=Get_deviceList()
        self.finish({
            "devices":devices
        })

class get_test_data(tornado.web.RequestHandler):
    def post(self,id):
        print("id: ",id)
        data=self.request.body.decode()
        data=json.loads(data)
        test_name,test_timer,is_save,other_get_data,selected_device,app_name,save_type=data["test_name"],data["test_time"],data["is_save"],data["other_data"],data["selected_device"],data["app_name"],data["save_type"]
        res=run_test_data(test_name=test_name,test_timer=test_timer,is_save=is_save,other_get_data=other_get_data,selected_device=selected_device,app_name=app_name,save_type=save_type)
        self.finish({
            "success":res
        })



def make_app():
    return tornado.web.Application([
        (r"/", MainHandler),
        (r"/devices", get_devicesHandler),
        (r"/save_test/([^/]+)", get_test_data),

    ],

        static_path=os.path.join(os.path.dirname(__file__), "static"),
        template_path=os.path.join(os.path.dirname(__file__), "templates"),
    )

if __name__ == "__main__":
    app = make_app()
    app.listen(8888)
    tornado.ioloop.IOLoop.current().start()

Demo.py
原汁原味代码,没有经过任何优化结构,适合初学者

# -*- coding: utf-8 -*-
import re
import subprocess
import time
import traceback
import os
import grpc
import perfdog_pb2_grpc
import perfdog_pb2
import threading
class Config():
    PERFDOGSERVER_PATH=r'C:\Users\Administrator\Desktop\perfdog\PerfDogService.exe' #PerfDogService的路径
    TOKEN='d9ba122735654db4a2417fe9e0cd74db981938ef5f8c415b1457ca60bb853813'#token
    DEVICES_UID="813QEDTE228ZK" #需要测试的手机序列号
    APP_NAME="天气" #需要测试的app名字
    TEST_TIMER=66 #需要测试时长
    LABEL="this is a label" #label内容
    NOTE="this is a note" #note内容
    CASENAME="天气测试数据" #用例描述 ,会覆盖相同用例名字的数据
    SAVEDATA_BEGINTIME=1 #导出到本地数据的开始时间(秒)
    SAVEDATA_ENDTIME=20 #导出到本地数据的结束时间(秒)
    OUTPUT="F:\\perfdog_service_output\\" #导出文件所保存的目录,
    SAVEDATA_TYPE=perfdog_pb2.EXPORT_TO_JSON #导出文件所保存的类型,


# 在代码里启动PerfDogService或手动启动PerfDogService
print("0.启动PerfDogService")
# 1.**************************填入PerfDogService的路径
perfDogService = subprocess.Popen(Config.PERFDOGSERVER_PATH)
# 等待PerfDogService启动完毕
time.sleep(5)
print("1.通过ip和端口连接到PerfDog Service")
options = [('grpc.max_receive_message_length', 100 * 1024 * 1024)]
channel = grpc.insecure_channel('127.0.0.1:23456', options=options)
print("2.新建一个stub,通过这个stub对象可以调用所有服务器提供的接口")
stub = perfdog_pb2_grpc.PerfDogServiceStub(channel)


def Get_deviceList():
    str_init = ' '
    all_info = os.popen('adb devices').readlines()
    for i in range(len(all_info)):
        str_init += all_info[i]
    devices_name = re.findall('\n(.+?)\t', str_init, re.S)
    print('所有设备名称:\n', devices_name)
    return devices_name#[{},{}]



def get_AppList():
    try:
        global stub
        devices = []
        deviceEventIterator = stub.startDeviceMonitor(perfdog_pb2.Empty())
        for deviceEvent in deviceEventIterator:
            # 从DeviceEvent中获取到device对象,device对象会在后面的接口中用到
            device = deviceEvent.device
            print("当前devices: ", device, " **** ", deviceEvent)
            devices.append(device.uid)
        print("手机列表", list(deviceEventIterator))
        print(devices)
        # return list(deviceEventIterator)

    except Exception as err:
        print("cuowu ",err)

def run_test_data(test_name=None,test_timer=None,is_save=None,other_get_data=None,selected_device=None,app_name=None,save_type=None):
    print("本次测试名称: ",test_name)
    try:
        run(DEVICES_UID=selected_device, APP_NAME=app_name, TEST_TIMER=test_timer, CASENAME=test_name,
            SAVEDATA_TYPE=save_type, IS_SAVE=is_save, OTHER_GETDATA=other_get_data)
        return True
    except Exception as err:
        print("DataError: ",err)
        return False
# 第一次运行demo前需要通过pip安装grpcio(1.23.0)和protobuf(3.10.0)
def run(DEVICES_UID=None,APP_NAME=None,TEST_TIMER=None, CASENAME=None,SAVEDATA_TYPE=None,IS_SAVE=None,OTHER_GETDATA=None):
    try:


        # 在代码里启动PerfDogService或手动启动PerfDogService
        print("0.启动PerfDogService")
        # 1.**************************填入PerfDogService的路径
        perfDogService = subprocess.Popen(Config.PERFDOGSERVER_PATH)
        # 等待PerfDogService启动完毕
        time.sleep(5)
        print("1.通过ip和端口连接到PerfDog Service")
        options = [('grpc.max_receive_message_length', 100 * 1024 * 1024)]
        channel = grpc.insecure_channel('127.0.0.1:23456', options=options)
        print("2.新建一个stub,通过这个stub对象可以调用所有服务器提供的接口")
        stub = perfdog_pb2_grpc.PerfDogServiceStub(channel)
        print("3.通过令牌登录,令牌可以在官网申请")
        userInfo = stub.loginWithToken(perfdog_pb2.Token(token=Config.TOKEN))
        print("UserInfo:\n", userInfo)
        print("4.启动设备监听器监听设备,每当设备插入和移除时会收到一个DeviceEvent")
        deviceEventIterator = stub.startDeviceMonitor(perfdog_pb2.Empty())
        for deviceEvent in deviceEventIterator:
            # 从DeviceEvent中获取到device对象,device对象会在后面的接口中用到
            device = deviceEvent.device

            print("当前devices: ",device," **** ",deviceEvent)
            # time.sleep(20000)
            if deviceEvent.eventType == perfdog_pb2.ADD and device.uid==DEVICES_UID:
                print("设备[%s:%s]插入\n" % (device.uid, perfdog_pb2.DEVICE_CONTYPE.Name(device.conType)))
                # 每台手机会返回两个conType不同的设备对象(USB的和WIFI的),如果是测有线,取其中的USB对象
                if device.conType == perfdog_pb2.USB:
                    print("5.初始化设备[%s:%s]\n" % (device.uid, perfdog_pb2.DEVICE_CONTYPE.Name(device.conType)))
                    stub.initDevice(device)
                    print("6.获取app列表")
                    appList = stub.getAppList(device)
                    #
                    apps = appList.app
                    app_index = 0
                    for app in apps:
                        print('%s: %s->%s' % (app_index, app.label, app.packageName))
                        if app.label==APP_NAME:
                            app_select=app_index
                            break
                        else:
                            app_index += 1
                            app_select=None
                    if app_select is None:app_select = int(input("未安装输入APP,请选择要测试App: "))

                    app = apps[app_select]

                    print("7.获取设备的详细信息")
                    deviceInfo = stub.getDeviceInfo(device)
                    print("8.开启性能数据项")
                    stub.enablePerfDataType(
                        perfdog_pb2.EnablePerfDataTypeReq(device=device, type=perfdog_pb2.NETWORK_USAGE))
                    if "CPU Core Usage" in OTHER_GETDATA:
                        stub.enablePerfDataType(
                            perfdog_pb2.EnablePerfDataTypeReq(device=device, type=perfdog_pb2.NORMALIZED_CPU_CORE_USAGE))

                    print("9.开始收集[%s:%s]的性能数据\n" % (app.label, app.packageName))
                    print(stub.startTestApp(perfdog_pb2.StartTestAppReq(device=device, app=app)))

                    req = perfdog_pb2.OpenPerfDataStreamReq(device=device)
                    perfDataIterator = stub.openPerfDataStream(req)

                    def perf_data_process():
                        for perfData in perfDataIterator:
                            print("数据: ",perfData)

                    threading.Thread(target=perf_data_process).start()
                    # 开始采集一些数据

                    time.sleep(int(TEST_TIMER))
                    print("10.设置label")
                    stub.setLabel(perfdog_pb2.SetLabelReq(device=device, label="I am a label"))
                    time.sleep(3)
                    print("11.添加批注")
                    stub.addNote(perfdog_pb2.AddNoteReq(device=device, time=5000, note="I am a note"))

                    if IS_SAVE:
                        print("12.上传和导出所有数据")
                        SAVEDATA_TYPE = perfdog_pb2.EXPORT_TO_JSON if SAVEDATA_TYPE == "json" else perfdog_pb2.EXPORT_TO_EXCEL
                        saveResult = stub.saveData(perfdog_pb2.SaveDataReq(
                            device=device,
                            caseName=CASENAME,  # web上case和excel的名字
                            uploadToServer=True,  # 上传到perfdog服务器
                            exportToFile=True,  # 保存到本地
                            outputDirectory=Config.OUTPUT,
                            dataExportFormat=SAVEDATA_TYPE
                        ))

                        print("保存结果:\n", saveResult)
                        print("12.上传和导出第5秒到20秒的数据")

                    print("13.停止测试")
                    stub.stopTest(perfdog_pb2.StopTestReq(device=device))
                    print("over")
                    break
                elif deviceEvent.eventType == perfdog_pb2.REMOVE:
                    print("设备[%s:%s]移除\n" % (device.uid, perfdog_pb2.DEVICE_CONTYPE.Name(device.conType)))
    except Exception as e:
        print("出现错误", e)
        traceback.print_exc()

if __name__ == '__main__':
    Get_deviceList()
#

在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值