streamingle-unity-utilities/CameraAI~/Tests/Editor/AICameraGeneratorWindowTests.cs
2026-08-03 04:42:44 +09:00

845 lines
31 KiB
C#

using System;
using System.Globalization;
using System.IO;
using System.Security.Cryptography;
using NUnit.Framework;
using UnityEngine;
namespace Streamingle.Editor.Tests
{
public sealed class AICameraGeneratorWindowTests
{
[Serializable]
private sealed class DirectiveDocument
{
public string schemaVersion;
public DirectiveShot[] shots;
}
[Serializable]
private sealed class DirectiveShot
{
public int index;
public string shotSize;
public string motion;
public string composition;
public float distanceMeters;
public float motionIntensity;
public float bodyFollowStrength;
}
private static string HybridMetadataJson(
string generationMode = "hierarchical_hybrid_retarget_v12",
string plannerMode = "hierarchical",
double targetAspectRatio = 16d / 9d)
{
return "{" +
"\"schemaVersion\":\"3.6\"," +
$"\"generationMode\":\"{generationMode}\"," +
"\"songId\":\"song-id\"," +
"\"datasetId\":\"dataset-id\"," +
"\"frameCount\":6," +
"\"sampleRate\":60," +
"\"shotCount\":2," +
$"\"plannerMode\":\"{plannerMode}\"," +
"\"targetAspectRatio\":" +
targetAspectRatio.ToString(
"R",
CultureInfo.InvariantCulture) + "," +
"\"worldCameraFile\":\"generated_camera_world.f32\"," +
"\"timeFile\":\"time.f64\"," +
"\"shotsFile\":\"shots.json\"," +
"\"outputSha256\":{" +
"\"worldCamera\":\"camera-hash\"," +
"\"time\":\"time-hash\"," +
"\"shots\":\"shots-hash\"}}";
}
private static string HybridShotsJson(string schemaVersion = "1.2")
{
return "{" +
$"\"schemaVersion\":\"{schemaVersion}\"," +
"\"shots\":[" +
"{\"index\":0,\"startFrame\":0,\"endFrameExclusive\":3}," +
"{\"index\":1,\"startFrame\":3,\"endFrameExclusive\":6}]}";
}
[Test]
public void DefaultCwAiRootPrefersConfiguredThenSiblingRepository()
{
var root = Path.Combine(
Path.GetTempPath(),
"cw-ai-root-tests-" + Guid.NewGuid().ToString("N"));
var project = Path.Combine(root, "UnityProject");
var configured = Path.Combine(root, "ConfiguredCWAI");
var sibling = Path.Combine(root, "CW-AI");
Directory.CreateDirectory(project);
CreateCwAiRootMarker(configured);
CreateCwAiRootMarker(sibling);
try
{
Assert.That(
AICameraGeneratorWindow.ResolveDefaultCwAiRoot(
project,
configured),
Is.EqualTo(Path.GetFullPath(configured)));
Assert.That(
AICameraGeneratorWindow.ResolveDefaultCwAiRoot(
project,
string.Empty),
Is.EqualTo(Path.GetFullPath(sibling)));
}
finally
{
Directory.Delete(root, true);
}
}
[Test]
public void DefaultCwAiRootSkipsProjectCopyWithoutTrainingIndex()
{
var root = Path.Combine(
Path.GetTempPath(),
"cw-ai-incomplete-root-tests-" + Guid.NewGuid().ToString("N"));
var project = Path.Combine(root, "UnityProject");
var sibling = Path.Combine(root, "CW-AI");
Directory.CreateDirectory(Path.Combine(project, "DatasetExports"));
Directory.CreateDirectory(Path.Combine(
project,
"MachineLearning",
"CameraDirector"));
CreateCwAiRootMarker(sibling);
try
{
Assert.That(
AICameraGeneratorWindow.IsGenerationLibraryRoot(project),
Is.False);
Assert.That(
AICameraGeneratorWindow.ResolveDefaultCwAiRoot(
project,
project),
Is.EqualTo(Path.GetFullPath(sibling)));
}
finally
{
Directory.Delete(root, true);
}
}
[Test]
public void DefaultCwAiRootSkipsMalformedConfiguredPath()
{
var root = Path.Combine(
Path.GetTempPath(),
"cw-ai-malformed-root-tests-" + Guid.NewGuid().ToString("N"));
var project = Path.Combine(root, "UnityProject");
var sibling = Path.Combine(root, "CW-AI");
Directory.CreateDirectory(project);
CreateCwAiRootMarker(sibling);
try
{
Assert.That(
AICameraGeneratorWindow.ResolveDefaultCwAiRoot(
project,
"\0invalid"),
Is.EqualTo(Path.GetFullPath(sibling)));
}
finally
{
Directory.Delete(root, true);
}
}
[Test]
public void ProjectCacheRootDefaultsToWritableProjectLibrary()
{
var project = Path.Combine(
Path.GetTempPath(),
"cw-ai-cache-root-tests-" + Guid.NewGuid().ToString("N"));
var configured = Path.Combine(project, "ConfiguredCache");
Assert.That(
AICameraGeneratorWindow.ResolveProjectCacheRoot(
project,
string.Empty),
Is.EqualTo(Path.Combine(
Path.GetFullPath(project),
"Library",
"CWAI")));
Assert.That(
AICameraGeneratorWindow.ResolveProjectCacheRoot(
project,
configured),
Is.EqualTo(Path.GetFullPath(configured)));
}
private static void CreateCwAiRootMarker(string root)
{
Directory.CreateDirectory(Path.Combine(root, "DatasetExports"));
Directory.CreateDirectory(Path.Combine(
root,
"MachineLearning",
"CameraDirector"));
var reports = Directory.CreateDirectory(Path.Combine(root, "reports"));
File.WriteAllText(
Path.Combine(reports.FullName, "training_index.json"),
"{}");
var models = Directory.CreateDirectory(Path.Combine(root, "models"));
File.WriteAllText(
Path.Combine(models.FullName, "cut_ranker_v2.json"),
"{}");
}
[Test]
public void CliWorkerIsTheOnlyHighQualityBackend()
{
Assert.That(
AICameraGeneratorWindow.HybridQualityBackendLabel,
Does.Contain("CWCameraWorker"));
Assert.That(
AICameraGeneratorWindow.HybridQualityBackendDescription,
Does.Contain("별도 Python 설치가 필요하지 않습니다"));
}
[TestCase(0, "auto")]
[TestCase(1, "slow")]
[TestCase(2, "normal")]
[TestCase(3, "fast")]
public void CutRhythmUsesStableDataPlannerCliValues(
int value,
string expected)
{
Assert.That(
AICameraGeneratorWindow.CutDensityCliValueForTests(value),
Is.EqualTo(expected));
Assert.That(
AICameraGeneratorWindow.CutRhythmExplanation,
Does.Contain("음악"));
}
[TestCase("auto", "balanced")]
[TestCase("center", "centered")]
[TestCase("centered", "centered")]
[TestCase("left_third", "left_third")]
[TestCase("right-third", "right_third")]
public void HybridHorizontalFramingPreservesExplicitDirection(
string composition,
string expected)
{
Assert.That(
AICameraGeneratorWindow.HybridHorizontalFraming(composition),
Is.EqualTo(expected));
}
[Test]
public void FullGenerationIgnoresPersistedManualOverrides()
{
var unusedOutput = Path.Combine(
Path.GetTempPath(),
"cw-ai-full-scope-" + Guid.NewGuid().ToString("N"));
var scope = AICameraGeneratorWindow.CreateGenerationControlScope(
false,
12,
unusedOutput,
"close",
"orbit_right",
"left_third",
9.5f,
2f,
1f);
Assert.That(scope.ShotSize, Is.EqualTo("auto"));
Assert.That(scope.Motion, Is.EqualTo("auto"));
Assert.That(scope.Composition, Is.EqualTo("auto"));
Assert.That(scope.DistanceMeters, Is.Zero);
Assert.That(scope.MotionIntensity, Is.EqualTo(1f));
Assert.That(scope.BodyFollowStrength, Is.EqualTo(0.18f));
Assert.That(scope.DirectivesPath, Is.Empty);
Assert.That(scope.UsesSelectedShotOverrides, Is.False);
Assert.That(Directory.Exists(unusedOutput), Is.False);
}
[Test]
public void SelectedShotRegenerationSerializesOnlyThatShotsOverrides()
{
var output = Path.Combine(
Path.GetTempPath(),
"cw-ai-selected-scope-" + Guid.NewGuid().ToString("N"));
try
{
var scope =
AICameraGeneratorWindow.CreateGenerationControlScope(
true,
7,
output,
"medium",
"truck_left",
"right_third",
6.25f,
1.4f,
0.08f);
Assert.That(scope.ShotSize, Is.EqualTo("auto"));
Assert.That(scope.Motion, Is.EqualTo("auto"));
Assert.That(scope.Composition, Is.EqualTo("auto"));
Assert.That(scope.DistanceMeters, Is.Zero);
Assert.That(scope.MotionIntensity, Is.EqualTo(1f));
Assert.That(scope.BodyFollowStrength, Is.EqualTo(0.18f));
Assert.That(scope.UsesSelectedShotOverrides, Is.True);
Assert.That(File.Exists(scope.DirectivesPath), Is.True);
var document = JsonUtility.FromJson<DirectiveDocument>(
File.ReadAllText(scope.DirectivesPath));
Assert.That(
document.schemaVersion,
Is.EqualTo("camera-directives-v1"));
Assert.That(document.shots, Has.Length.EqualTo(1));
Assert.That(document.shots[0].index, Is.EqualTo(7));
Assert.That(document.shots[0].shotSize, Is.EqualTo("medium"));
Assert.That(document.shots[0].motion, Is.EqualTo("truck_left"));
Assert.That(
document.shots[0].composition,
Is.EqualTo("right_third"));
Assert.That(document.shots[0].distanceMeters, Is.EqualTo(6.25f));
Assert.That(document.shots[0].motionIntensity, Is.EqualTo(1.4f));
Assert.That(document.shots[0].bodyFollowStrength, Is.EqualTo(0.08f));
}
finally
{
if (Directory.Exists(output))
{
Directory.Delete(output, true);
}
}
}
[Test]
public void FullGenerationDoesNotEmitSelectedShotOneShotArguments()
{
Assert.That(
AICameraGeneratorWindow.BuildSelectedShotOneShotArguments(
-1,
string.Empty),
Is.Empty);
}
[Test]
public void SelectedShotOneShotArgumentsIncludeIndexAndBaseGeneration()
{
var baseGeneration = Path.Combine(
Path.GetTempPath(),
"cw ai base generation");
var arguments =
AICameraGeneratorWindow.BuildSelectedShotOneShotArguments(
7,
baseGeneration);
Assert.That(arguments, Does.Contain("--only-shot-index"));
Assert.That(arguments, Does.Contain("7"));
Assert.That(arguments, Does.Contain("--base-generation"));
Assert.That(
arguments,
Does.Contain(Path.GetFullPath(baseGeneration)));
}
[Test]
public void SelectedShotTimingUsesSeparateOneShotHistory()
{
var text =
AICameraGeneratorWindow.BuildSelectedShotGenerationTimingText(
4d,
10d,
AICameraGeneratorWindow.CacheObservation.Hit,
AICameraGeneratorWindow.CacheObservation.Miss);
Assert.That(text, Does.Contain("선택 카메라 1개만 계산 중"));
Assert.That(text, Does.Contain("최근 단일 샷 기준 총"));
Assert.That(text, Does.Contain("새 후보 풀 계산 중"));
}
[Test]
public void FailedImportPreservesPreviousGeneratedFolder()
{
var previous = Path.Combine(Path.GetTempPath(), "previous-base");
var failedCandidate = Path.Combine(
Path.GetTempPath(),
"failed-candidate");
Assert.That(
AICameraGeneratorWindow.GeneratedFolderAfterImport(
previous,
failedCandidate,
false),
Is.EqualTo(previous));
}
[Test]
public void SuccessfulImportAdvancesGeneratedFolder()
{
var candidate = Path.Combine(Path.GetTempPath(), "new-base");
Assert.That(
AICameraGeneratorWindow.GeneratedFolderAfterImport(
"previous-base",
candidate,
true),
Is.EqualTo(Path.GetFullPath(candidate)));
}
[Test]
public void HybridBaseContractAcceptsFullCompositeSchema()
{
Assert.DoesNotThrow(() =>
AICameraGeneratorWindow
.ValidateHybridBaseGenerationContractForTests(
HybridMetadataJson(),
HybridShotsJson()));
}
[Test]
public void HybridBaseContractRejectsNonHybridOutputBeforeWorkerStarts()
{
var exception = Assert.Throws<InvalidDataException>(() =>
AICameraGeneratorWindow
.ValidateHybridBaseGenerationContractForTests(
HybridMetadataJson(
generationMode: "unity_native_fast",
plannerMode: string.Empty,
targetAspectRatio: 0d),
HybridShotsJson("1.3")));
Assert.That(exception.Message, Does.Contain("Hybrid"));
}
[Test]
public void HybridBaseContractRequiresTargetAspectRatio()
{
var exception = Assert.Throws<InvalidDataException>(() =>
AICameraGeneratorWindow
.ValidateHybridBaseGenerationContractForTests(
HybridMetadataJson(targetAspectRatio: 0d),
HybridShotsJson()));
Assert.That(exception.Message, Does.Contain("targetAspectRatio"));
}
[Test]
public void HybridBaseContractRequiresShotsSchema12()
{
var exception = Assert.Throws<InvalidDataException>(() =>
AICameraGeneratorWindow
.ValidateHybridBaseGenerationContractForTests(
HybridMetadataJson(),
HybridShotsJson("1.3")));
Assert.That(exception.Message, Does.Contain("schema 1.2"));
}
[Test]
public void WindowProvenanceRecoveryPrefersCwAiRelativePathAfterMove()
{
var newRoot = Path.Combine(
Path.GetTempPath(),
"cw-ai-moved-" + Guid.NewGuid().ToString("N"));
var relative = Path.Combine(
"examples",
"UnityGenerated",
"camera_result");
var movedFolder = Path.Combine(newRoot, relative);
Directory.CreateDirectory(movedFolder);
try
{
Assert.That(
AICameraGeneratorWindow
.ResolveGeneratedFolderFromProvenance(
newRoot,
relative.Replace('\\', '/'),
@"D:\OldPc\CW-AI\examples\UnityGenerated\camera_result"),
Is.EqualTo(Path.GetFullPath(movedFolder)));
}
finally
{
Directory.Delete(newRoot, true);
}
}
[Test]
public void WindowProvenanceRecoveryUsesAbsoluteFallback()
{
var fallback = Path.Combine(
Path.GetTempPath(),
"cw-ai-fallback-" + Guid.NewGuid().ToString("N"));
Directory.CreateDirectory(fallback);
try
{
Assert.That(
AICameraGeneratorWindow
.ResolveGeneratedFolderFromProvenance(
Path.Combine(fallback, "missing-root"),
"examples/UnityGenerated/missing",
fallback),
Is.EqualTo(Path.GetFullPath(fallback)));
}
finally
{
Directory.Delete(fallback, true);
}
}
[Test]
public void WindowProvenanceRejectsMetadataChangedBehindStoredHash()
{
var generatedFolder = Path.Combine(
Path.GetTempPath(),
"cw-ai-provenance-hash-" + Guid.NewGuid().ToString("N"));
Directory.CreateDirectory(generatedFolder);
var metadataPath = Path.Combine(generatedFolder, "metadata.json");
try
{
File.WriteAllText(metadataPath, "{\"generation\":1}");
string expected;
using (var algorithm = SHA256.Create())
{
expected = BitConverter.ToString(
algorithm.ComputeHash(File.ReadAllBytes(metadataPath)))
.Replace("-", string.Empty)
.ToLowerInvariant();
}
Assert.DoesNotThrow(() =>
AICameraGeneratorWindow.ValidateProvenanceMetadataSha256(
generatedFolder,
expected));
File.WriteAllText(metadataPath, "{\"generation\":2}");
Assert.That(
() => AICameraGeneratorWindow
.ValidateProvenanceMetadataSha256(
generatedFolder,
expected),
Throws.TypeOf<InvalidDataException>()
.With.Message.Contains("does not match"));
}
finally
{
Directory.Delete(generatedFolder, true);
}
}
[Test]
public void ColdStartHybridEstimateIsBroadAndMarkedInitial()
{
var estimate = AICameraGeneratorWindow.CalculateRuntimeEstimate(
204.48d,
0d,
0,
-1d,
0d);
Assert.That(estimate.IsAvailable, Is.True);
Assert.That(estimate.IsInitial, Is.True);
Assert.That(
estimate.MinimumTotalSeconds,
Is.EqualTo(79.4112d).Within(1e-4));
Assert.That(
estimate.MaximumTotalSeconds,
Is.EqualTo(148.23424d).Within(1e-4));
Assert.That(
estimate.MinimumRemainingSeconds,
Is.EqualTo(79.4112d).Within(1e-4));
Assert.That(
estimate.MaximumRemainingSeconds,
Is.EqualTo(148.23424d).Within(1e-4));
}
[Test]
public void HistoricalEstimateUsesObservedRateAndElapsedTime()
{
var estimate = AICameraGeneratorWindow.CalculateRuntimeEstimate(
200d,
20d,
4,
0.5d,
0.05d);
Assert.That(estimate.IsAvailable, Is.True);
Assert.That(estimate.IsInitial, Is.False);
Assert.That(estimate.MinimumTotalSeconds, Is.EqualTo(85d));
Assert.That(estimate.MaximumTotalSeconds, Is.EqualTo(115d));
Assert.That(estimate.MinimumRemainingSeconds, Is.EqualTo(65d));
Assert.That(estimate.MaximumRemainingSeconds, Is.EqualTo(95d));
}
[Test]
public void CandidateCacheHitReplacesGeneralHistoryWithShortTailBudget()
{
var estimate = AICameraGeneratorWindow.CalculateRuntimeEstimate(
200d,
10d,
4,
0.5d,
0.05d,
AICameraGeneratorWindow.CacheObservation.Hit,
AICameraGeneratorWindow.CacheObservation.Hit,
3,
false);
Assert.That(estimate.MinimumTotalSeconds, Is.EqualTo(13d));
Assert.That(estimate.MaximumTotalSeconds, Is.EqualTo(30d));
Assert.That(estimate.MinimumRemainingSeconds, Is.EqualTo(3d));
Assert.That(estimate.MaximumRemainingSeconds, Is.EqualTo(20d));
}
[Test]
public void CandidateCacheHitTailCountsDownFromObservationTime()
{
var initial = AICameraGeneratorWindow.CalculateRuntimeEstimate(
200d,
10d,
4,
0.5d,
0.05d,
AICameraGeneratorWindow.CacheObservation.Hit,
AICameraGeneratorWindow.CacheObservation.Hit,
3,
false,
10d);
var later = AICameraGeneratorWindow.CalculateRuntimeEstimate(
200d,
15d,
4,
0.5d,
0.05d,
AICameraGeneratorWindow.CacheObservation.Hit,
AICameraGeneratorWindow.CacheObservation.Hit,
3,
false,
10d);
Assert.That(initial.MaximumRemainingSeconds, Is.EqualTo(20d));
Assert.That(later.MaximumRemainingSeconds, Is.EqualTo(15d));
Assert.That(
later.MaximumRemainingSeconds,
Is.LessThan(initial.MaximumRemainingSeconds));
Assert.That(later.MaximumTotalSeconds, Is.EqualTo(30d));
}
[Test]
public void PreparationCacheMissAddsColdPreparationPenalty()
{
var estimate = AICameraGeneratorWindow.CalculateRuntimeEstimate(
200d,
20d,
4,
0.5d,
0.05d,
AICameraGeneratorWindow.CacheObservation.Miss,
AICameraGeneratorWindow.CacheObservation.Miss,
2,
false);
Assert.That(estimate.MinimumTotalSeconds, Is.EqualTo(103d));
Assert.That(estimate.MaximumTotalSeconds, Is.EqualTo(164d));
Assert.That(estimate.MinimumRemainingSeconds, Is.EqualTo(83d));
Assert.That(estimate.MaximumRemainingSeconds, Is.EqualTo(144d));
}
[Test]
public void ImportStageBoundsStaleRemainingEstimate()
{
var estimate = AICameraGeneratorWindow.CalculateRuntimeEstimate(
200d,
20d,
4,
0.5d,
0.05d,
AICameraGeneratorWindow.CacheObservation.Hit,
AICameraGeneratorWindow.CacheObservation.Miss,
4,
true);
Assert.That(estimate.MinimumRemainingSeconds, Is.Zero);
Assert.That(estimate.MaximumRemainingSeconds, Is.EqualTo(24d));
Assert.That(estimate.MaximumTotalSeconds, Is.EqualTo(44d));
}
[Test]
public void ImportStageTailCountsDownFromObservationTime()
{
var initial = AICameraGeneratorWindow.CalculateRuntimeEstimate(
200d,
20d,
4,
0.5d,
0.05d,
AICameraGeneratorWindow.CacheObservation.Hit,
AICameraGeneratorWindow.CacheObservation.Miss,
4,
true,
double.NaN,
20d);
var later = AICameraGeneratorWindow.CalculateRuntimeEstimate(
200d,
21d,
4,
0.5d,
0.05d,
AICameraGeneratorWindow.CacheObservation.Hit,
AICameraGeneratorWindow.CacheObservation.Miss,
4,
true,
double.NaN,
20d);
Assert.That(initial.MaximumRemainingSeconds, Is.EqualTo(24d));
Assert.That(later.MaximumRemainingSeconds, Is.EqualTo(23d));
Assert.That(
later.MaximumRemainingSeconds,
Is.LessThan(initial.MaximumRemainingSeconds));
Assert.That(initial.MaximumTotalSeconds, Is.EqualTo(44d));
Assert.That(later.MaximumTotalSeconds, Is.EqualTo(44d));
}
[Test]
public void InvalidDurationDoesNotClaimAnEstimate()
{
var estimate = AICameraGeneratorWindow.CalculateRuntimeEstimate(
0d,
10d,
2,
0.5d,
0.1d);
Assert.That(estimate.IsAvailable, Is.False);
Assert.That(estimate.MinimumTotalSeconds, Is.Zero);
Assert.That(estimate.MaximumTotalSeconds, Is.Zero);
Assert.That(estimate.MinimumRemainingSeconds, Is.Zero);
Assert.That(estimate.MaximumRemainingSeconds, Is.Zero);
}
[Test]
public void TimingTextShowsElapsedTotalRemainingAndCacheState()
{
var estimate = AICameraGeneratorWindow.CalculateRuntimeEstimate(
200d,
10d,
4,
0.5d,
0.05d,
AICameraGeneratorWindow.CacheObservation.Hit,
AICameraGeneratorWindow.CacheObservation.Hit,
3,
false);
var text = AICameraGeneratorWindow.BuildGenerationTimingText(
10d,
estimate,
AICameraGeneratorWindow.CacheObservation.Hit,
AICameraGeneratorWindow.CacheObservation.Hit);
Assert.That(text, Does.Contain("경과 10초"));
Assert.That(text, Does.Contain("최근 실측 예상 총"));
Assert.That(text, Does.Contain("남은 시간"));
Assert.That(text, Does.Contain("안전 후보 캐시 사용 중"));
}
[TestCase("Candidate cache hit: abc", "candidate", 1)]
[TestCase("[candidate-cache] miss: none", "candidate", 2)]
[TestCase(
"Candidate cache runtime validation failed; falling back",
"candidate",
2)]
[TestCase("Preparation cache invalid: sha mismatch", "preparation", 2)]
[TestCase("unrelated message", "candidate", 0)]
public void CacheLogObservationParsesStableSignals(
string message,
string cacheName,
int expected)
{
Assert.That(
AICameraGeneratorWindow.ParseCacheObservation(
message,
cacheName),
Is.EqualTo(
(AICameraGeneratorWindow.CacheObservation)expected));
}
[Test]
public void CandidateCacheModeDefaultsToAutoAndFreshPoolUsesOff()
{
Assert.That(
AICameraGeneratorWindow.CandidateCacheMode(false),
Is.EqualTo("auto"));
Assert.That(
AICameraGeneratorWindow.CandidateCacheMode(true),
Is.EqualTo("off"));
}
[Test]
public void AppliedSeedSummarySeparatesCurrentResultFromNextGeneration()
{
var summary = AICameraGeneratorWindow.BuildAppliedSeedSummary(
20489696,
20489697,
false,
-1);
Assert.That(
summary,
Is.EqualTo(
"현재 전체 생성 결과 Seed: 20489696\n" +
"다음 생성에 사용할 Seed: 20489697"));
}
[Test]
public void AppliedSeedSummaryMarksSelectedShotAsMixedResult()
{
var summary = AICameraGeneratorWindow.BuildAppliedSeedSummary(
20489696,
20489697,
true,
8);
Assert.That(summary, Does.Contain("카메라: 008"));
Assert.That(summary, Does.Contain("해당 카메라 Seed: 20489696"));
Assert.That(summary, Does.Contain("나머지 카메라는 기존 결과"));
}
[Test]
public void SeedAndReapplyLabelsDescribeDifferentActions()
{
Assert.That(
AICameraGeneratorWindow.NextGenerationSeedLabel,
Does.Contain("다음 생성"));
Assert.That(
AICameraGeneratorWindow.SelectedCameraRegenerationButtonLabel,
Does.Contain("안전 후보"));
Assert.That(
AICameraGeneratorWindow.ListedCameraRegenerationButtonLabel,
Does.Contain("안전 후보"));
Assert.That(
AICameraGeneratorWindow.ReapplyCurrentResultButtonLabel,
Does.Contain("Seed 사용 안 함"));
Assert.That(
AICameraGeneratorWindow.ReapplyCurrentResultWarning,
Does.Contain("새 후보를 생성하지 않으며"));
Assert.That(
AICameraGeneratorWindow.SeedCandidateExplanation,
Does.Contain("안전 후보 풀을 재사용"));
Assert.That(
AICameraGeneratorWindow.FreshCandidatePoolOptionLabel,
Does.Contain("후보 풀 캐시 무시하고 재계산"));
Assert.That(
AICameraGeneratorWindow.FullAutonomousGenerationButtonLabel,
Does.Contain("자율 생성"));
Assert.That(
AICameraGeneratorWindow.FullGenerationAutonomyExplanation,
Does.Contain("전체 생성에 적용되지 않습니다"));
Assert.That(
AICameraGeneratorWindow.SelectedShotOverrideExplanation,
Does.Contain("선택한 카메라를 다시 생성할 때만"));
}
}
}