319 lines
10 KiB
C#
Raw Normal View History

2023-09-12 13:32:06 +02:00
using Core;
2023-09-20 10:51:47 +02:00
using DistTestCore.Logs;
using FileUtils;
2023-04-14 12:37:05 +02:00
using Logging;
using NUnit.Framework;
2024-03-27 15:01:32 +01:00
using NUnit.Framework.Interfaces;
using System.Reflection;
2023-09-08 10:21:40 +02:00
using Utils;
2025-01-16 13:51:29 +01:00
using WebUtils;
2023-09-20 10:51:47 +02:00
using Assert = NUnit.Framework.Assert;
2023-04-12 16:06:04 +02:00
namespace DistTestCore
{
2023-05-04 08:25:48 +02:00
[Parallelizable(ParallelScope.All)]
public abstract class DistTest
2023-04-12 16:06:04 +02:00
{
2024-07-23 09:58:29 +02:00
private const string TestNamespacePrefix = "cdx-";
2023-04-14 14:53:39 +02:00
private readonly Configuration configuration = new Configuration();
private readonly Assembly[] testAssemblies;
private readonly FixtureLog fixtureLog;
2023-07-18 09:47:44 +02:00
private readonly StatusLog statusLog;
private readonly EntryPoint globalEntryPoint;
private readonly DistTestLifecycleComponents lifecycleComponents = new DistTestLifecycleComponents();
private readonly string deployId;
2023-09-12 10:31:55 +02:00
public DistTest()
{
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
testAssemblies = assemblies.Where(a => a.FullName!.ToLowerInvariant().Contains("test")).ToArray();
deployId = NameUtils.MakeDeployId();
2023-07-18 09:47:44 +02:00
var logConfig = configuration.GetLogConfig();
var startTime = DateTime.UtcNow;
fixtureLog = FixtureLog.Create(logConfig, startTime, deployId);
statusLog = new StatusLog(logConfig, startTime, "dist-tests", deployId);
2025-01-16 13:51:29 +01:00
globalEntryPoint = new EntryPoint(fixtureLog, configuration.GetK8sConfiguration(new DefaultK8sTimeSet(), TestNamespacePrefix), configuration.GetFileManagerFolder());
Initialize(fixtureLog);
}
2023-04-12 16:06:04 +02:00
[OneTimeSetUp]
public void GlobalSetup()
{
fixtureLog.Log($"Starting...");
globalEntryPoint.Announce();
2023-07-17 09:26:54 +02:00
2023-04-12 16:06:04 +02:00
// Previous test run may have been interrupted.
// Begin by cleaning everything up.
try
{
2023-04-14 14:53:39 +02:00
Stopwatch.Measure(fixtureLog, "Global setup", () =>
{
globalEntryPoint.Tools.CreateWorkflow().DeleteNamespacesStartingWith(TestNamespacePrefix, wait: true);
2023-04-14 14:53:39 +02:00
});
2023-04-12 16:06:04 +02:00
}
catch (Exception ex)
{
GlobalTestFailure.HasFailed = true;
fixtureLog.Error($"Global setup cleanup failed with: {ex}");
2023-04-12 16:06:04 +02:00
throw;
}
2023-04-14 14:53:39 +02:00
fixtureLog.Log("Test framework revision: " + GitInfo.GetStatus());
2023-04-14 14:53:39 +02:00
fixtureLog.Log("Global setup cleanup successful");
2023-04-12 16:06:04 +02:00
}
2023-09-12 10:31:55 +02:00
[OneTimeTearDown]
public void GlobalTearDown()
{
2023-09-21 10:33:09 +02:00
globalEntryPoint.Decommission(
// There shouldn't be any of either, but clean everything up regardless.
deleteKubernetesResources: true,
deleteTrackedFiles: true,
waitTillDone: true
2023-09-21 10:33:09 +02:00
);
2023-09-12 10:31:55 +02:00
}
2023-04-12 16:06:04 +02:00
[SetUp]
public void SetUpDistTest()
{
if (GlobalTestFailure.HasFailed)
{
Assert.Inconclusive("Skip test: Previous test failed during clean up.");
}
else
{
try
{
var testName = GetCurrentTestName();
fixtureLog.WriteLogTag();
Stopwatch.Measure(fixtureLog, $"Setup for {testName}", () =>
{
lifecycleComponents.Setup(testName, CreateComponents);
});
}
catch (Exception ex)
{
fixtureLog.Error("Setup failed: " + ex);
GlobalTestFailure.HasFailed = true;
}
2023-04-12 16:06:04 +02:00
}
}
[TearDown]
public void TearDownDistTest()
{
try
{
Stopwatch.Measure(fixtureLog, $"Teardown for {GetCurrentTestName()}", DisposeTestLifecycle);
2023-04-12 16:06:04 +02:00
}
catch (Exception ex)
{
2024-04-15 11:37:14 +02:00
fixtureLog.Error("Cleanup failed: " + ex);
2023-04-12 16:06:04 +02:00
GlobalTestFailure.HasFailed = true;
}
}
public CoreInterface Ci
{
get
{
return Get().CoreInterface;
}
}
2023-09-12 13:32:06 +02:00
public TrackedFile GenerateTestFile(ByteSize size, string label = "")
2023-04-12 16:06:04 +02:00
{
2023-09-13 10:03:11 +02:00
return Get().GenerateTestFile(size, label);
2023-04-12 16:06:04 +02:00
}
2024-07-01 15:59:08 +02:00
public TrackedFile GenerateTestFile(Action<IGenerateOption> options, string label = "")
{
return Get().GenerateTestFile(options, label);
}
/// <summary>
/// Any test files generated in 'action' will be deleted after it returns.
/// This helps prevent large tests from filling up discs.
/// </summary>
public void ScopedTestFiles(Action action)
{
2023-09-13 14:24:43 +02:00
Get().GetFileManager().ScopedFiles(action);
}
2023-09-12 10:31:55 +02:00
public ILog GetTestLog()
{
return Get().Log;
}
public void Log(string msg)
2023-04-26 14:40:54 +02:00
{
TestContext.Progress.WriteLine(msg);
GetTestLog().Log(msg);
}
public void Debug(string msg)
{
TestContext.Progress.WriteLine(msg);
GetTestLog().Debug(msg);
}
2023-08-22 15:51:39 +02:00
public void Measure(string name, Action action)
{
Stopwatch.Measure(Get().Log, name, action);
}
2023-12-20 10:55:29 +01:00
protected TimeRange GetTestRunTimeRange()
{
return new TimeRange(Get().TestStart, DateTime.UtcNow);
}
protected virtual void Initialize(FixtureLog fixtureLog)
{
}
protected virtual void CreateComponents(ILifecycleComponentCollector collector)
2023-12-06 10:50:02 +01:00
{
var testNamespace = TestNamespacePrefix + Guid.NewGuid().ToString();
var lifecycle = new TestLifecycle(
fixtureLog.CreateTestLog(),
configuration,
GetWebCallTimeSet(),
GetK8sTimeSet(),
testNamespace,
GetCurrentTestName(),
deployId,
ShouldWaitForCleanup());
2023-12-06 10:50:02 +01:00
collector.AddComponent(lifecycle);
2023-12-06 10:50:02 +01:00
}
protected virtual void DestroyComponents(TestLifecycle lifecycle, DistTestResult testResult)
2023-12-06 10:50:02 +01:00
{
}
public T Get<T>() where T : ILifecycleComponent
{
return lifecycleComponents.Get<T>(GetCurrentTestName());
2023-04-26 14:40:54 +02:00
}
private TestLifecycle Get()
2023-04-12 16:06:04 +02:00
{
return Get<TestLifecycle>();
2023-04-14 14:53:39 +02:00
}
private void DisposeTestLifecycle()
{
var testName = GetCurrentTestName();
var results = GetTestResult();
var lifecycle = Get();
2023-07-21 09:20:28 +02:00
var testDuration = lifecycle.GetTestDuration();
2023-12-06 10:50:02 +01:00
var data = lifecycle.GetPluginMetadata();
fixtureLog.Log($"{GetCurrentTestName()} = {results} ({testDuration})");
statusLog.ConcludeTest(results, testDuration, data);
2023-09-12 10:31:55 +02:00
lifecycleComponents.TearDown(testName, results);
2023-04-14 14:53:39 +02:00
}
2023-09-12 10:31:55 +02:00
2025-01-16 13:51:29 +01:00
private IWebCallTimeSet GetWebCallTimeSet()
{
2025-01-16 13:51:29 +01:00
if (ShouldUseLongTimeouts()) return new LongWebCallTimeSet();
return new DefaultWebCallTimeSet();
}
private IK8sTimeSet GetK8sTimeSet()
{
if (ShouldUseLongTimeouts()) return new LongK8sTimeSet();
return new DefaultK8sTimeSet();
}
private bool ShouldWaitForCleanup()
{
return CurrentTestMethodHasAttribute<WaitForCleanupAttribute>();
}
private bool ShouldUseLongTimeouts()
2024-04-22 11:17:47 +02:00
{
return CurrentTestMethodHasAttribute<UseLongTimeoutsAttribute>();
}
private bool HasDontDownloadAttribute()
{
return CurrentTestMethodHasAttribute<DontDownloadLogsAttribute>();
}
protected bool CurrentTestMethodHasAttribute<T>() where T : PropertyAttribute
{
return GetCurrentTestMethodAttribute<T>().Any();
}
protected T[] GetCurrentTestMethodAttribute<T>() where T : PropertyAttribute
{
// 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] or [Combinatorial]. So instead, we use reflection here to
// fetch the attributes of type T.
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.Select(m => m.GetCustomAttribute<T>())
.Where(a => a != null)
.Cast<T>()
.ToArray();
}
2023-04-14 14:53:39 +02:00
private string GetCurrentTestName()
{
return $"[{TestContext.CurrentContext.Test.Name}]";
}
private DistTestResult GetTestResult()
2023-04-14 14:53:39 +02:00
{
var success = TestContext.CurrentContext.Result.Outcome.Status == TestStatus.Passed;
var status = TestContext.CurrentContext.Result.Outcome.Status.ToString();
var result = TestContext.CurrentContext.Result.Message;
var trace = TestContext.CurrentContext.Result.StackTrace;
return new DistTestResult(success, status, result ?? string.Empty, trace ?? string.Empty);
2023-04-14 14:53:39 +02:00
}
private bool IsDownloadingLogsEnabled()
{
2024-04-22 11:17:47 +02:00
return !HasDontDownloadAttribute();
}
2023-04-12 16:06:04 +02:00
}
public class DistTestResult
{
public DistTestResult(bool success, string status, string result, string trace)
{
Success = success;
Status = status;
Result = result;
Trace = trace;
}
public bool Success { get; }
public string Status { get; }
public string Result { get; }
public string Trace { get; }
2024-08-20 15:31:45 +02:00
public override string ToString()
{
if (Success) return $"Passed ({Status}) ({Result})";
return $"Failed ({Status}) ({Result})";
}
}
2023-04-12 16:06:04 +02:00
public static class GlobalTestFailure
{
public static bool HasFailed { get; set; } = false;
}
}