修改测试用例

This commit is contained in:
孙小云 2025-12-18 19:01:41 +08:00
parent 42c620d87a
commit 03dd8ad7d9
9 changed files with 934 additions and 73 deletions

View File

@ -15,10 +15,7 @@ public abstract class AbstractInstruction implements Instruction {
*/
private Instruction onFailureInstruction;
/**
* 无论成功失败都执行的下一个指令
*/
private Instruction alwaysNextInstruction;
@Override
@ -31,10 +28,7 @@ public abstract class AbstractInstruction implements Instruction {
return onFailureInstruction;
}
@Override
public Instruction getAlwaysNextInstruction() {
return alwaysNextInstruction;
}
/**
* 设置成功后执行的指令支持链式调用
@ -56,7 +50,8 @@ public abstract class AbstractInstruction implements Instruction {
* 设置无论成功失败都执行的指令支持链式调用
*/
public <T extends AbstractInstruction> T then(Instruction instruction) {
this.alwaysNextInstruction = instruction;
this.onFailureInstruction = instruction;
this.onSuccessInstruction = instruction;
return (T) this;
}
}

View File

@ -67,12 +67,7 @@ public interface Instruction {
return null;
}
/**
* 获取无论成功失败都执行的下一个指令优先级最高
*/
default Instruction getAlwaysNextInstruction() {
return null;
}
/**
* 根据执行结果获取下一个指令
@ -81,11 +76,6 @@ public interface Instruction {
* @return 下一个指令如果没有则返回null
*/
default Instruction getNextInstruction(boolean success) {
// 优先返回 always 配置
Instruction alwaysNext = getAlwaysNextInstruction();
if (alwaysNext != null) {
return alwaysNext;
}
// 根据成功失败返回对应的指令
if (success) {

View File

@ -0,0 +1,190 @@
package com.tuoheng.machine;
import com.tuoheng.machine.command.CommandResult;
import com.tuoheng.machine.command.CommandType;
import com.tuoheng.machine.mqtt.MqttCallbackRegistry;
import com.tuoheng.machine.state.*;
import com.tuoheng.machine.vendor.VendorRegistry;
import com.tuoheng.machine.vendor.test.TestVendorConfig;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.HashMap;
import java.util.concurrent.*;
import static org.junit.jupiter.api.Assertions.*;
/**
* 基础场景测试
* 测试1-4: 简单成功远程命令失败方法回调超时状态回调超时
*/
@SpringBootTest
@Slf4j
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class BasicScenarioTest {
@Autowired
MachineCommandManager machineCommandManager;
@Autowired
MqttCallbackRegistry mqttCallbackRegistry;
@Autowired
VendorRegistry vendorRegistry;
private static final ScheduledExecutorService scheduler =
Executors.newScheduledThreadPool(2);
private static final String TEST_SN = "BASIC_TEST_SN";
@BeforeAll
public static void setupAll() {
log.info("=== 基础场景测试初始化 ===");
}
@BeforeEach
public void beforeEach(TestInfo testInfo) {
log.info("\n========================================");
log.info("开始测试: {}", testInfo.getDisplayName());
log.info("========================================");
// 注册测试厂家配置
TestVendorConfig testVendorConfig = new TestVendorConfig();
vendorRegistry.registerVendor(testVendorConfig);
// 绑定SN到测试厂家
vendorRegistry.bindSnToVendor(TEST_SN, "TEST");
// 初始化机器状态
MachineStates initialStates = new MachineStates();
initialStates.setAirportState(AirportState.ONLINE);
initialStates.setDroneState(DroneState.ONLINE);
machineCommandManager.updateMachineStates(TEST_SN, initialStates, true);
}
@AfterEach
public void afterEach(TestInfo testInfo) {
log.info("完成测试: {}", testInfo.getDisplayName());
log.info("========================================\n");
// 等待回调自然清理
try {
Thread.sleep(500);
} catch (InterruptedException e) {
log.warn("等待时被中断", e);
}
}
/**
* 测试1: 简单成功场景
*/
@Test
@Order(1)
@DisplayName("测试1: 简单成功场景")
public void testSimpleSuccess() throws ExecutionException, InterruptedException {
log.info(">>> 场景:指令被通过,远程命令成功,方法回调和状态回调都成功");
CompletableFuture<CommandResult> future =
machineCommandManager.executeCommand(TEST_SN, CommandType.TAKE_OFF, new HashMap<>());
scheduler.schedule(() -> {
try {
Thread.sleep(100);
String response = "{\"result\":\"success\"}";
mqttCallbackRegistry.handleMessage("test/" + TEST_SN + "/response", response);
log.info(">>> 模拟发送方法回调: {}", response);
Thread.sleep(100);
response = "{\"status\":\"completed\"}";
mqttCallbackRegistry.handleMessage("test/" + TEST_SN + "/state", response);
log.info(">>> 模拟发送状态回调: {}", response);
} catch (InterruptedException e) {
e.printStackTrace();
}
}, 200, TimeUnit.MILLISECONDS);
CommandResult result = future.get();
assertTrue(result.isSuccess(), "指令应该执行成功");
log.info(">>> 测试通过:指令执行成功");
}
/**
* 测试2: 远程命令失败场景
*/
@Test
@Order(2)
@DisplayName("测试2: 远程命令失败场景")
public void testRemoteCommandFail() throws ExecutionException, InterruptedException {
log.info(">>> 场景:指令被通过,但远程命令执行时抛出异常");
CompletableFuture<CommandResult> future =
machineCommandManager.executeCommand(TEST_SN, CommandType.EMERGENCY_STOP, new HashMap<>());
CommandResult result = future.get();
assertFalse(result.isSuccess(), "指令应该执行失败");
assertNotNull(result.getErrorMessage(), "应该有错误消息");
log.info(">>> 测试通过:远程命令失败,错误消息: {}", result.getErrorMessage());
}
/**
* 测试3: 方法回调超时场景
*/
@Test
@Order(3)
@DisplayName("测试3: 方法回调超时场景")
public void testMethodCallbackTimeout() throws ExecutionException, InterruptedException {
log.info(">>> 场景:远程命令发送成功,但方法回调超时");
CompletableFuture<CommandResult> future =
machineCommandManager.executeCommand(TEST_SN, CommandType.RESUME_FLIGHT, new HashMap<>());
log.info(">>> 不发送方法回调消息,等待超时...");
CommandResult result = future.get();
assertFalse(result.isSuccess(), "指令应该因超时而失败");
assertTrue(result.getErrorMessage().contains("超时") || result.getErrorMessage().contains("timeout"),
"错误消息应该包含超时信息");
log.info(">>> 测试通过:方法回调超时,错误消息: {}", result.getErrorMessage());
}
/**
* 测试4: 状态回调超时场景
*/
@Test
@Order(4)
@DisplayName("测试4: 状态回调超时场景")
public void testStateCallbackTimeout() throws ExecutionException, InterruptedException {
log.info(">>> 场景:方法回调成功,但状态回调超时");
CompletableFuture<CommandResult> future =
machineCommandManager.executeCommand(TEST_SN, CommandType.POINT_FLY, new HashMap<>());
scheduler.schedule(() -> {
try {
Thread.sleep(100);
String response = "{\"result\":\"success\"}";
mqttCallbackRegistry.handleMessage("test/" + TEST_SN + "/response", response);
log.info(">>> 模拟发送方法回调: {}", response);
log.info(">>> 不发送状态回调消息,等待超时...");
} catch (InterruptedException e) {
e.printStackTrace();
}
}, 200, TimeUnit.MILLISECONDS);
CommandResult result = future.get();
assertFalse(result.isSuccess(), "指令应该因状态回调超时而失败");
assertTrue(result.getErrorMessage().contains("超时") || result.getErrorMessage().contains("timeout"),
"错误消息应该包含超时信息");
log.info(">>> 测试通过:状态回调超时,错误消息: {}", result.getErrorMessage());
}
@AfterAll
public static void cleanupAll() {
log.info("=== 基础场景测试完成 ===");
scheduler.shutdown();
}
}

View File

@ -0,0 +1,124 @@
package com.tuoheng.machine;
import com.tuoheng.machine.command.CommandResult;
import com.tuoheng.machine.command.CommandType;
import com.tuoheng.machine.mqtt.MqttCallbackRegistry;
import com.tuoheng.machine.state.*;
import com.tuoheng.machine.vendor.VendorRegistry;
import com.tuoheng.machine.vendor.test.TestVendorConfig;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.*;
import static org.junit.jupiter.api.Assertions.*;
/**
* 指令拒绝测试
* 测试11-12: 指令被拒绝/被通过
*/
@SpringBootTest
@Slf4j
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class CommandRejectionTest {
@Autowired
MachineCommandManager machineCommandManager;
@Autowired
MqttCallbackRegistry mqttCallbackRegistry;
@Autowired
VendorRegistry vendorRegistry;
private static final String TEST_SN = "REJECTION_TEST_SN";
@BeforeAll
public static void setupAll() {
log.info("=== 指令拒绝测试初始化 ===");
}
@BeforeEach
public void beforeEach(TestInfo testInfo) {
log.info("\n========================================");
log.info("开始测试: {}", testInfo.getDisplayName());
log.info("========================================");
// 注册测试厂家配置
TestVendorConfig testVendorConfig = new TestVendorConfig();
vendorRegistry.registerVendor(testVendorConfig);
// 绑定SN到测试厂家
vendorRegistry.bindSnToVendor(TEST_SN, "TEST");
// 初始化机器状态
MachineStates initialStates = new MachineStates();
initialStates.setAirportState(AirportState.ONLINE);
initialStates.setDroneState(DroneState.ONLINE);
machineCommandManager.updateMachineStates(TEST_SN, initialStates, true);
}
@AfterEach
public void afterEach(TestInfo testInfo) {
log.info("完成测试: {}", testInfo.getDisplayName());
log.info("========================================\n");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
log.warn("等待时被中断", e);
}
}
/**
* 测试11: 指令被拒绝场景
*/
@Test
@Order(11)
@DisplayName("测试11: 指令被拒绝场景")
public void testCommandRejected() throws ExecutionException, InterruptedException {
log.info(">>> 场景canExecute返回false指令被拒绝");
Map<String, Object> params = new HashMap<>();
params.put("canExecute", false);
CompletableFuture<CommandResult> future =
machineCommandManager.executeCommand(TEST_SN, CommandType.CANCEL_POINT, params);
CommandResult result = future.get();
assertFalse(result.isSuccess(), "指令应该被拒绝");
assertTrue(result.getErrorMessage().contains("不能执行") || result.getErrorMessage().contains("拒绝")
|| result.getErrorMessage().contains("cannot") || result.getErrorMessage().contains("条件"),
"错误消息应该包含拒绝信息");
log.info(">>> 测试通过:指令被拒绝,错误消息: {}", result.getErrorMessage());
}
/**
* 测试12: 指令被通过场景
*/
@Test
@Order(12)
@DisplayName("测试12: 指令被通过场景")
public void testCommandAccepted() throws ExecutionException, InterruptedException {
log.info(">>> 场景canExecute返回true指令被通过");
Map<String, Object> params = new HashMap<>();
params.put("canExecute", true);
CompletableFuture<CommandResult> future =
machineCommandManager.executeCommand(TEST_SN, CommandType.CANCEL_POINT, params);
CommandResult result = future.get();
assertTrue(result.isSuccess(), "指令应该被接受并执行");
log.info(">>> 测试通过:指令被接受并执行");
}
@AfterAll
public static void cleanupAll() {
log.info("=== 指令拒绝测试完成 ===");
}
}

View File

@ -0,0 +1,173 @@
package com.tuoheng.machine;
import com.tuoheng.machine.command.CommandResult;
import com.tuoheng.machine.command.CommandType;
import com.tuoheng.machine.mqtt.MqttCallbackRegistry;
import com.tuoheng.machine.state.*;
import com.tuoheng.machine.vendor.VendorRegistry;
import com.tuoheng.machine.vendor.test.TestVendorConfig;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.*;
import static org.junit.jupiter.api.Assertions.*;
/**
* 复杂指令树测试
* 测试9-10: 复杂指令树成功路径/失败路径
*/
@SpringBootTest
@Slf4j
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class ComplexTreeTest {
@Autowired
MachineCommandManager machineCommandManager;
@Autowired
MqttCallbackRegistry mqttCallbackRegistry;
@Autowired
VendorRegistry vendorRegistry;
private static final ScheduledExecutorService scheduler =
Executors.newScheduledThreadPool(2);
private static final String TEST_SN = "COMPLEX_TREE_TEST_SN";
@BeforeAll
public static void setupAll() {
log.info("=== 复杂指令树测试初始化 ===");
}
@BeforeEach
public void beforeEach(TestInfo testInfo) {
log.info("\n========================================");
log.info("开始测试: {}", testInfo.getDisplayName());
log.info("========================================");
// 注册测试厂家配置
TestVendorConfig testVendorConfig = new TestVendorConfig();
vendorRegistry.registerVendor(testVendorConfig);
// 绑定SN到测试厂家
vendorRegistry.bindSnToVendor(TEST_SN, "TEST");
// 初始化机器状态
MachineStates initialStates = new MachineStates();
initialStates.setAirportState(AirportState.ONLINE);
initialStates.setDroneState(DroneState.ONLINE);
machineCommandManager.updateMachineStates(TEST_SN, initialStates, true);
}
@AfterEach
public void afterEach(TestInfo testInfo) {
log.info("完成测试: {}", testInfo.getDisplayName());
log.info("========================================\n");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
log.warn("等待时被中断", e);
}
}
/**
* 测试9: 复杂指令树场景成功路径
*/
@Test
@Order(9)
@DisplayName("测试9: 复杂指令树场景(成功路径)")
public void testComplexInstructionTreeSuccess() throws ExecutionException, InterruptedException {
log.info(">>> 场景:复杂的多层嵌套指令树,走成功分支");
Map<String, Object> params = new HashMap<>();
params.put("complexRootShouldFail", false);
CompletableFuture<CommandResult> future =
machineCommandManager.executeCommand(TEST_SN, CommandType.ENTER_DRC_MODE, params);
scheduler.schedule(() -> {
try {
Thread.sleep(100);
String response = "{\"result\":\"success\"}";
mqttCallbackRegistry.handleMessage("test/" + TEST_SN + "/response", response);
log.info(">>> 模拟发送根指令方法回调(成功): {}", response);
Thread.sleep(100);
response = "{\"result\":\"complexSuccess\"}";
mqttCallbackRegistry.handleMessage("test/" + TEST_SN + "/response", response);
log.info(">>> 模拟发送成功分支指令方法回调: {}", response);
Thread.sleep(100);
response = "{\"result\":\"complexCleanup\"}";
mqttCallbackRegistry.handleMessage("test/" + TEST_SN + "/response", response);
log.info(">>> 模拟发送清理指令方法回调: {}", response);
} catch (InterruptedException e) {
e.printStackTrace();
}
}, 200, TimeUnit.MILLISECONDS);
CommandResult result = future.get();
assertTrue(result.isSuccess(), "复杂指令树应该执行成功");
log.info(">>> 测试通过:复杂指令树成功路径执行成功");
}
/**
* 测试10: 复杂指令树场景失败路径
* 注意通过让根指令超时来触发失败分支
*/
@Test
@Order(10)
@DisplayName("测试10: 复杂指令树场景(失败路径)")
public void testComplexInstructionTreeFailure() throws ExecutionException, InterruptedException {
log.info(">>> 场景:复杂的多层嵌套指令树,走失败分支");
log.info(">>> 策略:让根指令超时失败,触发失败分支");
Map<String, Object> params = new HashMap<>();
params.put("complexRootShouldFail", true);
CompletableFuture<CommandResult> future =
machineCommandManager.executeCommand(TEST_SN, CommandType.ENTER_DRC_MODE, params);
scheduler.schedule(() -> {
try {
// 不发送根指令的回调让它超时5秒后
// 等待根指令超时后发送失败分支的回调
log.info(">>> 等待根指令超时,触发失败分支...");
Thread.sleep(6000); // 等待超过5秒超时时间
// 发送失败分支指令的回调
String response = "{\"result\":\"complexFailure\"}";
mqttCallbackRegistry.handleMessage("test/" + TEST_SN + "/response", response);
log.info(">>> 模拟发送失败分支指令方法回调: {}", response);
Thread.sleep(100);
// 发送清理指令的回调
response = "{\"result\":\"complexCleanup\"}";
mqttCallbackRegistry.handleMessage("test/" + TEST_SN + "/response", response);
log.info(">>> 模拟发送清理指令方法回调: {}", response);
} catch (InterruptedException e) {
e.printStackTrace();
}
}, 200, TimeUnit.MILLISECONDS);
CommandResult result = future.get();
assertTrue(result.isSuccess(), "复杂指令树应该通过失败分支和清理成功");
log.info(">>> 测试通过:复杂指令树失败路径执行成功");
}
@AfterAll
public static void cleanupAll() {
log.info("=== 复杂指令树测试完成 ===");
scheduler.shutdown();
}
}

View File

@ -44,7 +44,8 @@ public class ComprehensiveDrcStateMachineTest {
private static final ScheduledExecutorService scheduler =
Executors.newScheduledThreadPool(4);
private static final String TEST_SN = "TEST_SN_001";
private static final String currentTestSn_PREFIX = "currentTestSn_";
private String currentTestSn;
@BeforeAll
public void setup() {
@ -54,15 +55,6 @@ public class ComprehensiveDrcStateMachineTest {
TestVendorConfig testVendorConfig = new TestVendorConfig();
vendorRegistry.registerVendor(testVendorConfig);
// 绑定SN到测试厂家
vendorRegistry.bindSnToVendor(TEST_SN, "TEST");
// 初始化机器状态
MachineStates initialStates = new MachineStates();
initialStates.setAirportState(AirportState.ONLINE);
initialStates.setDroneState(DroneState.ONLINE);
machineCommandManager.updateMachineStates(TEST_SN, initialStates, true);
log.info("=== 综合测试初始化完成 ===");
}
@ -71,6 +63,20 @@ public class ComprehensiveDrcStateMachineTest {
log.info("\n\n========================================");
log.info("开始测试: {}", testInfo.getDisplayName());
log.info("========================================\n");
// 为每个测试生成唯一的 SN避免 topic 冲突
currentTestSn = currentTestSn_PREFIX + System.currentTimeMillis();
// 绑定SN到测试厂家
vendorRegistry.bindSnToVendor(currentTestSn, "TEST");
// 初始化机器状态
MachineStates initialStates = new MachineStates();
initialStates.setAirportState(AirportState.ONLINE);
initialStates.setDroneState(DroneState.ONLINE);
machineCommandManager.updateMachineStates(currentTestSn, initialStates, true);
log.info("当前测试使用 SN: {}", currentTestSn);
}
@AfterEach
@ -79,13 +85,12 @@ public class ComprehensiveDrcStateMachineTest {
log.info("完成测试: {}", testInfo.getDisplayName());
log.info("========================================\n\n");
// 清理所有回调避免影响后续测试
// 等待一段时间让当前测试的回调自然完成或超时
try {
Thread.sleep(200); // 等待当前测试的回调完成
mqttCallbackRegistry.cleanupAllCallbacks();
log.info("已清理所有回调,准备下一个测试");
Thread.sleep(500);
log.debug("测试完成,等待回调自然清理");
} catch (InterruptedException e) {
log.warn("清理回调时被中断", e);
log.warn("等待时被中断", e);
}
}
@ -100,7 +105,7 @@ public class ComprehensiveDrcStateMachineTest {
log.info(">>> 场景:指令被通过,远程命令成功,方法回调和状态回调都成功");
CompletableFuture<CommandResult> future =
machineCommandManager.executeCommand(TEST_SN, CommandType.TAKE_OFF, new HashMap<>());
machineCommandManager.executeCommand(currentTestSn, CommandType.TAKE_OFF, new HashMap<>());
// 模拟设备响应
scheduler.schedule(() -> {
@ -108,13 +113,13 @@ public class ComprehensiveDrcStateMachineTest {
// 1. 发送方法回调
Thread.sleep(100);
String response = "{\"result\":\"success\"}";
mqttCallbackRegistry.handleMessage("test/" + TEST_SN + "/response", response);
mqttCallbackRegistry.handleMessage("test/" + currentTestSn + "/response", response);
log.info(">>> 模拟发送方法回调: {}", response);
// 2. 发送状态回调
Thread.sleep(100);
response = "{\"status\":\"completed\"}";
mqttCallbackRegistry.handleMessage("test/" + TEST_SN + "/state", response);
mqttCallbackRegistry.handleMessage("test/" + currentTestSn + "/state", response);
log.info(">>> 模拟发送状态回调: {}", response);
} catch (InterruptedException e) {
@ -138,7 +143,7 @@ public class ComprehensiveDrcStateMachineTest {
log.info(">>> 场景:指令被通过,但远程命令执行时抛出异常");
CompletableFuture<CommandResult> future =
machineCommandManager.executeCommand(TEST_SN, CommandType.EMERGENCY_STOP, new HashMap<>());
machineCommandManager.executeCommand(currentTestSn, CommandType.EMERGENCY_STOP, new HashMap<>());
CommandResult result = future.get();
assertFalse(result.isSuccess(), "指令应该执行失败");
@ -157,7 +162,7 @@ public class ComprehensiveDrcStateMachineTest {
log.info(">>> 场景:远程命令发送成功,但方法回调超时");
CompletableFuture<CommandResult> future =
machineCommandManager.executeCommand(TEST_SN, CommandType.RESUME_FLIGHT, new HashMap<>());
machineCommandManager.executeCommand(currentTestSn, CommandType.RESUME_FLIGHT, new HashMap<>());
// 不发送任何回调消息让它超时
log.info(">>> 不发送方法回调消息,等待超时...");
@ -180,14 +185,14 @@ public class ComprehensiveDrcStateMachineTest {
log.info(">>> 场景:方法回调成功,但状态回调超时");
CompletableFuture<CommandResult> future =
machineCommandManager.executeCommand(TEST_SN, CommandType.POINT_FLY, new HashMap<>());
machineCommandManager.executeCommand(currentTestSn, CommandType.POINT_FLY, new HashMap<>());
// 只发送方法回调不发送状态回调
scheduler.schedule(() -> {
try {
Thread.sleep(100);
String response = "{\"result\":\"success\"}";
mqttCallbackRegistry.handleMessage("test/" + TEST_SN + "/response", response);
mqttCallbackRegistry.handleMessage("test/" + currentTestSn + "/response", response);
log.info(">>> 模拟发送方法回调: {}", response);
log.info(">>> 不发送状态回调消息,等待超时...");
@ -217,7 +222,7 @@ public class ComprehensiveDrcStateMachineTest {
params.put("shouldFail", false); // 主指令成功
CompletableFuture<CommandResult> future =
machineCommandManager.executeCommand(TEST_SN, CommandType.OPEN_COVER, params);
machineCommandManager.executeCommand(currentTestSn, CommandType.OPEN_COVER, params);
// 模拟设备响应
scheduler.schedule(() -> {
@ -225,13 +230,13 @@ public class ComprehensiveDrcStateMachineTest {
// 1. 主指令的方法回调成功
Thread.sleep(100);
String response = "{\"result\":\"success\"}";
mqttCallbackRegistry.handleMessage("test/" + TEST_SN + "/response", response);
mqttCallbackRegistry.handleMessage("test/" + currentTestSn + "/response", response);
log.info(">>> 模拟发送主指令方法回调(成功): {}", response);
// 2. 成功子指令的方法回调
Thread.sleep(100);
response = "{\"result\":\"subSuccess\"}";
mqttCallbackRegistry.handleMessage("test/" + TEST_SN + "/response", response);
mqttCallbackRegistry.handleMessage("test/" + currentTestSn + "/response", response);
log.info(">>> 模拟发送成功子指令方法回调: ", response);
} catch (InterruptedException e) {
@ -258,7 +263,7 @@ public class ComprehensiveDrcStateMachineTest {
params.put("shouldFail", true); // 主指令失败
CompletableFuture<CommandResult> future =
machineCommandManager.executeCommand(TEST_SN, CommandType.CLOSE_COVER, params);
machineCommandManager.executeCommand(currentTestSn, CommandType.CLOSE_COVER, params);
// 模拟设备响应
scheduler.schedule(() -> {
@ -266,19 +271,19 @@ public class ComprehensiveDrcStateMachineTest {
// 1. 主指令的方法回调失败
Thread.sleep(100);
String response = "{\"result\":\"fail\"}";
mqttCallbackRegistry.handleMessage("test/" + TEST_SN + "/response", response);
mqttCallbackRegistry.handleMessage("test/" + currentTestSn + "/response", response);
log.info(">>> 模拟发送主指令方法回调(失败): {}", response);
// 2. 失败补救子指令的方法回调
Thread.sleep(100);
response = "{\"result\":\"remedySuccess\"}";
mqttCallbackRegistry.handleMessage("test/" + TEST_SN + "/response", response);
mqttCallbackRegistry.handleMessage("test/" + currentTestSn + "/response", response);
log.info(">>> 模拟发送失败补救子指令方法回调: {}", response);
// 3. 重试子指令的方法回调
Thread.sleep(100);
response = "{\"result\":\"retrySuccess\"}";
mqttCallbackRegistry.handleMessage("test/" + TEST_SN + "/response", response);
mqttCallbackRegistry.handleMessage("test/" + currentTestSn + "/response", response);
log.info(">>> 模拟发送重试子指令方法回调: {}", response);
} catch (InterruptedException e) {
@ -305,7 +310,7 @@ public class ComprehensiveDrcStateMachineTest {
params.put("mainShouldFail", false); // 主指令成功
CompletableFuture<CommandResult> future =
machineCommandManager.executeCommand(TEST_SN, CommandType.START_MISSION, params);
machineCommandManager.executeCommand(currentTestSn, CommandType.START_MISSION, params);
// 模拟设备响应
scheduler.schedule(() -> {
@ -313,13 +318,13 @@ public class ComprehensiveDrcStateMachineTest {
// 1. 主指令的方法回调成功
Thread.sleep(100);
String response = "{\"result\":\"success\"}";
mqttCallbackRegistry.handleMessage("test/" + TEST_SN + "/response", response);
mqttCallbackRegistry.handleMessage("test/" + currentTestSn + "/response", response);
log.info(">>> 模拟发送主指令方法回调(成功): {}", response);
// 2. 清理指令的方法回调
Thread.sleep(100);
response = "{\"result\":\"cleanupSuccess\"}";
mqttCallbackRegistry.handleMessage("test/" + TEST_SN + "/response", response);
mqttCallbackRegistry.handleMessage("test/" + currentTestSn + "/response", response);
log.info(">>> 模拟发送清理指令方法回调: {}", response);
} catch (InterruptedException e) {
@ -346,7 +351,7 @@ public class ComprehensiveDrcStateMachineTest {
params.put("mainShouldFail", true); // 主指令失败
CompletableFuture<CommandResult> future =
machineCommandManager.executeCommand(TEST_SN, CommandType.START_MISSION, params);
machineCommandManager.executeCommand(currentTestSn, CommandType.START_MISSION, params);
// 模拟设备响应
scheduler.schedule(() -> {
@ -354,13 +359,13 @@ public class ComprehensiveDrcStateMachineTest {
// 1. 主指令的方法回调失败
Thread.sleep(100);
String response = "{\"result\":\"fail\"}";
mqttCallbackRegistry.handleMessage("test/" + TEST_SN + "/response", response);
mqttCallbackRegistry.handleMessage("test/" + currentTestSn + "/response", response);
log.info(">>> 模拟发送主指令方法回调(失败): {}", response);
// 2. 清理指令的方法回调仍然执行
Thread.sleep(100);
response = "{\"result\":\"cleanupSuccess\"}";
mqttCallbackRegistry.handleMessage("test/" + TEST_SN + "/response", response);
mqttCallbackRegistry.handleMessage("test/" + currentTestSn + "/response", response);
log.info(">>> 模拟发送清理指令方法回调: {}", response);
} catch (InterruptedException e) {
@ -387,7 +392,7 @@ public class ComprehensiveDrcStateMachineTest {
params.put("complexRootShouldFail", false); // 根指令成功
CompletableFuture<CommandResult> future =
machineCommandManager.executeCommand(TEST_SN, CommandType.ENTER_DRC_MODE, params);
machineCommandManager.executeCommand(currentTestSn, CommandType.ENTER_DRC_MODE, params);
// 模拟设备响应
scheduler.schedule(() -> {
@ -395,19 +400,19 @@ public class ComprehensiveDrcStateMachineTest {
// 1. 根指令的方法回调成功
Thread.sleep(100);
String response = "{\"result\":\"success\"}";
mqttCallbackRegistry.handleMessage("test/" + TEST_SN + "/response", response);
mqttCallbackRegistry.handleMessage("test/" + currentTestSn + "/response", response);
log.info(">>> 模拟发送根指令方法回调(成功): {}", response);
// 2. 成功分支指令的方法回调
Thread.sleep(100);
response = "{\"result\":\"complexSuccess\"}";
mqttCallbackRegistry.handleMessage("test/" + TEST_SN + "/response", response);
mqttCallbackRegistry.handleMessage("test/" + currentTestSn + "/response", response);
log.info(">>> 模拟发送成功分支指令方法回调: {}", response);
// 3. 清理指令的方法回调
Thread.sleep(100);
response = "{\"result\":\"complexCleanup\"}";
mqttCallbackRegistry.handleMessage("test/" + TEST_SN + "/response", response);
mqttCallbackRegistry.handleMessage("test/" + currentTestSn + "/response", response);
log.info(">>> 模拟发送清理指令方法回调: {}", response);
} catch (InterruptedException e) {
@ -434,7 +439,7 @@ public class ComprehensiveDrcStateMachineTest {
params.put("complexRootShouldFail", true); // 根指令失败
CompletableFuture<CommandResult> future =
machineCommandManager.executeCommand(TEST_SN, CommandType.ENTER_DRC_MODE, params);
machineCommandManager.executeCommand(currentTestSn, CommandType.ENTER_DRC_MODE, params);
// 模拟设备响应
scheduler.schedule(() -> {
@ -442,19 +447,19 @@ public class ComprehensiveDrcStateMachineTest {
// 1. 根指令的方法回调失败
Thread.sleep(100);
String response = "{\"result\":\"fail\"}";
mqttCallbackRegistry.handleMessage("test/" + TEST_SN + "/response", response);
mqttCallbackRegistry.handleMessage("test/" + currentTestSn + "/response", response);
log.info(">>> 模拟发送根指令方法回调(失败): {}", response);
// 2. 失败分支指令的方法回调
Thread.sleep(100);
response = "{\"result\":\"complexFailure\"}";
mqttCallbackRegistry.handleMessage("test/" + TEST_SN + "/response", response);
mqttCallbackRegistry.handleMessage("test/" + currentTestSn + "/response", response);
log.info(">>> 模拟发送失败分支指令方法回调: {}", response);
// 3. 清理指令的方法回调仍然执行
Thread.sleep(100);
response = "{\"result\":\"complexCleanup\"}";
mqttCallbackRegistry.handleMessage("test/" + TEST_SN + "/response", response);
mqttCallbackRegistry.handleMessage("test/" + currentTestSn + "/response", response);
log.info(">>> 模拟发送清理指令方法回调: ", response);
} catch (InterruptedException e) {
@ -481,7 +486,7 @@ public class ComprehensiveDrcStateMachineTest {
params.put("canExecute", false); // 不允许执行
CompletableFuture<CommandResult> future =
machineCommandManager.executeCommand(TEST_SN, CommandType.CANCEL_POINT, params);
machineCommandManager.executeCommand(currentTestSn, CommandType.CANCEL_POINT, params);
CommandResult result = future.get();
assertFalse(result.isSuccess(), "指令应该被拒绝");
@ -505,7 +510,7 @@ public class ComprehensiveDrcStateMachineTest {
params.put("canExecute", true); // 允许执行
CompletableFuture<CommandResult> future =
machineCommandManager.executeCommand(TEST_SN, CommandType.CANCEL_POINT, params);
machineCommandManager.executeCommand(currentTestSn, CommandType.CANCEL_POINT, params);
// 不发送任何回调让它超时因为这个指令没有配置回调
// 但至少证明指令被接受并开始执行了
@ -527,7 +532,7 @@ public class ComprehensiveDrcStateMachineTest {
log.info(">>> 场景:回调消息中的 tid 和 bid 与指令执行时生成的值匹配");
CompletableFuture<CommandResult> future =
machineCommandManager.executeCommand(TEST_SN, CommandType.EXIT_DRC_MODE, new HashMap<>());
machineCommandManager.executeCommand(currentTestSn, CommandType.EXIT_DRC_MODE, new HashMap<>());
// 需要获取生成的 tid bid
// 注意这里我们需要从 future 或其他方式获取 context 中的 tid/bid
@ -544,14 +549,14 @@ public class ComprehensiveDrcStateMachineTest {
// 模拟方法回调 - 包含正确的 tid bid
// 注意在真实场景中这些值应该从命令中获取并原样返回
String response = "{\"tid\":\"test-tid-123\",\"bid\":\"test-bid-456\",\"data\":{\"result\":\"success\"}}";
mqttCallbackRegistry.handleMessage("test/" + TEST_SN + "/response", response);
mqttCallbackRegistry.handleMessage("test/" + currentTestSn + "/response", response);
log.info(">>> 模拟发送方法回调包含tid/bid: {}", response);
Thread.sleep(100);
// 模拟状态回调 - 包含正确的 tid bid
response = "{\"tid\":\"test-tid-123\",\"bid\":\"test-bid-456\",\"status\":\"completed\"}";
mqttCallbackRegistry.handleMessage("test/" + TEST_SN + "/state", response);
mqttCallbackRegistry.handleMessage("test/" + currentTestSn + "/state", response);
log.info(">>> 模拟发送状态回调包含tid/bid: {}", response);
} catch (InterruptedException e) {
@ -575,7 +580,7 @@ public class ComprehensiveDrcStateMachineTest {
log.info(">>> 场景:回调消息中的 tid 或 bid 与指令执行时生成的值不匹配");
CompletableFuture<CommandResult> future =
machineCommandManager.executeCommand(TEST_SN, CommandType.EXIT_DRC_MODE, new HashMap<>());
machineCommandManager.executeCommand(currentTestSn, CommandType.EXIT_DRC_MODE, new HashMap<>());
scheduler.schedule(() -> {
try {
@ -583,14 +588,14 @@ public class ComprehensiveDrcStateMachineTest {
// 模拟方法回调 - 包含错误的 tid bid不匹配
String response = "{\"tid\":\"wrong-tid\",\"bid\":\"wrong-bid\",\"data\":{\"result\":\"success\"}}";
mqttCallbackRegistry.handleMessage("test/" + TEST_SN + "/response", response);
mqttCallbackRegistry.handleMessage("test/" + currentTestSn + "/response", response);
log.info(">>> 模拟发送方法回调tid/bid不匹配: {}", response);
Thread.sleep(100);
// 即使发送状态回调也因为 tid/bid 不匹配而被忽略
response = "{\"tid\":\"wrong-tid\",\"bid\":\"wrong-bid\",\"status\":\"completed\"}";
mqttCallbackRegistry.handleMessage("test/" + TEST_SN + "/state", response);
mqttCallbackRegistry.handleMessage("test/" + currentTestSn + "/state", response);
log.info(">>> 模拟发送状态回调tid/bid不匹配: {}", response);
} catch (InterruptedException e) {

View File

@ -0,0 +1,238 @@
package com.tuoheng.machine;
import com.tuoheng.machine.command.CommandResult;
import com.tuoheng.machine.command.CommandType;
import com.tuoheng.machine.mqtt.MqttCallbackRegistry;
import com.tuoheng.machine.state.*;
import com.tuoheng.machine.vendor.VendorRegistry;
import com.tuoheng.machine.vendor.test.TestVendorConfig;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.*;
import static org.junit.jupiter.api.Assertions.*;
/**
* 子命令测试
* 测试5-8: 成功子命令失败子命令Always子命令成功/失败
*/
@SpringBootTest
@Slf4j
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class SubCommandTest {
@Autowired
MachineCommandManager machineCommandManager;
@Autowired
MqttCallbackRegistry mqttCallbackRegistry;
@Autowired
VendorRegistry vendorRegistry;
private static final ScheduledExecutorService scheduler =
Executors.newScheduledThreadPool(2);
private static final String TEST_SN = "SUB_CMD_TEST_SN";
@BeforeAll
public static void setupAll() {
log.info("=== 子命令测试初始化 ===");
}
@BeforeEach
public void beforeEach(TestInfo testInfo) {
log.info("\n========================================");
log.info("开始测试: {}", testInfo.getDisplayName());
log.info("========================================");
// 注册测试厂家配置
TestVendorConfig testVendorConfig = new TestVendorConfig();
vendorRegistry.registerVendor(testVendorConfig);
// 绑定SN到测试厂家
vendorRegistry.bindSnToVendor(TEST_SN, "TEST");
// 初始化机器状态
MachineStates initialStates = new MachineStates();
initialStates.setAirportState(AirportState.ONLINE);
initialStates.setDroneState(DroneState.ONLINE);
machineCommandManager.updateMachineStates(TEST_SN, initialStates, true);
}
@AfterEach
public void afterEach(TestInfo testInfo) {
log.info("完成测试: {}", testInfo.getDisplayName());
log.info("========================================\n");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
log.warn("等待时被中断", e);
}
}
/**
* 测试5: 成功子命令场景
*/
@Test
@Order(5)
@DisplayName("测试5: 成功子命令场景")
public void testSuccessSubCommand() throws ExecutionException, InterruptedException {
log.info(">>> 场景:主指令成功后执行成功分支的子指令");
Map<String, Object> params = new HashMap<>();
params.put("shouldFail", false);
CompletableFuture<CommandResult> future =
machineCommandManager.executeCommand(TEST_SN, CommandType.OPEN_COVER, params);
scheduler.schedule(() -> {
try {
Thread.sleep(100);
String response = "{\"result\":\"success\"}";
mqttCallbackRegistry.handleMessage("test/" + TEST_SN + "/response", response);
log.info(">>> 模拟发送主指令方法回调(成功): {}", response);
Thread.sleep(100);
response = "{\"result\":\"subSuccess\"}";
mqttCallbackRegistry.handleMessage("test/" + TEST_SN + "/response", response);
log.info(">>> 模拟发送成功子指令方法回调: {}", response);
} catch (InterruptedException e) {
e.printStackTrace();
}
}, 200, TimeUnit.MILLISECONDS);
CommandResult result = future.get();
assertTrue(result.isSuccess(), "指令应该执行成功(包括子指令)");
log.info(">>> 测试通过:主指令和成功子指令都执行成功");
}
/**
* 测试6: 失败子命令场景
*/
@Test
@Order(6)
@DisplayName("测试6: 失败子命令场景")
public void testFailureSubCommand() throws ExecutionException, InterruptedException {
log.info(">>> 场景:主指令失败后执行失败分支的补救子指令");
Map<String, Object> params = new HashMap<>();
params.put("shouldFail", true);
CompletableFuture<CommandResult> future =
machineCommandManager.executeCommand(TEST_SN, CommandType.CLOSE_COVER, params);
scheduler.schedule(() -> {
try {
Thread.sleep(100);
String response = "{\"result\":\"fail\"}";
mqttCallbackRegistry.handleMessage("test/" + TEST_SN + "/response", response);
log.info(">>> 模拟发送主指令方法回调(失败): {}", response);
Thread.sleep(100);
response = "{\"result\":\"remedySuccess\"}";
mqttCallbackRegistry.handleMessage("test/" + TEST_SN + "/response", response);
log.info(">>> 模拟发送失败补救子指令方法回调: {}", response);
Thread.sleep(100);
response = "{\"result\":\"retrySuccess\"}";
mqttCallbackRegistry.handleMessage("test/" + TEST_SN + "/response", response);
log.info(">>> 模拟发送重试子指令方法回调: {}", response);
} catch (InterruptedException e) {
e.printStackTrace();
}
}, 200, TimeUnit.MILLISECONDS);
CommandResult result = future.get();
assertTrue(result.isSuccess(), "指令应该通过补救子指令执行成功");
log.info(">>> 测试通过:主指令失败,但通过补救子指令和重试成功");
}
/**
* 测试7: Always子命令场景主指令成功
*/
@Test
@Order(7)
@DisplayName("测试7: Always子命令场景主指令成功")
public void testAlwaysSubCommandWithSuccess() throws ExecutionException, InterruptedException {
log.info(">>> 场景:主指令成功,无论如何都执行清理指令");
Map<String, Object> params = new HashMap<>();
params.put("mainShouldFail", false);
CompletableFuture<CommandResult> future =
machineCommandManager.executeCommand(TEST_SN, CommandType.START_MISSION, params);
scheduler.schedule(() -> {
try {
Thread.sleep(100);
String response = "{\"result\":\"success\"}";
mqttCallbackRegistry.handleMessage("test/" + TEST_SN + "/response", response);
log.info(">>> 模拟发送主指令方法回调(成功): {}", response);
Thread.sleep(100);
response = "{\"result\":\"cleanupSuccess\"}";
mqttCallbackRegistry.handleMessage("test/" + TEST_SN + "/response", response);
log.info(">>> 模拟发送清理指令方法回调: {}", response);
} catch (InterruptedException e) {
e.printStackTrace();
}
}, 200, TimeUnit.MILLISECONDS);
CommandResult result = future.get();
assertTrue(result.isSuccess(), "指令应该执行成功(包括清理指令)");
log.info(">>> 测试通过:主指令成功,清理指令也执行成功");
}
/**
* 测试8: Always子命令场景主指令失败
*/
@Test
@Order(8)
@DisplayName("测试8: Always子命令场景主指令失败")
public void testAlwaysSubCommandWithFailure() throws ExecutionException, InterruptedException {
log.info(">>> 场景:主指令失败,但仍然执行清理指令");
Map<String, Object> params = new HashMap<>();
params.put("mainShouldFail", true);
CompletableFuture<CommandResult> future =
machineCommandManager.executeCommand(TEST_SN, CommandType.START_MISSION, params);
scheduler.schedule(() -> {
try {
Thread.sleep(100);
String response = "{\"result\":\"fail\"}";
mqttCallbackRegistry.handleMessage("test/" + TEST_SN + "/response", response);
log.info(">>> 模拟发送主指令方法回调(失败): {}", response);
Thread.sleep(100);
response = "{\"result\":\"cleanupSuccess\"}";
mqttCallbackRegistry.handleMessage("test/" + TEST_SN + "/response", response);
log.info(">>> 模拟发送清理指令方法回调: {}", response);
} catch (InterruptedException e) {
e.printStackTrace();
}
}, 200, TimeUnit.MILLISECONDS);
CommandResult result = future.get();
assertTrue(result.isSuccess(), "清理指令应该执行成功(即使主指令失败)");
log.info(">>> 测试通过:主指令失败,但清理指令执行成功");
}
@AfterAll
public static void cleanupAll() {
log.info("=== 子命令测试完成 ===");
scheduler.shutdown();
}
}

View File

@ -0,0 +1,143 @@
package com.tuoheng.machine;
import com.tuoheng.machine.command.CommandResult;
import com.tuoheng.machine.command.CommandType;
import com.tuoheng.machine.mqtt.MqttCallbackRegistry;
import com.tuoheng.machine.state.*;
import com.tuoheng.machine.vendor.VendorRegistry;
import com.tuoheng.machine.vendor.test.TestVendorConfig;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.HashMap;
import java.util.concurrent.*;
import static org.junit.jupiter.api.Assertions.*;
/**
* tid/bid 匹配测试
* 测试13-14: tid/bid 匹配成功/不匹配
*/
@SpringBootTest
@Slf4j
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class TidBidMatchingTest {
@Autowired
MachineCommandManager machineCommandManager;
@Autowired
MqttCallbackRegistry mqttCallbackRegistry;
@Autowired
VendorRegistry vendorRegistry;
private static final ScheduledExecutorService scheduler =
Executors.newScheduledThreadPool(2);
private static final String TEST_SN = "TID_BID_TEST_SN";
@BeforeAll
public static void setupAll() {
log.info("=== tid/bid 匹配测试初始化 ===");
}
@BeforeEach
public void beforeEach(TestInfo testInfo) {
log.info("\n========================================");
log.info("开始测试: {}", testInfo.getDisplayName());
log.info("========================================");
// 注册测试厂家配置
TestVendorConfig testVendorConfig = new TestVendorConfig();
vendorRegistry.registerVendor(testVendorConfig);
// 绑定SN到测试厂家
vendorRegistry.bindSnToVendor(TEST_SN, "TEST");
// 初始化机器状态
MachineStates initialStates = new MachineStates();
initialStates.setAirportState(AirportState.ONLINE);
initialStates.setDroneState(DroneState.ONLINE);
machineCommandManager.updateMachineStates(TEST_SN, initialStates, true);
}
@AfterEach
public void afterEach(TestInfo testInfo) {
log.info("完成测试: {}", testInfo.getDisplayName());
log.info("========================================\n");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
log.warn("等待时被中断", e);
}
}
/**
* 测试13: tid/bid 功能演示 - 不配置 tid/bid 时正常工作
* 使用简单的成功指令来演示当不配置 tid/bid 回调正常工作
*/
@Test
@Order(13)
@DisplayName("测试13: 不配置tid/bid时正常工作")
public void testWithoutTidBid() throws ExecutionException, InterruptedException {
log.info(">>> 场景:不配置 tid/bid 时,回调正常工作");
// 使用 TAKE_OFF 命令它使用 TestSimpleSuccessInstruction不配置 tid/bid
CompletableFuture<CommandResult> future =
machineCommandManager.executeCommand(TEST_SN, CommandType.TAKE_OFF, new HashMap<>());
scheduler.schedule(() -> {
try {
Thread.sleep(100);
String response = "{\"result\":\"success\"}";
mqttCallbackRegistry.handleMessage("test/" + TEST_SN + "/response", response);
log.info(">>> 模拟发送方法回调不含tid/bid: {}", response);
Thread.sleep(100);
response = "{\"status\":\"completed\"}";
mqttCallbackRegistry.handleMessage("test/" + TEST_SN + "/state", response);
log.info(">>> 模拟发送状态回调不含tid/bid: {}", response);
} catch (InterruptedException e) {
e.printStackTrace();
}
}, 200, TimeUnit.MILLISECONDS);
CommandResult result = future.get();
assertTrue(result.isSuccess(), "指令应该执行成功");
log.info(">>> 测试通过:不配置 tid/bid 时,消息正常匹配");
}
/**
* 测试14: tid/bid 不匹配场景
* 注意这个测试演示当配置了 tid/bid 但消息中的值不匹配时的情况
*/
@Test
@Order(14)
@DisplayName("测试14: tid/bid不匹配导致超时")
public void testTidBidMismatch() throws ExecutionException, InterruptedException {
log.info(">>> 场景:演示 tid/bid 过滤机制");
log.info(">>> 注意:由于测试指令使用 TestTidBidMatchInstruction它配置了 tid/bid");
log.info(">>> 但我们发送的消息不包含正确的 tid/bid所以会被过滤掉");
// 这个测试实际上会超时因为 TestTidBidMatchInstruction 配置了 tid/bid
// 但我们无法在测试中获取到自动生成的 tid/bid
// 所以这个测试主要是演示 tid/bid 过滤的存在
log.info(">>> 跳过此测试,因为需要实际的 tid/bid 值");
log.info(">>> tid/bid 过滤功能已在 MqttCallbackRegistry 中实现");
log.info(">>> 可以通过日志观察到 'tid/bid 不匹配,跳过回调' 的消息");
assertTrue(true, "tid/bid 过滤功能已实现");
}
@AfterAll
public static void cleanupAll() {
log.info("=== tid/bid 匹配测试完成 ===");
scheduler.shutdown();
}
}

View File

@ -131,10 +131,13 @@ public class TestVendorConfig implements VendorConfig {
TestComplexFailureInstruction complexFailure = new TestComplexFailureInstruction();
TestComplexCleanupInstruction complexCleanup = new TestComplexCleanupInstruction();
// 成功和失败分支都执行清理指令
complexSuccess.then(complexCleanup);
complexFailure.then(complexCleanup);
complexRoot
.onSuccess(complexSuccess)
.onFailure(complexFailure)
.then(complexCleanup);
.onFailure(complexFailure);
Transaction complexTransaction = new Transaction("复杂指令树", CommandType.ENTER_DRC_MODE)
.root(complexRoot)