ResourceDep[] getManyResources <T>(T[] steps) { if (steps.Length == 0) { return(Array.Empty <ResourceDep>()); } var result = new HashSet <ResourceDep>(); TestStepExtensions.GetObjectSettings <IResource, T, ResourceDep>(steps, true, FilterProps, result); result.RemoveWhere(dep => dep.Behavior == ResourceOpenBehavior.Ignore); return(result.ToArray()); }
/// <summary> /// Checks all validation rules on this object (<see cref="Rules"/>) and throws an AggregateException on errors. /// </summary> /// <param name="ignoreDisabledProperties">If true, ignores <see cref="Rules"/> related to properties that are disabled or hidden as a result of <see cref="EnabledIfAttribute"/> or <see cref="Enabled{T}"/>.</param> /// <exception cref="AggregateException">Thrown when any <see cref="Rules"/> on this object are invalid. This exception contains an ArgumentException for each invalid setting.</exception> protected void ThrowOnValidationError(bool ignoreDisabledProperties) { var propertyNames = new HashSet <string>(); TestStepExtensions.GetObjectSettings(this, ignoreDisabledProperties, (object o, IMemberData pi) => pi.Name, propertyNames); foreach (ValidationRule rule in Rules) { if (!rule.IsValid() && propertyNames.Contains(rule.PropertyName)) { throw new Exception($"The Property [{rule.PropertyName}] is invalid. Details: {rule.ErrorMessage}"); } } }
/// <summary> /// Finds all IResource properties on a list of references. The references can be any class derived from ITapPlugin, such as ITestStep or IResource. /// If a reference supplied in the <paramref name="references"/> list is a IResource itself it will be added to the resulting list. /// </summary> internal List <ResourceNode> GetAllResources(object[] references, out bool errorDetected) { errorDetected = false; ResourceDep[] stepResources = getManyResources(references); var resourceDeps = new List <ResourceDep>(stepResources); foreach (var reference in references) { if (reference is IResource res) { resourceDeps.Add(new ResourceDep(res)); } } List <ResourceNode> tree = GetResourceTree(resourceDeps); ExpandTree(tree); // Check that no resources have direct references to itself. foreach (var scc in tree.Where(x => x.StrongDependencies.Any(dep => dep == x.Resource))) { foreach (var dep in scc.StrongDependencies) { if (dep == scc.Resource) { errorDetected = true; Log.Error("Resource is referencing itself: {0}", scc.Resource); break; } } } // Figure out if all resources has their resource properties set foreach (var leaf in tree) { if (leaf.StrongDependencies.Any(s => s is null)) { errorDetected = true; Log.Error($"Resource setting not set on resource {leaf.Resource}. Please configure or disable the resource."); } } if (errorDetected) // If any resources has resource properties which is not set, let's return early, because FindStronglyConntectedComponents method below will throw a confusing error in this case. { return(tree); } // Figure out if there are circular references, and list the circular references in an exception each. var sccs = FindStronglyConnectedComponents(tree); if (sccs.Count > 0) { errorDetected = true; foreach (var scc in sccs) { Log.Error(string.Format("Circular references between resources: {0}", string.Join(",", scc.Select(res => res.Name)))); } } //Add users of resources foreach (var r in references) { TestStepExtensions.GetObjectSettings <IResource, object, ResourceNode>(r, true, (res, prop) => { var nodes = tree.Where(n => n.Resource == res); if (nodes.Count() > 1) { // Normally we would expect that the tree only contains one node representing each resource. // In case of null resources however, we want one node per property, such that ILockManager.BeforeOpen() // has a chance to set each property to a different resource instance. if (res != null) { throw new Exception($"Duplicate entry for Resource '{res.Name}' in tree."); } nodes = nodes.Where(n => n.Depender == prop); } var nodeRepresentingResource = nodes.FirstOrDefault(); if (nodeRepresentingResource != null) { nodeRepresentingResource.References.Add(new ResourceReference(r, prop)); } return(nodeRepresentingResource); }, new HashSet <ResourceNode>()); } return(tree); }
private TestPlanRun DoExecute(IEnumerable <IResultListener> resultListeners, IEnumerable <ResultParameter> metaDataParameters, HashSet <ITestStep> stepsOverride) { if (resultListeners == null) { throw new ArgumentNullException("resultListeners"); } if (PrintTestPlanRunSummary && !resultListeners.Contains(summaryListener)) { resultListeners = resultListeners.Concat(new IResultListener[] { summaryListener }); } resultListeners = resultListeners.Where(r => r is IEnabledResource ? ((IEnabledResource)r).IsEnabled : true); IList <ITestStep> steps; if (stepsOverride == null) { steps = Steps; } else { // Remove steps that are already included via their parent steps. foreach (var step in stepsOverride) { if (step == null) { throw new ArgumentException("stepsOverride may not contain null", "stepsOverride"); } var p = step.GetParent <ITestStep>(); while (p != null) { if (stepsOverride.Contains(p)) { throw new ArgumentException("stepsOverride may not contain steps and their parents.", "stepsOverride"); } p = p.GetParent <ITestStep>(); } } steps = Utils.FlattenHeirarchy(Steps, step => step.ChildTestSteps).Where(stepsOverride.Contains).ToList(); } long initTimeStamp = Stopwatch.GetTimestamp(); var initTime = DateTime.Now; Log.Info("-----------------------------------------------------------------"); var fileStreamFile = FileSystemHelper.CreateTempFile(); var logStream = new HybridStream(fileStreamFile, 1024 * 1024); var planRunLog = new FileTraceListener(logStream) { IsRelative = true }; OpenTap.Log.AddListener(planRunLog); var allSteps = Utils.FlattenHeirarchy(steps, step => step.ChildTestSteps); var allEnabledSteps = Utils.FlattenHeirarchy(steps.Where(x => x.Enabled), step => step.GetEnabledChildSteps()); var enabledSinks = new HashSet <IResultSink>(); TestStepExtensions.GetObjectSettings <IResultSink, ITestStep, IResultSink>(allEnabledSteps, true, null, enabledSinks); if (enabledSinks.Count > 0) { var sinkListener = new ResultSinkListener(enabledSinks); resultListeners = resultListeners.Append(sinkListener); } Log.Info("Starting TestPlan '{0}' on {1}, {2} of {3} TestSteps enabled.", Name, initTime, allEnabledSteps.Count, allSteps.Count); // Reset step verdict. foreach (var step in allSteps) { if (step.Verdict != Verdict.NotSet) { step.Verdict = Verdict.NotSet; step.OnPropertyChanged("Verdict"); } } if (currentExecutionState != null) { // load result listeners that are _not_ used in the previous runs. // otherwise they wont get opened later. foreach (var rl in resultListeners) { if (!currentExecutionState.ResultListeners.Contains(rl)) { currentExecutionState.ResultListeners.Add(rl); } } } var currentListeners = currentExecutionState != null ? currentExecutionState.ResultListeners : resultListeners; TestPlanRun execStage; bool continuedExecutionState = false; if (currentExecutionState != null) { execStage = new TestPlanRun(currentExecutionState, initTime, initTimeStamp); continuedExecutionState = true; } else { execStage = new TestPlanRun(this, resultListeners.ToList(), initTime, initTimeStamp); execStage.Parameters.AddRange(PluginManager.GetPluginVersions(allEnabledSteps)); execStage.ResourceManager.ResourceOpened += r => { execStage.Parameters.AddRange(PluginManager.GetPluginVersions(new List <object> { r })); }; } if (metaDataParameters != null) { execStage.Parameters.AddRange(metaDataParameters); } var prevExecutingPlanRun = executingPlanRun.LocalValue; executingPlanRun.LocalValue = execStage; CurrentRun = execStage; failState runWentOk = failState.StartFail; // ReSharper disable once InconsistentNaming var preRun_Run_PostRunTimer = Stopwatch.StartNew(); try { execStage.FailedToStart = true; // Set it here in case OpenInternal throws an exception. Could happen if a step is missing an instrument OpenInternal(execStage, continuedExecutionState, currentListeners.Cast <IResource>().ToList(), allEnabledSteps); execStage.WaitForSerialization(); execStage.ResourceManager.BeginStep(execStage, this, TestPlanExecutionStage.Execute, TapThread.Current.AbortToken); if (continuedExecutionState) { // Since resources are not opened, getting metadata cannot be done in the wait for resources continuation // like shown in TestPlanRun. Instead we do it here. foreach (var res in execStage.ResourceManager.Resources) { execStage.Parameters.AddRange(ResultParameters.GetMetadataFromObject(res)); } } runWentOk = failState.ExecFail; //important if test plan is aborted and runWentOk is never returned. runWentOk = execTestPlan(execStage, steps); } catch (Exception e) { if (e is OperationCanceledException && execStage.MainThread.AbortToken.IsCancellationRequested) { Log.Warning(String.Format("TestPlan aborted. ({0})", e.Message)); execStage.UpgradeVerdict(Verdict.Aborted); } else if (e is ThreadAbortException) { // It seems this actually never happens. Log.Warning("TestPlan aborted."); execStage.UpgradeVerdict(Verdict.Aborted); //Avoid entering the finally clause. Thread.Sleep(500); } else if (e is System.ComponentModel.LicenseException) { Log.Error(e.Message); execStage.UpgradeVerdict(Verdict.Error); } else { Log.Warning("TestPlan aborted."); Log.Error(e.Message); Log.Debug(e); execStage.UpgradeVerdict(Verdict.Error); } } finally { execStage.FailedToStart = (runWentOk == failState.StartFail); try { finishTestPlanRun(execStage, preRun_Run_PostRunTimer, runWentOk, planRunLog, logStream); } catch (Exception ex) { Log.Error("Error while finishing TestPlan."); Log.Debug(ex); } OpenTap.Log.RemoveListener(planRunLog); planRunLog.Dispose(); logStream.Dispose(); File.Delete(fileStreamFile); // Clean all test steps StepRun, otherwise the next test plan execution will be stuck at TestStep.DoRun at steps that does not have a cleared StepRun. foreach (var step in allSteps) { step.StepRun = null; } executingPlanRun.LocalValue = prevExecutingPlanRun; CurrentRun = prevExecutingPlanRun; } return(execStage); }