Makes timings not static and ties them to test lifecycle

This commit is contained in:
benbierens 2023-05-04 08:55:20 +02:00
parent 2ed6993b58
commit 5a4a5795b2
No known key found for this signature in database
GPG Key ID: FE44815D96D0A1AA
10 changed files with 56 additions and 87 deletions

View File

@ -6,10 +6,12 @@ namespace DistTestCore.Codex
public class CodexAccess public class CodexAccess
{ {
private readonly BaseLog log; private readonly BaseLog log;
private readonly ITimeSet timeSet;
public CodexAccess(BaseLog log, RunningContainer runningContainer) public CodexAccess(BaseLog log, ITimeSet timeSet, RunningContainer runningContainer)
{ {
this.log = log; this.log = log;
this.timeSet = timeSet;
Container = runningContainer; Container = runningContainer;
} }
@ -44,7 +46,7 @@ namespace DistTestCore.Codex
{ {
var ip = Container.Pod.Cluster.IP; var ip = Container.Pod.Cluster.IP;
var port = Container.ServicePorts[0].Number; var port = Container.ServicePorts[0].Number;
return new Http(log, ip, port, baseUrl: "/api/codex/v1"); return new Http(log, timeSet, ip, port, baseUrl: "/api/codex/v1");
} }
public string ConnectToPeer(string peerId, string peerMultiAddress) public string ConnectToPeer(string peerId, string peerMultiAddress)

View File

@ -64,7 +64,7 @@ namespace DistTestCore
private OnlineCodexNode CreateOnlineCodexNode(RunningContainer c, ICodexNodeFactory factory) private OnlineCodexNode CreateOnlineCodexNode(RunningContainer c, ICodexNodeFactory factory)
{ {
var access = new CodexAccess(lifecycle.Log, c); var access = new CodexAccess(lifecycle.Log, lifecycle.TimeSet, c);
EnsureOnline(access); EnsureOnline(access);
return factory.CreateOnlineCodexNode(access, this); return factory.CreateOnlineCodexNode(access, this);
} }

View File

@ -4,13 +4,13 @@ namespace DistTestCore
{ {
public class Configuration public class Configuration
{ {
public KubernetesWorkflow.Configuration GetK8sConfiguration() public KubernetesWorkflow.Configuration GetK8sConfiguration(ITimeSet timeSet)
{ {
return new KubernetesWorkflow.Configuration( return new KubernetesWorkflow.Configuration(
k8sNamespacePrefix: "ct-", k8sNamespacePrefix: "ct-",
kubeConfigFile: null, kubeConfigFile: null,
operationTimeout: Timing.K8sOperationTimeout(), operationTimeout: timeSet.K8sOperationTimeout(),
retryDelay: Timing.K8sServiceDelay(), retryDelay: timeSet.WaitForK8sServiceDelay(),
locationMap: new[] locationMap: new[]
{ {
new ConfigurationLocationEntry(Location.BensOldGamingMachine, "worker01"), new ConfigurationLocationEntry(Location.BensOldGamingMachine, "worker01"),

View File

@ -32,13 +32,11 @@ namespace DistTestCore
{ {
// Previous test run may have been interrupted. // Previous test run may have been interrupted.
// Begin by cleaning everything up. // Begin by cleaning everything up.
Timing.UseLongTimeouts = false;
try try
{ {
Stopwatch.Measure(fixtureLog, "Global setup", () => Stopwatch.Measure(fixtureLog, "Global setup", () =>
{ {
var wc = new WorkflowCreator(fixtureLog, configuration.GetK8sConfiguration()); var wc = new WorkflowCreator(fixtureLog, configuration.GetK8sConfiguration(GetTimeSet()));
wc.CreateWorkflow().DeleteAllResources(); wc.CreateWorkflow().DeleteAllResources();
}); });
} }
@ -58,8 +56,6 @@ namespace DistTestCore
[SetUp] [SetUp]
public void SetUpDistTest() public void SetUpDistTest()
{ {
Timing.UseLongTimeouts = ShouldUseLongTimeouts();
if (GlobalTestFailure.HasFailed) if (GlobalTestFailure.HasFailed)
{ {
Assert.Inconclusive("Skip test: Previous test failed during clean up."); Assert.Inconclusive("Skip test: Previous test failed during clean up.");
@ -145,21 +141,6 @@ namespace DistTestCore
} }
} }
private bool ShouldUseLongTimeouts()
{
// Don't be fooled! TestContext.CurrentTest.Test allows you easy access to the attributes of the current test.
// But this doesn't work for tests making use of [TestCase]. So instead, we use reflection here to figure out
// if the attribute is present.
var currentTest = TestContext.CurrentContext.Test;
var className = currentTest.ClassName;
var methodName = currentTest.MethodName;
var testClasses = 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.Any(m => m.GetCustomAttribute<UseLongTimeoutsAttribute>() != null);
}
private void CreateNewTestLifecycle() private void CreateNewTestLifecycle()
{ {
var testName = GetCurrentTestName(); var testName = GetCurrentTestName();
@ -167,7 +148,7 @@ namespace DistTestCore
{ {
lock (lifecycleLock) lock (lifecycleLock)
{ {
lifecycles.Add(testName, new TestLifecycle(fixtureLog.CreateTestLog(), configuration)); lifecycles.Add(testName, new TestLifecycle(fixtureLog.CreateTestLog(), configuration, GetTimeSet()));
} }
}); });
} }
@ -185,6 +166,27 @@ namespace DistTestCore
}); });
} }
private ITimeSet GetTimeSet()
{
if (ShouldUseLongTimeouts()) return new LongTimeSet();
return new DefaultTimeSet();
}
private bool ShouldUseLongTimeouts()
{
// Don't be fooled! TestContext.CurrentTest.Test allows you easy access to the attributes of the current test.
// But this doesn't work for tests making use of [TestCase]. So instead, we use reflection here to figure out
// if the attribute is present.
var currentTest = TestContext.CurrentContext.Test;
var className = currentTest.ClassName;
var methodName = currentTest.MethodName;
var testClasses = 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.Any(m => m.GetCustomAttribute<UseLongTimeoutsAttribute>() != null);
}
private void IncludeLogsAndMetricsOnTestFailure(TestLifecycle lifecycle) private void IncludeLogsAndMetricsOnTestFailure(TestLifecycle lifecycle)
{ {
var result = TestContext.CurrentContext.Result; var result = TestContext.CurrentContext.Result;

View File

@ -10,13 +10,15 @@ namespace DistTestCore
public class Http public class Http
{ {
private readonly BaseLog log; private readonly BaseLog log;
private readonly ITimeSet timeSet;
private readonly string ip; private readonly string ip;
private readonly int port; private readonly int port;
private readonly string baseUrl; private readonly string baseUrl;
public Http(BaseLog log, string ip, int port, string baseUrl) public Http(BaseLog log, ITimeSet timeSet, string ip, int port, string baseUrl)
{ {
this.log = log; this.log = log;
this.timeSet = timeSet;
this.ip = ip; this.ip = ip;
this.port = port; this.port = port;
this.baseUrl = baseUrl; this.baseUrl = baseUrl;
@ -103,7 +105,7 @@ namespace DistTestCore
log.Debug($"({url}) = '{message}'", 3); log.Debug($"({url}) = '{message}'", 3);
} }
private static T Retry<T>(Func<T> operation) private T Retry<T>(Func<T> operation)
{ {
var retryCounter = 0; var retryCounter = 0;
@ -115,9 +117,9 @@ namespace DistTestCore
} }
catch (Exception exception) catch (Exception exception)
{ {
Timing.HttpCallRetryDelay(); timeSet.HttpCallRetryDelay();
retryCounter++; retryCounter++;
if (retryCounter > Timing.HttpCallRetryCount()) if (retryCounter > timeSet.HttpCallRetryCount())
{ {
Assert.Fail(exception.ToString()); Assert.Fail(exception.ToString());
throw; throw;
@ -140,10 +142,10 @@ namespace DistTestCore
} }
} }
private static HttpClient GetClient() private HttpClient GetClient()
{ {
var client = new HttpClient(); var client = new HttpClient();
client.Timeout = Timing.HttpCallTimeout(); client.Timeout = timeSet.HttpCallTimeout();
return client; return client;
} }
} }

View File

@ -14,12 +14,14 @@ namespace DistTestCore.Metrics
public class MetricsAccess : IMetricsAccess public class MetricsAccess : IMetricsAccess
{ {
private readonly TestLog log; private readonly TestLog log;
private readonly ITimeSet timeSet;
private readonly MetricsQuery query; private readonly MetricsQuery query;
private readonly RunningContainer node; private readonly RunningContainer node;
public MetricsAccess(TestLog log, MetricsQuery query, RunningContainer node) public MetricsAccess(TestLog log, ITimeSet timeSet, MetricsQuery query, RunningContainer node)
{ {
this.log = log; this.log = log;
this.timeSet = timeSet;
this.query = query; this.query = query;
this.node = node; this.node = node;
} }
@ -47,7 +49,7 @@ namespace DistTestCore.Metrics
{ {
var mostRecent = GetMostRecent(metricName); var mostRecent = GetMostRecent(metricName);
if (mostRecent != null) return mostRecent; if (mostRecent != null) return mostRecent;
if (DateTime.UtcNow - start > Timing.WaitForMetricTimeout()) if (DateTime.UtcNow - start > timeSet.WaitForMetricTimeout())
{ {
Assert.Fail($"Timeout: Unable to get metric '{metricName}'."); Assert.Fail($"Timeout: Unable to get metric '{metricName}'.");
throw new TimeoutException(); throw new TimeoutException();

View File

@ -28,8 +28,8 @@ namespace DistTestCore.Metrics
public IMetricsAccess CreateMetricsAccess(RunningContainer codexContainer) public IMetricsAccess CreateMetricsAccess(RunningContainer codexContainer)
{ {
var query = new MetricsQuery(lifecycle.Log, prometheusContainer); var query = new MetricsQuery(lifecycle.Log, lifecycle.TimeSet, prometheusContainer);
return new MetricsAccess(lifecycle.Log, query, codexContainer); return new MetricsAccess(lifecycle.Log, lifecycle.TimeSet, query, codexContainer);
} }
} }
} }

View File

@ -9,12 +9,13 @@ namespace DistTestCore.Metrics
{ {
private readonly Http http; private readonly Http http;
public MetricsQuery(BaseLog log, RunningContainers runningContainers) public MetricsQuery(BaseLog log, ITimeSet timeSet, RunningContainers runningContainers)
{ {
RunningContainers = runningContainers; RunningContainers = runningContainers;
http = new Http( http = new Http(
log, log,
timeSet,
runningContainers.RunningPod.Cluster.IP, runningContainers.RunningPod.Cluster.IP,
runningContainers.Containers[0].ServicePorts[0].Number, runningContainers.Containers[0].ServicePorts[0].Number,
"api/v1"); "api/v1");

View File

@ -10,10 +10,11 @@ namespace DistTestCore
private readonly WorkflowCreator workflowCreator; private readonly WorkflowCreator workflowCreator;
private DateTime testStart = DateTime.MinValue; private DateTime testStart = DateTime.MinValue;
public TestLifecycle(TestLog log, Configuration configuration) public TestLifecycle(TestLog log, Configuration configuration, ITimeSet timeSet)
{ {
Log = log; Log = log;
workflowCreator = new WorkflowCreator(log, configuration.GetK8sConfiguration()); TimeSet = timeSet;
workflowCreator = new WorkflowCreator(log, configuration.GetK8sConfiguration(timeSet));
FileManager = new FileManager(Log, configuration); FileManager = new FileManager(Log, configuration);
CodexStarter = new CodexStarter(this, workflowCreator); CodexStarter = new CodexStarter(this, workflowCreator);
@ -23,6 +24,7 @@ namespace DistTestCore
} }
public TestLog Log { get; } public TestLog Log { get; }
public ITimeSet TimeSet { get; }
public FileManager FileManager { get; } public FileManager FileManager { get; }
public CodexStarter CodexStarter { get; } public CodexStarter CodexStarter { get; }
public PrometheusStarter PrometheusStarter { get; } public PrometheusStarter PrometheusStarter { get; }

View File

@ -8,53 +8,11 @@ namespace DistTestCore
{ {
} }
public static class Timing
{
public static bool UseLongTimeouts { get; set; }
public static TimeSpan HttpCallTimeout()
{
return GetTimes().HttpCallTimeout();
}
public static int HttpCallRetryCount()
{
return GetTimes().HttpCallRetryCount();
}
public static void HttpCallRetryDelay()
{
Time.Sleep(GetTimes().HttpCallRetryDelay());
}
public static TimeSpan K8sServiceDelay()
{
return GetTimes().WaitForK8sServiceDelay();
}
public static TimeSpan K8sOperationTimeout()
{
return GetTimes().K8sOperationTimeout();
}
public static TimeSpan WaitForMetricTimeout()
{
return GetTimes().WaitForMetricTimeout();
}
private static ITimeSet GetTimes()
{
if (UseLongTimeouts) return new LongTimeSet();
return new DefaultTimeSet();
}
}
public interface ITimeSet public interface ITimeSet
{ {
TimeSpan HttpCallTimeout(); TimeSpan HttpCallTimeout();
int HttpCallRetryCount(); int HttpCallRetryCount();
TimeSpan HttpCallRetryDelay(); void HttpCallRetryDelay();
TimeSpan WaitForK8sServiceDelay(); TimeSpan WaitForK8sServiceDelay();
TimeSpan K8sOperationTimeout(); TimeSpan K8sOperationTimeout();
TimeSpan WaitForMetricTimeout(); TimeSpan WaitForMetricTimeout();
@ -72,9 +30,9 @@ namespace DistTestCore
return 5; return 5;
} }
public TimeSpan HttpCallRetryDelay() public void HttpCallRetryDelay()
{ {
return TimeSpan.FromSeconds(3); Time.Sleep(TimeSpan.FromSeconds(3));
} }
public TimeSpan WaitForK8sServiceDelay() public TimeSpan WaitForK8sServiceDelay()
@ -105,9 +63,9 @@ namespace DistTestCore
return 2; return 2;
} }
public TimeSpan HttpCallRetryDelay() public void HttpCallRetryDelay()
{ {
return TimeSpan.FromMinutes(5); Time.Sleep(TimeSpan.FromMinutes(5));
} }
public TimeSpan WaitForK8sServiceDelay() public TimeSpan WaitForK8sServiceDelay()