Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
640 changes: 547 additions & 93 deletions Assets/Tests/Editor/ThirdPartyToolMigrationFileServiceTests.cs

Large diffs are not rendered by default.

286 changes: 261 additions & 25 deletions Assets/Tests/Editor/ThirdPartyToolMigrationRulesTests.cs

Large diffs are not rendered by default.

129 changes: 129 additions & 0 deletions Assets/Tests/Editor/ThirdPartyToolMigrationWizardWindowTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using NUnit.Framework;
using UnityEditor;
using UnityEngine;
using UnityEngine.UIElements;

using io.github.hatayama.UnityCliLoop.Domain;
using io.github.hatayama.UnityCliLoop.Presentation;
Expand Down Expand Up @@ -180,6 +181,125 @@ public void GetMigrationSkillButtonText_ReturnsExpectedLabel(
Assert.That(label, Is.EqualTo(expectedLabel));
}

[Test]
public void GetMigrationSkillPromptText_ReturnsActionablePrompt()
{
// Verifies that the wizard provides a copyable prompt for asking an AI agent to run the skill.
string prompt = ThirdPartyToolMigrationWizardWindow.GetMigrationSkillPromptText();

Assert.That(prompt, Does.Contain("v3-cli-invocation-migration"));
Assert.That(prompt, Does.Contain("SKILL.md, Markdown, POSIX shell scripts, and PowerShell scripts"));
Assert.That(prompt, Does.Contain("Do not change C# snippets"));
Assert.That(prompt, Does.Contain("summarize changed files"));
}

[Test]
public void GetMigrationSkillPromptCopyButtonText_ReturnsClearLabel()
{
// Verifies that the copy button label describes the clipboard action.
string label = ThirdPartyToolMigrationWizardWindow.GetMigrationSkillPromptCopyButtonText();

Assert.That(label, Is.EqualTo("Copy AI Prompt"));
}

[Test]
public void CopyMigrationSkillPromptToClipboard_WritesPrompt()
{
// Verifies that the copy action places the AI prompt on the system clipboard.
string originalClipboard = EditorGUIUtility.systemCopyBuffer;
try
{
ThirdPartyToolMigrationWizardWindow.CopyMigrationSkillPromptToClipboard();

Assert.That(
EditorGUIUtility.systemCopyBuffer,
Is.EqualTo(ThirdPartyToolMigrationWizardWindow.GetMigrationSkillPromptText()));
}
finally
{
EditorGUIUtility.systemCopyBuffer = originalClipboard;
}
}

[Test]
public void Create_WhenMigrationSkillSectionExists_PlacesPromptAfterInstallButton()
{
// Verifies that the AI prompt is shown below the migration skill install action.
VisualElement root = new();
ThirdPartyToolMigrationWizardView.Create(
root,
() => { },
() => { },
_ => { },
() => { },
() => { });

Button installButton = root.Query<Button>().ToList()
.Find(button => button.text == "Install Migration Skill");
Foldout promptFoldout = root.Query<Foldout>().ToList()
.Find(foldout => foldout.text == "Prompt for your AI agent");

Assert.That(installButton, Is.Not.Null);
Assert.That(promptFoldout, Is.Not.Null);
Assert.That(
GetVisualElementIndex(root, promptFoldout),
Is.GreaterThan(GetVisualElementIndex(root, installButton)));
}

[Test]
public void Create_WhenMigrationSkillSectionExists_PlacesCopyButtonOutsidePromptFoldout()
{
// Verifies that the copy action remains available outside the collapsible prompt body.
VisualElement root = new();
ThirdPartyToolMigrationWizardView.Create(
root,
() => { },
() => { },
_ => { },
() => { },
() => { });

Foldout promptFoldout = root.Query<Foldout>().ToList()
.Find(foldout => foldout.text == "Prompt for your AI agent");
Button copyButton = root.Query<Button>().ToList()
.Find(button => button.text == "Copy AI Prompt");

Assert.That(promptFoldout, Is.Not.Null);
Assert.That(copyButton, Is.Not.Null);
System.Collections.Generic.List<Button> foldoutButtons = promptFoldout.Query<Button>().ToList();
Assert.That(foldoutButtons.Contains(copyButton), Is.False);
Assert.That(
GetVisualElementIndex(root, copyButton),
Is.GreaterThan(GetVisualElementIndex(root, promptFoldout)));
}

[Test]
public void ShowNoMigrationTargetsState_DisablesCheckButtonWithoutShowingCloseButton()
{
// Verifies that the completed migration state keeps Check visible but unavailable.
VisualElement root = new();
ThirdPartyToolMigrationWizardView view = ThirdPartyToolMigrationWizardView.Create(
root,
() => { },
() => { },
_ => { },
() => { },
() => { });

view.ShowNoMigrationTargetsState(isMigrating: false);

Button checkButton = root.Query<Button>().ToList()
.Find(button => button.text == "Check");
Button closeButton = root.Query<Button>().ToList()
.Find(button => button.text == "Close");

Assert.That(checkButton, Is.Not.Null);
Assert.That(closeButton, Is.Not.Null);
Assert.That(checkButton.style.display.value, Is.EqualTo(DisplayStyle.Flex));
Assert.That(checkButton.enabledSelf, Is.False);
Assert.That(closeButton.style.display.value, Is.EqualTo(DisplayStyle.None));
}

[TestCase(SkillInstallState.Installed, true)]
[TestCase(SkillInstallState.Outdated, true)]
[TestCase(SkillInstallState.Missing, false)]
Expand Down Expand Up @@ -391,5 +511,14 @@ public void WithContentHeight_WhenCurrentWidthIsWide_UsesSetupWizardWidth()
Assert.That(resizedRect.center, Is.EqualTo(initialRect.center));
Assert.That(resizedRect.size, Is.EqualTo(new Vector2(360f, 148f)));
}

private static int GetVisualElementIndex(VisualElement root, VisualElement target)
{
Debug.Assert(root != null, "root must not be null");
Debug.Assert(target != null, "target must not be null");

System.Collections.Generic.List<VisualElement> elements = root.Query<VisualElement>().ToList();
return elements.IndexOf(target);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,13 @@ internal static string[] GetCurrentApplicationNamespaceAliases(string source)
return GetRegexGroupValuesInCode(source, CurrentApplicationNamespaceAliasRegex, "alias");
}

internal static string[] GetCurrentDomainNamespaceAliases(string source)
{
Debug.Assert(source != null, "source must not be null");

return GetRegexGroupValuesInCode(source, CurrentDomainNamespaceAliasRegex, "alias");
}

internal static string[] GetCombinedLegacyNamespaceAliases(
string source,
string[] legacyAssemblyAliases)
Expand Down Expand Up @@ -122,6 +129,21 @@ internal static string[] GetCombinedCurrentApplicationNamespaceAliases(
.ToArray();
}

internal static string[] GetCombinedCurrentDomainNamespaceAliases(
string source,
string[] currentDomainAssemblyAliases)
{
Debug.Assert(source != null, "source must not be null");
Debug.Assert(
currentDomainAssemblyAliases != null,
"currentDomainAssemblyAliases must not be null");

return GetCurrentDomainNamespaceAliases(source)
.Concat(currentDomainAssemblyAliases)
.Distinct(StringComparer.Ordinal)
.ToArray();
}

internal static string[] GetCombinedCurrentFirstPartyToolsNamespaceAliases(
string source,
string[] currentFirstPartyToolsAssemblyAliases)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,33 @@ internal static bool ContainsCurrentFirstPartyScreenshotApiForAssembly(
assemblyDeclaredTypeNames);
}

internal static bool ContainsCurrentFirstPartyScreenshotContractApiForAssembly(
string source,
bool hasAssemblyScopedCurrentFirstPartyToolsUsing,
string[] currentFirstPartyToolsAssemblyAliases,
string[] assemblyDeclaredTypeNames)
{
Debug.Assert(source != null, "source must not be null");
Debug.Assert(
currentFirstPartyToolsAssemblyAliases != null,
"currentFirstPartyToolsAssemblyAliases must not be null");
Debug.Assert(assemblyDeclaredTypeNames != null, "assemblyDeclaredTypeNames must not be null");

bool hasCurrentFirstPartyToolsNamespaceUsage =
RegexMatchesCode(source, CurrentFirstPartyToolsNamespaceRegex);
string[] currentFirstPartyToolsNamespaceAliases = GetCombinedCurrentFirstPartyToolsNamespaceAliases(
source,
currentFirstPartyToolsAssemblyAliases);
bool canUseBareCurrentFirstPartyScreenshotType =
hasAssemblyScopedCurrentFirstPartyToolsUsing ||
hasCurrentFirstPartyToolsNamespaceUsage;
return ContainsCurrentFirstPartyScreenshotContractReference(
source,
canUseBareCurrentFirstPartyScreenshotType,
currentFirstPartyToolsNamespaceAliases,
assemblyDeclaredTypeNames);
}

internal static bool ContainsCurrentCaptureGameRenderingDeconstructionMigrationForAssembly(
string source,
bool hasAssemblyScopedCurrentFirstPartyToolsUsing,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ internal static bool ContainsLegacyApplicationReference(

foreach (string alias in aliases)
{
if (ContainsLegacyAliasQualifiedName(source, alias, rule.LegacyName))
if (ContainsAliasQualifiedName(source, alias, rule.LegacyName))
{
return true;
}
Expand Down Expand Up @@ -197,7 +197,7 @@ internal static bool ContainsCurrentApplicationReference(

foreach (string alias in currentApplicationNamespaceAliases)
{
if (ContainsLegacyAliasQualifiedName(source, alias, rule.CurrentName))
if (ContainsAliasQualifiedName(source, alias, rule.CurrentName))
{
return true;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,10 +95,38 @@ internal static string[] GetMigratedLegacyEditorReferences(
{
CurrentToolContractsGuidReference
};
AddRequiredMigratedLegacyEditorReference(
references,
requiresApplicationReference,
CurrentApplicationGuidReference);
AddRequiredMigratedLegacyEditorReference(
references,
requiresDomainReference,
CurrentDomainGuidReference);
AddRequiredMigratedLegacyEditorReference(
references,
requiresFirstPartyScreenshotReference,
CurrentFirstPartyToolsScreenshotGuidReference);

return references.ToArray();
}

internal static void AddRequiredMigratedLegacyEditorReference(
List<string> references,
bool isRequired,
string reference)
{
Debug.Assert(references != null, "references must not be null");
Debug.Assert(!string.IsNullOrEmpty(reference), "reference must not be null or empty");

if (!isRequired)
{
return;
}

references.Add(reference);
}

internal static void AddRequiredCurrentAsmdefReferences(
JArray references,
HashSet<string> addedReferences,
Expand All @@ -111,17 +139,41 @@ internal static void AddRequiredCurrentAsmdefReferences(
Debug.Assert(references != null, "references must not be null");
Debug.Assert(addedReferences != null, "addedReferences must not be null");

if (requiresToolContractsReference ||
requiresApplicationReference ||
requiresDomainReference ||
requiresFirstPartyScreenshotReference)
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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,12 @@ internal static MigrationAssemblyUsage CreateMigrationAssemblyUsage(
HashSet<string> assemblyScopedLegacyDirectories,
HashSet<string> assemblyScopedCurrentToolContractsDirectories,
HashSet<string> assemblyScopedCurrentApplicationDirectories,
HashSet<string> assemblyScopedCurrentDomainDirectories,
HashSet<string> assemblyScopedCurrentFirstPartyToolsDirectories,
Dictionary<string, HashSet<string>> assemblyScopedLegacyAliasesByDirectory,
Dictionary<string, HashSet<string>> assemblyScopedLegacyToolInfoAliasesByDirectory,
Dictionary<string, HashSet<string>> assemblyScopedCurrentApplicationAliasesByDirectory,
Dictionary<string, HashSet<string>> assemblyScopedCurrentDomainAliasesByDirectory,
Dictionary<string, HashSet<string>> assemblyScopedCurrentFirstPartyToolsAliasesByDirectory,
Dictionary<string, HashSet<string>> assemblyDeclaredTypeNamesByDirectory,
HashSet<string> toolContractsReferenceAssemblyDirectories,
Expand All @@ -42,6 +44,9 @@ internal static MigrationAssemblyUsage CreateMigrationAssemblyUsage(
Debug.Assert(
assemblyScopedCurrentApplicationDirectories != null,
"assemblyScopedCurrentApplicationDirectories must not be null");
Debug.Assert(
assemblyScopedCurrentDomainDirectories != null,
"assemblyScopedCurrentDomainDirectories must not be null");
Debug.Assert(
assemblyScopedCurrentFirstPartyToolsDirectories != null,
"assemblyScopedCurrentFirstPartyToolsDirectories must not be null");
Expand All @@ -54,6 +59,9 @@ internal static MigrationAssemblyUsage CreateMigrationAssemblyUsage(
Debug.Assert(
assemblyScopedCurrentApplicationAliasesByDirectory != null,
"assemblyScopedCurrentApplicationAliasesByDirectory must not be null");
Debug.Assert(
assemblyScopedCurrentDomainAliasesByDirectory != null,
"assemblyScopedCurrentDomainAliasesByDirectory must not be null");
Debug.Assert(
assemblyScopedCurrentFirstPartyToolsAliasesByDirectory != null,
"assemblyScopedCurrentFirstPartyToolsAliasesByDirectory must not be null");
Expand All @@ -80,10 +88,12 @@ internal static MigrationAssemblyUsage CreateMigrationAssemblyUsage(
assemblyScopedLegacyDirectories,
assemblyScopedCurrentToolContractsDirectories,
assemblyScopedCurrentApplicationDirectories,
assemblyScopedCurrentDomainDirectories,
assemblyScopedCurrentFirstPartyToolsDirectories,
CreateAssemblyScopedLegacyAliasesByDirectory(assemblyScopedLegacyAliasesByDirectory),
CreateAssemblyScopedLegacyAliasesByDirectory(assemblyScopedLegacyToolInfoAliasesByDirectory),
CreateAssemblyScopedNamesByDirectory(assemblyScopedCurrentApplicationAliasesByDirectory),
CreateAssemblyScopedNamesByDirectory(assemblyScopedCurrentDomainAliasesByDirectory),
CreateAssemblyScopedNamesByDirectory(assemblyScopedCurrentFirstPartyToolsAliasesByDirectory),
CreateAssemblyScopedNamesByDirectory(assemblyDeclaredTypeNamesByDirectory),
toolContractsReferenceAssemblyDirectories,
Expand Down
Loading
Loading