using System.Text.Json; using System.Text.Json.Nodes; using System.Text.RegularExpressions; namespace decePubClient.E2E.Support { // Each render and action test is also a cell of the town's matrix, appended to the pasture's out/client.jsonl in the // cell format town.sh check writes, so report.html shows decePub next to the servers. A failure that an open gap of the // town's gaps.json names (a peer's own bug, say) is skipped with the gap's id rather than failed, as the report counts // it an expected failure. public static class Results { static readonly object Gate = new(); static readonly string FilePath = Path.Combine(Pasture.Root, "out", "client.jsonl"); static readonly Lazy Gaps = new(() => JsonNode.Parse(File.ReadAllText(Path.Combine(Pasture.Root, "town", "gaps.json")))!.AsArray()); // the open gap whose match (regular expressions over the cell's fields) fits this cell, if any static string KnownGap(string feature, string origin) { var fields = new Dictionary { ["feature"] = feature, ["origin"] = origin, ["observer"] = "decepub" }; return Gaps.Value.Where(g => g?["status"]?.GetValue() != "closed" && g?["match"] is JsonObject) .FirstOrDefault(g => g!["match"]!.AsObject().All(m => Regex.IsMatch(fields.GetValueOrDefault(m.Key, ""), $"^(?:{m.Value!.GetValue()})$"))) ?["id"]?.GetValue(); } public static async Task Cell(string feature, string origin, string reference, Func test) => await Cell(feature, origin, reference, test, page: null); // with a page, a failure also leaves its screenshot and text in the test output's .state/ (SignIn.Evidence) public static async Task Cell(string feature, string origin, string reference, Func test, Microsoft.Playwright.IPage page) { var ok = false; string got = null; try { await test(); ok = true; } catch (Exception e) { got = e.Message.Length > 400 ? e.Message[..400] : e.Message; if (page is not null) await SignIn.Evidence(page, $"{feature}-{origin}-{reference}".Replace('/', '_').Replace('@', '_')); if (KnownGap(feature, origin) is { } gap) Assert.Skip($"known gap {gap}: {got}"); throw; } finally { var line = JsonSerializer.Serialize(new { source = "client", cell = $"{feature}|{origin}|decepub|{(origin == "privapub" ? "local" : "in")}", feature, origin, observer = "decepub", direction = origin == "privapub" ? "local" : "in", ok, @ref = reference, got, t = DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ") }); lock (Gate) { Directory.CreateDirectory(Path.GetDirectoryName(FilePath)!); File.AppendAllText(FilePath, line + "\n"); } } } } }