using System; using System.Collections.Generic; using System.Linq; using NUnit.Framework; using OpenTap.Engine.UnitTests; using OpenTap.Engine.UnitTests.TestTestSteps; using OpenTap.Plugins.BasicSteps; namespace OpenTap.UnitTests { [TestFixture] public class ScopeParametersTest { [Test] public void TestIsParameterized() { var plan = new TestPlan(); var diag = new DialogStep() {UseTimeout = true}; var diag2 = new DialogStep(); var scope = new SequenceStep(); plan.ChildTestSteps.Add(scope); string parameterName = "Scope\"" + DisplayAttribute.GroupSeparator + "Title"; // name intentionally weird to mess with the serializer. scope.ChildTestSteps.Add(diag); scope.ChildTestSteps.Add(diag2); var member = TypeData.GetTypeData(diag).GetMember("Title"); Assert.IsFalse(DynamicMember.IsParameterized(member, diag)); Assert.IsFalse(DynamicMember.IsParameterized(member, diag2)); var param = member.Parameterize(scope, diag, parameterName); member.Parameterize(scope, diag2, parameterName); Assert.IsTrue(DynamicMember.IsParameterized(member, diag)); Assert.IsTrue(DynamicMember.IsParameterized(member, diag2)); plan.ChildTestSteps.Remove(scope); TypeData.GetTypeData(scope).GetMembers(); DynamicMember.UnparameterizeMember(param, member, diag); } [Test] public void ScopeStepTest() { var diag = new DialogStep() {UseTimeout = true}; var diag2 = new DialogStep(); var scope = new SequenceStep(); string parameterName = "Scope\"" + DisplayAttribute.GroupSeparator + "Title"; // name intentionally weird to mess with the serializer. scope.ChildTestSteps.Add(diag); scope.ChildTestSteps.Add(diag2); var member = TypeData.GetTypeData(diag).GetMember("Title"); member.Parameterize(scope, diag, parameterName); member.Parameterize(scope, diag2, parameterName); TypeData.GetTypeData(diag).GetMember("Timeout").Parameterize(scope, diag, "Group\\The Timeout"); var annotation = AnnotationCollection.Annotate(scope); var titleMember = annotation.GetMember(parameterName); titleMember.Get().Value = "New title"; annotation.Write(); Assert.AreEqual("New title", diag.Title); Assert.AreEqual("New title", diag2.Title); var timeoutMember = annotation.GetMember("Group\\The Timeout"); Assert.IsFalse(timeoutMember.Get().IsReadOnly); Assert.AreEqual("Group", TypeData.GetTypeData(scope).GetMember("Group\\The Timeout").GetDisplayAttribute().Group[0]); var plan = new TestPlan(); plan.Steps.Add(scope); var str = new TapSerializer().SerializeToString(plan); var plan2 = (TestPlan)new TapSerializer().DeserializeFromString(str); var scope2 = plan2.Steps[0]; var annotation2 = AnnotationCollection.Annotate(scope2); var titleMember2 = annotation2.GetMember(parameterName); Assert.IsNotNull(titleMember2); titleMember2.Get().Value = "New Title 2"; annotation2.Write(); foreach (var step in scope2.ChildTestSteps.Cast()) { Assert.AreEqual(step.Title, "New Title 2"); } var forwardedMember = (ParameterMemberData)TypeData.GetTypeData(scope2).GetMember(parameterName); Assert.IsNotNull(forwardedMember); member.Unparameterize(forwardedMember, scope2.ChildTestSteps[0]); Assert.IsNotNull(TypeData.GetTypeData(scope2).GetMember(parameterName)); member.Unparameterize(forwardedMember, scope2.ChildTestSteps[1]); Assert.IsNull(TypeData.GetTypeData(scope2).GetMember(parameterName)); // last 'Title' removed. } [Test] public void ParallelAddParametersTest() { // verify that we can add parameters while somebody else is iterating them. // this issue only previously occured if the number of parameters in the same collection was // greater than 1. var diag = new DialogStep() {UseTimeout = true}; var diag2 = new DialogStep(); var diag3 = new DialogStep(); var scope = new SequenceStep(); var plan = new TestPlan(); plan.ChildTestSteps.Add(scope); string parameterName = "param1"; scope.ChildTestSteps.Add(diag); scope.ChildTestSteps.Add(diag2); scope.ChildTestSteps.Add(diag3); var member = TypeData.GetTypeData(diag).GetMember("Title"); var p1 = member.Parameterize(scope, diag, parameterName); member.Parameterize(scope, diag2, parameterName); int count = 0; foreach (var _ in p1.ParameterizedMembers) { count++; if (count == 2) member.Parameterize(scope, diag3, parameterName); } Assert.AreEqual(2, count); count = 0; foreach (var _ in p1.ParameterizedMembers) count++; Assert.AreEqual(3, count); } [Test] public void MultiLevelScopeSerialization() { var plan = new TestPlan(); var seq1 = new SequenceStep(); var seq2 = new SequenceStep(); var delay = new DelayStep(); plan.ChildTestSteps.Add(seq1); seq1.ChildTestSteps.Add(seq2); seq2.ChildTestSteps.Add(delay); var member1 = TypeData.GetTypeData(delay).GetMember(nameof(DelayStep.DelaySecs)) .Parameterize(seq2, delay, "delay"); member1.Parameterize(seq1, seq2, "delay"); var str = new TapSerializer().SerializeToString(plan); var plan2 = (TestPlan)new TapSerializer().DeserializeFromString(str); var member2 = TypeData.GetTypeData(plan2.ChildTestSteps[0]).GetMember(member1.Name); var val = member2.GetValue(plan2.ChildTestSteps[0]); Assert.AreEqual(delay.DelaySecs, val); } [Test] public void CyclicScopeTest() { var seq = new SequenceStep(); var delay = new DelayStep() { DelaySecs = 1.5 }; seq.ChildTestSteps.Add(delay); var member = TypeData.GetTypeData(delay).GetMember("DelaySecs"); member.Parameterize(seq, delay, "something"); var value = AnnotationCollection.Annotate(delay).GetMember("DelaySecs").Get(); var value2 = AnnotationCollection.Annotate(seq).GetMember("something").Get(); try { member.Parameterize(delay, seq, "something"); Assert.Fail("Parameterize should have thrown an exception."); } catch (ArgumentException) { } // Stack overflow... value = AnnotationCollection.Annotate(delay).GetMember("DelaySecs").Get(); value2 = AnnotationCollection.Annotate(seq).GetMember("something").Get(); Assert.IsNotNull(value); Assert.IsNotNull(value2); } public class ScopeTestStep : TestStep{ public int A { get; set; } public List Collection = new List(); public Enabled EnabledTest { get; set; } = new Enabled(); public override void Run() { Collection.Add(A); UpgradeVerdict(Verdict.Pass); OnPropertyChanged(""); } } [Test] public void SweepLoopDisabledMembersOnMultiSelect() { var plan = new TestPlan(); var sweep = new SweepParameterRangeStep(); var sweep2 = new SweepParameterRangeStep(); var numberstep = new ScopeTestStep(); var numberstep2 = new ScopeTestStep(); plan.ChildTestSteps.Add(sweep); plan.ChildTestSteps.Add(sweep2); sweep.ChildTestSteps.Add(numberstep); sweep2.ChildTestSteps.Add(numberstep2); var member = TypeData.GetTypeData(numberstep).GetMember("A"); member.Parameterize(sweep, numberstep, "A"); member.Parameterize(sweep2, numberstep2, "A"); sweep.SelectedParameters = Enumerable.Empty().ToList(); Assert.AreEqual(0, sweep.SelectedParameters.Count()); { var a = AnnotationCollection.Annotate(sweep); var m = a.GetMember(nameof(SweepParameterRangeStep.SelectedParameters)); var sweptMember = a.GetMember("A"); Assert.IsTrue(sweptMember.Get().IsEnabled); var ms = m.Get(); var avail = m.Get(); ms.SelectedValues = avail.AvailableValues; a.Write(); sweptMember = a.GetMember("A"); Assert.IsFalse(sweptMember.Get().IsEnabled); } { var a = AnnotationCollection.Annotate(new object[] {sweep, sweep2}); var amem = a.GetMember("A"); var ienabled = amem.Get(); Assert.IsFalse(ienabled.IsEnabled); } } [Test] public void SweepLoopRange2Test() { var plan = new TestPlan(); var sweep = new SweepParameterRangeStep(); var numberstep = new ScopeTestStep(); plan.ChildTestSteps.Add(sweep); sweep.ChildTestSteps.Add(numberstep); var member = TypeData.GetTypeData(numberstep).GetMember("A"); member.Parameterize(sweep, numberstep, "A"); sweep.SelectedParameters = Enumerable.Empty().ToList(); Assert.AreEqual(0, sweep.SelectedParameters.Count()); { var a = AnnotationCollection.Annotate(sweep); var m = a.GetMember(nameof(SweepParameterRangeStep.SelectedParameters)); var sweptMember = a.GetMember("A"); Assert.IsTrue(sweptMember.Get().IsEnabled); var ms = m.Get(); var avail = m.Get(); ms.SelectedValues = avail.AvailableValues; a.Write(); sweptMember = a.GetMember("A"); Assert.IsFalse(sweptMember.Get().IsEnabled); } Assert.AreEqual(1, sweep.SelectedParameters.Count()); sweep.SweepStart = 1; sweep.SweepEnd = 10; sweep.SweepPoints = 10; Assert.IsTrue(string.IsNullOrEmpty(sweep.Error)); plan.Execute(); Assert.IsTrue(Enumerable.Range(1,10).SequenceEqual(numberstep.Collection)); { var sweep2 = new SweepLoopRange(); plan.ChildTestSteps.Add(sweep2); // verify that sweep Behavior selected value can be displayed. var annotation = AnnotationCollection.Annotate(sweep); var mem = annotation.GetMember(nameof(SweepParameterRangeStep.SweepBehavior)); var proxy = mem.Get(); var selectedBehavior = proxy.SelectedValue.Get(); Assert.AreEqual("Linear", selectedBehavior.Value); } } [Test] public void SweepLoopRange3Test() { var plan = new TestPlan(); var sweep = new SweepParameterRangeStep(); plan.ChildTestSteps.Add(sweep); var a = AnnotationCollection.Annotate(sweep).GetMember(nameof(sweep.SweepStep)); var menu = a.Get(); Assert.IsNotNull(menu); var icons = menu.MenuItems.ToLookup(x => x.Get()?.IconName ?? ""); var parameterize = icons[IconNames.ParameterizeOnTestPlan].First(); Assert.IsTrue(parameterize.Get().IsEnabled); Assert.AreEqual(0, plan.ExternalParameters.Entries.Count); parameterize.Get().Invoke(); Assert.AreEqual(1, plan.ExternalParameters.Entries.Count); sweep.SweepBehavior = SweepBehavior.Linear; sweep.SweepStart = 1; sweep.SweepEnd = 101; var param = plan.ExternalParameters.Entries.First(); { // Parameterized value updates SweepPoints param.Value = 1; Assert.AreEqual(101, sweep.SweepPoints); param.Value = 2; Assert.AreEqual(51, sweep.SweepPoints); param.Value = 4; Assert.AreEqual(26, sweep.SweepPoints); param.Value = 8; Assert.AreEqual(13, sweep.SweepPoints); } { // SweepPoints updates parameterized value sweep.SweepPoints = 2; Assert.AreEqual(100, param.Value); sweep.SweepPoints = 3; Assert.AreEqual(50, param.Value); sweep.SweepPoints = 5; Assert.AreEqual(25, param.Value); sweep.SweepPoints = 6; Assert.AreEqual(20, param.Value); } } [Test] public void SweepLoopRange4Test() { var plan = new TestPlan(); var sweep = new SweepParameterRangeStep(); var seq = new SequenceStep(); var delay = new DelayStep(); plan.ChildTestSteps.Add(seq); seq.ChildTestSteps.Add(sweep); sweep.ChildTestSteps.Add(delay); var a = AnnotationCollection.Annotate(delay).GetMember(nameof(delay.DelaySecs)); var menu = a.Get(); var icons = menu.MenuItems.ToLookup(x => x.Get()?.IconName ?? ""); var parameterize = icons[IconNames.ParameterizeOnParent].First(); parameterize.Get().Invoke(); a = AnnotationCollection.Annotate(sweep).GetMember("Parameters \\ Time Delay"); menu = a.Get(); icons = menu.MenuItems.ToLookup(x => x.Get()?.IconName ?? ""); // It should not be possible to parameterize (parameterized) properties which are selected for sweeping. var names = new [] {IconNames.ParameterizeOnTestPlan, IconNames.Parameterize, IconNames.ParameterizeOnParent }; foreach (var name in names) { parameterize = icons[name].First(); Assert.IsTrue(parameterize.GetAll().Any(x => x.IsEnabled == false)); } sweep.SelectedParameters.Clear(); a.Read(); // It should be possible to parameterize (parameterized) properties which are not selected for sweeping. foreach (var name in names) { parameterize = icons[name].First(); Assert.IsFalse(parameterize.GetAll().Any(x => x.IsEnabled == false)); } // Now parameterize it (sweep -> sequence step). icons[IconNames.ParameterizeOnParent].First().Get().Invoke(); // It should not be possible to select a parameterized property to be swept. Assert.IsTrue(!sweep.AvailableParameters.Any()); } [Test] public void SweepLoop2Test() { var plan = new TestPlan(); var sweep = new SweepParameterStep(); var step = new ScopeTestStep(); plan.ChildTestSteps.Add(sweep); sweep.ChildTestSteps.Add(step); sweep.SweepValues.Add(new SweepRow(sweep)); sweep.SweepValues.Add(new SweepRow(sweep)); TypeData.GetTypeData(step).GetMember(nameof(ScopeTestStep.A)).Parameterize(sweep, step, "Parameters \\ A"); TypeData.GetTypeData(step).GetMember(nameof(ScopeTestStep.EnabledTest)).Parameterize(sweep, step, nameof(ScopeTestStep.EnabledTest)); var td1 = TypeData.GetTypeData(sweep.SweepValues[0]); var memberA = td1.GetMember("Parameters \\ A"); memberA.SetValue(sweep.SweepValues[0], 10); memberA.SetValue(sweep.SweepValues[1], 20); { // verify Enabled works with SweepParameterStep. var annotation = AnnotationCollection.Annotate(sweep); var col = annotation.GetMember(nameof(SweepParameterStep.SelectedParameters)).Get().Value; // The order is not deterministic because the implementation is using a dictionary and looping over the content. // The string hashing function used by dotnet can vary across processes, which affects the order. // It may be confusing to users that restarting an application causes the displayed parameters to change, // but this is a cosmetic issue. We can consider changing the underlying implementation to ensure the list // remains sorted on insertion. I have opted not to change the implementation at this time because the // list implementation is directly exposed to the user with methods such as Insert(index, parameter) // which would be impossible to honor if we sort the list anyway. The way the comma separated string // is actually generated is through a MemberDataSequenceStringAnnotation which is widely used, so ordering // the members alphabetically in that implementation is also a no-go since ordering could be important in other contexts. // The least destructive way would be to use a separate annotator for this particular implementation, which seems excessive for a cosmetic issue. Assert.That(col, Is.EqualTo("A, EnabledTest").Or.EqualTo("EnabledTest, A")); var elements = annotation.GetMember(nameof(SweepParameterStep.SweepValues)) .Get().AnnotatedElements .Select(elem => elem.GetMember(nameof(ScopeTestStep.EnabledTest))) .ToArray(); annotation.Write(); Assert.IsFalse((bool) elements[0].GetMember("IsEnabled").Get().Value); elements[0].GetMember("IsEnabled").Get().Value = true; annotation.Write(); Assert.IsFalse((bool) elements[1].GetMember("IsEnabled").Get().Value); Assert.IsTrue((bool) elements[0].GetMember("IsEnabled").Get().Value); } var str = new TapSerializer().SerializeToString(plan); var plan2 = (TestPlan)new TapSerializer().DeserializeFromString(str); var sweep2 = (SweepParameterStep) plan2.Steps[0]; var td2 = TypeData.GetTypeData(sweep2); var members2 = td2.GetMembers(); var rows = sweep2.SweepValues; Assert.AreEqual(2, rows.Count); var msgmem = TypeData.GetTypeData(rows[0]).GetMember("Parameters \\ A"); Assert.AreEqual(10, msgmem.GetValue(rows[0])); // this feature was disabled. //var annotated = AnnotationCollection.Annotate(sweep2); //var messageMember = annotated.GetMember(nameof(ScopeTestStep.A)); //Assert.IsFalse(messageMember.Get().IsEnabled); var run = plan2.Execute(); Assert.AreEqual(Verdict.Pass, run.Verdict); Assert.IsTrue(((ScopeTestStep)sweep2.ChildTestSteps[0]).Collection.SequenceEqual(new[] {10, 20})); var name = sweep.GetFormattedName(); Assert.That(name, Is.EqualTo("Sweep A, EnabledTest").Or.EqualTo("Sweep EnabledTest, A")); { // Testing that sweep parameters are automatically removed after unparameterization. var p = (ParameterMemberData) TypeData.GetTypeData(sweep2).GetMember("Parameters \\ A"); p.ParameterizedMembers.First().Member.Unparameterize(p, p.ParameterizedMembers.First().Source); Assert.AreEqual(2, sweep2.SweepValues[0].Values.Count); sweep2.Error.ToString(); // getting the error causes validation to be done. Assert.AreEqual(1, sweep2.SweepValues[0].Values.Count); } } [Test] public void ScopedInputAnnotationTest() { var seqStep = new SequenceStep(); var verdictStep = new VerdictStep(); seqStep.ChildTestSteps.Add(verdictStep); var ifStep = new IfStep(); seqStep.ChildTestSteps.Add(ifStep); var member = TypeData.GetTypeData(ifStep).GetMember(nameof(IfStep.InputVerdict)); var parameterizedMember = member.Parameterize(seqStep, ifStep, member.Name); var annotation = AnnotationCollection.Annotate(seqStep); var memberAnnotation = annotation.GetMember(parameterizedMember.Name); var avail = memberAnnotation.Get(); Assert.IsNotNull(avail); // available values: None, verdict from itself, verdict from SetVerdict. Assert.AreEqual(3, avail.AvailableValues.Cast().Count()); var strings = avail.AvailableValues.Cast().Select(x => x.ToString()).ToArray(); Assert.IsTrue(strings.Contains($"Verdict from {ifStep.GetFormattedName()}")); Assert.IsTrue(strings.Contains("None")); Assert.IsTrue(strings.Contains($"Verdict from {verdictStep.GetFormattedName()}")); } [Test] public void ScopedInputAnnotationWithSweepTest() { var sweepStep = new SweepParameterStep(); var verdictStep = new VerdictStep(); sweepStep.ChildTestSteps.Add(verdictStep); var ifStep = new IfStep(); sweepStep.ChildTestSteps.Add(ifStep); var member = TypeData.GetTypeData(ifStep).GetMember(nameof(IfStep.InputVerdict)); var parameterizedMember = member.Parameterize(sweepStep, ifStep, member.Name); var annotation = AnnotationCollection.Annotate(sweepStep); var memberAnnotation = annotation.GetMember(nameof(sweepStep.SweepValues)); var col = memberAnnotation.Get(); col.AnnotatedElements = new[] {col.NewElement()}; annotation.Write(); annotation.Read(); var member2Annotation = col.AnnotatedElements.FirstOrDefault().GetMember(parameterizedMember.Name); var avail = member2Annotation.Get(); Assert.IsNotNull(avail); // available values: None, verdict from itself, verdict from SetVerdict. Assert.AreEqual(3, avail.AvailableValues.Cast().Count()); var strings = avail.AvailableValues.Cast().Select(x => x.ToString()).ToArray(); Assert.IsTrue(strings.Contains($"Verdict from {ifStep.GetFormattedName()}")); Assert.IsTrue(strings.Contains("None")); Assert.IsTrue(strings.Contains($"Verdict from {verdictStep.GetFormattedName()}")); } [Test] public void ScopedInputAnnotationWithSweepTestSerialized() { var plan = new TestPlan(); var sweepStep = new SweepParameterStep(); plan.Steps.Add(sweepStep); var verdictStep = new VerdictStep(); sweepStep.ChildTestSteps.Add(verdictStep); var ifStep = new IfStep(); sweepStep.ChildTestSteps.Add(ifStep); var member = TypeData.GetTypeData(ifStep).GetMember(nameof(IfStep.InputVerdict)); var parameterizedMember = member.Parameterize(sweepStep, ifStep, member.Name); var annotation = AnnotationCollection.Annotate(sweepStep); var memberAnnotation = annotation.GetMember(nameof(sweepStep.SweepValues)); var col = memberAnnotation.Get(); col.AnnotatedElements = new[] {col.NewElement()}; annotation.Write(); annotation.Read(); var sweepValuesMember = TypeData.GetTypeData(sweepStep).GetMember(nameof(sweepStep.SweepValues)); sweepValuesMember.Parameterize(plan, sweepStep, "SweepValues"); var planstr = plan.SerializeToString(); var plan2 = Utils.DeserializeFromString(planstr); var ext = plan2.ExternalParameters.Get("SweepValues"); Assert.IsNotNull(ext); } [Test] public void SweepLoopNoValuesSelected() { var plan = new TestPlan(); var sweep = new SweepParameterStep(); var step = new DelayStep(); plan.ChildTestSteps.Add(sweep); sweep.ChildTestSteps.Add(step); Assert.Throws(typeof(InvalidOperationException), sweep.PrePlanRun); // Select parameter to sweep TypeData.GetTypeData(step).GetMember(nameof(DelayStep.DelaySecs)).Parameterize(sweep, step, nameof(DelayStep.DelaySecs)); try { sweep.PrePlanRun(); Assert.Fail("An exception should have been thrown."); } catch (InvalidOperationException ex) { Assert.AreEqual(ex.Message, "No values selected to sweep"); } } public class NullPropertyStep : TestStep { public string NormallyNull { get; set; } public override void Run() => UpgradeVerdict(Verdict.Pass); } [Test] public void SweepLoopNullValueProperty() { var plan = new TestPlan(); var sweep = new SweepParameterStep(); var step = new NullPropertyStep(); plan.ChildTestSteps.Add(sweep); sweep.ChildTestSteps.Add(step); TypeData.GetTypeData(step).GetMember(nameof(step.NormallyNull)).Parameterize(sweep, step, nameof(step.NormallyNull)); var row = new SweepRow(sweep); row.Values[nameof(step.NormallyNull)] = null; sweep.SweepValues.Add(new SweepRow(sweep)); var run = plan.Execute(); Assert.AreEqual(Verdict.Pass, run.Verdict); } public class Unclonable { public int Value { get; set; } public Unclonable(int value) => Value = value; } public class UnmergableValueTest : TestStep { public Unclonable A { get; set; } = new Unclonable(5); public override void Run() { } } [Test] public void MergeableScopeTest() { var plan = new TestPlan(); var step1 = new UnmergableValueTest(); var step2 = new UnmergableValueTest(); plan.ChildTestSteps.Add(step1); plan.ChildTestSteps.Add(step2); foreach(var s in new []{step1, step2}) { var a = AnnotationCollection.Annotate(s) .GetMember(nameof(step1.A)) .Get().MenuItems.FirstOrDefault(x => x.Get()?.IconName == IconNames.ParameterizeOnTestPlan); var enabled = a.Get().IsEnabled; if (s == step1) { Assert.IsTrue(enabled); a.Get()?.Invoke(); } else Assert.IsFalse(enabled); } } [Test] public void MergeableScopeTest2() { var plan = new TestPlan(); var step1 = new SweepParameterStep(); var step2 = new SweepParameterStep(); plan.ChildTestSteps.Add(step1); plan.ChildTestSteps.Add(step2); var delay1 = new DelayStep(); var delay2 = new DelayStep(); step1.ChildTestSteps.Add(delay1); step2.ChildTestSteps.Add(delay2); TypeData.GetTypeData(delay1).GetMember(nameof(delay1.DelaySecs)).Parameterize(step1, delay1, "A"); TypeData.GetTypeData(delay2).GetMember(nameof(delay2.DelaySecs)).Parameterize(step2, delay2, "A"); foreach(var s in new []{step1, step2}) { var a = AnnotationCollection.Annotate(s) .GetMember(nameof(s.SweepValues)) .Get().MenuItems.FirstOrDefault(x => x.Get()?.IconName == IconNames.ParameterizeOnTestPlan); var enabled = a.Get().IsEnabled; if (s == step1) { Assert.IsTrue(enabled); a.Get()?.Invoke(); } else Assert.IsFalse(enabled); } } [Test] public void NestedPropertyUnparameterize() { // testing unparameterize on multiple levels of parameters. var plan = new TestPlan(); var seq = new SequenceStep(); var logOutput = new LogStep(); seq.ChildTestSteps.Add(logOutput); plan.Steps.Add(seq); var parameterName = "A"; var p1 = TypeData.GetTypeData(logOutput).GetMember(nameof(logOutput.Severity)).Parameterize(seq, logOutput, parameterName); TypeData.GetTypeData(seq).GetMember(parameterName).Parameterize(plan, seq, parameterName); plan.SerializeToString(true); TypeData.GetTypeData(plan).GetMembers(); // check sanity is called, everything seems good. var (x, y) = p1.ParameterizedMembers.First(); y.Unparameterize(p1, x); // originally it failed already here since the sanity check was not done properly. var param2 = TypeData.GetTypeData(plan).GetMember(parameterName); Assert.IsNull(param2); // this has always passed. var param1 = TypeData.GetTypeData(seq).GetMember(parameterName); Assert.IsNull(param1); // this was what failed in the original bug report. plan.SerializeToString(true); } public class FailParameterStep : TestStep { double value; public double Value { get => value; set { if (value < 0.0) { throw new Exception("!"); } this.value = value; } } public override void Run() { } } [Test] public void SweepParameterStepTest() { var plan = new TestPlan(); var step1 = new SweepParameterStep(); plan.ChildTestSteps.Add(step1); var failStep = new FailParameterStep(); step1.ChildTestSteps.Add(failStep); TypeData.GetTypeData(failStep).GetMember(nameof(failStep.Value)).Parameterize(step1, failStep, "A"); step1.SweepValues.Add(new SweepRow(step1)); // this should make failStep fail. step1.SweepValues[0].Values["A"] = -1.0; var run = plan.Execute(); Assert.AreEqual(Verdict.Error, run.Verdict); } [Test] public void SweepParameterErrorTest() { var plan = new TestPlan(); var sweep = new SweepParameterStep(); plan.ChildTestSteps.Add(sweep); var testStep = new DelayStep(); sweep.ChildTestSteps.Add(testStep); TypeData.GetTypeData(testStep).GetMember(nameof(testStep.DelaySecs)).Parameterize(sweep, testStep, "A"); sweep.SweepValues.Add(new SweepRow(sweep)); sweep.SweepValues[0].Values["A"] = -1; var result = plan.Execute(); result.Start(); Assert.AreEqual(Verdict.Error, result.Verdict); } [Test] public void MultiStepParameterize() { var plan = new TestPlan(); for (int i = 0; i < 4; i++) { var diag = new DialogStep(); plan.ChildTestSteps.Add(diag); var titleMem = TypeData.GetTypeData(diag).GetMember(nameof(diag.Title)); titleMem.Parameterize(plan, diag, nameof(DialogStep.Title)); } for (int i = 0; i < 4; i++) { Assert.IsNotNull(TypeData.GetTypeData(plan).GetMember(nameof(DialogStep.Title))); plan.ChildTestSteps.RemoveAt(0); } Assert.IsNull(TypeData.GetTypeData(plan).GetMember(nameof(DialogStep.Title))); } public class VerifyInstrumentTestStep : TestStep { public IInstrument Instrument { get; set; } public readonly HashSet Instruments = new HashSet(); public override void PrePlanRun() { base.PrePlanRun(); Instruments.Clear(); } public override void Run() { if (Instrument.IsConnected == false) throw new Exception("Instrument is not connected"); UpgradeVerdict(Verdict.Pass); Instruments.Add(Instrument); } } [Test] public void SweepResourcesTest() { using (Session.Create(SessionOptions.OverlayComponentSettings)) { InstrumentSettings.Current.Clear(); var plan = new TestPlan(); var step1 = new SweepParameterStep(); var step2 = new VerifyInstrumentTestStep(); int iterations = 10; var instruments = new DummyInstrument[iterations]; for (int i = 0; i < iterations; i++) instruments[i] = new DummyInstrument(); step2.Instrument = instruments[0]; foreach(var instr in instruments) InstrumentSettings.Current.Add(instr); plan.ChildTestSteps.Add(step1); step1.ChildTestSteps.Add(step2); var mem = TypeData.GetTypeData(step2).GetMember(nameof(step2.Instrument)); mem.Parameterize(step1, step2, "Instrument"); for (int i = 0; i < iterations; i++) { var row1 = new SweepRow(step1); row1.Values["Instrument"] = instruments[i]; step1.SweepValues.Add(row1); } var run = plan.Execute(); Assert.AreEqual(Verdict.Pass, run.Verdict); Assert.AreEqual(iterations, step2.Instruments.Count()); } } [Test] public void DynamicMemberNullTest() { var plan = new TestPlan(); var seq = new SequenceStep(); plan.ChildTestSteps.Add(seq); var log = new LogStep(); seq.ChildTestSteps.Add(log); var seqType1 = TypeData.GetTypeData(seq); var mems1 = seqType1.GetMembers().ToArray(); var logType = TypeData.GetTypeData(log); var param = logType.GetMember(nameof(log.LogMessage)).Parameterize(seq, log, "log1"); var seqType2 = TypeData.GetTypeData(seq); var mems2 = seqType2.GetMembers().ToArray(); logType.GetMember(nameof(log.LogMessage)).Unparameterize(param, log); // previously, this getter threw an exception, since seqType2 had changed compared to seqType1 // a coding error inside getDynamicStep caused that. var mems3 = seqType2.GetMembers().ToArray(); Assert.IsTrue(mems1.Length == mems2.Length - 1); // mems1 + parameter. Assert.IsTrue(mems1.Length == mems3.Length); // parameter was removed. } [Test] public void DynamicMemberNullTest2() { var plan = new TestPlan(); var seq = new SequenceStep(); plan.ChildTestSteps.Add(seq); var log = new LogStep(); seq.ChildTestSteps.Add(log); var logType = TypeData.GetTypeData(log); var param = logType.GetMember(nameof(log.LogMessage)).Parameterize(seq, log, "log1"); var param2 = param.Parameterize(plan, seq, "log2"); seq.ChildTestSteps.Clear(); using (ParameterManager.WithSanityCheckDelayed(quickCheck: false)) { var m2 = TypeData.GetTypeData(plan).GetMember(param2.Name); Assert.IsNotNull(m2); } using (ParameterManager.WithSanityCheckDelayed(quickCheck: true)) { // since a quick-check is allowed, the check will anyway be done. var m2 = TypeData.GetTypeData(plan).GetMember(param2.Name); Assert.IsNull(m2); } } [Test] public void DynamicTypeDataEqualityTest() { // the ITypeData implementation if DynamicTestStepTypeData did not implement Equals / GetHashCode. // this gives rise to issues with caching caching, leading to possible leaks. var plan = new TestPlan(); var scope = new SequenceStep(); var scope2 = new SequenceStep(); plan.ChildTestSteps.Add(scope); scope.ChildTestSteps.Add(scope2); Dictionary dict = new Dictionary(); var newMem = TypeData.GetTypeData(scope2).GetMember(nameof(scope2.Name)).Parameterize(scope, scope2, "param"); scope.Name = "Test {param}"; var tp0 = TypeData.GetTypeData(scope); Assert.IsNotNull(newMem); for (int i = 0; i < 5; i++) { var tp1 = TypeData.GetTypeData(scope); Assert.IsTrue(object.ReferenceEquals(tp1, tp0)); dict[tp1] = i; } // all the values should be inserted at the same key location. Assert.AreEqual(1, dict.Count); } } }