Skip to content

Logging concept #106

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 12 commits into from
4 changes: 2 additions & 2 deletions src/coverlet.core/Coverage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,14 @@ public void PrepareModules()
{
string[] modules = InstrumentationHelper.GetCoverableModules(_module);
string[] excludedFiles = InstrumentationHelper.GetExcludedFiles(_rules);
_filters = _filters?.Where(f => InstrumentationHelper.IsValidFilterExpression(f)).ToArray();
_filters = _filters?.Where(InstrumentationHelper.IsValidFilterExpression).ToArray();

foreach (var module in modules)
{
if (InstrumentationHelper.IsModuleExcluded(module, _filters))
continue;

var instrumenter = new Instrumenter(module, _identifier, _filters, excludedFiles);
var instrumenter = InstrumenterFactory.Create(module, _identifier, _filters, excludedFiles);
if (instrumenter.CanInstrument())
{
InstrumentationHelper.BackupOriginalModule(module, _identifier);
Expand Down
9 changes: 9 additions & 0 deletions src/coverlet.core/Instrumentation/IInstrumenter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace Coverlet.Core.Instrumentation
{
internal interface IInstrumenter
{
bool CanInstrument();

InstrumenterResult Instrument();
}
}
7 changes: 3 additions & 4 deletions src/coverlet.core/Instrumentation/Instrumenter.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
Expand All @@ -14,7 +13,7 @@

namespace Coverlet.Core.Instrumentation
{
internal class Instrumenter
internal class Instrumenter : IInstrumenter
{
private readonly string _module;
private readonly string _identifier;
Expand Down Expand Up @@ -116,7 +115,7 @@ private void InstrumentIL(MethodDefinition method)
var instruction = processor.Body.Instructions[index];
var sequencePoint = method.DebugInformation.GetSequencePoint(instruction);
var targetedBranchPoints = branchPoints.Where(p => p.EndOffset == instruction.Offset);

if (sequencePoint != null && !sequencePoint.IsHidden)
{
var target = AddInstrumentationCode(method, processor, instruction, sequencePoint);
Expand All @@ -139,7 +138,7 @@ private void InstrumentIL(MethodDefinition method)
*/
if (_branchTarget.StartLine == -1 || _branchTarget.Document == null)
continue;

var target = AddInstrumentationCode(method, processor, instruction, _branchTarget);
foreach (var _instruction in processor.Body.Instructions)
ReplaceInstructionTarget(_instruction, instruction, target);
Expand Down
29 changes: 29 additions & 0 deletions src/coverlet.core/Instrumentation/InstrumenterFactory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using System;
using System.Collections.Generic;

namespace Coverlet.Core.Instrumentation
{
internal static class InstrumenterFactory
{
private static readonly List<Func<IInstrumenter, IInstrumenter>> DecoratorFunctions = new List<Func<IInstrumenter, IInstrumenter>>();

public static IInstrumenter Create(string module, string identifier, string[] filters, string[] excludedFiles)
{
IInstrumenter result = new Instrumenter(module, identifier, filters, excludedFiles);

// Decorate the real Instrumenter instance
foreach (var decoratorFunc in DecoratorFunctions)
result = decoratorFunc.Invoke(result);

return result;
}

public static void RegisterDecorator(Func<IInstrumenter, IInstrumenter> decoratingFunc)
{
if (decoratingFunc == null)
throw new ArgumentNullException(nameof(decoratingFunc));

DecoratorFunctions.Add(decoratingFunc);
}
}
}
21 changes: 21 additions & 0 deletions src/coverlet.core/Logging/ConsoleLogger.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using System;

namespace Coverlet.Core.Logging
{
public class ConsoleLogger : ILogger
{
private ConsoleLogger()
{
}

public static ILogger Instance { get; } = new ConsoleLogger();

public void Log(string text)
{
if (text == null)
return;

Console.WriteLine(text);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using Coverlet.Core.Instrumentation;
using Coverlet.Core.Logging;

namespace coverlet.core.Logging.Decorators
{
internal class InstrumenterLoggerDecorator : IInstrumenter
{
private readonly IInstrumenter _decoratee;
private readonly ILogger _logger;

public InstrumenterLoggerDecorator(IInstrumenter decoratee, ILogger logger)
{
_decoratee = decoratee;
_logger = logger;
}

public bool CanInstrument()
{
return _decoratee.CanInstrument();
}

public InstrumenterResult Instrument()
{
var result = _decoratee.Instrument();
_logger.Log($"Module {result.Module} was instrumented.");
return result;
}
}
}
15 changes: 15 additions & 0 deletions src/coverlet.core/Logging/EnableLogging.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using coverlet.core.Logging.Decorators;
using Coverlet.Core.Instrumentation;
using Coverlet.Core.Logging;

namespace coverlet.core.Logging
{
public static class EnableLogging
{
public static void Execute()
{
LoggerFactory.GetLogger().Log("Logging enabled.");
InstrumenterFactory.RegisterDecorator(instrumenter => new InstrumenterLoggerDecorator(instrumenter, LoggerFactory.GetLogger()));
}
}
}
7 changes: 7 additions & 0 deletions src/coverlet.core/Logging/ILogger.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace Coverlet.Core.Logging
{
public interface ILogger
{
void Log(string text);
}
}
10 changes: 10 additions & 0 deletions src/coverlet.core/Logging/LoggerFactory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace Coverlet.Core.Logging
{
public static class LoggerFactory
{
public static ILogger GetLogger()
{
return ConsoleLogger.Instance;
}
}
}
8 changes: 7 additions & 1 deletion src/coverlet.msbuild.tasks/InstrumentationTask.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using coverlet.core.Logging;
using Coverlet.Core;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
Expand All @@ -23,7 +24,7 @@ public string Path
get { return _path; }
set { _path = value; }
}

public string Exclude
{
get { return _exclude; }
Expand All @@ -36,13 +37,18 @@ public string ExcludeByFile
set { _excludeByFile = value; }
}

public bool Verbose { get; set; }

public override bool Execute()
{
try
{
var rules = _excludeByFile?.Split(',');
var filters = _exclude?.Split(',');

if (Verbose)
EnableLogging.Execute();

_coverage = new Coverage(_path, Guid.NewGuid().ToString(), filters, rules);
_coverage.PrepareModules();
}
Expand Down
45 changes: 45 additions & 0 deletions test/coverlet.core.tests/Logging/ConsoleLoggerTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
using System;
using System.IO;
using Coverlet.Core.Logging;
using Xunit;

namespace Coverlet.Core.Tests.Logging
{
public class ConsoleLoggerTests
{
private readonly TextWriter _textWriter;

public ConsoleLoggerTests()
{
_textWriter = new StringWriter();
Console.SetOut(_textWriter);
}

[Fact]
public void TestConsoleLoggerLogShouldWriteMessageToConsole()
{
// arrange
const string message = "this is a test message";

// act
ConsoleLogger.Instance.Log(message);

// assert
_textWriter.Flush();
Assert.Equal($"{message}{Environment.NewLine}" , _textWriter.ToString());
}

[Fact]
public void TestConsoleLoggerLogShouldWriteNothingWhenMessageIsNull()
{
// arrange

// act
ConsoleLogger.Instance.Log(null);

// assert
_textWriter.Flush();
Assert.Empty(_textWriter.ToString());
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
using System;
using System.Text;
using coverlet.core.Logging.Decorators;
using Coverlet.Core.Instrumentation;
using Coverlet.Core.Logging;
using Xunit;

namespace Coverlet.Core.Tests.Logging.Decorators
{
public class InstrumenterLoggerDecoratorTests
{
private readonly FakeInstumenter _decoratee;
private readonly FakeLogger _logger;
private readonly InstrumenterLoggerDecorator _sut;

public InstrumenterLoggerDecoratorTests()
{
_decoratee = new FakeInstumenter();
_logger = new FakeLogger();
_sut = new InstrumenterLoggerDecorator(_decoratee, _logger);
}

[Theory]
[InlineData(true)]
[InlineData(false)]
public void TestCanInstrumentIsForwardedToDecoratee(bool setupCanInstrument)
{
// arrange
var canInstrumentCalled = false;
_decoratee.SetCanInstrument(() =>
{
canInstrumentCalled = true;
return setupCanInstrument;
});

// act
var result = _sut.CanInstrument();

// assert
Assert.True(canInstrumentCalled);
Assert.Equal(setupCanInstrument, result);
}


[Fact]
public void TestInstrumentIsForwardedToDecorateeAndLoggedInLogger()
{
// arrange
var setupResult = new InstrumenterResult { Module = "m0dule"};
var instrumentCalled = false;
_decoratee.SetInstrument(() =>
{
instrumentCalled = true;
return setupResult;
});

// act
var result = _sut.Instrument();

// assert
Assert.True(instrumentCalled);
Assert.Equal(setupResult, result);
Assert.Equal($"Module {result.Module} was instrumented.{Environment.NewLine}", _logger.ToString());
}
}

public class FakeLogger : ILogger
{
private readonly StringBuilder _sb;

public FakeLogger()
{
_sb = new StringBuilder();
}

public void Log(string text)
{
_sb.AppendLine(text);
}

public override string ToString()
{
return _sb.ToString();
}
}

internal class FakeInstumenter : IInstrumenter
{
private Func<bool> _canInstrument = () => throw new NotImplementedException();
private Func<InstrumenterResult> _instrumenterResult = () => throw new NotImplementedException();

public void SetCanInstrument(Func<bool> func)
{
_canInstrument = func ?? throw new ArgumentNullException(nameof(func));
}

public bool CanInstrument()
{
return _canInstrument.Invoke();
}

public void SetInstrument(Func<InstrumenterResult> instrumenterResult)
{
_instrumenterResult = instrumenterResult ?? throw new ArgumentNullException(nameof(instrumenterResult));
}

public InstrumenterResult Instrument()
{
return _instrumenterResult.Invoke();
}

}
}
Loading