Skip to content

Commit

Permalink
#97 - внедрение логгера (#108)
Browse files Browse the repository at this point in the history
* #97 - rename logging -> dumping

* #97 - refactoring of writer entity

* #97 - реализация вывода с помощью логгера

* #97 - реализация форматтера для эквивалентного вывода

* #97 - работа с логгером в тестах
  • Loading branch information
Stepami authored Aug 17, 2024
1 parent a1c2e1d commit 6a52cc6
Show file tree
Hide file tree
Showing 18 changed files with 124 additions and 77 deletions.
2 changes: 1 addition & 1 deletion src/Domain/HydraScript.Domain.BackEnd/IExecuteParams.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@ public interface IExecuteParams
public Stack<Call> CallStack { get; }
public Stack<Frame> Frames { get; }
public Queue<object?> Arguments { get; }
public TextWriter Writer { get; }
public IOutputWriter Writer { get; }
}
8 changes: 8 additions & 0 deletions src/Domain/HydraScript.Domain.BackEnd/IOutputWriter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace HydraScript.Domain.BackEnd;

public interface IOutputWriter
{
public void WriteLine(object? obj);

public void WriteError(Exception e, string message);
}
4 changes: 2 additions & 2 deletions src/Domain/HydraScript.Domain.BackEnd/Impl/ExecuteParams.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
namespace HydraScript.Domain.BackEnd.Impl;

public class ExecuteParams(TextWriter textWriter) : IExecuteParams
public class ExecuteParams(IOutputWriter textWriter) : IExecuteParams
{
public Stack<Call> CallStack { get; } = new();
public Stack<Frame> Frames { get; } = new();
public Queue<object?> Arguments { get; } = new();
public TextWriter Writer { get; } = textWriter;
public IOutputWriter Writer { get; } = textWriter;
}
Original file line number Diff line number Diff line change
@@ -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);

Expand Down
9 changes: 4 additions & 5 deletions src/HydraScript/ExecuteCommandHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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;
}
}
Expand Down
6 changes: 5 additions & 1 deletion src/HydraScript/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand All @@ -25,7 +26,10 @@ internal static Parser GetRunner(Action<IHostBuilder> 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<SimplestConsoleFormatter, ConsoleFormatterOptions>());
services.Configure<InvocationLifetimeOptions>(options => options.SuppressStatusMessages = true);
var parseResult = context.GetInvocationContext().ParseResult;
var fileInfo = parseResult.GetValueForArgument(Command.PathArgument);
var dump = parseResult.GetValueForOption(Command.DumpOption);
Expand Down
28 changes: 28 additions & 0 deletions src/HydraScript/SimplestConsoleFormatter.cs
Original file line number Diff line number Diff line change
@@ -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<TState>(
in LogEntry<TState> 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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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> inputFile) : ILexer
Expand Down
Original file line number Diff line number Diff line change
@@ -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)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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> inputFile) : IVirtualMachine
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.1" />
<PackageReference Include="Microsoft.Extensions.Options" Version="8.0.2" />
<PackageReference Include="Scrutor" Version="4.2.2" />
<PackageReference Include="System.IO.Abstractions" Version="21.0.29" />
Expand Down
13 changes: 13 additions & 0 deletions src/Infrastructure/HydraScript.Infrastructure/LoggingWriter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using HydraScript.Domain.BackEnd;
using Microsoft.Extensions.Logging;

namespace HydraScript.Infrastructure;

internal class LoggingWriter(ILogger<LoggingWriter> logger) : IOutputWriter
{
public void WriteLine(object? obj) =>
logger.LogInformation("{Object}", obj);

public void WriteError(Exception e, string message) =>
logger.LogError(e, "{Message}", message);
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -23,7 +23,7 @@ public static IServiceCollection AddDomain(this IServiceCollection services)
services.AddSingleton<ILexer, RegexLexer>();
services.AddSingleton<IParser, TopDownParser>();

services.AddSingleton(Console.Out);
services.AddSingleton<IOutputWriter, LoggingWriter>();
services.AddSingleton<IVirtualMachine, VirtualMachine>();

return services;
Expand All @@ -48,9 +48,9 @@ public static void AddInfrastructure(

if (dump)
{
services.Decorate<ILexer, LoggingLexer>();
services.Decorate<IParser, LoggingParser>();
services.Decorate<IVirtualMachine, LoggingVirtualMachine>();
services.Decorate<ILexer, DumpingLexer>();
services.Decorate<IParser, DumpingParser>();
services.Decorate<IVirtualMachine, DumpingVirtualMachine>();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,55 +6,41 @@ namespace HydraScript.IntegrationTests.ErrorPrograms;

public class VariableInitializationTests(
TestHostFixture fixture,
ITestOutputHelper testOutputHelper) : IClassFixture<TestHostFixture>, IDisposable
ITestOutputHelper testOutputHelper) : IClassFixture<TestHostFixture>
{
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<string> 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<string>([variableWithoutTypeDeclared, typedVariableDeclared]);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
<ItemGroup>
<PackageReference Include="FluentAssertions" Version="6.12.0"/>
<PackageReference Include="MartinCostello.Logging.XUnit" Version="0.4.0" />
<PackageReference Include="Microsoft.Extensions.Diagnostics.Testing" Version="8.8.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0"/>
<PackageReference Include="NSubstitute" Version="5.1.0" />
<PackageReference Include="xunit" Version="2.9.0"/>
Expand Down
27 changes: 17 additions & 10 deletions tests/HydraScript.IntegrationTests/TestHostFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,33 +10,40 @@ namespace HydraScript.IntegrationTests;

public class TestHostFixture : IDisposable
{
private readonly List<string> _logMessages = [];

public readonly string[] InMemoryScript = ["file.js"];
public IReadOnlyCollection<string> LogMessages => _logMessages;

public Parser GetRunner(
ITestOutputHelper testOutputHelper,
TextWriter? writer = null,
Action<IServiceCollection>? 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<InvocationLifetimeOptions>(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<ExecuteCommand, ExecuteCommandHandler>(),
useDefault: false);

public void Dispose()
{
}
public void Dispose() => _logMessages.Clear();
}
6 changes: 3 additions & 3 deletions tests/HydraScript.Tests/Unit/BackEnd/VirtualMachineTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@ public class VirtualMachineTests

public VirtualMachineTests()
{
_vm = new VirtualMachine(TextWriter.Null);
_vm = new VirtualMachine(Mock.Of<IOutputWriter>());
}

[Fact]
public void CorrectPrintToOutTest()
{
var writer = new Mock<TextWriter>();
var writer = new Mock<IOutputWriter>();
writer.Setup(x => x.WriteLine(It.IsAny<object?>()))
.Verifiable();

Expand Down Expand Up @@ -117,7 +117,7 @@ public void VirtualMachineHandlesRecursionTest()
[Fact]
public void CreateArrayReservesCertainSpaceTest()
{
var vm = new ExecuteParams(Console.Out);
var vm = new ExecuteParams(Mock.Of<IOutputWriter>());
vm.Frames.Push(new Frame(new HashAddress(0)));

var createArray = new CreateArray("arr", 6)
Expand Down
Loading

0 comments on commit 6a52cc6

Please sign in to comment.