diff --git a/XplorePlane.Tests/Services/DebugPanelConfigServiceTests.cs b/XplorePlane.Tests/Services/DebugPanelConfigServiceTests.cs
new file mode 100644
index 0000000..616af89
--- /dev/null
+++ b/XplorePlane.Tests/Services/DebugPanelConfigServiceTests.cs
@@ -0,0 +1,338 @@
+using Moq;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Windows;
+using Xunit;
+using XP.Common.Logging.Interfaces;
+using XplorePlane.Models;
+using XplorePlane.Services.Debug;
+
+namespace XplorePlane.Tests.Services
+{
+ ///
+ /// 测试 DebugPanelConfigService(任务 1.2)
+ /// Tests DebugPanelConfigService (Task 1.2)
+ ///
+ /// **Validates: Requirements 12.1-12.7**
+ ///
+ public class DebugPanelConfigServiceTests : IDisposable
+ {
+ private readonly string _tempDir;
+ private readonly string _configPath;
+ private readonly Mock _mockLogger;
+ private readonly Mock _mockModuleLogger;
+ private readonly DebugPanelConfigService _service;
+
+ public DebugPanelConfigServiceTests()
+ {
+ // 创建临时测试目录
+ _tempDir = Path.Combine(Path.GetTempPath(), "XplorePlaneTests", Guid.NewGuid().ToString());
+ Directory.CreateDirectory(_tempDir);
+ _configPath = Path.Combine(_tempDir, "DebugPanel.config");
+
+ // 设置 mock logger
+ _mockModuleLogger = new Mock();
+ _mockLogger = new Mock();
+ _mockLogger.Setup(l => l.ForModule())
+ .Returns(_mockModuleLogger.Object);
+
+ // 使用反射创建服务实例并设置配置路径
+ _service = new DebugPanelConfigService(_mockLogger.Object);
+
+ // 使用反射修改私有字段 _configPath
+ var field = typeof(DebugPanelConfigService).GetField("_configPath",
+ System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
+ field?.SetValue(_service, _configPath);
+ }
+
+ public void Dispose()
+ {
+ // 清理临时目录
+ if (Directory.Exists(_tempDir))
+ {
+ Directory.Delete(_tempDir, true);
+ }
+ }
+
+ ///
+ /// 测试:当配置文件不存在时,LoadConfig 应返回默认配置
+ /// **Validates: Requirement 12.7**
+ ///
+ [Fact]
+ public void LoadConfig_WhenFileDoesNotExist_ReturnsDefaultConfig()
+ {
+ // Act
+ var config = _service.LoadConfig();
+
+ // Assert
+ Assert.NotNull(config);
+ Assert.NotNull(config.Window);
+ Assert.Equal(1200, config.Window.Width);
+ Assert.Equal(800, config.Window.Height);
+ Assert.Equal(WindowState.Normal, config.Window.State);
+ Assert.NotNull(config.EventFilters);
+ Assert.Equal(8, config.EventFilters.Count);
+ Assert.True(config.EventFilters["MotionState"]);
+ Assert.True(config.EventFilters["RaySourceState"]);
+ Assert.True(config.EventFilters["DetectorState"]);
+ Assert.True(config.EventFilters["SystemState"]);
+ Assert.True(config.EventFilters["CameraState"]);
+ Assert.True(config.EventFilters["LinkedViewState"]);
+ Assert.True(config.EventFilters["RecipeExecutionState"]);
+ Assert.True(config.EventFilters["CalibrationMatrix"]);
+ }
+
+ ///
+ /// 测试:SaveConfig 应成功保存配置到 JSON 文件
+ /// **Validates: Requirements 12.1-12.5**
+ ///
+ [Fact]
+ public void SaveConfig_WithValidConfig_SavesSuccessfully()
+ {
+ // Arrange
+ var config = new DebugPanelConfig
+ {
+ Window = new WindowConfig
+ {
+ Left = 150,
+ Top = 200,
+ Width = 1400,
+ Height = 900,
+ State = WindowState.Maximized
+ },
+ EventFilters = new Dictionary
+ {
+ ["MotionState"] = false,
+ ["RaySourceState"] = true
+ },
+ DockingLayout = "Test"
+ };
+
+ // Act
+ _service.SaveConfig(config);
+
+ // Assert
+ Assert.True(File.Exists(_configPath));
+ var json = File.ReadAllText(_configPath);
+ Assert.Contains("\"Width\": 1400", json);
+ Assert.Contains("\"Height\": 900", json);
+ Assert.Contains("\"State\": 2", json); // WindowState.Maximized = 2
+ Assert.Contains("\"MotionState\": false", json);
+ Assert.Contains("\"RaySourceState\": true", json);
+ Assert.Contains("\"DockingLayout\": \"Test\"", json);
+ }
+
+ ///
+ /// 测试:LoadConfig 应成功从 JSON 文件加载配置
+ /// **Validates: Requirement 12.6**
+ ///
+ [Fact]
+ public void LoadConfig_WithExistingFile_LoadsSuccessfully()
+ {
+ // Arrange
+ var originalConfig = new DebugPanelConfig
+ {
+ Window = new WindowConfig
+ {
+ Left = 250,
+ Top = 300,
+ Width = 1600,
+ Height = 1000,
+ State = WindowState.Normal
+ },
+ EventFilters = new Dictionary
+ {
+ ["MotionState"] = true,
+ ["RaySourceState"] = false,
+ ["DetectorState"] = true
+ },
+ DockingLayout = "Custom"
+ };
+ _service.SaveConfig(originalConfig);
+
+ // Act
+ var loadedConfig = _service.LoadConfig();
+
+ // Assert
+ Assert.NotNull(loadedConfig);
+ Assert.NotNull(loadedConfig.Window);
+ Assert.Equal(250, loadedConfig.Window.Left);
+ Assert.Equal(300, loadedConfig.Window.Top);
+ Assert.Equal(1600, loadedConfig.Window.Width);
+ Assert.Equal(1000, loadedConfig.Window.Height);
+ Assert.Equal(WindowState.Normal, loadedConfig.Window.State);
+ Assert.NotNull(loadedConfig.EventFilters);
+ Assert.True(loadedConfig.EventFilters["MotionState"]);
+ Assert.False(loadedConfig.EventFilters["RaySourceState"]);
+ Assert.True(loadedConfig.EventFilters["DetectorState"]);
+ Assert.Equal("Custom", loadedConfig.DockingLayout);
+ }
+
+ ///
+ /// 测试:SaveConfig 应创建不存在的目录
+ /// **Validates: Requirement 12.4**
+ ///
+ [Fact]
+ public void SaveConfig_WhenDirectoryDoesNotExist_CreatesDirectory()
+ {
+ // Arrange
+ var nestedPath = Path.Combine(_tempDir, "nested", "path", "DebugPanel.config");
+ var field = typeof(DebugPanelConfigService).GetField("_configPath",
+ System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
+ field?.SetValue(_service, nestedPath);
+
+ var config = new DebugPanelConfig
+ {
+ Window = new WindowConfig { Width = 1200, Height = 800, State = WindowState.Normal },
+ EventFilters = new Dictionary()
+ };
+
+ // Act
+ _service.SaveConfig(config);
+
+ // Assert
+ Assert.True(File.Exists(nestedPath));
+ Assert.True(Directory.Exists(Path.GetDirectoryName(nestedPath)));
+ }
+
+ ///
+ /// 测试:SaveConfig 使用 null 配置应记录警告并不抛出异常
+ /// **Validates: Requirement 12.7**
+ ///
+ [Fact]
+ public void SaveConfig_WithNullConfig_LogsWarningAndDoesNotThrow()
+ {
+ // Act & Assert
+ var exception = Record.Exception(() => _service.SaveConfig(null));
+ Assert.Null(exception);
+
+ // 验证记录了警告日志
+ _mockModuleLogger.Verify(
+ l => l.Warn(It.IsAny(), It.IsAny