From cf036d8283e3bba2d3a98e2af986228812f11eda Mon Sep 17 00:00:00 2001 From: Nathan Baulch Date: Sun, 23 Jan 2022 11:13:53 +1100 Subject: [PATCH 1/3] Initial unit test harness --- .../AsciiChart.Sharp.TestApp.csproj | 2 +- .../AsciiChart.Sharp.Tests.csproj | 16 ++ AsciiChart.Sharp.Tests/Tests.cs | 190 ++++++++++++++++++ AsciiChart.Sharp.sln | 6 + 4 files changed, 213 insertions(+), 1 deletion(-) create mode 100644 AsciiChart.Sharp.Tests/AsciiChart.Sharp.Tests.csproj create mode 100644 AsciiChart.Sharp.Tests/Tests.cs diff --git a/AsciiChart.Sharp.TestApp/AsciiChart.Sharp.TestApp.csproj b/AsciiChart.Sharp.TestApp/AsciiChart.Sharp.TestApp.csproj index 580218f..b09dfe4 100644 --- a/AsciiChart.Sharp.TestApp/AsciiChart.Sharp.TestApp.csproj +++ b/AsciiChart.Sharp.TestApp/AsciiChart.Sharp.TestApp.csproj @@ -2,7 +2,7 @@ Exe - netcoreapp2.1 + netcoreapp3.1 diff --git a/AsciiChart.Sharp.Tests/AsciiChart.Sharp.Tests.csproj b/AsciiChart.Sharp.Tests/AsciiChart.Sharp.Tests.csproj new file mode 100644 index 0000000..7ebd6a3 --- /dev/null +++ b/AsciiChart.Sharp.Tests/AsciiChart.Sharp.Tests.csproj @@ -0,0 +1,16 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + diff --git a/AsciiChart.Sharp.Tests/Tests.cs b/AsciiChart.Sharp.Tests/Tests.cs new file mode 100644 index 0000000..b506749 --- /dev/null +++ b/AsciiChart.Sharp.Tests/Tests.cs @@ -0,0 +1,190 @@ +using System; +using System.Text.RegularExpressions; +using NUnit.Framework; + +namespace AsciiChart.Sharp.Tests +{ + public class Tests + { + private static readonly Regex _normalize = new Regex(@" *(\r\n|\n\r|\n|\r)", RegexOptions.Compiled); + + [Test] + [TestCaseSource(nameof(Cases))] + public void TestPlot(double[] series, Options opts, string expected) + { + var actual = AsciiChart.Plot(series, opts); + Assert.AreEqual(Normalize(expected), Normalize(actual)); + } + + private static string Normalize(string text) => _normalize.Replace(text.Trim('\r', '\n') + Environment.NewLine, Environment.NewLine); + + private static object[][] Cases = + { + new object[] + { + new double[] { 2, 1, 1, 2, -2, 5, 7, 11, 3, 7, 1 }, null, @" + 11.00 ┤ ╭╮ + 10.00 ┤ ││ + 9.00 ┤ ││ + 8.00 ┤ ││ + 7.00 ┤ ╭╯│╭╮ + 6.00 ┤ │ │││ + 5.00 ┤ ╭╯ │││ + 4.00 ┤ │ │││ + 3.00 ┤ │ ╰╯│ + 2.00 ┼╮ ╭╮│ │ + 1.00 ┤╰─╯││ ╰ + 0.00 ┼ ││ + -1.00 ┤ ││ + -2.00 ┤ ╰╯ " + }, + new object[] + { + new double[] { 2, 1, 1, 2, -2, 5, 7, 11, 3, 7, 4, 5, 6, 9, 4, 0, 6, 1, 5, 3, 6, 2 }, null, @" + 11.00 ┤ ╭╮ + 10.00 ┤ ││ + 9.00 ┤ ││ ╭╮ + 8.00 ┤ ││ ││ + 7.00 ┤ ╭╯│╭╮ ││ + 6.00 ┤ │ │││ ╭╯│ ╭╮ ╭╮ + 5.00 ┤ ╭╯ │││╭╯ │ ││╭╮││ + 4.00 ┤ │ ││╰╯ ╰╮││││││ + 3.00 ┤ │ ╰╯ ││││╰╯│ + 2.00 ┼╮ ╭╮│ ││││ ╰ + 1.00 ┤╰─╯││ ││╰╯ + 0.00 ┼ ││ ╰╯ + -1.00 ┤ ││ + -2.00 ┤ ╰╯ " + }, + new object[] + { + new[] { 0.2, 0.1, 0.2, 2, -0.9, 0.7, 0.91, 0.3, 0.7, 0.4, 0.5 }, null, @" + 2.00 ┤ ╭╮ + 1.03 ┤ ││╭─╮╭╮╭ + 0.07 ┼──╯││ ╰╯╰╯ + -0.90 ┤ ╰╯ " + }, + new object[] + { + new double[] { 2, 1, 1, 2, -2, 5, 7, 11, 3, 7, 1 }, new Options { Height = 4 }, @" + 11.00 ┤ ╭╮ + 7.75 ┤ ╭─╯│╭╮ + 4.50 ┼╮ ╭╮│ ╰╯│ + 1.25 ┤╰─╯││ ╰ + -2.00 ┤ ╰╯ " + }, + new object[] + { + new[] { 0.453, 0.141, 0.951, 0.251, 0.223, 0.581, 0.771, 0.191, 0.393, 0.617, 0.478 }, new Options { Height = 8 }, @" + 0.95 ┤ ╭╮ + 0.85 ┤ ││ ╭╮ + 0.75 ┤ ││ ││ + 0.65 ┤ ││ ╭╯│ ╭╮ + 0.55 ┤ ││ │ │ │╰ + 0.44 ┼╮││ │ │╭╯ + 0.34 ┤│││ │ ││ + 0.24 ┤││╰─╯ ╰╯ + 0.14 ┤╰╯ " + }, + new object[] + { + new[] { 0.01, 0.004, 0.003, 0.0042, 0.0083, 0.0033, 0.0079 }, new Options { AxisLabelFormat = "0.000", Height = 7 }, @" + 0.010 ┼╮ + 0.009 ┤│ + 0.008 ┤│ ╭╮╭ + 0.007 ┤│ │││ + 0.006 ┤│ │││ + 0.005 ┤│ │││ + 0.004 ┤╰╮╭╯││ + 0.003 ┤ ╰╯ ╰╯ " + }, + new object[] + { + new double[] { 192, 431, 112, 449, -122, 375, 782, 123, 911, 1711, 172 }, new Options { AxisLabelFormat = "0", Height = 10 }, @" + 1711 ┤ ╭╮ + 1528 ┤ ││ + 1344 ┤ ││ + 1161 ┤ ││ + 978 ┤ ╭╯│ + 795 ┤ ╭╮│ │ + 611 ┤ │││ │ + 428 ┤╭╮╭╮╭╯││ │ + 245 ┼╯╰╯││ ╰╯ ╰ + 61 ┤ ││ + -122 ┤ ╰╯ " + }, + new object[] + { + new[] + { + 0, 0, 0, 0, 1.5, 0, 0, -0.5, 9, -3, 0, 0, 1, 2, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 1.5, 0, 0, -0.5, 8, -3, 0, 0, 1, 2, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 1.5, 0, 0, -0.5, 10, -3, 0, 0, 1, 2, 1, 0, 0, 0, 0 + }, + new Options { AxisLabelRightMargin = 4, Height = 10 }, + @" + 10.00 ┤ ╭╮ + 8.70 ┤ ╭╮ ││ + 7.40 ┤ ││ ╭╮ ││ + 6.10 ┤ ││ ││ ││ + 4.80 ┤ ││ ││ ││ + 3.50 ┤ ││ ││ ││ + 2.20 ┤ ││ ╭╮ ││ ╭╮ ││ ╭╮ + 0.90 ┤ ╭╮ ││ ╭╯╰╮ ╭╮ ││ ╭╯╰╮ ╭╮ ││ ╭╯╰╮ + -0.40 ┼───╯╰──╯│╭─╯ ╰───────╯╰──╯│╭─╯ ╰───────╯╰──╯│╭─╯ ╰─── + -1.70 ┤ ││ ││ ││ + -3.00 ┤ ╰╯ ╰╯ ╰╯ " + }, + new object[] + { + new double[] { -5, -2, -3, -4, 0, -5, -6, -7, -8, 0, -9, -3, -5, -2, -9, -3, -1 }, null, @" + 0.00 ┼ ╭╮ ╭╮ + -1.00 ┤ ││ ││ ╭ + -2.00 ┤╭╮ ││ ││ ╭╮ │ + -3.00 ┤│╰╮││ ││╭╮││╭╯ + -4.00 ┤│ ╰╯│ │││││││ + -5.00 ┼╯ ╰╮ │││╰╯││ + -6.00 ┤ ╰╮ │││ ││ + -7.00 ┤ ╰╮│││ ││ + -8.00 ┤ ╰╯││ ││ + -9.00 ┤ ╰╯ ╰╯ " + }, + new object[] + { + new[] + { + 57.76, 54.04, 56.31, 57.02, 59.5, 52.63, 52.97, 56.44, 56.75, 52.96, 55.54, 55.09, 58.22, 56.85, 60.61, 59.62, 59.73, 59.93, 56.3, 54.69, 55.32, 54.03, 50.98, 50.48, 54.55, 47.49, + 55.3, 46.74, 46, 45.8, 49.6, 48.83, 47.64, 46.61, 54.72, 42.77, 50.3, 42.79, 41.84, 44.19, 43.36, 45.62, 45.09, 44.95, 50.36, 47.21, 47.77, 52.04, 47.46, 44.19, 47.22, 45.55, + 40.65, 39.64, 37.26, 40.71, 42.15, 36.45, 39.14, 36.62 + }, + new Options { Height = 24 }, + @" + 60.61 ┤ ╭╮ ╭╮ + 59.60 ┤ ╭╮ │╰─╯│ + 58.60 ┤ ││ ╭╮│ │ + 57.59 ┼╮ ╭╯│ │││ │ + 56.58 ┤│╭╯ │ ╭─╮ │╰╯ ╰╮ + 55.58 ┤││ │ │ │╭─╯ │╭╮ ╭╮ + 54.57 ┤╰╯ │ │ ││ ╰╯╰╮ ╭╮││ ╭╮ + 53.56 ┤ │╭╯ ╰╯ │ ││││ ││ + 52.56 ┤ ╰╯ │ ││││ ││ ╭╮ + 51.55 ┤ ╰╮││││ ││ ││ + 50.54 ┤ ╰╯│││ ││╭╮ ╭╮ ││ + 49.54 ┤ │││ ╭─╮ ││││ ││ ││ + 48.53 ┤ │││ │ │ ││││ ││ ││ + 47.52 ┤ ╰╯│ │ ╰╮││││ │╰─╯╰╮╭╮ + 46.52 ┤ ╰─╮│ ╰╯│││ │ │││ + 45.51 ┤ ╰╯ │││ ╭──╯ ││╰╮ + 44.50 ┤ │││ ╭╮│ ╰╯ │ + 43.50 ┤ ││╰╮│╰╯ │ + 42.49 ┤ ╰╯ ╰╯ │ ╭╮ + 41.48 ┤ │ ││ + 40.48 ┤ ╰╮ ╭╯│ + 39.47 ┤ ╰╮│ │╭╮ + 38.46 ┤ ││ │││ + 37.46 ┤ ╰╯ │││ + 36.45 ┤ ╰╯╰ " + } + }; + } +} \ No newline at end of file diff --git a/AsciiChart.Sharp.sln b/AsciiChart.Sharp.sln index 5256e9f..1504fd9 100644 --- a/AsciiChart.Sharp.sln +++ b/AsciiChart.Sharp.sln @@ -7,6 +7,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AsciiChart.Sharp", "AsciiCh EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AsciiChart.Sharp.TestApp", "AsciiChart.Sharp.TestApp\AsciiChart.Sharp.TestApp.csproj", "{F0FE0917-C3FF-4B1D-9143-43850116EDFD}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AsciiChart.Sharp.Tests", "AsciiChart.Sharp.Tests\AsciiChart.Sharp.Tests.csproj", "{D4D2802B-1207-43AA-8678-4F489A0BE698}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -21,6 +23,10 @@ Global {F0FE0917-C3FF-4B1D-9143-43850116EDFD}.Debug|Any CPU.Build.0 = Debug|Any CPU {F0FE0917-C3FF-4B1D-9143-43850116EDFD}.Release|Any CPU.ActiveCfg = Release|Any CPU {F0FE0917-C3FF-4B1D-9143-43850116EDFD}.Release|Any CPU.Build.0 = Release|Any CPU + {D4D2802B-1207-43AA-8678-4F489A0BE698}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D4D2802B-1207-43AA-8678-4F489A0BE698}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D4D2802B-1207-43AA-8678-4F489A0BE698}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D4D2802B-1207-43AA-8678-4F489A0BE698}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From 2e9545427ccefe6431b9009122e2b9400642667b Mon Sep 17 00:00:00 2001 From: Nathan Baulch Date: Sun, 23 Jan 2022 11:27:58 +1100 Subject: [PATCH 2/3] Remove extra y-axis tick at zero --- AsciiChart.Sharp.Tests/Tests.cs | 6 +++--- AsciiChart.Sharp/AsciiChart.cs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/AsciiChart.Sharp.Tests/Tests.cs b/AsciiChart.Sharp.Tests/Tests.cs index b506749..19914f4 100644 --- a/AsciiChart.Sharp.Tests/Tests.cs +++ b/AsciiChart.Sharp.Tests/Tests.cs @@ -34,7 +34,7 @@ public void TestPlot(double[] series, Options opts, string expected) 3.00 ┤ │ ╰╯│ 2.00 ┼╮ ╭╮│ │ 1.00 ┤╰─╯││ ╰ - 0.00 ┼ ││ + 0.00 ┤ ││ -1.00 ┤ ││ -2.00 ┤ ╰╯ " }, @@ -52,7 +52,7 @@ public void TestPlot(double[] series, Options opts, string expected) 3.00 ┤ │ ╰╯ ││││╰╯│ 2.00 ┼╮ ╭╮│ ││││ ╰ 1.00 ┤╰─╯││ ││╰╯ - 0.00 ┼ ││ ╰╯ + 0.00 ┤ ││ ╰╯ -1.00 ┤ ││ -2.00 ┤ ╰╯ " }, @@ -138,7 +138,7 @@ public void TestPlot(double[] series, Options opts, string expected) new object[] { new double[] { -5, -2, -3, -4, 0, -5, -6, -7, -8, 0, -9, -3, -5, -2, -9, -3, -1 }, null, @" - 0.00 ┼ ╭╮ ╭╮ + 0.00 ┤ ╭╮ ╭╮ -1.00 ┤ ││ ││ ╭ -2.00 ┤╭╮ ││ ││ ╭╮ │ -3.00 ┤│╰╮││ ││╭╮││╭╯ diff --git a/AsciiChart.Sharp/AsciiChart.cs b/AsciiChart.Sharp/AsciiChart.cs index 3b2d714..9fda3b7 100644 --- a/AsciiChart.Sharp/AsciiChart.cs +++ b/AsciiChart.Sharp/AsciiChart.cs @@ -112,7 +112,7 @@ static void ApplyYAxisLabels(string[][] resultArray, IReadOnlyList yA for (var i = 0; i < yAxisLabels.Count; i++) { resultArray[i][0] = yAxisLabels[i].Label; - resultArray[i][columnIndexOfFirstDataPoint - 1] = (Math.Abs(yAxisLabels[i].Value) < 0.001) ? "┼" : "┤"; + resultArray[i][columnIndexOfFirstDataPoint - 1] = "┤"; } } From 90ed6e75a7a380d1e7f4760eca156f259434fdf3 Mon Sep 17 00:00:00 2001 From: Nathan Baulch Date: Sun, 23 Jan 2022 11:39:24 +1100 Subject: [PATCH 3/3] Fix when all values are the same --- AsciiChart.Sharp.Tests/Tests.cs | 2 ++ AsciiChart.Sharp/AsciiChart.cs | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/AsciiChart.Sharp.Tests/Tests.cs b/AsciiChart.Sharp.Tests/Tests.cs index 19914f4..6075dea 100644 --- a/AsciiChart.Sharp.Tests/Tests.cs +++ b/AsciiChart.Sharp.Tests/Tests.cs @@ -20,6 +20,8 @@ public void TestPlot(double[] series, Options opts, string expected) private static object[][] Cases = { + new object[] { new double[] { 1, 1, 1, 1, 1 }, null, " 1.00 ┼──── " }, + new object[] { new double[] { 0, 0, 0, 0, 0 }, null, " 0.00 ┼──── " }, new object[] { new double[] { 2, 1, 1, 2, -2, 5, 7, 11, 3, 7, 1 }, null, @" diff --git a/AsciiChart.Sharp/AsciiChart.cs b/AsciiChart.Sharp/AsciiChart.cs index 9fda3b7..0475548 100644 --- a/AsciiChart.Sharp/AsciiChart.cs +++ b/AsciiChart.Sharp/AsciiChart.cs @@ -23,7 +23,7 @@ public static string Plot(IEnumerable series, Options options = null) var max = seriesList.Max(); var range = Math.Abs(max - min); - var ratio = ((options.Height) ?? range) / range; + var ratio = range == 0 ? 0 : (options.Height ?? range) / range; var min2 = Math.Round(min * ratio, MidpointRounding.AwayFromZero); var max2 = Math.Round(max * ratio, MidpointRounding.AwayFromZero); var rows = Math.Abs(max2 - min2); @@ -101,7 +101,7 @@ static IReadOnlyList GetYAxisTicks(double max, double range, double rows var yTicks = new List(); for (var i = 0; i < numberOfTicks; i++) { - yTicks.Add(max - i * range/rows); + yTicks.Add(max - i * (range == rows ? 1 : range/rows)); } return yTicks;