Merge branch 'master' into feature/extended-marketplace-testing

# Conflicts:
#	Tests/CodexReleaseTests/MarketTests/MarketplaceAutoBootstrapDistTest.cs
#	Tests/CodexReleaseTests/Parallelism.cs
#	Tests/DistTestCore/DistTest.cs
This commit is contained in:
ThatBen 2025-04-29 12:09:05 +02:00
commit 7d75cb784a
No known key found for this signature in database
GPG Key ID: E020A7DDCD52E1AB
12 changed files with 261 additions and 326 deletions

View File

@ -4,8 +4,11 @@
{
public static string Between(string input, string open, string close)
{
var openIndex = input.IndexOf(open) + open.Length;
var openI = input.IndexOf(open);
if (openI == -1) return input;
var openIndex = openI + open.Length;
var closeIndex = input.LastIndexOf(close);
if (closeIndex == -1) return input;
return input.Substring(openIndex, closeIndex - openIndex);
}

View File

@ -23,16 +23,27 @@ namespace CodexPlugin.OverwatchSupport
converter = new CodexLogConverter(writer, config, identityMap);
}
public void Finalize(string outputFilepath)
public void FinalizeWriter()
{
log.Log("Finalizing Codex transcript...");
writer.AddHeader(CodexHeaderKey, CreateCodexHeader());
writer.Write(outputFilepath);
writer.Write(GetOutputFullPath());
log.Log("Done");
}
private string GetOutputFullPath()
{
var outputPath = Path.GetDirectoryName(log.GetFullName());
if (outputPath == null) throw new Exception("Logfile path is null");
var filename = Path.GetFileNameWithoutExtension(log.GetFullName());
if (string.IsNullOrEmpty(filename)) throw new Exception("Logfile name is null or empty");
var outputFile = Path.Combine(outputPath, filename + "_" + config.OutputPath);
if (!outputFile.EndsWith(".owts")) outputFile += ".owts";
return outputFile;
}
public ICodexNodeHooks CreateHooks(string nodeName)
{
nodeName = Str.Between(nodeName, "'", "'");

View File

@ -2,11 +2,13 @@
{
public class CodexTranscriptWriterConfig
{
public CodexTranscriptWriterConfig(bool includeBlockReceivedEvents)
public CodexTranscriptWriterConfig(string outputPath, bool includeBlockReceivedEvents)
{
OutputPath = outputPath;
IncludeBlockReceivedEvents = includeBlockReceivedEvents;
}
public string OutputPath { get; }
public bool IncludeBlockReceivedEvents { get; }
}
}

View File

@ -3,7 +3,6 @@ using CodexContractsPlugin;
using CodexContractsPlugin.Marketplace;
using CodexPlugin;
using CodexTests;
using DistTestCore;
using GethPlugin;
using Logging;
using Nethereum.Hex.HexConvertors.Extensions;
@ -14,36 +13,33 @@ namespace CodexReleaseTests.MarketTests
{
public abstract class MarketplaceAutoBootstrapDistTest : AutoBootstrapDistTest
{
private readonly Dictionary<TestLifecycle, MarketplaceHandle> handles = new Dictionary<TestLifecycle, MarketplaceHandle>();
private MarketplaceHandle handle = null!;
protected const int StartingBalanceTST = 1000;
protected const int StartingBalanceEth = 10;
protected override void LifecycleStart(TestLifecycle lifecycle)
[SetUp]
public void SetupMarketplace()
{
base.LifecycleStart(lifecycle);
var geth = StartGethNode(s => s.IsMiner());
var contracts = Ci.StartCodexContracts(geth, BootstrapNode.Version);
var monitor = SetupChainMonitor(lifecycle.Log, contracts, lifecycle.TestStartUtc);
handles.Add(lifecycle, new MarketplaceHandle(geth, contracts, monitor));
var monitor = SetupChainMonitor(GetTestLog(), contracts, GetTestRunTimeRange().From);
handle = new MarketplaceHandle(geth, contracts, monitor));
}
protected override void LifecycleStop(TestLifecycle lifecycle, DistTestResult result)
[TearDown]
public void TearDownMarketplace()
{
var handle = handles[lifecycle];
if (handle.ChainMonitor != null) handle.ChainMonitor.Stop();
handles.Remove(lifecycle);
base.LifecycleStop(lifecycle, result);
}
protected IGethNode GetGeth()
{
return handles[Get()].Geth;
return handle.Geth;
}
protected ICodexContracts GetContracts()
{
return handles[Get()].Contracts;
return handle.Contracts;
}
protected TimeSpan GetPeriodDuration()

View File

@ -12,70 +12,45 @@ using Assert = NUnit.Framework.Assert;
namespace DistTestCore
{
[Parallelizable(ParallelScope.All)]
[FixtureLifeCycle(LifeCycle.InstancePerTestCase)]
public abstract class DistTest
{
private const string TestNamespacePrefix = "cdx-";
private readonly Configuration configuration = new Configuration();
private readonly Assembly[] testAssemblies;
private static readonly Global global = new Global();
private readonly FixtureLog fixtureLog;
private readonly StatusLog statusLog;
private readonly object lifecycleLock = new object();
private readonly EntryPoint globalEntryPoint;
private readonly Dictionary<string, TestLifecycle> lifecycles = new Dictionary<string, TestLifecycle>();
private readonly string deployId;
private readonly TestLifecycle lifecycle;
private readonly string deployId = NameUtils.MakeDeployId();
public DistTest()
{
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
testAssemblies = assemblies.Where(a => a.FullName!.ToLowerInvariant().Contains("test")).ToArray();
deployId = NameUtils.MakeDeployId();
var logConfig = configuration.GetLogConfig();
var logConfig = global.Configuration.GetLogConfig();
var startTime = DateTime.UtcNow;
fixtureLog = FixtureLog.Create(logConfig, startTime, deployId);
statusLog = new StatusLog(logConfig, startTime, "dist-tests", deployId);
globalEntryPoint = new EntryPoint(fixtureLog, configuration.GetK8sConfiguration(new DefaultK8sTimeSet(), TestNamespacePrefix), configuration.GetFileManagerFolder());
fixtureLog.Log("Test framework revision: " + GitInfo.GetStatus());
lifecycle = new TestLifecycle(fixtureLog.CreateTestLog(startTime), global.Configuration,
GetWebCallTimeSet(),
GetK8sTimeSet(),
Global.TestNamespacePrefix + Guid.NewGuid().ToString(),
deployId,
ShouldWaitForCleanup()
);
Initialize(fixtureLog);
}
[OneTimeSetUp]
public void GlobalSetup()
public static void GlobalSetup()
{
fixtureLog.Log($"Starting...");
globalEntryPoint.Announce();
// Previous test run may have been interrupted.
// Begin by cleaning everything up.
try
{
Stopwatch.Measure(fixtureLog, "Global setup", () =>
{
globalEntryPoint.Tools.CreateWorkflow().DeleteNamespacesStartingWith(TestNamespacePrefix, wait: true);
});
}
catch (Exception ex)
{
GlobalTestFailure.HasFailed = true;
fixtureLog.Error($"Global setup cleanup failed with: {ex}");
throw;
}
fixtureLog.Log("Test framework revision: " + GitInfo.GetStatus());
fixtureLog.Log("Global setup cleanup successful");
global.Setup();
}
[OneTimeTearDown]
public void GlobalTearDown()
public static void GlobalTearDown()
{
globalEntryPoint.Decommission(
// There shouldn't be any of either, but clean everything up regardless.
deleteKubernetesResources: true,
deleteTrackedFiles: true,
waitTillDone: true
);
global.TearDown();
}
[SetUp]
@ -85,18 +60,6 @@ namespace DistTestCore
{
Assert.Inconclusive("Skip test: Previous test failed during clean up.");
}
else
{
try
{
CreateNewTestLifecycle();
}
catch (Exception ex)
{
fixtureLog.Error("Setup failed: " + ex);
GlobalTestFailure.HasFailed = true;
}
}
}
[TearDown]
@ -117,18 +80,18 @@ namespace DistTestCore
{
get
{
return Get().CoreInterface;
return lifecycle.CoreInterface;
}
}
public TrackedFile GenerateTestFile(ByteSize size, string label = "")
{
return Get().GenerateTestFile(size, label);
return lifecycle.GenerateTestFile(size, label);
}
public TrackedFile GenerateTestFile(Action<IGenerateOption> options, string label = "")
{
return Get().GenerateTestFile(options, label);
return lifecycle.GenerateTestFile(options, label);
}
/// <summary>
@ -137,12 +100,22 @@ namespace DistTestCore
/// </summary>
public void ScopedTestFiles(Action action)
{
Get().GetFileManager().ScopedFiles(action);
lifecycle.GetFileManager().ScopedFiles(action);
}
public ILog GetTestLog()
{
return Get().Log;
return lifecycle.Log;
}
public IFileManager GetFileManager()
{
return lifecycle.GetFileManager();
}
public string GetTestNamespace()
{
return lifecycle.TestNamespace;
}
public void Log(string msg)
@ -159,64 +132,24 @@ namespace DistTestCore
public void Measure(string name, Action action)
{
Stopwatch.Measure(Get().Log, name, action);
Stopwatch.Measure(lifecycle.Log, name, action);
}
protected TimeRange GetTestRunTimeRange()
{
return new TimeRange(Get().TestStartUtc, DateTime.UtcNow);
return new TimeRange(lifecycle.TestStartUtc, DateTime.UtcNow);
}
protected virtual void Initialize(FixtureLog fixtureLog)
{
}
protected virtual void LifecycleStart(TestLifecycle lifecycle)
{
}
protected virtual void LifecycleStop(TestLifecycle lifecycle, DistTestResult testResult)
{
}
protected virtual void CollectStatusLogData(TestLifecycle lifecycle, Dictionary<string, string> data)
{
}
protected TestLifecycle Get()
{
lock (lifecycleLock)
{
return lifecycles[GetCurrentTestName()];
}
}
private void CreateNewTestLifecycle()
{
var testName = GetCurrentTestName();
fixtureLog.WriteLogTag();
Stopwatch.Measure(fixtureLog, $"Setup for {testName}", () =>
{
lock (lifecycleLock)
{
var testNamespace = TestNamespacePrefix + Guid.NewGuid().ToString();
var lifecycle = new TestLifecycle(
fixtureLog.CreateTestLog(),
configuration,
GetWebCallTimeSet(),
GetK8sTimeSet(),
testNamespace,
deployId,
ShouldWaitForCleanup());
lifecycles.Add(testName, lifecycle);
LifecycleStart(lifecycle);
}
});
}
private void DisposeTestLifecycle()
{
var lifecycle = Get();
var testResult = GetTestResult();
var testDuration = lifecycle.GetTestDuration();
var data = lifecycle.GetPluginMetadata();
@ -228,9 +161,7 @@ namespace DistTestCore
WriteEndTestLog(lifecycle.Log);
IncludeLogsOnTestFailure(lifecycle);
LifecycleStop(lifecycle, testResult);
lifecycle.DeleteAllResources();
lifecycles.Remove(GetCurrentTestName());
});
}
@ -287,7 +218,7 @@ namespace DistTestCore
var className = currentTest.ClassName;
var methodName = currentTest.MethodName;
var testClasses = testAssemblies.SelectMany(a => a.GetTypes()).Where(c => c.FullName == className).ToArray();
var testClasses = global.TestAssemblies.SelectMany(a => a.GetTypes()).Where(c => c.FullName == className).ToArray();
var testMethods = testClasses.SelectMany(c => c.GetMethods()).Where(m => m.Name == methodName).ToArray();
return testMethods.Select(m => m.GetCustomAttribute<T>())
@ -296,19 +227,24 @@ namespace DistTestCore
.ToArray();
}
protected IDownloadedLog[] DownloadAllLogs()
{
return lifecycle.DownloadAllLogs();
}
private void IncludeLogsOnTestFailure(TestLifecycle lifecycle)
{
var testStatus = TestContext.CurrentContext.Result.Outcome.Status;
if (ShouldDownloadAllLogs(testStatus))
{
lifecycle.Log.Log("Downloading all container logs...");
lifecycle.DownloadAllLogs();
DownloadAllLogs();
}
}
private bool ShouldDownloadAllLogs(TestStatus testStatus)
{
if (configuration.AlwaysDownloadContainerLogs) return true;
if (global.Configuration.AlwaysDownloadContainerLogs) return true;
if (!IsDownloadingLogsEnabled()) return false;
if (testStatus == TestStatus.Failed)
{
@ -323,7 +259,7 @@ namespace DistTestCore
return $"[{TestContext.CurrentContext.Test.Name}]";
}
private DistTestResult GetTestResult()
public DistTestResult GetTestResult()
{
var success = TestContext.CurrentContext.Result.Outcome.Status == TestStatus.Passed;
var status = TestContext.CurrentContext.Result.Outcome.Status.ToString();

View File

@ -0,0 +1,60 @@
using System.Reflection;
using Core;
using Logging;
namespace DistTestCore
{
public class Global
{
public const string TestNamespacePrefix = "cdx-";
public Configuration Configuration { get; } = new Configuration();
public Assembly[] TestAssemblies { get; }
private readonly EntryPoint globalEntryPoint;
private readonly ILog log;
public Global()
{
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
TestAssemblies = assemblies.Where(a => a.FullName!.ToLowerInvariant().Contains("test")).ToArray();
log = new ConsoleLog();
globalEntryPoint = new EntryPoint(
log,
Configuration.GetK8sConfiguration(
new DefaultK8sTimeSet(),
TestNamespacePrefix
),
Configuration.GetFileManagerFolder()
);
}
public void Setup()
{
try
{
Stopwatch.Measure(log, "Global setup", () =>
{
globalEntryPoint.Announce();
globalEntryPoint.Tools.CreateWorkflow().DeleteNamespacesStartingWith(TestNamespacePrefix, wait: true);
});
}
catch (Exception ex)
{
GlobalTestFailure.HasFailed = true;
log.Error($"Global setup cleanup failed with: {ex}");
throw;
}
}
public void TearDown()
{
globalEntryPoint.Decommission(
// There shouldn't be any of either, but clean everything up regardless.
deleteKubernetesResources: true,
deleteTrackedFiles: true,
waitTillDone: true
);
}
}
}

View File

@ -4,19 +4,14 @@ namespace DistTestCore.Logs
{
public class FixtureLog : BaseTestLog
{
private readonly ILog backingLog;
private readonly string deployId;
public FixtureLog(ILog backingLog, string deployId)
: base(backingLog, deployId)
{
this.backingLog = backingLog;
this.deployId = deployId;
}
public TestLog CreateTestLog(string name = "")
public TestLog CreateTestLog(DateTime start, string name = "")
{
return TestLog.Create(this, name);
return TestLog.Create(this, start, name);
}
public static FixtureLog Create(LogConfig config, DateTime start, string deployId, string name = "")

View File

@ -1,5 +1,4 @@
using Logging;
using System.Xml.Linq;
namespace DistTestCore.Logs
{
@ -11,9 +10,9 @@ namespace DistTestCore.Logs
backingLog.Log($"*** Begin: {methodName}");
}
public static TestLog Create(FixtureLog parentLog, string name = "")
public static TestLog Create(FixtureLog parentLog, DateTime start, string name = "")
{
var methodName = NameUtils.GetTestMethodName(name);
var methodName = NameUtils.GetTestLogFileName(start, name);
var fullName = Path.Combine(parentLog.GetFullName(), methodName);
var backingLog = CreateMainLog(fullName, name);
return new TestLog(backingLog, methodName, parentLog.DeployId);

View File

@ -5,6 +5,11 @@ namespace DistTestCore
{
public static class NameUtils
{
public static string GetTestLogFileName(DateTime start, string name = "")
{
return $"{Pad(start.Hour)}-{Pad(start.Minute)}-{Pad(start.Second)}Z_{GetTestMethodName(name)}";
}
public static string GetTestMethodName(string name = "")
{
if (!string.IsNullOrEmpty(name)) return name;
@ -16,7 +21,7 @@ namespace DistTestCore
public static string GetFixtureFullName(LogConfig config, DateTime start, string name)
{
var folder = DetermineFolder(config, start);
var fixtureName = GetFixtureName(name, start);
var fixtureName = GetRawFixtureName();
return Path.Combine(folder, fixtureName);
}
@ -85,13 +90,6 @@ namespace DistTestCore
Pad(start.Day));
}
private static string GetFixtureName(string name, DateTime start)
{
var fixtureName = GetRawFixtureName();
if (!string.IsNullOrEmpty(name)) fixtureName = name;
return $"{Pad(start.Hour)}-{Pad(start.Minute)}-{Pad(start.Second)}Z_{fixtureName.Replace('.', '-')}";
}
private static string Pad(int n)
{
return n.ToString().PadLeft(2, '0');

View File

@ -1,29 +1,27 @@
using CodexClient;
using CodexPlugin;
using DistTestCore;
using NUnit.Framework;
namespace CodexTests
{
public class AutoBootstrapDistTest : CodexDistTest
{
private readonly Dictionary<TestLifecycle, ICodexNode> bootstrapNodes = new Dictionary<TestLifecycle, ICodexNode>();
private bool isBooting = false;
protected override void LifecycleStart(TestLifecycle tl)
public ICodexNode BootstrapNode { get; private set; } = null!;
[SetUp]
public void SetupBootstrapNode()
{
base.LifecycleStart(tl);
if (!bootstrapNodes.ContainsKey(tl))
{
isBooting = true;
bootstrapNodes.Add(tl, StartCodex(s => s.WithName("BOOTSTRAP_" + tl.TestNamespace)));
isBooting = false;
}
isBooting = true;
BootstrapNode = StartCodex(s => s.WithName("BOOTSTRAP_" + GetTestNamespace()));
isBooting = false;
}
protected override void LifecycleStop(TestLifecycle lifecycle, DistTestResult result)
[TearDown]
public void TearDownBootstrapNode()
{
bootstrapNodes.Remove(lifecycle);
base.LifecycleStop(lifecycle, result);
BootstrapNode.Stop(waitTillStopped: false);
}
protected override void OnCodexSetup(ICodexSetup setup)
@ -33,18 +31,5 @@ namespace CodexTests
var node = BootstrapNode;
if (node != null) setup.WithBootstrapNode(node);
}
protected ICodexNode BootstrapNode
{
get
{
var tl = Get();
if (bootstrapNodes.TryGetValue(tl, out var node))
{
return node;
}
throw new InvalidOperationException("Bootstrap node not yet started.");
}
}
}
}

View File

@ -1,6 +1,5 @@
using BlockchainUtils;
using CodexClient;
using CodexClient.Hooks;
using CodexContractsPlugin;
using CodexNetDeployer;
using CodexPlugin;
@ -17,85 +16,14 @@ using Newtonsoft.Json;
using NUnit.Framework;
using NUnit.Framework.Constraints;
using OverwatchTranscript;
using Utils;
namespace CodexTests
{
public class CodexLogTrackerProvider : ICodexHooksProvider
{
private readonly Action<ICodexNode> addNode;
public CodexLogTrackerProvider(Action<ICodexNode> addNode)
{
this.addNode = addNode;
}
// See TestLifecycle.cs DownloadAllLogs()
public ICodexNodeHooks CreateHooks(string nodeName)
{
return new CodexLogTracker(addNode);
}
public class CodexLogTracker : ICodexNodeHooks
{
private readonly Action<ICodexNode> addNode;
public CodexLogTracker(Action<ICodexNode> addNode)
{
this.addNode = addNode;
}
public void OnFileDownloaded(ByteSize size, ContentId cid)
{
}
public void OnFileDownloading(ContentId cid)
{
}
public void OnFileUploaded(string uid, ByteSize size, ContentId cid)
{
}
public void OnFileUploading(string uid, ByteSize size)
{
}
public void OnNodeStarted(ICodexNode node, string peerId, string nodeId)
{
addNode(node);
}
public void OnNodeStarting(DateTime startUtc, string image, EthAccount? ethAccount)
{
}
public void OnNodeStopping()
{
}
public void OnStorageAvailabilityCreated(StorageAvailability response)
{
}
public void OnStorageContractSubmitted(StoragePurchaseContract storagePurchaseContract)
{
}
public void OnStorageContractUpdated(StoragePurchase purchaseStatus)
{
}
}
}
public class CodexDistTest : DistTest
{
private static readonly object _lock = new object();
private static readonly Dictionary<TestLifecycle, CodexTranscriptWriter> writers = new Dictionary<TestLifecycle, CodexTranscriptWriter>();
private static readonly Dictionary<TestLifecycle, BlockCache> blockCaches = new Dictionary<TestLifecycle, BlockCache>();
// this entire structure is not good and needs to be destroyed at the earliest convenience:
private static readonly Dictionary<TestLifecycle, List<ICodexNode>> nodes = new Dictionary<TestLifecycle, List<ICodexNode>>();
private readonly BlockCache blockCache = new BlockCache();
private readonly List<ICodexNode> nodes = new List<ICodexNode>();
private CodexTranscriptWriter? writer;
public CodexDistTest()
{
@ -105,41 +33,25 @@ namespace CodexTests
ProjectPlugin.Load<MetricsPlugin.MetricsPlugin>();
}
[SetUp]
public void SetupCodexDistTest()
{
writer = SetupTranscript();
}
[TearDown]
public void TearDownCodexDistTest()
{
TeardownTranscript();
}
protected override void Initialize(FixtureLog fixtureLog)
{
var localBuilder = new LocalCodexBuilder(fixtureLog);
localBuilder.Intialize();
localBuilder.Build();
}
protected override void LifecycleStart(TestLifecycle lifecycle)
{
base.LifecycleStart(lifecycle);
SetupTranscript(lifecycle);
Ci.AddCodexHooksProvider(new CodexLogTrackerProvider(n =>
{
lock (_lock)
{
if (!nodes.ContainsKey(lifecycle)) nodes.Add(lifecycle, new List<ICodexNode>());
nodes[lifecycle].Add(n);
}
}));
}
protected override void LifecycleStop(TestLifecycle lifecycle, DistTestResult result)
{
base.LifecycleStop(lifecycle, result);
TeardownTranscript(lifecycle, result);
if (!result.Success)
{
lock (_lock)
{
var codexNodes = nodes[lifecycle];
foreach (var node in codexNodes) node.DownloadLog();
}
}
Ci.AddCodexHooksProvider(new CodexLogTrackerProvider(nodes.Add));
}
public ICodexNode StartCodex()
@ -170,7 +82,7 @@ namespace CodexTests
public IGethNode StartGethNode(Action<IGethSetup> setup)
{
return Ci.StartGethNode(GetBlockCache(), setup);
return Ci.StartGethNode(blockCache, setup);
}
public PeerConnectionTestHelpers CreatePeerConnectionTestHelpers()
@ -180,7 +92,7 @@ namespace CodexTests
public PeerDownloadTestHelpers CreatePeerDownloadTestHelpers()
{
return new PeerDownloadTestHelpers(GetTestLog(), Get().GetFileManager());
return new PeerDownloadTestHelpers(GetTestLog(), GetFileManager());
}
public void AssertBalance(ICodexContracts contracts, ICodexNode codexNode, Constraint constraint, string msg = "")
@ -258,82 +170,47 @@ namespace CodexTests
return null;
}
private void SetupTranscript(TestLifecycle lifecycle)
private CodexTranscriptWriter? SetupTranscript()
{
var attr = GetTranscriptAttributeOfCurrentTest();
if (attr == null) return;
if (attr == null) return null;
var config = new CodexTranscriptWriterConfig(
attr.OutputFilename,
attr.IncludeBlockReceivedEvents
);
var log = new LogPrefixer(lifecycle.Log, "(Transcript) ");
var log = new LogPrefixer(GetTestLog(), "(Transcript) ");
var writer = new CodexTranscriptWriter(log, config, Transcript.NewWriter(log));
Ci.AddCodexHooksProvider(writer);
lock (_lock)
{
writers.Add(lifecycle, writer);
}
return writer;
}
private void TeardownTranscript(TestLifecycle lifecycle, DistTestResult result)
private void TeardownTranscript()
{
var attr = GetTranscriptAttributeOfCurrentTest();
if (attr == null) return;
var outputFilepath = GetOutputFullPath(lifecycle, attr);
CodexTranscriptWriter writer = null!;
lock (_lock)
{
writer = writers[lifecycle];
writers.Remove(lifecycle);
}
if (writer == null) return;
var result = GetTestResult();
var log = GetTestLog();
writer.AddResult(result.Success, result.Result);
try
{
Stopwatch.Measure(lifecycle.Log, "Transcript.ProcessLogs", () =>
Stopwatch.Measure(log, "Transcript.ProcessLogs", () =>
{
writer.ProcessLogs(lifecycle.DownloadAllLogs());
writer.ProcessLogs(DownloadAllLogs());
});
Stopwatch.Measure(lifecycle.Log, $"Transcript.Finalize: {outputFilepath}", () =>
Stopwatch.Measure(log, $"Transcript.FinalizeWriter", () =>
{
writer.IncludeFile(lifecycle.Log.GetFullName());
writer.Finalize(outputFilepath);
writer.IncludeFile(log.GetFullName() + ".log");
writer.FinalizeWriter();
});
}
catch (Exception ex)
{
lifecycle.Log.Error("Failure during transcript teardown: " + ex);
log.Error("Failure during transcript teardown: " + ex);
}
}
private string GetOutputFullPath(TestLifecycle lifecycle, CreateTranscriptAttribute attr)
{
var outputPath = Path.GetDirectoryName(lifecycle.Log.GetFullName());
if (outputPath == null) throw new Exception("Logfile path is null");
var filename = Path.GetFileNameWithoutExtension(lifecycle.Log.GetFullName());
if (string.IsNullOrEmpty(filename)) throw new Exception("Logfile name is null or empty");
var outputFile = Path.Combine(outputPath, filename + "_" + attr.OutputFilename);
if (!outputFile.EndsWith(".owts")) outputFile += ".owts";
return outputFile;
}
private BlockCache GetBlockCache()
{
var lifecycle = Get();
lock (_lock)
{
if (!blockCaches.ContainsKey(lifecycle))
{
blockCaches[lifecycle] = new BlockCache();
}
}
return blockCaches[lifecycle];
}
}
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]

View File

@ -0,0 +1,73 @@
using CodexClient;
using CodexClient.Hooks;
using Utils;
namespace CodexTests
{
public class CodexLogTrackerProvider : ICodexHooksProvider
{
private readonly Action<ICodexNode> addNode;
public CodexLogTrackerProvider(Action<ICodexNode> addNode)
{
this.addNode = addNode;
}
// See TestLifecycle.cs DownloadAllLogs()
public ICodexNodeHooks CreateHooks(string nodeName)
{
return new CodexLogTracker(addNode);
}
public class CodexLogTracker : ICodexNodeHooks
{
private readonly Action<ICodexNode> addNode;
public CodexLogTracker(Action<ICodexNode> addNode)
{
this.addNode = addNode;
}
public void OnFileDownloaded(ByteSize size, ContentId cid)
{
}
public void OnFileDownloading(ContentId cid)
{
}
public void OnFileUploaded(string uid, ByteSize size, ContentId cid)
{
}
public void OnFileUploading(string uid, ByteSize size)
{
}
public void OnNodeStarted(ICodexNode node, string peerId, string nodeId)
{
addNode(node);
}
public void OnNodeStarting(DateTime startUtc, string image, EthAccount? ethAccount)
{
}
public void OnNodeStopping()
{
}
public void OnStorageAvailabilityCreated(StorageAvailability response)
{
}
public void OnStorageContractSubmitted(StoragePurchaseContract storagePurchaseContract)
{
}
public void OnStorageContractUpdated(StoragePurchase purchaseStatus)
{
}
}
}
}