Initial commit: AIGame Unity client with URP, HybridCLR, YooAsset and tooling.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+138
@@ -0,0 +1,138 @@
|
||||
using System.Linq;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NUnit.Framework;
|
||||
using Unity.Pipeline.Commands;
|
||||
using Unity.Pipeline.Editor;
|
||||
using Unity.Pipeline.Editor.Commands.ProjectAudit;
|
||||
|
||||
namespace Unity.Pipeline.Tests.Editor.ProjectAudit
|
||||
{
|
||||
/// <summary>
|
||||
/// Unit tests for the <c>audit</c> / <c>audit_status</c> commands. These cover discovery/schema
|
||||
/// and the two pieces of pure logic — CSV escaping and category-name validation — without running
|
||||
/// a real Project Auditor scan (slow, and Project Auditor may be absent from the test project;
|
||||
/// the reflection-driven scan is exercised live against an Editor that has it installed).
|
||||
/// </summary>
|
||||
class ProjectAuditCommandsTests
|
||||
{
|
||||
[SetUp]
|
||||
public void SetUp() => CommandRegistry.SetDiscovery(new TypeCacheCommandDiscovery());
|
||||
|
||||
[Test]
|
||||
public void Audit_And_AuditStatus_AreDiscovered_WithExpectedSchema()
|
||||
{
|
||||
var commands = CommandRegistry.DiscoverCommands().ToList();
|
||||
|
||||
var audit = commands.FirstOrDefault(c => c.Name == "audit");
|
||||
Assert.IsNotNull(audit, "Should discover the audit command");
|
||||
Assert.IsFalse(audit.MainThreadRequired,
|
||||
"audit must not require the main thread (it only enqueues; the scan runs on a later tick)");
|
||||
CollectionAssert.AreEquivalent(new[] { "categories", "output" },
|
||||
audit.Parameters.Select(p => p.Name).ToList());
|
||||
Assert.IsTrue(audit.Parameters.All(p => !p.Required), "audit parameters should be optional");
|
||||
|
||||
var status = commands.FirstOrDefault(c => c.Name == "audit_status");
|
||||
Assert.IsNotNull(status, "Should discover the audit_status command");
|
||||
Assert.IsFalse(status.MainThreadRequired,
|
||||
"audit_status must be off-main-thread so it answers while a scan holds the main thread");
|
||||
Assert.AreEqual(0, status.Parameters.Count, "audit_status takes no parameters");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AuditStatus_ReturnsValidJsonWithStatusField()
|
||||
{
|
||||
// Returns idle/scanning/completed when Project Auditor is present, or unavailable when not;
|
||||
// in every case a 'status' field must be present.
|
||||
var json = JObject.Parse(ProjectAuditCommands.AuditStatus());
|
||||
Assert.IsNotNull(json["status"], "audit_status should always include a 'status' field");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void EscapeCsv_LeavesPlainFieldsUnquoted()
|
||||
{
|
||||
Assert.AreEqual("Code", ProjectAuditCommands.EscapeCsv("Code"));
|
||||
Assert.AreEqual("", ProjectAuditCommands.EscapeCsv(null));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void EscapeCsv_QuotesFieldsWithCommasQuotesOrNewlines()
|
||||
{
|
||||
Assert.AreEqual("\"a,b\"", ProjectAuditCommands.EscapeCsv("a,b"));
|
||||
Assert.AreEqual("\"line1\nline2\"", ProjectAuditCommands.EscapeCsv("line1\nline2"));
|
||||
Assert.AreEqual("\"has\r\ncrlf\"", ProjectAuditCommands.EscapeCsv("has\r\ncrlf"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void EscapeCsv_DoublesEmbeddedQuotes()
|
||||
{
|
||||
// Cache the result instead of calling "every frame". -> quoted, with the inner quotes doubled.
|
||||
Assert.AreEqual("\"say \"\"hi\"\"\"", ProjectAuditCommands.EscapeCsv("say \"hi\""));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ValidateCategories_ReturnsNull_WhenNoneRequested()
|
||||
{
|
||||
var valid = new[] { "Code", "ProjectSetting", "Texture" };
|
||||
Assert.IsNull(ProjectAuditCommands.ValidateCategories(new string[0], valid));
|
||||
Assert.IsNull(ProjectAuditCommands.ValidateCategories(null, valid));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ValidateCategories_ReturnsNull_WhenAllValid_CaseInsensitive()
|
||||
{
|
||||
var valid = new[] { "Code", "ProjectSetting", "Texture" };
|
||||
Assert.IsNull(ProjectAuditCommands.ValidateCategories(new[] { "Code", "texture" }, valid));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ValidateCategories_ReportsUnknown_AndListsValid()
|
||||
{
|
||||
var valid = new[] { "Code", "ProjectSetting", "Texture" };
|
||||
var error = ProjectAuditCommands.ValidateCategories(new[] { "Code", "Bogus" }, valid);
|
||||
|
||||
Assert.IsNotNull(error);
|
||||
// The unknown name is named before the "Valid categories:" list; the valid one is not.
|
||||
var unknownPart = error.Substring(0, error.IndexOf("Valid categories:", System.StringComparison.Ordinal));
|
||||
StringAssert.Contains("Bogus", unknownPart);
|
||||
StringAssert.DoesNotContain("Code", unknownPart, "the valid category 'Code' should not be reported as unknown");
|
||||
StringAssert.Contains("ProjectSetting", error, "the error should list the valid categories");
|
||||
}
|
||||
|
||||
/// <summary>Stand-in for Project Auditor's IssueCategory.</summary>
|
||||
enum FakeCategory { Code, Texture }
|
||||
|
||||
/// <summary>
|
||||
/// Stand-in for the serialization wrapper the built-in editor module stores categories in
|
||||
/// (SerializableEnum<IssueCategory>): a struct constructed from the enum value.
|
||||
/// </summary>
|
||||
struct FakeWrappedCategory
|
||||
{
|
||||
public readonly FakeCategory Value;
|
||||
public FakeWrappedCategory(FakeCategory value) => Value = value;
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void BuildCategoryArray_BuildsBareEnumArray_CaseInsensitively()
|
||||
{
|
||||
var array = ProjectAuditCommands.BuildCategoryArray(
|
||||
typeof(FakeCategory), null, typeof(FakeCategory), new[] { "texture", "Code" });
|
||||
|
||||
Assert.AreEqual(typeof(FakeCategory[]), array.GetType());
|
||||
CollectionAssert.AreEqual(new[] { FakeCategory.Texture, FakeCategory.Code }, array);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void BuildCategoryArray_WrapsElements_WhenFieldHoldsAWrapperType()
|
||||
{
|
||||
var wrapperCtor = typeof(FakeWrappedCategory).GetConstructor(new[] { typeof(FakeCategory) });
|
||||
|
||||
var array = ProjectAuditCommands.BuildCategoryArray(
|
||||
typeof(FakeWrappedCategory), wrapperCtor, typeof(FakeCategory), new[] { "Texture" });
|
||||
|
||||
// Assigning a bare-enum array into a wrapper-typed field throws at runtime, so the element
|
||||
// type must be the wrapper itself.
|
||||
Assert.AreEqual(typeof(FakeWrappedCategory[]), array.GetType());
|
||||
Assert.AreEqual(FakeCategory.Texture, ((FakeWrappedCategory[])array)[0].Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4d6b221674a7144d38f7d2805c26a11d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user