/// <summary> /// Reports execution time details, provided, it is allowed /// </summary> /// <param name="label">Report entry label</param> /// <param name="testItem">Test item</param> /// <param name="watch">Watch measuring time</param> private void ReportTimeDetail(string label, TestItemBase testItem, Stopwatch watch) { if (Package.Options.VerboseTestExecutionLogging && watch != null) { testItem.Log($"{label} {watch.Elapsed.TotalSeconds:####0.####} seconds"); } }
/// <summary> /// Set the state of the specified sub tree /// </summary> /// <param name="node">Subtree root node</param> /// <param name="state">State to set</param> /// <param name="except">Optional node to ignore</param> public void SetSubTreeState(TestItemBase node, TestState state, TestItemBase except = null) { if (node == except) { return; } node.State = state; foreach (var child in node.ChildItems) { SetSubTreeState(child, state, except); } }
/// <summary> /// Reports the result of a test or test case /// </summary> /// <param name="item"></param> private void ReportTestResult(TestItemBase item) { switch (item.State) { case TestState.Inconclusive: item.Log("Test is inconclusive", LogEntryType.Fail); break; case TestState.Failed: item.Log("Test failed", LogEntryType.Fail); break; case TestState.Success: item.Log("Test succeded", LogEntryType.Success); break; } }
private static void SetTestItemState(TestItemBase root, IEnumerable <TestItemBase> children) { if (root.State == TestState.Aborted) { return; } var childList = children.ToList(); if (childList.Any(i => i.State == TestState.Aborted || i.State == TestState.Inconclusive)) { root.State = TestState.Inconclusive; } else if (childList.Any(i => i.State == TestState.Running)) { root.State = TestState.Running; } else { root.State = childList.Any(i => i.State == TestState.Failed) ? TestState.Failed : TestState.Success; } }
/// <summary> /// Handle exceptions during testing /// </summary> /// <param name="item">Test item that raised the exception</param> /// <param name="ex">Exception raised</param> private static void HandleException(TestItemBase item, Exception ex) { if (ex is HandledTestExecutionException) { throw ex; } var handled = true; item.State = TestState.Aborted; string message; switch (ex) { case TaskCanceledException _: message = "The test has been cancelled by the user."; break; case TestExecutionException testEx: message = testEx.Message; break; default: message = $"The test engined detected an internal exception: {ex.Message}."; handled = false; break; } if (message.Length > 0) { message += " "; } message += "Test aborted."; item.Log(message, LogEntryType.Fail); throw handled ? new HandledTestExecutionException() : throw ex; }
/// <summary> /// Initializes the tree node with the specified parent /// </summary> /// <param name="vm">Parent view model</param> /// <param name="parent">Parent node</param> protected TestItemBase(TestExplorerToolWindowViewModel vm, TestItemBase parent) { Parent = parent; Vm = vm ?? throw new ArgumentNullException(nameof(vm)); }
/// <summary> /// Invokes the code and waits for its completion within the specified /// timeout limits. /// </summary> /// <param name="testItem">Test item that invokes this method</param> /// <param name="invokePlan">Invokation plan</param> /// <param name="timeout">Timeout in milliseconds</param> /// <param name="token">Token to cancel test run</param> /// <param name="watch">Optional stopwatch for diagnostics</param> /// <returns>True, if code completed; otherwise, false</returns> private async Task <bool> InvokeCodeAsync(TestItemBase testItem, InvokePlanBase invokePlan, int timeout, CancellationToken token, Stopwatch watch = null) { if (invokePlan == null) { return(true); } if (!(Package.MachineViewModel.SpectrumVm is ISpectrumVmRunCodeSupport runCodeSupport)) { return(false); } // --- Prepare code invocation ExecuteCycleOptions runOptions; bool removeFromHalt = false; var spectrumVm = Package.MachineViewModel.SpectrumVm; var timeoutTacts = timeout * spectrumVm.BaseClockFrequency * spectrumVm.ClockMultiplier / 1000; if (invokePlan is CallPlan callPlan) { // --- Obtain Call stub address TestSetPlan testSetPlan = null; switch (testItem) { case TestSetItem set: testSetPlan = set.Plan; break; case TestItem item: testSetPlan = item.Plan.TestSet; break; case TestCaseItem caseItem: testSetPlan = caseItem.Plan.TestBlock.TestSet; break; } var callStubAddress = testSetPlan?.CallStubAddress ?? DEFAULT_CALL_STUB_ADDRESS; // --- Create CALL stub Package.MachineViewModel.SpectrumVm.Cpu.Registers.PC = callStubAddress; runCodeSupport.InjectCodeToMemory(callStubAddress, new byte[] { 0xCD, (byte)callPlan.Address, (byte)(callPlan.Address >> 8) }); runOptions = new ExecuteCycleOptions(EmulationMode.UntilExecutionPoint, terminationPoint: (ushort)(callStubAddress + 3), fastVmMode: true, timeoutTacts: timeout * spectrumVm.BaseClockFrequency * spectrumVm.ClockMultiplier / 1000); } else if (invokePlan is StartPlan startPlan) { spectrumVm.Cpu.Registers.PC = startPlan.Address; if (startPlan.StopAddress == null) { // --- Start and run until halt runOptions = new ExecuteCycleOptions(EmulationMode.UntilHalt, fastVmMode: true, timeoutTacts: timeoutTacts); removeFromHalt = true; } else { // --- Start and run until the stop address is reached runOptions = new ExecuteCycleOptions(EmulationMode.UntilExecutionPoint, terminationPoint: startPlan.StopAddress.Value, fastVmMode: true, timeoutTacts: timeoutTacts); } } else { return(false); } // --- Prepare the machine to run the code var initialTacts = Package.MachineViewModel.SpectrumVm.Cpu.Tacts; var machine = Package.MachineViewModel.Machine; var cpuSupport = Package.MachineViewModel.SpectrumVm.Cpu as IZ80CpuTestSupport; cpuSupport.ExecutionFlowStatus.ClearAll(); cpuSupport.MemoryReadStatus.ClearAll(); cpuSupport.MemoryWriteStatus.ClearAll(); Package.MachineViewModel.NoToolRefreshMode = true; // --- Start the machine machine.Start(runOptions); ReportTimeDetail("Start VM:", testItem, watch); // --- Waith for completion var completion = machine.CompletionTask; await completion; // --- Report outcome ReportTimeDetail("Complete VM:", testItem, watch); var endTacts = Package.MachineViewModel.SpectrumVm.Cpu.Tacts; if (Package.Options.TestTStateExecutionLogging) { testItem.Log($"#of T-States consumed: {endTacts - initialTacts}"); } // --- Check if code ran successfully var success = !completion.IsFaulted && !completion.IsCanceled && !token.IsCancellationRequested && machine.ExecutionCycleResult; // --- Take care the VM is paused await machine.Pause(); ReportTimeDetail("Pause VM:", testItem, watch); // --- If the CPU was halted, it should be removed from this state for the next run if (removeFromHalt) { var cpu = Package.MachineViewModel.SpectrumVm.Cpu as IZ80CpuTestSupport; cpu?.RemoveFromHaltedState(); } // --- Handle user cancellation/Timeout if (!success) { if (token.IsCancellationRequested) { throw new TaskCanceledException(); } testItem.State = TestState.Aborted; testItem.Log("Timeout expired. Test aborted.", LogEntryType.Fail); } return(success); }
/// <summary> /// Executes all tests that start with the specified node /// </summary> /// <param name="vm">Test explorer view model</param> /// <param name="node">Root node of the subtree to run the tests for</param> /// <param name="token">Token to stop tests</param> public Task RunTestsFromNodeAsync(TestExplorerToolWindowViewModel vm, TestItemBase node, CancellationToken token) { TestRootItem rootToRun = null; switch (node) { case TestRootItem rootNode: // --- Prepare all file nodes to run rootNode.TestFilesToRun.Clear(); foreach (var child in rootNode.ChildItems) { if (!(child is TestFileItem fileItem)) { continue; } rootNode.TestFilesToRun.Add(fileItem); fileItem.CollectAllToRun(); } rootToRun = rootNode; break; case TestSetItem setNode: { // --- Prepare this test set to run setNode.TestsToRun.Clear(); setNode.CollectAllToRun(); var fileItem = setNode.Parent as TestFileItem; var root = rootToRun = fileItem.Parent as TestRootItem; root.TestFilesToRun.Clear(); root.TestFilesToRun.Add(fileItem); fileItem.TestSetsToRun.Clear(); fileItem.TestSetsToRun.Add(setNode); break; } case TestItem testNode: { // --- Prepare this test to run testNode.TestCasesToRun.Clear(); testNode.CollectAllToRun(); var setItem = testNode.Parent as TestSetItem; var fileItem = setItem.Parent as TestFileItem; var root = rootToRun = fileItem.Parent as TestRootItem; root.TestFilesToRun.Clear(); root.TestFilesToRun.Add(fileItem); fileItem.TestSetsToRun.Clear(); fileItem.TestSetsToRun.Add(setItem); setItem.TestsToRun.Clear(); setItem.TestsToRun.Add(testNode); break; } case TestCaseItem caseNode: { // --- Prepare this test case to run var testItem = caseNode.Parent as TestItem; var setItem = testItem.Parent as TestSetItem; var fileItem = setItem?.Parent as TestFileItem; var root = rootToRun = fileItem.Parent as TestRootItem; root.TestFilesToRun.Clear(); root.TestFilesToRun.Add(fileItem); fileItem.TestSetsToRun.Clear(); fileItem.TestSetsToRun.Add(setItem); setItem.TestsToRun.Clear(); setItem.TestsToRun.Add(testItem); testItem.TestCasesToRun.Clear(); testItem.TestCasesToRun.Add(caseNode); break; } } return(rootToRun != null ? ExecuteTestTreeAsync(vm, rootToRun, token) : Task.FromResult(0)); }
/// <summary> /// Reports the ellapsed test time /// </summary> /// <param name="label">Report entry label</param> /// <param name="testItem">Test item</param> /// <param name="watch">Watch measuring time</param> private void ReportEllapsedTime(string label, TestItemBase testItem, Stopwatch watch) { testItem.Log($"{label} execution completed in {watch.Elapsed.TotalSeconds:####0.####} seconds"); }