// Copyright Keysight Technologies 2012-2019
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, you can obtain one at http://mozilla.org/MPL/2.0/.
using NUnit.Framework;
using System;
using System.Diagnostics;
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Text.RegularExpressions;
using OpenTap.EngineUnitTestUtils;
using OpenTap.Plugins.BasicSteps;
using System.ComponentModel;
using System.Threading;
using OpenTap.Engine.UnitTests.TestTestSteps;
using System.Text;
namespace OpenTap.Engine.UnitTests
{
///
///This is a test class for TestPlanTest and is intended
///to contain all TestPlanTest Unit Tests
///
[TestFixture]
public class TestPlanTest
{
public class TestStepExceptionTest : TestStep
{
public override void Run()
{
throw new Exception("test");
}
}
public class TestStepPreExceptionTest : TestStep
{
public override void PrePlanRun()
{
throw new Exception("test");
}
public override void Run()
{
}
}
///
///Gets or sets the test context which provides
///information about and functionality for the current test run.
///
[Test]
public void SaveLoadWithNestedTypes()
{
TestPlan plan = new TestPlan();
plan.Steps.Add(new TestStepTest.NestedStep());
plan.Steps.Add(new TestStepTest2.NestedStep());
string fileName = Path.GetTempFileName();
plan.Save(fileName);
TestPlan plan2 = TestPlan.Load(fileName);
Assert.AreEqual(plan.Steps.Count, plan2.Steps.Count);
}
[Test]
public void ChildGetMethods()
{
TestPlan target = new TestPlan();
target.Steps.Add(new TestStepTest());
target.Steps.Add(new TestStepTest2());
target.Steps.Add(new TestStepTest { Enabled = false });
var recTest = new RecursiveTestStep();
target.Steps.Add(recTest);
var recursivelyLast = new TestStepTest3 { Enabled = false };
recTest.ChildTestSteps.Add(new TestStepTest3());
recTest.ChildTestSteps.Add(recursivelyLast);
try
{
recTest.ChildTestSteps.Add(new TestStepTest());
//throw new Exception("test exception"); saved for future conformance testing
}
catch (Exception e)
{
Assert.AreNotEqual(e.Message, "test exception");
}
Assert.AreEqual(2, recTest.RecursivelyGetChildSteps(TestStepSearch.All).Count());
Assert.AreEqual(6, target.Steps.RecursivelyGetAllTestSteps(TestStepSearch.All).Count());
Assert.AreEqual(4, target.Steps.RecursivelyGetAllTestSteps(TestStepSearch.EnabledOnly).Count());
// saved for future.. Assert.AreEqual(2, target.Steps.RecursivelyGetAllTestSteps(TestStep.Search.NotEnabledOnly).Count());
Assert.AreEqual(recursivelyLast, target.Steps.RecursivelyGetAllTestSteps(TestStepSearch.All).Last());
}
///
///A test for Run
///
[Test]
[Pairwise]
public void RunTest([Values(false, true)] bool open)
{
TestTraceListener trace = new TestTraceListener();
Log.AddListener(trace);
TestPlan target = new TestPlan();
target.Steps.Add(new TestStepTest());
ResultOrderTester orderTester = new ResultOrderTester();
ResultSettings.Current.Add(orderTester);
PlanRunCollectorListener pl = new PlanRunCollectorListener();
if (open)
target.Open();
var planRun = target.Execute(new IResultListener[] { orderTester, pl });
if (open)
{
Assert.IsTrue(orderTester.IsConnected);
Assert.IsTrue(pl.IsConnected);
target.Close();
}
Assert.AreEqual(true, orderTester.StepRunCompletedRan);
Assert.AreEqual(true, orderTester.PlanRunCompletedRan);
Log.RemoveListener(trace);
ResultSettings.Current.Remove(orderTester);
trace.AssertErrors(new string[] { "No instruments found.", "Keysight Internal! This version is not licensed. Do not distribute outside Keysight." });
Assert.AreEqual(Verdict.Pass, planRun.Verdict);
Assert.AreEqual(Verdict.Pass, pl.StepRuns.First().Verdict);
ResultSettings.Current.Clear();
Assert.AreEqual(0, orderTester.Errors.Count());
}
[Test]
public void PrintTestPlanRunSummaryTest()
{
var testFile = new byte[1500];
var testFileName = $"OpenTap.UnitTests.{nameof(PrintTestPlanRunSummaryTest)}.testFile.bin";
File.Delete(testFileName);
File.WriteAllBytes(testFileName, testFile);
TestTraceListener trace = new TestTraceListener();
Log.AddListener(trace);
TestPlan target = new TestPlan();
target.PrintTestPlanRunSummary = true;
target.Steps.Add(new TestStepTest() {PublishArtifact = testFileName});
PlanRunCollectorListener pl = new PlanRunCollectorListener();
var planRun = target.Execute(ResultSettings.Current.Concat(new IResultListener[] { pl }));
Log.Flush();
Log.RemoveListener(trace);
var summaryLines = trace.allLog.ToString().Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries).Where(l => l.Contains(": Summary ")).ToArray();
Assert.AreEqual(6, summaryLines.Count(), "Did not find the expected number of summary lines in the log.");
Assert.AreEqual(Verdict.Pass, pl.StepRuns.First().Verdict);
Assert.IsTrue(summaryLines[4].Contains("1 artifacts registered"));
Assert.IsTrue(summaryLines[5].Contains(testFileName));
Assert.IsTrue(summaryLines[5].EndsWith("[1.5 kB]"));
}
[Test]
public void TestRunSelectedResultParameter([Values(null, 1, 3)] int? runSelected)
{
var plan = new TestPlan();
var steps = Enumerable.Range(0, 10).Select(_ => new VerdictStep()).ToArray();
plan.ChildTestSteps.AddRange(steps);
HashSet selectedSteps = null;
if (runSelected.HasValue)
selectedSteps = new HashSet(steps.Take(runSelected.Value));
var results = plan.Execute( Array.Empty(), Array.Empty(), selectedSteps).Parameters;
var result = results.FirstOrDefault(param => param.Name == TestPlanRun.SpecialParameterNames.StepOverrideList);
if (runSelected.HasValue)
{
Assert.That(result, Is.Not.Null);
var ids = result.Value.ToString().Split(',');
Assert.That(ids.Length, Is.EqualTo(runSelected.Value));
for (int i = 0; i < runSelected; i++)
{
var id = steps[i].Id.ToString();
Assert.That(ids.Any(r => r == id));
}
}
else
{
Assert.That(result, Is.Null);
}
}
[Test]
public void TestBreakConditionResultParameter([Values(true, false)] bool doBreak)
{
var l = new PlanRunCollectorListener();
var plan = new TestPlan();
var sequenceStep = new SequenceStep();
plan.ChildTestSteps.Add(sequenceStep);
BreakConditionProperty.SetBreakCondition(sequenceStep, BreakCondition.BreakOnFail);
var verdictStep = new VerdictStep() { VerdictOutput = doBreak ? Verdict.Fail : Verdict.Pass };
sequenceStep.ChildTestSteps.Add(verdictStep);
var run = plan.Execute(new[] { l });
var breakResult = run.Parameters.FirstOrDefault(param => param.Name == TestPlanRun.SpecialParameterNames.BreakIssuedFrom);
if (doBreak)
{
var stepRun = l.StepRuns.First(r => r.TestStepId == verdictStep.Id);
Assert.That(breakResult, Is.Not.Null);
Assert.That(breakResult.Value.ToString(), Is.EqualTo(stepRun.Id.ToString()));
}
else
Assert.That(breakResult, Is.Null);
}
[Test]
public void TestCaughtBreakConditionNotPropagated([Values(true, false)] bool doCatch)
{
var l = new PlanRunCollectorListener();
var plan = new TestPlan();
var sequenceStep = new SequenceStep();
plan.ChildTestSteps.Add(sequenceStep);
var verdictStep = new VerdictStep() { VerdictOutput = Verdict.Fail };
sequenceStep.ChildTestSteps.Add(verdictStep);
BreakConditionProperty.SetBreakCondition(verdictStep, BreakCondition.BreakOnFail);
BreakConditionProperty.SetBreakCondition(plan, BreakCondition.BreakOnFail);
if (doCatch)
{
// Since sequence step breaks on error, it will 'catch' the break issued from verdictstep
BreakConditionProperty.SetBreakCondition(sequenceStep, BreakCondition.BreakOnError);
}
else
{
BreakConditionProperty.SetBreakCondition(sequenceStep, BreakCondition.BreakOnFail);
}
var run = plan.Execute(new[] { l });
var breakResult = run.Parameters.FirstOrDefault(param => param.Name == TestPlanRun.SpecialParameterNames.BreakIssuedFrom);
if (doCatch)
{
Assert.That(breakResult, Is.Null);
}
else
{
var stepRun = l.StepRuns.First(r => r.TestStepId == verdictStep.Id);
Assert.That(breakResult, Is.Not.Null);
Assert.That(breakResult.Value.ToString(), Is.EqualTo(stepRun.Id.ToString()));
}
}
[Test]
public void TestMoveSteps()
{
var plan = new TestPlan();
var par = new ParallelStep();
var repeat = new RepeatStep();
var dialog = new DialogStep();
var ifVerdict = new IfStep();
plan.ChildTestSteps.Add(par);
plan.ChildTestSteps.Add(repeat);
repeat.ChildTestSteps.Add(dialog);
repeat.ChildTestSteps.Add(ifVerdict);
{ // Set dialog as the input step for ifVerdict
ifVerdict.InputVerdict.Step = dialog;
Assert.That(ifVerdict.InputVerdict.Step, Is.EqualTo(dialog));
}
{ // Move repeat step into the parallel step
plan.ChildTestSteps.Remove(repeat);
par.ChildTestSteps.Add(repeat);
// Verify the step can still be computed after adding it back
Assert.That(ifVerdict.InputVerdict.Step, Is.EqualTo(dialog));
// after removing the dialog step the input step should be null.
repeat.ChildTestSteps.Remove(dialog);
Assert.That(ifVerdict.InputVerdict.Step, Is.Null);
}
}
[Test]
public void TestPlanStepExceptionTest()
{
var preAbortTestPlan = EngineSettings.Current.AbortTestPlan;
EngineSettings.Current.AbortTestPlan = default(EngineSettings.AbortTestPlanType);
try
{
PlanRunCollectorListener pl = new PlanRunCollectorListener();
TestPlan target = new TestPlan();
target.Steps.Add(new TestStepExceptionTest());
target.Steps.Add(new TestStepTest());
ResultSettings.Current.Add(pl);
target.Execute();
ResultSettings.Current.Remove(pl);
Assert.AreEqual(Verdict.Error, pl.StepRuns.ElementAt(0).Verdict, "Exception in teststep did not cause verdict to become 'Error'.");
Assert.AreEqual(Verdict.Pass, pl.StepRuns.ElementAt(1).Verdict, "TestStep did not pass after previous step caused an exception.");
ResultSettings.Current.Clear();
}
finally
{
EngineSettings.Current.AbortTestPlan = preAbortTestPlan;
}
}
[Test]
public void TestPlanExecuteResultListenerCheck()
{
// Verifies that only the ResultListeners added in execute are used, if that overload is selected.
PlanRunCollectorListener pl1 = new PlanRunCollectorListener();
PlanRunCollectorListener pl2 = new PlanRunCollectorListener();
TestPlan target = new TestPlan();
target.Steps.Add(new TestStepTest());
ResultSettings.Current.Add(pl2);
target.Open(new[] { pl1 });
target.Execute(new[] { pl1 });
target.Close();
target.Execute(new[] { pl1 });
ResultSettings.Current.Remove(pl2);
Assert.AreEqual(0, pl2.StepRuns.Count);
Assert.AreEqual(2, pl1.StepRuns.Count);
Assert.AreEqual(true, pl1.WasOpened);
Assert.AreEqual(false, pl2.WasOpened);
}
[Test]
public void OpenAdditionalResources()
{
var instr1 = new DummyInstrument();
var instr2 = new DummyInstrument();
var instr3 = new DummyInstrument();
var plan = new TestPlan();
plan.Open();
Assert.IsFalse(instr1.IsConnected);
Assert.IsFalse(instr2.IsConnected);
Assert.IsFalse(instr3.IsConnected);
plan.Open(new IResource[]{instr1,instr2});
Assert.IsTrue(instr1.IsConnected);
plan.Open(new IResource[]{instr3,instr2});
Assert.IsTrue(instr3.IsConnected);
Assert.IsTrue(instr2.IsConnected);
Assert.IsTrue(instr1.IsConnected);
plan.Close();
Assert.IsFalse(instr1.IsConnected);
Assert.IsFalse(instr2.IsConnected);
Assert.IsFalse(instr3.IsConnected);
}
[Test]
public void TestPlanLogErrorTest()
{
TestTraceListener trace = new TestTraceListener();
Log.AddListener(trace);
TestPlan target = new TestPlan();
target.Steps.Add(new TestStepTest());
var planRun = target.Execute();
Log.RemoveListener(trace);
string logStr = trace.GetLog();
Assert.IsFalse(Regex.IsMatch(logStr, "TestPlan\\s+; Error"));
}
[Test]
public void TestPlanResourceCrashTest()
{
var inst = new InstrumentTest() {CrashPhase = InstrumentTest.InstrPhase.Open};
var step = new InstrumentTestStep();
step.Instrument = inst;
var plan = new TestPlan();
plan.ChildTestSteps.Add(step);
string logString = null;
InstrumentSettings.Current.Add(inst);
try
{
TestTraceListener trace = new TestTraceListener();
Log.AddListener(trace);
var run = plan.Execute();
Log.Flush();
logString = trace.GetLog();
var err = trace.ErrorMessage;
Assert.AreEqual(Verdict.Error, run.Verdict);
// improvement: only one error in the log.
Assert.AreEqual(2, err.Count);
// the log should not be really long.
Assert.IsTrue(logString.Split('\n').Length < 70);
}
finally
{
InstrumentSettings.Current.Remove(inst);
}
}
[Test]
public void TestPlanSaveLoadTest()
{
TestPlan target = new TestPlan();
TestStepTest step1 = new TestStepTest();
step1.TestPoints.Add(new TestStepTest.TestPoint { Channel = 12 });
target.Steps.Add(step1);
TestStepTest2 step2 = new TestStepTest2();
step2.TestPoints.Add(new TestStepTest2.TestPoint { });
step2.ObjectProperty = new TestStepTest2.SomeObject { };
target.Steps.Add(step2);
SubSpace.TestStepTest step3 = new SubSpace.TestStepTest();
target.Steps.Add(step3);
TestPlan loadedPlan;
string xmlText;
using (Stream str = new MemoryStream(20000))
using (StreamReader reader = new StreamReader(str))
{
target.Save(str);
str.Seek(0, 0);
xmlText = reader.ReadToEnd();
str.Seek(0, 0);
loadedPlan = TestPlan.Load(str,target.Path);
}
Assert.AreEqual(loadedPlan.Steps.Count, target.Steps.Count);
Assert.AreEqual(loadedPlan.Steps[0].Name, target.Steps[0].Name);
}
[Test]
public void TestPlan_AbortIfNoResource()
{
PlanRunCollectorListener pl = new PlanRunCollectorListener();
TestPlan target = new TestPlan();
ScpiTestStep step1 = new ScpiTestStep();
target.Steps.Add(step1);
step1.Instrument = null;
var t = Task.Factory.StartNew(() =>
{
return target.Execute(ResultSettings.Current.Concat(new IResultListener[] { pl }));
});
t.Wait(1000);
if (t.Status == TaskStatus.Running)
Assert.Fail("TestPlan never completed.");
Assert.AreEqual(0, pl.StepRuns.Count(), "Step was run although resource was not set");
}
[Test]
public void TestResultOrderTester()
{
ResultOrderTester tester = new ResultOrderTester();
var testTestPlanRun = new TestPlanRun(new TestPlan(), new List(), DateTime.Now, Stopwatch.GetTimestamp());
try
{
tester.OnTestPlanRunCompleted(testTestPlanRun, new MemoryStream());
throw new Exception("This should not happen");
}
catch (InvalidOperationException)
{
}
try
{
TestStepRun stepRun = new TestStepRun(new TestStepTest(), testTestPlanRun.Id);
tester.OnTestStepRunCompleted(stepRun);
throw new Exception("This should not happen");
}
catch (InvalidOperationException)
{
}
var errs = tester.Errors;
Assert.AreEqual(2, errs.Count());
}
class HashCheckResultListener : ResultListener
{
TestPlan plan;
public HashCheckResultListener(TestPlan plan) => this.plan = plan;
public override void OnTestPlanRunStart(TestPlanRun planRun)
{
string getHash(string testPlanXml)
{
// this is the same code as inside TestPlanRun.
using (var algo = System.Security.Cryptography.SHA1.Create())
return BitConverter.ToString(algo.ComputeHash(System.Text.Encoding.UTF8.GetBytes(testPlanXml)), 0, 8).Replace("-", string.Empty);
}
var newxml = new TapSerializer().SerializeToString(plan);
if (newxml != planRun.TestPlanXml)
planRun.MainThread.Abort(); // cause Verdict.Abort.
if(planRun.Hash != getHash(newxml))
planRun.MainThread.Abort();
}
}
[Test]
public void TestPlanHash()
{
var plan = new TestPlan();
var step = new LogStep { LogMessage = "A" };
plan.ChildTestSteps.Add(step);
plan.Open(new[] { new HashCheckResultListener(plan) });
Assert.AreEqual(Verdict.NotSet, plan.Execute().Verdict);
step.LogMessage = "B";
Assert.AreEqual(Verdict.NotSet, plan.Execute().Verdict);
plan.Close();
}
class AbortImmediatelyResultListener : ResultListener
{
public override void OnTestPlanRunStart(TestPlanRun planRun) => planRun.MainThread.Abort();
}
[Test]
public void TestPlanAbortFromResultListener()
{
var plan = new TestPlan();
plan.ChildTestSteps.Add(new DelayStep());
var run = plan.Execute(new[] { new AbortImmediatelyResultListener() });
Assert.AreEqual(Verdict.Aborted, run.Verdict);
}
/// Tests to see if a prerun error in a teststep results in the run of the result listeners.
[Test]
public void TestStepPrePostRunExceptionsIntoResultListener()
{
PlanRunCollectorListener pl = new PlanRunCollectorListener();
TestPlan testPlan = new TestPlan();
testPlan.ChildTestSteps.Add(new TestStepPreExceptionTest());
ResultSettings.Current.Add(pl);
testPlan.Execute();
ResultSettings.Current.Remove(pl);
Assert.AreEqual(0, pl.StepRuns.Count);
Assert.AreEqual(1, pl.PlanRuns.Count);
Assert.AreEqual(true, pl.PlanRuns.ElementAt(0).FailedToStart);
}
[Test]
public void MissingInstReferencesIntoResultListener()
{
InstrumentSettings.Current.Clear();
PlanRunCollectorListener pl = new PlanRunCollectorListener();
TestPlan testPlan = new TestPlan();
ScpiTestStep step = new ScpiTestStep();
testPlan.ChildTestSteps.Add(step);
ResultSettings.Current.Add(pl);
var tpr = testPlan.Execute();
ResultSettings.Current.Remove(pl);
Assert.IsTrue(tpr.FailedToStart);
}
[Test]
public void InstExceptionIntoResultListener()
{
PlanRunCollectorListener pl = new PlanRunCollectorListener();
TestPlan testPlan = new TestPlan();
InstrumentTestStep step = new InstrumentTestStep();
InstrumentTest openCrash = new InstrumentTest { CrashPhase = InstrumentTest.InstrPhase.Open };
step.Instrument = openCrash;
testPlan.ChildTestSteps.Add(step);
ResultSettings.Current.Add(pl);
var planrun = testPlan.Execute();
ResultSettings.Current.Remove(pl);
Assert.AreEqual(1, pl.StepRuns.Count);
Assert.AreEqual(1, pl.PlanRuns.Count);
Assert.AreEqual(Verdict.Error, pl.PlanRuns.ElementAt(0).Verdict);
}
[Test]
public void BadResultListenerIntoGoodResultListener()
{
PlanRunCollectorListener pl = new PlanRunCollectorListener();
TestPlan testPlan = new TestPlan();
testPlan.ChildTestSteps.Add(new TestStepTest());
ResultSettings.Current.Add(pl);
// Now, we add a resultlistener that has exceptions
var erl = new resultListenerCrash { CrashResultPhase = resultListenerCrash.ResultPhase.Open };
ResultSettings.Current.Add(erl);
testPlan.Execute();
ResultSettings.Current.Clear();
Assert.AreEqual(0, pl.StepRuns.Count);
Assert.AreEqual(1, pl.PlanRuns.Count);
Assert.AreEqual(true, pl.PlanRuns.ElementAt(0).FailedToStart);
}
///
/// Run child steps in a loop.
///
[AllowAnyChild]
class RepeatRunChildSteps : TestStep
{
public int Repeats { get; set; }
public override void Run()
{
for(int i = 0; i < Repeats; i++)
{
RunChildSteps();
}
}
}
///
/// A result listener that just takes a bit of time in the result processing thread.
/// Not too much or the test is too slow.
///
class SlowResultListener : ResultListener
{
public override void OnTestStepRunCompleted(TestStepRun stepRun)
{
base.OnTestStepRunCompleted(stepRun);
}
public override void OnTestStepRunStart(TestStepRun stepRun)
{
base.OnTestStepRunStart(stepRun);
TapThread.Sleep(10);
}
}
///
/// Issue (#3898) is caused by a slight delay in the result processing thread that provokes a race condition
/// this affects steps that run child steps multiple times without waiting for defer completion.
///
[Test]
public void RepeatChildStepsFailure()
{
var plan = new TestPlan();
var repeat = new RepeatRunChildSteps { Repeats = 10 };
var repeat2 = new RepeatRunChildSteps { Repeats = 10 };
repeat2.ChildTestSteps.Add(new SequenceStep());
repeat.ChildTestSteps.Add(repeat2);
plan.ChildTestSteps.Add(repeat);
var run = plan.Execute(new IResultListener[] { new SlowResultListener() });
Assert.AreEqual(Verdict.NotSet, run.Verdict);
}
///
/// Tests that if the SuggestedNextStep is set to the current ID, thn the testStep will be repeated.
///
[Test]
public void SuggestedNextStepTest()
{
using (Session.Create(SessionOptions.OverlayComponentSettings))
{
var plan = new TestPlan();
var parallelStep = new ParallelStep();
var nextStepRepeater = new SuggestedNextStepRepeater();
nextStepRepeater.RepeatCount = 10;
parallelStep.ChildTestSteps.Add(nextStepRepeater);
plan.ChildTestSteps.Add(parallelStep);
PlanRunCollectorListener planRunListener = new PlanRunCollectorListener();
ResultSettings.Current.Add(planRunListener);
var planRun = plan.Execute();
var stepRuns = planRunListener.StepRuns;
//We compare repeatcount + 1 as the parallelStep is also counted in StepsRun.
Assert.AreEqual(nextStepRepeater.RepeatCount + 1, stepRuns.Count);
}
}
public class SuggestedNextStepRepeater : TestStep
{
public int RepeatCount { get; set; }
public int Repeats { get; set; }
public override void Run()
{
if(Repeats < RepeatCount -1)
{
this.StepRun.SuggestedNextStep = this.Id;
Repeats++;
}
}
}
public class VerifyTestStep : TestStep
{
public static int Value = 0;
public int Prop { get; set; }
public VerifyTestStep()
{
Prop = 123;
}
public override void Run()
{
Value = Prop;
}
}
class PrePostPlanTestStep : TestStep
{
public bool VerifyPrePlanRun;
public bool VerifyPostPlanRun;
public bool VerifyRun;
public bool VerifyDefer;
public override void PrePlanRun()
{
base.PrePlanRun();
VerifyPrePlanRun = false;
VerifyPostPlanRun = false;
VerifyRun = false;
VerifyDefer = false;
Assert.IsNotNull(PlanRun);
Assert.IsNull(StepRun);
VerifyPrePlanRun = true;
}
public override void Run()
{
VerifyRun = true;
Assert.IsNotNull(PlanRun);
Assert.IsNotNull(StepRun);
Results.Defer(() =>
{
Assert.IsNotNull(PlanRun);
Assert.IsNotNull(StepRun);
VerifyDefer = true;
UpgradeVerdict(Verdict.Pass);
});
}
public override void PostPlanRun()
{
base.PostPlanRun();
Assert.IsNotNull(PlanRun);
Assert.IsNull(StepRun);
VerifyPostPlanRun = true;
}
}
[Test]
public void VerifyPrePostPlanRun()
{
var tp = new TestPlan();
var step = new PrePostPlanTestStep();
tp.ChildTestSteps.Add(step);
for (int i = 0; i < 4; i++)
{
var run = tp.Execute();
Assert.AreEqual(Verdict.Pass, run.Verdict);
Assert.IsTrue(step.VerifyDefer);
Assert.IsTrue(step.VerifyRun);
Assert.IsTrue(step.VerifyPostPlanRun);
Assert.IsTrue(step.VerifyPrePlanRun);
}
}
class DeferredResultsStep : TestStep
{
public bool IsDone = false;
public Semaphore sem = new Semaphore(0, 1);
public Semaphore canFinish = new Semaphore(0, 1);
public override void Run()
{
Results.Defer(() =>
{
sem.Release();
canFinish.WaitOne();
});
}
}
[Test]
public void DeferAndAbort([Values(true, false)] bool WrapSequence)
{
var defer = new DeferredResultsStep();
var plan = new TestPlan();
if (WrapSequence)
{
var sequenceStep = new SequenceStep();
sequenceStep.ChildTestSteps.Add(defer);
plan.ChildTestSteps.Add(sequenceStep);
}
else
{
plan.ChildTestSteps.Add(defer);
}
var sem2 = new Semaphore(0, 1);
var thread = TapThread.Start(() =>
{
try
{
plan.Execute();
}
finally
{
sem2.Release();
}
});
defer.sem.WaitOne();
thread.Abort();
try
{
bool hangs = sem2.WaitOne(50) == false;
Assert.IsTrue(hangs, "Deferred results step should wait.");
}
finally
{
defer.canFinish.Release();
}
bool isDone = sem2.WaitOne(10000);
if(!isDone)
Assert.Fail("Test plan timed out");
Assert.IsTrue(isDone);
}
[Test]
public void RecursiveTestPlanReferenceTest()
{
TestPlan plan = new TestPlan();
var step = new TestPlanReference();
string filePath = "plan.TestPlan";
step.Filepath.Text = filePath;
plan.Steps.Add(step);
plan.Save(filePath);
// this threw an exception at one point.
step.LoadTestPlan();
TestTraceListener trace = new TestTraceListener();
Log.AddListener(trace);
var plan2 = TestPlan.Load(filePath);
Log.RemoveListener(trace);
StringAssert.Contains("Test plan reference is trying to load itself leading to recursive loop.", trace.GetLog());
File.Delete(filePath);
}
[Test]
public void ParameterizedTestPlanReferenceTest()
{
var path = Path.GetTempFileName();
{ // Create test plan with external parameter
var subplan = new TestPlan();
var logStep = new LogStep() { LogMessage = "Initial Value" };
var logInfo = TypeData.GetTypeData(logStep);
subplan.ChildTestSteps.Add(logStep);
subplan.ExternalParameters.Add(logStep, logInfo.GetMember(nameof(logStep.LogMessage)));
subplan.Save(path);
}
try
{ // Add the created test plan as a child step of a sweep loop
var reference = new TestPlanReference { Filepath = { Text = path } };
var sweep = new SweepLoop() {
ChildTestSteps =
{
reference
}
};
string GetLogMessageParameter(AnnotationCollection a)
{
var avail = a.Get().Members.FirstOrDefault(m => m.Name == "Sweep Parameters")
?.Get();
var v = avail?.AvailableValues.FirstOrDefault(v => v.Name == "ExpandedMemberData");
return v?.Get()?.Value;
}
var a = AnnotationCollection.Annotate(sweep);
Assert.That(GetLogMessageParameter(a), Is.Null);
reference.LoadTestPlan();
a.Read();
Assert.That(GetLogMessageParameter(a), Is.EqualTo("Log Message"));
}
finally
{
File.Delete(path);
}
}
[Test]
public void RelativeTestPlanTest()
{
int depth = 10;
string path = "RelativeTestPlanTest_TestDir";
for (int i = 0; i < depth; i++)
{
TestPlan plan = new TestPlan();
Directory.CreateDirectory(path);
var filepath = Path.Combine(path, "plan.TestPlan");
if (i < depth - 1)
{
var step = new TestPlanReference();
step.Filepath.Text = "/TestDir/plan.TestPlan";
plan.Steps.Add(step);
}
else
{
var step = new DelayStep();
step.DelaySecs = 0.001;
plan.Steps.Add(step);
}
plan.Save(filepath);
path = Path.Combine(path, "TestDir");
}
var bigplan = TestPlan.Load("RelativeTestPlanTest_TestDir/plan.TestPlan");
var run = bigplan.Execute();
Assert.AreEqual(depth, run.StepsWithPrePlanRun.Count);
Directory.Delete("RelativeTestPlanTest_TestDir", true);
}
[Test]
public void SweepDynamic()
{
string TempName = Path.GetTempFileName();
TestPlan tp = new TestPlan();
tp.ChildTestSteps.Add(new VerifyTestStep());
var delaystep = tp.ChildTestSteps.First() as VerifyTestStep;
tp.ExternalParameters.Add(delaystep, TypeData.GetTypeData(delaystep).GetMember("Prop"), "Prop");
tp.Save(TempName);
// Sanity check to verify that the step itself works
tp.ExternalParameters.Entries[0].Value = 1234;
VerifyTestStep.Value = 0;
var planrun = tp.Execute();
Assert.AreEqual(1234, VerifyTestStep.Value, "Wrong value before serialization");
// Test running after construction by code
tp = new TestPlan();
var step = new OpenTap.Plugins.BasicSteps.TestPlanReference();
tp.ChildTestSteps.Add(step);
step.Filepath.Text = TempName;
step.LoadTestPlan();
// The step is now replaced
ITestStep step2 = tp.ChildTestSteps.First();
Assert.AreEqual(1, step2.ChildTestSteps.Count, "Number of childsteps");
Assert.IsNotNull(TypeData.GetTypeData(step2).GetMember("Prop"), "Could not find external parameter property on testplan");
var sweep = new OpenTap.Plugins.BasicSteps.SweepLoop();
tp.ChildTestSteps.Add(sweep);
tp.ChildTestSteps.Remove(step2);
sweep.ChildTestSteps.Add(step2);
sweep.SweepParameters = new List { new OpenTap.Plugins.BasicSteps.SweepParam(new List { TypeData.GetTypeData(step2).GetMember("Prop") }, 1337) };
VerifyTestStep.Value = 0;
tp.Execute();
Assert.AreEqual(1337, VerifyTestStep.Value, "Wrong value after first run");
string TempName2 = Path.GetTempFileName();
tp.Save(TempName2);
// Test running after deserialization
tp = TestPlan.Load(TempName2);
VerifyTestStep.Value = 0;
tp.Execute();
Assert.AreEqual(1337, VerifyTestStep.Value, "Wrong value after deserialization");
}
[Test]
public void NullResourceTest()
{
InstrumentSettings.Current.Clear();
try
{
InstrumentSettings.Current.Add(new InstrumentTest() { CrashPhase = InstrumentTest.InstrPhase.Open });
// Plan with 4 steps.
//> Sequence disabled
// > Scpi enabled
//> Scpi disabled
//> LogStep enabled
TestPlan plan = new TestPlan();
ScpiTestStep step1 = new ScpiTestStep { Enabled = true, Instrument = null };
LogStep step2 = new LogStep { Enabled = true };
ScpiTestStep step3 = new ScpiTestStep { Enabled = false, Instrument = null };
InstrumentTestStep step4 = new InstrumentTestStep() { Instrument = InstrumentSettings.Current.First(), Enabled = false };
SequenceStep parent = new SequenceStep { Enabled = false };
parent.ChildTestSteps.Add(step1);
plan.ChildTestSteps.Add(parent);
plan.ChildTestSteps.Add(step2);
plan.ChildTestSteps.Add(step3);
plan.ChildTestSteps.Add(step4);
var planRun = plan.Execute();
Assert.AreEqual(Verdict.NotSet, planRun.Verdict);
plan.Open();
Assert.IsFalse(InstrumentSettings.Current.First().IsConnected);
var planRun2 = plan.Execute();
plan.Close();
Assert.AreEqual(Verdict.NotSet, planRun2.Verdict);
}
finally
{
InstrumentSettings.Current.Clear();
}
}
public class DelegateStep : TestStep
{
[System.Xml.Serialization.XmlIgnore]
[Browsable(true)]
public Func Action { get; set; }
[Browsable(true)]
[System.Xml.Serialization.XmlIgnore]
public Action Action2 { get; set; }
[Browsable(true)]
public void Action3()
{
Log.Info("Action3 called");
}
public DelegateStep()
{
Action2 = () => Log.Info("Action2 called");
Action = () => Verdict.Error;
}
public override void Run()
{
Thread.Sleep(10);
Verdict = Action();
Thread.Sleep(10);
}
}
[Test]
public void ChildITestStepLazy()
{
var prevResourceManager = EngineSettings.Current.ResourceManagerType;
try
{
EngineSettings.Current.ResourceManagerType = new LazyResourceManager();
ChildITestStep();
}
finally
{
EngineSettings.Current.ResourceManagerType = prevResourceManager;
}
}
[Test]
public void ChildITestStep()
{
// Trigger issue when running child steps that is an ITestStep.
SequenceStep seq = new SequenceStep();
TestPlan plan = new TestPlan();
var instr = new InterfaceTestInstrument();
Verdict checkInstr()
{
if (EngineSettings.Current.ResourceManagerType is LazyResourceManager)
{
return instr.IsConnected ? Verdict.Fail : Verdict.Pass;
}
return instr.IsConnected ? Verdict.Pass : Verdict.Fail;
}
var step = new TestITestStep();
step.Instrument = instr;
plan.Steps.Add(new DelegateStep { Action = checkInstr });
plan.Steps.Add(seq);
plan.Steps.Add(new DelegateStep { Action = checkInstr });
seq.ChildTestSteps.Add(step);
var run = plan.Execute();
Assert.IsTrue(run.Verdict == Verdict.Pass);
Assert.IsTrue(step.WasRun);
// Trigger issue with cycle on step.Parent / step.Parent.ChildTestSteps.
TestTraceListener trace = new TestTraceListener();
Log.AddListener(trace);
var file = "ChildITestStep.TapPlan";
plan.Save(file);
Log.RemoveListener(trace);
File.Delete(file);
StringAssert.Contains("Cycle detected", trace.GetLog());
// Trigger possible issue with null Name.
Assert.IsTrue(string.IsNullOrWhiteSpace(step.GetFormattedName()));
}
[Test]
public void TestChildStepsChanged()
{
List lst = new List();
var a = new SweepLoop();
a.ChildTestSteps.ChildStepsChanged += (s, action, obj, index) => lst.Add(action);
var b = new SequenceStep();
var c = new SequenceStep();
a.ChildTestSteps.Add(b);
Assert.AreEqual(1, lst.Count);
var tl2 = new TestStepList();
tl2.ChildStepsChanged += (s, action, obj, index) => lst.Add(action);
tl2.Add(c);
a.ChildTestSteps = tl2;
c.ChildTestSteps.Add(b);
c.ChildTestSteps.Remove(b);
c.ChildTestSteps = new TestStepList();
Assert.AreEqual(6, lst.Count);
Assert.IsTrue(lst.Contains(TestStepList.ChildStepsChangedAction.ListReplaced));
Assert.IsTrue(lst.Contains(TestStepList.ChildStepsChangedAction.RemovedStep));
Assert.IsTrue(lst.Contains(TestStepList.ChildStepsChangedAction.AddedStep));
Assert.AreEqual(2, lst.Count(x => x == TestStepList.ChildStepsChangedAction.ListReplaced));
}
/// Test running a sub-section of a test plan with an overload of TestPlan.Execute.
[Test]
public void SubPlanRunTest()
{
SequenceStep seq = new SequenceStep();
TestPlan plan = new TestPlan();
var step = new TestITestStep
{
Instrument = new InterfaceTestInstrument()
};
plan.Steps.Add(seq);
seq.ChildTestSteps.Add(step);
var run = plan.Execute(ResultSettings.Current, stepsOverride: new HashSet(new[] { step }));
Assert.IsTrue(run.Verdict < Verdict.Pass);
Assert.IsTrue(step.WasRun);
Assert.AreEqual(1, run.StepsWithPrePlanRun.Count);
}
[AllowAnyChild]
internal class TestSubStep : TestStep
{
internal static int PreRuns = 0;
internal static int Runs = 0;
public override void PrePlanRun()
{
base.PrePlanRun();
PreRuns++;
}
public override void PostPlanRun()
{
base.PostPlanRun();
}
public override void Run()
{
Runs++;
RunChildSteps();
}
internal static void Clear()
{
PreRuns = 0;
Runs = 0;
}
}
/// Test running a sub-section of a test plan with an overload of TestPlan.Execute.
[Test]
public void SubPlanRunTest2()
{
TestPlan plan = new TestPlan();
TestSubStep.Clear();
var s = new TestSubStep();
s.ChildTestSteps.Add(new TestSubStep());
plan.ChildTestSteps.Add(s);
plan.ChildTestSteps.Add(new TestSubStep());
var run = plan.Execute(ResultSettings.Current, stepsOverride: new HashSet(plan.ChildTestSteps));
Assert.AreEqual(3, TestSubStep.PreRuns);
Assert.AreEqual(3, TestSubStep.Runs);
Assert.AreEqual(3, run.StepsWithPrePlanRun.Count);
}
/// Test running a sub-section of a test plan with an overload of TestPlan.Execute.
[Test]
public void SubPlanRunTest3()
{
TestPlan plan = new TestPlan();
TestSubStep.Clear();
var s = new TestSubStep();
var sub = new TestSubStep();
s.ChildTestSteps.Add(sub);
plan.ChildTestSteps.Add(s);
plan.ChildTestSteps.Add(new TestSubStep());
var run = plan.Execute(ResultSettings.Current, stepsOverride: new HashSet(new [] { sub, sub, sub }));
Assert.AreEqual(1, TestSubStep.PreRuns);
Assert.AreEqual(1, TestSubStep.Runs);
Assert.AreEqual(1, run.StepsWithPrePlanRun.Count);
}
[Test]
public void ComponentSettingMetadataTest()
{
UserInput.SetInterface(new ComponentSettingPrompt());
try
{
TestComponentSettingList.Current.Clear();
TestComponentSettingList.Current.Add(new TestComponentSetting());
EngineSettings.Current.PromptForMetaData = true;
{
var plan = new TestPlan();
plan.Steps.Add(new ComponentSettingStep());
var run = plan.Execute();
Assert.IsFalse(run.FailedToStart);
Assert.AreEqual(Verdict.Pass, run.Verdict);
}
TestComponentSettingList.Current.OfType().ForEach(f => f.MetaComment = "Test meta data comment");
{
var plan = new TestPlan();
plan.Steps.Add(new ComponentSettingStep());
{
var run = plan.Execute();
Assert.IsFalse(run.FailedToStart);
Assert.AreEqual(Verdict.Pass, run.Verdict);
}
}
}
finally
{
UserInput.SetInterface(null);
EngineSettings.Current.PromptForMetaData = false;
}
}
class CheckAllowEditStep : OpenTap.TestStep
{
public bool? allowedEdit;
public override void Run()
{
allowedEdit = GetParent().AllowEdit;
UpgradeVerdict(Verdict.Pass);
}
}
[Test]
public void EditWhileBreak([Values(true,false)] bool allowEdit)
{
var plan = new TestPlan();
CheckAllowEditStep checkStep = new CheckAllowEditStep();
plan.ChildTestSteps.Add(checkStep);
plan.BreakOffered += PlanOnBreakOffered;
plan.AllowEditWhilePaused = allowEdit;
bool? allowedEdit = false;
void PlanOnBreakOffered(object sender, BreakOfferedEventArgs e)
{
allowedEdit = plan.AllowEdit;
}
Assert.AreEqual(Verdict.Pass, plan.Execute().Verdict);
// allow edit while paused and AllowEditWhilePaused is true.
Assert.AreEqual(allowEdit, allowedEdit);
// never allow edit while running.
Assert.AreEqual(false, checkStep.allowedEdit);
}
[Test]
public void DutPromptMetadataTest()
{
UserInput.SetInterface(new DutInfoPrompt());
try
{
DutSettings.Current.Clear();
DutSettings.Current.Add(new TestDut());
EngineSettings.Current.PromptForMetaData = true;
{
var plan = new TestPlan();
plan.Steps.Add(new DutStep() { Dut = DutSettings.Current.OfType().FirstOrDefault() });
var run = plan.Execute();
Assert.IsFalse(run.FailedToStart);
Assert.AreEqual(Verdict.Pass, run.Verdict);
}
DutSettings.Current.OfType().ToList().ForEach(f => f.Comment = "test");
{
var plan = new TestPlan();
plan.Steps.Add(new DutStep() { Dut = DutSettings.Current.OfType().FirstOrDefault() });
plan.Open();
{
var run = plan.Execute();
Assert.IsFalse(run.FailedToStart);
Assert.AreEqual(Verdict.Pass, run.Verdict);
}
{
var run = plan.Execute();
Assert.IsFalse(run.FailedToStart);
Assert.AreEqual(Verdict.Pass, run.Verdict);
}
plan.Close();
}
}
finally
{
UserInput.SetInterface(null);
EngineSettings.Current.PromptForMetaData = false;
}
}
public class StepWithArray : TestStep
{
public double[] ArrayValues { get; set; } = Array.Empty();
public override void Run()
{
ArrayValues = new double[] { 1, 2, 3, 4, 5, 6 };
}
}
[Test]
public void TestStepWithArray()
{
PlanRunCollectorListener pl = new PlanRunCollectorListener();
var arrayStep = new StepWithArray();
var plan = new TestPlan();
plan.ChildTestSteps.Add(arrayStep);
var planrun = plan.Execute(new[] { pl });
var steprun = pl.StepRuns.First();
Assert.IsTrue(steprun.Parameters["ArrayValues", ""].ToString() == new NumberFormatter(System.Globalization.CultureInfo.CurrentCulture){UseRanges = false}.FormatRange(arrayStep.ArrayValues));
}
[Test]
[Ignore("This test is very unstable in limited CPU situations. (for example on a server that is already busy with other things)")]
public void TestPlanDeferError()
{
//
// This unit test verifies that the verdict of the test plan behaves predictably when a test step throws an exception or aborts the plan
// during defer actions. It also tests what happens in general when a step is being aborted when something else is happening in parallel.
//
/* // instabilities can be tested by doing a bunch of processing in other threads while testing.
bool workhard = true;
for(int i = 0; i < 16; i++)
{
TapThread.Start(() =>
{
while (workhard)
{
Enumerable.Range(0, 1000).Count().ToString().GetHashCode();
}
});
}*/
var plan = new TestPlan();
var err = new ThrowInDefer() { WaitMs = 10 };
var delay = new DelayStep() { DelaySecs = 1.0 };
plan.ChildTestSteps.Add(err);
plan.ChildTestSteps.Add(delay);
var prevAbortPlanType = EngineSettings.Current.AbortTestPlan;
var sw = Stopwatch.StartNew();
// The abort mode decides which verdict the plan but also the delay step will get.
var abortModes = new[] { EngineSettings.AbortTestPlanType.Step_Error, (EngineSettings.AbortTestPlanType)0 };
foreach(var abortMode in abortModes)
{
EngineSettings.Current.AbortTestPlan = abortMode;
for (int i = 0; i < 5; i++)
{
bool isErrorDuringDelay = i != 4;
err.Throw = i % 2 == 0;
if (!isErrorDuringDelay)
{
// in this case the error will happen after delay has run
err.WaitMs = 50;
delay.DelaySecs = 0;
}
else
{
// in this case the error will happen during delay
err.WaitMs = 10;
delay.DelaySecs = 1.0;
}
if(isErrorDuringDelay && abortMode.HasFlag(EngineSettings.AbortTestPlanType.Step_Error) == false)
{
// since we dont abort on step errors, we dont want to wait 1s for this.
delay.DelaySecs = 0.1;
}
for (int iterations = 0; iterations < 2; iterations++)
{
// Iterate a bit to faster catch race condition errors.
PlanRunCollectorListener pl = new PlanRunCollectorListener();
TestPlanRun planRun = plan.ExecuteAsync(new[] { (IResultListener)pl }, null, null, CancellationToken.None).Result;
var delayRun = pl.StepRuns.FirstOrDefault(x => x.TestStepId == delay.Id);
var errorStepRun = pl.StepRuns.FirstOrDefault(x => x.TestStepId == err.Id);
// The error step verdict is always 'Error'.
Assert.AreEqual(Verdict.Error, errorStepRun.Verdict);
// The plan verdict depends on abortMode.
if (abortMode.HasFlag(EngineSettings.AbortTestPlanType.Step_Error))
Assert.AreEqual(Verdict.Aborted, planRun.Verdict);
else
Assert.AreEqual(Verdict.Error, planRun.Verdict);
// the verdict of delayRun depends on if the delay step completed before the error or abortMode.
if (abortMode.HasFlag(EngineSettings.AbortTestPlanType.Step_Error))
{
if (isErrorDuringDelay)
Assert.AreEqual(Verdict.Aborted, delayRun.Verdict);
else
Assert.AreEqual(Verdict.NotSet, delayRun.Verdict);
}
else
Assert.AreEqual(Verdict.NotSet, delayRun.Verdict);
}
}
}
EngineSettings.Current.AbortTestPlan = prevAbortPlanType;
Debug.WriteLine("TestPlanDeferError {0}", sw.Elapsed);
//workhard = false;
}
class ComponentSettingPrompt : IUserInputInterface
{
public void RequestUserInput(object dataObject, TimeSpan Timeout, bool modal)
{
var sub = AnnotationCollection.Annotate(dataObject).Get().Forwarded.ToArray();
foreach(string name in new []{"MetaComment", "MetaComment2"}){
var comments = sub.Where(x => x.Get().Member.Name == name);
Assert.AreEqual(1, comments.Count());
var comment = comments.First();
Assert.IsNotNull(comment != null);
comment.Get().Value = "Just another meta comment";
comment.Write();
}
}
}
public class TestComponentSettingList : ComponentSettingsList { }
public class TestComponentSetting : ComponentSettings
{
[MetaData(true)]
[Display("MetaComment", Description: "Some comment for meta data")]
public string MetaComment { get; set; }
[MetaData(true)]
[Display("MetaComment2", Description: "Some comment for meta data 2")]
public string MetaComment2 { get; set; }
public TestComponentSetting() { }
}
public class ComponentSettingStep : TestStep
{
public override void Run()
{
if (string.Compare("Just another meta comment", TestComponentSetting.Current.MetaComment, true) != 0)
throw new InvalidOperationException();
if (string.Compare("Just another meta comment", TestComponentSetting.Current.MetaComment2, true) != 0)
throw new InvalidOperationException();
UpgradeVerdict(Verdict.Pass);
}
}
class DutInfoPrompt : IUserInputInterface
{
public void RequestUserInput(object dataObject, TimeSpan Timeout, bool modal)
{
var sub = AnnotationCollection.Annotate(dataObject).Get().Forwarded.ToArray();
var comment = sub.First(x => x.Get().Member.Name == "Comment");
Assert.IsNotNull(comment != null);
comment.Get().Value = "some comment";
comment.Write();
Assert.IsNotNull(sub.First(x => x.Get().Member.Name == "ID"));
}
}
public class TestDut : Dut
{
}
public class DutStep : TestStep
{
public Dut Dut { get; set; }
public int Sleep { get; set; }
public override void Run()
{
if(Sleep != 0)
TapThread.Sleep(Sleep);
if (string.Compare("Some comment", Dut.Comment, true) != 0)
throw new InvalidOperationException();
if (Dut.IsConnected == false)
throw new InvalidOperationException("Dut is closed!");
UpgradeVerdict(Verdict.Pass);
}
}
[Test]
public void ErrorStackOverflow()
{
var repa = new RepeatStep();
var repb = new RepeatStep();
repa.TargetStep = repb;
repb.TargetStep = repa;
var error = repa.Error; // this could once throw a stackoverflow exception due to forwarded errors.
Assert.IsTrue(string.IsNullOrWhiteSpace(error));
}
[Test]
public void DeferSynchronizationManager()
{
using (var tm = new ThreadManager())
{
Stopwatch sw = Stopwatch.StartNew();
int finalCount = 10000;
var sem = new Semaphore(0, finalCount);
int counter = 0;
for (int i = 0; i < finalCount; i++)
{
tm.Enqueue(new TapThread(TapThread.Current,() =>
{
//Thread.Sleep(10);
Interlocked.Increment(ref counter);
sem.Release();
}));
}
for (int i = 0; i < finalCount; i++)
{
sem.WaitOne();
}
Assert.AreEqual(finalCount, counter);
var elapsed = sw.Elapsed;
Debug.WriteLine("{0}", elapsed.TotalMilliseconds);
}
}
///
/// This test proves that each WorkQueue can process things in sequence, while working in parallel.
///
[Test]
public void DeferSynchronization()
{
int testCount = 1000;
var tw = new WorkQueue(WorkQueue.Options.None, "Test");
var tw2 = new WorkQueue(WorkQueue.Options.None, "Test");
int counter = 0;
int counter2 = 0;
var sem = new SemaphoreSlim(0);
var sem2a = new SemaphoreSlim(0);
var sem2b = new SemaphoreSlim(0);
// enqueue a bunch of work.
for (int i = 0; i < testCount; i++)
{
tw.EnqueueWork(() =>
{
sem2a.Wait();
counter += 1;
sem.Release();
});
tw2.EnqueueWork(() =>
{
sem2b.Wait();
counter2 += 1;
sem.Release();
});
}
// Verify execution in parallel bit by bit (first half only).
for (int i = 0; i < testCount / 2; i++)
{
sem2a.Release();
sem2b.Release();
for (int j = 0; j < 2; j++)
{
if (!sem.Wait(TimeSpan.FromSeconds(10)))
{
throw new TimeoutException();
}
}
Assert.AreEqual(counter, i + 1);
Assert.AreEqual(counter2, i + 1);
}
// second half, execute as fast as possible without locks.
sem2a.Release(testCount / 2);
sem2b.Release(testCount / 2);
for(int i = 0; i < testCount/2; i++)
{
for (int j = 0; j < 2; j++)
{
if (!sem.Wait(TimeSpan.FromSeconds(10)))
{
throw new TimeoutException();
}
}
}
// verify it works at full speed.
Assert.AreEqual(testCount, counter);
Assert.AreEqual(testCount, counter2);
}
[Test]
public void RunMassiveSynchronousPlan()
{
// run an excessively long test plan.
int Count = 500;
double maxDuration = 50;
TestPlan plan = new TestPlan();
TimeGuardStep guard = new TimeGuardStep() { Timeout = maxDuration, StopOnTimeout = true };
plan.ChildTestSteps.Add(guard);
for(int i = 0; i < Count; i++)
{
var seq1 = new SequenceStep();
var verdict = new VerdictStep() { VerdictOutput = Verdict.Pass };
seq1.ChildTestSteps.Add(verdict);
guard.ChildTestSteps.Add(seq1);
}
var rl = new ResultListenerValidator();
var result = plan.Execute(new[] { rl });
Assert.AreEqual(Verdict.Pass, result.Verdict);
Assert.IsNull(rl.Exception);
}
[Test]
public void RunMassivelyParallelPlan()
{
// run an excessively long and parallel test plan.
// since its parallel, this should go very fast still.
int Count = 100;
double maxDuration = 50;
TestPlan plan = new TestPlan();
TimeGuardStep guard = new TimeGuardStep() { Timeout = maxDuration, StopOnTimeout = true };
var parallel = new ParallelStep();
plan.ChildTestSteps.Add(guard);
guard.ChildTestSteps.Add(parallel);
for (int i = 0; i < Count; i++)
{
var seq1 = new SequenceStep();
var verdict = new VerdictStep() { VerdictOutput = Verdict.Pass }; // make sure the plan gets verdict 'Pass' if everything goes well.
var delay = new DelayStep() { DelaySecs = 0.1 };
seq1.ChildTestSteps.Add(verdict);
seq1.ChildTestSteps.Add(delay);
parallel.ChildTestSteps.Add(seq1);
}
for (int i = 0; i < 2; i++)
{
var rl = new ResultListenerValidator();
var result = plan.Execute(new[] { rl });
Assert.IsNull(rl.Exception);
log.Debug(result.Duration, "Massively Parallel Plan");
Assert.AreEqual(Verdict.Pass, result.Verdict);
}
}
class DeferringStep : TestStep
{
public override void Run()
{
Results.Defer(() => { });
}
}
[Test]
public void RepeatRunDeferPlan()
{
var seq = new ParallelStep();
for (int i = 0; i < 4; i++)
{
var def = new DeferringStep();
seq.ChildTestSteps.Add(def);
}
var plan = new TestPlan();
plan.ChildTestSteps.Add(seq);
for (int i = 0; i < 100; i++)
{
var sem = new Semaphore(0, 1);
var trd = TapThread.Start(() => { plan.Execute();
sem.Release();
});
if (!sem.WaitOne(100000))
{
trd.Abort();
Assert.Fail("Deadlock occured in test plan");
}
}
}
class PlatformCheckResultListener : ResultListener
{
public bool TestPlanRunStarted;
public string CommentWas;
public string SerialWas;
public override void OnTestPlanRunStart(TestPlanRun planRun)
{
CommentWas = planRun.Parameters["Comment", "DUT"]?.ToString();
SerialWas = planRun.Parameters["Serial"]?.ToString();
TestPlanRunStarted = true;
base.OnTestPlanRunStart(planRun);
}
}
class FcnUserInput : IUserInputInterface
{
public Action