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
18 changes: 9 additions & 9 deletions Assets/Tests/Editor/CliInstallationDetectorTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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"));
Expand All @@ -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");

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand All @@ -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,
Expand Down Expand Up @@ -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"));
Expand All @@ -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);
Expand All @@ -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"));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,277 @@
using System.Collections.Generic;

using NUnit.Framework;

using io.github.hatayama.UnityCliLoop.Infrastructure;

namespace io.github.hatayama.UnityCliLoop.Tests.Editor
{
/// <summary>
/// Characterization tests that pin the current scan-state aggregation behavior before Extract Class.
/// </summary>
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<string> { AssemblyDirectory },
new List<AssemblyReferenceDirectory>());
}

[Test]
public void Constructor_WhenCreated_ExposesGivenAsmdefAndAssemblyReferenceDirectories()
{
// Verifies that scan-state construction stores caller-provided directory lists without copying.
List<string> asmdefDirectories = new() { AssemblyDirectory };
List<AssemblyReferenceDirectory> 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);
}
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading