From 25d570a220fb91bfe39d988c8e7382682f012158 Mon Sep 17 00:00:00 2001 From: Ben Russell Date: Fri, 19 Dec 2025 13:52:29 -0600 Subject: [PATCH 1/9] Move SqlErrorTest to unit test project --- ...soft.Data.SqlClient.FunctionalTests.csproj | 1 - .../tests/FunctionalTests/SqlErrorTest.cs | 71 ------------------- .../Microsoft/Data/SqlClient/SqlErrorTests.cs | 56 +++++++++++++++ 3 files changed, 56 insertions(+), 72 deletions(-) delete mode 100644 src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlErrorTest.cs create mode 100644 src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/SqlErrorTests.cs diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/Microsoft.Data.SqlClient.FunctionalTests.csproj b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/Microsoft.Data.SqlClient.FunctionalTests.csproj index 7f6d8abd2c..c967d800d5 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/Microsoft.Data.SqlClient.FunctionalTests.csproj +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/Microsoft.Data.SqlClient.FunctionalTests.csproj @@ -47,7 +47,6 @@ - diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlErrorTest.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlErrorTest.cs deleted file mode 100644 index ee05379870..0000000000 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlErrorTest.cs +++ /dev/null @@ -1,71 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.IO; -using System.Reflection; -using System.Runtime.Serialization; -using Xunit; - -namespace Microsoft.Data.SqlClient.Tests -{ - public class SqlErrorTest - { - private const string SQLMSF_FailoverPartnerNotSupported = - "Connecting to a mirrored SQL Server instance using the MultiSubnetFailover connection option is not supported."; - private const byte FATAL_ERROR_CLASS = 20; - -#if NETFRAMEWORK - [Fact] - public static void SqlErrorSerializationTest() - { - DataContractSerializer serializer = new DataContractSerializer(typeof(SqlError)); - SqlError expected = CreateError(); - SqlError actual = null; - using (var stream = new MemoryStream()) - { - try - { - serializer.WriteObject(stream, expected); - stream.Position = 0; - actual = (SqlError)serializer.ReadObject(stream); - } - catch (Exception ex) - { - Assert.Fail($"Unexpected Exception occurred: {ex.Message}"); - } - } - - Assert.Equal(expected.Message, actual.Message); - Assert.Equal(expected.Number, actual.Number); - Assert.Equal(expected.State, actual.State); - Assert.Equal(expected.Class, actual.Class); - Assert.Equal(expected.Server, actual.Server); - Assert.Equal(expected.Procedure, actual.Procedure); - Assert.Equal(expected.LineNumber, actual.LineNumber); - Assert.Equal(expected.Source, actual.Source); - } -#endif - - - private static SqlError CreateError() - { - string msg = SQLMSF_FailoverPartnerNotSupported; - - Type sqlErrorType = typeof(SqlError); - - // SqlError only has internal constructors, in order to instantiate this, we use reflection - SqlError sqlError = (SqlError)sqlErrorType.Assembly.CreateInstance( - sqlErrorType.FullName, - false, - BindingFlags.Instance | BindingFlags.NonPublic, - null, - new object[] { 100, (byte)0x00, FATAL_ERROR_CLASS, "ServerName", msg, "ProcedureName", 10, null, -1 }, - null, - null); - - return sqlError; - } - } -} diff --git a/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/SqlErrorTests.cs b/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/SqlErrorTests.cs new file mode 100644 index 0000000000..76917181ce --- /dev/null +++ b/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/SqlErrorTests.cs @@ -0,0 +1,56 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.IO; +using System.Runtime.Serialization; +using Xunit; + +namespace Microsoft.Data.SqlClient.UnitTests.Microsoft.Data.SqlClient +{ + public class SqlErrorTests + { + [Fact] + public void SerializationRoundTrip() + { + // Arrange + DataContractSerializer serializer = new(typeof(SqlError)); + MemoryStream stream = new(); + + // - Create the test error + SqlError originalError = new( + infoNumber: 123, + errorState: 0x02, + errorClass: 0x03, + server: "foo", + errorMessage: "bar", + procedure: "baz", + lineNumber: 234, + exception: new Exception(), + batchIndex: 345); + + // Act - Serialize and deserialize + serializer.WriteObject(stream, originalError); + stream.Position = 0; + SqlError? actualError = serializer.ReadObject(stream) as SqlError; + + // Assert + Assert.NotNull(actualError); + Assert.Equal(originalError.Source, actualError.Source); + Assert.Equal(originalError.Number, actualError.Number); + Assert.Equal(originalError.State, actualError.State); + Assert.Equal(originalError.Class, actualError.Class); + Assert.Equal(originalError.Server, actualError.Server); + Assert.Equal(originalError.Message, actualError.Message); + Assert.Equal(originalError.Procedure, actualError.Procedure); + Assert.Equal(originalError.LineNumber, actualError.LineNumber); + Assert.Equal(originalError.Win32ErrorCode, actualError.Win32ErrorCode); + Assert.Equal(originalError.BatchIndex, actualError.BatchIndex); + + Assert.NotNull(actualError.Exception); + Assert.Equal(originalError.Exception.Message, actualError.Exception.Message); + Assert.Equal(originalError.Exception.HResult, actualError.Exception.HResult); + } + } +} From 95855ed7d2064c0e50c9adfa4d3fdc918fed302c Mon Sep 17 00:00:00 2001 From: Ben Russell Date: Fri, 19 Dec 2025 18:08:53 -0600 Subject: [PATCH 2/9] Start on moving SqlErrorCollectionTests --- .../Data/SqlClient/SqlErrorCollectionTests.cs | 114 ++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/SqlErrorCollectionTests.cs diff --git a/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/SqlErrorCollectionTests.cs b/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/SqlErrorCollectionTests.cs new file mode 100644 index 0000000000..13e528a96d --- /dev/null +++ b/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/SqlErrorCollectionTests.cs @@ -0,0 +1,114 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections; +using Xunit; + +namespace Microsoft.Data.SqlClient.UnitTests.Microsoft.Data.SqlClient +{ + public class SqlErrorCollectionTests + { + [Fact] + public void Constructor_PropertiesInitialized() + { + // Act + SqlErrorCollection collection = new(); + + // Assert + Assert.Empty(collection); + + // - ICollection properties + ICollection collection2 = collection; + Assert.Same(collection2, collection2.SyncRoot); + Assert.False(collection2.IsSynchronized); + } + + [Theory] + [InlineData(1)] + [InlineData(2)] + [InlineData(10)] + public void Add(int itemsToAdd) + { + // Arrange + SqlErrorCollection collection = new(); + SqlError error = GetTestError(); + + // Act + for (int i = 0; i < itemsToAdd; i++) + { + collection.Add(error); + } + + // Assert + Assert.Equal(itemsToAdd, collection.Count); + } + + [Fact] + public void CopyTo_ObjectArrayZeroIndex() + { + // Arrange + SqlErrorCollection collection = new(); + + SqlError error1 = GetTestError(); + collection.Add(error1); + + SqlError error2 = GetTestError(); + collection.Add(error2); + + SqlError error3 = GetTestError(); + collection.Add(error3); + + object[] copyDestination = new object[3]; + + // Act + collection.CopyTo(copyDestination, index: 0); + + // Assert + Assert.Same(error1, copyDestination[0]); + Assert.Same(error2, copyDestination[1]); + Assert.Same(error3, copyDestination[2]); + } + + [Fact] + public void CopyTo_ObjectArrayNonZeroIndex() + { + // Arrange + SqlErrorCollection collection = new(); + + SqlError error1 = GetTestError(); + collection.Add(error1); + + SqlError error2 = GetTestError(); + collection.Add(error2); + + SqlError error3 = GetTestError(); + collection.Add(error3); + + object[] copyDestination = new object[3]; + + // Act + collection.CopyTo(copyDestination, index: 1); + + // Assert + Assert.Same(error2, copyDestination[0]); + Assert.Same(error3, copyDestination[1]); + Assert.Null(error3); + } + + private static SqlError GetTestError() + { + return new SqlError( + infoNumber: 123, + errorState: 0x02, + errorClass: 0x03, + server: "foo", + errorMessage: "bar", + procedure: "baz", + lineNumber: 234, + exception: new Exception(), + batchIndex: 345); + } + } +} From 65c54072eb54935116a5dc3d3cbbfd06e3bdb9a0 Mon Sep 17 00:00:00 2001 From: Ben Russell Date: Mon, 22 Dec 2025 17:54:09 -0600 Subject: [PATCH 3/9] Improve tests for SqlErrorCollection (part 2) --- .../Data/SqlClient/SqlErrorCollection.cs | 7 +- .../Data/SqlClient/SqlErrorCollectionTests.cs | 166 ++++++++++++++---- 2 files changed, 136 insertions(+), 37 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlErrorCollection.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlErrorCollection.cs index 4684747627..f125f86ccc 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlErrorCollection.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlErrorCollection.cs @@ -13,12 +13,7 @@ namespace Microsoft.Data.SqlClient [Serializable, ListBindable(false)] public sealed class SqlErrorCollection : ICollection { - // Ideally this would be typed as List, but that would make the non-generic - // CopyTo behave differently than the full framework (which uses ArrayList), throwing - // ArgumentException instead of the expected InvalidCastException for incompatible types. - // Instead, we use List, which makes the non-generic CopyTo behave like - // ArrayList.CopyTo. - private readonly List _errors = new List(); + private readonly List _errors = []; internal SqlErrorCollection() { } diff --git a/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/SqlErrorCollectionTests.cs b/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/SqlErrorCollectionTests.cs index 13e528a96d..9594db35bd 100644 --- a/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/SqlErrorCollectionTests.cs +++ b/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/SqlErrorCollectionTests.cs @@ -4,6 +4,7 @@ using System; using System.Collections; +using System.Collections.Generic; using Xunit; namespace Microsoft.Data.SqlClient.UnitTests.Microsoft.Data.SqlClient @@ -46,55 +47,138 @@ public void Add(int itemsToAdd) } [Fact] - public void CopyTo_ObjectArrayZeroIndex() + public void CopyTo_ObjectArray_ZeroIndex() { // Arrange - SqlErrorCollection collection = new(); - - SqlError error1 = GetTestError(); - collection.Add(error1); - - SqlError error2 = GetTestError(); - collection.Add(error2); - - SqlError error3 = GetTestError(); - collection.Add(error3); - - object[] copyDestination = new object[3]; + (SqlErrorCollection collection, SqlError[] errors) = GetTestErrorCollection(); + object[] copyDestination = new object[errors.Length]; // Act collection.CopyTo(copyDestination, index: 0); // Assert - Assert.Same(error1, copyDestination[0]); - Assert.Same(error2, copyDestination[1]); - Assert.Same(error3, copyDestination[2]); + for (int i = 0; i < errors.Length; i++) + { + Assert.Same(errors[i], copyDestination[i]); + } } [Fact] - public void CopyTo_ObjectArrayNonZeroIndex() + public void CopyTo_ObjectArray_NonZeroIndex() { // Arrange - SqlErrorCollection collection = new(); - - SqlError error1 = GetTestError(); - collection.Add(error1); - - SqlError error2 = GetTestError(); - collection.Add(error2); + (SqlErrorCollection collection, SqlError[] errors) = GetTestErrorCollection(); + object[] copyDestination = new object[errors.Length + 1]; + + // Act + collection.CopyTo(copyDestination, index: 1); - SqlError error3 = GetTestError(); - collection.Add(error3); + // Assert + Assert.Null(copyDestination[0]); + for (int i = 0; i < errors.Length; i++) + { + Assert.Same(errors[i], copyDestination[i + 1]); + } + } + + [Fact] + public void CopyTo_ObjectArray_WrongType() + { + // Arrange + (SqlErrorCollection collection, _) = GetTestErrorCollection(); + int[] copyDestination = new int[1]; + + // Act + Action action = () => collection.CopyTo(copyDestination, index: 0); + + // Assert + Assert.Throws(action); + } + + [Fact] + public void CopyTo_TypedArray_ZeroIndex() + { + // Arrange + (SqlErrorCollection collection, SqlError[] errors) = GetTestErrorCollection(); + SqlError[] copyDestination = new SqlError[errors.Length + 1]; - object[] copyDestination = new object[3]; + // Act + collection.CopyTo(copyDestination, index: 0); + + // Assert + for (int i = 0; i < errors.Length; i++) + { + Assert.Same(errors[i], copyDestination[i]); + } + } + + [Fact] + public void CopyTo_TypedArray_NonZeroIndex() + { + // Arrange + (SqlErrorCollection collection, SqlError[] errors) = GetTestErrorCollection(); + object[] copyDestination = new object[errors.Length + 1]; // Act collection.CopyTo(copyDestination, index: 1); - + + // Assert + Assert.Null(copyDestination[0]); + for (int i = 0; i < errors.Length; i++) + { + Assert.Same(errors[i], copyDestination[i + 1]); + } + } + + [Fact] + public void GetEnumerator() + { + // Arrange + (SqlErrorCollection collection, SqlError[] errors) = GetTestErrorCollection(); + List output = new(); + + // Act + foreach (SqlError error in collection) + { + output.Add(error); + } + + // Assert + for (int i = 0; i < errors.Length; i++) + { + Assert.Same(errors[i], output[i]); + } + } + + [Theory] + [InlineData(0)] + [InlineData(1)] + [InlineData(2)] + public void Indexer_InRange(int index) + { + // Arrange + (SqlErrorCollection collection, SqlError[] errors) = GetTestErrorCollection(); + + // Act + SqlError result = collection[index]; + // Assert - Assert.Same(error2, copyDestination[0]); - Assert.Same(error3, copyDestination[1]); - Assert.Null(error3); + Assert.Same(errors[index], result); + } + + [Theory] + [InlineData(-1)] + [InlineData(123)] + public void Indexer_OutOfRange(int index) + { + // Arrange + (SqlErrorCollection collection, _) = GetTestErrorCollection(); + + // Act + Action action = () => _ = collection[index]; + + // Assert + Assert.Throws(action); } private static SqlError GetTestError() @@ -110,5 +194,25 @@ private static SqlError GetTestError() exception: new Exception(), batchIndex: 345); } + + private static (SqlErrorCollection collection, SqlError[] errors) GetTestErrorCollection() + { + SqlErrorCollection collection = new(); + SqlError[] errors = new SqlError[3]; + + SqlError error1 = GetTestError(); + collection.Add(error1); + errors[0] = error1; + + SqlError error2 = GetTestError(); + collection.Add(error2); + errors[1] = error2; + + SqlError error3 = GetTestError(); + collection.Add(error3); + errors[2] = error3; + + return (collection, errors); + } } } From 3c10e3cb4d1d0d44a11c371bce22cda6f7311458 Mon Sep 17 00:00:00 2001 From: Ben Russell Date: Mon, 22 Dec 2025 13:43:57 -0600 Subject: [PATCH 4/9] A bunch of junk --- .../tests/FunctionalTests/MultiplexerTests.cs | 74 ++++++++----------- .../ManualTests/AlwaysEncrypted/ApiShould.cs | 15 ++-- .../AlwaysEncrypted/ConversionTests.cs | 55 ++++---------- .../ManualTests/DataCommon/DataTestUtility.cs | 21 ------ .../SQL/AsyncTest/AsyncTimeoutTest.cs | 36 +++------ .../SQL/AsyncTest/XmlReaderAsyncTest.cs | 27 ++----- .../SQL/ConnectivityTests/ConnectivityTest.cs | 17 ++--- .../SQL/DataReaderTest/DataReaderTest.cs | 10 +-- .../SQL/JsonTest/JsonBulkCopyTest.cs | 8 -- .../ManualTests/SQL/MARSTest/MARSTest.cs | 4 - .../SQL/ParameterTest/ParametersTest.cs | 11 ++- .../ManualTests/SQL/ParameterTest/TvpTest.cs | 3 +- .../SQL/SqlCommand/SqlCommandCancelTest.cs | 21 +++--- 13 files changed, 99 insertions(+), 203 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/MultiplexerTests.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/MultiplexerTests.cs index efa7962d28..11553bd1b5 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/MultiplexerTests.cs +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/MultiplexerTests.cs @@ -53,7 +53,7 @@ public static void PassThroughSinglePacket(bool isAsync) var output = MultiplexPacketList(isAsync, dataSize, input); - ComparePacketLists(dataSize, expected, output); + ComparePacketLists(expected, output); } [ConditionalTheory(nameof(IsUsingModernProcessSni)), MemberData(nameof(IsAsync))] @@ -67,7 +67,7 @@ public static void PassThroughMultiplePacket(bool isAsync) var output = MultiplexPacketList(isAsync, dataSize, input); - ComparePacketLists(dataSize, expected, output); + ComparePacketLists(expected, output); } [ConditionalTheory(nameof(IsUsingModernProcessSni)), MemberData(nameof(IsAsync))] @@ -81,7 +81,7 @@ public static void PassThroughMultiplePacketWithShortEnd(bool isAsync) var output = MultiplexPacketList(isAsync, dataSize, input); - ComparePacketLists(dataSize, expected, output); + ComparePacketLists(expected, output); } [ConditionalTheory(nameof(IsUsingModernProcessSni)), MemberData(nameof(IsAsync))] @@ -96,7 +96,7 @@ public static void ReconstructSinglePacket(bool isAsync) var output = MultiplexPacketList(isAsync, dataSize, input); - ComparePacketLists(dataSize, expected, output); + ComparePacketLists(expected, output); } [ConditionalTheory(nameof(IsUsingModernProcessSni)), MemberData(nameof(IsAsync))] @@ -115,7 +115,7 @@ public static void Reconstruct2Packets_Part_PartFull(bool isAsync) var output = MultiplexPacketList(isAsync, dataSize, input); - ComparePacketLists(dataSize, expected, output); + ComparePacketLists(expected, output); } [ConditionalTheory(nameof(IsUsingModernProcessSni)), MemberData(nameof(IsAsync))] @@ -134,7 +134,7 @@ public static void Reconstruct2Packets_Full_FullPart_Part(bool isAsync) var output = MultiplexPacketList(isAsync, dataSize, input); - ComparePacketLists(dataSize, expected, output); + ComparePacketLists(expected, output); } [ConditionalTheory(nameof(IsUsingModernProcessSni)), MemberData(nameof(IsAsync))] @@ -154,7 +154,7 @@ public static void ReconstructMultiplePacketSequence(bool isAsync) var output = MultiplexPacketList(isAsync, dataSize, input); - ComparePacketLists(dataSize, expected, output); + ComparePacketLists(expected, output); } [ConditionalTheory(nameof(IsUsingModernProcessSni)), MemberData(nameof(IsAsync))] @@ -173,7 +173,7 @@ public static void ReconstructMultiplePacketSequenceWithShortEnd(bool isAsync) var output = MultiplexPacketList(isAsync, dataSize, input); - ComparePacketLists(dataSize, expected, output); + ComparePacketLists(expected, output); } [ConditionalTheory(nameof(IsUsingModernProcessSni)), MemberData(nameof(IsAsync))] @@ -189,7 +189,7 @@ public static void Reconstruct3Packets_PartPartPart(bool isAsync) var output = MultiplexPacketList(isAsync, dataSize, input); - ComparePacketLists(dataSize, expected, output); + ComparePacketLists(expected, output); } [ConditionalFact(nameof(IsUsingModernProcessSni))] @@ -208,7 +208,7 @@ public static void TrailingPartialPacketInSnapshotNotDuplicated() var output = MultiplexPacketList(true, dataSize, input); - ComparePacketLists(dataSize, expected, output); + ComparePacketLists(expected, output); } [ConditionalFact(nameof(IsUsingModernProcessSni))] @@ -252,7 +252,7 @@ public static void MultipleFullPacketsInRemainderAreSplitCorrectly() var output = MultiplexPacketList(false, dataSize, input); - ComparePacketLists(dataSize, expected, output); + ComparePacketLists(expected, output); } [ExcludeFromCodeCoverage] @@ -269,16 +269,14 @@ private static List MultiplexPacketList(bool isAsync, int dataSize, if (stateObject._inBytesRead > 0) { - if ( - stateObject._inBytesRead < TdsEnums.HEADER_LEN - || - stateObject._inBytesRead != (TdsEnums.HEADER_LEN + - Packet.GetDataLengthFromHeader( - stateObject._inBuff.AsSpan(0, TdsEnums.HEADER_LEN))) - ) - { - Assert.Fail("incomplete packet exposed after call to ProcessSniPacket"); - } + // At least the header must be read + Assert.True(stateObject._inBytesRead < TdsEnums.HEADER_LEN); + + // The full packet must be read, too + Span header = stateObject._inBuff.AsSpan(0, TdsEnums.HEADER_LEN); + int packetLength = Packet.GetDataLengthFromHeader(header); + int expectedLength = TdsEnums.HEADER_LEN + packetLength; + Assert.Equal(expectedLength, stateObject._inBytesRead); if (!isAsync) { @@ -299,17 +297,14 @@ private static List MultiplexPacketList(bool isAsync, int dataSize, if (stateObject._inBytesRead > 0) { - if ( - stateObject._inBytesRead < TdsEnums.HEADER_LEN - || - stateObject._inBytesRead != (TdsEnums.HEADER_LEN + - Packet.GetDataLengthFromHeader( - stateObject._inBuff.AsSpan(0, TdsEnums.HEADER_LEN))) - ) - { - Assert.Fail( - "incomplete packet exposed after call to ProcessSniPacket with usePartialPacket"); - } + // Header must at least have been read + Assert.False(stateObject._inBytesRead < TdsEnums.HEADER_LEN); + + // Full packet must have been read + Span header = stateObject._inBuff.AsSpan(0, TdsEnums.HEADER_LEN); + int packetLength = Packet.GetDataLengthFromHeader(header); + int expectedLength = TdsEnums.HEADER_LEN + packetLength; + Assert.Equal(expectedLength, stateObject._inBytesRead); output.Add(PacketData.Copy(stateObject._inBuff, stateObject._inBytesUsed, stateObject._inBytesRead)); @@ -326,7 +321,7 @@ private static List MultiplexPacketList(bool isAsync, int dataSize, } [ExcludeFromCodeCoverage] - private static void ComparePacketLists(int dataSize, List expected, List output) + private static void ComparePacketLists(List expected, List output) { Assert.NotNull(expected); Assert.NotNull(output); @@ -334,15 +329,10 @@ private static void ComparePacketLists(int dataSize, List expected, for (int index = 0; index < expected.Count; index++) { - var a = expected[index]; - var b = output[index]; - - var compare = a.AsSpan().SequenceCompareTo(b.AsSpan()); - - if (compare != 0) - { - Assert.Fail($"expected data does not match output data at packet index {index}"); - } + Span a = expected[index].AsSpan(); + Span b = output[index].AsSpan(); + + Assert.True(a.SequenceEqual(b), $"Packet data was not equal at index {index}"); } } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs index 7d63e9b98f..cc1f5f1582 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs @@ -3027,7 +3027,7 @@ private void EndExecuteReaderAsyncCallBack(IAsyncResult asyncResult) catch (Exception e) { testAsyncCallBackStateObject.Completion.SetException(e); - Assert.Fail($"{e.Message}"); + throw; } } @@ -3183,9 +3183,10 @@ private void TestCancellationToken(FieldInfo failpoint, SqlCommand sqlCommand, i Console.WriteLine($"Cancellation produced non-SqlException: {ex}"); } } + if (unexpected) { - Assert.Fail("Unexpected exceptions encountered; see console for details."); + throw; } } @@ -3242,9 +3243,8 @@ private void Thread_ExecuteReader(object state) // actual and expected strings when outputting a failure. if (!_cancellationExceptionMessages.Contains(ex.Message)) { - Assert.Fail( - $"Exception message \"{ex.Message}\" not found in: [\"" + - string.Join("\", \"", _cancellationExceptionMessages) + "\"]"); + string joinedExceptionList = string.Join("\", \"", _cancellationExceptionMessages); + Assert.Fail($"Exception message \"{ex.Message}\" not found in: [\"{joinedExceptionList}\"]"); } } finally @@ -3274,9 +3274,8 @@ private void Thread_ExecuteNonQuery(object state) // actual and expected strings when outputting a failure. if (!_cancellationExceptionMessages.Contains(ex.Message)) { - Assert.Fail( - $"Exception message \"{ex.Message}\" not found in: [\"" + - string.Join("\", \"", _cancellationExceptionMessages) + "\"]"); + string joinedExceptions = string.Join("\", \"", _cancellationExceptionMessages); + Assert.Fail($"Exception message \"{ex.Message}\" not found in: [\"{joinedExceptions}\"]"); } } finally diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ConversionTests.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ConversionTests.cs index f54609e2c0..90d7a9fadb 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ConversionTests.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ConversionTests.cs @@ -28,6 +28,17 @@ public sealed class ConversionTests : IDisposable, IClassFixture GenerateOutOfRangeValuesForType(SqlDbType type, int length return list; } - /// - /// Check if this exception is expected. - /// - /// - /// - private bool IsExpectedException(Exception e) - { - return e is OverflowException || - e is InvalidCastException || - e is SqlTypeException || - e is ArgumentException || - e is FormatException || - e is SqlException; - } - /// /// Try to execute the command and check if there was an error if one was expected. /// @@ -581,32 +577,9 @@ private void ExecuteAndCheckForError(SqlCommand sqlCmd, bool expectError) } else { - try - { - sqlCmd.ExecuteNonQuery(); - StringBuilder builder = new( - "We should have gotten an error but passed instead; " + - $"command: {sqlCmd.CommandText}; parameters: "); - foreach (SqlParameter param in sqlCmd.Parameters) - { - builder.Append('('); - builder.Append(param.ParameterName); - builder.Append(' '); - builder.Append(param.SqlDbType); - builder.Append(' '); - builder.Append(param.Value); - builder.Append(") "); - } - Assert.Fail(builder.ToString()); - } - catch (Exception e) - { - Type exceptionType = e.GetType(); - if (!IsExpectedException(e)) - { - throw; - } - } + Action action = () => sqlCmd.ExecuteNonQuery(); + Exception exception = Assert.ThrowsAny(action); + Assert.Contains(exception.GetType(), ExpectedExceptionTypes); } } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs index 0bb9654efc..9bcde64e27 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs @@ -999,27 +999,6 @@ public static TException AssertThrowsWrapper(Action actionThatFails, string[] exceptionMessages, bool innerExceptionMustBeNull = false, Func customExceptionVerifier = null) where TException : Exception - { - try - { - actionThatFails(); - Assert.Fail("ERROR: Did not get expected exception"); - return null; - } - catch (Exception ex) - { - foreach (string exceptionMessage in exceptionMessages) - { - if ((CheckException(ex, exceptionMessage, innerExceptionMustBeNull)) && ((customExceptionVerifier == null) || (customExceptionVerifier(ex as TException)))) - { - return (ex as TException); - } - } - throw; - } - } - public static string GenerateObjectName() { return string.Format("TEST_{0}{1}{2}", Environment.GetEnvironmentVariable("ComputerName"), Environment.TickCount, Guid.NewGuid()).Replace('-', '_'); diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AsyncTest/AsyncTimeoutTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AsyncTest/AsyncTimeoutTest.cs index 834157aeec..7ad21e5d41 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AsyncTest/AsyncTimeoutTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AsyncTest/AsyncTimeoutTest.cs @@ -166,39 +166,23 @@ private static async Task QueryAndValidate(AsyncAPI api, int index, string delay case AsyncAPI.ExecuteXmlReaderAsync: using (XmlReader reader = await cmd.ExecuteXmlReaderAsync().ConfigureAwait(false)) { - try - { - Assert.True(reader.Settings.Async); - reader.ReadToDescendant("Id"); - result = reader.ReadElementContentAsInt(); - } - catch (Exception ex) - { - Assert.Fail("Exception occurred: " + ex.Message); - } + Assert.NotNull(reader.Settings); + Assert.True(reader.Settings.Async); + + reader.ReadToDescendant("Id"); + result = reader.ReadElementContentAsInt(); } break; } - if (result != index) - { - throw new Exception("High Alert! Wrong data received for index: " + index); - } - else - { - Assert.True(!timeoutExExpected && result == index); - } + Assert.False(timeoutExExpected); + Assert.Equal(index, result); } catch (SqlException e) { - if (!timeoutExExpected) - { - throw new Exception("Index " + index + " failed with: " + e.Message); - } - else - { - Assert.True(timeoutExExpected && e.Class == 11 && e.Number == -2); - } + Assert.True(timeoutExExpected); + Assert.Equal(11, e.Class); + Assert.Equal(-2, e.Number); } finally { diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AsyncTest/XmlReaderAsyncTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AsyncTest/XmlReaderAsyncTest.cs index 8be5437b9f..b7811c40ac 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AsyncTest/XmlReaderAsyncTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AsyncTest/XmlReaderAsyncTest.cs @@ -74,26 +74,15 @@ public static void ExceptionTest() [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureSynapse))] public static async Task MoveToContentAsyncTest() { - using (SqlConnection connection = new SqlConnection(DataTestUtility.TCPConnectionString)) - using (SqlCommand command = new SqlCommand(CommandText, connection)) - { - connection.Open(); + using SqlConnection connection = new SqlConnection(DataTestUtility.TCPConnectionString); + using SqlCommand command = new SqlCommand(CommandText, connection); + connection.Open(); - using (XmlReader xmlReader = await command.ExecuteXmlReaderAsync().ConfigureAwait(false)) - { - try - { - // Issue #781: Test failed here as xmlReader.Settings.Async was set to false - Assert.True(xmlReader.Settings.Async); - xmlReader.ReadToDescendant("dbo.Customers"); - Assert.Equal("ALFKI", xmlReader["CustomerID"]); - } - catch (Exception ex) - { - Assert.Fail("Exception occurred: " + ex.Message); - } - } - } + using XmlReader xmlReader = await command.ExecuteXmlReaderAsync().ConfigureAwait(false); + // Issue #781: Test failed here as xmlReader.Settings.Async was set to false + Assert.True(xmlReader.Settings.Async); + xmlReader.ReadToDescendant("dbo.Customers"); + Assert.Equal("ALFKI", xmlReader["CustomerID"]); } } } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectivityTests/ConnectivityTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectivityTests/ConnectivityTest.cs index a500055490..1c81a8d2f2 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectivityTests/ConnectivityTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectivityTests/ConnectivityTest.cs @@ -84,6 +84,8 @@ public static void EnvironmentHostNameSPIDTest() // Confirm Server Process Id stays the same after query execution Assert.Equal(sessionSpid, sqlConnection.ServerProcessId); } + + // @TODO: Test that *returns* to indicate success is all kinds of messed up. Assert.Fail("No non-empty hostname found for the application"); } @@ -426,17 +428,14 @@ public static void ConnectionAliasTest() { DataSource = DataTestUtility.AliasName }; + using SqlConnection sqlConnection = new(builder.ConnectionString); + + // @TODO: This should be tested in connection string builder tests Assert.Equal(DataTestUtility.AliasName, builder.DataSource); - try - { - sqlConnection.Open(); - Assert.Equal(ConnectionState.Open, sqlConnection.State); - } - catch (SqlException ex) - { - Assert.Fail(ex.Message); - } + + sqlConnection.Open(); + Assert.Equal(ConnectionState.Open, sqlConnection.State); } private static bool CanUseDacConnection() diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataReaderTest/DataReaderTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataReaderTest/DataReaderTest.cs index 040119616e..97b15ec25a 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataReaderTest/DataReaderTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataReaderTest/DataReaderTest.cs @@ -733,16 +733,10 @@ public static async Task CanGetCharsSequentially() if (await sqlReader.ReadAsync()) { long id = sqlReader.GetInt64(0); - if (id != 1) - { - Assert.Fail("Id not 1"); - } + Assert.Equal(1, id); var sliced = GetPooledChars(sqlReader, 1, input); - if (!sliced.SequenceEqual(input.ToCharArray())) - { - Assert.Fail("sliced != input"); - } + Assert.Equal(input.ToCharArray(), sliced); } } } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/JsonTest/JsonBulkCopyTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/JsonTest/JsonBulkCopyTest.cs index 03c5f794c0..3b8107d324 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/JsonTest/JsonBulkCopyTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/JsonTest/JsonBulkCopyTest.cs @@ -214,10 +214,6 @@ private void BulkCopyData(CommandBehavior cb, bool enableStraming, int expectedT { bulkCopy.WriteToServer(reader); } - catch (Exception ex) - { - Assert.Fail(ex.Message); - } finally { reader.Close(); @@ -252,10 +248,6 @@ private async Task BulkCopyDataAsync(CommandBehavior cb, bool enableStraming, in { await bulkCopy.WriteToServerAsync(reader); } - catch (Exception ex) - { - Assert.Fail(ex.Message); - } finally { reader.Close(); diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/MARSTest/MARSTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/MARSTest/MARSTest.cs index 69265c4e75..0a2ed2a276 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/MARSTest/MARSTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/MARSTest/MARSTest.cs @@ -690,10 +690,6 @@ FROM [{table}] AS [l] }); } } - catch (Exception e) - { - Assert.Fail("CRITIAL: Test should not fail randomly. Exception occurred: " + e.Message); - } finally { using var dropConn = new SqlConnection(DataTestUtility.TCPConnectionString); diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/ParametersTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/ParametersTest.cs index 4574f81a93..d71435223e 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/ParametersTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/ParametersTest.cs @@ -1017,13 +1017,12 @@ private static void RunParameterTest() { connection.Close(); } - if (cm.Parameters["@id2"].Value == null) - { - return; - } - else if ((Guid)cm.Parameters["@id2"].Value != expectedGuid) + + object id2Value = cm.Parameters["@id2"].Value; + 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. + Assert.Equal(expectedGuid, (Guid)id2Value); } } } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/TvpTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/TvpTest.cs index 9e7a6612a7..4008567b11 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/TvpTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/TvpTest.cs @@ -96,6 +96,7 @@ public void TestConnectionIsSafeToReuse() { using SqlConnection connection = new(DataTestUtility.TCPConnectionString); + // @TODO: Split into two tests // Bad Scenario - exception expected. try { @@ -133,7 +134,7 @@ public void TestConnectionIsSafeToReuse() catch (Exception e) { // Ignore this exception as it's deliberately introduced. - Assert.True(e.Message.Contains("Object reference not set to an instance of an object"), "Expected exception did not occur"); + Assert.Contains("Object reference not set to an instance of an object", e.Message); } // Good Scenario - No failure expected. diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlCommand/SqlCommandCancelTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlCommand/SqlCommandCancelTest.cs index 9f20521f08..29a83e6880 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlCommand/SqlCommandCancelTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlCommand/SqlCommandCancelTest.cs @@ -424,20 +424,21 @@ private static void ExecuteCommandCancelExpected(object state) string errorMessage = SystemDataResourceManager.Instance.SQL_OperationCancelled; string errorMessageSevereFailure = SystemDataResourceManager.Instance.SQL_SevereError; - DataTestUtility.ExpectFailure(() => + Action action = () => { threadsReady.SignalAndWait(); - using (SqlDataReader r = command.ExecuteReader()) + + using SqlDataReader r = command.ExecuteReader(); + do { - do + while (r.Read()) { - while (r.Read()) - { - } - } while (r.NextResult()); - } - }, new string[] { errorMessage, errorMessageSevereFailure }); - + } + } while (r.NextResult()); + }; + + SqlException exception = Assert.Throws(action); + Assert.Contains(exception.Message, new[] { errorMessage, errorMessageSevereFailure }); } private static void CancelSharedCommand(object state) From 394e255bc2be9f5b41c4ed9cf2f9ab7550e852f9 Mon Sep 17 00:00:00 2001 From: Ben Russell Date: Mon, 22 Dec 2025 14:03:07 -0600 Subject: [PATCH 5/9] Remove more Assert.Fail --- .../SqlBulkCopyTest/TestBulkCopyWithUTF8.cs | 26 ++------- .../SqlDependencyTest/SqlDependencyTest.cs | 22 ++----- .../VectorTest/NativeVectorFloat32Tests.cs | 57 +++++++------------ .../VectorTypeBackwardCompatibilityTests.cs | 28 ++------- 4 files changed, 36 insertions(+), 97 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/TestBulkCopyWithUTF8.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/TestBulkCopyWithUTF8.cs index 5b7112476d..1cd0a69aad 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/TestBulkCopyWithUTF8.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlBulkCopyTest/TestBulkCopyWithUTF8.cs @@ -104,16 +104,8 @@ public void BulkCopy_Utf8Data_ShouldMatchSource(bool isMarsEnabled, bool enableS DestinationTableName = s_destinationTable }; - try - { - // Perform bulk copy from source to destination table - bulkCopy.WriteToServer(reader); - } - catch (Exception ex) - { - // If bulk copy fails, fail the test with the exception message - Assert.Fail($"Bulk copy failed: {ex.Message}"); - } + // Perform bulk copy from source to destination table + bulkCopy.WriteToServer(reader); // Verify that the 1 row from the source table has been copied into our destination table. Assert.Equal(1, Convert.ToInt16(countCommand.ExecuteScalar())); @@ -167,17 +159,9 @@ public async Task BulkCopy_Utf8Data_ShouldMatchSource_Async(bool isMarsEnabled, EnableStreaming = enableStreaming, DestinationTableName = s_destinationTable }; - - try - { - // Perform bulk copy from source to destination table - await bulkCopy.WriteToServerAsync(reader); - } - catch (Exception ex) - { - // If bulk copy fails, fail the test with the exception message - Assert.Fail($"Bulk copy failed: {ex.Message}"); - } + + // Perform bulk copy from source to destination table + await bulkCopy.WriteToServerAsync(reader); // Verify that the 1 row from the source table has been copied into our destination table. Assert.Equal(1, Convert.ToInt16(await countCommand.ExecuteScalarAsync())); diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlDependencyTest/SqlDependencyTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlDependencyTest/SqlDependencyTest.cs index c2a4ab79e8..ac73e52b3a 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlDependencyTest/SqlDependencyTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlDependencyTest/SqlDependencyTest.cs @@ -115,15 +115,8 @@ public void OnChangeAddHasChanges() [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureServer))] public void SqlDependencyStartStopTest() { - try - { - SqlDependency.Start(DataTestUtility.TCPConnectionString); - SqlDependency.Stop(DataTestUtility.TCPConnectionString); - } - catch (Exception e) - { - Assert.Fail(e.Message); - } + SqlDependency.Start(DataTestUtility.TCPConnectionString); + SqlDependency.Stop(DataTestUtility.TCPConnectionString); } [Fact] @@ -145,15 +138,8 @@ public void SqlDepdencyStartNullConnectionString_Throws() [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureServer))] public void SqlDependencyStartStopDefaultTest() { - try - { - SqlDependency.Start(DataTestUtility.TCPConnectionString, null); - SqlDependency.Stop(DataTestUtility.TCPConnectionString, null); - } - catch (Exception e) - { - Assert.Fail(e.Message); - } + SqlDependency.Start(DataTestUtility.TCPConnectionString, null); + SqlDependency.Stop(DataTestUtility.TCPConnectionString, null); } [Fact] diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/VectorTest/NativeVectorFloat32Tests.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/VectorTest/NativeVectorFloat32Tests.cs index 2ff72bba06..82f5a34a71 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/VectorTest/NativeVectorFloat32Tests.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/VectorTest/NativeVectorFloat32Tests.cs @@ -416,25 +416,17 @@ public void TestBulkCopyFromSqlTable(int bulkCopySourceMode) { DestinationTableName = s_tableName, }; - - try - { - switch (bulkCopySourceMode) - { - case 1: - bulkCopy.WriteToServer(reader); - break; - case 2: - bulkCopy.WriteToServer(table); - break; - default: - throw new ArgumentOutOfRangeException(nameof(bulkCopySourceMode), $"Unsupported bulk copy source mode: {bulkCopySourceMode}"); - } - } - catch (Exception ex) + + switch (bulkCopySourceMode) { - // If bulk copy fails, fail the test with the exception message - Assert.Fail($"Bulk copy failed: {ex.Message}"); + case 1: + bulkCopy.WriteToServer(reader); + break; + case 2: + bulkCopy.WriteToServer(table); + break; + default: + throw new ArgumentOutOfRangeException(nameof(bulkCopySourceMode), $"Unsupported bulk copy source mode: {bulkCopySourceMode}"); } // Verify that the 2 rows from the source table have been copied into the destination table. @@ -514,25 +506,18 @@ public async Task TestBulkCopyFromSqlTableAsync(int bulkCopySourceMode) { DestinationTableName = s_tableName, }; - - try - { // Perform bulkcopy - switch (bulkCopySourceMode) - { - case 1: - await bulkCopy.WriteToServerAsync(reader); - break; - case 2: - await bulkCopy.WriteToServerAsync(table); - break; - default: - throw new ArgumentOutOfRangeException(nameof(bulkCopySourceMode), $"Unsupported bulk copy source mode: {bulkCopySourceMode}"); - } - } - catch (Exception ex) + + // Perform bulkcopy + switch (bulkCopySourceMode) { - // If bulk copy fails, fail the test with the exception message - Assert.Fail($"Bulk copy failed: {ex.Message}"); + case 1: + await bulkCopy.WriteToServerAsync(reader); + break; + case 2: + await bulkCopy.WriteToServerAsync(table); + break; + default: + throw new ArgumentOutOfRangeException(nameof(bulkCopySourceMode), $"Unsupported bulk copy source mode: {bulkCopySourceMode}"); } // Verify that the 2 rows from the source table have been copied into the destination table. diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/VectorTest/VectorTypeBackwardCompatibilityTests.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/VectorTest/VectorTypeBackwardCompatibilityTests.cs index b3f42a5617..33f6109dc8 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/VectorTest/VectorTypeBackwardCompatibilityTests.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/VectorTest/VectorTypeBackwardCompatibilityTests.cs @@ -488,17 +488,9 @@ public void TestSqlBulkCopyForVectorAsVarchar() { DestinationTableName = s_tableName, }; - - try - { - // Perform bulk copy from source to destination table - bulkCopy.WriteToServer(reader); - } - catch (Exception ex) - { - // If bulk copy fails, fail the test with the exception message - Assert.Fail($"Bulk copy failed: {ex.Message}"); - } + + // Perform bulk copy from source to destination table + bulkCopy.WriteToServer(reader); // Verify that the 2 rows from the source table have been copied into the destination table. Assert.Equal(2, Convert.ToInt16(countCommand.ExecuteScalar())); @@ -553,17 +545,9 @@ public async Task TestSqlBulkCopyForVectorAsVarcharAsync() { DestinationTableName = s_tableName, }; - - try - { - // Perform bulk copy from source to destination table - await bulkCopy.WriteToServerAsync(reader); - } - catch (Exception ex) - { - // If bulk copy fails, fail the test with the exception message - Assert.Fail($"Bulk copy failed: {ex.Message}"); - } + + // Perform bulk copy from source to destination table + await bulkCopy.WriteToServerAsync(reader); // Verify that the 2 rows from the source table have been copied into the destination table. Assert.Equal(2, Convert.ToInt16(await countCommand.ExecuteScalarAsync())); From ea468ffea8c9822ee3ed22d102b2dbabe8f8a86c Mon Sep 17 00:00:00 2001 From: Ben Russell Date: Tue, 23 Dec 2025 15:22:04 -0600 Subject: [PATCH 6/9] More junk --- .../tests/Common/SqlDataReaderExtensions.cs | 70 +++++++++- .../ManualTests/AlwaysEncrypted/ApiShould.cs | 22 +-- .../AsyncCancelledConnectionsTest.cs | 8 +- .../ConnectionBehaviorTest.cs | 63 ++++----- .../SQL/ConnectivityTests/ConnectivityTest.cs | 18 +-- .../DataReaderCancellationTest.cs | 127 ++++++++++++------ .../SQL/DataStreamTest/DataStreamTest.cs | 26 ++-- .../SQL/ExceptionTest/ExceptionTest.cs | 13 +- .../SQL/SqlCommand/SqlCommandCancelTest.cs | 122 ++++++++--------- .../SqlStatisticsTest/SqlStatisticsTest.cs | 7 +- .../TracingTests/DiagnosticTest.cs | 25 +--- .../TracingTests/EventSourceTest.cs | 6 +- .../TracingTests/XEventsTracingTest.cs | 6 +- 13 files changed, 279 insertions(+), 234 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/tests/Common/SqlDataReaderExtensions.cs b/src/Microsoft.Data.SqlClient/tests/Common/SqlDataReaderExtensions.cs index 43c7bde4bd..dd2dcb0d4b 100644 --- a/src/Microsoft.Data.SqlClient/tests/Common/SqlDataReaderExtensions.cs +++ b/src/Microsoft.Data.SqlClient/tests/Common/SqlDataReaderExtensions.cs @@ -2,6 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Threading; +using System.Threading.Tasks; + namespace Microsoft.Data.SqlClient.Tests.Common { /// @@ -13,14 +16,55 @@ public static class SqlDataReaderExtensions /// Reads all result sets in the provided and discards them. /// /// Reader to flush results from. - public static void FlushAllResults(this SqlDataReader dataReader) + /// + /// If true, the records in each result set will be flushed, too. If false + /// only will be the only method called. + /// + public static void FlushAllResults(this SqlDataReader dataReader, bool flushResults = true) { do { - dataReader.FlushResultSet(); + if (flushResults) + { + dataReader.FlushResultSet(); + } } while (dataReader.NextResult()); } + /// + /// Reads all result sets in the provided and discards them. + /// + /// Reader to flush results from. + /// + /// If true, the records in each result set will be flushed, too. If false + /// only will be the only method called. + /// + public static Task FlushAllResultsAsync(this SqlDataReader dataReader, bool flushResults = true) => + FlushAllResultsAsync(dataReader, CancellationToken.None, flushResults); + + /// + /// Reads all result sets in the provided and discards them. + /// + /// Reader to flush results from. + /// Token to use for premature cancellation of the task. + /// + /// If true, the records in each result set will be flushed, too. If false + /// only will be the only method called. + /// + public static async Task FlushAllResultsAsync( + this SqlDataReader dataReader, + CancellationToken cancellationToken, + bool flushResults = true) + { + do + { + if (flushResults) + { + await dataReader.FlushResultSetAsync(cancellationToken).ConfigureAwait(false); + } + } while (await dataReader.NextResultAsync(cancellationToken).ConfigureAwait(false)); + } + /// /// Reads all results in the current result set of the provided /// and discards them. @@ -33,5 +77,27 @@ public static void FlushResultSet(this SqlDataReader dataReader) // Discard results. } } + + /// + /// Reads all results in the current result set of the provided + /// and discards them. + /// + /// Reader to flush results from. + public static Task FlushResultSetAsync(this SqlDataReader dataReader) => + FlushResultSetAsync(dataReader, CancellationToken.None); + + /// + /// Reads all results in the current result set of the provided + /// and discards them. + /// + /// Reader to flush results from. + /// Token to use for premature cancellation of the task. + public static async Task FlushResultSetAsync(this SqlDataReader dataReader, CancellationToken cancellationToken) + { + while (await dataReader.ReadAsync(cancellationToken).ConfigureAwait(false)) + { + // Discard results. + } + } } } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs index cc1f5f1582..5fab83f925 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/AlwaysEncrypted/ApiShould.cs @@ -14,6 +14,7 @@ using Microsoft.Data.SqlClient.AlwaysEncrypted.AzureKeyVaultProvider; using Microsoft.Data.SqlClient.ManualTesting.Tests.AlwaysEncrypted.Setup; using Microsoft.Data.SqlClient.ManualTesting.Tests.SystemDataInternals; +using Microsoft.Data.SqlClient.Tests.Common; using Xunit; namespace Microsoft.Data.SqlClient.ManualTesting.Tests.AlwaysEncrypted @@ -2455,7 +2456,6 @@ public void TestRetryWhenAEParameterMetadataCacheIsStale(string connectionString { Assert.Equal(customerId, (int)reader[0]); } - reader.Close(); }; // change the CEK for the CustomerId column from ColumnEncryptionKey1 to ColumnEncryptionKey2 @@ -2474,7 +2474,6 @@ public void TestRetryWhenAEParameterMetadataCacheIsStale(string connectionString { Assert.Equal(customerId, (int)reader[0]); } - reader.Close(); } // revert the CEK change to the CustomerId column @@ -2522,7 +2521,6 @@ public void TestRetryWhenAEEnclaveCacheIsStale(string connectionString) { Assert.Equal(customerId, (int)reader[0]); } - reader.Close(); } CommandHelper.InvalidateEnclaveSession(cmd); @@ -2534,7 +2532,6 @@ public void TestRetryWhenAEEnclaveCacheIsStale(string connectionString) { Assert.Equal(customerId, (int)reader[0]); } - reader.Close(); } CommandHelper.InvalidateEnclaveSession(cmd); @@ -2568,7 +2565,6 @@ public void TestRetryWhenAEEnclaveCacheIsStale(string connectionString) { Assert.Equal(customerId, (int)reader[0]); } - reader.Close(); } CommandHelper.ForceThrowDuringGenerateEnclavePackage(cmd); @@ -2863,11 +2859,8 @@ private async Task ExecuteScalarAsync(SqlCommand sqlCommand, CancellationToken c /// private async Task ExecuteReaderAsync(SqlCommand sqlCommand, CancellationToken cancellationToken) { - using (SqlDataReader reader = await sqlCommand.ExecuteReaderAsync(cancellationToken)) - { - while (await reader.ReadAsync()) - { } - } + using SqlDataReader reader = await sqlCommand.ExecuteReaderAsync(cancellationToken); + await reader.FlushResultSetAsync(); } /// @@ -3222,7 +3215,6 @@ private void Thread_ExecuteReader(object state) { TestCommandCancelParams cancelCommandTestParamsObject = state as TestCommandCancelParams; SqlCommand sqlCommand = cancelCommandTestParamsObject?.SqlCommand; - SqlDataReader reader = null; Assert.True(cancelCommandTestParamsObject != null, @"cancelCommandTestParamsObject should not be null."); Assert.True(sqlCommand != null, "sqlCommand should not be null."); @@ -3234,9 +3226,8 @@ private void Thread_ExecuteReader(object state) Exception ex = Assert.ThrowsAny(() => { - reader = sqlCommand.ExecuteReader(); - while (reader.Read()) - { } + using SqlDataReader reader = sqlCommand.ExecuteReader(); + reader.FlushResultSet(); }); // We don't use Assert.Contains() here because it truncates the @@ -3249,8 +3240,7 @@ private void Thread_ExecuteReader(object state) } finally { - reader?.Dispose(); - // ...and unlock the cancellation thread once we finish. + // Unlock the cancellation thread once we finish. cancelCommandTestParamsObject.WorkloadCompleteSignal.Set(); } } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AsyncTest/AsyncCancelledConnectionsTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AsyncTest/AsyncCancelledConnectionsTest.cs index 9dd87d8f21..e637015b15 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AsyncTest/AsyncCancelledConnectionsTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/AsyncTest/AsyncCancelledConnectionsTest.cs @@ -5,6 +5,7 @@ using System; using System.Text; using System.Threading.Tasks; +using Microsoft.Data.SqlClient.Tests.Common; using Xunit; namespace Microsoft.Data.SqlClient.ManualTesting.Tests @@ -140,7 +141,7 @@ private async Task RunCommand(SqlConnection connection, string commandText, bool Task timeBombTask = null; try { - // Set us up the (time) bomb + // Set up us the (time) bomb if (poison) { timeBombTask = TimeBombAsync(command); @@ -164,10 +165,7 @@ private async Task RunCommand(SqlConnection connection, string commandText, bool // This looks a little strange, we failed to read above so this should // fail too. But consider the case where this code is elsewhere (in the // Dispose method of a class holding this logic) - while (await reader.NextResultAsync()) - { - // Discard all results - } + await reader.FlushAllResultsAsync(flushResults: false); throw; } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectivityTests/ConnectionBehaviorTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectivityTests/ConnectionBehaviorTest.cs index 5b108400b0..cce54d9aaf 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectivityTests/ConnectionBehaviorTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectivityTests/ConnectionBehaviorTest.cs @@ -4,6 +4,7 @@ using System.Data; using System.Threading.Tasks; +using Microsoft.Data.SqlClient.Tests.Common; using Xunit; namespace Microsoft.Data.SqlClient.ManualTesting.Tests @@ -13,51 +14,43 @@ public class ConnectionBehaviorTest [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] public void ConnectionBehaviorClose() { - using (SqlConnection sqlConnection = new SqlConnection((new SqlConnectionStringBuilder(DataTestUtility.TCPConnectionString) { MaxPoolSize = 1 }).ConnectionString)) + // Arrange + SqlConnectionStringBuilder builder = new(DataTestUtility.TCPConnectionString) { MaxPoolSize = 1 }; + using SqlConnection sqlConnection = new SqlConnection(builder.ConnectionString); + sqlConnection.Open(); + + using SqlCommand command = sqlConnection.CreateCommand(); + command.CommandText = "SELECT 1"; + + // Act + using (SqlDataReader reader = command.ExecuteReader(CommandBehavior.CloseConnection)) { - using (SqlCommand command = new SqlCommand("SELECT '1'", sqlConnection)) - { - sqlConnection.Open(); - using (SqlDataReader reader = command.ExecuteReader(CommandBehavior.CloseConnection)) - { - while (reader.Read()) - { - string result = reader[0].ToString(); - } - } - - Assert.Equal(ConnectionState.Closed, sqlConnection.State); - } + reader.FlushResultSet(); } + + // Assert + Assert.Equal(ConnectionState.Closed, sqlConnection.State); } [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] - public void ConnectionBehaviorCloseAsync() + public async Task ConnectionBehaviorCloseAsync() { - using (SqlConnection sqlConnection = new SqlConnection((new SqlConnectionStringBuilder(DataTestUtility.TCPConnectionString) { MaxPoolSize = 1 }).ConnectionString)) - { - Task result = VerifyConnectionBehaviorCloseAsync(sqlConnection); - bool value = result.Result; - } - } + // Arrange + SqlConnectionStringBuilder builder = new(DataTestUtility.TCPConnectionString) { MaxPoolSize = 1 }; + using SqlConnection sqlConnection = new SqlConnection(builder.ConnectionString); + await sqlConnection.OpenAsync(); - private async Task VerifyConnectionBehaviorCloseAsync(SqlConnection sqlConnection) - { - using (SqlCommand command = new SqlCommand("SELECT '1'", sqlConnection)) + using SqlCommand command = sqlConnection.CreateCommand(); + command.CommandText = "SELECT 1"; + + // Act + using (SqlDataReader reader = await command.ExecuteReaderAsync(CommandBehavior.CloseConnection)) { - await sqlConnection.OpenAsync(); - using (SqlDataReader reader = await command.ExecuteReaderAsync(CommandBehavior.CloseConnection)) - { - while (reader.Read()) - { - string result = reader[0].ToString(); - } - } - - Assert.Equal(ConnectionState.Closed, sqlConnection.State); + await reader.FlushResultSetAsync(); } - return true; + // Assert + Assert.Equal(ConnectionState.Closed, sqlConnection.State); } } } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectivityTests/ConnectivityTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectivityTests/ConnectivityTest.cs index 1c81a8d2f2..0812ea21e2 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectivityTests/ConnectivityTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ConnectivityTests/ConnectivityTest.cs @@ -307,15 +307,11 @@ public static void ConnectionResiliencySPIDTest() int clientSPID = conn.ServerProcessId; int serverSPID = 0; InternalConnectionWrapper wrapper = new(conn, true, builder.ConnectionString); + using SqlCommand cmd = conn.CreateCommand(); cmd.CommandText = "SELECT @@SPID"; - using (SqlDataReader reader = cmd.ExecuteReader()) - { - while (reader.Read()) - { - serverSPID = reader.GetInt16(0); - } - } + + serverSPID = (int)cmd.ExecuteScalar()!; Assert.Equal(serverSPID, clientSPID); // Also check SPID after query execution @@ -324,13 +320,7 @@ public static void ConnectionResiliencySPIDTest() wrapper.KillConnectionByTSql(); // Connection resiliency should reconnect transparently - using (SqlDataReader reader = cmd.ExecuteReader()) - { - while (reader.Read()) - { - serverSPID = reader.GetInt16(0); - } - } + serverSPID = (int)cmd.ExecuteScalar()!; // SPID should match server's SPID Assert.Equal(serverSPID, conn.ServerProcessId); diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataReaderTest/DataReaderCancellationTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataReaderTest/DataReaderCancellationTest.cs index 5199922174..cef8fb2278 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataReaderTest/DataReaderCancellationTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataReaderTest/DataReaderCancellationTest.cs @@ -2,15 +2,41 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; using System.Diagnostics; using System.Threading; using System.Threading.Tasks; +using Microsoft.Data.SqlClient.Tests.Common; using Xunit; namespace Microsoft.Data.SqlClient.ManualTesting.Tests { public class DataReaderCancellationTest { + // @TODO: Make this a commonly used long running query? + // This query generates a billion results by performing a cross join on 10 rows 3 times (1k + // records), then cross joining that set 3 times (1b records). + private const string LongRunningQuery = + @"WITH " + + @" TenRows AS ( " + + @" SELECT value " + + @" FROM ( " + + @" VALUES (1), (2), (3), (4), (5), (6), (7), (8), (9), (10) " + + @" ) AS TenRows (value) " + + @" ), " + + @" ThousandRows AS ( " + + @" SELECT A.value AS A, B.value AS B, C.value AS C " + + @" FROM " + + @" TenRows AS A, " + + @" TenRows AS B, " + + @" TenRows AS C, " + + @" ) " + + @"SELECT * " + + @"FROM " + + @" ThousandRows AS A, " + + @" ThousandRows AS B, " + + @" ThousandRows AS C"; + /// /// Test ensures cancellation token is registered before ReadAsync starts processing results from TDS Stream, /// such that when Cancel is triggered, the token is capable of canceling reading further results. @@ -20,31 +46,37 @@ public class DataReaderCancellationTest [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureSynapse))] public static async Task CancellationTokenIsRespected_ReadAsync() { - const string longRunningQuery = @" -with TenRows as (select Value from (values (1), (2), (3), (4), (5), (6), (7), (8), (9), (10)) as TenRows (Value)), - ThousandRows as (select A.Value as A, B.Value as B, C.Value as C from TenRows as A, TenRows as B, TenRows as C) -select * -from ThousandRows as A, ThousandRows as B, ThousandRows as C;"; - - using (var source = new CancellationTokenSource()) - using (var connection = new SqlConnection(DataTestUtility.TCPConnectionString)) + // Arrange + using CancellationTokenSource source = new(); + + using SqlConnection connection = new(DataTestUtility.TCPConnectionString); + await connection.OpenAsync(source.Token); + + // - Set up command that returns millions of rows + using SqlCommand command = connection.CreateCommand(); + command.CommandText = LongRunningQuery; + + // Act + Func action = async () => { - await connection.OpenAsync(source.Token); + // - Execute query + using SqlDataReader reader = await command.ExecuteReaderAsync(source.Token); - Stopwatch stopwatch = Stopwatch.StartNew(); - await Assert.ThrowsAsync(async () => + // - Cancel after each record is read (should cancel after first record) + while (await reader.ReadAsync(source.Token)) { - using (var command = new SqlCommand(longRunningQuery, connection)) - using (var reader = await command.ExecuteReaderAsync(source.Token)) - { - while (await reader.ReadAsync(source.Token)) - { - source.Cancel(); - } - } - }); - Assert.True(stopwatch.ElapsedMilliseconds < 10000, "Cancellation did not trigger on time."); - } + source.Cancel(); + } + }; + + // Assert + // - Action should throw task cancelled exception + Stopwatch stopwatch = Stopwatch.StartNew(); + await Assert.ThrowsAsync(action); + stopwatch.Stop(); + + // - Ensure exception was thrown within 10 seconds of execution + Assert.True(stopwatch.ElapsedMilliseconds < 10000, "Cancellation did not trigger on time"); } /// @@ -56,30 +88,37 @@ await Assert.ThrowsAsync(async () => [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureSynapse))] public static async Task CancelledCancellationTokenIsRespected_ReadAsync() { - const string longRunningQuery = @" -with TenRows as (select Value from (values (1), (2), (3), (4), (5), (6), (7), (8), (9), (10)) as TenRows (Value)), - ThousandRows as (select A.Value as A, B.Value as B, C.Value as C from TenRows as A, TenRows as B, TenRows as C) -select * -from ThousandRows as A, ThousandRows as B, ThousandRows as C;"; - - using (var source = new CancellationTokenSource()) - using (var connection = new SqlConnection(DataTestUtility.TCPConnectionString)) + // Arrange + using CancellationTokenSource source = new(); + + using SqlConnection connection = new(DataTestUtility.TCPConnectionString); + await connection.OpenAsync(source.Token); + + // - Set up command that returns millions of rows + using SqlCommand command = connection.CreateCommand(); + command.CommandText = LongRunningQuery; + + // Act + Func action = async () => { - await connection.OpenAsync(source.Token); + // - Execute query + using SqlDataReader reader = await command.ExecuteReaderAsync(source.Token); - Stopwatch stopwatch = Stopwatch.StartNew(); - await Assert.ThrowsAsync(async () => - { - using (var command = new SqlCommand(longRunningQuery, connection)) - using (var reader = await command.ExecuteReaderAsync(source.Token)) - { - source.Cancel(); - while (await reader.ReadAsync(source.Token)) - { } - } - }); - Assert.True(stopwatch.ElapsedMilliseconds < 10000, "Cancellation did not trigger on time."); - } + // - Cancel before reading + source.Cancel(); + + // - Read all results (should cancel before first record is read) + await reader.FlushResultSetAsync(source.Token); + }; + + // Assert + // - Action should throw task cancelled exception + Stopwatch stopwatch = Stopwatch.StartNew(); + await Assert.ThrowsAsync(action); + stopwatch.Stop(); + + // - Ensure exception was thrown within 10 seconds of execution + Assert.True(stopwatch.ElapsedMilliseconds < 10000, "Cancellation did not trigger on time"); } } } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataStreamTest/DataStreamTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataStreamTest/DataStreamTest.cs index 74423f9f13..61dbd9d594 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataStreamTest/DataStreamTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/DataStreamTest/DataStreamTest.cs @@ -13,6 +13,7 @@ using System.Threading; using System.Threading.Tasks; using System.Xml; +using Microsoft.Data.SqlClient.Tests.Common; using Microsoft.Data.SqlClient.TestUtilities; using Xunit; using Xunit.Abstractions; @@ -1215,25 +1216,18 @@ private static void SqlCharsBytesTest(string connectionString) private static void CloseConnection(string connectionString) { - using (SqlConnection conn = new SqlConnection(connectionString)) - { - conn.Open(); - using (SqlCommand cmd = new SqlCommand("select * from orders where orderid < 10253", conn)) - using (SqlDataReader reader = cmd.ExecuteReader(CommandBehavior.CloseConnection)) - { - DataTestUtility.AssertEqualsWithDescription(ConnectionState.Open, conn.State, "FAILED: Connection should be in open state"); + using SqlConnection conn = new SqlConnection(connectionString); + conn.Open(); - while (reader.Read()) - { - for (int i = 0; i < reader.FieldCount; i++) - { - reader.GetValue(i); - } - } - } + using SqlCommand cmd = new SqlCommand("select * from orders where orderid < 10253", conn); + using (SqlDataReader reader = cmd.ExecuteReader(CommandBehavior.CloseConnection)) + { + DataTestUtility.AssertEqualsWithDescription(ConnectionState.Open, conn.State, "FAILED: Connection should be in open state"); - DataTestUtility.AssertEqualsWithDescription(ConnectionState.Closed, conn.State, "FAILED: Connection should be in closed state after reader close"); + reader.FlushResultSet(); } + + DataTestUtility.AssertEqualsWithDescription(ConnectionState.Closed, conn.State, "FAILED: Connection should be in closed state after reader close"); } private static void OpenConnection(string connectionString) diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ExceptionTest/ExceptionTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ExceptionTest/ExceptionTest.cs index 4179b7bf4b..637061597a 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ExceptionTest/ExceptionTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ExceptionTest/ExceptionTest.cs @@ -8,6 +8,7 @@ using System.Data; using System.Globalization; using System.Threading.Tasks; +using Microsoft.Data.SqlClient.Tests.Common; using Xunit; namespace Microsoft.Data.SqlClient.ManualTesting.Tests @@ -288,6 +289,7 @@ public static void EnclavesConnectionExceptionTest() } } + // @TODO: Verify this test is doing what we expect it to do. [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] public static async Task UnobservedTaskExceptionTest() { @@ -308,15 +310,8 @@ public static async Task UnobservedTaskExceptionTest() { try { - using (var reader = await command.ExecuteReaderAsync()) - { - do - { - while (await reader.ReadAsync()) - { - } - } while (await reader.NextResultAsync()); - } + using SqlDataReader reader = await command.ExecuteReaderAsync(); + await reader.FlushAllResultsAsync(); } catch (SqlException ex) { diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlCommand/SqlCommandCancelTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlCommand/SqlCommandCancelTest.cs index 29a83e6880..1ca4885ce5 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlCommand/SqlCommandCancelTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlCommand/SqlCommandCancelTest.cs @@ -6,6 +6,7 @@ using System.Data; using System.Threading; using System.Threading.Tasks; +using Microsoft.Data.SqlClient.Tests.Common; using Xunit; namespace Microsoft.Data.SqlClient.ManualTesting.Tests @@ -46,80 +47,86 @@ public static void PlainMARSCancelTestNP() // Synapse: Remove dependency on Northwind database + WAITFOR not supported + ';' not supported [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureSynapse))] - public static void PlainCancelTestAsync() - { + public static Task PlainCancelTestAsync() => PlainCancelAsync(tcp_connStr); - } [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureServer))] [PlatformSpecific(TestPlatforms.Windows)] - public static void PlainCancelTestAsyncNP() - { + public static Task PlainCancelTestAsyncNP() => PlainCancelAsync(np_connStr); - } // Synapse: Remove dependency from Northwind database + WAITFOR not supported + ';' not supported. [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureSynapse))] - public static void PlainMARSCancelTestAsync() - { + public static Task PlainMARSCancelTestAsync() => PlainCancelAsync((new SqlConnectionStringBuilder(tcp_connStr) { MultipleActiveResultSets = true }).ConnectionString); - } [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureServer))] [PlatformSpecific(TestPlatforms.Windows)] - public static void PlainMARSCancelTestAsyncNP() - { + public static Task PlainMARSCancelTestAsyncNP() => PlainCancelAsync((new SqlConnectionStringBuilder(np_connStr) { MultipleActiveResultSets = true }).ConnectionString); - } private static void PlainCancel(string connString) { - using (SqlConnection conn = new SqlConnection(connString)) - using (SqlCommand cmd = new SqlCommand("select * from dbo.Orders; waitfor delay '00:00:10'; select * from dbo.Orders", conn)) + // Arrange + using SqlConnection conn = new SqlConnection(connString); + conn.Open(); + + using SqlCommand command = conn.CreateCommand(); + command.CommandText = + @"SELECT * FROM dbo.Orders; " + + @"WAITFOR DELAY '00:00:10'; " + + @"SELECT * FROM dbo.Orders;"; + + // Act + Action action = () => { - conn.Open(); - using (SqlDataReader reader = cmd.ExecuteReader()) - { - cmd.Cancel(); - DataTestUtility.AssertThrowsWrapper( - () => - { - do - { - while (reader.Read()) - { - } - } - while (reader.NextResult()); - }, - "A severe error occurred on the current command. The results, if any, should be discarded."); - } - } + // - Execute reader + using SqlDataReader reader = command.ExecuteReader(); + + // - Cancel command + command.Cancel(); + + // - Flush results - should throw + reader.FlushAllResults(); + }; + + // Assert + SqlException exception = Assert.Throws(action); + Assert.Contains( + "A severe error occurred on the current command. The results, if any, should be discarded.", + exception.Message); } - private static void PlainCancelAsync(string connString) + private static async Task PlainCancelAsync(string connString) { - using (SqlConnection conn = new SqlConnection(connString)) - using (SqlCommand cmd = new SqlCommand("select * from dbo.Orders; waitfor delay '00:00:10'; select * from dbo.Orders", conn)) + // Arrange + using SqlConnection conn = new SqlConnection(connString); + conn.Open(); + + using SqlCommand command = conn.CreateCommand(); + command.CommandText = + @"SELECT * FROM dbo.Orders; " + + @"WAITFOR DELAY '00:00:10'; " + + @"SELECT * FROM dbo.Orders;"; + + // Act + Func action = async () => { - conn.Open(); - Task readerTask = cmd.ExecuteReaderAsync(); - DataTestUtility.AssertThrowsWrapper( - () => - { - readerTask.Wait(2000); - SqlDataReader reader = readerTask.Result; - cmd.Cancel(); - do - { - while (reader.Read()) - { - } - } - while (reader.NextResult()); - }, - "A severe error occurred on the current command. The results, if any, should be discarded."); - } + // - Execute reader + SqlDataReader reader = await command.ExecuteReaderAsync(); + + // - Cancel command + command.Cancel(); + + // - Flush results - should throw + await reader.FlushAllResultsAsync(); + }; + + // Assert + SqlException exception = await Assert.ThrowsAsync(action); + Assert.Contains( + "A severe error occurred on the current command. The results, if any, should be discarded.", + exception.Message); } // Synapse: Remove dependency from Northwind database + WAITFOR not supported + ';' not supported. @@ -428,13 +435,8 @@ private static void ExecuteCommandCancelExpected(object state) { threadsReady.SignalAndWait(); - using SqlDataReader r = command.ExecuteReader(); - do - { - while (r.Read()) - { - } - } while (r.NextResult()); + using SqlDataReader reader = command.ExecuteReader(); + reader.FlushAllResults(); }; SqlException exception = Assert.Throws(action); diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlStatisticsTest/SqlStatisticsTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlStatisticsTest/SqlStatisticsTest.cs index f04bc38a4d..c035532229 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlStatisticsTest/SqlStatisticsTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/SqlStatisticsTest/SqlStatisticsTest.cs @@ -6,6 +6,7 @@ using System.Data; using System.Data.Common; using System.Collections; +using Microsoft.Data.SqlClient.Tests.Common; using Xunit; namespace Microsoft.Data.SqlClient.ManualTesting.Tests @@ -37,11 +38,7 @@ public static void TestRetrieveStatistics() clientConnectionId = connection.ClientConnectionId; Assert.True(clientConnectionId != Guid.Empty); - int row = 0; - while (dr.Read()) - { - row++; - } + dr.FlushResultSet(); } } // Ensure calling RetrieveStatistics multiple times do not affect the ConnectionTime diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/TracingTests/DiagnosticTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/TracingTests/DiagnosticTest.cs index 96108636d0..7e0f10ed3f 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/TracingTests/DiagnosticTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/TracingTests/DiagnosticTest.cs @@ -143,10 +143,7 @@ public void ExecuteReaderTest() conn.Open(); SqlDataReader reader = cmd.ExecuteReader(); - while (reader.Read()) - { - // Read until end. - } + reader.FlushResultSet(); } }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandAfter, WriteConnectionCloseBefore, WriteConnectionCloseAfter]); return RemoteExecutor.SuccessExitCode; @@ -190,10 +187,7 @@ public void ExecuteReaderWithCommandBehaviorTest() conn.Open(); SqlDataReader reader = cmd.ExecuteReader(CommandBehavior.Default); - while (reader.Read()) - { - // Read to end - } + reader.FlushResultSet(); } }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandAfter, WriteConnectionCloseBefore, WriteConnectionCloseAfter]); return RemoteExecutor.SuccessExitCode; @@ -217,10 +211,7 @@ public void ExecuteXmlReaderTest() conn.Open(); XmlReader reader = cmd.ExecuteXmlReader(); - while (reader.Read()) - { - // Read to end - } + reader.FlushResultSet(); } }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandAfter, WriteConnectionCloseBefore, WriteConnectionCloseAfter]); return RemoteExecutor.SuccessExitCode; @@ -373,10 +364,7 @@ public void ExecuteReaderAsyncTest() conn.Open(); SqlDataReader reader = await cmd.ExecuteReaderAsync(); - while (reader.Read()) - { - // Read to end - } + reader.FlushResultSet(); } }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandAfter, WriteConnectionCloseBefore, WriteConnectionCloseAfter]).Wait(); return RemoteExecutor.SuccessExitCode; @@ -432,10 +420,7 @@ public void ExecuteXmlReaderAsyncTest() conn.Open(); XmlReader reader = await cmd.ExecuteXmlReaderAsync(); - while (reader.Read()) - { - // Read to end - } + reader.FlushResultSet(); } }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandAfter, WriteConnectionCloseBefore, WriteConnectionCloseAfter]).Wait(); return RemoteExecutor.SuccessExitCode; diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/TracingTests/EventSourceTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/TracingTests/EventSourceTest.cs index 4992c55974..df6ca2a37e 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/TracingTests/EventSourceTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/TracingTests/EventSourceTest.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Linq; +using Microsoft.Data.SqlClient.Tests.Common; using Xunit; namespace Microsoft.Data.SqlClient.ManualTesting.Tests @@ -18,10 +19,7 @@ public void EventSourceTestAll() connection.Open(); using SqlCommand command = new("SELECT @@VERSION", connection); using SqlDataReader reader = command.ExecuteReader(); - while (reader.Read()) - { - // Flush data - } + reader.FlushResultSet(); } // Need to investigate better way of validating traces in sequential runs, diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/TracingTests/XEventsTracingTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/TracingTests/XEventsTracingTest.cs index ffd6c8ff79..5381f661ba 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/TracingTests/XEventsTracingTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/TracingTests/XEventsTracingTest.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Xml; using System.Xml.XPath; +using Microsoft.Data.SqlClient.Tests.Common; using Xunit; using Xunit.Abstractions; @@ -51,10 +52,7 @@ ADD EVENT RPC_STARTING (ACTION (client_connection_id) WHERE (client_connection_i { using SqlCommand command = new(query, activityConnection) { CommandType = commandType }; using SqlDataReader reader = command.ExecuteReader(); - while (reader.Read()) - { - // Flush data - } + reader.FlushResultSet(); ids = TraceListener.ActivityIDs; } From a8b7ee42f849d5ec065e63cd9620d07f7bfb6035 Mon Sep 17 00:00:00 2001 From: Ben Russell Date: Fri, 26 Dec 2025 13:43:59 -0600 Subject: [PATCH 7/9] Addressing comments from copilot --- .../tests/FunctionalTests/MultiplexerTests.cs | 2 +- .../ManualTests/SQL/ParameterTest/ParametersTest.cs | 2 +- .../tests/ManualTests/TracingTests/DiagnosticTest.cs | 11 +++++++++-- .../Microsoft/Data/SqlClient/SqlErrorTests.cs | 2 +- 4 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/MultiplexerTests.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/MultiplexerTests.cs index 11553bd1b5..76270480bf 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/MultiplexerTests.cs +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/MultiplexerTests.cs @@ -270,7 +270,7 @@ private static List MultiplexPacketList(bool isAsync, int dataSize, if (stateObject._inBytesRead > 0) { // At least the header must be read - Assert.True(stateObject._inBytesRead < TdsEnums.HEADER_LEN); + Assert.False(stateObject._inBytesRead < TdsEnums.HEADER_LEN); // The full packet must be read, too Span header = stateObject._inBuff.AsSpan(0, TdsEnums.HEADER_LEN); diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/ParametersTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/ParametersTest.cs index d71435223e..72c3ac31bf 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/ParametersTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/ParametersTest.cs @@ -1021,7 +1021,7 @@ private static void RunParameterTest() object id2Value = cm.Parameters["@id2"].Value; if (id2Value is not null) { - // 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. Assert.Equal(expectedGuid, (Guid)id2Value); } } diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/TracingTests/DiagnosticTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/TracingTests/DiagnosticTest.cs index 7e0f10ed3f..ee65271ac3 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/TracingTests/DiagnosticTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/TracingTests/DiagnosticTest.cs @@ -23,6 +23,7 @@ using System.Runtime.CompilerServices; using System; using System.Data; +using Microsoft.Data.SqlClient.Tests.Common; using Microsoft.DotNet.RemoteExecutor; namespace Microsoft.Data.SqlClient.ManualTesting.Tests @@ -211,7 +212,10 @@ public void ExecuteXmlReaderTest() conn.Open(); XmlReader reader = cmd.ExecuteXmlReader(); - reader.FlushResultSet(); + while (reader.Read()) + { + // Flush results + } } }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandAfter, WriteConnectionCloseBefore, WriteConnectionCloseAfter]); return RemoteExecutor.SuccessExitCode; @@ -420,7 +424,10 @@ public void ExecuteXmlReaderAsyncTest() conn.Open(); XmlReader reader = await cmd.ExecuteXmlReaderAsync(); - reader.FlushResultSet(); + while (reader.Read()) + { + // Flush results + } } }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandAfter, WriteConnectionCloseBefore, WriteConnectionCloseAfter]).Wait(); return RemoteExecutor.SuccessExitCode; diff --git a/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/SqlErrorTests.cs b/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/SqlErrorTests.cs index 76917181ce..f6ef6bef1a 100644 --- a/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/SqlErrorTests.cs +++ b/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/SqlErrorTests.cs @@ -16,7 +16,7 @@ public void SerializationRoundTrip() { // Arrange DataContractSerializer serializer = new(typeof(SqlError)); - MemoryStream stream = new(); + using MemoryStream stream = new(); // - Create the test error SqlError originalError = new( From 39d3839057086adf7e24b4ced1f51d603d236d6f Mon Sep 17 00:00:00 2001 From: Ben Russell Date: Mon, 29 Dec 2025 14:29:09 -0600 Subject: [PATCH 8/9] Fix issue with sqlerrorcollectiontests --- .../Data/SqlClient/SqlErrorCollection.cs | 7 +- .../Data/SqlClient/SqlErrorCollectionTests.cs | 149 ++++++++++-------- 2 files changed, 92 insertions(+), 64 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlErrorCollection.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlErrorCollection.cs index f125f86ccc..4684747627 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlErrorCollection.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlErrorCollection.cs @@ -13,7 +13,12 @@ namespace Microsoft.Data.SqlClient [Serializable, ListBindable(false)] public sealed class SqlErrorCollection : ICollection { - private readonly List _errors = []; + // Ideally this would be typed as List, but that would make the non-generic + // CopyTo behave differently than the full framework (which uses ArrayList), throwing + // ArgumentException instead of the expected InvalidCastException for incompatible types. + // Instead, we use List, which makes the non-generic CopyTo behave like + // ArrayList.CopyTo. + private readonly List _errors = new List(); internal SqlErrorCollection() { } diff --git a/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/SqlErrorCollectionTests.cs b/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/SqlErrorCollectionTests.cs index 9594db35bd..f9ea12027a 100644 --- a/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/SqlErrorCollectionTests.cs +++ b/src/Microsoft.Data.SqlClient/tests/UnitTests/Microsoft/Data/SqlClient/SqlErrorCollectionTests.cs @@ -11,6 +11,8 @@ namespace Microsoft.Data.SqlClient.UnitTests.Microsoft.Data.SqlClient { public class SqlErrorCollectionTests { + private const int ErrorsInTestCollection = 3; + [Fact] public void Constructor_PropertiesInitialized() { @@ -45,89 +47,93 @@ public void Add(int itemsToAdd) // Assert Assert.Equal(itemsToAdd, collection.Count); } - - [Fact] - public void CopyTo_ObjectArray_ZeroIndex() + + [Theory] + [InlineData(ErrorsInTestCollection, 0)] // Destination just right size + [InlineData(ErrorsInTestCollection + 10, 0)] // Null elements at end + [InlineData(ErrorsInTestCollection + 2, 2)] // Null elements at beginning + [InlineData(ErrorsInTestCollection + 10, 1)] // Null elements at beginning and end + public void CopyTo_SqlErrorArray_WithinRange(int destinationSize, int offset) { // Arrange (SqlErrorCollection collection, SqlError[] errors) = GetTestErrorCollection(); - object[] copyDestination = new object[errors.Length]; - + SqlError[] copyDestination = new SqlError[destinationSize]; + // Act - collection.CopyTo(copyDestination, index: 0); + // - Uses SqlErrorCollection.CopyTo + collection.CopyTo(copyDestination, offset); // Assert - for (int i = 0; i < errors.Length; i++) - { - Assert.Same(errors[i], copyDestination[i]); - } + AssertCopiedCollection(errors, copyDestination, offset); } - - [Fact] - public void CopyTo_ObjectArray_NonZeroIndex() + + [Theory] + [InlineData(ErrorsInTestCollection, -1)] // Offset is negative + [InlineData(ErrorsInTestCollection - 1, 0)] // Destination is too small + [InlineData(ErrorsInTestCollection, 1)] // Destination is big enough, but offset pushes it over edge + public void CopyTo_SqlErrorArray_OutOfRange(int destinationSize, int offset) { // Arrange - (SqlErrorCollection collection, SqlError[] errors) = GetTestErrorCollection(); - object[] copyDestination = new object[errors.Length + 1]; - + (SqlErrorCollection collection, SqlError[] _) = GetTestErrorCollection(); + SqlError[] copyDestination = new SqlError[destinationSize]; + // Act - collection.CopyTo(copyDestination, index: 1); + // - Uses ICollection.CopyTo + Action action = () => collection.CopyTo(copyDestination, offset); // Assert - Assert.Null(copyDestination[0]); - for (int i = 0; i < errors.Length; i++) - { - Assert.Same(errors[i], copyDestination[i + 1]); - } + Assert.ThrowsAny(action); } - [Fact] - public void CopyTo_ObjectArray_WrongType() + [Theory] + [InlineData(ErrorsInTestCollection, 0)] // Destination just right size + [InlineData(ErrorsInTestCollection + 10, 0)] // Null elements at end + [InlineData(ErrorsInTestCollection + 2, 2)] // Null elements at beginning + [InlineData(ErrorsInTestCollection + 10, 1)] // Null elements at beginning and end + public void CopyTo_ObjectArray_WithinRange(int destinationSize, int offset) { // Arrange - (SqlErrorCollection collection, _) = GetTestErrorCollection(); - int[] copyDestination = new int[1]; - + (SqlErrorCollection collection, SqlError[] errors) = GetTestErrorCollection(); + object[] copyDestination = new object[destinationSize]; + // Act - Action action = () => collection.CopyTo(copyDestination, index: 0); - + // - Uses ICollection.CopyTo + collection.CopyTo(copyDestination, offset); + // Assert - Assert.Throws(action); + AssertCopiedCollection(errors, copyDestination, offset); } - [Fact] - public void CopyTo_TypedArray_ZeroIndex() + [Theory] + [InlineData(ErrorsInTestCollection, -1)] // Offset is negative + [InlineData(ErrorsInTestCollection - 1, 0)] // Destination is too small + [InlineData(ErrorsInTestCollection, 1)] // Destination is big enough, but offset pushes it over edge + public void CopyTo_ObjectArray_OutOfRange(int destinationSize, int offset) { // Arrange - (SqlErrorCollection collection, SqlError[] errors) = GetTestErrorCollection(); - SqlError[] copyDestination = new SqlError[errors.Length + 1]; - + (SqlErrorCollection collection, SqlError[] _) = GetTestErrorCollection(); + SqlError[] copyDestination = new SqlError[destinationSize]; + // Act - collection.CopyTo(copyDestination, index: 0); - + // - Uses ICollection.CopyTo + Action action = () => collection.CopyTo(copyDestination, offset); + // Assert - for (int i = 0; i < errors.Length; i++) - { - Assert.Same(errors[i], copyDestination[i]); - } + Assert.ThrowsAny(action); } [Fact] - public void CopyTo_TypedArray_NonZeroIndex() + public void CopyTo_ObjectArray_WrongType() { // Arrange (SqlErrorCollection collection, SqlError[] errors) = GetTestErrorCollection(); - object[] copyDestination = new object[errors.Length + 1]; - + int[] destination = new int[errors.Length]; + // Act - collection.CopyTo(copyDestination, index: 1); - + Action action = () => collection.CopyTo(destination, 0); + // Assert - Assert.Null(copyDestination[0]); - for (int i = 0; i < errors.Length; i++) - { - Assert.Same(errors[i], copyDestination[i + 1]); - } + Assert.Throws(action); } [Fact] @@ -181,6 +187,28 @@ public void Indexer_OutOfRange(int index) Assert.Throws(action); } + private static void AssertCopiedCollection(SqlError[] source, IReadOnlyList destination, int offset) + { + for (int i = 0; i < destination.Count; i++) + { + if (i < offset) + { + // - Elements before the offset should be null + Assert.Null(destination[i]); + } + else if (i >= offset && i < source.Length + offset) + { + // - Elements after the offset but within the range of original elements should match + Assert.Same(source[i - offset], destination[i]); + } + else + { + // - Elements after the offset and original elements should be null + Assert.Null(destination[i]); + } + } + } + private static SqlError GetTestError() { return new SqlError( @@ -198,19 +226,14 @@ private static SqlError GetTestError() private static (SqlErrorCollection collection, SqlError[] errors) GetTestErrorCollection() { SqlErrorCollection collection = new(); - SqlError[] errors = new SqlError[3]; - - SqlError error1 = GetTestError(); - collection.Add(error1); - errors[0] = error1; - - SqlError error2 = GetTestError(); - collection.Add(error2); - errors[1] = error2; + SqlError[] errors = new SqlError[ErrorsInTestCollection]; - SqlError error3 = GetTestError(); - collection.Add(error3); - errors[2] = error3; + for (int i = 0; i < ErrorsInTestCollection; i++) + { + SqlError error = GetTestError(); + errors[i] = error; + collection.Add(error); + } return (collection, errors); } From 72bf54681f0550ac289755c2a1643fc19eb20c6b Mon Sep 17 00:00:00 2001 From: Ben Russell Date: Mon, 29 Dec 2025 14:29:39 -0600 Subject: [PATCH 9/9] Remove old SqlErrorCollectionTest --- ...soft.Data.SqlClient.FunctionalTests.csproj | 1 - .../FunctionalTests/SqlErrorCollectionTest.cs | 157 ------------------ 2 files changed, 158 deletions(-) delete mode 100644 src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlErrorCollectionTest.cs diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/Microsoft.Data.SqlClient.FunctionalTests.csproj b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/Microsoft.Data.SqlClient.FunctionalTests.csproj index c967d800d5..99287f012d 100644 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/Microsoft.Data.SqlClient.FunctionalTests.csproj +++ b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/Microsoft.Data.SqlClient.FunctionalTests.csproj @@ -53,7 +53,6 @@ - diff --git a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlErrorCollectionTest.cs b/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlErrorCollectionTest.cs deleted file mode 100644 index 32f236c761..0000000000 --- a/src/Microsoft.Data.SqlClient/tests/FunctionalTests/SqlErrorCollectionTest.cs +++ /dev/null @@ -1,157 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.Collections; -using Xunit; - -namespace Microsoft.Data.SqlClient.Tests -{ - public class SqlErrorCollectionTest - { - private const string badServer = "92B96911A0BD43E8ADA4451031F7E7CF"; - - [Fact] - public void IsSynchronized_Success() - { - ICollection c = CreateCollection(); - Assert.False(c.IsSynchronized); - } - - [Fact] - public void SyncRoot_Success() - { - ICollection c = CreateCollection(); - Assert.Same(c, c.SyncRoot); - } - - [Fact] - public void Indexer_Success() - { - SqlErrorCollection c = CreateCollection(); - Assert.NotNull(c[0]); - Assert.Same(c[0], c[0]); - } - - [Fact] - public void Indexer_Throws() - { - SqlErrorCollection c = CreateCollection(); - - AssertExtensions.Throws("index", () => c[-1]); - AssertExtensions.Throws("index", () => c[c.Count]); - } - - [Fact] - public void CopyTo_Success() - { - ValidateCopyTo((collection, array, index) => collection.CopyTo(array, index)); - } - - [Fact] - public void CopyTo_NonGeneric_Success() - { - ValidateCopyTo((collection, array, index) => ((ICollection)collection).CopyTo(array, index)); - } - - private static void ValidateCopyTo(Action copyTo) - { - SqlErrorCollection c = CreateCollection(); - SqlError[] destination = new SqlError[5]; - - copyTo(c, destination, 2); - - Assert.Null(destination[0]); - Assert.Null(destination[1]); - Assert.Same(c[0], destination[2]); - Assert.Null(destination[3]); - Assert.Null(destination[4]); - } - - [Fact] - public void CopyTo_Throws() - { - ValidateCopyToThrows((collection, array, index) => collection.CopyTo(array, index)); - } - - [Fact] - public void CopyTo_NonGeneric_Throws() - { - ValidateCopyToThrows((collection, array, index) => ((ICollection)collection).CopyTo(array, index), c => - { - ICollection ic = c; - AssertExtensions.Throws(null, () => ic.CopyTo(new SqlError[4, 3], 0)); - Assert.Throws(() => ic.CopyTo(new string[10], 0)); - }); - } - - private static void ValidateCopyToThrows( - Action copyTo, - Action additionalValidation = null) - { - SqlErrorCollection c = CreateCollection(); - - Assert.Throws(() => copyTo(c, null, 0)); - Assert.Throws(() => copyTo(c, null, -1)); - Assert.Throws(() => copyTo(c, new SqlError[10], -1)); - AssertExtensions.Throws("destinationArray", "", () => copyTo(c, new SqlError[10], 1000)); - - additionalValidation?.Invoke(c); - } - - [Fact] - public void GetEnumerator_Success() - { - SqlErrorCollection c = CreateCollection(); - - IEnumerator e = c.GetEnumerator(); - - Assert.NotNull(e); - Assert.NotSame(e, c.GetEnumerator()); - - for (int i = 0; i < 2; i++) - { - Assert.Throws(() => e.Current); - - Assert.True(e.MoveNext()); - Assert.Same(c[0], e.Current); - - Assert.False(e.MoveNext()); - Assert.False(e.MoveNext()); - Assert.False(e.MoveNext()); - - Assert.Throws(() => e.Current); - - e.Reset(); - } - } - - private static SqlErrorCollection CreateCollection() - { - var builder = new SqlConnectionStringBuilder() - { - DataSource = badServer, - ConnectTimeout = 1, - Pooling = false - }; - - using (var connection = new SqlConnection(builder.ConnectionString)) - { - try - { - connection.Open(); - } - catch (SqlException ex) - { - Assert.NotNull(ex.Errors); - Assert.Single(ex.Errors); - - return ex.Errors; - } - } - - throw new InvalidOperationException("SqlException.Errors should have been returned."); - } - } -}