Best Coyote code snippet using Microsoft.Coyote.Actors.Coverage.ActorRuntimeLogGraphBuilder.GetOrCreateNode
ActorRuntimeLogGraphBuilder.cs
Source:ActorRuntimeLogGraphBuilder.cs  
...95        {96            lock (this.Inbox)97            {98                var resolvedId = this.GetResolveActorId(id?.Name, id?.Type);99                GraphNode node = this.Graph.GetOrCreateNode(resolvedId);100                node.Category = ActorCategory;101                if (!string.IsNullOrEmpty(creatorName))102                {103                    var creatorId = this.GetResolveActorId(creatorName, creatorType);104                    GraphNode creator = this.Graph.GetOrCreateNode(creatorId);105                    this.GetOrCreateEventLink(creator, node, new EventInfo() { Event = "CreateActor" });106                }107            }108        }109        /// <inheritdoc/>110        public void OnCreateStateMachine(ActorId id, string creatorName, string creatorType)111        {112            lock (this.Inbox)113            {114                var resolvedId = this.GetResolveActorId(id?.Name, id?.Type);115                GraphNode node = this.Graph.GetOrCreateNode(resolvedId);116                node.Category = StateMachineCategory;117                if (!string.IsNullOrEmpty(creatorName))118                {119                    var creatorId = this.GetResolveActorId(creatorName, creatorType);120                    GraphNode creator = this.Graph.GetOrCreateNode(creatorId);121                    this.GetOrCreateEventLink(creator, node, new EventInfo() { Event = "CreateActor" });122                }123            }124        }125        /// <inheritdoc/>126        public void OnSendEvent(ActorId targetActorId, string senderName, string senderType, string senderStateName,127            Event e, Guid eventGroupId, bool isTargetHalted)128        {129            string eventName = e.GetType().FullName;130            this.AddEvent(targetActorId.Name, targetActorId.Type, senderName, senderType, senderStateName, eventName);131        }132        /// <inheritdoc/>133        public void OnRaiseEvent(ActorId id, string stateName, Event e)134        {135            string eventName = e.GetType().FullName;136            // Raising event to self.137            this.AddEvent(id.Name, id.Type, id.Name, id.Type, stateName, eventName);138        }139        /// <inheritdoc/>140        public void OnEnqueueEvent(ActorId id, Event e)141        {142        }143        /// <inheritdoc/>144        public void OnDequeueEvent(ActorId id, string stateName, Event e)145        {146            lock (this.Inbox)147            {148                var resolvedId = this.GetResolveActorId(id?.Name, id?.Type);149                string eventName = e.GetType().FullName;150                EventInfo info = this.PopEvent(resolvedId, eventName);151                if (info != null)152                {153                    this.Dequeued[id] = info;154                }155            }156        }157        private EventInfo PopEvent(string resolvedId, string eventName)158        {159            EventInfo result = null;160            lock (this.Inbox)161            {162                if (this.Inbox.TryGetValue(resolvedId, out List<EventInfo> inbox))163                {164                    for (int i = inbox.Count - 1; i >= 0; i--)165                    {166                        if (inbox[i].Event == eventName)167                        {168                            result = inbox[i];169                            inbox.RemoveAt(i);170                        }171                    }172                }173            }174            return result;175        }176        /// <inheritdoc/>177        public void OnReceiveEvent(ActorId id, string stateName, Event e, bool wasBlocked)178        {179            string resolvedId = this.GetResolveActorId(id?.Name, id?.Type);180            lock (this.Inbox)181            {182                if (this.Inbox.TryGetValue(resolvedId, out List<EventInfo> inbox))183                {184                    string eventName = e.GetType().FullName;185                    for (int i = inbox.Count - 1; i >= 0; i--)186                    {187                        EventInfo info = inbox[i];188                        if (info.Event == eventName)189                        {190                            // Yay, found it so we can draw the complete link connecting the Sender state to this state!191                            string category = string.IsNullOrEmpty(stateName) ? ActorCategory : StateMachineCategory;192                            var source = this.GetOrCreateChild(info.Name, info.Type, info.State);193                            var target = this.GetOrCreateChild(id?.Name, id?.Type, category, stateName);194                            this.GetOrCreateEventLink(source, target, info);195                            inbox.RemoveAt(i);196                            break;197                        }198                    }199                }200            }201        }202        /// <inheritdoc/>203        public void OnWaitEvent(ActorId id, string stateName, Type eventType)204        {205        }206        /// <inheritdoc/>207        public void OnWaitEvent(ActorId id, string stateName, params Type[] eventTypes)208        {209        }210        /// <inheritdoc/>211        public void OnStateTransition(ActorId id, string stateName, bool isEntry)212        {213            if (isEntry)214            {215                // record the fact we have entered this state216                this.GetOrCreateChild(id?.Name, id?.Type, stateName);217            }218        }219        /// <inheritdoc/>220        public void OnExecuteAction(ActorId id, string handlingStateName, string currentStateName, string actionName)221        {222            this.LinkTransition(typeof(DoActionEvent), id, handlingStateName, currentStateName, null);223        }224        /// <inheritdoc/>225        public void OnGotoState(ActorId id, string currentStateName, string newStateName)226        {227            this.LinkTransition(typeof(GotoStateEvent), id, currentStateName, currentStateName, newStateName);228        }229        /// <inheritdoc/>230        public void OnPushState(ActorId id, string currentStateName, string newStateName)231        {232            this.LinkTransition(typeof(PushStateEvent), id, currentStateName, currentStateName, newStateName);233        }234        /// <inheritdoc/>235        public void OnPopState(ActorId id, string currentStateName, string restoredStateName)236        {237            if (!string.IsNullOrEmpty(currentStateName))238            {239                this.LinkTransition(typeof(PopStateEvent), id, currentStateName,240                    currentStateName, restoredStateName);241            }242        }243        /// <inheritdoc/>244        public void OnHalt(ActorId id, int inboxSize)245        {246            lock (this.Inbox)247            {248                this.HaltedStates.TryGetValue(id, out string stateName);249                var target = this.GetOrCreateChild(id?.Name, id?.Type, "Halt", "Halt");250                // Transition to the Halt state.251                if (!string.IsNullOrEmpty(stateName))252                {253                    var source = this.GetOrCreateChild(id?.Name, id?.Type, stateName);254                    this.GetOrCreateEventLink(source, target, new EventInfo() { Event = typeof(HaltEvent).FullName });255                }256            }257        }258        private int? GetLinkIndex(GraphNode source, GraphNode target, string id)259        {260            if (this.MergeEventLinks)261            {262                return null;263            }264            return this.Graph.GetUniqueLinkIndex(source, target, id);265        }266        /// <inheritdoc/>267        public void OnDefaultEventHandler(ActorId id, string stateName)268        {269            lock (this.Inbox)270            {271                string resolvedId = this.GetResolveActorId(id?.Name, id?.Type);272                string eventName = typeof(DefaultEvent).FullName;273                this.AddEvent(id.Name, id.Type, id.Name, id.Type, stateName, eventName);274                this.Dequeued[id] = this.PopEvent(resolvedId, eventName);275            }276        }277        /// <inheritdoc/>278        public void OnHandleRaisedEvent(ActorId id, string stateName, Event e)279        {280            lock (this.Inbox)281            {282                // We used the inbox to store raised event, but it should be the first one handled since283                // raised events are highest priority.284                string resolvedId = this.GetResolveActorId(id?.Name, id?.Type);285                lock (this.Inbox)286                {287                    if (this.Inbox.TryGetValue(resolvedId, out List<EventInfo> inbox))288                    {289                        string eventName = e.GetType().FullName;290                        for (int i = inbox.Count - 1; i >= 0; i--)291                        {292                            EventInfo info = inbox[i];293                            if (info.Event == eventName)294                            {295                                this.Dequeued[id] = info;296                                break;297                            }298                        }299                    }300                }301            }302        }303        /// <inheritdoc/>304        public void OnPopStateUnhandledEvent(ActorId actorId, string currentStateName, Event e)305        {306            lock (this.Inbox)307            {308                if (e is HaltEvent)309                {310                    this.HaltedStates[actorId] = currentStateName;311                }312            }313        }314        /// <inheritdoc/>315        public void OnExceptionThrown(ActorId id, string stateName, string actionName, Exception ex)316        {317        }318        /// <inheritdoc/>319        public void OnExceptionHandled(ActorId id, string stateName, string actionName, Exception ex)320        {321        }322        /// <inheritdoc/>323        public void OnCreateTimer(TimerInfo info)324        {325            // TODO: figure out how to graph timers when we have no "timer id" at this point...326        }327        /// <inheritdoc/>328        public void OnStopTimer(TimerInfo info)329        {330        }331        /// <inheritdoc/>332        public void OnCreateMonitor(string monitorType)333        {334            lock (this.Inbox)335            {336                GraphNode node = this.Graph.GetOrCreateNode(monitorType, monitorType);337                node.Category = MonitorCategory;338            }339        }340        /// <inheritdoc/>341        public void OnMonitorExecuteAction(string monitorType, string stateName, string actionName)342        {343            // Monitors process actions immediately, so this state transition is a result of the only event in the inbox.344            lock (this.Inbox)345            {346                if (this.Inbox.TryGetValue(monitorType, out List<EventInfo> inbox) && inbox.Count > 0)347                {348                    var e = inbox[inbox.Count - 1];349                    inbox.RemoveAt(inbox.Count - 1);350                    // Draw the link connecting the Sender state to this state!351                    var source = this.GetOrCreateChild(e.Name, e.Type, e.State);352                    var target = this.GetOrCreateChild(monitorType, monitorType, stateName);353                    this.GetOrCreateEventLink(source, target, e);354                }355            }356        }357        /// <inheritdoc/>358        public void OnMonitorProcessEvent(string monitorType, string stateName, string senderName, string senderType,359            string senderStateName, Event e)360        {361            lock (this.Inbox)362            {363                string eventName = e.GetType().FullName;364                // Now add a fake event for internal monitor state transition that might now happen as a result of this event,365                // storing the monitor's current state in this event.366                var info = this.AddEvent(monitorType, monitorType, monitorType, monitorType, stateName, eventName);367                // Draw the link connecting the Sender state to this state!368                var source = this.GetOrCreateChild(senderName, senderType, senderStateName);369                var target = this.GetOrCreateChild(monitorType, monitorType, stateName);370                this.GetOrCreateEventLink(source, target, info);371            }372        }373        /// <inheritdoc/>374        public void OnMonitorRaiseEvent(string monitorType, string stateName, Event e)375        {376            // Raising event to self.377            string eventName = e.GetType().FullName;378            this.AddEvent(monitorType, monitorType, monitorType, monitorType, stateName, eventName);379        }380        /// <inheritdoc/>381        public void OnMonitorStateTransition(string monitorType, string stateName, bool isEntry, bool? isInHotState)382        {383            if (isEntry)384            {385                lock (this.Inbox)386                {387                    // Monitors process events immediately (and does not call OnDequeue), so this state transition is a result of388                    // the fake event we created in OnMonitorProcessEvent.389                    if (this.Inbox.TryGetValue(monitorType, out List<EventInfo> inbox) && inbox.Count > 0)390                    {391                        var info = inbox[inbox.Count - 1];392                        inbox.RemoveAt(inbox.Count - 1);393                        // draw the link connecting the current state to this new state!394                        var source = this.GetOrCreateChild(monitorType, monitorType, info.State);395                        var shortStateName = this.GetLabel(monitorType, monitorType, stateName);396                        string suffix = string.Empty;397                        if (isInHotState.HasValue)398                        {399                            suffix = (isInHotState is true) ? "[hot]" : "[cold]";400                            shortStateName += suffix;401                        }402                        string label = shortStateName;403                        var target = this.GetOrCreateChild(monitorType, monitorType, shortStateName, label);404                        // In case this node was already created, we may need to override the label here now that405                        // we know this is a hot state. This is because, unfortunately, other OnMonitor* methods406                        // do not provide the isInHotState parameter.407                        target.Label = label;408                        this.GetOrCreateEventLink(source, target, info);409                    }410                }411            }412        }413        /// <inheritdoc/>414        public void OnMonitorError(string monitorType, string stateName, bool? isInHotState)415        {416            var source = this.GetOrCreateChild(monitorType, monitorType, stateName);417            source.Category = "Error";418        }419        /// <inheritdoc/>420        public void OnRandom(object result, string callerName, string callerType)421        {422        }423        /// <inheritdoc/>424        public void OnAssertionFailure(string error)425        {426        }427        /// <inheritdoc/>428        public void OnStrategyDescription(string strategyName, string description)429        {430        }431        /// <inheritdoc/>432        public void OnCompleted()433        {434        }435        /// <summary>436        /// Return current graph and reset for next iteration.437        /// </summary>438        /// <param name="reset">Set to true will reset the graph for the next iteration.</param>439        /// <returns>The graph.</returns>440        public Graph SnapshotGraph(bool reset)441        {442            Graph result = this.CurrentGraph;443            if (reset)444            {445                // start fresh.446                this.CurrentGraph = null;447            }448            return result;449        }450        private string GetResolveActorId(string name, string type)451        {452            if (type is null)453            {454                // The sender id can be null if an event is fired from non-actor code.455                return ExternalCodeName;456            }457            if (this.CollapseMachineInstances)458            {459                return type;460            }461            return name;462        }463        private EventInfo AddEvent(string targetName, string targetType, string senderName, string senderType,464            string senderStateName, string eventName)465        {466            string targetId = this.GetResolveActorId(targetName, targetType);467            EventInfo info = null;468            lock (this.Inbox)469            {470                if (!this.Inbox.TryGetValue(targetId, out List<EventInfo> inbox))471                {472                    inbox = new List<EventInfo>();473                    this.Inbox[targetId] = inbox;474                }475                info = new EventInfo()476                {477                    Name = senderName ?? ExternalCodeName,478                    Type = senderType ?? ExternalCodeName,479                    State = senderStateName,480                    Event = eventName481                };482                inbox.Add(info);483            }484            return info;485        }486        private void LinkTransition(Type transitionType, ActorId id, string handlingStateName,487            string currentStateName, string newStateName)488        {489            string name = id.Name;490            string type = id.Type;491            lock (this.Inbox)492            {493                if (this.Dequeued.TryGetValue(id, out EventInfo info))494                {495                    // Event was dequeued, but now we know what state is handling this event, so connect the dots...496                    if (info.Type != type || info.Name != name || info.State != currentStateName)497                    {498                        var source = this.GetOrCreateChild(info.Name, info.Type, info.State);499                        var target = this.GetOrCreateChild(name, type, currentStateName);500                        info.HandlingState = handlingStateName;501                        this.GetOrCreateEventLink(source, target, info);502                    }503                }504                if (newStateName != null)505                {506                    // Then this is a goto or push and we can draw that link also.507                    var source = this.GetOrCreateChild(name, type, currentStateName);508                    var target = this.GetOrCreateChild(name, type, newStateName);509                    if (info is null)510                    {511                        info = new EventInfo { Event = transitionType.FullName };512                    }513                    this.GetOrCreateEventLink(source, target, info);514                }515                this.Dequeued.Remove(id);516            }517        }518        private GraphNode GetOrCreateChild(string name, string type, string stateName, string label = null)519        {520            GraphNode child = null;521            lock (this.Inbox)522            {523                this.AddNamespace(type);524                var initalStateName = stateName;525                // make label relative to fully qualified actor id (it's usually a nested class).526                stateName = this.GetLabel(name, type, stateName);527                string id = this.GetResolveActorId(name, type);528                GraphNode parent = this.Graph.GetOrCreateNode(id);529                parent.AddAttribute("Group", "Expanded");530                if (string.IsNullOrEmpty(label))531                {532                    label = stateName ?? ExternalStateName;533                }534                if (!string.IsNullOrEmpty(stateName))535                {536                    id += "." + stateName;537                }538                child = this.Graph.GetOrCreateNode(id, label);539                this.Graph.GetOrCreateLink(parent, child, null, null, "Contains");540            }541            return child;542        }543        private GraphLink GetOrCreateEventLink(GraphNode source, GraphNode target, EventInfo e)544        {545            GraphLink link = null;546            lock (this.Inbox)547            {548                string label = this.GetEventLabel(e.Event);549                var index = this.GetLinkIndex(source, target, label);550                var category = GetEventCategory(e.Event);551                link = this.Graph.GetOrCreateLink(source, target, index, label, category);552                if (this.MergeEventLinks)553                {554                    if (link.AddListAttribute("EventIds", e.Event) > 1)555                    {556                        link.Label = "*";557                    }558                }559                else560                {561                    if (e.Event != null)562                    {563                        link.AddAttribute("EventId", e.Event);564                    }565                    if (e.HandlingState != null)566                    {567                        link.AddAttribute("HandledBy", e.HandlingState);568                    }569                }570            }571            return link;572        }573        private void AddNamespace(string type)574        {575            if (type != null && !this.Namespaces.Contains(type))576            {577                string typeName = type;578                int index = typeName.Length;579                do580                {581                    typeName = typeName.Substring(0, index);582                    this.Namespaces.Add(typeName);583                    index = typeName.LastIndexOfAny(TypeSeparators);584                }585                while (index > 0);586            }587        }588        private string GetLabel(string name, string type, string fullyQualifiedName)589        {590            if (type is null)591            {592                // external code593                return fullyQualifiedName;594            }595            this.AddNamespace(type);596            if (string.IsNullOrEmpty(fullyQualifiedName))597            {598                // then this is probably an Actor, not a StateMachine.  For Actors we can invent a state599                // name equal to the short name of the class, this then looks like a Constructor which is fine.600                fullyQualifiedName = this.CollapseMachineInstances ? type : name;601            }602            var len = fullyQualifiedName.Length;603            var index = fullyQualifiedName.LastIndexOfAny(TypeSeparators);604            if (index > 0)605            {606                fullyQualifiedName = fullyQualifiedName.Substring(index).Trim('+').Trim('.');607            }608            return fullyQualifiedName;609        }610        private string GetEventLabel(string fullyQualifiedName)611        {612            if (EventAliases.TryGetValue(fullyQualifiedName, out string label))613            {614                return label;615            }616            int i = fullyQualifiedName.LastIndexOfAny(TypeSeparators);617            if (i > 0)618            {619                string ns = fullyQualifiedName.Substring(0, i);620                if (this.Namespaces.Contains(ns))621                {622                    return fullyQualifiedName.Substring(i + 1);623                }624            }625            return fullyQualifiedName;626        }627        private static string GetEventCategory(string fullyQualifiedName)628        {629            if (EventAliases.TryGetValue(fullyQualifiedName, out string label))630            {631                return label;632            }633            return null;634        }635    }636    /// <summary>637    /// A directed graph made up of Nodes and Links.638    /// </summary>639    [DataContract]640    public class Graph641    {642        internal const string DgmlNamespace = "http://schemas.microsoft.com/vs/2009/dgml";643        // These [DataMember] fields are here so we can serialize the Graph across parallel or distributed644        // test processes without losing any information.  There is more information here than in the serialized645        // DGML which is we we can't just use Save/LoadDgml to do the same.646        [DataMember]647        private readonly Dictionary<string, GraphNode> InternalNodes = new Dictionary<string, GraphNode>();648        [DataMember]649        private readonly Dictionary<string, GraphLink> InternalLinks = new Dictionary<string, GraphLink>();650        // last used index for simple link key "a->b".651        [DataMember]652        private readonly Dictionary<string, int> InternalNextLinkIndex = new Dictionary<string, int>();653        // maps augmented link key to the index that has been allocated for that link id "a->b(goto)" => 0654        [DataMember]655        private readonly Dictionary<string, int> InternalAllocatedLinkIndexes = new Dictionary<string, int>();656        [DataMember]657        private readonly Dictionary<string, string> InternalAllocatedLinkIds = new Dictionary<string, string>();658        /// <summary>659        /// Return the current list of nodes (in no particular order).660        /// </summary>661        public IEnumerable<GraphNode> Nodes662        {663            get { return this.InternalNodes.Values; }664        }665        /// <summary>666        /// Return the current list of links (in no particular order).667        /// </summary>668        public IEnumerable<GraphLink> Links669        {670            get671            {672                if (this.InternalLinks is null)673                {674                    return Array.Empty<GraphLink>();675                }676                return this.InternalLinks.Values;677            }678        }679        /// <summary>680        /// Get existing node or null.681        /// </summary>682        /// <param name="id">The id of the node.</param>683        public GraphNode GetNode(string id)684        {685            this.InternalNodes.TryGetValue(id, out GraphNode node);686            return node;687        }688        /// <summary>689        /// Get existing node or create a new one with the given id and label.690        /// </summary>691        /// <returns>Returns the new node or the existing node if it was already defined.</returns>692        public GraphNode GetOrCreateNode(string id, string label = null, string category = null)693        {694            if (!this.InternalNodes.TryGetValue(id, out GraphNode node))695            {696                node = new GraphNode(id, label, category);697                this.InternalNodes.Add(id, node);698            }699            return node;700        }701        /// <summary>702        /// Get existing node or create a new one with the given id and label.703        /// </summary>704        /// <returns>Returns the new node or the existing node if it was already defined.</returns>705        private GraphNode GetOrCreateNode(GraphNode newNode)706        {707            if (!this.InternalNodes.ContainsKey(newNode.Id))708            {709                this.InternalNodes.Add(newNode.Id, newNode);710            }711            return newNode;712        }713        /// <summary>714        /// Get existing link or create a new one connecting the given source and target nodes.715        /// </summary>716        /// <returns>The new link or the existing link if it was already defined.</returns>717        public GraphLink GetOrCreateLink(GraphNode source, GraphNode target, int? index = null, string linkLabel = null, string category = null)718        {719            string key = source.Id + "->" + target.Id;720            if (index.HasValue)721            {722                key += string.Format("({0})", index.Value);723            }724            if (!this.InternalLinks.TryGetValue(key, out GraphLink link))725            {726                link = new GraphLink(source, target, linkLabel, category);727                if (index.HasValue)728                {729                    link.Index = index.Value;730                }731                this.InternalLinks.Add(key, link);732            }733            return link;734        }735        internal int GetUniqueLinkIndex(GraphNode source, GraphNode target, string id)736        {737            // augmented key738            string key = string.Format("{0}->{1}({2})", source.Id, target.Id, id);739            if (this.InternalAllocatedLinkIndexes.TryGetValue(key, out int index))740            {741                return index;742            }743            // allocate a new index for the simple key744            var simpleKey = string.Format("{0}->{1}", source.Id, target.Id);745            if (this.InternalNextLinkIndex.TryGetValue(simpleKey, out index))746            {747                index++;748            }749            this.InternalNextLinkIndex[simpleKey] = index;750            // remember this index has been allocated for this link id.751            this.InternalAllocatedLinkIndexes[key] = index;752            // remember the original id associated with this link index.753            key = string.Format("{0}->{1}({2})", source.Id, target.Id, index);754            this.InternalAllocatedLinkIds[key] = id;755            return index;756        }757        /// <summary>758        /// Serialize the graph to a DGML string.759        /// </summary>760        public override string ToString()761        {762            using (var writer = new StringWriter())763            {764                this.WriteDgml(writer, false);765                return writer.ToString();766            }767        }768        internal void SaveDgml(string graphFilePath, bool includeDefaultStyles)769        {770            using (StreamWriter writer = new StreamWriter(graphFilePath, false, Encoding.UTF8))771            {772                this.WriteDgml(writer, includeDefaultStyles);773            }774        }775        /// <summary>776        /// Serialize the graph to DGML.777        /// </summary>778        public void WriteDgml(TextWriter writer, bool includeDefaultStyles)779        {780            writer.WriteLine("<DirectedGraph xmlns='{0}'>", DgmlNamespace);781            writer.WriteLine("  <Nodes>");782            if (this.InternalNodes != null)783            {784                List<string> nodes = new List<string>(this.InternalNodes.Keys);785                nodes.Sort(StringComparer.Ordinal);786                foreach (var id in nodes)787                {788                    GraphNode node = this.InternalNodes[id];789                    writer.Write("    <Node Id='{0}'", node.Id);790                    if (!string.IsNullOrEmpty(node.Label))791                    {792                        writer.Write(" Label='{0}'", node.Label);793                    }794                    if (!string.IsNullOrEmpty(node.Category))795                    {796                        writer.Write(" Category='{0}'", node.Category);797                    }798                    node.WriteAttributes(writer);799                    writer.WriteLine("/>");800                }801            }802            writer.WriteLine("  </Nodes>");803            writer.WriteLine("  <Links>");804            if (this.InternalLinks != null)805            {806                List<string> links = new List<string>(this.InternalLinks.Keys);807                links.Sort(StringComparer.Ordinal);808                foreach (var id in links)809                {810                    GraphLink link = this.InternalLinks[id];811                    writer.Write("    <Link Source='{0}' Target='{1}'", link.Source.Id, link.Target.Id);812                    if (!string.IsNullOrEmpty(link.Label))813                    {814                        writer.Write(" Label='{0}'", link.Label);815                    }816                    if (!string.IsNullOrEmpty(link.Category))817                    {818                        writer.Write(" Category='{0}'", link.Category);819                    }820                    if (link.Index.HasValue)821                    {822                        writer.Write(" Index='{0}'", link.Index.Value);823                    }824                    link.WriteAttributes(writer);825                    writer.WriteLine("/>");826                }827            }828            writer.WriteLine("  </Links>");829            if (includeDefaultStyles)830            {831                writer.WriteLine(832@"  <Styles>833    <Style TargetType=""Node"" GroupLabel=""Error"" ValueLabel=""True"">834      <Condition Expression=""HasCategory('Error')"" />835      <Setter Property=""Background"" Value=""#FFC15656"" />836    </Style>837    <Style TargetType=""Node"" GroupLabel=""Actor"" ValueLabel=""True"">838      <Condition Expression=""HasCategory('Actor')"" />839      <Setter Property=""Background"" Value=""#FF57AC56"" />840    </Style>841    <Style TargetType=""Node"" GroupLabel=""Monitor"" ValueLabel=""True"">842      <Condition Expression=""HasCategory('Monitor')"" />843      <Setter Property=""Background"" Value=""#FF558FDA"" />844    </Style>845    <Style TargetType=""Link"" GroupLabel=""halt"" ValueLabel=""True"">846      <Condition Expression=""HasCategory('halt')"" />847      <Setter Property=""Stroke"" Value=""#FFFF6C6C"" />848      <Setter Property=""StrokeDashArray"" Value=""4 2"" />849    </Style>850    <Style TargetType=""Link"" GroupLabel=""push"" ValueLabel=""True"">851      <Condition Expression=""HasCategory('push')"" />852      <Setter Property=""Stroke"" Value=""#FF7380F5"" />853      <Setter Property=""StrokeDashArray"" Value=""4 2"" />854    </Style>855    <Style TargetType=""Link"" GroupLabel=""pop"" ValueLabel=""True"">856      <Condition Expression=""HasCategory('pop')"" />857      <Setter Property=""Stroke"" Value=""#FF7380F5"" />858      <Setter Property=""StrokeDashArray"" Value=""4 2"" />859    </Style>860  </Styles>");861            }862            writer.WriteLine("</DirectedGraph>");863        }864        /// <summary>865        /// Load a DGML file into a new Graph object.866        /// </summary>867        /// <param name="graphFilePath">Full path to the DGML file.</param>868        /// <returns>The loaded Graph object.</returns>869        public static Graph LoadDgml(string graphFilePath)870        {871            XDocument doc = XDocument.Load(graphFilePath);872            Graph result = new Graph();873            var ns = doc.Root.Name.Namespace;874            if (ns != DgmlNamespace)875            {876                throw new Exception(string.Format("File '{0}' does not contain the DGML namespace", graphFilePath));877            }878            foreach (var e in doc.Root.Element(ns + "Nodes").Elements(ns + "Node"))879            {880                var id = (string)e.Attribute("Id");881                var label = (string)e.Attribute("Label");882                var category = (string)e.Attribute("Category");883                GraphNode node = new GraphNode(id, label, category);884                node.AddDgmlProperties(e);885                result.GetOrCreateNode(node);886            }887            foreach (var e in doc.Root.Element(ns + "Links").Elements(ns + "Link"))888            {889                var srcId = (string)e.Attribute("Source");890                var targetId = (string)e.Attribute("Target");891                var label = (string)e.Attribute("Label");892                var category = (string)e.Attribute("Category");893                var srcNode = result.GetOrCreateNode(srcId);894                var targetNode = result.GetOrCreateNode(targetId);895                XAttribute indexAttr = e.Attribute("index");896                int? index = null;897                if (indexAttr != null)898                {899                    index = (int)indexAttr;900                }901                var link = result.GetOrCreateLink(srcNode, targetNode, index, label, category);902                link.AddDgmlProperties(e);903            }904            return result;905        }906        /// <summary>907        /// Merge the given graph so that this graph becomes a superset of both graphs.908        /// </summary>909        /// <param name="other">The new graph to merge into this graph.</param>910        public void Merge(Graph other)911        {912            foreach (var node in other.InternalNodes.Values)913            {914                var newNode = this.GetOrCreateNode(node.Id, node.Label, node.Category);915                newNode.Merge(node);916            }917            foreach (var link in other.InternalLinks.Values)918            {919                var source = this.GetOrCreateNode(link.Source.Id, link.Source.Label, link.Source.Category);920                var target = this.GetOrCreateNode(link.Target.Id, link.Target.Label, link.Target.Category);921                int? index = null;922                if (link.Index.HasValue)923                {924                    // ouch, link indexes cannot be compared across Graph instances, we need to assign a new index here.925                    string key = string.Format("{0}->{1}({2})", source.Id, target.Id, link.Index.Value);926                    string linkId = other.InternalAllocatedLinkIds[key];927                    index = this.GetUniqueLinkIndex(source, target, linkId);928                }929                var newLink = this.GetOrCreateLink(source, target, index, link.Label, link.Category);930                newLink.Merge(link);931            }932        }933    }934    /// <summary>...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.
You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.
Get 100 minutes of automation test minutes FREE!!
