diff --git a/.editorconfig b/.editorconfig
index e2f359511..dba8b41c4 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -48,6 +48,12 @@ trim_trailing_whitespace = false
[*.sh]
end_of_line = lf
+###########################
+# Diagnsotic customizations
+###########################
+[*.{cs,csx,cake}]
+dotnet_diagnostic.RS0030.severity = error
+
###########################
# .NET Language Conventions
# https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-code-style-settings-reference#language-conventions
diff --git a/AudioAnalysis.sln b/AudioAnalysis.sln
index c6fe92d6e..8a480992d 100644
--- a/AudioAnalysis.sln
+++ b/AudioAnalysis.sln
@@ -12,6 +12,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
src\AssemblyMetadata.cs.template = src\AssemblyMetadata.cs.template
src\AssemblyMetadata.Generated.targets = src\AssemblyMetadata.Generated.targets
azure-pipelines.yml = azure-pipelines.yml
+ BannedSymbols.txt = BannedSymbols.txt
Directory.Build.props = Directory.Build.props
build\download_ap.ps1 = build\download_ap.ps1
src\git_version.ps1 = src\git_version.ps1
diff --git a/BannedSymbols.txt b/BannedSymbols.txt
new file mode 100644
index 000000000..887ef1db9
--- /dev/null
+++ b/BannedSymbols.txt
@@ -0,0 +1,2 @@
+T:CsvHelper.CsvReader; You must not use CsvReader. Use The Acoustics.Shared.Csv.Read methods which properly construct the reader.
+T:CsvHelper.CsvWriter; You must not use CsvWriter. Use The Acoustics.Shared.Csv.Write methods which properly construct the writer.
diff --git a/Directory.Build.props b/Directory.Build.props
index d533434bc..42ffd3572 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -1,7 +1,7 @@
true
- $(MSBuildThisFileDirectory)style.ruleset
+ $(MSBuildThisFileDirectory)\style.ruleset
win-x64;win-arm64;linux-x64;linux-musl-x64;linux-arm;linux-arm64;osx-x64
@@ -48,5 +48,17 @@
all
runtime; build; native; contentfiles; analyzers; buildtransitive
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Acoustics.Shared/Acoustics.Shared.csproj b/src/Acoustics.Shared/Acoustics.Shared.csproj
index b74acca5b..7dc8faf46 100644
--- a/src/Acoustics.Shared/Acoustics.Shared.csproj
+++ b/src/Acoustics.Shared/Acoustics.Shared.csproj
@@ -1,4 +1,4 @@
-
+
Acoustics.Shared
Library
@@ -14,7 +14,7 @@
-
+
diff --git a/src/Acoustics.Shared/Collections/IInterval2{TX,TY}.cs b/src/Acoustics.Shared/Collections/IInterval2{TX,TY}.cs
index 7b5de8b88..d0621a9de 100644
--- a/src/Acoustics.Shared/Collections/IInterval2{TX,TY}.cs
+++ b/src/Acoustics.Shared/Collections/IInterval2{TX,TY}.cs
@@ -9,8 +9,8 @@ namespace Acoustics.Shared.ImageSharp
using System.Collections.Generic;
public interface IInterval2
- where TX : struct, IComparable
- where TY : struct, IComparable
+ where TX : struct, IComparable, IFormattable
+ where TY : struct, IComparable, IFormattable
{
Interval X { get; }
diff --git a/src/Acoustics.Shared/Csv/Csv.cs b/src/Acoustics.Shared/Csv/Csv.cs
index 02915b00f..8d5abeac9 100644
--- a/src/Acoustics.Shared/Csv/Csv.cs
+++ b/src/Acoustics.Shared/Csv/Csv.cs
@@ -21,63 +21,34 @@ namespace Acoustics.Shared.Csv
using CsvHelper.Configuration;
using CsvHelper.TypeConversion;
using log4net;
+ using ObjectCloner.Extensions;
using SixLabors.ImageSharp;
///
/// Generic methods for reading and writing Csv file.
- /// .
/// *** DO NOT CHANGE THIS CLASS UNLESS INSTRUCTED TOO ***.
///
public static class Csv
{
private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
-#pragma warning disable IDE0032 // Use auto property
- private static readonly CsvConfiguration InternalConfig =
- new CsvConfiguration(CultureInfo.InvariantCulture)
- {
- // change the defaults here if you want
- HasHeaderRecord = true,
-
- // acoustic workbench used to output faulty data with padded headers
- PrepareHeaderForMatch = (x, i) => x.Trim(),
-
- // ensure we always use InvariantCulture - only reliable way to serialize data
- // Additionally R can parse invariant representations of Double.Infinity and
- // Double.NaN (whereas it can't in other cultures).
- CultureInfo = CultureInfo.InvariantCulture,
- };
-#pragma warning restore IDE0032 // Use auto property
-
- static Csv()
+ private static readonly Lazy> MapsInAssembly = new(() =>
{
- Initialize();
- }
-
- private static void Initialize()
+ // initialize and store
+ return Meta.GetTypesFromQutAssemblies().Select(ti => Activator.CreateInstance(ti)).Cast();
+ });
+
+ // ensure we always use InvariantCulture - only reliable way to serialize data
+ // Additionally R can parse invariant representations of Double.Infinity and
+ // Double.NaN (whereas it can't in other cultures).
+ public static CsvConfiguration DefaultConfiguration { get; } = new CsvConfiguration(CultureInfo.InvariantCulture)
{
- // Registers CsvHelper type converters that can allow serialization of complex types.
- InternalConfig.TypeConverterCache.AddConverter>(new CsvSetPointConverter());
+ // change the defaults here if you want
+ HasHeaderRecord = true,
- // ensure dates are always formatted as ISO8601 dates - note: R cannot by default parse proper ISO8601 dates
- var typeConverterOptions = new TypeConverterOptions()
- {
- DateTimeStyle = DateTimeStyles.RoundtripKind,
- Formats = "o".AsArray(),
- };
- InternalConfig.TypeConverterOptionsCache.AddOptions(typeConverterOptions);
- InternalConfig.TypeConverterOptionsCache.AddOptions(typeConverterOptions);
-
- // Find all of our custom class maps
- foreach (var classMapType in Meta.GetTypesFromQutAssemblies())
- {
- // initialize and store
- var instance = Activator.CreateInstance(classMapType) as ClassMap;
- InternalConfig.RegisterClassMap(instance);
- }
- }
-
- public static CsvConfiguration DefaultConfiguration => InternalConfig;
+ // acoustic workbench used to output faulty data with padded headers
+ PrepareHeaderForMatch = x => x.Header.Trim(),
+ };
///
/// Serialize results to CSV - if you want the concrete type to be serialized you need to ensure
@@ -91,11 +62,24 @@ public static void WriteToCsv(FileInfo destination, IEnumerable results)
Contract.Requires(destination != null);
// using CSV Helper
- using (var stream = destination.CreateText())
- {
- var writer = new CsvWriter(stream, DefaultConfiguration);
- writer.WriteRecords(results);
- }
+ using var stream = destination.CreateText();
+ WriteToCsv(stream, results);
+ }
+
+ ///
+ /// Serialize results to CSV - if you want the concrete type to be serialized you need to ensure
+ /// it is downcast before using this method.
+ ///
+ /// The type to serialize.
+ /// The data to serialize.
+ public static void WriteToCsv(TextWriter stream, IEnumerable results)
+ {
+ Contract.Requires(stream != null);
+
+ var writer = GetWriter(stream);
+
+ writer.WriteRecords(results);
+ writer.Flush();
}
///
@@ -134,14 +118,130 @@ public static IEnumerable ReadFromCsv(
}
}
+ public static void WriteMatrixToCsv(FileInfo destination, T[,] matrix, TwoDimensionalArray dimensionality = TwoDimensionalArray.None)
+ {
+ Contract.Requires(destination != null);
+
+ // not tested!
+ using (var stream = destination.CreateText())
+ {
+ var writer = GetWriter(stream);
+
+ var transformedMatrix = new TwoDimArrayMapper(matrix, dimensionality);
+
+ EncodeMatrixInner(writer, transformedMatrix, true);
+ }
+ }
+
+ public static T[,] ReadMatrixFromCsv(FileInfo source, TwoDimensionalArray transform = TwoDimensionalArray.None)
+ {
+ Contract.Requires(source != null);
+
+ using (var stream = source.OpenText())
+ {
+ var reader = GetReader(stream);
+
+ return reader.DecodeMatrix(transform, true);
+ }
+ }
+
+ public static void WriteMatrixToCsv(FileInfo destination, IEnumerable matrix)
+ {
+ Contract.Requires(destination != null);
+
+ // not tested!
+ using (var stream = destination.CreateText())
+ {
+ var writer = GetWriter(stream);
+
+ var transformedMatrix = new EnumerableMapper(matrix);
+
+ EncodeMatrixInner(writer, transformedMatrix, true);
+ }
+ }
+
+ public static IEnumerable ReadMatrixFromCsvAsEnumerable(FileInfo source)
+ {
+ Contract.Requires(source != null);
+
+ // not tested!
+ List matrix;
+ using (var stream = new StreamReader(source.FullName))
+ {
+ var reader = GetReader(stream);
+
+ matrix = reader.DecodeMatrix(true, out var rowCount, out var columnCount);
+ }
+
+ return matrix;
+ }
+
+ public static void WriteMatrixToCsv(FileInfo destination, IEnumerable matrix, Func selector)
+ {
+ Contract.Requires(destination != null);
+
+ using (var stream = destination.CreateText())
+ {
+ var writer = GetWriter(stream);
+
+ var transformedMatrix = new ObjectArrayMapper(matrix, selector);
+
+ EncodeMatrixInner(writer, transformedMatrix, true);
+ }
+ }
+
+ internal static CsvReader GetReader(TextReader stream, Action modifyConfig = null)
+ {
+ var config = DefaultConfiguration;
+ if (modifyConfig is not null)
+ {
+ config = config.DeepClone();
+ modifyConfig(config);
+ }
+
+ var reader = new CsvReader(stream, config);
+ ApplyConverters(reader.Context);
+ return reader;
+ }
+
+ internal static CsvWriter GetWriter(TextWriter stream)
+ {
+ var writer = new CsvWriter(stream, DefaultConfiguration);
+ ApplyConverters(writer.Context);
+ return writer;
+ }
+
+ private static void ApplyConverters(CsvContext context)
+ {
+ // Registers CsvHelper type converters that can allow serialization of complex types.
+ context.TypeConverterCache.AddConverter>(new CsvSetPointConverter());
+ context.TypeConverterCache.AddConverter(typeof(Interval), new CsvIntervalConverter());
+
+ // ensure dates are always formatted as ISO8601 dates - note: R cannot by default parse proper ISO8601 dates
+ var typeConverterOptions = new TypeConverterOptions()
+ {
+ DateTimeStyle = DateTimeStyles.RoundtripKind,
+ Formats = "o".AsArray(),
+ };
+ context.TypeConverterOptionsCache.AddOptions(typeConverterOptions);
+ context.TypeConverterOptionsCache.AddOptions(typeConverterOptions);
+
+ // Find all of our custom class maps
+ foreach (var classMap in MapsInAssembly.Value)
+ {
+ context.RegisterClassMap(classMap);
+ }
+ }
+
private static IEnumerable ReadFromCsv(Action readerHook, TextReader stream, bool throwOnMissingField = true)
{
try
{
- var configuration = DefaultConfiguration;
- configuration.MissingFieldFound = throwOnMissingField ? ConfigurationFunctions.MissingFieldFound : (Action)null;
- configuration.HeaderValidated = throwOnMissingField ? ConfigurationFunctions.HeaderValidated : (Action)null;
- var reader = new CsvReader(stream, configuration);
+ var reader = GetReader(stream, (configuration) =>
+ {
+ configuration.MissingFieldFound = throwOnMissingField ? ConfigurationFunctions.MissingFieldFound : (_) => { };
+ configuration.HeaderValidated = throwOnMissingField ? ConfigurationFunctions.HeaderValidated : (_) => { };
+ });
IEnumerable results = reader.GetRecords();
@@ -156,13 +256,14 @@ private static IEnumerable ReadFromCsv(Action readerHook, TextR
Log.Debug($"Error doing type conversion - dictionary contains {tce.Data.Count} entries. The error was: `{tce.Message}`");
// The CsvHelper exception messages are unhelpful... let us fix this
- if (tce.ReadingContext != null)
+ if (tce.Context != null)
{
var parserData = $@"
-Row: {tce.ReadingContext.Row}
-Column: {tce.ReadingContext.CurrentIndex}
-Field Name: {tce.ReadingContext.Field}
+Row: {tce.Context.Parser.Row}
+Column: {tce.Context.Reader.CurrentIndex}
+Column Name: {tce.Context.Reader.HeaderRecord[tce.Context.Reader.CurrentIndex]}
Member Name: {tce.MemberMapData.Member.Name}
+Member Value: {tce.Text}
";
var newMessage = tce.Message + Environment.NewLine + parserData;
@@ -188,7 +289,6 @@ private static void EncodeMatrixInner(this CsvWriter writer, MatrixMapper
writer.WriteField("c" + i.ToString("000000"));
}
- writer.Context.HasHeaderBeenWritten = true;
writer.NextRecord();
// write rows
@@ -204,8 +304,7 @@ private static void EncodeMatrixInner(this CsvWriter writer, MatrixMapper
}
}
- private static List DecodeMatrix(this CsvReader reader, bool includeRowIndex, out int rowCount,
- out int columnCount)
+ private static List DecodeMatrix(this CsvReader reader, bool includeRowIndex, out int rowCount, out int columnCount)
{
// read header
if (!reader.Read())
@@ -220,7 +319,7 @@ private static List DecodeMatrix(this CsvReader reader, bool includeRowI
throw new CsvHelperException(reader.Context, "Could not read headers");
}
- var headers = reader.Context.HeaderRecord;
+ var headers = reader.Context.Reader.HeaderRecord;
if (includeRowIndex && headers[0] != "Index")
{
throw new CsvHelperException(reader.Context, "Expected an index header and there was none");
@@ -254,8 +353,7 @@ private static List DecodeMatrix(this CsvReader reader, bool includeRowI
return csvRows;
}
- private static T[,] DecodeMatrix(this CsvReader reader, TwoDimensionalArray dimensionality,
- bool includeRowIndex)
+ private static T[,] DecodeMatrix(this CsvReader reader, TwoDimensionalArray dimensionality, bool includeRowIndex)
{
var csvRows = DecodeMatrix(reader, includeRowIndex, out var rowCount, out var columnCount);
@@ -295,77 +393,5 @@ private static List DecodeMatrix(this CsvReader reader, bool includeRowI
return result;
}
-
- public static void WriteMatrixToCsv(FileInfo destination, T[,] matrix, TwoDimensionalArray dimensionality = TwoDimensionalArray.None)
- {
- Contract.Requires(destination != null);
-
- // not tested!
- using (var stream = destination.CreateText())
- {
- var writer = new CsvWriter(stream, DefaultConfiguration);
-
- var transformedMatrix = new TwoDimArrayMapper(matrix, dimensionality);
-
- EncodeMatrixInner(writer, transformedMatrix, true);
- }
- }
-
- public static T[,] ReadMatrixFromCsv(FileInfo source, TwoDimensionalArray transform = TwoDimensionalArray.None)
- {
- Contract.Requires(source != null);
-
- using (var stream = source.OpenText())
- {
- var reader = new CsvReader(stream, DefaultConfiguration);
-
- return reader.DecodeMatrix(transform, true);
- }
- }
-
- public static void WriteMatrixToCsv(FileInfo destination, IEnumerable matrix)
- {
- Contract.Requires(destination != null);
-
- // not tested!
- using (var stream = destination.CreateText())
- {
- var writer = new CsvWriter(stream, DefaultConfiguration);
-
- var transformedMatrix = new EnumerableMapper(matrix);
-
- EncodeMatrixInner(writer, transformedMatrix, true);
- }
- }
-
- public static IEnumerable ReadMatrixFromCsvAsEnumerable(FileInfo source)
- {
- Contract.Requires(source != null);
-
- // not tested!
- List matrix;
- using (var stream = new StreamReader(source.FullName))
- {
- var reader = new CsvReader(stream, DefaultConfiguration);
-
- matrix = reader.DecodeMatrix(true, out var rowCount, out var columnCount);
- }
-
- return matrix;
- }
-
- public static void WriteMatrixToCsv(FileInfo destination, IEnumerable matrix, Func selector)
- {
- Contract.Requires(destination != null);
-
- using (var stream = destination.CreateText())
- {
- var writer = new CsvWriter(stream, DefaultConfiguration);
-
- var transformedMatrix = new ObjectArrayMapper(matrix, selector);
-
- EncodeMatrixInner(writer, transformedMatrix, true);
- }
- }
}
}
\ No newline at end of file
diff --git a/src/Acoustics.Shared/Csv/CsvIntervalConverter.cs b/src/Acoustics.Shared/Csv/CsvIntervalConverter.cs
new file mode 100644
index 000000000..27f78452d
--- /dev/null
+++ b/src/Acoustics.Shared/Csv/CsvIntervalConverter.cs
@@ -0,0 +1,36 @@
+//
+// All code in this file and all associated files are the copyright and property of the QUT Ecoacoustics Research Group (formerly MQUTeR, and formerly QUT Bioacoustics Research Group).
+//
+
+namespace Acoustics.Shared.Csv
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+ using System.Threading.Tasks;
+ using Acoustics.Shared.Contracts;
+ using CsvHelper;
+ using CsvHelper.Configuration;
+ using CsvHelper.TypeConversion;
+
+ // reference implementation: https://github.com/JoshClose/CsvHelper/blob/3b14b70fd1e9ce742375fbb116799b19fd0e7ccd/src/CsvHelper/TypeConversion/DoubleConverter.cs
+ public class CsvIntervalConverter : ITypeConverter
+ {
+ public object ConvertFromString(string text, IReaderRow row, MemberMapData memberMapData)
+ {
+ throw new NotImplementedException();
+ }
+
+ public string ConvertToString(object value, IWriterRow row, MemberMapData memberMapData)
+ {
+ if (value is Interval d)
+ {
+ var doubleOptions = row?.Context?.TypeConverterOptionsCache?.GetOptions();
+ return d.ToString(suppressName: true, doubleOptions?.Formats?.FirstOrDefault(), doubleOptions?.CultureInfo);
+ }
+
+ throw new InvalidOperationException("Cannot convert interval that is not have the generic type double");
+ }
+ }
+}
diff --git a/src/Acoustics.Shared/Csv/ISetPointConverter.cs b/src/Acoustics.Shared/Csv/CsvSetPointConverter.cs
similarity index 97%
rename from src/Acoustics.Shared/Csv/ISetPointConverter.cs
rename to src/Acoustics.Shared/Csv/CsvSetPointConverter.cs
index 59a0bed40..4eed3ba12 100644
--- a/src/Acoustics.Shared/Csv/ISetPointConverter.cs
+++ b/src/Acoustics.Shared/Csv/CsvSetPointConverter.cs
@@ -1,5 +1,5 @@
// --------------------------------------------------------------------------------------------------------------------
-//
+//
// All code in this file and all associated files are the copyright and property of the QUT Ecoacoustics Research Group (formerly MQUTeR, and formerly QUT Bioacoustics Research Group).
//
//
diff --git a/src/Acoustics.Shared/Extensions/IntervalExtensions.cs b/src/Acoustics.Shared/Extensions/IntervalExtensions.cs
index b2a7cea80..ffa60eec6 100644
--- a/src/Acoustics.Shared/Extensions/IntervalExtensions.cs
+++ b/src/Acoustics.Shared/Extensions/IntervalExtensions.cs
@@ -227,19 +227,19 @@ public static double ClampvValue(this Interval range, double value)
}
public static Interval AsInterval(this (T Minimum, T Maximum) pair, Topology topology = Topology.Default)
- where T : struct, IComparable
+ where T : struct, IComparable, IFormattable
{
return new Interval(pair.Minimum, pair.Maximum, topology);
}
public static Interval AsIntervalTo(this T minimum, T maximum, Topology topology = Topology.Default)
- where T : struct, IComparable
+ where T : struct, IComparable, IFormattable
{
return new Interval(minimum, maximum, topology);
}
public static Interval AsIntervalFromZero(this T maximum, Topology topology = Topology.Default)
- where T : struct, IComparable
+ where T : struct, IComparable, IFormattable
{
return new Interval(default, maximum, topology);
}
diff --git a/src/Acoustics.Shared/Extensions/ObjectExtensions.cs b/src/Acoustics.Shared/Extensions/ObjectExtensions.cs
index 9b1e02fe7..e678ae23c 100644
--- a/src/Acoustics.Shared/Extensions/ObjectExtensions.cs
+++ b/src/Acoustics.Shared/Extensions/ObjectExtensions.cs
@@ -29,5 +29,10 @@ public static List AsList(this T item)
{
return new List { item };
}
+
+ public static IEnumerable Wrap(this T item)
+ {
+ yield return item;
+ }
}
}
\ No newline at end of file
diff --git a/src/Acoustics.Shared/Interval.cs b/src/Acoustics.Shared/Interval.cs
index 3ea86442c..9a7be558d 100644
--- a/src/Acoustics.Shared/Interval.cs
+++ b/src/Acoustics.Shared/Interval.cs
@@ -11,6 +11,7 @@ namespace Acoustics.Shared
{
using System;
using System.Diagnostics.CodeAnalysis;
+ using System.Globalization;
public enum Topology : byte
{
@@ -44,7 +45,7 @@ public enum Topology : byte
/// The type used to represent the points in this interval.
///
public readonly struct Interval : IEquatable>, IComparable>
- where T : struct, IComparable
+ where T : struct, IComparable, IFormattable
{
public Interval(T minimum, T maximum)
{
@@ -266,12 +267,15 @@ public override string ToString()
///
/// String representation.
///
- public string ToString(bool suppressName)
+ public string ToString(bool suppressName, string tFormat = "R", CultureInfo culture = null)
{
+ culture = culture ?? CultureInfo.InvariantCulture;
var left = this.IsMinimumInclusive ? "[" : "(";
var right = this.IsMaximumInclusive ? "]" : ")";
+ var min = this.Minimum.ToString(tFormat, culture);
+ var max = this.Maximum.ToString(tFormat, culture);
var name = suppressName ? string.Empty : nameof(Interval) + ": ";
- return $"{name}{left}{this.Minimum}, {this.Maximum}{right}";
+ return $"{name}{left}{min}, {max}{right}";
}
public int CompareTo(Interval other)
diff --git a/src/TowseyLibrary/ImageTools.cs b/src/TowseyLibrary/ImageTools.cs
index f9828d83b..bbcaf8f74 100644
--- a/src/TowseyLibrary/ImageTools.cs
+++ b/src/TowseyLibrary/ImageTools.cs
@@ -3799,8 +3799,8 @@ public static Image CombineImagesInLine(params Image[] images)
public static Image CombineImagesInLine(Color fill, params Image[] images)
where T : unmanaged, IPixel
{
- var maxHeight = images.Max(i => i.Height);
- var totalWidth = images.Sum(i => i.Width);
+ var maxHeight = images.Max(i => i?.Height ?? 0);
+ var totalWidth = images.Sum(i => i?.Width ?? 0);
var composite = Drawing.NewImage(totalWidth, maxHeight, fill);
int xOffset = 0;
diff --git a/tests/Acoustics.Test/AnalysisPrograms/Recognizers/AustBitternTests.cs b/tests/Acoustics.Test/AnalysisPrograms/Recognizers/AustBitternTests.cs
index ad43a49b9..7d6079f70 100644
--- a/tests/Acoustics.Test/AnalysisPrograms/Recognizers/AustBitternTests.cs
+++ b/tests/Acoustics.Test/AnalysisPrograms/Recognizers/AustBitternTests.cs
@@ -33,6 +33,7 @@ public class AustBitternTests : OutputDirectoryTest
// as in the TestRecognizer() method below.
[TestMethod]
+ [Ignore("Recogniser removed temporarily")]
public void TestRecognizer()
{
var config = Recognizer.ParseConfig(ConfigFile);
diff --git a/tests/Acoustics.Test/Shared/CsvTests.cs b/tests/Acoustics.Test/Shared/CsvTests.cs
index 4126de228..7f70a862f 100644
--- a/tests/Acoustics.Test/Shared/CsvTests.cs
+++ b/tests/Acoustics.Test/Shared/CsvTests.cs
@@ -258,7 +258,7 @@ public void TestThatCsvDeserializerGivesHumanFriendlyErrors()
//Assert.IsNotNull(actual.InnerException);
StringAssert.Contains(actual.Message, "Row");
- StringAssert.Contains(actual.Message, "Field Name");
+ StringAssert.Contains(actual.Message, "Column Name");
}
[TestMethod]
@@ -274,7 +274,9 @@ public void ReaderHookIsExposed()
string[] headers = null;
var result = Csv.ReadFromCsv(file, false, (reader) =>
{
- headers = reader.Context.HeaderRecord;
+#pragma warning disable RS0030 // Do not used banned APIs
+ headers = reader.Context.Reader.HeaderRecord;
+#pragma warning restore RS0030 // Do not used banned APIs
});
var expected = new[] { "SomeNumber", "SomeTimeSpan", "A", "B", "C", "D" };
@@ -304,11 +306,26 @@ public void TestCsvClassMapsAreAutomaticallyRegistered()
partialExpected.Select(x => x.Item2).ToArray(),
actual);
- foreach (var (type, classMapType) in partialExpected)
+ using var reader = Csv.GetReader(new StringReader("hello"));
+ using var writer = Csv.GetWriter(new StringWriter());
+
+ // contexts should be unique
+#pragma warning disable RS0030 // Do not used banned APIs
+ Assert.AreNotEqual(reader.Context, writer.Context);
+
+ // type converters are registered
+ CheckConverters(reader.Context);
+ CheckConverters(writer.Context);
+#pragma warning restore RS0030 // Do not used banned APIs
+
+ void CheckConverters(CsvContext context)
{
- var mapping = Csv.DefaultConfiguration.Maps[type];
- Assert.IsNotNull(mapping, $"Mapping for type {type} was null");
- Assert.AreEqual(classMapType, mapping.GetType());
+ foreach (var (type, classMapType) in partialExpected)
+ {
+ var mapping = context.Maps[type];
+ Assert.IsNotNull(mapping, $"Mapping for type {type} was null");
+ Assert.AreEqual(classMapType, mapping.GetType());
+ }
}
}
@@ -318,11 +335,9 @@ public void TestAcousticEventClassMap()
var ae = new AcousticEvent();
var result = new StringBuilder();
- using (var str = new StringWriter(result))
+ using (var stream = new StringWriter(result))
{
- var writer = new CsvWriter(str, Csv.DefaultConfiguration);
-
- writer.WriteRecords(records: new[] { ae });
+ Csv.WriteToCsv(stream, ae.Wrap());
}
var actual = result.ToString();
@@ -393,8 +408,8 @@ public void TestBaseTypesAreNotSerializedAsArray()
public void TestChildTypesAreSerializedWhenWrappedAsEnumerableParentType()
{
var exampleIndices = new SummaryIndexValues();
- IEnumerable childArray = exampleIndices.AsArray().AsEnumerable();
- IEnumerable baseArray = exampleIndices.AsArray().AsEnumerable();
+ IEnumerable childArray = exampleIndices.Wrap();
+ IEnumerable baseArray = exampleIndices.Wrap();
Csv.WriteToCsv(this.testFile, childArray);
@@ -471,6 +486,28 @@ public void TestInvariantCultureIsUsedMatrix()
Assert.AreEqual(expected, actual);
}
+ [TestMethod]
+ public void EnumsAreConvertible()
+ {
+ var storage = new StringWriter();
+ using (var stream = storage) {
+ Csv.WriteToCsv(stream, new { Property = Topology.Closed }.Wrap());
+ }
+
+ Assert.AreEqual("Property\r\nInclusive\r\n", storage.ToString());
+ }
+
+ [TestMethod]
+ public void IntervalHasATypeConverter()
+ {
+ var storage = new StringWriter();
+ using (var stream = storage) {
+ Csv.WriteToCsv(stream, new { Property = new Interval(0.5,3) }.Wrap());
+ }
+
+ Assert.AreEqual("Property\r\n\"[0.5, 3)\"\r\n", storage.ToString());
+ }
+
private static void AssertCsvEqual(string expected, FileInfo actual)
{
var lines = File.ReadAllText(actual.FullName);