Jlm
Loading...
Searching...
No Matches
GraphWriterTests.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2024 HÃ¥vard Krogstie <krogstie.havard@gmail.com>
3 * See COPYING for terms of redistribution.
4 */
5
6#include <gtest/gtest.h>
7
9#include <jlm/util/strfmt.hpp>
10
11#include <sstream>
12
13static bool
14StringContains(const std::string & haystack, const std::string & needle)
15{
16 return haystack.find(needle) != std::string::npos;
17}
18
19TEST(GraphWriterTests, TestGraphElement)
20{
21 using namespace jlm::util;
22 using namespace jlm::util::graph;
23 Writer writer;
24 auto & graph = writer.CreateGraph();
25
26 // Test labels
27 graph.SetLabel("Test");
28 EXPECT_EQ(graph.GetLabel(), "Test");
29 EXPECT_TRUE(graph.HasLabel());
30 graph.SetLabel("");
31 EXPECT_FALSE(graph.HasLabel());
32 EXPECT_EQ(graph.GetLabelOr("default"), std::string("default"));
33
34 // Test label appending
35 graph.AppendToLabel("Text");
36 EXPECT_EQ(graph.GetLabel(), "Text");
37 graph.AppendToLabel("Text2", "\n");
38 EXPECT_EQ(graph.GetLabel(), "Text\nText2");
39
40 // Test assigning a program object to a graph element
41 int myInt = 0;
42 graph.SetProgramObject(myInt);
43 EXPECT_EQ(graph.GetProgramObject(), reinterpret_cast<uintptr_t>(&myInt));
44
45 // Set attributes
46 graph.SetAttribute("color", "\"dark\nbrown\"");
47 graph.SetAttribute("taste", "sweet");
48 graph.SetAttributeGraphElement("graph", graph);
49 graph.SetAttributeObject("another graph", myInt);
50
51 // Check getting attributes
52 EXPECT_TRUE(graph.HasAttribute("taste"));
53 EXPECT_EQ(graph.GetAttributeString("taste"), "sweet");
54 EXPECT_FALSE(graph.GetAttributeString("not-an-attribute"));
55 EXPECT_EQ(graph.GetAttributeGraphElement("graph"), &graph);
56 EXPECT_EQ(graph.GetAttributeObject("another graph"), reinterpret_cast<uintptr_t>(&myInt));
57 // Also check that one can get GraphElements based on the program object they represent
58 EXPECT_EQ(graph.GetAttributeGraphElement("another graph"), &graph);
59
60 // Test removing attributes
61 EXPECT_TRUE(graph.RemoveAttribute("taste"));
62 EXPECT_FALSE(graph.HasAttribute("taste"));
63 // Removing the attribute again returns false
64 EXPECT_FALSE(graph.RemoveAttribute("taste"));
65
66 // Finalizing and getting a unique id
67 EXPECT_FALSE(graph.IsFinalized());
68 graph.Finalize();
69 EXPECT_TRUE(graph.IsFinalized());
70 EXPECT_EQ(graph.GetUniqueIdSuffix(), 0u);
71 EXPECT_EQ(graph.GetFullId(), "graph0");
72
73 // Attribute printing
74 std::ostringstream out;
75 graph.OutputAttributes(out, AttributeOutputFormat::SpaceSeparatedList);
76 auto attributes = out.str();
77 EXPECT_TRUE(StringContains(attributes, "color=\"\\\"dark\\nbrown\\\"\""));
78 EXPECT_TRUE(StringContains(attributes, "graph=graph0"));
79 EXPECT_TRUE(StringContains(attributes, "\"another graph\"=graph0"));
80
81 // Also test HTML attribute escaping
82 out = std::ostringstream();
83 graph.OutputAttributes(out, AttributeOutputFormat::HTMLAttributes);
84 attributes = out.str();
85 EXPECT_TRUE(StringContains(attributes, "color=\"&quot;dark\nbrown&quot;\""));
86 EXPECT_TRUE(StringContains(attributes, "another-graph=\"graph0\""));
87}
88
89TEST(GraphWriterTests, TestNode)
90{
91 using namespace jlm::util;
92 using namespace jlm::util::graph;
93 Writer writer;
94 auto & graph = writer.CreateGraph();
95
96 auto & node = graph.CreateNode();
97 EXPECT_EQ(&node.GetNode(), &node);
98 EXPECT_EQ(&node.GetGraph(), &graph);
99
100 node.SetLabel("MyNode");
101
102 node.SetShape(Node::Shape::Rectangle);
103 EXPECT_TRUE(node.HasAttribute("shape"));
104
105 node.Finalize();
106
107 // Assert ASCII output
108 std::ostringstream out;
109 node.OutputASCII(out, 0);
110 auto string = out.str();
111 EXPECT_TRUE(StringContains(string, "MyNode"));
112
113 // Assert dot output
114 std::ostringstream out2;
115 node.OutputDot(out2, 0);
116 auto string2 = out2.str();
117 EXPECT_TRUE(StringContains(string2, "label=MyNode"));
118 EXPECT_TRUE(StringContains(string2, "shape=rect"));
119
120 // Assert json output
121 std::ostringstream jsonOut;
122 node.outputJson(jsonOut, 0);
123 auto jsonString = jsonOut.str();
124 EXPECT_TRUE(StringContains(jsonString, "\"label\": \"MyNode\""));
125 EXPECT_TRUE(StringContains(jsonString, "\"attr\": {\"shape\":\"rect\"}"));
126}
127
128TEST(GraphWriterTests, TestASCIIEdges)
129{
130 using namespace jlm::util;
131 using namespace jlm::util::graph;
132 Writer writer;
133 auto & graph = writer.CreateGraph();
134
135 auto & node0 = graph.CreateNode();
136 auto & node1 = graph.CreateNode();
137 auto & node2 = graph.CreateNode();
138
139 node0.SetLabel("NODE0");
140 node1.SetLabel("NODE1");
141 node2.SetLabel("NODE2");
142
143 graph.CreateDirectedEdge(node0, node1);
144 graph.CreateDirectedEdge(node0, node2);
145 graph.CreateDirectedEdge(node1, node2);
146
147 graph.Finalize();
148
149 std::ostringstream out;
150 node0.OutputASCII(out, 0);
151 node1.OutputASCII(out, 0);
152 node2.OutputASCII(out, 0);
153
154 auto string = out.str();
155 EXPECT_TRUE(StringContains(string, "node0:NODE0"));
156 EXPECT_TRUE(StringContains(string, "node1:NODE1<-node0"));
157 EXPECT_TRUE(StringContains(string, "NODE2<-[node0, node1]"));
158}
159
160TEST(GraphWriterTests, TestInOutNode)
161{
162 using namespace jlm::util;
163 using namespace jlm::util::graph;
164 Writer writer;
165 auto & graph = writer.CreateGraph();
166
167 auto & node = graph.CreateInOutNode(2, 3);
168 EXPECT_EQ(node.NumInputPorts(), 2u);
169 EXPECT_EQ(node.NumOutputPorts(), 3u);
170
171 node.SetLabel("My\nInOutNode");
172
173 graph.CreateDirectedEdge(node.GetOutputPort(2), node.GetInputPort(0));
174
175 // Also test subgraphs, and connecting argument nodes and result nodes to outside ports
176 auto & subgraph = node.CreateSubgraph();
177 EXPECT_EQ(node.NumSubgraphs(), 1u);
178 EXPECT_EQ(&node.GetSubgraph(0), &subgraph);
179 auto & argumentNode = subgraph.CreateArgumentNode();
180 argumentNode.SetLabel("CTX");
181 argumentNode.SetOutsideSource(node.GetInputPort(0));
182 auto & resultNode = subgraph.CreateResultNode();
183 resultNode.SetLabel("RETURN");
184 resultNode.SetOutsideDestination(node.GetOutputPort(0));
185
186 subgraph.CreateDirectedEdge(argumentNode, resultNode);
187 graph.Finalize();
188
189 // Assert
190 EXPECT_TRUE(node.IsFinalized());
191 EXPECT_TRUE(subgraph.IsFinalized());
192
193 std::ostringstream out;
194 node.OutputASCII(out, 0);
195 auto string = out.str();
196 EXPECT_TRUE(StringContains(string, "out0, out1, out2 := \"My\\nInOutNode\" out2, []"));
197
198 // Check that the subgraph is also printed
199 EXPECT_TRUE(StringContains(string, "ARG arg0:CTX <= out2"));
200 EXPECT_TRUE(StringContains(string, "RES arg0:RETURN => out0"));
201
202 // Check that HTML labels with newlines turn into <BR/>
203 std::ostringstream out2;
204 node.OutputDot(out2, 0);
205 auto string0 = out2.str();
206 EXPECT_TRUE(StringContains(string0, "My<BR/>InOutNode"));
207
208 // Assert json output
209 std::ostringstream jsonOut;
210 node.outputJson(jsonOut, 0);
211 auto jsonString = jsonOut.str();
212 EXPECT_TRUE(StringContains(jsonString, "\"label\": \"My\\nInOutNode\""));
213 EXPECT_TRUE(StringContains(jsonString, "\"type\": \"inout"));
214 // All input ports and output ports have corresponding json objects
215 EXPECT_TRUE(StringContains(jsonString, strfmt('"', node.GetInputPort(0).GetFullId(), "\": {")));
216 EXPECT_TRUE(StringContains(jsonString, strfmt('"', node.GetInputPort(1).GetFullId(), "\": {")));
217 EXPECT_TRUE(StringContains(jsonString, strfmt('"', node.GetOutputPort(0).GetFullId(), "\": {")));
218 EXPECT_TRUE(StringContains(jsonString, strfmt('"', node.GetOutputPort(1).GetFullId(), "\": {")));
219 EXPECT_TRUE(StringContains(jsonString, strfmt('"', node.GetOutputPort(2).GetFullId(), "\": {")));
220 // Assert that the InOutNode contains a list of subgraphs, holding the id of the subgraph
221 EXPECT_TRUE(
222 StringContains(jsonString, strfmt("\"subgraphs\": [\"", subgraph.GetFullId(), "\"]")));
223}
224
225TEST(GraphWriterTests, TestEdge)
226{
227 using namespace jlm::util;
228 using namespace jlm::util::graph;
229 Writer writer;
230 auto & graph = writer.CreateGraph();
231
232 auto & node0 = graph.CreateNode();
233 auto & node1 = graph.CreateNode();
234 auto & node2 = graph.CreateNode();
235
236 auto & edge0 = graph.CreateDirectedEdge(node0, node1);
237 auto & edge1 = graph.CreateUndirectedEdge(node1, node2);
238
239 EXPECT_EQ(&edge0.GetFrom(), &node0);
240 EXPECT_EQ(&edge0.GetTo(), &node1);
241 EXPECT_TRUE(edge0.IsDirected());
242 EXPECT_EQ(&edge1.GetFrom(), &node1);
243 EXPECT_EQ(&edge1.GetTo(), &node2);
244 EXPECT_TRUE(!edge1.IsDirected());
245
246 EXPECT_EQ(&edge0.GetOtherEnd(node0), &node1);
247 EXPECT_EQ(&edge0.GetOtherEnd(node1), &node0);
248
249 EXPECT_EQ(graph.NumEdges(), 2u);
250 EXPECT_EQ(&graph.GetEdge(0), &edge0);
251
252 EXPECT_EQ(graph.GetEdgeBetween(node0, node1), &edge0);
253 EXPECT_EQ(graph.GetEdgeBetween(node1, node2), &edge1);
254 EXPECT_EQ(graph.GetEdgeBetween(node2, node0), nullptr);
255
256 edge0.SetAttribute("color", Colors::Red);
257
258 auto & edge2 = graph.CreateUndirectedEdge(node2, node0);
259 edge2.SetArrowHead("odot");
260 edge2.SetArrowTail("normal");
261 edge2.SetStyle(Edge::Style::Tapered);
262
263 graph.Finalize();
264
265 std::ostringstream out0;
266 edge0.OutputDot(out0, 0);
267 auto string0 = out0.str();
268
269 EXPECT_TRUE(StringContains(string0, "node0 -> node1"));
270 EXPECT_TRUE(StringContains(string0, jlm::util::strfmt("color=\"", Colors::Red, "\"")));
271
272 std::ostringstream out1;
273 edge1.OutputDot(out1, 0);
274 auto string1 = out1.str();
275 EXPECT_TRUE(StringContains(string1, "node1 -> node2"));
276 EXPECT_TRUE(StringContains(string1, jlm::util::strfmt("dir=none")));
277
278 std::ostringstream out2;
279 edge2.OutputDot(out2, 0);
280 auto string2 = out2.str();
281 EXPECT_TRUE(StringContains(string2, "dir=both"));
282 EXPECT_TRUE(StringContains(string2, "arrowhead=odot"));
283 EXPECT_TRUE(StringContains(string2, "arrowtail=normal"));
284 EXPECT_TRUE(StringContains(string2, "style=tapered"));
285}
286
287TEST(GraphWriterTests, TestGraphCreateNodes)
288{
289 using namespace jlm::util;
290 using namespace jlm::util::graph;
291 Writer writer;
292 auto & graph = writer.CreateGraph();
293
294 // Test node creation and count
295 EXPECT_EQ(graph.NumNodes(), 0u);
296 auto & node = graph.CreateNode();
297 EXPECT_EQ(graph.NumNodes(), 1u);
298 EXPECT_EQ(&graph.GetNode(0), &node);
299
300 // Test InOutNode creation and count
301 auto & inOutNode = graph.CreateInOutNode(1, 1);
302 EXPECT_EQ(graph.NumNodes(), 2u);
303 EXPECT_EQ(&graph.GetNode(1), &inOutNode);
304
305 // Test argument node creation and count
306 EXPECT_EQ(graph.NumArgumentNodes(), 0u);
307 auto & argumentNode = graph.CreateArgumentNode();
308 EXPECT_EQ(graph.NumArgumentNodes(), 1u);
309 EXPECT_EQ(&graph.GetArgumentNode(0), &argumentNode);
310
311 // Test result node creation and count
312 EXPECT_EQ(graph.NumResultNodes(), 0u);
313 auto & resultNode = graph.CreateResultNode();
314 EXPECT_EQ(graph.NumResultNodes(), 1u);
315 EXPECT_EQ(&graph.GetResultNode(0), &resultNode);
316
317 // Test finalizing reaching every node
318 graph.Finalize();
319 EXPECT_TRUE(node.IsFinalized());
320 EXPECT_TRUE(argumentNode.IsFinalized());
321 EXPECT_TRUE(resultNode.IsFinalized());
322 EXPECT_TRUE(inOutNode.IsFinalized());
323}
324
325TEST(GraphWriterTests, TestGraphAttributes)
326{
327 using namespace jlm::util;
328 using namespace jlm::util::graph;
329 Writer writer;
330 auto & graph = writer.CreateGraph();
331 graph.SetLabel("My Graph");
332
333 EXPECT_EQ(&graph.GetWriter(), &writer);
334 auto & node = graph.CreateNode();
335
336 // Test associating a GraphElement with a pointer, and retrieving it
337 int myInt = 6;
338 node.SetProgramObject(myInt);
339 EXPECT_EQ(&graph.GetFromProgramObject<Node>(myInt), &node);
340
341 // Set some attributes, to test that they appear in the final output
342 graph.SetAttributeObject("friend", myInt);
343 graph.SetAttributeGraphElement("foe", graph);
344
345 graph.Finalize();
346
347 // Assert that the Dot output of the graph contains everything specified
348 std::ostringstream out;
349 graph.Output(out, OutputFormat::Dot, 0);
350 auto string = out.str();
351 EXPECT_TRUE(StringContains(string, "label=\"My Graph\""));
352 // Nodes referred to in attributes
353 EXPECT_TRUE(StringContains(string, "friend=node0"));
354 EXPECT_TRUE(StringContains(string, "foe=graph0"));
355
356 // Assert that the json output contains the attributes
357 std::ostringstream jsonOut;
358 graph.Output(jsonOut, OutputFormat::Json, 0);
359 auto jsonString = jsonOut.str();
360 // Check that the graph has its label
361 EXPECT_TRUE(StringContains(jsonString, "\"label\": \"My Graph\""));
362 // Check that the graph has the address of its program object
363 EXPECT_TRUE(
364 StringContains(jsonString, strfmt("\"obj\": \"", reinterpret_cast<void *>(&myInt), "\"")));
365 // Check that attributes are printed, and refer to graph elements by id
366 EXPECT_TRUE(StringContains(jsonString, "\"friend\":\"node0\""));
367 EXPECT_TRUE(StringContains(jsonString, "\"foe\":\"graph0\""));
368}
369
370TEST(GraphWriterTests, TestGraphWriterClass)
371{
372 using namespace jlm::util;
373 using namespace jlm::util::graph;
374 Writer writer;
375
376 auto & graph0 = writer.CreateGraph();
377 auto & graph1 = writer.CreateGraph();
378 EXPECT_EQ(writer.NumGraphs(), 2u);
379 EXPECT_EQ(&writer.GetGraph(0), &graph0);
380
381 auto & node0 = graph0.CreateNode();
382 auto & node1 = graph1.CreateNode();
383
384 // Test retrieving a GraphElement from its associated program object pointer
385 int myInt = 12;
386 node1.SetProgramObject(myInt);
387 EXPECT_EQ(writer.GetElementFromProgramObject(reinterpret_cast<uintptr_t>(&myInt)), &node1);
388
389 // Refer to program objects mapped to elements in other graphs
390 node0.SetAttributeObject("friend", myInt);
391
392 // Render all the graphs to dot, which first finalizes the graphs to assign unique IDs
393 std::ostringstream out;
394 writer.outputAllGraphs(out, OutputFormat::Dot);
395 auto string = out.str();
396
397 EXPECT_TRUE(graph0.IsFinalized());
398 EXPECT_TRUE(graph1.IsFinalized());
399
400 EXPECT_EQ(node0.GetFullId(), "node0");
401 EXPECT_EQ(node1.GetFullId(), "node1");
402
403 EXPECT_TRUE(StringContains(string, "friend=node1"));
404}
TEST(GraphWriterTests, TestGraphElement)
static bool StringContains(const std::string &haystack, const std::string &needle)
void SetLabel(std::string label)
InOutNode & CreateInOutNode(size_t inputPorts, size_t outputPorts)
GraphElement * GetElementFromProgramObject(uintptr_t object) const
size_t NumGraphs() const noexcept
void outputAllGraphs(std::ostream &out, OutputFormat format)
Graph & GetGraph(size_t index)
static std::string strfmt(Args... args)
Definition strfmt.hpp:35