From 6a52cc6715f02dd17e79aea20d2c26468621cf7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D1=82=D0=B5=D0=BF=D0=B0=D0=BD?= Date: Sun, 18 Aug 2024 00:07:33 +0300 Subject: [PATCH] =?UTF-8?q?#97=20-=20=D0=B2=D0=BD=D0=B5=D0=B4=D1=80=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D0=B5=20=D0=BB=D0=BE=D0=B3=D0=B3=D0=B5=D1=80=D0=B0?= =?UTF-8?q?=20(#108)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * #97 - rename logging -> dumping * #97 - refactoring of writer entity * #97 - реализация вывода с помощью логгера * #97 - реализация форматтера для эквивалентного вывода * #97 - работа с логгером в тестах --- .../IExecuteParams.cs | 2 +- .../IOutputWriter.cs | 8 +++ .../Impl/ExecuteParams.cs | 4 +- .../Impl/VirtualMachine.cs | 2 +- src/HydraScript/ExecuteCommandHandler.cs | 9 ++- src/HydraScript/Program.cs | 6 +- src/HydraScript/SimplestConsoleFormatter.cs | 28 ++++++++ .../DumpingLexer.cs} | 4 +- .../DumpingParser.cs} | 4 +- .../DumpingVirtualMachine.cs} | 4 +- .../HydraScript.Infrastructure.csproj | 1 + .../LoggingWriter.cs | 13 ++++ .../ServiceCollectionExtensions.cs | 10 +-- .../VariableInitializationTests.cs | 66 ++++++++----------- .../HydraScript.IntegrationTests.csproj | 1 + .../TestHostFixture.cs | 27 +++++--- .../Unit/BackEnd/VirtualMachineTests.cs | 6 +- .../Infrastructure/LoggingEntitiesTests.cs | 6 +- 18 files changed, 124 insertions(+), 77 deletions(-) create mode 100644 src/Domain/HydraScript.Domain.BackEnd/IOutputWriter.cs create mode 100644 src/HydraScript/SimplestConsoleFormatter.cs rename src/Infrastructure/HydraScript.Infrastructure/{Logging/LoggingLexer.cs => Dumping/DumpingLexer.cs} (90%) rename src/Infrastructure/HydraScript.Infrastructure/{Logging/LoggingParser.cs => Dumping/DumpingParser.cs} (73%) rename src/Infrastructure/HydraScript.Infrastructure/{Logging/LoggingVirtualMachine.cs => Dumping/DumpingVirtualMachine.cs} (88%) create mode 100644 src/Infrastructure/HydraScript.Infrastructure/LoggingWriter.cs diff --git a/src/Domain/HydraScript.Domain.BackEnd/IExecuteParams.cs b/src/Domain/HydraScript.Domain.BackEnd/IExecuteParams.cs index 347077d..8c58b10 100644 --- a/src/Domain/HydraScript.Domain.BackEnd/IExecuteParams.cs +++ b/src/Domain/HydraScript.Domain.BackEnd/IExecuteParams.cs @@ -5,5 +5,5 @@ public interface IExecuteParams public Stack CallStack { get; } public Stack Frames { get; } public Queue Arguments { get; } - public TextWriter Writer { get; } + public IOutputWriter Writer { get; } } \ No newline at end of file diff --git a/src/Domain/HydraScript.Domain.BackEnd/IOutputWriter.cs b/src/Domain/HydraScript.Domain.BackEnd/IOutputWriter.cs new file mode 100644 index 0000000..98a3caf --- /dev/null +++ b/src/Domain/HydraScript.Domain.BackEnd/IOutputWriter.cs @@ -0,0 +1,8 @@ +namespace HydraScript.Domain.BackEnd; + +public interface IOutputWriter +{ + public void WriteLine(object? obj); + + public void WriteError(Exception e, string message); +} \ No newline at end of file diff --git a/src/Domain/HydraScript.Domain.BackEnd/Impl/ExecuteParams.cs b/src/Domain/HydraScript.Domain.BackEnd/Impl/ExecuteParams.cs index 4af9255..19b4eee 100644 --- a/src/Domain/HydraScript.Domain.BackEnd/Impl/ExecuteParams.cs +++ b/src/Domain/HydraScript.Domain.BackEnd/Impl/ExecuteParams.cs @@ -1,9 +1,9 @@ namespace HydraScript.Domain.BackEnd.Impl; -public class ExecuteParams(TextWriter textWriter) : IExecuteParams +public class ExecuteParams(IOutputWriter textWriter) : IExecuteParams { public Stack CallStack { get; } = new(); public Stack Frames { get; } = new(); public Queue Arguments { get; } = new(); - public TextWriter Writer { get; } = textWriter; + public IOutputWriter Writer { get; } = textWriter; } \ No newline at end of file diff --git a/src/Domain/HydraScript.Domain.BackEnd/Impl/VirtualMachine.cs b/src/Domain/HydraScript.Domain.BackEnd/Impl/VirtualMachine.cs index 67a715f..f06c277 100644 --- a/src/Domain/HydraScript.Domain.BackEnd/Impl/VirtualMachine.cs +++ b/src/Domain/HydraScript.Domain.BackEnd/Impl/VirtualMachine.cs @@ -1,6 +1,6 @@ namespace HydraScript.Domain.BackEnd.Impl; -public class VirtualMachine(TextWriter writer) : IVirtualMachine +public class VirtualMachine(IOutputWriter writer) : IVirtualMachine { public IExecuteParams ExecuteParams { get; } = new ExecuteParams(writer); diff --git a/src/HydraScript/ExecuteCommandHandler.cs b/src/HydraScript/ExecuteCommandHandler.cs index a137f75..d4acda8 100644 --- a/src/HydraScript/ExecuteCommandHandler.cs +++ b/src/HydraScript/ExecuteCommandHandler.cs @@ -12,12 +12,12 @@ internal class ExecuteCommandHandler( ISourceCodeProvider sourceCodeProvider, IParser parser, ICodeGenerator codeGenerator, - IVirtualMachine virtualMachine, - TextWriter writer) : ICommandHandler + IVirtualMachine virtualMachine) : ICommandHandler { public int Invoke(InvocationContext context) { + var writer = virtualMachine.ExecuteParams.Writer; try { var sourceCode = sourceCodeProvider.GetText(); @@ -29,13 +29,12 @@ public int Invoke(InvocationContext context) catch (Exception ex) when (ex is LexerException or ParserException or SemanticException) { - writer.WriteLine(ex.Message); + writer.WriteError(ex, message: "HydraScript Error"); return ExitCodes.HydraScriptError; } catch (Exception ex) { - writer.WriteLine("Internal HydraScript Error"); - writer.WriteLine(ex); + writer.WriteError(ex, message: "Dotnet Runtime Error"); return ExitCodes.DotnetRuntimeError; } } diff --git a/src/HydraScript/Program.cs b/src/HydraScript/Program.cs index 4dd5f65..5db354d 100644 --- a/src/HydraScript/Program.cs +++ b/src/HydraScript/Program.cs @@ -6,6 +6,7 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Console; return GetRunner(ConfigureHost).Invoke(args); @@ -25,7 +26,10 @@ internal static Parser GetRunner(Action configureHost, bool useDef private static void ConfigureHost(IHostBuilder builder) => builder .ConfigureServices((context, services) => { - services.AddLogging(c => c.ClearProviders()); + services.AddLogging(c => c.ClearProviders() + .AddConsole(options => options.FormatterName = nameof(SimplestConsoleFormatter)) + .AddConsoleFormatter()); + services.Configure(options => options.SuppressStatusMessages = true); var parseResult = context.GetInvocationContext().ParseResult; var fileInfo = parseResult.GetValueForArgument(Command.PathArgument); var dump = parseResult.GetValueForOption(Command.DumpOption); diff --git a/src/HydraScript/SimplestConsoleFormatter.cs b/src/HydraScript/SimplestConsoleFormatter.cs new file mode 100644 index 0000000..8b0a20d --- /dev/null +++ b/src/HydraScript/SimplestConsoleFormatter.cs @@ -0,0 +1,28 @@ +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using Microsoft.Extensions.Logging.Console; + +namespace HydraScript; + +internal class SimplestConsoleFormatter() : ConsoleFormatter(nameof(SimplestConsoleFormatter)) +{ + public override void Write( + in LogEntry logEntry, + IExternalScopeProvider scopeProvider, + TextWriter textWriter) + { + if (logEntry.LogLevel is LogLevel.Error) + { + var message = logEntry.Formatter.Invoke(logEntry.State, logEntry.Exception); + textWriter.WriteLine($"{message}: {logEntry.Exception?.Message}"); +#if DEBUG + textWriter.WriteLine($"{logEntry.Exception?.StackTrace}"); +#endif + return; + } + + if (logEntry.LogLevel is not LogLevel.Information) + return; + textWriter.WriteLine(logEntry.State); + } +} \ No newline at end of file diff --git a/src/Infrastructure/HydraScript.Infrastructure/Logging/LoggingLexer.cs b/src/Infrastructure/HydraScript.Infrastructure/Dumping/DumpingLexer.cs similarity index 90% rename from src/Infrastructure/HydraScript.Infrastructure/Logging/LoggingLexer.cs rename to src/Infrastructure/HydraScript.Infrastructure/Dumping/DumpingLexer.cs index ccf98f5..f99952d 100644 --- a/src/Infrastructure/HydraScript.Infrastructure/Logging/LoggingLexer.cs +++ b/src/Infrastructure/HydraScript.Infrastructure/Dumping/DumpingLexer.cs @@ -3,9 +3,9 @@ using HydraScript.Domain.FrontEnd.Lexer; using Microsoft.Extensions.Options; -namespace HydraScript.Infrastructure.Logging; +namespace HydraScript.Infrastructure.Dumping; -internal class LoggingLexer( +internal class DumpingLexer( ILexer lexer, IFileSystem fileSystem, IOptions inputFile) : ILexer diff --git a/src/Infrastructure/HydraScript.Infrastructure/Logging/LoggingParser.cs b/src/Infrastructure/HydraScript.Infrastructure/Dumping/DumpingParser.cs similarity index 73% rename from src/Infrastructure/HydraScript.Infrastructure/Logging/LoggingParser.cs rename to src/Infrastructure/HydraScript.Infrastructure/Dumping/DumpingParser.cs index d6f3c36..e4b870b 100644 --- a/src/Infrastructure/HydraScript.Infrastructure/Logging/LoggingParser.cs +++ b/src/Infrastructure/HydraScript.Infrastructure/Dumping/DumpingParser.cs @@ -1,9 +1,9 @@ using System.IO.Abstractions; using HydraScript.Domain.FrontEnd.Parser; -namespace HydraScript.Infrastructure.Logging; +namespace HydraScript.Infrastructure.Dumping; -internal class LoggingParser(IParser parser, IFileSystem fileSystem) : IParser +internal class DumpingParser(IParser parser, IFileSystem fileSystem) : IParser { public IAbstractSyntaxTree Parse(string text) { diff --git a/src/Infrastructure/HydraScript.Infrastructure/Logging/LoggingVirtualMachine.cs b/src/Infrastructure/HydraScript.Infrastructure/Dumping/DumpingVirtualMachine.cs similarity index 88% rename from src/Infrastructure/HydraScript.Infrastructure/Logging/LoggingVirtualMachine.cs rename to src/Infrastructure/HydraScript.Infrastructure/Dumping/DumpingVirtualMachine.cs index 321ea67..96e6eae 100644 --- a/src/Infrastructure/HydraScript.Infrastructure/Logging/LoggingVirtualMachine.cs +++ b/src/Infrastructure/HydraScript.Infrastructure/Dumping/DumpingVirtualMachine.cs @@ -2,9 +2,9 @@ using HydraScript.Domain.BackEnd; using Microsoft.Extensions.Options; -namespace HydraScript.Infrastructure.Logging; +namespace HydraScript.Infrastructure.Dumping; -internal class LoggingVirtualMachine( +internal class DumpingVirtualMachine( IVirtualMachine virtualMachine, IFileSystem fileSystem, IOptions inputFile) : IVirtualMachine diff --git a/src/Infrastructure/HydraScript.Infrastructure/HydraScript.Infrastructure.csproj b/src/Infrastructure/HydraScript.Infrastructure/HydraScript.Infrastructure.csproj index 7056f88..17f5c40 100644 --- a/src/Infrastructure/HydraScript.Infrastructure/HydraScript.Infrastructure.csproj +++ b/src/Infrastructure/HydraScript.Infrastructure/HydraScript.Infrastructure.csproj @@ -19,6 +19,7 @@ + diff --git a/src/Infrastructure/HydraScript.Infrastructure/LoggingWriter.cs b/src/Infrastructure/HydraScript.Infrastructure/LoggingWriter.cs new file mode 100644 index 0000000..0ea8814 --- /dev/null +++ b/src/Infrastructure/HydraScript.Infrastructure/LoggingWriter.cs @@ -0,0 +1,13 @@ +using HydraScript.Domain.BackEnd; +using Microsoft.Extensions.Logging; + +namespace HydraScript.Infrastructure; + +internal class LoggingWriter(ILogger logger) : IOutputWriter +{ + public void WriteLine(object? obj) => + logger.LogInformation("{Object}", obj); + + public void WriteError(Exception e, string message) => + logger.LogError(e, "{Message}", message); +} \ No newline at end of file diff --git a/src/Infrastructure/HydraScript.Infrastructure/ServiceCollectionExtensions.cs b/src/Infrastructure/HydraScript.Infrastructure/ServiceCollectionExtensions.cs index ca07aca..b4cf85f 100644 --- a/src/Infrastructure/HydraScript.Infrastructure/ServiceCollectionExtensions.cs +++ b/src/Infrastructure/HydraScript.Infrastructure/ServiceCollectionExtensions.cs @@ -7,7 +7,7 @@ using HydraScript.Domain.FrontEnd.Lexer.Impl; using HydraScript.Domain.FrontEnd.Parser; using HydraScript.Domain.FrontEnd.Parser.Impl; -using HydraScript.Infrastructure.Logging; +using HydraScript.Infrastructure.Dumping; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; @@ -23,7 +23,7 @@ public static IServiceCollection AddDomain(this IServiceCollection services) services.AddSingleton(); services.AddSingleton(); - services.AddSingleton(Console.Out); + services.AddSingleton(); services.AddSingleton(); return services; @@ -48,9 +48,9 @@ public static void AddInfrastructure( if (dump) { - services.Decorate(); - services.Decorate(); - services.Decorate(); + services.Decorate(); + services.Decorate(); + services.Decorate(); } } } \ No newline at end of file diff --git a/tests/HydraScript.IntegrationTests/ErrorPrograms/VariableInitializationTests.cs b/tests/HydraScript.IntegrationTests/ErrorPrograms/VariableInitializationTests.cs index d37ae2b..f1bc804 100644 --- a/tests/HydraScript.IntegrationTests/ErrorPrograms/VariableInitializationTests.cs +++ b/tests/HydraScript.IntegrationTests/ErrorPrograms/VariableInitializationTests.cs @@ -6,55 +6,41 @@ namespace HydraScript.IntegrationTests.ErrorPrograms; public class VariableInitializationTests( TestHostFixture fixture, - ITestOutputHelper testOutputHelper) : IClassFixture, IDisposable + ITestOutputHelper testOutputHelper) : IClassFixture { - private readonly StringWriter _writer = new(); - - [Fact] - public void VariableWithoutTypeDeclared_AccessedBeforeInitialization_ExitCodeHydraScriptError() - { - const string script = -""" -let x = f() -function f() { - print(x as string) - return 5 -} -"""; - var runner = fixture.GetRunner( - testOutputHelper, - _writer, - configureTestServices: services => services.SetupInMemoryScript(script)); - var code = runner.Invoke(fixture.InMemoryScript); - code.Should().Be(ExitCodes.HydraScriptError); - var output = _writer.ToString().Trim(); - output.Should().Be("(3, 11)-(3, 12) Cannot access 'x' before initialization"); - } - - [Fact] - public void TypedVariableDeclared_AccessedBeforeInitialization_ExitCodeHydraScriptError() + [Theory, MemberData(nameof(VariableInitializationScripts))] + public void VariableWithoutTypeDeclared_AccessedBeforeInitialization_ExitCodeHydraScriptError(string script) { - const string script = -""" -let x: number = f() -function f() { - print(x as string) - return 5 -} -"""; var runner = fixture.GetRunner( testOutputHelper, - _writer, configureTestServices: services => services.SetupInMemoryScript(script)); var code = runner.Invoke(fixture.InMemoryScript); code.Should().Be(ExitCodes.HydraScriptError); - var output = _writer.ToString().Trim(); - output.Should().Be("(3, 11)-(3, 12) Cannot access 'x' before initialization"); + fixture.LogMessages.Should() + .Contain(x => x.Contains("Cannot access 'x' before initialization")); } - public void Dispose() + public static TheoryData VariableInitializationScripts { - _writer.Dispose(); - fixture.Dispose(); + get + { + const string variableWithoutTypeDeclared = + """ + let x = f() + function f() { + print(x as string) + return 5 + } + """; + const string typedVariableDeclared = + """ + let x: number = f() + function f() { + print(x as string) + return 5 + } + """; + return new TheoryData([variableWithoutTypeDeclared, typedVariableDeclared]); + } } } \ No newline at end of file diff --git a/tests/HydraScript.IntegrationTests/HydraScript.IntegrationTests.csproj b/tests/HydraScript.IntegrationTests/HydraScript.IntegrationTests.csproj index 6c8af68..ea370a0 100644 --- a/tests/HydraScript.IntegrationTests/HydraScript.IntegrationTests.csproj +++ b/tests/HydraScript.IntegrationTests/HydraScript.IntegrationTests.csproj @@ -11,6 +11,7 @@ + diff --git a/tests/HydraScript.IntegrationTests/TestHostFixture.cs b/tests/HydraScript.IntegrationTests/TestHostFixture.cs index 1fbca5c..92e8855 100644 --- a/tests/HydraScript.IntegrationTests/TestHostFixture.cs +++ b/tests/HydraScript.IntegrationTests/TestHostFixture.cs @@ -10,33 +10,40 @@ namespace HydraScript.IntegrationTests; public class TestHostFixture : IDisposable { + private readonly List _logMessages = []; + public readonly string[] InMemoryScript = ["file.js"]; + public IReadOnlyCollection LogMessages => _logMessages; public Parser GetRunner( ITestOutputHelper testOutputHelper, - TextWriter? writer = null, Action? configureTestServices = null) => Program.GetRunner(configureHost: builder => builder - .ConfigureLogging(x => - { - x.ClearProviders(); - x.AddXUnit(testOutputHelper); - }) + .ConfigureLogging(x => x.ClearProviders() + .AddXUnit(testOutputHelper) + .AddFakeLogging(options => + { + options.OutputSink = logMessage => _logMessages.Add(logMessage); + options.OutputFormatter = fakeLogRecord => + fakeLogRecord.Level switch + { + LogLevel.Error => $"{fakeLogRecord.Message} {fakeLogRecord.Exception?.Message}", + _ => fakeLogRecord.ToString() + }; + })) .ConfigureServices((context, services) => { + services.Configure(options => options.SuppressStatusMessages = true); var fileInfo = context.GetInvocationContext().ParseResult .GetValueForArgument(Program.Command.PathArgument); services .AddDomain() .AddApplication() .AddInfrastructure(dump: false, fileInfo); - services.AddSingleton(writer ?? TextWriter.Null); configureTestServices?.Invoke(services); }) .UseCommandHandler(), useDefault: false); - public void Dispose() - { - } + public void Dispose() => _logMessages.Clear(); } \ No newline at end of file diff --git a/tests/HydraScript.Tests/Unit/BackEnd/VirtualMachineTests.cs b/tests/HydraScript.Tests/Unit/BackEnd/VirtualMachineTests.cs index 5c62b90..ab7d512 100644 --- a/tests/HydraScript.Tests/Unit/BackEnd/VirtualMachineTests.cs +++ b/tests/HydraScript.Tests/Unit/BackEnd/VirtualMachineTests.cs @@ -19,13 +19,13 @@ public class VirtualMachineTests public VirtualMachineTests() { - _vm = new VirtualMachine(TextWriter.Null); + _vm = new VirtualMachine(Mock.Of()); } [Fact] public void CorrectPrintToOutTest() { - var writer = new Mock(); + var writer = new Mock(); writer.Setup(x => x.WriteLine(It.IsAny())) .Verifiable(); @@ -117,7 +117,7 @@ public void VirtualMachineHandlesRecursionTest() [Fact] public void CreateArrayReservesCertainSpaceTest() { - var vm = new ExecuteParams(Console.Out); + var vm = new ExecuteParams(Mock.Of()); vm.Frames.Push(new Frame(new HashAddress(0))); var createArray = new CreateArray("arr", 6) diff --git a/tests/HydraScript.Tests/Unit/Infrastructure/LoggingEntitiesTests.cs b/tests/HydraScript.Tests/Unit/Infrastructure/LoggingEntitiesTests.cs index e620c6e..146e8c1 100644 --- a/tests/HydraScript.Tests/Unit/Infrastructure/LoggingEntitiesTests.cs +++ b/tests/HydraScript.Tests/Unit/Infrastructure/LoggingEntitiesTests.cs @@ -2,7 +2,7 @@ using HydraScript.Domain.FrontEnd.Lexer; using HydraScript.Domain.FrontEnd.Parser; using HydraScript.Infrastructure; -using HydraScript.Infrastructure.Logging; +using HydraScript.Infrastructure.Dumping; using Microsoft.Extensions.Options; using Moq; using Xunit; @@ -38,7 +38,7 @@ public void CorrectFileNameProducedByLexerTest() It.IsAny())) .Verifiable(); - var loggingLexer = new LoggingLexer( + var loggingLexer = new DumpingLexer( lexer.Object, _fileSystem.Object, inputFile: Options.Create(new InputFile { Info = new FileInfo("file") })); @@ -66,7 +66,7 @@ public void CorrectTreeWrittenAndLoggingTreeProducedTest() It.IsAny(), It.IsAny() )).Verifiable(); - var loggingParser = new LoggingParser(parser.Object, _fileSystem.Object); + var loggingParser = new DumpingParser(parser.Object, _fileSystem.Object); _ = loggingParser.Parse(""); _file.Verify(