diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnection.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnection.cs index e22988b35d..b96e6f654d 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnection.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnection.cs @@ -1937,8 +1937,10 @@ private Task InternalOpenAsync(SqlConnectionOverrides overrides, CancellationTok s_diagnosticListener.IsEnabled(SqlClientConnectionOpenError.Name)) { result.Task.ContinueWith( - continuationAction: s_openAsyncComplete, + continuationAction: static (task, state) => s_openAsyncComplete(task, state), state: operationId, // connection is passed in TaskCompletionSource async state + cancellationToken: CancellationToken.None, // we want the continuation task to run even if the original operation was cancelled + continuationOptions: TaskContinuationOptions.ExecuteSynchronously, scheduler: TaskScheduler.Default ); } diff --git a/src/Microsoft.Data.SqlClient/tests/Directory.Packages.props b/src/Microsoft.Data.SqlClient/tests/Directory.Packages.props index 78ea36f1fa..72bddcc898 100644 --- a/src/Microsoft.Data.SqlClient/tests/Directory.Packages.props +++ b/src/Microsoft.Data.SqlClient/tests/Directory.Packages.props @@ -12,7 +12,6 @@ - diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTests.csproj b/src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTests.csproj index 336838b068..25a1d53d0d 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTests.csproj +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTests.csproj @@ -342,7 +342,8 @@ + Condition="'$(ReferenceType)' != 'Package'" + Private="true" /> @@ -360,7 +361,6 @@ - @@ -395,7 +395,6 @@ - @@ -422,4 +421,5 @@ + diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/TracingTests/DiagnosticTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/TracingTests/DiagnosticTest.cs index f550e7db70..db49fc7c32 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/TracingTests/DiagnosticTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/TracingTests/DiagnosticTest.cs @@ -2,31 +2,30 @@ // 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 System.Collections.Generic; +using System.Data; using System.Diagnostics; using System.Reflection; +using System.Runtime.CompilerServices; using System.Threading.Tasks; using System.Xml; using Microsoft.SqlServer.TDS; using Microsoft.SqlServer.TDS.Done; using Microsoft.SqlServer.TDS.EndPoint; using Microsoft.SqlServer.TDS.Error; -using Microsoft.SqlServer.TDS.Servers; using Microsoft.SqlServer.TDS.SQLBatch; +using Microsoft.SqlServer.TDS.Servers; using Xunit; -using System.Runtime.CompilerServices; -using System; -using System.Data; -using Microsoft.DotNet.RemoteExecutor; namespace Microsoft.Data.SqlClient.ManualTesting.Tests { - // TODO(ADO-39873): Re-enable these tests after addressing their flakiness. - [Trait("Category", "flaky")] + // Serialized execution: DiagnosticListener is global state, so these tests + // must not run in parallel with each other. + [Collection("DiagnosticTests")] public class DiagnosticTest { - private const string BadConnectionString = "data source = bad; initial catalog = bad; integrated security = true; connection timeout = 1;"; - private const string WriteCommandBefore = "Microsoft.Data.SqlClient.WriteCommandBefore"; private const string WriteCommandAfter = "Microsoft.Data.SqlClient.WriteCommandAfter"; private const string WriteCommandError = "Microsoft.Data.SqlClient.WriteCommandError"; @@ -35,969 +34,441 @@ public class DiagnosticTest private const string WriteConnectionOpenError = "Microsoft.Data.SqlClient.WriteConnectionOpenError"; private const string WriteConnectionCloseBefore = "Microsoft.Data.SqlClient.WriteConnectionCloseBefore"; private const string WriteConnectionCloseAfter = "Microsoft.Data.SqlClient.WriteConnectionCloseAfter"; - private const string WriteConnectionCloseError = "Microsoft.Data.SqlClient.WriteConnectionCloseError"; + private const string BadConnectionString = "data source = bad; initial catalog = bad; integrated security = true; connection timeout = 1;"; + + #region Sync tests [Fact] public void ExecuteScalarTest() { - RemoteExecutor.Invoke(() => + CollectStatisticsDiagnostics(connectionString => { - CollectStatisticsDiagnostics(connectionString => - { - using (SqlConnection conn = new SqlConnection(connectionString)) - using (SqlCommand cmd = new SqlCommand()) - { - cmd.Connection = conn; - cmd.CommandText = "SELECT [name], [state] FROM [sys].[databases] WHERE [name] = db_name();"; - - conn.Open(); - cmd.ExecuteScalar(); - } - }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandAfter, WriteConnectionCloseBefore, WriteConnectionCloseAfter]); - return RemoteExecutor.SuccessExitCode; - }).Dispose(); + using SqlConnection conn = new(connectionString); + using SqlCommand cmd = new("SELECT [name], [state] FROM [sys].[databases] WHERE [name] = db_name();", conn); + conn.Open(); + cmd.ExecuteScalar(); + }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandAfter, WriteConnectionCloseBefore, WriteConnectionCloseAfter]); } [Fact] public void ExecuteScalarErrorTest() { - RemoteExecutor.Invoke(() => + CollectStatisticsDiagnostics(connectionString => { - CollectStatisticsDiagnostics(connectionString => - { - using (SqlConnection conn = new SqlConnection(connectionString)) - using (SqlCommand cmd = new SqlCommand()) - { - cmd.Connection = conn; - cmd.CommandText = "SELECT 1 / 0;"; - - conn.Open(); - Assert.Throws(() => cmd.ExecuteScalar()); - } - }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandError, WriteConnectionCloseBefore, WriteConnectionCloseAfter]); - return RemoteExecutor.SuccessExitCode; - }).Dispose(); + using SqlConnection conn = new(connectionString); + using SqlCommand cmd = new("SELECT 1 / 0;", conn); + conn.Open(); + Assert.Throws(() => cmd.ExecuteScalar()); + }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandError, WriteConnectionCloseBefore, WriteConnectionCloseAfter]); } [Fact] public void ExecuteNonQueryTest() { - RemoteExecutor.Invoke(() => + CollectStatisticsDiagnostics(connectionString => { - CollectStatisticsDiagnostics(connectionString => - { - using (SqlConnection conn = new SqlConnection(connectionString)) - using (SqlCommand cmd = new SqlCommand()) - { - cmd.Connection = conn; - cmd.CommandText = "SELECT [name], [state] FROM [sys].[databases] WHERE [name] = db_name();"; - - conn.Open(); - cmd.ExecuteNonQuery(); - } - }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandAfter, WriteConnectionCloseBefore, WriteConnectionCloseAfter]); - return RemoteExecutor.SuccessExitCode; - }).Dispose(); + using SqlConnection conn = new(connectionString); + using SqlCommand cmd = new("SELECT [name], [state] FROM [sys].[databases] WHERE [name] = db_name();", conn); + conn.Open(); + cmd.ExecuteNonQuery(); + }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandAfter, WriteConnectionCloseBefore, WriteConnectionCloseAfter]); } [Fact] public void ExecuteNonQueryErrorTest() { - RemoteExecutor.Invoke(() => + CollectStatisticsDiagnostics(connectionString => { - CollectStatisticsDiagnostics(connectionString => - { - using (SqlConnection conn = new SqlConnection(connectionString)) - using (SqlCommand cmd = new SqlCommand()) - { - cmd.Connection = conn; - cmd.CommandText = "SELECT 1 / 0;"; - - // Limiting the command timeout to 3 seconds. This should be lower than the Process timeout. - cmd.CommandTimeout = 3; - conn.Open(); - - Assert.Throws(() => cmd.ExecuteNonQuery()); - } - }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandError, WriteConnectionCloseBefore, WriteConnectionCloseAfter]); - return RemoteExecutor.SuccessExitCode; - }).Dispose(); + using SqlConnection conn = new(connectionString); + using SqlCommand cmd = new("SELECT 1 / 0;", conn); + cmd.CommandTimeout = 3; + conn.Open(); + Assert.Throws(() => cmd.ExecuteNonQuery()); + }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandError, WriteConnectionCloseBefore, WriteConnectionCloseAfter]); } [Fact] public void ExecuteReaderTest() { - RemoteExecutor.Invoke(() => + CollectStatisticsDiagnostics(connectionString => { - CollectStatisticsDiagnostics(connectionString => - { - using (SqlConnection conn = new SqlConnection(connectionString)) - using (SqlCommand cmd = new SqlCommand()) - { - cmd.Connection = conn; - cmd.CommandText = "SELECT [name], [state] FROM [sys].[databases] WHERE [name] = db_name();"; - - conn.Open(); - SqlDataReader reader = cmd.ExecuteReader(); - while (reader.Read()) - { - // Read until end. - } - } - }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandAfter, WriteConnectionCloseBefore, WriteConnectionCloseAfter]); - return RemoteExecutor.SuccessExitCode; - }).Dispose(); + using SqlConnection conn = new(connectionString); + using SqlCommand cmd = new("SELECT [name], [state] FROM [sys].[databases] WHERE [name] = db_name();", conn); + conn.Open(); + using SqlDataReader reader = cmd.ExecuteReader(); + while (reader.Read()) { } + }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandAfter, WriteConnectionCloseBefore, WriteConnectionCloseAfter]); } [Fact] public void ExecuteReaderErrorTest() { - RemoteExecutor.Invoke(() => + CollectStatisticsDiagnostics(connectionString => { - CollectStatisticsDiagnostics(connectionString => - { - using (SqlConnection conn = new SqlConnection(connectionString)) - using (SqlCommand cmd = new SqlCommand()) - { - cmd.Connection = conn; - cmd.CommandText = "SELECT 1 / 0;"; - - conn.Open(); - // @TODO: TestTdsServer should not throw on ExecuteReader, it should throw on reader.Read - Assert.Throws(() => cmd.ExecuteReader()); - } - }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandError, WriteConnectionCloseBefore, WriteConnectionCloseAfter]); - return RemoteExecutor.SuccessExitCode; - }).Dispose(); + using SqlConnection conn = new(connectionString); + using SqlCommand cmd = new("SELECT 1 / 0;", conn); + conn.Open(); + Assert.Throws(() => cmd.ExecuteReader()); + }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandError, WriteConnectionCloseBefore, WriteConnectionCloseAfter]); } [Fact] public void ExecuteReaderWithCommandBehaviorTest() { - RemoteExecutor.Invoke(() => + CollectStatisticsDiagnostics(connectionString => { - CollectStatisticsDiagnostics(connectionString => - { - using (SqlConnection conn = new SqlConnection(connectionString)) - using (SqlCommand cmd = new SqlCommand()) - { - cmd.Connection = conn; - cmd.CommandText = "SELECT [name], [state] FROM [sys].[databases] WHERE [name] = db_name();"; - - conn.Open(); - SqlDataReader reader = cmd.ExecuteReader(CommandBehavior.Default); - while (reader.Read()) - { - // Read to end - } - } - }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandAfter, WriteConnectionCloseBefore, WriteConnectionCloseAfter]); - return RemoteExecutor.SuccessExitCode; - }).Dispose(); + using SqlConnection conn = new(connectionString); + using SqlCommand cmd = new("SELECT [name], [state] FROM [sys].[databases] WHERE [name] = db_name();", conn); + conn.Open(); + using SqlDataReader reader = cmd.ExecuteReader(CommandBehavior.Default); + while (reader.Read()) { } + }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandAfter, WriteConnectionCloseBefore, WriteConnectionCloseAfter]); } - // Synapse: Parse error at line: 1, column: 27: Incorrect syntax near 'for'. - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureSynapse))] + [Fact] public void ExecuteXmlReaderTest() { - RemoteExecutor.Invoke(() => + // The TDS test server does not return XML-formatted results, so + // ExecuteXmlReader will throw. We verify the error diagnostic path. + CollectStatisticsDiagnostics(connectionString => { - CollectStatisticsDiagnostics(_ => - { - // @TODO: Test TDS server doesn't support ExecuteXmlReader, so connect to real server as workaround - using (SqlConnection conn = new SqlConnection(DataTestUtility.TCPConnectionString)) - using (SqlCommand cmd = new SqlCommand()) - { - cmd.Connection = conn; - cmd.CommandText = "SELECT TOP 10 * FROM sys.objects FOR xml auto, xmldata;"; - - conn.Open(); - XmlReader reader = cmd.ExecuteXmlReader(); - while (reader.Read()) - { - // Read to end - } - } - }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandAfter, WriteConnectionCloseBefore, WriteConnectionCloseAfter]); - return RemoteExecutor.SuccessExitCode; - }).Dispose(); + using SqlConnection conn = new(connectionString); + using SqlCommand cmd = new("SELECT TOP 10 * FROM sys.objects FOR xml auto, xmldata;", conn); + conn.Open(); + Assert.Throws(() => cmd.ExecuteXmlReader()); + }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandError, WriteConnectionCloseBefore, WriteConnectionCloseAfter]); } - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] + [Fact] public void ExecuteXmlReaderErrorTest() { - RemoteExecutor.Invoke(() => + CollectStatisticsDiagnostics(connectionString => { - CollectStatisticsDiagnostics(connectionString => - { - using (SqlConnection conn = new SqlConnection(connectionString)) - using (SqlCommand cmd = new SqlCommand()) - { - cmd.Connection = conn; - cmd.CommandText = "SELECT *, baddata = 1 / 0 FROM sys.objects FOR xml auto, xmldata;"; - - conn.Open(); - // @TODO: TestTdsServer should not throw on ExecuteXmlReader, should throw on reader.Read - Assert.Throws(() => cmd.ExecuteXmlReader()); - } - }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandError, WriteConnectionCloseBefore, WriteConnectionCloseAfter]); - return RemoteExecutor.SuccessExitCode; - }).Dispose(); + using SqlConnection conn = new(connectionString); + using SqlCommand cmd = new("SELECT *, baddata = 1 / 0 FROM sys.objects FOR xml auto, xmldata;", conn); + conn.Open(); + Assert.Throws(() => cmd.ExecuteXmlReader()); + }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandError, WriteConnectionCloseBefore, WriteConnectionCloseAfter]); } [Fact] - public void ExecuteScalarAsyncTest() + public void ConnectionOpenTest() { - RemoteExecutor.Invoke(() => + CollectStatisticsDiagnostics(connectionString => { - CollectStatisticsDiagnosticsAsync(async connectionString => - { -#if NET - await using (SqlConnection conn = new SqlConnection(connectionString)) - await using (SqlCommand cmd = new SqlCommand()) -#else - using (SqlConnection conn = new SqlConnection(connectionString)) - using (SqlCommand cmd = new SqlCommand()) -#endif - { - cmd.Connection = conn; - cmd.CommandText = "SELECT [name], [state] FROM [sys].[databases] WHERE [name] = db_name();"; - - conn.Open(); - await cmd.ExecuteScalarAsync(); - } - }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandAfter, WriteConnectionCloseBefore, WriteConnectionCloseAfter]).Wait(); - return RemoteExecutor.SuccessExitCode; - }).Dispose(); + using SqlConnection conn = new(connectionString); + conn.Open(); + }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteConnectionCloseBefore, WriteConnectionCloseAfter]); } [Fact] - public void ExecuteScalarAsyncErrorTest() + public void ConnectionOpenErrorTest() { - RemoteExecutor.Invoke(() => + CollectStatisticsDiagnostics(_ => { - CollectStatisticsDiagnosticsAsync(async connectionString => - { -#if NET - await using (SqlConnection conn = new SqlConnection(connectionString)) - await using (SqlCommand cmd = new SqlCommand()) -#else - using (SqlConnection conn = new SqlConnection(connectionString)) - using (SqlCommand cmd = new SqlCommand()) -#endif - { - cmd.Connection = conn; - cmd.CommandText = "SELECT 1 / 0;"; - - conn.Open(); - await Assert.ThrowsAsync(() => cmd.ExecuteScalarAsync()); - } - }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandError, WriteConnectionCloseBefore, WriteConnectionCloseAfter]).Wait(); - return RemoteExecutor.SuccessExitCode; - }).Dispose(); + using SqlConnection conn = new(BadConnectionString); + Assert.Throws(() => conn.Open()); + }, [WriteConnectionOpenBefore, WriteConnectionOpenError]); } + #endregion + + #region Async tests + [Fact] - public void ExecuteNonQueryAsyncTest() + public async Task ExecuteScalarAsyncTest() { - RemoteExecutor.Invoke(() => + await CollectStatisticsDiagnosticsAsync(async connectionString => { - CollectStatisticsDiagnosticsAsync(async connectionString => - { #if NET - await using (SqlConnection conn = new SqlConnection(connectionString)) - await using (SqlCommand cmd = new SqlCommand()) + await using SqlConnection conn = new(connectionString); + await using SqlCommand cmd = new("SELECT [name], [state] FROM [sys].[databases] WHERE [name] = db_name();", conn); #else - using (SqlConnection conn = new SqlConnection(connectionString)) - using (SqlCommand cmd = new SqlCommand()) + using SqlConnection conn = new(connectionString); + using SqlCommand cmd = new("SELECT [name], [state] FROM [sys].[databases] WHERE [name] = db_name();", conn); #endif - { - cmd.Connection = conn; - cmd.CommandText = "SELECT [name], [state] FROM [sys].[databases] WHERE [name] = db_name();"; - - conn.Open(); - await cmd.ExecuteNonQueryAsync(); - } - }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandAfter, WriteConnectionCloseBefore, WriteConnectionCloseAfter]).Wait(); - return RemoteExecutor.SuccessExitCode; - }).Dispose(); + conn.Open(); + await cmd.ExecuteScalarAsync(); + }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandAfter, WriteConnectionCloseBefore, WriteConnectionCloseAfter]); } [Fact] - public void ExecuteNonQueryAsyncErrorTest() + public async Task ExecuteScalarAsyncErrorTest() { - RemoteExecutor.Invoke(() => + await CollectStatisticsDiagnosticsAsync(async connectionString => { - CollectStatisticsDiagnosticsAsync(async connectionString => - { #if NET - await using (SqlConnection conn = new SqlConnection(connectionString)) - await using (SqlCommand cmd = new SqlCommand()) + await using SqlConnection conn = new(connectionString); + await using SqlCommand cmd = new("SELECT 1 / 0;", conn); #else - using (SqlConnection conn = new SqlConnection(connectionString)) - using (SqlCommand cmd = new SqlCommand()) + using SqlConnection conn = new(connectionString); + using SqlCommand cmd = new("SELECT 1 / 0;", conn); #endif - { - cmd.Connection = conn; - cmd.CommandText = "SELECT 1 / 0;"; - - conn.Open(); - await Assert.ThrowsAsync(() => cmd.ExecuteNonQueryAsync()); - } - }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandError, WriteConnectionCloseBefore, WriteConnectionCloseAfter]).Wait(); - return RemoteExecutor.SuccessExitCode; - }).Dispose(); + conn.Open(); + await Assert.ThrowsAsync(() => cmd.ExecuteScalarAsync()); + }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandError, WriteConnectionCloseBefore, WriteConnectionCloseAfter]); } [Fact] - public void ExecuteReaderAsyncTest() + public async Task ExecuteNonQueryAsyncTest() { - RemoteExecutor.Invoke(() => + await CollectStatisticsDiagnosticsAsync(async connectionString => { - CollectStatisticsDiagnosticsAsync(async connectionString => - { #if NET - await using (SqlConnection conn = new SqlConnection(connectionString)) - await using (SqlCommand cmd = new SqlCommand()) + await using SqlConnection conn = new(connectionString); + await using SqlCommand cmd = new("SELECT [name], [state] FROM [sys].[databases] WHERE [name] = db_name();", conn); #else - using (SqlConnection conn = new SqlConnection(connectionString)) - using (SqlCommand cmd = new SqlCommand()) + using SqlConnection conn = new(connectionString); + using SqlCommand cmd = new("SELECT [name], [state] FROM [sys].[databases] WHERE [name] = db_name();", conn); #endif - { - cmd.Connection = conn; - cmd.CommandText = "SELECT [name], [state] FROM [sys].[databases] WHERE [name] = db_name();"; - - conn.Open(); - SqlDataReader reader = await cmd.ExecuteReaderAsync(); - while (reader.Read()) - { - // Read to end - } - } - }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandAfter, WriteConnectionCloseBefore, WriteConnectionCloseAfter]).Wait(); - return RemoteExecutor.SuccessExitCode; - }).Dispose(); + conn.Open(); + await cmd.ExecuteNonQueryAsync(); + }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandAfter, WriteConnectionCloseBefore, WriteConnectionCloseAfter]); } [Fact] - public void ExecuteReaderAsyncErrorTest() + public async Task ExecuteNonQueryAsyncErrorTest() { - RemoteExecutor.Invoke(() => + await CollectStatisticsDiagnosticsAsync(async connectionString => { - CollectStatisticsDiagnosticsAsync(async connectionString => - { #if NET - await using (SqlConnection conn = new SqlConnection(connectionString)) - await using (SqlCommand cmd = new SqlCommand()) + await using SqlConnection conn = new(connectionString); + await using SqlCommand cmd = new("SELECT 1 / 0;", conn); #else - using (SqlConnection conn = new SqlConnection(connectionString)) - using (SqlCommand cmd = new SqlCommand()) + using SqlConnection conn = new(connectionString); + using SqlCommand cmd = new("SELECT 1 / 0;", conn); #endif - { - cmd.Connection = conn; - cmd.CommandText = "SELECT 1 / 0;"; - - conn.Open(); - // @TODO: TestTdsServer should not throw on ExecuteReader, should throw on reader.Read - await Assert.ThrowsAsync(() => cmd.ExecuteReaderAsync()); - } - }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandError, WriteConnectionCloseBefore, WriteConnectionCloseAfter]).Wait(); - return RemoteExecutor.SuccessExitCode; - }).Dispose(); + conn.Open(); + await Assert.ThrowsAsync(() => cmd.ExecuteNonQueryAsync()); + }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandError, WriteConnectionCloseBefore, WriteConnectionCloseAfter]); } - // Synapse: Parse error at line: 1, column: 27: Incorrect syntax near 'for'. - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureSynapse))] - public void ExecuteXmlReaderAsyncTest() + [Fact] + public async Task ExecuteReaderAsyncTest() { - // @TODO: TestTdsServer does not handle xml reader, so connect to a real server as a workaround - RemoteExecutor.Invoke(() => + await CollectStatisticsDiagnosticsAsync(async connectionString => { - CollectStatisticsDiagnosticsAsync(async _ => - { #if NET - await using (SqlConnection conn = new SqlConnection(DataTestUtility.TCPConnectionString)) - await using (SqlCommand cmd = new SqlCommand()) + await using SqlConnection conn = new(connectionString); + await using SqlCommand cmd = new("SELECT [name], [state] FROM [sys].[databases] WHERE [name] = db_name();", conn); #else - using (SqlConnection conn = new SqlConnection(DataTestUtility.TCPConnectionString)) - using (SqlCommand cmd = new SqlCommand()) + using SqlConnection conn = new(connectionString); + using SqlCommand cmd = new("SELECT [name], [state] FROM [sys].[databases] WHERE [name] = db_name();", conn); #endif - { - cmd.Connection = conn; - cmd.CommandText = "SELECT TOP 10 * FROM sys.objects FOR xml auto, xmldata;"; - - conn.Open(); - XmlReader reader = await cmd.ExecuteXmlReaderAsync(); - while (reader.Read()) - { - // Read to end - } - } - }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandAfter, WriteConnectionCloseBefore, WriteConnectionCloseAfter]).Wait(); - return RemoteExecutor.SuccessExitCode; - }).Dispose(); + conn.Open(); + using SqlDataReader reader = await cmd.ExecuteReaderAsync(); + while (reader.Read()) { } + }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandAfter, WriteConnectionCloseBefore, WriteConnectionCloseAfter]); } [Fact] - public void ExecuteXmlReaderAsyncErrorTest() + public async Task ExecuteReaderAsyncErrorTest() { - // @TODO: TestTdsServer does not handle xml reader, so connect to a real server as a workaround - RemoteExecutor.Invoke(() => + await CollectStatisticsDiagnosticsAsync(async connectionString => { - - CollectStatisticsDiagnosticsAsync(async _ => - { #if NET - await using (SqlConnection conn = new SqlConnection(DataTestUtility.TCPConnectionString)) - await using (SqlCommand cmd = new SqlCommand()) + await using SqlConnection conn = new(connectionString); + await using SqlCommand cmd = new("SELECT 1 / 0;", conn); #else - using (SqlConnection conn = new SqlConnection(DataTestUtility.TCPConnectionString)) - using (SqlCommand cmd = new SqlCommand()) + using SqlConnection conn = new(connectionString); + using SqlCommand cmd = new("SELECT 1 / 0;", conn); #endif - { - cmd.Connection = conn; - cmd.CommandText = "select *, baddata = 1 / 0 from sys.objects for xml auto, xmldata;"; - - // @TODO: Since this test uses a real database connection, the exception is - // thrown during reader.Read. (ie, TestTdsServer does not obey proper - // exception behavior) - // NB: As a result of the exception being thrown during reader.Read, - // cmd.ExecuteXmlReaderAsync returns successfully. This means that we receive - // a WriteCommandAfter event rather than WriteCommandError. - await conn.OpenAsync(); - XmlReader reader = await cmd.ExecuteXmlReaderAsync(); - await Assert.ThrowsAsync(() => reader.ReadAsync()); - } - }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandAfter, WriteConnectionCloseBefore, WriteConnectionCloseAfter]).Wait(); - return RemoteExecutor.SuccessExitCode; - }).Dispose(); + conn.Open(); + await Assert.ThrowsAsync(() => cmd.ExecuteReaderAsync()); + }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandError, WriteConnectionCloseBefore, WriteConnectionCloseAfter]); } [Fact] - public void ConnectionOpenTest() + public async Task ExecuteXmlReaderAsyncTest() { - RemoteExecutor.Invoke(() => + // The TDS test server does not return XML-formatted results, so + // ExecuteXmlReaderAsync will throw. We verify the error diagnostic path. + await CollectStatisticsDiagnosticsAsync(async connectionString => { - CollectStatisticsDiagnostics(connectionString => - { - using (SqlConnection sqlConnection = new SqlConnection(connectionString)) - { - sqlConnection.Open(); - } - }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteConnectionCloseBefore, WriteConnectionCloseAfter]); - return RemoteExecutor.SuccessExitCode; - }).Dispose(); +#if NET + await using SqlConnection conn = new(connectionString); + await using SqlCommand cmd = new("SELECT TOP 10 * FROM sys.objects FOR xml auto, xmldata;", conn); +#else + using SqlConnection conn = new(connectionString); + using SqlCommand cmd = new("SELECT TOP 10 * FROM sys.objects FOR xml auto, xmldata;", conn); +#endif + conn.Open(); + await Assert.ThrowsAsync(() => cmd.ExecuteXmlReaderAsync()); + }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandError, WriteConnectionCloseBefore, WriteConnectionCloseAfter]); } [Fact] - public void ConnectionOpenErrorTest() + public async Task ExecuteXmlReaderAsyncErrorTest() { - RemoteExecutor.Invoke(() => + await CollectStatisticsDiagnosticsAsync(async connectionString => { - CollectStatisticsDiagnostics(_ => - { - using (SqlConnection sqlConnection = new SqlConnection(BadConnectionString)) - { - Assert.Throws(() => sqlConnection.Open()); - } - }, [WriteConnectionOpenBefore, WriteConnectionOpenError]); - return RemoteExecutor.SuccessExitCode; - }).Dispose(); +#if NET + await using SqlConnection conn = new(connectionString); + await using SqlCommand cmd = new("SELECT *, baddata = 1 / 0 FROM sys.objects FOR xml auto, xmldata;", conn); +#else + using SqlConnection conn = new(connectionString); + using SqlCommand cmd = new("SELECT *, baddata = 1 / 0 FROM sys.objects FOR xml auto, xmldata;", conn); +#endif + conn.Open(); + Assert.Throws(() => cmd.ExecuteXmlReader()); + }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandError, WriteConnectionCloseBefore, WriteConnectionCloseAfter]); } [Fact] - public void ConnectionOpenAsyncTest() + public async Task ConnectionOpenAsyncTest() { - RemoteExecutor.Invoke(() => + await CollectStatisticsDiagnosticsAsync(async connectionString => { - CollectStatisticsDiagnosticsAsync(async connectionString => - { #if NET - await using (SqlConnection sqlConnection = new SqlConnection(connectionString)) + await using SqlConnection conn = new(connectionString); #else - using (SqlConnection sqlConnection = new SqlConnection(connectionString)) + using SqlConnection conn = new(connectionString); #endif - { - await sqlConnection.OpenAsync(); - } - }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteConnectionCloseBefore, WriteConnectionCloseAfter]).Wait(); - return RemoteExecutor.SuccessExitCode; - }).Dispose(); + await conn.OpenAsync(); + }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteConnectionCloseBefore, WriteConnectionCloseAfter]); } [Fact] - public void ConnectionOpenAsyncErrorTest() + public async Task ConnectionOpenAsyncErrorTest() { - RemoteExecutor.Invoke(() => + await CollectStatisticsDiagnosticsAsync(async _ => { - CollectStatisticsDiagnosticsAsync(async _ => - { #if NET - await using (SqlConnection sqlConnection = new SqlConnection(BadConnectionString)) + await using SqlConnection conn = new(BadConnectionString); #else - using (SqlConnection sqlConnection = new SqlConnection(BadConnectionString)) + using SqlConnection conn = new(BadConnectionString); #endif - { - await Assert.ThrowsAsync(() => sqlConnection.OpenAsync()); - } - }, [WriteConnectionOpenBefore, WriteConnectionOpenError]).Wait(); - return RemoteExecutor.SuccessExitCode; - }).Dispose(); + await Assert.ThrowsAsync(() => conn.OpenAsync()); + }, [WriteConnectionOpenBefore, WriteConnectionOpenError]); } - private static void CollectStatisticsDiagnostics(Action sqlOperation, string[] expectedDiagnostics, [CallerMemberName] string methodName = "") - { - bool statsLogged = false; - bool operationHasError = false; - Guid beginOperationId = Guid.Empty; - - FakeDiagnosticListenerObserver diagnosticListenerObserver = new FakeDiagnosticListenerObserver(kvp => - { - IDictionary statistics; - - if (kvp.Key.Equals("Microsoft.Data.SqlClient.WriteCommandBefore")) - { - Assert.NotNull(kvp.Value); - - Guid retrievedOperationId = GetPropertyValueFromType(kvp.Value, "OperationId"); - Assert.NotEqual(retrievedOperationId, Guid.Empty); - - SqlCommand sqlCommand = GetPropertyValueFromType(kvp.Value, "Command"); - Assert.NotNull(sqlCommand); - - string operation = GetPropertyValueFromType(kvp.Value, "Operation"); - Assert.False(string.IsNullOrWhiteSpace(operation)); - - Guid connectionId = GetPropertyValueFromType(kvp.Value, "ConnectionId"); - if (sqlCommand.Connection.State == ConnectionState.Open) - { - Assert.NotEqual(connectionId, Guid.Empty); - } - - beginOperationId = retrievedOperationId; - - statsLogged = true; - } - else if (kvp.Key.Equals("Microsoft.Data.SqlClient.WriteCommandAfter")) - { - Assert.NotNull(kvp.Value); - - Guid retrievedOperationId = GetPropertyValueFromType(kvp.Value, "OperationId"); - Assert.NotEqual(retrievedOperationId, Guid.Empty); - - SqlCommand sqlCommand = GetPropertyValueFromType(kvp.Value, "Command"); - Assert.NotNull(sqlCommand); - - statistics = GetPropertyValueFromType(kvp.Value, "Statistics"); - if (!operationHasError) - { - Assert.NotNull(statistics); - } - - string operation = GetPropertyValueFromType(kvp.Value, "Operation"); - Assert.False(string.IsNullOrWhiteSpace(operation)); - - Guid connectionId = GetPropertyValueFromType(kvp.Value, "ConnectionId"); - if (sqlCommand.Connection.State == ConnectionState.Open) - { - Assert.NotEqual(connectionId, Guid.Empty); - } - - // if we get to this point, then statistics exist and this must be the "end" - // event, so we need to make sure the operation IDs match - Assert.Equal(retrievedOperationId, beginOperationId); - beginOperationId = Guid.Empty; - - statsLogged = true; - } - else if (kvp.Key.Equals("Microsoft.Data.SqlClient.WriteCommandError")) - { - operationHasError = true; - Assert.NotNull(kvp.Value); - - SqlCommand sqlCommand = GetPropertyValueFromType(kvp.Value, "Command"); - Assert.NotNull(sqlCommand); - - Exception ex = GetPropertyValueFromType(kvp.Value, "Exception"); - Assert.NotNull(ex); - - string operation = GetPropertyValueFromType(kvp.Value, "Operation"); - Assert.False(string.IsNullOrWhiteSpace(operation)); - - Guid connectionId = GetPropertyValueFromType(kvp.Value, "ConnectionId"); - if (sqlCommand.Connection.State == ConnectionState.Open) - { - Assert.NotEqual(connectionId, Guid.Empty); - } - - statsLogged = true; - } - else if (kvp.Key.Equals("Microsoft.Data.SqlClient.WriteConnectionOpenBefore")) - { - Assert.NotNull(kvp.Value); - - SqlConnection sqlConnection = GetPropertyValueFromType(kvp.Value, "Connection"); - Assert.NotNull(sqlConnection); - - string operation = GetPropertyValueFromType(kvp.Value, "Operation"); - Assert.False(string.IsNullOrWhiteSpace(operation)); - - statsLogged = true; - } - else if (kvp.Key.Equals("Microsoft.Data.SqlClient.WriteConnectionOpenAfter")) - { - Assert.NotNull(kvp.Value); - - SqlConnection sqlConnection = GetPropertyValueFromType(kvp.Value, "Connection"); - Assert.NotNull(sqlConnection); - - string operation = GetPropertyValueFromType(kvp.Value, "Operation"); - Assert.False(string.IsNullOrWhiteSpace(operation)); - - statistics = GetPropertyValueFromType(kvp.Value, "Statistics"); - Assert.NotNull(statistics); - - Guid connectionId = GetPropertyValueFromType(kvp.Value, "ConnectionId"); - Assert.NotEqual(connectionId, Guid.Empty); - - statsLogged = true; - } - else if (kvp.Key.Equals("Microsoft.Data.SqlClient.WriteConnectionOpenError")) - { - Assert.NotNull(kvp.Value); - - SqlConnection sqlConnection = GetPropertyValueFromType(kvp.Value, "Connection"); - Assert.NotNull(sqlConnection); - - string operation = GetPropertyValueFromType(kvp.Value, "Operation"); - Assert.False(string.IsNullOrWhiteSpace(operation)); - - Exception ex = GetPropertyValueFromType(kvp.Value, "Exception"); - Assert.NotNull(ex); - - statsLogged = true; - } - else if (kvp.Key.Equals("Microsoft.Data.SqlClient.WriteConnectionCloseBefore")) - { - Assert.NotNull(kvp.Value); - - SqlConnection sqlConnection = GetPropertyValueFromType(kvp.Value, "Connection"); - Assert.NotNull(sqlConnection); - - string operation = GetPropertyValueFromType(kvp.Value, "Operation"); - Assert.False(string.IsNullOrWhiteSpace(operation)); - - Guid connectionId = GetPropertyValueFromType(kvp.Value, "ConnectionId"); - Assert.NotEqual(connectionId, Guid.Empty); - - statsLogged = true; - } - else if (kvp.Key.Equals("Microsoft.Data.SqlClient.WriteConnectionCloseAfter")) - { - Assert.NotNull(kvp.Value); - - SqlConnection sqlConnection = GetPropertyValueFromType(kvp.Value, "Connection"); - Assert.NotNull(sqlConnection); + #endregion - string operation = GetPropertyValueFromType(kvp.Value, "Operation"); - Assert.False(string.IsNullOrWhiteSpace(operation)); + #region Helpers - statistics = GetPropertyValueFromType(kvp.Value, "Statistics"); - - Guid connectionId = GetPropertyValueFromType(kvp.Value, "ConnectionId"); - Assert.NotEqual(connectionId, Guid.Empty); - - statsLogged = true; - } - else if (kvp.Key.Equals("Microsoft.Data.SqlClient.WriteConnectionCloseError")) - { - Assert.NotNull(kvp.Value); - - SqlConnection sqlConnection = GetPropertyValueFromType(kvp.Value, "Connection"); - Assert.NotNull(sqlConnection); - - string operation = GetPropertyValueFromType(kvp.Value, "Operation"); - Assert.False(string.IsNullOrWhiteSpace(operation)); - - Exception ex = GetPropertyValueFromType(kvp.Value, "Exception"); - Assert.NotNull(ex); - - Guid connectionId = GetPropertyValueFromType(kvp.Value, "ConnectionId"); - Assert.NotEqual(connectionId, Guid.Empty); - - statsLogged = true; - } - }); - - diagnosticListenerObserver.Enable(); - using (DiagnosticListener.AllListeners.Subscribe(diagnosticListenerObserver)) + private static void CollectStatisticsDiagnostics( + Action sqlOperation, + string[] expectedDiagnostics, + [CallerMemberName] string methodName = "") + { + bool statsLogged = false; + FakeDiagnosticListenerObserver observer = new(kvp => { + ValidateDiagnosticPayload(kvp); + statsLogged = true; + }); - Console.WriteLine(string.Format("Test: {0} Enabled Listeners", methodName)); - - using (var server = new TdsServer(new DiagnosticsQueryEngine(), new TdsServerArguments())) + observer.Enable(); + using (DiagnosticListener.AllListeners.Subscribe(observer)) + using (var server = new TdsServer(new DiagnosticsQueryEngine(), new TdsServerArguments())) + { + server.Start(methodName); + string connectionString = new SqlConnectionStringBuilder { - server.Start(methodName); - Console.WriteLine(string.Format("Test: {0} Started Server", methodName)); - - var connectionString = new SqlConnectionStringBuilder - { - DataSource = $"localhost,{server.EndPoint.Port}", - Encrypt = SqlConnectionEncryptOption.Optional - }.ConnectionString; - - sqlOperation(connectionString); + DataSource = $"localhost,{server.EndPoint.Port}", + Encrypt = SqlConnectionEncryptOption.Optional + }.ConnectionString; - Console.WriteLine(string.Format("Test: {0} SqlOperation Successful", methodName)); + sqlOperation(connectionString); - Assert.True(statsLogged); + Assert.True(statsLogged, "Expected at least one diagnostic event"); + observer.Disable(); - diagnosticListenerObserver.Disable(); - foreach (string expected in expectedDiagnostics) - { - Assert.True(diagnosticListenerObserver.HasReceivedDiagnostic(expected), $"Missing diagnostic '{expected}'"); - } - - Console.WriteLine(string.Format("Test: {0} Listeners Disabled", methodName)); + foreach (string expected in expectedDiagnostics) + { + Assert.True(observer.HasReceivedDiagnostic(expected), $"Missing diagnostic '{expected}'"); } - Console.WriteLine(string.Format("Test: {0} Server Disposed", methodName)); } - Console.WriteLine(string.Format("Test: {0} Listeners Disposed Successfully", methodName)); } - private static async Task CollectStatisticsDiagnosticsAsync(Func sqlOperation, string[] expectedDiagnostics, [CallerMemberName] string methodName = "") + private static async Task CollectStatisticsDiagnosticsAsync( + Func sqlOperation, + string[] expectedDiagnostics, + [CallerMemberName] string methodName = "") { bool statsLogged = false; - bool operationHasError = false; - Guid beginOperationId = Guid.Empty; - - FakeDiagnosticListenerObserver diagnosticListenerObserver = new FakeDiagnosticListenerObserver(kvp => + FakeDiagnosticListenerObserver observer = new(kvp => { - IDictionary statistics; - - if (kvp.Key.Equals("Microsoft.Data.SqlClient.WriteCommandBefore")) - { - Assert.NotNull(kvp.Value); - - Guid retrievedOperationId = GetPropertyValueFromType(kvp.Value, "OperationId"); - Assert.NotEqual(retrievedOperationId, Guid.Empty); - - SqlCommand sqlCommand = GetPropertyValueFromType(kvp.Value, "Command"); - Assert.NotNull(sqlCommand); - - string operation = GetPropertyValueFromType(kvp.Value, "Operation"); - Assert.False(string.IsNullOrWhiteSpace(operation)); - - beginOperationId = retrievedOperationId; - - statsLogged = true; - } - else if (kvp.Key.Equals("Microsoft.Data.SqlClient.WriteCommandAfter")) - { - Assert.NotNull(kvp.Value); - - Guid retrievedOperationId = GetPropertyValueFromType(kvp.Value, "OperationId"); - Assert.NotEqual(retrievedOperationId, Guid.Empty); - - SqlCommand sqlCommand = GetPropertyValueFromType(kvp.Value, "Command"); - Assert.NotNull(sqlCommand); - - statistics = GetPropertyValueFromType(kvp.Value, "Statistics"); - if (!operationHasError) - { - Assert.NotNull(statistics); - } - - string operation = GetPropertyValueFromType(kvp.Value, "Operation"); - Assert.False(string.IsNullOrWhiteSpace(operation)); - - // if we get to this point, then statistics exist and this must be the "end" - // event, so we need to make sure the operation IDs match - Assert.Equal(retrievedOperationId, beginOperationId); - beginOperationId = Guid.Empty; - - statsLogged = true; - } - else if (kvp.Key.Equals("Microsoft.Data.SqlClient.WriteCommandError")) - { - operationHasError = true; - Assert.NotNull(kvp.Value); - - SqlCommand sqlCommand = GetPropertyValueFromType(kvp.Value, "Command"); - Assert.NotNull(sqlCommand); - - Exception ex = GetPropertyValueFromType(kvp.Value, "Exception"); - Assert.NotNull(ex); - - string operation = GetPropertyValueFromType(kvp.Value, "Operation"); - Assert.False(string.IsNullOrWhiteSpace(operation)); - - statsLogged = true; - } - else if (kvp.Key.Equals("Microsoft.Data.SqlClient.WriteConnectionOpenBefore")) - { - Assert.NotNull(kvp.Value); - - SqlConnection sqlConnection = GetPropertyValueFromType(kvp.Value, "Connection"); - Assert.NotNull(sqlConnection); - - string operation = GetPropertyValueFromType(kvp.Value, "Operation"); - Assert.False(string.IsNullOrWhiteSpace(operation)); - - statsLogged = true; - } - else if (kvp.Key.Equals("Microsoft.Data.SqlClient.WriteConnectionOpenAfter")) - { - Assert.NotNull(kvp.Value); - - SqlConnection sqlConnection = GetPropertyValueFromType(kvp.Value, "Connection"); - Assert.NotNull(sqlConnection); - - string operation = GetPropertyValueFromType(kvp.Value, "Operation"); - Assert.False(string.IsNullOrWhiteSpace(operation)); - - statistics = GetPropertyValueFromType(kvp.Value, "Statistics"); - Assert.NotNull(statistics); - - Guid connectionId = GetPropertyValueFromType(kvp.Value, "ConnectionId"); - if (sqlConnection.State == ConnectionState.Open) - { - Assert.NotEqual(connectionId, Guid.Empty); - } - - statsLogged = true; - } - else if (kvp.Key.Equals("Microsoft.Data.SqlClient.WriteConnectionOpenError")) - { - Assert.NotNull(kvp.Value); - - SqlConnection sqlConnection = GetPropertyValueFromType(kvp.Value, "Connection"); - Assert.NotNull(sqlConnection); - - string operation = GetPropertyValueFromType(kvp.Value, "Operation"); - Assert.False(string.IsNullOrWhiteSpace(operation)); - - Exception ex = GetPropertyValueFromType(kvp.Value, "Exception"); - Assert.NotNull(ex); + ValidateDiagnosticPayload(kvp); + statsLogged = true; + }); - statsLogged = true; - } - else if (kvp.Key.Equals("Microsoft.Data.SqlClient.WriteConnectionCloseBefore")) + observer.Enable(); + using (DiagnosticListener.AllListeners.Subscribe(observer)) + using (var server = new TdsServer(new DiagnosticsQueryEngine(), new TdsServerArguments())) + { + server.Start(methodName); + string connectionString = new SqlConnectionStringBuilder { - Assert.NotNull(kvp.Value); + DataSource = $"localhost,{server.EndPoint.Port}", + Encrypt = SqlConnectionEncryptOption.Optional + }.ConnectionString; - SqlConnection sqlConnection = GetPropertyValueFromType(kvp.Value, "Connection"); - Assert.NotNull(sqlConnection); + await sqlOperation(connectionString); - string operation = GetPropertyValueFromType(kvp.Value, "Operation"); - Assert.False(string.IsNullOrWhiteSpace(operation)); + Assert.True(statsLogged, "Expected at least one diagnostic event"); + observer.Disable(); - Guid connectionId = GetPropertyValueFromType(kvp.Value, "ConnectionId"); - Assert.NotEqual(connectionId, Guid.Empty); - - statsLogged = true; - } - else if (kvp.Key.Equals("Microsoft.Data.SqlClient.WriteConnectionCloseAfter")) + foreach (string expected in expectedDiagnostics) { - Assert.NotNull(kvp.Value); - - SqlConnection sqlConnection = GetPropertyValueFromType(kvp.Value, "Connection"); - Assert.NotNull(sqlConnection); - - string operation = GetPropertyValueFromType(kvp.Value, "Operation"); - Assert.False(string.IsNullOrWhiteSpace(operation)); - - statistics = GetPropertyValueFromType(kvp.Value, "Statistics"); - - Guid connectionId = GetPropertyValueFromType(kvp.Value, "ConnectionId"); - Assert.NotEqual(connectionId, Guid.Empty); - - statsLogged = true; + Assert.True(observer.HasReceivedDiagnostic(expected), $"Missing diagnostic '{expected}'"); } - else if (kvp.Key.Equals("Microsoft.Data.SqlClient.WriteConnectionCloseError")) - { - Assert.NotNull(kvp.Value); - - SqlConnection sqlConnection = GetPropertyValueFromType(kvp.Value, "Connection"); - Assert.NotNull(sqlConnection); - - string operation = GetPropertyValueFromType(kvp.Value, "Operation"); - Assert.False(string.IsNullOrWhiteSpace(operation)); - - Exception ex = GetPropertyValueFromType(kvp.Value, "Exception"); - Assert.NotNull(ex); - - Guid connectionId = GetPropertyValueFromType(kvp.Value, "ConnectionId"); - Assert.NotEqual(connectionId, Guid.Empty); + } + } - statsLogged = true; - } - }); + private static void ValidateDiagnosticPayload(KeyValuePair kvp) + { + Assert.NotNull(kvp.Value); - diagnosticListenerObserver.Enable(); - using (DiagnosticListener.AllListeners.Subscribe(diagnosticListenerObserver)) + if (kvp.Key.Contains("Command")) { - Console.WriteLine(string.Format("Test: {0} Enabled Listeners", methodName)); - using (var server = new TdsServer(new DiagnosticsQueryEngine(), new TdsServerArguments())) - { - server.Start(methodName); - Console.WriteLine(string.Format("Test: {0} Started Server", methodName)); - - var connectionString = new SqlConnectionStringBuilder - { - DataSource = $"localhost,{server.EndPoint.Port}", - Encrypt = SqlConnectionEncryptOption.Optional - }.ConnectionString; - await sqlOperation(connectionString); - - Console.WriteLine(string.Format("Test: {0} SqlOperation Successful", methodName)); - - Assert.True(statsLogged); - - diagnosticListenerObserver.Disable(); - foreach (string expected in expectedDiagnostics) - { - Assert.True(diagnosticListenerObserver.HasReceivedDiagnostic(expected), $"Missing diagnostic '{expected}'"); - } + SqlCommand cmd = GetPropertyValueFromType(kvp.Value, "Command"); + Assert.NotNull(cmd); + } + else if (kvp.Key.Contains("Connection")) + { + SqlConnection conn = GetPropertyValueFromType(kvp.Value, "Connection"); + Assert.NotNull(conn); + } - Console.WriteLine(string.Format("Test: {0} Listeners Disabled", methodName)); - } - Console.WriteLine(string.Format("Test: {0} Server Disposed", methodName)); + if (kvp.Key.Contains("Error")) + { + Exception ex = GetPropertyValueFromType(kvp.Value, "Exception"); + Assert.NotNull(ex); } - Console.WriteLine(string.Format("Test: {0} Listeners Disposed Successfully", methodName)); + + string operation = GetPropertyValueFromType(kvp.Value, "Operation"); + Assert.False(string.IsNullOrWhiteSpace(operation)); } private static T GetPropertyValueFromType(object obj, string propName) { Type type = obj.GetType(); PropertyInfo pi = type.GetRuntimeProperty(propName); - - var propertyValue = pi.GetValue(obj); - return (T)propertyValue; + return (T)pi.GetValue(obj); } + + #endregion } + /// + /// Query engine that handles error queries (division by zero). + /// public class DiagnosticsQueryEngine : QueryEngine { - public DiagnosticsQueryEngine() : base(new TdsServerArguments()) - { - } + public DiagnosticsQueryEngine() : base(new TdsServerArguments()) { } protected override TDSMessageCollection CreateQueryResponse(ITDSServerSession session, TDSSQLBatchToken batchRequest) { string lowerBatchText = batchRequest.Text.ToLowerInvariant(); - - if (lowerBatchText.Contains("1 / 0")) // SELECT 1/0 + if (lowerBatchText.Contains("1 / 0")) { - TDSErrorToken errorToken = new TDSErrorToken(8134, 1, 16, "Divide by zero error encountered."); - TDSDoneToken doneToken = new TDSDoneToken(TDSDoneTokenStatusType.Final | TDSDoneTokenStatusType.Count, TDSDoneTokenCommandType.Select, 1); - TDSMessage responseMessage = new TDSMessage(TDSMessageType.Response, errorToken, doneToken); + TDSErrorToken errorToken = new(8134, 1, 16, "Divide by zero error encountered."); + TDSDoneToken doneToken = new(TDSDoneTokenStatusType.Final | TDSDoneTokenStatusType.Count, TDSDoneTokenCommandType.Select, 1); + TDSMessage responseMessage = new(TDSMessageType.Response, errorToken, doneToken); return new TDSMessageCollection(responseMessage); } - else - { - return base.CreateQueryResponse(session, batchRequest); - } + return base.CreateQueryResponse(session, batchRequest); } } }