Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
9161bec
Scaffold empty Runtime.TestUtils and UI.TestUtils class libraries
jschick04 May 21, 2026
a0948b5
Add characterization tests for ProviderDatabase types and ProviderDet…
jschick04 May 21, 2026
64477da
Scaffold empty EventLogExpert.ProviderDatabase library and TestUtils …
jschick04 May 21, 2026
2293a1c
Introduce IProviderDetailsLookup abstraction to decouple EventResolve…
jschick04 May 21, 2026
d7e067b
Move ProviderDatabase EF Core files to EventLogExpert.ProviderDatabas…
jschick04 May 21, 2026
3bc5e78
Rename moved ProviderDatabase files to EventLogExpert.ProviderDatabas…
jschick04 May 21, 2026
9203781
Pass ProviderDbContextFactory to EventResolver in integration tests t…
jschick04 May 21, 2026
1724c07
Add cached elevation check, EventDbTool startup warning, and Tools me…
jschick04 May 21, 2026
fd76f44
Split Effects.cs into FilteringEffects, OpenLogEffects, LogReloadEffe…
jschick04 May 21, 2026
af6231e
Split DatabaseService into EntryStore, Classification, Upgrade, Impor…
jschick04 May 21, 2026
735533e
Dispose previously created lookups when LoadDatabases encounters an e…
jschick04 May 21, 2026
9742749
Restructure UI Base and Filters folders and relocate CloseAllLogsActi…
jschick04 May 21, 2026
77a3769
Split PreferencesProvider into 6 per-domain adapter classes under Ada…
jschick04 May 21, 2026
0f013dc
Rename Filtering Services to Compilation and Runtime to Evaluation
jschick04 May 21, 2026
274a4a4
Remove plan notation from test names and comments and delete TODO pla…
jschick04 May 21, 2026
7a00f23
Add container-gated integration test infrastructure
jschick04 May 22, 2026
f826de4
Extract ProviderDatabase maintenance abstraction, add DI registrar, m…
jschick04 May 22, 2026
8806775
Move DatabaseService sub-service construction to DI factory lambdas, …
jschick04 May 22, 2026
7825f96
Scaffold missing test trios, remove empty TestUtils, reorganize MAUI …
jschick04 May 22, 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
4 changes: 3 additions & 1 deletion .github/workflows/PullRequest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,14 @@ jobs:
if ($failed) { throw 'One or more unit test projects failed.' }
- name: Test (integration)
shell: pwsh
env:
EVENTLOG_CONTAINER: "1"
run: |
$projects = @(Get-ChildItem tests/Integration -Filter *.csproj -Recurse)
if ($projects.Count -eq 0) { throw 'No integration test projects found under tests/Integration.' }
$failed = $false
foreach ($project in $projects) {
dotnet test $project.FullName -c Release --no-build --no-restore --verbosity normal -p:RunSettingsFilePath=
dotnet test $project.FullName -c Release --no-build --no-restore --verbosity normal -p:RunSettingsFilePath=""
if ($LASTEXITCODE -ne 0) { $failed = $true }
}
if ($failed) { throw 'One or more integration test projects failed.' }
2 changes: 2 additions & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@
<PackageVersion Include="Microsoft.AspNetCore.Components.WebView.Maui" Version="$(MauiVersion)" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="10.0.5" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.5" />
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.5" />
<PackageVersion Include="System.CommandLine" Version="2.0.5" />
<PackageVersion Include="Microsoft.Data.Sqlite" Version="10.0.5" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Sqlite" Version="10.0.5" />
<PackageVersion Include="Fluxor.Blazor.Web" Version="6.9.0" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.4.0" />
Expand Down
7 changes: 7 additions & 0 deletions EventLogExpert.slnx
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@
</Configurations>
<Folder Name="/Solution Items/">
<File Path=".editorconfig" />
<File Path="compose.yml" />
<File Path="Directory.Build.props" />
<File Path="Directory.Packages.props" />
<File Path="EventLogExpert.runsettings" />
</Folder>
<Folder Name="/src/">
<Project Path="src/EventLogExpert/EventLogExpert.csproj">
Expand All @@ -16,18 +18,23 @@
<Project Path="src/EventLogExpert.Eventing/EventLogExpert.Eventing.csproj" />
<Project Path="src/EventLogExpert.EventDbTool/EventLogExpert.EventDbTool.csproj" />
<Project Path="src/EventLogExpert.Filtering/EventLogExpert.Filtering.csproj" />
<Project Path="src/EventLogExpert.ProviderDatabase/EventLogExpert.ProviderDatabase.csproj" />
</Folder>
<Folder Name="/tests/Unit/">
<Project Path="tests/Unit/EventLogExpert.UI.Tests/EventLogExpert.UI.Tests.csproj" />
<Project Path="tests/Unit/EventLogExpert.EventDbTool.Tests/EventLogExpert.EventDbTool.Tests.csproj" />
<Project Path="tests/Unit/EventLogExpert.Eventing.Tests/EventLogExpert.Eventing.Tests.csproj" />
<Project Path="tests/Unit/EventLogExpert.Runtime.Tests/EventLogExpert.Runtime.Tests.csproj" />
<Project Path="tests/Unit/EventLogExpert.Filtering.Tests/EventLogExpert.Filtering.Tests.csproj" />
<Project Path="tests/Unit/EventLogExpert.ProviderDatabase.Tests/EventLogExpert.ProviderDatabase.Tests.csproj" />
</Folder>
<Folder Name="/tests/Integration/">
<Project Path="tests/Integration/EventLogExpert.EventDbTool.IntegrationTests/EventLogExpert.EventDbTool.IntegrationTests.csproj" />
<Project Path="tests/Integration/EventLogExpert.Eventing.IntegrationTests/EventLogExpert.Eventing.IntegrationTests.csproj" />
<Project Path="tests/Integration/EventLogExpert.Filtering.IntegrationTests/EventLogExpert.Filtering.IntegrationTests.csproj" />
<Project Path="tests/Integration/EventLogExpert.Runtime.IntegrationTests/EventLogExpert.Runtime.IntegrationTests.csproj" />
<Project Path="tests/Integration/EventLogExpert.ProviderDatabase.IntegrationTests/EventLogExpert.ProviderDatabase.IntegrationTests.csproj" />
<Project Path="tests/Integration/EventLogExpert.UI.IntegrationTests/EventLogExpert.UI.IntegrationTests.csproj" />
</Folder>
<Folder Name="/tests/Shared/">
<Project Path="tests/Shared/EventLogExpert.Eventing.TestUtils/EventLogExpert.Eventing.TestUtils.csproj" />
Expand Down
153 changes: 153 additions & 0 deletions scripts/run-tests.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
<#
.SYNOPSIS
Runs tests via Docker Compose with Windows containers.

.DESCRIPTION
Detects the current Docker daemon mode, switches to Windows containers if needed,
runs the requested test suite(s), then restores the original daemon mode.

Available suites are discovered dynamically from compose.yml services.
Defaults to running all suites defined in compose.yml. Integration test projects
that do not require a Windows container (e.g., ProviderDatabase) are not included
in compose.yml and should be run directly via dotnet test.

.PARAMETER Suite
Which test suite(s) to run. Accepts service names from compose.yml.
Use 'all' (default) to run every service defined in compose.yml.
Use tab-completion or pass any service name from compose.yml.

.EXAMPLE
./scripts/run-tests.ps1
./scripts/run-tests.ps1 -Suite eventing
./scripts/run-tests.ps1 -Suite runtime,eventdbtool
#>
[CmdletBinding()]
param(
[string[]]$Suite = @("all")
)

Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"

$repoRoot = (Get-Item $PSScriptRoot).Parent.FullName
$composeFile = Join-Path $repoRoot "compose.yml"

if (-not (Test-Path $composeFile)) {
throw "compose.yml not found at '$composeFile'. Run this script from the repo root or scripts/ folder."
}

function Get-ComposeServices {
$services = docker compose -f $composeFile config --services 2>$null

if ($LASTEXITCODE -ne 0 -or -not $services) {
throw "Failed to read services from compose.yml. Is Docker running?"
}

return $services | ForEach-Object { $_.Trim() } | Where-Object { $_ -ne "" }
}

function Get-DockerOS {
$info = docker info --format '{{.OSType}}' 2>$null

if ($LASTEXITCODE -ne 0) {
throw "Docker is not running. Start Docker Desktop and try again."
}

return $info.Trim()
}

function Switch-DockerDaemon([string]$targetOS) {
$current = Get-DockerOS

if ($current -eq $targetOS) { return $false }

Write-Host "Switching Docker from '$current' to '$targetOS' containers..." -ForegroundColor Yellow

$dockerCmd = Get-Command docker -ErrorAction SilentlyContinue

if (-not $dockerCmd) {
throw "docker is not on PATH. Install Docker Desktop and ensure it is available."
}

$dockerCli = Join-Path (Split-Path (Split-Path $dockerCmd.Source)) "DockerCli.exe"

if (-not (Test-Path $dockerCli)) {
throw "DockerCli.exe not found at '$dockerCli'. Cannot switch Docker daemon mode."
}

& $dockerCli -SwitchDaemon 2>$null

$retries = 0

while ($retries -lt 30) {
Start-Sleep -Seconds 2
$retries++

try {
$now = Get-DockerOS

if ($now -eq $targetOS) {
Write-Host "Docker switched to '$targetOS' containers." -ForegroundColor Green
return $true
}
} catch {
# Docker not ready yet
}
}

throw "Timed out waiting for Docker to switch to '$targetOS' containers."
}

$availableServices = @(Get-ComposeServices)

if ($availableServices.Count -eq 0) {
throw "No services found in compose.yml."
}

$services = if ($Suite -contains "all") {
$availableServices
} else {
foreach ($s in $Suite) {
if ($s -notin $availableServices) {
throw "Suite '$s' not found in compose.yml. Available: $($availableServices -join ', ')"
}
}

$Suite
}

Write-Host "Suites to run: $($services -join ', ')" -ForegroundColor Cyan
Write-Host "Available suites: $($availableServices -join ', ')" -ForegroundColor DarkGray

$originalOS = Get-DockerOS
$switched = Switch-DockerDaemon "windows"

try {
$failed = $false

foreach ($service in $services) {
Write-Host "`n=== Running $service integration tests ===" -ForegroundColor Cyan

docker compose -f $composeFile run --rm $service

if ($LASTEXITCODE -ne 0) {
Write-Host "FAILED: $service" -ForegroundColor Red
$failed = $true
} else {
Write-Host "PASSED: $service" -ForegroundColor Green
}
}

if ($failed) {
Write-Host "`nOne or more test suites failed." -ForegroundColor Red
} else {
Write-Host "`nAll test suites passed." -ForegroundColor Green
}
} finally {
if ($switched) {
Write-Host "`nRestoring Docker to '$originalOS' containers..." -ForegroundColor Yellow
Switch-DockerDaemon $originalOS | Out-Null
}
}

if ($failed) { exit 1 }
3 changes: 2 additions & 1 deletion src/EventLogExpert.EventDbTool/CreateDatabaseCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
// // Licensed under the MIT License.

using EventLogExpert.Eventing.Logging;
using EventLogExpert.Eventing.ProviderDatabase;
using EventLogExpert.Eventing.Providers;
using EventLogExpert.ProviderDatabase;
using Microsoft.Extensions.DependencyInjection;
using System.CommandLine;
using System.Text.RegularExpressions;
Expand Down Expand Up @@ -59,6 +59,7 @@ public static Command GetCommand()
createDatabaseCommand.SetAction(result =>
{
using var sp = Program.BuildServiceProvider(result.GetValue(verboseOption));

new CreateDatabaseCommand(sp.GetRequiredService<ITraceLogger>())
.CreateDatabase(
result.GetRequiredValue(fileArgument),
Expand Down
4 changes: 2 additions & 2 deletions src/EventLogExpert.EventDbTool/DiffDatabaseCommand.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
// // Copyright (c) Microsoft Corporation.
// // Copyright (c) Microsoft Corporation.
// // Licensed under the MIT License.

using EventLogExpert.Eventing.Logging;
using EventLogExpert.Eventing.ProviderDatabase;
using EventLogExpert.ProviderDatabase;
using EventLogExpert.Eventing.Providers;
using Microsoft.Extensions.DependencyInjection;
using System.CommandLine;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

<ItemGroup>
<ProjectReference Include="..\EventLogExpert.Eventing\EventLogExpert.Eventing.csproj" />
<ProjectReference Include="..\EventLogExpert.ProviderDatabase\EventLogExpert.ProviderDatabase.csproj" />
</ItemGroup>

</Project>
3 changes: 2 additions & 1 deletion src/EventLogExpert.EventDbTool/MergeDatabaseCommand.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
// // Copyright (c) Microsoft Corporation.
// // Copyright (c) Microsoft Corporation.
// // Licensed under the MIT License.

using EventLogExpert.Eventing.Logging;
using EventLogExpert.Eventing.ProviderDatabase;
using EventLogExpert.ProviderDatabase;
using EventLogExpert.Eventing.Providers;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
Expand Down
16 changes: 16 additions & 0 deletions src/EventLogExpert.EventDbTool/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System.CommandLine;
using System.Security.Principal;

namespace EventLogExpert.EventDbTool;

Expand All @@ -17,6 +18,13 @@ internal static ServiceProvider BuildServiceProvider(bool verbose) =>

private static async Task<int> Main(string[] args)
{
if (!IsElevated())
{
await Console.Error.WriteLineAsync(
"WARNING: Running without administrator privileges. " +
"Some provider registry operations may fail or return incomplete results.");
}

RootCommand rootCommand = new("Tool used to create and modify databases for use with EventLogExpert");

rootCommand.Subcommands.Add(ShowCommand.GetCommand());
Expand All @@ -27,4 +35,12 @@ private static async Task<int> Main(string[] args)

return await rootCommand.Parse(args).InvokeAsync();
}

private static bool IsElevated()
{
using var identity = WindowsIdentity.GetCurrent();

return new WindowsPrincipal(identity)
.IsInRole(WindowsBuiltInRole.Administrator);
}
}
1 change: 1 addition & 0 deletions src/EventLogExpert.EventDbTool/ProviderSource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using EventLogExpert.Eventing.Logging;
using EventLogExpert.Eventing.ProviderDatabase;
using EventLogExpert.ProviderDatabase;
using EventLogExpert.Eventing.Providers;
using Microsoft.EntityFrameworkCore;
using System.Data.Common;
Expand Down
3 changes: 2 additions & 1 deletion src/EventLogExpert.EventDbTool/UpgradeDatabaseCommand.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
// // Copyright (c) Microsoft Corporation.
// // Copyright (c) Microsoft Corporation.
// // Licensed under the MIT License.

using EventLogExpert.Eventing.Logging;
using EventLogExpert.Eventing.ProviderDatabase;
using EventLogExpert.ProviderDatabase;
using Microsoft.Extensions.DependencyInjection;
using System.CommandLine;
using System.Data.Common;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" />
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// // Copyright (c) Microsoft Corporation.
// // Licensed under the MIT License.

namespace EventLogExpert.Eventing.ProviderDatabase;

/// <summary>
/// Abstracts provider-database maintenance operations (schema inspection, upgrade, connection lifecycle) so that
/// consumers do not depend on the EF Core / SQLite implementation directly.
/// </summary>
public interface IProviderDatabaseMaintenance
{
/// <summary>Inspects the schema version of the database at <paramref name="databasePath" />.</summary>
/// <param name="databasePath">Full path to the .db file.</param>
/// <param name="readOnly">
/// <c>true</c> for read-only verification (no EnsureCreated); <c>false</c> for classification
/// probes that may need write access.
/// </param>
ProviderDatabaseSchemaState CheckSchemaState(string databasePath, bool readOnly = false);

/// <summary>
/// Runs the schema migration on the database at <paramref name="databasePath" />. On return (normal or
/// exceptional), the context is disposed and all SQLite connection pools are flushed — callers may safely perform file
/// operations on the database.
/// </summary>
void PerformUpgrade(string databasePath);

/// <summary>
/// Executes <c>PRAGMA wal_checkpoint(TRUNCATE)</c> on the database, then flushes all SQLite connection pools. Both
/// operations are performed together so that the temporary connection opened for the checkpoint does not remain pooled.
/// </summary>
void WalCheckpoint(string databasePath);

/// <summary>
/// Flushes all SQLite connection pools process-wide so that pooled file handles are released and subsequent file
/// operations (delete, copy, move) do not race on Windows. This is a global operation — it affects every pooled
/// connection in the process, not just the connection for a specific database.
/// </summary>
void PrepareForFileDeletion();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// // Copyright (c) Microsoft Corporation.
// // Licensed under the MIT License.

using EventLogExpert.Eventing.Providers;

namespace EventLogExpert.Eventing.ProviderDatabase;

public interface IProviderDetailsLookup : IDisposable
{
string Name { get; }

ProviderDetails? FindProvider(string providerName);

ProviderDatabaseSchemaState IsUpgradeNeeded();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// // Copyright (c) Microsoft Corporation.
// // Licensed under the MIT License.

using EventLogExpert.Eventing.Logging;

namespace EventLogExpert.Eventing.ProviderDatabase;

public interface IProviderDetailsLookupFactory
{
IProviderDetailsLookup Create(string path, ITraceLogger? logger = null);
}
Loading
Loading