How to use GraphNode class of Microsoft.Coyote.Actors.Coverage package

Best Coyote code snippet using Microsoft.Coyote.Actors.Coverage.GraphNode

ActorRuntimeLogGraphBuilder.cs

Source:ActorRuntimeLogGraphBuilder.cs Github

copy

Full Screen

...107 {108 lock (this.Inbox)109 {110 var resolvedId = this.GetResolveActorId(id?.Name, id?.Type);111 GraphNode node = this.Graph.GetOrCreateNode(resolvedId);112 node.Category = ActorCategory;113 if (!string.IsNullOrEmpty(creatorName))114 {115 var creatorId = this.GetResolveActorId(creatorName, creatorType);116 GraphNode creator = this.Graph.GetOrCreateNode(creatorId);117 this.GetOrCreateEventLink(creator, node, new EventInfo() { Event = "CreateActor" });118 }119 }120 }121 /// <inheritdoc/>122 public void OnCreateStateMachine(ActorId id, string creatorName, string creatorType)123 {124 lock (this.Inbox)125 {126 var resolvedId = this.GetResolveActorId(id?.Name, id?.Type);127 GraphNode node = this.Graph.GetOrCreateNode(resolvedId);128 node.Category = StateMachineCategory;129 if (!string.IsNullOrEmpty(creatorName))130 {131 var creatorId = this.GetResolveActorId(creatorName, creatorType);132 GraphNode creator = this.Graph.GetOrCreateNode(creatorId);133 this.GetOrCreateEventLink(creator, node, new EventInfo() { Event = "CreateActor" });134 }135 }136 }137 /// <inheritdoc/>138 public void OnSendEvent(ActorId targetActorId, string senderName, string senderType, string senderStateName,139 Event e, Guid eventGroupId, bool isTargetHalted)140 {141 string eventName = e.GetType().FullName;142 this.AddEvent(targetActorId.Name, targetActorId.Type, senderName, senderType, senderStateName, eventName);143 }144 /// <inheritdoc/>145 public void OnRaiseEvent(ActorId id, string stateName, Event e)146 {147 string eventName = e.GetType().FullName;148 // Raising event to self.149 this.AddEvent(id.Name, id.Type, id.Name, id.Type, stateName, eventName);150 }151 /// <inheritdoc/>152 public void OnHandleRaisedEvent(ActorId id, string stateName, Event e)153 {154 lock (this.Inbox)155 {156 // We used the inbox to store raised event, but it should be the first one handled since157 // raised events are highest priority.158 string resolvedId = this.GetResolveActorId(id?.Name, id?.Type);159 lock (this.Inbox)160 {161 if (this.Inbox.TryGetValue(resolvedId, out List<EventInfo> inbox))162 {163 string eventName = e.GetType().FullName;164 for (int i = inbox.Count - 1; i >= 0; i--)165 {166 EventInfo info = inbox[i];167 if (info.Event == eventName)168 {169 this.Dequeued[id] = info;170 break;171 }172 }173 }174 }175 }176 }177 /// <inheritdoc/>178 public void OnEnqueueEvent(ActorId id, Event e)179 {180 }181 /// <inheritdoc/>182 public void OnDequeueEvent(ActorId id, string stateName, Event e)183 {184 lock (this.Inbox)185 {186 var resolvedId = this.GetResolveActorId(id?.Name, id?.Type);187 string eventName = e.GetType().FullName;188 EventInfo info = this.PopEvent(resolvedId, eventName);189 if (info != null)190 {191 this.Dequeued[id] = info;192 }193 }194 }195 private EventInfo PopEvent(string resolvedId, string eventName)196 {197 EventInfo result = null;198 lock (this.Inbox)199 {200 if (this.Inbox.TryGetValue(resolvedId, out List<EventInfo> inbox))201 {202 for (int i = inbox.Count - 1; i >= 0; i--)203 {204 if (inbox[i].Event == eventName)205 {206 result = inbox[i];207 inbox.RemoveAt(i);208 }209 }210 }211 }212 return result;213 }214 /// <inheritdoc/>215 public void OnReceiveEvent(ActorId id, string stateName, Event e, bool wasBlocked)216 {217 string resolvedId = this.GetResolveActorId(id?.Name, id?.Type);218 lock (this.Inbox)219 {220 if (this.Inbox.TryGetValue(resolvedId, out List<EventInfo> inbox))221 {222 string eventName = e.GetType().FullName;223 for (int i = inbox.Count - 1; i >= 0; i--)224 {225 EventInfo info = inbox[i];226 if (info.Event == eventName)227 {228 // Yay, found it so we can draw the complete link connecting the Sender state to this state!229 string category = string.IsNullOrEmpty(stateName) ? ActorCategory : StateMachineCategory;230 var source = this.GetOrCreateChild(info.Name, info.Type, info.State);231 var target = this.GetOrCreateChild(id?.Name, id?.Type, category, stateName);232 this.GetOrCreateEventLink(source, target, info);233 inbox.RemoveAt(i);234 break;235 }236 }237 }238 }239 }240 /// <inheritdoc/>241 public void OnWaitEvent(ActorId id, string stateName, Type eventType)242 {243 }244 /// <inheritdoc/>245 public void OnWaitEvent(ActorId id, string stateName, params Type[] eventTypes)246 {247 }248 /// <inheritdoc/>249 public void OnStateTransition(ActorId id, string stateName, bool isEntry)250 {251 if (isEntry)252 {253 // record the fact we have entered this state254 this.GetOrCreateChild(id?.Name, id?.Type, stateName);255 }256 }257 /// <inheritdoc/>258 public void OnExecuteAction(ActorId id, string handlingStateName, string currentStateName, string actionName)259 {260 this.LinkTransition(typeof(DoActionEvent), id, handlingStateName, currentStateName, null);261 }262 /// <inheritdoc/>263 public void OnGotoState(ActorId id, string currentStateName, string newStateName)264 {265 this.LinkTransition(typeof(GotoStateEvent), id, currentStateName, currentStateName, newStateName);266 }267 /// <inheritdoc/>268 public void OnPushState(ActorId id, string currentStateName, string newStateName)269 {270 this.LinkTransition(typeof(PushStateEvent), id, currentStateName, currentStateName, newStateName);271 }272 /// <inheritdoc/>273 public void OnPopState(ActorId id, string currentStateName, string restoredStateName)274 {275 if (!string.IsNullOrEmpty(currentStateName))276 {277 this.LinkTransition(typeof(PopStateEvent), id, currentStateName,278 currentStateName, restoredStateName);279 }280 }281 /// <inheritdoc/>282 public void OnHalt(ActorId id, int inboxSize)283 {284 lock (this.Inbox)285 {286 this.HaltedStates.TryGetValue(id, out string stateName);287 var target = this.GetOrCreateChild(id?.Name, id?.Type, "Halt", "Halt");288 // Transition to the Halt state.289 if (!string.IsNullOrEmpty(stateName))290 {291 var source = this.GetOrCreateChild(id?.Name, id?.Type, stateName);292 this.GetOrCreateEventLink(source, target, new EventInfo() { Event = typeof(HaltEvent).FullName });293 }294 }295 }296 private int? GetLinkIndex(GraphNode source, GraphNode target, string id)297 {298 if (this.MergeEventLinks)299 {300 return null;301 }302 return this.Graph.GetUniqueLinkIndex(source, target, id);303 }304 /// <inheritdoc/>305 public void OnDefaultEventHandler(ActorId id, string stateName)306 {307 lock (this.Inbox)308 {309 string resolvedId = this.GetResolveActorId(id?.Name, id?.Type);310 string eventName = typeof(DefaultEvent).FullName;311 this.AddEvent(id.Name, id.Type, id.Name, id.Type, stateName, eventName);312 this.Dequeued[id] = this.PopEvent(resolvedId, eventName);313 }314 }315 /// <inheritdoc/>316 public void OnEventHandlerTerminated(ActorId id, string stateName, DequeueStatus dequeueStatus)317 {318 }319 /// <inheritdoc/>320 public void OnPopStateUnhandledEvent(ActorId actorId, string currentStateName, Event e)321 {322 lock (this.Inbox)323 {324 if (e is HaltEvent)325 {326 this.HaltedStates[actorId] = currentStateName;327 }328 }329 }330 /// <inheritdoc/>331 public void OnExceptionThrown(ActorId id, string stateName, string actionName, Exception ex)332 {333 }334 /// <inheritdoc/>335 public void OnExceptionHandled(ActorId id, string stateName, string actionName, Exception ex)336 {337 }338 /// <inheritdoc/>339 public void OnCreateTimer(TimerInfo info)340 {341 // TODO: figure out how to graph timers when we have no "timer id" at this point...342 }343 /// <inheritdoc/>344 public void OnStopTimer(TimerInfo info)345 {346 }347 /// <inheritdoc/>348 public void OnCreateMonitor(string monitorType)349 {350 lock (this.Inbox)351 {352 GraphNode node = this.Graph.GetOrCreateNode(monitorType, monitorType);353 node.Category = MonitorCategory;354 }355 }356 /// <inheritdoc/>357 public void OnMonitorExecuteAction(string monitorType, string stateName, string actionName)358 {359 // Monitors process actions immediately, so this state transition is a result of the only event in the inbox.360 lock (this.Inbox)361 {362 if (this.Inbox.TryGetValue(monitorType, out List<EventInfo> inbox) && inbox.Count > 0)363 {364 var e = inbox[inbox.Count - 1];365 inbox.RemoveAt(inbox.Count - 1);366 // Draw the link connecting the Sender state to this state!367 var source = this.GetOrCreateChild(e.Name, e.Type, e.State);368 var target = this.GetOrCreateChild(monitorType, monitorType, stateName);369 this.GetOrCreateEventLink(source, target, e);370 }371 }372 }373 /// <inheritdoc/>374 public void OnMonitorProcessEvent(string monitorType, string stateName, string senderName, string senderType,375 string senderStateName, Event e)376 {377 lock (this.Inbox)378 {379 string eventName = e.GetType().FullName;380 // Now add a fake event for internal monitor state transition that might now happen as a result of this event,381 // storing the monitor's current state in this event.382 var info = this.AddEvent(monitorType, monitorType, monitorType, monitorType, stateName, eventName);383 // Draw the link connecting the Sender state to this state!384 var source = this.GetOrCreateChild(senderName, senderType, senderStateName);385 var target = this.GetOrCreateChild(monitorType, monitorType, stateName);386 this.GetOrCreateEventLink(source, target, info);387 }388 }389 /// <inheritdoc/>390 public void OnMonitorRaiseEvent(string monitorType, string stateName, Event e)391 {392 // Raising event to self.393 string eventName = e.GetType().FullName;394 this.AddEvent(monitorType, monitorType, monitorType, monitorType, stateName, eventName);395 }396 /// <inheritdoc/>397 public void OnMonitorStateTransition(string monitorType, string stateName, bool isEntry, bool? isInHotState)398 {399 if (isEntry)400 {401 lock (this.Inbox)402 {403 // Monitors process events immediately (and does not call OnDequeue), so this state transition is a result of404 // the fake event we created in OnMonitorProcessEvent.405 if (this.Inbox.TryGetValue(monitorType, out List<EventInfo> inbox) && inbox.Count > 0)406 {407 var info = inbox[inbox.Count - 1];408 inbox.RemoveAt(inbox.Count - 1);409 // draw the link connecting the current state to this new state!410 var source = this.GetOrCreateChild(monitorType, monitorType, info.State);411 var shortStateName = this.GetLabel(monitorType, monitorType, stateName);412 if (isInHotState.HasValue)413 {414 string suffix = (isInHotState is true) ? "[hot]" : "[cold]";415 shortStateName += suffix;416 }417 string label = shortStateName;418 var target = this.GetOrCreateChild(monitorType, monitorType, shortStateName, label);419 // In case this node was already created, we may need to override the label here now that420 // we know this is a hot state. This is because, unfortunately, other OnMonitor* methods421 // do not provide the isInHotState parameter.422 target.Label = label;423 this.GetOrCreateEventLink(source, target, info);424 }425 }426 }427 }428 /// <inheritdoc/>429 public void OnMonitorError(string monitorType, string stateName, bool? isInHotState)430 {431 var source = this.GetOrCreateChild(monitorType, monitorType, stateName);432 source.Category = "Error";433 }434 /// <inheritdoc/>435 public void OnRandom(bool result, string callerName, string callerType)436 {437 }438 /// <inheritdoc/>439 public void OnRandom(int result, string callerName, string callerType)440 {441 }442 /// <inheritdoc/>443 public void OnAssertionFailure(string error)444 {445 }446 /// <inheritdoc/>447 public void OnCompleted()448 {449 }450 /// <summary>451 /// Return current graph and reset for next iteration.452 /// </summary>453 /// <param name="reset">Set to true will reset the graph for the next iteration.</param>454 /// <returns>The graph.</returns>455 internal Graph SnapshotGraph(bool reset)456 {457 Graph result = this.CurrentGraph;458 if (reset)459 {460 // Reset the graph to start fresh.461 this.CurrentGraph = null;462 }463 return result;464 }465 private string GetResolveActorId(string name, string type)466 {467 if (type is null)468 {469 // The sender id can be null if an event is fired from non-actor code.470 return ExternalCodeName;471 }472 if (this.CollapseInstances)473 {474 return type;475 }476 return name;477 }478 private EventInfo AddEvent(string targetName, string targetType, string senderName, string senderType,479 string senderStateName, string eventName)480 {481 string targetId = this.GetResolveActorId(targetName, targetType);482 EventInfo info = null;483 lock (this.Inbox)484 {485 if (!this.Inbox.TryGetValue(targetId, out List<EventInfo> inbox))486 {487 inbox = new List<EventInfo>();488 this.Inbox[targetId] = inbox;489 }490 info = new EventInfo()491 {492 Name = senderName ?? ExternalCodeName,493 Type = senderType ?? ExternalCodeName,494 State = senderStateName,495 Event = eventName496 };497 inbox.Add(info);498 }499 return info;500 }501 private void LinkTransition(Type transitionType, ActorId id, string handlingStateName,502 string currentStateName, string newStateName)503 {504 string name = id.Name;505 string type = id.Type;506 lock (this.Inbox)507 {508 if (this.Dequeued.TryGetValue(id, out EventInfo info))509 {510 // Event was dequeued, but now we know what state is handling this event, so connect the dots...511 if (info.Type != type || info.Name != name || info.State != currentStateName)512 {513 var source = this.GetOrCreateChild(info.Name, info.Type, info.State);514 var target = this.GetOrCreateChild(name, type, currentStateName);515 info.HandlingState = handlingStateName;516 this.GetOrCreateEventLink(source, target, info);517 }518 }519 if (newStateName != null)520 {521 // Then this is a goto or push and we can draw that link also.522 var source = this.GetOrCreateChild(name, type, currentStateName);523 var target = this.GetOrCreateChild(name, type, newStateName);524 if (info is null)525 {526 info = new EventInfo { Event = transitionType.FullName };527 }528 this.GetOrCreateEventLink(source, target, info);529 }530 this.Dequeued.Remove(id);531 }532 }533 private GraphNode GetOrCreateChild(string name, string type, string stateName, string label = null)534 {535 GraphNode child = null;536 lock (this.Inbox)537 {538 this.AddNamespace(type);539 var initalStateName = stateName;540 // make label relative to fully qualified actor id (it's usually a nested class).541 stateName = this.GetLabel(name, type, stateName);542 string id = this.GetResolveActorId(name, type);543 GraphNode parent = this.Graph.GetOrCreateNode(id);544 parent.AddAttribute("Group", "Expanded");545 if (string.IsNullOrEmpty(label))546 {547 label = stateName ?? ExternalStateName;548 }549 if (!string.IsNullOrEmpty(stateName))550 {551 id += "." + stateName;552 }553 child = this.Graph.GetOrCreateNode(id, label);554 this.Graph.GetOrCreateLink(parent, child, null, null, "Contains");555 }556 return child;557 }558 private GraphLink GetOrCreateEventLink(GraphNode source, GraphNode target, EventInfo e)559 {560 GraphLink link = null;561 lock (this.Inbox)562 {563 string label = this.GetEventLabel(e.Event);564 var index = this.GetLinkIndex(source, target, label);565 var category = GetEventCategory(e.Event);566 link = this.Graph.GetOrCreateLink(source, target, index, label, category);567 if (this.MergeEventLinks)568 {569 if (link.AddListAttribute("EventIds", e.Event) > 1)570 {571 link.Label = "*";572 }573 }574 else575 {576 if (e.Event != null)577 {578 link.AddAttribute("EventId", e.Event);579 }580 if (e.HandlingState != null)581 {582 link.AddAttribute("HandledBy", e.HandlingState);583 }584 }585 }586 return link;587 }588 private void AddNamespace(string type)589 {590 if (type != null && !this.Namespaces.Contains(type))591 {592 string typeName = type;593 int index = typeName.Length;594 do595 {596 typeName = typeName.Substring(0, index);597 this.Namespaces.Add(typeName);598 index = typeName.LastIndexOfAny(TypeSeparators);599 }600 while (index > 0);601 }602 }603 private string GetLabel(string name, string type, string fullyQualifiedName)604 {605 if (type is null)606 {607 // external code608 return fullyQualifiedName;609 }610 this.AddNamespace(type);611 if (string.IsNullOrEmpty(fullyQualifiedName))612 {613 // then this is probably an Actor, not a StateMachine. For Actors we can invent a state614 // name equal to the short name of the class, this then looks like a Constructor which is fine.615 fullyQualifiedName = this.CollapseInstances ? type : name;616 }617 var index = fullyQualifiedName.LastIndexOfAny(TypeSeparators);618 if (index > 0)619 {620 fullyQualifiedName = fullyQualifiedName.Substring(index).Trim('+').Trim('.');621 }622 return fullyQualifiedName;623 }624 private string GetEventLabel(string fullyQualifiedName)625 {626 if (EventAliases.TryGetValue(fullyQualifiedName, out string label))627 {628 return label;629 }630 int i = fullyQualifiedName.LastIndexOfAny(TypeSeparators);631 if (i > 0)632 {633 string ns = fullyQualifiedName.Substring(0, i);634 if (this.Namespaces.Contains(ns))635 {636 return fullyQualifiedName.Substring(i + 1);637 }638 }639 return fullyQualifiedName;640 }641 private static string GetEventCategory(string fullyQualifiedName)642 {643 if (EventAliases.TryGetValue(fullyQualifiedName, out string label))644 {645 return label;646 }647 return null;648 }649 }650 /// <summary>651 /// A directed graph made up of Nodes and Links.652 /// </summary>653 [DataContract]654 public class Graph655 {656 internal const string DgmlNamespace = "http://schemas.microsoft.com/vs/2009/dgml";657 // These [DataMember] fields are here so we can serialize the Graph across parallel or distributed658 // test processes without losing any information. There is more information here than in the serialized659 // DGML which is we we can't just use Save/LoadDgml to do the same.660 [DataMember]661 private readonly Dictionary<string, GraphNode> InternalNodes = new Dictionary<string, GraphNode>();662 [DataMember]663 private readonly Dictionary<string, GraphLink> InternalLinks = new Dictionary<string, GraphLink>();664 // last used index for simple link key "a->b".665 [DataMember]666 private readonly Dictionary<string, int> InternalNextLinkIndex = new Dictionary<string, int>();667 // maps augmented link key to the index that has been allocated for that link id "a->b(goto)" => 0668 [DataMember]669 private readonly Dictionary<string, int> InternalAllocatedLinkIndexes = new Dictionary<string, int>();670 [DataMember]671 private readonly Dictionary<string, string> InternalAllocatedLinkIds = new Dictionary<string, string>();672 /// <summary>673 /// Return the current list of nodes (in no particular order).674 /// </summary>675 public IEnumerable<GraphNode> Nodes676 {677 get { return this.InternalNodes.Values; }678 }679 /// <summary>680 /// Return the current list of links (in no particular order).681 /// </summary>682 public IEnumerable<GraphLink> Links683 {684 get685 {686 if (this.InternalLinks is null)687 {688 return Array.Empty<GraphLink>();689 }690 return this.InternalLinks.Values;691 }692 }693 /// <summary>694 /// Get existing node or null.695 /// </summary>696 /// <param name="id">The id of the node.</param>697 public GraphNode GetNode(string id)698 {699 this.InternalNodes.TryGetValue(id, out GraphNode node);700 return node;701 }702 /// <summary>703 /// Get existing node or create a new one with the given id and label.704 /// </summary>705 /// <returns>Returns the new node or the existing node if it was already defined.</returns>706 public GraphNode GetOrCreateNode(string id, string label = null, string category = null)707 {708 if (!this.InternalNodes.TryGetValue(id, out GraphNode node))709 {710 node = new GraphNode(id, label, category);711 this.InternalNodes.Add(id, node);712 }713 return node;714 }715 /// <summary>716 /// Get existing node or create a new one with the given id and label.717 /// </summary>718 /// <returns>Returns the new node or the existing node if it was already defined.</returns>719 private GraphNode GetOrCreateNode(GraphNode newNode)720 {721 if (!this.InternalNodes.ContainsKey(newNode.Id))722 {723 this.InternalNodes.Add(newNode.Id, newNode);724 }725 return newNode;726 }727 /// <summary>728 /// Get existing link or create a new one connecting the given source and target nodes.729 /// </summary>730 /// <returns>The new link or the existing link if it was already defined.</returns>731 public GraphLink GetOrCreateLink(GraphNode source, GraphNode target, int? index = null, string linkLabel = null, string category = null)732 {733 string key = source.Id + "->" + target.Id;734 if (index.HasValue)735 {736 key += string.Format("({0})", index.Value);737 }738 if (!this.InternalLinks.TryGetValue(key, out GraphLink link))739 {740 link = new GraphLink(source, target, linkLabel, category);741 if (index.HasValue)742 {743 link.Index = index.Value;744 }745 this.InternalLinks.Add(key, link);746 }747 return link;748 }749 internal int GetUniqueLinkIndex(GraphNode source, GraphNode target, string id)750 {751 // augmented key752 string key = string.Format("{0}->{1}({2})", source.Id, target.Id, id);753 if (this.InternalAllocatedLinkIndexes.TryGetValue(key, out int index))754 {755 return index;756 }757 // allocate a new index for the simple key758 var simpleKey = string.Format("{0}->{1}", source.Id, target.Id);759 if (this.InternalNextLinkIndex.TryGetValue(simpleKey, out index))760 {761 index++;762 }763 this.InternalNextLinkIndex[simpleKey] = index;764 // remember this index has been allocated for this link id.765 this.InternalAllocatedLinkIndexes[key] = index;766 // remember the original id associated with this link index.767 key = string.Format("{0}->{1}({2})", source.Id, target.Id, index);768 this.InternalAllocatedLinkIds[key] = id;769 return index;770 }771 /// <summary>772 /// Serialize the graph to a DGML string.773 /// </summary>774 public override string ToString()775 {776 using var writer = new StringWriter();777 this.WriteDgml(writer, false);778 return writer.ToString();779 }780 internal void SaveDgml(string graphFilePath, bool includeDefaultStyles)781 {782 using StreamWriter writer = new StreamWriter(graphFilePath, false, Encoding.UTF8);783 this.WriteDgml(writer, includeDefaultStyles);784 }785 /// <summary>786 /// Serialize the graph to DGML.787 /// </summary>788 public void WriteDgml(TextWriter writer, bool includeDefaultStyles)789 {790 writer.WriteLine("<DirectedGraph xmlns='{0}'>", DgmlNamespace);791 writer.WriteLine(" <Nodes>");792 if (this.InternalNodes != null)793 {794 List<string> nodes = new List<string>(this.InternalNodes.Keys);795 nodes.Sort(StringComparer.Ordinal);796 foreach (var id in nodes)797 {798 GraphNode node = this.InternalNodes[id];799 writer.Write(" <Node Id='{0}'", node.Id);800 if (!string.IsNullOrEmpty(node.Label))801 {802 writer.Write(" Label='{0}'", node.Label);803 }804 if (!string.IsNullOrEmpty(node.Category))805 {806 writer.Write(" Category='{0}'", node.Category);807 }808 node.WriteAttributes(writer);809 writer.WriteLine("/>");810 }811 }812 writer.WriteLine(" </Nodes>");813 writer.WriteLine(" <Links>");814 if (this.InternalLinks != null)815 {816 List<string> links = new List<string>(this.InternalLinks.Keys);817 links.Sort(StringComparer.Ordinal);818 foreach (var id in links)819 {820 GraphLink link = this.InternalLinks[id];821 writer.Write(" <Link Source='{0}' Target='{1}'", link.Source.Id, link.Target.Id);822 if (!string.IsNullOrEmpty(link.Label))823 {824 writer.Write(" Label='{0}'", link.Label);825 }826 if (!string.IsNullOrEmpty(link.Category))827 {828 writer.Write(" Category='{0}'", link.Category);829 }830 if (link.Index.HasValue)831 {832 writer.Write(" Index='{0}'", link.Index.Value);833 }834 link.WriteAttributes(writer);835 writer.WriteLine("/>");836 }837 }838 writer.WriteLine(" </Links>");839 if (includeDefaultStyles)840 {841 writer.WriteLine(842@" <Styles>843 <Style TargetType=""Node"" GroupLabel=""Error"" ValueLabel=""True"">844 <Condition Expression=""HasCategory('Error')"" />845 <Setter Property=""Background"" Value=""#FFC15656"" />846 </Style>847 <Style TargetType=""Node"" GroupLabel=""Actor"" ValueLabel=""True"">848 <Condition Expression=""HasCategory('Actor')"" />849 <Setter Property=""Background"" Value=""#FF57AC56"" />850 </Style>851 <Style TargetType=""Node"" GroupLabel=""Monitor"" ValueLabel=""True"">852 <Condition Expression=""HasCategory('Monitor')"" />853 <Setter Property=""Background"" Value=""#FF558FDA"" />854 </Style>855 <Style TargetType=""Link"" GroupLabel=""halt"" ValueLabel=""True"">856 <Condition Expression=""HasCategory('halt')"" />857 <Setter Property=""Stroke"" Value=""#FFFF6C6C"" />858 <Setter Property=""StrokeDashArray"" Value=""4 2"" />859 </Style>860 <Style TargetType=""Link"" GroupLabel=""push"" ValueLabel=""True"">861 <Condition Expression=""HasCategory('push')"" />862 <Setter Property=""Stroke"" Value=""#FF7380F5"" />863 <Setter Property=""StrokeDashArray"" Value=""4 2"" />864 </Style>865 <Style TargetType=""Link"" GroupLabel=""pop"" ValueLabel=""True"">866 <Condition Expression=""HasCategory('pop')"" />867 <Setter Property=""Stroke"" Value=""#FF7380F5"" />868 <Setter Property=""StrokeDashArray"" Value=""4 2"" />869 </Style>870 </Styles>");871 }872 writer.WriteLine("</DirectedGraph>");873 }874 /// <summary>875 /// Load a DGML file into a new Graph object.876 /// </summary>877 /// <param name="graphFilePath">Full path to the DGML file.</param>878 /// <returns>The loaded Graph object.</returns>879 public static Graph LoadDgml(string graphFilePath)880 {881 XDocument doc = XDocument.Load(graphFilePath);882 Graph result = new Graph();883 var ns = doc.Root.Name.Namespace;884 if (ns != DgmlNamespace)885 {886 throw new InvalidOperationException(string.Format(887 "File '{0}' does not contain the DGML namespace", graphFilePath));888 }889 foreach (var e in doc.Root.Element(ns + "Nodes").Elements(ns + "Node"))890 {891 var id = (string)e.Attribute("Id");892 var label = (string)e.Attribute("Label");893 var category = (string)e.Attribute("Category");894 GraphNode node = new GraphNode(id, label, category);895 node.AddDgmlProperties(e);896 result.GetOrCreateNode(node);897 }898 foreach (var e in doc.Root.Element(ns + "Links").Elements(ns + "Link"))899 {900 var srcId = (string)e.Attribute("Source");901 var targetId = (string)e.Attribute("Target");902 var label = (string)e.Attribute("Label");903 var category = (string)e.Attribute("Category");904 var srcNode = result.GetOrCreateNode(srcId);905 var targetNode = result.GetOrCreateNode(targetId);906 XAttribute indexAttr = e.Attribute("index");907 int? index = null;908 if (indexAttr != null)909 {910 index = (int)indexAttr;911 }912 var link = result.GetOrCreateLink(srcNode, targetNode, index, label, category);913 link.AddDgmlProperties(e);914 }915 return result;916 }917 /// <summary>918 /// Merge the given graph so that this graph becomes a superset of both graphs.919 /// </summary>920 /// <param name="other">The new graph to merge into this graph.</param>921 public void Merge(Graph other)922 {923 foreach (var node in other.InternalNodes.Values)924 {925 var newNode = this.GetOrCreateNode(node.Id, node.Label, node.Category);926 newNode.Merge(node);927 }928 foreach (var link in other.InternalLinks.Values)929 {930 var source = this.GetOrCreateNode(link.Source.Id, link.Source.Label, link.Source.Category);931 var target = this.GetOrCreateNode(link.Target.Id, link.Target.Label, link.Target.Category);932 int? index = null;933 if (link.Index.HasValue)934 {935 // ouch, link indexes cannot be compared across Graph instances, we need to assign a new index here.936 string key = string.Format("{0}->{1}({2})", source.Id, target.Id, link.Index.Value);937 string linkId = other.InternalAllocatedLinkIds[key];938 index = this.GetUniqueLinkIndex(source, target, linkId);939 }940 var newLink = this.GetOrCreateLink(source, target, index, link.Label, link.Category);941 newLink.Merge(link);942 }943 }944 }945 /// <summary>946 /// A Node of a Graph.947 /// </summary>948 [DataContract]949 public class GraphObject950 {951 /// <summary>952 /// Optional list of attributes for the node.953 /// </summary>954 [DataMember]955 public Dictionary<string, string> Attributes { get; internal set; }956 /// <summary>957 /// Optional list of attributes that have a multi-part value.958 /// </summary>959 [DataMember]960 public Dictionary<string, HashSet<string>> AttributeLists { get; internal set; }961 /// <summary>962 /// Add an attribute to the node.963 /// </summary>964 public void AddAttribute(string name, string value)965 {966 if (this.Attributes is null)967 {968 this.Attributes = new Dictionary<string, string>();969 }970 this.Attributes[name] = value;971 }972 /// <summary>973 /// Creates a compound attribute value containing a merged list of unique values.974 /// </summary>975 /// <param name="key">The attribute name.</param>976 /// <param name="value">The new value to add to the unique list.</param>977 public int AddListAttribute(string key, string value)978 {979 if (this.AttributeLists is null)980 {981 this.AttributeLists = new Dictionary<string, HashSet<string>>();982 }983 if (!this.AttributeLists.TryGetValue(key, out HashSet<string> list))984 {985 list = new HashSet<string>();986 this.AttributeLists[key] = list;987 }988 list.Add(value);989 return list.Count;990 }991 internal void WriteAttributes(TextWriter writer)992 {993 if (this.Attributes != null)994 {995 List<string> names = new List<string>(this.Attributes.Keys);996 names.Sort(StringComparer.Ordinal); // creates a more stable output file (can be handy for expected output during testing).997 foreach (string name in names)998 {999 var value = this.Attributes[name];1000 writer.Write(" {0}='{1}'", name, value);1001 }1002 }1003 if (this.AttributeLists != null)1004 {1005 List<string> names = new List<string>(this.AttributeLists.Keys);1006 names.Sort(StringComparer.Ordinal); // creates a more stable output file (can be handy for expected output during testing).1007 foreach (string name in names)1008 {1009 var value = this.AttributeLists[name];1010 writer.Write(" {0}='{1}'", name, string.Join(",", value));1011 }1012 }1013 }1014 internal void Merge(GraphObject other)1015 {1016 if (other.Attributes != null)1017 {1018 foreach (var key in other.Attributes.Keys)1019 {1020 this.AddAttribute(key, other.Attributes[key]);1021 }1022 }1023 if (other.AttributeLists != null)1024 {1025 foreach (var key in other.AttributeLists.Keys)1026 {1027 foreach (var value in other.AttributeLists[key])1028 {1029 this.AddListAttribute(key, value);1030 }1031 }1032 }1033 }1034 }1035 /// <summary>1036 /// A Node of a Graph.1037 /// </summary>1038 [DataContract]1039 public class GraphNode : GraphObject1040 {1041 /// <summary>1042 /// The unique Id of the Node within the Graph.1043 /// </summary>1044 [DataMember]1045 public string Id { get; internal set; }1046 /// <summary>1047 /// An optional display label for the node (does not need to be unique).1048 /// </summary>1049 [DataMember]1050 public string Label { get; internal set; }1051 /// <summary>1052 /// An optional category for the node.1053 /// </summary>1054 [DataMember]1055 public string Category { get; internal set; }1056 /// <summary>1057 /// Initializes a new instance of the <see cref="GraphNode"/> class.1058 /// </summary>1059 public GraphNode(string id, string label, string category)1060 {1061 this.Id = id;1062 this.Label = label;1063 this.Category = category;1064 }1065 /// <summary>1066 /// Add additional properties from XML element.1067 /// </summary>1068 /// <param name="e">An XML element representing the graph node in DGML format.</param>1069 public void AddDgmlProperties(XElement e)1070 {1071 foreach (XAttribute a in e.Attributes())1072 {1073 switch (a.Name.LocalName)1074 {1075 case "Id":1076 case "Label":1077 case "Category":1078 break;1079 default:1080 this.AddAttribute(a.Name.LocalName, a.Value);1081 break;1082 }1083 }1084 }1085 }1086 /// <summary>1087 /// A Link represents a directed graph connection between two Nodes.1088 /// </summary>1089 [DataContract]1090 public class GraphLink : GraphObject1091 {1092 /// <summary>1093 /// An optional display label for the link.1094 /// </summary>1095 [DataMember]1096 public string Label { get; internal set; }1097 /// <summary>1098 /// An optional category for the link.1099 /// The special category "Contains" is reserved for building groups.1100 /// </summary>1101 [DataMember]1102 public string Category { get; internal set; }1103 /// <summary>1104 /// The source end of the link.1105 /// </summary>1106 [DataMember]1107 public GraphNode Source { get; internal set; }1108 /// <summary>1109 /// The target end of the link.1110 /// </summary>1111 [DataMember]1112 public GraphNode Target { get; internal set; }1113 /// <summary>1114 /// The optional link index.1115 /// </summary>1116 [DataMember]1117 public int? Index { get; internal set; }1118 /// <summary>1119 /// Initializes a new instance of the <see cref="GraphLink"/> class.1120 /// </summary>1121 public GraphLink(GraphNode source, GraphNode target, string label, string category)1122 {1123 this.Source = source;1124 this.Target = target;1125 this.Label = label;1126 this.Category = category;1127 }1128 /// <summary>1129 /// Add additional properties from XML element.1130 /// </summary>1131 /// <param name="e">An XML element representing the graph node in DGML format.</param>1132 public void AddDgmlProperties(XElement e)1133 {1134 foreach (XAttribute a in e.Attributes())1135 {...

Full Screen

Full Screen

GraphNode

Using AI Code Generation

copy

Full Screen

1using Microsoft.Coyote.Actors.Coverage;2using System;3using System.Collections.Generic;4using System.Linq;5using System.Text;6using System.Threading.Tasks;7{8 {9 public string Name { get; set; }10 public List<GraphNode> Children { get; set; }11 public GraphNode(string name)12 {13 Name = name;14 Children = new List<GraphNode>();15 }16 public void AddChild(GraphNode node)17 {18 Children.Add(node);19 }20 }21}22using Microsoft.Coyote.Actors.Coverage;23using System;24using System.Collections.Generic;25using System.Linq;26using System.Text;27using System.Threading.Tasks;28{29 {30 public GraphNode Root { get; set; }31 public Graph(GraphNode root)32 {33 Root = root;34 }35 }36}37using Microsoft.Coyote.Actors.Coverage;38using System;39using System.Collections.Generic;40using System.Linq;41using System.Text;42using System.Threading.Tasks;43{44 {45 public Graph BuildGraph(ActorRuntime runtime)46 {47 var root = new GraphNode("root");48 var rootChildren = new List<GraphNode>();49 foreach (var actor in runtime.GetActors())50 {51 var actorNode = new GraphNode(actor.Id.ToString());52 var actorChildren = new List<GraphNode>();53 foreach (var state in actor.GetStateNames())54 {55 var stateNode = new GraphNode(state);56 actorChildren.Add(stateNode);57 }58 actorNode.Children = actorChildren;59 rootChildren.Add(actorNode);60 }61 root.Children = rootChildren;62 return new Graph(root);63 }64 }65}66using Microsoft.Coyote.Actors.Coverage;67using Microsoft.Coyote.Runtime;68using System;69using System.Collections.Generic;70using System.Linq;

Full Screen

Full Screen

GraphNode

Using AI Code Generation

copy

Full Screen

1using Microsoft.Coyote.Actors.Coverage;2using System;3using System.Collections.Generic;4using System.Linq;5using System.Text;6using System.Threading.Tasks;7{8 {9 static void Main(string[] args)10 {11 GraphNode node1 = new GraphNode("Node1");12 GraphNode node2 = new GraphNode("Node2");13 GraphNode node3 = new GraphNode("Node3");14 GraphNode node4 = new GraphNode("Node4");15 GraphNode node5 = new GraphNode("Node5");16 GraphNode node6 = new GraphNode("Node6");17 GraphNode node7 = new GraphNode("Node7");18 GraphNode node8 = new GraphNode("Node8");19 GraphNode node9 = new GraphNode("Node9");20 GraphNode node10 = new GraphNode("Node10");21 GraphNode node11 = new GraphNode("Node11");22 GraphNode node12 = new GraphNode("Node12");23 GraphNode node13 = new GraphNode("Node13");24 GraphNode node14 = new GraphNode("Node14");25 GraphNode node15 = new GraphNode("Node15");26 GraphNode node16 = new GraphNode("Node16");27 GraphNode node17 = new GraphNode("Node17");28 GraphNode node18 = new GraphNode("Node18");29 GraphNode node19 = new GraphNode("Node19");30 GraphNode node20 = new GraphNode("Node20");31 GraphNode node21 = new GraphNode("Node21");32 GraphNode node22 = new GraphNode("Node22");33 GraphNode node23 = new GraphNode("Node23");34 GraphNode node24 = new GraphNode("Node24");35 GraphNode node25 = new GraphNode("Node25");36 GraphNode node26 = new GraphNode("Node26");37 GraphNode node27 = new GraphNode("Node27");38 GraphNode node28 = new GraphNode("Node28");39 GraphNode node29 = new GraphNode("Node29");40 GraphNode node30 = new GraphNode("Node30");41 GraphNode node31 = new GraphNode("Node31");42 GraphNode node32 = new GraphNode("Node32");43 GraphNode node33 = new GraphNode("Node33");44 GraphNode node34 = new GraphNode("Node34");

Full Screen

Full Screen

GraphNode

Using AI Code Generation

copy

Full Screen

1using Microsoft.Coyote.Actors.Coverage;2using System;3using System.Collections.Generic;4using System.Linq;5using System.Text;6using System.Threading.Tasks;7{8 {9 static void Main(string[] args)10 {11 GraphNode node1 = new GraphNode("1");12 GraphNode node2 = new GraphNode("2");13 GraphNode node3 = new GraphNode("3");14 GraphNode node4 = new GraphNode("4");15 GraphNode node5 = new GraphNode("5");16 GraphNode node6 = new GraphNode("6");17 GraphNode node7 = new GraphNode("7");18 GraphNode node8 = new GraphNode("8");19 GraphNode node9 = new GraphNode("9");20 GraphNode node10 = new GraphNode("10");21 GraphNode node11 = new GraphNode("11");22 GraphNode node12 = new GraphNode("12");23 GraphNode node13 = new GraphNode("13");24 GraphNode node14 = new GraphNode("14");25 GraphNode node15 = new GraphNode("15");26 GraphNode node16 = new GraphNode("16");27 GraphNode node17 = new GraphNode("17");28 GraphNode node18 = new GraphNode("18");29 GraphNode node19 = new GraphNode("19");30 GraphNode node20 = new GraphNode("20");31 GraphNode node21 = new GraphNode("21");32 GraphNode node22 = new GraphNode("22");33 GraphNode node23 = new GraphNode("23");34 GraphNode node24 = new GraphNode("24");35 GraphNode node25 = new GraphNode("25");36 GraphNode node26 = new GraphNode("26");37 GraphNode node27 = new GraphNode("27");38 GraphNode node28 = new GraphNode("28");39 GraphNode node29 = new GraphNode("29");40 GraphNode node30 = new GraphNode("30");41 GraphNode node31 = new GraphNode("31");42 GraphNode node32 = new GraphNode("32");43 GraphNode node33 = new GraphNode("33");44 GraphNode node34 = new GraphNode("34");45 GraphNode node35 = new GraphNode("35");46 GraphNode node36 = new GraphNode("36");47 GraphNode node37 = new GraphNode("37

Full Screen

Full Screen

GraphNode

Using AI Code Generation

copy

Full Screen

1using Microsoft.Coyote.Actors.Coverage;2using System;3using System.Collections.Generic;4using System.Linq;5using System.Text;6using System.Threading.Tasks;7{8 {9 static void Main(string[] args)10 {11 GraphNode node1 = new GraphNode("node1");12 GraphNode node2 = new GraphNode("node2");13 GraphNode node3 = new GraphNode("node3");14 GraphNode node4 = new GraphNode("node4");15 GraphNode node5 = new GraphNode("node5");16 GraphNode node6 = new GraphNode("node6");17 GraphNode node7 = new GraphNode("node7");18 GraphNode node8 = new GraphNode("node8");19 GraphNode node9 = new GraphNode("node9");20 GraphNode node10 = new GraphNode("node10");21 GraphNode node11 = new GraphNode("node11");22 GraphNode node12 = new GraphNode("node12");23 GraphNode node13 = new GraphNode("node13");24 GraphNode node14 = new GraphNode("node14");25 GraphNode node15 = new GraphNode("node15");26 GraphNode node16 = new GraphNode("node16");27 GraphNode node17 = new GraphNode("node17");28 GraphNode node18 = new GraphNode("node18");29 GraphNode node19 = new GraphNode("node19");30 GraphNode node20 = new GraphNode("node20");31 GraphNode node21 = new GraphNode("node21");

Full Screen

Full Screen

GraphNode

Using AI Code Generation

copy

Full Screen

1using Microsoft.Coyote.Actors.Coverage;2using System;3using System.Collections.Generic;4using System.Linq;5using System.Text;6using System.Threading.Tasks;7{8 {9 static void Main(string[] args)10 {11 var graphNode = new GraphNode();12 graphNode.Id = 1;13 graphNode.Name = "node1";14 Console.WriteLine(graphNode.Id);15 Console.WriteLine(graphNode.Name);16 Console.Read();17 }18 }19}

Full Screen

Full Screen

GraphNode

Using AI Code Generation

copy

Full Screen

1using Microsoft.Coyote.Actors.Coverage;2using Microsoft.Coyote.Actors.Coverage;3using Microsoft.Coyote.Actors.Coverage;4using Microsoft.Coyote.Actors.Coverage;5using Microsoft.Coyote.Actors.Coverage;6using Microsoft.Coyote.Actors.Coverage;7using Microsoft.Coyote.Actors.Coverage;8using Microsoft.Coyote.Actors.Coverage;9using Microsoft.Coyote.Actors.Coverage;10using Microsoft.Coyote.Actors.Coverage;11using Microsoft.Coyote.Actors.Coverage;12using Microsoft.Coyote.Actors.Coverage;13using Microsoft.Coyote.Actors.Coverage;14using Microsoft.Coyote.Actors.Coverage;15using Microsoft.Coyote.Actors.Coverage;16using Microsoft.Coyote.Actors.Coverage;

Full Screen

Full Screen

GraphNode

Using AI Code Generation

copy

Full Screen

1using Microsoft.Coyote.Actors.Coverage;2{3 {4 public static void Main(string[] args)5 {6 GraphNode node = new GraphNode();7 node.Name = "TestNode";8 node.Label = "TestLabel";9 node.IsEntry = true;10 node.IsExit = true;11 node.IsError = true;12 node.IsAccept = true;13 node.IsReject = true;14 node.IsSkip = true;15 node.IsReplay = true;16 node.IsReplayAsync = true;17 node.IsReplayNext = true;18 node.IsReplayNextAsync = true;19 node.IsReplayChoice = true;20 node.IsReplayChoiceAsync = true;21 node.IsReplayChoiceNext = true;22 node.IsReplayChoiceNextAsync = true;23 node.IsReplayChoiceNondet = true;24 node.IsReplayChoiceNondetAsync = true;25 node.IsReplayChoiceNondetNext = true;26 node.IsReplayChoiceNondetNextAsync = true;27 node.IsReplayNondet = true;28 node.IsReplayNondetAsync = true;29 node.IsReplayNondetNext = true;30 node.IsReplayNondetNextAsync = true;31 node.IsReplayTask = true;32 node.IsReplayTaskAsync = true;33 node.IsReplayTaskNext = true;34 node.IsReplayTaskNextAsync = true;35 node.IsReplayTaskNondet = true;36 node.IsReplayTaskNondetAsync = true;37 node.IsReplayTaskNondetNext = true;38 node.IsReplayTaskNondetNextAsync = true;39 node.IsReplayTaskChoice = true;40 node.IsReplayTaskChoiceAsync = true;41 node.IsReplayTaskChoiceNext = true;42 node.IsReplayTaskChoiceNextAsync = true;43 node.IsReplayTaskChoiceNondet = true;44 node.IsReplayTaskChoiceNondetAsync = true;45 node.IsReplayTaskChoiceNondetNext = true;46 node.IsReplayTaskChoiceNondetNextAsync = true;47 node.IsReplayTaskWait = true;48 node.IsReplayTaskWaitAsync = true;

Full Screen

Full Screen

GraphNode

Using AI Code Generation

copy

Full Screen

1using System;2using System.Collections.Generic;3using System.Linq;4using System.Text;5using System.Threading.Tasks;6using Microsoft.Coyote.Actors.Coverage;7using Microsoft.Coyote.Actors;8using Microsoft.Coyote.Runtime;9using Microsoft.Coyote;10using System.Threading;11using System.IO;12{13 {14 static void Main(string[] args)15 {16 var g = new GraphNode("main");17 var g1 = g.AddChild("g1");18 var g2 = g1.AddChild("g2");19 var g3 = g2.AddChild("g3");20 var g4 = g3.AddChild("g4");21 var g5 = g4.AddChild("g5");22 var g6 = g5.AddChild("g6");23 var g7 = g6.AddChild("g7");24 var g8 = g7.AddChild("g8");25 var g9 = g8.AddChild("g9");26 var g10 = g9.AddChild("g10");27 var g11 = g10.AddChild("g11");28 var g12 = g11.AddChild("g12");29 var g13 = g12.AddChild("g13");30 var g14 = g13.AddChild("g14");31 var g15 = g14.AddChild("g15");32 var g16 = g15.AddChild("g16");33 var g17 = g16.AddChild("g17");34 var g18 = g17.AddChild("g18");35 var g19 = g18.AddChild("g19");36 var g20 = g19.AddChild("g20");37 var g21 = g20.AddChild("g21");38 var g22 = g21.AddChild("g22");39 var g23 = g22.AddChild("g23");40 var g24 = g23.AddChild("g24");41 var g25 = g24.AddChild("g25");42 var g26 = g25.AddChild("g26");43 var g27 = g26.AddChild("g27");44 var g28 = g27.AddChild("g28");45 var g29 = g28.AddChild("g29");46 var g30 = g29.AddChild("g30");

Full Screen

Full Screen

GraphNode

Using AI Code Generation

copy

Full Screen

1using Microsoft.Coyote.Actors.Coverage;2{3 {4 static void Main(string[] args)5 {6 var graphNode = new GraphNode();7 graphNode.AddEdge(new GraphNode());8 graphNode.AddEdge(new GraphNode());9 }10 }11}12using Microsoft.Coyote.Actors.Coverage;13{14 {15 static void Main(string[] args)16 {17 var graphNode = new GraphNode();18 graphNode.AddEdge(new GraphNode());19 graphNode.AddEdge(new GraphNode());20 }21 }22}23using Microsoft.Coyote.Actors.Coverage;24{25 {26 static void Main(string[] args)27 {28 var graphNode = new GraphNode();29 graphNode.AddEdge(new GraphNode());30 graphNode.AddEdge(new GraphNode());31 }32 }33}34using Microsoft.Coyote.Actors.Coverage;35{36 {37 static void Main(string[] args)38 {39 var graphNode = new GraphNode();40 graphNode.AddEdge(new GraphNode());41 graphNode.AddEdge(new GraphNode());42 }43 }44}45using Microsoft.Coyote.Actors.Coverage;46{47 {48 static void Main(string[] args)49 {50 var graphNode = new GraphNode();51 graphNode.AddEdge(new GraphNode());52 graphNode.AddEdge(new GraphNode());53 }54 }55}56using Microsoft.Coyote.Actors.Coverage;57{58 {59 static void Main(string[] args)60 {61 var graphNode = new GraphNode();62 graphNode.AddEdge(new Graph

Full Screen

Full Screen

Automation Testing Tutorials

Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.

LambdaTest Learning Hubs:

YouTube

You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.

Run Coyote automation tests on LambdaTest cloud grid

Perform automation testing on 3000+ real desktop and mobile devices online.

Most used methods in GraphNode

Try LambdaTest Now !!

Get 100 minutes of automation test minutes FREE!!

Next-Gen App & Browser Testing Cloud

Was this article helpful?

Helpful

NotHelpful