Testing Diagram in Code

Hi,

We are trying to write some tests for some functionality and are stuck in creating the diagram in Code. We can create a model, add it to the diagram which is created in code but after Diagram.Model = model the Diagram.Nodes list is empty. Is there any way to get this list filled with the nodes created in the Model in code?

Thanks,
Frank

Is the Diagram already visible in your app at the time that you execute this code? If it is, then the Nodes should exist but they might not be measured/arranged.

If you cannot see the Diagram in your app, then it might not have had its ControlTemplate applied, so it hasn’t been initialized yet. In this case there is no DiagramPanel for the Diagram, so there are no Layers yet, so there could not be any Nodes in any Layers. You’ll need to wait until the Diagram.InitialLayoutCompleted event to make sure that all of the Nodes and Links are there and have been fully initialized.

No the Diagram is not visisble. We were trying to do these test in the same silverlight space were our unit tests run. So what I understand from this that it will not be possible to run tests in a unit test way on the Diagram, is that correct?

That depends on what kind of unit tests you want to write. If you are doing the normal kinds of tests where you are making sure the Nodes and Links match up with the model data, or that Nodes have a particular size or location or appearance, or that those Nodes or Links respond to particular user events, then you are going to have to use an initialized Diagram. This is no different (except in complexity) than having a Page be initialized and loaded so that you can make sure it is showing the correct application view-model data or that certain controls such as textboxes or scrollbars on the page have expected behavior.

Our own regression test system does make every Diagram visible as part of the test framework app. (We created this for ourselves before any other test frameworks existed.) But our model tests don’t make use of Diagrams at all, so those run without any Diagram.

Hi Walter,

The Test I want to do is on a circuit diagram where each node gets input from the link and the link gets input from the prior node, kind of like the example that is shipped with GoXam. I want to add two nodes and a link, initialize the timer, Cal Sub UpdateValues ( like in the Goxam circuit example ) Which calls update collection. After the nodes are updated I want to test if the value on one of the nodes is what I expect. Here follows a part of the test wjithou the initialaization of

Dim

model = New GraphLinksModel(Of TestNodeBranchData, String, String, TestCableLinkData)()

model.Load(

Of TestNodeBranchData, TestCableLinkData)(NodeBranchRoot, “NodeBranchData”, “CableLinkData”)


Diagram.Model = model

ForwardLevelCalculator.UpdateValues(Diagram, _DesignEnvironment)

For Each Node As Northwoods.GoXam.Node In Diagram.Nodes

Dim element As TestNodeBranchData = TryCast(Node.Data, TestNodeBranchData)

If element.Text = "A01" Then

Assert.IsTrue(element.ElementInOutLevelList(0).OutPut = CDec(39))

Exit For

End If

NodeBranchRoot contains the XML to create the nodes and as I mentioned Diagram.nodes is empty as you explained. What would I have to add to make this work? Do you have some type of example to point me in the right direction?

Next

Have you put that Diagram into the visual tree of the App?

Make sure that you execute the "check" or "test" part of your code after Silverlight has had a chance to initialize and load everything, including your "setup" code. Typically you can get code to execute "later" by calling Dispatcher.BeginInvoke(Action) on your app container element.

Hi Walter

We have added a page with a very small diagram ( 3 nodes ) to the testpanel of the Silverlight testing framework and hooked into the Page loaded event. We now see the diagram.nodes but keep getting a silverlight layout cycle detected. layout could not complete error. Have you ever encountered this? We are using a async testmethod.

Frank

I would search the web for help on that error. I would guess that you have implemented some layout-related event handler that modifies some child element, causing Silverlight layout to try to occur again.

Hi Walter,

We have narrowed it down to whenevr we ad a link the error occurs, when we add just two nodes without a link there is no error. Is there a way that we can turn off the layout of the diagram other than go:Part.LayoutId=“None” and go:TreeLayout ConditionFlags="" ? We actualy do not need the Diagram at all but are forced to load it to get to the Diagram.Nodes so that we can use the Node.LinksOutOf and the Node.NodesOutOf. If we use the model to do the test these methods are not there. We have kept it very simple and have no layout events implemented

You don’t need to test with the actual Diagram? OK, then just create a model and use its methods. Don’t use Diagram/Layer/Part/Node/Link/FrameworkElement/UIElement/DependencyObject or DataTemplate/Binding at all.

Hi Walter,

We have the test working and changed all of our calculation on nodes to accept the Model. We now have another problem which is that the results don’t get updated in the diagram. We are making calculation changes directly to the Model. We have implemented RaisePropertychanged calls on all properties. Shouldn’t changes to the Model update the Diagram?



myDiagram.StartTransaction(“LevelCalc”)
ForwardLevelCalculator.UpdateValues(myDiagram, _DesignEnvironment.FrequencyList)
myDiagram.CommitTransaction(“LevelCalc”)

UpdateValues calls:

UpdateCollection(myDiagram.Model, Nodes, freq.PrimKey, freq.SignalType)

And UpdateCollection is as follows:

Private Shared Sub UpdateCollection(ByRef Model As Model.ILinksModel, ByVal Nodes As List(Of ICableElement), FrequencyKey As Int32, SignalType As String)
Dim newnodes As New List(Of ICableElement)
Dim NodeLinks As Object
Dim OutNodes As Object
Dim LinksOutOf As Object
For Each StartNode As ICableElement In Nodes
'Dim Elementdata As ICableElement = TryCast(Element.Data, ICableElement)
’ One of the starter nodes might have had its value changed by the user.
’ So, update the values of the links leading out of it to reflect that change.
If StartNode IsNot Nothing Then

'NodeLinks = Model.GetLinksForNode(StartNode)
OutNodes = Model.GetToNodesForNode(StartNode)




LinksOutOf = Model.GetToLinksForNode(StartNode)
For Each l As ICableKabel In LinksOutOf
If l IsNot Nothing Then
If Not l.Processed Then
l.setInputLevel(FrequencyKey, SignalType, StartNode.getOutputLevel(FrequencyKey))
l.Processed = True
End If
End If
Next

End If

For Each node As ICableElement In OutNodes

’ ignore nodes already “visited”
If Not node.Processed Then
node.Processed = True ’ declare “visited”
newnodes.Add(node)
Dim cableOutPut As Decimal
Dim LinksInTo As Object
LinksInTo = Model.GetFromLinksForNode(node)
For Each l As ICableKabel In LinksInTo
’ There is only one link going in
cableOutPut = l.getOutputLevel(FrequencyKey)
Next

node.setInputLevel(FrequencyKey, SignalType, cableOutPut)

’ Once the value of a Node has been updated,
’ set the values of the links leading out of it
LinksOutOf = Model.GetToLinksForNode(node)
For Each l As ICableKabel In LinksOutOf
’ There can be more links going out but this is only true for splitter, coupler and extender
’ these links going out have to only pass the value so that the branch can do the calculation
l.setInputLevel(FrequencyKey, SignalType, node.getOutputLevel(FrequencyKey))
Next l

End If
Next node
Next
’ modify the collection to reflect new nodes that need to be executed
Nodes.Clear()
Nodes.AddRange(newnodes)
End Sub

I’m confused – I thought you weren’t going to depend on the existence of any Diagram, but now you are saying that there is a Diagram and it isn’t being updated?

So if there is a Diagram, what do you see in it? Does the Diagram not show any Nodes or Links at all? If this is the case, perhaps your code is not operating on the Diagram that you think you are, or perhaps it does not have the model that you think it does. Maybe it got replaced by some other code.

Or does the Diagram seem to have the right Nodes and Links but they don’t show updated properties in them? This might be another case of operating on the wrong diagram or model. Or maybe your templates aren’t binding to all of the desired data properties. If you select Nodes and drag them a bit, does anything update to be the way that you expected?

Did you set a breakpoint on setInputLevel? Is it being called, and what does it do?

Sorry for confusing you but the test is not depending on the diagram anymore since we changed our methods to receive a model directly. Before we were sending the whole diagram to our calculation routine. Since we changed this we are able to run tests on our calculation methods. Our program does use the diagram to display the nodes. Each Node has a InputLevelList ( property of our nodeData ) and this list is displayed in our diagram. After we changed our calculation methods to work directly with the model this list gets updated but does not display in the Diagram anymore. The nodes and links are there but the list is empty. Before calculations were done on the collection Diagram.nodes, We would go through each node in this collection, cast it as nodedata and call setinputlevel. Calculations are now being done directly on the model using model.nodesource which contains the NodeData already ( no casting needed ).

Set inputLevel Updates a list which is a property of our NodeData

Public

Sub setInputLevel(FrequencyKey As Int32, SignalType As String, Input As Decimal) Implements ICableElement.setInputLevel

Dim insertLoss As Decimal

Dim bLevelUpdated As Boolean = False

Dim old As ObservableCollection(Of ElementInOutLevel) = _ElementInOutLevelList

insertLoss = GetInsertLoss(FrequencyKey, SignalType)

For Each level As ElementInOutLevel In ElementInOutLevelList

If level.FrequencyKey = FrequencyKey Then

level.InPut = Input

level.OutPut = Input - insertLoss

bLevelUpdated =

True

Exit For

End If

Next

If Not bLevelUpdated Then

ElementInOutLevelList.Add(

New ElementInOutLevel With {.FrequencyKey = FrequencyKey, .InPut = Input, .SignalType = SignalType, .OutPut = Input - insertLoss})

End If

 

_ElementInOutLevelList = ElementInOutLevelList

RaisePropertyChanged(

"ElementInOutLevelList", old, _ElementInOutLevelList)

End Sub

So the list you are talking about is ElementInOutLevelList, and it is an ObservableCollection.
How does your Node DataTemplate bind to that ElementInOutLevelList?

Here is the Template:

Resource

<

DataTemplate x:Key=“OutTemplate”>


<StackPanel Orientation=“Horizontal” Background=“Transparent” >


<TextBlock Text="{Binding Path=SignalDisplay}" Width=“100” FontSize=“10” Foreground=“Beige”/>

</StackPanel>

</DataTemplate>

Template:

<

DataTemplate x:Key=“NodeTemplate”>


<go:SpotPanel go:Node.Location="{Binding Path=Data.Location, Mode=TwoWay}" go:Part.LayoutId="{Binding Path=Data.LayoutId, Mode=TwoWay}"


go:Part.SelectionAdorned=“True” go:Part.SelectionAdornmentTemplate="{StaticResource SelectedElementTemplate}">



<go:NodePanel

Sizing="Auto"

go:Part.Copyable="False"

MinWidth="10">

<Grid>

<Grid.RowDefinitions>

<RowDefinition />

<RowDefinition />

<RowDefinition />

<RowDefinition />

</Grid.RowDefinitions>

<csla:BusyAnimation Height="10" x:Name="NodeBusySpinner" Width="10" Margin="2" IsRunning="{Binding Path=Data.IsBussy}" />

<Path Data="{Binding Path=Data.Figure, Mode=OneWay,Converter={StaticResource ShapeConverter}}"

Grid.Row="1"

Fill="{Binding Path=Data.Color}" Stroke="Black" StrokeThickness="1" Width="{Binding Path=Data.Width}" Height="{Binding Path=Data.Heigth}"

Stretch="Fill" />

<TextBlock Text="{Binding Path=Data.FillCode, Mode=TwoWay}" FontSize="14" Foreground="White" Grid.Row="1" HorizontalAlignment="Center" VerticalAlignment="Center"/>

<TextBlock Text="{Binding Path=Data.Text, Mode=TwoWay}" FontSize="10" Grid.Row="2" VerticalAlignment="Center" HorizontalAlignment="Center"/>

</Grid>

</go:NodePanel>

<ListBox ItemsSource="{Binding Path=Data.ElementInsertLossList}" Name="InsertLoss" ItemTemplate="{StaticResource LossTemplate}" go:SpotPanel.Spot="0.50 0 0 -15" Width="100" HorizontalAlignment="Left" BorderThickness="0" Background="Transparent" ScrollViewer.HorizontalScrollBarVisibility="Disabled" ScrollViewer.VerticalScrollBarVisibility="Disabled" Style="{StaticResource MyListBoxStyle}" >

<ListBox.ItemsPanel>

<ItemsPanelTemplate>

<StackPanel Orientation="Horizontal" />

</ItemsPanelTemplate>

</ListBox.ItemsPanel>

</ListBox>

<ListBox ItemsSource="{Binding Path=Data.ElementInOutLevelList}" Name="InOutLevel" ItemTemplate="{StaticResource OutTemplate}" go:SpotPanel.Spot="0.50 1 0 15" Width="100" HorizontalAlignment="Left" BorderThickness="0" Background="Transparent" ScrollViewer.HorizontalScrollBarVisibility="Disabled" ScrollViewer.VerticalScrollBarVisibility="Disabled" >

<ListBox.ItemsPanel>

<ItemsPanelTemplate>

<StackPanel Orientation="Horizontal" />

</ItemsPanelTemplate>

</ListBox.ItemsPanel>

</ListBox>

<Rectangle go:SpotPanel.Spot="MiddleLeft" Width="8" Height="8"

go:Node.PortId="MiddleLeft" go:Node.LinkableTo="True" go:Node.LinkableFrom="True" Fill="Black"

go:Node.LinkableMaximum="1"

go:Node.ToSpot="MiddleLeft" Cursor="Hand" />

<Rectangle go:SpotPanel.Spot="MiddleRight" Width="8" Height="8"

go:Node.PortId="MiddleRight" go:Node.LinkableTo="True" go:Node.LinkableFrom="True" Fill="Black"

go:Node.LinkableMaximum="1"

go:Node.ToSpot="MiddleRight" Cursor="Hand" />

</go:SpotPanel>

</DataTemplate>

I don’t see anything obviously wrong.

So that means that whatever we do in the Model should without any other method calls propagate to the diagram? Just to exclude that we do not have to look for the problem in this area.

That’s basically right – you need to support notification (PropertyChanged and CollectionChanged), the Bindings should be correctly set up, and (GoXam-specific) you need to commit a transaction.

You can test this by adding a Button to your page/window, or just a MouseLeftButtonUp event handler in your Node DataTemplate, which adds a new ElementInOutLevel to the Diagram.SelectedNode.Data.ElementInOutLevelList inside a transaction.

OK Thank you for your time.