1+ package sk.ainet.lang.graph.utils
2+
3+ import sk.ainet.lang.graph.*
4+ import kotlin.test.Test
5+ import kotlin.test.assertNotNull
6+ import kotlin.test.assertTrue
7+ import kotlin.test.assertEquals
8+
9+ /* *
10+ * Test for GraphViz export functionality
11+ */
12+ class GraphVizExportTest {
13+
14+ private class TestOperation (
15+ override val name : String ,
16+ override val type : String ,
17+ override val parameters : Map <String , Any > = emptyMap()
18+ ) : BaseOperation(name, type, parameters) {
19+
20+ override fun <T : sk.ainet.lang.types.DType , V > execute (inputs : List <sk.ainet.lang.tensor.Tensor <T , V >>): List <sk.ainet.lang.tensor.Tensor <T , V >> {
21+ return inputs // Pass through for testing
22+ }
23+
24+ override fun validateInputs (inputs : List <TensorSpec >): ValidationResult {
25+ return ValidationResult .Valid
26+ }
27+
28+ override fun inferOutputs (inputs : List <TensorSpec >): List <TensorSpec > {
29+ return inputs // Pass through for testing
30+ }
31+
32+ override fun clone (newParameters : Map <String , Any >): Operation {
33+ return TestOperation (name, type, newParameters)
34+ }
35+ }
36+
37+ @Test
38+ fun testBasicGraphVizExport () {
39+ println (" [DEBUG_LOG] Testing basic GraphViz export functionality" )
40+
41+ // Create a simple compute graph
42+ val graph = DefaultComputeGraph ()
43+
44+ // Create test operations
45+ val inputOp = TestOperation (" input" , " input" )
46+ val processOp = TestOperation (" process" , " compute" , mapOf (" kernel_size" to 3 , " stride" to 1 ))
47+ val outputOp = TestOperation (" output" , " output" )
48+
49+ // Create test nodes
50+ val inputNode = GraphNode (
51+ id = " input_node" ,
52+ operation = inputOp,
53+ inputs = emptyList(),
54+ outputs = listOf (TensorSpec (" input_out" , listOf (1 , 10 ), " FP32" ))
55+ )
56+
57+ val processNode = GraphNode (
58+ id = " process_node" ,
59+ operation = processOp,
60+ inputs = listOf (TensorSpec (" process_in" , listOf (1 , 10 ), " FP32" )),
61+ outputs = listOf (TensorSpec (" process_out" , listOf (1 , 5 ), " FP32" ))
62+ )
63+
64+ val outputNode = GraphNode (
65+ id = " output_node" ,
66+ operation = outputOp,
67+ inputs = listOf (TensorSpec (" output_in" , listOf (1 , 5 ), " FP32" )),
68+ outputs = listOf (TensorSpec (" output_out" , listOf (1 , 5 ), " FP32" ))
69+ )
70+
71+ // Add nodes to graph
72+ graph.addNode(inputNode)
73+ graph.addNode(processNode)
74+ graph.addNode(outputNode)
75+
76+ // Add edges to connect them
77+ graph.addEdge(GraphEdge (" edge1" , inputNode, processNode, 0 , 0 , inputNode.outputs.first()))
78+ graph.addEdge(GraphEdge (" edge2" , processNode, outputNode, 0 , 0 , processNode.outputs.first()))
79+
80+ println (" [DEBUG_LOG] Created graph with ${graph.nodes.size} nodes and ${graph.edges.size} edges" )
81+
82+ // Test full graph export
83+ val dotGraph = drawDot(graph)
84+ assertNotNull(dotGraph, " DOT graph should not be null" )
85+ assertNotNull(dotGraph.content, " DOT content should not be null" )
86+ assertTrue(dotGraph.content.isNotEmpty(), " DOT content should not be empty" )
87+
88+ println (" [DEBUG_LOG] Full graph DOT export:" )
89+ println (dotGraph.content)
90+
91+ // Verify DOT content structure
92+ assertTrue(dotGraph.content.contains(" digraph {" ), " Should contain digraph declaration" )
93+ assertTrue(dotGraph.content.contains(" rankdir=LR" ), " Should contain rankdir setting" )
94+ assertTrue(dotGraph.content.contains(" input_node" ), " Should contain input node" )
95+ assertTrue(dotGraph.content.contains(" process_node" ), " Should contain process node" )
96+ assertTrue(dotGraph.content.contains(" output_node" ), " Should contain output node" )
97+ assertTrue(dotGraph.content.contains(" ->" ), " Should contain edges" )
98+ assertTrue(dotGraph.content.contains(" }" ), " Should contain closing brace" )
99+
100+ // Test parameters are included
101+ assertTrue(dotGraph.content.contains(" kernel_size" ), " Should include operation parameters" )
102+
103+ println (" [DEBUG_LOG] Basic GraphViz export test passed" )
104+ }
105+
106+ @Test
107+ fun testSubsetGraphVizExport () {
108+ println (" [DEBUG_LOG] Testing subset GraphViz export functionality" )
109+
110+ // Create a compute graph
111+ val graph = DefaultComputeGraph ()
112+
113+ // Create test nodes
114+ val node1 = GraphNode (" node1" , TestOperation (" op1" , " type1" ), emptyList(), listOf (TensorSpec (" out1" , listOf (1 ), " FP32" )))
115+ val node2 = GraphNode (" node2" , TestOperation (" op2" , " type2" ), listOf (TensorSpec (" in2" , listOf (1 ), " FP32" )), listOf (TensorSpec (" out2" , listOf (1 ), " FP32" )))
116+ val node3 = GraphNode (" node3" , TestOperation (" op3" , " type3" ), listOf (TensorSpec (" in3" , listOf (1 ), " FP32" )), listOf (TensorSpec (" out3" , listOf (1 ), " FP32" )))
117+
118+ graph.addNode(node1)
119+ graph.addNode(node2)
120+ graph.addNode(node3)
121+
122+ graph.addEdge(GraphEdge (" edge1" , node1, node2, 0 , 0 , node1.outputs.first()))
123+ graph.addEdge(GraphEdge (" edge2" , node2, node3, 0 , 0 , node2.outputs.first()))
124+
125+ // Test subset export (only from node3 backward)
126+ val dotGraphSubset = drawDot(graph, listOf (node3))
127+ assertNotNull(dotGraphSubset, " Subset DOT graph should not be null" )
128+ assertTrue(dotGraphSubset.content.isNotEmpty(), " Subset DOT content should not be empty" )
129+
130+ println (" [DEBUG_LOG] Subset DOT export:" )
131+ println (dotGraphSubset.content)
132+
133+ // All nodes should be included since node3 depends on all previous nodes
134+ assertTrue(dotGraphSubset.content.contains(" node1" ), " Should contain node1 in subset" )
135+ assertTrue(dotGraphSubset.content.contains(" node2" ), " Should contain node2 in subset" )
136+ assertTrue(dotGraphSubset.content.contains(" node3" ), " Should contain node3 in subset" )
137+
138+ println (" [DEBUG_LOG] Subset GraphViz export test passed" )
139+ }
140+
141+ @Test
142+ fun testDifferentRankDirections () {
143+ println (" [DEBUG_LOG] Testing different rank directions" )
144+
145+ val graph = DefaultComputeGraph ()
146+ val node = GraphNode (" test" , TestOperation (" test" , " test" ), emptyList(), listOf (TensorSpec (" out" , listOf (1 ), " FP32" )))
147+ graph.addNode(node)
148+
149+ // Test LR direction (default)
150+ val dotLR = drawDot(graph, " LR" )
151+ assertTrue(dotLR.content.contains(" rankdir=LR" ), " Should set LR rank direction" )
152+
153+ // Test TB direction
154+ val dotTB = drawDot(graph, " TB" )
155+ assertTrue(dotTB.content.contains(" rankdir=TB" ), " Should set TB rank direction" )
156+
157+ println (" [DEBUG_LOG] Rank direction test passed" )
158+ }
159+ }
0 commit comments