This commit is contained in:
2026-05-13 11:21:48 +09:00
parent 960163dad8
commit 8b5c92194f
66 changed files with 12393 additions and 939 deletions

View File

@@ -337,6 +337,12 @@ if (args.Length > 0 && string.Equals(args[0], "--report-cut-debug-coverage", Str
return;
}
if (args.Length > 0 && string.Equals(args[0], "--audit-cut-files", StringComparison.OrdinalIgnoreCase))
{
Environment.ExitCode = await CutFileAudit.RunAsync(args[1..]).ConfigureAwait(false);
return;
}
var options = ProbeOptions.Parse(args);
Console.WriteLine($"Karisma TCP probe starting. target={options.Host}:{options.Port} timeout={options.Timeout.TotalSeconds:0}s");
@@ -862,6 +868,25 @@ static Task<SaveSceneImageProbeResult> SaveSceneImageAsync(SaveSceneImageOptions
}
}
IReadOnlyList<SceneValidationOperation> operations = string.IsNullOrWhiteSpace(options.OperationsPath)
? Array.Empty<SceneValidationOperation>()
: LoadSceneOperations(options.ScenePath, options.OperationsPath);
foreach (var operation in operations)
{
var operationResult = ApplySceneOperation(handler, scene!, operation, options.Connection.Timeout);
if (!string.Equals(operationResult.Result, eKResult.RESULT_SUCCESS.ToString(), StringComparison.Ordinal))
{
completion.TrySetResult(
new SaveSceneImageProbeResult(
true,
"SUCCESS",
operationResult.Result,
options.OutputPath,
$"Operation {operationResult.Method} failed for '{operationResult.ObjectName}': {operationResult.Detail}"));
return;
}
}
if (options.MaterialOpacity is not null)
{
Console.WriteLine(
@@ -1283,7 +1308,16 @@ static Task<SceneCatalogProbeResult> CatalogScenesAsync(SceneCatalogOptions opti
.EnumerateFiles(options.RootPath, "*.tscn", SearchOption.AllDirectories)
.Where(path => string.IsNullOrWhiteSpace(options.SceneFilter) ||
path.Contains(options.SceneFilter, StringComparison.OrdinalIgnoreCase))
.OrderBy(path => path, StringComparer.OrdinalIgnoreCase)
.Select(path => new
{
Path = path,
RelativePath = Path.GetRelativePath(options.RootPath, path)
})
.OrderBy(item => item.RelativePath.Count(character =>
character == Path.DirectorySeparatorChar ||
character == Path.AltDirectorySeparatorChar))
.ThenBy(item => item.RelativePath, StringComparer.OrdinalIgnoreCase)
.Select(item => item.Path)
.Take(options.MaxScenes ?? int.MaxValue)
.ToArray();
@@ -2309,142 +2343,7 @@ static Task<SceneValidationProbeResult> ValidateSceneOperationsAsync(SceneValida
foreach (var operation in operations)
{
Console.WriteLine($"[VALIDATE] {operation.Method} object={operation.ObjectName}");
var sceneObject = scene.GetObject(operation.ObjectName);
if (sceneObject is null)
{
results.Add(new SceneOperationValidationResult(
operation.ObjectName,
operation.Method,
DescribeOperationPayload(operation),
"OBJECT_NOT_FOUND",
"scene.GetObject returned null."));
continue;
}
if (string.Equals(operation.Method, "SetCounterNumberKey", StringComparison.OrdinalIgnoreCase))
{
if (sceneObject is not IKACounter counter)
{
results.Add(new SceneOperationValidationResult(
operation.ObjectName,
operation.Method,
DescribeOperationPayload(operation),
"NOT_A_COUNTER",
"Object does not implement IKACounter."));
continue;
}
handler.ResetCounterNumberKeyTask();
counter.SetCounterNumberKey(operation.KeyIndex, operation.Number);
if (!WaitForTaskWithMessagePump(handler.CounterNumberKeyTask, options.Connection.Timeout))
{
results.Add(new SceneOperationValidationResult(
operation.ObjectName,
operation.Method,
DescribeOperationPayload(operation),
"TIMEOUT",
"OnSetCounterNumberKey timed out."));
continue;
}
var callbackResult = handler.CounterNumberKeyTask.Result;
results.Add(new SceneOperationValidationResult(
operation.ObjectName,
operation.Method,
DescribeOperationPayload(operation),
callbackResult.ToString(),
string.Empty));
continue;
}
if (string.Equals(operation.Method, "SetVisible", StringComparison.OrdinalIgnoreCase))
{
handler.ResetVisibleTask();
sceneObject.SetVisible(operation.Visible ? 1 : 0);
if (!WaitForTaskWithMessagePump(handler.VisibleTask, options.Connection.Timeout))
{
results.Add(new SceneOperationValidationResult(
operation.ObjectName,
operation.Method,
DescribeOperationPayload(operation),
"TIMEOUT",
"OnSetVisible timed out."));
continue;
}
var callbackResult = handler.VisibleTask.Result;
results.Add(new SceneOperationValidationResult(
operation.ObjectName,
operation.Method,
DescribeOperationPayload(operation),
callbackResult.ToString(),
string.Empty));
continue;
}
if (string.Equals(operation.Method, "SetStyleColor", StringComparison.OrdinalIgnoreCase))
{
if (sceneObject is not IKAStyle style)
{
results.Add(new SceneOperationValidationResult(
operation.ObjectName,
operation.Method,
DescribeOperationPayload(operation),
"NOT_A_STYLE_OBJECT",
"Object does not implement IKAStyle."));
continue;
}
handler.ResetStyleColorTask();
style.SetStyleColor(
ParseStyleType(operation.StyleType),
operation.Order,
operation.R,
operation.G,
operation.B,
operation.A);
if (!WaitForTaskWithMessagePump(handler.StyleColorTask, options.Connection.Timeout))
{
results.Add(new SceneOperationValidationResult(
operation.ObjectName,
operation.Method,
DescribeOperationPayload(operation),
"TIMEOUT",
"OnSetStyleColor timed out."));
continue;
}
var callbackResult = handler.StyleColorTask.Result;
results.Add(new SceneOperationValidationResult(
operation.ObjectName,
operation.Method,
DescribeOperationPayload(operation),
callbackResult.ToString(),
string.Empty));
continue;
}
handler.ResetSetValueTask();
sceneObject.SetValue(operation.Value ?? string.Empty);
if (!WaitForTaskWithMessagePump(handler.SetValueTask, options.Connection.Timeout))
{
results.Add(new SceneOperationValidationResult(
operation.ObjectName,
operation.Method,
DescribeOperationPayload(operation),
"TIMEOUT",
"OnSetValue timed out."));
continue;
}
var setValueResult = handler.SetValueTask.Result;
results.Add(new SceneOperationValidationResult(
operation.ObjectName,
operation.Method,
DescribeOperationPayload(operation),
setValueResult.ToString(),
string.Empty));
results.Add(ApplySceneOperation(handler, scene, operation, options.Connection.Timeout));
}
WriteSceneValidationMarkdown(options, results);
@@ -2532,7 +2431,16 @@ static Task<FolderInspectionProbeResult> InspectTscnFolderAsync(FolderInspection
.EnumerateFiles(options.RootPath, "*.tscn", SearchOption.AllDirectories)
.Where(path => string.IsNullOrWhiteSpace(options.SceneFilter) ||
path.Contains(options.SceneFilter, StringComparison.OrdinalIgnoreCase))
.OrderBy(path => path, StringComparer.OrdinalIgnoreCase)
.Select(path => new
{
Path = path,
RelativePath = Path.GetRelativePath(options.RootPath, path)
})
.OrderBy(item => item.RelativePath.Count(character =>
character == Path.DirectorySeparatorChar ||
character == Path.AltDirectorySeparatorChar))
.ThenBy(item => item.RelativePath, StringComparer.OrdinalIgnoreCase)
.Select(item => item.Path)
.Take(options.MaxScenes ?? int.MaxValue)
.ToArray();
@@ -2865,7 +2773,12 @@ static void WriteFolderInspectionMarkdown(
static List<SceneValidationOperation> LoadValidationOperations(SceneValidationOptions options)
{
var json = File.ReadAllText(options.OperationsPath);
return LoadSceneOperations(options.ScenePath, options.OperationsPath);
}
static List<SceneValidationOperation> LoadSceneOperations(string scenePath, string operationsPath)
{
var json = File.ReadAllText(operationsPath);
var operations = JsonSerializer.Deserialize<List<SceneValidationOperation>>(json, new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
@@ -2875,13 +2788,143 @@ static List<SceneValidationOperation> LoadValidationOperations(SceneValidationOp
{
if (!string.IsNullOrWhiteSpace(operation.Value))
{
operation.Value = operation.Value.Replace("${SCENE_DIR}", Path.GetDirectoryName(options.ScenePath) ?? string.Empty, StringComparison.Ordinal);
operation.Value = operation.Value.Replace("${SCENE_DIR}", Path.GetDirectoryName(scenePath) ?? string.Empty, StringComparison.Ordinal);
}
}
return operations;
}
static SceneOperationValidationResult ApplySceneOperation(
ProbeEventHandler handler,
IKAScene scene,
SceneValidationOperation operation,
TimeSpan timeout)
{
Console.WriteLine($"[VALIDATE] {operation.Method} object={operation.ObjectName}");
var sceneObject = scene.GetObject(operation.ObjectName);
if (sceneObject is null)
{
return new SceneOperationValidationResult(
operation.ObjectName,
operation.Method,
DescribeOperationPayload(operation),
"OBJECT_NOT_FOUND",
"scene.GetObject returned null.");
}
if (string.Equals(operation.Method, "SetCounterNumberKey", StringComparison.OrdinalIgnoreCase))
{
if (sceneObject is not IKACounter counter)
{
return new SceneOperationValidationResult(
operation.ObjectName,
operation.Method,
DescribeOperationPayload(operation),
"NOT_A_COUNTER",
"Object does not implement IKACounter.");
}
handler.ResetCounterNumberKeyTask();
counter.SetCounterNumberKey(operation.KeyIndex, operation.Number);
if (!WaitForTaskWithMessagePump(handler.CounterNumberKeyTask, timeout))
{
return new SceneOperationValidationResult(
operation.ObjectName,
operation.Method,
DescribeOperationPayload(operation),
"TIMEOUT",
"OnSetCounterNumberKey timed out.");
}
return new SceneOperationValidationResult(
operation.ObjectName,
operation.Method,
DescribeOperationPayload(operation),
handler.CounterNumberKeyTask.Result.ToString(),
string.Empty);
}
if (string.Equals(operation.Method, "SetVisible", StringComparison.OrdinalIgnoreCase))
{
handler.ResetVisibleTask();
sceneObject.SetVisible(operation.Visible ? 1 : 0);
if (!WaitForTaskWithMessagePump(handler.VisibleTask, timeout))
{
return new SceneOperationValidationResult(
operation.ObjectName,
operation.Method,
DescribeOperationPayload(operation),
"TIMEOUT",
"OnSetVisible timed out.");
}
return new SceneOperationValidationResult(
operation.ObjectName,
operation.Method,
DescribeOperationPayload(operation),
handler.VisibleTask.Result.ToString(),
string.Empty);
}
if (string.Equals(operation.Method, "SetStyleColor", StringComparison.OrdinalIgnoreCase))
{
if (sceneObject is not IKAStyle style)
{
return new SceneOperationValidationResult(
operation.ObjectName,
operation.Method,
DescribeOperationPayload(operation),
"NOT_A_STYLE_OBJECT",
"Object does not implement IKAStyle.");
}
handler.ResetStyleColorTask();
style.SetStyleColor(
ParseStyleType(operation.StyleType),
operation.Order,
operation.R,
operation.G,
operation.B,
operation.A);
if (!WaitForTaskWithMessagePump(handler.StyleColorTask, timeout))
{
return new SceneOperationValidationResult(
operation.ObjectName,
operation.Method,
DescribeOperationPayload(operation),
"TIMEOUT",
"OnSetStyleColor timed out.");
}
return new SceneOperationValidationResult(
operation.ObjectName,
operation.Method,
DescribeOperationPayload(operation),
handler.StyleColorTask.Result.ToString(),
string.Empty);
}
handler.ResetSetValueTask();
sceneObject.SetValue(operation.Value ?? string.Empty);
if (!WaitForTaskWithMessagePump(handler.SetValueTask, timeout))
{
return new SceneOperationValidationResult(
operation.ObjectName,
operation.Method,
DescribeOperationPayload(operation),
"TIMEOUT",
"OnSetValue timed out.");
}
return new SceneOperationValidationResult(
operation.ObjectName,
operation.Method,
DescribeOperationPayload(operation),
handler.SetValueTask.Result.ToString(),
string.Empty);
}
static string DescribeOperationPayload(SceneValidationOperation operation)
{
if (string.Equals(operation.Method, "SetVisible", StringComparison.OrdinalIgnoreCase))
@@ -3393,6 +3436,7 @@ internal sealed record SaveSceneImageOptions(
bool? VisibleObjectValue,
VariableNameUpdate? VariableName,
CloneObjectUpdate? CloneObject,
string? OperationsPath,
MaterialOpacityUpdate? MaterialOpacity,
SizeUpdate? Size,
PositionUpdate? Position,
@@ -3419,6 +3463,7 @@ internal sealed record SaveSceneImageOptions(
string? variableNameValue = null;
string? cloneSourceObjectName = null;
string? cloneVariableName = null;
string? operationsPath = null;
string? materialOpacityObjectName = null;
float? materialOpacityValue = null;
string? sizeObjectName = null;
@@ -3481,6 +3526,9 @@ internal sealed record SaveSceneImageOptions(
case "--clone-name" when index + 1 < args.Length:
cloneVariableName = args[++index];
break;
case "--operations" when index + 1 < args.Length:
operationsPath = args[++index];
break;
case "--material-opacity-object" when index + 1 < args.Length:
materialOpacityObjectName = args[++index];
break;
@@ -3558,6 +3606,9 @@ internal sealed record SaveSceneImageOptions(
scenePath = Path.GetFullPath(scenePath);
outputPath = Path.GetFullPath(outputPath);
operationsPath = string.IsNullOrWhiteSpace(operationsPath)
? null
: Path.GetFullPath(operationsPath);
sceneAlias ??= Path.GetFileNameWithoutExtension(scenePath);
return new SaveSceneImageOptions(
connection,
@@ -3573,6 +3624,7 @@ internal sealed record SaveSceneImageOptions(
visibleObjectValue,
ParseVariableName(variableNameObjectName, variableNameValue),
ParseCloneObject(cloneSourceObjectName, cloneVariableName),
operationsPath,
ParseMaterialOpacity(materialOpacityObjectName, materialOpacityValue),
ParseSize(sizeObjectName, sizeRaw),
ParsePosition(positionObjectName, positionRaw),
@@ -3857,7 +3909,7 @@ internal sealed record SceneCatalogOptions(
switch (args[index])
{
case "--root" when index + 1 < args.Length:
index++;
rootPath = args[++index];
break;
case "--output" when index + 1 < args.Length:
outputPath = args[++index];
@@ -4195,7 +4247,7 @@ internal sealed record FolderInspectionOptions(ProbeOptions Connection, string R
switch (args[index])
{
case "--root" when index + 1 < args.Length:
index++;
rootPath = args[++index];
break;
case "--output" when index + 1 < args.Length:
outputPath = args[++index];