Procházet zdrojové kódy

1. 添加拉取任务的逻辑
2. 重构一些小地方

LiHaoyu před 5 roky
rodič
revize
368686ee76
23 změnil soubory, kde provedl 450 přidání a 79 odebrání
  1. 8 0
      scripts/Main.java
  2. 10 4
      src/main/java/net/mooctest/www/android_auto_test/Scripts/DefaultScript.java
  3. 2 2
      src/main/java/net/mooctest/www/android_auto_test/common/constant/Consts.java
  4. 5 4
      src/main/java/net/mooctest/www/android_auto_test/common/constant/enums/DeviceStatus.java
  5. 135 0
      src/main/java/net/mooctest/www/android_auto_test/common/runner/MyApplicationRunner.java
  6. 30 0
      src/main/java/net/mooctest/www/android_auto_test/controller/MockController.java
  7. 19 0
      src/main/java/net/mooctest/www/android_auto_test/controller/SecondaryController.java
  8. 11 6
      src/main/java/net/mooctest/www/android_auto_test/dao/RedisMappers/DeviceRunningStatusMap.java
  9. 34 0
      src/main/java/net/mooctest/www/android_auto_test/dao/RedisMappers/DeviceStatusMap.java
  10. 5 5
      src/main/java/net/mooctest/www/android_auto_test/dao/RedisMappers/Trace2DeviceMap.java
  11. 11 6
      src/main/java/net/mooctest/www/android_auto_test/dao/RedisMappers/TraceStatusMap.java
  12. 0 1
      src/main/java/net/mooctest/www/android_auto_test/models/Device.java
  13. 2 0
      src/main/java/net/mooctest/www/android_auto_test/services/ApkService.java
  14. 15 0
      src/main/java/net/mooctest/www/android_auto_test/services/DeviceService.java
  15. 2 2
      src/main/java/net/mooctest/www/android_auto_test/services/Impl/ApkServiceImpl.java
  16. 6 3
      src/main/java/net/mooctest/www/android_auto_test/services/Impl/AutoTestServiceImpl.java
  17. 24 2
      src/main/java/net/mooctest/www/android_auto_test/services/Impl/DeviceServiceImpl.java
  18. 36 0
      src/main/java/net/mooctest/www/android_auto_test/services/Impl/SecondaryServiceImpl.java
  19. 6 0
      src/main/java/net/mooctest/www/android_auto_test/services/SecondaryService.java
  20. 6 2
      src/main/java/net/mooctest/www/android_auto_test/utils/AppiumManager.java
  21. 4 0
      src/main/java/net/mooctest/www/android_auto_test/utils/CoverageTest.java
  22. 77 42
      src/main/java/net/mooctest/www/android_auto_test/utils/TraceDaemon.java
  23. 2 0
      src/main/java/net/mooctest/www/android_auto_test/vo/TraceMetaInfo.java

+ 8 - 0
scripts/Main.java

@@ -0,0 +1,8 @@
+import io.appium.java_client.AppiumDriver;
+
+public class Main {
+    public void test(AppiumDriver driver){
+        System.out.println("execute custom script");
+        driver.findElementByXPath("//android.widget.Button[contains(@text,'始终允许')]").click();
+    }
+}

+ 10 - 4
src/main/java/net/mooctest/www/android_auto_test/Scripts/DefaultScript.java

@@ -7,7 +7,6 @@ import io.appium.java_client.android.AndroidDriver;
 import io.appium.java_client.android.AndroidTouchAction;
 import io.appium.java_client.android.nativekey.AndroidKey;
 import io.appium.java_client.android.nativekey.KeyEvent;
-import io.appium.java_client.touch.TapOptions;
 import io.appium.java_client.touch.WaitOptions;
 import io.appium.java_client.touch.offset.PointOption;
 import net.mooctest.www.android_auto_test.common.constant.Consts;
@@ -26,7 +25,7 @@ import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
 
-import static java.nio.charset.StandardCharsets.*;
+import static java.nio.charset.StandardCharsets.UTF_8;
 
 /**
  * @author henrylee
@@ -375,7 +374,11 @@ public class DefaultScript extends AbstractBaseScript {
             Point p = element.getLocation();
             int x = p.x + element.getSize().getWidth() / 2;
             int y = p.y + element.getSize().getHeight() / 4;
-            new TouchAction<>(driver).tap(PointOption.point(x, y)).perform();
+            try {
+                new TouchAction<>(driver).tap(PointOption.point(x, y)).perform();
+            } catch (InvalidElementStateException e){
+                element.click();
+            }
         }
 
         String timeAfterAction = DeviceUtil.getDeviceTime(udid);
@@ -515,7 +518,10 @@ public class DefaultScript extends AbstractBaseScript {
             }
             String packageName = com.getPackagename();
             boolean isComponentAlert = (resourceId.contains("alertTitle") || resourceId.contains("event_title"))
-                    || packageName != null && (packageName.contains("packageinstaller") || packageName.contains("permissioncontroller"));
+                    || packageName != null
+                    && (packageName.contains("packageinstaller")
+                        || packageName.contains("permissioncontroller")
+                        || packageName.contains("security.miui"));
             if (isComponentAlert){
                 haveAlert = true;
                 break;

+ 2 - 2
src/main/java/net/mooctest/www/android_auto_test/common/constant/Consts.java

@@ -7,7 +7,7 @@ public class Consts {
     /**
      * 线程name前缀
      */
-    public static final String AUTO_TEST_THREAD_NAME_PREFIX = "COVERAGE_TEST_THREAD";
+    public static final String AUTO_TEST_THREAD_NAME_PREFIX = "COVERAGE_TEST_THREAD_";
 
     public static final String DAEMON_THREAD_NAME_PREFIX = "DAEMON_THREAD_";
 
@@ -25,7 +25,7 @@ public class Consts {
     /**
      * 监控线程的监控间隔,单位秒
      */
-    public static final int TRACE_CHECK_TERMINAL = 10;
+    public static final int TRACE_CHECK_TERMINAL = 5;
 
     public static final int INIT_DRIVER_TIMES = 3;
 

+ 5 - 4
src/main/java/net/mooctest/www/android_auto_test/common/constant/enums/DeviceStatus.java

@@ -5,13 +5,14 @@ import lombok.Getter;
 @Getter
 public enum DeviceStatus {
     /**
-     * 设备在线
+     * 设备正在运行任务
      */
-    ONLINE(1, "在线"),
+    RUNNING(1, "执行中"),
     /**
-     * 设备离线
+     * 设备空闲
      */
-    OFFLINE(0, "离线");
+    FREE(0, "空闲");
+
 
     private int code;
     private String description;

+ 135 - 0
src/main/java/net/mooctest/www/android_auto_test/common/runner/MyApplicationRunner.java

@@ -0,0 +1,135 @@
+package net.mooctest.www.android_auto_test.common.runner;
+
+import com.sinaapp.msdxblog.apkUtil.entity.ApkInfo;
+import com.sinaapp.msdxblog.apkUtil.utils.ApkUtil;
+import net.mooctest.www.android_auto_test.common.constant.Consts;
+import net.mooctest.www.android_auto_test.common.constant.enums.DeviceStatus;
+import net.mooctest.www.android_auto_test.models.Device;
+import net.mooctest.www.android_auto_test.services.ApkService;
+import net.mooctest.www.android_auto_test.services.DeviceService;
+import net.mooctest.www.android_auto_test.utils.AddressUtil;
+import net.mooctest.www.android_auto_test.utils.CoverageTest;
+import net.mooctest.www.android_auto_test.utils.PrintUtil;
+import net.mooctest.www.android_auto_test.utils.TraceDaemon;
+import net.mooctest.www.android_auto_test.vo.TraceMetaInfo;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.ApplicationArguments;
+import org.springframework.boot.ApplicationRunner;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.MediaType;
+import org.springframework.stereotype.Component;
+import org.springframework.web.client.RestTemplate;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+@Component
+public class MyApplicationRunner implements ApplicationRunner {
+
+    public static final String TAG = Thread.currentThread() .getStackTrace()[1].getClassName();
+
+    @Autowired
+    DeviceService deviceService;
+
+    @Autowired
+    ApkService apkService;
+
+    @Override
+    public void run(ApplicationArguments args) throws Exception {
+        String onOff = args.getSourceArgs()[0];
+        if (!"pollingOn".equals(onOff)){
+            return;
+        }
+        System.out.println("轮询获取任务信息");
+        //TODO 先获取当前空闲设备
+        // 然后带着设备信息去发请求,获取任务信息
+        // 获取到任务信息后,起一个线程去跑任务
+        // 如果有一些额外配置,则触发生成报告的操作,然后告诉企业版,报告好了
+        runDevicesTasks();
+    }
+
+    private void runDevicesTasks(){
+        while (true){
+            // 获取目前在线设备且可用的设备
+            List<Device> deviceList = deviceService.getOnlineDeviceList();
+            for (Device device: deviceList){
+                //TODO 这里用CAS去更新device的状态
+                // 或者把接收任务的口子关了。。。
+                DeviceStatus deviceStatus = deviceService.getDeviceStatus(device.getUdid());
+                if (deviceStatus == DeviceStatus.RUNNING){
+                    PrintUtil.print("Device is running, continue.", TAG, device.getUdid());
+                    continue;
+                }
+                device = buildDevice(device);
+                PrintUtil.print("Get TraceInfo.", TAG, device.getUdid());
+                TraceMetaInfo traceMetaInfo = getTraceMetaInfo(device);
+                if (traceMetaInfo == null){
+                    continue;
+                }
+
+                String downloadUrl = traceMetaInfo.getDownloadUrl();
+                String traceId = traceMetaInfo.getTraceId();
+                int limitTime = traceMetaInfo.getLimitTime();
+
+                String[] temp = downloadUrl.split("/");
+                String fileName = temp[temp.length-1];
+                String apkPath = AddressUtil.getApkDownloadPath(traceId, fileName);
+                File apkFile = new File(apkPath);
+                // 判断是否是第一次下载apk,如果是,需要下载,保存解析的apkInfo信息
+                // 如果曾经下载过(就是这个Trace不是第一次跑),就只解析ApkInfo
+                boolean haveDownloadApkFile = apkFile.exists();
+                if (!haveDownloadApkFile) {
+                    PrintUtil.print("This is first task, download apk file.", TAG, device.getUdid());
+                    apkService.downloadApk(downloadUrl, traceId);
+                }
+                ApkInfo apkInfo = apkService.parseAPK(apkPath, traceId);
+                if (!haveDownloadApkFile){
+                    PrintUtil.print("This is first task, save apkInfo.", TAG, device.getUdid());
+                    apkService.saveApkInfo(apkInfo, apkPath, traceId);
+                    apkService.updateTraceStartTime(traceId);
+                }
+                // 启动测试线程
+                PrintUtil.print("Start test thread and update device status", TAG, device.getUdid());
+                List<CoverageTest> oneTraceTasks = new ArrayList<>(1);
+                CoverageTest coverageTest = new CoverageTest(apkInfo, apkPath, device, traceId);
+                coverageTest.setName(Consts.AUTO_TEST_THREAD_NAME_PREFIX + device.getUdid());
+                oneTraceTasks.add(coverageTest);
+                //TODO 这里需要CAS更新,而不是单纯的更新
+                deviceService.updateDeviceStatus(device.getUdid(), DeviceStatus.RUNNING);
+                coverageTest.start();
+
+                //启动守护线程
+                PrintUtil.print("Start test daemon thread.", TAG, device.getUdid());
+                TraceDaemon traceDaemon = new TraceDaemon(traceId, limitTime, oneTraceTasks, traceMetaInfo.isNeedGenerateReport(),true, "BugReport");
+                traceDaemon.setName(Consts.DAEMON_THREAD_NAME_PREFIX + traceId);
+                traceDaemon.start();
+
+            }
+            try {
+                Thread.sleep(60000);
+            } catch (InterruptedException e) {
+                e.printStackTrace();
+            }
+        }
+    }
+
+    private Device buildDevice(Device device){
+        return device;
+    }
+
+    private TraceMetaInfo getTraceMetaInfo(Device device){
+        // TODO 发送请求,获取Trace信息,这里Mock一下发给自己这个项目
+        RestTemplate rt = new RestTemplate();
+        HttpHeaders headers = new HttpHeaders();
+        headers.setContentType(MediaType.APPLICATION_JSON);
+        HttpEntity<Device> httpEntity = new HttpEntity<>(device, headers);
+        String url = "http://localhost:15926/api/v1/mock/TraceInfo";
+        TraceMetaInfo traceMetaInfo = rt.postForObject(url, httpEntity, TraceMetaInfo.class);
+        if (traceMetaInfo == null || traceMetaInfo.getTraceId() == null || traceMetaInfo.getDownloadUrl() == null){
+            return null;
+        }
+        return traceMetaInfo;
+    }
+}

+ 30 - 0
src/main/java/net/mooctest/www/android_auto_test/controller/MockController.java

@@ -0,0 +1,30 @@
+package net.mooctest.www.android_auto_test.controller;
+
+import net.mooctest.www.android_auto_test.models.Device;
+import net.mooctest.www.android_auto_test.vo.TraceMetaInfo;
+import net.mooctest.www.android_auto_test.vo.TraceStatusResult;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+public class MockController {
+
+    @RequestMapping(value = "/api/v1/mock/TraceInfo", method = RequestMethod.POST)
+    public TraceMetaInfo runTest(@RequestBody Device device){
+        TraceMetaInfo traceMetaInfo = new TraceMetaInfo();
+        traceMetaInfo.setLimitTime(5);
+        if (device.getUdid().equals("FJH5T18830019181")){
+            traceMetaInfo.setDownloadUrl("http://mooctest-share.oss-cn-hangzhou.aliyuncs.com/mooctest-accept-test-dataset/APP扫描/1more/天气通_6.28.apk");
+            traceMetaInfo.setTraceId("30000");
+        }else if (device.getUdid().equals("Q5S5T19411008221")){
+            traceMetaInfo.setDownloadUrl("http://mooctest-share.oss-cn-hangzhou.aliyuncs.com/mooctest-accept-test-dataset/APP扫描/1more/趣头条_3.9.59.000.1231.1404.apk");
+            traceMetaInfo.setTraceId("30001");
+        }else if (device.getUdid().equals("66J5T19401004951")){
+            traceMetaInfo.setDownloadUrl("http://mooctest-share.oss-cn-hangzhou.aliyuncs.com/mooctest-accept-test-dataset/APP扫描/1more/美团外卖_7.27.2.apk");
+            traceMetaInfo.setTraceId("30002");
+        }
+        return traceMetaInfo;
+    }
+}

+ 19 - 0
src/main/java/net/mooctest/www/android_auto_test/controller/SecondaryController.java

@@ -0,0 +1,19 @@
+package net.mooctest.www.android_auto_test.controller;
+
+import net.mooctest.www.android_auto_test.services.SecondaryService;
+import net.mooctest.www.android_auto_test.vo.TraceMetaInfo;
+import net.mooctest.www.android_auto_test.vo.TraceStatusResult;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+@RestController
+public class SecondaryController {
+
+    @Autowired
+    SecondaryService secondaryService;
+
+    @RequestMapping(value = "/api/v1/secondaryAnalysis", method = RequestMethod.POST)
+    public void runTest(@RequestParam(name = "traceId") String traceId){
+        secondaryService.generateCrowdSourced(traceId);
+    }
+}

+ 11 - 6
src/main/java/net/mooctest/www/android_auto_test/dao/RedisMappers/DeviceRunningStatusMap.java

@@ -1,7 +1,9 @@
 package net.mooctest.www.android_auto_test.dao.RedisMappers;
 
 import net.mooctest.www.android_auto_test.common.constant.enums.DeviceRunningStatus;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.data.redis.core.StringRedisTemplate;
 import org.springframework.data.redis.serializer.StringRedisSerializer;
 import org.springframework.stereotype.Component;
 
@@ -14,17 +16,20 @@ import javax.annotation.Resource;
 public class DeviceRunningStatusMap {
     private final String DEVICE_RUNNING_STATUS_PREFIX = "DEVICE_RUNNING_STATUS_";
 
-    @Resource(name = "redisTemplate")
-    RedisTemplate<Object, Object> redisTemplate;
+    @Autowired
+    StringRedisTemplate redisTemplate;
 
     public DeviceRunningStatus get(String udid, String traceId){
-        redisTemplate.setKeySerializer(new StringRedisSerializer());
-        return (DeviceRunningStatus) redisTemplate.opsForValue().get(DEVICE_RUNNING_STATUS_PREFIX + traceId + "_" + udid);
+        String o = redisTemplate.opsForValue().get(DEVICE_RUNNING_STATUS_PREFIX + traceId + "_" + udid);
+        if (o == null){
+            return null;
+        }
+        int code = Integer.parseInt(o);
+        return DeviceRunningStatus.getStatusByCode(code);
     }
 
     public void put(String udid, String traceId, DeviceRunningStatus status){
-        redisTemplate.setKeySerializer(new StringRedisSerializer());
-        redisTemplate.opsForValue().set(DEVICE_RUNNING_STATUS_PREFIX + traceId + "_" + udid, status);
+        redisTemplate.opsForValue().set(DEVICE_RUNNING_STATUS_PREFIX + traceId + "_" + udid, String.valueOf(status.getCode()));
     }
 
 }

+ 34 - 0
src/main/java/net/mooctest/www/android_auto_test/dao/RedisMappers/DeviceStatusMap.java

@@ -0,0 +1,34 @@
+package net.mooctest.www.android_auto_test.dao.RedisMappers;
+
+import net.mooctest.www.android_auto_test.common.constant.enums.DeviceStatus;
+import net.mooctest.www.android_auto_test.common.constant.enums.TraceStatus;
+import net.mooctest.www.android_auto_test.models.Device;
+import net.mooctest.www.android_auto_test.services.DeviceService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.data.redis.core.StringRedisTemplate;
+import org.springframework.data.redis.serializer.StringRedisSerializer;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
+
+@Component
+public class DeviceStatusMap {
+    private final String DEVICE_STATUS_PREFIX = "DEVICE_STATUS_";
+
+    @Autowired
+    StringRedisTemplate redisTemplate;
+
+    public DeviceStatus get(String udid){
+        String o = redisTemplate.opsForValue().get(DEVICE_STATUS_PREFIX + udid);
+        if (o == null){
+            return DeviceStatus.FREE;
+        }
+        int statusCode =  Integer.parseInt(o);
+        return DeviceStatus.getStatusByCode(statusCode);
+    }
+
+    public void put(String udid, DeviceStatus status){
+        redisTemplate.opsForValue().set(DEVICE_STATUS_PREFIX + udid, String.valueOf(status.getCode()));
+    }
+}

+ 5 - 5
src/main/java/net/mooctest/www/android_auto_test/dao/RedisMappers/Trace2DeviceMap.java

@@ -1,7 +1,9 @@
 package net.mooctest.www.android_auto_test.dao.RedisMappers;
 
 import com.alibaba.fastjson.JSON;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.data.redis.core.StringRedisTemplate;
 import org.springframework.data.redis.serializer.StringRedisSerializer;
 import org.springframework.stereotype.Component;
 
@@ -15,17 +17,15 @@ import java.util.List;
 public class Trace2DeviceMap {
     private final String TRACE_DEVICE_PREFIX = "TRACE_DEVICES_";
 
-    @Resource(name="redisTemplate")
-    RedisTemplate<Object, Object> redisTemplate;
+    @Autowired
+    StringRedisTemplate redisTemplate;
 
     public List<String> get(String traceId){
-        redisTemplate.setKeySerializer(new StringRedisSerializer());
-        String re = (String) redisTemplate.opsForValue().get(TRACE_DEVICE_PREFIX + traceId);
+        String re = redisTemplate.opsForValue().get(TRACE_DEVICE_PREFIX + traceId);
         return JSON.parseArray(re, String.class);
     }
 
     public void put(String traceId, List<String> udids){
-        redisTemplate.setKeySerializer(new StringRedisSerializer());
         String list = JSON.toJSONString(udids);
         redisTemplate.opsForValue().set(TRACE_DEVICE_PREFIX + traceId, list);
     }

+ 11 - 6
src/main/java/net/mooctest/www/android_auto_test/dao/RedisMappers/TraceStatusMap.java

@@ -2,7 +2,9 @@ package net.mooctest.www.android_auto_test.dao.RedisMappers;
 
 
 import net.mooctest.www.android_auto_test.common.constant.enums.TraceStatus;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.data.redis.core.StringRedisTemplate;
 import org.springframework.data.redis.serializer.StringRedisSerializer;
 import org.springframework.stereotype.Component;
 
@@ -15,16 +17,19 @@ import javax.annotation.Resource;
 public class TraceStatusMap {
     private final String TRACE_STATUS_PREFIX = "TRACE_STATUS_";
 
-    @Resource(name = "redisTemplate")
-    RedisTemplate<Object, Object> redisTemplate;
+    @Autowired
+    StringRedisTemplate redisTemplate;
 
     public TraceStatus get(String traceId){
-        redisTemplate.setKeySerializer(new StringRedisSerializer());
-        return (TraceStatus) redisTemplate.opsForValue().get(TRACE_STATUS_PREFIX + traceId);
+        String o = redisTemplate.opsForValue().get(TRACE_STATUS_PREFIX + traceId);
+        if (o == null){
+            return null;
+        }
+        int code = Integer.parseInt(o);
+        return TraceStatus.getStatusByCode(code);
     }
 
     public void put(String traceId, TraceStatus status){
-        redisTemplate.setKeySerializer(new StringRedisSerializer());
-        redisTemplate.opsForValue().set(TRACE_STATUS_PREFIX + traceId, status);
+        redisTemplate.opsForValue().set(TRACE_STATUS_PREFIX + traceId, String.valueOf(status.getCode()));
     }
 }

+ 0 - 1
src/main/java/net/mooctest/www/android_auto_test/models/Device.java

@@ -11,6 +11,5 @@ import java.io.Serializable;
 @Data
 public class Device{
     private String id;
-    private String pcId;
     private String udid;
 }

+ 2 - 0
src/main/java/net/mooctest/www/android_auto_test/services/ApkService.java

@@ -19,6 +19,8 @@ public interface ApkService {
      * @return
      */
     String downloadApk(String downloadUrl, String traceId);
+    
+    void saveApkInfo(ApkInfo apkInfo, String apkPath, String traceId);
 
     /**
      * @param traceId traceId

+ 15 - 0
src/main/java/net/mooctest/www/android_auto_test/services/DeviceService.java

@@ -2,6 +2,7 @@ package net.mooctest.www.android_auto_test.services;
 
 import com.sinaapp.msdxblog.apkUtil.entity.ApkInfo;
 import net.mooctest.www.android_auto_test.common.constant.enums.DeviceRunningStatus;
+import net.mooctest.www.android_auto_test.common.constant.enums.DeviceStatus;
 import net.mooctest.www.android_auto_test.models.Device;
 
 import java.util.List;
@@ -25,4 +26,18 @@ public interface DeviceService {
     void updateDeviceRunningStatus(String traceId, String udid, DeviceRunningStatus deviceRunningStatus);
 
     DeviceRunningStatus getDeviceRunningStatus(String traceId, String udid);
+
+    /**
+     * 更新设备执行状态。此方法只用于机柜主动拉任务的情况
+     * @param udid 设备Id
+     * @param deviceStatus 设备状态
+     */
+    void updateDeviceStatus(String udid, DeviceStatus deviceStatus);
+
+    /**
+     * 获取当前设备状态,此方法只用于机柜主动拉任务的情况
+     * @param udid 设备Id
+     * @return 设备状态
+     */
+    DeviceStatus getDeviceStatus(String udid);
 }

+ 2 - 2
src/main/java/net/mooctest/www/android_auto_test/services/Impl/ApkServiceImpl.java

@@ -35,7 +35,6 @@ public class ApkServiceImpl implements ApkService {
         ApkInfo apkInfo = null;
         try {
             apkInfo = new ApkUtil().getApkInfo(apkPath);
-            saveApkInfo(apkInfo, apkPath, traceId);
         } catch (Exception e) {
             throw new ApkParseException("APK 解析失败");
         }
@@ -94,7 +93,8 @@ public class ApkServiceImpl implements ApkService {
         }
     }
 
-    private void saveApkInfo(ApkInfo apkInfo, String apkPath, String traceId){
+    @Override
+    public void saveApkInfo(ApkInfo apkInfo, String apkPath, String traceId){
         String label = apkInfo.getApplicationLable();
         String sdkVersion = apkInfo.getSdkVersion();
         String targetSdkVersion = apkInfo.getTargetSdkVersion();

+ 6 - 3
src/main/java/net/mooctest/www/android_auto_test/services/Impl/AutoTestServiceImpl.java

@@ -5,6 +5,7 @@ import com.alibaba.fastjson.JSONObject;
 import com.sinaapp.msdxblog.apkUtil.entity.ApkInfo;
 import com.sinaapp.msdxblog.apkUtil.utils.ApkUtil;
 import net.mooctest.www.android_auto_test.common.constant.Consts;
+import net.mooctest.www.android_auto_test.common.constant.enums.DeviceStatus;
 import net.mooctest.www.android_auto_test.common.constant.enums.TraceStatus;
 import net.mooctest.www.android_auto_test.common.exceptions.ApkDownloadException;
 import net.mooctest.www.android_auto_test.common.exceptions.ApkParseException;
@@ -77,6 +78,7 @@ public class AutoTestServiceImpl implements AutoTestService {
                 filePath = apkService.downloadApk(downloadUrl, traceId);
                 PrintUtil.print("Start parse apkFile, TraceId: " + traceId, TAG);
                 apkInfo = apkService.parseAPK(filePath, traceId);
+                apkService.saveApkInfo(apkInfo, filePath, traceId);
             } catch (ApkDownloadException e){
                 PrintUtil.print(String.format("Download failed, trace %s end.", traceId), TAG);
                 traceService.updateTraceStatue(traceId, TraceStatus.DOWNLOAD_FAILED);
@@ -88,24 +90,25 @@ public class AutoTestServiceImpl implements AutoTestService {
             }
             // 获取目前在线设备
             List<Device> deviceList = deviceService.getOnlineDeviceList();
-            // 选择设备,后期可以修改为如果前端指定了设备or条件,则根据指定的来;目前是选择所有在线的
             List<Device> selectedDevices = deviceService.selectDeviceByApp(apkInfo, deviceList, selectDevices);
             if (selectedDevices == null || selectedDevices.size() == 0){
                 traceService.updateTraceStatue(traceId, TraceStatus.FAILED);
-                throw new NoFreeDeviceException("没有空闲设备");
+                throw new NoFreeDeviceException("暂无空闲设备");
             }
             traceService.setTraceDevices(traceId, selectedDevices);
             traceService.updateTraceStatue(traceId, TraceStatus.RUNNING);
             apkService.updateTraceStartTime(traceId);
             List<CoverageTest> oneTraceTasks = new ArrayList<>(selectedDevices.size());
             for (Device device: selectedDevices){
+                //TODO 这里需要CAS更新,而不是单纯的更新
+                deviceService.updateDeviceStatus(device.getUdid(), DeviceStatus.RUNNING);
                 CoverageTest coverageTest = new CoverageTest(apkInfo, filePath, device, traceId);
                 coverageTest.setName(Consts.AUTO_TEST_THREAD_NAME_PREFIX + device.getUdid());
                 coverageTest.start();
                 oneTraceTasks.add(coverageTest);
             }
             //启动监控线程监控该trace中所有执行线程
-            TraceDaemon traceDaemon = new TraceDaemon(traceId,timeout, oneTraceTasks);
+            TraceDaemon traceDaemon = new TraceDaemon(traceId,timeout, oneTraceTasks, true, false,"BugReport");
             traceDaemon.setName(Consts.DAEMON_THREAD_NAME_PREFIX + traceId);
             traceDaemon.start();
         }

+ 24 - 2
src/main/java/net/mooctest/www/android_auto_test/services/Impl/DeviceServiceImpl.java

@@ -2,7 +2,9 @@ package net.mooctest.www.android_auto_test.services.Impl;
 
 import com.sinaapp.msdxblog.apkUtil.entity.ApkInfo;
 import net.mooctest.www.android_auto_test.common.constant.enums.DeviceRunningStatus;
+import net.mooctest.www.android_auto_test.common.constant.enums.DeviceStatus;
 import net.mooctest.www.android_auto_test.dao.RedisMappers.DeviceRunningStatusMap;
+import net.mooctest.www.android_auto_test.dao.RedisMappers.DeviceStatusMap;
 import net.mooctest.www.android_auto_test.models.Device;
 import net.mooctest.www.android_auto_test.services.DeviceService;
 import net.mooctest.www.android_auto_test.utils.OsUtil;
@@ -21,6 +23,9 @@ public class DeviceServiceImpl implements DeviceService {
     @Autowired
     DeviceRunningStatusMap deviceRunningStatusMap;
 
+    @Autowired
+    DeviceStatusMap deviceStatusMap;
+
     @Override
     public List<Device> getOnlineDeviceList() {
         String command = "adb devices";
@@ -48,11 +53,18 @@ public class DeviceServiceImpl implements DeviceService {
 
     @Override
     public List<Device> selectDeviceByApp(ApkInfo apkInfo, List<Device> devices, List<String> selectDevices) {
+        List<Device> freeDevices = new ArrayList<>();
+        for (Device d: devices){
+            DeviceStatus ds = deviceStatusMap.get(d.getUdid());
+            if (ds == null || ds == DeviceStatus.FREE){
+                freeDevices.add(d);
+            }
+        }
         if (selectDevices == null || selectDevices.size() == 0){
-            return devices;
+            return freeDevices;
         }
         List<Device> selected = new ArrayList<>();
-        for (Device d: devices){
+        for (Device d: freeDevices){
             String udid = d.getUdid();
             if (selectDevices.contains(udid)){
                 selected.add(d);
@@ -70,4 +82,14 @@ public class DeviceServiceImpl implements DeviceService {
     public DeviceRunningStatus getDeviceRunningStatus(String traceId, String udid) {
         return deviceRunningStatusMap.get(udid, traceId);
     }
+
+    @Override
+    public void updateDeviceStatus(String udid, DeviceStatus deviceStatus) {
+        deviceStatusMap.put(udid, deviceStatus);
+    }
+
+    @Override
+    public DeviceStatus getDeviceStatus(String udid) {
+        return deviceStatusMap.get(udid);
+    }
 }

+ 36 - 0
src/main/java/net/mooctest/www/android_auto_test/services/Impl/SecondaryServiceImpl.java

@@ -0,0 +1,36 @@
+package net.mooctest.www.android_auto_test.services.Impl;
+
+import net.mooctest.www.android_auto_test.common.exceptions.HttpNotFoundException;
+import net.mooctest.www.android_auto_test.services.SecondaryService;
+import net.mooctest.www.android_auto_test.utils.AddressUtil;
+import org.springframework.stereotype.Service;
+
+import java.io.File;
+
+@Service
+public class SecondaryServiceImpl implements SecondaryService {
+
+    @Override
+    public void generateCrowdSourced(String traceId) {
+        File traceDir = new File(AddressUtil.getTraceDir(traceId));
+        if (!traceDir.exists()) {
+            throw new HttpNotFoundException("任务不存在或信息已被清理,请重新测试");
+        }
+        RunTraceThread t = new RunTraceThread(traceId);
+        t.start();
+    }
+
+    private class RunTraceThread extends Thread{
+        private String traceId;
+        RunTraceThread(String traceId) {
+            this.traceId = traceId;
+        }
+
+        @Override
+        public void run(){
+            //TODO 执行韦志宾的命令,然后生成文件,发给众测平台
+            System.out.println("Generate Crowdsourced requirements for trace " + traceId);
+            System.out.println("Send Crowdsourced requirements to Mooctest.");
+        }
+    }
+}

+ 6 - 0
src/main/java/net/mooctest/www/android_auto_test/services/SecondaryService.java

@@ -0,0 +1,6 @@
+package net.mooctest.www.android_auto_test.services;
+
+public interface SecondaryService{
+
+    public void generateCrowdSourced(String traceId);
+}

+ 6 - 2
src/main/java/net/mooctest/www/android_auto_test/utils/AppiumManager.java

@@ -16,7 +16,7 @@ import java.util.HashMap;
 public class AppiumManager {
     public static final String TAG = Thread.currentThread() .getStackTrace()[1].getClassName();
     private static final int INIT_PORT = 4730;
-    private static AppiumManager manager;
+    private volatile static AppiumManager manager;
     private HashMap<String, String> udidAndPortMap = new HashMap<>();
     private HashMap<String, String> portAndUdidMap = new HashMap<>();
     private int nextPort;
@@ -28,7 +28,11 @@ public class AppiumManager {
 
     public static AppiumManager getInstance() {
         if (manager == null) {
-            manager = new AppiumManager();
+            synchronized (AppiumManager.class){
+                if (manager == null){
+                    manager = new AppiumManager();
+                }
+            }
         }
         return manager;
     }

+ 4 - 0
src/main/java/net/mooctest/www/android_auto_test/utils/CoverageTest.java

@@ -2,6 +2,7 @@ package net.mooctest.www.android_auto_test.utils;
 
 import com.sinaapp.msdxblog.apkUtil.entity.ApkInfo;
 import io.appium.java_client.android.AndroidDriver;
+import lombok.Getter;
 import net.mooctest.www.android_auto_test.Obversers.DeviceObserver;
 import net.mooctest.www.android_auto_test.Obversers.ScreenShotThread;
 import net.mooctest.www.android_auto_test.Obversers.monitor.LogMonitor;
@@ -9,6 +10,7 @@ import net.mooctest.www.android_auto_test.Scripts.AbstractBaseScript;
 import net.mooctest.www.android_auto_test.Scripts.DefaultScript;
 import net.mooctest.www.android_auto_test.common.BeanFactory;
 import net.mooctest.www.android_auto_test.common.constant.Consts;
+import net.mooctest.www.android_auto_test.common.constant.enums.DeviceStatus;
 import net.mooctest.www.android_auto_test.common.exceptions.AppiumDriverInitException;
 import net.mooctest.www.android_auto_test.common.exceptions.TraceTimeoutException;
 import net.mooctest.www.android_auto_test.common.constant.enums.DeviceRunningStatus;
@@ -47,6 +49,7 @@ public class CoverageTest extends Thread{
     private String apkPath;
     private ApkInfo apkInfo;
     private Device device;
+    @Getter
     private String udid;
     private String logPath;
     private String port;
@@ -131,6 +134,7 @@ public class CoverageTest extends Thread{
         }
         finally {
             deviceService.updateDeviceRunningStatus(traceId, udid, DeviceRunningStatus.ENDING);
+            deviceService.updateDeviceStatus(udid, DeviceStatus.FREE);
             this.endTime = System.currentTimeMillis();
             // 恢复输入法
             setDefaultIME();

+ 77 - 42
src/main/java/net/mooctest/www/android_auto_test/utils/TraceDaemon.java

@@ -8,8 +8,10 @@ import com.google.gson.JsonArray;
 import com.google.gson.JsonObject;
 import net.mooctest.www.android_auto_test.common.BeanFactory;
 import net.mooctest.www.android_auto_test.common.constant.Consts;
+import net.mooctest.www.android_auto_test.common.constant.enums.DeviceStatus;
 import net.mooctest.www.android_auto_test.common.constant.enums.TraceStatus;
 import net.mooctest.www.android_auto_test.services.ApkService;
+import net.mooctest.www.android_auto_test.services.DeviceService;
 import net.mooctest.www.android_auto_test.services.OssService;
 import net.mooctest.www.android_auto_test.services.TraceService;
 import org.apache.commons.io.FileUtils;
@@ -34,11 +36,18 @@ public class TraceDaemon extends Thread{
     private int minutes;
     private List<CoverageTest> deviceThreads;
     private boolean stopImmediately = false;
+    private boolean needGenerateReport;
+    private boolean needSendReport;
+    private String reportType;
 
-    public TraceDaemon(String traceId, int minutes, List<CoverageTest> deviceThreads){
+    public TraceDaemon(String traceId, int minutes, List<CoverageTest> deviceThreads,
+                       boolean needGenerateReport, boolean needSendReport, String reportType){
         this.traceId = traceId;
         this.minutes = minutes;
         this.deviceThreads = deviceThreads;
+        this.needGenerateReport = needGenerateReport;
+        this.needSendReport = needSendReport;
+        this.reportType = reportType;
         traceService = (TraceService) BeanFactory.getBean(TraceService.class);
         ossService = (OssService) BeanFactory.getBean(OssService.class);
         apkService = (ApkService) BeanFactory.getBean(ApkService.class);
@@ -82,62 +91,88 @@ public class TraceDaemon extends Thread{
         } catch (InterruptedException e) {
             e.printStackTrace();
         } finally {
-            traceService.updateTraceStatue(traceId, TraceStatus.GEN_REPORT);
-            // 上传截图到OSS
-            File traceDir = new File(AddressUtil.getTraceDir(traceId));
-            if (traceDir.exists()){
-                for (File f: traceDir.listFiles()){
-                    if (f.isDirectory()){
-                        String udid = f.getName();
-                        File screenDir = new File(AddressUtil.getScreenShotPath(traceId, udid));
-                        if (!screenDir.exists()){
-                            continue;
-                        }
-                        for (File screenShot: screenDir.listFiles()){
-                            System.out.println(screenShot);
-                            ossService.uploadFileToTraceDir(screenShot, traceId, screenShot.getName());
-                        }
+            if (needGenerateReport) {
+                generateReport();
+            }
+            if (needSendReport) {
+                //TODO 生成报告之后,将报告发给企业版主站
+                sendReport();
+            }
+            apkService.updateTraceEndTime(traceId);
+            PrintUtil.print(String.format("Trace %s is done!", traceId), TAG);
+            traceService.updateTraceStatue(traceId, TraceStatus.FINISH);
+        }
+    }
+
+    private void generateReport(){
+        traceService.updateTraceStatue(traceId, TraceStatus.GEN_REPORT);
+        // 上传截图到OSS
+        File traceDir = new File(AddressUtil.getTraceDir(traceId));
+        if (traceDir.exists()){
+            for (File f: traceDir.listFiles()){
+                if (f.isDirectory()){
+                    String udid = f.getName();
+                    File screenDir = new File(AddressUtil.getScreenShotPath(traceId, udid));
+                    if (!screenDir.exists()){
+                        continue;
+                    }
+                    for (File screenShot: screenDir.listFiles()){
+                        System.out.println(screenShot);
+                        ossService.uploadFileToTraceDir(screenShot, traceId, screenShot.getName());
                     }
                 }
             }
-            // TODO 生成报告
-            try {
+        }
+        try {
+            if ("BugReport".equals(reportType)) {
                 PrintUtil.print("Generate bug report by AllenTian's docker", TAG);
                 String command = "java -jar tasks/AutoTestReportGenerator.jar " + traceId;
                 String result = OsUtil.runCommand(command);
+                // TODO 这里在田老师改了生成逻辑后,就可以删掉了
                 if (!"success\n".equals(result)){
                     PrintUtil.print(result, TAG);
                     PrintUtil.print("Generate bug report failed, use default report.", TAG);
                     FileUtils.copyFile(new File(AddressUtil.getMockDataJsonPath()), new File(AddressUtil.getDataJsonPath(traceId)));
                 }
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        // 上传data.json文件
+        File data = new File(AddressUtil.getDataJsonPath(traceId));
+        String path = ossService.uploadFileToTraceDir(data, traceId, Consts.REPORT_FILE_NAME);
+        int retryTimes = 0;
+        // 上传失败,重试5次
+        while (path != null && retryTimes < Consts.UPLOAD_OSS_MAX_ATTEMPTS){
+            try {
+                Thread.sleep(3000);
+                path = ossService.uploadFileToTraceDir(data, traceId, Consts.REPORT_FILE_NAME);
             } catch (Exception e) {
                 e.printStackTrace();
+                PrintUtil.print(String.format("Trace %s upload data failed %s times.", traceId, retryTimes), TAG);
+            } finally {
+                retryTimes++;
             }
-            // 上传data.json文件
-            File data = new File(AddressUtil.getDataJsonPath(traceId));
-            String path = ossService.uploadFileToTraceDir(data, traceId, Consts.REPORT_FILE_NAME);
-            int retryTimes = 0;
-            // 上传失败,重试5次
-            while (path != null && retryTimes < Consts.UPLOAD_OSS_MAX_ATTEMPTS){
-                try {
-                    Thread.sleep(3000);
-                    path = ossService.uploadFileToTraceDir(data, traceId, Consts.REPORT_FILE_NAME);
-                } catch (Exception e) {
-                    e.printStackTrace();
-                    PrintUtil.print(String.format("Trace %s upload data failed %s times.", traceId, retryTimes), TAG);
-                } finally {
-                    retryTimes++;
-                }
-            }
-            apkService.updateTraceEndTime(traceId);
-            if (path != null){
-                PrintUtil.print(String.format("Trace %s's data upload success.", traceId), TAG);
-            }else {
-                PrintUtil.print(String.format("Trace %s's data upload failed after tried 5 times.", traceId), TAG);
-            }
-            PrintUtil.print(String.format("Trace %s is done!", traceId), TAG);
-            traceService.updateTraceStatue(traceId, TraceStatus.FINISH);
         }
+        if (path != null){
+            PrintUtil.print(String.format("Trace %s's data upload success.", traceId), TAG);
+        }else {
+            PrintUtil.print(String.format("Trace %s's data upload failed after tried 5 times.", traceId), TAG);
+        }
+    }
+
+    private void sendReport(){
+        System.out.println("Send report to Mooctest.");
+//        RestTemplate rt = new RestTemplate();
+//        HttpHeaders headers = new HttpHeaders();
+//        headers.setContentType(MediaType.APPLICATION_JSON);
+//        TraceStatusResult traceResult = new TraceStatusResult();
+//        traceResult.setTraceId(traceId);
+//        String downloadUrl = ossService.getDadaJsonDownloadPath(traceId);
+//        traceResult.setDownloadUrl(downloadUrl);
+//        HttpEntity<TraceStatusResult> httpEntity = new HttpEntity<>(traceResult, headers);
+//        String url = "http://localhost:15926/api/v1/mock/TraceInfo";
+//        rt.exchange(url, HttpMethod.POST, httpEntity, String.class);
     }
 
     /**

+ 2 - 0
src/main/java/net/mooctest/www/android_auto_test/vo/TraceMetaInfo.java

@@ -13,4 +13,6 @@ public class TraceMetaInfo {
     private String traceId;
     private int limitTime = 20;
     private List<String> selectDevices;
+    private boolean needGenerateReport = false;
+    private String reportType = "BugReport";
 }