public override void Run() { // There are four levels of log messages Info, Warning, Error, Debug. MyLog.Info("Info from Run"); for (int i = 0; i < 10; i++) { MyLog.Debug("Debug {0} from Run", i); // MyLog.X works like string.Format with regards to arguments. } MyLog.Warning("Warning from Run"); MyLog.Error("Error from Run"); // The Log can accept a Stopwatch Object to be used for timing analysis. Stopwatch sw1 = Stopwatch.StartNew(); TapThread.Sleep(100); MyLog.Info(sw1, "Info from Run"); Stopwatch sw2 = Stopwatch.StartNew(); TapThread.Sleep(200); MyLog.Error(sw2, "Error from step"); // Tracebar can be used to show results in the MyLog. var traceBar = new TraceBar(); traceBar.LowerLimit = -3.0; for (var i = -2; i < 11; i++) { traceBar.UpperLimit = i < 5 ? 3 : 15; // GetBar returns a string with value, low limit, a dashed line // indicating magnitude, the upper limit, and (if failing), a fail indicator. string temp = traceBar.GetBar(i); MyLog.Info("MyResult: " + temp); TapThread.Sleep(200); } // Sample output shown below. // MyResult: 2.00 - 3-------------------------|----- 3 // MyResult: 3.00 - 3------------------------------ | 3 // MyResult: 4.00 - 3------------------------------ > 3 Fail // MyResult: 5.00 - 3------------ -| -----------------15 // MyResult: 6.00 - 3-------------- -| ---------------15 // TraceBar remembers if any results failed, so it can be used for the verdict. UpgradeVerdict(traceBar.CombinedVerdict); // The log also supports showing stack traces. // Useful for debugging. try { throw new Exception("My exception"); } catch (Exception e) { MyLog.Error("Caught exception: '{0}'", e.Message); MyLog.Debug(e); // Prints the stack trace to the MyLog. } }
///<summary>Searches for plugins.</summary> public static void Search() { searchTask.Reset(); searcher = null; assemblyResolver.Invalidate(DirectoriesToSearch); ChangeID++; try { IEnumerable <string> fileNames = assemblyResolver.GetAssembliesToSearch(); searcher = SearchAndAddToStore(fileNames); } catch (Exception e) { log.Error("Caught exception while searching for plugins: '{0}'", e.Message); log.Debug(e); searcher = null; } finally { searchTask.Set(); } }
/// <summary> /// Run an action as the final step after the last deferred action /// </summary> /// <param name="action"></param> void IResultSource.Finally(Action <Task> action) { if (deferCount == 0) { action(Finished); DeferWorker?.Dispose(); } else { DeferWorker.EnqueueWork(() => { try { if (deferExceptions.Count == 1) { action(Task.FromException(deferExceptions[0])); } else if (deferExceptions.Count > 1) { action(Task.FromException(new AggregateException(deferExceptions.ToArray()))); } else { action(Finished); } DeferWorker.Dispose(); } catch (OperationCanceledException) { } catch (Exception e) { log.Error("Caught error while finalizing test step run. {0}", e.Message); log.Debug(e); } }); } }
/// <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); }
/// <summary> /// Blocks the thread while closing all resources in parallel. /// </summary> private void CloseAllResources() { Dictionary <IResource, List <IResource> > dependencies = openedResources.ToDictionary(r => r.Resource, r => new List <IResource>()); // this dictionary will hold resources with dependencies (keys) and what resources depend on them (values) foreach (var n in openedResources) { foreach (var dep in n.StrongDependencies) { dependencies[dep].Add(n.Resource); } } Dictionary <IResource, Task> closeTasks = new Dictionary <IResource, Task>(); foreach (var r in openedResources) { closeTasks[r.Resource] = new Task(o => { ResourceNode res = (ResourceNode)o; // Wait for the resource to open to open before closing it. // in rare cases, another instrument failing open will cause close to be called. try { openTasks[res.Resource].Wait(); } catch { } // wait for resources that depend on this resource (res) to close before closing this Task.WaitAll(dependencies[res.Resource].Select(x => closeTasks[x]).ToArray()); var reslog = GetLogSource(res.Resource); Stopwatch timer = Stopwatch.StartNew(); try { res.Resource.Close(); } catch (Exception e) { log.Error("Error while closing \"{0}\": {1}", res.Resource.Name, e.Message); log.Debug(e); } if (reslog != null) { reslog.Info(timer, "Resource \"{0}\" closed.", res.Resource); } }, r); } closeTasks.Values.ToList().ForEach(t => t.Start()); void complainAboutWait() { log.Debug("Waiting for resources to close:"); foreach (var res in openTasks.Keys) { if (res.IsConnected) { log.Debug(" - {0}", res); } } } using (TimeoutOperation.Create(complainAboutWait)) Task.WaitAll(closeTasks.Values.ToArray()); }