-
Notifications
You must be signed in to change notification settings - Fork 317
Draft | Some more test cleanup #3860
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this 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 = []; |
Copilot
AI
Dec 23, 2025
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 }); |
Copilot
AI
Dec 23, 2025
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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(); |
Copilot
AI
Dec 23, 2025
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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); |
Copilot
AI
Dec 23, 2025
There was a problem hiding this comment.
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 '>='.
| Assert.True(stateObject._inBytesRead < TdsEnums.HEADER_LEN); | |
| Assert.False(stateObject._inBytesRead < TdsEnums.HEADER_LEN); |
There was a problem hiding this comment.
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. |
Copilot
AI
Dec 23, 2025
There was a problem hiding this comment.
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."
There was a problem hiding this comment.
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(); |
Copilot
AI
Dec 23, 2025
There was a problem hiding this comment.
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.
| reader.FlushResultSet(); | |
| while (reader.Read()) | |
| { | |
| // Consume all XML data to flush the result set. | |
| } |
There was a problem hiding this comment.
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); |
Copilot
AI
Dec 23, 2025
There was a problem hiding this comment.
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.
| Assert.False(stateObject._inBytesRead < TdsEnums.HEADER_LEN); | |
| Assert.True(stateObject._inBytesRead >= TdsEnums.HEADER_LEN); |
There was a problem hiding this comment.
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. |
Copilot
AI
Dec 23, 2025
There was a problem hiding this comment.
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."
There was a problem hiding this comment.
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. |
Copilot
AI
Dec 23, 2025
There was a problem hiding this comment.
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."
| // 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. |
There was a problem hiding this comment.
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(); |
Copilot
AI
Dec 23, 2025
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, fair enough
6a72f2b to
a8b7ee4
Compare
There was a problem hiding this 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.
| 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}"); |
Copilot
AI
Dec 29, 2025
There was a problem hiding this comment.
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.
| @" FROM " + | ||
| @" TenRows AS A, " + | ||
| @" TenRows AS B, " + | ||
| @" TenRows AS C, " + |
Copilot
AI
Dec 29, 2025
There was a problem hiding this comment.
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.
| @" TenRows AS C, " + | |
| @" TenRows AS C " + |
| /// <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> |
Copilot
AI
Dec 29, 2025
There was a problem hiding this comment.
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."
| /// <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> |
Copilot
AI
Dec 29, 2025
There was a problem hiding this comment.
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."
| } | ||
|
|
||
| // @TODO: Test that *returns* to indicate success is all kinds of messed up. | ||
| Assert.Fail("No non-empty hostname found for the application"); |
Copilot
AI
Dec 29, 2025
There was a problem hiding this comment.
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.
| Assert.Fail("No non-empty hostname found for the application"); |
| if (unexpected) | ||
| { | ||
| Assert.Fail("Unexpected exceptions encountered; see console for details."); | ||
| throw; |
Copilot
AI
Dec 29, 2025
There was a problem hiding this comment.
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.
| /// <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> |
Copilot
AI
Dec 29, 2025
There was a problem hiding this comment.
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."
| try | ||
| { | ||
| // Set us up the (time) bomb | ||
| // Set up us the (time) bomb |
Copilot
AI
Dec 29, 2025
There was a problem hiding this comment.
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".
| // Set up us the (time) bomb | |
| // Set up the time bomb task |
| { | ||
| testAsyncCallBackStateObject.Completion.SetException(e); | ||
| Assert.Fail($"{e.Message}"); | ||
| throw; |
Copilot
AI
Dec 29, 2025
There was a problem hiding this comment.
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.
| throw; | |
| return; |
Experimental test cleanup work. Please ignore. I just want to run the test suite against it.