Add Unity Pipeline package support
This commit is contained in:
+199
@@ -0,0 +1,199 @@
|
||||
using System.Linq;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NUnit.Framework;
|
||||
using Unity.Pipeline.Commands;
|
||||
using Unity.Pipeline.Editor;
|
||||
using Unity.Pipeline.Editor.Commands.Baking;
|
||||
using UnityEditor;
|
||||
using UnityEditor.SceneManagement;
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
namespace Unity.Pipeline.Tests.Editor.Baking
|
||||
{
|
||||
/// <summary>
|
||||
/// Tests for the CLI-215 lighting bake commands. These cover the parts that are deterministic
|
||||
/// without waiting on a real (minutes-long) lightmap bake: command discovery/schema, the
|
||||
/// settings get/set round-trip, the destructive clear guard, the idle status, the no_scene guard,
|
||||
/// and dry_run being a no-op. The full bake → completed acceptance flow is exercised live against a
|
||||
/// running Editor (it is too slow/platform-dependent for the unit suite).
|
||||
/// </summary>
|
||||
public class LightingBakeCommandsTests
|
||||
{
|
||||
[SetUp]
|
||||
public void SetUp() => CommandRegistry.SetDiscovery(new TypeCacheCommandDiscovery());
|
||||
|
||||
[TearDown]
|
||||
public void TearDown()
|
||||
{
|
||||
// Leave the editor with a fresh empty (untitled) scene so other tests are unaffected.
|
||||
EditorSceneManager.NewScene(NewSceneSetup.EmptyScene, NewSceneMode.Single);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void LightingCommands_AreDiscovered_WithExpectedSchema()
|
||||
{
|
||||
var commands = CommandRegistry.DiscoverCommands().ToList();
|
||||
|
||||
var bake = commands.FirstOrDefault(c => c.Name == "bake_lighting");
|
||||
Assert.IsNotNull(bake, "Should discover bake_lighting");
|
||||
CollectionAssert.AreEquivalent(new[] { "confirm", "dry_run" },
|
||||
bake.Parameters.Select(p => p.Name).ToList());
|
||||
|
||||
var status = commands.FirstOrDefault(c => c.Name == "lighting_bake_status");
|
||||
Assert.IsNotNull(status, "Should discover lighting_bake_status");
|
||||
Assert.IsFalse(status.MainThreadRequired,
|
||||
"lighting_bake_status must be off-main-thread so it answers while a bake holds the main thread");
|
||||
Assert.AreEqual(0, status.Parameters.Count, "lighting_bake_status takes no parameters");
|
||||
|
||||
Assert.IsNotNull(commands.FirstOrDefault(c => c.Name == "cancel_lighting_bake"));
|
||||
Assert.IsNotNull(commands.FirstOrDefault(c => c.Name == "clear_baked_lighting"));
|
||||
Assert.IsNotNull(commands.FirstOrDefault(c => c.Name == "get_lighting_settings"));
|
||||
Assert.IsNotNull(commands.FirstOrDefault(c => c.Name == "set_lighting_settings"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetLightingSettings_ReturnsCoreFields()
|
||||
{
|
||||
var settings = LightingBakeCommands.GetLightingSettings();
|
||||
if (!settings.Available)
|
||||
{
|
||||
// No LightingSettings are exposed when no scene is open (e.g. batchmode test
|
||||
// projects start with an untitled empty scene). Skip rather than fail.
|
||||
Assert.Pass("No active LightingSettings — skipping (no scene open in test project).");
|
||||
return;
|
||||
}
|
||||
Assert.IsNotNull(settings.Lightmapper, "lightmapper should be reported");
|
||||
|
||||
// directionalMode must be one of the two spec values, never a raw flags-enum string.
|
||||
Assert.That(settings.DirectionalMode, Is.EqualTo("NonDirectional").Or.EqualTo("Directional"),
|
||||
"directionalMode must be 'NonDirectional' or 'Directional'");
|
||||
|
||||
// lightmapCompression must be the LightmapCompression enum name, not a bool.
|
||||
var validCompressions = new[] { "None", "LowQuality", "NormalQuality", "HighQuality" };
|
||||
CollectionAssert.Contains(validCompressions, settings.LightmapCompression,
|
||||
"lightmapCompression must be one of the LightmapCompression enum names");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SetLightingSettings_DirectionalMode_RoundTrips()
|
||||
{
|
||||
var before = LightingBakeCommands.GetLightingSettings();
|
||||
if (!before.Available)
|
||||
{
|
||||
Assert.Pass("No active LightingSettings — skipping directionalMode round-trip test.");
|
||||
return;
|
||||
}
|
||||
|
||||
var target = before.DirectionalMode == "NonDirectional" ? "Directional" : "NonDirectional";
|
||||
try
|
||||
{
|
||||
var result = JObject.FromObject(LightingBakeCommands.SetLightingSettings(
|
||||
new JObject { ["directionalMode"] = target }));
|
||||
CollectionAssert.Contains(result["applied"].Select(t => t.Value<string>()).ToList(),
|
||||
"directionalMode", "directionalMode should be in applied[]");
|
||||
|
||||
var after = LightingBakeCommands.GetLightingSettings();
|
||||
Assert.That(after.DirectionalMode, Is.EqualTo("NonDirectional").Or.EqualTo("Directional"),
|
||||
"directionalMode must remain a spec-valid string after set");
|
||||
}
|
||||
finally
|
||||
{
|
||||
LightingBakeCommands.SetLightingSettings(new JObject { ["directionalMode"] = before.DirectionalMode });
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SetLightingSettings_RoundTrips_BouncesAndResolution()
|
||||
{
|
||||
var before = LightingBakeCommands.GetLightingSettings();
|
||||
var newBounces = before.Bounces == 3 ? 2 : 3;
|
||||
var newResolution = before.LightmapResolution >= 40f ? 20f : 50f;
|
||||
|
||||
try
|
||||
{
|
||||
var settings = new JObject
|
||||
{
|
||||
["bounces"] = newBounces,
|
||||
["lightmapResolution"] = newResolution
|
||||
};
|
||||
LightingBakeCommands.SetLightingSettings(settings);
|
||||
|
||||
var after = LightingBakeCommands.GetLightingSettings();
|
||||
Assert.AreEqual(newBounces, after.Bounces, "bounces should round-trip");
|
||||
Assert.AreEqual(newResolution, after.LightmapResolution, 0.001f, "lightmapResolution should round-trip");
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Restore the prior values so the suite leaves no residue in the host project's
|
||||
// lighting settings (the active settings may be a shared scene-default or asset).
|
||||
LightingBakeCommands.SetLightingSettings(new JObject
|
||||
{
|
||||
["bounces"] = before.Bounces,
|
||||
["lightmapResolution"] = before.LightmapResolution
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SetLightingSettings_ReportsUnknownKeys()
|
||||
{
|
||||
var settings = new JObject
|
||||
{
|
||||
["bounces"] = 2,
|
||||
["totallyNotARealKey"] = 5
|
||||
};
|
||||
var result = JObject.FromObject(LightingBakeCommands.SetLightingSettings(settings));
|
||||
|
||||
var applied = result["applied"].Select(t => t.Value<string>()).ToList();
|
||||
var unknown = result["unknown"].Select(t => t.Value<string>()).ToList();
|
||||
CollectionAssert.Contains(applied, "bounces");
|
||||
CollectionAssert.Contains(unknown, "totallyNotARealKey");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SetLightingSettings_DryRun_DoesNotChangeAnything()
|
||||
{
|
||||
var before = LightingBakeCommands.GetLightingSettings();
|
||||
var changed = before.Bounces == 1 ? 4 : 1;
|
||||
|
||||
LightingBakeCommands.SetLightingSettings(new JObject { ["bounces"] = changed }, dryRun: true);
|
||||
|
||||
var after = LightingBakeCommands.GetLightingSettings();
|
||||
Assert.AreEqual(before.Bounces, after.Bounces, "dry_run must not mutate settings");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ClearBakedLighting_WithoutConfirm_Throws()
|
||||
{
|
||||
Assert.Throws<System.ArgumentException>(() => LightingBakeCommands.ClearBakedLighting());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ClearBakedLighting_DryRun_DoesNotThrow_AndReportsWouldClear()
|
||||
{
|
||||
var result = JObject.FromObject(LightingBakeCommands.ClearBakedLighting(confirm: true, dryRun: true));
|
||||
Assert.AreEqual("dry_run", result["status"].Value<string>());
|
||||
Assert.IsTrue(result["wouldClear"].Value<bool>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void BakeLighting_NoOpenSavedScene_ReturnsNoScene()
|
||||
{
|
||||
// A fresh untitled scene has an empty path, so it is not a bakeable (saved) scene.
|
||||
EditorSceneManager.NewScene(NewSceneSetup.EmptyScene, NewSceneMode.Single);
|
||||
Assert.IsTrue(string.IsNullOrEmpty(SceneManager.GetActiveScene().path), "test precondition: untitled scene");
|
||||
|
||||
var result = JObject.FromObject(LightingBakeCommands.BakeLighting());
|
||||
Assert.AreEqual("no_scene", result["code"].Value<string>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void LightingBakeStatus_Idle_WhenNothingTriggered_OrAfterStatusFile()
|
||||
{
|
||||
// The status reflects either a clean idle or the result of a prior (cancelled/completed)
|
||||
// bake from another test; in all cases it must be valid JSON carrying a "status" field.
|
||||
var json = JObject.Parse(LightingBakeCommands.LightingBakeStatus());
|
||||
Assert.IsNotNull(json["status"], "lighting_bake_status should always include a 'status' field");
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bdeb4ada982746e1aa5d72b49157fd3e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+125
@@ -0,0 +1,125 @@
|
||||
using System.Linq;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NUnit.Framework;
|
||||
using Unity.Pipeline.Commands;
|
||||
using Unity.Pipeline.Editor;
|
||||
using Unity.Pipeline.Editor.Commands.Baking;
|
||||
using UnityEditor.SceneManagement;
|
||||
|
||||
namespace Unity.Pipeline.Tests.Editor.Baking
|
||||
{
|
||||
/// <summary>
|
||||
/// Tests for the CLI-215 legacy NavMesh bake commands. Covers discovery/schema, the settings
|
||||
/// get/set round-trip, the destructive clear guard, the idle status, the no_scene guard, dry_run
|
||||
/// being a no-op, and the AI-Navigation stub returning package_not_found when the package is
|
||||
/// absent. The full bake → succeeded flow is exercised live.
|
||||
/// </summary>
|
||||
public class NavMeshBakeCommandsTests
|
||||
{
|
||||
[SetUp]
|
||||
public void SetUp() => CommandRegistry.SetDiscovery(new TypeCacheCommandDiscovery());
|
||||
|
||||
[TearDown]
|
||||
public void TearDown()
|
||||
{
|
||||
EditorSceneManager.NewScene(NewSceneSetup.EmptyScene, NewSceneMode.Single);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void NavMeshCommands_AreDiscovered_WithExpectedSchema()
|
||||
{
|
||||
var commands = CommandRegistry.DiscoverCommands().ToList();
|
||||
|
||||
Assert.IsNotNull(commands.FirstOrDefault(c => c.Name == "bake_navmesh"));
|
||||
Assert.IsNotNull(commands.FirstOrDefault(c => c.Name == "cancel_navmesh_bake"));
|
||||
Assert.IsNotNull(commands.FirstOrDefault(c => c.Name == "clear_navmesh"));
|
||||
Assert.IsNotNull(commands.FirstOrDefault(c => c.Name == "get_navmesh_settings"));
|
||||
Assert.IsNotNull(commands.FirstOrDefault(c => c.Name == "set_navmesh_settings"));
|
||||
|
||||
var status = commands.FirstOrDefault(c => c.Name == "navmesh_bake_status");
|
||||
Assert.IsNotNull(status, "Should discover navmesh_bake_status");
|
||||
Assert.IsFalse(status.MainThreadRequired, "navmesh_bake_status must be off-main-thread");
|
||||
Assert.AreEqual(0, status.Parameters.Count, "navmesh_bake_status takes no parameters");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetNavMeshSettings_ReturnsAgentFields()
|
||||
{
|
||||
var settings = NavMeshBakeCommands.GetNavMeshSettings();
|
||||
Assert.IsTrue(settings.Available, "Legacy NavMesh settings should be available");
|
||||
Assert.Greater(settings.AgentRadius, 0f, "agentRadius should be a sensible positive default");
|
||||
Assert.Greater(settings.AgentHeight, 0f, "agentHeight should be a sensible positive default");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SetNavMeshSettings_RoundTrips_AgentRadius()
|
||||
{
|
||||
var before = NavMeshBakeCommands.GetNavMeshSettings();
|
||||
var newRadius = before.AgentRadius >= 0.5f ? 0.35f : 0.75f;
|
||||
|
||||
NavMeshBakeCommands.SetNavMeshSettings(new JObject { ["agentRadius"] = newRadius });
|
||||
|
||||
var after = NavMeshBakeCommands.GetNavMeshSettings();
|
||||
Assert.AreEqual(newRadius, after.AgentRadius, 0.001f, "agentRadius should round-trip");
|
||||
|
||||
// Restore the prior value so the suite leaves no residue in the host project's settings.
|
||||
NavMeshBakeCommands.SetNavMeshSettings(new JObject { ["agentRadius"] = before.AgentRadius });
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SetNavMeshSettings_ReportsUnknownKeys()
|
||||
{
|
||||
var result = JObject.FromObject(
|
||||
NavMeshBakeCommands.SetNavMeshSettings(new JObject { ["agentRadius"] = 0.5f, ["nope"] = 1 }, dryRun: true));
|
||||
|
||||
var applied = result["applied"].Select(t => t.Value<string>()).ToList();
|
||||
var unknown = result["unknown"].Select(t => t.Value<string>()).ToList();
|
||||
CollectionAssert.Contains(applied, "agentRadius");
|
||||
CollectionAssert.Contains(unknown, "nope");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SetNavMeshSettings_DryRun_DoesNotChangeAnything()
|
||||
{
|
||||
var before = NavMeshBakeCommands.GetNavMeshSettings();
|
||||
var changed = before.AgentRadius >= 0.5f ? 0.2f : 0.9f;
|
||||
|
||||
NavMeshBakeCommands.SetNavMeshSettings(new JObject { ["agentRadius"] = changed }, dryRun: true);
|
||||
|
||||
var after = NavMeshBakeCommands.GetNavMeshSettings();
|
||||
Assert.AreEqual(before.AgentRadius, after.AgentRadius, 0.001f, "dry_run must not mutate settings");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ClearNavMesh_WithoutConfirm_Throws()
|
||||
{
|
||||
Assert.Throws<System.ArgumentException>(() => NavMeshBakeCommands.ClearNavMesh());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void BakeNavMesh_NoOpenSavedScene_ReturnsNoScene()
|
||||
{
|
||||
EditorSceneManager.NewScene(NewSceneSetup.EmptyScene, NewSceneMode.Single);
|
||||
|
||||
var result = JObject.FromObject(NavMeshBakeCommands.BakeNavMesh());
|
||||
Assert.AreEqual("no_scene", result["code"].Value<string>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void NavMeshBakeStatus_AlwaysHasStatusField()
|
||||
{
|
||||
var json = JObject.Parse(NavMeshBakeCommands.NavMeshBakeStatus());
|
||||
Assert.IsNotNull(json["status"]);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void BakeNavMeshSurfaces_WithoutPackage_ReturnsPackageNotFound()
|
||||
{
|
||||
// This repo does not reference com.unity.ai.navigation, so the stub should report the
|
||||
// package is absent (or, if the package is present in the host project, not_implemented).
|
||||
var result = JObject.FromObject(NavMeshBakeCommands.BakeNavMeshSurfaces());
|
||||
var code = result["code"].Value<string>();
|
||||
Assert.That(code, Is.EqualTo("package_not_found").Or.EqualTo("not_implemented"));
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d88fed4842cc4242b9e25237547c5514
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
using System.Linq;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NUnit.Framework;
|
||||
using Unity.Pipeline.Commands;
|
||||
using Unity.Pipeline.Editor;
|
||||
using Unity.Pipeline.Editor.Commands.Baking;
|
||||
using UnityEditor.SceneManagement;
|
||||
|
||||
namespace Unity.Pipeline.Tests.Editor.Baking
|
||||
{
|
||||
/// <summary>
|
||||
/// Tests for the CLI-215 occlusion-culling bake commands. Covers discovery/schema, the destructive
|
||||
/// clear guard, the idle status, the no_scene guard, and dry_run being a no-op (and echoing the
|
||||
/// effective parameters). The full bake → succeeded flow is exercised live.
|
||||
/// </summary>
|
||||
public class OcclusionBakeCommandsTests
|
||||
{
|
||||
[SetUp]
|
||||
public void SetUp() => CommandRegistry.SetDiscovery(new TypeCacheCommandDiscovery());
|
||||
|
||||
[TearDown]
|
||||
public void TearDown()
|
||||
{
|
||||
EditorSceneManager.NewScene(NewSceneSetup.EmptyScene, NewSceneMode.Single);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void OcclusionCommands_AreDiscovered_WithExpectedSchema()
|
||||
{
|
||||
var commands = CommandRegistry.DiscoverCommands().ToList();
|
||||
|
||||
var bake = commands.FirstOrDefault(c => c.Name == "bake_occlusion_culling");
|
||||
Assert.IsNotNull(bake, "Should discover bake_occlusion_culling");
|
||||
CollectionAssert.AreEquivalent(
|
||||
new[] { "smallest_occluder", "smallest_hole", "backface_threshold", "confirm", "dry_run" },
|
||||
bake.Parameters.Select(p => p.Name).ToList());
|
||||
|
||||
var status = commands.FirstOrDefault(c => c.Name == "occlusion_bake_status");
|
||||
Assert.IsNotNull(status, "Should discover occlusion_bake_status");
|
||||
Assert.IsFalse(status.MainThreadRequired, "occlusion_bake_status must be off-main-thread");
|
||||
Assert.AreEqual(0, status.Parameters.Count, "occlusion_bake_status takes no parameters");
|
||||
|
||||
Assert.IsNotNull(commands.FirstOrDefault(c => c.Name == "cancel_occlusion_bake"));
|
||||
Assert.IsNotNull(commands.FirstOrDefault(c => c.Name == "clear_occlusion_culling"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ClearOcclusion_WithoutConfirm_Throws()
|
||||
{
|
||||
Assert.Throws<System.ArgumentException>(() => OcclusionBakeCommands.ClearOcclusionCulling());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void BakeOcclusion_NoOpenSavedScene_ReturnsNoScene()
|
||||
{
|
||||
EditorSceneManager.NewScene(NewSceneSetup.EmptyScene, NewSceneMode.Single);
|
||||
|
||||
var result = JObject.FromObject(OcclusionBakeCommands.BakeOcclusionCulling());
|
||||
Assert.AreEqual("no_scene", result["code"].Value<string>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void OcclusionBakeStatus_AlwaysHasStatusField()
|
||||
{
|
||||
var json = JObject.Parse(OcclusionBakeCommands.OcclusionBakeStatus());
|
||||
Assert.IsNotNull(json["status"]);
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f65c8110a95f44cbbf3aaf0d9832aa0e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user