From 1e1a04b7f2d56923afeaef7dfb5b7dd524fb9c54 Mon Sep 17 00:00:00 2001 From: hatayama Date: Tue, 14 Jul 2026 19:42:40 +0900 Subject: [PATCH 1/2] test: Pin ThirdPartyToolMigrationAssemblyUsageScanState aggregation behavior Add characterization coverage before Extract Class so scan-state transitions and reference aggregation stay observable without restructuring production code in the same commit. Co-authored-by: Cursor --- ...oolMigrationAssemblyUsageScanStateTests.cs | 277 ++++++++++++++++++ ...grationAssemblyUsageScanStateTests.cs.meta | 11 + 2 files changed, 288 insertions(+) create mode 100644 Assets/Tests/Editor/ThirdPartyToolMigrationAssemblyUsageScanStateTests.cs create mode 100644 Assets/Tests/Editor/ThirdPartyToolMigrationAssemblyUsageScanStateTests.cs.meta diff --git a/Assets/Tests/Editor/ThirdPartyToolMigrationAssemblyUsageScanStateTests.cs b/Assets/Tests/Editor/ThirdPartyToolMigrationAssemblyUsageScanStateTests.cs new file mode 100644 index 0000000000..ed7e5eee22 --- /dev/null +++ b/Assets/Tests/Editor/ThirdPartyToolMigrationAssemblyUsageScanStateTests.cs @@ -0,0 +1,277 @@ +using System.Collections.Generic; + +using NUnit.Framework; + +using io.github.hatayama.UnityCliLoop.Infrastructure; + +namespace io.github.hatayama.UnityCliLoop.Tests.Editor +{ + /// + /// Characterization tests that pin the current scan-state aggregation behavior before Extract Class. + /// + public sealed class ThirdPartyToolMigrationAssemblyUsageScanStateTests + { + private const string ProjectRoot = "/Project"; + private const string AssemblyDirectory = "/Project/Assets/VendorTools"; + private const string SourceFilePath = "/Project/Assets/VendorTools/Foo.cs"; + + private static ThirdPartyToolMigrationAssemblyUsageScanState CreateScanState() + { + return new ThirdPartyToolMigrationAssemblyUsageScanState( + ProjectRoot, + new List { AssemblyDirectory }, + new List()); + } + + [Test] + public void Constructor_WhenCreated_ExposesGivenAsmdefAndAssemblyReferenceDirectories() + { + // Verifies that scan-state construction stores caller-provided directory lists without copying. + List asmdefDirectories = new() { AssemblyDirectory }; + List assemblyReferenceDirectories = new() + { + new AssemblyReferenceDirectory("/Project/Assets/VendorTools/Sub", AssemblyDirectory) + }; + + ThirdPartyToolMigrationAssemblyUsageScanState scanState = new( + ProjectRoot, + asmdefDirectories, + assemblyReferenceDirectories); + + Assert.That(scanState.AsmdefDirectories, Is.SameAs(asmdefDirectories)); + Assert.That(scanState.AssemblyReferenceDirectories, Is.SameAs(assemblyReferenceDirectories)); + } + + [Test] + public void RecordInitialSourceFacts_WhenSourceHasNoMigrationCandidateText_ReturnsFalseAndRecordsNothing() + { + // Verifies that unrelated source text leaves every aggregation set untouched. + ThirdPartyToolMigrationAssemblyUsageScanState scanState = CreateScanState(); + + bool recorded = scanState.RecordInitialSourceFacts( + "public class Foo { }", + SourceFilePath); + + Assert.That(recorded, Is.False); + Assert.That(scanState.LegacyAssemblyDirectories, Is.Empty); + Assert.That(scanState.AssemblyDeclaredTypeNamesByDirectory, Is.Empty); + } + + [Test] + public void RecordInitialSourceFacts_WhenSourceContainsLegacyNamespaceUsage_RecordsLegacyDirectoryAndDeclaredTypeNames() + { + // Verifies that legacy namespace text marks the owning assembly as legacy and captures declared types. + ThirdPartyToolMigrationAssemblyUsageScanState scanState = CreateScanState(); + + bool recorded = scanState.RecordInitialSourceFacts( + "using io.github.hatayama.uLoopMCP;\npublic class Foo { }\n", + SourceFilePath); + + Assert.That(recorded, Is.True); + Assert.That(scanState.LegacyAssemblyDirectories, Is.EquivalentTo(new[] { AssemblyDirectory })); + Assert.That( + scanState.AssemblyDeclaredTypeNamesByDirectory[AssemblyDirectory], + Is.EquivalentTo(new[] { "Foo" })); + } + + [Test] + public void RecordInitialSourceFacts_WhenSourceHasGlobalUsingAliasForLegacyNamespace_RecordsAssemblyScopedAliasAndDirectory() + { + // Verifies that a global-using alias for the legacy namespace is captured per assembly directory. + ThirdPartyToolMigrationAssemblyUsageScanState scanState = CreateScanState(); + + scanState.RecordInitialSourceFacts( + "global using LegacyAlias = io.github.hatayama.uLoopMCP;\n", + SourceFilePath); + + Assert.That(scanState.AssemblyScopedLegacyDirectories, Is.EquivalentTo(new[] { AssemblyDirectory })); + Assert.That( + scanState.AssemblyScopedLegacyAliasesByDirectory[AssemblyDirectory], + Is.EquivalentTo(new[] { "LegacyAlias" })); + } + + [Test] + public void RecordInitialSourceFacts_WhenSourceHasGlobalUsingForCurrentDomainNamespace_RecordsDomainScopeAndReference() + { + // Verifies that a global using for the current domain namespace scopes the assembly and adds a reference. + ThirdPartyToolMigrationAssemblyUsageScanState scanState = CreateScanState(); + + scanState.RecordInitialSourceFacts( + "global using io.github.hatayama.UnityCliLoop.Domain;\n", + SourceFilePath); + + Assert.That( + scanState.AssemblyScopedCurrentDomainDirectories, + Is.EquivalentTo(new[] { AssemblyDirectory })); + Assert.That( + scanState.DomainReferenceAssemblyDirectories, + Is.EquivalentTo(new[] { AssemblyDirectory })); + } + + [Test] + public void RecordInitialSourceFacts_WhenSourceHasGlobalUsingForCurrentApplicationNamespace_RecordsApplicationScopeAndReference() + { + // Verifies that a global using for the current application namespace scopes the assembly and adds a reference. + ThirdPartyToolMigrationAssemblyUsageScanState scanState = CreateScanState(); + + scanState.RecordInitialSourceFacts( + "global using io.github.hatayama.UnityCliLoop.Application;\n", + SourceFilePath); + + Assert.That( + scanState.AssemblyScopedCurrentApplicationDirectories, + Is.EquivalentTo(new[] { AssemblyDirectory })); + Assert.That( + scanState.ApplicationReferenceAssemblyDirectories, + Is.EquivalentTo(new[] { AssemblyDirectory })); + } + + [Test] + public void RecordInitialSourceFacts_WhenSourceHasGlobalUsingForCurrentFirstPartyToolsNamespace_RecordsFirstPartyScopeAndReference() + { + // Verifies that a global using for the first-party tools namespace scopes the assembly and adds a reference. + ThirdPartyToolMigrationAssemblyUsageScanState scanState = CreateScanState(); + + scanState.RecordInitialSourceFacts( + "global using io.github.hatayama.UnityCliLoop.FirstPartyTools;\n", + SourceFilePath); + + Assert.That( + scanState.AssemblyScopedCurrentFirstPartyToolsDirectories, + Is.EquivalentTo(new[] { AssemblyDirectory })); + Assert.That( + scanState.FirstPartyScreenshotReferenceAssemblyDirectories, + Is.EquivalentTo(new[] { AssemblyDirectory })); + } + + [Test] + public void RecordInitialSourceFacts_WhenSourceContainsBareCustomToolManager_ReturnsTrueWithoutToolContractsReference() + { + // why: CustomToolManager is a migration-candidate fragment, but bare registrar API recording requires legacy assembly/namespace context + ThirdPartyToolMigrationAssemblyUsageScanState scanState = CreateScanState(); + + bool recorded = scanState.RecordInitialSourceFacts( + "public void Foo() { CustomToolManager.Register(); }", + SourceFilePath); + + Assert.That(recorded, Is.True); + Assert.That(scanState.ToolContractsReferenceAssemblyDirectories, Is.Empty); + } + + [Test] + public void RecordTargetScanInitialSourceFacts_WhenSourceContainsLegacyRegistrarApi_DoesNotRecordToolContractsReference() + { + // Verifies that the fast target scan skips registrar-API-based tool-contracts requirement recording. + ThirdPartyToolMigrationAssemblyUsageScanState scanState = CreateScanState(); + + scanState.RecordTargetScanInitialSourceFacts( + "public void Foo() { CustomToolManager.Register(); }", + SourceFilePath); + + Assert.That(scanState.ToolContractsReferenceAssemblyDirectories, Is.Empty); + } + + [Test] + public void RecordInitialSourceFacts_WhenSourceContainsCurrentDomainMetadataApi_RecordsDomainAndToolContractsReferences() + { + // Verifies that the full initial scan records both domain and tool-contracts references for domain metadata usage. + ThirdPartyToolMigrationAssemblyUsageScanState scanState = CreateScanState(); + + scanState.RecordInitialSourceFacts( + "object info = io.github.hatayama.UnityCliLoop.Domain.ToolInfo.Empty;", + SourceFilePath); + + Assert.That( + scanState.DomainReferenceAssemblyDirectories, + Is.EquivalentTo(new[] { AssemblyDirectory })); + Assert.That( + scanState.ToolContractsReferenceAssemblyDirectories, + Is.EquivalentTo(new[] { AssemblyDirectory })); + } + + [Test] + public void RecordTargetScanInitialSourceFacts_WhenSourceContainsCurrentDomainMetadataApi_RecordsNeitherDomainNorToolContractsReferences() + { + // Verifies that the fast target scan skips domain-metadata-based reference recording entirely. + ThirdPartyToolMigrationAssemblyUsageScanState scanState = CreateScanState(); + + scanState.RecordTargetScanInitialSourceFacts( + "object info = io.github.hatayama.UnityCliLoop.Domain.ToolInfo.Empty;", + SourceFilePath); + + Assert.That(scanState.DomainReferenceAssemblyDirectories, Is.Empty); + Assert.That(scanState.ToolContractsReferenceAssemblyDirectories, Is.Empty); + } + + [Test] + public void RecordInitialSourceFacts_WhenSourceContainsCurrentRegistrarApi_CreateUsageBridgesRegistrarIntoApplicationReference() + { + // Verifies that CreateUsage folds registrar-owning assemblies into the application reference set. + ThirdPartyToolMigrationAssemblyUsageScanState scanState = CreateScanState(); + scanState.RecordInitialSourceFacts( + "public void Foo() { UnityCliLoopToolRegistrar.Register(); }", + SourceFilePath); + + MigrationAssemblyUsage usage = scanState.CreateUsage(); + + Assert.That( + usage.ApplicationReferenceAssemblyDirectories, + Is.EquivalentTo(new[] { AssemblyDirectory })); + } + + [Test] + public void RecordInitialSourceFacts_WhenSourceContainsCurrentRegistrarApi_CreateReferenceRequirementUsageDoesNotBridgeRegistrar() + { + // Verifies that CreateReferenceRequirementUsage does not fold registrar-owning assemblies into the application set. + ThirdPartyToolMigrationAssemblyUsageScanState scanState = CreateScanState(); + scanState.RecordInitialSourceFacts( + "public void Foo() { UnityCliLoopToolRegistrar.Register(); }", + SourceFilePath); + + MigrationAssemblyUsage usage = scanState.CreateReferenceRequirementUsage(); + + Assert.That(usage.ApplicationReferenceAssemblyDirectories, Is.Empty); + } + + [Test] + public void RecordReferenceRequirements_WhenSourceHasNoMigrationCandidateText_RecordsNothing() + { + // Verifies that reference-requirement scanning ignores source without any migration candidate text. + ThirdPartyToolMigrationAssemblyUsageScanState scanState = CreateScanState(); + + scanState.RecordReferenceRequirements("public class Foo { }", SourceFilePath); + + Assert.That(scanState.ToolContractsReferenceAssemblyDirectories, Is.Empty); + Assert.That(scanState.HasReferenceRequirements, Is.False); + } + + [Test] + public void RecordReferenceRequirements_WhenSourceContainsCurrentRegistrarDomainReturnApi_RecordsToolContractsReference() + { + // Verifies that a current registrar domain-return call is recorded as a tool-contracts requirement. + ThirdPartyToolMigrationAssemblyUsageScanState scanState = CreateScanState(); + + scanState.RecordReferenceRequirements( + "var tools = UnityCliLoopToolRegistrar.GetRegisteredCustomTools();", + SourceFilePath); + + Assert.That( + scanState.ToolContractsReferenceAssemblyDirectories, + Is.EquivalentTo(new[] { AssemblyDirectory })); + Assert.That(scanState.HasReferenceRequirements, Is.True); + } + + [Test] + public void CreateUsage_WhenNothingRecorded_ReturnsEmptyUsageWithGivenDirectories() + { + // Verifies that CreateUsage on a fresh scan state yields an otherwise-empty usage snapshot. + ThirdPartyToolMigrationAssemblyUsageScanState scanState = CreateScanState(); + + MigrationAssemblyUsage usage = scanState.CreateUsage(); + + Assert.That(usage.AsmdefDirectories, Is.EquivalentTo(new[] { AssemblyDirectory })); + Assert.That(usage.LegacyAssemblyDirectories, Is.Empty); + Assert.That(usage.ToolContractsReferenceAssemblyDirectories, Is.Empty); + } + } +} diff --git a/Assets/Tests/Editor/ThirdPartyToolMigrationAssemblyUsageScanStateTests.cs.meta b/Assets/Tests/Editor/ThirdPartyToolMigrationAssemblyUsageScanStateTests.cs.meta new file mode 100644 index 0000000000..d80bee57e8 --- /dev/null +++ b/Assets/Tests/Editor/ThirdPartyToolMigrationAssemblyUsageScanStateTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 812724901c23a4d37af04f9b1cd9a585 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: From 6e54152a77acb7ca6c901c5cf810cbb1ca546bef Mon Sep 17 00:00:00 2001 From: hatayama Date: Tue, 14 Jul 2026 19:46:05 +0900 Subject: [PATCH 2/2] refactor: extract Infrastructure scan/probe helpers under 500 lines Extract Class: move asmdef reference-requirement recording out of ThirdPartyToolMigrationAssemblyUsageScanState and move login-shell CLI probe command/parsing out of CliInstallationDetector. No behavior change; characterization and existing CliInstallationDetector tests stay green. Co-authored-by: Cursor --- .../Editor/CliInstallationDetectorTests.cs | 18 +- .../CLI/CliInstallationDetector.cs | 258 +---------------- .../CLI/CliShellInstallationProbe.cs | 268 ++++++++++++++++++ .../CLI/CliShellInstallationProbe.cs.meta | 11 + ...semblyUsageReferenceRequirementRecorder.cs | 258 +++++++++++++++++ ...yUsageReferenceRequirementRecorder.cs.meta | 11 + ...artyToolMigrationAssemblyUsageScanState.cs | 219 +------------- 7 files changed, 564 insertions(+), 479 deletions(-) create mode 100644 Packages/src/Editor/Infrastructure/CLI/CliShellInstallationProbe.cs create mode 100644 Packages/src/Editor/Infrastructure/CLI/CliShellInstallationProbe.cs.meta create mode 100644 Packages/src/Editor/Infrastructure/ThirdPartyToolMigration/ThirdPartyToolMigrationAssemblyUsageReferenceRequirementRecorder.cs create mode 100644 Packages/src/Editor/Infrastructure/ThirdPartyToolMigration/ThirdPartyToolMigrationAssemblyUsageReferenceRequirementRecorder.cs.meta diff --git a/Assets/Tests/Editor/CliInstallationDetectorTests.cs b/Assets/Tests/Editor/CliInstallationDetectorTests.cs index 3b94f0eab2..a927b75f25 100644 --- a/Assets/Tests/Editor/CliInstallationDetectorTests.cs +++ b/Assets/Tests/Editor/CliInstallationDetectorTests.cs @@ -108,7 +108,7 @@ public void SelectPreferredDetection_WhenShellVersionExistsWithoutPathUsesShellV public void BuildShellCliDetectionCommand_UsesShortVersionFlag() { // Verifies that shell detection asks the command itself for its terminal-visible version. - string command = CliInstallationDetector.BuildShellCliDetectionCommand("uloop"); + string command = CliShellInstallationProbe.BuildShellCliDetectionCommand("uloop"); Assert.That(command, Does.Contain("command -v uloop")); Assert.That(command, Does.Contain("uloop --version --json")); @@ -123,7 +123,7 @@ public void BuildShellCliDetectionCommand_UsesShortVersionFlag() public void BuildShellCliDetectionCommandForShell_WhenRuntimeShellIsFish_UsesFishStatusSyntax() { // Verifies that command syntax follows the actual shell process, not PATH setup support. - string command = CliInstallationDetector.BuildShellCliDetectionCommandForShell( + string command = CliShellInstallationProbe.BuildShellCliDetectionCommandForShell( "uloop", "/opt/homebrew/bin/fish"); @@ -154,7 +154,7 @@ public void ParseShellCliInstallationOutput_WhenPathAndVersionExist_ReturnsDetec + "__ULOOP_VERSION_STATUS_END__\n"; CliInstallationDetection detection = - CliInstallationDetector.ParseShellCliInstallationOutput(output); + CliShellInstallationProbe.ParseShellCliInstallationOutput(output); Assert.That(detection.Version, Is.EqualTo("2.1.1")); Assert.That(detection.IsDispatcher, Is.False); @@ -182,7 +182,7 @@ public void ParseShellCliInstallationOutput_WhenLegacyCliJsonExists_ReturnsVersi + "__ULOOP_VERSION_STATUS_END__\n"; CliInstallationDetection detection = - CliInstallationDetector.ParseShellCliInstallationOutput(output); + CliShellInstallationProbe.ParseShellCliInstallationOutput(output); Assert.That(detection.Version, Is.EqualTo("3.0.0-beta.31")); Assert.That(detection.IsDispatcher, Is.False); @@ -210,7 +210,7 @@ public void ParseShellCliInstallationOutput_WhenDispatcherJsonExists_ReturnsDisp + "__ULOOP_VERSION_STATUS_END__\n"; CliInstallationDetection detection = - CliInstallationDetector.ParseShellCliInstallationOutput(output); + CliShellInstallationProbe.ParseShellCliInstallationOutput(output); Assert.That(detection.Version, Is.EqualTo("3.0.0")); Assert.That(detection.IsDispatcher, Is.True); @@ -225,7 +225,7 @@ public void IsShellDetectionUsableForPathSetup_WhenLegacyCliVersionIsHigh_Return "3.0.0-beta.40", "/Users/ExampleUser/.npm-global/bin/uloop"); - bool result = CliInstallationDetector.IsShellDetectionUsableForPathSetup( + bool result = CliShellInstallationProbe.IsShellDetectionUsableForPathSetup( detection, UnityEngine.RuntimePlatform.OSXEditor, (path, platform) => false, @@ -255,7 +255,7 @@ public void ParseShellCliInstallationOutput_WhenProtocolVersionIsOutsideIntRange + "__ULOOP_VERSION_STATUS_END__\n"; CliInstallationDetection detection = - CliInstallationDetector.ParseShellCliInstallationOutput(output); + CliShellInstallationProbe.ParseShellCliInstallationOutput(output); Assert.That(detection.Version, Is.EqualTo("3.0.0-beta.31")); Assert.That(detection.ExecutablePath, Is.EqualTo("/tmp/uloop")); @@ -275,7 +275,7 @@ public void ParseShellCliInstallationOutput_WhenOnlyVersionExists_ReturnsInstall + "__ULOOP_VERSION_STATUS_END__\n"; CliInstallationDetection detection = - CliInstallationDetector.ParseShellCliInstallationOutput(output); + CliShellInstallationProbe.ParseShellCliInstallationOutput(output); Assert.That(detection.Version, Is.EqualTo("2.1.1")); Assert.That(detection.ExecutablePath, Is.Null); @@ -296,7 +296,7 @@ public void ParseShellCliInstallationOutput_WhenVersionCommandFails_ReturnsPathW + "__ULOOP_VERSION_STATUS_END__\n"; CliInstallationDetection detection = - CliInstallationDetector.ParseShellCliInstallationOutput(output); + CliShellInstallationProbe.ParseShellCliInstallationOutput(output); Assert.That(detection.Version, Is.Null); Assert.That(detection.ExecutablePath, Is.EqualTo("/Users/ExampleUser/.npm-global/bin/uloop")); diff --git a/Packages/src/Editor/Infrastructure/CLI/CliInstallationDetector.cs b/Packages/src/Editor/Infrastructure/CLI/CliInstallationDetector.cs index 52d819f9e1..6168cc7965 100644 --- a/Packages/src/Editor/Infrastructure/CLI/CliInstallationDetector.cs +++ b/Packages/src/Editor/Infrastructure/CLI/CliInstallationDetector.cs @@ -3,8 +3,6 @@ using System.IO; using System.Threading; using System.Threading.Tasks; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; using UnityEngine; using io.github.hatayama.UnityCliLoop.Application; @@ -32,21 +30,6 @@ public CliInstallationDetection(string version, string executablePath, bool isDi /// public sealed class CliInstallationDetector : ICliInstallationDetector { - private const string SHELL_PATH_START_MARKER = "__ULOOP_PATH_START__"; - private const string SHELL_PATH_END_MARKER = "__ULOOP_PATH_END__"; - private const string SHELL_VERSION_START_MARKER = "__ULOOP_VERSION_START__"; - private const string SHELL_VERSION_END_MARKER = "__ULOOP_VERSION_END__"; - private const string SHELL_VERSION_STATUS_START_MARKER = "__ULOOP_VERSION_STATUS_START__"; - private const string SHELL_VERSION_STATUS_END_MARKER = "__ULOOP_VERSION_STATUS_END__"; - private const string SHELL_CONTRACT_START_MARKER = "__ULOOP_CONTRACT_START__"; - private const string SHELL_CONTRACT_END_MARKER = "__ULOOP_CONTRACT_END__"; - private const string SHELL_CONTRACT_STATUS_START_MARKER = "__ULOOP_CONTRACT_STATUS_START__"; - private const string SHELL_CONTRACT_STATUS_END_MARKER = "__ULOOP_CONTRACT_STATUS_END__"; - private const string SHELL_SUCCESS_EXIT_CODE = "0"; - private const string VERSION_JSON_PROJECT_RUNNER_VERSION_PROPERTY = "ProjectRunnerVersion"; - private const string VERSION_JSON_LEGACY_CLI_VERSION_PROPERTY = "CliVersion"; - private const string VERSION_JSON_DISPATCHER_VERSION_PROPERTY = "DispatcherVersion"; - private readonly ICliPinReader _cliPinReader; private string _cachedCliVersion; @@ -133,7 +116,7 @@ public Task IsCliVisibleFromShellAsync(RuntimePlatform platform, Cancellat () => { CliInstallationDetection detection = DetectShellCliInstallationBlocking(platform, ct); - return IsShellDetectionUsableForPathSetup( + return CliShellInstallationProbe.IsShellDetectionUsableForPathSetup( detection, platform, NativeCliInstallPathResolver.IsPackageOwnedCurrentUserInstallPath, @@ -209,7 +192,7 @@ private static CliInstallationDetection DetectShellCliInstallationFromLoginShell { string shell = NodeEnvironmentResolver.GetUserShell(); CliPathSetupPlan pathSetupPlan = CliPathSetupProfileResolver.ResolveCurrentUserPlan(platform); - ProcessStartInfo startInfo = BuildShellCliDetectionStartInfo( + ProcessStartInfo startInfo = CliShellInstallationProbe.BuildShellCliDetectionStartInfo( shell, platform, pathSetupPlan, @@ -219,240 +202,7 @@ private static CliInstallationDetection DetectShellCliInstallationFromLoginShell string output = commandResult == null ? null : string.Join(Environment.NewLine, commandResult.StandardOutputLines).Trim(); - return ParseShellCliInstallationOutput(output); - } - - internal static ProcessStartInfo BuildShellCliDetectionStartInfo( - string shell, - RuntimePlatform platform, - CliPathSetupPlan pathSetupPlan, - string currentPath) - { - UnityEngine.Debug.Assert(!string.IsNullOrWhiteSpace(shell), "shell must not be null or empty"); - - ProcessStartInfo startInfo = new() - { - FileName = shell, - Arguments = "-l -i -c " + QuoteProcessArgument(BuildShellCliDetectionCommandForShell( - CliConstants.EXECUTABLE_NAME, - shell)), - UseShellExecute = false, - RedirectStandardOutput = true, - RedirectStandardError = true, - CreateNoWindow = true - }; - if (platform != RuntimePlatform.WindowsEditor) - { - startInfo.EnvironmentVariables[CliConstants.POSIX_PATH_ENVIRONMENT_VARIABLE] = - NativeCliInstallPathResolver.BuildPathWithoutInstallDirectory( - currentPath, - pathSetupPlan.InstallDirectory, - platform); - } - - return startInfo; - } - - internal static bool IsShellDetectionUsableForPathSetup( - CliInstallationDetection detection, - RuntimePlatform platform, - Func isPackageOwnedCurrentUserInstallPath, - string minimumDispatcherVersion) - { - UnityEngine.Debug.Assert(isPackageOwnedCurrentUserInstallPath != null, "isPackageOwnedCurrentUserInstallPath must not be null"); - UnityEngine.Debug.Assert(!string.IsNullOrEmpty(minimumDispatcherVersion), "minimumDispatcherVersion must not be null or empty"); - - if (isPackageOwnedCurrentUserInstallPath(detection.ExecutablePath, platform)) - { - return true; - } - - if (!detection.IsDispatcher) - { - return false; - } - - return CliVersionComparer.IsVersionGreaterThanOrEqual( - detection.Version, - minimumDispatcherVersion); - } - - internal static string BuildShellCliDetectionCommandForShell( - string executableName, - string shellPath) - { - string shellName = string.IsNullOrWhiteSpace(shellPath) - ? string.Empty - : Path.GetFileName(shellPath.Trim()).ToLowerInvariant(); - if (string.Equals(shellName, "fish", StringComparison.Ordinal)) - { - return BuildFishShellCliDetectionCommand(executableName); - } - - return BuildShellCliDetectionCommand(executableName); - } - - internal static string BuildShellCliDetectionCommand(string executableName) - { - UnityEngine.Debug.Assert(!string.IsNullOrEmpty(executableName), "executableName must not be null or empty"); - - return "echo " + SHELL_PATH_START_MARKER + "\n" - + "command -v " + executableName + "\n" - + "echo " + SHELL_PATH_END_MARKER + "\n" - + "echo " + SHELL_CONTRACT_START_MARKER + "\n" - + executableName + " " + CliConstants.VERSION_FLAG + " " + CliConstants.JSON_FLAG + "\n" - + "uloop_contract_status=$?\n" - + "echo " + SHELL_CONTRACT_END_MARKER + "\n" - + "echo " + SHELL_CONTRACT_STATUS_START_MARKER + "\n" - + "echo $uloop_contract_status\n" - + "echo " + SHELL_CONTRACT_STATUS_END_MARKER + "\n" - + "echo " + SHELL_VERSION_START_MARKER + "\n" - + executableName + " " + CliConstants.SHORT_VERSION_FLAG + "\n" - + "uloop_version_status=$?\n" - + "echo " + SHELL_VERSION_END_MARKER + "\n" - + "echo " + SHELL_VERSION_STATUS_START_MARKER + "\n" - + "echo $uloop_version_status\n" - + "echo " + SHELL_VERSION_STATUS_END_MARKER; - } - - private static string BuildFishShellCliDetectionCommand(string executableName) - { - UnityEngine.Debug.Assert(!string.IsNullOrEmpty(executableName), "executableName must not be null or empty"); - - return "echo " + SHELL_PATH_START_MARKER + "\n" - + "command -v " + executableName + "\n" - + "echo " + SHELL_PATH_END_MARKER + "\n" - + "echo " + SHELL_CONTRACT_START_MARKER + "\n" - + executableName + " " + CliConstants.VERSION_FLAG + " " + CliConstants.JSON_FLAG + "\n" - + "set uloop_contract_status $status\n" - + "echo " + SHELL_CONTRACT_END_MARKER + "\n" - + "echo " + SHELL_CONTRACT_STATUS_START_MARKER + "\n" - + "echo $uloop_contract_status\n" - + "echo " + SHELL_CONTRACT_STATUS_END_MARKER + "\n" - + "echo " + SHELL_VERSION_START_MARKER + "\n" - + executableName + " " + CliConstants.SHORT_VERSION_FLAG + "\n" - + "set uloop_version_status $status\n" - + "echo " + SHELL_VERSION_END_MARKER + "\n" - + "echo " + SHELL_VERSION_STATUS_START_MARKER + "\n" - + "echo $uloop_version_status\n" - + "echo " + SHELL_VERSION_STATUS_END_MARKER; - } - - internal static CliInstallationDetection ParseShellCliInstallationOutput(string output) - { - string pathBlock = NodeEnvironmentResolver.ExtractBetweenMarkers( - output, - SHELL_PATH_START_MARKER, - SHELL_PATH_END_MARKER); - string versionBlock = NodeEnvironmentResolver.ExtractBetweenMarkers( - output, - SHELL_VERSION_START_MARKER, - SHELL_VERSION_END_MARKER); - string versionStatusBlock = NodeEnvironmentResolver.ExtractBetweenMarkers( - output, - SHELL_VERSION_STATUS_START_MARKER, - SHELL_VERSION_STATUS_END_MARKER); - string contractBlock = NodeEnvironmentResolver.ExtractBetweenMarkers( - output, - SHELL_CONTRACT_START_MARKER, - SHELL_CONTRACT_END_MARKER); - string contractStatusBlock = NodeEnvironmentResolver.ExtractBetweenMarkers( - output, - SHELL_CONTRACT_STATUS_START_MARKER, - SHELL_CONTRACT_STATUS_END_MARKER); - string executablePath = NodeEnvironmentResolver.ExtractAbsolutePathLine(pathBlock); - if (IsSuccessfulShellStatus(contractStatusBlock)) - { - CliInstallationDetection contractDetection = ParseCliContractOutput(contractBlock, executablePath); - if (!string.IsNullOrEmpty(contractDetection.Version)) - { - return contractDetection; - } - } - - string version = IsSuccessfulShellStatus(versionStatusBlock) - ? ExtractFirstNonEmptyLine(versionBlock) - : null; - return new CliInstallationDetection(version, executablePath); - } - - private static CliInstallationDetection ParseCliContractOutput(string output, string executablePath) - { - string jsonLine = ExtractFirstNonEmptyLine(output); - if (string.IsNullOrEmpty(jsonLine)) - { - return new CliInstallationDetection(null, executablePath); - } - - try - { - JObject parsed = JObject.Parse(jsonLine); - CliInstallationDetection dispatcherDetection = ParseDispatcherContract(parsed, executablePath); - if (!string.IsNullOrEmpty(dispatcherDetection.Version)) - { - return dispatcherDetection; - } - - string version = parsed[VERSION_JSON_PROJECT_RUNNER_VERSION_PROPERTY]?.ToString(); - if (string.IsNullOrEmpty(version)) - { - version = parsed[VERSION_JSON_LEGACY_CLI_VERSION_PROPERTY]?.ToString(); - } - return new CliInstallationDetection(version, executablePath); - } - catch (JsonException) - { - return new CliInstallationDetection(null, executablePath); - } - } - - private static CliInstallationDetection ParseDispatcherContract(JObject parsed, string executablePath) - { - // Why: dispatcher compatibility is enforced by the pin's minimumDispatcherVersion (semver floor), - // so identifying the dispatcher only needs its release version. - string dispatcherVersion = parsed[VERSION_JSON_DISPATCHER_VERSION_PROPERTY]?.ToString(); - if (string.IsNullOrEmpty(dispatcherVersion)) - { - return new CliInstallationDetection(null, executablePath); - } - - return new CliInstallationDetection( - dispatcherVersion, - executablePath, - true); - } - - private static bool IsSuccessfulShellStatus(string statusBlock) - { - string status = ExtractFirstNonEmptyLine(statusBlock); - return string.Equals(status, SHELL_SUCCESS_EXIT_CODE, StringComparison.Ordinal); - } - - private static string ExtractFirstNonEmptyLine(string block) - { - if (string.IsNullOrEmpty(block)) - { - return null; - } - - string[] lines = block.Split('\n'); - foreach (string rawLine in lines) - { - string line = rawLine.Trim(); - if (!string.IsNullOrEmpty(line)) - { - return line; - } - } - - return null; - } - - private static string QuoteProcessArgument(string value) - { - UnityEngine.Debug.Assert(value != null, "value must not be null"); - - return "\"" + value.Replace("\\", "\\\\").Replace("\"", "\\\"") + "\""; + return CliShellInstallationProbe.ParseShellCliInstallationOutput(output); } private static CliInstallationDetection DetectCliInstallationAtExecutablePath( @@ -461,7 +211,7 @@ private static CliInstallationDetection DetectCliInstallationAtExecutablePath( { string fileName = executablePath ?? CliConstants.EXECUTABLE_NAME; string contractOutput = ExecuteCliVersionCommand(fileName, CliConstants.VERSION_FLAG + " " + CliConstants.JSON_FLAG, ct); - CliInstallationDetection contractDetection = ParseCliContractOutput(contractOutput, executablePath); + CliInstallationDetection contractDetection = CliShellInstallationProbe.ParseCliContractOutput(contractOutput, executablePath); if (!string.IsNullOrEmpty(contractDetection.Version)) { return contractDetection; diff --git a/Packages/src/Editor/Infrastructure/CLI/CliShellInstallationProbe.cs b/Packages/src/Editor/Infrastructure/CLI/CliShellInstallationProbe.cs new file mode 100644 index 0000000000..3429742e2e --- /dev/null +++ b/Packages/src/Editor/Infrastructure/CLI/CliShellInstallationProbe.cs @@ -0,0 +1,268 @@ +using System; +using System.Diagnostics; +using System.IO; + +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +using UnityEngine; + +using io.github.hatayama.UnityCliLoop.Application; +using io.github.hatayama.UnityCliLoop.Domain; + +namespace io.github.hatayama.UnityCliLoop.Infrastructure +{ + /// + /// Builds login-shell CLI probe commands and parses their marker-delimited output. + /// + internal static class CliShellInstallationProbe + { + private const string SHELL_PATH_START_MARKER = "__ULOOP_PATH_START__"; + private const string SHELL_PATH_END_MARKER = "__ULOOP_PATH_END__"; + private const string SHELL_VERSION_START_MARKER = "__ULOOP_VERSION_START__"; + private const string SHELL_VERSION_END_MARKER = "__ULOOP_VERSION_END__"; + private const string SHELL_VERSION_STATUS_START_MARKER = "__ULOOP_VERSION_STATUS_START__"; + private const string SHELL_VERSION_STATUS_END_MARKER = "__ULOOP_VERSION_STATUS_END__"; + private const string SHELL_CONTRACT_START_MARKER = "__ULOOP_CONTRACT_START__"; + private const string SHELL_CONTRACT_END_MARKER = "__ULOOP_CONTRACT_END__"; + private const string SHELL_CONTRACT_STATUS_START_MARKER = "__ULOOP_CONTRACT_STATUS_START__"; + private const string SHELL_CONTRACT_STATUS_END_MARKER = "__ULOOP_CONTRACT_STATUS_END__"; + private const string SHELL_SUCCESS_EXIT_CODE = "0"; + private const string VERSION_JSON_PROJECT_RUNNER_VERSION_PROPERTY = "ProjectRunnerVersion"; + private const string VERSION_JSON_LEGACY_CLI_VERSION_PROPERTY = "CliVersion"; + private const string VERSION_JSON_DISPATCHER_VERSION_PROPERTY = "DispatcherVersion"; + + public static ProcessStartInfo BuildShellCliDetectionStartInfo( + string shell, + RuntimePlatform platform, + CliPathSetupPlan pathSetupPlan, + string currentPath) + { + UnityEngine.Debug.Assert(!string.IsNullOrWhiteSpace(shell), "shell must not be null or empty"); + + ProcessStartInfo startInfo = new() + { + FileName = shell, + Arguments = "-l -i -c " + QuoteProcessArgument(BuildShellCliDetectionCommandForShell( + CliConstants.EXECUTABLE_NAME, + shell)), + UseShellExecute = false, + RedirectStandardOutput = true, + RedirectStandardError = true, + CreateNoWindow = true + }; + if (platform != RuntimePlatform.WindowsEditor) + { + startInfo.EnvironmentVariables[CliConstants.POSIX_PATH_ENVIRONMENT_VARIABLE] = + NativeCliInstallPathResolver.BuildPathWithoutInstallDirectory( + currentPath, + pathSetupPlan.InstallDirectory, + platform); + } + + return startInfo; + } + + public static bool IsShellDetectionUsableForPathSetup( + CliInstallationDetection detection, + RuntimePlatform platform, + Func isPackageOwnedCurrentUserInstallPath, + string minimumDispatcherVersion) + { + UnityEngine.Debug.Assert(isPackageOwnedCurrentUserInstallPath != null, "isPackageOwnedCurrentUserInstallPath must not be null"); + UnityEngine.Debug.Assert(!string.IsNullOrEmpty(minimumDispatcherVersion), "minimumDispatcherVersion must not be null or empty"); + + if (isPackageOwnedCurrentUserInstallPath(detection.ExecutablePath, platform)) + { + return true; + } + + if (!detection.IsDispatcher) + { + return false; + } + + return CliVersionComparer.IsVersionGreaterThanOrEqual( + detection.Version, + minimumDispatcherVersion); + } + + public static string BuildShellCliDetectionCommandForShell( + string executableName, + string shellPath) + { + string shellName = string.IsNullOrWhiteSpace(shellPath) + ? string.Empty + : Path.GetFileName(shellPath.Trim()).ToLowerInvariant(); + if (string.Equals(shellName, "fish", StringComparison.Ordinal)) + { + return BuildFishShellCliDetectionCommand(executableName); + } + + return BuildShellCliDetectionCommand(executableName); + } + + public static string BuildShellCliDetectionCommand(string executableName) + { + UnityEngine.Debug.Assert(!string.IsNullOrEmpty(executableName), "executableName must not be null or empty"); + + return "echo " + SHELL_PATH_START_MARKER + "\n" + + "command -v " + executableName + "\n" + + "echo " + SHELL_PATH_END_MARKER + "\n" + + "echo " + SHELL_CONTRACT_START_MARKER + "\n" + + executableName + " " + CliConstants.VERSION_FLAG + " " + CliConstants.JSON_FLAG + "\n" + + "uloop_contract_status=$?\n" + + "echo " + SHELL_CONTRACT_END_MARKER + "\n" + + "echo " + SHELL_CONTRACT_STATUS_START_MARKER + "\n" + + "echo $uloop_contract_status\n" + + "echo " + SHELL_CONTRACT_STATUS_END_MARKER + "\n" + + "echo " + SHELL_VERSION_START_MARKER + "\n" + + executableName + " " + CliConstants.SHORT_VERSION_FLAG + "\n" + + "uloop_version_status=$?\n" + + "echo " + SHELL_VERSION_END_MARKER + "\n" + + "echo " + SHELL_VERSION_STATUS_START_MARKER + "\n" + + "echo $uloop_version_status\n" + + "echo " + SHELL_VERSION_STATUS_END_MARKER; + } + + private static string BuildFishShellCliDetectionCommand(string executableName) + { + UnityEngine.Debug.Assert(!string.IsNullOrEmpty(executableName), "executableName must not be null or empty"); + + return "echo " + SHELL_PATH_START_MARKER + "\n" + + "command -v " + executableName + "\n" + + "echo " + SHELL_PATH_END_MARKER + "\n" + + "echo " + SHELL_CONTRACT_START_MARKER + "\n" + + executableName + " " + CliConstants.VERSION_FLAG + " " + CliConstants.JSON_FLAG + "\n" + + "set uloop_contract_status $status\n" + + "echo " + SHELL_CONTRACT_END_MARKER + "\n" + + "echo " + SHELL_CONTRACT_STATUS_START_MARKER + "\n" + + "echo $uloop_contract_status\n" + + "echo " + SHELL_CONTRACT_STATUS_END_MARKER + "\n" + + "echo " + SHELL_VERSION_START_MARKER + "\n" + + executableName + " " + CliConstants.SHORT_VERSION_FLAG + "\n" + + "set uloop_version_status $status\n" + + "echo " + SHELL_VERSION_END_MARKER + "\n" + + "echo " + SHELL_VERSION_STATUS_START_MARKER + "\n" + + "echo $uloop_version_status\n" + + "echo " + SHELL_VERSION_STATUS_END_MARKER; + } + + public static CliInstallationDetection ParseShellCliInstallationOutput(string output) + { + string pathBlock = NodeEnvironmentResolver.ExtractBetweenMarkers( + output, + SHELL_PATH_START_MARKER, + SHELL_PATH_END_MARKER); + string versionBlock = NodeEnvironmentResolver.ExtractBetweenMarkers( + output, + SHELL_VERSION_START_MARKER, + SHELL_VERSION_END_MARKER); + string versionStatusBlock = NodeEnvironmentResolver.ExtractBetweenMarkers( + output, + SHELL_VERSION_STATUS_START_MARKER, + SHELL_VERSION_STATUS_END_MARKER); + string contractBlock = NodeEnvironmentResolver.ExtractBetweenMarkers( + output, + SHELL_CONTRACT_START_MARKER, + SHELL_CONTRACT_END_MARKER); + string contractStatusBlock = NodeEnvironmentResolver.ExtractBetweenMarkers( + output, + SHELL_CONTRACT_STATUS_START_MARKER, + SHELL_CONTRACT_STATUS_END_MARKER); + string executablePath = NodeEnvironmentResolver.ExtractAbsolutePathLine(pathBlock); + if (IsSuccessfulShellStatus(contractStatusBlock)) + { + CliInstallationDetection contractDetection = ParseCliContractOutput(contractBlock, executablePath); + if (!string.IsNullOrEmpty(contractDetection.Version)) + { + return contractDetection; + } + } + + string version = IsSuccessfulShellStatus(versionStatusBlock) + ? ExtractFirstNonEmptyLine(versionBlock) + : null; + return new CliInstallationDetection(version, executablePath); + } + + public static CliInstallationDetection ParseCliContractOutput(string output, string executablePath) + { + string jsonLine = ExtractFirstNonEmptyLine(output); + if (string.IsNullOrEmpty(jsonLine)) + { + return new CliInstallationDetection(null, executablePath); + } + + try + { + JObject parsed = JObject.Parse(jsonLine); + CliInstallationDetection dispatcherDetection = ParseDispatcherContract(parsed, executablePath); + if (!string.IsNullOrEmpty(dispatcherDetection.Version)) + { + return dispatcherDetection; + } + + string version = parsed[VERSION_JSON_PROJECT_RUNNER_VERSION_PROPERTY]?.ToString(); + if (string.IsNullOrEmpty(version)) + { + version = parsed[VERSION_JSON_LEGACY_CLI_VERSION_PROPERTY]?.ToString(); + } + return new CliInstallationDetection(version, executablePath); + } + catch (JsonException) + { + return new CliInstallationDetection(null, executablePath); + } + } + + private static CliInstallationDetection ParseDispatcherContract(JObject parsed, string executablePath) + { + // Why: dispatcher compatibility is enforced by the pin's minimumDispatcherVersion (semver floor), + // so identifying the dispatcher only needs its release version. + string dispatcherVersion = parsed[VERSION_JSON_DISPATCHER_VERSION_PROPERTY]?.ToString(); + if (string.IsNullOrEmpty(dispatcherVersion)) + { + return new CliInstallationDetection(null, executablePath); + } + + return new CliInstallationDetection( + dispatcherVersion, + executablePath, + true); + } + + private static bool IsSuccessfulShellStatus(string statusBlock) + { + string status = ExtractFirstNonEmptyLine(statusBlock); + return string.Equals(status, SHELL_SUCCESS_EXIT_CODE, StringComparison.Ordinal); + } + + private static string ExtractFirstNonEmptyLine(string block) + { + if (string.IsNullOrEmpty(block)) + { + return null; + } + + string[] lines = block.Split('\n'); + foreach (string rawLine in lines) + { + string line = rawLine.Trim(); + if (!string.IsNullOrEmpty(line)) + { + return line; + } + } + + return null; + } + + private static string QuoteProcessArgument(string value) + { + UnityEngine.Debug.Assert(value != null, "value must not be null"); + + return "\"" + value.Replace("\\", "\\\\").Replace("\"", "\\\"") + "\""; + } + } +} diff --git a/Packages/src/Editor/Infrastructure/CLI/CliShellInstallationProbe.cs.meta b/Packages/src/Editor/Infrastructure/CLI/CliShellInstallationProbe.cs.meta new file mode 100644 index 0000000000..9cc61a29e6 --- /dev/null +++ b/Packages/src/Editor/Infrastructure/CLI/CliShellInstallationProbe.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5adfd558b96847638990c9dcf3a51025 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/src/Editor/Infrastructure/ThirdPartyToolMigration/ThirdPartyToolMigrationAssemblyUsageReferenceRequirementRecorder.cs b/Packages/src/Editor/Infrastructure/ThirdPartyToolMigration/ThirdPartyToolMigrationAssemblyUsageReferenceRequirementRecorder.cs new file mode 100644 index 0000000000..f408393bd6 --- /dev/null +++ b/Packages/src/Editor/Infrastructure/ThirdPartyToolMigration/ThirdPartyToolMigrationAssemblyUsageReferenceRequirementRecorder.cs @@ -0,0 +1,258 @@ +using System.Diagnostics; + +using static io.github.hatayama.UnityCliLoop.Infrastructure.ThirdPartyToolMigrationAssemblyScopedNameMap; + +using io.github.hatayama.UnityCliLoop.Domain; + +namespace io.github.hatayama.UnityCliLoop.Infrastructure +{ + /// + /// Records asmdef reference requirements from a single source file against scan-state collections. + /// + internal static class ThirdPartyToolMigrationAssemblyUsageReferenceRequirementRecorder + { + public static void Record( + ThirdPartyToolMigrationAssemblyUsageScanState scanState, + string source, + string assemblyDirectory) + { + Debug.Assert(scanState != null, "scanState must not be null"); + Debug.Assert(source != null, "source must not be null"); + Debug.Assert(!string.IsNullOrEmpty(assemblyDirectory), "assemblyDirectory must not be null or empty"); + + string[] legacyAssemblyAliases = GetAssemblyScopedNames( + scanState.AssemblyScopedLegacyAliasesByDirectory, + assemblyDirectory); + string[] assemblyDeclaredTypeNames = GetAssemblyScopedNames( + scanState.AssemblyDeclaredTypeNamesByDirectory, + assemblyDirectory); + bool hasLegacyCSharpApi = ThirdPartyToolMigrationRules.ContainsLegacyCSharpApi(source); + RecordToolContractsRequirement( + scanState, + source, + assemblyDirectory, + legacyAssemblyAliases, + assemblyDeclaredTypeNames, + hasLegacyCSharpApi); + RecordRegistrarDomainReturnRequirement(scanState, source, assemblyDirectory, legacyAssemblyAliases); + RecordDomainContractRequirement(scanState, source, assemblyDirectory); + RecordFirstPartyScreenshotRequirement( + scanState, + source, + assemblyDirectory, + legacyAssemblyAliases, + assemblyDeclaredTypeNames, + hasLegacyCSharpApi); + RecordEditorWindowCaptureTimeoutRequirement( + scanState, + source, + assemblyDirectory, + legacyAssemblyAliases, + assemblyDeclaredTypeNames, + hasLegacyCSharpApi); + } + + private static void RecordToolContractsRequirement( + ThirdPartyToolMigrationAssemblyUsageScanState scanState, + string source, + string assemblyDirectory, + string[] legacyAssemblyAliases, + string[] assemblyDeclaredTypeNames, + bool hasLegacyCSharpApi) + { + string[] currentApplicationAssemblyAliases = GetAssemblyScopedNames( + scanState.AssemblyScopedCurrentApplicationAliasesByDirectory, + assemblyDirectory); + bool hasCurrentApplicationSourceTarget = + ThirdPartyToolMigrationRules.ContainsCurrentApplicationApiForAssembly( + source, + scanState.AssemblyScopedCurrentApplicationDirectories.Contains(assemblyDirectory), + currentApplicationAssemblyAliases, + assemblyDeclaredTypeNames); + if (!ContainsToolContractsReferenceRequirement( + scanState, + source, + assemblyDirectory, + legacyAssemblyAliases, + currentApplicationAssemblyAliases, + assemblyDeclaredTypeNames, + hasLegacyCSharpApi, + hasCurrentApplicationSourceTarget)) + { + return; + } + + scanState.ToolContractsReferenceAssemblyDirectories.Add(assemblyDirectory); + } + + private static bool ContainsToolContractsReferenceRequirement( + ThirdPartyToolMigrationAssemblyUsageScanState scanState, + string source, + string assemblyDirectory, + string[] legacyAssemblyAliases, + string[] currentApplicationAssemblyAliases, + string[] assemblyDeclaredTypeNames, + bool hasLegacyCSharpApi, + bool hasCurrentApplicationSourceTarget) + { + bool canUseLegacyAssemblyApi = + scanState.AssemblyScopedLegacyDirectories.Contains(assemblyDirectory) || + hasLegacyCSharpApi || + ThirdPartyToolMigrationRules.ContainsCurrentToolContractsApi(source) || + scanState.AssemblyScopedCurrentToolContractsDirectories.Contains(assemblyDirectory) || + scanState.AssemblyScopedCurrentApplicationDirectories.Contains(assemblyDirectory); + return ThirdPartyToolMigrationRules.ContainsLegacyRegistrarApiForAssembly( + source, + scanState.AssemblyScopedLegacyDirectories.Contains(assemblyDirectory), + legacyAssemblyAliases) || + ThirdPartyToolMigrationRules.ContainsLegacyApplicationApiForAssembly( + source, + canUseLegacyAssemblyApi, + legacyAssemblyAliases, + currentApplicationAssemblyAliases, + assemblyDeclaredTypeNames) || + hasCurrentApplicationSourceTarget; + } + + private static void RecordRegistrarDomainReturnRequirement( + ThirdPartyToolMigrationAssemblyUsageScanState scanState, + string source, + string assemblyDirectory, + string[] legacyAssemblyAliases) + { + if (!ThirdPartyToolMigrationRules.ContainsRegistrarDomainReturnApiForAssembly( + source, + scanState.AssemblyScopedLegacyDirectories.Contains(assemblyDirectory), + legacyAssemblyAliases)) + { + return; + } + + scanState.ToolContractsReferenceAssemblyDirectories.Add(assemblyDirectory); + } + + private static void RecordDomainContractRequirement( + ThirdPartyToolMigrationAssemblyUsageScanState scanState, + string source, + string assemblyDirectory) + { + string[] currentDomainAssemblyAliases = GetAssemblyScopedNames( + scanState.AssemblyScopedCurrentDomainAliasesByDirectory, + assemblyDirectory); + string[] currentDomainNamespaceAliases = + ThirdPartyToolMigrationAliasRules.GetCombinedCurrentDomainNamespaceAliases( + source, + currentDomainAssemblyAliases); + if (!ThirdPartyToolMigrationRules.ContainsCurrentDomainMetadataApiForAssembly( + source, + scanState.AssemblyScopedCurrentDomainDirectories.Contains(assemblyDirectory)) && + !ThirdPartyToolMigrationRules.ContainsCurrentDomainContractAliasReference( + source, + currentDomainNamespaceAliases)) + { + return; + } + + scanState.ToolContractsReferenceAssemblyDirectories.Add(assemblyDirectory); + } + + private static void RecordFirstPartyScreenshotRequirement( + ThirdPartyToolMigrationAssemblyUsageScanState scanState, + string source, + string assemblyDirectory, + string[] legacyAssemblyAliases, + string[] assemblyDeclaredTypeNames, + bool hasLegacyCSharpApi) + { + string[] currentFirstPartyToolsAssemblyAliases = GetAssemblyScopedNames( + scanState.AssemblyScopedCurrentFirstPartyToolsAliasesByDirectory, + assemblyDirectory); + bool hasCurrentFirstPartyScreenshotReferenceRequirement = + ThirdPartyToolMigrationRules.ContainsCurrentFirstPartyScreenshotApiForAssembly( + source, + scanState.AssemblyScopedCurrentFirstPartyToolsDirectories.Contains(assemblyDirectory), + currentFirstPartyToolsAssemblyAliases, + assemblyDeclaredTypeNames); + if (ContainsFirstPartyScreenshotToolContractsRequirement( + scanState, + source, + assemblyDirectory, + legacyAssemblyAliases, + currentFirstPartyToolsAssemblyAliases, + assemblyDeclaredTypeNames, + hasLegacyCSharpApi, + hasCurrentFirstPartyScreenshotReferenceRequirement)) + { + scanState.ToolContractsReferenceAssemblyDirectories.Add(assemblyDirectory); + } + + if (hasCurrentFirstPartyScreenshotReferenceRequirement) + { + scanState.FirstPartyScreenshotReferenceAssemblyDirectories.Add(assemblyDirectory); + } + } + + private static bool ContainsFirstPartyScreenshotToolContractsRequirement( + ThirdPartyToolMigrationAssemblyUsageScanState scanState, + string source, + string assemblyDirectory, + string[] legacyAssemblyAliases, + string[] currentFirstPartyToolsAssemblyAliases, + string[] assemblyDeclaredTypeNames, + bool hasLegacyCSharpApi, + bool hasCurrentFirstPartyScreenshotReferenceRequirement) + { + bool hasAssemblyScopedCurrentToolContractsUsing = + scanState.AssemblyScopedCurrentToolContractsDirectories.Contains(assemblyDirectory); + bool hasAssemblyScopedCurrentFirstPartyToolsUsing = + scanState.AssemblyScopedCurrentFirstPartyToolsDirectories.Contains(assemblyDirectory); + return ThirdPartyToolMigrationRules.ContainsLegacyFirstPartyScreenshotApiForAssembly( + source, + scanState.AssemblyScopedLegacyDirectories.Contains(assemblyDirectory) || + hasLegacyCSharpApi || + ThirdPartyToolMigrationRules.ContainsCurrentToolContractsApi(source) || + hasAssemblyScopedCurrentToolContractsUsing, + legacyAssemblyAliases, + assemblyDeclaredTypeNames) || + ThirdPartyToolMigrationRules.ContainsLegacyEditorWindowCaptureUtilityMigrationForAssembly( + source, + scanState.AssemblyScopedLegacyDirectories.Contains(assemblyDirectory) || hasLegacyCSharpApi, + hasAssemblyScopedCurrentToolContractsUsing, + hasAssemblyScopedCurrentFirstPartyToolsUsing, + legacyAssemblyAliases, + currentFirstPartyToolsAssemblyAliases, + assemblyDeclaredTypeNames) || + ThirdPartyToolMigrationRules.ContainsCurrentFirstPartyScreenshotContractApiForAssembly( + source, + hasAssemblyScopedCurrentFirstPartyToolsUsing, + currentFirstPartyToolsAssemblyAliases, + assemblyDeclaredTypeNames) || + hasCurrentFirstPartyScreenshotReferenceRequirement; + } + + private static void RecordEditorWindowCaptureTimeoutRequirement( + ThirdPartyToolMigrationAssemblyUsageScanState scanState, + string source, + string assemblyDirectory, + string[] legacyAssemblyAliases, + string[] assemblyDeclaredTypeNames, + bool hasLegacyCSharpApi) + { + if (!ThirdPartyToolMigrationRules.ContainsLegacyEditorWindowCaptureUtilityTimeoutMigrationForAssembly( + source, + scanState.AssemblyScopedLegacyDirectories.Contains(assemblyDirectory) || hasLegacyCSharpApi, + scanState.AssemblyScopedCurrentToolContractsDirectories.Contains(assemblyDirectory), + scanState.AssemblyScopedCurrentFirstPartyToolsDirectories.Contains(assemblyDirectory), + legacyAssemblyAliases, + GetAssemblyScopedNames( + scanState.AssemblyScopedCurrentFirstPartyToolsAliasesByDirectory, + assemblyDirectory), + assemblyDeclaredTypeNames)) + { + return; + } + + scanState.ToolContractsReferenceAssemblyDirectories.Add(assemblyDirectory); + } + } +} diff --git a/Packages/src/Editor/Infrastructure/ThirdPartyToolMigration/ThirdPartyToolMigrationAssemblyUsageReferenceRequirementRecorder.cs.meta b/Packages/src/Editor/Infrastructure/ThirdPartyToolMigration/ThirdPartyToolMigrationAssemblyUsageReferenceRequirementRecorder.cs.meta new file mode 100644 index 0000000000..0fc4ad22de --- /dev/null +++ b/Packages/src/Editor/Infrastructure/ThirdPartyToolMigration/ThirdPartyToolMigrationAssemblyUsageReferenceRequirementRecorder.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d231db7e7c4c4bc0be5ae98261e4d23c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/src/Editor/Infrastructure/ThirdPartyToolMigration/ThirdPartyToolMigrationAssemblyUsageScanState.cs b/Packages/src/Editor/Infrastructure/ThirdPartyToolMigration/ThirdPartyToolMigrationAssemblyUsageScanState.cs index b0b55b060b..8c881ca693 100644 --- a/Packages/src/Editor/Infrastructure/ThirdPartyToolMigration/ThirdPartyToolMigrationAssemblyUsageScanState.cs +++ b/Packages/src/Editor/Infrastructure/ThirdPartyToolMigration/ThirdPartyToolMigrationAssemblyUsageScanState.cs @@ -131,33 +131,10 @@ public void RecordReferenceRequirements(string source, string csharpFilePath) } string assemblyDirectory = FindAssemblyDirectory(csharpFilePath); - string[] legacyAssemblyAliases = GetAssemblyScopedNames( - AssemblyScopedLegacyAliasesByDirectory, - assemblyDirectory); - string[] assemblyDeclaredTypeNames = GetAssemblyScopedNames( - AssemblyDeclaredTypeNamesByDirectory, - assemblyDirectory); - bool hasLegacyCSharpApi = ThirdPartyToolMigrationRules.ContainsLegacyCSharpApi(source); - RecordToolContractsRequirement( - source, - assemblyDirectory, - legacyAssemblyAliases, - assemblyDeclaredTypeNames, - hasLegacyCSharpApi); - RecordRegistrarDomainReturnRequirement(source, assemblyDirectory, legacyAssemblyAliases); - RecordDomainContractRequirement(source, assemblyDirectory); - RecordFirstPartyScreenshotRequirement( - source, - assemblyDirectory, - legacyAssemblyAliases, - assemblyDeclaredTypeNames, - hasLegacyCSharpApi); - RecordEditorWindowCaptureTimeoutRequirement( + ThirdPartyToolMigrationAssemblyUsageReferenceRequirementRecorder.Record( + this, source, - assemblyDirectory, - legacyAssemblyAliases, - assemblyDeclaredTypeNames, - hasLegacyCSharpApi); + assemblyDirectory); } public MigrationAssemblyUsage CreateUsage() @@ -353,196 +330,6 @@ private void RecordDomainMetadataSourceFacts(string source, string assemblyDirec } } - private void RecordToolContractsRequirement( - string source, - string assemblyDirectory, - string[] legacyAssemblyAliases, - string[] assemblyDeclaredTypeNames, - bool hasLegacyCSharpApi) - { - string[] currentApplicationAssemblyAliases = GetAssemblyScopedNames( - AssemblyScopedCurrentApplicationAliasesByDirectory, - assemblyDirectory); - bool hasCurrentApplicationSourceTarget = - ThirdPartyToolMigrationRules.ContainsCurrentApplicationApiForAssembly( - source, - AssemblyScopedCurrentApplicationDirectories.Contains(assemblyDirectory), - currentApplicationAssemblyAliases, - assemblyDeclaredTypeNames); - if (!ContainsToolContractsReferenceRequirement( - source, - assemblyDirectory, - legacyAssemblyAliases, - currentApplicationAssemblyAliases, - assemblyDeclaredTypeNames, - hasLegacyCSharpApi, - hasCurrentApplicationSourceTarget)) - { - return; - } - - ToolContractsReferenceAssemblyDirectories.Add(assemblyDirectory); - } - - private bool ContainsToolContractsReferenceRequirement( - string source, - string assemblyDirectory, - string[] legacyAssemblyAliases, - string[] currentApplicationAssemblyAliases, - string[] assemblyDeclaredTypeNames, - bool hasLegacyCSharpApi, - bool hasCurrentApplicationSourceTarget) - { - bool canUseLegacyAssemblyApi = - AssemblyScopedLegacyDirectories.Contains(assemblyDirectory) || - hasLegacyCSharpApi || - ThirdPartyToolMigrationRules.ContainsCurrentToolContractsApi(source) || - AssemblyScopedCurrentToolContractsDirectories.Contains(assemblyDirectory) || - AssemblyScopedCurrentApplicationDirectories.Contains(assemblyDirectory); - return ThirdPartyToolMigrationRules.ContainsLegacyRegistrarApiForAssembly( - source, - AssemblyScopedLegacyDirectories.Contains(assemblyDirectory), - legacyAssemblyAliases) || - ThirdPartyToolMigrationRules.ContainsLegacyApplicationApiForAssembly( - source, - canUseLegacyAssemblyApi, - legacyAssemblyAliases, - currentApplicationAssemblyAliases, - assemblyDeclaredTypeNames) || - hasCurrentApplicationSourceTarget; - } - - private void RecordRegistrarDomainReturnRequirement( - string source, - string assemblyDirectory, - string[] legacyAssemblyAliases) - { - if (!ThirdPartyToolMigrationRules.ContainsRegistrarDomainReturnApiForAssembly( - source, - AssemblyScopedLegacyDirectories.Contains(assemblyDirectory), - legacyAssemblyAliases)) - { - return; - } - - ToolContractsReferenceAssemblyDirectories.Add(assemblyDirectory); - } - - private void RecordDomainContractRequirement(string source, string assemblyDirectory) - { - string[] currentDomainAssemblyAliases = GetAssemblyScopedNames( - AssemblyScopedCurrentDomainAliasesByDirectory, - assemblyDirectory); - string[] currentDomainNamespaceAliases = - ThirdPartyToolMigrationAliasRules.GetCombinedCurrentDomainNamespaceAliases( - source, - currentDomainAssemblyAliases); - if (!ThirdPartyToolMigrationRules.ContainsCurrentDomainMetadataApiForAssembly( - source, - AssemblyScopedCurrentDomainDirectories.Contains(assemblyDirectory)) && - !ThirdPartyToolMigrationRules.ContainsCurrentDomainContractAliasReference( - source, - currentDomainNamespaceAliases)) - { - return; - } - - ToolContractsReferenceAssemblyDirectories.Add(assemblyDirectory); - } - - private void RecordFirstPartyScreenshotRequirement( - string source, - string assemblyDirectory, - string[] legacyAssemblyAliases, - string[] assemblyDeclaredTypeNames, - bool hasLegacyCSharpApi) - { - string[] currentFirstPartyToolsAssemblyAliases = GetAssemblyScopedNames( - AssemblyScopedCurrentFirstPartyToolsAliasesByDirectory, - assemblyDirectory); - bool hasCurrentFirstPartyScreenshotReferenceRequirement = - ThirdPartyToolMigrationRules.ContainsCurrentFirstPartyScreenshotApiForAssembly( - source, - AssemblyScopedCurrentFirstPartyToolsDirectories.Contains(assemblyDirectory), - currentFirstPartyToolsAssemblyAliases, - assemblyDeclaredTypeNames); - if (ContainsFirstPartyScreenshotToolContractsRequirement( - source, - assemblyDirectory, - legacyAssemblyAliases, - currentFirstPartyToolsAssemblyAliases, - assemblyDeclaredTypeNames, - hasLegacyCSharpApi, - hasCurrentFirstPartyScreenshotReferenceRequirement)) - { - ToolContractsReferenceAssemblyDirectories.Add(assemblyDirectory); - } - - if (hasCurrentFirstPartyScreenshotReferenceRequirement) - { - FirstPartyScreenshotReferenceAssemblyDirectories.Add(assemblyDirectory); - } - } - - private bool ContainsFirstPartyScreenshotToolContractsRequirement( - string source, - string assemblyDirectory, - string[] legacyAssemblyAliases, - string[] currentFirstPartyToolsAssemblyAliases, - string[] assemblyDeclaredTypeNames, - bool hasLegacyCSharpApi, - bool hasCurrentFirstPartyScreenshotReferenceRequirement) - { - bool hasAssemblyScopedCurrentToolContractsUsing = - AssemblyScopedCurrentToolContractsDirectories.Contains(assemblyDirectory); - bool hasAssemblyScopedCurrentFirstPartyToolsUsing = - AssemblyScopedCurrentFirstPartyToolsDirectories.Contains(assemblyDirectory); - return ThirdPartyToolMigrationRules.ContainsLegacyFirstPartyScreenshotApiForAssembly( - source, - AssemblyScopedLegacyDirectories.Contains(assemblyDirectory) || - hasLegacyCSharpApi || - ThirdPartyToolMigrationRules.ContainsCurrentToolContractsApi(source) || - hasAssemblyScopedCurrentToolContractsUsing, - legacyAssemblyAliases, - assemblyDeclaredTypeNames) || - ThirdPartyToolMigrationRules.ContainsLegacyEditorWindowCaptureUtilityMigrationForAssembly( - source, - AssemblyScopedLegacyDirectories.Contains(assemblyDirectory) || hasLegacyCSharpApi, - hasAssemblyScopedCurrentToolContractsUsing, - hasAssemblyScopedCurrentFirstPartyToolsUsing, - legacyAssemblyAliases, - currentFirstPartyToolsAssemblyAliases, - assemblyDeclaredTypeNames) || - ThirdPartyToolMigrationRules.ContainsCurrentFirstPartyScreenshotContractApiForAssembly( - source, - hasAssemblyScopedCurrentFirstPartyToolsUsing, - currentFirstPartyToolsAssemblyAliases, - assemblyDeclaredTypeNames) || - hasCurrentFirstPartyScreenshotReferenceRequirement; - } - - private void RecordEditorWindowCaptureTimeoutRequirement( - string source, - string assemblyDirectory, - string[] legacyAssemblyAliases, - string[] assemblyDeclaredTypeNames, - bool hasLegacyCSharpApi) - { - if (!ThirdPartyToolMigrationRules.ContainsLegacyEditorWindowCaptureUtilityTimeoutMigrationForAssembly( - source, - AssemblyScopedLegacyDirectories.Contains(assemblyDirectory) || hasLegacyCSharpApi, - AssemblyScopedCurrentToolContractsDirectories.Contains(assemblyDirectory), - AssemblyScopedCurrentFirstPartyToolsDirectories.Contains(assemblyDirectory), - legacyAssemblyAliases, - GetAssemblyScopedNames(AssemblyScopedCurrentFirstPartyToolsAliasesByDirectory, assemblyDirectory), - assemblyDeclaredTypeNames)) - { - return; - } - - ToolContractsReferenceAssemblyDirectories.Add(assemblyDirectory); - } - private void AddRegistrarApplicationReferences() { foreach (string registrarAssemblyDirectory in RegistrarAssemblyDirectories)