Skip to content

Commit c2554ad

Browse files
authored
Tests | Active Issues (Part 3) (#3642)
1 parent a8770ab commit c2554ad

File tree

6 files changed

+239
-223
lines changed

6 files changed

+239
-223
lines changed

src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/LocalDBTest/LocalDBTest.cs

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -92,25 +92,22 @@ public static void SqlLocalDbSharedInstanceConnectionTest()
9292

9393
#region NamedPipeTests
9494

95-
[Fact]
96-
[ActiveIssue("20245")] //pending pipeline configuration
95+
[ConditionalFact(nameof(IsLocalDBEnvironmentSet))]
9796
public static void SqlLocalDbNamedPipeConnectionTest()
9897
{
9998
ConnectionTest(s_localDbNamedPipeConnectionString);
10099
}
101100

102-
[Fact]
103-
[ActiveIssue("20245")] //pending pipeline configuration
104-
public static void LocalDBNamedPipeEncryptionNotSupportedTest()
101+
[ConditionalFact(nameof(IsLocalDBEnvironmentSet))]
102+
public static void LocalDbNamedPipeEncryptionNotSupportedTest()
105103
{
106104
// Encryption is not supported by SQL Local DB.
107105
// But connection should succeed as encryption is disabled by driver.
108106
ConnectionWithEncryptionTest(s_localDbNamedPipeConnectionString);
109107
}
110108

111-
[Fact]
112-
[ActiveIssue("20245")] //pending pipeline configuration
113-
public static void LocalDBNamepipeMarsTest()
109+
[ConditionalFact(nameof(IsLocalDBEnvironmentSet))]
110+
public static void LocalDbNamedPipeMarsTest()
114111
{
115112
ConnectionWithMarsTest(s_localDbNamedPipeConnectionString);
116113
}

src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/ParameterTest/TvpTest.cs

Lines changed: 46 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -53,34 +53,42 @@ public void TestMain()
5353
}
5454

5555
[ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureServer))]
56-
[ActiveIssue("5531")]
57-
public void TestPacketNumberWraparound()
58-
{
59-
// this test uses a specifically crafted sql record enumerator and data to put the TdsParserStateObject.WritePacket(byte,bool)
60-
// into a state where it can't differentiate between a packet in the middle of a large packet-set after a byte counter wraparound
61-
// and the first packet of the connection and in doing so trips over a check for packet length from the input which has been
62-
// forced to tell it that there is no output buffer space left, this causes an uncancellable infinite loop
63-
64-
// if the enumerator is completely read to the end then the bug is no longer present and the packet creation task returns,
65-
// if the timeout occurs it is probable (but not absolute) that the write is stuck
66-
56+
public async Task TestPacketNumberWraparound()
57+
{
58+
// This test uses a specifically crafted SQL record enumerator and data to put the
59+
// TdsParserStateObject.WritePacket(byte,bool) into a state where it can't
60+
// differentiate between a packet in the middle of a large packet-set after a byte
61+
// counter wraparound and the first packet of the connection and in doing so trips over
62+
// a check for packet length from the input which has been forced to tell it that there
63+
// is no output buffer space left, this causes an uncancellable infinite loop.
64+
//
65+
// If the enumerator is completely read to the end then the bug is no longer present
66+
// and the packet creation task returns, if the timeout occurs it is probable (but not
67+
// absolute) that the write operation is stuck.
68+
69+
// Arrange
6770
var enumerator = new WraparoundRowEnumerator(1000000);
71+
using var cancellationTokenSource = new CancellationTokenSource();
6872

73+
// Act
6974
Stopwatch stopwatch = new();
7075
stopwatch.Start();
71-
int returned = Task.WaitAny(
72-
Task.Factory.StartNew(
73-
() => RunPacketNumberWraparound(enumerator),
74-
TaskCreationOptions.DenyChildAttach | TaskCreationOptions.LongRunning
75-
),
76-
Task.Delay(TimeSpan.FromSeconds(60))
77-
);
76+
77+
Task actionTask = Task.Factory.StartNew(
78+
async () => await RunPacketNumberWraparound(enumerator, cancellationTokenSource.Token),
79+
TaskCreationOptions.DenyChildAttach | TaskCreationOptions.LongRunning);
80+
Task timeoutTask = Task.Delay(TimeSpan.FromSeconds(60), cancellationTokenSource.Token);
81+
await Task.WhenAny(actionTask, timeoutTask);
82+
7883
stopwatch.Stop();
79-
if (enumerator.MaxCount != enumerator.Count)
80-
{
81-
Console.WriteLine($"enumerator.Count={enumerator.Count}, enumerator.MaxCount={enumerator.MaxCount}, elapsed={stopwatch.Elapsed.TotalSeconds}");
82-
}
83-
Assert.True(enumerator.MaxCount == enumerator.Count);
84+
cancellationTokenSource.Cancel();
85+
86+
// Assert
87+
Assert.True(
88+
enumerator.MaxCount == enumerator.Count,
89+
$"enumerator.Count={enumerator.Count}, " +
90+
$"enumerator.MaxCount={enumerator.MaxCount}, " +
91+
$"elapsed={stopwatch.Elapsed}");
8492
}
8593

8694
[ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureServer))]
@@ -694,23 +702,27 @@ private void QueryHintsTest()
694702
}
695703
}
696704

697-
private static async Task RunPacketNumberWraparound(WraparoundRowEnumerator enumerator)
705+
private static async Task RunPacketNumberWraparound(
706+
WraparoundRowEnumerator enumerator,
707+
CancellationToken cancellationToken)
698708
{
699709
using var connection = new SqlConnection(DataTestUtility.TCPConnectionString);
700-
using var cmd = new SqlCommand("unimportant")
701-
{
702-
CommandType = CommandType.StoredProcedure,
703-
Connection = connection,
704-
};
705-
await cmd.Connection.OpenAsync();
706-
cmd.Parameters.Add(new SqlParameter("@rows", SqlDbType.Structured)
710+
await connection.OpenAsync(cancellationToken);
711+
712+
using var cmd = connection.CreateCommand();
713+
cmd.CommandType = CommandType.StoredProcedure;
714+
cmd.CommandText = "unimportant";
715+
716+
var parameter = new SqlParameter("@rows", SqlDbType.Structured)
707717
{
708718
TypeName = "unimportant",
709-
Value = enumerator,
710-
});
719+
Value = enumerator
720+
};
721+
cmd.Parameters.Add(parameter);
722+
711723
try
712724
{
713-
await cmd.ExecuteNonQueryAsync();
725+
await cmd.ExecuteNonQueryAsync(cancellationToken);
714726
}
715727
catch (Exception)
716728
{

src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/RetryLogic/RetryLogicTestHelper.cs

Lines changed: 56 additions & 152 deletions
Original file line numberDiff line numberDiff line change
@@ -7,41 +7,10 @@
77
using System.Linq;
88
using System.Text;
99
using System.Text.RegularExpressions;
10+
using Xunit;
1011

1112
namespace Microsoft.Data.SqlClient.ManualTesting.Tests
1213
{
13-
/// Define the SQL command type by filtering purpose.
14-
[Flags]
15-
public enum FilterSqlStatements
16-
{
17-
/// Don't filter any SQL commands
18-
None = 0,
19-
/// Filter INSERT or INSERT INTO
20-
Insert = 1,
21-
/// Filter UPDATE
22-
Update = 2,
23-
/// Filter DELETE
24-
Delete = 1 << 2,
25-
/// Filter EXECUTE or EXEC
26-
Execute = 1 << 3,
27-
/// Filter ALTER
28-
Alter = 1 << 4,
29-
/// Filter CREATE
30-
Create = 1 << 5,
31-
/// Filter DROP
32-
Drop = 1 << 6,
33-
/// Filter TRUNCATE
34-
Truncate = 1 << 7,
35-
/// Filter SELECT
36-
Select = 1 << 8,
37-
/// Filter data manipulation commands consist of INSERT, INSERT INTO, UPDATE, and DELETE
38-
DML = Insert | Update | Delete | Truncate,
39-
/// Filter data definition commands consist of ALTER, CREATE, and DROP
40-
DDL = Alter | Create | Drop,
41-
/// Filter any SQL command types
42-
All = DML | DDL | Execute | Select
43-
}
44-
4514
public class RetryLogicTestHelper
4615
{
4716
private static readonly HashSet<int> s_defaultTransientErrors
@@ -75,152 +44,87 @@ private static readonly HashSet<int> s_defaultTransientErrors
7544
18456 // Using managed identity in Azure Sql Server throws 18456 for non-existent database instead of 4060.
7645
};
7746

47+
public static readonly Regex FilterDmlStatements = new Regex(
48+
@"\b(INSERT( +INTO)|UPDATE|DELETE|TRUNCATE)\b",
49+
RegexOptions.Compiled | RegexOptions.IgnoreCase);
50+
7851
internal static readonly string s_exceedErrMsgPattern = SystemDataResourceManager.Instance.SqlRetryLogic_RetryExceeded;
7952
internal static readonly string s_cancelErrMsgPattern = SystemDataResourceManager.Instance.SqlRetryLogic_RetryCanceled;
8053

81-
public static IEnumerable<object[]> GetConnectionStrings()
54+
public static TheoryData<string, SqlRetryLogicBaseProvider> GetConnectionStringAndRetryProviders(
55+
int numberOfRetries,
56+
TimeSpan maxInterval,
57+
TimeSpan? deltaTime = null,
58+
IEnumerable<int> transientErrorCodes = null,
59+
Regex unauthorizedStatementRegex = null)
8260
{
83-
var builder = new SqlConnectionStringBuilder();
84-
85-
foreach (var cnnString in DataTestUtility.GetConnectionStrings(withEnclave: false))
86-
{
87-
builder.Clear();
88-
builder.ConnectionString = cnnString;
89-
builder.ConnectTimeout = 5;
90-
builder.Pooling = false;
91-
yield return new object[] { builder.ConnectionString };
92-
93-
builder.Pooling = true;
94-
yield return new object[] { builder.ConnectionString };
95-
}
96-
}
97-
98-
public static IEnumerable<object[]> GetConnectionAndRetryStrategy(int numberOfRetries,
99-
TimeSpan maxInterval,
100-
FilterSqlStatements unauthorizedStatemets,
101-
IEnumerable<int> transientErrors,
102-
int deltaTimeMillisecond = 10,
103-
bool custom = true)
104-
{
105-
var option = new SqlRetryLogicOption()
61+
var option = new SqlRetryLogicOption
10662
{
10763
NumberOfTries = numberOfRetries,
108-
DeltaTime = TimeSpan.FromMilliseconds(deltaTimeMillisecond),
64+
DeltaTime = deltaTime ?? TimeSpan.FromMilliseconds(10),
10965
MaxTimeInterval = maxInterval,
110-
TransientErrors = transientErrors ?? (custom ? s_defaultTransientErrors : null),
111-
AuthorizedSqlCondition = custom ? RetryPreConditon(unauthorizedStatemets) : null
66+
TransientErrors = transientErrorCodes ?? s_defaultTransientErrors,
67+
AuthorizedSqlCondition = RetryPreCondition(unauthorizedStatementRegex)
11268
};
11369

114-
foreach (var item in GetRetryStrategies(option))
115-
foreach (var cnn in GetConnectionStrings())
116-
yield return new object[] { cnn[0], item[0] };
117-
}
118-
119-
public static IEnumerable<object[]> GetConnectionAndRetryStrategyInvalidCatalog(int numberOfRetries)
120-
{
121-
return GetConnectionAndRetryStrategy(numberOfRetries, TimeSpan.FromSeconds(1), FilterSqlStatements.None, null, 250, true);
122-
}
70+
var result = new TheoryData<string, SqlRetryLogicBaseProvider>();
71+
foreach (var connectionString in GetConnectionStringsTyped())
72+
{
73+
foreach (var retryProvider in GetRetryStrategiesTyped(option))
74+
{
75+
result.Add(connectionString, retryProvider);
76+
}
77+
}
12378

124-
public static IEnumerable<object[]> GetConnectionAndRetryStrategyInvalidCommand(int numberOfRetries)
125-
{
126-
return GetConnectionAndRetryStrategy(numberOfRetries, TimeSpan.FromMilliseconds(100), FilterSqlStatements.None, null);
79+
return result;
12780
}
12881

129-
public static IEnumerable<object[]> GetConnectionAndRetryStrategyFilterDMLStatements(int numberOfRetries)
130-
{
131-
return GetConnectionAndRetryStrategy(numberOfRetries, TimeSpan.FromMilliseconds(100), FilterSqlStatements.DML, new int[] { 207, 102, 2812 });
132-
}
82+
public static TheoryData<string, SqlRetryLogicBaseProvider> GetNonRetriableCases() =>
83+
new TheoryData<string, SqlRetryLogicBaseProvider>
84+
{
85+
{ DataTestUtility.TCPConnectionString, null },
86+
{ DataTestUtility.TCPConnectionString, SqlConfigurableRetryFactory.CreateNoneRetryProvider() }
87+
};
13388

134-
//40613: Database '%.*ls' on server '%.*ls' is not currently available. Please retry the connection later. If the problem persists, contact customer support, and provide them the session tracing ID of '%.*ls'.
135-
public static IEnumerable<object[]> GetConnectionAndRetryStrategyLongRunner(int numberOfRetries)
89+
private static IEnumerable<string> GetConnectionStringsTyped()
13690
{
137-
return GetConnectionAndRetryStrategy(numberOfRetries, TimeSpan.FromSeconds(120), FilterSqlStatements.None, null, 20 * 1000);
138-
}
91+
var builder = new SqlConnectionStringBuilder();
92+
foreach (var connectionString in DataTestUtility.GetConnectionStrings(withEnclave: false))
93+
{
94+
builder.Clear();
95+
builder.ConnectionString = connectionString;
96+
builder.ConnectTimeout = 5;
97+
builder.Pooling = false;
98+
yield return builder.ConnectionString;
13999

140-
public static IEnumerable<object[]> GetConnectionAndRetryStrategyDropDB(int numberOfRetries)
141-
{
142-
List<int> faults = s_defaultTransientErrors.ToList();
143-
faults.Add(3702); // Cannot drop database because it is currently in use.
144-
return GetConnectionAndRetryStrategy(numberOfRetries, TimeSpan.FromMilliseconds(2000), FilterSqlStatements.None, faults, 500);
100+
builder.Pooling = true;
101+
yield return builder.ConnectionString;
102+
}
145103
}
146104

147-
public static IEnumerable<object[]> GetConnectionAndRetryStrategyLockedTable(int numberOfRetries)
105+
private static IEnumerable<SqlRetryLogicBaseProvider> GetRetryStrategiesTyped(SqlRetryLogicOption option)
148106
{
149-
return GetConnectionAndRetryStrategy(numberOfRetries, TimeSpan.FromMilliseconds(100), FilterSqlStatements.None, null);
107+
yield return SqlConfigurableRetryFactory.CreateExponentialRetryProvider(option);
108+
yield return SqlConfigurableRetryFactory.CreateIncrementalRetryProvider(option);
109+
yield return SqlConfigurableRetryFactory.CreateFixedRetryProvider(option);
150110
}
151111

152-
public static IEnumerable<object[]> GetNoneRetriableCondition()
112+
public static IEnumerable<int> GetDefaultTransientErrorCodes(params int[] additionalCodes)
153113
{
154-
yield return new object[] { DataTestUtility.TCPConnectionString, null };
155-
yield return new object[] { DataTestUtility.TCPConnectionString, SqlConfigurableRetryFactory.CreateNoneRetryProvider() };
156-
}
114+
var transientErrorCodes = new HashSet<int>(s_defaultTransientErrors);
115+
foreach (int additionalCode in additionalCodes)
116+
{
117+
transientErrorCodes.Add(additionalCode);
118+
}
157119

158-
private static IEnumerable<object[]> GetRetryStrategies(SqlRetryLogicOption retryLogicOption)
159-
{
160-
yield return new object[] { SqlConfigurableRetryFactory.CreateExponentialRetryProvider(retryLogicOption) };
161-
yield return new object[] { SqlConfigurableRetryFactory.CreateIncrementalRetryProvider(retryLogicOption) };
162-
yield return new object[] { SqlConfigurableRetryFactory.CreateFixedRetryProvider(retryLogicOption) };
120+
return transientErrorCodes;
163121
}
164122

165123
/// Generate a predicate function to skip unauthorized SQL commands.
166-
private static Predicate<string> RetryPreConditon(FilterSqlStatements unauthorizedSqlStatements)
167-
{
168-
var pattern = GetRegexPattern(unauthorizedSqlStatements);
169-
return (commandText) => string.IsNullOrEmpty(pattern)
170-
|| !Regex.IsMatch(commandText, pattern, RegexOptions.Compiled | RegexOptions.IgnoreCase);
171-
}
172-
173-
/// Provide a regex pattern regarding to the SQL statement.
174-
private static string GetRegexPattern(FilterSqlStatements sqlStatements)
124+
private static Predicate<string> RetryPreCondition(Regex unauthorizedStatementRegex)
175125
{
176-
if (sqlStatements == FilterSqlStatements.None)
177-
{
178-
return string.Empty;
179-
}
180-
181-
var pattern = new StringBuilder();
182-
183-
if (sqlStatements.HasFlag(FilterSqlStatements.Insert))
184-
{
185-
pattern.Append(@"INSERT( +INTO){0,1}|");
186-
}
187-
if (sqlStatements.HasFlag(FilterSqlStatements.Update))
188-
{
189-
pattern.Append(@"UPDATE|");
190-
}
191-
if (sqlStatements.HasFlag(FilterSqlStatements.Delete))
192-
{
193-
pattern.Append(@"DELETE|");
194-
}
195-
if (sqlStatements.HasFlag(FilterSqlStatements.Execute))
196-
{
197-
pattern.Append(@"EXEC(UTE){0,1}|");
198-
}
199-
if (sqlStatements.HasFlag(FilterSqlStatements.Alter))
200-
{
201-
pattern.Append(@"ALTER|");
202-
}
203-
if (sqlStatements.HasFlag(FilterSqlStatements.Create))
204-
{
205-
pattern.Append(@"CREATE|");
206-
}
207-
if (sqlStatements.HasFlag(FilterSqlStatements.Drop))
208-
{
209-
pattern.Append(@"DROP|");
210-
}
211-
if (sqlStatements.HasFlag(FilterSqlStatements.Truncate))
212-
{
213-
pattern.Append(@"TRUNCATE|");
214-
}
215-
if (sqlStatements.HasFlag(FilterSqlStatements.Select))
216-
{
217-
pattern.Append(@"SELECT|");
218-
}
219-
if (pattern.Length > 0)
220-
{
221-
pattern.Remove(pattern.Length - 1, 1);
222-
}
223-
return string.Format(@"\b({0})\b", pattern.ToString());
126+
return commandText => unauthorizedStatementRegex is null ||
127+
!unauthorizedStatementRegex.IsMatch(commandText);
224128
}
225129
}
226130
}

0 commit comments

Comments
 (0)