From 93fb94eb01110a7d56972e892bcf07269eb4e447 Mon Sep 17 00:00:00 2001 From: Cheena Malhotra Date: Tue, 24 Mar 2026 06:05:05 +0000 Subject: [PATCH 1/6] Fix Diagnostic Tests and Remote Executor --- ...icrosoft.Data.SqlClient.ManualTests.csproj | 4 + .../TracingTests/DiagnosticTest.cs | 182 ++++++++++-------- tools/targets/PatchTestDeps.targets | 95 +++++++++ 3 files changed, 203 insertions(+), 78 deletions(-) create mode 100644 tools/targets/PatchTestDeps.targets 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..f2b0325165 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 @@ -422,4 +422,8 @@ + + + + diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/TracingTests/DiagnosticTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/TracingTests/DiagnosticTest.cs index f550e7db70..067f125b53 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/TracingTests/DiagnosticTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/TracingTests/DiagnosticTest.cs @@ -3,30 +3,24 @@ // See the LICENSE file in the project root for more information. using System.Collections; +using System.Data; using System.Diagnostics; using System.Reflection; -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 Xunit; using System.Runtime.CompilerServices; -using System; -using System.Data; -using Microsoft.DotNet.RemoteExecutor; +using System.Xml; namespace Microsoft.Data.SqlClient.ManualTesting.Tests { - // TODO(ADO-39873): Re-enable these tests after addressing their flakiness. - [Trait("Category", "flaky")] public class DiagnosticTest { - private const string BadConnectionString = "data source = bad; initial catalog = bad; integrated security = true; connection timeout = 1;"; + private readonly ITestOutputHelper _output; + public DiagnosticTest(ITestOutputHelper output) + { + _output = output; + } + + 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"; @@ -37,10 +31,42 @@ public class DiagnosticTest private const string WriteConnectionCloseAfter = "Microsoft.Data.SqlClient.WriteConnectionCloseAfter"; private const string WriteConnectionCloseError = "Microsoft.Data.SqlClient.WriteConnectionCloseError"; - [Fact] + /// + /// Invokes a delegate via RemoteExecutor while capturing stdout/stderr + /// from the child process and forwarding them to xUnit's ITestOutputHelper. + /// + private void InvokeRemote(Func func, [CallerMemberName] string testName = "") + { + var options = new RemoteInvokeOptions(); + options.StartInfo.RedirectStandardOutput = true; + options.StartInfo.RedirectStandardError = true; + + using RemoteInvokeHandle handle = RemoteExecutor.Invoke(func, options); + Process process = handle.Process; + + // Read stdout/stderr asynchronously so we don't deadlock. + Task stdoutTask = process.StandardOutput.ReadToEndAsync(); + Task stderrTask = process.StandardError.ReadToEndAsync(); + + process.WaitForExit(); + + string stdout = stdoutTask.GetAwaiter().GetResult(); + string stderr = stderrTask.GetAwaiter().GetResult(); + + if (!string.IsNullOrWhiteSpace(stdout)) + { + _output.WriteLine($"[{testName} stdout]{Environment.NewLine}{stdout}"); + } + if (!string.IsNullOrWhiteSpace(stderr)) + { + _output.WriteLine($"[{testName} stderr]{Environment.NewLine}{stderr}"); + } + } + + [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] public void ExecuteScalarTest() { - RemoteExecutor.Invoke(() => + InvokeRemote(() => { CollectStatisticsDiagnostics(connectionString => { @@ -55,13 +81,13 @@ public void ExecuteScalarTest() } }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandAfter, WriteConnectionCloseBefore, WriteConnectionCloseAfter]); return RemoteExecutor.SuccessExitCode; - }).Dispose(); + }); } - [Fact] + [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] public void ExecuteScalarErrorTest() { - RemoteExecutor.Invoke(() => + InvokeRemote(() => { CollectStatisticsDiagnostics(connectionString => { @@ -76,13 +102,13 @@ public void ExecuteScalarErrorTest() } }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandError, WriteConnectionCloseBefore, WriteConnectionCloseAfter]); return RemoteExecutor.SuccessExitCode; - }).Dispose(); + }); } - [Fact] + [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] public void ExecuteNonQueryTest() { - RemoteExecutor.Invoke(() => + InvokeRemote(() => { CollectStatisticsDiagnostics(connectionString => { @@ -97,13 +123,13 @@ public void ExecuteNonQueryTest() } }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandAfter, WriteConnectionCloseBefore, WriteConnectionCloseAfter]); return RemoteExecutor.SuccessExitCode; - }).Dispose(); + }); } - [Fact] + [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] public void ExecuteNonQueryErrorTest() { - RemoteExecutor.Invoke(() => + InvokeRemote(() => { CollectStatisticsDiagnostics(connectionString => { @@ -121,13 +147,13 @@ public void ExecuteNonQueryErrorTest() } }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandError, WriteConnectionCloseBefore, WriteConnectionCloseAfter]); return RemoteExecutor.SuccessExitCode; - }).Dispose(); + }); } - [Fact] + [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] public void ExecuteReaderTest() { - RemoteExecutor.Invoke(() => + InvokeRemote(() => { CollectStatisticsDiagnostics(connectionString => { @@ -146,13 +172,13 @@ public void ExecuteReaderTest() } }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandAfter, WriteConnectionCloseBefore, WriteConnectionCloseAfter]); return RemoteExecutor.SuccessExitCode; - }).Dispose(); + }); } - [Fact] + [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] public void ExecuteReaderErrorTest() { - RemoteExecutor.Invoke(() => + InvokeRemote(() => { CollectStatisticsDiagnostics(connectionString => { @@ -168,13 +194,13 @@ public void ExecuteReaderErrorTest() } }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandError, WriteConnectionCloseBefore, WriteConnectionCloseAfter]); return RemoteExecutor.SuccessExitCode; - }).Dispose(); + }); } - [Fact] + [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] public void ExecuteReaderWithCommandBehaviorTest() { - RemoteExecutor.Invoke(() => + InvokeRemote(() => { CollectStatisticsDiagnostics(connectionString => { @@ -193,14 +219,14 @@ public void ExecuteReaderWithCommandBehaviorTest() } }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandAfter, WriteConnectionCloseBefore, WriteConnectionCloseAfter]); return RemoteExecutor.SuccessExitCode; - }).Dispose(); + }); } // Synapse: Parse error at line: 1, column: 27: Incorrect syntax near 'for'. - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureSynapse))] + [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] public void ExecuteXmlReaderTest() { - RemoteExecutor.Invoke(() => + InvokeRemote(() => { CollectStatisticsDiagnostics(_ => { @@ -220,13 +246,13 @@ public void ExecuteXmlReaderTest() } }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandAfter, WriteConnectionCloseBefore, WriteConnectionCloseAfter]); return RemoteExecutor.SuccessExitCode; - }).Dispose(); + }); } - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup))] + [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] public void ExecuteXmlReaderErrorTest() { - RemoteExecutor.Invoke(() => + InvokeRemote(() => { CollectStatisticsDiagnostics(connectionString => { @@ -242,13 +268,13 @@ public void ExecuteXmlReaderErrorTest() } }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandError, WriteConnectionCloseBefore, WriteConnectionCloseAfter]); return RemoteExecutor.SuccessExitCode; - }).Dispose(); + }); } - [Fact] + [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] public void ExecuteScalarAsyncTest() { - RemoteExecutor.Invoke(() => + InvokeRemote(() => { CollectStatisticsDiagnosticsAsync(async connectionString => { @@ -268,13 +294,13 @@ public void ExecuteScalarAsyncTest() } }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandAfter, WriteConnectionCloseBefore, WriteConnectionCloseAfter]).Wait(); return RemoteExecutor.SuccessExitCode; - }).Dispose(); + }); } - [Fact] + [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] public void ExecuteScalarAsyncErrorTest() { - RemoteExecutor.Invoke(() => + InvokeRemote(() => { CollectStatisticsDiagnosticsAsync(async connectionString => { @@ -294,13 +320,13 @@ public void ExecuteScalarAsyncErrorTest() } }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandError, WriteConnectionCloseBefore, WriteConnectionCloseAfter]).Wait(); return RemoteExecutor.SuccessExitCode; - }).Dispose(); + }); } - [Fact] + [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] public void ExecuteNonQueryAsyncTest() { - RemoteExecutor.Invoke(() => + InvokeRemote(() => { CollectStatisticsDiagnosticsAsync(async connectionString => { @@ -320,13 +346,13 @@ public void ExecuteNonQueryAsyncTest() } }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandAfter, WriteConnectionCloseBefore, WriteConnectionCloseAfter]).Wait(); return RemoteExecutor.SuccessExitCode; - }).Dispose(); + }); } - [Fact] + [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] public void ExecuteNonQueryAsyncErrorTest() { - RemoteExecutor.Invoke(() => + InvokeRemote(() => { CollectStatisticsDiagnosticsAsync(async connectionString => { @@ -346,13 +372,13 @@ public void ExecuteNonQueryAsyncErrorTest() } }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandError, WriteConnectionCloseBefore, WriteConnectionCloseAfter]).Wait(); return RemoteExecutor.SuccessExitCode; - }).Dispose(); + }); } - [Fact] + [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] public void ExecuteReaderAsyncTest() { - RemoteExecutor.Invoke(() => + InvokeRemote(() => { CollectStatisticsDiagnosticsAsync(async connectionString => { @@ -376,13 +402,13 @@ public void ExecuteReaderAsyncTest() } }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandAfter, WriteConnectionCloseBefore, WriteConnectionCloseAfter]).Wait(); return RemoteExecutor.SuccessExitCode; - }).Dispose(); + }); } - [Fact] + [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] public void ExecuteReaderAsyncErrorTest() { - RemoteExecutor.Invoke(() => + InvokeRemote(() => { CollectStatisticsDiagnosticsAsync(async connectionString => { @@ -403,15 +429,15 @@ public void ExecuteReaderAsyncErrorTest() } }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandError, WriteConnectionCloseBefore, WriteConnectionCloseAfter]).Wait(); return RemoteExecutor.SuccessExitCode; - }).Dispose(); + }); } // Synapse: Parse error at line: 1, column: 27: Incorrect syntax near 'for'. - [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.AreConnStringsSetup), nameof(DataTestUtility.IsNotAzureSynapse))] + [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] public void ExecuteXmlReaderAsyncTest() { // @TODO: TestTdsServer does not handle xml reader, so connect to a real server as a workaround - RemoteExecutor.Invoke(() => + InvokeRemote(() => { CollectStatisticsDiagnosticsAsync(async _ => { @@ -435,14 +461,14 @@ public void ExecuteXmlReaderAsyncTest() } }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandAfter, WriteConnectionCloseBefore, WriteConnectionCloseAfter]).Wait(); return RemoteExecutor.SuccessExitCode; - }).Dispose(); + }); } - [Fact] + [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] public void ExecuteXmlReaderAsyncErrorTest() { // @TODO: TestTdsServer does not handle xml reader, so connect to a real server as a workaround - RemoteExecutor.Invoke(() => + InvokeRemote(() => { CollectStatisticsDiagnosticsAsync(async _ => @@ -470,13 +496,13 @@ public void ExecuteXmlReaderAsyncErrorTest() } }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandAfter, WriteConnectionCloseBefore, WriteConnectionCloseAfter]).Wait(); return RemoteExecutor.SuccessExitCode; - }).Dispose(); + }); } - [Fact] + [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] public void ConnectionOpenTest() { - RemoteExecutor.Invoke(() => + InvokeRemote(() => { CollectStatisticsDiagnostics(connectionString => { @@ -486,13 +512,13 @@ public void ConnectionOpenTest() } }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteConnectionCloseBefore, WriteConnectionCloseAfter]); return RemoteExecutor.SuccessExitCode; - }).Dispose(); + }); } - [Fact] + [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] public void ConnectionOpenErrorTest() { - RemoteExecutor.Invoke(() => + InvokeRemote(() => { CollectStatisticsDiagnostics(_ => { @@ -502,13 +528,13 @@ public void ConnectionOpenErrorTest() } }, [WriteConnectionOpenBefore, WriteConnectionOpenError]); return RemoteExecutor.SuccessExitCode; - }).Dispose(); + }); } - [Fact] + [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] public void ConnectionOpenAsyncTest() { - RemoteExecutor.Invoke(() => + InvokeRemote(() => { CollectStatisticsDiagnosticsAsync(async connectionString => { @@ -522,13 +548,13 @@ public void ConnectionOpenAsyncTest() } }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteConnectionCloseBefore, WriteConnectionCloseAfter]).Wait(); return RemoteExecutor.SuccessExitCode; - }).Dispose(); + }); } - [Fact] + [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] public void ConnectionOpenAsyncErrorTest() { - RemoteExecutor.Invoke(() => + InvokeRemote(() => { CollectStatisticsDiagnosticsAsync(async _ => { @@ -542,7 +568,7 @@ public void ConnectionOpenAsyncErrorTest() } }, [WriteConnectionOpenBefore, WriteConnectionOpenError]).Wait(); return RemoteExecutor.SuccessExitCode; - }).Dispose(); + }); } private static void CollectStatisticsDiagnostics(Action sqlOperation, string[] expectedDiagnostics, [CallerMemberName] string methodName = "") diff --git a/tools/targets/PatchTestDeps.targets b/tools/targets/PatchTestDeps.targets new file mode 100644 index 0000000000..0ce4e6fea6 --- /dev/null +++ b/tools/targets/PatchTestDeps.targets @@ -0,0 +1,95 @@ + + + + + + + + + + + + + + + + + + + += 0) +{ + string peek = json.Substring(ki, System.Math.Min(200, json.Length - ki)); + if (peek.Contains("\"runtime\"")) + return true; +} + +// Inject a runtime target entry after the TFM opening brace. +string runtimeBlock = keyLiteral + @": { + ""dependencies"": {}, + ""runtime"": { + """ + dllName + @""": { + ""assemblyVersion"": """ + AssemblyVersion + @""", + ""fileVersion"": """ + AssemblyVersion + @""" + } + } + }, + "; + +var tfmMatch = Regex.Match(json, @"""\.NETCoreApp[^""]*""\s*:\s*\{"); +if (tfmMatch.Success) + json = json.Insert(tfmMatch.Index + tfmMatch.Length, "\n " + runtimeBlock); + +// Ensure a library entry exists. +int libIdx = json.IndexOf("\"libraries\""); +if (libIdx >= 0) +{ + int libStart = json.IndexOf("{", libIdx + 11); + if (libStart >= 0 && json.IndexOf(keyLiteral, libStart) < 0) + { + string libEntry = "\n " + keyLiteral + @": { + ""type"": ""project"", + ""serviceable"": false, + ""sha512"": """" + },"; + json = json.Insert(libStart + 1, libEntry); + } +} + +System.IO.File.WriteAllText(DepsFilePath, json); +Log.LogMessage(MessageImportance.High, + "Patched " + System.IO.Path.GetFileName(DepsFilePath) + + ": added runtime entry for " + libraryKey); +]]> + + + + + From cc47b9d390bc1dd8a52d2b6319494b4a3ed63383 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Tue, 24 Mar 2026 00:14:03 -0700 Subject: [PATCH 2/6] Fix missing using directives in DiagnosticTest.cs (#4079) * Initial plan * Add missing using directives to DiagnosticTest.cs Co-authored-by: cheenamalhotra <13396919+cheenamalhotra@users.noreply.github.com> Agent-Logs-Url: https://github.com/dotnet/SqlClient/sessions/244c9288-ae91-4b54-8cd6-0837024e8792 --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: cheenamalhotra <13396919+cheenamalhotra@users.noreply.github.com> --- .../tests/ManualTests/TracingTests/DiagnosticTest.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/TracingTests/DiagnosticTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/TracingTests/DiagnosticTest.cs index 067f125b53..68594d6dcd 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/TracingTests/DiagnosticTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/TracingTests/DiagnosticTest.cs @@ -2,12 +2,23 @@ // 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.Data; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; +using System.Threading.Tasks; using System.Xml; +using Microsoft.DotNet.RemoteExecutor; +using Microsoft.SqlServer.TDS; +using Microsoft.SqlServer.TDS.Done; +using Microsoft.SqlServer.TDS.EndPoint; +using Microsoft.SqlServer.TDS.Error; +using Microsoft.SqlServer.TDS.SQLBatch; +using Microsoft.SqlServer.TDS.Servers; +using Xunit; +using Xunit.Abstractions; namespace Microsoft.Data.SqlClient.ManualTesting.Tests { From 188221aaaaaada3bab45e42109b8e56803156a42 Mon Sep 17 00:00:00 2001 From: Cheena Malhotra Date: Tue, 24 Mar 2026 00:39:36 -0700 Subject: [PATCH 3/6] Fix target --- tools/targets/PatchTestDeps.targets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/targets/PatchTestDeps.targets b/tools/targets/PatchTestDeps.targets index 0ce4e6fea6..eddb06a9e6 100644 --- a/tools/targets/PatchTestDeps.targets +++ b/tools/targets/PatchTestDeps.targets @@ -13,7 +13,7 @@ + Condition="'$(MdsPackageVersion)' == '' OR '$(MdsAssemblyVersion)' == ''" /> Date: Tue, 24 Mar 2026 12:40:59 -0700 Subject: [PATCH 4/6] Minor touch-ups --- ...icrosoft.Data.SqlClient.ManualTests.csproj | 13 ++- tools/targets/PatchTestDeps.targets | 83 ++++++------------- 2 files changed, 36 insertions(+), 60 deletions(-) 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 f2b0325165..67bef4ae9c 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" /> @@ -423,7 +424,13 @@ - - + + diff --git a/tools/targets/PatchTestDeps.targets b/tools/targets/PatchTestDeps.targets index eddb06a9e6..a72ebc21f8 100644 --- a/tools/targets/PatchTestDeps.targets +++ b/tools/targets/PatchTestDeps.targets @@ -1,14 +1,7 @@ @@ -17,7 +10,7 @@ + Condition="Exists('$(ProjectDepsFilePath)')"> - + - -= 0) -{ - string peek = json.Substring(ki, System.Math.Min(200, json.Length - ki)); - if (peek.Contains("\"runtime\"")) - return true; -} +// Already has a runtime entry — nothing to do. +int ki = json.IndexOf(key); +if (ki >= 0 && json.Substring(ki, System.Math.Min(200, json.Length - ki)).Contains("\"runtime\"")) + return true; -// Inject a runtime target entry after the TFM opening brace. -string runtimeBlock = keyLiteral + @": { +// Inject target entry with runtime section. +string block = key + @": { ""dependencies"": {}, - ""runtime"": { - """ + dllName + @""": { - ""assemblyVersion"": """ + AssemblyVersion + @""", - ""fileVersion"": """ + AssemblyVersion + @""" - } - } - }, - "; - -var tfmMatch = Regex.Match(json, @"""\.NETCoreApp[^""]*""\s*:\s*\{"); -if (tfmMatch.Success) - json = json.Insert(tfmMatch.Index + tfmMatch.Length, "\n " + runtimeBlock); + ""runtime"": { """ + PackageId + @".dll"": { ""assemblyVersion"": """ + AssemblyVersion + @""", ""fileVersion"": """ + AssemblyVersion + @""" } } + }, "; +var m = Regex.Match(json, @"""\.NETCoreApp[^""]*""\s*:\s*\{"); +if (m.Success) + json = json.Insert(m.Index + m.Length, "\n " + block); -// Ensure a library entry exists. -int libIdx = json.IndexOf("\"libraries\""); -if (libIdx >= 0) +// Inject library entry if missing. +int li = json.IndexOf("\"libraries\""); +if (li >= 0) { - int libStart = json.IndexOf("{", libIdx + 11); - if (libStart >= 0 && json.IndexOf(keyLiteral, libStart) < 0) - { - string libEntry = "\n " + keyLiteral + @": { - ""type"": ""project"", - ""serviceable"": false, - ""sha512"": """" - },"; - json = json.Insert(libStart + 1, libEntry); - } + int lb = json.IndexOf("{", li + 11); + if (lb >= 0 && json.IndexOf(key, lb) < 0) + json = json.Insert(lb + 1, "\n " + key + @": { ""type"": ""project"", ""serviceable"": false, ""sha512"": """" },"); } System.IO.File.WriteAllText(DepsFilePath, json); -Log.LogMessage(MessageImportance.High, - "Patched " + System.IO.Path.GetFileName(DepsFilePath) - + ": added runtime entry for " + libraryKey); -]]> - +Log.LogMessage(MessageImportance.High, "Patched " + System.IO.Path.GetFileName(DepsFilePath) + ": added runtime entry for " + PackageId + "/" + PackageVersion); +]]> From 3302ca760f6edc96cd1a3caccd8d1b3b11f62968 Mon Sep 17 00:00:00 2001 From: Cheena Malhotra Date: Tue, 24 Mar 2026 14:17:40 -0700 Subject: [PATCH 5/6] Ensure diagnostics are always sent synchronously after connection attempt is completed. --- .../src/Microsoft/Data/SqlClient/SqlConnection.cs | 1 + 1 file changed, 1 insertion(+) 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..a5742416fd 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnection.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnection.cs @@ -1939,6 +1939,7 @@ private Task InternalOpenAsync(SqlConnectionOverrides overrides, CancellationTok result.Task.ContinueWith( continuationAction: s_openAsyncComplete, state: operationId, // connection is passed in TaskCompletionSource async state + continuationOptions: TaskContinuationOptions.ExecuteSynchronously, scheduler: TaskScheduler.Default ); } From a7d283cf8f08d069736f190e6134b46ff8524768 Mon Sep 17 00:00:00 2001 From: Cheena Malhotra Date: Tue, 24 Mar 2026 14:56:30 -0700 Subject: [PATCH 6/6] Remove RemoteExecutor --- .../Microsoft/Data/SqlClient/SqlConnection.cs | 3 +- .../tests/Directory.Packages.props | 1 - ...icrosoft.Data.SqlClient.ManualTests.csproj | 13 +- .../TracingTests/DiagnosticTest.cs | 1116 ++++------------- tools/targets/PatchTestDeps.targets | 64 - 5 files changed, 278 insertions(+), 919 deletions(-) delete mode 100644 tools/targets/PatchTestDeps.targets 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 a5742416fd..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,9 @@ 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 67bef4ae9c..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 @@ -361,7 +361,6 @@ - @@ -396,7 +395,6 @@ - @@ -423,14 +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 68594d6dcd..db49fc7c32 100644 --- a/src/Microsoft.Data.SqlClient/tests/ManualTests/TracingTests/DiagnosticTest.cs +++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/TracingTests/DiagnosticTest.cs @@ -4,13 +4,13 @@ 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.DotNet.RemoteExecutor; using Microsoft.SqlServer.TDS; using Microsoft.SqlServer.TDS.Done; using Microsoft.SqlServer.TDS.EndPoint; @@ -18,20 +18,14 @@ using Microsoft.SqlServer.TDS.SQLBatch; using Microsoft.SqlServer.TDS.Servers; using Xunit; -using Xunit.Abstractions; namespace Microsoft.Data.SqlClient.ManualTesting.Tests { + // Serialized execution: DiagnosticListener is global state, so these tests + // must not run in parallel with each other. + [Collection("DiagnosticTests")] public class DiagnosticTest { - private readonly ITestOutputHelper _output; - - public DiagnosticTest(ITestOutputHelper output) - { - _output = output; - } - - 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"; @@ -40,1001 +34,441 @@ public DiagnosticTest(ITestOutputHelper output) 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"; - - /// - /// Invokes a delegate via RemoteExecutor while capturing stdout/stderr - /// from the child process and forwarding them to xUnit's ITestOutputHelper. - /// - private void InvokeRemote(Func func, [CallerMemberName] string testName = "") - { - var options = new RemoteInvokeOptions(); - options.StartInfo.RedirectStandardOutput = true; - options.StartInfo.RedirectStandardError = true; - - using RemoteInvokeHandle handle = RemoteExecutor.Invoke(func, options); - Process process = handle.Process; - - // Read stdout/stderr asynchronously so we don't deadlock. - Task stdoutTask = process.StandardOutput.ReadToEndAsync(); - Task stderrTask = process.StandardError.ReadToEndAsync(); - - process.WaitForExit(); + private const string BadConnectionString = "data source = bad; initial catalog = bad; integrated security = true; connection timeout = 1;"; - string stdout = stdoutTask.GetAwaiter().GetResult(); - string stderr = stderrTask.GetAwaiter().GetResult(); + #region Sync tests - if (!string.IsNullOrWhiteSpace(stdout)) - { - _output.WriteLine($"[{testName} stdout]{Environment.NewLine}{stdout}"); - } - if (!string.IsNullOrWhiteSpace(stderr)) - { - _output.WriteLine($"[{testName} stderr]{Environment.NewLine}{stderr}"); - } - } - - [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + [Fact] public void ExecuteScalarTest() { - InvokeRemote(() => + 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; - }); + 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]); } - [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + [Fact] public void ExecuteScalarErrorTest() { - InvokeRemote(() => + 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; - }); + 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]); } - [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + [Fact] public void ExecuteNonQueryTest() { - InvokeRemote(() => + 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; - }); + 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]); } - [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + [Fact] public void ExecuteNonQueryErrorTest() { - InvokeRemote(() => + 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; - }); + 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]); } - [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + [Fact] public void ExecuteReaderTest() { - InvokeRemote(() => + 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; - }); + 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]); } - [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + [Fact] public void ExecuteReaderErrorTest() { - InvokeRemote(() => + 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; - }); + 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]); } - [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + [Fact] public void ExecuteReaderWithCommandBehaviorTest() { - InvokeRemote(() => + 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; - }); + 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(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + [Fact] public void ExecuteXmlReaderTest() { - InvokeRemote(() => + // 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; - }); + 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(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + [Fact] public void ExecuteXmlReaderErrorTest() { - InvokeRemote(() => + 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; - }); + 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]); } - [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public void ExecuteScalarAsyncTest() + [Fact] + public void ConnectionOpenTest() { - InvokeRemote(() => + 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; - }); + using SqlConnection conn = new(connectionString); + conn.Open(); + }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteConnectionCloseBefore, WriteConnectionCloseAfter]); } - [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public void ExecuteScalarAsyncErrorTest() + [Fact] + public void ConnectionOpenErrorTest() { - InvokeRemote(() => + 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; - }); + using SqlConnection conn = new(BadConnectionString); + Assert.Throws(() => conn.Open()); + }, [WriteConnectionOpenBefore, WriteConnectionOpenError]); } - [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public void ExecuteNonQueryAsyncTest() + #endregion + + #region Async tests + + [Fact] + public async Task ExecuteScalarAsyncTest() { - InvokeRemote(() => + 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; - }); + conn.Open(); + await cmd.ExecuteScalarAsync(); + }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandAfter, WriteConnectionCloseBefore, WriteConnectionCloseAfter]); } - [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public void ExecuteNonQueryAsyncErrorTest() + [Fact] + public async Task ExecuteScalarAsyncErrorTest() { - InvokeRemote(() => + 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; - }); + conn.Open(); + await Assert.ThrowsAsync(() => cmd.ExecuteScalarAsync()); + }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandError, WriteConnectionCloseBefore, WriteConnectionCloseAfter]); } - [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public void ExecuteReaderAsyncTest() + [Fact] + public async Task ExecuteNonQueryAsyncTest() { - InvokeRemote(() => + 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; - }); + conn.Open(); + await cmd.ExecuteNonQueryAsync(); + }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandAfter, WriteConnectionCloseBefore, WriteConnectionCloseAfter]); } - [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public void ExecuteReaderAsyncErrorTest() + [Fact] + public async Task ExecuteNonQueryAsyncErrorTest() { - InvokeRemote(() => + 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; - }); + 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(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public void ExecuteXmlReaderAsyncTest() + [Fact] + public async Task ExecuteReaderAsyncTest() { - // @TODO: TestTdsServer does not handle xml reader, so connect to a real server as a workaround - InvokeRemote(() => + 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; - }); + conn.Open(); + using SqlDataReader reader = await cmd.ExecuteReaderAsync(); + while (reader.Read()) { } + }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandAfter, WriteConnectionCloseBefore, WriteConnectionCloseAfter]); } - [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public void ExecuteXmlReaderAsyncErrorTest() + [Fact] + public async Task ExecuteReaderAsyncErrorTest() { - // @TODO: TestTdsServer does not handle xml reader, so connect to a real server as a workaround - InvokeRemote(() => + 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; - }); + conn.Open(); + await Assert.ThrowsAsync(() => cmd.ExecuteReaderAsync()); + }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteCommandBefore, WriteCommandError, WriteConnectionCloseBefore, WriteConnectionCloseAfter]); } - [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public void ConnectionOpenTest() + [Fact] + public async Task ExecuteXmlReaderAsyncTest() { - InvokeRemote(() => + // 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; - }); +#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]); } - [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public void ConnectionOpenErrorTest() + [Fact] + public async Task ExecuteXmlReaderAsyncErrorTest() { - InvokeRemote(() => + await CollectStatisticsDiagnosticsAsync(async connectionString => { - CollectStatisticsDiagnostics(_ => - { - using (SqlConnection sqlConnection = new SqlConnection(BadConnectionString)) - { - Assert.Throws(() => sqlConnection.Open()); - } - }, [WriteConnectionOpenBefore, WriteConnectionOpenError]); - return RemoteExecutor.SuccessExitCode; - }); +#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]); } - [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public void ConnectionOpenAsyncTest() + [Fact] + public async Task ConnectionOpenAsyncTest() { - InvokeRemote(() => + 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; - }); + await conn.OpenAsync(); + }, [WriteConnectionOpenBefore, WriteConnectionOpenAfter, WriteConnectionCloseBefore, WriteConnectionCloseAfter]); } - [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public void ConnectionOpenAsyncErrorTest() + [Fact] + public async Task ConnectionOpenAsyncErrorTest() { - InvokeRemote(() => + 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; - }); + 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); + #endregion - Exception ex = GetPropertyValueFromType(kvp.Value, "Exception"); - Assert.NotNull(ex); + #region Helpers - 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); - - 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; - } - 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); - - Console.WriteLine(string.Format("Test: {0} SqlOperation Successful", methodName)); + DataSource = $"localhost,{server.EndPoint.Port}", + Encrypt = SqlConnectionEncryptOption.Optional + }.ConnectionString; - Assert.True(statsLogged); + sqlOperation(connectionString); - diagnosticListenerObserver.Disable(); - foreach (string expected in expectedDiagnostics) - { - Assert.True(diagnosticListenerObserver.HasReceivedDiagnostic(expected), $"Missing diagnostic '{expected}'"); - } + Assert.True(statsLogged, "Expected at least one diagnostic event"); + observer.Disable(); - 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); } } } diff --git a/tools/targets/PatchTestDeps.targets b/tools/targets/PatchTestDeps.targets deleted file mode 100644 index a72ebc21f8..0000000000 --- a/tools/targets/PatchTestDeps.targets +++ /dev/null @@ -1,64 +0,0 @@ - - - - - - - - - - - - - - - - - - - = 0 && json.Substring(ki, System.Math.Min(200, json.Length - ki)).Contains("\"runtime\"")) - return true; - -// Inject target entry with runtime section. -string block = key + @": { - ""dependencies"": {}, - ""runtime"": { """ + PackageId + @".dll"": { ""assemblyVersion"": """ + AssemblyVersion + @""", ""fileVersion"": """ + AssemblyVersion + @""" } } - }, "; -var m = Regex.Match(json, @"""\.NETCoreApp[^""]*""\s*:\s*\{"); -if (m.Success) - json = json.Insert(m.Index + m.Length, "\n " + block); - -// Inject library entry if missing. -int li = json.IndexOf("\"libraries\""); -if (li >= 0) -{ - int lb = json.IndexOf("{", li + 11); - if (lb >= 0 && json.IndexOf(key, lb) < 0) - json = json.Insert(lb + 1, "\n " + key + @": { ""type"": ""project"", ""serviceable"": false, ""sha512"": """" },"); -} - -System.IO.File.WriteAllText(DepsFilePath, json); -Log.LogMessage(MessageImportance.High, "Patched " + System.IO.Path.GetFileName(DepsFilePath) + ": added runtime entry for " + PackageId + "/" + PackageVersion); -]]> - - - -