Przeglądaj źródła

设备状态添加准备态,添加设备状态的CAS lua脚本,以解决一些同步问题
TODO:CAS同步的使用

LiHaoyu 5 lat temu
rodzic
commit
318d66538d
18 zmienionych plików z 157 dodań i 79 usunięć
  1. 5 0
      pom.xml
  2. 0 20
      src/main/java/net/mooctest/www/android_auto_test/common/DeviceStatusRunner.java
  3. 5 0
      src/main/java/net/mooctest/www/android_auto_test/common/constant/Consts.java
  4. 10 10
      src/main/java/net/mooctest/www/android_auto_test/common/constant/enums/DeviceRunningStatus.java
  5. 12 6
      src/main/java/net/mooctest/www/android_auto_test/common/constant/enums/DeviceStatus.java
  6. 11 11
      src/main/java/net/mooctest/www/android_auto_test/common/constant/enums/TraceStatus.java
  7. 12 6
      src/main/java/net/mooctest/www/android_auto_test/common/runner/MyApplicationRunner.java
  8. 4 5
      src/main/java/net/mooctest/www/android_auto_test/dao/RedisMappers/DeviceRunningStatusMap.java
  9. 32 9
      src/main/java/net/mooctest/www/android_auto_test/dao/RedisMappers/DeviceStatusMap.java
  10. 4 5
      src/main/java/net/mooctest/www/android_auto_test/dao/RedisMappers/TraceStatusMap.java
  11. 5 1
      src/main/java/net/mooctest/www/android_auto_test/services/Impl/AutoTestServiceImpl.java
  12. 1 1
      src/main/java/net/mooctest/www/android_auto_test/utils/CoverageTest.java
  13. 1 1
      src/main/java/net/mooctest/www/android_auto_test/vo/DeviceStatusResult.java
  14. 1 0
      src/main/java/net/mooctest/www/android_auto_test/vo/TraceMetaInfo.java
  15. 1 1
      src/main/java/net/mooctest/www/android_auto_test/vo/TraceStatusResult.java
  16. 14 0
      src/main/resources/CompareAndSet.lua
  17. 1 0
      src/main/resources/application.yaml
  18. 38 3
      src/test/java/net/mooctest/www/android_auto_test/AndroidAutoTestApplicationTests.java

+ 5 - 0
pom.xml

@@ -19,6 +19,11 @@
     </properties>
 
     <dependencies>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
         <!-- https://mvnrepository.com/artifact/org.jdom/jdom2 -->
         <dependency>
             <groupId>org.jdom</groupId>

+ 0 - 20
src/main/java/net/mooctest/www/android_auto_test/common/DeviceStatusRunner.java

@@ -1,20 +0,0 @@
-package net.mooctest.www.android_auto_test.common;
-
-import net.mooctest.www.android_auto_test.utils.DeviceDaemon;
-import org.springframework.boot.CommandLineRunner;
-import org.springframework.core.annotation.Order;
-import org.springframework.stereotype.Component;
-
-/**
- * @author henrylee
- */
-@Component
-@Order(value = 1)
-public class DeviceStatusRunner implements CommandLineRunner {
-
-    @Override
-    public void run(String... args) throws Exception {
-//        DeviceDaemon deviceDaemon = new DeviceDaemon();
-//        deviceDaemon.start();
-    }
-}

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

@@ -32,4 +32,9 @@ public class Consts {
     public static final String[] IGNORE_KEYWORD = {"id", "text", "content_desc"};
 
     public static final String REPORT_FILE_NAME = "reportData.json";
+
+    /**
+     * Lua脚本常量
+     */
+    public static final String CAS_SCRIPT = "CompareAndSet.lua";
 }

+ 10 - 10
src/main/java/net/mooctest/www/android_auto_test/common/constant/enums/DeviceRunningStatus.java

@@ -10,37 +10,37 @@ public enum DeviceRunningStatus {
     /**
      * 设备初始化状态,包括启动apppium和监控logcat线程
      */
-    INIT(0, "初始化"),
+    INIT("0", "初始化"),
     /**
      * 设备准备中,包括初始化driver,各种监控线程
      */
-    PRAPARE(1, "准备中"),
+    PRAPARE("1", "准备中"),
     /**
      * 设备执行自动化测试状态
      */
-    RUNNING(2, "运行中"),
+    RUNNING("2", "运行中"),
     /**
      * 收尾工作,包括结束监控线程、还原设备状态、停止appium
      */
-    ENDING(3, "结束中"),
+    ENDING("3", "结束中"),
     /**
      * 收尾工作结束
      */
-    FINISH(4, "完成"),
+    FINISH("4", "完成"),
 
-    PENDING(5, "设备离线,重试中");
+    PENDING("5", "设备离线,重试中");
 
-    private int code;
+    private String code;
     private String description;
 
-    DeviceRunningStatus(int code, String des){
+    DeviceRunningStatus(String code, String des){
         this.code = code;
         this.description = des;
     }
 
-    public static DeviceRunningStatus getStatusByCode(int code){
+    public static DeviceRunningStatus getStatusByCode(String code){
         for (DeviceRunningStatus ds:values()){
-            if (ds.code == code){
+            if (ds.code.equals(code)){
                 return ds;
             }
         }

+ 12 - 6
src/main/java/net/mooctest/www/android_auto_test/common/constant/enums/DeviceStatus.java

@@ -7,24 +7,30 @@ public enum DeviceStatus {
     /**
      * 设备正在运行任务
      */
-    RUNNING(1, "执行中"),
+    RUNNING("1", "执行中"),
     /**
      * 设备空闲
      */
-    FREE(0, "空闲");
+    FREE("0", "空闲"),
+    /**
+     * 准备中,可以理解为空闲到执行中的中间状态
+     * 当任何线程发现设备是空闲的时候,立刻吧空闲改成准备
+     * 然后再做后面的操作,成功则变为执行、否者变回空闲
+     */
+    OCCUPY("2", "占用中");
 
 
-    private int code;
+    private String code;
     private String description;
 
-    private DeviceStatus(int code, String des){
+    private DeviceStatus(String code, String des){
         this.code = code;
         this.description = des;
     }
 
-    public static DeviceStatus getStatusByCode(int code){
+    public static DeviceStatus getStatusByCode(String code){
         for (DeviceStatus ts: values()){
-            if (ts.code == code){
+            if (ts.code.equals(code)){
                 return ts;
             }
         }

+ 11 - 11
src/main/java/net/mooctest/www/android_auto_test/common/constant/enums/TraceStatus.java

@@ -10,44 +10,44 @@ public enum TraceStatus {
     /**
      * 初始化,包括下载apk
      */
-    INIT(0, "初始化"),
+    INIT("0", "初始化"),
     /**
      * 执行中,至少有一台device的状态不是Finish {@link DeviceRunningStatus#FINISH}
      */
-    RUNNING(1, "执行中"),
+    RUNNING("1", "执行中"),
     /**
      * 生成报告中
      */
-    GEN_REPORT(2, "报告生成中"),
+    GEN_REPORT("2", "报告生成中"),
     /**
      * 报告生成结束
      */
-    FINISH(3, "完成"),
+    FINISH("3", "完成"),
 
     /**
      * APK下载失败
      */
-    DOWNLOAD_FAILED(4, "下载失败"),
+    DOWNLOAD_FAILED("4", "下载失败"),
 
     /**
      * APK包解析失败
      */
-    BAGGING_FAILED(5, "APK解包失败"),
+    BAGGING_FAILED("5", "APK解包失败"),
 
 
-    FAILED(6, "失败");
+    FAILED("6", "失败");
 
-    private int code;
+    private String code;
     private String description;
 
-    private TraceStatus(int code, String des){
+    private TraceStatus(String  code, String des){
         this.code = code;
         this.description = des;
     }
 
-    public static TraceStatus getStatusByCode(int code){
+    public static TraceStatus getStatusByCode(String code){
         for (TraceStatus ts: values()){
-            if (ts.code == code){
+            if (ts.code.equals(code)){
                 return ts;
             }
         }

+ 12 - 6
src/main/java/net/mooctest/www/android_auto_test/common/runner/MyApplicationRunner.java

@@ -13,6 +13,7 @@ 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.beans.factory.annotation.Value;
 import org.springframework.boot.ApplicationArguments;
 import org.springframework.boot.ApplicationRunner;
 import org.springframework.http.HttpEntity;
@@ -36,10 +37,12 @@ public class MyApplicationRunner implements ApplicationRunner {
     @Autowired
     ApkService apkService;
 
+    @Value("${poll-tasks}")
+    private boolean pollTasks;
+
     @Override
     public void run(ApplicationArguments args) throws Exception {
-        String onOff = args.getSourceArgs()[0];
-        if (!"pollingOn".equals(onOff)){
+        if (!pollTasks){
             return;
         }
         System.out.println("轮询获取任务信息");
@@ -55,13 +58,13 @@ public class MyApplicationRunner implements ApplicationRunner {
             // 获取目前在线设备且可用的设备
             List<Device> deviceList = deviceService.getOnlineDeviceList();
             for (Device device: deviceList){
-                //TODO 这里用CAS去更新device的状态
-                // 或者把接收任务的口子关了。。。
                 DeviceStatus deviceStatus = deviceService.getDeviceStatus(device.getUdid());
-                if (deviceStatus == DeviceStatus.RUNNING){
+                if (deviceStatus != DeviceStatus.FREE){
                     PrintUtil.print("Device is running, continue.", TAG, device.getUdid());
                     continue;
                 }
+                //TODO 这里用CAS去更新device的状态
+                // 或者把接收任务的口子关了。。
                 device = buildDevice(device);
                 PrintUtil.print("Get TraceInfo.", TAG, device.getUdid());
                 TraceMetaInfo traceMetaInfo = getTraceMetaInfo(device);
@@ -102,7 +105,10 @@ public class MyApplicationRunner implements ApplicationRunner {
 
                 //启动守护线程
                 PrintUtil.print("Start test daemon thread.", TAG, device.getUdid());
-                TraceDaemon traceDaemon = new TraceDaemon(traceId, limitTime, oneTraceTasks, traceMetaInfo.isNeedGenerateReport(),true, "BugReport");
+                TraceDaemon traceDaemon = new TraceDaemon(traceId, limitTime, oneTraceTasks,
+                        traceMetaInfo.isNeedGenerateReport(),
+                        traceMetaInfo.isNeedSendReport(),
+                        traceMetaInfo.getReportType());
                 traceDaemon.setName(Consts.DAEMON_THREAD_NAME_PREFIX + traceId);
                 traceDaemon.start();
 

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

@@ -20,16 +20,15 @@ public class DeviceRunningStatusMap {
     StringRedisTemplate redisTemplate;
 
     public DeviceRunningStatus get(String udid, String traceId){
-        String o = redisTemplate.opsForValue().get(DEVICE_RUNNING_STATUS_PREFIX + traceId + "_" + udid);
-        if (o == null){
+        String statusCode = redisTemplate.opsForValue().get(DEVICE_RUNNING_STATUS_PREFIX + traceId + "_" + udid);
+        if (statusCode == null){
             return null;
         }
-        int code = Integer.parseInt(o);
-        return DeviceRunningStatus.getStatusByCode(code);
+        return DeviceRunningStatus.getStatusByCode(statusCode);
     }
 
     public void put(String udid, String traceId, DeviceRunningStatus status){
-        redisTemplate.opsForValue().set(DEVICE_RUNNING_STATUS_PREFIX + traceId + "_" + udid, String.valueOf(status.getCode()));
+        redisTemplate.opsForValue().set(DEVICE_RUNNING_STATUS_PREFIX + traceId + "_" + udid, status.getCode());
     }
 
 }

+ 32 - 9
src/main/java/net/mooctest/www/android_auto_test/dao/RedisMappers/DeviceStatusMap.java

@@ -1,16 +1,17 @@
 package net.mooctest.www.android_auto_test.dao.RedisMappers;
 
+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.models.Device;
-import net.mooctest.www.android_auto_test.services.DeviceService;
+import org.apache.commons.io.IOUtils;
 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.data.redis.core.script.DefaultRedisScript;
+import org.springframework.data.redis.core.script.RedisScript;
 import org.springframework.stereotype.Component;
 
-import javax.annotation.Resource;
+import java.io.IOException;
+import java.util.Collections;
 
 @Component
 public class DeviceStatusMap {
@@ -20,15 +21,37 @@ public class DeviceStatusMap {
     StringRedisTemplate redisTemplate;
 
     public DeviceStatus get(String udid){
-        String o = redisTemplate.opsForValue().get(DEVICE_STATUS_PREFIX + udid);
-        if (o == null){
+        String statusCode = redisTemplate.opsForValue().get(DEVICE_STATUS_PREFIX + udid);
+        if (statusCode == 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()));
+        redisTemplate.opsForValue().set(DEVICE_STATUS_PREFIX + udid, status.getCode());
+    }
+
+    /**
+     * 使用Lua脚本执行CAS操作
+     * @param udid 设备ID
+     * @param oldStatus 旧状态
+     * @param newStatus 新状态
+     * @return 是否成功
+     */
+    public boolean compareAndSet(String udid, DeviceStatus oldStatus, DeviceStatus newStatus){
+        try {
+            // <1.1> 读取 /resources/lua/compareAndSet.lua 脚本 。注意,需要引入下 commons-io 依赖。
+            String scriptContents = IOUtils.toString(getClass().getResourceAsStream("/" + Consts.CAS_SCRIPT), "UTF-8");
+            // <1.2> 创建 RedisScript 对象
+            RedisScript<Long> script = new DefaultRedisScript<>(scriptContents, Long.class);
+            // <2> 执行 LUA 脚本
+            Long result = redisTemplate.execute(script,
+                    Collections.singletonList(DEVICE_STATUS_PREFIX + udid),
+                    oldStatus.getCode(), newStatus.getCode());
+            return result == 1;
+        } catch (IOException e){
+            return false;
+        }
     }
 }

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

@@ -21,15 +21,14 @@ public class TraceStatusMap {
     StringRedisTemplate redisTemplate;
 
     public TraceStatus get(String traceId){
-        String o = redisTemplate.opsForValue().get(TRACE_STATUS_PREFIX + traceId);
-        if (o == null){
+        String statusCode = redisTemplate.opsForValue().get(TRACE_STATUS_PREFIX + traceId);
+        if (statusCode == null){
             return null;
         }
-        int code = Integer.parseInt(o);
-        return TraceStatus.getStatusByCode(code);
+        return TraceStatus.getStatusByCode(statusCode);
     }
 
     public void put(String traceId, TraceStatus status){
-        redisTemplate.opsForValue().set(TRACE_STATUS_PREFIX + traceId, String.valueOf(status.getCode()));
+        redisTemplate.opsForValue().set(TRACE_STATUS_PREFIX + traceId, status.getCode());
     }
 }

+ 5 - 1
src/main/java/net/mooctest/www/android_auto_test/services/Impl/AutoTestServiceImpl.java

@@ -90,6 +90,7 @@ public class AutoTestServiceImpl implements AutoTestService {
             }
             // 获取目前在线设备
             List<Device> deviceList = deviceService.getOnlineDeviceList();
+            // 获取空闲设备
             List<Device> selectedDevices = deviceService.selectDeviceByApp(apkInfo, deviceList, selectDevices);
             if (selectedDevices == null || selectedDevices.size() == 0){
                 traceService.updateTraceStatue(traceId, TraceStatus.FAILED);
@@ -108,7 +109,10 @@ public class AutoTestServiceImpl implements AutoTestService {
                 oneTraceTasks.add(coverageTest);
             }
             //启动监控线程监控该trace中所有执行线程
-            TraceDaemon traceDaemon = new TraceDaemon(traceId,timeout, oneTraceTasks, true, false,"BugReport");
+            TraceDaemon traceDaemon = new TraceDaemon(traceId,timeout, oneTraceTasks,
+                    true,
+                    false,
+                    traceInfo.getReportType());
             traceDaemon.setName(Consts.DAEMON_THREAD_NAME_PREFIX + traceId);
             traceDaemon.start();
         }

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

@@ -134,7 +134,6 @@ public class CoverageTest extends Thread{
         }
         finally {
             deviceService.updateDeviceRunningStatus(traceId, udid, DeviceRunningStatus.ENDING);
-            deviceService.updateDeviceStatus(udid, DeviceStatus.FREE);
             this.endTime = System.currentTimeMillis();
             // 恢复输入法
             setDefaultIME();
@@ -152,6 +151,7 @@ public class CoverageTest extends Thread{
             // 停止appium server
             stopAppiumServer(this.port);
             PrintUtil.print("Device " + udid + ", test has finished", TAG, udid);
+            deviceService.updateDeviceStatus(udid, DeviceStatus.FREE);
             deviceService.updateDeviceRunningStatus(traceId, udid, DeviceRunningStatus.FINISH);
             finalEnd = true;
         }

+ 1 - 1
src/main/java/net/mooctest/www/android_auto_test/vo/DeviceStatusResult.java

@@ -5,6 +5,6 @@ import lombok.Data;
 @Data
 public class DeviceStatusResult {
     private String deviceId;
-    private int statusCode;
+    private String statusCode;
     private String description;
 }

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

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

+ 1 - 1
src/main/java/net/mooctest/www/android_auto_test/vo/TraceStatusResult.java

@@ -9,7 +9,7 @@ import lombok.Data;
 @Data
 public class TraceStatusResult {
     private String traceId;
-    private int statusCode;
+    private String statusCode;
     private String description;
     private String downloadUrl;
     private JSONObject extraInfo;

+ 14 - 0
src/main/resources/CompareAndSet.lua

@@ -0,0 +1,14 @@
+-- key不存在,赋值key
+if redis.call('EXISTS', KEYS[1]) == 0 then
+    redis.call('SET', KEYS[1], ARGV[2])
+    return 1
+end
+
+-- key存在,但与旧值不相等,不进行赋值
+if redis.call('GET', KEYS[1]) ~= ARGV[1] then
+    return 0
+end
+
+-- key存在,且与旧值相等,进行赋值
+redis.call('SET', KEYS[1], ARGV[2])
+return 1

+ 1 - 0
src/main/resources/application.yaml

@@ -1,4 +1,5 @@
 demo: true
+poll-tasks: false
 spring.profiles.active: dev
 
 ---

+ 38 - 3
src/test/java/net/mooctest/www/android_auto_test/AndroidAutoTestApplicationTests.java

@@ -1,13 +1,48 @@
 package net.mooctest.www.android_auto_test;
 
-import org.junit.jupiter.api.Test;
+
+import net.mooctest.www.android_auto_test.common.constant.enums.DeviceStatus;
+import net.mooctest.www.android_auto_test.dao.RedisMappers.DeviceStatusMap;
+import net.mooctest.www.android_auto_test.services.DeviceService;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
 import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.data.redis.core.StringRedisTemplate;
+
+import java.util.List;
+
+import static org.mockito.Mockito.*;
+
 
 @SpringBootTest
-class AndroidAutoTestApplicationTests {
+public class AndroidAutoTestApplicationTests {
+
+	@Mock
+	private StringRedisTemplate redisTemplate;
+
+	@InjectMocks
+	private DeviceStatusMap deviceStatusMap = new DeviceStatusMap();
+
+	@Before
+	public void setUp(){
+		MockitoAnnotations.initMocks(this);
+	}
 
 	@Test
-	void contextLoads() {
+	public void testLuaScript(){
+		when(redisTemplate.execute(any(), any(List.class), anyString(), anyString()))
+				.thenReturn(1L);
+
+		boolean result = deviceStatusMap.compareAndSet("test", DeviceStatus.RUNNING, DeviceStatus.FREE);
+
+		verify(redisTemplate).execute(any(), any(List.class), anyString(), anyString());
+
+		//junit测试
+		Assert.assertTrue(result);
 	}
 
 }