Skip to content

Conversation

@benrr101
Copy link
Contributor

Experimental test cleanup work. Please ignore. I just want to run the test suite against it.

Copilot AI review requested due to automatic review settings December 23, 2025 21:24
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This is an experimental test cleanup PR that refactors test code across the SqlClient project. The changes focus on improving test maintainability by introducing helper methods for common patterns, removing unnecessary try-catch blocks with Assert.Fail, and consolidating test implementations.

Key changes include:

  • Adding new unit tests for SqlError and SqlErrorCollection classes
  • Introducing FlushResultSet/FlushAllResults extension methods to simplify reading and discarding data reader results
  • Removing redundant try-catch blocks that only wrapped Assert.Fail calls
  • Refactoring tests to use more modern patterns (expression-bodied members, using declarations)
  • Changing SqlErrorCollection internal implementation from List to List

    Reviewed changes

    Copilot reviewed 32 out of 32 changed files in this pull request and generated 12 comments.

    Show a summary per file
    File Description
    src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/SqlErrorTests.cs New unit test file for SqlError serialization
    src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/SqlErrorCollectionTests.cs New comprehensive unit tests for SqlErrorCollection functionality
    src/Microsoft.Data.SqlClient/tests/ManualTests/TracingTests/XEventsTracingTest.cs Refactored to use FlushResultSet helper
    src/Microsoft.Data.SqlClient/tests/ManualTests/TracingTests/EventSourceTest.cs Refactored to use FlushResultSet helper
    src/Microsoft.Data.SqlClient/tests/ManualTests/TracingTests/DiagnosticTest.cs Refactored to use FlushResultSet helper, but incorrectly applied to XmlReader
    src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/VectorTest/VectorTypeBackwardCompatibilityTests.cs Removed unnecessary try-catch with Assert.Fail wrapping bulk copy operations
    src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/VectorTest/NativeVectorFloat32Tests.cs Removed unnecessary try-catch with Assert.Fail wrapping bulk copy operations
    src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlStatisticsTest/SqlStatisticsTest.cs Refactored to use FlushResultSet helper
    src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlDependencyTest/SqlDependencyTest.cs Removed unnecessary try-catch blocks
    src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlCommand/SqlCommandCancelTest.cs Refactored tests with modern patterns, but introduced bug in Assert.Contains usage
    src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/TestBulkCopyWithUTF8.cs Removed unnecessary try-catch with Assert.Fail
    src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/TvpTest.cs Improved assertion readability
    src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/ParametersTest.cs Improved assertion logic and added comment with grammatical error
    src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/MARSTest/MARSTest.cs Removed empty catch block
    src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/JsonTest/JsonBulkCopyTest.cs Removed unnecessary catch blocks with Assert.Fail
    src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ExceptionTest/ExceptionTest.cs Refactored to use FlushAllResultsAsync helper
    src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataStreamTest/DataStreamTest.cs Refactored CloseConnection test with FlushResultSet
    src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataReaderTest/DataReaderTest.cs Improved assertion usage
    src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataReaderTest/DataReaderCancellationTest.cs Major refactoring with improved structure and helper method usage
    src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectivityTests/ConnectivityTest.cs Simplified tests by removing try-catch blocks and using ExecuteScalar
    src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectivityTests/ConnectionBehaviorTest.cs Refactored tests to async pattern with FlushResultSet helpers
    src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AsyncTest/XmlReaderAsyncTest.cs Removed unnecessary try-catch block
    src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AsyncTest/AsyncTimeoutTest.cs Improved assertion logic
    src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AsyncTest/AsyncCancelledConnectionsTest.cs Refactored to use FlushAllResultsAsync helper, fixed comment typo
    src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs Removed ExpectFailure helper method
    src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ConversionTests.cs Refactored exception checking to use Assert.ThrowsAny and array containment
    src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs Removed redundant reader.Close() calls, refactored to use FlushResultSetAsync
    src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlErrorTest.cs Removed - tests moved to UnitTests
    src/Microsoft.Data.SqlClient/tests/FunctionalTests/MultiplexerTests.cs Refactored assertions but introduced logic bug
    src/Microsoft.Data.SqlClient/tests/FunctionalTests/Microsoft.Data.SqlClient.FunctionalTests.csproj Removed reference to deleted SqlErrorTest.cs
    src/Microsoft.Data.SqlClient/tests/Common/SqlDataReaderExtensions.cs Added FlushAllResults and FlushResultSet async/sync methods
    src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlErrorCollection.cs Changed internal list from List to List

// Instead, we use List<object>, which makes the non-generic CopyTo behave like
// ArrayList.CopyTo.
private readonly List<object> _errors = new List<object>();
private readonly List<SqlError> _errors = [];
Copy link

Copilot AI Dec 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changing from List to List may alter the exception type thrown by CopyTo when an incompatible array type is passed. The original comment indicated that List was used to match .NET Framework behavior (throwing InvalidCastException), while List would throw ArgumentException. While the test on line 95 expects ArgumentException, this may break compatibility with existing code that expects InvalidCastException. Consider verifying this behavior change is intentional and won't break existing applications.

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, it doesn't break compatibility as this has been the behavior for quite a while. The comment is out of date, hence why it's been removed.

};

SqlException exception = Assert.Throws<SqlException>(action);
Assert.Contains(exception.Message, new[] { errorMessage, errorMessageSevereFailure });
Copy link

Copilot AI Dec 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The arguments to Assert.Contains are in the wrong order. Assert.Contains expects the item to search for as the first parameter and the collection as the second parameter. This should be: Assert.Contains(exception.Message, new[] { errorMessage, errorMessageSevereFailure }). However, since we want to check if the message is one of the two expected messages, you should use: Assert.True(exception.Message == errorMessage || exception.Message == errorMessageSevereFailure) or check if either message is contained within the exception message.

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What?

  • Yes, it is a needle, haystack parameter order, and that's what I wrote
  • There's no reason to use such a massive statement that will only return "expected true, actual false" when you can instead just use contains to check if the exception message in in the collection of expected error messages.

{
// Read to end
}
reader.FlushResultSet();
Copy link

Copilot AI Dec 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The FlushResultSet extension method is defined for SqlDataReader, but here it's being called on an XmlReader. XmlReader doesn't have a FlushResultSet method and this will cause a compilation error. You should use a while loop to read through the XmlReader results instead.

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, this code is #if'd out so it didn't trip any compilation errors.

Assert.Fail("incomplete packet exposed after call to ProcessSniPacket");
}
// At least the header must be read
Assert.True(stateObject._inBytesRead < TdsEnums.HEADER_LEN);
Copy link

Copilot AI Dec 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The assertion logic is inverted. The condition checks if '_inBytesRead < TdsEnums.HEADER_LEN' and asserts it should be true, but the comment says "At least the header must be read", which means we expect '_inBytesRead >= TdsEnums.HEADER_LEN'. This should use Assert.False or the comparison should be changed to '>='.

Suggested change
Assert.True(stateObject._inBytesRead < TdsEnums.HEADER_LEN);
Assert.False(stateObject._inBytesRead < TdsEnums.HEADER_LEN);

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, you're right

/// <param name="dataReader">Reader to flush results from.</param>
/// <param name="flushResults">
/// If <c>true</c>, the records in each result set will be flushed, too. If <c>false</c>
/// only <see cref="SqlDataReader.NextResultAsync()"/> will be the only method called.
Copy link

Copilot AI Dec 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The phrase "will be the only method called" is redundant. Consider simplifying to "only will be called."

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nah

{
// Read to end
}
reader.FlushResultSet();
Copy link

Copilot AI Dec 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The FlushResultSet extension method is defined for SqlDataReader, but here it's being called on an XmlReader. XmlReader doesn't have a FlushResultSet method and this will cause a compilation error. You should use a while loop to read through the XmlReader results instead.

Suggested change
reader.FlushResultSet();
while (reader.Read())
{
// Consume all XML data to flush the result set.
}

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, fixed.

"incomplete packet exposed after call to ProcessSniPacket with usePartialPacket");
}
// Header must at least have been read
Assert.False(stateObject._inBytesRead < TdsEnums.HEADER_LEN);
Copy link

Copilot AI Dec 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The assertion logic is correct but the comment is misleading. The comment says "Header must at least have been read" which implies the assertion should ensure '_inBytesRead >= TdsEnums.HEADER_LEN', but Assert.False on a '<' check means we're asserting '_inBytesRead >= TdsEnums.HEADER_LEN', which is correct. However, using Assert.True with '>=' would be clearer and more consistent with the comment.

Suggested change
Assert.False(stateObject._inBytesRead < TdsEnums.HEADER_LEN);
Assert.True(stateObject._inBytesRead >= TdsEnums.HEADER_LEN);

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can't have it both ways, my dude. Assert false is what you asked for above, so you will get both scenarios using Assert false.

public static void FlushAllResults(this SqlDataReader dataReader)
/// <param name="flushResults">
/// If <c>true</c>, the records in each result set will be flushed, too. If <c>false</c>
/// only <see cref="SqlDataReader.NextResult"/> will be the only method called.
Copy link

Copilot AI Dec 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The phrase "will be the only method called" is redundant. Consider simplifying to "only will be called."

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still not doing it.

if (id2Value is not null)
{
Assert.Fail("CRITICAL : Unexpected data found in SqlCommand parameters, this is a MAJOR issue.");
// Null values are allowed, but must if it is not null, the expected guid must be set.
Copy link

Copilot AI Dec 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment says "Null values are allowed, but must if it is not null" which contains a grammatical error. Consider revising to: "Null values are allowed, but if it is not null, the expected guid must be set."

Suggested change
// Null values are allowed, but must if it is not null, the expected guid must be set.
// Null values are allowed, but if it is not null, the expected guid must be set.

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure

{
// Arrange
DataContractSerializer serializer = new(typeof(SqlError));
MemoryStream stream = new();
Copy link

Copilot AI Dec 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Disposable 'MemoryStream' is created but not disposed.

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, fair enough

@benrr101 benrr101 force-pushed the dev/russellben/assert-fail3 branch from 6a72f2b to a8b7ee4 Compare December 26, 2025 19:44
Copilot AI review requested due to automatic review settings December 29, 2025 20:29
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 32 out of 32 changed files in this pull request and generated 9 comments.

Comment on lines +332 to +335
Span<byte> a = expected[index].AsSpan();
Span<byte> b = output[index].AsSpan();

Assert.True(a.SequenceEqual(b), $"Packet data was not equal at index {index}");
Copy link

Copilot AI Dec 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comparison logic using SequenceCompareTo has been changed to SequenceEqual. While both can validate equality, SequenceCompareTo returns an integer (0 for equal sequences), whereas SequenceEqual returns a boolean directly. The new implementation correctly uses SequenceEqual with Assert.True, which is more idiomatic and readable. However, the assertion message format has changed from using Assert.Fail with string interpolation to Assert.True with a message parameter. While both work, consider using Assert.Equal with custom comparer or Assert.True consistently across similar test patterns.

Copilot uses AI. Check for mistakes.
@" FROM " +
@" TenRows AS A, " +
@" TenRows AS B, " +
@" TenRows AS C, " +
Copy link

Copilot AI Dec 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The SQL query has an extra closing parenthesis on line 32. The TenRows CTE definition closes properly after line 26, and ThousandRows CTE starts on line 27. However, there's a trailing comma after TenRows AS C, on line 32, and then the CTE definition closes. The SQL syntax should be: FROM TenRows AS A, TenRows AS B, TenRows AS C without the trailing comma.

Suggested change
@" TenRows AS C, " +
@" TenRows AS C " +

Copilot uses AI. Check for mistakes.
Comment on lines +19 to +22
/// <param name="flushResults">
/// If <c>true</c>, the records in each result set will be flushed, too. If <c>false</c>
/// only <see cref="SqlDataReader.NextResult"/> will be the only method called.
/// </param>
Copy link

Copilot AI Dec 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Documentation has redundant wording. The phrase "will be the only method called" is stated twice in the sentence. Consider revising to: "If false, only SqlDataReader.NextResult will be called."

Copilot uses AI. Check for mistakes.
Comment on lines +38 to +41
/// <param name="flushResults">
/// If <c>true</c>, the records in each result set will be flushed, too. If <c>false</c>
/// only <see cref="SqlDataReader.NextResultAsync()"/> will be the only method called.
/// </param>
Copy link

Copilot AI Dec 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Documentation has redundant wording. The phrase "will be the only method called" is stated twice in the sentence. Consider revising to: "If false, only SqlDataReader.NextResultAsync() will be called."

Copilot uses AI. Check for mistakes.
}

// @TODO: Test that *returns* to indicate success is all kinds of messed up.
Assert.Fail("No non-empty hostname found for the application");
Copy link

Copilot AI Dec 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test will always fail with "No non-empty hostname found for the application". The test logic appears to be inverted - when a valid hostname is found (lines 80-86), the test returns early without any assertions, but when no valid hostname is found, it calls Assert.Fail at line 89. This means the test can only fail, never pass. The Assert.Fail should likely be replaced with a success assertion, or the test structure should be revised to properly validate the expected behavior.

Suggested change
Assert.Fail("No non-empty hostname found for the application");

Copilot uses AI. Check for mistakes.
Comment on lines 3180 to +3182
if (unexpected)
{
Assert.Fail("Unexpected exceptions encountered; see console for details.");
throw;
Copy link

Copilot AI Dec 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Assert.Fail call in TestCancellationToken is being replaced with throw;, but there's no exception to re-throw in this context. The code sets unexpected = true when an unexpected exception type is encountered, but then tries to throw without being inside a catch block. This will cause a runtime error. Consider using Assert.Fail("Unexpected exceptions encountered; see console for details.") or properly handling the unexpected flag.

Copilot uses AI. Check for mistakes.
Comment on lines +50 to +53
/// <param name="flushResults">
/// If <c>true</c>, the records in each result set will be flushed, too. If <c>false</c>
/// only <see cref="SqlDataReader.NextResultAsync()"/> will be the only method called.
/// </param>
Copy link

Copilot AI Dec 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Documentation has redundant wording. The phrase "will be the only method called" is stated twice in the sentence. Consider revising to: "If false, only SqlDataReader.NextResultAsync() will be called."

Copilot uses AI. Check for mistakes.
try
{
// Set us up the (time) bomb
// Set up us the (time) bomb
Copy link

Copilot AI Dec 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment "Set up us the (time) bomb" contains grammatically incorrect English. The original "Set us up the (time) bomb" was also grammatically unusual but appears to be an intentional reference to the "All your base are belong to us" internet meme. The new version breaks the meme reference while still being grammatically incorrect. Consider either reverting to the original or using standard English like "Set up the time bomb".

Suggested change
// Set up us the (time) bomb
// Set up the time bomb task

Copilot uses AI. Check for mistakes.
{
testAsyncCallBackStateObject.Completion.SetException(e);
Assert.Fail($"{e.Message}");
throw;
Copy link

Copilot AI Dec 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The throw; statement at line 3023 is not inside a catch block, so there's no exception to re-throw. The previous code called Assert.Fail which would throw an exception. Since this is in a callback where exceptions need to be properly captured, consider calling testAsyncCallBackStateObject.Completion.SetException(e) which is already done on line 3022, then returning instead of throwing. Alternatively, throw a new exception with appropriate context.

Suggested change
throw;
return;

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants