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/Library.fs b/Startwatch.Library/Library.fs new file mode 100644 index 0000000..30125e5 --- /dev/null +++ b/Startwatch.Library/Library.fs @@ -0,0 +1,65 @@ +namespace Startwatch.Library + +open System +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 < 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 + 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 + +/// 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.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..8a2c2de --- /dev/null +++ b/Startwatch.Library/Startwatch.Library.fsproj @@ -0,0 +1,30 @@ + + + + net9.0 + true + CodeConscious.Startwatch + 1.0.0 + CodeConscious + CodeConscious + A simple wrapper for System.Diagnostics.Stopwatch. + https://github.com/codeconscious/startwatch + https://github.com/codeconscious/startwatch + readme.md + MIT + true + true + Rewrote in F# and changed the display of very short times. + startwatch stopwatch timer fsharp codeconscious + 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.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. diff --git a/Startwatch.Tests/Program.fs b/Startwatch.Tests/Program.fs new file mode 100644 index 0000000..52113d0 --- /dev/null +++ b/Startwatch.Tests/Program.fs @@ -0,0 +1,4 @@ +module Program + +[] +let main _ = 0 \ No newline at end of file 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/Startwatch.Tests.fsproj b/Startwatch.Tests/Startwatch.Tests.fsproj new file mode 100644 index 0000000..7859d12 --- /dev/null +++ b/Startwatch.Tests/Startwatch.Tests.fsproj @@ -0,0 +1,25 @@ + + + + net9.0 + false + false + + + + + + + + + + + + + + + + + + + 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/Tests.fs b/Startwatch.Tests/Tests.fs new file mode 100644 index 0000000..dc85600 --- /dev/null +++ b/Startwatch.Tests/Tests.fs @@ -0,0 +1,277 @@ +module Tests + +open System +open Xunit +open Startwatch.Library.Logic + +[] +let ``Throws for negative TimeSpans`` () = + let timeSpan = TimeSpan.FromTicks(-8001) + Assert.Throws(fun _ -> format timeSpan :> obj) + +[] +let ``Zero timespans`` () = + let timeSpan = TimeSpan.Zero + let actual = format timeSpan + let expected = "no time" + Assert.Equal(expected, actual) + +[] +let ``Millisecond ten-thousandths (with nanoseconds)`` () = + let timeSpan = TimeSpan.FromMilliseconds(0.0001) + let actual = format timeSpan + 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) + let actual = format timeSpan + let expected = "0.001ms (1,000ns)" + Assert.Equal(expected, actual) + +[] +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 ``One millisecond`` () = + let timeSpan = TimeSpan.FromMilliseconds(1) + let actual = format timeSpan + let expected = "1ms" + Assert.Equal(expected, actual) + +[] +let ``Double-digit milliseconds`` () = + let timeSpan = TimeSpan.FromMilliseconds(99.0) + 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 = 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 = format timeSpan + let expected = "100ms" + Assert.Equal(expected, actual) + + +[] +let ``Triple-digit milliseconds`` () = + let timeSpan = TimeSpan.FromMilliseconds(999.0) + 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 = 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 = format timeSpan + let expected = "1,000ms" + Assert.Equal(expected, actual) + +[] +let ``Single-digit seconds`` () = + let timeSpan = TimeSpan(0, 0, 3) + 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 = 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 = 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 = 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 = 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 = format timeSpan + let expected = "1m30s" + Assert.Equal(expected, actual) + +[] +let ``Single-digit minute with maximum seconds`` () = + let timeSpan = TimeSpan(0, 1, 59) + 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = format timeSpan + let expected = "2,423h59m59s" + Assert.Equal(expected, actual) + +[] +let ``Maximum TimeSpan`` () = + let timeSpan = TimeSpan.MaxValue + let actual = format timeSpan + let expected = "256,204,778h48m05s" + 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 09434bb..7f52969 100644 --- a/Startwatch.sln +++ b/Startwatch.sln @@ -3,9 +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}" +Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Startwatch.Tests", "Startwatch.Tests\Startwatch.Tests.fsproj", "{88AB89DE-6161-48E8-A95B-5E49FC2F1BED}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -13,14 +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