Skip to content

Commit

Permalink
Fixes all unit tests for .NET Core 😁
Browse files Browse the repository at this point in the history
- made config file dictionary concurrent to handle concurrently running unit tests
- add unit tests for various ImageSharp quirks
- add specialised test for ImageSharp text rendering bug
- add accessor for logging properties to make it easier for parallelised tests to restore state after test
- tried to make logging tests more robust
- made sure PathHelper and OutputDirectoryTest now use the same test output fodlers (in particular, GetTempDir will now always be in mstest output folder)
  • Loading branch information
atruskie committed Mar 2, 2020
1 parent 47ee8ff commit a0bc5ab
Show file tree
Hide file tree
Showing 18 changed files with 461 additions and 150 deletions.
1 change: 1 addition & 0 deletions AudioAnalysis.sln.DotSettings
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,7 @@
<s:Boolean x:Key="/Default/UserDictionary/Words/=resampled/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Runtimes/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=spectrograms/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Subband/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Submatrix/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Anthony/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=appenders/@EntryIndexedValue">True</s:Boolean>
Expand Down
10 changes: 5 additions & 5 deletions src/Acoustics.Shared/ConfigFile/ConfigFile.Deserialize.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,21 @@
namespace Acoustics.Shared.ConfigFile
{
using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.IO;
using Contracts;
using Acoustics.Shared.Contracts;
using log4net;
using Newtonsoft.Json;
using ObjectCloner.Extensions;

public static partial class ConfigFile
{
private static readonly Dictionary<string, IConfig> CachedProperties;
private static readonly ConcurrentDictionary<string, IConfig> CachedProperties;
private static JsonSerializerSettings configJsonSerializerSettings;

static ConfigFile()
{
CachedProperties = new Dictionary<string, IConfig>();
CachedProperties = new ConcurrentDictionary<string, IConfig>();
configJsonSerializerSettings = new JsonSerializerSettings()
{
ContractResolver = ConfigSerializeContractResolver.Instance,
Expand Down Expand Up @@ -139,7 +139,7 @@ private static T LoadAndCache<T>(string path, Func<T> factory)
((IConfig)loadedConfig).InvokeLoaded();

// cache the config (with possible nested configs)
CachedProperties.Add(path, loadedConfig);
CachedProperties.AddOrUpdate(path, loadedConfig, (key, existing) => loadedConfig);

cachedConfig = loadedConfig;
}
Expand Down
62 changes: 55 additions & 7 deletions src/Acoustics.Shared/ImageSharp/ImageSharpExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -214,16 +214,15 @@ public static void DrawLine(this IImageProcessingContext context, Pen pen, int x

}

public static void DrawRectangle(this IImageProcessingContext context, Pen pen, int x1, int y1, int x2, int y2)
public static void DrawRectangle(this IImageProcessingContext context, Pen pen, int x, int y, int width, int height)
{
var r = RectangleF.FromLTRB(x1, y1, x2, y2);
var r = new RectangleF(x, y, width, height);
context.Draw(pen, r);

}

public static void FillRectangle(this IImageProcessingContext context, IBrush brush, int x1, int y1, int x2, int y2)
public static void FillRectangle(this IImageProcessingContext context, IBrush brush, int x, int y, int width, int height)
{
var r = RectangleF.FromLTRB(x1, y1, x2, y2);
var r = new RectangleF(x, y, width, height);
context.Fill(brush, r);
}

Expand Down Expand Up @@ -307,15 +306,64 @@ public static Rectangle AsRect(this (int X, int Y, int Width, int Height) rect)
return new Rectangle(rect.X, rect.Y, rect.Width, rect.Height);
}

public static RectangleF AsRect(this PointF point, SizeF size)
{
return new RectangleF(point, size);
}

public static RectangleF AsRect(this FontRectangle rectangle)
{
return new RectangleF(rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height);
}

public static void DrawTextSafe(this IImageProcessingContext context, string text, Font font, Color color, PointF location)
{
if (text.IsNullOrEmpty())
{
return;
}

context.DrawText(text, font, color, location);

// check to see if text overlaps with image
var destArea = new RectangleF(PointF.Empty, context.GetCurrentSize());
var rendererOptions = new RendererOptions(font, location);
var textArea = TextMeasurer.MeasureBounds(text, rendererOptions);
if (destArea.IntersectsWith(textArea.AsRect()))
{
if (textArea.X < 0)
{
// TODO BUG: see https://github.com/SixLabors/ImageSharp.Drawing/issues/30
// to get around the bug, we measure each character, and then trim them from the
// start of the text, move the location right by the width of the trimmed character
// and continue until we're in a spot that does not trigger the bug;
int trim = 0;
float trimOffset = 0;
if (TextMeasurer.TryMeasureCharacterBounds(text, rendererOptions, out var characterBounds))
{
foreach (var characterBound in characterBounds)
{
// magic value found empirically, does not seem to trigger bug when first char less than offset equal to char size
if (characterBound.Bounds.X > -(font.Size - 2))
{
break;
}

trim++;
trimOffset += characterBound.Bounds.Width;
}
}
else
{
throw new NotSupportedException("Due to a bug with ImageSharp this text cannot be rendered");
}

location.Offset(trimOffset, 0);
context.DrawText(text[trim..], font, color, location);
}
else
{
context.DrawText(text, font, color, location);
}
}
}

public static void DrawVerticalText(this IImageProcessingContext context, string text, Font font, Color color, Point location)
Expand Down
9 changes: 9 additions & 0 deletions src/Acoustics.Shared/Logging/Logging.cs
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,9 @@ internal Logging(
this.repository.Threshold = defaultLevel;
this.rootLogger.Level = defaultLevel;

this.Verbosity = defaultLevel;
this.QuietConsole = quietConsole;

// log to a file
PatternLayout standardPattern = new PatternLayout
{
Expand Down Expand Up @@ -204,6 +207,10 @@ internal Logging(
}
}

public Level Verbosity { get; private set; }

public bool QuietConsole { get; private set; }

public string LogFileName { get; }

public string LogFilePath { get; }
Expand Down Expand Up @@ -239,6 +246,8 @@ public void ModifyVerbosity(Level defaultLevel, bool quietConsole)
}

this.repository.RaiseConfigurationChanged(EventArgs.Empty);
this.Verbosity = defaultLevel;
this.QuietConsole = quietConsole;
}

public void TestLogging()
Expand Down
23 changes: 10 additions & 13 deletions src/AnalysisPrograms/Production/MainEntryUtilities.cs
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ internal static void AttachDebugger(DebugOptions options)
return;
}
#if DEBUG
if (!Debugger.IsAttached && !IsMsTestRunningMe())
if (!Debugger.IsAttached && !IsMsTestRunningMe)
{
if (options == DebugOptions.Prompt)
{
Expand Down Expand Up @@ -210,8 +210,8 @@ internal static bool CheckAndUpdateApplicationConfig()
// https://github.com/QutEcoacoustics/audio-analysis/issues/241
var executableName = Process.GetCurrentProcess().MainModule.ModuleName;
var expectedName = Assembly.GetAssembly(typeof(MainEntry)).ManifestModule.ScopeName.Replace(".dll", ".exe");
Log.Verbose($"!!! IMPORTANT: Executable name is {executableName} and expected name is {expectedName}");
if (expectedName != executableName)

if (expectedName != executableName && !IsMsTestRunningMe)
{
Log.Warn($"!!! IMPORTANT: Executable name is {executableName} and expected name is {expectedName}");
}
Expand Down Expand Up @@ -285,7 +285,7 @@ internal static void HangBeforeExit()
return;
}

if (IsMsTestRunningMe())
if (IsMsTestRunningMe)
{
return;
}
Expand All @@ -300,15 +300,12 @@ internal static void HangBeforeExit()
#endif
}

#if DEBUG
internal static bool IsMsTestRunningMe()
{
// https://stackoverflow.com/questions/3167617/determine-if-code-is-running-as-part-of-a-unit-test
string testAssemblyName = "Microsoft.VisualStudio.TestPlatform.TestFramework";
return AppDomain.CurrentDomain.GetAssemblies()
.Any(a => a.FullName.StartsWith(testAssemblyName));
}
#endif
// https://stackoverflow.com/questions/3167617/determine-if-code-is-running-as-part-of-a-unit-test
internal static bool IsMsTestRunningMe { get; } =
AppDomain
.CurrentDomain
.GetAssemblies()
.Any(a => a.FullName.StartsWith("Microsoft.VisualStudio.TestPlatform.TestFramework"));

internal static void PrintUsage(string message, Usages usageStyle, string commandName = null)
{
Expand Down
33 changes: 17 additions & 16 deletions src/AudioAnalysisTools/StandardSpectrograms/SpectrogramTools.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ namespace AudioAnalysisTools.StandardSpectrograms
using System.IO;
using Accord;
using Acoustics.Shared;
using Acoustics.Shared.Contracts;
using DSP;
using LongDurationSpectrograms;
using SixLabors.ImageSharp.ColorSpaces;
Expand Down Expand Up @@ -80,33 +81,33 @@ public static Image<Rgb24> GetSonogramPlusCharts(BaseSonogram sonogram, List<Aco
*/

/// <summary>
/// THis method draws a spectrogram with other useful information attached.
/// This method draws a spectrogram with other useful information attached.
/// </summary>
/// <param name="sonogram">of BaseSonogram class.</param>
/// <param name="events">a list of acoustic events.</param>
/// <param name="plots">a list of plots relevant to the spectrogram scores.</param>
/// <param name="hits">not often used - can be null.</param>
public static Image<Rgb24> GetSonogramPlusCharts(BaseSonogram sonogram, List<AcousticEvent> events, List<Plot> plots, double[,] hits)
public static Image<Rgb24> GetSonogramPlusCharts(
BaseSonogram sonogram,
List<AcousticEvent> events,
List<Plot> plots,
double[,] hits)
{
var spImage = sonogram.GetImage(doHighlightSubband: false, add1KHzLines: true, doMelScale: false);
var spectrogram = sonogram.GetImage(doHighlightSubband: false, add1KHzLines: true, doMelScale: false);
Contract.RequiresNotNull(spectrogram, nameof(spectrogram));

if (spImage == null)
{
throw new ArgumentNullException(nameof(spImage));
}

var spHeight = spImage.Height;
var height = spectrogram.Height;
var frameSize = sonogram.Configuration.WindowSize;

// init with linear frequency scale and draw freq grid lines on image
int hertzInterval = 1000;
if (spHeight < 200)
if (height < 200)
{
hertzInterval = 2000;
}

var freqScale = new FrequencyScale(sonogram.NyquistFrequency, frameSize, hertzInterval);
FrequencyScale.DrawFrequencyLinesOnImage(spImage, freqScale.GridLineLocations, includeLabels: true);
FrequencyScale.DrawFrequencyLinesOnImage(spectrogram, freqScale.GridLineLocations, includeLabels: true);

// draw event outlines onto spectrogram.
if (events != null && events.Count > 0)
Expand All @@ -116,27 +117,27 @@ public static Image<Rgb24> GetSonogramPlusCharts(BaseSonogram sonogram, List<Aco
{
ev.BorderColour = AcousticEvent.DefaultBorderColor;
ev.ScoreColour = AcousticEvent.DefaultScoreColor;
ev.DrawEvent(spImage, sonogram.FramesPerSecond, sonogram.FBinWidth, spHeight);
ev.DrawEvent(spectrogram, sonogram.FramesPerSecond, sonogram.FBinWidth, height);
}
}

// now add in hits to the spectrogram image.
if (hits != null)
{
spImage = Image_MultiTrack.OverlayScoresAsRedTransparency(spImage, hits);
spectrogram = Image_MultiTrack.OverlayScoresAsRedTransparency(spectrogram, hits);
//OverlayRedTransparency(bmp, this.SuperimposedRedTransparency);
//this.SonogramImage = this.OverlayRedTransparency((Image<Rgb24>)this.SonogramImage);
}

int pixelWidth = spImage.Width;
int pixelWidth = spectrogram.Width;
var titleBar = LDSpectrogramRGB.DrawTitleBarOfGrayScaleSpectrogram("TITLE", pixelWidth);
var timeTrack = ImageTrack.DrawTimeTrack(sonogram.Duration, pixelWidth);

var imageList = new List<Image<Rgb24>>
{
titleBar,
timeTrack,
spImage,
spectrogram,
timeTrack,
};

Expand All @@ -154,7 +155,7 @@ public static Image<Rgb24> GetSonogramPlusCharts(BaseSonogram sonogram, List<Aco
}

var compositeImage = ImageTools.CombineImagesVertically(imageList);
//return image.GetImage().CloneAs<Rgb24>();

return compositeImage;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ namespace Acoustics.Test.AnalysisPrograms.Draw.Zooming
[TestClass]
public class DrawZoomingTests : OutputDirectoryTest
{
public static DirectoryInfo ResultsDirectory { get; set; }
private static DirectoryInfo indicesDirectory;

[ClassInitialize]
public static void ClassInitialize(TestContext context)
Expand All @@ -35,12 +35,12 @@ public static void ClassInitialize(TestContext context)

context.WriteLine($"{DateTime.Now} generating indices fixture data");
AnalyseLongRecording.Execute(arguments);
context.WriteLine($"{DateTime.Now} finished generting fixture");
context.WriteLine($"{DateTime.Now} finished generating fixture");

ResultsDirectory = ResultsDirectory.Combine("Towsey.Acoustic");
indicesDirectory = ResultsDirectory.Combine("Towsey.Acoustic");

// do some basic checks that the indices were generated
var listOfFiles = ResultsDirectory.EnumerateFiles().ToArray();
var listOfFiles = indicesDirectory.EnumerateFiles().ToArray();
Assert.AreEqual(19, listOfFiles.Length);
var csvCount = listOfFiles.Count(f => f.Name.EndsWith(".csv"));
Assert.AreEqual(15, csvCount);
Expand Down Expand Up @@ -72,7 +72,7 @@ void SetupAndRun(params double[] scales)
new DrawZoomingSpectrograms.Arguments()
{
Output = this.TestOutputDirectory.FullName,
SourceDirectory = ResultsDirectory.FullName,
SourceDirectory = indicesDirectory.FullName,
SpectrogramZoomingConfig = newConfigFile.FullName,
ZoomAction = DrawZoomingSpectrograms.Arguments.ZoomActionType.Tile,
});
Expand All @@ -92,12 +92,12 @@ void SetupAndRun(params double[] scales)
public void TestGenerateTiles()
{
// generate the zooming spectrograms
var zoomOutput = this.TestOutputDirectory.Combine("Zooming");
var zoomOutput = this.TestOutputDirectory;
DrawZoomingSpectrograms.Execute(
new DrawZoomingSpectrograms.Arguments()
{
Output = zoomOutput.FullName,
SourceDirectory = ResultsDirectory.FullName,
SourceDirectory = indicesDirectory.FullName,
SpectrogramZoomingConfig = PathHelper.ResolveConfigFile("SpectrogramZoomingConfig.yml").FullName,
ZoomAction = DrawZoomingSpectrograms.Arguments.ZoomActionType.Tile,
});
Expand Down Expand Up @@ -134,7 +134,7 @@ public void TestGenerateTilesSqlite()
{
Output = zoomOutput.FullName,
OutputFormat = "sqlite3",
SourceDirectory = ResultsDirectory.FullName,
SourceDirectory = indicesDirectory.FullName,
SpectrogramZoomingConfig = PathHelper.ResolveConfigFile("SpectrogramZoomingConfig.yml").FullName,
ZoomAction = DrawZoomingSpectrograms.Arguments.ZoomActionType.Tile,
});
Expand Down
14 changes: 6 additions & 8 deletions tests/Acoustics.Test/AnalysisPrograms/MainEntryTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,16 +38,14 @@ public async Task DefaultCliWorks()
[TestMethod]
public async Task DefaultHelpWorks()
{
using (var console = new ConsoleRedirector())
{
var code = await MainEntry.Main(new[] { "--help" });
using var console = new ConsoleRedirector();
var code = await MainEntry.Main(new[] { "--help" });

Assert.AreEqual(0, code);
Assert.AreEqual(0, code);

this.AssertContainsCopyright(console.Lines);
this.AssertContainsGitHashAndVersion(console.Lines);
StringAssert.StartsWith(console.Lines[6], Meta.Description);
}
this.AssertContainsCopyright(console.Lines);
this.AssertContainsGitHashAndVersion(console.Lines);
StringAssert.StartsWith(console.Lines[6], Meta.Description);
}

[DoNotParallelize]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -214,10 +214,10 @@ public void TestAnnotatedSonogramWithPlots()
var image = SpectrogramTools.GetSonogramPlusCharts(actualDecibelSpectrogram, events, plots, null);

// create the image for visual confirmation
image.Save(Path.Combine(this.outputDirectory.FullName, this.recording.BaseName + ".png"));
image.Save(this.outputDirectory.CombineFile(this.recording.BaseName + ".png"));

Assert.AreEqual(1621, image.Width);
Assert.AreEqual(647, image.Height);
Assert.AreEqual(656, image.Height);
}
}
}
Loading

0 comments on commit a0bc5ab

Please sign in to comment.