using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using System.Threading; using NUnit.Framework; using OpenTap.Diagnostic; using OpenTap.Engine.UnitTests.TestTestSteps; using OpenTap.EngineUnitTestUtils; using OpenTap.Plugins.BasicSteps; namespace OpenTap.UnitTests { public class SubPlanTest { class SubPlanResultListener : ResultListener { ResultSource proxy; public SubPlanResultListener(ResultSource proxy) => this.proxy = proxy; public override void OnResultPublished(Guid stepRunId, ResultTable result) { base.OnResultPublished(stepRunId, result); proxy.PublishTable(result); } } public class SubDut : Dut { public override void Open() { base.Open(); } public override void Close() { base.Close(); } } public class SubPlanStep : TestStep { public TestPlan Plan { get; set; } public SubDut Dut{ get; set; } public override void Run() { var planXml = Plan.SerializeToString(); using (Session.Create(SessionOptions.OverlayComponentSettings)) { var plan = Utils.DeserializeFromString(planXml); var subRun = plan.Execute(new IResultListener[] {new SubPlanResultListener(Results)}); UpgradeVerdict(subRun.Verdict); } } } public class DutStep : TestStep { public Dut Dut { get; set; } public override void Run() { Assert.IsTrue(Dut.IsConnected); } } [Test] public void TestRunningSubPlan() { using (Session.Create()) { string knownLogMessage = "asdasdasd"; var dut = new SubDut(); DutSettings.Current.Add(dut); var subPlan = new TestPlan(); subPlan.Steps.Add(new LogStep {LogMessage = knownLogMessage}); subPlan.Steps.Add(new VerdictStep {Verdict = Verdict.Pass}); subPlan.Steps.Add(new DutStep {Dut = dut}); var plan = new TestPlan(); plan.Steps.Add(new SubPlanStep {Plan = subPlan, Dut = dut}); var listener = new MemoryTraceListener(); using (Session.Create(SessionOptions.RedirectLogging)) { Log.AddListener(listener); var run = plan.Execute(); Assert.IsTrue(run.Verdict == Verdict.Pass); } Assert.IsTrue(listener.Events.Any(x => x.Message == knownLogMessage)); } } class MemoryTraceListener : ILogListener { public readonly List Events = new List(); public void EventsLogged(IEnumerable Events) { this.Events.AddRange(Events); } public void Flush() { } } [Test] public void RedirectedLogTest() { var rootListener = new MemoryTraceListener(); var sessionListener = new MemoryTraceListener(); var log = Log.CreateSource("Redirect?"); string msg1 = "This is redirected"; string msg2 = "This is redirected and from another thread"; string msg3 = "This is also redirected"; Log.AddListener(rootListener); log.Debug("This is not redirected"); var trd = TapThread.Start(() => { try { while (true) { TapThread.Sleep(1); log.Debug("Not redirected!"); } } catch { } }); using (Session.Create(SessionOptions.RedirectLogging)) { Log.AddListener(sessionListener); var sem = new Semaphore(0, 1); log.Debug(msg1); TapThread.Start(() => { // messages from a different thread should also be redirected. log.Info(msg2); sem.Release(); }); sem.WaitOne(); } log.Debug("This is also not redirected"); using (Session.Create(SessionOptions.RedirectLogging)) { Log.AddListener(sessionListener); log.Debug(msg3); } trd.Abort(); Assert.AreEqual(3, sessionListener.Events.Count); Assert.IsTrue(sessionListener.Events[0].Message == msg1); Assert.IsTrue(sessionListener.Events[1].Message == msg2); Assert.IsTrue(sessionListener.Events[2].Message == msg3); Assert.IsFalse(rootListener.Events.Any(e => e.Message == msg1)); } [Test] [Retry(3)] public void RedirectedLogTest2() { var listener = new MemoryTraceListener(); var listener1 = new MemoryTraceListener(); var listener2 = new MemoryTraceListener(); var log = Log.CreateSource("Redirect?"); string msg1 = "This is redirected"; string msg2 = "This is redirected and from another thread"; Log.AddListener(listener); log.Debug("This is not redirected0"); var sem = new Semaphore(0, 1); using (Session.Create(SessionOptions.RedirectLogging)) { Log.AddListener(listener1); log.Debug(msg1); TapThread.Start(() => { Log.AddListener(listener2); Thread.Sleep(50); // messages from a different thread should also be redirected. log.Info(msg2); sem.Release(); }); } log.Debug("This is not redirected1"); sem.WaitOne(); log.Debug("This is not redirected2"); log.Flush(); Assert.AreEqual(3, listener.Events.Count, string.Join(Environment.NewLine, listener.Events.Select(evt => evt.Message))); Assert.IsTrue(listener1.Events[0].Message == msg1); Assert.IsTrue(listener1.Events[1].Message == msg2); Assert.IsTrue(listener2.Events[0].Message == msg2); } public enum SessionEnum { Parent, SessionA, SessionB } public class TestUserInterface : IUserInputInterface { public SessionEnum SessionEnum { get; set; } public TestUserInterface(SessionEnum caller) { SessionEnum = caller; } void IUserInputInterface.RequestUserInput(object dataObject, TimeSpan timeout, bool modal) { } } void InterfaceAssert(SessionEnum expected) { var i = UserInput.GetInterface() as TestUserInterface; Assert.IsTrue(i?.SessionEnum == expected, $"User interface '{expected}' expected, but was {i?.SessionEnum.ToString() ?? "Not Set"}"); } [Test] public void RedirectedUserInputTest() { var previousInterface = UserInput.GetInterface(); try { var parentInterface = new TestUserInterface(SessionEnum.Parent); var aInterface = new TestUserInterface(SessionEnum.SessionA); var bInterface = new TestUserInterface(SessionEnum.SessionB); UserInput.SetInterface(parentInterface); InterfaceAssert(SessionEnum.Parent); // Verify the user input is correctly inherited using (Session.Create()) { InterfaceAssert(SessionEnum.Parent); UserInput.SetInterface(aInterface); InterfaceAssert(SessionEnum.SessionA); } // Verify the user input is correctly reset InterfaceAssert(SessionEnum.Parent); // Verify the user input is correctly inherited in nested contexts using (Session.Create()) { InterfaceAssert(SessionEnum.Parent); UserInput.SetInterface(aInterface); InterfaceAssert(SessionEnum.SessionA); using (Session.Create()) { InterfaceAssert(SessionEnum.SessionA); UserInput.SetInterface(bInterface); InterfaceAssert(SessionEnum.SessionB); } InterfaceAssert(SessionEnum.SessionA); } // Verify the user input is correctly reset from nested contexts InterfaceAssert(SessionEnum.Parent); } finally { UserInput.SetInterface(previousInterface); } } [Test] public void RedirectedUserInputParallelTest() { // 1: Session B sets its interface // 2: Session A verifies its interface is unchanged var e2 = new ManualResetEventSlim(false); // 3: Session A verifies its interface changes // 4: Session B verifies it still has the correct interface var e4 = new ManualResetEventSlim(false); // 5: Session A verifies it still has the correct interface var e5 = new ManualResetEventSlim(false); // 6: Session A verifies its interface has been reset to 'parent' // 7: Session B verifies its interface has been reset to 'parent' var e7 = new ManualResetEventSlim(false); var previousInterface = UserInput.GetInterface(); try { var parentInterface = new TestUserInterface(SessionEnum.Parent); var aInterface = new TestUserInterface(SessionEnum.SessionA); var bInterface = new TestUserInterface(SessionEnum.SessionB); UserInput.SetInterface(parentInterface); InterfaceAssert(SessionEnum.Parent); { // Session A TapThread.Start(() => { e2.Wait(); using (Session.Create()) { InterfaceAssert(SessionEnum.Parent); // 2: Session A verifies its interface is unchanged UserInput.SetInterface(aInterface); InterfaceAssert(SessionEnum.SessionA); // 3: Session A verifies its interface changes e4.Set(); e5.Wait(); InterfaceAssert(SessionEnum.SessionA); // 5: Session A verifies it still has the correct interface } InterfaceAssert(SessionEnum.Parent); // 6: Session A verifies its interface has been reset to 'parent' e7.Set(); }); } { // Session B using (Session.Create()) { InterfaceAssert(SessionEnum.Parent); UserInput.SetInterface(bInterface); // 1: Session B sets its interface InterfaceAssert(SessionEnum.SessionB); e2.Set(); e4.Wait(); InterfaceAssert(SessionEnum.SessionB); // 4: Session B verifies it still has the correct interface e5.Set(); e7.Wait(); } InterfaceAssert(SessionEnum.Parent); // 7: Session B verifies its interface has been reset to 'parent' } // Verify the user input is correctly reset from nested contexts InterfaceAssert(SessionEnum.Parent); } finally { UserInput.SetInterface(previousInterface); } } [Test] public void ComponentSettingSession() { DutSettings.Current.Clear(); var dut1 = new SubDut(); try { DutSettings.Current.Add(dut1); var profile1 = EngineSettings.Current.OperatorName; using (Session.Create(SessionOptions.OverlayComponentSettings)) { var profile2 = "profile2"; EngineSettings.Current.OperatorName = profile2; var dut2 = new SubDut(); DutSettings.Current.Add(dut2); Assert.AreEqual(2, DutSettings.Current.Count); using (Session.Create(SessionOptions.OverlayComponentSettings)) { var profile3 = "profile3"; var dut3 = new SubDut(); DutSettings.Current.Add(dut3); Assert.AreEqual(3, DutSettings.Current.Count); Assert.AreEqual(profile2, EngineSettings.Current.OperatorName); EngineSettings.Current.OperatorName = profile3; } using (Session.Create(SessionOptions.OverlayComponentSettings)) { Assert.AreEqual(profile2, EngineSettings.Current.OperatorName); } Assert.AreEqual(2, DutSettings.Current.Count); Assert.AreEqual(profile2, EngineSettings.Current.OperatorName); } Assert.AreEqual(1, DutSettings.Current.Count); Assert.AreEqual(profile1, EngineSettings.Current.OperatorName); } finally { DutSettings.Current.Remove(dut1); } Assert.AreEqual(0, DutSettings.Current.Count); } [Test] public void TestPlanReferenceDetectChangesTest() { var planName = Path.Combine(Path.GetTempPath(), Guid.NewGuid() + ".TapPlan"); var subPlan = new TestPlan(); var delay = new DelayStep(); delay.DelaySecs = 0.1; subPlan.Steps.Add(delay); var mainPlan = new TestPlan(); var tpr = new TestPlanReference() { Filepath = { Text = planName } }; mainPlan.ChildTestSteps.Add(tpr); string saveAndRun() { subPlan.Save(planName); tpr.LoadTestPlan(); var hash = mainPlan.Execute().Hash; Assert.IsFalse(string.IsNullOrWhiteSpace(hash)); return hash; } var firstHash = saveAndRun(); Assert.AreEqual(firstHash, saveAndRun(), "Expected hash to be the same."); delay.DelaySecs = 0.01; Assert.AreNotEqual(firstHash, saveAndRun(), "Expected hash to be different."); } [Test] public void TestPlanReferenceSubPlanTest() { int parallelism = 10; var planName = Path.Combine(Path.GetTempPath(), Guid.NewGuid() + ".TapPlan"); string knownLogMessage = "Hello"; using (Session.Create()) try { var dut = new SubDut(); DutSettings.Current.Add(dut); { var subPlan = new TestPlan(); subPlan.Steps.Add(new LogStep {LogMessage = knownLogMessage}); subPlan.Steps.Add(new VerdictStep {Verdict = Verdict.Pass}); subPlan.Steps.Add(new DutStep {Dut = dut}); subPlan.Save(planName); } var plan = new TestPlan(); var par = new ParallelStep(); plan.ChildTestSteps.Add(par); for (int i = 0; i < parallelism; i++) { var tpr1 = new TestPlanReference { Filepath = {Text = planName} }; var hideSteps = tpr1.GetType().GetProperty("HideSteps", BindingFlags.Instance | BindingFlags.NonPublic); hideSteps.SetValue(tpr1, true); par.ChildTestSteps.Add(tpr1); } var testListener = new TestTraceListener(); Log.AddListener(testListener); var run = plan.Execute(); var log = testListener.GetLog(); Assert.AreEqual(Verdict.Pass, run.Verdict, log); } finally { File.Delete(planName); } } } }