using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace OpenTap
{
///
/// Marks a setting that can be enabled/disabled by the user. UIs are expected to render a checkbox in front of the actual value.
/// Settings of type gets annotated with an annotation that implements this.
///
public interface IEnabledValueAnnotation : IAnnotation
{
///
/// Indicates whether this setting is enabled.
///
AnnotationCollection IsEnabled { get; }
///
/// Annotations describing the actual value.
///
AnnotationCollection Value { get; }
}
internal class BreakConditionsAnnotation : IEnabledValueAnnotation, IOwnedAnnotation, IMembersAnnotation
{
internal class BreakConditionValueAnnotation : IStringReadOnlyValueAnnotation, IValueDescriptionAnnotation, IAccessAnnotation
{
static BreakCondition[] conditions = Enum.GetValues(typeof(BreakCondition)).OfType().ToArray();
static BreakCondition[] breakConditions =
conditions.Where(x => x.ToString().Contains("Break")).ToArray();
static string getEnumString(BreakCondition value)
{
if (value == 0) return "None";
var sb = new StringBuilder();
var breakFlags = breakConditions.Where(x => value.HasFlag(x));
if (breakFlags.Any())
{
sb.AppendFormat("Break on {0}", breakFlags.First().ToString().Substring("BreakOn".Length));
var breakFlags2 = breakFlags.Skip(1);
foreach (var x in breakFlags2)
{
sb.AppendFormat(" or {0}", x.ToString().Substring("BreakOn".Length));
}
}
return sb.ToString();
}
static BreakCondition convertAbortCondition(EngineSettings.AbortTestPlanType abortType)
{
return ((abortType.HasFlag(EngineSettings.AbortTestPlanType.Step_Fail)) ? BreakCondition.BreakOnFail : 0)
| (abortType.HasFlag(EngineSettings.AbortTestPlanType.Step_Error) ? BreakCondition.BreakOnError : 0)
| (abortType.HasFlag(EngineSettings.AbortTestPlanType.Step_Inconclusive) ? BreakCondition.BreakOnInconclusive : 0)
| (abortType.HasFlag(EngineSettings.AbortTestPlanType.Step_Pass) ? BreakCondition.BreakOnPass : 0);
}
private static (BreakCondition Condition, string InheritKind, bool MultiselectDifference) getInheritedVerdict(ITestStepParent _step)
{
ITestStepParent src = _step;
src = src.Parent;
while (src != null)
{
var cond = BreakConditionProperty.GetBreakCondition(src);
if (cond.HasFlag(BreakCondition.Inherit) == false)
{
if (src is TestPlan)
return (cond, $"test plan", false);
return (cond, $"parent step '{((ITestStep)src).GetFormattedName()}'", false);
}
src = src.Parent;
}
return (convertAbortCondition(EngineSettings.Current.AbortTestPlan), "engine settings", false);
}
public (BreakCondition Condition, string InheritKind, bool MultiselectDifference) GetCondition()
{
if (annotation.Conditions.HasFlag(BreakCondition.Inherit))
{
if(annotation.annotation.Source is ITestStepParent step)
return getInheritedVerdict(step);
if (annotation.annotation.Source is IEnumerable stepList)
{
return getInheritedVerdict(stepList.First());
}
}
if (valueAnnotation.Get().Value == null)
{
var valuemem = (BreakCondition)0;
return (valuemem, null, true);
}
else
{
var valuemem = (BreakCondition)valueAnnotation.Get().Value;
return (valuemem, null, false);
}
}
public string Value
{
get
{
var (condition, _, multiselectDifference) = GetCondition();
if (multiselectDifference)
return "";
return getEnumString(condition);
}
}
public bool IsReadOnly => annotation.Conditions.HasFlag(BreakCondition.Inherit);
public bool IsVisible => true;
readonly BreakConditionsAnnotation annotation;
public AnnotationCollection valueAnnotation;
public string Describe()
{
var (condition, kind, multiselectDifference) = GetCondition();
if (multiselectDifference)
return "Selected Test Steps has different values for this setting.";
var str = getEnumString(condition);
if (kind == null) return str;
return $"{str} (inherited from {kind}).";
}
public BreakConditionValueAnnotation(BreakConditionsAnnotation annotation)
{
this.annotation = annotation;
}
}
[Flags]
public enum Values
{
/// If a step completes with verdict 'Error', stop execution of any subsequent steps at this level, and return control to the parent step.
[Display("On Error", "If a step completes with verdict 'Error', stop execution of any subsequent steps at this level, and return control to the parent step.")]
BreakOnError = 2,
/// If a step completes with verdict 'Fail', stop execution of any subsequent steps at this level, and return control to the parent step.
[Display("On Fail", "If a step completes with verdict 'Fail', stop execution of any subsequent steps at this level, and return control to the parent step.")]
BreakOnFail = 4,
/// If a step completes with verdict 'Inclusive' the step should break execution.
[Display("On Inconclusive", "If a step completes with verdict 'inconclusive', stop execution of any subsequent steps at this level, and return control to the parent step.")]
BreakOnInconclusive = 8,
/// If a step completes with verdict 'Pass' the step should break execution.
[Display("On Pass", "If a step completes with verdict 'pass', stop execution of any subsequent steps at this level, and return control to the parent step.")]
BreakOnPass = 16,
}
AnnotationCollection createEnabledAnnotation()
{
bool isEnabled = !Conditions.HasFlag(BreakCondition.Inherit);
var sub = annotation.AnnotateSub(TypeData.GetTypeData(isEnabled), isEnabled);
sub.Add(new AnnotationCollection.MemberAnnotation(TypeData.FromType(typeof(IEnabled)).GetMember(nameof(IEnabledValue.IsEnabled)))); // for compatibility with 9.8 UIs, emulate that this is a IsEnabled member from a Enabled class
return sub;
}
AnnotationCollection enabledAnnotation;
public AnnotationCollection IsEnabled => (enabledAnnotation ?? (enabledAnnotation = createEnabledAnnotation()));
BreakConditionValueAnnotation str;
AnnotationCollection createValueAnnotation()
{
var _value = (Values)(int)Conditions;
var sub = annotation.AnnotateSub(TypeData.GetTypeData(_value), _value);
sub.Add(new AnnotationCollection.MemberAnnotation(TypeData.FromType(typeof(Enabled)).GetMember("Value"))); // for compatibility with 9.8 UIs, emulate that this is a Value member from a Enabled class
sub.Add(str = new BreakConditionValueAnnotation(this) { valueAnnotation = sub });
return sub;
}
AnnotationCollection subannotations;
public AnnotationCollection Value => (subannotations ?? (subannotations = createValueAnnotation()));
internal BreakCondition Conditions
{
get => (BreakCondition) annotation.Get().Value;
set { annotation.Get().Value = value; }
}
public IEnumerable Members => new[] { IsEnabled, Value }; // IMembersAnnotationthis is implemented here for compatablility with 9.8 UIs
AnnotationCollection annotation;
internal BreakConditionsAnnotation(AnnotationCollection annotation)
{
this.annotation = annotation;
}
public void Read(object source)
{
if (subannotations != null)
{
Value.Get().Value = (Values)(int)Conditions;
Value.Read();
}
if (enabledAnnotation != null)
{
IsEnabled.Get().Value = false == Conditions.HasFlag(BreakCondition.Inherit);
IsEnabled.Read();
}
}
public void Write(object source)
{
if (subannotations == null && enabledAnnotation == null) return;
Value?.Write();
var cond = (BreakCondition)(int)subannotations.Get().Value;
var dontInherit = (bool)(enabledAnnotation?.Get().Value ?? false);
if (dontInherit && cond.HasFlag(BreakCondition.Inherit))
{
var cond2 = str.GetCondition();
cond = cond2.Condition;
}
else if (dontInherit == false)
{
cond = BreakCondition.Inherit;
}
cond = cond.SetFlag(BreakCondition.Inherit, !dontInherit);
Conditions = cond;
annotation.Get().Value = cond;
Read(source);
}
}
}