diff --git a/docs/Releases.md b/docs/Releases.md index 0bf08430..065f8032 100644 --- a/docs/Releases.md +++ b/docs/Releases.md @@ -6,7 +6,9 @@ layout: "default" This page tracks major changes included in any update starting with version 4.0.0.3 #### Unreleased -No pending unreleased changes. +- **Fixes/Changes**: + - Fixes MiniProfiler.Current.RenderIncludes generating invalid HTML ([#681](https://github.com/MiniProfiler/dotnet/pull/681) - thanks [rikbosch](https://github.com/dazbradbury)) + - Fixed [#671](https://github.com/MiniProfiler/dotnet/issues/671): SQL profiling network Time for some configurations ([#686](https://github.com/MiniProfiler/dotnet/pull/686) - thanks [dazbradbury](https://github.com/dazbradbury)) #### Version 4.5.4 - **New**: diff --git a/src/MiniProfiler.EntityFrameworkCore/RelationalDiagnosticListener.cs b/src/MiniProfiler.EntityFrameworkCore/RelationalDiagnosticListener.cs index 7f8724ee..0730f2dc 100644 --- a/src/MiniProfiler.EntityFrameworkCore/RelationalDiagnosticListener.cs +++ b/src/MiniProfiler.EntityFrameworkCore/RelationalDiagnosticListener.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; +using System.Data.Common; using System.Diagnostics; namespace StackExchange.Profiling.Data @@ -63,7 +64,7 @@ public void OnNext(KeyValuePair kv) if (val is CommandExecutedEventData data && _commands.TryRemove(data.CommandId, out var current)) { // A completion for a DataReader only means we *started* getting data back, not finished. - if (data.Result is RelationalDataReader or SqlDataReader) + if (data.Result is RelationalDataReader or DbDataReader) { _readers[data.CommandId] = current; current.FirstFetchCompleted(); diff --git a/src/MiniProfiler.Shared/SqlFormatters/InlineFormatter.cs b/src/MiniProfiler.Shared/SqlFormatters/InlineFormatter.cs index f2fc4635..2574252a 100644 --- a/src/MiniProfiler.Shared/SqlFormatters/InlineFormatter.cs +++ b/src/MiniProfiler.Shared/SqlFormatters/InlineFormatter.cs @@ -1,4 +1,6 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; +using System.Data; using System.Linq; using System.Text.RegularExpressions; @@ -9,9 +11,24 @@ namespace StackExchange.Profiling.SqlFormatters /// public class InlineFormatter : ISqlFormatter { - private static readonly Regex CommandSpacing = new Regex(@",([^\s])", RegexOptions.Compiled); + private static readonly Regex CommandSpacing = new(@",([^\s])", RegexOptions.Compiled); private static bool includeTypeInfo; + private static readonly HashSet QuotesDbTypes = new(StringComparer.OrdinalIgnoreCase) + { + nameof(DbType.AnsiString), + nameof(DbType.AnsiStringFixedLength), + nameof(DbType.Date), + nameof(DbType.DateTime), + nameof(DbType.DateTime2), + nameof(DbType.DateTimeOffset), + nameof(DbType.Guid), + nameof(DbType.String), + nameof(DbType.StringFixedLength), + nameof(DbType.Time), + nameof(DbType.Xml), + }; + /// /// Whether to modify the output query by adding spaces after commas. /// @@ -74,20 +91,18 @@ public string GetParameterValue(SqlTimingParameter param) if (result != null) { - switch (type.ToLower()) + if (QuotesDbTypes.Contains(type)) + { + result = string.Format("'{0}'", result); + } + else if (string.Equals(type, nameof(DbType.Boolean), StringComparison.OrdinalIgnoreCase)) { - case "string": - case "datetime": - result = string.Format("'{0}'", result); - break; - case "boolean": - result = result switch - { - "True" => "1", - "False" => "0", - _ => null, - }; - break; + result = result switch + { + "True" => "1", + "False" => "0", + _ => null, + }; } } diff --git a/tests/Directory.Build.props b/tests/Directory.Build.props index 010905a3..118a58c3 100644 --- a/tests/Directory.Build.props +++ b/tests/Directory.Build.props @@ -1,7 +1,7 @@ - Library + Exe bin\$(Configuration)\ false @@ -12,9 +12,9 @@ - - - + + + diff --git a/tests/MiniProfiler.Tests.AspNet/AspNetTest.cs b/tests/MiniProfiler.Tests.AspNet/AspNetTest.cs index eeb5c295..55230ca3 100644 --- a/tests/MiniProfiler.Tests.AspNet/AspNetTest.cs +++ b/tests/MiniProfiler.Tests.AspNet/AspNetTest.cs @@ -3,7 +3,6 @@ using System.Threading; using System.Threading.Tasks; using Xunit; -using Xunit.Abstractions; namespace StackExchange.Profiling.Tests { diff --git a/tests/MiniProfiler.Tests.AspNet/BasicTests.cs b/tests/MiniProfiler.Tests.AspNet/BasicTests.cs index f0100fd9..0b0de4bd 100644 --- a/tests/MiniProfiler.Tests.AspNet/BasicTests.cs +++ b/tests/MiniProfiler.Tests.AspNet/BasicTests.cs @@ -1,16 +1,14 @@ using Xunit; -using Xunit.Abstractions; namespace StackExchange.Profiling.Tests { [Collection(NonParallel)] - public class BasicTests : AspNetTest + public class BasicTests(ITestOutputHelper output) : AspNetTest(output) { - public BasicTests(ITestOutputHelper output) : base(output) { } - - [Fact(WindowsOnly = true)] + [Fact] public void Simple() { + Skip.IfNotWindows(); using (GetRequest("http://localhost/Test.aspx", startAndStopProfiler: false)) { var mp = Options.StartProfiler(); @@ -28,9 +26,10 @@ public void Simple() } } - [Fact(WindowsOnly = true)] + [Fact] public void StepIf_Basic() { + Skip.IfNotWindows(); using (GetRequest()) { var mp = Options.StartProfiler(); @@ -56,9 +55,10 @@ public void StepIf_Basic() } } - [Fact(WindowsOnly = true)] + [Fact] public void StepIf_IncludeChildren() { + Skip.IfNotWindows(); using (GetRequest()) { var mp = Options.StartProfiler(); @@ -100,9 +100,10 @@ public void StepIf_IncludeChildren() } } - [Fact(WindowsOnly = true)] + [Fact] public void CustomTimingIf_Basic() { + Skip.IfNotWindows(); using (GetRequest()) { var mp = Options.StartProfiler(); @@ -128,9 +129,10 @@ public void CustomTimingIf_Basic() } } - [Fact(WindowsOnly = true)] + [Fact] public void DiscardResults() { + Skip.IfNotWindows(); using (GetRequest(startAndStopProfiler: false)) { var mp = Options.StartProfiler(); @@ -143,9 +145,10 @@ public void DiscardResults() } } - [Fact(WindowsOnly = true)] + [Fact] public void GetProfiler_NoChildren() { + Skip.IfNotWindows(); // this won't create any child steps var mp = GetProfiler(); @@ -154,9 +157,10 @@ public void GetProfiler_NoChildren() Assert.False(mp.Root.HasChildren); } - [Fact(WindowsOnly = true)] + [Fact] public void GetProfiler_Children() { + Skip.IfNotWindows(); const int depth = 5; var mp = GetProfiler(childDepth: depth); @@ -174,9 +178,10 @@ public void GetProfiler_Children() Assert.Equal(depth, children); } - [Fact(WindowsOnly = true)] + [Fact] public void GetRequest_StartAndStopProfiler() { + Skip.IfNotWindows(); MiniProfiler? mp; using (GetRequest()) { diff --git a/tests/MiniProfiler.Tests.AspNet/EF6Tests.cs b/tests/MiniProfiler.Tests.AspNet/EF6Tests.cs index 786358ed..f11eeb10 100644 --- a/tests/MiniProfiler.Tests.AspNet/EF6Tests.cs +++ b/tests/MiniProfiler.Tests.AspNet/EF6Tests.cs @@ -5,7 +5,6 @@ using StackExchange.Profiling.Data; using StackExchange.Profiling.EntityFramework6; using Xunit; -using Xunit.Abstractions; namespace StackExchange.Profiling.Tests { @@ -16,9 +15,10 @@ public EF6Tests(ITestOutputHelper output) : base(output) MiniProfilerEF6.Initialize(); } - [Fact(WindowsOnly = true)] + [Fact] public void ServicesCheck() { + Skip.IfNotWindows(); const string providerKey = "System.Data.SQLite"; Assert.IsType(DbConfiguration.DependencyResolver.GetService(typeof(DbProviderServices), providerKey)); diff --git a/tests/MiniProfiler.Tests.AspNet/MiniProfileHandlerTests.cs b/tests/MiniProfiler.Tests.AspNet/MiniProfileHandlerTests.cs index 20b510b9..cbdb31a1 100644 --- a/tests/MiniProfiler.Tests.AspNet/MiniProfileHandlerTests.cs +++ b/tests/MiniProfiler.Tests.AspNet/MiniProfileHandlerTests.cs @@ -11,7 +11,7 @@ namespace StackExchange.Profiling.Tests { public class MiniProfilerHandlerTests { - [Theory(WindowsOnly = true)] + [Theory] [InlineData("BRILLANT", 404)] [InlineData("underscore.js", 404)] [InlineData("results-list", 200)] @@ -19,6 +19,7 @@ public class MiniProfilerHandlerTests [InlineData("includes.min.css", 200)] public void GivenContext_WhenAResourceIsRequested_ThenTheCorrectHttpStatusCodeIsReturned(string resourceName, int expectedHttpStatus) { + Skip.IfNotWindows(); var sut = new MiniProfilerHandler(new MiniProfilerOptions() { ResultsListAuthorize = null @@ -31,7 +32,7 @@ public void GivenContext_WhenAResourceIsRequested_ThenTheCorrectHttpStatusCodeIs private static readonly FieldInfo _cacheability = typeof(HttpCachePolicy).GetField("_cacheability", BindingFlags.Instance | BindingFlags.NonPublic); private static readonly FieldInfo _maxAge = typeof(HttpCachePolicy).GetField("_maxAge", BindingFlags.Instance | BindingFlags.NonPublic); - [Theory(WindowsOnly = true)] + [Theory] [InlineData("BRILLANT", (HttpCacheability)6, null)] [InlineData("underscore.js", (HttpCacheability)6, null)] [InlineData("results-list", (HttpCacheability)6, null)] @@ -39,6 +40,7 @@ public void GivenContext_WhenAResourceIsRequested_ThenTheCorrectHttpStatusCodeIs [InlineData("includes.min.css", HttpCacheability.Public, 2592000)] public void GivenContext_WhenAResourceIsRequested_ThenTheCorrectHttpCacheControlIsReturned(string resourceName, HttpCacheability expectedCacheability, int? expectedMaxAgeSeconds) { + Skip.IfNotWindows(); var sut = new MiniProfilerHandler(new MiniProfilerOptions() { ResultsListAuthorize = null @@ -52,11 +54,12 @@ public void GivenContext_WhenAResourceIsRequested_ThenTheCorrectHttpCacheControl } } - [Theory(WindowsOnly = true)] + [Theory] [InlineData(true, 200)] [InlineData(false, 401)] public void GivenContext_WhenIndexIsRequested_ThenTheCorrectHttpStatusCodeIsReturned(bool isRequestAuthorized, int expectedHttpStatus) { + Skip.IfNotWindows(); var sut = new MiniProfilerHandler(new MiniProfilerOptions() { ResultsListAuthorize = _ => isRequestAuthorized @@ -66,15 +69,16 @@ public void GivenContext_WhenIndexIsRequested_ThenTheCorrectHttpStatusCodeIsRetu Assert.Equal(expectedHttpStatus, res); } - [Theory(WindowsOnly = true)] + [Theory] [InlineData("gzip", typeof(GZipStream))] [InlineData("deflate", typeof(DeflateStream))] [InlineData("unknown", null)] [InlineData("", null)] public void GivenContext_WhenIndexIsRequested_ThenTheCorrectHttpStatusCodeIsReturnedType(string acceptEncoding, Type? expectedEncodingFilterType) - { - // Arrange - var sut = new MiniProfilerHandler(new MiniProfilerOptions()); + { + Skip.IfNotWindows(); + // Arrange + var sut = new MiniProfilerHandler(new MiniProfilerOptions()); // Act var res = GetRequestResponseEncoding(sut, "includes.min.js", acceptEncoding); diff --git a/tests/MiniProfiler.Tests.AspNet/Storage/MemoryCacheStorageTests.cs b/tests/MiniProfiler.Tests.AspNet/Storage/MemoryCacheStorageTests.cs index d5138f87..fba848bf 100644 --- a/tests/MiniProfiler.Tests.AspNet/Storage/MemoryCacheStorageTests.cs +++ b/tests/MiniProfiler.Tests.AspNet/Storage/MemoryCacheStorageTests.cs @@ -1,15 +1,11 @@ using System; using StackExchange.Profiling.Storage; using Xunit; -using Xunit.Abstractions; namespace StackExchange.Profiling.Tests.Storage { - public class MemoryCacheStorageTests : StorageBaseTest, IClassFixture + public class MemoryCacheStorageTests(MemoryCacheStorageFixture fixture, ITestOutputHelper output) : StorageBaseTest(fixture, output), IClassFixture { - public MemoryCacheStorageTests(MemoryCacheStorageFixture fixture, ITestOutputHelper output) : base(fixture, output) - { - } } public class MemoryCacheStorageFixture : StorageFixtureBase diff --git a/tests/MiniProfiler.Tests.AspNet/Storage/TestHttpRuntimeCacheStorage.cs b/tests/MiniProfiler.Tests.AspNet/Storage/TestHttpRuntimeCacheStorage.cs index 537525d7..923b93fd 100644 --- a/tests/MiniProfiler.Tests.AspNet/Storage/TestHttpRuntimeCacheStorage.cs +++ b/tests/MiniProfiler.Tests.AspNet/Storage/TestHttpRuntimeCacheStorage.cs @@ -18,9 +18,10 @@ public TestMemoryCacheStorage() }; } - [Fact(WindowsOnly = true)] + [Fact] public void TestWeCanSaveTheSameProfilerTwice() { + Skip.IfNotWindows(); var profiler = new MiniProfiler("/", Options) { Started = DateTime.UtcNow, Id = Guid.NewGuid() }; Options.Storage.Save(profiler); Options.Storage.Save(profiler); @@ -29,9 +30,10 @@ public void TestWeCanSaveTheSameProfilerTwice() Assert.Single(guids); } - [Fact(WindowsOnly = true)] + [Fact] public void TestRangeQueries() { + Skip.IfNotWindows(); var now = DateTime.UtcNow; var inASec = now.AddSeconds(1); var in2Secs = now.AddSeconds(2); diff --git a/tests/MiniProfiler.Tests.AspNet/WebRequestProfilerTests.cs b/tests/MiniProfiler.Tests.AspNet/WebRequestProfilerTests.cs index 1dceafd5..4bd48116 100644 --- a/tests/MiniProfiler.Tests.AspNet/WebRequestProfilerTests.cs +++ b/tests/MiniProfiler.Tests.AspNet/WebRequestProfilerTests.cs @@ -1,6 +1,6 @@ using System; +using System.Web; using Xunit; -using Xunit.Abstractions; namespace StackExchange.Profiling.Tests { @@ -16,13 +16,23 @@ public void Dispose() Options = null!; } - [Fact(WindowsOnly = true)] + [Fact] public void WebRequestEnsureName() { - using (var rq = GetRequest("http://localhost/Test.aspx", startAndStopProfiler: false)) + Skip.IfNotWindows(); + using (var rq = GetRequest("http://localhost/Test.aspx")) { + try + { + _ = HttpContext.Current.Request.Url; + } + catch (Exception ex) + { + Output.WriteLine("eating initial .config load exception: " + ex); + } var mp = new MiniProfiler(null, Options); mp.Increment(); // 1 ms + Output.WriteLine("Url: " + HttpContext.Current.Request.Url); mp.Stop(false); Assert.NotNull(mp); diff --git a/tests/MiniProfiler.Tests.AspNetCore/AspNetCoreTest.cs b/tests/MiniProfiler.Tests.AspNetCore/AspNetCoreTest.cs index e927562f..51e11db4 100644 --- a/tests/MiniProfiler.Tests.AspNetCore/AspNetCoreTest.cs +++ b/tests/MiniProfiler.Tests.AspNetCore/AspNetCoreTest.cs @@ -6,9 +6,9 @@ using Microsoft.AspNetCore.TestHost; using Microsoft.Extensions.Caching.Memory; using StackExchange.Profiling.Storage; -using Xunit.Abstractions; using Microsoft.AspNetCore.Builder; using System.Collections.Generic; +using Xunit; namespace StackExchange.Profiling.Tests { diff --git a/tests/MiniProfiler.Tests.AspNetCore/Middleware.cs b/tests/MiniProfiler.Tests.AspNetCore/Middleware.cs index 00641c17..65b06e98 100644 --- a/tests/MiniProfiler.Tests.AspNetCore/Middleware.cs +++ b/tests/MiniProfiler.Tests.AspNetCore/Middleware.cs @@ -8,15 +8,12 @@ using Microsoft.AspNetCore.TestHost; using Microsoft.Extensions.DependencyInjection; using Xunit; -using Xunit.Abstractions; namespace StackExchange.Profiling.Tests { [Collection(NonParallel)] - public class Middleware : AspNetCoreTest + public class Middleware(ITestOutputHelper output) : AspNetCoreTest(output) { - public Middleware(ITestOutputHelper output) : base(output) { } - private static TestServer GetTestServer(Action configOptions) { var builder = new WebHostBuilder() @@ -51,9 +48,9 @@ public async Task BasicProfiling() CurrentOptions = o; })) { - using (var response = await server.CreateClient().GetAsync("").ConfigureAwait(false)) + using (var response = await server.CreateClient().GetAsync("", TestContext.Current.CancellationToken)) { - var responseText = await response.Content.ReadAsStringAsync().ConfigureAwait(false); + var responseText = await response.Content.ReadAsStringAsync(TestContext.Current.CancellationToken); Assert.Contains("Heyyy", responseText); } @@ -104,22 +101,22 @@ public async Task StaticFileFetch() using (var server = new TestServer(builder)) { // Test CSS - using (var response = await server.CreateClient().GetAsync("/mini-profiler-resources/includes.min.css").ConfigureAwait(false)) + using (var response = await server.CreateClient().GetAsync("/mini-profiler-resources/includes.min.css", TestContext.Current.CancellationToken)) { Assert.Equal(TimeSpan.FromDays(30), response.Headers.CacheControl?.MaxAge); Assert.True(response.Headers.CacheControl?.Public); Assert.Equal("text/css", response.Content.Headers.ContentType?.MediaType); // Checking for wrapping/scoping class - Assert.StartsWith(":root", await response.Content.ReadAsStringAsync().ConfigureAwait(false)); + Assert.StartsWith(":root", await response.Content.ReadAsStringAsync(TestContext.Current.CancellationToken)); } // Test JS - using (var response = await server.CreateClient().GetAsync("/mini-profiler-resources/includes.min.js").ConfigureAwait(false)) + using (var response = await server.CreateClient().GetAsync("/mini-profiler-resources/includes.min.js", TestContext.Current.CancellationToken)) { Assert.Equal(TimeSpan.FromDays(30), response.Headers.CacheControl?.MaxAge); Assert.True(response.Headers.CacheControl?.Public); Assert.Equal("application/javascript", response.Content.Headers.ContentType?.MediaType); // Checking for license header - Assert.Contains("jQuery", await response.Content.ReadAsStringAsync().ConfigureAwait(false)); + Assert.Contains("jQuery", await response.Content.ReadAsStringAsync(TestContext.Current.CancellationToken)); } } } @@ -180,7 +177,7 @@ public async Task ResultsAuthorization( Output.WriteLine("Testing: " + name); var client = server.CreateClient(); string id; - using (var response = await client.GetAsync("")) + using (var response = await client.GetAsync("", TestContext.Current.CancellationToken)) { Assert.Equal(HttpStatusCode.OK, response.StatusCode); id = Assert.Single(response.Headers.GetValues("X-MiniProfiler-Ids")); @@ -190,28 +187,28 @@ public async Task ResultsAuthorization( Assert.NotNull(CurrentOptions); string Path(string path) => CurrentOptions.RouteBasePath + "/" + path; - using (var response = await client.GetAsync(Path("results-index"))) + using (var response = await client.GetAsync(Path("results-index"), TestContext.Current.CancellationToken)) { Output.WriteLine("Hitting: " + response.RequestMessage?.RequestUri); - Output.WriteLine(" Response: " + await response.Content.ReadAsStringAsync()); + Output.WriteLine(" Response: " + await response.Content.ReadAsStringAsync(TestContext.Current.CancellationToken)); Output.WriteLine(" Code: " + response.StatusCode); Output.WriteLine(" Expected Code: " + indexExpected); Assert.Equal(indexExpected, response.StatusCode); } - using (var response = await client.GetAsync(Path("results-list"))) + using (var response = await client.GetAsync(Path("results-list"), TestContext.Current.CancellationToken)) { Output.WriteLine("Hitting: " + response.RequestMessage?.RequestUri); - Output.WriteLine(" Response: " + await response.Content.ReadAsStringAsync()); + Output.WriteLine(" Response: " + await response.Content.ReadAsStringAsync(TestContext.Current.CancellationToken)); Output.WriteLine(" Code: " + response.StatusCode); Output.WriteLine(" Expected Code: " + listExpected); Assert.Equal(listExpected, response.StatusCode); } - using (var response = await client.GetAsync(Path("results?id=" + id))) + using (var response = await client.GetAsync(Path("results?id=" + id), TestContext.Current.CancellationToken)) { Output.WriteLine("Hitting: " + response.RequestMessage?.RequestUri); - Output.WriteLine(" Response: " + await response.Content.ReadAsStringAsync()); + Output.WriteLine(" Response: " + await response.Content.ReadAsStringAsync(TestContext.Current.CancellationToken)); Output.WriteLine(" Code: " + response.StatusCode); Output.WriteLine(" Expected Code: " + singleExpected); Assert.Equal(singleExpected, response.StatusCode); diff --git a/tests/MiniProfiler.Tests.AspNetCore/Storage/MemoryCacheStorageTests.cs b/tests/MiniProfiler.Tests.AspNetCore/Storage/MemoryCacheStorageTests.cs index f94bbd5d..18c5d6c0 100644 --- a/tests/MiniProfiler.Tests.AspNetCore/Storage/MemoryCacheStorageTests.cs +++ b/tests/MiniProfiler.Tests.AspNetCore/Storage/MemoryCacheStorageTests.cs @@ -2,15 +2,11 @@ using Microsoft.Extensions.Caching.Memory; using StackExchange.Profiling.Storage; using Xunit; -using Xunit.Abstractions; namespace StackExchange.Profiling.Tests.Storage { - public class MemoryCacheStorageTests : StorageBaseTest, IClassFixture + public class MemoryCacheStorageTests(MemoryCacheStorageFixture fixture, ITestOutputHelper output) : StorageBaseTest(fixture, output), IClassFixture { - public MemoryCacheStorageTests(MemoryCacheStorageFixture fixture, ITestOutputHelper output) : base(fixture, output) - { - } } public class MemoryCacheStorageFixture : StorageFixtureBase diff --git a/tests/MiniProfiler.Tests/Async/AsyncRealTimeTests.cs b/tests/MiniProfiler.Tests/Async/AsyncRealTimeTests.cs index 88502b0a..41e3f35a 100644 --- a/tests/MiniProfiler.Tests/Async/AsyncRealTimeTests.cs +++ b/tests/MiniProfiler.Tests/Async/AsyncRealTimeTests.cs @@ -4,7 +4,6 @@ using System.Threading.Tasks; using StackExchange.Profiling.Internal; using Xunit; -using Xunit.Abstractions; namespace StackExchange.Profiling.Tests.Async { @@ -17,9 +16,10 @@ public AsyncRealTimeTests(ITestOutputHelper output) : base(output) Options.StopwatchProvider = StopwatchWrapper.StartNew; } - [FactLongRunning] + [Fact] public async Task Step_WithParallelTasks_RealTime() { + Skip.IfNotLongRunning(); Thread.Sleep(1000); // calm down there stupid laptop var profiler = Options.StartProfiler("root"); Assert.NotNull(profiler); @@ -32,7 +32,7 @@ public async Task Step_WithParallelTasks_RealTime() timing31 = null; // Add 100ms to root - await Task.Delay(100).ConfigureAwait(false); + await Task.Delay(100, TestContext.Current.CancellationToken); // Start tasks in parallel var whenAllTask = Task.WhenAll( @@ -51,7 +51,7 @@ await Task.Run(async () => } }).ConfigureAwait(false); } - }), + }, TestContext.Current.CancellationToken), Task.Factory.StartNew(async () => { // timing20: 200 + 100 = 300 ms @@ -88,7 +88,7 @@ await Task.Run(async () => }, TaskCreationOptions.LongRunning).Unwrap() ); - await whenAllTask.ConfigureAwait(false); + await whenAllTask; profiler.Stop(); @@ -115,9 +115,10 @@ await Task.Run(async () => AssertNear(100, timing31.DurationMilliseconds, 50); } - [FactLongRunning] + [Fact] public void Step_WithParallelThreads_RealTime() { + Skip.IfNotLongRunning(); var profiler = Options.StartProfiler("root"); Assert.NotNull(profiler); diff --git a/tests/MiniProfiler.Tests/Async/AsyncTests.cs b/tests/MiniProfiler.Tests/Async/AsyncTests.cs index 0a9a9cdb..0417a327 100644 --- a/tests/MiniProfiler.Tests/Async/AsyncTests.cs +++ b/tests/MiniProfiler.Tests/Async/AsyncTests.cs @@ -4,7 +4,6 @@ using System.Threading; using System.Threading.Tasks; using Xunit; -using Xunit.Abstractions; namespace StackExchange.Profiling.Tests.Async { @@ -23,7 +22,7 @@ public async Task SimpleAsync() Assert.NotNull(profiler); // Add 100ms to root - await profiler.IncrementAsync(100).ConfigureAwait(false); + await profiler.IncrementAsync(100); // 100ms + 100ms = 200ms var step1 = Task.Run(async () => @@ -40,7 +39,7 @@ await Task.Run(async () => } }).ConfigureAwait(false); } - }); + }, TestContext.Current.CancellationToken); // 100ms var step2 = Task.Run(async () => @@ -49,10 +48,10 @@ await Task.Run(async () => { await profiler.IncrementAsync(100).ConfigureAwait(false); } - }); + }, TestContext.Current.CancellationToken); // Longest task is 200ms - await Task.WhenAll(step1, step2).ConfigureAwait(false); + await Task.WhenAll(step1, step2); profiler.Stop(); @@ -92,7 +91,7 @@ await Task.Run(() => } }).ConfigureAwait(false); } - }), + }, TestContext.Current.CancellationToken), Task.Factory.StartNew(async () => { // timing20: 2 + 1 = 2 ms diff --git a/tests/MiniProfiler.Tests/Helpers/BaseTest.cs b/tests/MiniProfiler.Tests/BaseTest.cs similarity index 99% rename from tests/MiniProfiler.Tests/Helpers/BaseTest.cs rename to tests/MiniProfiler.Tests/BaseTest.cs index 7df127b8..4ff49644 100644 --- a/tests/MiniProfiler.Tests/Helpers/BaseTest.cs +++ b/tests/MiniProfiler.Tests/BaseTest.cs @@ -8,7 +8,6 @@ using StackExchange.Profiling.Internal; using StackExchange.Profiling.Tests.Helpers; using Xunit; -using Xunit.Abstractions; namespace StackExchange.Profiling.Tests { diff --git a/tests/MiniProfiler.Tests/DbProfilerTests.cs b/tests/MiniProfiler.Tests/DbProfilerTests.cs index 44fcbb02..7325434c 100644 --- a/tests/MiniProfiler.Tests/DbProfilerTests.cs +++ b/tests/MiniProfiler.Tests/DbProfilerTests.cs @@ -7,21 +7,15 @@ using StackExchange.Profiling.Data; using StackExchange.Profiling.Tests.Helpers; using Xunit; -using Xunit.Abstractions; namespace StackExchange.Profiling.Tests { /// /// Tests for . /// - public class DbProfilerTests : BaseTest, IClassFixture + public class DbProfilerTests(SqliteFixture fixture, ITestOutputHelper output) : BaseTest(output), IClassFixture { - public SqliteFixture Fixture; - - public DbProfilerTests(SqliteFixture fixture, ITestOutputHelper output) : base(output) - { - Fixture = fixture; - } + public SqliteFixture Fixture = fixture; [Fact] public void NonQuery() @@ -52,12 +46,12 @@ public async Task NonQueryAsync() var profiler = conn.CountingProfiler; conn.Execute("CREATE TABLE TestTable (Id int null)"); - await conn.ExecuteAsync("INSERT INTO TestTable VALUES (1)").ConfigureAwait(false); + await conn.ExecuteAsync("INSERT INTO TestTable VALUES (1)"); Assert.Equal(2, profiler.ExecuteStartCount); Assert.Equal(2, profiler.ExecuteFinishCount); Assert.True(profiler.CompleteStatementMeasured); - await conn.ExecuteAsync("DELETE FROM TestTable WHERE Id = 1").ConfigureAwait(false); + await conn.ExecuteAsync("DELETE FROM TestTable WHERE Id = 1"); Assert.Equal(3, profiler.ExecuteStartCount); Assert.Equal(3, profiler.ExecuteFinishCount); Assert.True(profiler.CompleteStatementMeasured); @@ -90,7 +84,7 @@ public async Task ScalarAsync() var profiler = conn.CountingProfiler; cmd.CommandText = "select 1"; - await cmd.ExecuteScalarAsync().ConfigureAwait(false); + await cmd.ExecuteScalarAsync(TestContext.Current.CancellationToken); Assert.Equal(1, profiler.ExecuteStartCount); Assert.Equal(1, profiler.ExecuteFinishCount); @@ -130,9 +124,9 @@ public async Task DataReaderAsync() cmd.CommandText = "select 1"; - using (var reader = await cmd.ExecuteReaderAsync().ConfigureAwait(false)) + using (var reader = await cmd.ExecuteReaderAsync(TestContext.Current.CancellationToken)) { - while (await reader.NextResultAsync().ConfigureAwait(false)) { } + while (await reader.NextResultAsync(TestContext.Current.CancellationToken)) { } } Assert.Equal(1, profiler.ExecuteStartCount); @@ -214,7 +208,7 @@ public async Task ErrorsAsync() try { - await conn.ExecuteAsync(BadSql).ConfigureAwait(false); + await conn.ExecuteAsync(BadSql); } catch (DbException) { /* yep */ } @@ -227,7 +221,7 @@ public async Task ErrorsAsync() try { - await conn.QueryAsync(BadSql).ConfigureAwait(false); + await conn.QueryAsync(BadSql); } catch (DbException) { /* yep */ } @@ -241,7 +235,7 @@ public async Task ErrorsAsync() using (var cmd = conn.CreateCommand()) { cmd.CommandText = BadSql; - await cmd.ExecuteScalarAsync().ConfigureAwait(false); + await cmd.ExecuteScalarAsync(TestContext.Current.CancellationToken); } } catch (DbException) { /* yep */ } @@ -296,7 +290,7 @@ public async Task TrackingOptionsAsync(bool track) Assert.NotNull(profiler); const string cmdString = "Select 1"; - await GetUnopenedConnection(profiler).QueryAsync(cmdString).ConfigureAwait(false); + await GetUnopenedConnection(profiler).QueryAsync(cmdString); CheckConnectionTracking(track, profiler, cmdString, true, true); } @@ -356,10 +350,9 @@ public void AlwaysWrapReaders() } } - private class CurrentDbProfiler : IDbProfiler + private class CurrentDbProfiler(Func getProfiler) : IDbProfiler { - private Func GetProfiler { get; } - public CurrentDbProfiler(Func getProfiler) => GetProfiler = getProfiler; + private Func GetProfiler { get; } = getProfiler; public bool IsActive => GetProfiler()?.IsActive ?? false; @@ -409,28 +402,21 @@ private CountingConnection GetConnection() return result; } - public class CountingConnection : ProfiledDbConnection + public class CountingConnection(DbConnection connection, IDbProfiler profiler) : ProfiledDbConnection(connection, profiler) { - public CountingDbProfiler CountingProfiler { get; set; } - - public CountingConnection(DbConnection connection, IDbProfiler profiler) - : base(connection, profiler) - { - CountingProfiler = (CountingDbProfiler)profiler; - } + public CountingDbProfiler CountingProfiler { get; set; } = (CountingDbProfiler)profiler; } - public class OverrideTestConnection : ProfiledDbConnection + public class OverrideTestConnection(DbConnection connection, IDbProfiler profiler) : ProfiledDbConnection(connection, profiler) { public bool AlwaysWrapReaders { get; set; } - public OverrideTestConnection(DbConnection connection, IDbProfiler profiler) : base(connection, profiler) { } + protected override DbCommand CreateDbCommand() => new OverrideTestCommand(WrappedConnection.CreateCommand(), this, Profiler); } - public class OverrideTestCommand : ProfiledDbCommand + public class OverrideTestCommand(DbCommand command, DbConnection connection, IDbProfiler? profiler) : ProfiledDbCommand(command, connection, profiler) { - protected override bool AlwaysWrapReaders => (Connection as OverrideTestConnection)?.AlwaysWrapReaders == true; - public OverrideTestCommand(DbCommand command, DbConnection connection, IDbProfiler? profiler) : base(command, connection, profiler) { } + protected override bool AlwaysWrapReaders => Connection is OverrideTestConnection { AlwaysWrapReaders: true }; } } diff --git a/tests/MiniProfiler.Tests/Helpers/Attributes.cs b/tests/MiniProfiler.Tests/Helpers/Attributes.cs deleted file mode 100644 index bbb514c3..00000000 --- a/tests/MiniProfiler.Tests/Helpers/Attributes.cs +++ /dev/null @@ -1,211 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Runtime.InteropServices; -using System.Threading; -using System.Threading.Tasks; -using Xunit.Abstractions; -using Xunit.Sdk; - -namespace StackExchange.Profiling.Tests -{ - /// - /// Override for that truncates our DisplayName down. - /// - /// Attribute that is applied to a method to indicate that it is a fact that should - /// be run by the test runner. It can also be extended to support a customized definition - /// of a test method. - /// - /// - [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] - [XunitTestCaseDiscoverer("StackExchange.Profiling.Tests.FactDiscoverer", "MiniProfiler.Tests")] - public class FactAttribute : Xunit.FactAttribute - { - private bool _windowsOnly; - - public bool WindowsOnly - { - get => _windowsOnly; - set - { - _windowsOnly = value; - if (_windowsOnly && !RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - Skip = "Only runnable on Windows"; - } - } - } - } - - [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] - public class FactLongRunningAttribute : FactAttribute - { - public override string Skip - { - get => TestConfig.Current.RunLongRunning ? base.Skip : "Config.RunLongRunning is false - skipping long test."; - set => base.Skip = value; - } - } - - /// - /// Override for that truncates our DisplayName down. - /// - /// Marks a test method as being a data theory. Data theories are tests which are - /// fed various bits of data from a data source, mapping to parameters on the test - /// method. If the data source contains multiple rows, then the test method is executed - /// multiple times (once with each data row). Data is provided by attributes which - /// derive from Xunit.Sdk.DataAttribute (notably, Xunit.InlineDataAttribute and Xunit.MemberDataAttribute). - /// - /// - [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] - [XunitTestCaseDiscoverer("StackExchange.Profiling.Tests.TheoryDiscoverer", "MiniProfiler.Tests")] - public class TheoryAttribute : Xunit.TheoryAttribute - { - private bool _windowsOnly; - - public bool WindowsOnly - { - get => _windowsOnly; - set - { - _windowsOnly = value; - if (_windowsOnly && !RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - Skip = "Only runnable on Windows"; - } - } - } - } - - public class FactDiscoverer : Xunit.Sdk.FactDiscoverer - { - public FactDiscoverer(IMessageSink diagnosticMessageSink) : base(diagnosticMessageSink) { } - - protected override IXunitTestCase CreateTestCase(ITestFrameworkDiscoveryOptions discoveryOptions, ITestMethod testMethod, IAttributeInfo factAttribute) - => new SkippableTestCase(DiagnosticMessageSink, discoveryOptions.MethodDisplayOrDefault(), discoveryOptions.MethodDisplayOptionsOrDefault(), testMethod); - } - - public class TheoryDiscoverer : Xunit.Sdk.TheoryDiscoverer - { - public TheoryDiscoverer(IMessageSink diagnosticMessageSink) : base(diagnosticMessageSink) { } - - protected override IEnumerable CreateTestCasesForDataRow(ITestFrameworkDiscoveryOptions discoveryOptions, ITestMethod testMethod, IAttributeInfo theoryAttribute, object[] dataRow) - => new[] { new SkippableTestCase(DiagnosticMessageSink, discoveryOptions.MethodDisplayOrDefault(), discoveryOptions.MethodDisplayOptionsOrDefault(), testMethod, dataRow) }; - - protected override IEnumerable CreateTestCasesForSkip(ITestFrameworkDiscoveryOptions discoveryOptions, ITestMethod testMethod, IAttributeInfo theoryAttribute, string skipReason) - => new[] { new SkippableTestCase(DiagnosticMessageSink, discoveryOptions.MethodDisplayOrDefault(), discoveryOptions.MethodDisplayOptionsOrDefault(), testMethod) }; - - protected override IEnumerable CreateTestCasesForTheory(ITestFrameworkDiscoveryOptions discoveryOptions, ITestMethod testMethod, IAttributeInfo theoryAttribute) - => new[] { new SkippableTheoryTestCase(DiagnosticMessageSink, discoveryOptions.MethodDisplayOrDefault(), discoveryOptions.MethodDisplayOptionsOrDefault(), testMethod) }; - - protected override IEnumerable CreateTestCasesForSkippedDataRow(ITestFrameworkDiscoveryOptions discoveryOptions, ITestMethod testMethod, IAttributeInfo theoryAttribute, object[] dataRow, string skipReason) - => new[] { new NamedSkippedDataRowTestCase(DiagnosticMessageSink, discoveryOptions.MethodDisplayOrDefault(), discoveryOptions.MethodDisplayOptionsOrDefault(), testMethod, skipReason, dataRow) }; - } - - public class SkippableTestCase : XunitTestCase - { - protected override string GetDisplayName(IAttributeInfo factAttribute, string displayName) => - base.GetDisplayName(factAttribute, displayName).StripName(); - - [Obsolete("Called by the de-serializer; should only be called by deriving classes for de-serialization purposes")] - public SkippableTestCase() { } - - public SkippableTestCase(IMessageSink diagnosticMessageSink, TestMethodDisplay defaultMethodDisplay, TestMethodDisplayOptions defaultMethodDisplayOptions, ITestMethod testMethod, object[]? testMethodArguments = null) - : base(diagnosticMessageSink, defaultMethodDisplay, defaultMethodDisplayOptions, testMethod, testMethodArguments) - { - } - - public override async Task RunAsync( - IMessageSink diagnosticMessageSink, - IMessageBus messageBus, - object[] constructorArguments, - ExceptionAggregator aggregator, - CancellationTokenSource cancellationTokenSource) - { - var skipMessageBus = new SkippableMessageBus(messageBus); - var result = await base.RunAsync(diagnosticMessageSink, skipMessageBus, constructorArguments, aggregator, cancellationTokenSource).ConfigureAwait(false); - return result.Update(skipMessageBus); - } - } - - public class SkippableTheoryTestCase : XunitTheoryTestCase - { - protected override string GetDisplayName(IAttributeInfo factAttribute, string displayName) => - base.GetDisplayName(factAttribute, displayName).StripName(); - - [Obsolete("Called by the de-serializer; should only be called by deriving classes for de-serialization purposes")] - public SkippableTheoryTestCase() { } - - public SkippableTheoryTestCase(IMessageSink diagnosticMessageSink, TestMethodDisplay defaultMethodDisplay, TestMethodDisplayOptions defaultMethodDisplayOptions, ITestMethod testMethod) - : base(diagnosticMessageSink, defaultMethodDisplay, defaultMethodDisplayOptions, testMethod) { } - - public override async Task RunAsync( - IMessageSink diagnosticMessageSink, - IMessageBus messageBus, - object[] constructorArguments, - ExceptionAggregator aggregator, - CancellationTokenSource cancellationTokenSource) - { - var skipMessageBus = new SkippableMessageBus(messageBus); - var result = await base.RunAsync(diagnosticMessageSink, skipMessageBus, constructorArguments, aggregator, cancellationTokenSource).ConfigureAwait(false); - return result.Update(skipMessageBus); - } - } - - public class NamedSkippedDataRowTestCase : XunitSkippedDataRowTestCase - { - protected override string GetDisplayName(IAttributeInfo factAttribute, string displayName) => - base.GetDisplayName(factAttribute, displayName).StripName(); - - [Obsolete("Called by the de-serializer; should only be called by deriving classes for de-serialization purposes")] - public NamedSkippedDataRowTestCase() { } - - public NamedSkippedDataRowTestCase(IMessageSink diagnosticMessageSink, TestMethodDisplay defaultMethodDisplay, TestMethodDisplayOptions defaultMethodDisplayOptions, ITestMethod testMethod, string skipReason, object[]? testMethodArguments = null) - : base(diagnosticMessageSink, defaultMethodDisplay, defaultMethodDisplayOptions, testMethod, skipReason, testMethodArguments) { } - } - - public class SkippableMessageBus : IMessageBus - { - private readonly IMessageBus InnerBus; - public SkippableMessageBus(IMessageBus innerBus) => InnerBus = innerBus; - - public int DynamicallySkippedTestCount { get; private set; } - - public void Dispose() - { - InnerBus?.Dispose(); - GC.SuppressFinalize(this); - } - - public bool QueueMessage(IMessageSinkMessage message) - { - if (message is ITestFailed testFailed) - { - for (var i = 0; i < testFailed.ExceptionTypes.Length; i++) - { - if (testFailed.ExceptionTypes[i] == typeof(SkipTestException).FullName) - { - DynamicallySkippedTestCount++; - return InnerBus.QueueMessage(new TestSkipped(testFailed.Test, testFailed.Messages[i])); - } - } - } - return InnerBus.QueueMessage(message); - } - } - - internal static class XUnitExtensions - { - internal static string StripName(this string name) => - name.Replace("StackExchange.Profiling.Tests.", ""); - - public static RunSummary Update(this RunSummary summary, SkippableMessageBus bus) - { - if (bus.DynamicallySkippedTestCount > 0) - { - summary.Failed -= bus.DynamicallySkippedTestCount; - summary.Skipped += bus.DynamicallySkippedTestCount; - } - return summary; - } - } -} diff --git a/tests/MiniProfiler.Tests/Helpers/CountingDbProfiler.cs b/tests/MiniProfiler.Tests/Helpers/CountingDbProfiler.cs index c6ade878..45cd2d62 100644 --- a/tests/MiniProfiler.Tests/Helpers/CountingDbProfiler.cs +++ b/tests/MiniProfiler.Tests/Helpers/CountingDbProfiler.cs @@ -1,10 +1,9 @@ using System; using System.Data; using System.Diagnostics; - using StackExchange.Profiling.Data; -namespace StackExchange.Profiling.Tests +namespace StackExchange.Profiling.Tests.Helpers { /// /// The counting profiler. diff --git a/tests/MiniProfiler.Tests/Helpers/Skip.cs b/tests/MiniProfiler.Tests/Helpers/Skip.cs deleted file mode 100644 index ce35343b..00000000 --- a/tests/MiniProfiler.Tests/Helpers/Skip.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace StackExchange.Profiling.Tests -{ - public static class Skip - { - public static void Inconclusive(string message) => throw new SkipTestException(message); - - public static void IfNoConfig(string prop, string value) - { - if (string.IsNullOrEmpty(value)) - { - throw new SkipTestException($"Config.{prop} is not set, skipping test."); - } - } - - public static void IfNoConfig(string prop, List values) - { - if (values == null || values.Count == 0) - { - throw new SkipTestException($"Config.{prop} is not set, skipping test."); - } - } - } - -#pragma warning disable RCS1194 // Implement exception constructors. - public class SkipTestException : Exception - { - public string? MissingFeatures { get; set; } - - public SkipTestException(string reason) : base(reason) { } - } -#pragma warning restore RCS1194 // Implement exception constructors. -} diff --git a/tests/MiniProfiler.Tests/Helpers/TestConfig.cs b/tests/MiniProfiler.Tests/Helpers/TestConfig.cs index bb24896b..6069bb92 100644 --- a/tests/MiniProfiler.Tests/Helpers/TestConfig.cs +++ b/tests/MiniProfiler.Tests/Helpers/TestConfig.cs @@ -32,8 +32,8 @@ public class Config public bool RunLongRunning { get; set; } public bool EnableTestLogging { get; set; } = bool.TryParse(Environment.GetEnvironmentVariable(nameof(EnableTestLogging)), out var enableTestLogging) && enableTestLogging; - public string RedisConnectionString { get; set; } = Environment.GetEnvironmentVariable(nameof(RedisConnectionString)) ?? "localhost:6379"; - public string SQLServerConnectionString { get; set; } = Environment.GetEnvironmentVariable(nameof(SQLServerConnectionString)) ?? "Server=.;Database=tempdb;Trusted_Connection=True;"; + public string RedisConnectionString { get; set; } = Environment.GetEnvironmentVariable(nameof(RedisConnectionString)) ?? "localhost:7379"; + public string SQLServerConnectionString { get; set; } = Environment.GetEnvironmentVariable(nameof(SQLServerConnectionString)) ?? "Server=.;Database=tempdb;TrustServerCertificate=true;User Id=sa;Password=Strong!Passw0rd;"; public string SQLServerCeConnectionString { get; set; } = Environment.GetEnvironmentVariable(nameof(SQLServerCeConnectionString)) ?? "Data Source=TestData.sdf;"; public string MySQLConnectionString { get; set; } = Environment.GetEnvironmentVariable(nameof(MySQLConnectionString)) ?? "server=localhost;uid=root;pwd=root;database=test;Allow User Variables=True;SslMode=none;AllowPublicKeyRetrieval=True"; public string PostgreSqlConnectionString { get; set; } = Environment.GetEnvironmentVariable(nameof(PostgreSqlConnectionString)) ?? "Server=localhost;Port=5432;Database=test;User Id=postgres;Password=postgres;"; diff --git a/tests/MiniProfiler.Tests/InternalErrorTests.cs b/tests/MiniProfiler.Tests/InternalErrorTests.cs index 7fee4b09..bcd862d5 100644 --- a/tests/MiniProfiler.Tests/InternalErrorTests.cs +++ b/tests/MiniProfiler.Tests/InternalErrorTests.cs @@ -4,14 +4,11 @@ using StackExchange.Profiling.Internal; using StackExchange.Profiling.Storage; using Xunit; -using Xunit.Abstractions; namespace StackExchange.Profiling.Tests { - public class InternalErrorTests : BaseTest + public class InternalErrorTests(ITestOutputHelper output) : BaseTest(output) { - public InternalErrorTests(ITestOutputHelper output) : base(output) { } - [Fact] public async Task StopErrorLogging() { @@ -41,7 +38,7 @@ void Log(Exception ex) Assert.NotNull(profiler); AddRecursiveChildren(profiler, 1, 10); Assert.Equal(1, errorCount); - await profiler.StopAsync().ConfigureAwait(false); + await profiler.StopAsync(); Assert.Equal(2, errorCount); Assert.IsType(lastError); } diff --git a/tests/MiniProfiler.Tests/MiniProfiler.Tests.csproj b/tests/MiniProfiler.Tests/MiniProfiler.Tests.csproj index d3473d4f..d2f6c2ea 100644 --- a/tests/MiniProfiler.Tests/MiniProfiler.Tests.csproj +++ b/tests/MiniProfiler.Tests/MiniProfiler.Tests.csproj @@ -8,7 +8,7 @@ - + diff --git a/tests/MiniProfiler.Tests/ProtobufSerializationTests.cs b/tests/MiniProfiler.Tests/ProtobufSerializationTests.cs index c2d4259b..7582df7d 100644 --- a/tests/MiniProfiler.Tests/ProtobufSerializationTests.cs +++ b/tests/MiniProfiler.Tests/ProtobufSerializationTests.cs @@ -1,13 +1,10 @@ using System.IO; using Xunit; -using Xunit.Abstractions; namespace StackExchange.Profiling.Tests { - public class ProtobufSerializationTests : BaseTest + public class ProtobufSerializationTests(ITestOutputHelper output) : BaseTest(output) { - public ProtobufSerializationTests(ITestOutputHelper output) : base(output) { } - [Fact] public void Simple() { diff --git a/tests/MiniProfiler.Tests/RenderTests.cs b/tests/MiniProfiler.Tests/RenderTests.cs index a88eba0c..9c01a7f9 100644 --- a/tests/MiniProfiler.Tests/RenderTests.cs +++ b/tests/MiniProfiler.Tests/RenderTests.cs @@ -2,14 +2,11 @@ using System.Collections.Generic; using StackExchange.Profiling.Internal; using Xunit; -using Xunit.Abstractions; namespace StackExchange.Profiling.Tests { - public class RenderTests : BaseTest + public class RenderTests(ITestOutputHelper output) : BaseTest(output) { - public RenderTests(ITestOutputHelper output) : base(output) { } - [Fact] public void DefaultRender() { diff --git a/tests/MiniProfiler.Tests/SerializationTests.cs b/tests/MiniProfiler.Tests/SerializationTests.cs index ce5640a2..ff68eb4f 100644 --- a/tests/MiniProfiler.Tests/SerializationTests.cs +++ b/tests/MiniProfiler.Tests/SerializationTests.cs @@ -1,13 +1,10 @@ using StackExchange.Profiling.Internal; using Xunit; -using Xunit.Abstractions; namespace StackExchange.Profiling.Tests { - public class SerializationTests : BaseTest + public class SerializationTests(ITestOutputHelper output) : BaseTest(output) { - public SerializationTests(ITestOutputHelper output) : base(output) { } - [Fact] public void ParentMapping() { diff --git a/tests/MiniProfiler.Tests/ServerTimingTests.cs b/tests/MiniProfiler.Tests/ServerTimingTests.cs index cf21ce66..eb925ab8 100644 --- a/tests/MiniProfiler.Tests/ServerTimingTests.cs +++ b/tests/MiniProfiler.Tests/ServerTimingTests.cs @@ -1,13 +1,10 @@ using System; using Xunit; -using Xunit.Abstractions; namespace StackExchange.Profiling.Tests { - public class ServerTimingTests : BaseTest + public class ServerTimingTests(ITestOutputHelper output) : BaseTest(output) { - public ServerTimingTests(ITestOutputHelper output) : base(output) { } - [Fact] [Obsolete("Still awaiting browser support")] public void ServerTimingFormat() diff --git a/tests/MiniProfiler.Tests/Skip.cs b/tests/MiniProfiler.Tests/Skip.cs new file mode 100644 index 00000000..4c601ee2 --- /dev/null +++ b/tests/MiniProfiler.Tests/Skip.cs @@ -0,0 +1,35 @@ +using System; +using System.Runtime.InteropServices; +using Xunit; + +namespace StackExchange.Profiling.Tests +{ + public static class Skip + { + public static void Inconclusive(string message) => Assert.Skip(message); + + public static void IfNoConfig(string prop, string value) + { + if (string.IsNullOrEmpty(value)) + { + Assert.Skip($"Config.{prop} is not set, skipping test."); + } + } + + public static void IfNotWindows() + { + if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + Assert.Skip("Only runnable on Windows"); + } + } + + public static void IfNotLongRunning() + { + if (!TestConfig.Current.RunLongRunning) + { + Assert.Skip("Config.RunLongRunning is false - skipping long test."); + } + } + } +} diff --git a/tests/MiniProfiler.Tests/SqlFormatterTests.cs b/tests/MiniProfiler.Tests/SqlFormatterTests.cs index 9ef15a9b..417f4821 100644 --- a/tests/MiniProfiler.Tests/SqlFormatterTests.cs +++ b/tests/MiniProfiler.Tests/SqlFormatterTests.cs @@ -54,10 +54,10 @@ public class SqlFormatterTests private const string None = ""; private const string At = "@"; - public static IEnumerable GetParamPrefixes() + public static IEnumerable> GetParamPrefixes() { - yield return new object[] { None }; - yield return new object[] { At }; + yield return new(None); + yield return new(At); } private static SqlCommand CreateDbCommand(CommandType commandType, string text) @@ -100,6 +100,7 @@ public void InlineParameterNamesInParameterValues() var formatted = formatter.FormatSql(command, parameters); Assert.Equal("SELECT * FROM urls WHERE url = 'http://www.example.com?myid=1' OR myid = '1'", formatted); } + [Fact] public void InlineParameterValuesDisplayNullForStrings() { @@ -148,6 +149,79 @@ public void InlineSpacesAfterCommasDisabled() Assert.Equal("SELECT myid,url FROM urls WHERE url = 'http://www.example.com?myid=1' OR myid = '1'", formatted); } + [Fact] + public void InlineGuidsQuoted() + { + var formatter = new InlineFormatter() + { + InsertSpacesAfterCommas = false + }; + var guid = Guid.NewGuid(); + var parameters = new List + { + new SqlTimingParameter() { DbType = "guid", Name = "id", Value = guid.ToString() }, + }; + const string command = "SELECT 1 FROM urls WHERE id = @id"; + var formatted = formatter.FormatSql(command, parameters); + Assert.Equal($"SELECT 1 FROM urls WHERE id = '{guid}'", formatted); + } + + [Fact] + public void InlineDatesQuoted() + { + var formatter = new InlineFormatter() + { + InsertSpacesAfterCommas = false + }; + var dt = DateTime.UtcNow; + var parameters = new List + { + new SqlTimingParameter() { DbType = "datetime", Name = "start", Value = dt.ToString() }, + }; + const string command = "SELECT 1 FROM urls WHERE start > @start"; + var formatted = formatter.FormatSql(command, parameters); + Assert.Equal($"SELECT 1 FROM urls WHERE start > '{dt}'", formatted); + } + + [Theory] + [InlineData(DbType.Decimal, 0.01, "0.01")] + [InlineData(DbType.Boolean, true, "1")] + [InlineData(DbType.Boolean, false, "0")] + [InlineData(DbType.Byte, 255, "255")] + [InlineData(DbType.SByte, -128, "-128")] + [InlineData(DbType.Int16, -32768, "-32768")] + [InlineData(DbType.UInt16, 65535, "65535")] + [InlineData(DbType.Int32, 2147483647, "2147483647")] + [InlineData(DbType.UInt32, 4294967295, "4294967295")] + [InlineData(DbType.Int64, 9223372036854775807, "9223372036854775807")] + [InlineData(DbType.UInt64, 18446744073709551615, "18446744073709551615")] + [InlineData(DbType.Single, 3.14159, "3.14159")] + [InlineData(DbType.Double, 3.141592, "3.141592")] + [InlineData(DbType.Currency, 1234.56, "1234.56")] + [InlineData(DbType.String, "test string", "'test string'")] + [InlineData(DbType.AnsiString, "test ansi", "'test ansi'")] + [InlineData(DbType.StringFixedLength, "fixed", "'fixed'")] + [InlineData(DbType.AnsiStringFixedLength, "ansi fixed", "'ansi fixed'")] + [InlineData(DbType.Date, "2023-01-01", "'2023-01-01'")] + [InlineData(DbType.DateTime, "2023-01-01T12:00:00", "'2023-01-01T12:00:00'")] + [InlineData(DbType.DateTime2, "2023-01-01T12:00:00.0000000", "'2023-01-01T12:00:00.0000000'")] + [InlineData(DbType.DateTimeOffset, "2023-01-01T12:00:00+00:00", "'2023-01-01T12:00:00+00:00'")] + [InlineData(DbType.Guid, "12345678-1234-1234-1234-123456789012", "'12345678-1234-1234-1234-123456789012'")] + [InlineData(DbType.Time, "12:00:00", "'12:00:00'")] + [InlineData(DbType.Xml, "value", "'value'")] + [InlineData(DbType.Binary, "0x0123456789ABCDEF", "0x0123456789ABCDEF")] + public void InlineParameterFormatting(DbType dbType, object input, string expected) + { + var formatter = new InlineFormatter(); + var param = new SqlTimingParameter + { + DbType = dbType.ToString(), + Value = input.ToString(), + Direction = ParameterDirection.Input.ToString(), + }; + Assert.Equal(expected, formatter.GetParameterValue(param)); + } + [Fact] public void EnsureVerboseSqlServerFormatterOnlyAddsInformation() { diff --git a/tests/MiniProfiler.Tests/Storage/MongoDbStorageTests.cs b/tests/MiniProfiler.Tests/Storage/MongoDbStorageTests.cs index 56a6746a..94c6c150 100644 --- a/tests/MiniProfiler.Tests/Storage/MongoDbStorageTests.cs +++ b/tests/MiniProfiler.Tests/Storage/MongoDbStorageTests.cs @@ -1,16 +1,11 @@ using System; using MongoDB.Driver; using Xunit; -using Xunit.Abstractions; namespace StackExchange.Profiling.Tests.Storage { - public class MongoDbStorageTests : StorageBaseTest, IClassFixture + public class MongoDbStorageTests(MongoDbStorageFixture fixture, ITestOutputHelper output) : StorageBaseTest(fixture, output), IClassFixture { - public MongoDbStorageTests(MongoDbStorageFixture fixture, ITestOutputHelper output) : base(fixture, output) - { - } - [Fact] public void RecreationHandling() { diff --git a/tests/MiniProfiler.Tests/Storage/MySqlStorageTests.cs b/tests/MiniProfiler.Tests/Storage/MySqlStorageTests.cs index 68ee7049..0fa44ba5 100644 --- a/tests/MiniProfiler.Tests/Storage/MySqlStorageTests.cs +++ b/tests/MiniProfiler.Tests/Storage/MySqlStorageTests.cs @@ -1,15 +1,11 @@ using System; using StackExchange.Profiling.Storage; using Xunit; -using Xunit.Abstractions; namespace StackExchange.Profiling.Tests.Storage { - public class MySqlStorageTests : StorageBaseTest, IClassFixture + public class MySqlStorageTests(MySqlStorageFixture fixture, ITestOutputHelper output) : StorageBaseTest(fixture, output), IClassFixture { - public MySqlStorageTests(MySqlStorageFixture fixture, ITestOutputHelper output) : base(fixture, output) - { - } } public class MySqlStorageFixture : StorageFixtureBase, IDisposable diff --git a/tests/MiniProfiler.Tests/Storage/PostgreSqlStorageTests.cs b/tests/MiniProfiler.Tests/Storage/PostgreSqlStorageTests.cs index f8af1a6e..4712aeb5 100644 --- a/tests/MiniProfiler.Tests/Storage/PostgreSqlStorageTests.cs +++ b/tests/MiniProfiler.Tests/Storage/PostgreSqlStorageTests.cs @@ -1,16 +1,11 @@ -#if (NETCOREAPP2_0 || NET472) -using System; +using System; using StackExchange.Profiling.Storage; using Xunit; -using Xunit.Abstractions; namespace StackExchange.Profiling.Tests.Storage { - public class PostgreSqlStorageTests : StorageBaseTest, IClassFixture + public class PostgreSqlStorageTests(PostgreSqlStorageFixture fixture, ITestOutputHelper output) : StorageBaseTest(fixture, output), IClassFixture { - public PostgreSqlStorageTests(PostgreSqlStorageFixture fixture, ITestOutputHelper output) : base(fixture, output) - { - } } public class PostgreSqlStorageFixture : StorageFixtureBase, IDisposable @@ -45,4 +40,3 @@ protected override void Dispose(bool disposing) } } } -#endif diff --git a/tests/MiniProfiler.Tests/Storage/RedisStorageTests.cs b/tests/MiniProfiler.Tests/Storage/RedisStorageTests.cs index a490842f..e3dbba95 100644 --- a/tests/MiniProfiler.Tests/Storage/RedisStorageTests.cs +++ b/tests/MiniProfiler.Tests/Storage/RedisStorageTests.cs @@ -3,16 +3,11 @@ using StackExchange.Profiling.Storage.Internal; using StackExchange.Redis; using Xunit; -using Xunit.Abstractions; namespace StackExchange.Profiling.Tests.Storage { - public class RedisStorageTests : StorageBaseTest, IClassFixture + public class RedisStorageTests(RedisStorageFixture fixture, ITestOutputHelper output) : StorageBaseTest(fixture, output), IClassFixture { - public RedisStorageTests(RedisStorageFixture fixture, ITestOutputHelper output) : base(fixture, output) - { - } - [Fact] public void Serialization() { diff --git a/tests/MiniProfiler.Tests/Storage/SqlServerStorageTests.cs b/tests/MiniProfiler.Tests/Storage/SqlServerStorageTests.cs index c19c25b5..b846f254 100644 --- a/tests/MiniProfiler.Tests/Storage/SqlServerStorageTests.cs +++ b/tests/MiniProfiler.Tests/Storage/SqlServerStorageTests.cs @@ -1,15 +1,11 @@ using System; using StackExchange.Profiling.Storage; using Xunit; -using Xunit.Abstractions; namespace StackExchange.Profiling.Tests.Storage { - public class SqlServerStorageTests : StorageBaseTest, IClassFixture + public class SqlServerStorageTests(SqlServerStorageFixture fixture, ITestOutputHelper output) : StorageBaseTest(fixture, output), IClassFixture { - public SqlServerStorageTests(SqlServerStorageFixture fixture, ITestOutputHelper output) : base(fixture, output) - { - } } public class SqlServerStorageFixture : StorageFixtureBase, IDisposable diff --git a/tests/MiniProfiler.Tests/Storage/SqliteStorageTests.cs b/tests/MiniProfiler.Tests/Storage/SqliteStorageTests.cs index eb121700..8d9ecf28 100644 --- a/tests/MiniProfiler.Tests/Storage/SqliteStorageTests.cs +++ b/tests/MiniProfiler.Tests/Storage/SqliteStorageTests.cs @@ -2,15 +2,11 @@ using System.IO; using StackExchange.Profiling.Storage; using Xunit; -using Xunit.Abstractions; namespace StackExchange.Profiling.Tests.Storage { - public class SqliteStorageTests : StorageBaseTest, IClassFixture + public class SqliteStorageTests(SqliteStorageFixture fixture, ITestOutputHelper output) : StorageBaseTest(fixture, output), IClassFixture { - public SqliteStorageTests(SqliteStorageFixture fixture, ITestOutputHelper output) : base(fixture, output) - { - } } public class SqliteStorageFixture : StorageFixtureBase, IDisposable diff --git a/tests/MiniProfiler.Tests/Storage/StorageBaseTest.cs b/tests/MiniProfiler.Tests/Storage/StorageBaseTest.cs index 8d6b5568..daf091ba 100644 --- a/tests/MiniProfiler.Tests/Storage/StorageBaseTest.cs +++ b/tests/MiniProfiler.Tests/Storage/StorageBaseTest.cs @@ -5,7 +5,6 @@ using Dapper; using StackExchange.Profiling.Storage; using Xunit; -using Xunit.Abstractions; namespace StackExchange.Profiling.Tests.Storage { @@ -46,11 +45,11 @@ public async Task GetUnviewedIdsAsync() var mp1 = GetMiniProfiler("Test1"); var mp2 = GetMiniProfiler("Test2"); var mp3 = GetMiniProfiler("Test3"); - await Storage.SaveAsync(mp1).ConfigureAwait(false); - await Storage.SaveAsync(mp2).ConfigureAwait(false); - await Storage.SaveAsync(mp3).ConfigureAwait(false); + await Storage.SaveAsync(mp1); + await Storage.SaveAsync(mp2); + await Storage.SaveAsync(mp3); - var unviewed = await Storage.GetUnviewedIdsAsync(nameof(GetUnviewedIdsAsync)).ConfigureAwait(false); + var unviewed = await Storage.GetUnviewedIdsAsync(nameof(GetUnviewedIdsAsync)); Assert.Equal(3, unviewed.Count); Assert.Contains(mp1.Id, unviewed); Assert.Contains(mp2.Id, unviewed); @@ -84,7 +83,7 @@ public async Task ListAsync() Storage.Save(mp2); Storage.Save(mp3); - var stored = (await Storage.ListAsync(200).ConfigureAwait(false)).ToList(); + var stored = (await Storage.ListAsync(200)).ToList(); Assert.True(stored.Count >= 3); Assert.Contains(mp1.Id, stored); Assert.Contains(mp2.Id, stored); @@ -109,10 +108,10 @@ public void SaveAndLoad() public async Task SaveAndLoadAsync() { var mp = GetMiniProfiler(); - await Storage.SaveAsync(mp).ConfigureAwait(false); + await Storage.SaveAsync(mp); var timings = mp.GetTimingHierarchy(); - var fetched = await Storage.LoadAsync(mp.Id).ConfigureAwait(false); + var fetched = await Storage.LoadAsync(mp.Id); Assert.NotNull(fetched); Assert.Equal(mp, fetched); Assert.NotNull(fetched.Options); @@ -139,13 +138,13 @@ public async Task SetViewedAsync() { var mp = GetMiniProfiler(); Assert.False(mp.HasUserViewed); - await Storage.SaveAsync(mp).ConfigureAwait(false); + await Storage.SaveAsync(mp); Assert.False(mp.HasUserViewed); - var unviewedIds = await Storage.GetUnviewedIdsAsync(mp.User).ConfigureAwait(false); + var unviewedIds = await Storage.GetUnviewedIdsAsync(mp.User); Assert.Contains(mp.Id, unviewedIds); - await Storage.SetViewedAsync(mp).ConfigureAwait(false); - var unviewedIds2 = await Storage.GetUnviewedIdsAsync(mp.User).ConfigureAwait(false); + await Storage.SetViewedAsync(mp); + var unviewedIds2 = await Storage.GetUnviewedIdsAsync(mp.User); Assert.DoesNotContain(mp.Id, unviewedIds2); } @@ -171,17 +170,17 @@ public void SetUnviewed() public async Task SetUnviewedAsync() { var mp = GetMiniProfiler(); - await Storage.SaveAsync(mp).ConfigureAwait(false); + await Storage.SaveAsync(mp); - var unviewedIds = await Storage.GetUnviewedIdsAsync(mp.User).ConfigureAwait(false); + var unviewedIds = await Storage.GetUnviewedIdsAsync(mp.User); Assert.Contains(mp.Id, unviewedIds); - await Storage.SetViewedAsync(mp).ConfigureAwait(false); - var unviewedIds2 = await Storage.GetUnviewedIdsAsync(mp.User).ConfigureAwait(false); + await Storage.SetViewedAsync(mp); + var unviewedIds2 = await Storage.GetUnviewedIdsAsync(mp.User); Assert.DoesNotContain(mp.Id, unviewedIds2); - await Storage.SetUnviewedAsync(mp).ConfigureAwait(false); - var unviewedIds3 = await Storage.GetUnviewedIdsAsync(mp.User).ConfigureAwait(false); + await Storage.SetUnviewedAsync(mp); + var unviewedIds3 = await Storage.GetUnviewedIdsAsync(mp.User); Assert.Contains(mp.Id, unviewedIds3); } diff --git a/tests/MiniProfiler.Tests/TimingInstrumentationTest.cs b/tests/MiniProfiler.Tests/TimingInstrumentationTest.cs index f50a97ad..b1198285 100644 --- a/tests/MiniProfiler.Tests/TimingInstrumentationTest.cs +++ b/tests/MiniProfiler.Tests/TimingInstrumentationTest.cs @@ -1,20 +1,15 @@ using System; using Xunit; -using Xunit.Abstractions; namespace StackExchange.Profiling.Tests { - public class TimingInstrumentationTest : BaseTest + public class TimingInstrumentationTest(ITestOutputHelper output) : BaseTest(output) { - public TimingInstrumentationTest(ITestOutputHelper output) : base(output) { } - - private class TimingInstrumentation : IDisposable + private class TimingInstrumentation(Timing timing) : IDisposable { - public Timing Timing { get; set; } + public Timing Timing { get; set; } = timing; public bool Disposed { get; set; } public void Dispose() => Disposed = true; - - public TimingInstrumentation(Timing timing) => Timing = timing; } [Fact] diff --git a/tests/MiniProfiler.Tests/xunit.runner.json b/tests/MiniProfiler.Tests/xunit.runner.json index 5558408f..c0602c67 100644 --- a/tests/MiniProfiler.Tests/xunit.runner.json +++ b/tests/MiniProfiler.Tests/xunit.runner.json @@ -1 +1,8 @@ -{ "appDomain": "denied" } \ No newline at end of file +{ + "methodDisplay": "classAndMethod", + "parallelizeAssembly": true, + "maxParallelThreads": "2x", + "parallelizeTestCollections": true, + "diagnosticMessages": false, + "longRunningTestSeconds": 60 +} \ No newline at end of file diff --git a/tests/docker-compose.yml b/tests/docker-compose.yml index 952daf4a..84152037 100644 --- a/tests/docker-compose.yml +++ b/tests/docker-compose.yml @@ -1,12 +1,14 @@ -version: "3" services: mongo: - image: mongo:4 + image: mongo:8-noble container_name: mongo + restart: always ports: - 27017:27017 + environment: + MONGO_INITDB_DATABASE: test mysql: - image: mysql:8 + image: mysql:9 container_name: mysql ports: - 3306:3306 @@ -23,7 +25,16 @@ services: POSTGRES_PASSWORD: postgres POSTGRES_DB: test redis: - image: redis:6-alpine + image: redis:8-alpine container_name: redis ports: - - 6379:6379 \ No newline at end of file + - 7379:6379 + sqlserver: + image: mcr.microsoft.com/mssql/server:2025-latest + container_name: sqlserver + ports: + - 1433:1433 + environment: + ACCEPT_EULA: Y + MSSQL_SA_PASSWORD: Strong!Passw0rd + MSSQL_PID: Developer \ No newline at end of file