using System;
using System.Collections.Generic;
using System.Linq;
namespace OpenTap
{
///
/// Mixins can be used with EmbedPropertiesAttribute to give extra functionality to a test step.
///
public interface IMixin : ITapPlugin
{
}
abstract class MixinEvent where T2: IMixin
{
static readonly TraceSource log = Log.CreateSource("Mixin");
protected static T1 Invoke(object target, Action f, T1 arg) => Invoke(target, f, null, arg, out _);
protected static T1 Invoke(object target, Action f, Func ordering, T1 arg, out bool anyInvoked)
{
anyInvoked = false;
var emb = TypeData.GetTypeData(target).GetBaseType();
if (emb == null) return arg;
var embeddingMembers = emb.GetEmbeddingMembers();
List objects = null;
foreach (var mem in embeddingMembers)
{
if (!mem.TypeDescriptor.DescendsTo(typeof(T2))) continue;
if (mem.Readable == false) continue;
if (mem.GetValue(target) is T2 mixin)
{
(objects ??= []).Add(mixin);
}
}
if (objects != null)
{
IEnumerable orderedObjects = ordering != null ? objects.OrderBy(ordering) : objects;
foreach (var mixin in orderedObjects)
{
try
{
f(mixin, arg);
}
catch (Exception e) when (e is not OperationCanceledException)
{
log.Error("Caught error in mixin: {0}", e.Message);
log.Debug(e);
}
}
anyInvoked = true;
}
return arg;
}
}
class TestStepPreRunEvent : MixinEvent
{
public static TestStepPreRunEventArgs Invoke(ITestStep step)
{
var eventArg = new TestStepPreRunEventArgs(step);
Invoke(step, static (v, arg) => v.OnPreRun(arg), static v => (v as ITestStepPreRunMixinOrder)?.GetPreRunOrder() ?? 0.0, eventArg, out bool anyInvoked);
eventArg.AnyPrerunsInvoked = anyInvoked;
return eventArg;
}
}
/// Event args for ITestStepPreRun mixin.
public sealed class TestStepPreRunEventArgs
{
/// The step for which the event happens.
public ITestStep TestStep { get; }
/// Can be set to true to skip the step
public bool SkipStep { get; set; }
/// Indicates whether any prerun mixins were invoked.
internal bool AnyPrerunsInvoked { get; set; }
internal TestStepPreRunEventArgs(ITestStep step) => TestStep = step;
}
class TestPlanPreRunEvent : MixinEvent
{
public static TestPlanPreRunEventArgs Invoke(TestPlan plan) =>
Invoke(plan, (v, arg) => v.OnPreRun(arg), new TestPlanPreRunEventArgs(plan));
}
/// Event args for ITestStepPreRun mixin.
public sealed class TestPlanPreRunEventArgs
{
/// The step for which the event happens.
public TestPlan TestPlan { get; }
internal TestPlanPreRunEventArgs(TestPlan step) => TestPlan = step;
}
class TestStepPostRunEvent : MixinEvent
{
public static void Invoke(ITestStep step) =>
Invoke(step, static (mixin, args) => mixin.OnPostRun(args), static v => (v as ITestStepPostRunMixinOrder)?.GetPostRunOrder() ?? 0, new TestStepPostRunEventArgs(step), out _);
}
/// Event args for ITestStepPostRun mixin.
public sealed class TestStepPostRunEventArgs
{
/// The step for which the event happens.
public ITestStep TestStep { get; }
internal TestStepPostRunEventArgs(ITestStep step) => TestStep = step;
}
class ResourcePreOpenEvent: MixinEvent
{
public static void Invoke(IResource resource) =>
Invoke(resource, static (mixin, args) => mixin.OnPreOpen(args), new ResourcePreOpenEventArgs(resource));
}
/// Event args for IResourcePreOpenMixin mixin.
public class ResourcePreOpenEventArgs
{
/// The resource for which the event happens.
public IResource Resource { get; }
internal ResourcePreOpenEventArgs(IResource resource) => Resource = resource;
}
/// This mixin is activated just after a step has been executed. It allows modifying the test step run.
public interface ITestStepPostRunMixin : IMixin
{
/// Invoked after test step run.
void OnPostRun(TestStepPostRunEventArgs eventArgs);
}
/// Defines an ordering for test step post run mixins.
public interface ITestStepPostRunMixinOrder : ITestStepPostRunMixin
{
///
/// Gets the order. Default value is 0. The ordering is ascending.
///
double GetPostRunOrder();
}
/// This mixin is activated just before a step is executed. It allows modifying the test step run.
public interface ITestStepPreRunMixin : IMixin
{
/// Invoked before test step run.
void OnPreRun(TestStepPreRunEventArgs eventArgs);
}
/// Defines an ordering for test step pre run mixins.
public interface ITestStepPreRunMixinOrder : ITestStepPreRunMixin
{
/// Gets the order. Default value is 0. The ordering is ascending.
double GetPreRunOrder();
}
/// This mixin is activated just before a resource opens.
public interface IResourcePreOpenMixin : IMixin
{
/// Invoked just before IResource.Open is called.
void OnPreOpen(ResourcePreOpenEventArgs eventArgs);
}
/// This mixin is activated just before a step is executed. It allows modifying the test step run.
public interface ITestPlanPreRunMixin : IMixin
{
/// Invoked before test step run.
void OnPreRun(TestPlanPreRunEventArgs eventArgs);
}
}