From fc6d52162a969767ac47fbffb9b844340a237fb0 Mon Sep 17 00:00:00 2001 From: Tyrie Vella Date: Tue, 10 Nov 2026 09:41:06 -0800 Subject: [PATCH 1/3] Resolve unit-test Git dependencies through PATH Find Git through PATH instead of a fixed system installation directory. Share the resolver across the Git-dependent test fixtures. Preserve missing-Git failure and skip policies. Keep metadata-only worktree tests on a mock Git path. Add filesystem-only tests for search order, quoted directories, empty entries, relative paths, and absent Git. Assisted-by: GPT-6.1 Sol Signed-off-by: Tyrie Vella --- .../Git/AuthenticationConfigurationTests.cs | 4 +- .../Common/Git/GitProcessCredentialTests.cs | 6 +- .../GVFS.UnitTests/Common/RetryConfigTests.cs | 6 +- .../Common/WorktreeEnlistmentTests.cs | 22 +++--- GVFS/GVFS.UnitTests/Git/GitProcessTests.cs | 12 +-- GVFS/GVFS.UnitTests/Git/GitTestHelperTests.cs | 78 +++++++++++++++++++ GVFS/GVFS.UnitTests/GitTestHelper.cs | 38 +++++++++ 7 files changed, 141 insertions(+), 25 deletions(-) create mode 100644 GVFS/GVFS.UnitTests/Git/GitTestHelperTests.cs create mode 100644 GVFS/GVFS.UnitTests/GitTestHelper.cs diff --git a/GVFS/GVFS.UnitTests/Common/Git/AuthenticationConfigurationTests.cs b/GVFS/GVFS.UnitTests/Common/Git/AuthenticationConfigurationTests.cs index 2a1b94835..835d29a9b 100644 --- a/GVFS/GVFS.UnitTests/Common/Git/AuthenticationConfigurationTests.cs +++ b/GVFS/GVFS.UnitTests/Common/Git/AuthenticationConfigurationTests.cs @@ -24,7 +24,7 @@ public class AuthenticationConfigurationTests [SetUp] public void Setup() { - this.gitPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "Git", "cmd", "git.exe"); + this.gitPath = GitTestHelper.FindGitOnPath(); this.root = Path.Combine(Path.GetTempPath(), nameof(AuthenticationConfigurationTests) + Guid.NewGuid().ToString("N")); Directory.CreateDirectory(Path.Combine(this.root, ".git", "objects")); Directory.CreateDirectory(Path.Combine(this.root, ".git", "refs", "heads")); @@ -579,7 +579,7 @@ private EventMetadata Collect( { if (!File.Exists(this.gitPath)) { - Assert.Fail("The authentication configuration integration tests require Git for Windows."); + Assert.Fail("The authentication configuration integration tests require Git on PATH."); } GitProcess git = new GitProcess(this.gitPath, this.root); diff --git a/GVFS/GVFS.UnitTests/Common/Git/GitProcessCredentialTests.cs b/GVFS/GVFS.UnitTests/Common/Git/GitProcessCredentialTests.cs index 39899ec0c..434af25e8 100644 --- a/GVFS/GVFS.UnitTests/Common/Git/GitProcessCredentialTests.cs +++ b/GVFS/GVFS.UnitTests/Common/Git/GitProcessCredentialTests.cs @@ -156,10 +156,10 @@ public void CredentialFillDoesNotRetryWhenTheTimeoutCannotCoverTheDelay() [TestCase(false, Timeout.Infinite)] public void CredentialFillHandlesARealLockedGitConfig(bool releaseAfterFirstAttempt, int timeoutMs) { - string gitPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "Git", "cmd", "git.exe"); - if (!File.Exists(gitPath)) + string gitPath = GitTestHelper.FindGitOnPath(); + if (gitPath == null) { - Assert.Ignore("Git is not installed at the expected path."); + Assert.Ignore("Git was not found on PATH."); } string repoPath = Path.Combine(Path.GetTempPath(), nameof(GitProcessCredentialTests) + Guid.NewGuid().ToString("N")); diff --git a/GVFS/GVFS.UnitTests/Common/RetryConfigTests.cs b/GVFS/GVFS.UnitTests/Common/RetryConfigTests.cs index 64ab99190..e2d3c87c0 100644 --- a/GVFS/GVFS.UnitTests/Common/RetryConfigTests.cs +++ b/GVFS/GVFS.UnitTests/Common/RetryConfigTests.cs @@ -595,10 +595,10 @@ public void LibGit2ReadsConfigWithIncludeConditionsItEvaluates(string condition) private static string GetGitPathOrIgnore() { - string gitPath = "C:\\Program Files\\Git\\cmd\\git.exe"; - if (!File.Exists(gitPath)) + string gitPath = GitTestHelper.FindGitOnPath(); + if (gitPath == null) { - Assert.Ignore("Git not found at expected path — skipping integration test"); + Assert.Ignore("Git was not found on PATH."); } return gitPath; diff --git a/GVFS/GVFS.UnitTests/Common/WorktreeEnlistmentTests.cs b/GVFS/GVFS.UnitTests/Common/WorktreeEnlistmentTests.cs index 04fc9038d..24064db64 100644 --- a/GVFS/GVFS.UnitTests/Common/WorktreeEnlistmentTests.cs +++ b/GVFS/GVFS.UnitTests/Common/WorktreeEnlistmentTests.cs @@ -77,10 +77,8 @@ public void TearDown() } } - private GVFSEnlistment CreateWorktreeEnlistment() + private GVFSEnlistment CreateWorktreeEnlistment(string gitBinPath = @"mock:\git") { - string gitBinPath = GVFSPlatform.Instance.GitInstallation.GetInstalledGitBinPath() - ?? @"C:\Program Files\Git\cmd\git.exe"; return GVFSEnlistment.CreateForWorktree( this.primaryRoot, gitBinPath, authentication: null, GVFSEnlistment.TryGetWorktreeInfo(this.worktreePath), @@ -300,7 +298,7 @@ public void WorktreeMetadataCreatesEnlistmentIdWhenNoneIsGivenOrStored(string st [TestCase] public void RequiredSettingsEnableWorktreeConfigAndWritePerWorktreeValues() { - GVFSEnlistment enlistment = this.CreateWorktreeEnlistment(); + GVFSEnlistment enlistment = this.CreateWorktreeEnlistment(GetGitBinPath()); string error; RequiredGitConfig.TrySetRequiredSettings( @@ -323,7 +321,7 @@ public void RequiredSettingsRepairCopiedWorktreeValueAndSharedValueOfPrimary() { this.AppendToSharedConfig("[status]\n\tdeserializePath = C:/sibling/status.dat\n"); File.WriteAllText(this.WorktreeConfigPath, "[status]\n\tdeserializePath = C:/sibling/status.dat\n"); - GVFSEnlistment enlistment = this.CreateWorktreeEnlistment(); + GVFSEnlistment enlistment = this.CreateWorktreeEnlistment(GetGitBinPath()); string error; RequiredGitConfig.TrySetRequiredSettings( @@ -340,7 +338,7 @@ public void RequiredSettingsDisableNullPerWorktreeValueInWorktreeConfig() { this.AppendToSharedConfig("[extensions]\n\tworktreeConfig = true\n[status]\n\tdeserializePath = C:/primary/status.dat\n"); File.WriteAllText(this.WorktreeConfigPath, "[status]\n\tdeserializePath = C:/worktree/status.dat\n"); - GVFSEnlistment enlistment = this.CreateWorktreeEnlistment(); + GVFSEnlistment enlistment = this.CreateWorktreeEnlistment(GetGitBinPath()); string error; RequiredGitConfig.TrySetRequiredSettings( @@ -357,7 +355,7 @@ public void RequiredSettingsDisableNullPerWorktreeValueInWorktreeConfig() public void RequiredSettingsRemoveRequiredOverridesFromWorktreeConfig() { File.WriteAllText(this.WorktreeConfigPath, "[core]\n\tautocrlf = true\n[gvfs]\n\tmount-id = worktreemountid\n"); - GVFSEnlistment enlistment = this.CreateWorktreeEnlistment(); + GVFSEnlistment enlistment = this.CreateWorktreeEnlistment(GetGitBinPath()); string error; RequiredGitConfig.TrySetRequiredSettings( @@ -374,7 +372,7 @@ public void RequiredSettingsRemoveRequiredOverridesFromWorktreeConfig() public void RequiredSettingsReplaceDuplicateWorktreeConfigExtensionValues() { this.AppendToSharedConfig("[extensions]\n\tworktreeConfig = true\n\tworktreeConfig = false\n"); - GVFSEnlistment enlistment = this.CreateWorktreeEnlistment(); + GVFSEnlistment enlistment = this.CreateWorktreeEnlistment(GetGitBinPath()); string error; RequiredGitConfig.TrySetRequiredSettings(enlistment, new Dictionary(), out error).ShouldBeTrue(error); @@ -421,7 +419,7 @@ public void PrimaryRequiredSettingsDoNotEnableWorktreeConfigWithoutLinkedWorktre public void RequiredSettingsWriteNothingWhenAppliedAgain() { GVFSEnlistment primary = new GVFSEnlistment(this.primaryRoot, "https://mock/repo", GetGitBinPath(), authentication: null); - GVFSEnlistment worktree = this.CreateWorktreeEnlistment(); + GVFSEnlistment worktree = this.CreateWorktreeEnlistment(GetGitBinPath()); string error; RequiredGitConfig.TrySetRequiredSettings(primary, out error).ShouldBeTrue(error); @@ -441,7 +439,7 @@ public void RequiredSettingsWriteNothingWhenAppliedAgain() public void RequiredSettingsFailWhenWorktreeConfigIsCorrupt() { File.WriteAllText(this.WorktreeConfigPath, "[status\n"); - GVFSEnlistment enlistment = this.CreateWorktreeEnlistment(); + GVFSEnlistment enlistment = this.CreateWorktreeEnlistment(GetGitBinPath()); string error; RequiredGitConfig.TrySetRequiredSettings( @@ -462,7 +460,9 @@ public void RequiredSettingsFailWhenWorktreeConfigIsCorrupt() private static string GetGitBinPath() { - return GVFSPlatform.Instance.GitInstallation.GetInstalledGitBinPath() ?? @"C:\Program Files\Git\cmd\git.exe"; + string gitPath = GitTestHelper.FindGitOnPath(); + Assert.That(gitPath, Is.Not.Null, "The required-settings tests require Git on PATH."); + return gitPath; } private static string GetRepoMetadataPath(string dotGVFSRoot) diff --git a/GVFS/GVFS.UnitTests/Git/GitProcessTests.cs b/GVFS/GVFS.UnitTests/Git/GitProcessTests.cs index 4dd4b0fec..b401efd5b 100644 --- a/GVFS/GVFS.UnitTests/Git/GitProcessTests.cs +++ b/GVFS/GVFS.UnitTests/Git/GitProcessTests.cs @@ -555,10 +555,10 @@ public void QuoteGitPath(string input, string expected) [TestCase(Timeout.Infinite)] public void GitCommandWaitsForOutputCallbacks(int timeoutMs) { - string gitPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "Git", "cmd", "git.exe"); - if (!File.Exists(gitPath)) + string gitPath = GitTestHelper.FindGitOnPath(); + if (gitPath == null) { - Assert.Ignore("Git is not installed at the expected path."); + Assert.Ignore("Git was not found on PATH."); } OutputCallbackGitProcess gitProcess = new OutputCallbackGitProcess(gitPath); @@ -605,10 +605,10 @@ public void QuoteGitPath_RoundTripThroughProcess() "path\\with\\backslashes\\file.txt", }; - string gitPath = "C:\\Program Files\\Git\\cmd\\git.exe"; - if (!System.IO.File.Exists(gitPath)) + string gitPath = GitTestHelper.FindGitOnPath(); + if (gitPath == null) { - Assert.Ignore("Git not found at expected path — skipping integration test"); + Assert.Ignore("Git was not found on PATH."); } foreach (string testPath in testPaths) diff --git a/GVFS/GVFS.UnitTests/Git/GitTestHelperTests.cs b/GVFS/GVFS.UnitTests/Git/GitTestHelperTests.cs new file mode 100644 index 000000000..310f5087f --- /dev/null +++ b/GVFS/GVFS.UnitTests/Git/GitTestHelperTests.cs @@ -0,0 +1,78 @@ +using NUnit.Framework; +using System; +using System.IO; + +namespace GVFS.UnitTests.Git +{ + [TestFixture] + [NonParallelizable] + public class GitTestHelperTests + { + private string root; + + [SetUp] + public void SetUp() + { + this.root = Path.Combine(Path.GetTempPath(), nameof(GitTestHelperTests) + Guid.NewGuid().ToString("N")); + Directory.CreateDirectory(this.root); + } + + [TearDown] + public void TearDown() + { + Directory.Delete(this.root, recursive: true); + } + + [Test] + public void FindsTheFirstGitInQuotedPathEntriesWithSpaces() + { + string firstDirectory = Path.Combine(this.root, "First Git"); + string secondDirectory = Path.Combine(this.root, "Second Git"); + Directory.CreateDirectory(firstDirectory); + Directory.CreateDirectory(secondDirectory); + string expectedGit = Path.Combine(firstDirectory, "git.exe"); + File.WriteAllText(expectedGit, string.Empty); + File.WriteAllText(Path.Combine(secondDirectory, "git.exe"), string.Empty); + string searchPath = string.Join(Path.PathSeparator, + Path.Combine(this.root, "missing"), "\"" + firstDirectory + "\"", secondDirectory); + + Assert.That(GitTestHelper.FindGitOnPath(searchPath), Is.EqualTo(expectedGit)); + } + + [TestCase(null)] + [TestCase("")] + [TestCase(";;")] + [TestCase("\"\"; ;")] + public void EmptyPathEntriesDoNotSearchTheWorkingDirectory(string searchPath) + { + File.WriteAllText(Path.Combine(this.root, "git.exe"), string.Empty); + string originalDirectory = Environment.CurrentDirectory; + try + { + Environment.CurrentDirectory = this.root; + + Assert.That(GitTestHelper.FindGitOnPath(searchPath), Is.Null); + } + finally + { + Environment.CurrentDirectory = originalDirectory; + } + } + + [Test] + public void RelativePathEntriesReturnAnAbsolutePath() + { + string expectedGit = Path.Combine(this.root, "git.exe"); + File.WriteAllText(expectedGit, string.Empty); + string searchPath = Path.GetRelativePath(Environment.CurrentDirectory, this.root); + + Assert.That(GitTestHelper.FindGitOnPath(searchPath), Is.EqualTo(expectedGit)); + } + + [Test] + public void MissingGitReturnsNull() + { + Assert.That(GitTestHelper.FindGitOnPath(this.root), Is.Null); + } + } +} diff --git a/GVFS/GVFS.UnitTests/GitTestHelper.cs b/GVFS/GVFS.UnitTests/GitTestHelper.cs new file mode 100644 index 000000000..976c503b7 --- /dev/null +++ b/GVFS/GVFS.UnitTests/GitTestHelper.cs @@ -0,0 +1,38 @@ +using System; +using System.IO; + +namespace GVFS.UnitTests +{ + internal static class GitTestHelper + { + internal static string FindGitOnPath() + { + return FindGitOnPath(Environment.GetEnvironmentVariable("PATH")); + } + + internal static string FindGitOnPath(string searchPath) + { + if (string.IsNullOrEmpty(searchPath)) + { + return null; + } + + foreach (string entry in searchPath.Split(Path.PathSeparator)) + { + string directory = entry.Trim('"'); + if (string.IsNullOrWhiteSpace(directory)) + { + continue; + } + + string gitPath = Path.Combine(directory, "git.exe"); + if (File.Exists(gitPath)) + { + return Path.GetFullPath(gitPath); + } + } + + return null; + } + } +} From f50985aa24b9b04c6efc367d78ef5864e950abc6 Mon Sep 17 00:00:00 2001 From: Tyrie Vella Date: Tue, 10 Nov 2026 09:57:09 -0800 Subject: [PATCH 2/3] Handle quoted PATH separators and missing Git diagnostics Keep semicolons inside quoted PATH directories. Add a filesystem-only regression case for that directory shape. Compare resolved Windows paths without case sensitivity. Use one Git prerequisite guard across authentication test entry points. Assisted-by: GPT-6.1 Sol Signed-off-by: Tyrie Vella --- .../Git/AuthenticationConfigurationTests.cs | 21 ++++++++++++------- GVFS/GVFS.UnitTests/Git/GitTestHelperTests.cs | 9 ++++---- GVFS/GVFS.UnitTests/GitTestHelper.cs | 20 ++++++++++++++++-- 3 files changed, 36 insertions(+), 14 deletions(-) diff --git a/GVFS/GVFS.UnitTests/Common/Git/AuthenticationConfigurationTests.cs b/GVFS/GVFS.UnitTests/Common/Git/AuthenticationConfigurationTests.cs index 835d29a9b..aeb47628d 100644 --- a/GVFS/GVFS.UnitTests/Common/Git/AuthenticationConfigurationTests.cs +++ b/GVFS/GVFS.UnitTests/Common/Git/AuthenticationConfigurationTests.cs @@ -521,7 +521,7 @@ public void ProductionFillCarriesItsOwnEnvironmentIntoTheCollector() { File.AppendAllText(Path.Combine(this.root, ".git", "config"), "[credential]\nhelper =\ninteractive = never\n"); - PlumbingGitProcess git = new PlumbingGitProcess(this.gitPath, this.root); + PlumbingGitProcess git = new PlumbingGitProcess(this.GetGitPath(), this.root); MockTracer tracer = new MockTracer(); Assert.That(git.TryGetCredential(tracer, "https://dev.azure.com/org/repo", out _, out _, out _, timeoutMs: 5000), Is.False); Assert.That(git.CollectedEnvironment, Is.SameAs(git.ResultEnvironment)); @@ -551,7 +551,7 @@ public void FailedCredentialFillBeforeCloneCreatesTheWorkingDirectoryCollectsCon { "GIT_TERMINAL_PROMPT", "0" }, { "GCM_VALIDATE", "0" }, }; - PreCloneGitProcess git = new PreCloneGitProcess(this.gitPath, workingDirectory, environment); + PreCloneGitProcess git = new PreCloneGitProcess(this.GetGitPath(), workingDirectory, environment); MockTracer tracer = new MockTracer(); Assert.That(git.TryGetCredential(tracer, "https://dev.azure.com/org/repo", out _, out _, out _), Is.False); @@ -571,18 +571,23 @@ public void FailedCredentialFillBeforeCloneCreatesTheWorkingDirectoryCollectsCon this.AssertNoSecret(snapshot); } - private EventMetadata Collect( - string url, - Action customize = null, - Dictionary credentialFillEnvironment = null, - ITracer tracer = null) + private string GetGitPath() { if (!File.Exists(this.gitPath)) { Assert.Fail("The authentication configuration integration tests require Git on PATH."); } - GitProcess git = new GitProcess(this.gitPath, this.root); + return this.gitPath; + } + + private EventMetadata Collect( + string url, + Action customize = null, + Dictionary credentialFillEnvironment = null, + ITracer tracer = null) + { + GitProcess git = new GitProcess(this.GetGitPath(), this.root); return AuthenticationConfiguration.Collect(url, command => { Process process = git.GetGitProcess(command, this.root, Path.Combine(this.root, ".git"), false, null, false); diff --git a/GVFS/GVFS.UnitTests/Git/GitTestHelperTests.cs b/GVFS/GVFS.UnitTests/Git/GitTestHelperTests.cs index 310f5087f..607680b93 100644 --- a/GVFS/GVFS.UnitTests/Git/GitTestHelperTests.cs +++ b/GVFS/GVFS.UnitTests/Git/GitTestHelperTests.cs @@ -23,10 +23,11 @@ public void TearDown() Directory.Delete(this.root, recursive: true); } - [Test] - public void FindsTheFirstGitInQuotedPathEntriesWithSpaces() + [TestCase("First Git")] + [TestCase("First; Git")] + public void FindsTheFirstGitInQuotedPathEntriesWithSpaces(string firstDirectoryName) { - string firstDirectory = Path.Combine(this.root, "First Git"); + string firstDirectory = Path.Combine(this.root, firstDirectoryName); string secondDirectory = Path.Combine(this.root, "Second Git"); Directory.CreateDirectory(firstDirectory); Directory.CreateDirectory(secondDirectory); @@ -66,7 +67,7 @@ public void RelativePathEntriesReturnAnAbsolutePath() File.WriteAllText(expectedGit, string.Empty); string searchPath = Path.GetRelativePath(Environment.CurrentDirectory, this.root); - Assert.That(GitTestHelper.FindGitOnPath(searchPath), Is.EqualTo(expectedGit)); + Assert.That(GitTestHelper.FindGitOnPath(searchPath), Is.EqualTo(expectedGit).IgnoreCase); } [Test] diff --git a/GVFS/GVFS.UnitTests/GitTestHelper.cs b/GVFS/GVFS.UnitTests/GitTestHelper.cs index 976c503b7..6a947558b 100644 --- a/GVFS/GVFS.UnitTests/GitTestHelper.cs +++ b/GVFS/GVFS.UnitTests/GitTestHelper.cs @@ -17,9 +17,25 @@ internal static string FindGitOnPath(string searchPath) return null; } - foreach (string entry in searchPath.Split(Path.PathSeparator)) + int entryStart = 0; + bool quoted = false; + for (int index = 0; index <= searchPath.Length; ++index) { - string directory = entry.Trim('"'); + if (index < searchPath.Length) + { + if (searchPath[index] == '"') + { + quoted = !quoted; + } + + if (searchPath[index] != Path.PathSeparator || quoted) + { + continue; + } + } + + string directory = searchPath.Substring(entryStart, index - entryStart).Trim('"'); + entryStart = index + 1; if (string.IsNullOrWhiteSpace(directory)) { continue; From 7adbf809c579e8a42915526be8c6914b3ddbed41 Mon Sep 17 00:00:00 2001 From: Tyrie Vella Date: Tue, 10 Nov 2026 14:08:33 -0800 Subject: [PATCH 3/3] Preserve real Git checks in worktree ID tests Resolve Git through PATH in both tests that reject the shared config ID. Keep all three enlistment constructions on the real Git executable. Assisted-by: GPT-6.1 Sol Signed-off-by: Tyrie Vella --- GVFS/GVFS.UnitTests/Common/WorktreeEnlistmentTests.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/GVFS/GVFS.UnitTests/Common/WorktreeEnlistmentTests.cs b/GVFS/GVFS.UnitTests/Common/WorktreeEnlistmentTests.cs index 24064db64..2f587b698 100644 --- a/GVFS/GVFS.UnitTests/Common/WorktreeEnlistmentTests.cs +++ b/GVFS/GVFS.UnitTests/Common/WorktreeEnlistmentTests.cs @@ -196,7 +196,7 @@ public void RepoUrlIsReadFromSharedConfig() public void WorktreeEnlistmentIdIsEmptyWhenNothingIsPersisted() { this.AppendToSharedConfig("[gvfs]\n\tenlistment-id = primaryenlistmentid\n"); - GVFSEnlistment enlistment = this.CreateWorktreeEnlistment(); + GVFSEnlistment enlistment = this.CreateWorktreeEnlistment(GetGitBinPath()); enlistment.GetEnlistmentId().ShouldEqual(string.Empty); File.Exists(GetRepoMetadataPath(enlistment.DotGVFSRoot)).ShouldBeFalse("Reading the id must not create worktree repo metadata"); @@ -208,10 +208,10 @@ public void WorktreeEnlistmentIdIsDerivedFromPrimaryRepoMetadata() { this.AppendToSharedConfig("[gvfs]\n\tenlistment-id = sharedconfigenlistmentid\n"); EditRepoMetadata(this.PrimaryDotGVFSRoot, metadata => metadata.SetEntry(RepoMetadata.Keys.EnlistmentId, "primaryenlistmentid")); - GVFSEnlistment enlistment = this.CreateWorktreeEnlistment(); + GVFSEnlistment enlistment = this.CreateWorktreeEnlistment(GetGitBinPath()); enlistment.GetEnlistmentId().ShouldEqual(DerivedEnlistmentIdOfAgentWorktree); - this.CreateWorktreeEnlistment().GetEnlistmentId().ShouldEqual(DerivedEnlistmentIdOfAgentWorktree); + this.CreateWorktreeEnlistment(GetGitBinPath()).GetEnlistmentId().ShouldEqual(DerivedEnlistmentIdOfAgentWorktree); RepoMetadata.Instance.ShouldBeNull("Reading the id must not initialize RepoMetadata.Instance"); }