From 3bca0081afca4a4256dbd409166577e0960c8c47 Mon Sep 17 00:00:00 2001 From: CodeConscious <50596087+codeconscious@users.noreply.github.com> Date: Tue, 24 Dec 2024 15:55:07 +0900 Subject: [PATCH 01/12] Add F# library and file --- Startwatch.Library.FSharp/Library.fs | 45 +++++++++++++++++++ .../Startwatch.Library.FSharp.fsproj | 12 +++++ Startwatch.sln | 6 +++ 3 files changed, 63 insertions(+) create mode 100644 Startwatch.Library.FSharp/Library.fs create mode 100644 Startwatch.Library.FSharp/Startwatch.Library.FSharp.fsproj diff --git a/Startwatch.Library.FSharp/Library.fs b/Startwatch.Library.FSharp/Library.fs new file mode 100644 index 0000000..0067ad1 --- /dev/null +++ b/Startwatch.Library.FSharp/Library.fs @@ -0,0 +1,45 @@ +namespace Startwatch.Library.FSharp + +open System +open System.Diagnostics + +module Startwatch = + type Watch() = + let startedAt = Stopwatch.GetTimestamp() + + let elapsedFriendly (startedAt: int64) = + let timeSpan = Stopwatch.GetElapsedTime(startedAt) + match timeSpan with + | t when t.TotalMilliseconds < 1 -> sprintf "%s" (t.TotalNanoseconds.ToString("#,##0ns")) + | t when t.TotalMilliseconds < 1000 -> sprintf "%s" (t.TotalMilliseconds.ToString("#,##0ms")) + | t -> + let days = t.Days + let hours = t.Hours + let mins = t.Minutes + let secs = t.Seconds + + let prependText = if secs = 0 then "exactly " else String.Empty + + let hoursText = + match (days, hours) with + | d, _ when d > 0 -> sprintf "%s" ((hours + (days * 24)).ToString("#,##0h")) + | _, h when h > 0 -> sprintf "%s" ((hours + (days * 24)).ToString("#,##0h")) + | _ -> String.Empty + + let minsText = + match (hours, mins) with + | h, m when h > 0 && m > 0 -> sprintf "%s" (m.ToString("00m")) + | _, m when m > 0 -> sprintf "%s" (m.ToString("0m")) + | _ -> String.Empty + + let secsText = + match (hours, mins, secs) with + | 0, 0, s when s > 0 -> sprintf "%s\\.%ff" (t.ToString("s")) (t.TotalSeconds % 1.0) + | h, _, s when h > 0 && s > 0 -> sprintf "%ss" (t.ToString("ss")) + | _, m, s when m > 0 && s > 0 -> sprintf "%ss" (t.ToString("ss")) + | _, _, s when s > 0 -> sprintf "%ss" (t.ToString("s")) + | _ -> String.Empty + + $"{prependText}{hoursText}{minsText}{secsText}" + + member this.ElapsedFriendly = elapsedFriendly startedAt diff --git a/Startwatch.Library.FSharp/Startwatch.Library.FSharp.fsproj b/Startwatch.Library.FSharp/Startwatch.Library.FSharp.fsproj new file mode 100644 index 0000000..850ca49 --- /dev/null +++ b/Startwatch.Library.FSharp/Startwatch.Library.FSharp.fsproj @@ -0,0 +1,12 @@ + + + + net9.0 + true + + + + + + + diff --git a/Startwatch.sln b/Startwatch.sln index 09434bb..a6c9551 100644 --- a/Startwatch.sln +++ b/Startwatch.sln @@ -7,6 +7,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Startwatch.Library", ".\Sta EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Startwatch.Tests", ".\Startwatch.Tests\Startwatch.Tests.csproj", "{295D954F-453F-4311-8D72-1EFF57261A38}" EndProject +Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Startwatch.Library.FSharp", "Startwatch.Library.FSharp\Startwatch.Library.FSharp.fsproj", "{2D3CE51D-304B-41F5-B86A-C6794E5364E8}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -21,6 +23,10 @@ Global {295D954F-453F-4311-8D72-1EFF57261A38}.Debug|Any CPU.Build.0 = Debug|Any CPU {295D954F-453F-4311-8D72-1EFF57261A38}.Release|Any CPU.ActiveCfg = Release|Any CPU {295D954F-453F-4311-8D72-1EFF57261A38}.Release|Any CPU.Build.0 = Release|Any CPU + {2D3CE51D-304B-41F5-B86A-C6794E5364E8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2D3CE51D-304B-41F5-B86A-C6794E5364E8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2D3CE51D-304B-41F5-B86A-C6794E5364E8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2D3CE51D-304B-41F5-B86A-C6794E5364E8}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From 80319d8d0bfe32da582301a58a63f4154baaac2c Mon Sep 17 00:00:00 2001 From: CodeConscious <50596087+codeconscious@users.noreply.github.com> Date: Tue, 24 Dec 2024 22:19:02 +0900 Subject: [PATCH 02/12] Recreate tests in F# --- .../Startwatch.Library.FSharp.fsproj | 12 - Startwatch.Library/ExtensionMethods.cs | 51 ---- .../Library.fs | 23 +- Startwatch.Library/Startwatch.Library.csproj | 20 -- Startwatch.Library/Startwatch.Library.fsproj | 27 ++ Startwatch.Library/Watch.cs | 42 --- Startwatch.Tests.FSharp/Program.fs | 4 + .../Startwatch.Tests.FSharp.fsproj | 25 ++ Startwatch.Tests.FSharp/Tests.fs | 188 ++++++++++++++ Startwatch.Tests/Startwatch.Tests.csproj | 24 -- Startwatch.Tests/StartwatchTests.cs | 242 ------------------ Startwatch.Tests/Usings.cs | 1 - Startwatch.sln | 18 +- 13 files changed, 262 insertions(+), 415 deletions(-) delete mode 100644 Startwatch.Library.FSharp/Startwatch.Library.FSharp.fsproj delete mode 100644 Startwatch.Library/ExtensionMethods.cs rename {Startwatch.Library.FSharp => Startwatch.Library}/Library.fs (78%) delete mode 100644 Startwatch.Library/Startwatch.Library.csproj create mode 100644 Startwatch.Library/Startwatch.Library.fsproj delete mode 100644 Startwatch.Library/Watch.cs create mode 100644 Startwatch.Tests.FSharp/Program.fs create mode 100644 Startwatch.Tests.FSharp/Startwatch.Tests.FSharp.fsproj create mode 100644 Startwatch.Tests.FSharp/Tests.fs delete mode 100644 Startwatch.Tests/Startwatch.Tests.csproj delete mode 100644 Startwatch.Tests/StartwatchTests.cs delete mode 100644 Startwatch.Tests/Usings.cs diff --git a/Startwatch.Library.FSharp/Startwatch.Library.FSharp.fsproj b/Startwatch.Library.FSharp/Startwatch.Library.FSharp.fsproj deleted file mode 100644 index 850ca49..0000000 --- a/Startwatch.Library.FSharp/Startwatch.Library.FSharp.fsproj +++ /dev/null @@ -1,12 +0,0 @@ - - - - net9.0 - true - - - - - - - diff --git a/Startwatch.Library/ExtensionMethods.cs b/Startwatch.Library/ExtensionMethods.cs deleted file mode 100644 index 82502f6..0000000 --- a/Startwatch.Library/ExtensionMethods.cs +++ /dev/null @@ -1,51 +0,0 @@ -using System; - -namespace Startwatch.Library; - -public static class ExtensionMethods -{ - /// - /// Get a short, friendly, human-readable string representation of a TimeSpan. - /// - public static string ElapsedFriendly(this TimeSpan timeSpan) - { - if (timeSpan.TotalMilliseconds < 1000) - { - return timeSpan switch - { - { TotalMilliseconds: <1 } => $"{timeSpan.TotalNanoseconds:#,##0}ns", - _ => $"{timeSpan.TotalMilliseconds:#,##0}ms" - }; - } - - int days = timeSpan.Days; - int hours = timeSpan.Hours; - int mins = timeSpan.Minutes; - int secs = timeSpan.Seconds; - - var prependText = secs == 0 ? "exactly " : string.Empty; - - var hoursText = (days, hours) switch - { - (>0, _) or (_, >0) => $"{hours + (days * 24):#,##0}h", - _ => string.Empty - }; - - var minsText = (hours, mins) switch - { - (>0, >0) => $"{mins:00}m", - (_, >0) => $"{mins:0}m", - _ => string.Empty - }; - - var secsText = (hours, mins, secs) switch - { - (0, 0, >0) => $"{timeSpan:s\\.ff}s", - (>0, _, >0) or (_, >0, >0) => $"{timeSpan:ss}s", - (_, _, >0) => $"{timeSpan:s}s", - _ => string.Empty - }; - - return $"{prependText}{hoursText}{minsText}{secsText}"; - } -} diff --git a/Startwatch.Library.FSharp/Library.fs b/Startwatch.Library/Library.fs similarity index 78% rename from Startwatch.Library.FSharp/Library.fs rename to Startwatch.Library/Library.fs index 0067ad1..412a96b 100644 --- a/Startwatch.Library.FSharp/Library.fs +++ b/Startwatch.Library/Library.fs @@ -1,15 +1,12 @@ -namespace Startwatch.Library.FSharp +namespace Startwatch.Library open System open System.Diagnostics -module Startwatch = - type Watch() = - let startedAt = Stopwatch.GetTimestamp() - - let elapsedFriendly (startedAt: int64) = - let timeSpan = Stopwatch.GetElapsedTime(startedAt) - match timeSpan with +module Extensions = + type TimeSpan with + member this.ElapsedFriendly() = + match this with | t when t.TotalMilliseconds < 1 -> sprintf "%s" (t.TotalNanoseconds.ToString("#,##0ns")) | t when t.TotalMilliseconds < 1000 -> sprintf "%s" (t.TotalMilliseconds.ToString("#,##0ms")) | t -> @@ -34,12 +31,16 @@ module Startwatch = let secsText = match (hours, mins, secs) with - | 0, 0, s when s > 0 -> sprintf "%s\\.%ff" (t.ToString("s")) (t.TotalSeconds % 1.0) + | 0, 0, s when s > 0 -> sprintf "%ss" (t.ToString("s\\.ff")) | h, _, s when h > 0 && s > 0 -> sprintf "%ss" (t.ToString("ss")) | _, m, s when m > 0 && s > 0 -> sprintf "%ss" (t.ToString("ss")) - | _, _, s when s > 0 -> sprintf "%ss" (t.ToString("s")) + | _, _, s when s > 0 -> sprintf "%s" (t.ToString("s")) | _ -> String.Empty $"{prependText}{hoursText}{minsText}{secsText}" - member this.ElapsedFriendly = elapsedFriendly startedAt +module Startwatch = + type Watch() = + let startedAt = Stopwatch.GetTimestamp() + + member this.ElapsedFriendly = Stopwatch.GetElapsedTime(startedAt) diff --git a/Startwatch.Library/Startwatch.Library.csproj b/Startwatch.Library/Startwatch.Library.csproj deleted file mode 100644 index e3fc4dd..0000000 --- a/Startwatch.Library/Startwatch.Library.csproj +++ /dev/null @@ -1,20 +0,0 @@ - - - net9.0 - disable - enable - CodeConscious.Startwatch - 0.0.4 - CodeConscious - CodeConscious - A simple Stopwatch wrapper. - https://github.com/codeconscious/startwatch - https://github.com/codeconscious/startwatch - readme.md - MIT - true - - - - - diff --git a/Startwatch.Library/Startwatch.Library.fsproj b/Startwatch.Library/Startwatch.Library.fsproj new file mode 100644 index 0000000..cdba923 --- /dev/null +++ b/Startwatch.Library/Startwatch.Library.fsproj @@ -0,0 +1,27 @@ + + + + net9.0 + true + CodeConscious.Startwatch + 0.0.5 + CodeConscious + CodeConscious + A simple wrapper for System.Diagnostics.Stopwatch. + https://github.com/codeconscious/startwatch + https://github.com/codeconscious/startwatch + readme.md + MIT + true + Startwatch.Library + + + + + + + + + + + diff --git a/Startwatch.Library/Watch.cs b/Startwatch.Library/Watch.cs deleted file mode 100644 index e8c8151..0000000 --- a/Startwatch.Library/Watch.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System; -using System.Diagnostics; - -namespace Startwatch.Library; - -/// -/// A `Stopwatch` wrapper. Starts the enclosed `Stopwatch` upon instantiation. -/// -public sealed class Watch -{ - private readonly Stopwatch _stopwatch = new(); - - /// - /// Returns a formatted version of the elapsed time since the timer was started. - /// - /// - /// Using ticks because .ElapsedMilliseconds can be wildly inaccurate. - /// (Reference: https://stackoverflow.com/q/5113750/11767771) - /// - /// Also, use `Stopwatch.Elapsed.Ticks` over `Stopwatch.ElapsedTicks`. - /// For reasons of which I'm unware, the latter returns unexpected values. - /// - public string ElapsedFriendly => - TimeSpan.FromTicks(_stopwatch.Elapsed.Ticks).ElapsedFriendly(); - - /// - /// A constructor that automatically starts the enclosed `Stopwatch`. - /// - public Watch() - { - _stopwatch.Start(); - } - - /// - /// Stops time interval measurement, resets the elapsed time to zero, - /// and starts measuring elapsed time. - /// - public void Restart() - { - _stopwatch.Restart(); - } -} diff --git a/Startwatch.Tests.FSharp/Program.fs b/Startwatch.Tests.FSharp/Program.fs new file mode 100644 index 0000000..52113d0 --- /dev/null +++ b/Startwatch.Tests.FSharp/Program.fs @@ -0,0 +1,4 @@ +module Program + +[] +let main _ = 0 \ No newline at end of file diff --git a/Startwatch.Tests.FSharp/Startwatch.Tests.FSharp.fsproj b/Startwatch.Tests.FSharp/Startwatch.Tests.FSharp.fsproj new file mode 100644 index 0000000..7859d12 --- /dev/null +++ b/Startwatch.Tests.FSharp/Startwatch.Tests.FSharp.fsproj @@ -0,0 +1,25 @@ + + + + net9.0 + false + false + + + + + + + + + + + + + + + + + + + diff --git a/Startwatch.Tests.FSharp/Tests.fs b/Startwatch.Tests.FSharp/Tests.fs new file mode 100644 index 0000000..043d016 --- /dev/null +++ b/Startwatch.Tests.FSharp/Tests.fs @@ -0,0 +1,188 @@ +module Tests + +open System +open Startwatch.Library.Extensions +open Xunit + +[] +let ``nanosecond with three digits are formatted correctly`` () = + // Assert.True(true) + let timeSpan = TimeSpan.FromTicks(1) // 1 tick == 100 nanoseconds + let actual = timeSpan.ElapsedFriendly() + let expected = "100ns" + Assert.Equal(expected, actual) + +[] +let ``Nanoseconds_FiveDigits_FormatsCorrectly`` () = + let timeSpan = TimeSpan.FromTicks(100L) // 1 tick == 100 nanoseconds + let actual = timeSpan.ElapsedFriendly() + let expected = "10,000ns" + Assert.Equal(expected, actual) + +[] +let ``Milliseconds_OneDigit_FormatsCorrectly`` () = + let timeSpan = TimeSpan.FromMilliseconds(1.0) + let actual = timeSpan.ElapsedFriendly() + let expected = "1ms" + Assert.Equal(expected, actual) + +[] +let ``Milliseconds_TwoDigits_FormatsCorrectly`` () = + let timeSpan = TimeSpan.FromMilliseconds(99.0) + let actual = timeSpan.ElapsedFriendly() + let expected = "99ms" + Assert.Equal(expected, actual) + +[] +let ``Milliseconds_ThreeDigits_FormatsCorrectly`` () = + let timeSpan = TimeSpan.FromMilliseconds(999.0) + let actual = timeSpan.ElapsedFriendly() + let expected = "999ms" + Assert.Equal(expected, actual) + +[] +let ``Milliseconds_ThreeDigitsWithSmallDecimal_FormatsCorrectly`` () = + let timeSpan = TimeSpan.FromMilliseconds(999.3) + let actual = timeSpan.ElapsedFriendly() + let expected = "999ms" + Assert.Equal(expected, actual) + +[] +let ``Milliseconds_ThreeDigitsWithLargeDecimal_FormatsCorrectly`` () = + let timeSpan = TimeSpan.FromMilliseconds(999.9) + let actual = timeSpan.ElapsedFriendly() + let expected = "1,000ms" + Assert.Equal(expected, actual) + +[] +let ``SecondsOnly_FormatsCorrectly`` () = + let timeSpan = TimeSpan(0, 0, 3) + let actual = timeSpan.ElapsedFriendly() + let expected = "3.00s" + Assert.Equal(expected, actual) + +[] +let ``Seconds_With500Milliseconds_FormatsCorrectly`` () = + let timeSpan = TimeSpan(0, 0, 0, 3, 500) + let actual = timeSpan.ElapsedFriendly() + let expected = "3.50s" + Assert.Equal(expected, actual) + +[] +let ``Seconds_With520Milliseconds_FormatsCorrectly`` () = + let timeSpan = TimeSpan(0, 0, 0, 3, 520) + let actual = timeSpan.ElapsedFriendly() + let expected = "3.52s" + Assert.Equal(expected, actual) + +[] +let ``Minute_NoSeconds_FormatsCorrectly`` () = + let timeSpan = TimeSpan(0, 1, 0) + let actual = timeSpan.ElapsedFriendly() + let expected = "exactly 1m" + Assert.Equal(expected, actual) + +[] +let ``Minute_30Seconds_FormatsCorrectly`` () = + let timeSpan = TimeSpan(0, 1, 30) + let actual = timeSpan.ElapsedFriendly() + let expected = "1m30s" + Assert.Equal(expected, actual) + +[] +let ``Minutes_TwoDigits_NoSeconds_FormatsCorrectly`` () = + let timeSpan = TimeSpan(0, 59, 0) + let actual = timeSpan.ElapsedFriendly() + let expected = "exactly 59m" + Assert.Equal(expected, actual) + +[] +let ``Minutes_TwoDigits_NoSeconds_WithSeconds_FormatsCorrectly`` () = + let timeSpan = TimeSpan(0, 59, 30) + let actual = timeSpan.ElapsedFriendly() + let expected = "59m30s" + Assert.Equal(expected, actual) + +[] +let ``SingleDigitHour_NoMinutes_NoSeconds_FormatsCorrectly`` () = + let timeSpan = TimeSpan(7, 0, 0) + let actual = timeSpan.ElapsedFriendly() + let expected = "exactly 7h" + Assert.Equal(expected, actual) + +[] +let ``SingleDigitHour_WithMinutes_NoSeconds_FormatsCorrectly`` () = + let timeSpan = TimeSpan(7, 20, 0) + let actual = timeSpan.ElapsedFriendly() + let expected = "exactly 7h20m" + Assert.Equal(expected, actual) + +[] +let ``SingleDigitHour_NoMinutes_WithSeconds_FormatsCorrectly`` () = + let timeSpan = TimeSpan(7, 0, 47) + let actual = timeSpan.ElapsedFriendly() + let expected = "7h47s" + Assert.Equal(expected, actual) + +[] +let ``DoubleDigitHour_NoMinutes_NoSeconds_FormatsCorrectly`` () = + let timeSpan = TimeSpan(13, 0, 0) + let actual = timeSpan.ElapsedFriendly() + let expected = "exactly 13h" + Assert.Equal(expected, actual) + +[] +let ``SingleDigitHour_SingleDigitMinutes_NoSeconds_FormatsCorrectly`` () = + let timeSpan = TimeSpan(1, 5, 0) + let actual = timeSpan.ElapsedFriendly() + let expected = "exactly 1h05m" + Assert.Equal(expected, actual) + +[] +let ``SingleDigitHour_DoubleDigitMinutes_NoSeconds_FormatsCorrectly`` () = + let timeSpan = TimeSpan(1, 55, 0) + let actual = timeSpan.ElapsedFriendly() + let expected = "exactly 1h55m" + Assert.Equal(expected, actual) + +[] +let ``SingleDigitHour_DoubleDigitMinutes_SingleDigitSeconds_FormatsCorrectly`` () = + let timeSpan = TimeSpan(1, 55, 8) + let actual = timeSpan.ElapsedFriendly() + let expected = "1h55m08s" + Assert.Equal(expected, actual) + +[] +let ``DoubleDigitHour_SingleDigitMinutes_SingleDigitSeconds_FormatsCorrectly`` () = + let timeSpan = TimeSpan(12, 5, 8) + let actual = timeSpan.ElapsedFriendly() + let expected = "12h05m08s" + Assert.Equal(expected, actual) + +[] +let ``TripleDigitHour_SingleDigitMinutes_SingleDigitSeconds_FormatsCorrectly`` () = + let timeSpan = TimeSpan(36, 59, 59) + let actual = timeSpan.ElapsedFriendly() + let expected = "36h59m59s" + Assert.Equal(expected, actual) + +[] +let ``SingleDigitDay_NoHoursMinutesOrSeconds_FormatsCorrectly`` () = + let timeSpan = TimeSpan(10, 0, 0, 0) + let actual = timeSpan.ElapsedFriendly() + let expected = "exactly 240h" + Assert.Equal(expected, actual) + +[] +let ``SingleDigitDay_WithHoursMinutesOrSeconds_FormatsCorrectly`` () = + let timeSpan = TimeSpan(4, 4, 59, 59) + let actual = timeSpan.ElapsedFriendly() + let expected = "100h59m59s" + Assert.Equal(expected, actual) + +[] +let ``TripleDigitDay_WithHoursMinutesOrSeconds_FormatsCorrectly`` () = + let timeSpan = TimeSpan(100, 23, 59, 59) + let actual = timeSpan.ElapsedFriendly() + let expected = "2,423h59m59s" + Assert.Equal(expected, actual) diff --git a/Startwatch.Tests/Startwatch.Tests.csproj b/Startwatch.Tests/Startwatch.Tests.csproj deleted file mode 100644 index 85fe16c..0000000 --- a/Startwatch.Tests/Startwatch.Tests.csproj +++ /dev/null @@ -1,24 +0,0 @@ - - - net9.0 - disable - enable - false - true - - - - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - - - - diff --git a/Startwatch.Tests/StartwatchTests.cs b/Startwatch.Tests/StartwatchTests.cs deleted file mode 100644 index 8a8f626..0000000 --- a/Startwatch.Tests/StartwatchTests.cs +++ /dev/null @@ -1,242 +0,0 @@ -using System; -using Startwatch.Library; - -namespace Startwatch.Tests; - -public sealed class StartwatchTests -{ - [Fact] - public void Nanoseconds_ThreeDigits_FormatsCorrectly() - { - var timeSpan = TimeSpan.FromTicks(1); // 1 tick == 100 nanoseconds - string actual = timeSpan.ElapsedFriendly(); - const string expected = "100ns"; - Assert.Equal(expected, actual); - } - - [Fact] - public void Nanoseconds_FiveDigits_FormatsCorrectly() - { - var timeSpan = TimeSpan.FromTicks(100); // 1 tick == 100 nanoseconds - string actual = timeSpan.ElapsedFriendly(); - const string expected = "10,000ns"; - Assert.Equal(expected, actual); - } - - [Fact] - public void Milliseconds_OneDigit_FormatsCorrectly() - { - var timeSpan = TimeSpan.FromMilliseconds(1); - string actual = timeSpan.ElapsedFriendly(); - const string expected = "1ms"; - Assert.Equal(expected, actual); - } - - [Fact] - public void Milliseconds_TwoDigits_FormatsCorrectly() - { - var timeSpan = TimeSpan.FromMilliseconds(99); - string actual = timeSpan.ElapsedFriendly(); - const string expected = "99ms"; - Assert.Equal(expected, actual); - } - - [Fact] - public void Milliseconds_ThreeDigits_FormatsCorrectly() - { - var timeSpan = TimeSpan.FromMilliseconds(999); - string actual = timeSpan.ElapsedFriendly(); - const string expected = "999ms"; - Assert.Equal(expected, actual); - } - - [Fact] - public void Milliseconds_ThreeDigitsWithSmallDecimal_FormatsCorrectly() - { - var timeSpan = TimeSpan.FromMilliseconds(999.3); - string actual = timeSpan.ElapsedFriendly(); - const string expected = "999ms"; - Assert.Equal(expected, actual); - } - - [Fact] - public void Milliseconds_ThreeDigitsWithLargeDecimal_FormatsCorrectly() - { - var timeSpan = TimeSpan.FromMilliseconds(999.9); - string actual = timeSpan.ElapsedFriendly(); - const string expected = "1,000ms"; - Assert.Equal(expected, actual); - } - - [Fact] - public void SecondsOnly_FormatsCorrectly() - { - TimeSpan timeSpan = new(0, 0, 3); - string actual = timeSpan.ElapsedFriendly(); - const string expected = "3.00s"; - Assert.Equal(expected, actual); - } - - [Fact] - public void Seconds_With500Milliseconds_FormatsCorrectly() - { - TimeSpan timeSpan = new(0, 0, 0, 3, 500); - string actual = timeSpan.ElapsedFriendly(); - const string expected = "3.50s"; - Assert.Equal(expected, actual); - } - - [Fact] - public void Seconds_With520Milliseconds_FormatsCorrectly() - { - TimeSpan timeSpan = new(0, 0, 0, 3, 520); - string actual = timeSpan.ElapsedFriendly(); - const string expected = "3.52s"; - Assert.Equal(expected, actual); - } - - [Fact] - public void Minute_NoSeconds_FormatsCorrectly() - { - TimeSpan timeSpan = new(0, 1, 0); - string actual = timeSpan.ElapsedFriendly(); - const string expected = "exactly 1m"; - Assert.Equal(expected, actual); - } - - [Fact] - public void Minute_30Seconds_FormatsCorrectly() - { - TimeSpan timeSpan = new(0, 1, 30); - string actual = timeSpan.ElapsedFriendly(); - const string expected = "1m30s"; - Assert.Equal(expected, actual); - } - - [Fact] - public void Minutes_TwoDigits_NoSeconds_FormatsCorrectly() - { - TimeSpan timeSpan = new(0, 59, 0); - string actual = timeSpan.ElapsedFriendly(); - const string expected = "exactly 59m"; - Assert.Equal(expected, actual); - } - - [Fact] - public void Minutes_TwoDigits_NoSeconds_WithSeconds_FormatsCorrectly() - { - TimeSpan timeSpan = new(0, 59, 30); - string actual = timeSpan.ElapsedFriendly(); - const string expected = "59m30s"; - Assert.Equal(expected, actual); - } - - - [Fact] - public void SingleDigitHour_NoMinutes_NoSeconds_FormatsCorrectly() - { - TimeSpan timeSpan = new(7, 0, 0); - string actual = timeSpan.ElapsedFriendly(); - const string expected = "exactly 7h"; - Assert.Equal(expected, actual); - } - - [Fact] - public void SingleDigitHour_WithMinutes_NoSeconds_FormatsCorrectly() - { - TimeSpan timeSpan = new(7, 20, 0); - string actual = timeSpan.ElapsedFriendly(); - const string expected = "exactly 7h20m"; - Assert.Equal(expected, actual); - } - - [Fact] - public void SingleDigitHour_NoMinutes_WithSeconds_FormatsCorrectly() - { - TimeSpan timeSpan = new(7, 0, 47); - string actual = timeSpan.ElapsedFriendly(); - const string expected = "7h47s"; - Assert.Equal(expected, actual); - } - - [Fact] - public void DoubleDigitHour_NoMinutes_NoSeconds_FormatsCorrectly() - { - TimeSpan timeSpan = new(13, 0, 0); - string actual = timeSpan.ElapsedFriendly(); - const string expected = "exactly 13h"; - Assert.Equal(expected, actual); - } - - [Fact] - public void SingleDigitHour_SingleDigitMinutes_NoSeconds_FormatsCorrectly() - { - TimeSpan timeSpan = new(1, 5, 0); - string actual = timeSpan.ElapsedFriendly(); - const string expected = "exactly 1h05m"; - Assert.Equal(expected, actual); - } - - [Fact] - public void SingleDigitHour_DoubleDigitMinutes_NoSeconds_FormatsCorrectly() - { - TimeSpan timeSpan = new(1, 55, 0); - string actual = timeSpan.ElapsedFriendly(); - const string expected = "exactly 1h55m"; - Assert.Equal(expected, actual); - } - - [Fact] - public void SingleDigitHour_DoubleDigitMinutes_SingleDigitSeconds_FormatsCorrectly() - { - TimeSpan timeSpan = new(1, 55, 8); - string actual = timeSpan.ElapsedFriendly(); - const string expected = "1h55m08s"; - Assert.Equal(expected, actual); - } - - [Fact] - public void DoubleDigitHour_SingleDigitMinutes_SingleDigitSeconds_FormatsCorrectly() - { - TimeSpan timeSpan = new(12, 5, 8); - string actual = timeSpan.ElapsedFriendly(); - const string expected = "12h05m08s"; - Assert.Equal(expected, actual); - } - - [Fact] - public void TripleDigitHour_SingleDigitMinutes_SingleDigitSeconds_FormatsCorrectly() - { - TimeSpan timeSpan = new(36, 59, 59); - string actual = timeSpan.ElapsedFriendly(); - const string expected = "36h59m59s"; - Assert.Equal(expected, actual); - } - - [Fact] - public void SingleDigitDay_NoHoursMinutesOrSeconds_FormatsCorrectly() - { - TimeSpan timeSpan = new(10, 0, 0, 0); - string actual = timeSpan.ElapsedFriendly(); - const string expected = "exactly 240h"; - Assert.Equal(expected, actual); - } - - [Fact] - public void SingleDigitDay_WithHoursMinutesOrSeconds_FormatsCorrectly() - { - TimeSpan timeSpan = new(4, 4, 59, 59); - string actual = timeSpan.ElapsedFriendly(); - const string expected = "100h59m59s"; - Assert.Equal(expected, actual); - } - - [Fact] - public void TripleDigitDay_WithHoursMinutesOrSeconds_FormatsCorrectly() - { - TimeSpan timeSpan = new(100, 23, 59, 59); - string actual = timeSpan.ElapsedFriendly(); - const string expected = "2,423h59m59s"; - Assert.Equal(expected, actual); - } -} diff --git a/Startwatch.Tests/Usings.cs b/Startwatch.Tests/Usings.cs deleted file mode 100644 index 9df1d42..0000000 --- a/Startwatch.Tests/Usings.cs +++ /dev/null @@ -1 +0,0 @@ -global using Xunit; diff --git a/Startwatch.sln b/Startwatch.sln index a6c9551..bd09414 100644 --- a/Startwatch.sln +++ b/Startwatch.sln @@ -3,11 +3,9 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 25.0.1706.0 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Startwatch.Library", ".\Startwatch.Library\Startwatch.Library.csproj", "{598A49D6-9724-400C-853A-8B950297DEBD}" +Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Startwatch.Library", "Startwatch.Library\Startwatch.Library.fsproj", "{2D3CE51D-304B-41F5-B86A-C6794E5364E8}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Startwatch.Tests", ".\Startwatch.Tests\Startwatch.Tests.csproj", "{295D954F-453F-4311-8D72-1EFF57261A38}" -EndProject -Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Startwatch.Library.FSharp", "Startwatch.Library.FSharp\Startwatch.Library.FSharp.fsproj", "{2D3CE51D-304B-41F5-B86A-C6794E5364E8}" +Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Startwatch.Tests.FSharp", "Startwatch.Tests.FSharp\Startwatch.Tests.FSharp.fsproj", "{88AB89DE-6161-48E8-A95B-5E49FC2F1BED}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -15,18 +13,14 @@ Global Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {598A49D6-9724-400C-853A-8B950297DEBD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {598A49D6-9724-400C-853A-8B950297DEBD}.Debug|Any CPU.Build.0 = Debug|Any CPU - {598A49D6-9724-400C-853A-8B950297DEBD}.Release|Any CPU.ActiveCfg = Release|Any CPU - {598A49D6-9724-400C-853A-8B950297DEBD}.Release|Any CPU.Build.0 = Release|Any CPU - {295D954F-453F-4311-8D72-1EFF57261A38}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {295D954F-453F-4311-8D72-1EFF57261A38}.Debug|Any CPU.Build.0 = Debug|Any CPU - {295D954F-453F-4311-8D72-1EFF57261A38}.Release|Any CPU.ActiveCfg = Release|Any CPU - {295D954F-453F-4311-8D72-1EFF57261A38}.Release|Any CPU.Build.0 = Release|Any CPU {2D3CE51D-304B-41F5-B86A-C6794E5364E8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {2D3CE51D-304B-41F5-B86A-C6794E5364E8}.Debug|Any CPU.Build.0 = Debug|Any CPU {2D3CE51D-304B-41F5-B86A-C6794E5364E8}.Release|Any CPU.ActiveCfg = Release|Any CPU {2D3CE51D-304B-41F5-B86A-C6794E5364E8}.Release|Any CPU.Build.0 = Release|Any CPU + {88AB89DE-6161-48E8-A95B-5E49FC2F1BED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {88AB89DE-6161-48E8-A95B-5E49FC2F1BED}.Debug|Any CPU.Build.0 = Debug|Any CPU + {88AB89DE-6161-48E8-A95B-5E49FC2F1BED}.Release|Any CPU.ActiveCfg = Release|Any CPU + {88AB89DE-6161-48E8-A95B-5E49FC2F1BED}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From 5e229111c8e733b087b8a4b1a7eb92d2f505d037 Mon Sep 17 00:00:00 2001 From: CodeConscious <50596087+codeconscious@users.noreply.github.com> Date: Tue, 24 Dec 2024 22:20:10 +0900 Subject: [PATCH 03/12] Rename test project --- {Startwatch.Tests.FSharp => Startwatch.Tests}/Program.fs | 0 .../Startwatch.Tests.fsproj | 0 {Startwatch.Tests.FSharp => Startwatch.Tests}/Tests.fs | 3 +-- Startwatch.sln | 2 +- 4 files changed, 2 insertions(+), 3 deletions(-) rename {Startwatch.Tests.FSharp => Startwatch.Tests}/Program.fs (100%) rename Startwatch.Tests.FSharp/Startwatch.Tests.FSharp.fsproj => Startwatch.Tests/Startwatch.Tests.fsproj (100%) rename {Startwatch.Tests.FSharp => Startwatch.Tests}/Tests.fs (99%) diff --git a/Startwatch.Tests.FSharp/Program.fs b/Startwatch.Tests/Program.fs similarity index 100% rename from Startwatch.Tests.FSharp/Program.fs rename to Startwatch.Tests/Program.fs diff --git a/Startwatch.Tests.FSharp/Startwatch.Tests.FSharp.fsproj b/Startwatch.Tests/Startwatch.Tests.fsproj similarity index 100% rename from Startwatch.Tests.FSharp/Startwatch.Tests.FSharp.fsproj rename to Startwatch.Tests/Startwatch.Tests.fsproj diff --git a/Startwatch.Tests.FSharp/Tests.fs b/Startwatch.Tests/Tests.fs similarity index 99% rename from Startwatch.Tests.FSharp/Tests.fs rename to Startwatch.Tests/Tests.fs index 043d016..cc0b561 100644 --- a/Startwatch.Tests.FSharp/Tests.fs +++ b/Startwatch.Tests/Tests.fs @@ -1,12 +1,11 @@ module Tests open System -open Startwatch.Library.Extensions open Xunit +open Startwatch.Library.Extensions [] let ``nanosecond with three digits are formatted correctly`` () = - // Assert.True(true) let timeSpan = TimeSpan.FromTicks(1) // 1 tick == 100 nanoseconds let actual = timeSpan.ElapsedFriendly() let expected = "100ns" diff --git a/Startwatch.sln b/Startwatch.sln index bd09414..7f52969 100644 --- a/Startwatch.sln +++ b/Startwatch.sln @@ -5,7 +5,7 @@ VisualStudioVersion = 25.0.1706.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Startwatch.Library", "Startwatch.Library\Startwatch.Library.fsproj", "{2D3CE51D-304B-41F5-B86A-C6794E5364E8}" EndProject -Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Startwatch.Tests.FSharp", "Startwatch.Tests.FSharp\Startwatch.Tests.FSharp.fsproj", "{88AB89DE-6161-48E8-A95B-5E49FC2F1BED}" +Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Startwatch.Tests", "Startwatch.Tests\Startwatch.Tests.fsproj", "{88AB89DE-6161-48E8-A95B-5E49FC2F1BED}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution From 0bd40f0789ab9adfd99ba58ebf246f551aef1287 Mon Sep 17 00:00:00 2001 From: CodeConscious <50596087+codeconscious@users.noreply.github.com> Date: Wed, 25 Dec 2024 10:34:36 +0900 Subject: [PATCH 04/12] Update tests --- Startwatch.Library/Library.fs | 11 ++-- Startwatch.Tests/Tests.fs | 117 ++++++++++++++++++++++++---------- 2 files changed, 89 insertions(+), 39 deletions(-) diff --git a/Startwatch.Library/Library.fs b/Startwatch.Library/Library.fs index 412a96b..3b8ed50 100644 --- a/Startwatch.Library/Library.fs +++ b/Startwatch.Library/Library.fs @@ -30,11 +30,12 @@ module Extensions = | _ -> String.Empty let secsText = - match (hours, mins, secs) with - | 0, 0, s when s > 0 -> sprintf "%ss" (t.ToString("s\\.ff")) - | h, _, s when h > 0 && s > 0 -> sprintf "%ss" (t.ToString("ss")) - | _, m, s when m > 0 && s > 0 -> sprintf "%ss" (t.ToString("ss")) - | _, _, s when s > 0 -> sprintf "%s" (t.ToString("s")) + match (days, hours, mins, secs) with + | d, _, 0, s when d > 0 && s > 0 -> sprintf "%ss" (t.ToString("ss")) + | _, 0, 0, s when s > 0 -> sprintf "%ss" (t.ToString("s\\.ff")) + | _, h, _, s when h > 0 && s > 0 -> sprintf "%ss" (t.ToString("ss")) + | _, _, m, s when m > 0 && s > 0 -> sprintf "%ss" (t.ToString("ss")) + | _, _, _, s when s > 0 -> sprintf "%s" (t.ToString("s")) | _ -> String.Empty $"{prependText}{hoursText}{minsText}{secsText}" diff --git a/Startwatch.Tests/Tests.fs b/Startwatch.Tests/Tests.fs index cc0b561..e7b3654 100644 --- a/Startwatch.Tests/Tests.fs +++ b/Startwatch.Tests/Tests.fs @@ -5,182 +5,231 @@ open Xunit open Startwatch.Library.Extensions [] -let ``nanosecond with three digits are formatted correctly`` () = +let ``Nanoseconds with 3 digits`` () = let timeSpan = TimeSpan.FromTicks(1) // 1 tick == 100 nanoseconds let actual = timeSpan.ElapsedFriendly() let expected = "100ns" Assert.Equal(expected, actual) [] -let ``Nanoseconds_FiveDigits_FormatsCorrectly`` () = +let ``Nanoseconds with 5 digits`` () = let timeSpan = TimeSpan.FromTicks(100L) // 1 tick == 100 nanoseconds let actual = timeSpan.ElapsedFriendly() let expected = "10,000ns" Assert.Equal(expected, actual) [] -let ``Milliseconds_OneDigit_FormatsCorrectly`` () = +let ``Single-digit milliseconds`` () = let timeSpan = TimeSpan.FromMilliseconds(1.0) let actual = timeSpan.ElapsedFriendly() let expected = "1ms" Assert.Equal(expected, actual) [] -let ``Milliseconds_TwoDigits_FormatsCorrectly`` () = +let ``Double-digit milliseconds`` () = let timeSpan = TimeSpan.FromMilliseconds(99.0) let actual = timeSpan.ElapsedFriendly() let expected = "99ms" Assert.Equal(expected, actual) [] -let ``Milliseconds_ThreeDigits_FormatsCorrectly`` () = +let ``Double-digit milliseconds with a small decimal`` () = + let timeSpan = TimeSpan.FromMilliseconds(99.2) + let actual = timeSpan.ElapsedFriendly() + let expected = "99ms" + Assert.Equal(expected, actual) + +[] +let ``Double-digit milliseconds with a large decimal`` () = + let timeSpan = TimeSpan.FromMilliseconds(99.9) + let actual = timeSpan.ElapsedFriendly() + let expected = "100ms" + Assert.Equal(expected, actual) + + +[] +let ``Triple-digit milliseconds`` () = let timeSpan = TimeSpan.FromMilliseconds(999.0) let actual = timeSpan.ElapsedFriendly() let expected = "999ms" Assert.Equal(expected, actual) [] -let ``Milliseconds_ThreeDigitsWithSmallDecimal_FormatsCorrectly`` () = +let ``Triple-digit milliseconds with a small decimal`` () = let timeSpan = TimeSpan.FromMilliseconds(999.3) let actual = timeSpan.ElapsedFriendly() let expected = "999ms" Assert.Equal(expected, actual) [] -let ``Milliseconds_ThreeDigitsWithLargeDecimal_FormatsCorrectly`` () = +let ``Triple-digit milliseconds with a large decimal`` () = let timeSpan = TimeSpan.FromMilliseconds(999.9) let actual = timeSpan.ElapsedFriendly() let expected = "1,000ms" Assert.Equal(expected, actual) [] -let ``SecondsOnly_FormatsCorrectly`` () = +let ``Single-digit seconds`` () = let timeSpan = TimeSpan(0, 0, 3) let actual = timeSpan.ElapsedFriendly() let expected = "3.00s" Assert.Equal(expected, actual) [] -let ``Seconds_With500Milliseconds_FormatsCorrectly`` () = +let ``Single-digit seconds with 500 milliseconds`` () = let timeSpan = TimeSpan(0, 0, 0, 3, 500) let actual = timeSpan.ElapsedFriendly() let expected = "3.50s" Assert.Equal(expected, actual) [] -let ``Seconds_With520Milliseconds_FormatsCorrectly`` () = +let ``Single-digit seconds with 520 milliseconds`` () = let timeSpan = TimeSpan(0, 0, 0, 3, 520) let actual = timeSpan.ElapsedFriendly() let expected = "3.52s" Assert.Equal(expected, actual) [] -let ``Minute_NoSeconds_FormatsCorrectly`` () = +let ``Single-digit minute with no seconds`` () = let timeSpan = TimeSpan(0, 1, 0) let actual = timeSpan.ElapsedFriendly() let expected = "exactly 1m" Assert.Equal(expected, actual) [] -let ``Minute_30Seconds_FormatsCorrectly`` () = +let ``Single-digit minute with single-digit seconds`` () = + let timeSpan = TimeSpan(0, 1, 5) + let actual = timeSpan.ElapsedFriendly() + let expected = "1m05s" + Assert.Equal(expected, actual) + +[] +let ``Single-digit minute with double-digit seconds`` () = let timeSpan = TimeSpan(0, 1, 30) let actual = timeSpan.ElapsedFriendly() let expected = "1m30s" Assert.Equal(expected, actual) [] -let ``Minutes_TwoDigits_NoSeconds_FormatsCorrectly`` () = +let ``Single-digit minute with maximum seconds`` () = + let timeSpan = TimeSpan(0, 1, 59) + let actual = timeSpan.ElapsedFriendly() + let expected = "1m59s" + Assert.Equal(expected, actual) + +[] +let ``Two-digit minutes with no seconds`` () = let timeSpan = TimeSpan(0, 59, 0) let actual = timeSpan.ElapsedFriendly() let expected = "exactly 59m" Assert.Equal(expected, actual) [] -let ``Minutes_TwoDigits_NoSeconds_WithSeconds_FormatsCorrectly`` () = +let ``Two-digit minutes with 30 seconds`` () = let timeSpan = TimeSpan(0, 59, 30) let actual = timeSpan.ElapsedFriendly() let expected = "59m30s" Assert.Equal(expected, actual) [] -let ``SingleDigitHour_NoMinutes_NoSeconds_FormatsCorrectly`` () = +let ``Single-digit hour with no minutes or seconds`` () = let timeSpan = TimeSpan(7, 0, 0) let actual = timeSpan.ElapsedFriendly() let expected = "exactly 7h" Assert.Equal(expected, actual) [] -let ``SingleDigitHour_WithMinutes_NoSeconds_FormatsCorrectly`` () = +let ``Single-digit hour with single-digit minutes but no seconds`` () = + let timeSpan = TimeSpan(7, 7, 0) + let actual = timeSpan.ElapsedFriendly() + let expected = "exactly 7h07m" + Assert.Equal(expected, actual) + +[] +let ``Single-digit hour with single-digit minutes and single-digit seconds`` () = + let timeSpan = TimeSpan(7, 7, 4) + let actual = timeSpan.ElapsedFriendly() + let expected = "7h07m04s" + Assert.Equal(expected, actual) + +let ``Single-digit hour with double-digit minutes but no seconds`` () = let timeSpan = TimeSpan(7, 20, 0) let actual = timeSpan.ElapsedFriendly() let expected = "exactly 7h20m" Assert.Equal(expected, actual) [] -let ``SingleDigitHour_NoMinutes_WithSeconds_FormatsCorrectly`` () = +let ``Single-digit hour without minutes but with seconds`` () = let timeSpan = TimeSpan(7, 0, 47) let actual = timeSpan.ElapsedFriendly() let expected = "7h47s" Assert.Equal(expected, actual) [] -let ``DoubleDigitHour_NoMinutes_NoSeconds_FormatsCorrectly`` () = +let ``Double-digit hour with no minutes or seconds`` () = let timeSpan = TimeSpan(13, 0, 0) let actual = timeSpan.ElapsedFriendly() let expected = "exactly 13h" Assert.Equal(expected, actual) [] -let ``SingleDigitHour_SingleDigitMinutes_NoSeconds_FormatsCorrectly`` () = - let timeSpan = TimeSpan(1, 5, 0) - let actual = timeSpan.ElapsedFriendly() - let expected = "exactly 1h05m" - Assert.Equal(expected, actual) - -[] -let ``SingleDigitHour_DoubleDigitMinutes_NoSeconds_FormatsCorrectly`` () = - let timeSpan = TimeSpan(1, 55, 0) +let ``Single-digit hour with single-digit minutes and with seconds`` () = + let timeSpan = TimeSpan(1, 3, 8) let actual = timeSpan.ElapsedFriendly() - let expected = "exactly 1h55m" + let expected = "1h03m08s" Assert.Equal(expected, actual) [] -let ``SingleDigitHour_DoubleDigitMinutes_SingleDigitSeconds_FormatsCorrectly`` () = +let ``Single-digit hour with double-digit minutes and with seconds`` () = let timeSpan = TimeSpan(1, 55, 8) let actual = timeSpan.ElapsedFriendly() let expected = "1h55m08s" Assert.Equal(expected, actual) [] -let ``DoubleDigitHour_SingleDigitMinutes_SingleDigitSeconds_FormatsCorrectly`` () = +let ``Double-digit hour with single-digit minutes and with seconds`` () = let timeSpan = TimeSpan(12, 5, 8) let actual = timeSpan.ElapsedFriendly() let expected = "12h05m08s" Assert.Equal(expected, actual) [] -let ``TripleDigitHour_SingleDigitMinutes_SingleDigitSeconds_FormatsCorrectly`` () = +let ``Double-digit hour over 24 hours with maximum minutes and seconds`` () = let timeSpan = TimeSpan(36, 59, 59) let actual = timeSpan.ElapsedFriendly() let expected = "36h59m59s" Assert.Equal(expected, actual) [] -let ``SingleDigitDay_NoHoursMinutesOrSeconds_FormatsCorrectly`` () = +let ``Single-digit day with no minutes, hours, or seconds`` () = let timeSpan = TimeSpan(10, 0, 0, 0) let actual = timeSpan.ElapsedFriendly() let expected = "exactly 240h" Assert.Equal(expected, actual) [] -let ``SingleDigitDay_WithHoursMinutesOrSeconds_FormatsCorrectly`` () = +let ``Single-digit day with no minutes, hours, but with single-digit seconds`` () = + let timeSpan = TimeSpan(10, 0, 0, 2) + let actual = timeSpan.ElapsedFriendly() + let expected = "240h02s" + Assert.Equal(expected, actual) + +[] +let ``Single-digit day with no hours, but with single-digit minutes seconds`` () = + let timeSpan = TimeSpan(10, 0, 0, 2) + let actual = timeSpan.ElapsedFriendly() + let expected = "240h02s" + Assert.Equal(expected, actual) + +[] +let ``Single-digit day with minutes, hours, and seconds`` () = let timeSpan = TimeSpan(4, 4, 59, 59) let actual = timeSpan.ElapsedFriendly() let expected = "100h59m59s" Assert.Equal(expected, actual) [] -let ``TripleDigitDay_WithHoursMinutesOrSeconds_FormatsCorrectly`` () = +let ``Triple-digit day with minutes, hours, and seconds`` () = let timeSpan = TimeSpan(100, 23, 59, 59) let actual = timeSpan.ElapsedFriendly() let expected = "2,423h59m59s" From cd8c8635b9ecdd40f3bfa5a8a6e2bdfec67a3132 Mon Sep 17 00:00:00 2001 From: CodeConscious <50596087+codeconscious@users.noreply.github.com> Date: Wed, 25 Dec 2024 12:32:08 +0900 Subject: [PATCH 05/12] Add logic and tests for negative and empty TimeSpans --- Startwatch.Library/Library.fs | 12 +++++++----- Startwatch.Tests/Tests.fs | 12 ++++++++++++ 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/Startwatch.Library/Library.fs b/Startwatch.Library/Library.fs index 3b8ed50..6ce0613 100644 --- a/Startwatch.Library/Library.fs +++ b/Startwatch.Library/Library.fs @@ -7,6 +7,8 @@ module Extensions = type TimeSpan with member this.ElapsedFriendly() = match this with + | t when t.Ticks < 0 -> raise <| NotSupportedException("Negative TimeSpans are currently not supported.") + | t when t.Ticks = 0 -> "no time" | t when t.TotalMilliseconds < 1 -> sprintf "%s" (t.TotalNanoseconds.ToString("#,##0ns")) | t when t.TotalMilliseconds < 1000 -> sprintf "%s" (t.TotalMilliseconds.ToString("#,##0ms")) | t -> @@ -17,19 +19,19 @@ module Extensions = let prependText = if secs = 0 then "exactly " else String.Empty - let hoursText = + let hourText = match (days, hours) with | d, _ when d > 0 -> sprintf "%s" ((hours + (days * 24)).ToString("#,##0h")) | _, h when h > 0 -> sprintf "%s" ((hours + (days * 24)).ToString("#,##0h")) | _ -> String.Empty - let minsText = + let minText = match (hours, mins) with | h, m when h > 0 && m > 0 -> sprintf "%s" (m.ToString("00m")) | _, m when m > 0 -> sprintf "%s" (m.ToString("0m")) | _ -> String.Empty - let secsText = + let secText = match (days, hours, mins, secs) with | d, _, 0, s when d > 0 && s > 0 -> sprintf "%ss" (t.ToString("ss")) | _, 0, 0, s when s > 0 -> sprintf "%ss" (t.ToString("s\\.ff")) @@ -38,10 +40,10 @@ module Extensions = | _, _, _, s when s > 0 -> sprintf "%s" (t.ToString("s")) | _ -> String.Empty - $"{prependText}{hoursText}{minsText}{secsText}" + $"{prependText}{hourText}{minText}{secText}" module Startwatch = type Watch() = let startedAt = Stopwatch.GetTimestamp() - member this.ElapsedFriendly = Stopwatch.GetElapsedTime(startedAt) + member _.ElapsedFriendly = Stopwatch.GetElapsedTime(startedAt) diff --git a/Startwatch.Tests/Tests.fs b/Startwatch.Tests/Tests.fs index e7b3654..a26cf4a 100644 --- a/Startwatch.Tests/Tests.fs +++ b/Startwatch.Tests/Tests.fs @@ -4,6 +4,18 @@ open System open Xunit open Startwatch.Library.Extensions +[] +let ``Throws for negative TimeSpans`` () = + let timeSpan = TimeSpan.FromTicks(-7) + Assert.Throws(fun _ -> timeSpan.ElapsedFriendly() :> obj) + +[] +let ``Zero timespan`` () = + let timeSpan = TimeSpan.Zero // 1 tick == 100 nanoseconds + let actual = timeSpan.ElapsedFriendly() + let expected = "no time" + Assert.Equal(expected, actual) + [] let ``Nanoseconds with 3 digits`` () = let timeSpan = TimeSpan.FromTicks(1) // 1 tick == 100 nanoseconds From 989be170c5d7eb1a829556886ddfe87ba8f4144f Mon Sep 17 00:00:00 2001 From: CodeConscious <50596087+codeconscious@users.noreply.github.com> Date: Wed, 25 Dec 2024 14:09:35 +0900 Subject: [PATCH 06/12] Relocate Watch class; readd Restart member --- Startwatch.Library/Library.fs | 10 ++++++---- Startwatch.Library/Startwatch.Library.fsproj | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/Startwatch.Library/Library.fs b/Startwatch.Library/Library.fs index 6ce0613..1d119f3 100644 --- a/Startwatch.Library/Library.fs +++ b/Startwatch.Library/Library.fs @@ -42,8 +42,10 @@ module Extensions = $"{prependText}{hourText}{minText}{secText}" -module Startwatch = - type Watch() = - let startedAt = Stopwatch.GetTimestamp() +type Watch() = + let mutable startedAt = Stopwatch.GetTimestamp() - member _.ElapsedFriendly = Stopwatch.GetElapsedTime(startedAt) + member _.ElapsedFriendly = Stopwatch.GetElapsedTime(startedAt) + + member _.Restart = + startedAt <- Stopwatch.GetTimestamp() diff --git a/Startwatch.Library/Startwatch.Library.fsproj b/Startwatch.Library/Startwatch.Library.fsproj index cdba923..c10db64 100644 --- a/Startwatch.Library/Startwatch.Library.fsproj +++ b/Startwatch.Library/Startwatch.Library.fsproj @@ -4,7 +4,7 @@ net9.0 true CodeConscious.Startwatch - 0.0.5 + 0.0.6 CodeConscious CodeConscious A simple wrapper for System.Diagnostics.Stopwatch. From 4128ae54654d9b5d6547ace61f70be0d46116f5c Mon Sep 17 00:00:00 2001 From: CodeConscious <50596087+codeconscious@users.noreply.github.com> Date: Wed, 25 Dec 2024 20:29:34 +0900 Subject: [PATCH 07/12] Fix ElapsedFriendly; add TimeSpan.MaxValue test --- Startwatch.Library/Library.fs | 8 +++++++- Startwatch.Library/Startwatch.Library.fsproj | 2 +- Startwatch.Tests/Tests.fs | 7 +++++++ 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/Startwatch.Library/Library.fs b/Startwatch.Library/Library.fs index 1d119f3..05d6266 100644 --- a/Startwatch.Library/Library.fs +++ b/Startwatch.Library/Library.fs @@ -42,10 +42,16 @@ module Extensions = $"{prependText}{hourText}{minText}{secText}" +open Extensions + type Watch() = + let mutable startedAt = Stopwatch.GetTimestamp() - member _.ElapsedFriendly = Stopwatch.GetElapsedTime(startedAt) + member _.ElapsedFriendly = + Stopwatch + .GetElapsedTime(startedAt) + .ElapsedFriendly() member _.Restart = startedAt <- Stopwatch.GetTimestamp() diff --git a/Startwatch.Library/Startwatch.Library.fsproj b/Startwatch.Library/Startwatch.Library.fsproj index c10db64..ea64785 100644 --- a/Startwatch.Library/Startwatch.Library.fsproj +++ b/Startwatch.Library/Startwatch.Library.fsproj @@ -4,7 +4,7 @@ net9.0 true CodeConscious.Startwatch - 0.0.6 + 0.0.7-beta CodeConscious CodeConscious A simple wrapper for System.Diagnostics.Stopwatch. diff --git a/Startwatch.Tests/Tests.fs b/Startwatch.Tests/Tests.fs index a26cf4a..d9c4edb 100644 --- a/Startwatch.Tests/Tests.fs +++ b/Startwatch.Tests/Tests.fs @@ -246,3 +246,10 @@ let ``Triple-digit day with minutes, hours, and seconds`` () = let actual = timeSpan.ElapsedFriendly() let expected = "2,423h59m59s" Assert.Equal(expected, actual) + +[] +let ``Maximum TimeSpan`` () = + let timeSpan = TimeSpan.MaxValue + let actual = timeSpan.ElapsedFriendly() + let expected = "256,204,778h48m05s" + Assert.Equal(expected, actual) From 268d50564e582d9882d52d86f8865440f4aaf8bf Mon Sep 17 00:00:00 2001 From: CodeConscious <50596087+codeconscious@users.noreply.github.com> Date: Wed, 25 Dec 2024 21:43:26 +0900 Subject: [PATCH 08/12] Refactor formatting not to be an extension method --- Startwatch.Library/Library.fs | 84 +++++++++++++++++------------------ Startwatch.Tests/Tests.fs | 80 ++++++++++++++++----------------- 2 files changed, 80 insertions(+), 84 deletions(-) diff --git a/Startwatch.Library/Library.fs b/Startwatch.Library/Library.fs index 05d6266..855cca9 100644 --- a/Startwatch.Library/Library.fs +++ b/Startwatch.Library/Library.fs @@ -3,55 +3,51 @@ open System open System.Diagnostics -module Extensions = - type TimeSpan with - member this.ElapsedFriendly() = - match this with - | t when t.Ticks < 0 -> raise <| NotSupportedException("Negative TimeSpans are currently not supported.") - | t when t.Ticks = 0 -> "no time" - | t when t.TotalMilliseconds < 1 -> sprintf "%s" (t.TotalNanoseconds.ToString("#,##0ns")) - | t when t.TotalMilliseconds < 1000 -> sprintf "%s" (t.TotalMilliseconds.ToString("#,##0ms")) - | t -> - let days = t.Days - let hours = t.Hours - let mins = t.Minutes - let secs = t.Seconds - - let prependText = if secs = 0 then "exactly " else String.Empty - - let hourText = - match (days, hours) with - | d, _ when d > 0 -> sprintf "%s" ((hours + (days * 24)).ToString("#,##0h")) - | _, h when h > 0 -> sprintf "%s" ((hours + (days * 24)).ToString("#,##0h")) - | _ -> String.Empty - - let minText = - match (hours, mins) with - | h, m when h > 0 && m > 0 -> sprintf "%s" (m.ToString("00m")) - | _, m when m > 0 -> sprintf "%s" (m.ToString("0m")) - | _ -> String.Empty - - let secText = - match (days, hours, mins, secs) with - | d, _, 0, s when d > 0 && s > 0 -> sprintf "%ss" (t.ToString("ss")) - | _, 0, 0, s when s > 0 -> sprintf "%ss" (t.ToString("s\\.ff")) - | _, h, _, s when h > 0 && s > 0 -> sprintf "%ss" (t.ToString("ss")) - | _, _, m, s when m > 0 && s > 0 -> sprintf "%ss" (t.ToString("ss")) - | _, _, _, s when s > 0 -> sprintf "%s" (t.ToString("s")) - | _ -> String.Empty - - $"{prependText}{hourText}{minText}{secText}" - -open Extensions +module Logic = + let format (timespan: TimeSpan) = + match timespan with + | t when t.Ticks < 0 -> raise <| NotSupportedException("Negative TimeSpans are currently not supported.") + | t when t.Ticks = 0 -> "no time" + | t when t.TotalMilliseconds < 1 -> sprintf "%s" (t.TotalNanoseconds.ToString("#,##0ns")) + | t when t.TotalMilliseconds < 1000 -> sprintf "%s" (t.TotalMilliseconds.ToString("#,##0ms")) + | t -> + let days = t.Days + let hours = t.Hours + let mins = t.Minutes + let secs = t.Seconds + + let prependText = if secs = 0 then "exactly " else String.Empty + + let hourText = + match (days, hours) with + | d, _ when d > 0 -> sprintf "%s" ((hours + (days * 24)).ToString("#,##0h")) + | _, h when h > 0 -> sprintf "%s" ((hours + (days * 24)).ToString("#,##0h")) + | _ -> String.Empty + + let minText = + match (hours, mins) with + | h, m when h > 0 && m > 0 -> sprintf "%s" (m.ToString("00m")) + | _, m when m > 0 -> sprintf "%s" (m.ToString("0m")) + | _ -> String.Empty + + let secText = + match (days, hours, mins, secs) with + | d, _, 0, s when d > 0 && s > 0 -> sprintf "%ss" (t.ToString("ss")) + | _, 0, 0, s when s > 0 -> sprintf "%ss" (t.ToString("s\\.ff")) + | _, h, _, s when h > 0 && s > 0 -> sprintf "%ss" (t.ToString("ss")) + | _, _, m, s when m > 0 && s > 0 -> sprintf "%ss" (t.ToString("ss")) + | _, _, _, s when s > 0 -> sprintf "%s" (t.ToString("s")) + | _ -> String.Empty + + $"{prependText}{hourText}{minText}{secText}" + +open Logic type Watch() = - let mutable startedAt = Stopwatch.GetTimestamp() member _.ElapsedFriendly = - Stopwatch - .GetElapsedTime(startedAt) - .ElapsedFriendly() + format <| Stopwatch.GetElapsedTime(startedAt) member _.Restart = startedAt <- Stopwatch.GetTimestamp() diff --git a/Startwatch.Tests/Tests.fs b/Startwatch.Tests/Tests.fs index d9c4edb..cd0d2bf 100644 --- a/Startwatch.Tests/Tests.fs +++ b/Startwatch.Tests/Tests.fs @@ -2,59 +2,59 @@ open System open Xunit -open Startwatch.Library.Extensions +open Startwatch.Library.Logic [] let ``Throws for negative TimeSpans`` () = let timeSpan = TimeSpan.FromTicks(-7) - Assert.Throws(fun _ -> timeSpan.ElapsedFriendly() :> obj) + Assert.Throws(fun _ -> format timeSpan :> obj) [] -let ``Zero timespan`` () = +let ``Zero timespans`` () = let timeSpan = TimeSpan.Zero // 1 tick == 100 nanoseconds - let actual = timeSpan.ElapsedFriendly() + let actual = format timeSpan let expected = "no time" Assert.Equal(expected, actual) [] -let ``Nanoseconds with 3 digits`` () = +let ``Three-digit nanoseconds`` () = let timeSpan = TimeSpan.FromTicks(1) // 1 tick == 100 nanoseconds - let actual = timeSpan.ElapsedFriendly() + let actual = format timeSpan let expected = "100ns" Assert.Equal(expected, actual) [] -let ``Nanoseconds with 5 digits`` () = +let ``Five-digit nanoseconds`` () = let timeSpan = TimeSpan.FromTicks(100L) // 1 tick == 100 nanoseconds - let actual = timeSpan.ElapsedFriendly() + let actual = format timeSpan let expected = "10,000ns" Assert.Equal(expected, actual) [] let ``Single-digit milliseconds`` () = let timeSpan = TimeSpan.FromMilliseconds(1.0) - let actual = timeSpan.ElapsedFriendly() + let actual = format timeSpan let expected = "1ms" Assert.Equal(expected, actual) [] let ``Double-digit milliseconds`` () = let timeSpan = TimeSpan.FromMilliseconds(99.0) - let actual = timeSpan.ElapsedFriendly() + let actual = format timeSpan let expected = "99ms" Assert.Equal(expected, actual) [] let ``Double-digit milliseconds with a small decimal`` () = let timeSpan = TimeSpan.FromMilliseconds(99.2) - let actual = timeSpan.ElapsedFriendly() + let actual = format timeSpan let expected = "99ms" Assert.Equal(expected, actual) [] let ``Double-digit milliseconds with a large decimal`` () = let timeSpan = TimeSpan.FromMilliseconds(99.9) - let actual = timeSpan.ElapsedFriendly() + let actual = format timeSpan let expected = "100ms" Assert.Equal(expected, actual) @@ -62,194 +62,194 @@ let ``Double-digit milliseconds with a large decimal`` () = [] let ``Triple-digit milliseconds`` () = let timeSpan = TimeSpan.FromMilliseconds(999.0) - let actual = timeSpan.ElapsedFriendly() + let actual = format timeSpan let expected = "999ms" Assert.Equal(expected, actual) [] let ``Triple-digit milliseconds with a small decimal`` () = let timeSpan = TimeSpan.FromMilliseconds(999.3) - let actual = timeSpan.ElapsedFriendly() + let actual = format timeSpan let expected = "999ms" Assert.Equal(expected, actual) [] let ``Triple-digit milliseconds with a large decimal`` () = let timeSpan = TimeSpan.FromMilliseconds(999.9) - let actual = timeSpan.ElapsedFriendly() + let actual = format timeSpan let expected = "1,000ms" Assert.Equal(expected, actual) [] let ``Single-digit seconds`` () = let timeSpan = TimeSpan(0, 0, 3) - let actual = timeSpan.ElapsedFriendly() + let actual = format timeSpan let expected = "3.00s" Assert.Equal(expected, actual) [] let ``Single-digit seconds with 500 milliseconds`` () = let timeSpan = TimeSpan(0, 0, 0, 3, 500) - let actual = timeSpan.ElapsedFriendly() + let actual = format timeSpan let expected = "3.50s" Assert.Equal(expected, actual) [] let ``Single-digit seconds with 520 milliseconds`` () = let timeSpan = TimeSpan(0, 0, 0, 3, 520) - let actual = timeSpan.ElapsedFriendly() + let actual = format timeSpan let expected = "3.52s" Assert.Equal(expected, actual) [] let ``Single-digit minute with no seconds`` () = let timeSpan = TimeSpan(0, 1, 0) - let actual = timeSpan.ElapsedFriendly() + let actual = format timeSpan let expected = "exactly 1m" Assert.Equal(expected, actual) [] let ``Single-digit minute with single-digit seconds`` () = let timeSpan = TimeSpan(0, 1, 5) - let actual = timeSpan.ElapsedFriendly() + let actual = format timeSpan let expected = "1m05s" Assert.Equal(expected, actual) [] let ``Single-digit minute with double-digit seconds`` () = let timeSpan = TimeSpan(0, 1, 30) - let actual = timeSpan.ElapsedFriendly() + let actual = format timeSpan let expected = "1m30s" Assert.Equal(expected, actual) [] let ``Single-digit minute with maximum seconds`` () = let timeSpan = TimeSpan(0, 1, 59) - let actual = timeSpan.ElapsedFriendly() + let actual = format timeSpan let expected = "1m59s" Assert.Equal(expected, actual) [] let ``Two-digit minutes with no seconds`` () = let timeSpan = TimeSpan(0, 59, 0) - let actual = timeSpan.ElapsedFriendly() + let actual = format timeSpan let expected = "exactly 59m" Assert.Equal(expected, actual) [] let ``Two-digit minutes with 30 seconds`` () = let timeSpan = TimeSpan(0, 59, 30) - let actual = timeSpan.ElapsedFriendly() + let actual = format timeSpan let expected = "59m30s" Assert.Equal(expected, actual) [] let ``Single-digit hour with no minutes or seconds`` () = let timeSpan = TimeSpan(7, 0, 0) - let actual = timeSpan.ElapsedFriendly() + let actual = format timeSpan let expected = "exactly 7h" Assert.Equal(expected, actual) [] let ``Single-digit hour with single-digit minutes but no seconds`` () = let timeSpan = TimeSpan(7, 7, 0) - let actual = timeSpan.ElapsedFriendly() + let actual = format timeSpan let expected = "exactly 7h07m" Assert.Equal(expected, actual) [] let ``Single-digit hour with single-digit minutes and single-digit seconds`` () = let timeSpan = TimeSpan(7, 7, 4) - let actual = timeSpan.ElapsedFriendly() + let actual = format timeSpan let expected = "7h07m04s" Assert.Equal(expected, actual) let ``Single-digit hour with double-digit minutes but no seconds`` () = let timeSpan = TimeSpan(7, 20, 0) - let actual = timeSpan.ElapsedFriendly() + let actual = format timeSpan let expected = "exactly 7h20m" Assert.Equal(expected, actual) [] let ``Single-digit hour without minutes but with seconds`` () = let timeSpan = TimeSpan(7, 0, 47) - let actual = timeSpan.ElapsedFriendly() + let actual = format timeSpan let expected = "7h47s" Assert.Equal(expected, actual) [] let ``Double-digit hour with no minutes or seconds`` () = let timeSpan = TimeSpan(13, 0, 0) - let actual = timeSpan.ElapsedFriendly() + let actual = format timeSpan let expected = "exactly 13h" Assert.Equal(expected, actual) [] let ``Single-digit hour with single-digit minutes and with seconds`` () = let timeSpan = TimeSpan(1, 3, 8) - let actual = timeSpan.ElapsedFriendly() + let actual = format timeSpan let expected = "1h03m08s" Assert.Equal(expected, actual) [] let ``Single-digit hour with double-digit minutes and with seconds`` () = let timeSpan = TimeSpan(1, 55, 8) - let actual = timeSpan.ElapsedFriendly() + let actual = format timeSpan let expected = "1h55m08s" Assert.Equal(expected, actual) [] let ``Double-digit hour with single-digit minutes and with seconds`` () = let timeSpan = TimeSpan(12, 5, 8) - let actual = timeSpan.ElapsedFriendly() + let actual = format timeSpan let expected = "12h05m08s" Assert.Equal(expected, actual) [] let ``Double-digit hour over 24 hours with maximum minutes and seconds`` () = let timeSpan = TimeSpan(36, 59, 59) - let actual = timeSpan.ElapsedFriendly() + let actual = format timeSpan let expected = "36h59m59s" Assert.Equal(expected, actual) [] let ``Single-digit day with no minutes, hours, or seconds`` () = let timeSpan = TimeSpan(10, 0, 0, 0) - let actual = timeSpan.ElapsedFriendly() + let actual = format timeSpan let expected = "exactly 240h" Assert.Equal(expected, actual) [] let ``Single-digit day with no minutes, hours, but with single-digit seconds`` () = let timeSpan = TimeSpan(10, 0, 0, 2) - let actual = timeSpan.ElapsedFriendly() + let actual = format timeSpan let expected = "240h02s" Assert.Equal(expected, actual) [] let ``Single-digit day with no hours, but with single-digit minutes seconds`` () = let timeSpan = TimeSpan(10, 0, 0, 2) - let actual = timeSpan.ElapsedFriendly() + let actual = format timeSpan let expected = "240h02s" Assert.Equal(expected, actual) [] let ``Single-digit day with minutes, hours, and seconds`` () = let timeSpan = TimeSpan(4, 4, 59, 59) - let actual = timeSpan.ElapsedFriendly() + let actual = format timeSpan let expected = "100h59m59s" Assert.Equal(expected, actual) [] let ``Triple-digit day with minutes, hours, and seconds`` () = let timeSpan = TimeSpan(100, 23, 59, 59) - let actual = timeSpan.ElapsedFriendly() + let actual = format timeSpan let expected = "2,423h59m59s" Assert.Equal(expected, actual) [] let ``Maximum TimeSpan`` () = let timeSpan = TimeSpan.MaxValue - let actual = timeSpan.ElapsedFriendly() + let actual = format timeSpan let expected = "256,204,778h48m05s" Assert.Equal(expected, actual) From 43efd9309f6f17a3398b5af0ea9421014c689e67 Mon Sep 17 00:00:00 2001 From: CodeConscious <50596087+codeconscious@users.noreply.github.com> Date: Thu, 26 Dec 2024 18:19:04 +0900 Subject: [PATCH 09/12] Update logic and tests for tiny times; add comments --- Startwatch.Library/Library.fs | 20 ++++++++-- Startwatch.Library/Startwatch.Library.fsproj | 3 +- Startwatch.Tests/Tests.fs | 41 +++++++++++++++----- 3 files changed, 49 insertions(+), 15 deletions(-) diff --git a/Startwatch.Library/Library.fs b/Startwatch.Library/Library.fs index 855cca9..30125e5 100644 --- a/Startwatch.Library/Library.fs +++ b/Startwatch.Library/Library.fs @@ -6,10 +6,18 @@ open System.Diagnostics module Logic = let format (timespan: TimeSpan) = match timespan with - | t when t.Ticks < 0 -> raise <| NotSupportedException("Negative TimeSpans are currently not supported.") - | t when t.Ticks = 0 -> "no time" - | t when t.TotalMilliseconds < 1 -> sprintf "%s" (t.TotalNanoseconds.ToString("#,##0ns")) - | t when t.TotalMilliseconds < 1000 -> sprintf "%s" (t.TotalMilliseconds.ToString("#,##0ms")) + | t when t.Ticks < 0 -> + raise <| NotSupportedException("Negative TimeSpans are currently not supported.") + | t when t.Ticks = 0 -> + "no time" + | t when t.TotalMilliseconds < 0.01 -> + sprintf "%s (%s)" + (t.TotalMilliseconds.ToString("#,##0.#####ms")) + (t.TotalNanoseconds.ToString("#,##0ns")) + | t when t.TotalMilliseconds < 1 -> + sprintf "%s" (t.TotalMilliseconds.ToString("#,##0.#####ms")) + | t when t.TotalMilliseconds < 1000 -> + sprintf "%s" (t.TotalMilliseconds.ToString("#,##0ms")) | t -> let days = t.Days let hours = t.Hours @@ -43,11 +51,15 @@ module Logic = open Logic +/// A simple wrapper for `System.Diagnostic.Stopwatch` class saves its start time +/// and provides friendly representations of the elapsed time. type Watch() = let mutable startedAt = Stopwatch.GetTimestamp() + /// Returns a formatted "friendly" version of the elapsed time. member _.ElapsedFriendly = format <| Stopwatch.GetElapsedTime(startedAt) + /// Sets the start time for this instance to the current time. member _.Restart = startedAt <- Stopwatch.GetTimestamp() diff --git a/Startwatch.Library/Startwatch.Library.fsproj b/Startwatch.Library/Startwatch.Library.fsproj index ea64785..3043eed 100644 --- a/Startwatch.Library/Startwatch.Library.fsproj +++ b/Startwatch.Library/Startwatch.Library.fsproj @@ -4,7 +4,7 @@ net9.0 true CodeConscious.Startwatch - 0.0.7-beta + 0.0.7-rc CodeConscious CodeConscious A simple wrapper for System.Diagnostics.Stopwatch. @@ -13,6 +13,7 @@ readme.md MIT true + true Startwatch.Library diff --git a/Startwatch.Tests/Tests.fs b/Startwatch.Tests/Tests.fs index cd0d2bf..4fcda03 100644 --- a/Startwatch.Tests/Tests.fs +++ b/Startwatch.Tests/Tests.fs @@ -6,33 +6,54 @@ open Startwatch.Library.Logic [] let ``Throws for negative TimeSpans`` () = - let timeSpan = TimeSpan.FromTicks(-7) + let timeSpan = TimeSpan.FromTicks(-8001) Assert.Throws(fun _ -> format timeSpan :> obj) [] let ``Zero timespans`` () = - let timeSpan = TimeSpan.Zero // 1 tick == 100 nanoseconds + let timeSpan = TimeSpan.Zero let actual = format timeSpan let expected = "no time" Assert.Equal(expected, actual) [] -let ``Three-digit nanoseconds`` () = - let timeSpan = TimeSpan.FromTicks(1) // 1 tick == 100 nanoseconds +let ``Millisecond ten-thousandths (with nanoseconds)`` () = + let timeSpan = TimeSpan.FromMilliseconds(0.0001) let actual = format timeSpan - let expected = "100ns" + let expected = "0.0001ms (100ns)" Assert.Equal(expected, actual) [] -let ``Five-digit nanoseconds`` () = - let timeSpan = TimeSpan.FromTicks(100L) // 1 tick == 100 nanoseconds +let ``Millisecond thousandths (with nanoseconds)`` () = + let timeSpan = TimeSpan.FromMilliseconds(0.001) let actual = format timeSpan - let expected = "10,000ns" + let expected = "0.001ms (1,000ns)" Assert.Equal(expected, actual) [] -let ``Single-digit milliseconds`` () = - let timeSpan = TimeSpan.FromMilliseconds(1.0) +let ``Millisecond hundredths`` () = + let timeSpan = TimeSpan.FromMilliseconds(0.01) + let actual = format timeSpan + let expected = "0.01ms" + Assert.Equal(expected, actual) + +[] +let ``Millisecond tenths`` () = + let timeSpan = TimeSpan.FromMilliseconds(0.1) + let actual = format timeSpan + let expected = "0.1ms" + Assert.Equal(expected, actual) + +[] +let ``Milliseconds with 4 decimals`` () = + let timeSpan = TimeSpan.FromMilliseconds(0.9598) + let actual = format timeSpan + let expected = "0.9598ms" + Assert.Equal(expected, actual) + +[] +let ``One millisecond`` () = + let timeSpan = TimeSpan.FromMilliseconds(1) let actual = format timeSpan let expected = "1ms" Assert.Equal(expected, actual) From ed5012b93dc8b98aacac9edfbf79a2d419143fc8 Mon Sep 17 00:00:00 2001 From: CodeConscious <50596087+codeconscious@users.noreply.github.com> Date: Thu, 26 Dec 2024 20:27:30 +0900 Subject: [PATCH 10/12] Update version and readme --- Startwatch.Library/Startwatch.Library.fsproj | 3 ++- Startwatch.Library/docs/readme.md | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Startwatch.Library/Startwatch.Library.fsproj b/Startwatch.Library/Startwatch.Library.fsproj index 3043eed..16ab552 100644 --- a/Startwatch.Library/Startwatch.Library.fsproj +++ b/Startwatch.Library/Startwatch.Library.fsproj @@ -4,7 +4,7 @@ net9.0 true CodeConscious.Startwatch - 0.0.7-rc + 1.0.0-alpha CodeConscious CodeConscious A simple wrapper for System.Diagnostics.Stopwatch. @@ -14,6 +14,7 @@ MIT true true + Rewrote in F# and changed the display of very short times. Startwatch.Library diff --git a/Startwatch.Library/docs/readme.md b/Startwatch.Library/docs/readme.md index 8e8b148..035a69c 100644 --- a/Startwatch.Library/docs/readme.md +++ b/Startwatch.Library/docs/readme.md @@ -1,7 +1,7 @@ # Startwatch -This is a very simple wrapper around `System.Diagnostics.Stopwatch`. Since its stopwatch starts automatically, its name is "Startwatch." ^_^ +This is a very simple wrapper around the `System.Diagnostics.Stopwatch` class that just shows "friendly" versions of elapsed times via its `GetElapsedTime` property. -I largely created this to get some experience uploading a package to Nuget.org, but also to use in some of my personal projects. +Since it starts tracking time at class instantiation, its name is "Startwatch." ^_^ -Note that the API of this library could change significantly at any moment. +I largely created this to get some experience uploading packages to Nuget.org, but I also use it in some of my personal projects. From fcb84f0a81a8cd2a71f788d3fb0fb2b18fff70e4 Mon Sep 17 00:00:00 2001 From: CodeConscious <50596087+codeconscious@users.noreply.github.com> Date: Fri, 27 Dec 2024 12:09:32 +0900 Subject: [PATCH 11/12] Move test up --- Startwatch.Tests/Tests.fs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/Startwatch.Tests/Tests.fs b/Startwatch.Tests/Tests.fs index 4fcda03..dc85600 100644 --- a/Startwatch.Tests/Tests.fs +++ b/Startwatch.Tests/Tests.fs @@ -23,6 +23,14 @@ let ``Millisecond ten-thousandths (with nanoseconds)`` () = let expected = "0.0001ms (100ns)" Assert.Equal(expected, actual) + +[] +let ``Milliseconds with 4 decimals`` () = + let timeSpan = TimeSpan.FromMilliseconds(0.9598) + let actual = format timeSpan + let expected = "0.9598ms" + Assert.Equal(expected, actual) + [] let ``Millisecond thousandths (with nanoseconds)`` () = let timeSpan = TimeSpan.FromMilliseconds(0.001) @@ -44,13 +52,6 @@ let ``Millisecond tenths`` () = let expected = "0.1ms" Assert.Equal(expected, actual) -[] -let ``Milliseconds with 4 decimals`` () = - let timeSpan = TimeSpan.FromMilliseconds(0.9598) - let actual = format timeSpan - let expected = "0.9598ms" - Assert.Equal(expected, actual) - [] let ``One millisecond`` () = let timeSpan = TimeSpan.FromMilliseconds(1) From cad97927d2252c70394b875211f2daf4d90a025d Mon Sep 17 00:00:00 2001 From: CodeConscious <50596087+codeconscious@users.noreply.github.com> Date: Fri, 27 Dec 2024 12:13:06 +0900 Subject: [PATCH 12/12] Update to v1.0.0; add tags --- Startwatch.Library/Startwatch.Library.fsproj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Startwatch.Library/Startwatch.Library.fsproj b/Startwatch.Library/Startwatch.Library.fsproj index 16ab552..8a2c2de 100644 --- a/Startwatch.Library/Startwatch.Library.fsproj +++ b/Startwatch.Library/Startwatch.Library.fsproj @@ -4,7 +4,7 @@ net9.0 true CodeConscious.Startwatch - 1.0.0-alpha + 1.0.0 CodeConscious CodeConscious A simple wrapper for System.Diagnostics.Stopwatch. @@ -15,6 +15,7 @@ true true Rewrote in F# and changed the display of very short times. + startwatch stopwatch timer fsharp codeconscious Startwatch.Library