diff --git a/Assets/Tests/Editor/CliInstallationDetectorTests.cs b/Assets/Tests/Editor/CliInstallationDetectorTests.cs index c0c6fcc70..3199d06d6 100644 --- a/Assets/Tests/Editor/CliInstallationDetectorTests.cs +++ b/Assets/Tests/Editor/CliInstallationDetectorTests.cs @@ -17,17 +17,17 @@ public void SelectPreferredDetection_WhenShellCommandShadowsPackageOwnedCliUsesS // Verifies that the settings UI reports the same CLI command the user's terminal runs. CliInstallationDetection packageOwnedDetection = new( "3.0.0-beta.3", - "/Users/masamichi/.local/bin/uloop"); + "/Users/ExampleUser/.local/bin/uloop"); CliInstallationDetection shellDetection = new( "2.1.0", - "/Users/masamichi/.npm-global/bin/uloop"); + "/Users/ExampleUser/.npm-global/bin/uloop"); CliInstallationDetection result = CliInstallationDetector.SelectPreferredDetection( packageOwnedDetection, shellDetection); Assert.That(result.Version, Is.EqualTo("2.1.0")); - Assert.That(result.ExecutablePath, Is.EqualTo("/Users/masamichi/.npm-global/bin/uloop")); + Assert.That(result.ExecutablePath, Is.EqualTo("/Users/ExampleUser/.npm-global/bin/uloop")); } [Test] @@ -36,7 +36,7 @@ public void SelectPreferredDetection_WhenShellCommandMissingUsesPackageOwnedCli( // Verifies that package-owned installs still count when the shell cannot resolve uloop. CliInstallationDetection packageOwnedDetection = new( "3.0.0-beta.3", - "/Users/masamichi/.local/bin/uloop"); + "/Users/ExampleUser/.local/bin/uloop"); CliInstallationDetection shellDetection = new( null, null); @@ -46,7 +46,7 @@ public void SelectPreferredDetection_WhenShellCommandMissingUsesPackageOwnedCli( shellDetection); Assert.That(result.Version, Is.EqualTo("3.0.0-beta.3")); - Assert.That(result.ExecutablePath, Is.EqualTo("/Users/masamichi/.local/bin/uloop")); + Assert.That(result.ExecutablePath, Is.EqualTo("/Users/ExampleUser/.local/bin/uloop")); } [Test] @@ -55,17 +55,17 @@ public void SelectPreferredDetection_WhenShellCommandExistsButVersionFailsUsesSh // Verifies that a broken PATH command is surfaced instead of hidden by the package-owned binary. CliInstallationDetection packageOwnedDetection = new( "3.0.0-beta.3", - "/Users/masamichi/.local/bin/uloop"); + "/Users/ExampleUser/.local/bin/uloop"); CliInstallationDetection shellDetection = new( null, - "/Users/masamichi/.npm-global/bin/uloop"); + "/Users/ExampleUser/.npm-global/bin/uloop"); CliInstallationDetection result = CliInstallationDetector.SelectPreferredDetection( packageOwnedDetection, shellDetection); Assert.That(result.Version, Is.Null); - Assert.That(result.ExecutablePath, Is.EqualTo("/Users/masamichi/.npm-global/bin/uloop")); + Assert.That(result.ExecutablePath, Is.EqualTo("/Users/ExampleUser/.npm-global/bin/uloop")); } [Test] @@ -74,17 +74,17 @@ public void SelectPreferredDetection_WhenPackageOwnedCliMissingUsesShellPath() // Verifies that legacy CLI installs still surface as update candidates. CliInstallationDetection packageOwnedDetection = new( null, - "/Users/masamichi/.local/bin/uloop"); + "/Users/ExampleUser/.local/bin/uloop"); CliInstallationDetection shellDetection = new( "2.1.0", - "/Users/masamichi/.npm-global/bin/uloop"); + "/Users/ExampleUser/.npm-global/bin/uloop"); CliInstallationDetection result = CliInstallationDetector.SelectPreferredDetection( packageOwnedDetection, shellDetection); Assert.That(result.Version, Is.EqualTo("2.1.0")); - Assert.That(result.ExecutablePath, Is.EqualTo("/Users/masamichi/.npm-global/bin/uloop")); + Assert.That(result.ExecutablePath, Is.EqualTo("/Users/ExampleUser/.npm-global/bin/uloop")); } [Test] @@ -93,7 +93,7 @@ public void SelectPreferredDetection_WhenShellVersionExistsWithoutPathUsesShellV // Verifies that installed state does not depend on command path availability. CliInstallationDetection packageOwnedDetection = new( "3.0.0-beta.3", - "/Users/masamichi/.local/bin/uloop"); + "/Users/ExampleUser/.local/bin/uloop"); CliInstallationDetection shellDetection = new( "2.1.0", null); @@ -125,7 +125,7 @@ public void ParseShellCliInstallationOutput_WhenPathAndVersionExist_ReturnsDetec // Verifies that shell detection keeps terminal-visible path data as auxiliary UI context. string output = "banner\n" + "__ULOOP_PATH_START__\n" - + "/Users/masamichi/.npm-global/bin/uloop\n" + + "/Users/ExampleUser/.npm-global/bin/uloop\n" + "__ULOOP_PATH_END__\n" + "__ULOOP_VERSION_START__\n" + "2.1.1\n" @@ -138,7 +138,7 @@ public void ParseShellCliInstallationOutput_WhenPathAndVersionExist_ReturnsDetec CliInstallationDetector.ParseShellCliInstallationOutput(output); Assert.That(detection.Version, Is.EqualTo("2.1.1")); - Assert.That(detection.ExecutablePath, Is.EqualTo("/Users/masamichi/.npm-global/bin/uloop")); + Assert.That(detection.ExecutablePath, Is.EqualTo("/Users/ExampleUser/.npm-global/bin/uloop")); } [Test] @@ -166,7 +166,7 @@ public void ParseShellCliInstallationOutput_WhenVersionCommandFails_ReturnsPathW { // Verifies that failed shell probes do not treat stdout usage text as a CLI version. string output = "__ULOOP_PATH_START__\n" - + "/Users/masamichi/.npm-global/bin/uloop\n" + + "/Users/ExampleUser/.npm-global/bin/uloop\n" + "__ULOOP_PATH_END__\n" + "__ULOOP_VERSION_START__\n" + "usage: broken uloop\n" @@ -179,7 +179,7 @@ public void ParseShellCliInstallationOutput_WhenVersionCommandFails_ReturnsPathW CliInstallationDetector.ParseShellCliInstallationOutput(output); Assert.That(detection.Version, Is.Null); - Assert.That(detection.ExecutablePath, Is.EqualTo("/Users/masamichi/.npm-global/bin/uloop")); + Assert.That(detection.ExecutablePath, Is.EqualTo("/Users/ExampleUser/.npm-global/bin/uloop")); } [Test] diff --git a/Assets/Tests/Editor/DynamicCodeToolTests/SharedRoslynCompilerWorkerHostTests.cs b/Assets/Tests/Editor/DynamicCodeToolTests/SharedRoslynCompilerWorkerHostTests.cs index c719c21b2..1e23ec57b 100644 --- a/Assets/Tests/Editor/DynamicCodeToolTests/SharedRoslynCompilerWorkerHostTests.cs +++ b/Assets/Tests/Editor/DynamicCodeToolTests/SharedRoslynCompilerWorkerHostTests.cs @@ -31,7 +31,7 @@ public void ConfigureWorkerDotnetRuntimeEnvironment_WhenCalled_ShouldDisableMult public void CreateCompileRequestCommand_WhenPathIsWindowsAbsolutePath_ShouldEncodeAsciiPayload() { string requestFilePath = - @"C:\Users\S06870\Documents\unity\vision-client_02\vision-client\Temp\UnityCliLoopCompilation\DynamicCommand_1.worker"; + @"C:\Users\ExampleUser\Documents\unity\SampleWorkspace\SampleUnityProject\Temp\UnityCliLoopCompilation\DynamicCommand_1.worker"; string command = SharedRoslynCompilerWorkerHost.CreateCompileRequestCommandForTests(requestFilePath); diff --git a/Assets/Tests/Editor/NativeCliInstallerTests.cs b/Assets/Tests/Editor/NativeCliInstallerTests.cs index 7e4955880..99559dca7 100644 --- a/Assets/Tests/Editor/NativeCliInstallerTests.cs +++ b/Assets/Tests/Editor/NativeCliInstallerTests.cs @@ -1,5 +1,6 @@ using System.IO; using System.Threading; +using System.Threading.Tasks; using NUnit.Framework; using UnityEngine; @@ -262,7 +263,7 @@ public void RunUninstallCommand_WhenCanceledReportsUninstallCommand() CliInstallResult result = NativeCliInstaller.RunUninstallCommand( command, - "/Users/masamichi/.local/bin", + "/Users/ExampleUser/.local/bin", cts.Token, 1000); @@ -270,17 +271,63 @@ public void RunUninstallCommand_WhenCanceledReportsUninstallCommand() Assert.That(result.ErrorOutput, Is.EqualTo("Global CLI uninstall command was canceled.")); } + [Test] + public async Task WaitForUninstallTargetRemovalAsync_WhenDeferredRemovalCompletesReturnsSuccess() + { + // Verifies that Settings uninstall waits for deferred launcher self-removal before refreshing CLI status. + int remainingExistingChecks = 2; + int delayCount = 0; + + CliInstallResult result = await NativeCliInstaller.WaitForUninstallTargetRemovalAsync( + "C:\\Users\\ExampleUser\\AppData\\Local\\Programs\\uloop\\bin\\uloop.exe", + CancellationToken.None, + 1000, + 100, + executablePath => remainingExistingChecks-- > 0, + (delayMs, ct) => + { + delayCount++; + return Task.CompletedTask; + }); + + Assert.That(result.Success, Is.True, result.ErrorOutput); + Assert.That(delayCount, Is.EqualTo(2)); + } + + [Test] + public async Task WaitForUninstallTargetRemovalAsync_WhenTargetRemainsReturnsFailure() + { + // Verifies that delayed launcher removal failures do not recache a stale installed CLI. + int delayCount = 0; + + CliInstallResult result = await NativeCliInstaller.WaitForUninstallTargetRemovalAsync( + "C:\\Users\\ExampleUser\\AppData\\Local\\Programs\\uloop\\bin\\uloop.exe", + CancellationToken.None, + 250, + 100, + executablePath => true, + (delayMs, ct) => + { + delayCount++; + return Task.CompletedTask; + }); + + Assert.That(result.Success, Is.False); + Assert.That(result.ErrorOutput, Does.Contain("Timed out waiting for uLoop CLI uninstall")); + Assert.That(delayCount, Is.EqualTo(3)); + } + [Test] public void BuildUninstallCommand_OnMacRunsInstalledLauncher() { // Verifies that editor uninstall delegates removal to the installed uloop command. NativeCliInstallCommand command = NativeCliInstaller.BuildUninstallCommand( - "/Users/masamichi/.local/bin", + "/Users/ExampleUser/.local/bin", RuntimePlatform.OSXEditor); - Assert.That(command.FileName, Is.EqualTo("/Users/masamichi/.local/bin/uloop")); + Assert.That(command.FileName, Is.EqualTo("/Users/ExampleUser/.local/bin/uloop")); Assert.That(command.Arguments, Is.EqualTo("uninstall")); - Assert.That(command.ManualCommand, Is.EqualTo("\"/Users/masamichi/.local/bin/uloop\" uninstall")); + Assert.That(command.ManualCommand, Is.EqualTo("\"/Users/ExampleUser/.local/bin/uloop\" uninstall")); } [Test] @@ -288,13 +335,13 @@ public void BuildUninstallCommand_OnWindowsRunsInstalledLauncher() { // Verifies that Windows editor uninstall delegates removal to the installed uloop command. NativeCliInstallCommand command = NativeCliInstaller.BuildUninstallCommand( - "C:\\Users\\masamichi\\AppData\\Local\\Programs\\uloop\\bin", + "C:\\Users\\ExampleUser\\AppData\\Local\\Programs\\uloop\\bin", RuntimePlatform.WindowsEditor); - Assert.That(command.FileName, Does.Contain("C:\\Users\\masamichi\\AppData\\Local\\Programs\\uloop\\bin")); + Assert.That(command.FileName, Does.Contain("C:\\Users\\ExampleUser\\AppData\\Local\\Programs\\uloop\\bin")); Assert.That(command.FileName, Does.EndWith("uloop.exe")); Assert.That(command.Arguments, Is.EqualTo("uninstall")); - Assert.That(command.ManualCommand, Does.Contain("C:\\Users\\masamichi\\AppData\\Local\\Programs\\uloop\\bin")); + Assert.That(command.ManualCommand, Does.Contain("C:\\Users\\ExampleUser\\AppData\\Local\\Programs\\uloop\\bin")); Assert.That(command.ManualCommand, Does.EndWith("uloop.exe\" uninstall")); } @@ -304,10 +351,10 @@ public void BuildPathWithInstallDirectory_OnWindowsPrependsMissingNativeInstallD // Verifies that Unity's current Windows PATH prefers the freshly installed native CLI. string result = NativeCliInstaller.BuildPathWithInstallDirectory( "C:\\npm", - "C:\\Users\\masamichi\\Programs\\uloop\\bin", + "C:\\Users\\ExampleUser\\Programs\\uloop\\bin", RuntimePlatform.WindowsEditor); - Assert.That(result, Is.EqualTo("C:\\Users\\masamichi\\Programs\\uloop\\bin;C:\\npm")); + Assert.That(result, Is.EqualTo("C:\\Users\\ExampleUser\\Programs\\uloop\\bin;C:\\npm")); } private static NativeCliInstallCommand BuildLongRunningInstallCommand() @@ -331,11 +378,11 @@ public void BuildPathWithInstallDirectory_OnWindowsMovesExistingNativeInstallDir { // Verifies that a later Windows native install dir does not leave an earlier npm shim first. string result = NativeCliInstaller.BuildPathWithInstallDirectory( - "C:\\npm;C:\\USERS\\MASAMICHI\\PROGRAMS\\ULOOP\\BIN", - "C:\\Users\\masamichi\\Programs\\uloop\\bin", + "C:\\npm;C:\\USERS\\EXAMPLEUSER\\PROGRAMS\\ULOOP\\BIN", + "C:\\Users\\ExampleUser\\Programs\\uloop\\bin", RuntimePlatform.WindowsEditor); - Assert.That(result, Is.EqualTo("C:\\Users\\masamichi\\Programs\\uloop\\bin;C:\\npm")); + Assert.That(result, Is.EqualTo("C:\\Users\\ExampleUser\\Programs\\uloop\\bin;C:\\npm")); } [Test] @@ -344,10 +391,10 @@ public void BuildPathWithInstallDirectory_OnMacPrependsMissingNativeInstallDir() // Verifies that POSIX PATH prefers the freshly installed native CLI. string result = NativeCliInstaller.BuildPathWithInstallDirectory( "/usr/local/bin", - "/Users/masamichi/.local/bin", + "/Users/ExampleUser/.local/bin", RuntimePlatform.OSXEditor); - Assert.That(result, Is.EqualTo("/Users/masamichi/.local/bin:/usr/local/bin")); + Assert.That(result, Is.EqualTo("/Users/ExampleUser/.local/bin:/usr/local/bin")); } [Test] @@ -359,7 +406,7 @@ public void PersistInstallDirectoryToUserPath_OnWindowsUpdatesUserPath() System.EnvironmentVariableTarget capturedTarget = default; CliInstallResult result = NativeCliInstaller.PersistInstallDirectoryToUserPath( - "C:\\Users\\masamichi\\Programs\\uloop\\bin", + "C:\\Users\\ExampleUser\\Programs\\uloop\\bin", RuntimePlatform.WindowsEditor, (name, target) => "C:\\npm", (name, value, target) => @@ -371,7 +418,7 @@ public void PersistInstallDirectoryToUserPath_OnWindowsUpdatesUserPath() Assert.That(result.Success, Is.True); Assert.That(capturedName, Is.EqualTo("Path")); - Assert.That(capturedValue, Is.EqualTo("C:\\Users\\masamichi\\Programs\\uloop\\bin;C:\\npm")); + Assert.That(capturedValue, Is.EqualTo("C:\\Users\\ExampleUser\\Programs\\uloop\\bin;C:\\npm")); Assert.That(capturedTarget, Is.EqualTo(System.EnvironmentVariableTarget.User)); } @@ -382,7 +429,7 @@ public void PersistInstallDirectoryToUserPath_OnMacDoesNothing() bool wroteUserPath = false; CliInstallResult result = NativeCliInstaller.PersistInstallDirectoryToUserPath( - "/Users/masamichi/.local/bin", + "/Users/ExampleUser/.local/bin", RuntimePlatform.OSXEditor, (name, target) => "/usr/local/bin", (name, value, target) => { wroteUserPath = true; }); @@ -396,7 +443,7 @@ public void PersistInstallDirectoryToUserPath_OnWindowsSurfacesPermissionFailure { // Verifies that permission failures are reported instead of crashing the editor installer. CliInstallResult result = NativeCliInstaller.PersistInstallDirectoryToUserPath( - "C:\\Users\\masamichi\\Programs\\uloop\\bin", + "C:\\Users\\ExampleUser\\Programs\\uloop\\bin", RuntimePlatform.WindowsEditor, (name, target) => "C:\\npm", (name, value, target) => throw new System.UnauthorizedAccessException("denied")); @@ -406,98 +453,6 @@ public void PersistInstallDirectoryToUserPath_OnWindowsSurfacesPermissionFailure Assert.That(result.ErrorOutput, Does.Contain("denied")); } - [Test] - public void BuildPathWithoutInstallDirectory_OnWindowsRemovesNativeInstallDir() - { - // Verifies that uninstall removes every matching native CLI PATH entry. - string result = NativeCliInstaller.BuildPathWithoutInstallDirectory( - "C:\\npm;C:\\Users\\masamichi\\Programs\\uloop\\bin;C:\\Other;C:\\USERS\\MASAMICHI\\PROGRAMS\\ULOOP\\BIN", - "C:\\Users\\masamichi\\Programs\\uloop\\bin", - RuntimePlatform.WindowsEditor); - - Assert.That(result, Is.EqualTo("C:\\npm;C:\\Other")); - } - - [Test] - public void RemoveInstallDirectoryFromUserPath_OnWindowsUpdatesUserPath() - { - // Verifies that uninstall persists removal from Windows User PATH. - string capturedName = null; - string capturedValue = null; - System.EnvironmentVariableTarget capturedTarget = default; - - CliInstallResult result = NativeCliInstaller.RemoveInstallDirectoryFromUserPath( - "C:\\Users\\masamichi\\Programs\\uloop\\bin", - RuntimePlatform.WindowsEditor, - (name, target) => "C:\\npm;C:\\Users\\masamichi\\Programs\\uloop\\bin", - (name, value, target) => - { - capturedName = name; - capturedValue = value; - capturedTarget = target; - }); - - Assert.That(result.Success, Is.True); - Assert.That(capturedName, Is.EqualTo("Path")); - Assert.That(capturedValue, Is.EqualTo("C:\\npm")); - Assert.That(capturedTarget, Is.EqualTo(System.EnvironmentVariableTarget.User)); - } - - [Test] - public void RemoveInstallDirectoryFromUserPath_OnMacDoesNothing() - { - // Verifies that POSIX uninstalls do not attempt unsupported .NET User PATH writes. - bool wroteUserPath = false; - - CliInstallResult result = NativeCliInstaller.RemoveInstallDirectoryFromUserPath( - "/Users/masamichi/.local/bin", - RuntimePlatform.OSXEditor, - (name, target) => "/Users/masamichi/.local/bin", - (name, value, target) => { wroteUserPath = true; }); - - Assert.That(result.Success, Is.True); - Assert.That(wroteUserPath, Is.False); - } - - [Test] - public void UninstallGlobalCli_OnWindowsDeletesCommandAndEmptyNativeInstallTree() - { - // Verifies that uninstall removes the native CLI binary and empty package-owned directories. - string tempRoot = Path.Combine( - Path.GetTempPath(), - "uloop-native-installer-tests", - System.Guid.NewGuid().ToString("N")); - string nativeRoot = Path.Combine(tempRoot, "Programs", "uloop"); - string installDir = Path.Combine(nativeRoot, "bin"); - string installPath = NativeCliInstaller.GetGlobalCliInstallPath( - installDir, - RuntimePlatform.WindowsEditor); - string stagedInstallPath = Path.Combine(installDir, ".uloop.exe.install-test"); - - Directory.CreateDirectory(installDir); - File.WriteAllText(installPath, "native-binary"); - File.WriteAllText(stagedInstallPath, "staged-binary"); - - try - { - CliInstallResult result = NativeCliInstaller.UninstallGlobalCli( - installDir, - RuntimePlatform.WindowsEditor); - - Assert.That(result.Success, Is.True, result.ErrorOutput); - Assert.That(File.Exists(installPath), Is.False); - Assert.That(File.Exists(stagedInstallPath), Is.False); - Assert.That(Directory.Exists(nativeRoot), Is.False); - } - finally - { - if (Directory.Exists(tempRoot)) - { - Directory.Delete(tempRoot, true); - } - } - } - [Test] public void FinishSuccessfulInstall_WhenPathPersistenceFailsReturnsPathFailure() { @@ -506,7 +461,7 @@ public void FinishSuccessfulInstall_WhenPathPersistenceFailsReturnsPathFailure() CliInstallResult result = NativeCliInstaller.FinishSuccessfulInstall( new CliInstallResult(true, ""), - "C:\\Users\\masamichi\\Programs\\uloop\\bin", + "C:\\Users\\ExampleUser\\Programs\\uloop\\bin", RuntimePlatform.WindowsEditor, platform => { appliedCurrentPath = true; }, (installDirectory, platform) => new CliInstallResult(false, "path failed")); @@ -522,10 +477,10 @@ public void GetDefaultInstallDirectoryFromRoots_OnMacMatchesInstallerDefault() // Verifies that Unity mirrors the POSIX installer default install directory. string result = NativeCliInstaller.GetDefaultInstallDirectoryFromRoots( RuntimePlatform.OSXEditor, - "/Users/masamichi", + "/Users/ExampleUser", null); - Assert.That(result, Is.EqualTo(System.IO.Path.Combine("/Users/masamichi", ".local", "bin"))); + Assert.That(result, Is.EqualTo(System.IO.Path.Combine("/Users/ExampleUser", ".local", "bin"))); } [Test] @@ -535,52 +490,22 @@ public void GetDefaultInstallDirectoryFromRoots_OnWindowsMatchesInstallerDefault string result = NativeCliInstaller.GetDefaultInstallDirectoryFromRoots( RuntimePlatform.WindowsEditor, null, - "C:\\Users\\masamichi\\AppData\\Local"); + "C:\\Users\\ExampleUser\\AppData\\Local"); Assert.That(result, Is.EqualTo(System.IO.Path.Combine( - "C:\\Users\\masamichi\\AppData\\Local", + "C:\\Users\\ExampleUser\\AppData\\Local", "Programs", "uloop", "bin"))); } - [Test] - public void IsDefaultInstallDirectoryForCurrentUser_WhenWindowsDefaultDirectoryReturnsTrue() - { - // Verifies that uninstall can clean PATH entries for the package-owned default directory. - bool result = NativeCliInstaller.IsDefaultInstallDirectoryForCurrentUser( - System.IO.Path.Combine( - "C:\\Users\\masamichi\\AppData\\Local", - "Programs", - "uloop", - "bin"), - RuntimePlatform.WindowsEditor, - null, - "C:\\Users\\masamichi\\AppData\\Local"); - - Assert.That(result, Is.True); - } - - [Test] - public void IsDefaultInstallDirectoryForCurrentUser_WhenWindowsSharedDirectoryReturnsFalse() - { - // Verifies that uninstall preserves user-owned shared PATH directories such as C:\Tools. - bool result = NativeCliInstaller.IsDefaultInstallDirectoryForCurrentUser( - "C:\\Tools", - RuntimePlatform.WindowsEditor, - null, - "C:\\Users\\masamichi\\AppData\\Local"); - - Assert.That(result, Is.False); - } - [Test] public void IsPackageOwnedInstallPath_WhenExecutableMatchesInstallDirectoryReturnsTrue() { // Verifies that uninstall is available only for the package-owned command path. bool result = NativeCliInstaller.IsPackageOwnedInstallPath( - "C:/Users/masamichi/AppData/Local/Programs/uloop/bin/uloop.exe", - "C:\\Users\\masamichi\\AppData\\Local\\Programs\\uloop\\bin", + "C:/Users/ExampleUser/AppData/Local/Programs/uloop/bin/uloop.exe", + "C:\\Users\\ExampleUser\\AppData\\Local\\Programs\\uloop\\bin", RuntimePlatform.WindowsEditor); Assert.That(result, Is.True); @@ -592,53 +517,11 @@ public void IsPackageOwnedInstallPath_WhenExecutableIsSharedCommandReturnsFalse( // Verifies that same-version shared commands do not route the settings button to uninstall. bool result = NativeCliInstaller.IsPackageOwnedInstallPath( "C:\\Tools\\uloop.exe", - "C:\\Users\\masamichi\\AppData\\Local\\Programs\\uloop\\bin", + "C:\\Users\\ExampleUser\\AppData\\Local\\Programs\\uloop\\bin", RuntimePlatform.WindowsEditor); Assert.That(result, Is.False); } - [Test] - public void ShouldRemoveInstallDirectoryFromPath_WhenWindowsDefaultDirectoryReturnsTrue() - { - // Verifies that Windows uninstalls can remove the package-owned default PATH directory. - bool result = NativeCliInstaller.ShouldRemoveInstallDirectoryFromPath( - System.IO.Path.Combine( - "C:\\Users\\masamichi\\AppData\\Local", - "Programs", - "uloop", - "bin"), - RuntimePlatform.WindowsEditor, - null, - "C:\\Users\\masamichi\\AppData\\Local"); - - Assert.That(result, Is.True); - } - - [Test] - public void ShouldRemoveInstallDirectoryFromPath_WhenMacDefaultDirectoryReturnsFalse() - { - // Verifies that POSIX uninstalls preserve shared directories such as ~/.local/bin. - bool result = NativeCliInstaller.ShouldRemoveInstallDirectoryFromPath( - System.IO.Path.Combine("/Users/masamichi", ".local", "bin"), - RuntimePlatform.OSXEditor, - "/Users/masamichi", - null); - - Assert.That(result, Is.False); - } - - [Test] - public void ShouldRemoveInstallDirectoryFromPath_WhenWindowsSharedDirectoryReturnsFalse() - { - // Verifies that Windows uninstalls preserve user-owned shared PATH directories. - bool result = NativeCliInstaller.ShouldRemoveInstallDirectoryFromPath( - "C:\\Tools", - RuntimePlatform.WindowsEditor, - null, - "C:\\Users\\masamichi\\AppData\\Local"); - - Assert.That(result, Is.False); - } } } diff --git a/Assets/Tests/Editor/ToolSkillSynchronizerTests.cs b/Assets/Tests/Editor/ToolSkillSynchronizerTests.cs index b04061f53..93d9c3a3f 100644 --- a/Assets/Tests/Editor/ToolSkillSynchronizerTests.cs +++ b/Assets/Tests/Editor/ToolSkillSynchronizerTests.cs @@ -1706,7 +1706,7 @@ private static string CreateFakeProjectLocalSkill( string skillDir = Path.Combine( projectRoot, "Assets", - "Vision", + "SampleFeature", "Editor", "McpExtensions", toolDirectoryName); diff --git a/Packages/src/Cli~/internal/cli/completion_test.go b/Packages/src/Cli~/internal/cli/completion_test.go index fc3de34af..a9213fdfd 100644 --- a/Packages/src/Cli~/internal/cli/completion_test.go +++ b/Packages/src/Cli~/internal/cli/completion_test.go @@ -510,18 +510,18 @@ func TestGetHomeDirectoryForShellOnWindowsPowerShellIgnoresHomeOverride(t *testi "windows", func() (string, error) { environmentHomeCalls++ - return "/c/Users/masamichi", nil + return "/c/Users/ExampleUser", nil }, func() (string, error) { userHomeCalls++ - return `C:\Users\masamichi`, nil + return `C:\Users\ExampleUser`, nil }, ) if err != nil { t.Fatalf("getHomeDirectoryForShell failed: %v", err) } - if home != `C:\Users\masamichi` { + if home != `C:\Users\ExampleUser` { t.Fatalf("windows %s home mismatch: %s", shellName, home) } } @@ -543,18 +543,18 @@ func TestGetHomeDirectoryForShellOnWindowsBashNormalizesMsysHome(t *testing.T) { "windows", func() (string, error) { environmentHomeCalls++ - return "/c/Users/masamichi", nil + return "/c/Users/ExampleUser", nil }, func() (string, error) { userHomeCalls++ - return `C:\Users\masamichi`, nil + return `C:\Users\ExampleUser`, nil }, ) if err != nil { t.Fatalf("getHomeDirectoryForShell failed: %v", err) } - if home != `C:\Users\masamichi` { + if home != `C:\Users\ExampleUser` { t.Fatalf("windows bash home mismatch: %s", home) } if environmentHomeCalls != 1 { @@ -571,17 +571,17 @@ func TestGetHomeDirectoryForShellOnWindowsZshNormalizesWslHome(t *testing.T) { "zsh", "windows", func() (string, error) { - return "/mnt/c/Users/masamichi", nil + return "/mnt/c/Users/ExampleUser", nil }, func() (string, error) { - return `C:\Users\ignored`, nil + return `C:\Users\IgnoredUser`, nil }, ) if err != nil { t.Fatalf("getHomeDirectoryForShell failed: %v", err) } - if home != `C:\Users\masamichi` { + if home != `C:\Users\ExampleUser` { t.Fatalf("windows zsh home mismatch: %s", home) } } @@ -595,7 +595,7 @@ func TestGetHomeDirectoryForShellOnWindowsBashNormalizesWslDriveRoot(t *testing. return "/mnt/c/", nil }, func() (string, error) { - return `C:\Users\ignored`, nil + return `C:\Users\IgnoredUser`, nil }, ) if err != nil { diff --git a/Packages/src/Cli~/internal/cli/focus_test.go b/Packages/src/Cli~/internal/cli/focus_test.go index dd37591a2..aaca22337 100644 --- a/Packages/src/Cli~/internal/cli/focus_test.go +++ b/Packages/src/Cli~/internal/cli/focus_test.go @@ -6,9 +6,9 @@ import ( ) func TestParseMacUnityProcessesExtractsProjectPath(t *testing.T) { - output := `123 /Applications/Unity/Hub/Editor/6000.0.0f1/Unity.app/Contents/MacOS/Unity -projectPath "/Users/me/My Project" -useHub -hubIPC -456 /Applications/Unity/Hub/Editor/6000.0.0f1/Unity.app/Contents/MacOS/Unity -batchmode -projectPath "/Users/me/Batch" -789 /Applications/Unity/Hub/Editor/6000.0.0f1/Unity.app/Contents/MacOS/Unity -projectPath /Users/me/Other -logFile - + output := `123 /Applications/Unity/Hub/Editor/6000.0.0f1/Unity.app/Contents/MacOS/Unity -projectPath "/Users/ExampleUser/My Project" -useHub -hubIPC +456 /Applications/Unity/Hub/Editor/6000.0.0f1/Unity.app/Contents/MacOS/Unity -batchmode -projectPath "/Users/ExampleUser/Batch" +789 /Applications/Unity/Hub/Editor/6000.0.0f1/Unity.app/Contents/MacOS/Unity -projectPath /Users/ExampleUser/Other -logFile - ` processes := parseMacUnityProcesses(output) @@ -16,17 +16,17 @@ func TestParseMacUnityProcessesExtractsProjectPath(t *testing.T) { if len(processes) != 2 { t.Fatalf("process count mismatch: %#v", processes) } - if processes[0].pid != 123 || processes[0].projectPath != "/Users/me/My Project" { + if processes[0].pid != 123 || processes[0].projectPath != "/Users/ExampleUser/My Project" { t.Fatalf("first process mismatch: %#v", processes[0]) } - if processes[1].pid != 789 || processes[1].projectPath != "/Users/me/Other" { + if processes[1].pid != 789 || processes[1].projectPath != "/Users/ExampleUser/Other" { t.Fatalf("second process mismatch: %#v", processes[1]) } } func TestParseWindowsUnityProcessesExtractsProjectPath(t *testing.T) { - output := `123|C:\Program Files\Unity\Hub\Editor\6000.0.0f1\Editor\Unity.exe -projectPath "C:\Users\me\My Project" -useHub -456|C:\Program Files\Unity\Hub\Editor\6000.0.0f1\Editor\Unity.exe -batchmode -projectPath "C:\Users\me\Batch" + output := `123|C:\Program Files\Unity\Hub\Editor\6000.0.0f1\Editor\Unity.exe -projectPath "C:\Users\ExampleUser\My Project" -useHub +456|C:\Program Files\Unity\Hub\Editor\6000.0.0f1\Editor\Unity.exe -batchmode -projectPath "C:\Users\ExampleUser\Batch" ` processes := parseWindowsUnityProcesses(output) @@ -34,20 +34,20 @@ func TestParseWindowsUnityProcessesExtractsProjectPath(t *testing.T) { if len(processes) != 1 { t.Fatalf("process count mismatch: %#v", processes) } - if processes[0].pid != 123 || processes[0].projectPath != `C:\Users\me\My Project` { + if processes[0].pid != 123 || processes[0].projectPath != `C:\Users\ExampleUser\My Project` { t.Fatalf("process mismatch: %#v", processes[0]) } } func TestExtractProjectPathSupportsEqualsAndSpaces(t *testing.T) { cases := map[string]string{ - `Unity -projectPath="/Users/me/My Project" -useHub`: "/Users/me/My Project", - `Unity -projectpath '/Users/me/Other Project' -flag`: "/Users/me/Other Project", - `Unity -projectPath /Users/me/Plain -flag`: "/Users/me/Plain", - `Unity Hub -- --silent -- -projectPath /Users/me/vision-client-3/vision-client -cacheServerEnableUpload`: "/Users/me/vision-client-3/vision-client", - `Unity -projectPath /Users/me/vision-client-3/vision-client -acceptSoftwareTermsForThisRunOnly -useHub`: "/Users/me/vision-client-3/vision-client", - `Unity -projectPath /Users/me/vision-client-3/vision-client -cacheServerEnableDownload=false -useHub`: "/Users/me/vision-client-3/vision-client", - `Unity -projectPath /Users/me/vision-client-3/vision-client -hubSessionId 715810a5-220d-411e-a7d2-28cf46f`: "/Users/me/vision-client-3/vision-client", + `Unity -projectPath="/Users/ExampleUser/My Project" -useHub`: "/Users/ExampleUser/My Project", + `Unity -projectpath '/Users/ExampleUser/Other Project' -flag`: "/Users/ExampleUser/Other Project", + `Unity -projectPath /Users/ExampleUser/Plain -flag`: "/Users/ExampleUser/Plain", + `Unity Hub -- --silent -- -projectPath /Users/ExampleUser/SampleWorkspace/SampleUnityProject -cacheServerEnableUpload`: "/Users/ExampleUser/SampleWorkspace/SampleUnityProject", + `Unity -projectPath /Users/ExampleUser/SampleWorkspace/SampleUnityProject -acceptSoftwareTermsForThisRunOnly -useHub`: "/Users/ExampleUser/SampleWorkspace/SampleUnityProject", + `Unity -projectPath /Users/ExampleUser/SampleWorkspace/SampleUnityProject -cacheServerEnableDownload=false -useHub`: "/Users/ExampleUser/SampleWorkspace/SampleUnityProject", + `Unity -projectPath /Users/ExampleUser/SampleWorkspace/SampleUnityProject -hubSessionId 715810a5-220d-411e-a7d2-28cf46f`: "/Users/ExampleUser/SampleWorkspace/SampleUnityProject", } for command, expected := range cases { diff --git a/Packages/src/Cli~/internal/project/project_test.go b/Packages/src/Cli~/internal/project/project_test.go index af679e7b2..db741db83 100644 --- a/Packages/src/Cli~/internal/project/project_test.go +++ b/Packages/src/Cli~/internal/project/project_test.go @@ -35,9 +35,9 @@ func TestTrimTrailingSeparators_WhenWindowsPathIsNotRoot_ShouldRemoveTrailingSep } // Verifies that a normal Windows project path matches the Editor endpoint input. - path := trimTrailingSeparators(`C:\Users\booql\oss\unity-cli-loop\`) + path := trimTrailingSeparators(`C:\Users\ExampleUser\Projects\unity-cli-loop\`) - if path != `C:\Users\booql\oss\unity-cli-loop` { + if path != `C:\Users\ExampleUser\Projects\unity-cli-loop` { t.Fatalf("path should not keep trailing separator: %s", path) } } diff --git a/Packages/src/Cli~/internal/uninstall/command_test.go b/Packages/src/Cli~/internal/uninstall/command_test.go index fe54dd59b..15a039d62 100644 --- a/Packages/src/Cli~/internal/uninstall/command_test.go +++ b/Packages/src/Cli~/internal/uninstall/command_test.go @@ -8,7 +8,7 @@ import ( func TestCommandForDarwinRemovesUloopFromInstallDirectory(t *testing.T) { // Verifies macOS uninstall removes the launcher binary from the selected install directory. command, err := CommandForOS("darwin", Options{ - InstallDir: "/Users/test/.local/bin", + InstallDir: "/Users/ExampleUser/.local/bin", CurrentPID: 1234, }) if err != nil { @@ -19,13 +19,13 @@ func TestCommandForDarwinRemovesUloopFromInstallDirectory(t *testing.T) { t.Fatalf("command name mismatch: %s", command.Name) } joinedArgs := strings.Join(command.Args, " ") - if !strings.Contains(joinedArgs, "/Users/test/.local/bin/uloop") { + if !strings.Contains(joinedArgs, "/Users/ExampleUser/.local/bin/uloop") { t.Fatalf("target path missing: %s", joinedArgs) } if !strings.Contains(joinedArgs, "rm -f") { t.Fatalf("remove command missing: %s", joinedArgs) } - if command.TargetPath != "/Users/test/.local/bin/uloop" { + if command.TargetPath != "/Users/ExampleUser/.local/bin/uloop" { t.Fatalf("target path mismatch: %s", command.TargetPath) } } @@ -33,7 +33,7 @@ func TestCommandForDarwinRemovesUloopFromInstallDirectory(t *testing.T) { func TestCommandForWindowsSchedulesRemovalAfterCurrentProcessExits(t *testing.T) { // Verifies Windows uninstall defers deletion until the running launcher process exits. command, err := CommandForOS("windows", Options{ - InstallDir: `C:\Users\test\AppData\Local\Programs\uloop\bin`, + InstallDir: `C:\Users\ExampleUser\AppData\Local\Programs\uloop\bin`, CurrentPID: 5678, }) if err != nil { @@ -56,7 +56,7 @@ func TestCommandForWindowsSchedulesRemovalAfterCurrentProcessExits(t *testing.T) if !command.Deferred { t.Fatal("windows uninstall should be deferred") } - if command.TargetPath != `C:\Users\test\AppData\Local\Programs\uloop\bin\uloop.exe` { + if command.TargetPath != `C:\Users\ExampleUser\AppData\Local\Programs\uloop\bin\uloop.exe` { t.Fatalf("target path mismatch: %s", command.TargetPath) } } diff --git a/Packages/src/Editor/Infrastructure/CLI/NativeCliInstaller.cs b/Packages/src/Editor/Infrastructure/CLI/NativeCliInstaller.cs index 6bf114d12..6368ec3de 100644 --- a/Packages/src/Editor/Infrastructure/CLI/NativeCliInstaller.cs +++ b/Packages/src/Editor/Infrastructure/CLI/NativeCliInstaller.cs @@ -20,6 +20,9 @@ public static class NativeCliInstaller { private const int INSTALL_PROCESS_TIMEOUT_MS = 300000; private const int INSTALL_PROCESS_WAIT_SLICE_MS = 250; + private const int UNINSTALL_TARGET_REMOVAL_TIMEOUT_MS = 5000; + private const string WINDOWS_FILE_PATH_SEPARATOR = "\\"; + private const string POSIX_FILE_PATH_SEPARATOR = "/"; public static NativeCliInstallCommand GetInstallCommand( RuntimePlatform platform, @@ -142,9 +145,24 @@ public static async Task UninstallAsync(RuntimePlatform platfo } NativeCliInstallCommand command = BuildUninstallCommand(installDirectory, platform); - return await Task.Run( + CliInstallResult result = await Task.Run( () => RunUninstallCommand(command, installDirectory, ct, INSTALL_PROCESS_TIMEOUT_MS), ct); + if (!result.Success) + { + return result; + } + + string installPath = GetGlobalCliInstallPath(installDirectory, platform); + CliInstallResult removalResult = await WaitForUninstallTargetRemovalAsync( + installPath, + ct, + UNINSTALL_TARGET_REMOVAL_TIMEOUT_MS, + INSTALL_PROCESS_WAIT_SLICE_MS, + File.Exists, + Task.Delay); + + return removalResult.Success ? result : removalResult; } internal static NativeCliInstallCommand BuildUninstallCommand( @@ -286,49 +304,46 @@ private static CliInstallResult RunCliSetupCommand( : new CliInstallResult(false, BuildCliSetupCommandFailure(commandDescription, errorOutput, standardOutput)); } - internal static CliInstallResult UninstallGlobalCli(string installDirectory, RuntimePlatform platform) + internal static async Task WaitForUninstallTargetRemovalAsync( + string targetPath, + CancellationToken ct, + int timeoutMs, + int pollMs, + Func fileExists, + Func delayAsync) { - UnityEngine.Debug.Assert(!string.IsNullOrEmpty(installDirectory), "installDirectory must not be null or empty"); + UnityEngine.Debug.Assert(!string.IsNullOrWhiteSpace(targetPath), "targetPath must not be null or empty"); + UnityEngine.Debug.Assert(timeoutMs > 0, "timeoutMs must be greater than zero"); + UnityEngine.Debug.Assert(pollMs > 0, "pollMs must be greater than zero"); + UnityEngine.Debug.Assert(fileExists != null, "fileExists must not be null"); + UnityEngine.Debug.Assert(delayAsync != null, "delayAsync must not be null"); - try + int elapsedMs = 0; + while (fileExists(targetPath)) { - string installPath = GetGlobalCliInstallPath(installDirectory, platform); - if (File.Exists(installPath)) + ct.ThrowIfCancellationRequested(); + if (elapsedMs >= timeoutMs) { - File.Delete(installPath); + return new CliInstallResult( + false, + $"Timed out waiting for uLoop CLI uninstall to remove {targetPath}."); } - DeleteStagedInstallFiles(installDirectory, platform); - DeleteNativeInstallTreeIfEmpty(installDirectory); - return new CliInstallResult(true, ""); - } - catch (IOException ex) - { - return BuildCliUninstallFailure(ex); - } - catch (UnauthorizedAccessException ex) - { - return BuildCliUninstallFailure(ex); - } - catch (ArgumentException ex) - { - return BuildCliUninstallFailure(ex); - } - catch (NotSupportedException ex) - { - return BuildCliUninstallFailure(ex); - } - catch (SecurityException ex) - { - return BuildCliUninstallFailure(ex); + int delayMs = Math.Min(pollMs, timeoutMs - elapsedMs); + await delayAsync(delayMs, ct); + elapsedMs += delayMs; } + + return new CliInstallResult(true, ""); } internal static string GetGlobalCliInstallPath(string installDirectory, RuntimePlatform platform) { UnityEngine.Debug.Assert(!string.IsNullOrEmpty(installDirectory), "installDirectory must not be null or empty"); - return Path.Combine(installDirectory, GetGlobalCliInstallFileName(platform)); + string separator = GetFilePathSeparator(platform); + string normalizedInstallDirectory = installDirectory.TrimEnd('\\', '/'); + return normalizedInstallDirectory + separator + GetGlobalCliInstallFileName(platform); } internal static CliInstallResult PersistInstallDirectoryToUserPath( @@ -369,44 +384,6 @@ internal static CliInstallResult PersistInstallDirectoryToUserPath( } } - internal static CliInstallResult RemoveInstallDirectoryFromUserPath( - string installDirectory, - RuntimePlatform platform, - Func getEnvironmentVariable, - Action setEnvironmentVariable) - { - UnityEngine.Debug.Assert(!string.IsNullOrEmpty(installDirectory), "installDirectory must not be null or empty"); - UnityEngine.Debug.Assert(getEnvironmentVariable != null, "getEnvironmentVariable must not be null"); - UnityEngine.Debug.Assert(setEnvironmentVariable != null, "setEnvironmentVariable must not be null"); - - if (platform != RuntimePlatform.WindowsEditor) - { - return new CliInstallResult(true, ""); - } - - string pathVariableName = GetPathEnvironmentVariableName(platform); - try - { - string currentUserPath = getEnvironmentVariable(pathVariableName, EnvironmentVariableTarget.User); - string updatedUserPath = BuildPathWithoutInstallDirectory(currentUserPath, installDirectory, platform); - if (string.Equals(currentUserPath, updatedUserPath, GetPathComparison(platform))) - { - return new CliInstallResult(true, ""); - } - - setEnvironmentVariable(pathVariableName, updatedUserPath, EnvironmentVariableTarget.User); - return new CliInstallResult(true, ""); - } - catch (SecurityException ex) - { - return BuildUserPathRemovalFailure(ex); - } - catch (UnauthorizedAccessException ex) - { - return BuildUserPathRemovalFailure(ex); - } - } - internal static CliInstallResult FinishSuccessfulInstall( CliInstallResult installResult, string installDirectory, @@ -462,43 +439,6 @@ internal static string BuildPathWithInstallDirectory( return builder.ToString(); } - internal static string BuildPathWithoutInstallDirectory( - string currentPath, - string installDirectory, - RuntimePlatform platform) - { - UnityEngine.Debug.Assert(!string.IsNullOrWhiteSpace(installDirectory), "installDirectory must not be null or empty"); - - string normalizedPath = currentPath ?? ""; - if (string.IsNullOrEmpty(normalizedPath)) - { - return ""; - } - - string separator = GetPathSeparator(platform); - string[] entries = normalizedPath.Split( - new[] { separator }, - StringSplitOptions.RemoveEmptyEntries); - StringComparison comparison = GetPathComparison(platform); - StringBuilder builder = new(); - foreach (string entry in entries) - { - if (string.Equals(entry, installDirectory, comparison)) - { - continue; - } - - if (builder.Length > 0) - { - builder.Append(separator); - } - - builder.Append(entry); - } - - return builder.ToString(); - } - internal static string GetDefaultInstallDirectoryFromRoots( RuntimePlatform platform, string homeDirectory, @@ -529,29 +469,6 @@ internal static string GetDefaultInstallDirectoryFromRoots( CliConstants.NATIVE_INSTALL_BIN_DIR_NAME); } - internal static bool IsDefaultInstallDirectoryForCurrentUser( - string installDirectory, - RuntimePlatform platform, - string homeDirectory, - string localAppData) - { - UnityEngine.Debug.Assert(!string.IsNullOrWhiteSpace(installDirectory), "installDirectory must not be null or empty"); - - string defaultInstallDirectory = GetDefaultInstallDirectoryFromRoots( - platform, - homeDirectory, - localAppData); - if (string.IsNullOrWhiteSpace(defaultInstallDirectory)) - { - return false; - } - - return string.Equals( - installDirectory, - defaultInstallDirectory, - GetPathComparison(platform)); - } - private static void ApplyInstallDirectoryToCurrentProcessPath(RuntimePlatform platform) { string installDirectory = GetInstallDirectoryForCurrentUser(platform); @@ -566,58 +483,6 @@ private static void ApplyInstallDirectoryToCurrentProcessPath(RuntimePlatform pl Environment.SetEnvironmentVariable(pathVariableName, updatedPath); } - private static void RemoveInstallDirectoryFromCurrentProcessPath( - string installDirectory, - RuntimePlatform platform) - { - UnityEngine.Debug.Assert(!string.IsNullOrEmpty(installDirectory), "installDirectory must not be null or empty"); - - string pathVariableName = GetPathEnvironmentVariableName(platform); - string currentPath = Environment.GetEnvironmentVariable(pathVariableName); - string updatedPath = BuildPathWithoutInstallDirectory(currentPath, installDirectory, platform); - Environment.SetEnvironmentVariable(pathVariableName, updatedPath); - } - - private static bool ShouldRemoveInstallDirectoryFromPath( - string installDirectory, - RuntimePlatform platform) - { - UnityEngine.Debug.Assert(!string.IsNullOrWhiteSpace(installDirectory), "installDirectory must not be null or empty"); - - string homeDirectory = Environment.GetEnvironmentVariable(CliConstants.POSIX_HOME_ENVIRONMENT_VARIABLE); - if (string.IsNullOrWhiteSpace(homeDirectory)) - { - homeDirectory = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); - } - - string localAppData = Environment.GetEnvironmentVariable(CliConstants.WINDOWS_LOCAL_APPDATA_ENVIRONMENT_VARIABLE); - return ShouldRemoveInstallDirectoryFromPath( - installDirectory, - platform, - homeDirectory, - localAppData); - } - - internal static bool ShouldRemoveInstallDirectoryFromPath( - string installDirectory, - RuntimePlatform platform, - string homeDirectory, - string localAppData) - { - UnityEngine.Debug.Assert(!string.IsNullOrWhiteSpace(installDirectory), "installDirectory must not be null or empty"); - - if (platform != RuntimePlatform.WindowsEditor) - { - return false; - } - - return IsDefaultInstallDirectoryForCurrentUser( - installDirectory, - platform, - homeDirectory, - localAppData); - } - internal static bool IsPackageOwnedCurrentUserInstallPath( string executablePath, RuntimePlatform platform) @@ -697,6 +562,13 @@ private static string GetPathSeparator(RuntimePlatform platform) : CliConstants.POSIX_PATH_SEPARATOR; } + private static string GetFilePathSeparator(RuntimePlatform platform) + { + return platform == RuntimePlatform.WindowsEditor + ? WINDOWS_FILE_PATH_SEPARATOR + : POSIX_FILE_PATH_SEPARATOR; + } + private static StringComparison GetPathComparison(RuntimePlatform platform) { return platform == RuntimePlatform.WindowsEditor @@ -727,24 +599,6 @@ private static CliInstallResult BuildUserPathPersistenceFailure(Exception ex) return new CliInstallResult(false, errorOutput); } - private static CliInstallResult BuildUserPathRemovalFailure(Exception ex) - { - UnityEngine.Debug.Assert(ex != null, "ex must not be null"); - - string errorOutput = - "Removed the uLoop CLI binary, but failed to remove the uLoop CLI install directory from the Windows User PATH. " - + $"Update {CliConstants.WINDOWS_PATH_ENVIRONMENT_VARIABLE} manually.\n{ex.Message}"; - return new CliInstallResult(false, errorOutput); - } - - private static CliInstallResult BuildCliUninstallFailure(Exception ex) - { - UnityEngine.Debug.Assert(ex != null, "ex must not be null"); - - string errorOutput = $"Failed to uninstall CLI: {ex.Message}"; - return new CliInstallResult(false, errorOutput); - } - private static string BuildCliSetupCommandFailure( string commandDescription, string errorOutput, @@ -847,57 +701,6 @@ private static string GetGlobalCliInstallFileName(RuntimePlatform platform) : CliConstants.GLOBAL_UNIX_COMMAND_NAME; } - private static void DeleteStagedInstallFiles(string installDirectory, RuntimePlatform platform) - { - UnityEngine.Debug.Assert(!string.IsNullOrEmpty(installDirectory), "installDirectory must not be null or empty"); - - if (!Directory.Exists(installDirectory)) - { - return; - } - - string fileName = GetGlobalCliInstallFileName(platform); - string stagedFilePattern = $".{fileName}.install-*"; - string[] stagedInstallFiles = Directory.GetFiles(installDirectory, stagedFilePattern); - foreach (string stagedInstallFile in stagedInstallFiles) - { - File.Delete(stagedInstallFile); - } - } - - private static void DeleteNativeInstallTreeIfEmpty(string installDirectory) - { - UnityEngine.Debug.Assert(!string.IsNullOrEmpty(installDirectory), "installDirectory must not be null or empty"); - - DirectoryInfo installDirectoryInfo = new(installDirectory); - DirectoryInfo nativeInstallRoot = installDirectoryInfo.Parent; - if (nativeInstallRoot == null) - { - return; - } - - if (!string.Equals(installDirectoryInfo.Name, CliConstants.NATIVE_INSTALL_BIN_DIR_NAME, StringComparison.OrdinalIgnoreCase) - || !string.Equals(nativeInstallRoot.Name, CliConstants.NATIVE_INSTALL_DIR_NAME, StringComparison.OrdinalIgnoreCase)) - { - return; - } - - DeleteDirectoryIfEmpty(installDirectoryInfo.FullName); - DeleteDirectoryIfEmpty(nativeInstallRoot.FullName); - } - - private static void DeleteDirectoryIfEmpty(string directoryPath) - { - UnityEngine.Debug.Assert(!string.IsNullOrEmpty(directoryPath), "directoryPath must not be null or empty"); - - if (!Directory.Exists(directoryPath) || Directory.GetFileSystemEntries(directoryPath).Length > 0) - { - return; - } - - Directory.Delete(directoryPath); - } - private static string BuildPosixRemoteInstallScriptCommand(string scriptUrl, string releaseTag) { UnityEngine.Debug.Assert(!string.IsNullOrWhiteSpace(scriptUrl), "scriptUrl must not be null or empty");