2a8888089f
Deploy / deploy (push) Successful in 6m33s
PrepareOutputDir cleared every file in the output dir whose name started
with the output pattern's prefix. The preview stage writes its MOV and MP4
into that same folder from the same basename ({outputDir}/{base}.mov), so
they matched the prefix and were deleted before aerender started.
The dir is not private to one export: outputDir is keyed on the shot's
manually-set shotVersion, not on the export, so re-queuing without a
Version Up renders into the folder again. That is exactly the "No preview"
workflow - export scaled, reverse the scale, re-render only the EXRs and
inherit the earlier MOV - and inheritPreviewMedia then handed the new
export a deliveryMovPath pointing at the file the render had just removed.
Nothing surfaced it: the review MP4 is an uploaded blob and still played,
and DeliveryStage treats a missing MOV as non-fatal, so the package
silently shipped EXRs with no viewing copy.
Match on prefix and extension instead, via a new
SequencePattern.MatchesOutputFile - the predicate ValidationStage and
DeliveryStage already applied inline. Crashed-attempt cleanup is unchanged;
only files with a different extension are now spared.
JobExecutor's post-render stats loop matched on prefix alone too. With the
media no longer deleted it would have counted the MOV/MP4 as frames and
failed validation's file_count check, so it uses the same matcher.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
141 lines
4.9 KiB
C#
141 lines
4.9 KiB
C#
using VFXReviewWorker;
|
|
using Xunit;
|
|
|
|
namespace VFXReviewWorker.Tests;
|
|
|
|
public class PathMapperTests
|
|
{
|
|
private static PathMapper Mapper(params (string from, string to)[] maps) =>
|
|
new(maps.Select(m => new PathMapping { From = m.from, To = m.to }));
|
|
|
|
[Fact]
|
|
public void MapsUncToDriveLetter()
|
|
{
|
|
var mapper = Mapper(("//SAN/", "S:/"));
|
|
Assert.Equal(@"S:\renders\UNG_S1\106\shot\v004", mapper.Map("//SAN/renders/UNG_S1/106/shot/v004"));
|
|
}
|
|
|
|
[Fact]
|
|
public void MapsBackslashInputToo()
|
|
{
|
|
var mapper = Mapper(("//SAN/", "S:/"));
|
|
Assert.Equal(@"S:\projects\a.aep", mapper.Map(@"\\SAN\projects\a.aep"));
|
|
}
|
|
|
|
[Fact]
|
|
public void CaseInsensitiveMatch()
|
|
{
|
|
var mapper = Mapper(("//SAN/", "S:/"));
|
|
Assert.Equal(@"S:\x", mapper.Map("//san/x"));
|
|
}
|
|
|
|
[Fact]
|
|
public void FirstMatchingMappingWins()
|
|
{
|
|
var mapper = Mapper(("//SAN/renders/", "R:/"), ("//SAN/", "S:/"));
|
|
Assert.Equal(@"R:\a", mapper.Map("//SAN/renders/a"));
|
|
Assert.Equal(@"S:\projects\a", mapper.Map("//SAN/projects/a"));
|
|
}
|
|
|
|
[Fact]
|
|
public void UnmappedPathPassesThroughWithLocalSeparators()
|
|
{
|
|
var mapper = Mapper(("//SAN/", "S:/"));
|
|
Assert.Equal(@"D:\local\thing", mapper.Map("D:/local/thing"));
|
|
}
|
|
}
|
|
|
|
public class PrepareOutputDirTests
|
|
{
|
|
[Fact]
|
|
public void ClearsOnlyFilesMatchingPatternPrefix()
|
|
{
|
|
var dir = Path.Combine(Path.GetTempPath(), "vfxr-test-" + Guid.NewGuid().ToString("N"));
|
|
Directory.CreateDirectory(dir);
|
|
try
|
|
{
|
|
File.WriteAllText(Path.Combine(dir, "SHOT_cmp_TT_v004.1001.exr"), "old");
|
|
File.WriteAllText(Path.Combine(dir, "SHOT_cmp_TT_v004.1002.exr"), "old");
|
|
File.WriteAllText(Path.Combine(dir, "unrelated-notes.txt"), "keep me");
|
|
|
|
AerenderRunner.PrepareOutputDir(dir, "SHOT_cmp_TT_v004.[####].exr");
|
|
|
|
Assert.False(File.Exists(Path.Combine(dir, "SHOT_cmp_TT_v004.1001.exr")));
|
|
Assert.False(File.Exists(Path.Combine(dir, "SHOT_cmp_TT_v004.1002.exr")));
|
|
Assert.True(File.Exists(Path.Combine(dir, "unrelated-notes.txt")));
|
|
}
|
|
finally
|
|
{
|
|
Directory.Delete(dir, recursive: true);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// The version folder holds the preview MOV/MP4 built from the same
|
|
/// basename. A shot re-rendered at the same version — the "No preview"
|
|
/// workflow, where the EXRs are re-exported unscaled and the earlier MOV is
|
|
/// inherited as the review media — must not have that MOV cleared out from
|
|
/// under it before aerender starts.
|
|
/// </summary>
|
|
[Fact]
|
|
public void KeepsPreviewMediaSharingThePatternPrefix()
|
|
{
|
|
var dir = Path.Combine(Path.GetTempPath(), "vfxr-test-" + Guid.NewGuid().ToString("N"));
|
|
Directory.CreateDirectory(dir);
|
|
try
|
|
{
|
|
File.WriteAllText(Path.Combine(dir, "SHOT_cmp_TT_v004.1001.exr"), "old");
|
|
File.WriteAllText(Path.Combine(dir, "SHOT_cmp_TT_v004.mov"), "delivery mov");
|
|
File.WriteAllText(Path.Combine(dir, "SHOT_cmp_TT_v004.mp4"), "review mp4");
|
|
|
|
AerenderRunner.PrepareOutputDir(dir, "SHOT_cmp_TT_v004.[####].exr");
|
|
|
|
Assert.False(File.Exists(Path.Combine(dir, "SHOT_cmp_TT_v004.1001.exr")));
|
|
Assert.True(File.Exists(Path.Combine(dir, "SHOT_cmp_TT_v004.mov")));
|
|
Assert.True(File.Exists(Path.Combine(dir, "SHOT_cmp_TT_v004.mp4")));
|
|
}
|
|
finally
|
|
{
|
|
Directory.Delete(dir, recursive: true);
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void ClearsOnlyThisVersionsFrames()
|
|
{
|
|
var dir = Path.Combine(Path.GetTempPath(), "vfxr-test-" + Guid.NewGuid().ToString("N"));
|
|
Directory.CreateDirectory(dir);
|
|
try
|
|
{
|
|
File.WriteAllText(Path.Combine(dir, "SHOT_cmp_TT_v004.1001.exr"), "old");
|
|
File.WriteAllText(Path.Combine(dir, "SHOT_cmp_TT_v005.1001.exr"), "other version");
|
|
File.WriteAllText(Path.Combine(dir, "OTHERSHOT_cmp_TT_v004.1001.exr"), "other shot");
|
|
|
|
AerenderRunner.PrepareOutputDir(dir, "SHOT_cmp_TT_v004.[####].exr");
|
|
|
|
Assert.False(File.Exists(Path.Combine(dir, "SHOT_cmp_TT_v004.1001.exr")));
|
|
Assert.True(File.Exists(Path.Combine(dir, "SHOT_cmp_TT_v005.1001.exr")));
|
|
Assert.True(File.Exists(Path.Combine(dir, "OTHERSHOT_cmp_TT_v004.1001.exr")));
|
|
}
|
|
finally
|
|
{
|
|
Directory.Delete(dir, recursive: true);
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void CreatesMissingDirectory()
|
|
{
|
|
var dir = Path.Combine(Path.GetTempPath(), "vfxr-test-" + Guid.NewGuid().ToString("N"), "v001");
|
|
try
|
|
{
|
|
AerenderRunner.PrepareOutputDir(dir, "X.[####].exr");
|
|
Assert.True(Directory.Exists(dir));
|
|
}
|
|
finally
|
|
{
|
|
Directory.Delete(Path.GetDirectoryName(dir)!, recursive: true);
|
|
}
|
|
}
|
|
}
|