using System; using NUnit.Framework; using UnityEditor.Recorder.Timeline; using UnityEngine; using UnityEngine.Timeline; namespace Streamingle.Editor.Tests { public sealed class AICameraABRenderTests { [Test] public void ToInclusiveEndFrame_UsesEndExclusiveContract() { Assert.That( AICameraABRenderUtility.ToInclusiveEndFrame(10, 20, 100), Is.EqualTo(19)); } [Test] public void ValidateFrameRange_RejectsEmptyRange() { Assert.Throws( () => AICameraABRenderUtility.ValidateFrameRange( 10, 10, 100)); } [Test] public void ValidateOutputSettings_RejectsOddMp4Dimensions() { Assert.Throws( () => AICameraABRenderUtility.ValidateOutputSettings( AICameraABRenderCodec.MP4, 1919, 1080)); Assert.DoesNotThrow( () => AICameraABRenderUtility.ValidateOutputSettings( AICameraABRenderCodec.WEBM, 1919, 1080)); } [Test] public void ValidateCandidatePair_RejectsDifferentTimeData() { var candidateA = CreateProbe("same-time"); var candidateB = CreateProbe("different-time"); var request = CreateRequest(); Assert.Throws( () => AICameraABRenderUtility.ValidateCandidatePair( candidateA, candidateB, request)); } [Test] public void ValidateCandidatePair_RejectsDifferentDatasets() { var candidateA = CreateProbe("same-time", "dataset-a"); var candidateB = CreateProbe("same-time", "dataset-b"); Assert.Throws( () => AICameraABRenderUtility.ValidateCandidatePair( candidateA, candidateB, CreateRequest())); } [Test] public void MuteCopiedRecorderTracks_MutesExistingRecorderTrack() { var timeline = ScriptableObject.CreateInstance(); try { var recorderTrack = timeline.CreateTrack( null, "Existing Recorder"); recorderTrack.muted = false; var mutedCount = AICameraTimelinePreviewImporter.MuteCopiedRecorderTracks( timeline); Assert.That(mutedCount, Is.EqualTo(1)); Assert.That(recorderTrack.muted, Is.True); Assert.That( AICameraTimelinePreviewImporter.MuteCopiedRecorderTracks( timeline), Is.Zero); } finally { UnityEngine.Object.DestroyImmediate(timeline); } } [Test] public void WriteJsonAtomic_ReplacesExistingStateWithoutTempFiles() { var directory = System.IO.Path.Combine( System.IO.Path.GetTempPath(), $"cw-ai-ab-json-{Guid.NewGuid():N}"); var path = System.IO.Path.Combine(directory, "active_job.json"); try { AICameraABRenderUtility.WriteJsonAtomic( path, new AICameraABRenderJobState { jobId = "first" }); AICameraABRenderUtility.WriteJsonAtomic( path, new AICameraABRenderJobState { jobId = "second" }); var restored = AICameraABRenderUtility.ReadJson( path); Assert.That(restored.jobId, Is.EqualTo("second")); Assert.That( System.IO.Directory.GetFiles(directory, "*.tmp"), Is.Empty); } finally { if (System.IO.Directory.Exists(directory)) { System.IO.Directory.Delete(directory, true); } } } private static AICameraABCandidateProbe CreateProbe( string timeSha, string datasetId = "dataset") { return new AICameraABCandidateProbe { Metadata = new AICameraABGeneratedCameraMetadata(), Result = new AICameraABCandidateResult { sampleRate = 60, frameCount = 120, duration = 2, timeSha256 = timeSha, songId = "song", datasetId = datasetId, targetEvaluationGroupId = "group", targetAspectRatio = 16.0 / 9.0 } }; } private static AICameraABRenderRequest CreateRequest() { return new AICameraABRenderRequest { startFrame = 0, endFrameExclusive = 120, codec = AICameraABRenderCodec.MP4, width = 1920, height = 1080 }; } } }