diff --git a/Assets/Tests/Editor/ThirdPartyToolMigrationRulesTests.cs b/Assets/Tests/Editor/ThirdPartyToolMigrationRulesTests.cs index b36d2226e6..0fe67abadf 100644 --- a/Assets/Tests/Editor/ThirdPartyToolMigrationRulesTests.cs +++ b/Assets/Tests/Editor/ThirdPartyToolMigrationRulesTests.cs @@ -55,6 +55,86 @@ public void ThirdPartyToolMigrationContentResult_WhenRemovedSignaturesIsNull_Thr null)); } + [Test] + public void ThirdPartyToolMigrationAsmdefReferenceMigrationResult_WhenReferencesIsNull_Throws() + { + // Verifies that asmdef migration results fail fast when migrated references are missing. + Assert.Throws(() => new ThirdPartyToolMigrationAsmdefReferenceMigrationResult( + null, + 0)); + } + + [Test] + public void MigrateAsmdefReferences_WhenLegacyEditorReferenceNeedsDomain_AddsRequiredReferences() + { + // Verifies that pure asmdef reference policy expands legacy editor references with required V3 assemblies. + ThirdPartyToolMigrationAsmdefReferenceMigrationResult result = + ThirdPartyToolMigrationAsmdefReferenceRules.MigrateAsmdefReferences( + new[] { ThirdPartyToolMigrationRuleCatalog.LegacyEditorAssemblyName }, + hasLegacyCSharpSource: false, + requiresToolContractsReference: false, + requiresApplicationReference: false, + requiresDomainReference: true, + requiresFirstPartyScreenshotReference: false); + + Assert.That(result.Changed, Is.True); + Assert.That(result.ReplacementCount, Is.EqualTo(1)); + Assert.That(result.References, Is.EqualTo(new[] + { + ThirdPartyToolMigrationRuleCatalog.CurrentToolContractsGuidReference, + ThirdPartyToolMigrationRuleCatalog.CurrentDomainGuidReference + })); + } + + [Test] + public void MigrateAsmdefReferences_WhenRequiredReferenceIsMissing_AddsReferenceWithoutJson() + { + // Verifies that pure asmdef reference policy can add required references without JSON mutation. + ThirdPartyToolMigrationAsmdefReferenceMigrationResult result = + ThirdPartyToolMigrationAsmdefReferenceRules.MigrateAsmdefReferences( + Array.Empty(), + hasLegacyCSharpSource: false, + requiresToolContractsReference: true, + requiresApplicationReference: false, + requiresDomainReference: false, + requiresFirstPartyScreenshotReference: false); + + Assert.That(result.Changed, Is.True); + Assert.That(result.ReplacementCount, Is.EqualTo(1)); + Assert.That(result.References, Is.EqualTo(new[] + { + ThirdPartyToolMigrationRuleCatalog.CurrentToolContractsGuidReference + })); + } + + [Test] + public void MigrateAsmdefReferences_WhenCurrentNamesAlreadyExist_DoesNotAddDuplicateGuids() + { + // Verifies that pure asmdef reference policy deduplicates name and GUID forms of current references. + ThirdPartyToolMigrationAsmdefReferenceMigrationResult result = + ThirdPartyToolMigrationAsmdefReferenceRules.MigrateAsmdefReferences( + new[] + { + ThirdPartyToolMigrationRuleCatalog.CurrentToolContractsAssemblyName, + ThirdPartyToolMigrationRuleCatalog.CurrentApplicationAssemblyName, + ThirdPartyToolMigrationRuleCatalog.CurrentDomainAssemblyName + }, + hasLegacyCSharpSource: false, + requiresToolContractsReference: true, + requiresApplicationReference: true, + requiresDomainReference: true, + requiresFirstPartyScreenshotReference: false); + + Assert.That(result.Changed, Is.False); + Assert.That(result.ReplacementCount, Is.EqualTo(0)); + Assert.That(result.References, Is.EqualTo(new[] + { + ThirdPartyToolMigrationRuleCatalog.CurrentToolContractsAssemblyName, + ThirdPartyToolMigrationRuleCatalog.CurrentApplicationAssemblyName, + ThirdPartyToolMigrationRuleCatalog.CurrentDomainAssemblyName + })); + } + [Test] public void MigrateCSharpSource_WhenLegacyToolApiIsUsed_RewritesToV3Contracts() { diff --git a/Packages/src/Editor/Domain/ThirdPartyToolMigrationAsmdefReferenceRules.cs b/Packages/src/Editor/Domain/ThirdPartyToolMigrationAsmdefReferenceRules.cs index 90d65bdda8..56cd85f830 100644 --- a/Packages/src/Editor/Domain/ThirdPartyToolMigrationAsmdefReferenceRules.cs +++ b/Packages/src/Editor/Domain/ThirdPartyToolMigrationAsmdefReferenceRules.cs @@ -43,6 +43,67 @@ public static string[] GetMigratedAsmdefReferences( return new[] { reference }; } + /// + /// Resolves the final asmdef reference list without JSON mutation concerns. + /// + public static ThirdPartyToolMigrationAsmdefReferenceMigrationResult MigrateAsmdefReferences( + string[] references, + bool hasLegacyCSharpSource, + bool requiresToolContractsReference, + bool requiresApplicationReference, + bool requiresDomainReference, + bool requiresFirstPartyScreenshotReference) + { + Debug.Assert(references != null, "references must not be null"); + + string[] sourceReferences = references ?? throw new ArgumentNullException(nameof(references)); + int replacementCount = 0; + HashSet addedReferences = new(StringComparer.Ordinal); + List migratedReferences = new(); + foreach (string sourceReference in sourceReferences) + { + string reference = sourceReference ?? string.Empty; + string[] migratedReferenceItems = GetMigratedAsmdefReferences( + reference, + hasLegacyCSharpSource, + requiresApplicationReference, + requiresDomainReference, + requiresFirstPartyScreenshotReference); + bool referenceChanged = migratedReferenceItems.Length != 1 || + !string.Equals(migratedReferenceItems[0], reference, StringComparison.Ordinal); + if (referenceChanged) + { + replacementCount++; + } + + foreach (string migratedReference in migratedReferenceItems) + { + string migratedReferenceKey = GetCurrentAsmdefReferenceKey(migratedReference); + if (!addedReferences.Add(migratedReferenceKey)) + { + continue; + } + + migratedReferences.Add(migratedReference); + } + } + + replacementCount += AppendRequiredCurrentAsmdefReferences( + migratedReferences, + addedReferences, + hasLegacyCSharpSource || requiresToolContractsReference, + requiresApplicationReference, + requiresDomainReference, + requiresFirstPartyScreenshotReference); + + string[] resultReferences = replacementCount == 0 + ? sourceReferences + : migratedReferences.ToArray(); + return new ThirdPartyToolMigrationAsmdefReferenceMigrationResult( + resultReferences, + replacementCount); + } + private static string[] GetMigratedLegacyEditorReferences( bool requiresApplicationReference, bool requiresDomainReference, @@ -84,6 +145,72 @@ private static void AddRequiredMigratedLegacyEditorReference( references.Add(reference); } + private static int AppendRequiredCurrentAsmdefReferences( + List references, + HashSet addedReferences, + bool requiresToolContractsReference, + bool requiresApplicationReference, + bool requiresDomainReference, + bool requiresFirstPartyScreenshotReference) + { + Debug.Assert(references != null, "references must not be null"); + Debug.Assert(addedReferences != null, "addedReferences must not be null"); + + int replacementCount = 0; + if (requiresToolContractsReference) + { + replacementCount += AppendRequiredCurrentAsmdefReference( + references, + addedReferences, + CurrentToolContractsGuidReference); + } + + if (requiresApplicationReference) + { + replacementCount += AppendRequiredCurrentAsmdefReference( + references, + addedReferences, + CurrentApplicationGuidReference); + } + + if (requiresDomainReference) + { + replacementCount += AppendRequiredCurrentAsmdefReference( + references, + addedReferences, + CurrentDomainGuidReference); + } + + if (requiresFirstPartyScreenshotReference) + { + replacementCount += AppendRequiredCurrentAsmdefReference( + references, + addedReferences, + CurrentFirstPartyToolsScreenshotGuidReference); + } + + return replacementCount; + } + + private static int AppendRequiredCurrentAsmdefReference( + List references, + HashSet addedReferences, + string reference) + { + Debug.Assert(references != null, "references must not be null"); + Debug.Assert(addedReferences != null, "addedReferences must not be null"); + Debug.Assert(!string.IsNullOrEmpty(reference), "reference must not be null or empty"); + + string referenceKey = GetCurrentAsmdefReferenceKey(reference); + if (!addedReferences.Add(referenceKey)) + { + return 0; + } + + references.Add(reference); + return 1; + } + public static string GetCurrentAsmdefReferenceKey(string reference) { Debug.Assert(!string.IsNullOrEmpty(reference), "reference must not be null or empty"); diff --git a/Packages/src/Editor/Domain/ThirdPartyToolMigrationRuleData.cs b/Packages/src/Editor/Domain/ThirdPartyToolMigrationRuleData.cs index 7a409a6910..3275233d8c 100644 --- a/Packages/src/Editor/Domain/ThirdPartyToolMigrationRuleData.cs +++ b/Packages/src/Editor/Domain/ThirdPartyToolMigrationRuleData.cs @@ -106,4 +106,25 @@ public ThirdPartyToolMigrationContentResult( public RemovedLegacyPlayerLoopTimingSignature[] RemovedPlayerLoopTimingSignatures { get; } public bool Changed => ReplacementCount > 0; } + + /// + /// Describes migrated asmdef references resolved by pure migration policy. + /// + public readonly struct ThirdPartyToolMigrationAsmdefReferenceMigrationResult + { + public ThirdPartyToolMigrationAsmdefReferenceMigrationResult( + string[] references, + int replacementCount) + { + Debug.Assert(references != null, "references must not be null"); + Debug.Assert(replacementCount >= 0, "replacementCount must not be negative"); + + References = references ?? throw new ArgumentNullException(nameof(references)); + ReplacementCount = replacementCount; + } + + public string[] References { get; } + public int ReplacementCount { get; } + public bool Changed => ReplacementCount > 0; + } } diff --git a/Packages/src/Editor/Infrastructure/ThirdPartyToolMigration/ThirdPartyToolMigrationAsmdefRules.cs b/Packages/src/Editor/Infrastructure/ThirdPartyToolMigration/ThirdPartyToolMigrationAsmdefRules.cs index 6894d9b8df..5e526acf46 100644 --- a/Packages/src/Editor/Infrastructure/ThirdPartyToolMigration/ThirdPartyToolMigrationAsmdefRules.cs +++ b/Packages/src/Editor/Infrastructure/ThirdPartyToolMigration/ThirdPartyToolMigrationAsmdefRules.cs @@ -1,88 +1,18 @@ using System; -using System.Collections.Generic; using System.Diagnostics; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using RemovedLegacyPlayerLoopTimingSignature = io.github.hatayama.UnityCliLoop.Domain.RemovedLegacyPlayerLoopTimingSignature; +using ThirdPartyToolMigrationAsmdefReferenceMigrationResult = io.github.hatayama.UnityCliLoop.Domain.ThirdPartyToolMigrationAsmdefReferenceMigrationResult; using ThirdPartyToolMigrationContentResult = io.github.hatayama.UnityCliLoop.Domain.ThirdPartyToolMigrationContentResult; using static io.github.hatayama.UnityCliLoop.Domain.ThirdPartyToolMigrationAsmdefReferenceRules; -using static io.github.hatayama.UnityCliLoop.Domain.ThirdPartyToolMigrationRuleCatalog; namespace io.github.hatayama.UnityCliLoop.Infrastructure { internal static class ThirdPartyToolMigrationAsmdefRules { - internal static void AddRequiredCurrentAsmdefReferences( - JArray references, - HashSet addedReferences, - bool requiresToolContractsReference, - bool requiresApplicationReference, - bool requiresDomainReference, - bool requiresFirstPartyScreenshotReference, - ref int replacementCount) - { - Debug.Assert(references != null, "references must not be null"); - Debug.Assert(addedReferences != null, "addedReferences must not be null"); - - if (requiresToolContractsReference) - { - AddRequiredCurrentAsmdefReference( - references, - addedReferences, - CurrentToolContractsGuidReference, - ref replacementCount); - } - - if (requiresApplicationReference) - { - AddRequiredCurrentAsmdefReference( - references, - addedReferences, - CurrentApplicationGuidReference, - ref replacementCount); - } - - if (requiresDomainReference) - { - AddRequiredCurrentAsmdefReference( - references, - addedReferences, - CurrentDomainGuidReference, - ref replacementCount); - } - - if (requiresFirstPartyScreenshotReference) - { - AddRequiredCurrentAsmdefReference( - references, - addedReferences, - CurrentFirstPartyToolsScreenshotGuidReference, - ref replacementCount); - } - } - - internal static void AddRequiredCurrentAsmdefReference( - JArray references, - HashSet addedReferences, - string reference, - ref int replacementCount) - { - Debug.Assert(references != null, "references must not be null"); - Debug.Assert(addedReferences != null, "addedReferences must not be null"); - Debug.Assert(!string.IsNullOrEmpty(reference), "reference must not be null or empty"); - - string referenceKey = GetCurrentAsmdefReferenceKey(reference); - if (!addedReferences.Add(referenceKey)) - { - return; - } - - references.Add(reference); - replacementCount++; - } - internal static ThirdPartyToolMigrationContentResult MigrateAsmdefSource( string source, bool hasLegacyCSharpSource, @@ -104,47 +34,16 @@ internal static ThirdPartyToolMigrationContentResult MigrateAsmdefSource( Array.Empty()); } - int replacementCount = 0; - HashSet addedReferences = new(StringComparer.Ordinal); - JArray migratedReferences = new(); - foreach (JToken referenceToken in references) - { - string reference = referenceToken.Value() ?? string.Empty; - string[] migratedReferenceItems = GetMigratedAsmdefReferences( - reference, - hasLegacyCSharpSource, - requiresApplicationReference, - requiresDomainReference, - requiresFirstPartyScreenshotReference); - bool referenceChanged = migratedReferenceItems.Length != 1 || - !string.Equals(migratedReferenceItems[0], reference, StringComparison.Ordinal); - if (referenceChanged) - { - replacementCount++; - } - - foreach (string migratedReference in migratedReferenceItems) - { - string migratedReferenceKey = GetCurrentAsmdefReferenceKey(migratedReference); - if (!addedReferences.Add(migratedReferenceKey)) - { - continue; - } - - migratedReferences.Add(migratedReference); - } - } - - AddRequiredCurrentAsmdefReferences( - migratedReferences, - addedReferences, - hasLegacyCSharpSource || requiresToolContractsReference, + string[] referenceValues = ReadReferenceValues(references); + ThirdPartyToolMigrationAsmdefReferenceMigrationResult migrationResult = MigrateAsmdefReferences( + referenceValues, + hasLegacyCSharpSource, + requiresToolContractsReference, requiresApplicationReference, requiresDomainReference, - requiresFirstPartyScreenshotReference, - ref replacementCount); + requiresFirstPartyScreenshotReference); - if (replacementCount == 0) + if (!migrationResult.Changed) { return new ThirdPartyToolMigrationContentResult( source, @@ -152,12 +51,38 @@ internal static ThirdPartyToolMigrationContentResult MigrateAsmdefSource( Array.Empty()); } + JArray migratedReferences = CreateReferencesArray(migrationResult.References); asmdef["references"] = migratedReferences; return new ThirdPartyToolMigrationContentResult( asmdef.ToString(Formatting.Indented), - replacementCount, + migrationResult.ReplacementCount, Array.Empty()); } + private static string[] ReadReferenceValues(JArray references) + { + Debug.Assert(references != null, "references must not be null"); + + string[] referenceValues = new string[references.Count]; + for (int index = 0; index < references.Count; index++) + { + referenceValues[index] = references[index]?.Value() ?? string.Empty; + } + + return referenceValues; + } + + private static JArray CreateReferencesArray(string[] references) + { + Debug.Assert(references != null, "references must not be null"); + + JArray referencesArray = new(); + foreach (string reference in references) + { + referencesArray.Add(reference); + } + + return referencesArray; + } } }