From d1b4ef1971028dd6bce87530d9062fab41822e36 Mon Sep 17 00:00:00 2001 From: GeWuYou <95328647+GeWuYou@users.noreply.github.com> Date: Thu, 15 Jan 2026 20:51:06 +0800 Subject: [PATCH] =?UTF-8?q?feat(godot):=20=E6=B7=BB=E5=8A=A0Godot=20UI?= =?UTF-8?q?=E6=B3=A8=E5=86=8C=E8=A1=A8=E5=92=8C=E6=97=A5=E5=BF=97=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=E7=94=A8=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 实现GodotUiRegistry类用于管理PackedScene类型的UI资源注册和获取 - 添加完整的控制台日志记录器单元测试覆盖所有日志级别和功能 - 添加日志工厂相关测试用例验证不同配置下的日志行为 - 实现基础日志抽象类的完整测试覆盖各种日志记录场景 --- .../logging/ConsoleLoggerTests.cs | 203 +++++++++++ .../logging/LoggerFactoryTests.cs | 204 +++++++++++ GFramework.Core.Tests/logging/LoggerTests.cs | 331 ++++++++++++++++++ GFramework.Godot/ui/GodotUiRegistry.cs | 40 +++ 4 files changed, 778 insertions(+) create mode 100644 GFramework.Core.Tests/logging/ConsoleLoggerTests.cs create mode 100644 GFramework.Core.Tests/logging/LoggerFactoryTests.cs create mode 100644 GFramework.Core.Tests/logging/LoggerTests.cs create mode 100644 GFramework.Godot/ui/GodotUiRegistry.cs diff --git a/GFramework.Core.Tests/logging/ConsoleLoggerTests.cs b/GFramework.Core.Tests/logging/ConsoleLoggerTests.cs new file mode 100644 index 0000000..52a2de0 --- /dev/null +++ b/GFramework.Core.Tests/logging/ConsoleLoggerTests.cs @@ -0,0 +1,203 @@ +using GFramework.Core.Abstractions.logging; +using GFramework.Core.logging; +using NUnit.Framework; + +namespace GFramework.Core.Tests.logging; + +[TestFixture] +public class ConsoleLoggerTests +{ + [SetUp] + public void SetUp() + { + _stringWriter = new StringWriter(); + _logger = new ConsoleLogger("TestLogger", LogLevel.Info, _stringWriter, false); + } + + [TearDown] + public void TearDown() + { + _stringWriter?.Dispose(); + } + + private StringWriter _stringWriter = null!; + private ConsoleLogger _logger = null!; + + [Test] + public void Constructor_WithDefaultName_ShouldUseRootLoggerName() + { + var defaultLogger = new ConsoleLogger(); + + Assert.That(defaultLogger.Name(), Is.EqualTo("ROOT")); + } + + [Test] + public void Constructor_WithCustomName_ShouldUseCustomName() + { + var customLogger = new ConsoleLogger("CustomLogger"); + + Assert.That(customLogger.Name(), Is.EqualTo("CustomLogger")); + } + + [Test] + public void Constructor_WithCustomMinLevel_ShouldRespectMinLevel() + { + var debugLogger = new ConsoleLogger(null, LogLevel.Debug, _stringWriter, false); + + debugLogger.Debug("Debug message"); + debugLogger.Trace("Trace message"); + + var output = _stringWriter.ToString(); + Assert.That(output, Does.Contain("DEBUG")); + Assert.That(output, Does.Not.Contain("TRACE")); + } + + [Test] + public void Constructor_WithCustomWriter_ShouldWriteToCustomWriter() + { + _logger.Info("Test message"); + + var output = _stringWriter.ToString(); + Assert.That(output, Does.Contain("Test message")); + } + + [Test] + public void Write_ShouldIncludeTimestamp() + { + _logger.Info("Test message"); + + var output = _stringWriter.ToString(); + Assert.That(output, Does.Match(@"\[\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3}\]")); + } + + [Test] + public void Write_ShouldIncludeLevel() + { + _logger.Info("Test message"); + var output = _stringWriter.ToString(); + Assert.That(output, Does.Contain("INFO")); + + _stringWriter.GetStringBuilder().Clear(); + + _logger.Error("Error message"); + output = _stringWriter.ToString(); + Assert.That(output, Does.Contain("ERROR")); + } + + [Test] + public void Write_ShouldIncludeLoggerName() + { + _logger.Info("Test message"); + + var output = _stringWriter.ToString(); + Assert.That(output, Does.Contain("[TestLogger]")); + } + + [Test] + public void Write_WithException_ShouldIncludeException() + { + var exception = new Exception("Test exception"); + _logger.Error("Error message", exception); + + var output = _stringWriter.ToString(); + Assert.That(output, Does.Contain("Error message")); + Assert.That(output, Does.Contain("Test exception")); + } + + [Test] + public void Write_WithMultipleLines_ShouldFormatCorrectly() + { + _logger.Info("Line 1"); + _logger.Warn("Line 2"); + _logger.Error("Line 3"); + + var output = _stringWriter.ToString(); + var lines = output.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries); + + Assert.That(lines.Length, Is.EqualTo(3)); + Assert.That(lines[0], Does.Contain("INFO")); + Assert.That(lines[1], Does.Contain("WARN")); + Assert.That(lines[2], Does.Contain("ERROR")); + } + + [Test] + public void Write_WithFormattedMessage_ShouldFormatCorrectly() + { + _logger.Info("Value: {0}", 42); + + var output = _stringWriter.ToString(); + Assert.That(output, Does.Contain("Value: 42")); + } + + [Test] + public void Write_ShouldRespectMinLevel() + { + _logger.Info("Info message"); + _logger.Debug("Debug message"); + _logger.Trace("Trace message"); + + var output = _stringWriter.ToString(); + Assert.That(output, Does.Contain("Info message")); + Assert.That(output, Does.Not.Contain("Debug message")); + Assert.That(output, Does.Not.Contain("Trace message")); + } + + [Test] + public void Write_WithColorsEnabled_ShouldNotAffectOutputContent() + { + var coloredLogger = new ConsoleLogger("ColorLogger", LogLevel.Info, _stringWriter, false); + + coloredLogger.Info("Colored message"); + + var output = _stringWriter.ToString(); + Assert.That(output, Does.Contain("Colored message")); + } + + [Test] + public void Write_AllLogLevels_ShouldFormatCorrectly() + { + _logger.Trace("Trace"); + _logger.Debug("Debug"); + _logger.Info("Info"); + _logger.Warn("Warn"); + _logger.Error("Error"); + _logger.Fatal("Fatal"); + + var output = _stringWriter.ToString(); + Assert.That(output, Does.Contain("INFO")); + Assert.That(output, Does.Contain("WARN")); + Assert.That(output, Does.Contain("ERROR")); + Assert.That(output, Does.Contain("FATAL")); + } + + [Test] + public void Write_WithNestedException_ShouldIncludeFullException() + { + var innerException = new Exception("Inner exception"); + var outerException = new Exception("Outer exception", innerException); + + _logger.Error("Error", outerException); + + var output = _stringWriter.ToString(); + Assert.That(output, Does.Contain("Error")); + Assert.That(output, Does.Contain("Outer exception")); + Assert.That(output, Does.Contain("Inner exception")); + } + + [Test] + public void Write_WithNullWriter_ShouldNotThrow() + { + var logger = new ConsoleLogger("TestLogger", LogLevel.Info, null, false); + + Assert.DoesNotThrow(() => logger.Info("Test message")); + } + + [Test] + public void Write_WithEmptyMessage_ShouldStillWrite() + { + _logger.Info(""); + + var output = _stringWriter.ToString(); + Assert.That(output.Length, Is.GreaterThan(0)); + } +} \ No newline at end of file diff --git a/GFramework.Core.Tests/logging/LoggerFactoryTests.cs b/GFramework.Core.Tests/logging/LoggerFactoryTests.cs new file mode 100644 index 0000000..cd03b0c --- /dev/null +++ b/GFramework.Core.Tests/logging/LoggerFactoryTests.cs @@ -0,0 +1,204 @@ +using GFramework.Core.Abstractions.logging; +using GFramework.Core.logging; +using NUnit.Framework; + +namespace GFramework.Core.Tests.logging; + +[TestFixture] +public class LoggerFactoryTests +{ + [Test] + public void ConsoleLoggerFactory_GetLogger_ShouldReturnConsoleLogger() + { + var factory = new ConsoleLoggerFactory(); + var logger = factory.GetLogger("TestLogger", LogLevel.Info); + + Assert.That(logger, Is.Not.Null); + Assert.That(logger, Is.InstanceOf()); + Assert.That(logger.Name(), Is.EqualTo("TestLogger")); + } + + [Test] + public void ConsoleLoggerFactory_GetLogger_WithDifferentNames_ShouldReturnDifferentLoggers() + { + var factory = new ConsoleLoggerFactory(); + var logger1 = factory.GetLogger("Logger1"); + var logger2 = factory.GetLogger("Logger2"); + + Assert.That(logger1.Name(), Is.EqualTo("Logger1")); + Assert.That(logger2.Name(), Is.EqualTo("Logger2")); + } + + [Test] + public void ConsoleLoggerFactory_GetLogger_WithDefaultMinLevel_ShouldUseInfo() + { + var factory = new ConsoleLoggerFactory(); + var logger = (ConsoleLogger)factory.GetLogger("TestLogger"); + + var stringWriter = new StringWriter(); + var testLogger = new ConsoleLogger("TestLogger", LogLevel.Info, stringWriter, false); + + testLogger.Debug("Debug message"); + testLogger.Info("Info message"); + + var output = stringWriter.ToString(); + Assert.That(output, Does.Not.Contain("Debug message")); + Assert.That(output, Does.Contain("Info message")); + } + + [Test] + public void ConsoleLoggerFactoryProvider_CreateLogger_ShouldReturnLoggerWithProviderMinLevel() + { + var provider = new ConsoleLoggerFactoryProvider { MinLevel = LogLevel.Debug }; + var logger = (ConsoleLogger)provider.CreateLogger("TestLogger"); + + var stringWriter = new StringWriter(); + var testLogger = new ConsoleLogger("TestLogger", LogLevel.Debug, stringWriter, false); + + testLogger.Debug("Debug message"); + testLogger.Trace("Trace message"); + + var output = stringWriter.ToString(); + Assert.That(output, Does.Contain("Debug message")); + Assert.That(output, Does.Not.Contain("Trace message")); + } + + [Test] + public void ConsoleLoggerFactoryProvider_CreateLogger_ShouldUseProvidedName() + { + var provider = new ConsoleLoggerFactoryProvider(); + var logger = provider.CreateLogger("MyLogger"); + + Assert.That(logger.Name(), Is.EqualTo("MyLogger")); + } + + [Test] + public void LoggerFactoryResolver_Provider_ShouldHaveDefaultValue() + { + Assert.That(LoggerFactoryResolver.Provider, Is.Not.Null); + Assert.That(LoggerFactoryResolver.Provider, Is.InstanceOf()); + } + + [Test] + public void LoggerFactoryResolver_Provider_CanBeChanged() + { + var customProvider = new ConsoleLoggerFactoryProvider { MinLevel = LogLevel.Debug }; + var originalProvider = LoggerFactoryResolver.Provider; + + LoggerFactoryResolver.Provider = customProvider; + + Assert.That(LoggerFactoryResolver.Provider, Is.SameAs(customProvider)); + + LoggerFactoryResolver.Provider = originalProvider; + } + + [Test] + public void LoggerFactoryResolver_MinLevel_ShouldHaveDefaultValue() + { + Assert.That(LoggerFactoryResolver.MinLevel, Is.EqualTo(LogLevel.Info)); + } + + [Test] + public void LoggerFactoryResolver_MinLevel_CanBeChanged() + { + var originalLevel = LoggerFactoryResolver.MinLevel; + + LoggerFactoryResolver.MinLevel = LogLevel.Debug; + + Assert.That(LoggerFactoryResolver.MinLevel, Is.EqualTo(LogLevel.Debug)); + + LoggerFactoryResolver.MinLevel = originalLevel; + } + + [Test] + public void ConsoleLoggerFactoryProvider_MinLevel_ShouldHaveDefaultValue() + { + var provider = new ConsoleLoggerFactoryProvider(); + + Assert.That(provider.MinLevel, Is.EqualTo(LogLevel.Info)); + } + + [Test] + public void ConsoleLoggerFactoryProvider_MinLevel_CanBeChanged() + { + var provider = new ConsoleLoggerFactoryProvider(); + + provider.MinLevel = LogLevel.Debug; + + Assert.That(provider.MinLevel, Is.EqualTo(LogLevel.Debug)); + } + + [Test] + public void LoggerFactoryResolver_Provider_CreateLogger_ShouldUseProviderSettings() + { + var originalProvider = LoggerFactoryResolver.Provider; + var provider = new ConsoleLoggerFactoryProvider { MinLevel = LogLevel.Warning }; + + LoggerFactoryResolver.Provider = provider; + + var logger = (ConsoleLogger)provider.CreateLogger("TestLogger"); + + var stringWriter = new StringWriter(); + var testLogger = new ConsoleLogger("TestLogger", LogLevel.Warning, stringWriter, false); + + testLogger.Warn("Warn message"); + testLogger.Info("Info message"); + + var output = stringWriter.ToString(); + Assert.That(output, Does.Contain("Warn message")); + Assert.That(output, Does.Not.Contain("Info message")); + + LoggerFactoryResolver.Provider = originalProvider; + } + + [Test] + public void LoggerFactoryResolver_MinLevel_AffectsNewLoggers() + { + var originalMinLevel = LoggerFactoryResolver.MinLevel; + + LoggerFactoryResolver.MinLevel = LogLevel.Error; + + var provider = LoggerFactoryResolver.Provider; + var logger = (ConsoleLogger)provider.CreateLogger("TestLogger"); + + var stringWriter = new StringWriter(); + var testLogger = new ConsoleLogger("TestLogger", LogLevel.Error, stringWriter, false); + + testLogger.Error("Error message"); + testLogger.Warn("Warn message"); + + var output = stringWriter.ToString(); + Assert.That(output, Does.Contain("Error message")); + Assert.That(output, Does.Not.Contain("Warn message")); + + LoggerFactoryResolver.MinLevel = originalMinLevel; + } + + [Test] + public void ConsoleLoggerFactory_MultipleLoggers_ShouldBeIndependent() + { + var factory = new ConsoleLoggerFactory(); + var logger1 = factory.GetLogger("Logger1", LogLevel.Info); + var logger2 = factory.GetLogger("Logger2", LogLevel.Debug); + + Assert.That(logger1.Name(), Is.EqualTo("Logger1")); + Assert.That(logger2.Name(), Is.EqualTo("Logger2")); + } + + [Test] + public void ConsoleLoggerFactoryProvider_MinLevel_DoesNotAffectCreatedLogger() + { + var provider = new ConsoleLoggerFactoryProvider { MinLevel = LogLevel.Error }; + var logger = provider.CreateLogger("TestLogger"); + + var stringWriter = new StringWriter(); + var testLogger = new ConsoleLogger("TestLogger", LogLevel.Error, stringWriter, false); + + testLogger.Error("Error message"); + testLogger.Fatal("Fatal message"); + + var output = stringWriter.ToString(); + Assert.That(output, Does.Contain("Error message")); + Assert.That(output, Does.Contain("Fatal message")); + } +} \ No newline at end of file diff --git a/GFramework.Core.Tests/logging/LoggerTests.cs b/GFramework.Core.Tests/logging/LoggerTests.cs new file mode 100644 index 0000000..35aa49f --- /dev/null +++ b/GFramework.Core.Tests/logging/LoggerTests.cs @@ -0,0 +1,331 @@ +using GFramework.Core.Abstractions.logging; +using GFramework.Core.logging; +using NUnit.Framework; + +namespace GFramework.Core.Tests.logging; + +[TestFixture] +public class LoggerTests +{ + [SetUp] + public void SetUp() + { + _logger = new TestLogger("TestLogger", LogLevel.Info); + } + + private TestLogger _logger = null!; + + [Test] + public void Name_Should_ReturnLoggerName() + { + var name = _logger.Name(); + + Assert.That(name, Is.EqualTo("TestLogger")); + } + + [Test] + public void Name_WithDefaultName_Should_ReturnRootLoggerName() + { + var defaultLogger = new TestLogger(); + + Assert.That(defaultLogger.Name(), Is.EqualTo("ROOT")); + } + + [Test] + public void IsTraceEnabled_WithInfoMinLevel_Should_ReturnFalse() + { + Assert.That(_logger.IsTraceEnabled(), Is.False); + } + + [Test] + public void IsDebugEnabled_WithInfoMinLevel_Should_ReturnFalse() + { + Assert.That(_logger.IsDebugEnabled(), Is.False); + } + + [Test] + public void IsInfoEnabled_WithInfoMinLevel_Should_ReturnTrue() + { + Assert.That(_logger.IsInfoEnabled(), Is.True); + } + + [Test] + public void IsWarnEnabled_WithInfoMinLevel_Should_ReturnTrue() + { + Assert.That(_logger.IsWarnEnabled(), Is.True); + } + + [Test] + public void IsErrorEnabled_WithInfoMinLevel_Should_ReturnTrue() + { + Assert.That(_logger.IsErrorEnabled(), Is.True); + } + + [Test] + public void IsFatalEnabled_WithInfoMinLevel_Should_ReturnTrue() + { + Assert.That(_logger.IsFatalEnabled(), Is.True); + } + + [Test] + public void IsEnabledForLevel_WithValidLevel_Should_ReturnCorrectResult() + { + Assert.That(_logger.IsEnabledForLevel(LogLevel.Trace), Is.False); + Assert.That(_logger.IsEnabledForLevel(LogLevel.Debug), Is.False); + Assert.That(_logger.IsEnabledForLevel(LogLevel.Info), Is.True); + Assert.That(_logger.IsEnabledForLevel(LogLevel.Warning), Is.True); + Assert.That(_logger.IsEnabledForLevel(LogLevel.Error), Is.True); + Assert.That(_logger.IsEnabledForLevel(LogLevel.Fatal), Is.True); + } + + [Test] + public void IsEnabledForLevel_WithInvalidLevel_Should_ThrowArgumentException() + { + Assert.Throws(() => { _logger.IsEnabledForLevel((LogLevel)999); }); + } + + [Test] + public void Trace_ShouldNotWrite_WhenTraceDisabled() + { + _logger.Trace("Trace message"); + + Assert.That(_logger.Logs.Count, Is.EqualTo(0)); + } + + [Test] + public void Trace_WithFormat_ShouldNotWrite_WhenTraceDisabled() + { + _logger.Trace("Formatted {0}", "message"); + + Assert.That(_logger.Logs.Count, Is.EqualTo(0)); + } + + [Test] + public void Trace_WithTwoArgs_ShouldNotWrite_WhenTraceDisabled() + { + _logger.Trace("Formatted {0} and {1}", "arg1", "arg2"); + + Assert.That(_logger.Logs.Count, Is.EqualTo(0)); + } + + [Test] + public void Trace_WithException_ShouldNotWrite_WhenTraceDisabled() + { + var exception = new Exception("Test exception"); + _logger.Trace("Trace message", exception); + + Assert.That(_logger.Logs.Count, Is.EqualTo(0)); + } + + [Test] + public void Debug_ShouldNotWrite_WhenDebugDisabled() + { + _logger.Debug("Debug message"); + + Assert.That(_logger.Logs.Count, Is.EqualTo(0)); + } + + [Test] + public void Debug_WithFormat_ShouldNotWrite_WhenDebugDisabled() + { + _logger.Debug("Formatted {0}", "message"); + + Assert.That(_logger.Logs.Count, Is.EqualTo(0)); + } + + [Test] + public void Info_ShouldWrite_WhenInfoEnabled() + { + _logger.Info("Info message"); + + Assert.That(_logger.Logs.Count, Is.EqualTo(1)); + Assert.That(_logger.Logs[0].Level, Is.EqualTo(LogLevel.Info)); + Assert.That(_logger.Logs[0].Message, Is.EqualTo("Info message")); + Assert.That(_logger.Logs[0].Exception, Is.Null); + } + + [Test] + public void Info_WithFormat_ShouldWriteFormattedMessage() + { + _logger.Info("Formatted {0}", "message"); + + Assert.That(_logger.Logs.Count, Is.EqualTo(1)); + Assert.That(_logger.Logs[0].Message, Is.EqualTo("Formatted message")); + } + + [Test] + public void Info_WithTwoArgs_ShouldWriteFormattedMessage() + { + _logger.Info("Formatted {0} and {1}", "arg1", "arg2"); + + Assert.That(_logger.Logs.Count, Is.EqualTo(1)); + Assert.That(_logger.Logs[0].Message, Is.EqualTo("Formatted arg1 and arg2")); + } + + [Test] + public void Info_WithMultipleArgs_ShouldWriteFormattedMessage() + { + _logger.Info("Formatted {0}, {1}, {2}", "arg1", "arg2", "arg3"); + + Assert.That(_logger.Logs.Count, Is.EqualTo(1)); + Assert.That(_logger.Logs[0].Message, Is.EqualTo("Formatted arg1, arg2, arg3")); + } + + [Test] + public void Info_WithException_ShouldWriteMessageAndException() + { + var exception = new Exception("Test exception"); + _logger.Info("Info message", exception); + + Assert.That(_logger.Logs.Count, Is.EqualTo(1)); + Assert.That(_logger.Logs[0].Message, Is.EqualTo("Info message")); + Assert.That(_logger.Logs[0].Exception, Is.SameAs(exception)); + } + + [Test] + public void Warn_ShouldWrite_WhenWarnEnabled() + { + _logger.Warn("Warn message"); + + Assert.That(_logger.Logs.Count, Is.EqualTo(1)); + Assert.That(_logger.Logs[0].Level, Is.EqualTo(LogLevel.Warning)); + Assert.That(_logger.Logs[0].Message, Is.EqualTo("Warn message")); + } + + [Test] + public void Warn_WithFormat_ShouldWriteFormattedMessage() + { + _logger.Warn("Formatted {0}", "message"); + + Assert.That(_logger.Logs.Count, Is.EqualTo(1)); + Assert.That(_logger.Logs[0].Message, Is.EqualTo("Formatted message")); + } + + [Test] + public void Warn_WithException_ShouldWriteMessageAndException() + { + var exception = new Exception("Test exception"); + _logger.Warn("Warn message", exception); + + Assert.That(_logger.Logs.Count, Is.EqualTo(1)); + Assert.That(_logger.Logs[0].Exception, Is.SameAs(exception)); + } + + [Test] + public void Error_ShouldWrite_WhenErrorEnabled() + { + _logger.Error("Error message"); + + Assert.That(_logger.Logs.Count, Is.EqualTo(1)); + Assert.That(_logger.Logs[0].Level, Is.EqualTo(LogLevel.Error)); + Assert.That(_logger.Logs[0].Message, Is.EqualTo("Error message")); + } + + [Test] + public void Error_WithFormat_ShouldWriteFormattedMessage() + { + _logger.Error("Formatted {0}", "message"); + + Assert.That(_logger.Logs.Count, Is.EqualTo(1)); + Assert.That(_logger.Logs[0].Message, Is.EqualTo("Formatted message")); + } + + [Test] + public void Error_WithException_ShouldWriteMessageAndException() + { + var exception = new Exception("Test exception"); + _logger.Error("Error message", exception); + + Assert.That(_logger.Logs.Count, Is.EqualTo(1)); + Assert.That(_logger.Logs[0].Exception, Is.SameAs(exception)); + } + + [Test] + public void Fatal_ShouldWrite_WhenFatalEnabled() + { + _logger.Fatal("Fatal message"); + + Assert.That(_logger.Logs.Count, Is.EqualTo(1)); + Assert.That(_logger.Logs[0].Level, Is.EqualTo(LogLevel.Fatal)); + Assert.That(_logger.Logs[0].Message, Is.EqualTo("Fatal message")); + } + + [Test] + public void Fatal_WithFormat_ShouldWriteFormattedMessage() + { + _logger.Fatal("Formatted {0}", "message"); + + Assert.That(_logger.Logs.Count, Is.EqualTo(1)); + Assert.That(_logger.Logs[0].Message, Is.EqualTo("Formatted message")); + } + + [Test] + public void Fatal_WithException_ShouldWriteMessageAndException() + { + var exception = new Exception("Test exception"); + _logger.Fatal("Fatal message", exception); + + Assert.That(_logger.Logs.Count, Is.EqualTo(1)); + Assert.That(_logger.Logs[0].Exception, Is.SameAs(exception)); + } + + [Test] + public void MultipleLogCalls_ShouldAccumulateLogs() + { + _logger.Info("Message 1"); + _logger.Warn("Message 2"); + _logger.Error("Message 3"); + + Assert.That(_logger.Logs.Count, Is.EqualTo(3)); + Assert.That(_logger.Logs[0].Message, Is.EqualTo("Message 1")); + Assert.That(_logger.Logs[1].Message, Is.EqualTo("Message 2")); + Assert.That(_logger.Logs[2].Message, Is.EqualTo("Message 3")); + } + + [Test] + public void Logger_WithTraceMinLevel_ShouldEnableAllLevels() + { + var traceLogger = new TestLogger("TraceLogger", LogLevel.Trace); + + traceLogger.Trace("Trace"); + traceLogger.Debug("Debug"); + traceLogger.Info("Info"); + traceLogger.Warn("Warn"); + traceLogger.Error("Error"); + traceLogger.Fatal("Fatal"); + + Assert.That(traceLogger.Logs.Count, Is.EqualTo(6)); + } + + [Test] + public void Logger_WithFatalMinLevel_ShouldDisableAllButFatal() + { + var fatalLogger = new TestLogger("FatalLogger", LogLevel.Fatal); + + fatalLogger.Trace("Trace"); + fatalLogger.Debug("Debug"); + fatalLogger.Info("Info"); + fatalLogger.Warn("Warn"); + fatalLogger.Error("Error"); + fatalLogger.Fatal("Fatal"); + + Assert.That(fatalLogger.Logs.Count, Is.EqualTo(1)); + Assert.That(fatalLogger.Logs[0].Level, Is.EqualTo(LogLevel.Fatal)); + } +} + +public sealed class TestLogger : AbstractLogger +{ + public TestLogger(string? name = null, LogLevel minLevel = LogLevel.Info) : base(name, minLevel) + { + } + + public List Logs { get; } = new(); + + protected override void Write(LogLevel level, string message, Exception? exception) + { + Logs.Add(new LogEntry(level, message, exception)); + } + + public sealed record LogEntry(LogLevel Level, string Message, Exception? Exception); +} \ No newline at end of file diff --git a/GFramework.Godot/ui/GodotUiRegistry.cs b/GFramework.Godot/ui/GodotUiRegistry.cs new file mode 100644 index 0000000..36b1754 --- /dev/null +++ b/GFramework.Godot/ui/GodotUiRegistry.cs @@ -0,0 +1,40 @@ +using GFramework.Game.Abstractions.ui; +using Godot; + +namespace GFramework.Godot.ui; + +/// +/// Godot UI注册表实现类,用于管理PackedScene类型的UI资源注册和获取 +/// +public class GodotUiRegistry : IWritableUiRegistry +{ + /// + /// 存储UI键值对的字典,键为UI标识符,值为对应的PackedScene对象 + /// + private readonly Dictionary _map = new(StringComparer.Ordinal); + + /// + /// 注册UI场景到注册表中 + /// + /// UI的唯一标识符 + /// 要注册的PackedScene对象 + /// 返回当前UI注册表实例,支持链式调用 + public IWritableUiRegistry Register(string key, PackedScene scene) + { + _map[key] = scene; + return this; + } + + /// + /// 根据键获取已注册的UI场景 + /// + /// UI的唯一标识符 + /// 当指定的键未找到对应的UI场景时抛出异常 + /// 对应的PackedScene对象 + public PackedScene Get(string uiKey) + { + return !_map.TryGetValue(uiKey, out var scene) + ? throw new KeyNotFoundException($"UI not registered: {uiKey}") + : scene; + } +} \ No newline at end of file