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
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public void Resolve_WhenUnresolvedType_ShouldReportAddedNamespace()
{
string body = "StringBuilder builder = new StringBuilder();\nreturn builder.ToString();";
string wrappedSource = WrapperTemplate.Build(
new List<string>(), "TestNs", "TestClass", body);
new List<string>(), System.Array.Empty<string>(), "TestNs", "TestClass", body);

PreUsingResult result = PreUsingResolver.Resolve(wrappedSource, AssemblyTypeIndex.Instance);

Expand All @@ -28,7 +28,7 @@ public void Resolve_WhenUnresolvedType_ShouldReportAddedAssemblyReference()
{
string body = "StringBuilder builder = new StringBuilder();\nreturn builder.ToString();";
string wrappedSource = WrapperTemplate.Build(
new List<string>(), "TestNs", "TestClass", body);
new List<string>(), System.Array.Empty<string>(), "TestNs", "TestClass", body);

PreUsingResult result = PreUsingResolver.Resolve(wrappedSource, AssemblyTypeIndex.Instance);

Expand All @@ -40,7 +40,7 @@ public void Resolve_WhenNoMissingUsings_ShouldReportEmptyAddedNamespaces()
{
string body = "int x = 42;\nreturn x;";
string wrappedSource = WrapperTemplate.Build(
new List<string>(), "TestNs", "TestClass", body);
new List<string>(), System.Array.Empty<string>(), "TestNs", "TestClass", body);

PreUsingResult result = PreUsingResolver.Resolve(wrappedSource, AssemblyTypeIndex.Instance);

Expand All @@ -52,7 +52,7 @@ public void Resolve_WhenMultipleTypes_ShouldReportAllAddedNamespaces()
{
string body = "StringBuilder sb = new StringBuilder();\nRegex r = new Regex(\"x\");\nreturn sb.ToString();";
string wrappedSource = WrapperTemplate.Build(
new List<string>(), "TestNs", "TestClass", body);
new List<string>(), System.Array.Empty<string>(), "TestNs", "TestClass", body);

PreUsingResult result = PreUsingResolver.Resolve(wrappedSource, AssemblyTypeIndex.Instance);

Expand All @@ -65,7 +65,7 @@ public void Resolve_WhenAlreadyHasUsing_ShouldNotReportIt()
{
List<string> usings = new() { "using System.Text;" };
string body = "StringBuilder builder = new StringBuilder();\nreturn builder.ToString();";
string wrappedSource = WrapperTemplate.Build(usings, "TestNs", "TestClass", body);
string wrappedSource = WrapperTemplate.Build(usings, System.Array.Empty<string>(), "TestNs", "TestClass", body);

PreUsingResult result = PreUsingResolver.Resolve(wrappedSource, AssemblyTypeIndex.Instance);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,87 @@ public void Prepare_WhenStringLiteralExists_ShouldEmitPreambleBeforeUserCodeMark
prepared.PreparedSource.IndexOf(WrapperTemplate.UserCodeStartMarker, System.StringComparison.Ordinal));
}

[Test]
public void Prepare_WhenScriptUsesBareUnityObject_ShouldAddObjectAlias()
{
// Verifies a bare "Object" call resolves to UnityEngine.Object via an injected alias.
PreparedDynamicCode prepared = DynamicCodeSourcePreparer.Prepare(
"GameObject go = new GameObject(\"source\");\nObject.Instantiate(go);\nreturn null;",
DynamicCodeConstants.DEFAULT_NAMESPACE,
DynamicCodeConstants.DEFAULT_CLASS_NAME);

Assert.IsNotNull(prepared.PreparedSource);
StringAssert.Contains("using Object = UnityEngine.Object;", prepared.PreparedSource);
}

[Test]
public void Prepare_WhenObjectAliasAlreadyExists_ShouldNotAddDuplicateAlias()
{
// Verifies the default Object alias is not injected a second time when the user already declared it.
PreparedDynamicCode prepared = DynamicCodeSourcePreparer.Prepare(
"using Object = UnityEngine.Object;\nObject.Instantiate(new GameObject(\"source\"));\nreturn null;",
DynamicCodeConstants.DEFAULT_NAMESPACE,
DynamicCodeConstants.DEFAULT_CLASS_NAME);

Assert.IsNotNull(prepared.PreparedSource);
Assert.AreEqual(
1,
DynamicCodeTestStringUtility.CountSubstring(prepared.PreparedSource, "using Object = UnityEngine.Object;"));
}

[Test]
public void Prepare_WhenCustomObjectAliasAlreadyExists_ShouldRespectUserAlias()
{
// Verifies a user-defined "Object" alias to a different type is preserved, not overridden by the default.
PreparedDynamicCode prepared = DynamicCodeSourcePreparer.Prepare(
"using Object = System.Object;\nreturn new Object();",
DynamicCodeConstants.DEFAULT_NAMESPACE,
DynamicCodeConstants.DEFAULT_CLASS_NAME);

Assert.IsNotNull(prepared.PreparedSource);
Assert.AreEqual(
1,
DynamicCodeTestStringUtility.CountSubstring(prepared.PreparedSource, "using Object = "));
}

[Test]
public void Prepare_WhenVerbatimObjectAliasAlreadyExists_ShouldRespectUserAlias()
{
// Verifies a verbatim "@Object" alias is detected so the default Object alias is skipped.
PreparedDynamicCode prepared = DynamicCodeSourcePreparer.Prepare(
"using @Object = System.Object;\nreturn new @Object();",
DynamicCodeConstants.DEFAULT_NAMESPACE,
DynamicCodeConstants.DEFAULT_CLASS_NAME);

Assert.IsNotNull(prepared.PreparedSource);
StringAssert.Contains("using @Object = System.Object;", prepared.PreparedSource);
Assert.AreEqual(
0,
DynamicCodeTestStringUtility.CountSubstring(prepared.PreparedSource, "using Object = UnityEngine.Object;"));
}

[Test]
public void Prepare_WhenScriptUsesBareUnityRandom_ShouldAddRandomAlias()
{
// Verifies a bare "Random" call resolves to UnityEngine.Random via an injected alias.
PreparedDynamicCode prepared = DynamicCodeSourcePreparer.Prepare(
"int value = Random.Range(0, 10);\nreturn value;",
DynamicCodeConstants.DEFAULT_NAMESPACE,
DynamicCodeConstants.DEFAULT_CLASS_NAME);

Assert.IsNotNull(prepared.PreparedSource);
StringAssert.Contains("using Random = UnityEngine.Random;", prepared.PreparedSource);
}

[Test]
public void CountSubstring_WhenTargetIsEmpty_ShouldReturnZero()
{
// Verifies the shared string-counting test helper treats an empty target as zero matches.
int count = DynamicCodeTestStringUtility.CountSubstring("source", "");

Assert.AreEqual(0, count);
}

[Test]
public void Prepare_WhenInterpolatedStringExists_ShouldSkipLiteralHoisting()
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
namespace io.github.hatayama.UnityCliLoop.Tests.Editor.DynamicCodeToolTests
{
/// <summary>
/// Shared string-counting helper for dynamic code source assertions.
/// </summary>
internal static class DynamicCodeTestStringUtility
{
public static int CountSubstring(string source, string target)
{
if (string.IsNullOrEmpty(target))
{
return 0;
}

int count = 0;
int index = 0;
while ((index = source.IndexOf(target, index, System.StringComparison.Ordinal)) >= 0)
{
count++;
index += target.Length;
}

return count;
}
}
}

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

Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ public void Resolve_WhenUnresolvedType_ShouldInjectUsing()
{
string body = "StringBuilder builder = new StringBuilder();\nreturn builder.ToString();";
string wrappedSource = WrapperTemplate.Build(
new List<string>(), "TestNs", "TestClass", body);
new List<string>(), System.Array.Empty<string>(), "TestNs", "TestClass", body);

PreUsingResult result = PreUsingResolver.Resolve(wrappedSource, AssemblyTypeIndex.Instance);

Expand All @@ -184,7 +184,7 @@ public void Resolve_WhenUnresolvedType_ShouldReportAssemblyReference()
{
string body = "StringBuilder builder = new StringBuilder();\nreturn builder.ToString();";
string wrappedSource = WrapperTemplate.Build(
new List<string>(), "TestNs", "TestClass", body);
new List<string>(), System.Array.Empty<string>(), "TestNs", "TestClass", body);

PreUsingResult result = PreUsingResolver.Resolve(wrappedSource, AssemblyTypeIndex.Instance);

Expand All @@ -196,7 +196,7 @@ public void Resolve_WhenAlreadyHasUsing_ShouldNotAddDuplicate()
{
List<string> usings = new() { "using System.Text;" };
string body = "StringBuilder builder = new StringBuilder();\nreturn builder.ToString();";
string wrappedSource = WrapperTemplate.Build(usings, "TestNs", "TestClass", body);
string wrappedSource = WrapperTemplate.Build(usings, System.Array.Empty<string>(), "TestNs", "TestClass", body);

PreUsingResult result = PreUsingResolver.Resolve(wrappedSource, AssemblyTypeIndex.Instance);

Expand All @@ -209,7 +209,7 @@ public void Resolve_WhenNoUserTypes_ShouldNotAddSystemText()
{
string body = "int x = 42;\nreturn x;";
string wrappedSource = WrapperTemplate.Build(
new List<string>(), "TestNs", "TestClass", body);
new List<string>(), System.Array.Empty<string>(), "TestNs", "TestClass", body);

PreUsingResult result = PreUsingResolver.Resolve(wrappedSource, AssemblyTypeIndex.Instance);

Expand All @@ -233,7 +233,7 @@ public void Resolve_WhenMultipleTypes_ShouldInjectAll()
{
string body = "StringBuilder sb = new StringBuilder();\nRegex r = new Regex(\"x\");\nreturn sb.ToString();";
string wrappedSource = WrapperTemplate.Build(
new List<string>(), "TestNs", "TestClass", body);
new List<string>(), System.Array.Empty<string>(), "TestNs", "TestClass", body);

PreUsingResult result = PreUsingResolver.Resolve(wrappedSource, AssemblyTypeIndex.Instance);

Expand All @@ -246,7 +246,7 @@ public void Resolve_WhenFullyQualifiedTypeIsUsed_ShouldReportAssemblyReference()
{
string body = "System.Text.StringBuilder builder = new System.Text.StringBuilder();\nreturn builder.ToString();";
string wrappedSource = WrapperTemplate.Build(
new List<string>(), "TestNs", "TestClass", body);
new List<string>(), System.Array.Empty<string>(), "TestNs", "TestClass", body);

PreUsingResult result = PreUsingResolver.Resolve(wrappedSource, AssemblyTypeIndex.Instance);

Expand Down
42 changes: 42 additions & 0 deletions Assets/Tests/Editor/DynamicCodeToolTests/SourceShaperTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,5 +59,47 @@ public void Analyze_WhenAttributedTypeHasAccessModifier_ShouldDetectTypeDeclarat
Assert.IsTrue(result.HasTypeDeclaration);
Assert.IsFalse(result.HasTopLevelStatements);
}

[Test]
public void Analyze_WhenVerbatimUsingAlias_ShouldRecordNormalizedAliasName()
{
// Verifies "using @Object = ..." records the alias name without the leading '@'.
SourceShapeResult result = SourceShaper.Analyze(
"using @Object = System.Object;\nreturn new @Object();");

Assert.That(result.AliasedNames, Does.Contain("Object"));
}

[Test]
public void Analyze_WhenGlobalUsingAliasHasComments_ShouldRecordAliasName()
{
// Verifies comments between "global", "using", and the alias name do not block alias detection.
SourceShapeResult result = SourceShaper.Analyze(
"global /* comment */ using /* comment */ Random /* comment */ = UnityEngine.Random;\nreturn Random.Range(0, 1);");

Assert.That(result.AliasedNames, Does.Contain("Random"));
}

[Test]
public void Analyze_WhenUsingAliasHasComments_ShouldRecordAliasName()
{
// Verifies comments between "using" and the alias name do not block alias detection.
SourceShapeResult result = SourceShaper.Analyze(
"using /* comment */ Object /* comment */ = UnityEngine.Object;\nreturn null;");

Assert.That(result.AliasedNames, Does.Contain("Object"));
}

[Test]
public void Analyze_WhenUsingVarHasCommentBeforeVar_ShouldTreatAsTopLevelStatement()
{
// Verifies a comment between "using" and "var" still resolves to a using-statement,
// not a using-directive (regression guard for the SkipWhitespaceAndComments fix).
SourceShapeResult result = SourceShaper.Analyze(
"using /* comment */ var scope = new System.IO.MemoryStream();\nreturn null;");

Assert.IsTrue(result.HasTopLevelStatements);
Assert.AreEqual(0, result.UsingDirectives.Count);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ private static PreparedDynamicCode Prepare(
: DynamicCodeLiteralHoister.Rewrite(body);
string preparedSource = WrapperTemplate.Build(
shape.UsingDirectives,
shape.AliasedNames,
namespaceName,
className,
hoistedResult.RewrittenSource,
Expand Down
Loading
Loading