Jlm
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 
13 static bool
14 StringContains(const std::string & haystack, const std::string & needle)
15 {
16  return haystack.find(needle) != std::string::npos;
17 }
18 
19 TEST(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 
89 TEST(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 
128 TEST(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 
160 TEST(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 
225 TEST(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 
287 TEST(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 
325 TEST(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 
370 TEST(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)
const char *const Red
static std::string strfmt(Args... args)
Definition: strfmt.hpp:35
static const char *const Tapered
static const char *const Rectangle