From 6328b123d6907aa45aa94f50aee09197e57d714f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 8 Feb 2026 07:21:57 +0000 Subject: [PATCH 1/6] Initial plan From 470590b5b9c755b9749b8fe692591af8bfdbe1db Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 8 Feb 2026 07:23:55 +0000 Subject: [PATCH 2/6] Initial plan for ExpectThrows implementation Co-authored-by: BenjaminMichaelis <22186029+BenjaminMichaelis@users.noreply.github.com> --- :GITHUB_ENV | 1 + 1 file changed, 1 insertion(+) create mode 100644 :GITHUB_ENV diff --git a/:GITHUB_ENV b/:GITHUB_ENV new file mode 100644 index 0000000..f06e6e8 --- /dev/null +++ b/:GITHUB_ENV @@ -0,0 +1 @@ +COPILOT_SETUP_COMPLETE=true From aceb17a4ae6ed1edaec2039e0f5445badf5df207 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 8 Feb 2026 07:25:17 +0000 Subject: [PATCH 3/6] Implement ExpectThrows and ExpectThrowsAsync methods with comprehensive tests Co-authored-by: BenjaminMichaelis <22186029+BenjaminMichaelis@users.noreply.github.com> --- .../ConsoleAssertTests.cs | 106 ++++++++++++++++++ .../ConsoleAssert.cs | 70 ++++++++++++ 2 files changed, 176 insertions(+) diff --git a/IntelliTect.TestTools.Console.Tests/ConsoleAssertTests.cs b/IntelliTect.TestTools.Console.Tests/ConsoleAssertTests.cs index e6c76b5..a3558cf 100644 --- a/IntelliTect.TestTools.Console.Tests/ConsoleAssertTests.cs +++ b/IntelliTect.TestTools.Console.Tests/ConsoleAssertTests.cs @@ -232,4 +232,110 @@ public void ExecuteAsync_GivenVariableCRLFWithNLComparedToCRNL_Success() System.Console.WriteLine(output); }); } + + [TestMethod] + public void ExpectThrows_WhenExceptionIsThrown_CapturesException() + { + const string view = @"Enter a number: <>Error: Invalid input"; + + FormatException exception = ConsoleAssert.ExpectThrows(view, () => + { + System.Console.Write("Enter a number: "); + string input = System.Console.ReadLine(); + System.Console.Write("Error: Invalid input"); + int.Parse(input); // This will throw FormatException + }); + + Assert.IsNotNull(exception); + Assert.IsInstanceOfType(exception); + } + + [TestMethod] + public async Task ExpectThrowsAsync_WhenExceptionIsThrown_CapturesException() + { + const string view = @"Enter a number: <>Error: Invalid input"; + + FormatException exception = await ConsoleAssert.ExpectThrowsAsync(view, async () => + { + await Task.Yield(); + System.Console.Write("Enter a number: "); + string input = System.Console.ReadLine(); + System.Console.Write("Error: Invalid input"); + int.Parse(input); // This will throw FormatException + }); + + Assert.IsNotNull(exception); + Assert.IsInstanceOfType(exception); + } + + [TestMethod] + public void ExpectThrows_WhenNoExceptionIsThrown_ThrowsException() + { + const string view = @"Hello World"; + + Exception exception = Assert.ThrowsExactly(() => + { + ConsoleAssert.ExpectThrows(view, () => + { + System.Console.Write("Hello World"); + // No exception thrown + }); + }); + + StringAssert.Contains(exception.Message, "Expected exception of type FormatException was not thrown"); + } + + [TestMethod] + public async Task ExpectThrowsAsync_WhenNoExceptionIsThrown_ThrowsException() + { + const string view = @"Hello World"; + + Exception exception = await Assert.ThrowsExactlyAsync(async () => + { + await ConsoleAssert.ExpectThrowsAsync(view, async () => + { + await Task.Yield(); + System.Console.Write("Hello World"); + // No exception thrown + }); + }); + + StringAssert.Contains(exception.Message, "Expected exception of type FormatException was not thrown"); + } + + [TestMethod] + public void ExpectThrows_WithDifferentExceptionType_ThrowsOriginalException() + { + const string view = @"Enter a number: <>Error: Invalid input"; + + // Expecting ArgumentException but FormatException is thrown + Assert.ThrowsExactly(() => + { + ConsoleAssert.ExpectThrows(view, () => + { + System.Console.Write("Enter a number: "); + string input = System.Console.ReadLine(); + System.Console.Write("Error: Invalid input"); + int.Parse(input); // This throws FormatException, not ArgumentException + }); + }); + } + + [TestMethod] + public void ExpectThrows_WithNormalizeOptions_AppliesNormalization() + { + const string view = "Hello World\n"; + + ArgumentException exception = ConsoleAssert.ExpectThrows(view, () => + { + System.Console.WriteLine("Hello World"); + throw new ArgumentException("Test exception"); + }, NormalizeOptions.NormalizeLineEndings); + + Assert.IsNotNull(exception); + StringAssert.Contains(exception.Message, "Test exception"); + } } \ No newline at end of file diff --git a/IntelliTect.TestTools.Console/ConsoleAssert.cs b/IntelliTect.TestTools.Console/ConsoleAssert.cs index 7d23f31..c2affb2 100644 --- a/IntelliTect.TestTools.Console/ConsoleAssert.cs +++ b/IntelliTect.TestTools.Console/ConsoleAssert.cs @@ -721,4 +721,74 @@ public static Process ExecuteProcess(string expected, string fileName, string ar AssertExpectation(expected, standardOutput, (left, right) => LikeOperator(left, right), "The values are not like (using wildcards) each other"); return process; } + + /// + /// Performs a unit test on a console-based method that is expected to throw an exception. + /// A "view" of what a user would see in their console is provided as a string, + /// where their input (including line-breaks) is surrounded by double + /// less-than/greater-than signs, like so: "Input please: <<Input>>" + /// + /// The type of exception expected to be thrown + /// Expected "view" to be seen on the console, + /// including both input and output + /// Method to be run that is expected to throw an exception + /// Options to normalize input and expected output + /// The exception that was thrown + public static TException ExpectThrows(string expected, + Action action, + NormalizeOptions normalizeOptions = NormalizeOptions.Default) + where TException : Exception + { + TException caughtException = null; + try + { + Expect(expected, action, normalizeOptions); + } + catch (TException ex) + { + caughtException = ex; + } + + if (caughtException == null) + { + throw new Exception($"Expected exception of type {typeof(TException).Name} was not thrown."); + } + + return caughtException; + } + + /// + /// Performs a unit test on a console-based async method that is expected to throw an exception. + /// A "view" of what a user would see in their console is provided as a string, + /// where their input (including line-breaks) is surrounded by double + /// less-than/greater-than signs, like so: "Input please: <<Input>>" + /// + /// The type of exception expected to be thrown + /// Expected "view" to be seen on the console, + /// including both input and output + /// Async method to be run that is expected to throw an exception + /// Options to normalize input and expected output + /// The exception that was thrown + public static async Task ExpectThrowsAsync(string expected, + Func action, + NormalizeOptions normalizeOptions = NormalizeOptions.Default) + where TException : Exception + { + TException caughtException = null; + try + { + await ExpectAsync(expected, action, normalizeOptions); + } + catch (TException ex) + { + caughtException = ex; + } + + if (caughtException == null) + { + throw new Exception($"Expected exception of type {typeof(TException).Name} was not thrown."); + } + + return caughtException; + } } \ No newline at end of file From c8f3d1a4a7c93ee7b431bc48dcc3312206c7ca0f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 8 Feb 2026 07:26:41 +0000 Subject: [PATCH 4/6] Refactor ExpectThrows methods to eliminate nullable warnings Co-authored-by: BenjaminMichaelis <22186029+BenjaminMichaelis@users.noreply.github.com> --- .../ConsoleAssert.cs | 20 ++++--------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/IntelliTect.TestTools.Console/ConsoleAssert.cs b/IntelliTect.TestTools.Console/ConsoleAssert.cs index c2affb2..a91491c 100644 --- a/IntelliTect.TestTools.Console/ConsoleAssert.cs +++ b/IntelliTect.TestTools.Console/ConsoleAssert.cs @@ -739,22 +739,16 @@ public static TException ExpectThrows(string expected, NormalizeOptions normalizeOptions = NormalizeOptions.Default) where TException : Exception { - TException caughtException = null; try { Expect(expected, action, normalizeOptions); } catch (TException ex) { - caughtException = ex; + return ex; } - if (caughtException == null) - { - throw new Exception($"Expected exception of type {typeof(TException).Name} was not thrown."); - } - - return caughtException; + throw new Exception($"Expected exception of type {typeof(TException).Name} was not thrown."); } /// @@ -774,21 +768,15 @@ public static async Task ExpectThrowsAsync(string expect NormalizeOptions normalizeOptions = NormalizeOptions.Default) where TException : Exception { - TException caughtException = null; try { await ExpectAsync(expected, action, normalizeOptions); } catch (TException ex) { - caughtException = ex; - } - - if (caughtException == null) - { - throw new Exception($"Expected exception of type {typeof(TException).Name} was not thrown."); + return ex; } - return caughtException; + throw new Exception($"Expected exception of type {typeof(TException).Name} was not thrown."); } } \ No newline at end of file From 40f4b32777ad0d2b2062d203ec989155e0b61db8 Mon Sep 17 00:00:00 2001 From: Benjamin Michaelis Date: Sat, 7 Feb 2026 23:27:12 -0800 Subject: [PATCH 5/6] Delete :GITHUB_ENV --- :GITHUB_ENV | 1 - 1 file changed, 1 deletion(-) delete mode 100644 :GITHUB_ENV diff --git a/:GITHUB_ENV b/:GITHUB_ENV deleted file mode 100644 index f06e6e8..0000000 --- a/:GITHUB_ENV +++ /dev/null @@ -1 +0,0 @@ -COPILOT_SETUP_COMPLETE=true From c54cefc71997947fbc10460f3f017913de4d8fb1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 8 Feb 2026 07:29:06 +0000 Subject: [PATCH 6/6] Remove GitHub environment file and update .gitignore Co-authored-by: BenjaminMichaelis <22186029+BenjaminMichaelis@users.noreply.github.com> --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 46f0db5..584d96c 100644 --- a/.gitignore +++ b/.gitignore @@ -214,3 +214,6 @@ ModelManifest.xml # Jetbrains .idea + +# GitHub Actions environment files +:GITHUB_ENV