can generate transcript for two-client test

This commit is contained in:
benbierens 2024-07-26 10:11:29 +02:00
parent 410d62849a
commit ecada92dc5
No known key found for this signature in database
GPG Key ID: 877D2C2E09A22F3A
7 changed files with 189 additions and 33 deletions

View File

@ -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 '<Downloader1>'
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<OverwatchCodexEvent> 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<Action<OverwatchCodexEvent>> addEvent);
}
}

View File

@ -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

View File

@ -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<Action<OverwatchCodexEvent>> 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<string> 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);
}
}
}
}

View File

@ -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

View File

@ -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);
}
}
}
}

View File

@ -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;

View File

@ -20,9 +20,9 @@ namespace DistTestCore.Logs
this.deployId = deployId;
}
public void ConcludeTest(string resultStatus, TimeSpan testDuration, Dictionary<string, string> data)
public void ConcludeTest(DistTestResult resultStatus, TimeSpan testDuration, Dictionary<string, string> 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<string, string> data)