/// <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> /// 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; } }
/// <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> /// 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> /// 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"); }