第6.3节 iOS Agent开发<一> - 教程

iOS由于其系统的特殊性,很多处理需要在Mac电脑上完毕,所以也就无法部署到容器上。于是将iOS Agent开发成单独的服务,由Django开发,对外提供http请求接口,部署到Mac机器上以响应精准测试平台的请求。

6.3.1 Agent整体架构介绍

1,Agent能力架构图

2,功能模块介绍

(1)OC项目模块
针对 Object-C任务的生成覆盖率相关的处理,包括Clone项目,构建项目(需要构建后的Class文件),上传覆盖率记录(此文件为打包后的zip文件),XcodeCoverage生成覆盖率报告,以及解析覆盖率报告数据(取行覆盖率相关内容),下载覆盖率报告,以便精准测试平台展示。
  • 构建项目生成报告类iOSCovOperations:
# coding=utf-8
import os,shutil
import zipfile
from iOSOCAndSwiftAgent.UtilsOperation.const import DEBUG_SERVICE
class iOSCovOperations(object):
    """
    iOS覆盖率相关操作
    """
    def getGcnoFilePath(self,propath):
        """获取项目构建后的gcno文件路径"""
        envfilepath=propath+"Pods/XcodeCoverage/env.sh";
        gcnopath=""
        rfile=open(envfilepath)
        line=rfile.readline()
        while line:
            if line.find("OBJECT_FILE_DIR_normal")>-1:
                gcnopath=line[line.index("=")+2:line.rindex("\"")]
                break
            line=rfile.readline()
        gcnopath=gcnopath+"/arm64"
        return gcnopath
    def copygcnofile(self,propath):
        """
        拷贝构建后的文件
        :param propath:
        :return:
        """
        # 4,拷贝构建后的代码到指定路径
        gcnopath=propath+"Pods/XcodeCoverage/gcnofolder"
        if os.path.exists(gcnopath):
            shutil.rmtree(gcnopath)
        shutil.copytree(self.getGcnoFilePath(propath),gcnopath)
    def buildPorject(self,propath):
        """构建iOS项目"""
        curpath = os.getcwd();
        scriptpath=""
        print("当前目录是:"+curpath)
        if DEBUG_SERVICE.find("Agent")>-1:
            #写死Agent上的路径
            scriptpath="/Users/****/ScriptFiles/"
        else:
            #本机debug
            scriptpath="/Users/*****/ScriptFiles/"
        os.chdir(propath)
        # 1,pod安装
        os.system("pod install")
        # 2,拷贝构建脚本
        os.system("cp "+scriptpath+"EnterpriseExportOptionsPlist.plist "+propath+"EnterpriseExportOptionsPlist.plist")
        os.system("cp "+scriptpath+"xcodebuild.sh "+propath+"xcodebuild.sh")
        os.system("chmod 777 "+propath+"xcodebuild.sh")
        # 获取项目名称
        proname=""
        for f in os.listdir(propath):
            print("file name:"+f)
            if f.find("xcworkspace")>-1:
                proname=f[0:f.index(".")]
                break
        #3,构建项目
        if len(proname)>0:
            os.system(propath+"xcodebuild.sh "+proname+" &")
        else:
            print("项目中没有项目文件,请检测项目内容是否有遗漏!")
        # # 4,拷贝构建后的代码到指定路径
        # gcnopath=propath+"Pods/XcodeCoverage/gcnofolder"
        # if os.path.exists(gcnopath):
        #     shutil.rmtree(gcnopath)
        # shutil.copytree(self.getGcnoFilePath(propath),gcnopath)
        #5,切换回当前目录
        os.chdir(curpath)
        return True
    def createiOSCovReport(self,propath,covdatapath):
        """根据覆盖率数据文件,生成覆盖率执行"""
        #1,拼出gcno文件路径
        gcnopath=propath+"Pods/XcodeCoverage/gcnofolder"
        #2,拷贝构建后的文件到覆盖率文件中
        for fileName in os.listdir(gcnopath):
            srcFile = os.path.join(gcnopath,fileName)
            tarFile = os.path.join(covdatapath,fileName)
            shutil.copyfile(srcFile,tarFile)
        #3,覆盖率报告路径
        covreppath=propath+"Pods/XcodeCoverage/tempreports/"+covdatapath[covdatapath.rindex("/")+1:len(covdatapath)]
        if not os.path.exists(covreppath):
            os.makedirs(covreppath)
        #4,重写env.sh文件内容
        envfile=propath+"Pods/XcodeCoverage/env.sh"
        os.remove(envfile)
        wrfile=open(envfile,"w+")
        #写入BUILT_PRODUCTS_DIR
        wrfile.write("export BUILT_PRODUCTS_DIR=\""+covreppath+"\"\t\n");
        #写入export CURRENT_ARCH=""
        wrfile.write("export CURRENT_ARCH=\"\"\t\n");
        #写入OBJECT_FILE_DIR_normal
        wrfile.write("export OBJECT_FILE_DIR_normal=\""+covdatapath+"\"\t\n");
        #写入OBJROOT
        wrfile.write("export OBJROOT=\"\"\t\n");
        #写入SRCROOT
        wrfile.write("export SRCROOT=\""+propath+"\"\t\n");
        wrfile.close()
        #5,执行生成报告命令
        getcovcmd=propath+"Pods/XcodeCoverage/getcov"
        os.system(getcovcmd)
        return covreppath
    def createXcodeCoverageReport(self,propath,covdatapath):
        """根据覆盖率数据,合并报告生成最终的报告"""
        gedatapath = propath + "Pods/XcodeCoverage/coverage"
        if not os.path.exists(gedatapath):
            os.makedirs(gedatapath)
        xcreppath = propath + "Pods/XcodeCoverage/report"
        if not os.path.exists(xcreppath):
            os.makedirs(xcreppath)
        #遍历覆盖率数据文件夹,分别生成报告
        count=0
        for file in os.listdir(covdatapath):
            if file.find("arm")>-1:
                tempcovpath=covdatapath+"/"+file
                print("数据文件:"+tempcovpath)
                temprep=self.createiOSCovReport(propath,tempcovpath)
                #拷贝生成的Coverage.info文件
                os.system("cp "+temprep+"/lcov/Coverage.info "+gedatapath+"/Coverage"+str(count)+".info")
                count=count+1
        # 生成整体覆盖率的报告
        mercmd=propath+"Pods/XcodeCoverage/mergecov"
        curpath = os.getcwd();
        if DEBUG_SERVICE.find("Agent")>-1:
            #写死Agent上的路径
            scriptpath="/Users/****/ScriptFiles/"
        else:
            #本机debug
            scriptpath="/Users/*****/ScriptFiles/"
        os.system("cp "+scriptpath+"mergecov "+mercmd)
        os.system("chmod 777 "+mercmd)
        os.system(mercmd)
        # 将生成的测试报告打包
        output_filename=propath+"report.zip"
        zipf = zipfile.ZipFile(output_filename, 'w')
        pre_len = len(os.path.dirname(xcreppath))
        for parent, dirnames, filenames in os.walk(xcreppath):
            for filename in filenames:
                pathfile = os.path.join(parent, filename)
                arcname = pathfile[pre_len:].strip(os.path.sep)
                zipf.write(pathfile, arcname)
        zipf.close()
        return xcreppath
    def getCovData(self,reppath):
        """
        获取覆盖率报告中的数据
        :param reppath:
        :return:
        """
        file=open(reppath,"r")
        line=file.readline()
        covlines=""
        totallines=""
        covlinerate=""
        covfuns=""
        totalfuns=""
        covfunrate=""
        while line:
            line=file.readline()
            if line.find("headerCovTableEntry")>-1:
                if len(covlines)==0:
                    covlines=line[line.index(">")+1:line.rindex("<")]
                    continue
                if len(covlines)>0 and len(totallines)==0:
                    totallines=line[line.index(">")+1:line.rindex("<")]
                    continue
                if line.find("%")>-1 and len(covlinerate)==0:
                    covlinerate=line[line.index(">")+1:line.rindex("<")]
                    continue
                if len(covfuns)==0:
                    covfuns=line[line.index(">")+1:line.rindex("<")]
                    continue
                if len(covfuns)>0 and len(totalfuns)==0:
                    totalfuns=line[line.index(">")+1:line.rindex("<")]
                    continue
                if line.find("%")>-1 and len(covfunrate)==0:
                    covfunrate=line[line.index(">")+1:line.rindex("<")]
                    break
        repdata={"totalines":totallines,"covlines":covlines,"covlinerate":covlinerate,"totalfuns":totalfuns,"covfuns":covfuns,"covfunrate":covfunrate}
        return repdata
if __name__ == '__main__':
    iosopr=iOSCovOperations()
    propath="/Users/****/ghdropmenudemo/"
covdatapath="/Users/****/ghdropmenudemo/Pods/XcodeCoverage/tempcovdata"
    iosopr.getCovData(propath+"Pods/XcodeCoverage/report/index.html")

此方案关键在构建项目上比较耗时,行采取在公司打包平台打包后上传构建资料,以节省时间;同时上传的覆盖率数据文件也较大,由于公司的项目是Swift的,这个模块也没有再继续优化,使用测试Demo验证,相关功能没有问题
posted @ 2025-10-23 14:55  yjbjingcha  阅读(1)  评论(0)    收藏  举报