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
206 changes: 206 additions & 0 deletions Assets/Tests/Editor/ThirdPartyToolMigrationFileServiceTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
using io.github.hatayama.UnityCliLoop.Domain;
using io.github.hatayama.UnityCliLoop.Infrastructure;

using static io.github.hatayama.UnityCliLoop.Infrastructure.ThirdPartyToolMigrationFileServiceConstants;

namespace io.github.hatayama.UnityCliLoop.Tests.Editor
{
/// <summary>
Expand Down Expand Up @@ -70,6 +72,162 @@ public void ThirdPartyToolMigrationSourceFileCache_WhenFileIsReadTwice_UsesCache
Assert.That(readCount, Is.EqualTo(1));
}

[Test]
[Platform(Include = "Win")]
public void ThirdPartyToolMigrationSourceFileCache_WhenWindowsPathExceedsLegacyLimit_ReadsSource()
{
// Verifies that migration preview can inspect source files beyond the legacy Windows path limit.
string projectRoot = CreateProjectRoot();
try
{
string longDirectoryPath = CreateLongDirectoryPath(projectRoot);
string filePath = Path.Combine(longDirectoryPath, "LongPathTool.cs");
Directory.CreateDirectory(ToWindowsExtendedLengthPath(longDirectoryPath));
File.WriteAllText(
ToWindowsExtendedLengthPath(filePath),
"public sealed class LongPathTool {}");

Assert.That(filePath.Length, Is.GreaterThanOrEqualTo(WindowsLegacyMaxPathLength));
ThirdPartyToolMigrationSourceFileCache cache = new();

string source = cache.ReadAllText(filePath);

Assert.That(source, Does.Contain("LongPathTool"));
}
finally
{
Directory.Delete(ToWindowsExtendedLengthPath(projectRoot), recursive: true);
}
}

[Test]
[Platform(Include = "Win")]
public async Task ApplyMigrationAsync_WhenWindowsPathExceedsLegacyLimit_MigratesSource()
{
// Verifies that migration apply can rewrite source files beyond the legacy Windows path limit.
string projectRoot = CreateProjectRoot();
try
{
string longDirectoryPath = CreateLongDirectoryPath(projectRoot);
string filePath = Path.Combine(longDirectoryPath, "LongPathTool.cs");
Directory.CreateDirectory(ToWindowsExtendedLengthPath(longDirectoryPath));
File.WriteAllText(
ToWindowsExtendedLengthPath(filePath),
@"using io.github.hatayama.uLoopMCP;

[McpTool]
public sealed class LongPathTool : AbstractUnityTool<LongPathSchema, LongPathResponse>
{
}

public sealed class LongPathSchema : BaseToolSchema
{
}

public sealed class LongPathResponse : BaseToolResponse
{
}");

ThirdPartyToolMigrationFileService service = new();

ThirdPartyToolMigrationResult result =
await service.ApplyMigrationAsync(
projectRoot,
new Progress<ThirdPartyToolMigrationProgress>(),
CancellationToken.None);
string migratedSource = File.ReadAllText(ToWindowsExtendedLengthPath(filePath));

Assert.That(result.FilePaths.Contains(filePath), Is.True);
Assert.That(migratedSource, Does.Contain("UnityCliLoopTool<LongPathSchema, LongPathResponse>"));
}
finally
{
Directory.Delete(ToWindowsExtendedLengthPath(projectRoot), recursive: true);
}
}

[Test]
[Platform(Include = "Win")]
public async Task ApplyMigrationAsync_WhenWindowsDirectoryPathExceedsLegacyLimit_MigratesSource()
{
// Verifies that migration apply can discover source files in directories beyond the legacy Windows path limit.
string projectRoot = CreateProjectRoot();
try
{
string longDirectoryPath = CreateVeryLongDirectoryPath(projectRoot);
string filePath = Path.Combine(longDirectoryPath, "LongDirectoryTool.cs");
Directory.CreateDirectory(ToWindowsExtendedLengthPath(longDirectoryPath));
File.WriteAllText(
ToWindowsExtendedLengthPath(filePath),
@"using io.github.hatayama.uLoopMCP;

[McpTool]
public sealed class LongDirectoryTool : AbstractUnityTool<LongDirectorySchema, LongDirectoryResponse>
{
}

public sealed class LongDirectorySchema : BaseToolSchema
{
}

public sealed class LongDirectoryResponse : BaseToolResponse
{
}");

ThirdPartyToolMigrationFileService service = new();

ThirdPartyToolMigrationResult result =
await service.ApplyMigrationAsync(
projectRoot,
new Progress<ThirdPartyToolMigrationProgress>(),
CancellationToken.None);
string migratedSource = File.ReadAllText(ToWindowsExtendedLengthPath(filePath));

Assert.That(longDirectoryPath.Length, Is.GreaterThanOrEqualTo(WindowsLegacyMaxPathLength));
Assert.That(result.FilePaths.Contains(filePath), Is.True);
Assert.That(
migratedSource,
Does.Contain("UnityCliLoopTool<LongDirectorySchema, LongDirectoryResponse>"));
}
finally
{
Directory.Delete(ToWindowsExtendedLengthPath(projectRoot), recursive: true);
}
}

[Test]
[Platform(Include = "Win")]
public void MigrationProjectFingerprint_WhenWindowsPathExceedsLegacyLimit_DetectsContentChange()
{
// Verifies that long-path source edits invalidate cached migration plans.
string projectRoot = CreateProjectRoot();
try
{
string longDirectoryPath = CreateLongDirectoryPath(projectRoot);
string filePath = Path.Combine(longDirectoryPath, "LongPathTool.cs");
string originalSource = "public sealed class AlphaTool {}";
string changedSource = "public sealed class BravoTool {}";
DateTime originalLastWriteTimeUtc = new DateTime(2024, 1, 1, 0, 0, 0, DateTimeKind.Utc);
Directory.CreateDirectory(ToWindowsExtendedLengthPath(longDirectoryPath));
File.WriteAllText(ToWindowsExtendedLengthPath(filePath), originalSource);
File.SetLastWriteTimeUtc(ToWindowsExtendedLengthPath(filePath), originalLastWriteTimeUtc);
ProjectFileInventory firstInventory = ProjectFileInventory.Create(projectRoot);
MigrationProjectFingerprint fingerprint =
MigrationProjectFingerprint.CaptureFromInventory(firstInventory);

File.WriteAllText(ToWindowsExtendedLengthPath(filePath), changedSource);
File.SetLastWriteTimeUtc(ToWindowsExtendedLengthPath(filePath), originalLastWriteTimeUtc);
ProjectFileInventory changedInventory = ProjectFileInventory.Create(projectRoot);

Assert.That(changedSource.Length, Is.EqualTo(originalSource.Length));
Assert.That(fingerprint.Matches(changedInventory), Is.False);
}
finally
{
Directory.Delete(ToWindowsExtendedLengthPath(projectRoot), recursive: true);
}
}

[Test]
public void MigrationProjectFingerprint_WhenCandidateFileChanges_DoesNotMatch()
{
Expand Down Expand Up @@ -5472,6 +5630,54 @@ private static string CreateProjectRoot()
return projectRoot;
}

private static string CreateLongDirectoryPath(string projectRoot)
{
Assert.That(projectRoot, Is.Not.Empty);

string directoryPath = Path.Combine(projectRoot, "Assets");
int segmentIndex = 0;
while (Path.Combine(directoryPath, "LongPathTool.cs").Length < WindowsLegacyMaxPathLength)
{
directoryPath = Path.Combine(directoryPath, "LongPathSegment" + segmentIndex.ToString("D2"));
segmentIndex++;
}

return directoryPath;
}

private static string CreateVeryLongDirectoryPath(string projectRoot)
{
Assert.That(projectRoot, Is.Not.Empty);

string directoryPath = Path.Combine(projectRoot, "Assets");
int segmentIndex = 0;
while (directoryPath.Length < WindowsLegacyMaxPathLength)
{
directoryPath = Path.Combine(directoryPath, "LongDirectorySegment" + segmentIndex.ToString("D2"));
segmentIndex++;
}

return directoryPath;
}

private static string ToWindowsExtendedLengthPath(string path)
{
Assert.That(path, Is.Not.Empty);

string fullPath = Path.GetFullPath(path);
if (fullPath.StartsWith(WindowsExtendedLengthPathPrefix, StringComparison.Ordinal))
{
return fullPath;
}

if (fullPath.StartsWith(WindowsUncPathPrefix, StringComparison.Ordinal))
{
return WindowsExtendedLengthUncPathPrefix + fullPath.Substring(WindowsUncPathPrefix.Length);
}

return WindowsExtendedLengthPathPrefix + fullPath;
}

private sealed class RecordingMigrationProgress : IProgress<ThirdPartyToolMigrationProgress>
{
private readonly List<ThirdPartyToolMigrationProgress> _reports;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ internal static string ReadAsmdefGuidReferenceFromAsmdefPath(string asmdefFilePa
Debug.Assert(!string.IsNullOrEmpty(asmdefFilePath), "asmdefFilePath must not be null or empty");

string metaPath = asmdefFilePath + ".meta";
if (!File.Exists(metaPath))
if (!ThirdPartyToolMigrationFileAccess.Exists(metaPath))
{
return string.Empty;
}

return ReadAsmdefGuidReferenceFromMetaFile(metaPath, File.ReadLines);
return ReadAsmdefGuidReferenceFromMetaFile(metaPath, ThirdPartyToolMigrationFileAccess.ReadLines);
}

internal static string ReadAsmdefGuidReferenceFromMetaFile(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ internal static bool ContainsAsmdefMigrationTarget(
out requiresApplicationReference,
out requiresDomainReference,
out requiresFirstPartyScreenshotReference);
string source = File.ReadAllText(asmdefFilePath);
string source = ThirdPartyToolMigrationFileAccess.ReadAllText(asmdefFilePath);
if (!hasAssemblyMigrationRequirement &&
!ThirdPartyToolMigrationRules.ContainsLegacyMigrationCandidateText(source))
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,10 @@ internal static async Task<Dictionary<string, string>> CreateAsmdefDirectoryMapA

internal static bool TryReadJsonObjectFromFile(string filePath, out JObject jsonObject)
{
return TryReadJsonObjectForMigration(filePath, File.ReadAllText, out jsonObject);
return TryReadJsonObjectForMigration(
filePath,
ThirdPartyToolMigrationFileAccess.ReadAllText,
out jsonObject);
}

internal static bool TryReadJsonObjectFromCache(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ internal static MigrationAssemblyUsage FindMigrationAssemblyUsage(

foreach (string csharpFilePath in csharpFilePaths)
{
string source = File.ReadAllText(csharpFilePath);
string source = ThirdPartyToolMigrationFileAccess.ReadAllText(csharpFilePath);
sourceByCSharpFilePath.Add(csharpFilePath, source);
if (!ThirdPartyToolMigrationRules.ContainsMigrationCandidateText(source))
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Threading;
using System.Threading.Tasks;

Expand Down Expand Up @@ -166,7 +165,7 @@ internal static async Task CollectFastAssemblyReferenceRequirementsAsync(
return;
}

string source = File.ReadAllText(csharpFilePath);
string source = ThirdPartyToolMigrationFileAccess.ReadAllText(csharpFilePath);
inspectedEntryCount++;
if (inspectedEntryCount % ThirdPartyToolMigrationFileServiceConstants.PreviewYieldBatchSize == 0)
{
Expand Down Expand Up @@ -250,7 +249,7 @@ internal static async Task CollectFastAssemblyScopedCurrentToolContractsRequirem
return;
}

string source = File.ReadAllText(csharpFilePath);
string source = ThirdPartyToolMigrationFileAccess.ReadAllText(csharpFilePath);
inspectedEntryCount++;
if (inspectedEntryCount % ThirdPartyToolMigrationFileServiceConstants.PreviewYieldBatchSize == 0)
{
Expand Down Expand Up @@ -317,7 +316,7 @@ internal static async Task CollectFastAssemblyScopedCurrentDomainRequirementsAsy
return;
}

string source = File.ReadAllText(csharpFilePath);
string source = ThirdPartyToolMigrationFileAccess.ReadAllText(csharpFilePath);
inspectedEntryCount++;
if (inspectedEntryCount % ThirdPartyToolMigrationFileServiceConstants.PreviewYieldBatchSize == 0)
{
Expand Down Expand Up @@ -391,7 +390,7 @@ internal static async Task<bool> CollectFastFirstPartyScreenshotRequirementsAsyn
return false;
}

string source = File.ReadAllText(csharpFilePath);
string source = ThirdPartyToolMigrationFileAccess.ReadAllText(csharpFilePath);
inspectedEntryCount++;
if (inspectedEntryCount % ThirdPartyToolMigrationFileServiceConstants.PreviewYieldBatchSize == 0)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Threading;
using System.Threading.Tasks;

Expand Down Expand Up @@ -89,7 +88,7 @@ internal static async Task<bool> ContainsFastCSharpSourceMigrationTargetAsync(
return false;
}

string source = File.ReadAllText(csharpFilePath);
string source = ThirdPartyToolMigrationFileAccess.ReadAllText(csharpFilePath);
inspectedEntryCount++;
if (inspectedEntryCount % ThirdPartyToolMigrationFileServiceConstants.PreviewYieldBatchSize == 0)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ namespace io.github.hatayama.UnityCliLoop.Infrastructure
/// </summary>
internal static class ThirdPartyToolMigrationFileServiceConstants
{
internal const int WindowsLegacyMaxPathLength = 260;
internal const string WindowsExtendedLengthPathPrefix = @"\\?\";
internal const string WindowsExtendedLengthUncPathPrefix = @"\\?\UNC\";
internal const string WindowsUncPathPrefix = @"\\";
internal const string ImplicitEditorAssemblyDirectoryName = "__UnityCliLoopImplicitEditorAssembly";
internal const string ImplicitRuntimeAssemblyDirectoryName = "__UnityCliLoopImplicitRuntimeAssembly";
internal const string ImplicitFirstPassEditorAssemblyDirectoryName =
Expand Down
Loading
Loading