diff --git a/src/main/java/net/geedge/api/util/PlaybookRunnable.java b/src/main/java/net/geedge/api/util/PlaybookRunnable.java new file mode 100644 index 0000000..23d9719 --- /dev/null +++ b/src/main/java/net/geedge/api/util/PlaybookRunnable.java @@ -0,0 +1,202 @@ +package net.geedge.api.util; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.collection.ListUtil; +import cn.hutool.core.io.FileUtil; +import cn.hutool.log.Log; +import net.geedge.api.entity.EnvApiYml; +import net.geedge.common.APIException; +import net.geedge.common.Constant; +import net.geedge.common.RCode; +import net.geedge.common.T; + +import java.io.File; +import java.util.List; +import java.util.Map; + +public class PlaybookRunnable implements Runnable { + private final static Log log = Log.get(); + + private AdbUtil adbUtil; + private EnvApiYml envApiYml; + private String tid; + private File apkFile; + private String packageName; + private File playbookDir; + private boolean reInstall; + private boolean clearCache; + private boolean unInstall; + private boolean interrupt; + + public PlaybookRunnable(EnvApiYml envApiYml, File apkFile, File playbookDir, String tid, String packageName, Boolean reInstall, Boolean clearCache, Boolean unInstall) { + this.envApiYml = envApiYml; + this.tid = tid; + this.apkFile = apkFile; + this.packageName = packageName; + this.playbookDir = playbookDir; + this.reInstall = reInstall; + this.clearCache = clearCache; + this.unInstall = unInstall; + this.interrupt = false; + } + + @Override + public void run() { + Thread.currentThread().setName("exec-playbook-thread-" + tid); + Constant.ACTIVE_TASKS.add(this); + File logFile = FileUtil.file(Constant.TEMP_PATH, tid, "result.log"); + File statusFile = FileUtil.file(Constant.TEMP_PATH, tid, "result.json"); + AdbUtil.CommandResult tcpdumpPackage = null; + AdbUtil.CommandResult tcpdumpAll = null; + try { + Map resultMap = T.MapUtil.builder() + .put("status", "running") + .build(); + T.FileUtil.writeString(T.JSONUtil.toJsonStr(resultMap), statusFile, "UTF-8"); + + if (interrupt) return; + T.FileUtil.appendString(String.format("Running with %s:%s Android Simulator \n", envApiYml.getAdb().getHost(), envApiYml.getAdb().getPort()), logFile, "UTF-8"); + adbUtil = new AdbUtil(envApiYml.getAdb(), new CommandExec(logFile)); + + // Check if the package is installed + if (interrupt) return; + boolean packageIsInstall = adbUtil.findPackageInstall(packageName); + if (packageIsInstall) { + if (!reInstall) { + // install apk + if (interrupt) return; + AdbUtil.CommandResult install = adbUtil.install(apkFile.getAbsolutePath(), true, true); + if (0 != install.exitCode()) { + T.FileUtil.appendString(String.format("ERROR: Install apk failed: exit code %s \n", install.exitCode()), logFile, "UTF-8"); + throw new APIException(install.output()); + } + } + } else { + // install apk + if (interrupt) return; + AdbUtil.CommandResult install = adbUtil.install(apkFile.getAbsolutePath(), true, true); + if (0 != install.exitCode()) { + T.FileUtil.appendString(String.format("ERROR: Install apk failed: exit code %s \n", install.exitCode()), logFile, "UTF-8"); + throw new APIException(install.output()); + } + } + + //Close other apps + if (interrupt) return; + List packageNameList = adbUtil.findPackageNameList(); + this.closeApp(packageNameList, packageName); + + // clear app data + if (interrupt) return; + if (clearCache) { + AdbUtil.CommandResult clearData = adbUtil.clearAppData(packageName); + if (0 != clearData.exitCode()) { + T.FileUtil.appendString(String.format("ERROR: Clear %s data error: exit code %s \n", packageName, clearData.exitCode()), logFile, "UTF-8"); + throw new APIException(clearData.output()); + } + } + + // Launch the app + if (interrupt) return; + adbUtil.startApp(packageName); + + // star tcpdump: package name + if (interrupt) return; + tcpdumpPackage = adbUtil.startTcpdump(packageName); + if (0 != tcpdumpPackage.exitCode()) { + T.FileUtil.appendString(String.format("ERROR: Start tcpdump %s failed: exit code %s \n", packageName, tcpdumpPackage.exitCode()), logFile, "UTF-8"); + throw new APIException(String.format("tcpdump %s error", packageName)); + } + + // star tcpdump: all + if (interrupt) return; + tcpdumpAll = adbUtil.startTcpdump(T.StrUtil.EMPTY); + if (0 != tcpdumpAll.exitCode()) { + T.FileUtil.appendString(String.format("ERROR: Start tcpdump all failed: exit code %s \n", tcpdumpAll.exitCode()), logFile, "UTF-8"); + throw new APIException("tcpdump all error"); + } + + // exec playbook + if (interrupt) return; + AdbUtil.CommandResult airtestResult = adbUtil.execPlaybook(playbookDir.getPath(), tid, packageName, logFile); + if (0 != airtestResult.exitCode()) { + T.FileUtil.appendString(String.format("ERROR: Exec playbook failed: exit code %s \n", airtestResult.exitCode()), logFile, "UTF-8"); + throw new APIException("playbook exec error"); + } + + // stop package tcpdump + if (interrupt) return; + stopTcpdump(tcpdumpPackage, logFile, packageName); + + // stop all tcpdump + if (interrupt) return; + stopTcpdump(tcpdumpAll, logFile, T.StrUtil.EMPTY); + + resultMap = T.MapUtil.builder() + .put("status", "done") + .build(); + T.FileUtil.writeString(T.JSONUtil.toJsonStr(resultMap), statusFile, "UTF-8"); + } catch (Exception e) { + log.error(e); + Map resultMap = T.MapUtil.builder() + .put("status", "error") + .build(); + T.FileUtil.writeString(T.JSONUtil.toJsonStr(resultMap), statusFile, "UTF-8"); + } finally { + if (T.StrUtil.isNotEmpty(tcpdumpPackage.output())) { + AdbUtil.CommandResult packageTcpdump = adbUtil.stopTcpdump(tcpdumpPackage.output()); + adbUtil.execShellCommand(String.format("shell rm -rf %s", packageTcpdump.output())); + } + if (T.StrUtil.isNotEmpty(tcpdumpAll.output())) { + AdbUtil.CommandResult allTcpdump = adbUtil.stopTcpdump(tcpdumpAll.output()); + adbUtil.execShellCommand(String.format("shell rm -rf %s", allTcpdump.output())); + } + this.closeApp(ListUtil.empty(), packageName); + if (unInstall) { + adbUtil.uninstall(packageName); + } + T.FileUtil.appendString(String.format("Job execution ends"), logFile, "UTF-8"); + Constant.ACTIVE_TASKS.remove(this); + } + } + + public void interrupt() { + this.interrupt = true; + adbUtil.setInterrupt(true); + } + + private void closeApp(List packageNameList, String packageName) { + if (CollUtil.isNotEmpty(packageNameList)) { + for (String name : packageNameList) { + adbUtil.stopApp(name); + } + } + adbUtil.stopApp(packageName); + } + + private void stopTcpdump(AdbUtil.CommandResult tcpdump, File logFile, String packageName) { + // stop tcpdump + AdbUtil.CommandResult stopTcpdump = adbUtil.stopTcpdump(tcpdump.output()); + if (0 != stopTcpdump.exitCode()) { + T.FileUtil.appendString(String.format("ERROR: Stop tcpdump failed: exit code %s \n", stopTcpdump.exitCode()), logFile, "UTF-8"); + throw new APIException(stopTcpdump.output()); + } + + // pull pcap file + String filePath = stopTcpdump.output(); + packageName = T.StrUtil.isEmpty(packageName) ? "all" : packageName; + File localPcapFile = T.FileUtil.file(Constant.TEMP_PATH, tid, String.format("%s-%s%s", tcpdump.output(), packageName, ".pcap")); + if (T.StrUtil.isEmpty(filePath)) { + throw new APIException(RCode.NOT_EXISTS); + } + + AdbUtil.CommandResult pull = adbUtil.pull(filePath, localPcapFile.getAbsolutePath()); + if (0 != pull.exitCode()) { + T.FileUtil.appendString(String.format("ERROR: Pull pcap file failed: exit code %s \n", pull.exitCode()), logFile, "UTF-8"); + throw new APIException(pull.output()); + } + + // delete android pcap + adbUtil.execShellCommand(String.format("shell rm -rf %s", filePath)); + } +} \ No newline at end of file