平台支持monkey命令并过滤日志展示-安卓

前言:

做测试开发有一段时间了,总会碰到各种各样的问题,特此记录下,已做备忘;

任务目标:

最近接手的工作是需要在平台上集成monkey命令,支持命令执行并对日志进行筛选,将包含exception、crash和anr的记录筛选出来并统计出现次数;

任务整理与分析:

1.实际概况:

平台部署在linux服务器上;用户使用自己的电脑(win)连接真机(安卓)执行monkey命令;

2.思路分析:

1)服务器需要收集执行monkey命令的主机信息(主机名,IP地址等);

2)通过收集到的主机信息,控制主机执行monkey命令;

3.技术选型:

1)通过redis上传主机信息;

2)jsonp跨域传递命令,flask本地运行,接收命令并使用subprocess的Popen模块执行命令,执行后将结果返回给服务器并展示;

项目实施:

1.收集主机信息:

前提:用户本机需准备好python环境并安装flask和redis模块;本机配置好adb环境;

执行:运行提供的salve.py文件,上传主机信息;

代码:

 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 import flask,redis,os,socket from flask import request from subprocess import * import shlex,time,datetime,re,json def check_salve():     try:         import redis         import flask     except Exception as e:         if 'redis' in e:             quit('运行salve节点需要装redis模块')         elif 'flask' in e:             quit('运行salve节点需要装flask模块')   def reg_salve():     #把salve节点注册到服务端的redis里面     myname = socket.getfqdn(socket.gethostname())     myaddr = socket.gethostbyname(myname) #获取本机ip     r=redis.Redis(host='xxxx',port=6379)#这里写服务端redis的ip     salve_key = 'salves'     r.hset(salve_key,myname,myaddr) #set key,哈希类型,大key是 salves,小key是 salve节点的主机名,value是子节点ip check_salve() reg_salve()

 

2.平台展示主机信息提供执行入口:

从redis读取主机信息并展示,提供执行入口,选择本机信息后点击后跳转到执行页面;

3.执行过程:

1)执行页面显示IP地址和执行按钮;

html模板:

 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 <form id="mobile_form" enctype="multipart/form-data">     <div style="margin-bottom: 20px;">         <span style="color:greenyellow;font-weight: bolder">您的IP地址是:</span>         <span id="id_select_ip">             {{ ip }}         </span>     </div>     <span class="btn btn-primary" id="sub_command">开始执行</span> </form> <div id="result_show" class="hide">     <div style="margin:20px 0;">         <h5>执行结果如下所示:</h5>     </div>     <table class="table table-bordered">         <thead>             <tr>                 <th scope="col" style="text-align: center;width:680px;">执行命令</th>                 <th scope="col" style="text-align: center">Exception次数</th>                 <th scope="col" style="text-align: center">CRASH次数</th>                 <th scope="col" style="text-align: center">ANR次数</th>                 <th scope="col" style="text-align: center">执行结果</th>             </tr>         </thead>         <tbody>             <tr>                 <td id="command_td"></td>                 <td id="exception_td" style="text-align: center"></td>                 <td id="crash_td" style="text-align: center"></td>                 <td id="anr_td" style="text-align: center"></td>                 <td id="result_td" style="text-align: center"></td>             </tr>             <tr id="Except_tr" class="hide">                 <td colspan="5">                     <div id="except_span">                     </div>                 </td>             </tr>             <tr id="crash_tr" class="hide">                 <td colspan="5">                     <div id="crash_span">                     </div>                 </td>             </tr>             <tr id="anr_tr" class="hide">                 <td colspan="5">                     <div id="anr_span">                     </div>                 </td>             </tr>         </tbody>     </table> </div> <div class="mobile_cover hide" id="mobile_cover">     <div class="mobile_coverShow hide" id="mobile_coverShow">         <span class="notice_text">执行monkey测试中,请耐心等候......</span>     </div> </div>

2)点击执行按钮,通过jsonp将命令发送到本机;

JS代码:

 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 $("#sub_command").click(function () {     $("#mobile_cover").removeClass("hide");     $("#mobile_coverShow").removeClass("hide");     var ipadrress = $("#id_select_ip").text();     ipadrress=ipadrress.replace(/ /g,''); //过滤空格     var hide_val = $("#result_show").attr("class");     if(hide_val != "hide"){         $("#result_show").addClass("hide");     };     $.ajax({         url:"http://"+ipadrress+":8989/",         type:"get",         async:false,         dataType:"jsonp",         jsonp:"callback",         success:function (sout_dir) {             $("#command_td").text(sout_dir['command']);             $("#result_show").removeClass("hide");             $("#mobile_cover").addClass("hide");             $("#mobile_coverShow").addClass("hide");             var ecount = sout_dir['ERROR']['Exception_count'];             if(ecount == 0){                 $("#exception_td").text("0");             }else{                 $("#exception_td").text(ecount);                 $("#exception_td").addClass("except_detail");                 var except_infos = sout_dir['ERROR']['Exception_info'];                 if($("#except_span:has(div)").length){                     $("#except_span>div").remove();                 };                 for(except_info in except_infos ){                     $("#except_span").append("<div style='height:25px;color:red;'>" + except_infos[except_info] +"</div>");                 };             };             var Ccount = sout_dir['CRASH']['Ccount'];             if(Ccount==0){                 $("#crash_td").text("0");             }else{                 $("#crash_td").text(Ccount);                 $("#crash_td").addClass("crash_detail");                 var crash_infos = sout_dir['CRASH']['Crash_info'];                 if($("#crash_span:has(div)").length){                     $("#crash_span>div").remove();                 };                 for(crash_info in crash_infos ){                     $("#crash_span").append("<div style='height:25px;color:red;'>"+crash_infos[crash_info]+"</div>");                 };             };             var Acount = sout_dir['ANR']['Acount'];             if(Acount==0){                 $("#anr_td").text("0");             }else{                 $("#anr_td").text(Acount);                 $("#anr_td").addClass("anr_detail");                 var anr_infos = sout_dir['ANR']['Anr_info'];                 if($("#anr_span:has(div)").length){                     $("#anr_span>div").remove();                 };                 for(anr_info in anr_infos ){                     $("#anr_span").append("<div style='height:25px;color:red;'>"+anr_infos[anr_info]+"</div>");                 };             };             if (sout_dir['result_info']){                 $("#result_td").text(sout_dir['result_info'])             }else{                 $("#result_td").text("执行失败!");             }         }     }) }); $("#exception_td").click(function () {     var hide_ex = $("#Except_tr").attr("class");     if($("#except_span:has(div)").length){         if(hide_ex == "hide"){             $("#Except_tr").removeClass("hide");         }else{             $("#Except_tr").addClass("hide");         }     } }); $("#crash_td").click(function () {     var hide_cr = $("#crash_td").attr("class");     if($("#crash_span:has(div)").length){         if(hide_cr == "hide"){             $("#crash_tr").removeClass("hide");         }else{             $("#crash_tr").addClass("hide");         }     } }); $("#anr_td").click(function () {     var hide_anr = $("#anr_tr").attr("class");     if($("#anr_span:has(div)").length){          if(hide_anr == "hide"){             $("#anr_tr").removeClass("hide");         }else{             $("#anr_tr").addClass("hide");         }     } });

3)本机接收到命令后执行并过滤日志,返回数据;

salve.py完整代码:

 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 import flask,redis,os,socket from flask import request from subprocess import * import shlex,time,datetime,re,json def check_salve():     try:         import redis         import flask     except Exception as e:         if 'redis' in e:             quit('运行salve节点需要装redis模块')         elif 'flask' in e:             quit('运行salve节点需要装flask模块')   def reg_salve():     #把salve节点注册到服务端的redis里面     myname = socket.getfqdn(socket.gethostname())     myaddr = socket.gethostbyname(myname) #获取本机ip     r=redis.Redis(host='10.103.27.223',port=6379)#这里写服务端redis的ip     salve_key = 'salves'     r.hset(salve_key,myname,myaddr) #set key,哈希类型,大key是 salves,小key是 salve节点的主机名,value是子节点ip check_salve() reg_salve() server = flask.Flask(__name__) @server.route('/') def run():     callback = request.values.get('callback')     #判断真机连接状态     check_command = "adb devices"     check_command = shlex.split(check_command)     check_status = Popen(check_command,bufsize=0,shell=True,stdout=PIPE,stderr=PIPE)     check_infos = check_status.stdout.read()     check_infos_new = check_infos.strip().replace("\r","").replace("\t"," ").split("\n") #对获取到的数据进行过滤并按照\n切割成列表     command_old = "adb shell monkey -p com.laijin.simplefinance -s 500 --pct-touch 20  --pct-motion  20 --pct-trackball 20  --throttle 100 --ignore-crashes --ignore-timeouts --monitor-native-crashes -v -v 3000"     command = shlex.split(command_old)  # 将获取到的命令切割成列表     # 如果列表最后包含device字样,表示设备已连接成功     if "device" in check_infos_new[-1]:         print(u"---------设备连接成功,开始执行命令~!--------------")         res= Popen(command,bufsize=0,shell=True,stdout=PIPE,stderr=PIPE)         filelines = res.stdout.readlines()         # 准备日志分析         str1 = '.*ANR.*'         str2 = '.*CRASH.*'         str3 = '.*Exception.*'         str4 = '.*finished.*'         Acount, Ccount, Ecount = 0, 0, 0         Anr_info = []         Crash_info = []         Exception_info = []         result_info = ""         # 如获取到的信息为空,说明端口被占用,需杀掉占用端口的进程         if len(filelines) == 0:             query_command = "netstat -ano |findstr '5037'"             query_command = shlex.split(query_command)             query_info = Popen(query_command, bufsize=0, shell=True, stdout=PIPE, stderr=PIPE)             query_infos = query_info.stdout.readlines()             for q in query_infos:                 q_list = q.strip().split(" ")                 while '' in q_list:                     q_list.remove('')                 if q_list[-2] == "LISTENING":                     result_info = u"端口号被占用,请杀死进程号为%s的进程后重试!"%q_list[-1]             Exception_info = ""             Ecount = 0             Anr_info = ""             Acount = 0             Crash_info = ""             Ccount = 0         else:             #执行monkey命令并返回             for i in filelines:                 i = i.strip()                 if re.match(str1, i):                     print u'测试过程中出现程序无响应,具体内容为:\n', i                     Anr_info.append(i)                     Acount += 1                 elif re.match(str2, i):                     print u'测试过程中出现程序崩溃,具体内容为:\n', i                     Crash_info.append(i)                     Ccount += 1                 elif re.match(str3, i):                     print u'测试过程中出现程序异常,异体内容为:\n', i                     Exception_info.append(i)                     Ecount += 1                 # 如果存在任何异常则不用判断日志是否正常完成             if Acount or Ccount or Ecount == 0:                 for i in filelines:                     if re.match(str4, i):                         print u'测试过程中正常'                         result_info = u"测试过程中正常"     else:         print u"------------设备连接异常,请校验后重试!---------------"         result_info = u"设备连接异常,请校验后重试!"         Exception_info = ""         Ecount=0         Anr_info = ""         Acount=0         Crash_info=""         Ccount=0     sout_dir = {         'ERROR':{             "Exception_info":Exception_info,             "Exception_count":Ecount         },         'ANR':{             "Anr_info":Anr_info,             "Acount":Acount         },         'CRASH':{             "Crash_info":Crash_info,             "Ccount":Ccount         },         'result_info':result_info,         'command':command_old     }     return ''.join([         callback,         '(',         json.dumps(sout_dir),         ');'     ]) server.run(host='0.0.0.0',port=8989)

4)平台接收到数据,通过html模板展示;

遗留问题:

1. 端口被占用时,应自动杀死占用端口的进程后重新执行进程;(目前发现占用端口的为360手机助手,无法单独杀死进程,暂放弃);

2.本地修改salve.py文件后,需手动杀死adb.exe进程,再重新运行salve.py文件;

最终效果图:

平台支持monkey命令并过滤日志展示-安卓

平台支持monkey命令并过滤日志展示-安卓

上一篇:Android 系統自動化Monkey測試


下一篇:adb与monkey命令使用 第二章