1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4 
5 using System.Collections;
6 using System.Diagnostics;
7 using System.Reflection;
8 using System.Threading.Tasks;
9 using System.Xml;
10 using Microsoft.SqlServer.TDS;
11 using Microsoft.SqlServer.TDS.Done;
12 using Microsoft.SqlServer.TDS.EndPoint;
13 using Microsoft.SqlServer.TDS.Error;
14 using Microsoft.SqlServer.TDS.Servers;
15 using Microsoft.SqlServer.TDS.SQLBatch;
16 using Xunit;
17 using System.Runtime.CompilerServices;
18 
19 namespace System.Data.SqlClient.Tests
20 {
21     [ActiveIssue("dotnet/corefx #17925", TestPlatforms.Any)]
22     public class DiagnosticTest : RemoteExecutorTestBase
23     {
24         private const string BadConnectionString = "data source = bad; initial catalog = bad; uid = bad; password = bad; connection timeout = 1;";
25         private static readonly string s_tcpConnStr = Environment.GetEnvironmentVariable("TEST_TCP_CONN_STR") ?? string.Empty;
26 
IsConnectionStringConfigured()27         public static bool IsConnectionStringConfigured() => s_tcpConnStr != string.Empty;
28 
29         [Fact]
30         [SkipOnTargetFramework(TargetFrameworkMonikers.UapAot)] // Internals reflection not supported on uapaot
ExecuteScalarTest()31         public void ExecuteScalarTest()
32         {
33             RemoteInvoke(() =>
34             {
35                 CollectStatisticsDiagnostics(connectionString =>
36                 {
37                     using (SqlConnection conn = new SqlConnection(connectionString))
38                     using (SqlCommand cmd = new SqlCommand())
39                     {
40                         cmd.Connection = conn;
41                         cmd.CommandText = "SELECT [name], [state] FROM [sys].[databases] WHERE [name] = db_name();";
42 
43                         conn.Open();
44                         var output = cmd.ExecuteScalar();
45                     }
46                 });
47                 return SuccessExitCode;
48             }).Dispose();
49         }
50 
51         [Fact]
52         [SkipOnTargetFramework(TargetFrameworkMonikers.UapAot)] // Internals reflection not supported on uapaot
ExecuteScalarErrorTest()53         public void ExecuteScalarErrorTest()
54         {
55             RemoteInvoke(() =>
56             {
57                 CollectStatisticsDiagnostics(connectionString =>
58                 {
59                     using (SqlConnection conn = new SqlConnection(connectionString))
60                     using (SqlCommand cmd = new SqlCommand())
61                     {
62                         cmd.Connection = conn;
63                         cmd.CommandText = "select 1 / 0;";
64 
65                         conn.Open();
66 
67                         try { var output = cmd.ExecuteScalar(); }
68                         catch { }
69                     }
70                 });
71                 return SuccessExitCode;
72             }).Dispose();
73         }
74 
75         [Fact]
76         [SkipOnTargetFramework(TargetFrameworkMonikers.UapAot)] // Internals reflection not supported on uapaot
ExecuteNonQueryTest()77         public void ExecuteNonQueryTest()
78         {
79             RemoteInvoke(() =>
80             {
81                 CollectStatisticsDiagnostics(connectionString =>
82                 {
83                     using (SqlConnection conn = new SqlConnection(connectionString))
84                     using (SqlCommand cmd = new SqlCommand())
85                     {
86                         cmd.Connection = conn;
87                         cmd.CommandText = "SELECT [name], [state] FROM [sys].[databases] WHERE [name] = db_name();";
88 
89                         conn.Open();
90                         var output = cmd.ExecuteNonQuery();
91                     }
92                 });
93                 return SuccessExitCode;
94             }).Dispose();
95         }
96 
97         [Fact]
98         [SkipOnTargetFramework(TargetFrameworkMonikers.UapAot)] // Internals reflection not supported on uapaot
ExecuteNonQueryErrorTest()99         public void ExecuteNonQueryErrorTest()
100         {
101             RemoteInvoke(() =>
102             {
103                 CollectStatisticsDiagnostics(connectionString =>
104                 {
105                     using (SqlConnection conn = new SqlConnection(connectionString))
106                     {
107                         using (SqlCommand cmd = new SqlCommand())
108                         {
109                             cmd.Connection = conn;
110                             cmd.CommandText = "select 1 / 0;";
111 
112                             // Limiting the command timeout to 3 seconds. This should be lower than the Process timeout.
113                             cmd.CommandTimeout = 3;
114                             conn.Open();
115                             Console.WriteLine("SqlClient.DiagnosticTest.ExecuteNonQueryErrorTest Connection Open Successful");
116 
117                             try
118                             {
119                                 var output = cmd.ExecuteNonQuery();
120                             }
121                             catch (Exception e)
122                             {
123                                 Console.WriteLine("SqlClient.DiagnosticTest.ExecuteNonQueryErrorTest " + e.Message);
124                             }
125                             Console.WriteLine("SqlClient.DiagnosticTest.ExecuteNonQueryErrorTest Command Executed");
126                         }
127                         Console.WriteLine("SqlClient.DiagnosticTest.ExecuteNonQueryErrorTest Command Disposed");
128                     }
129                     Console.WriteLine("SqlClient.DiagnosticTest.ExecuteNonQueryErrorTest Connection Disposed");
130                 });
131                 return SuccessExitCode;
132             }).Dispose();
133         }
134 
135         [Fact]
136         [SkipOnTargetFramework(TargetFrameworkMonikers.UapAot)] // Internals reflection not supported on uapaot
ExecuteReaderTest()137         public void ExecuteReaderTest()
138         {
139             RemoteInvoke(() =>
140             {
141                 CollectStatisticsDiagnostics(connectionString =>
142                 {
143                     using (SqlConnection conn = new SqlConnection(connectionString))
144                     using (SqlCommand cmd = new SqlCommand())
145                     {
146                         cmd.Connection = conn;
147                         cmd.CommandText = "SELECT [name], [state] FROM [sys].[databases] WHERE [name] = db_name();";
148 
149                         conn.Open();
150                         SqlDataReader reader = cmd.ExecuteReader();
151                         while (reader.Read()) { }
152                     }
153                 });
154                 return SuccessExitCode;
155             }).Dispose();
156         }
157 
158         [Fact]
159         [SkipOnTargetFramework(TargetFrameworkMonikers.UapAot)] // Internals reflection not supported on uapaot
ExecuteReaderErrorTest()160         public void ExecuteReaderErrorTest()
161         {
162             RemoteInvoke(() =>
163             {
164                 CollectStatisticsDiagnostics(connectionString =>
165                 {
166                     using (SqlConnection conn = new SqlConnection(connectionString))
167                     using (SqlCommand cmd = new SqlCommand())
168                     {
169                         cmd.Connection = conn;
170                         cmd.CommandText = "select 1 / 0;";
171 
172                         try
173                         {
174                             SqlDataReader reader = cmd.ExecuteReader();
175                             while (reader.Read()) { }
176                         }
177                         catch { }
178                     }
179                 });
180                 return SuccessExitCode;
181             }).Dispose();
182         }
183 
184         [Fact]
185         [SkipOnTargetFramework(TargetFrameworkMonikers.UapAot)] // Internals reflection not supported on uapaot
ExecuteReaderWithCommandBehaviorTest()186         public void ExecuteReaderWithCommandBehaviorTest()
187         {
188             RemoteInvoke(() =>
189             {
190                 CollectStatisticsDiagnostics(connectionString =>
191                 {
192                     using (SqlConnection conn = new SqlConnection(connectionString))
193                     using (SqlCommand cmd = new SqlCommand())
194                     {
195                         cmd.Connection = conn;
196                         cmd.CommandText = "SELECT [name], [state] FROM [sys].[databases] WHERE [name] = db_name();";
197 
198                         conn.Open();
199                         SqlDataReader reader = cmd.ExecuteReader(CommandBehavior.Default);
200                         while (reader.Read()) { }
201                     }
202                 });
203                 return SuccessExitCode;
204             }).Dispose();
205         }
206 
207         [ConditionalFact(nameof(IsConnectionStringConfigured))]
208         [SkipOnTargetFramework(TargetFrameworkMonikers.UapAot)] // Internals reflection not supported on uapaot
ExecuteXmlReaderTest()209         public void ExecuteXmlReaderTest()
210         {
211             RemoteInvoke(cs =>
212             {
213                 CollectStatisticsDiagnostics(_ =>
214                 {
215                     using (SqlConnection conn = new SqlConnection(cs))
216                     using (SqlCommand cmd = new SqlCommand())
217                     {
218                         cmd.Connection = conn;
219                         cmd.CommandText = "select * from sys.objects for xml auto, xmldata;";
220 
221                         conn.Open();
222                         XmlReader reader = cmd.ExecuteXmlReader();
223                         while (reader.Read()) { }
224                     }
225                 });
226                 return SuccessExitCode;
227             }, s_tcpConnStr).Dispose();
228         }
229 
230         [Fact]
231         [SkipOnTargetFramework(TargetFrameworkMonikers.UapAot)] // Internals reflection not supported on uapaot
ExecuteXmlReaderErrorTest()232         public void ExecuteXmlReaderErrorTest()
233         {
234             RemoteInvoke(() =>
235             {
236                 CollectStatisticsDiagnostics(connectionString =>
237                 {
238                     using (SqlConnection conn = new SqlConnection(connectionString))
239                     using (SqlCommand cmd = new SqlCommand())
240                     {
241                         cmd.Connection = conn;
242                         cmd.CommandText = "select *, baddata = 1 / 0 from sys.objects for xml auto, xmldata;";
243 
244                         try
245                         {
246                             XmlReader reader = cmd.ExecuteXmlReader();
247                             while (reader.Read()) { }
248                         }
249                         catch { }
250                     }
251                 });
252                 return SuccessExitCode;
253             }).Dispose();
254         }
255 
256         [Fact]
257         [SkipOnTargetFramework(TargetFrameworkMonikers.UapAot)] // Internals reflection not supported on uapaot
ExecuteScalarAsyncTest()258         public void ExecuteScalarAsyncTest()
259         {
260             RemoteInvoke(() =>
261             {
262                 CollectStatisticsDiagnosticsAsync(async connectionString =>
263                 {
264                     using (SqlConnection conn = new SqlConnection(connectionString))
265                     using (SqlCommand cmd = new SqlCommand())
266                     {
267                         cmd.Connection = conn;
268                         cmd.CommandText = "SELECT [name], [state] FROM [sys].[databases] WHERE [name] = db_name();";
269 
270                         conn.Open();
271                         var output = await cmd.ExecuteScalarAsync();
272                     }
273                 }).GetAwaiter().GetResult();
274                 return SuccessExitCode;
275             }).Dispose();
276         }
277 
278         [Fact]
279         [SkipOnTargetFramework(TargetFrameworkMonikers.UapAot)] // Internals reflection not supported on uapaot
ExecuteScalarAsyncErrorTest()280         public void ExecuteScalarAsyncErrorTest()
281         {
282             RemoteInvoke(() =>
283             {
284                 CollectStatisticsDiagnosticsAsync(async connectionString =>
285                 {
286                     using (SqlConnection conn = new SqlConnection(connectionString))
287                     using (SqlCommand cmd = new SqlCommand())
288                     {
289                         cmd.Connection = conn;
290                         cmd.CommandText = "select 1 / 0;";
291 
292                         conn.Open();
293 
294                         try { var output = await cmd.ExecuteScalarAsync(); }
295                         catch { }
296                     }
297                 }).GetAwaiter().GetResult();
298                 return SuccessExitCode;
299             }).Dispose();
300         }
301 
302         [Fact]
303         [SkipOnTargetFramework(TargetFrameworkMonikers.UapAot)] // Internals reflection not supported on uapaot
ExecuteNonQueryAsyncTest()304         public void ExecuteNonQueryAsyncTest()
305         {
306             RemoteInvoke(() =>
307             {
308                 CollectStatisticsDiagnosticsAsync(async connectionString =>
309                 {
310                     using (SqlConnection conn = new SqlConnection(connectionString))
311                     using (SqlCommand cmd = new SqlCommand())
312                     {
313                         cmd.Connection = conn;
314                         cmd.CommandText = "SELECT [name], [state] FROM [sys].[databases] WHERE [name] = db_name();";
315 
316                         conn.Open();
317                         var output = await cmd.ExecuteNonQueryAsync();
318                     }
319                 }).GetAwaiter().GetResult();
320                 return SuccessExitCode;
321             }).Dispose();
322         }
323 
324         [Fact]
325         [SkipOnTargetFramework(TargetFrameworkMonikers.UapAot)] // Internals reflection not supported on uapaot
ExecuteNonQueryAsyncErrorTest()326         public void ExecuteNonQueryAsyncErrorTest()
327         {
328             RemoteInvoke(() =>
329             {
330                 CollectStatisticsDiagnosticsAsync(async connectionString =>
331                 {
332                     using (SqlConnection conn = new SqlConnection(connectionString))
333                     using (SqlCommand cmd = new SqlCommand())
334                     {
335                         cmd.Connection = conn;
336                         cmd.CommandText = "select 1 / 0;";
337 
338                         conn.Open();
339                         try { var output = await cmd.ExecuteNonQueryAsync(); }
340                         catch { }
341                     }
342                 }).GetAwaiter().GetResult();
343                 return SuccessExitCode;
344             }).Dispose();
345         }
346 
347         [Fact]
348         [SkipOnTargetFramework(TargetFrameworkMonikers.UapAot)] // Internals reflection not supported on uapaot
ExecuteReaderAsyncTest()349         public void ExecuteReaderAsyncTest()
350         {
351             RemoteInvoke(() =>
352             {
353                 CollectStatisticsDiagnosticsAsync(async connectionString =>
354                 {
355                     using (SqlConnection conn = new SqlConnection(connectionString))
356                     using (SqlCommand cmd = new SqlCommand())
357                     {
358                         cmd.Connection = conn;
359                         cmd.CommandText = "SELECT [name], [state] FROM [sys].[databases] WHERE [name] = db_name();";
360 
361                         conn.Open();
362                         SqlDataReader reader = await cmd.ExecuteReaderAsync();
363                         while (reader.Read()) { }
364                     }
365                 }).GetAwaiter().GetResult();
366                 return SuccessExitCode;
367             }).Dispose();
368         }
369 
370         [Fact]
371         [SkipOnTargetFramework(TargetFrameworkMonikers.UapAot)] // Internals reflection not supported on uapaot
ExecuteReaderAsyncErrorTest()372         public void ExecuteReaderAsyncErrorTest()
373         {
374             RemoteInvoke(() =>
375             {
376                 CollectStatisticsDiagnosticsAsync(async connectionString =>
377                 {
378                     using (SqlConnection conn = new SqlConnection(connectionString))
379                     using (SqlCommand cmd = new SqlCommand())
380                     {
381                         cmd.Connection = conn;
382                         cmd.CommandText = "select 1 / 0;";
383 
384                         try
385                         {
386                             SqlDataReader reader = await cmd.ExecuteReaderAsync();
387                             while (reader.Read()) { }
388                         }
389                         catch { }
390                     }
391                 }).GetAwaiter().GetResult();
392                 return SuccessExitCode;
393             }).Dispose();
394         }
395 
396         [ConditionalFact(nameof(IsConnectionStringConfigured))]
397         [SkipOnTargetFramework(TargetFrameworkMonikers.UapAot)] // Internals reflection not supported on uapaot
ExecuteXmlReaderAsyncTest()398         public void ExecuteXmlReaderAsyncTest()
399         {
400             RemoteInvoke(cs =>
401             {
402                 CollectStatisticsDiagnosticsAsync(async _ =>
403                 {
404                     using (SqlConnection conn = new SqlConnection(cs))
405                     using (SqlCommand cmd = new SqlCommand())
406                     {
407                         cmd.Connection = conn;
408                         cmd.CommandText = "select * from sys.objects for xml auto, xmldata;";
409 
410                         conn.Open();
411                         XmlReader reader = await cmd.ExecuteXmlReaderAsync();
412                         while (reader.Read()) { }
413                     }
414                 }).GetAwaiter().GetResult();
415                 return SuccessExitCode;
416             }, s_tcpConnStr).Dispose();
417         }
418 
419         [ConditionalFact(nameof(IsConnectionStringConfigured))]
420         [SkipOnTargetFramework(TargetFrameworkMonikers.UapAot)] // Internals reflection not supported on uapaot
ExecuteXmlReaderAsyncErrorTest()421         public void ExecuteXmlReaderAsyncErrorTest()
422         {
423             RemoteInvoke(cs =>
424             {
425                 CollectStatisticsDiagnosticsAsync(async _ =>
426                 {
427                     using (SqlConnection conn = new SqlConnection(cs))
428                     using (SqlCommand cmd = new SqlCommand())
429                     {
430                         cmd.Connection = conn;
431                         cmd.CommandText = "select *, baddata = 1 / 0 from sys.objects for xml auto, xmldata;";
432 
433                         try
434                         {
435                             XmlReader reader = await cmd.ExecuteXmlReaderAsync();
436                             while (reader.Read()) { }
437                         }
438                         catch { }
439                     }
440                 }).GetAwaiter().GetResult();
441                 return SuccessExitCode;
442             }, s_tcpConnStr).Dispose();
443         }
444 
445         [Fact]
446         [SkipOnTargetFramework(TargetFrameworkMonikers.UapAot)] // Internals reflection not supported on uapaot
ConnectionOpenTest()447         public void ConnectionOpenTest()
448         {
449             RemoteInvoke(() =>
450             {
451                 CollectStatisticsDiagnostics(connectionString =>
452                 {
453                     using (SqlConnection sqlConnection = new SqlConnection(connectionString))
454                     {
455                         sqlConnection.Open();
456                         Console.WriteLine("SqlClient.DiagnosticsTest.ConnectionOpenTest:: Connection Opened ");
457                     }
458                     Console.WriteLine("SqlClient.DiagnosticsTest.ConnectionOpenTest:: Connection Should Be Disposed");
459                 }, true);
460 
461                 Console.WriteLine("SqlClient.DiagnosticsTest.ConnectionOpenTest:: Done with Diagnostics collection");
462                 return SuccessExitCode;
463             }).Dispose();
464         }
465 
466         [Fact]
467         [SkipOnTargetFramework(TargetFrameworkMonikers.UapAot)] // Internals reflection not supported on uapaot
ConnectionOpenErrorTest()468         public void ConnectionOpenErrorTest()
469         {
470             RemoteInvoke(() =>
471             {
472                 CollectStatisticsDiagnostics(_ =>
473                 {
474                     using (SqlConnection sqlConnection = new SqlConnection(BadConnectionString))
475                     {
476                         try { sqlConnection.Open(); } catch { }
477                     }
478                 });
479                 return SuccessExitCode;
480             }).Dispose();
481         }
482 
483         [Fact]
484         [SkipOnTargetFramework(TargetFrameworkMonikers.UapAot)] // Internals reflection not supported on uapaot
ConnectionOpenAsyncTest()485         public void ConnectionOpenAsyncTest()
486         {
487             RemoteInvoke(() =>
488             {
489                 CollectStatisticsDiagnosticsAsync(async connectionString =>
490                 {
491                     using (SqlConnection sqlConnection = new SqlConnection(connectionString))
492                     {
493                         await sqlConnection.OpenAsync();
494                     }
495                 }).GetAwaiter().GetResult();
496                 return SuccessExitCode;
497             }).Dispose();
498         }
499 
500         [Fact]
501         [SkipOnTargetFramework(TargetFrameworkMonikers.UapAot)] // Internals reflection not supported on uapaot
ConnectionOpenAsyncErrorTest()502         public void ConnectionOpenAsyncErrorTest()
503         {
504             RemoteInvoke(() =>
505             {
506                 CollectStatisticsDiagnosticsAsync(async _ =>
507                 {
508                     using (SqlConnection sqlConnection = new SqlConnection(BadConnectionString))
509                     {
510                         try { await sqlConnection.OpenAsync(); } catch { }
511                     }
512                 }).GetAwaiter().GetResult();
513                 return SuccessExitCode;
514             }).Dispose();
515         }
516 
CollectStatisticsDiagnostics(Action<string> sqlOperation, bool enableServerLogging = false, [CallerMemberName] string methodName = R)517         private static void CollectStatisticsDiagnostics(Action<string> sqlOperation, bool enableServerLogging = false, [CallerMemberName] string methodName = "")
518         {
519             bool statsLogged = false;
520             bool operationHasError = false;
521             Guid beginOperationId = Guid.Empty;
522 
523             FakeDiagnosticListenerObserver diagnosticListenerObserver = new FakeDiagnosticListenerObserver(kvp =>
524                 {
525                     IDictionary statistics;
526 
527                     if (kvp.Key.Equals("System.Data.SqlClient.WriteCommandBefore"))
528                     {
529                         Assert.NotNull(kvp.Value);
530 
531                         Guid retrievedOperationId = GetPropertyValueFromType<Guid>(kvp.Value, "OperationId");
532                         Assert.NotEqual(retrievedOperationId, Guid.Empty);
533 
534                         SqlCommand sqlCommand = GetPropertyValueFromType<SqlCommand>(kvp.Value, "Command");
535                         Assert.NotNull(sqlCommand);
536 
537                         string operation = GetPropertyValueFromType<string>(kvp.Value, "Operation");
538                         Assert.False(string.IsNullOrWhiteSpace(operation));
539 
540                         Guid connectionId = GetPropertyValueFromType<Guid>(kvp.Value, "ConnectionId");
541                         if (sqlCommand.Connection.State == ConnectionState.Open)
542                         {
543                             Assert.NotEqual(connectionId, Guid.Empty);
544                         }
545 
546                         beginOperationId = retrievedOperationId;
547 
548                         statsLogged = true;
549                     }
550                     else if (kvp.Key.Equals("System.Data.SqlClient.WriteCommandAfter"))
551                     {
552                         Assert.NotNull(kvp.Value);
553 
554                         Guid retrievedOperationId = GetPropertyValueFromType<Guid>(kvp.Value, "OperationId");
555                         Assert.NotEqual(retrievedOperationId, Guid.Empty);
556 
557                         SqlCommand sqlCommand = GetPropertyValueFromType<SqlCommand>(kvp.Value, "Command");
558                         Assert.NotNull(sqlCommand);
559 
560                         statistics = GetPropertyValueFromType<IDictionary>(kvp.Value, "Statistics");
561                         if (!operationHasError)
562                             Assert.NotNull(statistics);
563 
564                         string operation = GetPropertyValueFromType<string>(kvp.Value, "Operation");
565                         Assert.False(string.IsNullOrWhiteSpace(operation));
566 
567                         Guid connectionId = GetPropertyValueFromType<Guid>(kvp.Value, "ConnectionId");
568                         if (sqlCommand.Connection.State == ConnectionState.Open)
569                         {
570                             Assert.NotEqual(connectionId, Guid.Empty);
571                         }
572 
573                         // if we get to this point, then statistics exist and this must be the "end"
574                         // event, so we need to make sure the operation IDs match
575                         Assert.Equal(retrievedOperationId, beginOperationId);
576                         beginOperationId = Guid.Empty;
577 
578                         statsLogged = true;
579                     }
580                     else if (kvp.Key.Equals("System.Data.SqlClient.WriteCommandError"))
581                     {
582                         operationHasError = true;
583                         Assert.NotNull(kvp.Value);
584 
585                         SqlCommand sqlCommand = GetPropertyValueFromType<SqlCommand>(kvp.Value, "Command");
586                         Assert.NotNull(sqlCommand);
587 
588                         Exception ex = GetPropertyValueFromType<Exception>(kvp.Value, "Exception");
589                         Assert.NotNull(ex);
590 
591                         string operation = GetPropertyValueFromType<string>(kvp.Value, "Operation");
592                         Assert.False(string.IsNullOrWhiteSpace(operation));
593 
594                         Guid connectionId = GetPropertyValueFromType<Guid>(kvp.Value, "ConnectionId");
595                         if (sqlCommand.Connection.State == ConnectionState.Open)
596                         {
597                             Assert.NotEqual(connectionId, Guid.Empty);
598                         }
599 
600                         statsLogged = true;
601                     }
602                     else if (kvp.Key.Equals("System.Data.SqlClient.WriteConnectionOpenBefore"))
603                     {
604                         Assert.NotNull(kvp.Value);
605 
606                         SqlConnection sqlConnection = GetPropertyValueFromType<SqlConnection>(kvp.Value, "Connection");
607                         Assert.NotNull(sqlConnection);
608 
609                         string operation = GetPropertyValueFromType<string>(kvp.Value, "Operation");
610                         Assert.False(string.IsNullOrWhiteSpace(operation));
611 
612                         statsLogged = true;
613                     }
614                     else if (kvp.Key.Equals("System.Data.SqlClient.WriteConnectionOpenAfter"))
615                     {
616                         Assert.NotNull(kvp.Value);
617 
618                         SqlConnection sqlConnection = GetPropertyValueFromType<SqlConnection>(kvp.Value, "Connection");
619                         Assert.NotNull(sqlConnection);
620 
621                         string operation = GetPropertyValueFromType<string>(kvp.Value, "Operation");
622                         Assert.False(string.IsNullOrWhiteSpace(operation));
623 
624                         statistics = GetPropertyValueFromType<IDictionary>(kvp.Value, "Statistics");
625                         Assert.NotNull(statistics);
626 
627                         Guid connectionId = GetPropertyValueFromType<Guid>(kvp.Value, "ConnectionId");
628                         Assert.NotEqual(connectionId, Guid.Empty);
629 
630                         statsLogged = true;
631                     }
632                     else if (kvp.Key.Equals("System.Data.SqlClient.WriteConnectionOpenError"))
633                     {
634                         Assert.NotNull(kvp.Value);
635 
636                         SqlConnection sqlConnection = GetPropertyValueFromType<SqlConnection>(kvp.Value, "Connection");
637                         Assert.NotNull(sqlConnection);
638 
639                         string operation = GetPropertyValueFromType<string>(kvp.Value, "Operation");
640                         Assert.False(string.IsNullOrWhiteSpace(operation));
641 
642                         Exception ex = GetPropertyValueFromType<Exception>(kvp.Value, "Exception");
643                         Assert.NotNull(ex);
644 
645                         statsLogged = true;
646                     }
647                     else if (kvp.Key.Equals("System.Data.SqlClient.WriteConnectionCloseBefore"))
648                     {
649                         Assert.NotNull(kvp.Value);
650 
651                         SqlConnection sqlConnection = GetPropertyValueFromType<SqlConnection>(kvp.Value, "Connection");
652                         Assert.NotNull(sqlConnection);
653 
654                         string operation = GetPropertyValueFromType<string>(kvp.Value, "Operation");
655                         Assert.False(string.IsNullOrWhiteSpace(operation));
656 
657                         Guid connectionId = GetPropertyValueFromType<Guid>(kvp.Value, "ConnectionId");
658                         Assert.NotEqual(connectionId, Guid.Empty);
659 
660                         statsLogged = true;
661                     }
662                     else if (kvp.Key.Equals("System.Data.SqlClient.WriteConnectionCloseAfter"))
663                     {
664                         Assert.NotNull(kvp.Value);
665 
666                         SqlConnection sqlConnection = GetPropertyValueFromType<SqlConnection>(kvp.Value, "Connection");
667                         Assert.NotNull(sqlConnection);
668 
669                         string operation = GetPropertyValueFromType<string>(kvp.Value, "Operation");
670                         Assert.False(string.IsNullOrWhiteSpace(operation));
671 
672                         statistics = GetPropertyValueFromType<IDictionary>(kvp.Value, "Statistics");
673 
674                         Guid connectionId = GetPropertyValueFromType<Guid>(kvp.Value, "ConnectionId");
675                         Assert.NotEqual(connectionId, Guid.Empty);
676 
677                         statsLogged = true;
678                     }
679                     else if (kvp.Key.Equals("System.Data.SqlClient.WriteConnectionCloseError"))
680                     {
681                         Assert.NotNull(kvp.Value);
682 
683                         SqlConnection sqlConnection = GetPropertyValueFromType<SqlConnection>(kvp.Value, "Connection");
684                         Assert.NotNull(sqlConnection);
685 
686                         string operation = GetPropertyValueFromType<string>(kvp.Value, "Operation");
687                         Assert.False(string.IsNullOrWhiteSpace(operation));
688 
689                         Exception ex = GetPropertyValueFromType<Exception>(kvp.Value, "Exception");
690                         Assert.NotNull(ex);
691 
692                         Guid connectionId = GetPropertyValueFromType<Guid>(kvp.Value, "ConnectionId");
693                         Assert.NotEqual(connectionId, Guid.Empty);
694 
695                         statsLogged = true;
696                     }
697                 });
698 
699             diagnosticListenerObserver.Enable();
700             using (DiagnosticListener.AllListeners.Subscribe(diagnosticListenerObserver))
701             {
702 
703                 Console.WriteLine(string.Format("Test: {0} Enabled Listeners", methodName));
704                 using (var server = TestTdsServer.StartServerWithQueryEngine(new DiagnosticsQueryEngine(), enableLog:enableServerLogging, methodName: methodName))
705                 {
706                     Console.WriteLine(string.Format("Test: {0} Started Server", methodName));
707                     sqlOperation(server.ConnectionString);
708 
709                     Console.WriteLine(string.Format("Test: {0} SqlOperation Successful", methodName));
710 
711                     Assert.True(statsLogged);
712 
713                     diagnosticListenerObserver.Disable();
714 
715                     Console.WriteLine(string.Format("Test: {0} Listeners Disabled", methodName));
716                 }
717                 Console.WriteLine(string.Format("Test: {0} Server Disposed", methodName));
718             }
719             Console.WriteLine(string.Format("Test: {0} Listeners Disposed Successfully", methodName));
720         }
721 
CollectStatisticsDiagnosticsAsync(Func<string, Task> sqlOperation, [CallerMemberName] string methodName = R)722         private static async Task CollectStatisticsDiagnosticsAsync(Func<string, Task> sqlOperation, [CallerMemberName] string methodName = "")
723         {
724             bool statsLogged = false;
725             bool operationHasError = false;
726             Guid beginOperationId = Guid.Empty;
727 
728             FakeDiagnosticListenerObserver diagnosticListenerObserver = new FakeDiagnosticListenerObserver(kvp =>
729             {
730                 IDictionary statistics;
731 
732                 if (kvp.Key.Equals("System.Data.SqlClient.WriteCommandBefore"))
733                 {
734                     Assert.NotNull(kvp.Value);
735 
736                     Guid retrievedOperationId = GetPropertyValueFromType<Guid>(kvp.Value, "OperationId");
737                     Assert.NotEqual(retrievedOperationId, Guid.Empty);
738 
739                     SqlCommand sqlCommand = GetPropertyValueFromType<SqlCommand>(kvp.Value, "Command");
740                     Assert.NotNull(sqlCommand);
741 
742                     string operation = GetPropertyValueFromType<string>(kvp.Value, "Operation");
743                     Assert.False(string.IsNullOrWhiteSpace(operation));
744 
745                     beginOperationId = retrievedOperationId;
746 
747                     statsLogged = true;
748                 }
749                 else if (kvp.Key.Equals("System.Data.SqlClient.WriteCommandAfter"))
750                 {
751                     Assert.NotNull(kvp.Value);
752 
753                     Guid retrievedOperationId = GetPropertyValueFromType<Guid>(kvp.Value, "OperationId");
754                     Assert.NotEqual(retrievedOperationId, Guid.Empty);
755 
756                     SqlCommand sqlCommand = GetPropertyValueFromType<SqlCommand>(kvp.Value, "Command");
757                     Assert.NotNull(sqlCommand);
758 
759                     statistics = GetPropertyValueFromType<IDictionary>(kvp.Value, "Statistics");
760                     if (!operationHasError)
761                         Assert.NotNull(statistics);
762 
763                     string operation = GetPropertyValueFromType<string>(kvp.Value, "Operation");
764                     Assert.False(string.IsNullOrWhiteSpace(operation));
765 
766                     // if we get to this point, then statistics exist and this must be the "end"
767                     // event, so we need to make sure the operation IDs match
768                     Assert.Equal(retrievedOperationId, beginOperationId);
769                     beginOperationId = Guid.Empty;
770 
771                     statsLogged = true;
772                 }
773                 else if (kvp.Key.Equals("System.Data.SqlClient.WriteCommandError"))
774                 {
775                     operationHasError = true;
776                     Assert.NotNull(kvp.Value);
777 
778                     SqlCommand sqlCommand = GetPropertyValueFromType<SqlCommand>(kvp.Value, "Command");
779                     Assert.NotNull(sqlCommand);
780 
781                     Exception ex = GetPropertyValueFromType<Exception>(kvp.Value, "Exception");
782                     Assert.NotNull(ex);
783 
784                     string operation = GetPropertyValueFromType<string>(kvp.Value, "Operation");
785                     Assert.False(string.IsNullOrWhiteSpace(operation));
786 
787                     statsLogged = true;
788                 }
789                 else if (kvp.Key.Equals("System.Data.SqlClient.WriteConnectionOpenBefore"))
790                 {
791                     Assert.NotNull(kvp.Value);
792 
793                     SqlConnection sqlConnection = GetPropertyValueFromType<SqlConnection>(kvp.Value, "Connection");
794                     Assert.NotNull(sqlConnection);
795 
796                     string operation = GetPropertyValueFromType<string>(kvp.Value, "Operation");
797                     Assert.False(string.IsNullOrWhiteSpace(operation));
798 
799                     statsLogged = true;
800                 }
801                 else if (kvp.Key.Equals("System.Data.SqlClient.WriteConnectionOpenAfter"))
802                 {
803                     Assert.NotNull(kvp.Value);
804 
805                     SqlConnection sqlConnection = GetPropertyValueFromType<SqlConnection>(kvp.Value, "Connection");
806                     Assert.NotNull(sqlConnection);
807 
808                     string operation = GetPropertyValueFromType<string>(kvp.Value, "Operation");
809                     Assert.False(string.IsNullOrWhiteSpace(operation));
810 
811                     statistics = GetPropertyValueFromType<IDictionary>(kvp.Value, "Statistics");
812                     Assert.NotNull(statistics);
813 
814                     Guid connectionId = GetPropertyValueFromType<Guid>(kvp.Value, "ConnectionId");
815                     if (sqlConnection.State == ConnectionState.Open)
816                     {
817                         Assert.NotEqual(connectionId, Guid.Empty);
818                     }
819 
820                     statsLogged = true;
821                 }
822                 else if (kvp.Key.Equals("System.Data.SqlClient.WriteConnectionOpenError"))
823                 {
824                     Assert.NotNull(kvp.Value);
825 
826                     SqlConnection sqlConnection = GetPropertyValueFromType<SqlConnection>(kvp.Value, "Connection");
827                     Assert.NotNull(sqlConnection);
828 
829                     string operation = GetPropertyValueFromType<string>(kvp.Value, "Operation");
830                     Assert.False(string.IsNullOrWhiteSpace(operation));
831 
832                     Exception ex = GetPropertyValueFromType<Exception>(kvp.Value, "Exception");
833                     Assert.NotNull(ex);
834 
835                     statsLogged = true;
836                 }
837                 else if (kvp.Key.Equals("System.Data.SqlClient.WriteConnectionCloseBefore"))
838                 {
839                     Assert.NotNull(kvp.Value);
840 
841                     SqlConnection sqlConnection = GetPropertyValueFromType<SqlConnection>(kvp.Value, "Connection");
842                     Assert.NotNull(sqlConnection);
843 
844                     string operation = GetPropertyValueFromType<string>(kvp.Value, "Operation");
845                     Assert.False(string.IsNullOrWhiteSpace(operation));
846 
847                     Guid connectionId = GetPropertyValueFromType<Guid>(kvp.Value, "ConnectionId");
848                     Assert.NotEqual(connectionId, Guid.Empty);
849 
850                     statsLogged = true;
851                 }
852                 else if (kvp.Key.Equals("System.Data.SqlClient.WriteConnectionCloseAfter"))
853                 {
854                     Assert.NotNull(kvp.Value);
855 
856                     SqlConnection sqlConnection = GetPropertyValueFromType<SqlConnection>(kvp.Value, "Connection");
857                     Assert.NotNull(sqlConnection);
858 
859                     string operation = GetPropertyValueFromType<string>(kvp.Value, "Operation");
860                     Assert.False(string.IsNullOrWhiteSpace(operation));
861 
862                     statistics = GetPropertyValueFromType<IDictionary>(kvp.Value, "Statistics");
863 
864                     Guid connectionId = GetPropertyValueFromType<Guid>(kvp.Value, "ConnectionId");
865                     Assert.NotEqual(connectionId, Guid.Empty);
866 
867                     statsLogged = true;
868                 }
869                 else if (kvp.Key.Equals("System.Data.SqlClient.WriteConnectionCloseError"))
870                 {
871                     Assert.NotNull(kvp.Value);
872 
873                     SqlConnection sqlConnection = GetPropertyValueFromType<SqlConnection>(kvp.Value, "Connection");
874                     Assert.NotNull(sqlConnection);
875 
876                     string operation = GetPropertyValueFromType<string>(kvp.Value, "Operation");
877                     Assert.False(string.IsNullOrWhiteSpace(operation));
878 
879                     Exception ex = GetPropertyValueFromType<Exception>(kvp.Value, "Exception");
880                     Assert.NotNull(ex);
881 
882                     Guid connectionId = GetPropertyValueFromType<Guid>(kvp.Value, "ConnectionId");
883                     Assert.NotEqual(connectionId, Guid.Empty);
884 
885                     statsLogged = true;
886                 }
887             });
888 
889             diagnosticListenerObserver.Enable();
890             using (DiagnosticListener.AllListeners.Subscribe(diagnosticListenerObserver))
891             {
892                 Console.WriteLine(string.Format("Test: {0} Enabled Listeners", methodName));
893                 using (var server = TestTdsServer.StartServerWithQueryEngine(new DiagnosticsQueryEngine(), methodName: methodName))
894                 {
895                     Console.WriteLine(string.Format("Test: {0} Started Server", methodName));
896 
897                     await sqlOperation(server.ConnectionString);
898 
899                     Console.WriteLine(string.Format("Test: {0} SqlOperation Successful", methodName));
900 
901                     Assert.True(statsLogged);
902 
903                     diagnosticListenerObserver.Disable();
904 
905                     Console.WriteLine(string.Format("Test: {0} Listeners Disabled", methodName));
906                 }
907                 Console.WriteLine(string.Format("Test: {0} Server Disposed", methodName));
908             }
909             Console.WriteLine(string.Format("Test: {0} Listeners Disposed Successfully", methodName));
910         }
911 
GetPropertyValueFromType(object obj, string propName)912         private static T GetPropertyValueFromType<T>(object obj, string propName)
913         {
914             Type type = obj.GetType();
915             PropertyInfo pi = type.GetRuntimeProperty(propName);
916 
917             var propertyValue = pi.GetValue(obj);
918             return (T)propertyValue;
919         }
920     }
921 
922     public class DiagnosticsQueryEngine : QueryEngine
923     {
DiagnosticsQueryEngine()924         public DiagnosticsQueryEngine() : base(new TDSServerArguments())
925         {
926         }
927 
CreateQueryResponse(ITDSServerSession session, TDSSQLBatchToken batchRequest)928         protected override TDSMessageCollection CreateQueryResponse(ITDSServerSession session, TDSSQLBatchToken batchRequest)
929         {
930             string lowerBatchText = batchRequest.Text.ToLowerInvariant();
931 
932             if (lowerBatchText.Contains("1 / 0")) // SELECT 1/0
933             {
934                 TDSErrorToken errorToken = new TDSErrorToken(8134, 1, 16, "Divide by zero error encountered.");
935                 TDSDoneToken doneToken = new TDSDoneToken(TDSDoneTokenStatusType.Final | TDSDoneTokenStatusType.Count, TDSDoneTokenCommandType.Select, 1);
936                 TDSMessage responseMessage = new TDSMessage(TDSMessageType.Response, errorToken, doneToken);
937                 return new TDSMessageCollection(responseMessage);
938             }
939             else
940             {
941                 return base.CreateQueryResponse(session, batchRequest);
942             }
943         }
944     }
945 }
946