From 170426150e9a3652cf3fd75f23e283ef7d3df34b Mon Sep 17 00:00:00 2001 From: Alberto Spelta Date: Tue, 14 Jul 2026 13:22:44 +0200 Subject: [PATCH] Fix GoldenFile.GetSnapshotPath under ContinuousIntegrationBuild Azure Pipelines' build step applies /p:ContinuousIntegrationBuild=true to every project (src/**/*.csproj), which makes Roslyn remap the [CallerFilePath] literal compiled into GoldenFile to a deterministic placeholder like "/_/src/Dax.Template.Tests/...". That path never exists on disk, so walking up from it to find the test project's .csproj always bottomed out at null and threw. Fall back to AppContext.BaseDirectory (the real runtime output directory, unaffected by compile-time path mapping) when the caller-path walk doesn't resolve. --- .../Infrastructure/GoldenFile.cs | 24 ++++++++++++++----- 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/src/Dax.Template.Tests/Infrastructure/GoldenFile.cs b/src/Dax.Template.Tests/Infrastructure/GoldenFile.cs index d2c5b08..e362a84 100644 --- a/src/Dax.Template.Tests/Infrastructure/GoldenFile.cs +++ b/src/Dax.Template.Tests/Infrastructure/GoldenFile.cs @@ -63,17 +63,29 @@ private static string GetSnapshotPath(string name, string extension, string call // callerFilePath points at the calling test's source file, which lives somewhere under the test // project. Walk up to the directory that holds the .csproj so snapshots are committed next to the // source and located independently of the build output directory or the caller's subfolder depth. - var dir = Path.GetDirectoryName(callerFilePath); - while (dir != null && Directory.GetFiles(dir, "*.csproj").Length == 0) - { - dir = Path.GetDirectoryName(dir); - } + // Under a ContinuousIntegrationBuild (e.g. the Azure Pipelines build step, which applies + // /p:ContinuousIntegrationBuild=true to every project), Roslyn's deterministic source-path mapping + // rewrites the [CallerFilePath] literal to a placeholder like "/_/src/...", which never exists on + // disk. AppContext.BaseDirectory is the test assembly's real runtime output directory and is + // unaffected by that compile-time remapping, so fall back to it when the caller path doesn't resolve. + var dir = FindProjectDirectory(Path.GetDirectoryName(callerFilePath)) + ?? FindProjectDirectory(AppContext.BaseDirectory); if (dir == null) { throw new InvalidOperationException( - $"Could not locate the test project directory (.csproj) from caller path '{callerFilePath}'."); + $"Could not locate the test project directory (.csproj) from caller path '{callerFilePath}' or base directory '{AppContext.BaseDirectory}'."); } return Path.Combine(dir, "_data", "Golden", name + "." + extension); } + + private static string? FindProjectDirectory(string? startDirectory) + { + var dir = startDirectory; + while (dir != null && Directory.Exists(dir) && Directory.GetFiles(dir, "*.csproj").Length == 0) + { + dir = Path.GetDirectoryName(dir); + } + return dir != null && Directory.Exists(dir) && Directory.GetFiles(dir, "*.csproj").Length > 0 ? dir : null; + } } } \ No newline at end of file