Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

- Fixes memory usage (Req #8380)

 - Added more MIME types
 - Fixes a filesystem cache issue
 - Added .phpt unit tests


git-svn-id: http://svn.php.net/repository/pear/packages/Image_GraphViz/trunk@247212 c90b9560-bf6c-de11-be94-00142212c4b1
  • Loading branch information...
commit 633dedb5190c9bbeb43f179388d396994e44b4b4 1 parent ec5545c
Philippe Jausions authored
View
156 GraphViz.php
@@ -20,7 +20,7 @@
* @author Karsten Dambekalns <k.dambekalns@fishfarm.de>
* @author Michael Lively Jr. <mlively@ft11.net>
* @author Philippe Jausions <Philippe.Jausions@11abacus.com>
- * @copyright 2001-2007 Sebastian Bergmann <sb@sebastian-bergmann.de>
+ * @copyright 2001-2007 Dr. Volker Göbbels <vmg@arachnion.de> and Sebastian Bergmann <sb@sebastian-bergmann.de>
* @license http://www.php.net/license/3_0.txt PHP License 3.0
* @version CVS: $Id$
* @link http://pear.php.net/package/Image_GraphViz
@@ -146,8 +146,8 @@ class Image_GraphViz
/**
* Constructor.
*
- * Setting the name of the Graph is useful for including multiple image maps on
- * one page. If not set, the graph will be named 'G'.
+ * Setting the name of the Graph is useful for including multiple image maps
+ * on one page. If not set, the graph will be named 'G'.
*
* @param boolean $directed Directed (TRUE) or undirected (FALSE) graph.
* Note: You MUST pass a boolean, and not just an expression that evaluates
@@ -169,53 +169,99 @@ function Image_GraphViz($directed = true, $attributes = array(),
}
/**
- * Output image of the graph in a given format.
+ * Outputs image of the graph in a given format
+ *
+ * This methods send HTTP headers
*
* @param string Format of the output image.
* This may be one of the formats supported by GraphViz.
* @param string $command "dot" or "neato"
*
+ * @return boolean TRUE on success, FALSE otherwise
* @access public
*/
function image($format = 'svg', $command = null)
{
- if ($data = $this->fetch($format, $command)) {
- $sendContentLengthHeader = true;
-
- switch ($format) {
- case 'gif':
- case 'png':
- case 'wbmp':
- header('Content-Type: image/' . $format);
- break;
+ $file = $this->saveParsedGraph();
+ if (!$file) {
+ return false;
+ }
- case 'jpg':
- case 'jpeg':
- header('Content-Type: image/jpeg');
- break;
+ $outputfile = $file . '.' . $format;
- case 'pdf':
- header('Content-Type: application/pdf');
- break;
+ $rendered = $this->renderDotFile($file, $outputfile, $format, $command);
+ if ($rendered !== true) {
+ return $rendered;
+ }
- case 'svg':
- header('Content-Type: image/svg+xml');
- break;
+ $sendContentLengthHeader = true;
- default:
- $sendContentLengthHeader = false;
- }
+ switch (strtolower($format)) {
+ case 'gif':
+ case 'png':
+ case 'bmp':
+ case 'jpeg':
+ case 'tiff':
+ header('Content-Type: image/' . $format);
+ break;
- if ($sendContentLengthHeader) {
- header('Content-Length: ' . strlen($data));
- }
+ case 'tif':
+ header('Content-Type: image/tiff');
+ break;
+
+ case 'jpg':
+ header('Content-Type: image/jpeg');
+ break;
+
+ case 'ico':
+ header('Content-Type: image/x-icon');
+ break;
+
+ case 'wbmp':
+ header('Content-Type: image/vnd.wap.wbmp');
+ break;
+
+ case 'pdf':
+ header('Content-Type: application/pdf');
+ break;
+
+ case 'mif':
+ header('Content-Type: application/vnd.mif');
+ break;
+
+ case 'vrml':
+ header('Content-Type: application/x-vrml');
+ break;
+
+ case 'svg':
+ header('Content-Type: image/svg+xml');
+ break;
+
+ case 'plain':
+ case 'plain-ext':
+ header('Content-Type: text/plain');
+ break;
- echo $data;
+ default:
+ header('Content-Type: application/octet-stream');
+ $sendContentLengthHeader = false;
+ }
+
+ if ($sendContentLengthHeader) {
+ header('Content-Length: ' . filesize($outputfile));
+ }
+
+ $return = true;
+ if (readfile($outputfile) === false) {
+ $return = false;
}
+ @unlink($outputfile);
+
+ return $return;
}
/**
- * Return image (data) of the graph in a given format.
+ * Returns image (data) of the graph in a given format.
*
* @param string Format of the output image.
* This may be one of the formats supported by GraphViz.
@@ -234,20 +280,11 @@ function fetch($format = 'svg', $command = null)
$outputfile = $file . '.' . $format;
- switch ($command) {
- case 'dot':
- case 'neato':
- break;
- default:
- $command = $this->graph['directed'] ? 'dot' : 'neato';
+ $rendered = $this->renderDotFile($file, $outputfile, $format, $command);
+ if ($rendered !== true) {
+ return $rendered;
}
- $command = $this->binPath . (($command == 'dot')
- ? $this->dotCommand : $this->neatoCommand);
- $command .= ' -T'.escapeshellarg($format).' -o'
- .escapeshellarg($outputfile).' '.escapeshellarg($file);
-
- @`$command`;
@unlink($file);
$fp = fopen($outputfile, 'rb');
@@ -264,7 +301,7 @@ function fetch($format = 'svg', $command = null)
}
/**
- * Render a given dot file into another format.
+ * Renders a given dot file into a given format.
*
* @param string The absolute path of the dot file to use.
* @param string The absolute path of the file to save to.
@@ -272,7 +309,7 @@ function fetch($format = 'svg', $command = null)
* This may be one of the formats supported by GraphViz.
* @param string $command "dot" or "neato"
*
- * @return bool True if the file was saved, false otherwise.
+ * @return boolean TRUE if the file was saved, FALSE otherwise.
* @access public
*/
function renderDotFile($dotfile, $outputfile, $format = 'svg',
@@ -297,6 +334,7 @@ function renderDotFile($dotfile, $outputfile, $format = 'svg',
.' '.escapeshellarg($dotfile);
@`$command`;
+ clearstatcache();
if (file_exists($outputfile) && filemtime($outputfile) > $oldmtime) {
return true;
}
@@ -306,7 +344,7 @@ function renderDotFile($dotfile, $outputfile, $format = 'svg',
}
/**
- * Add a cluster to the graph.
+ * Adds a cluster to the graph.
*
* @param string ID.
* @param array Title.
@@ -322,7 +360,7 @@ function addCluster($id, $title, $attributes = array())
}
/**
- * Add a note to the graph.
+ * Adds a note to the graph.
*
* @param string Name of the node.
* @param array Attributes of the node.
@@ -337,7 +375,7 @@ function addNode($name, $attributes = array(), $group = 'default')
}
/**
- * Remove a node from the graph.
+ * Removes a node from the graph.
*
* This method doesn't remove edges associated with the node.
*
@@ -354,7 +392,7 @@ function removeNode($name, $group = 'default')
}
/**
- * Add an edge to the graph.
+ * Adds an edge to the graph.
*
* Examples:
* <code>
@@ -418,7 +456,7 @@ function addEdge($edge, $attributes = array(), $ports = array())
}
/**
- * Remove an edge from the graph.
+ * Removes an edge from the graph.
*
* @param array Start and End node of the edge to be removed.
* @param integer $id specific edge ID (only usefull when multiple edges
@@ -450,7 +488,7 @@ function removeEdge($edge, $id = null)
}
/**
- * Add attributes to the graph.
+ * Adds attributes to the graph.
*
* @param array Attributes to be added to the graph.
*
@@ -468,7 +506,7 @@ function addAttributes($attributes)
}
/**
- * Set attributes of the graph.
+ * Sets attributes of the graph.
*
* @param array Attributes to be set for the graph.
*
@@ -554,7 +592,7 @@ function _escape($input, $html = false)
}
/**
- * Set directed/undirected flag for the graph.
+ * Sets directed/undirected flag for the graph.
*
* Note: You MUST pass a boolean, and not just an expression that evaluates
* to TRUE or FALSE (i.e. NULL, empty string, 0 will not work)
@@ -572,7 +610,7 @@ function setDirected($directed)
}
/**
- * Load graph from file.
+ * Loads a graph from a file in Image_GraphViz format
*
* @param string File to load graph from.
*
@@ -616,7 +654,7 @@ function load($file)
}
/**
- * Save graph to file.
+ * Save graph to file in Image_GraphViz format
*
* @param string File to save the graph to.
* @return mixed File the graph was saved to, FALSE on failure.
@@ -641,7 +679,7 @@ function save($file = '')
}
/**
- * Parse the graph into GraphViz markup.
+ * Parses the graph into GraphViz markup.
*
* @return string GraphViz markup
* @access public
@@ -752,7 +790,7 @@ function parse()
}
/**
- * Save GraphViz markup to file.
+ * Saves GraphViz markup to file (in DOT language)
*
* @param string File to write the GraphViz markup to.
* @return mixed File to which the GraphViz markup was
@@ -768,7 +806,7 @@ function saveParsedGraph($file = '')
$file = System::mktemp('graph_');
}
- if ($fp = @fopen($file, 'w')) {
+ if ($fp = @fopen($file, 'wb')) {
@fputs($fp, $parsedGraph);
@fclose($fp);
@@ -787,4 +825,4 @@ function saveParsedGraph($file = '')
* c-hanging-comment-ender-p: nil
* End:
*/
-?>
+?>
View
108 package.xml
@@ -9,51 +9,60 @@ http://pear.php.net/dtd/package-2.0.xsd">
<description>The GraphViz class allows for the creation of and the work with directed and undirected graphs and their visualization with AT&amp;T&apos;s GraphViz tools.
</description>
<lead>
+ <name>Philippe Jausions</name>
+ <user>jausions</user>
+ <email>jausions@php.net</email>
+ <active>yes</active>
+ </lead>
+ <lead>
<name>Dr. Volker Goebbels</name>
<user>vmg</user>
<email>vmg@arachnion.de</email>
- <active>yes</active>
+ <active>no</active>
</lead>
<lead>
<name>Sebastian Bergmann</name>
<user>sebastian</user>
<email>sb@sebastian-bergmann.de</email>
- <active>yes</active>
+ <active>no</active>
</lead>
- <date>2007-11-19</date>
- <time>08:08:19</time>
+ <date>2007-11-27</date>
+ <time>16:45:30</time>
<version>
- <release>1.3.0RC1</release>
- <api>1.3.0RC1</api>
+ <release>1.3.0RC2</release>
+ <api>1.3.0RC2</api>
</version>
<stability>
<release>beta</release>
<api>beta</api>
</stability>
<license uri="http://www.php.net/license">PHP License</license>
- <notes>- PEAR Coding Standard fixes:
- + TRUE vs true, FALSE vs false,
- + @return docblock
- + Missing docblocks
- + switch/case blocks
- + Better E_ALL compliance
-- Support for HTML-like labels
-- Support for node port (Bug #4924)
-- Proper escaping of IDs/values
-- Support for multiline values
-- Support for multiple edges between same nodes (Req #6630)
-- Choice of GraphViz command to use for rendering (Bug #10753)
-- Support for &quot;strict&quot; graphs
-- Fix for undirected vs. directed graph (Bug #10753)
-- Base binary path (Req #8295)
-- Better error handling (more checking)
-- Nodes in subgraphs
-- Indentation in DOT file</notes>
+ <notes>
+ - Fixes memory usage (Req #8380)
+ - Added MIME types
+ - Fixes a filesystem cache issue
+ - Added .phpt unit tests
+ </notes>
<contents>
<dir name="/">
<file baseinstalldir="Image" name="GraphViz.php" role="php">
<tasks:replace from="@package_version@" to="version" type="package-info" />
</file>
+ <dir name="tests">
+ <file name="test1.phpt" role="test" />
+ <file name="test2.phpt" role="test" />
+ <file name="test3.phpt" role="test" />
+ <file name="test4.phpt" role="test" />
+ <file name="test5.phpt" role="test" />
+ <file name="test6.phpt" role="test" />
+ <file name="test9.phpt" role="test" />
+ <file name="test12.phpt" role="test" />
+ <file name="test14.phpt" role="test" />
+ <file name="test16.phpt" role="test" />
+ <file name="test17.phpt" role="test" />
+ <file name="test19.phpt" role="test" />
+ <file name="test20.phpt" role="test" />
+ </dir> <!-- /tests -->
</dir> <!-- / -->
</contents>
<dependencies>
@@ -69,6 +78,37 @@ http://pear.php.net/dtd/package-2.0.xsd">
<phprelease />
<changelog>
<release>
+ <date>2007-11-19</date>
+ <version>
+ <release>1.3.0RC1</release>
+ <api>1.3.0RC1</api>
+ </version>
+ <stability>
+ <release>beta</release>
+ <api>beta</api>
+ </stability>
+ <license uri="http://www.php.net/license">PHP License</license>
+ <notes>- PEAR Coding Standard fixes:
+ + TRUE vs true, FALSE vs false,
+ + @return docblock
+ + Missing docblocks
+ + switch/case blocks
+ + Better E_ALL compliance
+- Support for HTML-like labels
+- Support for node port (Bug #4924)
+- Proper escaping of IDs/values
+- Support for multiline values
+- Support for multiple edges between same nodes (Req #6630)
+- Choice of GraphViz command to use for rendering (Bug #10753)
+- Support for &quot;strict&quot; graphs
+- Fix for undirected vs. directed graph (Bug #10753)
+- Base binary path (Req #8295)
+- Better error handling (more checking)
+- Nodes in subgraphs
+- Indentation in DOT file
+ </notes>
+ </release>
+ <release>
<version>
<release>1.2.1</release>
<api>1.2.1</api>
@@ -94,7 +134,7 @@ http://pear.php.net/dtd/package-2.0.xsd">
<license uri="http://www.php.net/license">PHP License</license>
<notes>+ Added renderDotFile() method that renders an existing DOT file.
-
+
</notes>
</release>
<release>
@@ -116,7 +156,7 @@ http://pear.php.net/dtd/package-2.0.xsd">
* Fixed a bug with graph attributes.
-
+
</notes>
</release>
<release>
@@ -132,7 +172,7 @@ http://pear.php.net/dtd/package-2.0.xsd">
<license uri="http://www.php.net/license">PHP License</license>
<notes>* Fixed bugs #109 and #852.
-
+
</notes>
</release>
<release>
@@ -150,7 +190,7 @@ http://pear.php.net/dtd/package-2.0.xsd">
* Improved temporary file creation by using System::mktemp() instead of tempnam().
-
+
</notes>
</release>
<release>
@@ -168,7 +208,7 @@ http://pear.php.net/dtd/package-2.0.xsd">
+ The new addCluster() method can be used to give a label to a group of nodes and let GraphViz treat it as a node cluster.
-
+
</notes>
</release>
<release>
@@ -184,7 +224,7 @@ http://pear.php.net/dtd/package-2.0.xsd">
<license uri="http://www.php.net/license">PHP License</license>
<notes>* Added some missing newline characters in GraphViz markup.
-
+
</notes>
</release>
<release>
@@ -202,7 +242,7 @@ http://pear.php.net/dtd/package-2.0.xsd">
* Renamed methods to conform with PEAR standard.
-
+
</notes>
</release>
<release>
@@ -218,7 +258,7 @@ http://pear.php.net/dtd/package-2.0.xsd">
<license uri="http://www.php.net/license">PHP License</license>
<notes>* Allow for edges without attributes. (Patch by Carsten Saathoff &lt;kodemaniak@gmx.de&gt;)
-
+
</notes>
</release>
<release>
@@ -238,7 +278,7 @@ http://pear.php.net/dtd/package-2.0.xsd">
* Changed Image_GraphViz.php to Image/GraphViz.php.
-
+
</notes>
</release>
<release>
@@ -254,7 +294,7 @@ http://pear.php.net/dtd/package-2.0.xsd">
<license uri="http://www.php.net/license">PHP License</license>
<notes>First release.
-
+
</notes>
</release>
</changelog>
View
52 tests/test1.phpt
@@ -0,0 +1,52 @@
+--TEST--
+Unit test for undirected graph
+--FILE--
+<?php
+
+/**
+ * Test 1: "Process States in an Operating System Kernel"
+ *
+ * Graph definition taken from GraphViz documentation
+ *
+ * @category Image
+ * @package Image_GraphViz
+ * @author Philippe Jausions <jausions@php.net>
+ * @version $Id$
+ */
+require_once 'Image/GraphViz.php';
+
+$graph = new Image_GraphViz(false, null, 'G', false);
+
+$graph->addEdge(array('run' => 'intr'));
+$graph->addEdge(array('intr' => 'runbl'));
+$graph->addEdge(array('runbl' => 'run'));
+$graph->addEdge(array('run' => 'kernel'));
+$graph->addEdge(array('kernel' => 'zombie'));
+$graph->addEdge(array('kernel' => 'sleep'));
+$graph->addEdge(array('kernel' => 'runmem'));
+$graph->addEdge(array('sleep' => 'swap'));
+$graph->addEdge(array('swap' => 'runswap'));
+$graph->addEdge(array('runswap' => 'new'));
+$graph->addEdge(array('runswap' => 'runmem'));
+$graph->addEdge(array('new' => 'runmem'));
+$graph->addEdge(array('sleep' => 'runmem'));
+
+echo $graph->parse();
+
+?>
+--EXPECT--
+graph G {
+ run -- intr;
+ run -- kernel;
+ intr -- runbl;
+ runbl -- run;
+ kernel -- zombie;
+ kernel -- sleep;
+ kernel -- runmem;
+ sleep -- swap;
+ sleep -- runmem;
+ swap -- runswap;
+ runswap -- new;
+ runswap -- runmem;
+ new -- runmem;
+}
View
77 tests/test12.phpt
@@ -0,0 +1,77 @@
+--TEST--
+Unit test for graph with ports
+--FILE--
+<?php
+
+/**
+ * Test 12: "Graph of binary search tree"
+ *
+ * Graph definition taken from GraphViz documentation
+ *
+ * @category Image
+ * @package Image_GraphViz
+ * @author Philippe Jausions <jausions@php.net>
+ */
+require_once 'Image/GraphViz.php';
+
+$graph = new Image_GraphViz(true, array(), 'structs', false);
+
+$graph->addNode('node0', array('shape' => 'record',
+ 'label' => '<f0> |<f1> G|<f2> '));
+$graph->addNode('node1', array('shape' => 'record',
+ 'label' => '<f0> |<f1> E|<f2> '));
+$graph->addNode('node2', array('shape' => 'record',
+ 'label' => '<f0> |<f1> B|<f2> '));
+$graph->addNode('node3', array('shape' => 'record',
+ 'label' => '<f0> |<f1> F|<f2> '));
+$graph->addNode('node4', array('shape' => 'record',
+ 'label' => '<f0> |<f1> R|<f2> '));
+$graph->addNode('node5', array('shape' => 'record',
+ 'label' => '<f0> |<f1> H|<f2> '));
+$graph->addNode('node6', array('shape' => 'record',
+ 'label' => '<f0> |<f1> Y|<f2> '));
+$graph->addNode('node7', array('shape' => 'record',
+ 'label' => '<f0> |<f1> A|<f2> '));
+$graph->addNode('node8', array('shape' => 'record',
+ 'label' => '<f0> |<f1> C|<f2> '));
+
+$graph->addEdge(array('node0' => 'node4'), null,
+ array('node0' => 'f2', 'node4' => 'f1'));
+$graph->addEdge(array('node0' => 'node1'), null,
+ array('node0' => 'f0', 'node1' => 'f1'));
+$graph->addEdge(array('node1' => 'node2'), null,
+ array('node1' => 'f0', 'node2' => 'f1'));
+$graph->addEdge(array('node1' => 'node3'), null,
+ array('node1' => 'f2', 'node3' => 'f1'));
+$graph->addEdge(array('node2' => 'node8'), null,
+ array('node2' => 'f2', 'node8' => 'f1'));
+$graph->addEdge(array('node2' => 'node7'), null,
+ array('node2' => 'f0', 'node7' => 'f1'));
+$graph->addEdge(array('node4' => 'node6'), null,
+ array('node4' => 'f2', 'node6' => 'f1'));
+$graph->addEdge(array('node4' => 'node5'), null,
+ array('node4' => 'f0', 'node5' => 'f1'));
+
+echo $graph->parse();
+
+?>
+--EXPECT--
+digraph structs {
+ node0 [ shape=record,label="<f0> |<f1> G|<f2> " ];
+ node1 [ shape=record,label="<f0> |<f1> E|<f2> " ];
+ node2 [ shape=record,label="<f0> |<f1> B|<f2> " ];
+ node3 [ shape=record,label="<f0> |<f1> F|<f2> " ];
+ node4 [ shape=record,label="<f0> |<f1> R|<f2> " ];
+ node5 [ shape=record,label="<f0> |<f1> H|<f2> " ];
+ node6 [ shape=record,label="<f0> |<f1> Y|<f2> " ];
+ node7 [ shape=record,label="<f0> |<f1> A|<f2> " ];
+ node8 [ shape=record,label="<f0> |<f1> C|<f2> " ];
+ node0:f2 -> node4:f1;
+ node0:f0 -> node1:f1;
+ node1:f0 -> node2:f1;
+ node1:f2 -> node3:f1;
+ node2:f2 -> node8:f1;
+ node2:f0 -> node7:f1;
+ node4:f2 -> node6:f1;
+ node4:f0 -> node5:f1;
+}
View
40 tests/test14.phpt
@@ -0,0 +1,40 @@
+--TEST--
+Unit test for graphs with ports
+--FILE--
+<?php
+
+/**
+ * Test 14: "Drawing of records (revisited)"
+ *
+ * Graph definition taken from GraphViz documentation
+ *
+ * @category Image
+ * @package Image_GraphViz
+ * @author Philippe Jausions <jausions@php.net>
+ */
+require_once 'Image/GraphViz.php';
+
+$graph = new Image_GraphViz(true, array(), 'structs', false);
+
+$graph->addNode('struct1', array('shape' => 'record',
+ 'label' => '<f0> left|<f1> middle|<f2> right'));
+$graph->addNode('struct2', array('shape' => 'record',
+ 'label' => '<f0> one|<f1> two'));
+$graph->addNode('struct3', array('shape' => 'record',
+ 'label' => "hello\nworld | { b |{c|<here> d|e}| f}| g | h"));
+$graph->addEdge(array('struct1' => 'struct2'), array(),
+ array('struct1' => 'f1', 'struct2' => 'f0'));
+$graph->addEdge(array('struct1' => 'struct3'), array(),
+ array('struct1' => 'f1', 'struct3' => 'here'));
+
+echo $graph->parse();
+
+?>
+--EXPECT--
+digraph structs {
+ struct1 [ shape=record,label="<f0> left|<f1> middle|<f2> right" ];
+ struct2 [ shape=record,label="<f0> one|<f1> two" ];
+ struct3 [ shape=record,label="hello\nworld | { b |{c|<here> d|e}| f}| g | h" ];
+ struct1:f1 -> struct2:f0;
+ struct1:f1 -> struct3:here;
+}
View
77 tests/test16.phpt
@@ -0,0 +1,77 @@
+--TEST--
+Unit test for graph with ports
+--FILE--
+<?php
+
+/**
+ * Test 16: "Drawing of hash table"
+ *
+ * Graph definition taken from GraphViz documentation
+ *
+ * @category Image
+ * @package Image_GraphViz
+ * @author Philippe Jausions <jausions@php.net>
+ */
+require_once 'Image/GraphViz.php';
+
+$graph = new Image_GraphViz(true, array(), 'G', false);
+
+$graph->setAttributes(array('nodesep' => 0.05,
+ 'rankdir' => 'LR'));
+
+$graph->addNode('node0', array('shape' => 'record',
+ 'label' => '<f0> |<f1> |<f2> |<f3> |<f4> |<f5> |<f6> | ',
+ 'height' => 2.5));
+$graph->addNode('node1', array('shape' => 'record',
+ 'label' => '{<n> n14 | 719 |<p> }'));
+$graph->addNode('node2', array('shape' => 'record',
+ 'label' => '{<n> a1 | 805 |<p> }'));
+$graph->addNode('node3', array('shape' => 'record',
+ 'label' => '{<n> i9 | 718 |<p> }'));
+$graph->addNode('node4', array('shape' => 'record',
+ 'label' => '{<n> e5 | 989 |<p> }'));
+$graph->addNode('node5', array('shape' => 'record',
+ 'label' => '{<n> t20 | 959 |<p> }'));
+$graph->addNode('node6', array('shape' => 'record',
+ 'label' => '{<n> o15 | 794 |<p> }'));
+$graph->addNode('node7', array('shape' => 'record',
+ 'label' => '{<n> s19 | 659 |<p> }'));
+
+$graph->addEdge(array('node0' => 'node1'), null,
+ array('node0' => 'f0', 'node1' => 'n'));
+$graph->addEdge(array('node0' => 'node2'), null,
+ array('node0' => 'f1', 'node2' => 'n'));
+$graph->addEdge(array('node0' => 'node3'), null,
+ array('node0' => 'f2', 'node3' => 'n'));
+$graph->addEdge(array('node0' => 'node4'), null,
+ array('node0' => 'f5', 'node4' => 'n'));
+$graph->addEdge(array('node0' => 'node5'), null,
+ array('node0' => 'f6', 'node5' => 'n'));
+$graph->addEdge(array('node2' => 'node6'), null,
+ array('node2' => 'p', 'node6' => 'n'));
+$graph->addEdge(array('node4' => 'node7'), null,
+ array('node4' => 'p', 'node7' => 'n'));
+
+echo $graph->parse();
+
+?>
+--EXPECT--
+digraph G {
+ nodesep=0.05;
+ rankdir=LR;
+ node0 [ shape=record,label="<f0> |<f1> |<f2> |<f3> |<f4> |<f5> |<f6> | ",height=2.5 ];
+ node1 [ shape=record,label="{<n> n14 | 719 |<p> }" ];
+ node2 [ shape=record,label="{<n> a1 | 805 |<p> }" ];
+ node3 [ shape=record,label="{<n> i9 | 718 |<p> }" ];
+ node4 [ shape=record,label="{<n> e5 | 989 |<p> }" ];
+ node5 [ shape=record,label="{<n> t20 | 959 |<p> }" ];
+ node6 [ shape=record,label="{<n> o15 | 794 |<p> }" ];
+ node7 [ shape=record,label="{<n> s19 | 659 |<p> }" ];
+ node0:f0 -> node1:n;
+ node0:f1 -> node2:n;
+ node0:f2 -> node3:n;
+ node0:f5 -> node4:n;
+ node0:f6 -> node5:n;
+ node2:p -> node6:n;
+ node4:p -> node7:n;
+}
View
88 tests/test17.phpt
@@ -0,0 +1,88 @@
+--TEST--
+Unit test for graph with clusters
+--FILE--
+<?php
+
+/**
+ * Test 17: "Process diagram with clusters"
+ *
+ * Graph definition taken from GraphViz documentation
+ *
+ * @category Image
+ * @package Image_GraphViz
+ * @author Philippe Jausions <jausions@php.net>
+ */
+require_once 'Image/GraphViz.php';
+
+$graph = new Image_GraphViz(true, array(), 'G', false);
+
+// cluster0
+$graph->addCluster('cluster0', 'process #1',
+ array('style' => 'filled', 'color' => 'lightgrey'));
+$graph->addNode('a0', null, 'cluster0');
+$graph->addNode('a1', null, 'cluster0');
+$graph->addNode('a2', null, 'cluster0');
+$graph->addNode('a3', null, 'cluster0');
+$graph->addEdge(array('a0' => 'a1'));
+$graph->addEdge(array('a1' => 'a2'));
+$graph->addEdge(array('a2' => 'a3'));
+
+// cluster1
+$graph->addCluster('cluster1', 'process #2',
+ array('color' => 'blue'));
+$graph->addNode('b0', null, 'cluster1');
+$graph->addNode('b1', null, 'cluster1');
+$graph->addNode('b2', null, 'cluster1');
+$graph->addNode('b3', null, 'cluster1');
+$graph->addEdge(array('b0' => 'b1'));
+$graph->addEdge(array('b1' => 'b2'));
+$graph->addEdge(array('b2' => 'b3'));
+
+// Global
+$graph->addNode('start', array('shape' => 'Mdiamond'));
+$graph->addNode('end', array('shape' => 'Msquare'));
+
+$graph->addEdge(array('start' => 'a0'));
+$graph->addEdge(array('start' => 'b0'));
+
+$graph->addEdge(array('a1' => 'b3'));
+$graph->addEdge(array('b2' => 'a3'));
+$graph->addEdge(array('a3' => 'a0'));
+$graph->addEdge(array('a3' => 'end'));
+$graph->addEdge(array('b3' => 'end'));
+
+echo $graph->parse();
+
+?>
+--EXPECT--
+digraph G {
+ subgraph cluster0 {
+ graph [ filled,lightgrey,style=filled,color=lightgrey,label="process #1" ];
+ a0;
+ a1;
+ a2;
+ a3;
+ }
+ subgraph cluster1 {
+ graph [ blue,color=blue,label="process #2" ];
+ b0;
+ b1;
+ b2;
+ b3;
+ }
+ start [ shape=Mdiamond ];
+ end [ shape=Msquare ];
+ a0 -> a1;
+ a1 -> a2;
+ a1 -> b3;
+ a2 -> a3;
+ b0 -> b1;
+ b1 -> b2;
+ b2 -> b3;
+ b2 -> a3;
+ start -> a0;
+ start -> b0;
+ a3 -> a0;
+ a3 -> end;
+ b3 -> end;
+}
View
204 tests/test19.phpt
@@ -0,0 +1,204 @@
+--TEST--
+Unit test for call graph with labeled clusters
+--FILE--
+<?php
+
+/**
+ * Test 17: "Call graph with labeled clusters"
+ *
+ * Graph definition taken from GraphViz documentation
+ *
+ * @category Image
+ * @package Image_GraphViz
+ * @author Philippe Jausions <jausions@php.net>
+ */
+require_once 'Image/GraphViz.php';
+
+$graph = new Image_GraphViz(true, array('size' => 8.6, 'ratio' => 'fill'),
+ 'G', false);
+
+$graph->addCluster('cluster_error.h', 'error.h');
+$graph->addNode('interp_err', null, 'cluster_error.h');
+
+$graph->addCluster('cluster_sfio.h', 'sfio.h');
+$graph->addNode('sfprintf', null, 'cluster_sfio.h');
+
+$graph->addCluster('cluster_ciafan.c', 'ciafan.c');
+$graph->addNode('ciafan', null, 'cluster_ciafan.c');
+$graph->addNode('computefan', null, 'cluster_ciafan.c');
+$graph->addNode('increment', null, 'cluster_ciafan.c');
+
+$graph->addCluster('cluster_util.c', 'util.c');
+$graph->addNode('stringdup', null, 'cluster_util.c');
+$graph->addNode('fatal', null, 'cluster_util.c');
+$graph->addNode('debug', null, 'cluster_util.c');
+
+$graph->addCluster('cluster_query.h', 'query.h');
+$graph->addNode('ref', null, 'cluster_query.h');
+$graph->addNode('def', null, 'cluster_query.h');
+
+$graph->addCluster('cluster_field.h', '');
+$graph->addNode('get_sym_fields', null, 'cluster_field.h');
+
+$graph->addCluster('cluster_stdio.h', 'stdio.h');
+$graph->addNode('stdprintf', null, 'cluster_stdio.h');
+$graph->addNode('stdsprintf', null, 'cluster_stdio.h');
+
+$graph->addCluster('cluster_<libc.a>', '');
+$graph->addNode('getopt', null, 'cluster_<libc.a>');
+
+$graph->addCluster('cluster_stdlib.h', 'stdlib.h');
+$graph->addNode('exit', null, 'cluster_stdlib.h');
+$graph->addNode('malloc', null, 'cluster_stdlib.h');
+$graph->addNode('free', null, 'cluster_stdlib.h');
+$graph->addNode('realloc', null, 'cluster_stdlib.h');
+
+$graph->addCluster('cluster_main.c', '');
+$graph->addNode('main', null, 'cluster_main.c');
+
+$graph->addCluster('cluster_index.h', '');
+$graph->addNode('init_index', null, 'cluster_index.h');
+
+$graph->addCluster('cluster_string.h', 'string.h');
+$graph->addNode('strcpy', null, 'cluster_string.h');
+$graph->addNode('strlen', null, 'cluster_string.h');
+$graph->addNode('strcmp', null, 'cluster_string.h');
+$graph->addNode('strcat', null, 'cluster_string.h');
+
+$graph->addEdge(array('ciafan' => 'computefan'));
+$graph->addEdge(array('fan' => 'increment'));
+$graph->addEdge(array('computefan' => 'fan'));
+$graph->addEdge(array('stringdup' => 'fatal'));
+$graph->addEdge(array('main' => 'exit'));
+$graph->addEdge(array('main' => 'interp_err'));
+$graph->addEdge(array('main' => 'ciafan'));
+$graph->addEdge(array('main' => 'fatal'));
+$graph->addEdge(array('main' => 'malloc'));
+$graph->addEdge(array('main' => 'strcpy'));
+$graph->addEdge(array('main' => 'getopt'));
+$graph->addEdge(array('main' => 'init_index'));
+$graph->addEdge(array('main' => 'strlen'));
+$graph->addEdge(array('fan' => 'fatal'));
+$graph->addEdge(array('fan' => 'ref'));
+$graph->addEdge(array('fan' => 'interp_err'));
+$graph->addEdge(array('ciafan' => 'def'));
+$graph->addEdge(array('fan' => 'free'));
+$graph->addEdge(array('computefan' => 'stdprintf'));
+$graph->addEdge(array('computefan' => 'get_sym_fields'));
+$graph->addEdge(array('fan' => 'exit'));
+$graph->addEdge(array('fan' => 'malloc'));
+$graph->addEdge(array('increment' => 'strcmp'));
+$graph->addEdge(array('computefan' => 'malloc'));
+$graph->addEdge(array('fan' => 'stdsprintf'));
+$graph->addEdge(array('fan' => 'strlen'));
+$graph->addEdge(array('computefan' => 'strcmp'));
+$graph->addEdge(array('computefan' => 'realloc'));
+$graph->addEdge(array('computefan' => 'strlen'));
+$graph->addEdge(array('debug' => 'sfprintf'));
+$graph->addEdge(array('debug' => 'strcat'));
+$graph->addEdge(array('stringdup' => 'malloc'));
+$graph->addEdge(array('fatal' => 'sfprintf'));
+$graph->addEdge(array('stringdup' => 'strcpy'));
+$graph->addEdge(array('stringdup' => 'strlen'));
+$graph->addEdge(array('fatal' => 'exit'));
+
+echo $graph->parse();
+
+?>
+--EXPECT--
+digraph G {
+ size=8.6;
+ ratio=fill;
+ subgraph "cluster_error.h" {
+ graph [ label="error.h" ];
+ interp_err;
+ }
+ subgraph "cluster_sfio.h" {
+ graph [ label="sfio.h" ];
+ sfprintf;
+ }
+ subgraph "cluster_ciafan.c" {
+ graph [ label="ciafan.c" ];
+ ciafan;
+ computefan;
+ increment;
+ }
+ subgraph "cluster_util.c" {
+ graph [ label="util.c" ];
+ stringdup;
+ fatal;
+ debug;
+ }
+ subgraph "cluster_query.h" {
+ graph [ label="query.h" ];
+ ref;
+ def;
+ }
+ subgraph "cluster_field.h" {
+ get_sym_fields;
+ }
+ subgraph "cluster_stdio.h" {
+ graph [ label="stdio.h" ];
+ stdprintf;
+ stdsprintf;
+ }
+ subgraph "cluster_<libc.a>" {
+ getopt;
+ }
+ subgraph "cluster_stdlib.h" {
+ graph [ label="stdlib.h" ];
+ exit;
+ malloc;
+ free;
+ realloc;
+ }
+ subgraph "cluster_main.c" {
+ main;
+ }
+ subgraph "cluster_index.h" {
+ init_index;
+ }
+ subgraph "cluster_string.h" {
+ graph [ label="string.h" ];
+ strcpy;
+ strlen;
+ strcmp;
+ strcat;
+ }
+ ciafan -> computefan;
+ ciafan -> def;
+ fan -> increment;
+ fan -> fatal;
+ fan -> ref;
+ fan -> interp_err;
+ fan -> free;
+ fan -> exit;
+ fan -> malloc;
+ fan -> stdsprintf;
+ fan -> strlen;
+ computefan -> fan;
+ computefan -> stdprintf;
+ computefan -> get_sym_fields;
+ computefan -> malloc;
+ computefan -> strcmp;
+ computefan -> realloc;
+ computefan -> strlen;
+ stringdup -> fatal;
+ stringdup -> malloc;
+ stringdup -> strcpy;
+ stringdup -> strlen;
+ main -> exit;
+ main -> interp_err;
+ main -> ciafan;
+ main -> fatal;
+ main -> malloc;
+ main -> strcpy;
+ main -> getopt;
+ main -> init_index;
+ main -> strlen;
+ increment -> strcmp;
+ debug -> sfprintf;
+ debug -> strcat;
+ fatal -> sfprintf;
+ fatal -> exit;
+}
View
81 tests/test2.phpt
@@ -0,0 +1,81 @@
+--TEST--
+Unit test for HTML-like labels and ports
+--FILE--
+<?php
+
+/**
+ * Test 2: "HTML-like labels"
+ *
+ * Graph definition taken from GraphViz documentation
+ *
+ * @category Image
+ * @package Image_GraphViz
+ * @author Philippe Jausions <jausions@php.net>
+ * @version $Id$
+ */
+require_once 'Image/GraphViz.php';
+
+$graph = new Image_GraphViz(true, null, 'structs', false);
+
+$graph->addNode('struct1', array(
+ 'shape' => 'plaintext',
+ 'label' => '<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
+ <TR><TD>left</TD><TD PORT="f1">mid dle</TD><TD PORT="f2">right</TD></TR>
+</TABLE>'));
+
+$graph->addNode('struct2', array(
+ 'shape' => 'plaintext',
+ 'label' => '<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
+ <TR><TD PORT="f0">one</TD><TD>two</TD></TR>
+</TABLE>'));
+
+$graph->addNode('struct3', array(
+ 'shape' => 'plaintext',
+ 'label' => '<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="4">
+ <TR>
+ <TD ROWSPAN="3">hello<BR/>world</TD>
+ <TD COLSPAN="3">b</TD>
+ <TD ROWSPAN="3">g</TD>
+ <TD ROWSPAN="3">h</TD>
+ </TR>
+ <TR>
+ <TD>c</TD><TD PORT="here">d</TD><TD>e</TD>
+ </TR>
+ <TR>
+ <TD COLSPAN="3">f</TD>
+ </TR>
+</TABLE>'));
+
+$graph->addEdge(array('struct1' => 'struct2'), null, array('struct1' => 'f1',
+ 'struct2' => 'f0'));
+
+$graph->addEdge(array('struct1' => 'struct3'), null, array('struct1' => 'f2',
+ 'struct3' => 'here'));
+echo $graph->parse();
+
+?>
+--EXPECT--
+digraph structs {
+ struct1 [ shape=plaintext,label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
+ <TR><TD>left</TD><TD PORT="f1">mid dle</TD><TD PORT="f2">right</TD></TR>
+</TABLE>> ];
+ struct2 [ shape=plaintext,label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
+ <TR><TD PORT="f0">one</TD><TD>two</TD></TR>
+</TABLE>> ];
+ struct3 [ shape=plaintext,label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="4">
+ <TR>
+ <TD ROWSPAN="3">hello<BR/>world</TD>
+ <TD COLSPAN="3">b</TD>
+ <TD ROWSPAN="3">g</TD>
+ <TD ROWSPAN="3">h</TD>
+ </TR>
+ <TR>
+ <TD>c</TD><TD PORT="here">d</TD><TD>e</TD>
+ </TR>
+ <TR>
+ <TD COLSPAN="3">f</TD>
+ </TR>
+</TABLE>> ];
+ struct1:f1 -> struct2:f0;
+ struct1:f2 -> struct3:here;
+}
View
74 tests/test20.phpt
@@ -0,0 +1,74 @@
+--TEST--
+Unit test for graph with edges on clusters
+--FILE--
+<?php
+
+/**
+ * Test 17: "Graph with edges on clusters"
+ *
+ * Graph definition taken from GraphViz documentation
+ *
+ * @category Image
+ * @package Image_GraphViz
+ * @author Philippe Jausions <jausions@php.net>
+ */
+require_once 'Image/GraphViz.php';
+
+$graph = new Image_GraphViz(true, array('compound' => true), 'G', false);
+
+$graph->addCluster('cluster0', '');
+$graph->addNode('a', null, 'cluster0');
+$graph->addNode('b', null, 'cluster0');
+$graph->addNode('c', null, 'cluster0');
+$graph->addNode('d', null, 'cluster0');
+
+$graph->addEdge(array('a' => 'b'));
+$graph->addEdge(array('a' => 'c'));
+$graph->addEdge(array('b' => 'd'));
+$graph->addEdge(array('c' => 'd'));
+
+$graph->addCluster('cluster1', '');
+$graph->addNode('e', null, 'cluster1');
+$graph->addNode('f', null, 'cluster1');
+$graph->addNode('g', null, 'cluster1');
+
+$graph->addEdge(array('e' => 'g'));
+$graph->addEdge(array('e' => 'f'));
+
+$graph->addEdge(array('b' => 'f'), array('lhead' => 'cluster1'));
+$graph->addEdge(array('d' => 'e'));
+$graph->addEdge(array('c' => 'g'), array('ltail' => 'cluster0',
+ 'lhead' => 'cluster1'));
+$graph->addEdge(array('c' => 'e'), array('ltail' => 'cluster0'));
+
+$graph->addEdge(array('d' => 'h'));
+
+echo $graph->parse();
+
+?>
+--EXPECT--
+digraph G {
+ compound=true;
+ subgraph cluster0 {
+ a;
+ b;
+ c;
+ d;
+ }
+ subgraph cluster1 {
+ e;
+ f;
+ g;
+ }
+ a -> b;
+ a -> c;
+ b -> d;
+ b -> f [ lhead=cluster1 ];
+ c -> d;
+ c -> g [ ltail=cluster0,lhead=cluster1 ];
+ c -> e [ ltail=cluster0 ];
+ e -> g;
+ e -> f;
+ d -> e;
+ d -> h;
+}
View
57 tests/test3.phpt
@@ -0,0 +1,57 @@
+--TEST--
+Unit test for fancy graph (comment, colors, shapes)
+--FILE--
+<?php
+
+/**
+ * Test 3: "Fancy graph"
+ *
+ * Graph definition taken from GraphViz documentation
+ *
+ * @category Image
+ * @package Image_GraphViz
+ * @author Philippe Jausions <jausions@php.net>
+ */
+require_once 'Image/GraphViz.php';
+
+$graph = new Image_GraphViz(true, null, 'G', false);
+
+$graph->addNode('main', array('shape' => 'box',
+ 'comment' => 'this is a comment'));
+
+$graph->addEdge(array('main' => 'parse'), array('weight' => 8));
+
+$graph->addEdge(array('parse' => 'execute'));
+$graph->addEdge(array('main' => 'init'), array('style' => 'dotted'));
+$graph->addEdge(array('main' => 'cleanup'));
+$graph->addEdge(array('execute' => 'make_string'));
+$graph->addEdge(array('execute' => 'printf'));
+$graph->addEdge(array('init' => 'make_string'));
+
+$graph->addEdge(array('main' => 'printf'), array('style' => 'bold',
+ 'label' => '100 times'));
+$graph->addNode('make_string', array('label' => "make a\nstring"));
+$graph->addNode('compare', array('shape' => 'box',
+ 'style' => 'filled',
+ 'color' => '.7 .3 1.0'));
+$graph->addEdge(array('execute' => 'compare'), array('color' => 'red',
+ 'comment' => 'so is this'));
+
+echo $graph->parse();
+
+?>
+--EXPECT--
+digraph G {
+ main [ shape=box,comment="this is a comment" ];
+ make_string [ label="make a\nstring" ];
+ compare [ shape=box,style=filled,color=".7 .3 1.0" ];
+ main -> parse [ weight=8 ];
+ main -> init [ style=dotted ];
+ main -> cleanup;
+ main -> printf [ style=bold,label="100 times" ];
+ parse -> execute;
+ execute -> make_string;
+ execute -> printf;
+ execute -> compare [ color=red,comment="so is this" ];
+ init -> make_string;
+}
View
100 tests/test4.phpt
@@ -0,0 +1,100 @@
+--TEST--
+Unit test for HTML-like labels
+--FILE--
+<?php
+
+/**
+ * Test 4: "HTML-like labels"
+ *
+ * Graph definition taken from GraphViz documentation
+ *
+ * @category Image
+ * @package Image_GraphViz
+ * @author Philippe Jausions <jausions@php.net>
+ */
+require_once 'Image/GraphViz.php';
+
+$graph = new Image_GraphViz(true, array('rankdir' => 'LR'), 'G', false);
+
+$graph->addNode('a', array('shape' => 'plaintext',
+ 'label' => '<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
+ <TR><TD ROWSPAN="3" BGCOLOR="yellow">class</TD></TR>
+ <TR><TD PORT="here" BGCOLOR="lightblue">qualifier</TD></TR>
+</TABLE>'));
+
+$graph->addNode('b', array('shape' => 'ellipse',
+ 'style' => 'filled',
+ 'label' => '<TABLE BGCOLOR="bisque">
+ <TR><TD COLSPAN="3">elephant</TD>
+ <TD ROWSPAN="2" BGCOLOR="chartreuse"
+ VALIGN="bottom" ALIGN="right">two</TD> </TR>
+ <TR><TD COLSPAN="2" ROWSPAN="2">
+ <TABLE BGCOLOR="grey">
+ <TR> <TD>corn</TD> </TR>
+ <TR> <TD BGCOLOR="yellow">c</TD> </TR>
+ <TR> <TD>f</TD> </TR>
+ </TABLE> </TD>
+ <TD BGCOLOR="white">penguin</TD>
+ </TR>
+ <TR> <TD COLSPAN="2" BORDER="4" ALIGN="right" PORT="there">4</TD> </TR>
+</TABLE>
+ '), 'subgraph');
+
+$graph->addNode('c', array('shape' => 'plaintext',
+ 'label' => 'long line 1<BR/>line 2<BR ALIGN="LEFT"/>line 3<BR ALIGN="RIGHT"/>'), 'subgraph');
+
+$graph->addCluster('subgraph', '', array('rank' => 'same'));
+
+$graph->addEdge(array('c' => 'b'));
+
+$graph->addNode('d', array('shape' => 'triangle'));
+
+$graph->addEdge(array('d' => 'c'), array('label' => '<TABLE>
+ <TR><TD BGCOLOR="red" WIDTH="10"> </TD>
+ <TD>Edge labels<BR/>also</TD>
+ <TD BGCOLOR="blue" WIDTH="10"> </TD>
+ </TR>
+</TABLE>'));
+
+$graph->addEdge(array('a' => 'b'), array('arrowtail' => 'diamond'),
+ array('a' => 'here', 'b' => 'there'));
+
+echo $graph->parse();
+
+?>
+--EXPECT--
+digraph G {
+ rankdir=LR;
+ a [ shape=plaintext,label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
+ <TR><TD ROWSPAN="3" BGCOLOR="yellow">class</TD></TR>
+ <TR><TD PORT="here" BGCOLOR="lightblue">qualifier</TD></TR>
+</TABLE>> ];
+ d [ shape=triangle ];
+ subgraph "subgraph" {
+ graph [ same,rank=same ];
+ b [ shape=ellipse,style=filled,label=<<TABLE BGCOLOR="bisque">
+ <TR><TD COLSPAN="3">elephant</TD>
+ <TD ROWSPAN="2" BGCOLOR="chartreuse"
+ VALIGN="bottom" ALIGN="right">two</TD> </TR>
+ <TR><TD COLSPAN="2" ROWSPAN="2">
+ <TABLE BGCOLOR="grey">
+ <TR> <TD>corn</TD> </TR>
+ <TR> <TD BGCOLOR="yellow">c</TD> </TR>
+ <TR> <TD>f</TD> </TR>
+ </TABLE> </TD>
+ <TD BGCOLOR="white">penguin</TD>
+ </TR>
+ <TR> <TD COLSPAN="2" BORDER="4" ALIGN="right" PORT="there">4</TD> </TR>
+</TABLE>
+ > ];
+ c [ shape=plaintext,label=<long line 1<BR/>line 2<BR ALIGN="LEFT"/>line 3<BR ALIGN="RIGHT"/>> ];
+ }
+ c -> b;
+ d -> c [ label=<<TABLE>
+ <TR><TD BGCOLOR="red" WIDTH="10"> </TD>
+ <TD>Edge labels<BR/>also</TD>
+ <TD BGCOLOR="blue" WIDTH="10"> </TD>
+ </TR>
+</TABLE>> ];
+ a:here -> b:there [ arrowtail=diamond ];
+}
View
49 tests/test5.phpt
@@ -0,0 +1,49 @@
+--TEST--
+Unit test for Graph with polygonal shapes
+--FILE--
+<?php
+
+/**
+ * Test 5: "Graph with polygonal shapes"
+ *
+ * Graph definition taken from GraphViz documentation
+ *
+ * @category Image
+ * @package Image_GraphViz
+ * @author Philippe Jausions <jausions@php.net>
+ */
+require_once 'Image/GraphViz.php';
+
+$graph = new Image_GraphViz(true, null, 'G', false);
+
+$graph->addNode('a', array('shape' => 'polygon',
+ 'sides' => 5,
+ 'peripheries' => 3,
+ 'color' => 'lightblue',
+ 'style' => 'filled'));
+$graph->addNode('c', array('shape' => 'polygon',
+ 'sides' => 4,
+ 'skew' => .4,
+ 'label' => 'hello world'));
+$graph->addNode('d', array('shape' => 'invtriangle'));
+$graph->addNode('e', array('shape' => 'polygon',
+ 'sides' => 4,
+ 'distortion' => .7));
+
+$graph->addEdge(array('a' => 'b'));
+$graph->addEdge(array('b' => 'c'));
+$graph->addEdge(array('b' => 'd'));
+
+echo $graph->parse();
+
+?>
+--EXPECT--
+digraph G {
+ a [ shape=polygon,sides=5,peripheries=3,color=lightblue,style=filled ];
+ c [ shape=polygon,sides=4,skew=0.4,label="hello world" ];
+ d [ shape=invtriangle ];
+ e [ shape=polygon,sides=4,distortion=0.7 ];
+ a -> b;
+ b -> c;
+ b -> d;
+}
View
40 tests/test6.phpt
@@ -0,0 +1,40 @@
+--TEST--
+Unit test for nodes and clusters using keyword as name
+--FILE--
+<?php
+
+/**
+ * Test 5: Keywords
+ *
+ * @category Image
+ * @package Image_GraphViz
+ * @author Philippe Jausions <jausions@php.net>
+ */
+require_once 'Image/GraphViz.php';
+
+$graph = new Image_GraphViz(true, null, 'strict', true);
+
+$graph->addNode('graph');
+
+$graph->addCluster('subgraph', '');
+$graph->addCluster('digraph', '');
+
+$graph->addNode('node', null, 'subgraph');
+$graph->addNode('edge', null, 'digraph');
+
+$graph->addEdge(array('node' => 'edge'));
+
+echo $graph->parse();
+
+?>
+--EXPECT--
+strict digraph "strict" {
+ "graph";
+ subgraph "subgraph" {
+ "node";
+ }
+ subgraph "digraph" {
+ "edge";
+ }
+ "node" -> "edge";
+}
View
403 tests/test9.phpt
@@ -0,0 +1,403 @@
+--TEST--
+Unit test for graph with constrained rank clusters
+--FILE--
+<?php
+
+/**
+ * Test 9: "Graph with constrained ranks"
+ *
+ * Graph definition taken from GraphViz documentation
+ *
+ * @category Image
+ * @package Image_GraphViz
+ * @author Philippe Jausions <jausions@php.net>
+ */
+require_once 'Image/GraphViz.php';
+
+$graph = new Image_GraphViz(true, array('ranksep' => .75),
+ 'asde91', false);
+
+/* the time-line graph */
+$graph->addEdge(array('past' => 1978));
+$graph->addEdge(array(1978 => 1980));
+$graph->addEdge(array(1980 => 1982));
+$graph->addEdge(array(1982 => 1983));
+$graph->addEdge(array(1983 => 1985));
+$graph->addEdge(array(1985 => 1986));
+$graph->addEdge(array(1986 => 1987));
+$graph->addEdge(array(1987 => 1988));
+$graph->addEdge(array(1988 => 1989));
+$graph->addEdge(array(1989 => 1990));
+$graph->addEdge(array(1990 => 'future'));
+
+/* program types graph */
+$graph->addCluster('type', '', array('rank' => 'same'));
+
+$graph->addNode('Software IS', null, 'type');
+$graph->addNode('Configuration Mgt', null, 'type');
+$graph->addNode('Architecture & Libraries', null, 'type');
+$graph->addNode('Process', null, 'type');
+
+/* time graphs */
+$graph->addCluster('past', '', array('rank' => 'same'));
+$graph->addCluster(1978, '', array('rank' => 'same'));
+$graph->addCluster(1980, '', array('rank' => 'same'));
+$graph->addCluster(1982, '', array('rank' => 'same'));
+$graph->addCluster(1983, '', array('rank' => 'same'));
+$graph->addCluster(1985, '', array('rank' => 'same'));
+$graph->addCluster(1986, '', array('rank' => 'same'));
+$graph->addCluster(1987, '', array('rank' => 'same'));
+$graph->addCluster(1988, '', array('rank' => 'same'));
+$graph->addCluster(1989, '', array('rank' => 'same'));
+$graph->addCluster(1990, '', array('rank' => 'same'));
+$graph->addCluster('future', '', array('rank' => 'same'));
+
+/* programs */
+$graph->addNode('Bourne sh', null, 'past');
+$graph->addNode('make', null, 'past');
+$graph->addNode('SCCS', null, 'past');
+$graph->addNode('yacc', null, 'past');
+$graph->addNode('cron', null, 'past');
+
+$graph->addNode('Reiser cpp', null, 1978);
+$graph->addNode('Cshell', null, 1978);
+
+$graph->addNode('emacs', null, 1980);
+$graph->addNode('build', null, 1980);
+$graph->addNode('vi', null, 1980);
+
+$graph->addNode('<curses>', null, 1982);
+$graph->addNode('RCS', null, 1982);
+$graph->addNode('IMX', null, 1982);
+$graph->addNode('SYNED', null, 1982);
+
+$graph->addNode('ksh', null, 1983);
+$graph->addNode('IFS', null, 1983);
+$graph->addNode('TTU', null, 1983);
+
+$graph->addNode('nmake', null, 1985);
+$graph->addNode('Peggy', null, 1985);
+
+$graph->addNode('ncpp', null, 1986);
+$graph->addNode('ksh-i', null, 1986);
+$graph->addNode('<curses-i>', null, 1986);
+$graph->addNode('PG2', null, 1986);
+$graph->addNode('C*', null, 1986);
+
+$graph->addNode('Ansi cpp', null, 1987);
+$graph->addNode('nmake 2.0', null, 1987);
+$graph->addNode('3D File System', null, 1987);
+$graph->addNode('fdelta', null, 1987);
+$graph->addNode('DAG', null, 1987);
+$graph->addNode('CSAS', null, 1987);
+
+$graph->addNode('CIA', null, 1988);
+$graph->addNode('SBCS', null, 1988);
+$graph->addNode('ksh-88', null, 1988);
+$graph->addNode('PEGASUS/PML', null, 1988);
+$graph->addNode('PAX', null, 1988);
+$graph->addNode('backtalk', null, 1988);
+
+$graph->addNode('CIA++', null, 1989);
+$graph->addNode('APP', null, 1989);
+$graph->addNode('SHIP', null, 1989);
+$graph->addNode('DataShare', null, 1989);
+$graph->addNode('ryacc', null, 1989);
+$graph->addNode('Mosaic', null, 1989);
+
+$graph->addNode('libft', null, 1990);
+$graph->addNode('CoShell', null, 1990);
+$graph->addNode('DIA', null, 1990);
+$graph->addNode('IFS-i', null, 1990);
+$graph->addNode('kyacc', null, 1990);
+$graph->addNode('sfio', null, 1990);
+$graph->addNode('yeast', null, 1990);
+$graph->addNode('ML-X', null, 1990);
+$graph->addNode('DOT', null, 1990);
+
+$graph->addNode('Adv. Software Technology', null, 'future');
+
+/* hierachy */
+$graph->addEdge(array('SCCS' => 'RCS'));
+$graph->addEdge(array('SCCS' => '3D File System'));
+$graph->addEdge(array('SCCS' => 'nmake'));
+$graph->addEdge(array('make' => 'nmake'));
+$graph->addEdge(array('make' => 'build'));
+$graph->addEdge(array('Bourne sh' => 'Cshell'));
+$graph->addEdge(array('Bourne sh' => 'ksh'));
+$graph->addEdge(array('yacc' => 'ryacc'));
+$graph->addEdge(array('cron' => 'yeast'));
+
+$graph->addEdge(array('Reiser cpp' => 'ncpp'));
+$graph->addEdge(array('Cshell' => 'ksh'));
+
+$graph->addEdge(array('build' => 'nmake 2.0'));
+$graph->addEdge(array('vi' => 'ksh'));
+$graph->addEdge(array('vi' => '<curses>'));
+$graph->addEdge(array('emacs' => 'ksh'));
+
+$graph->addEdge(array('RCS' => 'SBCS'));
+$graph->addEdge(array('RCS' => 'fdelta'));
+$graph->addEdge(array('<curses>' => '<curses-i>'));
+$graph->addEdge(array('SYNED' => 'Peggy'));
+$graph->addEdge(array('IMX' => 'TTU'));
+
+$graph->addEdge(array('ksh' => 'nmake'));
+$graph->addEdge(array('ksh' => 'ksh-i'));
+$graph->addEdge(array('ksh' => 'ksh-88'));
+$graph->addEdge(array('IFS' => '<curses-i>'));
+$graph->addEdge(array('IFS' => 'sfio'));
+$graph->addEdge(array('IFS' => 'IFS-i'));
+$graph->addEdge(array('TTU' => 'PG2'));
+
+$graph->addEdge(array('nmake' => 'ksh'));
+$graph->addEdge(array('nmake' => 'ncpp'));
+$graph->addEdge(array('nmake' => '3D File System'));
+$graph->addEdge(array('nmake' => 'nmake 2.0'));
+$graph->addEdge(array('Peggy' => 'PEGASUS/PML'));
+$graph->addEdge(array('Peggy' => 'ryacc'));
+
+$graph->addEdge(array('C*' => 'CSAS'));
+$graph->addEdge(array('ncpp' => 'Ansi cpp'));
+$graph->addEdge(array('<curses-i>' => 'fdelta'));
+$graph->addEdge(array('ksh-i' => 'ksh-88'));
+$graph->addEdge(array('PG2' => 'backtalk'));
+
+$graph->addEdge(array('DAG' => 'Sotware IS'));
+$graph->addEdge(array('DAG' => 'DOT'));
+$graph->addEdge(array('DAG' => 'DIA'));
+$graph->addEdge(array('CSAS' => 'CIA'));
+$graph->addEdge(array('Ansi cpp' => 'Configuration Mgt'));
+$graph->addEdge(array('fdelta' => 'SBCS'));
+$graph->addEdge(array('fdelta' => 'PAX'));
+$graph->addEdge(array('3D File System' => 'Configuration Mgt'));
+$graph->addEdge(array('nmake 2.0' => 'Configuration Mgt'));
+$graph->addEdge(array('nmake 2.0' => 'CoShell'));
+
+$graph->addEdge(array('CIA' => 'CIA++'));
+$graph->addEdge(array('CIA' => 'DIA'));
+$graph->addEdge(array('SBCS' => 'Configuration Mgt'));
+$graph->addEdge(array('PAX' => 'SHIP'));
+$graph->addEdge(array('ksh-88' => 'Configuration Mgt'));
+$graph->addEdge(array('ksh-88' => 'Architecture & Libraries'));
+$graph->addEdge(array('ksh-88' => 'sfio'));
+$graph->addEdge(array('PEGASUS/PML' => 'ML-X'));
+$graph->addEdge(array('PEGASUS/PML' => 'Architecture & Libraries'));
+$graph->addEdge(array('backtalk' => 'DataShare'));
+
+$graph->addEdge(array('CIA++' => 'Software IS'));
+$graph->addEdge(array('APP' => 'DIA'));
+$graph->addEdge(array('APP' => 'Software IS'));
+$graph->addEdge(array('SHIP' => 'Configuration Mgt'));
+$graph->addEdge(array('DataShare' => 'Architecture & Libraries'));
+$graph->addEdge(array('ryacc' => 'kyacc'));
+$graph->addEdge(array('Mosaic' => 'Process'));
+
+$graph->addEdge(array('DOT' => 'Software IS'));
+$graph->addEdge(array('DIA' => 'Software IS'));
+$graph->addEdge(array('libft' => 'Software IS'));
+$graph->addEdge(array('CoShell' => 'Configuration Mgt'));
+$graph->addEdge(array('CoShell' => 'Architecture & Libraries'));
+$graph->addEdge(array('sfio' => 'Architecture & Libraries'));
+$graph->addEdge(array('IFS-i' => 'Architecture & Libraries'));
+$graph->addEdge(array('ML-X' => 'Architecture & Libraries'));
+$graph->addEdge(array('kyacc' => 'Architecture & Libraries'));
+$graph->addEdge(array('yeast' => 'Process'));
+
+$graph->addEdge(array('Architecture & Libraries' => 'Adv. Software Technology'));
+$graph->addEdge(array('Software IS' => 'Adv. Software Technology'));
+$graph->addEdge(array('Configuration Mgt' => 'Adv. Software Technology'));
+$graph->addEdge(array('Process' => 'Adv. Software Technology'));
+
+echo $graph->parse();
+
+?>
+--EXPECT--
+digraph asde91 {
+ ranksep=0.75;
+ subgraph type {
+ graph [ same,rank=same ];
+ "Software IS";
+ "Configuration Mgt";
+ "Architecture & Libraries";
+ Process;
+ }
+ subgraph past {
+ graph [ same,rank=same ];
+ "Bourne sh";
+ make;
+ SCCS;
+ yacc;
+ cron;
+ }
+ subgraph 1978 {
+ graph [ same,rank=same ];
+ "Reiser cpp";
+ Cshell;
+ }
+ subgraph 1980 {
+ graph [ same,rank=same ];
+ emacs;
+ build;
+ vi;
+ }
+ subgraph 1982 {
+ graph [ same,rank=same ];
+ "<curses>";
+ RCS;
+ IMX;
+ SYNED;
+ }
+ subgraph 1983 {
+ graph [ same,rank=same ];
+ ksh;
+ IFS;
+ TTU;
+ }
+ subgraph 1985 {
+ graph [ same,rank=same ];
+ nmake;
+ Peggy;
+ }
+ subgraph 1986 {
+ graph [ same,rank=same ];
+ ncpp;
+ "ksh-i";
+ "<curses-i>";
+ PG2;
+ "C*";
+ }
+ subgraph 1987 {
+ graph [ same,rank=same ];
+ "Ansi cpp";
+ "nmake 2.0";
+ "3D File System";
+ fdelta;
+ DAG;
+ CSAS;
+ }
+ subgraph 1988 {
+ graph [ same,rank=same ];
+ CIA;
+ SBCS;
+ "ksh-88";
+ "PEGASUS/PML";
+ PAX;
+ backtalk;
+ }
+ subgraph 1989 {
+ graph [ same,rank=same ];
+ "CIA++";
+ APP;
+ SHIP;
+ DataShare;
+ ryacc;
+ Mosaic;
+ }
+ subgraph 1990 {
+ graph [ same,rank=same ];
+ libft;
+ CoShell;
+ DIA;
+ "IFS-i";
+ kyacc;
+ sfio;
+ yeast;
+ "ML-X";
+ DOT;
+ }
+ subgraph future {
+ graph [ same,rank=same ];
+ "Adv. Software Technology";
+ }
+ past -> 1978;
+ 1978 -> 1980;
+ 1980 -> 1982;
+ 1982 -> 1983;
+ 1983 -> 1985;
+ 1985 -> 1986;
+ 1986 -> 1987;
+ 1987 -> 1988;
+ 1988 -> 1989;
+ 1989 -> 1990;
+ 1990 -> future;
+ SCCS -> RCS;
+ SCCS -> "3D File System";
+ SCCS -> nmake;
+ make -> nmake;
+ make -> build;
+ "Bourne sh" -> Cshell;
+ "Bourne sh" -> ksh;
+ yacc -> ryacc;
+ cron -> yeast;
+ "Reiser cpp" -> ncpp;
+ Cshell -> ksh;
+ build -> "nmake 2.0";
+ vi -> ksh;
+ vi -> "<curses>";
+ emacs -> ksh;
+ RCS -> SBCS;
+ RCS -> fdelta;
+ "<curses>" -> "<curses-i>";
+ SYNED -> Peggy;
+ IMX -> TTU;
+ ksh -> nmake;
+ ksh -> "ksh-i";
+ ksh -> "ksh-88";
+ IFS -> "<curses-i>";
+ IFS -> sfio;
+ IFS -> "IFS-i";
+ TTU -> PG2;
+ nmake -> ksh;
+ nmake -> ncpp;
+ nmake -> "3D File System";
+ nmake -> "nmake 2.0";
+ Peggy -> "PEGASUS/PML";
+ Peggy -> ryacc;
+ "C*" -> CSAS;
+ ncpp -> "Ansi cpp";
+ "<curses-i>" -> fdelta;
+ "ksh-i" -> "ksh-88";
+ PG2 -> backtalk;
+ DAG -> "Sotware IS";
+ DAG -> DOT;
+ DAG -> DIA;
+ CSAS -> CIA;
+ "Ansi cpp" -> "Configuration Mgt";
+ fdelta -> SBCS;
+ fdelta -> PAX;
+ "3D File System" -> "Configuration Mgt";
+ "nmake 2.0" -> "Configuration Mgt";
+ "nmake 2.0" -> CoShell;
+ CIA -> "CIA++";
+ CIA -> DIA;
+ SBCS -> "Configuration Mgt";
+ PAX -> SHIP;
+ "ksh-88" -> "Configuration Mgt";
+ "ksh-88" -> "Architecture & Libraries";
+ "ksh-88" -> sfio;
+ "PEGASUS/PML" -> "ML-X";
+ "PEGASUS/PML" -> "Architecture & Libraries";
+ backtalk -> DataShare;
+ "CIA++" -> "Software IS";
+ APP -> DIA;
+ APP -> "Software IS";
+ SHIP -> "Configuration Mgt";
+ DataShare -> "Architecture & Libraries";
+ ryacc -> kyacc;
+ Mosaic -> Process;
+ DOT -> "Software IS";
+ DIA -> "Software IS";
+ libft -> "Software IS";
+ CoShell -> "Configuration Mgt";
+ CoShell -> "Architecture & Libraries";
+ sfio -> "Architecture & Libraries";
+ "IFS-i" -> "Architecture & Libraries";
+ "ML-X" -> "Architecture & Libraries";
+ kyacc -> "Architecture & Libraries";
+ yeast -> Process;
+ "Architecture & Libraries" -> "Adv. Software Technology";
+ "Software IS" -> "Adv. Software Technology";
+ "Configuration Mgt" -> "Adv. Software Technology";
+ Process -> "Adv. Software Technology";
+}
Please sign in to comment.
Something went wrong with that request. Please try again.