From ecada92dc53884b1abf1f3ceb8da80d8aef70fa8 Mon Sep 17 00:00:00 2001 From: benbierens Date: Fri, 26 Jul 2024 10:11:29 +0200 Subject: [PATCH] can generate transcript for two-client test --- .../OverwatchSupport/CodexLogConverter.cs | 67 +++++++++++++++++-- .../OverwatchSupport/CodexTranscriptWriter.cs | 15 +++++ .../BlockReceivedLineConverter.cs | 44 ++++++++++++ .../OverwatchSupport/ModelExtensions.cs | 4 +- Tests/CodexTests/CodexDistTest.cs | 63 +++++++++++------ Tests/DistTestCore/DistTest.cs | 25 +++++-- Tests/DistTestCore/Logs/StatusLog.cs | 4 +- 7 files changed, 189 insertions(+), 33 deletions(-) create mode 100644 ProjectPlugins/CodexPlugin/OverwatchSupport/LineConverters/BlockReceivedLineConverter.cs diff --git a/ProjectPlugins/CodexPlugin/OverwatchSupport/CodexLogConverter.cs b/ProjectPlugins/CodexPlugin/OverwatchSupport/CodexLogConverter.cs index 2368748..799811b 100644 --- a/ProjectPlugins/CodexPlugin/OverwatchSupport/CodexLogConverter.cs +++ b/ProjectPlugins/CodexPlugin/OverwatchSupport/CodexLogConverter.cs @@ -1,4 +1,5 @@ -using Core; +using CodexPlugin.OverwatchSupport.LineConverters; +using Core; using OverwatchTranscript; using Utils; @@ -18,8 +19,8 @@ namespace CodexPlugin.OverwatchSupport public void ProcessLog(IDownloadedLog log) { var peerId = DeterminPeerId(log); - - + var runner = new ConversionRunner(writer, peerId); + runner.Run(log); } private string DeterminPeerId(IDownloadedLog log) @@ -30,8 +31,8 @@ namespace CodexPlugin.OverwatchSupport // Expected string: // Downloading container log for '' - var nameLine = log.FindLinesThatContain("Downloading container log for").Single(); - var name = Str.Between(nameLine, "'<", ">'"); + var nameLine = log.FindLinesThatContain("Downloading container log for").First(); + var name = Str.Between(nameLine, "'", "'"); var peerId = nameIdMap.GetPeerId(name); var shortPeerId = CodexUtils.ToShortId(peerId); @@ -46,4 +47,60 @@ namespace CodexPlugin.OverwatchSupport return peerId; } } + + public class ConversionRunner + { + private readonly ITranscriptWriter writer; + private readonly string peerId; + private readonly ILineConverter[] converters = new ILineConverter[] + { + new BlockReceivedLineConverter() + }; + + public ConversionRunner(ITranscriptWriter writer, string peerId) + { + this.writer = writer; + this.peerId = peerId; + } + + public void Run(IDownloadedLog log) + { + log.IterateLines(line => + { + foreach (var converter in converters) + { + ProcessLine(line, converter); + } + }); + } + + public void AddEvent(DateTime utc, Action action) + { + var e = new OverwatchCodexEvent + { + PeerId = peerId, + }; + action(e); + writer.Add(utc, e); + } + + private void ProcessLine(string line, ILineConverter converter) + { + if (!line.Contains(converter.Interest)) return; + + var codexLine = CodexLogLine.Parse(line); + if (codexLine == null) throw new Exception("Unable to parse required line"); + + converter.Process(codexLine, (action) => + { + AddEvent(codexLine.TimestampUtc, action); + }); + } + } + + public interface ILineConverter + { + string Interest { get; } + void Process(CodexLogLine line, Action> addEvent); + } } diff --git a/ProjectPlugins/CodexPlugin/OverwatchSupport/CodexTranscriptWriter.cs b/ProjectPlugins/CodexPlugin/OverwatchSupport/CodexTranscriptWriter.cs index d031baa..6388680 100644 --- a/ProjectPlugins/CodexPlugin/OverwatchSupport/CodexTranscriptWriter.cs +++ b/ProjectPlugins/CodexPlugin/OverwatchSupport/CodexTranscriptWriter.cs @@ -1,6 +1,7 @@ using CodexPlugin.Hooks; using Core; using OverwatchTranscript; +using Utils; namespace CodexPlugin.OverwatchSupport { @@ -23,6 +24,7 @@ namespace CodexPlugin.OverwatchSupport public ICodexNodeHooks CreateHooks(string nodeName) { + nodeName = Str.Between(nodeName, "'", "'"); return new CodexNodeTranscriptWriter(writer, nameIdMap, nodeName); } @@ -39,6 +41,19 @@ namespace CodexPlugin.OverwatchSupport converter.ProcessLog(log); } } + + public void AddResult(bool success, string result) + { + writer.Add(DateTime.UtcNow, new OverwatchCodexEvent + { + PeerId = string.Empty, + ScenarioFinished = new ScenarioFinishedEvent + { + Success = success, + Result = result + } + }); + } } public class CodexNodeTranscriptWriter : ICodexNodeHooks diff --git a/ProjectPlugins/CodexPlugin/OverwatchSupport/LineConverters/BlockReceivedLineConverter.cs b/ProjectPlugins/CodexPlugin/OverwatchSupport/LineConverters/BlockReceivedLineConverter.cs new file mode 100644 index 0000000..c9259df --- /dev/null +++ b/ProjectPlugins/CodexPlugin/OverwatchSupport/LineConverters/BlockReceivedLineConverter.cs @@ -0,0 +1,44 @@ +namespace CodexPlugin.OverwatchSupport.LineConverters +{ + public class BlockReceivedLineConverter : ILineConverter + { + public string Interest => "Received blocks from peer"; + + public void Process(CodexLogLine line, Action> addEvent) + { + var peer = line.Attributes["peer"]; + var blockAddresses = line.Attributes["blocks"]; + + SplitBlockAddresses(blockAddresses, address => + { + addEvent(e => + { + e.BlockReceived = new BlockReceivedEvent + { + SenderPeerId = peer, + BlockAddress = address + }; + }); + }); + } + + private void SplitBlockAddresses(string blockAddresses, Action onBlockAddress) + { + // Single line can contain multiple block addresses. + var tokens = blockAddresses.Split(",", StringSplitOptions.RemoveEmptyEntries).ToList(); + while (tokens.Count > 0) + { + if (tokens.Count == 1) + { + onBlockAddress(tokens[0]); + return; + } + + var blockAddress = $"{tokens[0]}, {tokens[1]}"; + tokens.RemoveRange(0, 2); + + onBlockAddress(blockAddress); + } + } + } +} diff --git a/ProjectPlugins/CodexPlugin/OverwatchSupport/ModelExtensions.cs b/ProjectPlugins/CodexPlugin/OverwatchSupport/ModelExtensions.cs index dfed289..9a18098 100644 --- a/ProjectPlugins/CodexPlugin/OverwatchSupport/ModelExtensions.cs +++ b/ProjectPlugins/CodexPlugin/OverwatchSupport/ModelExtensions.cs @@ -1,4 +1,4 @@ -namespace OverwatchTranscript +namespace CodexPlugin.OverwatchSupport { [Serializable] public class OverwatchCodexHeader @@ -62,7 +62,7 @@ public class BlockReceivedEvent { public string BlockAddress { get; set; } = string.Empty; - public string PeerId { get; set; } = string.Empty; + public string SenderPeerId { get; set; } = string.Empty; } #endregion diff --git a/Tests/CodexTests/CodexDistTest.cs b/Tests/CodexTests/CodexDistTest.cs index fdb2f63..2bb4fba 100644 --- a/Tests/CodexTests/CodexDistTest.cs +++ b/Tests/CodexTests/CodexDistTest.cs @@ -10,6 +10,7 @@ using DistTestCore.Logs; using Logging; using MetricsPlugin; using Newtonsoft.Json; +using NUnit.Framework; using NUnit.Framework.Constraints; using OverwatchTranscript; @@ -38,29 +39,13 @@ namespace CodexTests protected override void LifecycleStart(TestLifecycle lifecycle) { base.LifecycleStart(lifecycle); - if (!enableOverwatchTranscript) return; - - var writer = new CodexTranscriptWriter(Transcript.NewWriter()); - Ci.SetCodexHooksProvider(writer); - writers.Add(lifecycle, writer); + SetupTranscript(lifecycle); } - protected override void LifecycleStop(TestLifecycle lifecycle) + protected override void LifecycleStop(TestLifecycle lifecycle, DistTestResult result) { - base.LifecycleStop(lifecycle); - if (!enableOverwatchTranscript) return; - - var writer = writers[lifecycle]; - writers.Remove(lifecycle); - - writer.ProcessLogs(lifecycle.DownloadAllLogs()); - - var file = lifecycle.Log.CreateSubfile("owts"); - Stopwatch.Measure(lifecycle.Log, $"Transcript.Finalize: {file.FullFilename}", () => - { - writer.IncludeFile(lifecycle.Log.LogFile.FullFilename); - writer.Finalize(file.FullFilename); - }); + base.LifecycleStop(lifecycle, result); + TeardownTranscript(lifecycle, result); } public ICodexNode StartCodex() @@ -142,5 +127,43 @@ namespace CodexTests protected virtual void OnCodexSetup(ICodexSetup setup) { } + + private void SetupTranscript(TestLifecycle lifecycle) + { + if (!enableOverwatchTranscript) return; + + var writer = new CodexTranscriptWriter(Transcript.NewWriter()); + Ci.SetCodexHooksProvider(writer); + writers.Add(lifecycle, writer); + } + + private void TeardownTranscript(TestLifecycle lifecycle, DistTestResult result) + { + if (!enableOverwatchTranscript) return; + + var writer = writers[lifecycle]; + writers.Remove(lifecycle); + + writer.AddResult(result.Success, result.Result); + + try + { + Stopwatch.Measure(lifecycle.Log, "Transcript.ProcessLogs", () => + { + writer.ProcessLogs(lifecycle.DownloadAllLogs()); + }); + + var file = lifecycle.Log.CreateSubfile("owts"); + Stopwatch.Measure(lifecycle.Log, $"Transcript.Finalize: {file.FullFilename}", () => + { + writer.IncludeFile(lifecycle.Log.LogFile.FullFilename); + writer.Finalize(file.FullFilename); + }); + } + catch (Exception ex) + { + lifecycle.Log.Error("Failure during transcript teardown: " + ex); + } + } } } diff --git a/Tests/DistTestCore/DistTest.cs b/Tests/DistTestCore/DistTest.cs index cc44f53..09e1a85 100644 --- a/Tests/DistTestCore/DistTest.cs +++ b/Tests/DistTestCore/DistTest.cs @@ -166,7 +166,7 @@ namespace DistTestCore { } - protected virtual void LifecycleStop(TestLifecycle lifecycle) + protected virtual void LifecycleStop(TestLifecycle lifecycle, DistTestResult testResult) { } @@ -218,7 +218,7 @@ namespace DistTestCore WriteEndTestLog(lifecycle.Log); IncludeLogsOnTestFailure(lifecycle); - LifecycleStop(lifecycle); + LifecycleStop(lifecycle, testResult); lifecycle.DeleteAllResources(); lifecycles.Remove(GetCurrentTestName()); }); @@ -309,9 +309,12 @@ namespace DistTestCore return $"[{TestContext.CurrentContext.Test.Name}]"; } - private string GetTestResult() + private DistTestResult GetTestResult() { - return TestContext.CurrentContext.Result.Outcome.Status.ToString(); + var success = TestContext.CurrentContext.Result.Outcome.Status == TestStatus.Passed; + var status = TestContext.CurrentContext.Result.Outcome.Status.ToString(); + var result = TestContext.CurrentContext.Result.Message; + return new DistTestResult(success, status, result ?? string.Empty); } private bool IsDownloadingLogsEnabled() @@ -320,6 +323,20 @@ namespace DistTestCore } } + public class DistTestResult + { + public DistTestResult(bool success, string status, string result) + { + Success = success; + Status = status; + Result = result; + } + + public bool Success { get; } + public string Status { get; } + public string Result { get; } + } + public static class GlobalTestFailure { public static bool HasFailed { get; set; } = false; diff --git a/Tests/DistTestCore/Logs/StatusLog.cs b/Tests/DistTestCore/Logs/StatusLog.cs index 0706eb8..2bf15ad 100644 --- a/Tests/DistTestCore/Logs/StatusLog.cs +++ b/Tests/DistTestCore/Logs/StatusLog.cs @@ -20,9 +20,9 @@ namespace DistTestCore.Logs this.deployId = deployId; } - public void ConcludeTest(string resultStatus, TimeSpan testDuration, Dictionary data) + public void ConcludeTest(DistTestResult resultStatus, TimeSpan testDuration, Dictionary data) { - ConcludeTest(resultStatus, testDuration.TotalSeconds.ToString(CultureInfo.InvariantCulture), data); + ConcludeTest(resultStatus.Status, testDuration.TotalSeconds.ToString(CultureInfo.InvariantCulture), data); } public void ConcludeTest(string resultStatus, string testDuration, Dictionary data)