Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
fb07999
Long Queries banner says the session is per database on Azure SQL Dat…
erikdarlingdata Oct 2, 2026
2e98575
Tests for the long-query trace on Azure SQL Database, with the hooks …
erikdarlingdata Oct 2, 2026
821b82e
Long-query trace on Azure SQL Database follows the monitored database…
erikdarlingdata Oct 2, 2026
2850b6e
List the long-query trace's two database listings in the member-scan …
erikdarlingdata Oct 2, 2026
f4f1a3a
Long-query read skip: say what each app reads in a database with no s…
erikdarlingdata Oct 2, 2026
7cc63c1
Long-query banner on Azure SQL Database: a database monitored as its …
erikdarlingdata Oct 2, 2026
d1a8499
Long-query trace on Azure SQL Database: tests for drops that leave a …
erikdarlingdata Oct 2, 2026
464e314
Long-query trace on Azure SQL Database: a drop leaves a database wher…
erikdarlingdata Oct 2, 2026
824ea3d
Merge remote-tracking branch 'origin/dev' into fix/long-query-banner-…
erikdarlingdata Oct 2, 2026
7e5c34e
Long-query trace: tests for the hourly attempt after the cleanup's ca…
erikdarlingdata Oct 2, 2026
13dcb7a
Long-query trace: after the cleanup's cap, try again once an hour at …
erikdarlingdata Oct 2, 2026
e0c83f7
Long-query trace: the drop-sessions warning and README say Darling cr…
erikdarlingdata Oct 2, 2026
ad84ea9
Merge remote-tracking branch 'origin/dev' into fix/long-query-banner-…
erikdarlingdata Oct 2, 2026
f07fc9e
Long-query trace: tests for the hourly passes that throw, the edition…
erikdarlingdata Oct 2, 2026
cf5bd03
Long-query trace: a pass that throws waits an hour, Lite reads the ed…
erikdarlingdata Oct 2, 2026
5a92b8b
Merge remote-tracking branch 'origin/dev' into r4
erikdarlingdata Oct 2, 2026
5712458
--drop-xe-sessions: an excluded database it cannot open is a note and…
erikdarlingdata Oct 2, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions Darling/Darling.Tests/CollectorDatabaseScopeRungTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -473,9 +473,11 @@ public void BothProviders_SpliceTheScopeIntoTheDatabaseListPlan_AndTheMaintenanc
/// <summary>
/// The runner feeds ONE resolution per run to all three consumers — the dispatch probe, the
/// per-database loop's list plan, and the enumeration context — and the stated boundaries hold:
/// the worker's delegate reads the SAME live overrides the cadence gate reads; the Azure XE
/// session provisioner deliberately passes NO scope (session inventory is server-shaped — three
/// collectors with three possible scopes share those sessions); and Lite's definition runner
/// the worker's delegate reads the SAME live overrides the cadence gate reads; the always-on Azure XE
/// session provisioner deliberately passes NO scope (session inventory is server-shaped — one list
/// provisions the deadlock and blocked-process sessions, whose collectors may carry different scopes), while the opt-in long-query trace
/// follows its own collector's scope through the runner's accessor (its lifecycle covers what its read
/// covers); and Lite's definition runner
/// never names the scope at all, so every Lite query stays byte-identical to the unscoped form.
/// </summary>
[Fact]
Expand All @@ -494,7 +496,8 @@ public void TheRunnerResolvesOnceAndFeedsAllThreeConsumers_AndTheStatedBoundarie
worker, StringComparison.Ordinal);

var xe = RepoFile.ReadRepoFile("Darling", "PerformanceMonitor.Darling.Service", "DarlingXeSessions.cs");
Assert.Equal(2, CountOf(xe, "GetAzureDatabaseListAsync(server, databaseScope: null"));
Assert.Equal(1, CountOf(xe, "GetAzureDatabaseListAsync(server, databaseScope: null"));
Assert.Contains("runner.DatabaseScopeFor(LongQueryCompletionsCollector.Instance.Name, server.ServerId)", xe, StringComparison.Ordinal);

var lite = RepoFile.ReadRepoFile("Lite", "Services", "RemoteCollectorService.DefinitionRunner.cs");
Assert.DoesNotContain("DatabaseScope", lite, StringComparison.Ordinal);
Expand Down
258 changes: 255 additions & 3 deletions Darling/Darling.Tests/DropXeSessionsVerbTests.cs

Large diffs are not rendered by default.

99 changes: 99 additions & 0 deletions Darling/Darling.Tests/LongQueryBannerSessionScopeTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/*
* Copyright (c) 2026 Erik Darling, Darling Data LLC
*
* This file is part of the SQL Server Performance Monitor.
*
* Licensed under the MIT License. See LICENSE file in the project root for full license information.
*/

using System;
using System.IO;
using System.Runtime.CompilerServices;
using PerformanceMonitor.Collectors;
using PerformanceMonitor.Darling.Viewer;
using Xunit;

namespace Darling.Tests;

/// <summary>
/// The Viewer's Long Queries banner, shown while the long-query completion trace is off. Its last sentence says where
/// turning the trace on creates the Extended Events session. On Azure SQL Database the session is per monitored
/// database (<c>LongQueryCompletionsCollector.RunsPerDatabase</c>), so the banner says so for that target and keeps
/// the server wording for every other one. The README's sentence about the session says the same.
/// </summary>
public sealed class LongQueryBannerSessionScopeTests
{
[Fact]
public void TheViewerBanner_TakesItsTextFromTheTarget_NotFromAFixedServerSentence()
{
var banner = BannerElement(File.ReadAllText(RepoPath("Darling", "PerformanceMonitor.Darling.Viewer", "ViewerServerTab.xaml")));
Assert.DoesNotContain("session on this server", banner, StringComparison.Ordinal);

var load = MethodBody(RepoPath("Darling", "PerformanceMonitor.Darling.Viewer", "ViewerServerTab.LongQueries.cs"), "private async Task LoadLongQueriesAsync(");
Assert.Contains("LongQueriesDisabledWarning.Text = LongQueriesDisabledText(_server.EngineEdition)", load, StringComparison.Ordinal);
}

[Fact]
public void TheViewerBanner_OnAzureSqlDatabase_SaysEachMonitoredDatabase_NotThisServer()
{
var text = ViewerServerTab.LongQueriesDisabledText(CollectorEngineCapability.AzureSqlDatabaseEngineEdition);

Assert.EndsWith(
"Enabling it creates the Extended Events session in each monitored database. Disabling it drops the session from every database that has it, except where another registration of that database still has the trace on. A database that is also monitored as its own server follows that server's setting.",
text,
StringComparison.Ordinal);
Assert.DoesNotContain("session on this server", text, StringComparison.Ordinal);
Assert.Contains("Settings → Collection Schedule → Edit Collector Schedules…", text, StringComparison.Ordinal);
}

[Theory]
[InlineData(CollectorEngineCapability.UnknownEngineEdition)]
[InlineData(2)]
[InlineData(3)]
[InlineData(4)]
[InlineData(8)]
public void TheViewerBanner_OnEveryOtherEdition_KeepsTheServerSentence(int engineEdition)
{
var text = ViewerServerTab.LongQueriesDisabledText(engineEdition);

Assert.EndsWith(
"Enabling it creates the Extended Events session on this server. Disabling it drops the session.",
text,
StringComparison.Ordinal);
Assert.DoesNotContain("monitored database", text, StringComparison.Ordinal);
}

[Fact]
public void TheReadme_SaysTheSessionIsPerDatabase_OnAzureSqlDatabase()
{
var readme = File.ReadAllText(RepoPath("Darling", "README.md"));

Assert.Contains(
"On Azure SQL Database the session is database-scoped. Enabling it creates the session in each monitored database and drops it from any other database that has it. Disabling it drops the session from every database that has it. Neither drop removes the session from a database where another registration of that database still has the trace on. A database that is also monitored as its own server is left to that registration.",
readme,
StringComparison.Ordinal);
}

/// <summary>The banner TextBlock's own markup, from its name to the end of its start tag.</summary>
internal static string BannerElement(string xaml)
{
var name = xaml.IndexOf("x:Name=\"LongQueriesDisabledWarning\"", StringComparison.Ordinal);
Assert.True(name >= 0, "the Long Queries tab has its disabled-trace banner");
var end = xaml.IndexOf("/>", name, StringComparison.Ordinal);
Assert.True(end > name, "the banner TextBlock is self-closing");
return xaml[name..end];
}

private static string MethodBody(string path, string signature)
{
var source = CSharpSourceWalker.StripCommentsAndStrings(File.ReadAllText(path));
var at = source.IndexOf(signature, StringComparison.Ordinal);
Assert.True(at >= 0, $"{signature} must exist");
return CSharpSourceWalker.BraceBalanced(source, source.IndexOf('{', at));
}

private static string RepoPath(params string[] parts) => Path.Combine([RepoRoot(), .. parts]);

private static string RepoRoot([CallerFilePath] string thisFile = "")
=> Path.GetFullPath(Path.Combine(Path.GetDirectoryName(thisFile)!, "..", ".."));
}
Loading
Loading