using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Xml.Serialization; using NUnit.Framework; using OpenTap.Engine.UnitTests; using OpenTap.Engine.UnitTests.TestTestSteps; using OpenTap.EngineUnitTestUtils; using OpenTap.Plugins.BasicSteps; namespace OpenTap.UnitTests { public class AnnotationTest { [Test] public void TestPlanReferenceNameTest() { var step = new DelayStep(); var testPlanReference = new TestPlanReference(); var repeatStep = new RepeatStep(); repeatStep.ChildTestSteps.Add(testPlanReference); var plan = new TestPlan(); plan.ChildTestSteps.Add(step); plan.ChildTestSteps.Add(repeatStep); var mem = AnnotationCollection.Annotate(repeatStep).Get().Members .FirstOrDefault(x => x.Get().Member.Name == nameof(RepeatStep.TargetStep)); var avail = mem.Get().AvailableValues; var availStrings = avail.Select(x => x.Get().Value).ToArray(); Assert.IsTrue(availStrings.Contains(step.GetFormattedName())); Assert.IsTrue(availStrings.Contains(testPlanReference.GetFormattedName())); } [Test] public void TestStepDuplicateIdWarning() { using var session = Session.Create(SessionOptions.OverlayComponentSettings); var path = Path.GetTempFileName(); try { // The two delay steps have identical IDs -- verify we get a warning while loading this plan var testplan = @" true Delay 1 true Delay 2 "; File.WriteAllText(path, testplan); { // Verify loading generates no warnings or errors var listener = new TestTraceListener(); Log.AddListener(listener); var tp = TestPlan.Load(path); Log.RemoveListener(listener); Assert.That(listener.ErrorMessage.Count, Is.EqualTo(0)); Assert.That(listener.WarningMessage.Any(warning => warning == "Duplicate test step ID found. The duplicate ID has been changed for step 'Delay 2'.")); } } finally { if (File.Exists(path)) File.Delete(path); } } [Test] public void TestTestPlanReferenceDuplicates() { using var session = Session.Create(SessionOptions.OverlayComponentSettings); var tempfiles = new[] { Path.GetTempFileName(), Path.GetTempFileName(), Path.GetTempFileName(), }; try { { // Create test plans new TestPlan() { ChildTestSteps = { new DelayStep(), new DelayStep() } }.Save((tempfiles[0])); new TestPlan() { ChildTestSteps = { new TestPlanReference() { Filepath = new MacroString() { Text = tempfiles[0] } }, new TestPlanReference() { Filepath = new MacroString() { Text = tempfiles[0] } }, } }.Save(tempfiles[1]); new TestPlan() { ChildTestSteps = { new TestPlanReference() { Filepath = new MacroString() { Text = tempfiles[1] } }, new TestPlanReference() { Filepath = new MacroString() { Text = tempfiles[1] } }, } }.Save(tempfiles[2]); } { // Verify loading generates no warnings or errors var listener = new TestTraceListener(); Log.AddListener(listener); var tp = TestPlan.Load(tempfiles[2]); ((TestPlanReference)tp.ChildTestSteps[0]).LoadTestPlan(); ((TestPlanReference)tp.ChildTestSteps[1]).LoadTestPlan(); Log.RemoveListener(listener); Assert.That(listener.ErrorMessage.Count, Is.EqualTo(0)); Assert.That(listener.WarningMessage.Count, Is.EqualTo(0)); } } finally { foreach (var tempfile in tempfiles) if (File.Exists(tempfile)) File.Delete(tempfile); } } [Test] public void TestPlanReferenceAnnotationTest() { var planname = Guid.NewGuid().ToString() + ".TestPlan"; { var innerplan = new TestPlan(); var step = new DelayStep(); innerplan.Steps.Add(step); innerplan.ExternalParameters.Add(step, TypeData.GetTypeData(step).GetMember(nameof(DelayStep.DelaySecs))); innerplan.Save(planname); } try { var outerplan = new TestPlan(); var tpr = new TestPlanReference(); outerplan.Steps.Add(tpr); { var annotation = AnnotationCollection.Annotate(tpr); var loadTestPlanMem = annotation.GetMember("LoadTestPlan"); var enabled = loadTestPlanMem.Get(); Assert.IsFalse(enabled.IsEnabled); var filePathMem = annotation.GetMember("Filepath"); var fpEnabled = filePathMem.Get(); Assert.IsTrue(fpEnabled?.IsEnabled ?? true); var fpString = filePathMem.Get(); fpString.Value = planname; annotation.Write(); annotation.Read(); Assert.IsTrue(enabled.IsEnabled); } tpr.Filepath.Text = planname; tpr.LoadTestPlan(); { var annotation = AnnotationCollection.Annotate(tpr); var delaymem = annotation.GetMember("Time Delay"); Assert.IsNotNull(delaymem); } } finally { File.Delete(planname); } } [Test] public void ArrayStepNameAnnotationTest() { var step1 = new DelayStep() { Name = "A" }; var step2 = new DelayStep() { Name = "A" }; var step3 = new DelayStep() { Name = "A" }; var steps = new ITestStep[] { step1, step2, step3 }; Assert.AreEqual(GetValue(steps), "A"); step2.Name = "B"; Assert.IsNull(GetValue(steps)); static string GetValue(ITestStep[] steps) { var annotations = AnnotationCollection.Annotate(steps); var membersAnnotation = annotations.Get(); var memberAnnotation = membersAnnotation.Members.First(m => m.Name == "Step Name"); var stepNameAnnotation = memberAnnotation.Get(); Assert.DoesNotThrow(() => _ = stepNameAnnotation.Value); return stepNameAnnotation.Value; } } [Test] public void SweepLoopEnabledTest() { var plan = new TestPlan(); var sweep = new SweepLoop(); var prog = new ProcessStep(); plan.ChildTestSteps.Add(sweep); sweep.ChildTestSteps.Add(prog); sweep.SweepParameters.Add(new SweepParam(new [] {TypeData.FromType(prog.GetType()).GetMember(nameof(ProcessStep.RegularExpressionPattern))})); var a = AnnotationCollection.Annotate(sweep); a.Read(); var a2 = a.GetMember(nameof(SweepLoop.SweepParameters)); var col = a2.Get(); var new1 = col.NewElement(); col.AnnotatedElements = col.AnnotatedElements.Append(col.NewElement(), new1); var enabledmem = new1.Get().Members.Last(); var boolmember = enabledmem.Get().Members.First(); var val = boolmember.Get(); val.Value = true; a.Write(); var sweepParam = sweep.SweepParameters.FirstOrDefault(); var en = (Enabled)sweepParam.Values[1]; Assert.IsTrue(en.IsEnabled); // from val.Value = true. } public class ErrorMetadataDutResource : Dut { [MetaData(true)] public double ErrorProperty { get; set; } = -5; public ErrorMetadataDutResource() { this.Rules.Add(() => ErrorProperty > 0, "Error property must be > 0", nameof(ErrorProperty)); } } [Test] public void MetadataErrorAnnotation() { var p = new MetadataPromptObject() {Resources = new Dut[] {new ErrorMetadataDutResource()}}; var test = AnnotationCollection.Annotate(p); var forwarded = test.Get(); var member = forwarded.Forwarded.FirstOrDefault(x => x.Get().Member.Name == nameof(ErrorMetadataDutResource.ErrorProperty)); var error = member.GetAll().SelectMany(x => x.Errors).ToArray(); Assert.AreEqual(1, error.Length); void checkValue(string value, int errors) { member.Get().Value = value; test.Write(); test.Read(); error = member.GetAll().SelectMany(x => x.Errors).ToArray(); Assert.AreEqual(errors, error.Length); } for (int i = 0; i < 4; i++) { checkValue("2.0", 0); // no errors. checkValue("-2.0", 1); // validation failed. checkValue("invalid", 2); // validation failed + parse error. } } public class ClassWithMacroString { public MacroString String { get; set; } = new MacroString(); } [Test] public void TestClassWithMacroStringMultiSelect() { var elements = new[] {new ClassWithMacroString(), new ClassWithMacroString()}; var elems = AnnotationCollection.Annotate(elements); var sv = elems.GetMember(nameof(ClassWithMacroString.String)).Get(); sv.Value = "test"; elems.Write(); Assert.IsNotNull(sv.Value); // Check that multi-edit works. foreach (var elem in elements) { Assert.AreEqual("test", elem.String.ToString()); } // verify that the same MacroPath instance is not being used. var elem2 = AnnotationCollection.Annotate(elements[0]); var sv2 = elem2.GetMember(nameof(ClassWithMacroString.String)).Get(); sv2.Value = "test2"; elem2.Write(); Assert.AreEqual("test", elements[1].String.ToString()); Assert.AreEqual("test2", elements[0].String.ToString()); elems.Read(); Assert.IsNull(sv.Value); } public class StepWithMacroString : TestStep { public string A { get; set; } = "Hello"; public MacroString MacroString { get; set; } public override void Run() { } } [Test] public void TestMacroStringContext() { var plan = new TestPlan(); var step = new StepWithMacroString(); plan.ChildTestSteps.Add(step); step.MacroString = new MacroString(step); Assert.IsTrue(step.MacroString.Context == plan.ChildTestSteps.First(), "MacroString Context is set."); plan = new TapSerializer().Clone(plan) as TestPlan; step = plan.ChildTestSteps.First() as StepWithMacroString; Assert.IsTrue(step.MacroString.Context == plan.ChildTestSteps.First(), "MacroString Context is set."); } public class ClassWithMethodAnnotation { public int TimesCalled { private set; get; } [Browsable(true)] public void CallableMethod() => TimesCalled += 1; } [Test] public void TestMultiSelectCallMethodAnnotation() { var elements = new[] {new ClassWithMethodAnnotation(), new ClassWithMethodAnnotation()}; var annotation = AnnotationCollection.Annotate(elements); var method = annotation.GetMember(nameof(ClassWithMethodAnnotation.CallableMethod)).Get(); method.Invoke(); foreach(var elem in elements) Assert.AreEqual(1, elem.TimesCalled); } public class ClassWithListOfString : TestStep { public class NestedObject { public bool State { get; set; } public IEnumerable Avail => Enumerable.Range(State ? 5 : 0, 5); [AvailableValues(nameof(Avail))] public int SelectedInt { get; set; } } public List List { get; set; } = new List{"A", "B"}; [AvailableValues(nameof(List))] public string Selected { get; set; } public List List2 { get; set; } = new List{}; public override void Run() { } } [Test] public void ListOfObjectAnnotation() { var obj = new ClassWithListOfString(); var a = AnnotationCollection.Annotate(obj); var member = a.GetMember(nameof(ClassWithListOfString.List2)); var col = member.Get(); var newelem = col.NewElement(); col.AnnotatedElements = new[] { newelem }; a.Write(); a.Read(); // if we take the first element here, then previously updates would be lost after the first read. // - at least if reading from the point of view of an available values proxy. newelem = col.AnnotatedElements.FirstOrDefault(); newelem.GetMember("State").Get().Value = "true"; a.Write(); a.Read(); var selected = newelem.GetMember("SelectedInt").Get().SelectedValue; var vals = newelem.GetMember("SelectedInt").Get().AvailableValues; // verify that the available values corresponds to NestedObject.State = true; Assert.IsTrue(vals.Select(x => x.Source).SequenceEqual(obj.List2[0].Avail.Cast())); } [Test] public void ListOfStringAnnotation() { var obj = new ClassWithListOfString(); var a = AnnotationCollection.Annotate(obj); var member = a.GetMember(nameof(ClassWithListOfString.List)); var col = member.Get(); var newelem = col.NewElement(); Assert.IsTrue(newelem.Get().ReflectionInfo.DescendsTo(typeof(string))); Assert.IsNotNull(newelem.Get().Value); } [Test] public void ListOfStringAnnotation2() { var obj = new ClassWithListOfString(); var targetObject = new SequenceStep(); var obj2 = new ClassWithListOfString(); targetObject.ChildTestSteps.Add(obj); targetObject.ChildTestSteps.Add(obj2); obj2.List.Add("C"); var selectedMember = TypeData.GetTypeData(obj).GetMember(nameof(ClassWithListOfString.Selected)); selectedMember.Parameterize(targetObject, obj, selectedMember.Name); Assert.Throws(() => selectedMember.Parameterize(targetObject, obj, selectedMember.Name)); selectedMember.Parameterize(targetObject, obj2, selectedMember.Name); Assert.Throws(() => selectedMember.Parameterize(targetObject, obj2, selectedMember.Name)); var b = AnnotationCollection.Annotate(targetObject); var avail = b.GetMember(selectedMember.Name).Get(); Assert.AreEqual(2, avail.AvailableValues.Cast().Count()); } [Test] public void ListOfStringAnnotationTpr() { var tp = new TestPlan(); var stepa = new ClassWithListOfString(); var selectedMember = TypeData.GetTypeData(stepa).GetMember(nameof(ClassWithListOfString.Selected)); var stepb = new ClassWithListOfString(); tp.ChildTestSteps.Add(stepa); tp.ChildTestSteps.Add(stepb); selectedMember.Parameterize(tp, stepa, selectedMember.Name); selectedMember.Parameterize(tp, stepb, selectedMember.Name); var name = Guid.NewGuid().ToString() + ".TapPlan"; tp.Save(name); var plan2 = new TestPlan(); var tpr = new TestPlanReference(); plan2.ChildTestSteps.Add(tpr); tpr.Filepath.Text = name; tpr.LoadTestPlan(); File.Delete(name); var member = AnnotationCollection.Annotate(tpr).GetMember(selectedMember.Name); var avail = member.Get(); Assert.AreEqual(2, avail.AvailableValues.Cast().Count()); } class MenuTestUserInterface : IUserInputInterface, IUserInterface { [Flags] public enum Mode { Standard = 1, Rename = 2, Create = 4, TestPlan = 8, Remove = 16, ExpectNoRename = 32, RenameBlank = 64, Merge = 128, Error = 256, Cancel = 512 } public bool WasInvoked; public string SelectName { get; set; } public Mode SelectedMode; public string LastError = ""; public string ErrorString { get; set; } public void RequestUserInput(object dataObject, TimeSpan Timeout, bool modal) { var datas = AnnotationCollection.Annotate(dataObject); var selectedName = datas.GetMember("SelectedName"); var message = datas.GetMember("Message"); var response = datas.GetMember("Response"); var resp = response.Get(); if (SelectedMode.HasFlag(Mode.Cancel)) { resp.SelectedValue = resp.AvailableValues.First(x => x.Get().Value == "Cancel"); } selectedName.Get().Value = SelectName; var scope = datas.GetMember("Scope"); var avail = scope.Get(); var values = avail.AvailableValues.ToArray(); avail.SelectedValue = avail.AvailableValues.First(); // sequence step! if (SelectedMode.HasFlag(Mode.TestPlan)) { avail.SelectedValue = avail.AvailableValues.Last(); } if (SelectedMode.HasFlag(Mode.Remove)) { var settings = datas.GetMember("Settings"); settings.Get().SelectedValues = Array.Empty(); } Assert.IsTrue(values.Length > 0); datas.Write(); if (SelectedMode.HasFlag(Mode.Rename)) { var msg = message.Get().Value; Assert.AreEqual(false == SelectedMode.HasFlag(Mode.ExpectNoRename), msg.Contains("Rename ")); } if (SelectedMode.HasFlag(Mode.RenameBlank)) { var error = selectedName.Get().Errors.First(); Assert.AreEqual("Name cannot be left empty.", error); } if (SelectedMode == (Mode.Create|Mode.TestPlan)) { var msg = message.Get().Value; Assert.IsTrue(msg.Contains("Create new parameter ")); } LastError = message.Get()?.Errors.FirstOrDefault(); if (SelectedMode == (Mode.Merge|Mode.TestPlan)) { var msg = message.Get().Value; Assert.IsTrue(msg.Contains("Merge with an existing parameter ")); } if (SelectedMode.HasFlag(Mode.Error)) { Assert.IsTrue(LastError != null); if(ErrorString != null) Assert.IsTrue(LastError.Contains(ErrorString)); } WasInvoked = true; } public void NotifyChanged(object obj, string property) { } } [Test] public void TestReparameterize() { var testplan = new TestPlan(); var seq = new SequenceStep(); var delay = new DelayStep(); seq.ChildTestSteps.Add(delay); testplan.ChildTestSteps.Add(seq); seq.ChildTestSteps.Add(new VerdictStep() { Verdict = Verdict.Pass }); var timeDelay = AnnotationCollection.Annotate(delay).GetMember("DelaySecs"); var menu = timeDelay.Get(); var icons = menu.MenuItems.ToLookup(x => x.Get()?.IconName ?? ""); var parameterize = icons[IconNames.ParameterizeOnParent].First(); var unparameterize = icons[IconNames.Unparameterize].First(); void invoke(AnnotationCollection a) { a.Get().Invoke(); } void run() { var planRun = testplan.Execute(); Assert.AreEqual(Verdict.Pass, planRun.Verdict); } { // Verify nothing breaks when parameterizing, unparameterizing, and reparameterizing run(); invoke(parameterize); run(); invoke(unparameterize); run(); invoke(parameterize); run(); invoke(unparameterize); run(); } } [Test] public void MenuAnnotationTest() { var currentUserInterface = UserInput.Interface; var menuInterface = new MenuTestUserInterface(); UserInput.SetInterface(menuInterface); try { var plan = new TestPlan(); var sequenceRoot = new SequenceStep(); var sequence = new SequenceStep(); var step = new DelayStep(); plan.Steps.Add(sequenceRoot); sequenceRoot.ChildTestSteps.Add(sequence); var step2 = new DelayStep(); sequenceRoot.ChildTestSteps.Add(step); sequence.ChildTestSteps.Add(step2); { // basic functionalities test var member = AnnotationCollection.Annotate(step2).GetMember(nameof(DelayStep.DelaySecs)); var menu = member.Get(); var items = menu.MenuItems; var icons = items.ToLookup(item => item.Get()?.IconName ?? ""); var parameterizeOnTestPlan = icons[IconNames.ParameterizeOnTestPlan].First(); Assert.IsNotNull(parameterizeOnTestPlan); // invoking this method should var method = parameterizeOnTestPlan.Get(); method.Invoke(); Assert.IsNotNull(plan.ExternalParameters.Get("Parameters \\ Time Delay")); var unparameterize = icons[IconNames.Unparameterize].First(); unparameterize.Get().Invoke(); Assert.IsNull(plan.ExternalParameters.Get("Parameters \\ Time Delay")); var createOnParent = icons[IconNames.ParameterizeOnParent].First(); Assert.IsNotNull(createOnParent); createOnParent.Get().Invoke(); Assert.IsNotNull(TypeData.GetTypeData(sequence).GetMember("Parameters \\ Time Delay")); unparameterize.Get().Invoke(); Assert.IsNull(TypeData.GetTypeData(sequence).GetMember("Parameters \\ Time Delay")); menuInterface.SelectName = "A"; var parameterize = icons[IconNames.Parameterize].First(); parameterize.Get().Invoke(); Assert.IsTrue(menuInterface.WasInvoked); { // verify that the right icon annotation now appears. var member2 = AnnotationCollection.Annotate(step2).GetMember(nameof(DelayStep.DelaySecs)); var parameterized = member2.Get(); Assert.IsNotNull(parameterized); var disabled = member2.GetAll().Any(x => x.IsEnabled == false); Assert.IsTrue(disabled); } var newParameter = TypeData.GetTypeData(sequence).GetMember("A"); Assert.IsNotNull(newParameter); unparameterize.Get().Invoke(); Assert.IsNull(TypeData.GetTypeData(sequence).GetMember("A")); parameterize.Get().Invoke(); var editParameter = AnnotationCollection.Annotate(sequence).GetMember("A").Get() .MenuItems .FirstOrDefault(x => x.Get()?.IconName == IconNames.EditParameter); menuInterface.SelectName = " "; menuInterface.SelectedMode = MenuTestUserInterface.Mode.RenameBlank; editParameter.Get().Invoke(); Assert.IsNotNull(TypeData.GetTypeData(sequence).GetMember("A")); Assert.IsNull(TypeData.GetTypeData(sequence).GetMember("")); menuInterface.SelectName = "B"; menuInterface.SelectedMode = MenuTestUserInterface.Mode.Rename; editParameter.Get().Invoke(); var bMember = TypeData.GetTypeData(sequence).GetMember("B"); Assert.IsNull(TypeData.GetTypeData(sequence).GetMember("A")); Assert.IsNotNull(bMember); menuInterface.SelectedMode |= MenuTestUserInterface.Mode.ExpectNoRename; editParameter = AnnotationCollection.Annotate(sequence).GetMember("B").Get() .MenuItems .FirstOrDefault(x => x.Get()?.IconName == IconNames.EditParameter); editParameter.Get().Invoke(); Assert.IsTrue(object.ReferenceEquals(bMember, TypeData.GetTypeData(sequence).GetMember("B"))); unparameterize.Get().Invoke(); Assert.IsNull(TypeData.GetTypeData(sequence).GetMember("B")); menuInterface.SelectedMode = MenuTestUserInterface.Mode.Create | MenuTestUserInterface.Mode.TestPlan; parameterize.Get().Invoke(); { var member2 = AnnotationCollection.Annotate(plan).GetMember("B"); { // verify that the right icon annotation now appears. var parameter = member2.GetAll() .First(x => x.IconName == IconNames.EditParameter); Assert.IsNotNull(parameter.Action.Get()); } var edit = member2.Get().MenuItems.FirstOrDefault(x => x.Get()?.IconName == IconNames.EditParameter); menuInterface.SelectedMode = MenuTestUserInterface.Mode.Rename| MenuTestUserInterface.Mode.TestPlan; menuInterface.SelectName = "C"; edit.Get().Invoke(); member2 = AnnotationCollection.Annotate(plan).GetMember("C"); edit = member2.Get().MenuItems.FirstOrDefault(x => x.Get()?.IconName == IconNames.EditParameter); menuInterface.SelectedMode = MenuTestUserInterface.Mode.Remove; edit.Get().Invoke(); Assert.IsNull(AnnotationCollection.Annotate(plan).GetMember("C")); } } { // test multi-select var memberMulti = AnnotationCollection.Annotate(new[] {step, step2}) .GetMember(nameof(DelayStep.DelaySecs)); var menuMulti = memberMulti.Get(); Assert.IsNotNull(menuMulti); var icons2 = menuMulti.MenuItems.ToLookup(x => x.Get()?.IconName ?? ""); var parmeterizeOnTestPlanMulti = icons2[IconNames.ParameterizeOnTestPlan].First(); parmeterizeOnTestPlanMulti.Get().Invoke(); Assert.AreEqual(2, plan.ExternalParameters.Entries.FirstOrDefault().Properties.Count()); { // check that the right icon appeared. var memberMulti2 = AnnotationCollection.Annotate(new[] {step, step2}) .GetMember(nameof(DelayStep.DelaySecs)); bool parameterizedIcon = memberMulti2.GetAll().Any(x => x.IconName == IconNames.Parameterized); Assert.IsTrue(parameterizedIcon); } var unparmeterizePlanMulti = icons2[IconNames.Unparameterize].First(); unparmeterizePlanMulti.Get().Invoke(); Assert.IsNull(plan.ExternalParameters.Entries.FirstOrDefault()); var parmeterizeOnParentMulti = icons2[IconNames.ParameterizeOnParent].First(); Assert.IsFalse(parmeterizeOnParentMulti.Get().IsEnabled); } { // Test Plan Enabled Items Locked var annotation = AnnotationCollection.Annotate(step); var menu = annotation.GetMember(nameof(DelayStep.DelaySecs)) .Get(); var icons = menu.MenuItems.ToLookup(x => x.Get()?.IconName ?? ""); icons[IconNames.ParameterizeOnTestPlan].First().Get().Invoke(); annotation.Read(); Assert.IsFalse(icons[IconNames.ParameterizeOnTestPlan].First().Get().IsEnabled); Assert.IsFalse(icons[IconNames.Parameterize].First().Get().IsEnabled); Assert.IsFalse(icons[IconNames.ParameterizeOnParent].First().Get().IsEnabled); Assert.IsTrue(icons[IconNames.Unparameterize].First().Get().IsEnabled); Assert.IsFalse(icons[IconNames.EditParameter].First().Get().IsEnabled); Assert.IsFalse(icons[IconNames.RemoveParameter].First().Get().IsEnabled); var planAnnotation = AnnotationCollection.Annotate(plan); var planMenu = planAnnotation.GetMember("Parameters \\ Time Delay") .Get(); var planIcons = planMenu.MenuItems.ToLookup(x => x.Get()?.IconName ?? ""); Assert.IsFalse(planIcons[IconNames.ParameterizeOnTestPlan].First().Get().IsEnabled); Assert.IsFalse(planIcons[IconNames.Parameterize].First().Get().IsEnabled); Assert.IsTrue(planIcons[IconNames.EditParameter].First().Get().IsEnabled); Assert.IsFalse(planIcons[IconNames.ParameterizeOnParent].First().Get().IsEnabled); Assert.IsFalse(planIcons[IconNames.Unparameterize].First().Get().IsEnabled); Assert.IsTrue(planIcons[IconNames.RemoveParameter].First().Get().IsEnabled); plan.Locked = true; menu = AnnotationCollection.Annotate(step).GetMember(nameof(DelayStep.DelaySecs)) .Get(); icons = menu.MenuItems.ToLookup(x => x.Get()?.IconName ?? ""); Assert.IsFalse(icons[IconNames.ParameterizeOnTestPlan].First().Get().IsEnabled); Assert.IsFalse(icons[IconNames.Parameterize].First().Get().IsEnabled); Assert.IsFalse(icons[IconNames.ParameterizeOnParent].First().Get().IsEnabled); Assert.IsFalse(icons[IconNames.Unparameterize].First().Get().IsEnabled); Assert.IsFalse(icons[IconNames.EditParameter].First().Get().IsEnabled); planAnnotation = AnnotationCollection.Annotate(plan); planMenu = planAnnotation.GetMember("Parameters \\ Time Delay") .Get(); planIcons = planMenu.MenuItems.ToLookup(x => x.Get()?.IconName ?? ""); Assert.IsFalse(planIcons[IconNames.ParameterizeOnTestPlan].First().Get().IsEnabled); Assert.IsFalse(planIcons[IconNames.Parameterize].First().Get().IsEnabled); Assert.IsFalse(planIcons[IconNames.EditParameter].First().Get().IsEnabled); Assert.IsFalse(planIcons[IconNames.ParameterizeOnParent].First().Get().IsEnabled); Assert.IsFalse(planIcons[IconNames.Unparameterize].First().Get().IsEnabled); Assert.IsFalse(planIcons[IconNames.RemoveParameter].First().Get().IsEnabled); } { // remove parameter plan.Locked = false; var planAnnotation = AnnotationCollection.Annotate(plan); var menu = planAnnotation.GetMember("Parameters \\ Time Delay").Get(); var removeItem = menu.MenuItems.First(x => x.Get()?.IconName == IconNames.RemoveParameter); removeItem.Get().Invoke(); planAnnotation = AnnotationCollection.Annotate(plan); // after removing there is not Time Delay parameter.. Assert.IsNull(planAnnotation.GetMember("Parameters \\ Time Delay")); } {// Break Conditions var member = AnnotationCollection.Annotate(step2).GetMember("BreakConditions"); Assert.NotNull(member); var menu = member.Get(); Assert.NotNull(menu); var parameterize = menu.MenuItems.FirstOrDefault(x => x.Get()?.IconName == IconNames.ParameterizeOnTestPlan); Assert.IsTrue(parameterize.Get().IsVisible); Assert.AreEqual(1, TypeData.GetTypeData(plan).GetMembers().Count(x => x.Name.Contains("BreakConditions") || x.Name.Contains("BreakConditions"))); parameterize.Get().Invoke(); Assert.AreEqual(2, TypeData.GetTypeData(plan).GetMembers().Count(x => x.Name.Contains("Break Conditions") || x.Name.Contains("BreakConditions"))); } } finally { UserInput.SetInterface(currentUserInterface as IUserInputInterface); } } [Test] public void MenuAnnotationTest4() { var currentUserInterface = UserInput.Interface; var menuInterface = new MenuTestUserInterface(); UserInput.SetInterface(menuInterface); try { var plan = new TestPlan(); var sequence = new SequenceStep(); var step = new DelayStep(); plan.Steps.Add(sequence); sequence.ChildTestSteps.Add(step); { var p1 = TypeData.GetTypeData(step).GetMember(nameof(step.DelaySecs)).Parameterize(sequence, step, "A"); p1.Parameterize(plan, sequence, p1.Name); var editParameterIcon = AnnotationCollection.Annotate(sequence).GetMember(p1.Name).Get().MenuItems .First(x => x.Get()?.IconName == IconNames.EditParameter); menuInterface.SelectedMode = MenuTestUserInterface.Mode.TestPlan | MenuTestUserInterface.Mode.Error; menuInterface.SelectName = p1.Name; editParameterIcon.Get().Invoke(); } } finally { UserInput.SetInterface(currentUserInterface as IUserInputInterface); } } [Test] public void MenuAnnotationTest2() { var currentUserInterface = UserInput.Interface; var menuInterface = new MenuTestUserInterface(); UserInput.SetInterface(menuInterface); try { var plan = new TestPlan(); var delay = new DelayStep(); plan.Steps.Add(delay); { // basic functionalities test var member = AnnotationCollection.Annotate(delay).GetMember(nameof(DelayStep.DelaySecs)); var menu = member.Get(); var items = menu.MenuItems; var icons = items.ToLookup(item => item.Get()?.IconName ?? ""); var parameterizeOnTestPlan = icons[IconNames.ParameterizeOnTestPlan].First(); Assert.IsNotNull(parameterizeOnTestPlan); // invoking this method should var method = parameterizeOnTestPlan.Get(); method.Invoke(); Assert.IsNotNull(plan.ExternalParameters.Get("Parameters \\ Time Delay")); member = AnnotationCollection.Annotate(delay).GetMember(nameof(DelayStep.DelaySecs)); menu = member.Get(); items = menu.MenuItems; icons = items.ToLookup(item => item.Get()?.IconName ?? ""); parameterizeOnTestPlan = icons[IconNames.ParameterizeOnTestPlan].First(); Assert.IsNotNull(parameterizeOnTestPlan); // This fails, which it should not. Assert.IsFalse(parameterizeOnTestPlan.Get().IsEnabled); } } finally { UserInput.SetInterface(currentUserInterface as IUserInputInterface); } } [Test] public void MenuAnnotationTest3() { var currentUserInterface = UserInput.Interface; var menuInterface = new MenuTestUserInterface(); UserInput.SetInterface(menuInterface); try { const string doubleTimeDelay = "Parameters \\ Time Delay"; const string byteTimeDelay = "Parameters \\ Byte Time Delay"; const string shortTimeDelay = "Parameters \\ Short Time Delay"; var plan = new TestPlan(); var delay1 = new DelayStep(); var delay2 = new ByteDelayStep(); var delay3 = new ShortDelayStep(); plan.Steps.Add(delay1); plan.Steps.Add(delay2); plan.Steps.Add(delay3); { // basic functionalities test var member = AnnotationCollection.Annotate(delay1).GetMember(nameof(DelayStep.DelaySecs)); var menu = member.Get(); var items = menu.MenuItems; var icons = items.ToLookup(item => item.Get()?.IconName ?? ""); var parameterizeOnTestPlan = icons[IconNames.ParameterizeOnTestPlan].First(); Assert.IsNotNull(parameterizeOnTestPlan); var method = parameterizeOnTestPlan.Get(); method.Invoke(); Assert.IsNotNull(plan.ExternalParameters.Get(doubleTimeDelay)); member = AnnotationCollection.Annotate(delay2).GetMember(nameof(ByteDelayStep.DelaySecs)); menu = member.Get(); items = menu.MenuItems; icons = items.ToLookup(item => item.Get()?.IconName ?? ""); parameterizeOnTestPlan = icons[IconNames.ParameterizeOnTestPlan].First(); Assert.IsNotNull(parameterizeOnTestPlan); method = parameterizeOnTestPlan.Get(); method.Invoke(); Assert.IsNotNull(plan.ExternalParameters.Get(byteTimeDelay)); member = AnnotationCollection.Annotate(delay3).GetMember(nameof(ShortDelayStep.DelaySecs)); menu = member.Get(); items = menu.MenuItems; icons = items.ToLookup(item => item.Get()?.IconName ?? ""); parameterizeOnTestPlan = icons[IconNames.ParameterizeOnTestPlan].First(); Assert.IsNotNull(parameterizeOnTestPlan); method = parameterizeOnTestPlan.Get(); method.Invoke(); Assert.IsNotNull(plan.ExternalParameters.Get(shortTimeDelay)); var editParameter = AnnotationCollection.Annotate(plan).GetMember(byteTimeDelay).Get() .MenuItems .FirstOrDefault(x => x.Get()?.IconName == IconNames.EditParameter); // Try to merge byte time delay and time delay parameters menuInterface.SelectName = doubleTimeDelay; menuInterface.SelectedMode = MenuTestUserInterface.Mode.Rename | MenuTestUserInterface.Mode.Cancel; editParameter.Get().Invoke(); Assert.IsNull(menuInterface.LastError); // Try to merge byte time delay and short time delay parameters, overflow exception occurs editParameter = AnnotationCollection.Annotate(plan).GetMember(shortTimeDelay).Get() .MenuItems .FirstOrDefault(x => x.Get()?.IconName == IconNames.EditParameter); menuInterface.SelectName = byteTimeDelay; menuInterface.SelectedMode = MenuTestUserInterface.Mode.Rename | MenuTestUserInterface.Mode.Cancel; editParameter.Get().Invoke(); Assert.IsNull(menuInterface.LastError); menuInterface.SelectedMode = MenuTestUserInterface.Mode.Rename; // Try to merge byte and short time delay parameters, this is allowed delay3.DelaySecs = 12; editParameter.Get().Invoke(); Assert.IsNull(menuInterface.LastError); // Try to set an overflowed value member = AnnotationCollection.Annotate(plan).GetMember(byteTimeDelay); member.SetValue(50000); var memberParam = member.Get(); Assert.IsNotNull(memberParam.Errors); } } finally { UserInput.SetInterface(currentUserInterface as IUserInputInterface); } } public class ShortDelayStep : TestStep { [Display("Short Time Delay")] public short DelaySecs { get; set; } = 222; public override void Run() { TapThread.Sleep(Time.FromSeconds(DelaySecs)); } } public class ByteDelayStep : TestStep { [Display("Byte Time Delay")] public sbyte DelaySecs { get; set; } = 3; public override void Run() { TapThread.Sleep(Time.FromSeconds(DelaySecs)); } } public class TwoDelayStep : TestStep { public double DelaySecs { get; set; } public double DelaySecs2 { get; set; } public override void Run() { } } [Test] public void ParameterizeTest() { var plan = new TestPlan(); var delayStep = new DelayStep(); plan.ChildTestSteps.Add(delayStep); // Check can parameterize var member = AnnotationCollection.Annotate(delayStep).GetMember(nameof(DelayStep.DelaySecs)); var menu = member.Get(); var menuItems = menu.MenuItems; var icons = menuItems.ToLookup(item => item.Get()?.IconName ?? ""); var parameterizeIcon = icons[IconNames.Parameterize].First(); Assert.IsTrue(parameterizeIcon.Get().IsVisible); parameterizeIcon = icons[IconNames.Unparameterize].First(); Assert.IsFalse(parameterizeIcon.Get().IsVisible); // Parameterize var delayMember = TypeData.GetTypeData(delayStep).GetMember(nameof(DelayStep.DelaySecs)); delayMember.Parameterize(plan, delayStep, "Delay"); // Check can parameterize member = AnnotationCollection.Annotate(delayStep).GetMember(nameof(DelayStep.DelaySecs)); menu = member.Get(); menuItems = menu.MenuItems; icons = menuItems.ToLookup(item => item.Get()?.IconName ?? ""); parameterizeIcon = icons[IconNames.Parameterize].First(); Assert.IsFalse(parameterizeIcon.Get().IsVisible); parameterizeIcon = icons[IconNames.Unparameterize].First(); Assert.IsTrue(parameterizeIcon.Get().IsVisible); // Set read only delayStep.IsReadOnly = true; // Check can parameterize member = AnnotationCollection.Annotate(delayStep).GetMember(nameof(DelayStep.DelaySecs)); menu = member.Get(); menuItems = menu.MenuItems; icons = menuItems.ToLookup(item => item.Get()?.IconName ?? ""); parameterizeIcon = icons[IconNames.Unparameterize].First(); Assert.IsFalse(parameterizeIcon.Get().IsVisible); } [Test] public void TestMergedInputVerdict([Values(true, false)]bool availableProxy) { var dialog = new DialogStep(); var if1 = new IfStep(); var if2 = new IfStep(); var if3 = new IfStep(); var plan = new TestPlan() { ChildTestSteps = { dialog, if1, if2, if3 } }; ITestStep[] merged = { if1, if2, if3 }; void setInputVerdict(AnnotationCollection a, ITestStep targetStep) { var member = a.GetMember(nameof(if1.InputVerdict)); if (availableProxy) { var avail = member.Get(); var sel = avail.AvailableValues.First(av => av.GetMember("Step").Get().Value == targetStep); avail.SelectedValue = sel; } else { var input = new Input() { Property = TypeData.FromType(typeof(DialogStep)).GetMember(nameof(dialog.Verdict)), Step = targetStep }; member.Get().Value = input; } a.Write(); a.Read(); } { // Test multiselect editing var a = AnnotationCollection.Annotate(merged); setInputVerdict(a, dialog); Assert.AreSame(dialog, if1.InputVerdict.Step); Assert.AreSame(dialog, if2.InputVerdict.Step); Assert.AreSame(dialog, if3.InputVerdict.Step); } { // Unset all var a = AnnotationCollection.Annotate(merged); setInputVerdict(a, null); Assert.AreSame(null, if1.InputVerdict.Step); Assert.AreSame(null, if2.InputVerdict.Step); Assert.AreSame(null, if3.InputVerdict.Step); } { // Set if1 independent of if2 var a = AnnotationCollection.Annotate(if1); setInputVerdict(a, if2); Assert.AreSame(if2, if1.InputVerdict.Step); Assert.AreSame(null, if2.InputVerdict.Step); Assert.AreSame(null, if3.InputVerdict.Step); } { // Set if2 independent of if1 var a = AnnotationCollection.Annotate(if2); setInputVerdict(a, if1); Assert.AreSame(if2, if1.InputVerdict.Step); Assert.AreSame(if1, if2.InputVerdict.Step); Assert.AreSame(null, if3.InputVerdict.Step); } { // Set all back to dialog var a = AnnotationCollection.Annotate(merged); setInputVerdict(a, dialog); Assert.AreSame(dialog, if1.InputVerdict.Step); Assert.AreSame(dialog, if2.InputVerdict.Step); Assert.AreSame(dialog, if3.InputVerdict.Step); } { // Unset all var a = AnnotationCollection.Annotate(merged); setInputVerdict(a, null); Assert.AreSame(null, if1.InputVerdict.Step); Assert.AreSame(null, if2.InputVerdict.Step); Assert.AreSame(null, if3.InputVerdict.Step); } } public class DoubleOut : TestStep { [Output] public double Out { get; set; } = 123; public override void Run() { } } public class DoubleIn : TestStep { public Input In { get; set; } = new(); public override void Run() { } } [Test] public void TestSerializeMergedInput() { var plan = new TestPlan(); var out1 = new DoubleOut(); var seq = new SequenceStep(); var in1 = new DoubleIn { Name = "In 1" }; var in2 = new DoubleIn { Name = "In 2" }; var in3 = new DoubleIn { Name = "In 3" }; plan.ChildTestSteps.AddRange([out1, seq]); ITestStep[] merged = [in1, in2, in3]; seq.ChildTestSteps.AddRange(merged); { // Parameterize `In` on the parent sequence step var a = AnnotationCollection.Annotate(merged); var mem = a.GetMember(nameof(in1.In)); var icon = mem.Get().MenuItems .FirstOrDefault(m => m.Get().IconName == IconNames.ParameterizeOnParent); var method = icon.Get(); method.Invoke(); a.Write(); a.Read(); } { // Assign the parameterized member to the output from `Out` var a = AnnotationCollection.Annotate(seq); var mem = a.GetMember("Parameters \\ In"); var avail = mem.Get(); avail.SelectedValue = avail.AvailableValues.LastOrDefault(); a.Write(); a.Read(); } { // Verify the merged inputs are wired up correctly in this session Input parameter = (Input)TypeData.GetTypeData(seq).GetMember("Parameters \\ In").GetValue(seq); Assert.AreEqual(parameter.Step.Id, out1.Id); Assert.AreEqual(in1.In.Step.Id, out1.Id); Assert.AreEqual(in2.In.Step.Id, out1.Id); Assert.AreEqual(in3.In.Step.Id, out1.Id); } { // Verify the merged inputs are wired up correctly after reloading var ser = new TapSerializer() { IgnoreErrors = false, ThrowOnErrors = true, }; var str = ser.SerializeToString(plan); var plan2 = (TestPlan)ser.DeserializeFromString(str); out1 = (DoubleOut)plan2.ChildTestSteps[0]; seq = (SequenceStep)plan2.ChildTestSteps[1]; in1 = (DoubleIn)seq.ChildTestSteps[0]; in2 = (DoubleIn)seq.ChildTestSteps[1]; in3 = (DoubleIn)seq.ChildTestSteps[2]; Input parameter = (Input)TypeData.GetTypeData(seq).GetMember("Parameters \\ In").GetValue(seq); Assert.AreEqual(parameter.Step.Id, out1.Id); Assert.AreEqual(in1.In.Step.Id, out1.Id); Assert.AreEqual(in2.In.Step.Id, out1.Id); Assert.AreEqual(in3.In.Step.Id, out1.Id); } } [Test] public void TestMergedInputVerdict2([Values(true, false)] bool availableProxy) { var repeat1 = new RepeatStep() { Action = RepeatStep.RepeatStepAction.While }; var repeat2 = new RepeatStep() { Action = RepeatStep.RepeatStepAction.While }; var plan = new TestPlan() { ChildTestSteps = { repeat1, repeat2 } }; ITestStep[] merged = { repeat1, repeat2 }; void setInputVerdict(AnnotationCollection a, ITestStep targetStep) { var member = a.GetMember(nameof(repeat1.TargetStep)); if (availableProxy) { var avail = member.Get(); var sel = avail.AvailableValues.FirstOrDefault(av => av.Get().Value == targetStep); avail.SelectedValue = sel; } else { member.Get().Value = targetStep; } a.Write(); a.Read(); } { // Test multiselect editing var a = AnnotationCollection.Annotate(merged); setInputVerdict(a, repeat1); Assert.AreSame(repeat1, repeat1.TargetStep); Assert.AreSame(repeat1, repeat1.TargetStep); } { // Set repeat1 independent of repeat2 var a = AnnotationCollection.Annotate(repeat1); setInputVerdict(a, repeat2); Assert.AreSame(repeat2, repeat1.TargetStep); Assert.AreSame(repeat1, repeat2.TargetStep); } { // Set repeat2 independent of repeat1 var a = AnnotationCollection.Annotate(repeat2); setInputVerdict(a, repeat1); Assert.AreSame(repeat2, repeat1.TargetStep); Assert.AreSame(repeat1, repeat2.TargetStep); } { // Set all back to repeat1 var a = AnnotationCollection.Annotate(merged); setInputVerdict(a, repeat1); Assert.AreSame(repeat1, repeat1.TargetStep); Assert.AreSame(repeat1, repeat2.TargetStep); } } [Test] public void ParameterizeOnParentTest() { var plan = new TestPlan(); var seqStep = new SequenceStep(); plan.ChildTestSteps.Add(seqStep); var delayStep = new DelayStep(); seqStep.ChildTestSteps.Add(delayStep); // Check can parameterize var member = AnnotationCollection.Annotate(delayStep).GetMember(nameof(DelayStep.DelaySecs)); var menu = member.Get(); var menuItems = menu.MenuItems; var icons = menuItems.ToLookup(item => item.Get()?.IconName ?? ""); var parameterizeIcon = icons[IconNames.Parameterize].First(); Assert.IsTrue(parameterizeIcon.Get().IsVisible); parameterizeIcon = icons[IconNames.Unparameterize].First(); Assert.IsFalse(parameterizeIcon.Get().IsVisible); // Parameterize var delayMember = TypeData.GetTypeData(delayStep).GetMember(nameof(DelayStep.DelaySecs)); delayMember.Parameterize(seqStep, delayStep, "Delay"); // Check can parameterize member = AnnotationCollection.Annotate(delayStep).GetMember(nameof(DelayStep.DelaySecs)); menu = member.Get(); menuItems = menu.MenuItems; icons = menuItems.ToLookup(item => item.Get()?.IconName ?? ""); parameterizeIcon = icons[IconNames.Parameterize].First(); Assert.IsFalse(parameterizeIcon.Get().IsVisible); parameterizeIcon = icons[IconNames.Unparameterize].First(); Assert.IsTrue(parameterizeIcon.Get().IsVisible); var paramIconAnnotation = member.Get(); Assert.AreEqual(seqStep.Id, paramIconAnnotation.TestStepReference); Assert.AreEqual("Delay", paramIconAnnotation.MemberName); // Set read only delayStep.IsReadOnly = true; // Check can parameterize member = AnnotationCollection.Annotate(delayStep).GetMember(nameof(DelayStep.DelaySecs)); menu = member.Get(); menuItems = menu.MenuItems; icons = menuItems.ToLookup(item => item.Get()?.IconName ?? ""); parameterizeIcon = icons[IconNames.Unparameterize].First(); Assert.IsFalse(parameterizeIcon.Get().IsVisible); } /// /// When the test plan is changed, parameters that becomes invalid needs to be removed. /// This is done by doing some checks in DynamicMemberTypeDataProvider. /// In this test it is verified that that behavior works as expected. /// [Test] public void AutoRemoveParameters() { var plan = new TestPlan(); var step = new TwoDelayStep(); plan.ChildTestSteps.Add(step); var member = TypeData.GetTypeData(step).GetMember(nameof(step.DelaySecs)); var parameter = member.Parameterize(plan, step, "delay"); Assert.IsNotNull(TypeData.GetTypeData(plan).GetMember(parameter.Name)); plan.ChildTestSteps.Remove(step); Assert.IsNull(TypeData.GetTypeData(plan).GetMember(parameter.Name)); var seq = new SequenceStep(); plan.ChildTestSteps.Add(seq); seq.ChildTestSteps.Add(step); parameter = member.Parameterize(seq, step, "delay"); Assert.IsNotNull(TypeData.GetTypeData(seq).GetMember(parameter.Name)); seq.ChildTestSteps.Remove(step); Assert.IsNull(TypeData.GetTypeData(seq).GetMember(parameter.Name)); seq.ChildTestSteps.Add(step); parameter = member.Parameterize(seq, step, "delay"); var member2 = TypeData.GetTypeData(seq).GetMember(parameter.Name); Assert.IsNotNull(member2); Assert.AreEqual(member2, parameter); var parameter2 = member2.Parameterize(plan, seq, "delay"); Assert.IsNotNull(TypeData.GetTypeData(plan).GetMember(parameter2.Name)); plan.ChildTestSteps.Remove(seq); Assert.IsNull(TypeData.GetTypeData(plan).GetMember(parameter2.Name)); Assert.IsNotNull(TypeData.GetTypeData(seq).GetMember(parameter.Name)); plan.ChildTestSteps.Add(seq); parameter2 = member2.Parameterize(plan, seq, "delay"); var member3 = TypeData.GetTypeData(step).GetMember(nameof(TwoDelayStep.DelaySecs2)); var parameter3 = member3.Parameterize(plan, step, "delay2"); Assert.IsNotNull(TypeData.GetTypeData(plan).GetMember(parameter2.Name)); Assert.IsNotNull(TypeData.GetTypeData(plan).GetMember(parameter3.Name)); Assert.IsNotNull(TypeData.GetTypeData(seq).GetMember(parameter.Name)); seq.ChildTestSteps.Remove(step); Assert.IsNull(TypeData.GetTypeData(plan).GetMember(parameter2.Name)); Assert.IsNull(TypeData.GetTypeData(plan).GetMember(parameter3.Name)); Assert.IsNull(TypeData.GetTypeData(seq).GetMember(parameter.Name)); } [Test] public void EnabledAnnotated() { var step = new ProcessStep(); var a = AnnotationCollection.Annotate(step); var x = a.GetMember(nameof(step.RegularExpressionPattern)); var str = x.GetMember("Value"); var strval =str.Get(); var v1 = strval.Value.ToString(); Assert.AreEqual("(.*)", v1); step.RegularExpressionPattern.Value = "test"; a.Read(); var v2 = strval.Value; Assert.AreEqual("test", v2); } [Test] public void TestPlanAnnotated() { var plan = new TestPlan(); var step = new DelayStep(); plan.ChildTestSteps.Add(step); var stepType = TypeData.GetTypeData(step); var mem = stepType.GetMember(nameof(DelayStep.DelaySecs)); mem.Parameterize(plan, step, "delay"); var a = AnnotationCollection.Annotate(plan); var members = a.Get().Members; bool filterCol(AnnotationCollection col) { if (col.Get() is IAccessAnnotation access) { if (access.IsVisible == false) return false; } var m = col.Get().Member; var browsable = m.GetAttribute(); // Browsable overrides everything if (browsable != null) return browsable.Browsable; var xmlIgnore = m.GetAttribute(); if (xmlIgnore != null) return false; if (m.HasAttribute()) return true; if (!m.Writable || !m.Readable) return false; return true; } members = members.Where(filterCol).ToArray(); // Name, delay Assert.AreEqual(5, members.Count()); AnnotationCollection getMember(string name) => members.FirstOrDefault(x => x.Get().Member.Name == name); var nameMem = getMember(nameof(TestPlan.Name)); Assert.IsNotNull(nameMem); Assert.IsTrue(nameMem.Get().IsReadOnly); var delayMember = getMember("delay"); Assert.IsNotNull(delayMember); Assert.IsFalse(delayMember.Get().IsReadOnly); var lockedMember = getMember(nameof(TestPlan.Locked)); Assert.IsNotNull(lockedMember); Assert.IsFalse(lockedMember.Get().IsReadOnly); var breakConditionsMember = getMember("BreakConditions"); Assert.IsNotNull(breakConditionsMember); var en = breakConditionsMember.GetMember("IsEnabled"); en.Get().Value = true; en.Write(); a.Write(); var descr = AnnotationCollection.Annotate(step).GetMember("BreakConditions").GetMember("Value") .Get().Describe(); Assert.AreEqual("Break on Error (inherited from test plan).", descr); { var stepAnnotation = AnnotationCollection.Annotate(step); var enabled = stepAnnotation.GetMember("BreakConditions").GetMember("IsEnabled").Get(); enabled.Value = true; //stepAnnotation.Write(); var value = stepAnnotation.GetMember("BreakConditions").GetMember("Value").Get(); var thing = stepAnnotation.GetMember("BreakConditions").GetMember("Value"); var proxy = thing.Get(); var sel = proxy.SelectedValues.ToArray(); value.Value = BreakCondition.BreakOnInconclusive; stepAnnotation.Write(); var descr2 = stepAnnotation.GetMember("BreakConditions").GetMember("Value") .Get().Describe(); } } class ListOfEnumAnnotationClass { public List Strings { get; set; } = new List{"A", "B", "C"}; public List Verdicts { get; set; } = new List{Verdict.Aborted, Verdict.Error}; public List Dates { get; set; } = new List{DateTime.Now}; public List TimeSpans { get; set; } = new List{TimeSpan.Zero, TimeSpan.Zero}; public List Bools { get; set; } = new List() {true, false}; } [Test] public void ListOfEnumAnnotation() { var obj = new ListOfEnumAnnotationClass(); var annotation = AnnotationCollection.Annotate(obj); Assert.AreEqual(5, TypeData.GetTypeData(obj).GetMembers().Count()); foreach (var member in TypeData.GetTypeData(obj).GetMembers()) { int initCount = (member.GetValue(obj) as IList).Count; var memberAnnotation = annotation.GetMember(member.Name); var collection = memberAnnotation.Get(); collection.AnnotatedElements = collection.AnnotatedElements.Append(collection.NewElement()); annotation.Write(); int finalCount = (TypeData.GetTypeData(obj).GetMember(member.Name).GetValue(obj) as IList).Count; Assert.IsTrue(initCount == finalCount - 1); } } class ArrayAnnotationClass { public string[] Strings { get; set; } = new string[] {"A", "B", "C"}; public Verdict[] Verdicts { get; set; } = new Verdict[] {Verdict.Aborted, Verdict.Error}; public DateTime[] Dates { get; set; } = new DateTime[] {DateTime.Now}; public TimeSpan[] TimeSpans { get; set; } = new TimeSpan[] {TimeSpan.Zero, TimeSpan.Zero}; public bool[] Bools { get; set; } = new bool[] {true, false}; } [Test] public void AddToFixedSizeAnnotation() { var obj = new ArrayAnnotationClass(); var annotation = AnnotationCollection.Annotate(obj); Assert.AreEqual(5, TypeData.GetTypeData(obj).GetMembers().Count()); foreach (var member in TypeData.GetTypeData(obj).GetMembers()) { int initCount = (member.GetValue(obj) as IList).Count; var memberAnnotation = annotation.GetMember(member.Name); var collection = memberAnnotation.Get(); collection.AnnotatedElements = collection.AnnotatedElements.Append(collection.NewElement()); annotation.Write(); int finalCount = (TypeData.GetTypeData(obj).GetMember(member.Name).GetValue(obj) as IList).Count; Assert.IsTrue(initCount == finalCount - 1); } } [Test] public void MultiSelectParameterize() { // Previously some performance issue causes this use-case to take several minutes. // After fixing I added this test that shows that it does not take an extraordinary amount // of time to parameterize a property on many test steps. var plan = new TestPlan(); List steps = new List(); for (int i = 0; i < 500; i++) // note: Run this at 50000 iterations to determine limits. { var step = new DelayStep(); plan.ChildTestSteps.Add(step); steps.Add(step); } var a = AnnotationCollection.Annotate(steps.ToArray()); var menu = a.GetMember(nameof(DelayStep.DelaySecs)).Get(); var parameterize = menu.MenuItems.FirstOrDefault(x => x.Get().IconName == IconNames.ParameterizeOnTestPlan); var unparameterize = menu.MenuItems.FirstOrDefault(x => x.Get().IconName == IconNames.Unparameterize); var sw = Stopwatch.StartNew(); parameterize.Get().Invoke(); var elapsed = sw.Elapsed; var sw4 = Stopwatch.StartNew(); var xml = plan.SerializeToString(); var serializeElapsed = sw4.Elapsed; var sw5= Stopwatch.StartNew(); Utils.DeserializeFromString(xml); var deserializeElapsed = sw5.Elapsed; Assert.AreEqual(1, TypeData.GetTypeData(plan).GetMembers().OfType().Count()); var parameter = TypeData.GetTypeData(plan).GetMembers().OfType().First(); var editParameter = AnnotationCollection.Annotate(plan).GetMember(parameter.Name).Get().MenuItems.First(x => x.Get()?.IconName == IconNames.EditParameter); var currentUserInterface = UserInput.Interface; var menuInterface = new MenuTestUserInterface(); UserInput.SetInterface(menuInterface); TimeSpan elapsed3 = TimeSpan.MaxValue; try { menuInterface.SelectName = "B"; var sw3 = Stopwatch.StartNew(); editParameter.Get().Invoke(); elapsed3 = sw3.Elapsed; } finally { UserInput.SetInterface((IUserInputInterface)currentUserInterface); } var sw2 = Stopwatch.StartNew(); unparameterize.Get().Invoke(); var elapsed2 = sw2.Elapsed; Assert.AreEqual(0, TypeData.GetTypeData(plan).GetMembers().OfType().Count()); // these limits are found at 50000 entries, so it is assumed that at 500, they will always work // unless some quadratic complexity has been introduced. Assert.IsTrue(serializeElapsed.TotalSeconds < 10); Assert.IsTrue(deserializeElapsed.TotalSeconds < 15); Assert.IsTrue(elapsed.TotalSeconds < 3); Assert.IsTrue(elapsed2.TotalSeconds < 3); Assert.IsTrue(elapsed3.TotalSeconds < 4); } public class InstrumentStep1 : TestStep { public Instrument Instrument { get; set; } public override void Run() { } } public class InstrumentStep2 : TestStep { public Instrument Instrument { get; set; } public override void Run() { } } [Test] public void MultiSelectUnparameterize() { var step1 = new InstrumentStep1(); var step2 = new InstrumentStep2(); var plan = new TestPlan(); plan.ChildTestSteps.Add(step1); plan.ChildTestSteps.Add(step2); { // 1. parameterize them var a = AnnotationCollection.Annotate(new ITestStep[] { step1, step2 }); var member = a.GetMember(nameof(step1.Instrument)); member.GetIcon(IconNames.ParameterizeOnTestPlan).Get()?.Invoke(); } // 2. check. Assert.IsNotEmpty(TypeData.GetTypeData(plan).GetMembers().OfType()); { // 3. unparameterize them var a = AnnotationCollection.Annotate(new ITestStep[] { step1, step2 }); var member = a.GetMember(nameof(step1.Instrument)); member.GetIcon(IconNames.Unparameterize).Get()? .Invoke(); } // 4. check. Assert.IsEmpty(TypeData.GetTypeData(plan).GetMembers().OfType()); } [Test] public void MultiSelectList() { var step1 = new ClassWithListOfString(); var step2 = new ClassWithListOfString(); var a = AnnotationCollection.Annotate(new []{step1, step2}); var lst = a.GetMember(nameof(step1.List)); var valAnnotation = lst.Get(); valAnnotation.AnnotatedElements = valAnnotation.AnnotatedElements.Append(valAnnotation.NewElement()).ToArray(); a.Write(); Assert.IsTrue(step1.List.Count == 3); Assert.IsTrue(step1.List.SequenceEqual(step2.List)); Assert.AreNotSame(step1.List, step2.List); } [Test] public void FixNullList() { var step1 = new ClassWithListOfString { List = null }; var a = AnnotationCollection.Annotate(step1); var lst = a.GetMember(nameof(step1.List)); var seqAnnotation = lst.Get(); var numbers = seqAnnotation.AnnotatedElements.ToArray(); seqAnnotation.AnnotatedElements = numbers.Append(seqAnnotation.NewElement()); a.Write(); // step1.List should not be null as we have just added an element to it. Assert.IsNotNull(step1.List); Assert.AreEqual(1, step1.List.Count); } [Test] public void ParameterInputsTest() { var plan = new TestPlan(); var verdictStep = new VerdictStep(); var ifStep1 = new IfStep(); var ifStep2 = new IfStep(); plan.ChildTestSteps.Add(verdictStep); plan.ChildTestSteps.Add(ifStep1); plan.ChildTestSteps.Add(ifStep2); var member = TypeData.GetTypeData(ifStep1).GetMember(nameof(ifStep1.InputVerdict)); Assert.IsTrue(ParameterManager.CanParameter(member, new ITestStepParent[] {ifStep1})); ParameterManager.Parameterize(plan, member, new ITestStepParent[] {ifStep1, ifStep2}, "A"); var verdictParameter = TypeData.GetTypeData(plan).GetMember("A"); var value = (Input)verdictParameter.GetValue(plan); Assert.IsFalse(object.ReferenceEquals(ifStep1.InputVerdict, ifStep2.InputVerdict)); verdictParameter.SetValue(plan, value); value.Step = verdictStep; TypeData.GetTypeData(plan).GetMember("A").SetValue(plan, value); Assert.IsFalse(object.ReferenceEquals(ifStep1.InputVerdict, ifStep2.InputVerdict)); Assert.IsTrue(object.ReferenceEquals(ifStep1.InputVerdict.Step, ifStep2.InputVerdict.Step)); } [Test] public void RemoveFromFixedSizeAnnotation() { var obj = new ArrayAnnotationClass(); var annotation = AnnotationCollection.Annotate(obj); Assert.AreEqual(5, TypeData.GetTypeData(obj).GetMembers().Count()); foreach (var member in TypeData.GetTypeData(obj).GetMembers()) { int initCount = (member.GetValue(obj) as IList).Count; var memberAnnotation = annotation.GetMember(member.Name); var collection = memberAnnotation.Get(); collection.AnnotatedElements = collection.AnnotatedElements .Take(collection.AnnotatedElements.Count() - 1).ToList(); annotation.Write(); int finalCount = (TypeData.GetTypeData(obj).GetMember(member.Name).GetValue(obj) as IList).Count; Assert.IsTrue(initCount == finalCount + 1); } } public class TestStepWithLists : TestStep { public List DoubleValues { get; set; } = new List(); public class Item { public double X { get; set; } } public List Items { get; set; } = new List(); public override void Run() { } } [Test] public void ParameteriseLists() { var obj1 = new TestStepWithLists(); var obj2 = new TestStepWithLists(); var plan = new TestPlan(); plan.ChildTestSteps.Add(obj1); plan.ChildTestSteps.Add(obj2); void doTest(string name, TestStepWithLists obj, bool canAnnotate) { var a1 = AnnotationCollection.Annotate(obj).GetMember(name); var p = a1.GetIcon(IconNames.ParameterizeOnTestPlan); if (canAnnotate) { Assert.IsTrue(p.Get().IsEnabled); p.Get().Invoke(); p.Read(); Assert.IsFalse(p.Get().IsEnabled); } else { Assert.IsFalse(p.Get().IsEnabled); } } doTest(nameof(obj1.DoubleValues), obj1, true); doTest(nameof(obj2.DoubleValues), obj2, true); doTest(nameof(obj1.Items), obj1, true); doTest(nameof(obj2.Items), obj2, true); // finally, test that modifying the value also propagates. var items = AnnotationCollection.Annotate(plan).GetMember("Parameters \\ " + nameof(obj1.Items)); Assert.IsNotNull(items); var collection = items.Get(); collection.AnnotatedElements = collection.AnnotatedElements.Append(collection.NewElement()).ToArray(); items.Write(); Assert.AreEqual(1, obj1.Items.Count); Assert.AreEqual(1, obj2.Items.Count); // not same reference. Assert.AreNotEqual(obj1.Items, obj2.Items); } [Test] public void AddExternalParameterWarning() { var obj1 = new TestStepWithLists(); var obj2 = new TestStepWithLists(); var plan = new TestPlan(); plan.ChildTestSteps.Add(obj1); plan.ChildTestSteps.Add(obj2); var member= TypeData.GetTypeData(obj1).GetMember(nameof(obj1.Items)); plan.ExternalParameters.Add(obj1, member, "items"); // produces warning, does not add since the item type is not compatible. var ext = plan.ExternalParameters.Add(obj2, member, "items"); Assert.AreEqual(1, ext.Properties.Count()); } public class MemberWithException { public class SubThing { public double Value => throw new ArgumentNullException(); } public SubThing Value { get => throw new ArgumentNullException(); set { } } public SubThing Value2 { set{} } } [Test] public void TestMemberWithExceptionAnnotation() { var annotation = AnnotationCollection.Annotate(new MemberWithException()); Assert.AreEqual(1, annotation.Get().Members.Count()); annotation.Read(); Assert.AreEqual(1, annotation.Get().Members.Count()); } [Test] public void SweepParametersAnnotation() { var plan = new TestPlan(); var sweep = new SweepParameterStep(); var log = new LogStep() { }; log.LogMessage = "msg0"; plan.Steps.Add(sweep); sweep.ChildTestSteps.Add(log); AnnotationCollection.Annotate(log) .GetMember(nameof(log.LogMessage)) .ExecuteIcon(IconNames.ParameterizeOnParent); AnnotationCollection.Annotate(sweep) .GetMember(nameof(sweep.SweepValues)) .ExecuteIcon(IconNames.ParameterizeOnTestPlan); var sweepValues = AnnotationCollection.Annotate(plan).GetMember("Parameters \\ Sweep \\ Sweep Values"); Assert.IsNotNull(sweepValues); var collection = sweepValues.Get(); for (int i = 0; i < 5; i++) { collection.AnnotatedElements = collection.AnnotatedElements.Append(collection.NewElement()); sweepValues.Write(); sweepValues.Read(); Assert.IsTrue(collection.AnnotatedElements.Count() == i + 1); var row1 = collection.AnnotatedElements.Last(); var enabledMember = row1.GetMember("Enabled"); var valueMember = row1.GetMember("Parameters \\ Log Message"); valueMember.SetValue("msg" + i); Assert.IsNotNull(enabledMember); Assert.IsNotNull(valueMember); } sweepValues.Write(); for (int i = 0; i < 5; i++) { Assert.AreEqual("msg" + i, sweep.SweepValues[i].Values["Parameters \\ Log Message"]); } } [Display("Validation Error Step")] public class ValidationErrorTestStep : TestStep { public bool HasValidationErrors { get; set; } public ValidationErrorTestStep() { Rules.Add(() => !HasValidationErrors, "Validation errors", nameof(ValidationErrorTestStep)); } public override void Run() { } } [Test] public void SweepParametersValidationTest() { var plan = new TestPlan(); var sweep = new SweepParameterStep(); var errorStep = new DelayStep() {Name = "errorStep"}; var noErrorStep = new DelayStep() {Name = "noErrorStep"}; var validationErrorStep = new ValidationErrorTestStep(); plan.ChildTestSteps.Add(sweep); sweep.ChildTestSteps.Add(errorStep); sweep.ChildTestSteps.Add(noErrorStep); sweep.ChildTestSteps.Add(validationErrorStep); TypeData.GetTypeData(errorStep).GetMember(nameof(errorStep.DelaySecs)) .Parameterize(sweep, errorStep, nameof(errorStep.DelaySecs)); TypeData.GetTypeData(validationErrorStep).GetMember(nameof(validationErrorStep.HasValidationErrors)) .Parameterize(sweep, validationErrorStep, nameof(validationErrorStep.HasValidationErrors)); void addRow(double delay, bool validationErrors) { sweep.SweepValues.Add(new SweepRow(sweep)); var td = TypeData.GetTypeData(sweep.SweepValues[0]); var timeDelay = td.GetMember(nameof(errorStep.DelaySecs)); var valErrors = td.GetMember(nameof(validationErrorStep.HasValidationErrors)); timeDelay.SetValue(sweep.SweepValues.Last(), delay); valErrors.SetValue(sweep.SweepValues.Last(), validationErrors); } // First row has no errors addRow(1, false); // Second row has an invalid time delay addRow(-1, false); // Third row as validation errors addRow(1, true); // Fourth row has an invalid time delay and validation errors addRow(-1, true); void TestErrors() { var lines = sweep.Error.Split('\n'); // Test errors outside of run { // Test row 1 var row = lines.Where(l => l.Contains("Row 1")); CollectionAssert.IsEmpty(row); } { // Test row 2 var row = lines.Where(l => l.Contains("Row 2")).ToArray(); Assert.AreEqual(1, row.Length); Assert.IsTrue(row.First().Contains($"Delay must be a positive value."), "Row 2 should have an invalid DelaySecs value."); } { // Test row 3 var row = lines.Where(l => l.Contains("Row 3")).ToArray(); Assert.AreEqual(1, row.Length); Assert.IsTrue(row.Any(r => r.Contains("Validation Error Step - Validation errors")), "Row 3 should have validation errors."); } { // Test row 4 var row = lines.Where(l => l.Contains("Row 4")).ToArray(); Assert.AreEqual(2, row.Length); var row1 = row.First(); Assert.IsTrue(row1.Contains($"Delay must be a positive value."), "Row 4 should have an invalid DelaySecs value."); var row2 = row.Last(); Assert.IsTrue(row2.Contains("Validation Error Step - Validation errors"), "Row 4 should have validation errors."); } } TestErrors(); var t = TapThread.Start(() => { plan.Execute(); }); while (plan.IsRunning == false) TapThread.Sleep(10); // Validation should work while the test plan is running by returning the most recent validation error string TestErrors(); while (plan.IsRunning) TapThread.Sleep(10); TestErrors(); } [Test] public void SweepLoopUnparameterizable() { var plan = new TestPlan(); var sweep = new SweepLoop(); var delay = new DelayStep(); plan.ChildTestSteps.Add(sweep); sweep.ChildTestSteps.Add(delay); var members = new List() {TypeData.GetTypeData(delay).GetMember(nameof(delay.DelaySecs))}; sweep.SweepParameters.Add(new SweepParam(members)); var sweepParametersAnnotation = AnnotationCollection.Annotate(sweep).GetMember(nameof(sweep.SweepParameters)); Assert.IsFalse(sweepParametersAnnotation.GetIcon(IconNames.ParameterizeOnTestPlan).Get().IsEnabled); var nameAnnotation = AnnotationCollection.Annotate(sweep).GetMember(nameof(sweep.Name)); Assert.IsTrue(nameAnnotation.GetIcon(IconNames.ParameterizeOnTestPlan).Get().IsEnabled); } [TestCase(nameof(DialogStep.Title), nameof(DialogStep.Message), true)] [TestCase(nameof(DialogStep.Buttons), nameof(DialogStep.Message), false)] [TestCase(nameof(DialogStep.Message), nameof(DialogStep.Buttons), false)] [TestCase(nameof(DialogStep.Title), nameof(DialogStep.Buttons), false)] [TestCase(nameof(DialogStep.Title), nameof(DialogStep.UseTimeout), false)] [TestCase(nameof(DialogStep.UseTimeout), nameof(DialogStep.Buttons), false)] [TestCase(nameof(DialogStep.Timeout), nameof(DialogStep.Message), false)] [TestCase(nameof(DialogStep.Message), nameof(DialogStep.Timeout), false)] public void TestMergeBadParameters(string paramA, string paramB, bool canMerge) { var plan = new TestPlan(); var dialog = new DialogStep(); plan.Steps.Add(dialog); var a = AnnotationCollection.Annotate(dialog); var menuInterface = new MenuTestUserInterface(); var currentInterface = UserInput.Interface as IUserInputInterface; UserInput.SetInterface(menuInterface); try { menuInterface.SelectName = "a"; menuInterface.SelectedMode = MenuTestUserInterface.Mode.Create | MenuTestUserInterface.Mode.TestPlan; a.GetMember(paramA).GetIcon(IconNames.Parameterize).Get().Invoke(); if (canMerge) { menuInterface.SelectedMode = MenuTestUserInterface.Mode.Merge | MenuTestUserInterface.Mode.TestPlan; } else { menuInterface.SelectedMode = MenuTestUserInterface.Mode.Merge | MenuTestUserInterface.Mode.TestPlan | MenuTestUserInterface.Mode.Error; menuInterface.ErrorString = "Cannot merge properties of this kind."; } a.GetMember(paramB).GetIcon(IconNames.Parameterize).Get().Invoke(); } finally { UserInput.SetInterface(currentInterface); } } class BoolArrayClass { public List A { get; set; }= new List(); public bool[] B { get; set; }= new bool[0]; //public int[] C { get; set; }= new int[0]; //public List D { get; set; }= new List(); public List E { get; set; }= new List(); public List F { get; set; }= new List(); public Verdict[] G { get; set; }= new Verdict[0]; } [Test] public void TestSetBoolArrayValues() { var obj = new BoolArrayClass() ; var a = AnnotationCollection.Annotate(obj); foreach (var mem in a.Get().Members) { for (int i = 0; i < 5; i++) { a.Read(); var col = mem.Get(); col.AnnotatedElements = col.AnnotatedElements.Append(col.NewElement()); mem.Write(); Assert.AreEqual(i + 1, col.AnnotatedElements.Count()); } } } public class SuggestedValuesObject { public List SuggestedValues { get; set; } = new List {1, 2, 3}; [SuggestedValues(nameof(SuggestedValues))] public int SelectedValue { get; set; } [AvailableValues(nameof(SuggestedValues))] public int SelectedValue2 { get; set; } } [Test] public void SuggestedAndAvailableValuesUpdateTest() { // an issue was discovered that when the list of suggested values is updated, without replacing it with // a new list instance the an issue occurs because the ISuggestedValueAnnotationProxy does some internal caching. var obj = new SuggestedValuesObject(); var a = AnnotationCollection.Annotate(obj); var sv = a.GetMember(nameof(obj.SelectedValue)).Get(); var av = a.GetMember(nameof(obj.SelectedValue2)).Get(); Assert.AreEqual(3, sv.SuggestedValues.Count()); Assert.AreEqual(3, av.AvailableValues.Count()); obj.SuggestedValues.Add(4); a.Read(); Assert.AreEqual(4, sv.SuggestedValues.Count()); // Failed initially Assert.AreEqual(4, av.AvailableValues.Count()); } public class InstrumentStep : TestStep { public IInstrument Instrument { get; set; } public override void Run() { } } [Test] public void TestSetStringResourceTest() { using (Session.Create(SessionOptions.OverlayComponentSettings)) { var step = new InstrumentStep(); var instrument = new DummyInstrument() { Name = "Instr" }; InstrumentSettings.Current.Add(instrument); var a = AnnotationCollection.Annotate(step); var instr = a.GetMember(nameof(step.Instrument)); var strval = instr.Get(); var current = strval.Value; Assert.AreNotEqual(instrument.Name, current); // this should be null or some other default value. strval.Value = instrument.Name; a.Write(); a.Read(); var next = strval.Value; Assert.AreEqual(instrument, step.Instrument); Assert.AreEqual(instrument.Name, next); } } class ClassWithNumbers { [Unit("things")] public int[] Values { get; set; } = new int[] {1, 2, 3}; } [Test] public void TestNumberSequenceAnnotation() { var obj = new ClassWithNumbers(); var annotation = AnnotationCollection.Annotate(obj); var str = annotation.GetMember(nameof(obj.Values)).Get(); var err = annotation.GetMember(nameof(obj.Values)).Get(); // this should not throw. str.Value = "asd"; Assert.IsTrue(err.Errors.Count() == 1); str.Value = "1, 2, 3, 4"; Assert.IsTrue(err.Errors.Count() == 0); annotation.Write(); Assert.IsTrue(obj.Values.SequenceEqual(new int[] {1, 2, 3, 4})); } [Test] public void WriteMergedAnnotationTest() { var names = new[] { "name1", "name1", "name2" }; var delaySteps = new DelayStep[3]; for (int i = 0; i < 3; i++) { var delay = new DelayStep() { Name = names[i] }; delaySteps[i] = delay; } var a = AnnotationCollection.Annotate(delaySteps); { // verify merge fails var mem = a.GetMember(nameof(DelayStep.Name)); var name = mem.Get(); Assert.AreEqual(null, name.Value); } { // Verify collection is not modified on write a.Write(); CollectionAssert.AreEqual(names, delaySteps.Select(d => d.Name)); } { // Verify merge succeeds delaySteps[2].Name = "name1"; a.Read(); var mem = a.GetMember(nameof(DelayStep.Name)); var name = mem.Get(); Assert.AreEqual("name1", name.Value); } } public class DutAndInstrumentResource : Resource, IDut, IInstrument { } public class DutAndInstrumentUser : TestStep { public IDut DUT { get; set; } public IInstrument Instrument { get; set; } public DutAndInstrumentResource Both { get; set; } public override void Run() { } } [Test] public void AvailableResourcesTest() { using var session = Session.Create(SessionOptions.OverlayComponentSettings); DutSettings.Current.Clear(); InstrumentSettings.Current.Clear(); var ins = new DutAndInstrumentResource() { Name = "The Instrument" }; var dut = new DutAndInstrumentResource() { Name = "The DUT" }; InstrumentSettings.Current.Add(ins); DutSettings.Current.Add(dut); var step = new DutAndInstrumentUser(); var a = AnnotationCollection.Annotate(step); DutAndInstrumentResource[] getAvailable(string name) => a.GetMember(name).Get().AvailableValues.Cast().ToArray(); var availableInstruments = getAvailable(nameof(step.Instrument)); var availableDuts = getAvailable(nameof(step.DUT)); var availableBoth = getAvailable(nameof(step.Both)); Assert.AreEqual(2, availableInstruments.Length); CollectionAssert.Contains(availableBoth, ins); CollectionAssert.Contains(availableBoth, dut); Assert.AreEqual(2, availableDuts.Length); CollectionAssert.Contains(availableBoth, ins); CollectionAssert.Contains(availableBoth, dut); Assert.AreEqual(2, availableBoth.Length); CollectionAssert.Contains(availableBoth, ins); CollectionAssert.Contains(availableBoth, dut); } [Test] public void AvailableValuesUpdateTest() { var step = new AvailableValuesUpdateTest(); var annotation = AnnotationCollection.Annotate(step); var a = annotation.GetMember(nameof(step.A)); var b = annotation.GetMember(nameof(step.B)); var a1 = a.Get(); var b1 = b.Get(); bool doTest() { // A available values is not allowed to contain the value of B and vice versa. var test2 = b1.AvailableValues.Select(x => x.Get().Value).Contains(a1.SelectedValue.Get().Value); var test1 = a1.AvailableValues.Select(x => x.Get().Value).Contains(b1.SelectedValue.Get().Value); var b1val = b1.SelectedValue.Get().Value; var a1val = a1.SelectedValue.Get().Value; if (Equals(b1val, a1val)) return false; return !test1 && !test2; } for (int i = 0; i < 10; i++) { Assert.IsTrue(doTest()); b1.SelectedValue = b1.AvailableValues.FirstOrDefault(x => !Equals(x.Get().Value, b1.SelectedValue.Get().Value)); annotation.Write(); annotation.Read(); Assert.IsTrue(doTest()); a1.SelectedValue = a1.AvailableValues.FirstOrDefault(x => !Equals(x.Get().Value, a1.SelectedValue.Get().Value)); annotation.Write(); annotation.Read(); Assert.IsTrue(doTest()); } for (int i = 0; i < 5; i++) { var number = annotation.GetMember(nameof(step.FromIncreasingNumber)); var number1 = number.Get(); var numbers = number1.AvailableValues.Select(x => (int)x.Get().Value).ToArray(); Assert.AreEqual(step.IncreasingNumber, numbers[0]); var numberIncrease = annotation.GetMember(nameof(step.IncreaseNumber)).Get(); numberIncrease.Invoke(); annotation.Read(); } } public enum Overlapping { A = 0, X = 1, Z = 2, // there is one constraint, which is that the selected name must be the first one. Otherwise Overlapping.Z.ToString() => "Y" [Obsolete] [Browsable(false)] Y = 2, [Browsable(false)] [Obsolete] Y2 = 2, W = 3 } class ClassWithOverlapping { public Overlapping Overlapping { get; set; } = Overlapping.X; } [Test] public void OverlappingEnumTest() { var o = new ClassWithOverlapping(); var a = AnnotationCollection.Annotate(o); var a2 = a.GetMember(nameof(o.Overlapping)); var available = a2.Get().AvailableValues.Cast(); CollectionAssert.AreEqual(available, new[] { Overlapping.A, Overlapping.X, Overlapping.Z, Overlapping.W }); var a3 = a2.Get(); var strValues = a3.AvailableValues.Select(x => x.Get().Value).ToArray(); CollectionAssert.AreEqual(strValues, new[] { "A", "X", "Z", "W" }); a3.SelectedValue = a3.AvailableValues.Skip(2).FirstOrDefault(); a.Write(); a.Read(); Assert.AreEqual(Overlapping.Z, o.Overlapping); } class EnabledThings { public double X { get; set; } } class MaybeEnabled : IEnabledAnnotation { public bool IsEnabled { get; set; } } public class TestAnnotator : IAnnotator { public double Priority => 0; public void Annotate(AnnotationCollection annotations) { // Disable 'X' of EnabledThings using multiple IEnabledAnnotations: var member = annotations.Get()?.Member; if (member != null && member.Name == "X" && TypeData.FromType(typeof(EnabledThings)).GetMember("X") == member) { annotations.Add(new MaybeEnabled(){IsEnabled = true}); annotations.Add(new MaybeEnabled(){IsEnabled = false}); annotations.Add(new MaybeEnabled(){IsEnabled = true}); } } } [Test] public void TestMultipleEnabled() { var obj = new EnabledThings(); var obj2 = new EnabledThings(); var x2 = AnnotationCollection.Annotate(new[] { obj, obj2 }).GetMember("X"); // after multi-selecting this should be disabled. // when this issue occured it was not. var enabled = x2.Get().IsEnabled; Assert.IsFalse(enabled); } public class AvailableValuesArrayUser { #region Test strings public List AvailableStrings { get; set; } = new List() { "A", "B", "C" }; [AvailableValues(nameof(AvailableStrings))] public List SelectedStringsList { get; set; } = new List(); [AvailableValues(nameof(AvailableStrings))] public string[] SelectedStringsArray { get; set; } = Array.Empty(); #endregion #region Test numbers public List AvailableNumbers { get; set; } = new List() { 7, 9, 13 }; [AvailableValues(nameof(AvailableNumbers))] public List SelectedNumbersList { get; set; } = new List(); [AvailableValues(nameof(AvailableNumbers))] public int[] SelectedNumbersArray { get; set; } = Array.Empty(); #endregion } [Test] public void TestAddToArrayWithAvailableValues() { { // Test writing strings var av = new AvailableValuesArrayUser(); var a = AnnotationCollection.Annotate(av); var selectedItems = a.GetMember(nameof(av.SelectedStringsArray)); var availProxy = selectedItems.Get(); var multiselect = selectedItems.Get(); { multiselect.SelectedValues = availProxy.AvailableValues.Take(2); a.Write(); a.Read(); Assert.AreEqual(2, av.SelectedStringsArray.Length); Assert.AreEqual("A", av.SelectedStringsArray[0]); Assert.AreEqual("B", av.SelectedStringsArray[1]); } { multiselect.SelectedValues = availProxy.AvailableValues.Skip(2).Take(1); a.Write(); a.Read(); Assert.AreEqual(1, av.SelectedStringsArray.Length); Assert.AreEqual("C", av.SelectedStringsArray[0]); } } { // Test writing numbers var av = new AvailableValuesArrayUser(); var a = AnnotationCollection.Annotate(av); var selectedItems = a.GetMember(nameof(av.SelectedNumbersArray)); var availProxy = selectedItems.Get(); var multiselect = selectedItems.Get(); { multiselect.SelectedValues = availProxy.AvailableValues.Take(2); a.Write(); a.Read(); Assert.AreEqual(2, av.SelectedNumbersArray.Length); Assert.AreEqual(7, av.SelectedNumbersArray[0]); Assert.AreEqual(9, av.SelectedNumbersArray[1]); } { multiselect.SelectedValues = availProxy.AvailableValues.Skip(2).Take(1); a.Write(); a.Read(); Assert.AreEqual(1, av.SelectedNumbersArray.Length); Assert.AreEqual(13, av.SelectedNumbersArray[0]); } } } [Test] public void TestAddToListWithAvailableValues() { { // Test writing strings var av = new AvailableValuesArrayUser(); var a = AnnotationCollection.Annotate(av); var selectedItems = a.GetMember(nameof(av.SelectedStringsList)); var availProxy = selectedItems.Get(); var multiselect = selectedItems.Get(); { // Test writing two values multiselect.SelectedValues = availProxy.AvailableValues.Take(2); a.Write(); a.Read(); Assert.AreEqual(2, av.SelectedStringsList.Count); Assert.AreEqual("A", av.SelectedStringsList[0]); Assert.AreEqual("B", av.SelectedStringsList[1]); } { multiselect.SelectedValues = availProxy.AvailableValues.Skip(2).Take(1); a.Write(); a.Read(); Assert.AreEqual(1, av.SelectedStringsList.Count); Assert.AreEqual("C", av.SelectedStringsList[0]); } } { // Test writing numbers var av = new AvailableValuesArrayUser(); var a = AnnotationCollection.Annotate(av); var selectedItems = a.GetMember(nameof(av.SelectedNumbersList)); var availProxy = selectedItems.Get(); var multiselect = selectedItems.Get(); { // Test writing strings multiselect.SelectedValues = availProxy.AvailableValues.Take(2); a.Write(); a.Read(); Assert.AreEqual(2, av.SelectedNumbersList.Count); Assert.AreEqual(7, av.SelectedNumbersList[0]); Assert.AreEqual(9, av.SelectedNumbersList[1]); } { multiselect.SelectedValues = availProxy.AvailableValues.Skip(2).Take(1); a.Write(); a.Read(); Assert.AreEqual(1, av.SelectedNumbersList.Count); Assert.AreEqual(13, av.SelectedNumbersList[0]); } } } [AllowAnyChild] public class ReadOnlyDelays : TestStep { public ReadOnlyDelays() { for (int i = 0; i < 3; i++) { // TestStep.IsReadOnly does not cause the settings to be read-only, but if the ChildTetSteps.IsReadOnly is set this is the case. ChildTestSteps.Add(new DelayStep() { IsReadOnly = true }); } ChildTestSteps.IsReadOnly = true; } public override void Run() { } } [Test] public void TestReadOnlyStepSettings() { var step = new ReadOnlyDelays(); var a = AnnotationCollection.Annotate(step.ChildTestSteps[0]); var member = a.GetMember(nameof(DelayStep.DelaySecs)); Assert.IsTrue(member.GetAll().Any(x => x.IsEnabled == false)); } public class DateTimeStep : TestStep { public DateTime MyDate { get; set; } public override void Run() { throw new NotImplementedException(); } } [Test] public void TestTimeAnnotations() { var times = new DateTimeStep(); var a = AnnotationCollection.Annotate(times); var members = a.Get().Members.ToLookup(m => m.Name); var date = members[nameof(DateTimeStep.MyDate)].First(); var dsv = date.Get(); string[] GetErrors() => date.GetAll().SelectMany(a => a.Errors).ToArray(); Assert.AreEqual(default(DateTime), times.MyDate); StringAssert.StartsWith("0001-01-01", dsv.Value); times.MyDate = DateTime.Parse("1000-11-30", DateTimeFormatInfo.InvariantInfo); a.Read(); StringAssert.StartsWith("1000-11-30", dsv.Value); dsv.Value = "1978-07-27"; Assert.AreEqual(0, GetErrors().Count()); a.Write(); Assert.AreEqual(7, times.MyDate.Month); Assert.AreEqual(27, times.MyDate.Day); Assert.AreEqual(1978, times.MyDate.Year); dsv.Value = "garbage string"; Assert.AreEqual(1, GetErrors().Count()); dsv.Value = "0001-01-01"; Assert.AreEqual(0, GetErrors().Count()); } [Test] public void TestTimeSpanAnnotationError() { var timeStep = new TestPlanTimespan.TimespanStep(); var annotations = AnnotationCollection.Annotate(timeStep); var member = annotations.GetMember("TestProp"); var annotation = member.Get(); Assert.DoesNotThrow(() => { annotation.Value = "test123"; }); Assert.AreEqual(1, annotation.Errors.Count()); } } }