89 lines
3.6 KiB
C#
89 lines
3.6 KiB
C#
using System.Text.Json;
|
|
using ProjectQuint.Probes;
|
|
|
|
var json = new JsonSerializerOptions { WriteIndented = true, PropertyNamingPolicy = JsonNamingPolicy.CamelCase };
|
|
try
|
|
{
|
|
var action = args.Length == 0 ? "list" : args[0];
|
|
var options = new Dictionary<string, int>(StringComparer.Ordinal);
|
|
string? output = null;
|
|
for (var index = 1; index < args.Length; index += 2)
|
|
{
|
|
if (index + 1 >= args.Length)
|
|
throw new ArgumentException("Every option requires a value.");
|
|
if (args[index] == "--output" && output == null)
|
|
output = args[index + 1];
|
|
else if (args[index] is "--notes" or "--seed" or "--fps" or "--iterations" &&
|
|
int.TryParse(args[index + 1], out var value) && options.TryAdd(args[index], value))
|
|
continue;
|
|
else
|
|
throw new ArgumentException("Unknown, duplicated or invalid option.");
|
|
}
|
|
var notes = options.GetValueOrDefault("--notes", 10_000);
|
|
var seed = options.GetValueOrDefault("--seed", 42);
|
|
var fps = options.GetValueOrDefault("--fps", 240);
|
|
var iterations = options.GetValueOrDefault("--iterations", 20);
|
|
if (seed < 0)
|
|
throw new ArgumentException("Seed must be nonnegative.");
|
|
object report;
|
|
switch (action)
|
|
{
|
|
case "list":
|
|
if (options.Count != 0 || output != null)
|
|
throw new ArgumentException("The list command accepts no options.");
|
|
report = new
|
|
{
|
|
schemaVersion = 1,
|
|
component = "headless-core",
|
|
commands = new[] { "list", "replay", "benchmark", "test (wrapper)" },
|
|
input = "seeded synthetic four-lane notes; no music or external library",
|
|
options = new[] { "--notes", "--seed", "--fps", "--iterations (benchmark)", "--output" },
|
|
};
|
|
break;
|
|
case "replay":
|
|
if (options.ContainsKey("--iterations"))
|
|
throw new ArgumentException("Iterations applies only to benchmarks.");
|
|
var workload = SyntheticWorkload.Create(notes, (uint)seed);
|
|
report = new
|
|
{
|
|
schemaVersion = 1, complete = true, notes, seed, frameRate = fps,
|
|
eventCount = workload.Events.Length,
|
|
result = ReplayRunner.Run(workload, fps),
|
|
};
|
|
break;
|
|
case "benchmark":
|
|
report = CoreBenchmark.Run(notes, (uint)seed, fps, iterations, checkpoint =>
|
|
{
|
|
Console.Error.WriteLine($"benchmark {checkpoint.Samples.Length}/{iterations}");
|
|
if (output != null)
|
|
Save(output, checkpoint);
|
|
});
|
|
break;
|
|
default:
|
|
throw new ArgumentException("Unknown command. Use list, replay, benchmark or test.");
|
|
}
|
|
if (output != null)
|
|
Save(output, report);
|
|
Console.WriteLine(JsonSerializer.Serialize(report, json));
|
|
return 0;
|
|
}
|
|
catch (Exception error)
|
|
{
|
|
// Paths in I/O exceptions can identify the local machine; keep structured failures generic.
|
|
Console.Error.WriteLine(JsonSerializer.Serialize(new
|
|
{
|
|
error = error.GetType().Name,
|
|
detail = error is ArgumentException ? error.Message : "The probe failed; no complete result was produced.",
|
|
}));
|
|
return 1;
|
|
}
|
|
|
|
void Save(string path, object report)
|
|
{
|
|
var destination = Path.GetFullPath(path);
|
|
Directory.CreateDirectory(Path.GetDirectoryName(destination)!);
|
|
var temporary = destination + ".tmp";
|
|
File.WriteAllText(temporary, JsonSerializer.Serialize(report, json) + "\n");
|
|
File.Move(temporary, destination, overwrite: true);
|
|
}
|