JacocoController.java
package com.test.testmanagement.controller;
import com.test.testmanagement.controller.jacococlient.DownloadRecursiveFolderFromSFTP;
import com.test.testmanagement.controller.jacococlient.ExecutionDataClient;
import com.test.testmanagement.controller.jacococlient.MergeDump;
import com.test.testmanagement.controller.jacococlient.ReportGenerator;
import com.test.testmanagement.model.ProjectInfo;
import com.test.testmanagement.model.TestProject;
import com.test.testmanagement.service.*;
import org.apache.tomcat.util.http.fileupload.FileUtils;
import org.jacoco.core.tools.ExecDumpClient;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.io.File;
import java.io.IOException;
import java.util.*;
@RestController
@RequestMapping(value = "/api/jacoco")
public class JacocoController {
private static String LOCAL_DATA_STORE = "/opt/app/mskyprocess/file/jacocoCode/"; // 源码路径
private static String LOCAL_REPORT_DIR = "/opt/app/mskyprocess/file/jacocoReport/"; // 报告路径
//jenkins机器
static String SFTPHOST = "10.5.144.210"; // SFTP Host Name or SFTP Host IP Address
static String SFTPPORT = "22"; // SFTP Port Number
static String SFTPUSER = "root"; // User Name
static String SFTPPASS = "ume@20150916"; // Password
/*Spring-Mybatis*/
@Autowired
private TestProjectService service;
@Autowired
private ProjectInfoService projectInfoService;
static String testname = "2019-07-29";
static Element element_project_name = null;
static String pom_project_name = "";
static Document root_doc = null;
/**
* 清空tomcat服务器的覆盖率文件
* @param JACOCO_SERVER_IP tomcat服务器ip
* @param JACOCO_SERVER_PORT 8080 or 8044
* @return
* http://127.0.0.1:8180/api/jacoco/cleantcpserver?ip=10.237.78.108&port=8080
*/
@RequestMapping(value = "/cleantcpserver", method = RequestMethod.GET)
public boolean CleanTcpServer(@RequestParam(value = "ip", required = false) String JACOCO_SERVER_IP,
@RequestParam(value = "port") String JACOCO_SERVER_PORT,
@RequestParam(value = "projectname") String PROJECT_NAME
) {
boolean result = true;
Map<String, Object> requestParamMap = new HashMap<String, Object>();
requestParamMap.put("projectname", PROJECT_NAME);
// 取tcpserver的ip 数据库表:projectinfo
List<ProjectInfo> list = projectInfoService.findJacocoserverIpbyProjectname(requestParamMap);
// JACOCO_SERVER_IP = list.get(0).getJacocoserverip();
String[] ipInfo = list.get(0).getJacocoserverip().split(",");
ExecDumpClient client = new ExecDumpClient();
client.setReset(true);
client.setDump(false);
//目标机器的ip和端口,对应着运行程序时javaagent参数里的ip和端口
try {
for (String ipinfo : ipInfo) {
JACOCO_SERVER_IP = ipinfo.split(":")[0];
JACOCO_SERVER_PORT = ipinfo.split(":")[1];
client.dump(JACOCO_SERVER_IP, Integer.parseInt(JACOCO_SERVER_PORT));
}
} catch (IOException e) {
result = false;
e.printStackTrace();
}
System.out.println("清除就的覆盖率文件");
return result;
}
/**
* 获取单个工程
* 覆盖率、覆盖率报告、解析html存到DB
* @param JACOCO_SERVER_IP tomcat服务器ip
* @param JACOCO_SERVER_PORT 8080 or 8044
* @param PROJECT_NAME 工程名 umemid-airport
* @return
* http://127.0.0.1:8180/api/jacoco/jacocoReport?ip=10.237.78.108&port=8080&projectname=umemid-airport
*/
@RequestMapping(value = "/jacocoReport", method = RequestMethod.GET)
public boolean JacocoReport(@RequestParam(value = "ip", required = false) String JACOCO_SERVER_IP,
@RequestParam(value = "port") String JACOCO_SERVER_PORT,
@RequestParam(value = "projectname") String PROJECT_NAME) {
boolean result = true;
boolean hasParenetPom = true;
Map<String, Object> requestParamMap = new HashMap<String, Object>();
requestParamMap.put("projectname", PROJECT_NAME);
// 取tcpserver的ip 数据库表:projectinfo
List<ProjectInfo> list = projectInfoService.findJacocoserverIpbyProjectname(requestParamMap);
String[] ipInfo = list.get(0).getJacocoserverip().split(",");
List<String> JACOCO_SERVER_IP_LIST = new ArrayList<>();
for (String ip : ipInfo) {
JACOCO_SERVER_IP_LIST.add(ip);
}
// 判断工程子pom.xml
if (PROJECT_NAME.contains("umemid-") ||PROJECT_NAME.contains("umepsr-") ) {
System.out.println(PROJECT_NAME +"有子pom");
}
else {
hasParenetPom = false;
}
try {
long for_startTime = System.currentTimeMillis();
//Step1:创建本地src以及target目录
init_Project_Src_Classes(PROJECT_NAME);
long for_startTime2 = System.currentTimeMillis();
System.out.println("创建本地目录时间:" + (for_startTime2 - for_startTime));
//Step2:sftp下载相关src及target目录下的文件
download_Src_Classes(SFTPHOST,SFTPPORT,SFTPUSER,SFTPPASS,hasParenetPom,PROJECT_NAME);
long for_startTime3 = System.currentTimeMillis();
System.out.println("下载src和claess文件时间:" + (for_startTime3 - for_startTime2));
//Step3 socket_client后去集成测试exec文件 ExecutionDataClient.getJacocoExec(JACOCO_SERVER_IP,JACOCO_SERVER_PORT,LOCAL_DATA_STORE+PROJECT_NAME+"/jacoco-it.exec");
//2020-04-08 改成多ip支持获取覆盖率,参数为ip+端口
String pathdir = ExecutionDataClient.downLoadDump(JACOCO_SERVER_IP_LIST, LOCAL_DATA_STORE + PROJECT_NAME);
// 把各个机器拿得exec文件,合并成jacoco.exec
MergeDump.executeMerge(LOCAL_DATA_STORE + PROJECT_NAME);
long for_startTime4 = System.currentTimeMillis();
//Step4 生成集成测试覆盖率报告
ReportGenerator.generateItCoverageReport(
LOCAL_DATA_STORE+PROJECT_NAME,
LOCAL_REPORT_DIR+PROJECT_NAME,
"jacoco.exec",
"target",
"src/main/java",
"it-coveragereport");
long for_startTime5 = System.currentTimeMillis();
System.out.println("生成覆盖率报告时间:" + (for_startTime5 - for_startTime4));
//Step5 存取html解析到db
JacocoReportToDB(LOCAL_REPORT_DIR + PROJECT_NAME + "/it-coveragereport/");
long for_endTime = System.currentTimeMillis();
System.out.println("解析html的时间:" + (for_endTime - for_startTime5));
long consumetime = for_endTime - for_startTime;
System.out.println("覆盖率获取时间:" + consumetime);
} catch (Exception e) {
result = false;
e.printStackTrace();
}
return result;
}
/**
* 初始化删除创建工程的src、classes存储路径
* @param projectname
*/
public static void init_Project_Src_Classes(String projectname){
try {
//如果文件夹不存在则创建
File project_file =new File(LOCAL_DATA_STORE + projectname);
File project_file_src = new File(LOCAL_DATA_STORE + projectname+"/src");
File project_file_classes = new File(LOCAL_DATA_STORE + projectname+"/target/classes");
File project_reportfile = new File(LOCAL_REPORT_DIR + projectname);
if (!project_file.exists() && !project_file.isDirectory())
{
System.out.println(project_file+"不存在");
System.out.println("创建多级目录"+project_file_src+";"+project_file_classes);
project_file_src.mkdirs();
project_file_classes.mkdirs();
}else
{
//删除文件夹
System.out.println("删除文件夹"+project_file);
FileUtils.deleteDirectory(project_file);
System.out.println("创建多级目录"+project_file_src+";"+project_file_classes);
project_file_src.mkdirs();
project_file_classes.mkdirs();
}
if (!project_reportfile.exists() && !project_reportfile.isDirectory()) {
System.out.println(project_reportfile + "不存在");
project_reportfile.mkdirs();
}else {
FileUtils.deleteDirectory(project_reportfile); }
// //创建文件夹
// FileUtils.
}catch (Exception e){
}finally {
}
}
/**
* 从打包Jenkins通过SFTF 获取class文件 & 源码
* @param ip jenkins地址
* @param port 22
* @param username 登录用户 root or jboss5
* @param password 密码
* @param hasParenetPom 是否有子工程 true:umemid-airport false:没有
* @param PROJECT_NAME 工程名
*/
public static void download_Src_Classes(String ip,String port,String username,String password,boolean hasParenetPom,String PROJECT_NAME){
/*
/opt/app/jenkins/workspace/Pipeline_umemid-airport/umemid-airport/src
/opt/app/jenkins/workspace/Pipeline_umemid-airport/umemid-airport/target/classes
*/
String srcDir ="";
if (hasParenetPom){
srcDir = "/opt/app/jenkins/workspace/PipelineCI_"+PROJECT_NAME+"/"+PROJECT_NAME+"/src";
System.out.println("有子pom");
}else{
srcDir = "/opt/app/jenkins/workspace/PipelineCI_"+PROJECT_NAME+"/"+"src";
}
String local_srcDir = LOCAL_DATA_STORE+PROJECT_NAME+"/src";
String classesDir = "";
if(hasParenetPom){
classesDir = "/opt/app/jenkins/workspace/PipelineCI_"+PROJECT_NAME+"/"+PROJECT_NAME+"/target/classes";
}else {
classesDir = "/opt/app/jenkins/workspace/PipelineCI_"+PROJECT_NAME+"/"+"target/classes";
}
String local_classesDir = LOCAL_DATA_STORE+PROJECT_NAME+"/target/classes";
// String unittest_jacocoexec = "/opt/app/jenkins/workspace/Pipeline_"+PROJECT_NAME+"/"+PROJECT_NAME+"/target/jacoco.exec";
// String local_jacoco_utexec = LOCAL_DATA_STORE+PROJECT_NAME+"/target/jacoco-ut.exec";
DownloadRecursiveFolderFromSFTP.downloadDir(ip,port,username,password,srcDir,local_srcDir);
DownloadRecursiveFolderFromSFTP.downloadDir(ip,port,username,password,classesDir,local_classesDir);
// DownloadRecursiveFolderFromSFTP.downloadDir(ip,port,username,password,unittest_jacocoexec,local_jacoco_utexec);
}
/**
* 存取html解析到db
* @param htmlPath index.html路径
* @throws Exception
* http://127.0.0.1:8180/api/jacoco/jacocoParseHtmltoDB?htmlPath=/data/Mock/testmanagement/file/umemid-airport/it-coveragereport/
*/
public void JacocoReportToDB(String htmlPath) throws Exception {
File root_input = new File(htmlPath + "index.html");
/*开始读取Jacoco覆盖率并存储到数据库*/
TestProject testProject = new TestProject();
testProject.setTestname(testname);
Date d = new Date();
testProject.setStarttime(d);//获取starttime
root_doc = Jsoup.parse(root_input, "UTF-8");
element_project_name = root_doc.select("H1").first();//查找第一个H1元素
pom_project_name = element_project_name.text();
testProject.setProjectname(pom_project_name);
Integer project_id = service.insertTestProject(testProject);//插入项目
//1)project_name
//UmeTest
System.out.println(pom_project_name);
//2)包名
/*
com.umetrip.unittest.plugins.dbReporter
com.umetrip.unittest
com.umetrip.unittest.testcase
com.umetrip.unittest.plugins.htmlReporter
*/
Elements elements_index_package = root_doc.getElementsByClass("el_package");
Integer i = 0;
Element tfoot = root_doc.getElementsByTag("tfoot").first();
// Element tr = tfoot.getElementsByTag("tr").first();
Elements tds = tfoot.getElementsByTag("td");
StringBuffer total_coverage = new StringBuffer();
total_coverage.append(pom_project_name + "#");
for (Element td : tds) {
if (td.text().contains("Total")) {
continue;
}
//System.out.println(td.text());
total_coverage.append(td.text());
total_coverage.append("#");
}
System.out.println("================" + pom_project_name + "项目整体覆盖");
System.out.println(total_coverage);
TestProject someProjectTotalSummary = new TestProject();
someProjectTotalSummary.setId(project_id);
someProjectTotalSummary.setMissedinstructions(total_coverage.toString().split("#")[1]);
someProjectTotalSummary.setInstructioncoverage(total_coverage.toString().split("#")[2]);
someProjectTotalSummary.setMissedbranches(total_coverage.toString().split("#")[3]);
someProjectTotalSummary.setBranchcoverage(total_coverage.toString().split("#")[4]);
someProjectTotalSummary.setMissedcxty(total_coverage.toString().split("#")[5]);
someProjectTotalSummary.setTotalcxty(total_coverage.toString().split("#")[6]);
someProjectTotalSummary.setMissedlines(total_coverage.toString().split("#")[7]);
someProjectTotalSummary.setTotallines(total_coverage.toString().split("#")[8]);
someProjectTotalSummary.setMissedmethods(total_coverage.toString().split("#")[9]);
someProjectTotalSummary.setTotalmethods(total_coverage.toString().split("#")[10]);
someProjectTotalSummary.setMissedclasses(total_coverage.toString().split("#")[11]);
someProjectTotalSummary.setTotalclasses(total_coverage.toString().split("#")[12]);
service.updateTestProjectById(someProjectTotalSummary);
}
}
DownloadRecursiveFolderFromSFTP.java
package com.test.testmanagement.controller.jacococlient;
import com.jcraft.jsch.*;
import java.io.File;
import java.util.Vector;
/**
* @author http://kodehelp.com
*
*/
public class DownloadRecursiveFolderFromSFTP {
static ChannelSftp channelSftp = null;
static Session session = null;
static Channel channel = null;
static String PATHSEPARATOR = "/";
public static void downloadDir(String ip,String port,String username,String password,String downloadDir,String localDir){
try {
JSch jsch = new JSch();
session = jsch.getSession(username, ip, Integer.parseInt(port));
session.setPassword(password);
java.util.Properties config = new java.util.Properties();
config.put("StrictHostKeyChecking", "no");
session.setConfig(config);
session.connect(); // Create SFTP Session
channel = session.openChannel("sftp"); // Open SFTP Channel
channel.connect();
channelSftp = (ChannelSftp) channel;
channelSftp.cd(downloadDir); // Change Directory on SFTP Server
recursiveFolderDownload(downloadDir, localDir); // Recursive folder content download from SFTP server
} catch (Exception ex) {
ex.printStackTrace();
} finally {
if (channelSftp != null)
channelSftp.disconnect();
if (channel != null)
channel.disconnect();
if (session != null)
session.disconnect();
}
}
/**
* @param args
*/
public static void main(String[] args) {
String SFTPHOST = "10.5.144.210"; // SFTP Host Name or SFTP Host IP Address
int SFTPPORT = 22; // SFTP Port Number
String SFTPUSER = "root"; // User Name
String SFTPPASS = "ume@20150916"; // Password
String SFTPWORKINGDIR = "/opt/app/Python-2.7.15"; // Source Directory on SFTP server
String LOCALDIRECTORY = "/data/file"; // Local Target Directory
String SRCDIR = "";
try {
JSch jsch = new JSch();
session = jsch.getSession(SFTPUSER, SFTPHOST, SFTPPORT);
session.setPassword(SFTPPASS);
java.util.Properties config = new java.util.Properties();
config.put("StrictHostKeyChecking", "no");
session.setConfig(config);
session.connect(); // Create SFTP Session
channel = session.openChannel("sftp"); // Open SFTP Channel
channel.connect();
channelSftp = (ChannelSftp) channel;
channelSftp.cd(SFTPWORKINGDIR); // Change Directory on SFTP Server
recursiveFolderDownload(SFTPWORKINGDIR, LOCALDIRECTORY); // Recursive folder content download from SFTP server
} catch (Exception ex) {
ex.printStackTrace();
} finally {
if (channelSftp != null)
channelSftp.disconnect();
if (channel != null)
channel.disconnect();
if (session != null)
session.disconnect();
}
}
/**
* This method is called recursively to download the folder content from SFTP server
*
* @param sourcePath
* @param destinationPath
* @throws SftpException
*/
@SuppressWarnings("unchecked")
private static void recursiveFolderDownload(String sourcePath, String destinationPath) throws SftpException {
Vector<ChannelSftp.LsEntry> fileAndFolderList = channelSftp.ls(sourcePath); // Let list of folder content
//Iterate through list of folder content
for (ChannelSftp.LsEntry item : fileAndFolderList) {
if (!item.getAttrs().isDir()) { // Check if it is a file (not a directory).
if (!(new File(destinationPath + PATHSEPARATOR + item.getFilename())).exists()
|| (item.getAttrs().getMTime() > Long
.valueOf(new File(destinationPath + PATHSEPARATOR + item.getFilename()).lastModified()
/ (long) 1000)
.intValue())) { // Download only if changed later.
new File(destinationPath + PATHSEPARATOR + item.getFilename());
channelSftp.get(sourcePath + PATHSEPARATOR + item.getFilename(),
destinationPath + PATHSEPARATOR + item.getFilename()); // Download file from source (source filename, destination filename).
}
} else if (!(".".equals(item.getFilename()) || "..".equals(item.getFilename()))) {
new File(destinationPath + PATHSEPARATOR + item.getFilename()).mkdirs(); // Empty folder copy.
recursiveFolderDownload(sourcePath + PATHSEPARATOR + item.getFilename(),
destinationPath + PATHSEPARATOR + item.getFilename()); // Enter found folder on server to read its contents and create locally.
}
}
}
}
ExecutionDataClient.java
package com.test.testmanagement.controller.jacococlient;
/*******************************************************************************
* Copyright (c) 2009, 2019 Mountainminds GmbH & Co. KG and Contributors
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Marc R. Hoffmann - initial API and implementation
*
*******************************************************************************/
//package org.jacoco.examples;
import org.jacoco.core.data.ExecutionDataWriter;
import org.jacoco.core.runtime.RemoteControlReader;
import org.jacoco.core.runtime.RemoteControlWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.util.List;
/**
* This example connects to a coverage agent that run in output mode
* <code>tcpserver</code> and requests execution data. The collected data is
* dumped to a local file.
*/
public final class ExecutionDataClient {
private static final String DESTFILE = "jacoco-client.exec";
private static final String ADDRESS = "10.237.78.108";
private static final int PORT = 8080;
public static void getJacocoExec(String ip, String port, String destFile) {
try {
final FileOutputStream localFile = new FileOutputStream(destFile);
final ExecutionDataWriter localWriter = new ExecutionDataWriter(
localFile);
// Open a socket to the coverage agent:
final Socket socket = new Socket(InetAddress.getByName(ip), Integer.parseInt(port));
final RemoteControlWriter writer = new RemoteControlWriter(
socket.getOutputStream());
final RemoteControlReader reader = new RemoteControlReader(
socket.getInputStream());
reader.setSessionInfoVisitor(localWriter);
reader.setExecutionDataVisitor(localWriter);
// Send a dump command and read the response:
writer.visitDumpCommand(true, false);
if (!reader.read()) {
throw new IOException("Socket closed unexpectedly.");
}
socket.close();
localFile.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
// socket.close();
}
}
// 相比较getJacocoExec是多个ip和端口配合的获取exec文件 2020-04-08 zmy
public static String downLoadDump(List<String> ipPort, String destFile) {
// int flag = 0; // 可能为了比增量
// String getDirName = getDirNum(destFile); // 可能为了比增量
String result = "";
for (String getIPPort : ipPort) {
String dirName = destFile + "/" + getIPPort.replace(":", "_") + "_jacoco.exec";
String ip = getIPPort.split(":")[0];
int port = Integer.parseInt(getIPPort.split(":")[1]);
try {
final FileOutputStream localFile = new FileOutputStream(dirName);
final ExecutionDataWriter localWriter = new ExecutionDataWriter(
localFile);
// Open a socket to the coverage agent:
final Socket socket = new Socket(InetAddress.getByName(ip), port);
final RemoteControlWriter writer = new RemoteControlWriter(
socket.getOutputStream());
final RemoteControlReader reader = new RemoteControlReader(
socket.getInputStream());
reader.setSessionInfoVisitor(localWriter);
reader.setExecutionDataVisitor(localWriter);
// Send a dump command and read the response:
writer.visitDumpCommand(true, false);
if (!reader.read()) {
throw new IOException("Socket closed unexpectedly.");
}
socket.close();
localFile.close();
System.out.println("Download file success....");
System.out.println("File path is : " + dirName);
// flag++; // 可能为了比增量
result = result + dirName;
} catch (IOException e) {
e.printStackTrace();
}
}
/*if (flag != 0) {
return getDirName;
} else {
return "path";
}*/ // 可能为了比增量
return result;
}
/**
* @param path
* @return
*/
private static String getDirNum(String path) {
File file = null;
String newPath = "";
for (int i = 0; ; i++) {
newPath = path + "/" + i;
// newPath = path;
file = new File(newPath);
System.out.println("new path = " + newPath);
if (!file.isDirectory()) {
System.out.println(newPath);
file.mkdirs();
break;
}
}
return newPath;
}
/**
* Starts the execution data request.
*
* @param args
* @throws IOException
*/
public static void main(final String[] args) throws IOException {
final FileOutputStream localFile = new FileOutputStream(DESTFILE);
final ExecutionDataWriter localWriter = new ExecutionDataWriter(
localFile);
// Open a socket to the coverage agent:
final Socket socket = new Socket(InetAddress.getByName(ADDRESS), PORT);
final RemoteControlWriter writer = new RemoteControlWriter(
socket.getOutputStream());
final RemoteControlReader reader = new RemoteControlReader(
socket.getInputStream());
reader.setSessionInfoVisitor(localWriter);
reader.setExecutionDataVisitor(localWriter);
// Send a dump command and read the response:
writer.visitDumpCommand(true, false);
if (!reader.read()) {
throw new IOException("Socket closed unexpectedly.");
}
socket.close();
localFile.close();
}
private ExecutionDataClient() {
}
}
MergeDump.java
package com.test.testmanagement.controller.jacococlient;
import org.jacoco.core.tools.ExecFileLoader;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class MergeDump {
/* private final String path ;
private final File destFile ;
public MergeDump(String path){
this.path = path;
this.destFile = new File(path + "/jacoco.exec");
}*/
private static List<File> fileSets(String dir){
System.out.println(dir);
List<File> fileSetList = new ArrayList<File>();
File path = new File(dir);
if ( ! path.exists() ){
System.out.println("No path name is :" + dir);
return null;
}
File[] files = path.listFiles();
try {
if (files == null || files.length == 0) {
return null;
}
} catch (NullPointerException npe) {
npe.printStackTrace();
}
for (File file : files) {
if (file.getName().contains(".exec")) {
System.out.println("文件:" + file.getAbsolutePath());
fileSetList.add(file);
} else {
System.out.println("非exec文件:" + file.getAbsolutePath());
}
}
return fileSetList;
}
public static void executeMerge(String path) throws Exception {
final ExecFileLoader loader = new ExecFileLoader();
load(loader,path);
save(loader,path);
// 执行完成后,删除非必须的dump文件
for (final File fileSet : fileSets(path)) {
if ( ! fileSet.getName().equals("jacoco.exec") ) {
fileSet.delete();
}
}
}
/**
* 加载dump文件
* @param loader
* @param path
* @throws Exception
*/
public static void load(final ExecFileLoader loader, String path) throws Exception {
for (final File fileSet : fileSets(path)) {
// System.out.println(fileSet.getAbsoluteFile());
final File inputFile = new File(path, fileSet.getName());
if (inputFile.isDirectory()) {
continue;
}
try {
System.out.println("Loading execution data file " + inputFile.getAbsolutePath());
loader.load(inputFile);
// System.out.println(loader.getExecutionDataStore().getContents());
} catch (final IOException e) {
throw new Exception("Unable to read "
+ inputFile.getAbsolutePath(), e);
}
}
}
/**
* 执行合并文件
* @param loader
* @param path
* @throws Exception
*/
public static void save(final ExecFileLoader loader, String path) throws Exception {
File destFile = new File(path + "/jacoco.exec");
if (loader.getExecutionDataStore().getContents().isEmpty()) {
System.out.println("Skipping JaCoCo merge execution due to missing execution data files");
return;
}
System.out.println("Writing merged execution data to " + destFile.getAbsolutePath());
try {
loader.save(destFile, false);
} catch (final IOException e) {
throw new Exception("Unable to write merged file "
+ destFile.getAbsolutePath(), e);
}
}
}
ParseJacocoReport.java
package com.test.testmanagement.controller.jacococlient;
import com.test.testmanagement.model.TestClass;
import com.test.testmanagement.model.TestMethod;
import com.test.testmanagement.model.TestPackage;
import com.test.testmanagement.model.TestProject;
import com.test.testmanagement.service.TestClassService;
import com.test.testmanagement.service.TestMethodService;
import com.test.testmanagement.service.TestPackageService;
import com.test.testmanagement.service.TestProjectService;
import org.springframework.beans.factory.annotation.Autowired;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import java.io.File;
import java.util.Date;
public class ParseJacocoReport {
/*Spring-Mybatis*/
@Autowired
private TestProjectService service;
@Autowired
private TestPackageService packages;
@Autowired
private TestClassService classes;
@Autowired
private TestMethodService funtions;
String testname = "2019-07-26";
// String baseUri = "/data/Mock/testmanagement/file/umemid-airport/it-coveragereport/";
Element element_project_name = null;
String pom_project_name = "";
Document root_doc = null;
public void JacocoReportToDB(String htmlPath) throws Exception {
File root_input = new File(htmlPath + "index.html");
/*开始读取Jacoco覆盖率并存储到数据库*/
TestProject testProject = new TestProject();
testProject.setTestname(testname);
Date d = new Date();
testProject.setStarttime(d);//获取starttime
// testProject.setStarttime(String.valueOf(System.currentTimeMillis()));//获取starttime
root_doc = Jsoup.parse(root_input, "UTF-8");
element_project_name = root_doc.select("H1").first();//查找第一个H1元素
pom_project_name = element_project_name.text();
testProject.setProjectname(pom_project_name);
Integer project_id = service.insertTestProject(testProject);//插入项目
//1)project_name
//UmeTest
System.out.println(pom_project_name);
//2)包名
/*
com.umetrip.unittest.plugins.dbReporter
com.umetrip.unittest
com.umetrip.unittest.testcase
com.umetrip.unittest.plugins.htmlReporter
*/
Elements elements_index_package = root_doc.getElementsByClass("el_package");
Integer i = 0;
for (Element packagename : elements_index_package) {
TestPackage testPackage = new TestPackage();
/******************************************/
System.out.println(packagename.text());//包名com.umetrip.unittest.plugins.dbReporter ...
testPackage.setPackagename(packagename.text());
testPackage.setTestproject_id(project_id);
testPackage.setTestname(testname);
Integer packageid = packages.insertTestPackage(testPackage);//插入包
File class_input = new File(htmlPath + packagename.text() + "/index.html");
Document class_doc = Jsoup.parse(class_input, "UTF-8");
Elements class_names = class_doc.getElementsByClass("el_class");
Integer j = 0;
for (Element class_name : class_names) {
TestClass testClass = new TestClass();
testClass.setClassname(class_name.text());
testClass.setTestpackage_id(packageid);
testClass.setTestname(testname);
Integer classid = classes.insertTestClass(testClass);//插入类
System.out.println(" " + class_name.text());//类名TestReportListener
if (class_name.text().contains(".")) {
continue;
}
File method_input = new File(htmlPath + packagename.text() + "/" + class_name.text() + ".html");
Document method_doc = Jsoup.parse(method_input, "UTF-8");
Elements method_names = method_doc.getElementsByClass("el_method");
TestMethod testMethod = new TestMethod();
/********************************************************************************************/
Element tbody = method_doc.getElementsByTag("tbody").first();
Elements trs = tbody.getElementsByTag("tr");
testMethod.setTestclass_id(classid);
for (Element tr : trs) {
Elements tds = tr.getElementsByTag("td");
Integer td_counter = 0;
for (Element td : tds) {
System.out.println(td.text());
switch (td_counter) {
case 0://方法名
System.out.println("方法名");
Element methodName = td.getElementsByTag("a").first();
testMethod.setMethodname(methodName.text());
td_counter = td_counter + 1;
break;
case 1: //missedinstructions(百分比条)
Elements imgs = td.getElementsByTag("img");
StringBuffer missedInstructions = new StringBuffer();
for (Element img : imgs) {
if (img.attr("src").contains("green")) {
missedInstructions.append("|覆盖:" + img.attr("title") + "|");
} else if (img.attr("src").contains("red")) {
missedInstructions.append("|未覆盖:" + img.attr("title") + "|");
}
}
testMethod.setMissedinstructions(missedInstructions.toString());
td_counter = td_counter + 1;
break;
case 2://missed coverage
testMethod.setInstructioncoverage(td.text());
td_counter = td_counter + 1;
break;
case 3://missed branches(百分比条)
Elements img1s = td.getElementsByTag("img");
StringBuffer missedBranches = new StringBuffer();
for (Element img : img1s) {
if (img.attr("src").contains("green")) {
missedBranches.append("|覆盖:" + img.attr("title") + "|");
} else if (img.attr("src").contains("red")) {
missedBranches.append("|未覆盖:" + img.attr("title") + "|");
}
}
testMethod.setMissedbranches(missedBranches.toString());
td_counter = td_counter + 1;
break;
case 4://branch coverage
testMethod.setBranchcoverage(td.text());
td_counter = td_counter + 1;
break;
case 5://missed cxty
testMethod.setMissedcxty(td.text());
td_counter = td_counter + 1;
break;
case 6://cxty
testMethod.setTotalcxty(td.text());
td_counter = td_counter + 1;
break;
case 7://missed lines
testMethod.setMissedlines(td.text());
td_counter = td_counter + 1;
break;
case 8://lines
testMethod.setTotallines(td.text());
td_counter = td_counter + 1;
break;
case 9://missed method
testMethod.setMissedmethods(td.text());
td_counter = td_counter + 1;
break;
case 10://methods
testMethod.setTotalmethods(td.text());
td_counter = td_counter + 1;
break;
/*default:
//System.out.println("default");
break;
*/
}
}
testMethod.setTestname(testname);
funtions.insertTestMethod(testMethod);
}
/*****************************************************************/
Element tfoot = method_doc.getElementsByTag("tfoot").first();
Element tr = tfoot.getElementsByTag("tr").first();
Elements tds = tfoot.getElementsByTag("td");
StringBuffer class_coverage = new StringBuffer();
class_coverage.append(class_name.text() + "#");
for (Element td : tds) {
if (td.text().contains("Total")) {
continue;
}
//System.out.println(td.text());
class_coverage.append(td.text());
class_coverage.append("#");
}
System.out.println("%%%%%%%%%%" + class_name.text() + "类整体覆盖");
System.out.println(class_coverage);
TestClass someClassTotalSummary = new TestClass();
someClassTotalSummary.setTestname(testname);
someClassTotalSummary.setId(classid);
someClassTotalSummary.setMissedinstructions(class_coverage.toString().split("#")[1]);
someClassTotalSummary.setInstructioncoverage(class_coverage.toString().split("#")[2]);
someClassTotalSummary.setMissedbranches(class_coverage.toString().split("#")[3]);
someClassTotalSummary.setBranchcoverage(class_coverage.toString().split("#")[4]);
someClassTotalSummary.setMissedcxty(class_coverage.toString().split("#")[5]);
someClassTotalSummary.setTotalcxty(class_coverage.toString().split("#")[6]);
someClassTotalSummary.setMissedlines(class_coverage.toString().split("#")[7]);
someClassTotalSummary.setTotallines(class_coverage.toString().split("#")[8]);
someClassTotalSummary.setMissedmethods(class_coverage.toString().split("#")[9]);
someClassTotalSummary.setTotalmethods(class_coverage.toString().split("#")[10]);
classes.updateTestClass(someClassTotalSummary);//更新Class表
j = j + 1;
}
Element tfoot = class_doc.getElementsByTag("tfoot").first();
Element tr = tfoot.getElementsByTag("tr").first();
Elements tds = tfoot.getElementsByTag("td");
StringBuffer package_coverage = new StringBuffer();
package_coverage.append(packagename.text() + "#");
for (Element td : tds) {
if (td.text().contains("Total")) {
continue;
}
//System.out.println(td.text());
package_coverage.append(td.text());
package_coverage.append("#");
}
System.out.println("&&&&&&&&&&" + packagename.text() + "包整体覆盖");
System.out.println(package_coverage);
//更新Package表
TestPackage somePackageTotalSummary = new TestPackage();
somePackageTotalSummary.setId(packageid);
somePackageTotalSummary.setMissedinstructions(package_coverage.toString().split("#")[1]);
somePackageTotalSummary.setInstructioncoverage(package_coverage.toString().split("#")[2]);
somePackageTotalSummary.setMissedbranches(package_coverage.toString().split("#")[3]);
somePackageTotalSummary.setBranchcoverage(package_coverage.toString().split("#")[4]);
somePackageTotalSummary.setMissedcxty(package_coverage.toString().split("#")[5]);
somePackageTotalSummary.setTotalcxty(package_coverage.toString().split("#")[6]);
somePackageTotalSummary.setMissedlines(package_coverage.toString().split("#")[7]);
somePackageTotalSummary.setTotallines(package_coverage.toString().split("#")[8]);
somePackageTotalSummary.setMissedmethods(package_coverage.toString().split("#")[9]);
somePackageTotalSummary.setTotalmethods(package_coverage.toString().split("#")[10]);
somePackageTotalSummary.setMissedclasses(package_coverage.toString().split("#")[11]);
somePackageTotalSummary.setTotalclasses(package_coverage.toString().split("#")[12]);
packages.updateTestPackageById(somePackageTotalSummary);
i = i + 1;
}//package for
Element tfoot = root_doc.getElementsByTag("tfoot").first();
Element tr = tfoot.getElementsByTag("tr").first();
Elements tds = tfoot.getElementsByTag("td");
StringBuffer total_coverage = new StringBuffer();
total_coverage.append(pom_project_name + "#");
for (Element td : tds) {
if (td.text().contains("Total")) {
continue;
}
//System.out.println(td.text());
total_coverage.append(td.text());
total_coverage.append("#");
}
System.out.println("================" + pom_project_name + "项目整体覆盖");
System.out.println(total_coverage);
TestProject someProjectTotalSummary = new TestProject();
someProjectTotalSummary.setId(project_id);
someProjectTotalSummary.setMissedinstructions(total_coverage.toString().split("#")[1]);
someProjectTotalSummary.setInstructioncoverage(total_coverage.toString().split("#")[2]);
someProjectTotalSummary.setMissedbranches(total_coverage.toString().split("#")[3]);
someProjectTotalSummary.setBranchcoverage(total_coverage.toString().split("#")[4]);
someProjectTotalSummary.setMissedcxty(total_coverage.toString().split("#")[5]);
someProjectTotalSummary.setTotalcxty(total_coverage.toString().split("#")[6]);
someProjectTotalSummary.setMissedlines(total_coverage.toString().split("#")[7]);
someProjectTotalSummary.setTotallines(total_coverage.toString().split("#")[8]);
someProjectTotalSummary.setMissedmethods(total_coverage.toString().split("#")[9]);
someProjectTotalSummary.setTotalmethods(total_coverage.toString().split("#")[10]);
someProjectTotalSummary.setMissedclasses(total_coverage.toString().split("#")[11]);
someProjectTotalSummary.setTotalclasses(total_coverage.toString().split("#")[12]);
service.updateTestProjectById(someProjectTotalSummary);
}
public static void main(String[] args) {
ParseJacocoReport parseJacocoReport = new ParseJacocoReport();
try {
parseJacocoReport.JacocoReportToDB("/data/Mock/testmanagement/file/umemid-airport/it-coveragereport/");
} catch (Exception e) {
e.printStackTrace();
}
}
}
ReportGenerator.java
/*******************************************************************************
* Copyright (c) 2009, 2019 Mountainminds GmbH & Co. KG and Contributors
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Brock Janiczak - initial API and implementation
*
*******************************************************************************/
package com.test.testmanagement.controller.jacococlient;
import org.jacoco.core.analysis.Analyzer;
import org.jacoco.core.analysis.CoverageBuilder;
import org.jacoco.core.analysis.IBundleCoverage;
import org.jacoco.core.tools.ExecFileLoader;
import org.jacoco.report.DirectorySourceFileLocator;
import org.jacoco.report.FileMultiReportOutput;
import org.jacoco.report.IReportVisitor;
import org.jacoco.report.html.HTMLFormatter;
import java.io.File;
import java.io.IOException;
/**
* This example creates a HTML report for eclipse like projects based on a
* single execution data store called jacoco.exec. The report contains no
* grouping information.
*
* The class files under test must be compiled with debug information, otherwise
* source highlighting will not work.
*/
public class ReportGenerator {
private final String title;
private final File executionDataFile;
private final File classesDirectory;
private final File sourceDirectory;
private final File reportDirectory;
private ExecFileLoader execFileLoader;
public static void generateItCoverageReport(String projectDirectory,
String ReportDataFile,
String executionDataFile,
String classesDirectory,
String sourceDirectory,
String reportDirectory){
final ReportGenerator generator = new ReportGenerator(
projectDirectory,
ReportDataFile,
executionDataFile,
classesDirectory,
sourceDirectory,
reportDirectory
);
try {
generator.create();
} catch (IOException e) {
e.printStackTrace();
}
}
public ReportGenerator(String projectDirectory,
String ReportDataFile,
String executionDataFile,
String classesDirectory,
String sourceDirectory,
String reportDirectory ) {
File projectDirectory_File = new File(projectDirectory);
File report_File = new File(ReportDataFile);
this.title = projectDirectory_File.getName();
this.executionDataFile = new File(projectDirectory_File, executionDataFile);
this.classesDirectory = new File(projectDirectory_File, classesDirectory);
this.sourceDirectory = new File(projectDirectory_File, sourceDirectory);
this.reportDirectory = new File(report_File, reportDirectory);
}
/**
* Create a new generator based for the given project.
*
* @param projectDirectory
*/
/*
class 文件 /opt/app/jenkins/workspace/Pipeline_umemid-airport/umemid-airport/target/classes
src 文件夹 /opt/app/jenkins/workspace/Pipeline_umemid-airport/umemid-airport/src
*/
public ReportGenerator(final File projectDirectory) {
this.title = projectDirectory.getName();
this.executionDataFile = new File(projectDirectory, "jacoco-it.exec");
this.classesDirectory = new File(projectDirectory, "target");
this.sourceDirectory = new File(projectDirectory, "src/main/java");
this.reportDirectory = new File(projectDirectory, "coveragereport");
}
/**
* Create the report.
*
* @throws IOException
*/
public void create() throws IOException {
// Read the jacoco.exec file. Multiple data files could be merged
// at this point
loadExecutionData();
// Run the structure analyzer on a single class folder to build up
// the coverage model. The process would be similar if your classes
// were in a jar file. Typically you would create a bundle for each
// class folder and each jar you want in your report. If you have
// more than one bundle you will need to add a grouping node to your
// report
final IBundleCoverage bundleCoverage = analyzeStructure();
createReport(bundleCoverage);
}
private void createReport(final IBundleCoverage bundleCoverage)
throws IOException {
// Create a concrete report visitor based on some supplied
// configuration. In this case we use the defaults
final HTMLFormatter htmlFormatter = new HTMLFormatter();
final IReportVisitor visitor = htmlFormatter
.createVisitor(new FileMultiReportOutput(reportDirectory));
// Initialize the report with all of the execution and session
// information. At this point the report doesn't know about the
// structure of the report being created
visitor.visitInfo(execFileLoader.getSessionInfoStore().getInfos(),
execFileLoader.getExecutionDataStore().getContents());
// Populate the report structure with the bundle coverage information.
// Call visitGroup if you need groups in your report.
visitor.visitBundle(bundleCoverage, new DirectorySourceFileLocator(
sourceDirectory, "utf-8", 4));
// Signal end of structure information to allow report to write all
// information out
visitor.visitEnd();
}
private void loadExecutionData() throws IOException {
execFileLoader = new ExecFileLoader();
execFileLoader.load(executionDataFile);
}
private IBundleCoverage analyzeStructure() throws IOException {
final CoverageBuilder coverageBuilder = new CoverageBuilder();
final Analyzer analyzer = new Analyzer(
execFileLoader.getExecutionDataStore(), coverageBuilder);
analyzer.analyzeAll(classesDirectory);
return coverageBuilder.getBundle(title);
}
/**
* Starts the report generation process
*
* @param args
* Arguments to the application. This will be the location of the
* eclipse projects that will be used to generate reports for
* @throws IOException
*/
public static void main(final String[] args) throws IOException {
for (int i = 0; i < args.length; i++) {
final ReportGenerator generator = new ReportGenerator(new File(
args[i]));
generator.create();
}
}
}