first commit

This commit is contained in:
douboer
2025-09-17 16:08:16 +08:00
parent 9395faa6b2
commit 3ff47c11d5
1318 changed files with 117477 additions and 0 deletions

View File

@@ -0,0 +1,116 @@
#!/usr/bin/perl -w
# test anonymous nodes
use Test::More;
use strict;
BEGIN
{
plan tests => 31;
chdir 't' if -d 't';
use lib '../lib';
use_ok ("Graph::Easy::Node::Anon") or die($@);
use_ok ("Graph::Easy") or die($@);
use_ok ("Graph::Easy::As_txt") or die($@);
require_ok ("Graph::Easy::As_ascii") or die($@);
};
can_ok ("Graph::Easy::Node::Anon", qw/
new
as_txt as_html
error
class
name
successors
predecessors
width
height
pos
x
y
class
del_attribute
set_attribute
set_attributes
attribute
attributes_as_txt
as_pure_txt
group add_to_group
/);
#############################################################################
my $node = Graph::Easy::Node::Anon->new();
is (ref($node), 'Graph::Easy::Node::Anon');
is ($node->error(), '', 'no error yet');
is ($node->x(), undef, 'x == undef');
is ($node->y(), undef, 'y == undef');
is ($node->width(), undef, 'w == undef');
is ($node->height(), undef, 'h == undef');
is ($node->label(), ' ', 'label');
is ($node->name(), '#0', 'name');
is ($node->title(), '', 'no title per default');
is (join(",", $node->pos()), "0,0", 'pos = 0,0');
is ($node->{graph}, undef, 'no graph');
is (scalar $node->successors(), undef, 'no outgoing links');
is (scalar $node->predecessors(), undef, 'no incoming links');
is ($node->{graph}, undef, 'successors/predecssors leave graph alone');
$node->_correct_size();
is ($node->width(), 3, 'w == 3');
is ($node->height(), 3, 'h == 3');
#############################################################################
# as_txt/as_html
my $graph = Graph::Easy->new();
$graph->add_node($node);
is ($node->as_txt(), '[ ]', 'anon as_txt');
is ($node->as_html(), " <td colspan=4 rowspan=4 class='node_anon'></td>\n",
'as_html');
is ($node->as_ascii(), " \n \n ", 'anon as_ascii');
require Graph::Easy::As_graphviz;
is ($node->as_graphviz_txt(), '"#0"', 'anon as_graphviz');
#############################################################################
# anon node as_graphviz
my $grviz = $graph->as_graphviz();
my $match = quotemeta('"#0" [ color="#ffffff", label=" ", style=filled ]');
like ($grviz, qr/$match/, 'anon node');
#############################################################################
# with border attribute
$node->set_attribute('border-style', 'dotted');
is ($node->as_txt(), '[ ] { border: dotted; }', 'anon as_txt');
is ($node->as_html(), " <td colspan=4 rowspan=4 class='node_anon' style=\"border: dotted 1px #000000\"></td>\n",
'as_html');
$grviz = $graph->as_graphviz();
$match = quotemeta('"#0" [ label=" ", style="filled,dotted" ]');
like ($grviz, qr/$match/, 'anon node as graphviz');
#############################################################################
# with fill attribute
$node->set_attribute('fill', 'orange');
is ($node->as_txt(), '[ ] { fill: orange; border: dotted; }', 'anon as_txt');
is ($node->as_html(), " <td colspan=4 rowspan=4 class='node_anon' style=\"background: #ffa500; border: dotted 1px #000000\"></td>\n",
'as_html');

View File

@@ -0,0 +1,83 @@
#!/usr/bin/perl -w
# test anonymous groups
use Test::More;
use strict;
BEGIN
{
plan tests => 15;
chdir 't' if -d 't';
use lib '../lib';
use_ok ("Graph::Easy::Group::Anon") or die($@);
use_ok ("Graph::Easy") or die($@);
use_ok ("Graph::Easy::As_txt") or die($@);
require_ok ("Graph::Easy::As_ascii") or die($@);
};
can_ok ("Graph::Easy::Group::Anon", qw/
new
as_txt as_html
error
class
name
successors
predecessors
width
height
pos
x
y
class
del_attribute
set_attribute
set_attributes
attribute
attributes_as_txt
as_pure_txt
group add_to_group
/);
#############################################################################
my $group = Graph::Easy::Group::Anon->new();
is (ref($group), 'Graph::Easy::Group::Anon');
is ($group->error(), '', 'no error yet');
is ($group->label(), '', 'label');
is ($group->name(), 'Group #0', 'name');
is ($group->title(), '', 'no title per default');
is ($group->{graph}, undef, 'no graph');
is (scalar $group->successors(), undef, 'no outgoing links');
is (scalar $group->predecessors(), undef, 'no incoming links');
is ($group->{graph}, undef, 'successors/predecssors leave graph alone');
#############################################################################
# as_txt/as_html
my $graph = Graph::Easy->new();
$graph->add_group($group);
is ($group->as_txt(), "( )\n\n", 'anon group as_txt');
#is ($group->as_html(), " <td colspan=4 rowspan=4 class='node_anon'></td>\n",
# 'as_html');
#is ($group->as_ascii(), "", 'anon as_ascii');
#is ($group->as_graphviz_txt(), '"\#0"', 'anon as_graphviz');
#############################################################################
# anon node as_graphviz
#my $grviz = $graph->as_graphviz();
#my $match = quotemeta('"\#0" [ color="#ffffff", label=" ", style=filled ]');
#like ($grviz, qr/$match/, 'anon node');

View File

@@ -0,0 +1,36 @@
#!/usr/bin/perl -w
use Test::More;
use strict;
BEGIN
{
plan tests => 4;
chdir 't' if -d 't';
use lib '../lib';
use_ok ("Graph::Easy") or die($@);
};
can_ok ("Graph::Easy", qw/
as_txt
/);
#############################################################################
# as_txt
use Graph::Easy::Parser;
my $parser = Graph::Easy::Parser->new();
my $graph = $parser->from_text(
"[A] { link: http://foo.com; color: red; origin: B; offset: 2,1; }"
);
is ($parser->error(), '', 'no parsing error' );
is ($graph->as_txt(), <<EOF
[ A ] { color: red; link: http://foo.com; offset: 2,1; origin: B; }
[ B ]
EOF
, 'as_txt with offset and origin');

View File

@@ -0,0 +1,54 @@
#!/usr/bin/perl -w
# Some basic as_vcg tests
use Test::More;
use strict;
BEGIN
{
plan tests => 7;
chdir 't' if -d 't';
use lib '../lib';
use_ok ("Graph::Easy") or die($@);
use_ok ("Graph::Easy::Parser") or die($@);
};
can_ok ('Graph::Easy', qw/
as_vcg
as_vcg_file
/);
#############################################################################
my $graph = Graph::Easy->new();
my $vcg = $graph->as_vcg();
my $vcg_file = $graph->as_vcg_file();
# remove time stamp:
$vcg =~ s/ at.*//;
$vcg_file =~ s/ at.*//;
is ($vcg, $vcg_file, 'as_vcg and as_vcg_file are equal');
$graph->add_edge('A','B');
like ($graph->as_vcg(), qr/edge: \{ sourcename: "A" targetname: "B" \}/,
'as_vcg matches');
# set edge label
my @edges = $graph->edges();
$edges[0]->set_attribute('label', 'my car');
like ($graph->as_vcg(),
qr/edge: \{ label: "my car" sourcename: "A" targetname: "B" \}/,
'as_vcg matches');
#############################################################################
# graph label
$graph = Graph::Easy->new();
$graph->set_attribute('label', 'my graph label');
like ($graph->as_vcg(), qr/title: "my graph label"/,
'as_vcg has graph label');

View File

@@ -0,0 +1,164 @@
#!/usr/bin/perl -w
use Test::More;
use strict;
sub _write_utf8_file
{
my ($out_path, $contents) = @_;
open my $out_fh, '>:encoding(utf8)', $out_path
or die "Cannot open '$out_path' for writing - $!";
print {$out_fh} $contents;
close($out_fh);
return;
}
# test text file input => ASCII output, and back to as_txt() again
BEGIN
{
plan tests => 451;
# TEST
use_ok ("Graph::Easy") or die($@);
# TEST
use_ok ("Graph::Easy::Parser") or die($@);
};
#############################################################################
# parser object
my $parser = Graph::Easy::Parser->new( debug => 0);
is (ref($parser), 'Graph::Easy::Parser');
is ($parser->error(), '', 'no error yet');
opendir DIR, "t/in" or die ("Cannot read dir 'in': $!");
my @files = readdir(DIR); closedir(DIR);
my @failures;
eval { require Test::Differences; };
binmode (STDERR, ':utf8') or die ("Cannot do binmode(':utf8') on STDERR: $!");
binmode (STDOUT, ':utf8') or die ("Cannot do binmode(':utf8') on STDOUT: $!");
foreach my $f (sort @files)
{
my $path = "t/in/$f";
next unless -f $path; # only files
next unless $f =~ /\.txt/; # ignore anything else
print "# at $f\n";
my $txt = readfile($path);
my $graph = $parser->from_text($txt); # reuse parser object
$txt =~ s/\n\s+\z/\n/; # remove trailing whitespace
$txt =~ s/(^|\n)\s*#[^#]{2}.*\n//g; # remove comments
$f =~ /^(\d+)/;
my $nodes = $1;
if (!defined $graph)
{
warn ("Graph input was invalid: " . $parser->error());
push @failures, $f;
next;
}
is (scalar $graph->nodes(), $nodes, "$nodes nodes");
# for slow testing machines
$graph->timeout(20);
my $ascii = $graph->as_ascii();
my $out_path = "t/out/$f";
my $out = readfile($out_path);
$out =~ s/(^|\n)\s*#[^#=]{2}.*\n//g; # remove comments
$out =~ s/\n\n\z/\n/mg; # remove empty lines
# print "txt: $txt\n";
# print "ascii: $ascii\n";
# print "should: $out\n";
if (!
is ($ascii, $out, "from $f"))
{
if ($ENV{__SHLOMIF__UPDATE_ME})
{
_write_utf8_file($out_path, $ascii);
}
push @failures, $f;
if (defined $Test::Differences::VERSION)
{
Test::Differences::eq_or_diff ($ascii, $out);
}
else
{
fail ("Test::Differences not installed");
}
}
my $txt_path = "t/txt/$f";
# if the txt output differes, read it in
if (-f $txt_path)
{
$txt = readfile($txt_path);
}
# else
# {
# # input might have whitespace at front, remove it because output doesn't
# $txt =~ s/(^|\n)\x20+/$1/g;
# }
if (!
is ($graph->as_txt(), $txt, "$f as_txt"))
{
if ($ENV{__SHLOMIF__UPDATE_ME})
{
_write_utf8_file($txt_path, scalar( $graph->as_txt() ));
}
push @failures, $f;
if (defined $Test::Differences::VERSION)
{
Test::Differences::eq_or_diff ($graph->as_txt(), $txt);
}
else
{
fail ("Test::Differences not installed");
}
}
# print a debug output
my $debug = $ascii;
$debug =~ s/\n/\n# /g;
print "# Generated:\n#\n# $debug\n";
}
if (@failures)
{
print "# !!! Failed the following tests:\n";
for my $f (@failures)
{
print "# $f\n";
}
print "# !!!\n\n";
}
1;
sub readfile
{
my ($filename) = @_;
open my $fh, $filename or die ("Cannot read file ${filename}: $!");
binmode ($fh, ':utf8') or die ("Cannot do binmode(':utf8') on ${fh}: $!");
local $/ = undef; # slurp mode
my $doc = <$fh>;
close $fh;
$doc;
}

View File

@@ -0,0 +1,168 @@
#!/usr/bin/perl -w
use Test::More;
use strict;
BEGIN
{
plan tests => 21;
chdir 't' if -d 't';
use lib '../lib';
use_ok ("Graph::Easy") or die($@);
};
can_ok ("Graph::Easy", qw/
new
_find_path_astar
_astar_distance
_astar_modifier
_astar_edge_type
/);
can_ok ("Graph::Easy::Heap", qw/
new
extract_top
add
/);
use Graph::Easy::Edge::Cell qw/
EDGE_N_E EDGE_N_W EDGE_S_E EDGE_S_W
EDGE_HOR EDGE_VER
/;
#############################################################################
# _distance tests
my $dis = 'Graph::Easy::_astar_distance';
my $mod = 'Graph::Easy::_astar_modifier';
my $typ = 'Graph::Easy::_astar_edge_type';
{ no strict 'refs';
is (&$dis( 0,0, 3,0 ), 3 + 0 + 0, '0,0 => 3,0: 4 (no corner)');
is (&$dis( 3,0, 3,5 ), 0 + 5 + 0, '3,0 => 3,5: 5 (no corner)');
is (&$dis( 0,0, 3,5 ), 3 + 5 + 1, '0,0 => 3,5: 3+5+1 (one corner)');
is (&$mod( 0,0 ), 1, 'modifier(0,0) is 1');
is (&$mod( 0,0, 1,0, 0,1 ), 7, 'going round a bend is 7');
is (&$mod( 0,0, 1,0, -1,0 ), 1, 'going straight is 1');
is (&$typ( 0,0, 1,0, 2,0 ), EDGE_HOR, 'EDGE_HOR');
is (&$typ( 2,0, 3,0, 4,0 ), EDGE_HOR, 'EDGE_HOR');
is (&$typ( 4,0, 3,0, 2,0 ), EDGE_HOR, 'EDGE_HOR');
is (&$typ( 2,0, 2,1, 2,2 ), EDGE_VER, 'EDGE_VER');
is (&$typ( 2,2, 2,3, 2,4 ), EDGE_VER, 'EDGE_VER');
is (&$typ( 2,2, 2,1, 2,0 ), EDGE_VER, 'EDGE_VER');
is (&$typ( 0,0, 1,0, 1,1 ), EDGE_S_W, 'EDGE_S_W');
is (&$typ( 1,1, 1,0, 0,0 ), EDGE_S_W, 'EDGE_S_W');
is (&$typ( 1,1, 1,0, 2,0 ), EDGE_S_E, 'EDGE_S_E');
is (&$typ( 2,0, 1,0, 1,1 ), EDGE_S_E, 'EDGE_S_E');
is (&$typ( 0,0, 1,0, 1,-1 ), EDGE_N_W, 'EDGE_N_W');
is (&$typ( 1,-1, 1,0, 0,0 ), EDGE_N_W, 'EDGE_N_W');
#print &$typ( 1,2, 2,2, 2,1),"\n";
#print &$typ( 0,2, 1,2, 2,2),"\n";
#print &$typ( 0,1, 0,2, 1,2),"\n";
}
exit;
#############################################################################
# path finding tests
my $graph = Graph::Easy->new();
is (ref($graph), 'Graph::Easy');
is ($graph->error(), '', 'no error yet');
my $node = Graph::Easy::Node->new( name => 'Bonn' );
my $node2 = Graph::Easy::Node->new( name => 'Berlin' );
my $cells = {};
place($cells, $node, 0, 0);
place($cells, $node2, 3, 0);
#my $path = $graph->_find_path_astar( $cells, $node, $node2 );
#is_deeply ($path, [ 0,0, 1,0, 2,0, 3,0 ], '0,0 => 1,0 => 2,0 => 3,0');
place($cells, $node, 0, 0);
place($cells, $node2, 3, 1);
#$path = $graph->_find_path_astar( $cells, $node, $node2 );
#is_deeply ($path, [ 0,0, 1,0, 2,0, 3,0, 3,1 ], '0,0 => 1,0 => 2,0 => 3,0 => 3,1');
$cells = {};
place($cells, $node, 5, 7);
$node2->{cx} = 2;
$node2->{cy} = 2;
place($cells, $node2, 14, 14);
block ($cells,13,14);
block ($cells,14,13);
block ($cells,13,15);
block ($cells,15,13);
block ($cells,14,16);
block ($cells,16,14);
#block ($cells,3,11);
#block ($cells,3,10);
#block ($cells,4,9);
#block ($cells,5,9);
#block ($cells,5,11);
#block ($cells,5,13);
#for (5..15)
# {
# block ($cells,15,$_);
# block ($cells,$_,5);
# block ($cells,$_,15);
# }
#block ($cells,15,16);
#block ($cells,14,17);
#block ($cells,3,16);
$graph->{cells} = $cells;
$graph->{_astar_bias} = 0;
my ($p, $closed, $open) = $graph->_find_path_astar($node, $node2 );
#use Data::Dumper; print Dumper($cells);
open FILE, ">test.html" or die ("Cannot write test.html: $!");
print FILE $graph->_map_as_html($cells, $p, $closed, $open);
close FILE;
sub block
{
my ($cells, $x,$y) = @_;
$cells->{"$x,$y"} = 1;
}
sub place
{
my ($cells, $node,$x,$y) = @_;
my $c = ($node->{cx} || 1) - 1;
my $r = ($node->{cy} || 1) - 1;
$node->{x} = $x; $node->{y} = $y;
for my $rr (0..$r)
{
my $cy = $y + $rr;
for my $cc (0..$c)
{
my $cx = $x + $cc;
$cells->{"$cx,$cy"} = $node;
}
}
diag ("Placing $node->{name} at $node->{x},$node->{y}\n");
}

View File

@@ -0,0 +1,379 @@
#!/usr/bin/perl -w
# Test the attribute system, especially getting, setting attributes
# on objects and classes:
use Test::More;
use strict;
BEGIN
{
plan tests => 123;
chdir 't' if -d 't';
use lib '../lib';
use_ok ("Graph::Easy::Attributes") or die($@);
use_ok ("Graph::Easy") or die($@);
};
can_ok ("Graph::Easy", qw/
color_as_hex
color_name
color_value
_remap_attributes
valid_attribute
get_custom_attributes
raw_attributes
custom_attributes
/);
can_ok ("Graph::Easy::Node", qw/
get_custom_attributes
raw_attributes
set_attribute
get_attribute
custom_attributes
/);
can_ok ("Graph::Easy::Edge", qw/
get_custom_attributes
raw_attributes
set_attribute
get_attribute
custom_attributes
/);
can_ok ("Graph::Easy::Group", qw/
get_custom_attributes
raw_attributes
set_attribute
get_attribute
custom_attributes
/);
#############################################################################
# color_as_hex:
my $att = 'Graph::Easy';
is ($att->color_as_hex( 'red' ), '#ff0000', 'color red');
is ($att->color_as_hex( '#0000ff' ), '#0000ff', 'color #0000ff');
is ($att->color_as_hex( '#f0c' ), '#ff00cc', 'color #f0c');
is ($att->color_as_hex( 'rgb(128,255,0)' ), '#80ff00', 'color rgb(128,255,0)');
is ($att->color_as_hex('lavender'), '#e6e6fa', 'color lavender');
is ($att->color_as_hex('lavenderblush'), '#fff0f5', 'color lavenderblush');
is ($att->color_as_hex('lavenderbush'), undef, 'color lavenderbush does not exist');
#############################################################################
# color_name:
is ($att->color_name('red'), 'red', 'red => red');
is ($att->color_name('#ff0000'), 'red', '#ff0000 => red');
is ($att->color_name('#ffffff'), 'white', '#ffffff => white');
is ($att->color_name('#808080'), 'gray', '#808080 => gray');
#############################################################################
# color scheme support:
is ($att->color_name('grey', 'x11'), 'grey', 'grey => grey');
is ($att->color_name('#c0c0c0','x11'), 'gray', '#c0c0c0 => gray');
is ($att->color_name('#ffffff','x11'), 'white', '#ffffff => white');
is ($att->color_name('grey23','x11'), 'grey23', 'grey23 => grey23');
# 1 => '#ca0020', 2 => '#f4a582', 3 => '#bababa', 4 => '#404040',
is ($att->color_name('1','rdgy4'), '1', '1 => 1 under rdgy4');
#############################################################################
# color_value:
is ($att->color_value('red'), '#ff0000', 'red => #ff0000');
is ($att->color_value('grey'), '#808080', 'grey => #808080');
is ($att->color_value('grey','x11'), '#c0c0c0', 'grey => #c0c0c0 under x11');
is ($att->color_value('grey23','x11'), '#3b3b3b', 'grey23 => #3b3b3b under x11');
# 1 => '#ca0020', 2 => '#f4a582', 3 => '#bababa', 4 => '#404040',
is ($att->color_value('1','rdgy4'), '#ca0020', '1 => #ca0020 under rdgy4');
is ($att->color_value('4','rdgy4'), '#404040', '4 => #404040 under rdgy4');
#############################################################################
# valid_attribute:
$att = Graph::Easy->new();
$att->no_fatal_errors(1);
my $new_value = $att->valid_attribute( 'color', 'redbrownish' );
is ($new_value, undef, 'color redbrownish is not valid');
$new_value = $att->valid_attribute( 'fill', 'redbrownish' );
is ($new_value, undef, 'fill redbrownish is not valid');
$new_value = $att->valid_attribute( 'border-shape', 'double' );
is (ref($new_value), 'ARRAY', 'border-shape is not valied');
# no class name: 'all' will be tested
for my $name (
'red','w3c/red','x11/red', 'chocolate4', 'rgb(1,2,3)',
'rgb(10%,1%,2%)', 'rgb(8,1%,0.2)', 'w3c/grey',
)
{
for my $class ( undef, 'node', 'node.subclass')
{
my $new_value = $att->valid_attribute( 'color', $name, $class );
is ($new_value, $name, "color $name is valid");
}
}
#############################################################################
# fallback to color scheme 'x11'
$new_value = $att->valid_attribute( 'color', 'chocolate4' );
is ($new_value, 'chocolate4', 'color chocolate4 is valid');
#############################################################################
# valid_attribute for graph only:
$new_value = $att->valid_attribute( 'gid', '123', 'graph' );
is ($new_value, '123', 'gid 123 is valid for graph');
$new_value = $att->valid_attribute( 'gid', '123', 'node' );
is (ref($new_value), 'ARRAY', 'gid is invalid for nodes');
$new_value = $att->valid_attribute( 'gid', '123', 'edge' );
is (ref($new_value), 'ARRAY', 'gid is invalid for edges');
$new_value = $att->valid_attribute( 'output', 'html', 'graph' );
is ($new_value, 'html', 'output "html" is valid for graph');
$new_value = $att->valid_attribute( 'output', 'html', 'node' );
is (ref($new_value), 'ARRAY', 'output is invalid for nodes');
$new_value = $att->valid_attribute( 'output', 'html', 'edge' );
is (ref($new_value), 'ARRAY', 'output is invalid for edges');
#############################################################################
# setting attributes on graphs, nodes and edges
my $graph = Graph::Easy->new();
$graph->no_fatal_errors(1);
my ($n,$m,$e) = $graph->add_edge('A','B');
$n->set_attribute('color','red');
is ($graph->error(),'','no error');
$graph->error(''); # reset potential error for next test
$n->set_attribute('shape','point');
is ($graph->error(),'','no error');
$graph->error(''); # reset potential error for next test
$graph->set_attribute('graph', 'shape', 'point');
is ($graph->error(),"Error in attribute: 'shape' is not a valid attribute name for a graph",
'shape is not a valid attribute');
$graph->error(''); # reset potential error for next test
$e->no_fatal_errors(1);
$e->set_attribute('shape','point');
is ($graph->error(),"Error in attribute: 'shape' is not a valid attribute name for a edge",
'shape is not a valid attribute');
$graph->error(''); # reset potential error for next test
#############################################################################
# Setting an attribute on the graph directly is the same as setting it on
# the class 'graph':
$graph->set_attribute('graph', 'flow', 'south');
is ($graph->attribute('flow'), 'south', 'flow was set to south');
$graph->set_attribute('flow', 'west');
is ($graph->attribute('flow'), 'west', 'flow was set to south');
is ($graph->attribute('label-pos'), 'top', 'label-pos defaults to top');
is ($graph->attribute('labelpos'), 'top', 'label-pos defaults to top');
$graph->set_attribute('graph', 'label-pos', 'bottom');
is ($graph->attribute('label-pos'), 'bottom', 'label-pos was set to bottom');
is ($graph->attribute('labelpos'), 'bottom', 'label-pos was set to bottom');
$graph->del_attribute('label-pos');
is ($graph->attribute('label-pos'), 'top', 'label-pos defaults to top');
is ($graph->attribute('labelpos'), 'top', 'label-pos defaults to top');
$graph->set_attribute('graph', 'labelpos', 'bottom');
is ($graph->attribute('label-pos'), 'bottom', 'label-pos was set to bottom');
is ($graph->attribute('labelpos'), 'bottom', 'label-pos was set to bottom');
#############################################################################
# text-style attribute
for my $class (qw/edge graph node group/)
{
$graph->set_attribute($class, 'text-style', 'none');
is ($graph->error(), '', "could set text-style on $class");
$graph->error(''); # reset potential error for next test
$graph->set_attribute($class, 'text-style', 'bold');
is ($graph->error(), '', "could set text-style on $class");
$graph->error(''); # reset potential error for next test
$graph->set_attribute($class, 'text-style', 'bold underline');
is ($graph->error(), '', "could set text-style on $class");
$graph->error(''); # reset potential error for next test
$graph->set_attribute($class, 'text-style', 'bold underline overline italic');
is ($graph->error(), '', "could set text-style on $class");
$graph->error(''); # reset potential error for next test
}
$graph->set_attribute('graph', 'text-style', 'bold underline overline italic');
my $styles = $graph->text_styles();
is (join(',', sort keys %$styles), 'bold,italic,overline,underline', 'text_styles()');
my $node = $graph->add_node('one');
$node->set_attribute('text-style', 'bold underline overline italic');
$styles = $node->text_styles();
is (join(',', sort keys %$styles), 'bold,italic,overline,underline', 'text_styles() on node');
#############################################################################
# border-style vs. borderstyle
$graph = Graph::Easy->new();
$graph->no_fatal_errors(1);
($n,$m,$e) = $graph->add_edge('A','B');
is ($n->attribute('border-style'),'solid', 'border-style is solid');
is ($n->attribute('borderstyle'),'solid', 'borderstyle is solid');
$n->set_attribute('border-style','dashed');
is ($n->attribute('border-style'),'dashed', 'border-style is now dashed');
is ($n->attribute('borderstyle'),'dashed', 'border-style is now dashed');
#############################################################################
# inheritance of values ('inherit')
$graph = Graph::Easy->new();
($n,$m,$e) = $graph->add_edge('A','B');
$graph->set_attribute('node', 'color', 'red');
$graph->set_attribute('color', 'green');
$n->set_attribute('color', 'inherit');
$n->set_attribute('class', 'foo');
is ($n->attribute('class'), 'foo', 'get_attribute("class") works');
# N inherits from class "node"
is ($n->attribute('color'),'red', 'inherited red from class "node"');
is ($m->attribute('color'),'red', 'inherited red from class "node"');
$graph->set_attribute('node', 'color', 'inherit');
is ($n->attribute('color'),'green', 'inherited green from graph');
is ($m->attribute('color'),'green', 'inherited green from graph');
$m->set_attribute('color', 'blue');
is ($m->attribute('color'),'blue', 'got blue');
#############################################################################
# raw_attribute() and get_raw_attributes()
$graph = Graph::Easy->new();
($n,$m,$e) = $graph->add_edge('A','B');
$graph->set_attribute('node', 'color', 'red');
$graph->set_attribute('color', 'green');
$n->set_attribute('color', 'inherit');
$n->set_attribute('class', 'foo');
$m->set_attribute('color', 'blue');
# N inherits from class "node"
is ($n->raw_attribute('fill'), undef, 'attribute fill not set');
is ($n->raw_attribute('color'), 'red',
'attribute color set to inherit, so we inherit red');
is ($graph->raw_attribute('fill'), undef, 'attribute fill not set on graph');
is ($graph->raw_attribute('color'), 'green',
'attribute color set to green on graph');
is ($m->raw_attribute('color'), 'blue',
'attribute color set to blue on node B');
is ($m->raw_attribute('fill'), undef,
'attribute fill not set on node m');
my $str = _att_to_str($n->raw_attributes());
is ($str, 'color=>red;', 'node A has only color set');
$str = _att_to_str($m->raw_attributes());
is ($str, 'color=>blue;', 'node B has only color set');
$str = _att_to_str($graph->raw_attributes());
is ($str, 'color=>green;', 'graph has only color set');
$str = _att_to_str($e->raw_attributes());
is ($str, '', 'edge has no attributes set');
#############################################################################
# virtual attribute 'class'
$graph = Graph::Easy->new();
($n,$m,$e) = $graph->add_edge('Bonn','Berlin');
is ($graph->attribute('class'), '', 'class graph');
is ($n->attribute('class'), '', 'class node');
is ($e->attribute('class'), '', 'class edge');
$n->set_attribute('class', 'anon');
is ($n->attribute('class'), 'anon', 'class anon for node Bonn');
$e->set_attribute('class', 'foo');
is ($e->attribute('class'), 'foo', 'class foo for edge');
#############################################################################
# attribute 'link'
$graph = Graph::Easy->new();
($n,$m,$e) = $graph->add_edge('Bonn','Berlin');
$n->set_attribute('autolink','name');
# default linkbase + autolink from name
is ($n->link(), '/wiki/index.php/Bonn', "link() for 'Bonn'");
is ($graph->link(), '', "no link on graph");
$graph->set_attribute('autolink','name');
# graph doesn't have a name => no link
is ($graph->link(), '', "link() is 'Bonn'");
$graph->set_attribute('link','Berlin');
# default linkbase + link
is ($graph->link(), '/wiki/index.php/Berlin', "link() for graph");
1;
#############################################################################
sub _att_to_str
{
my $out = shift;
my $str = '';
for my $k (sort keys %$out)
{
$str .= $k . '=>' . $out->{$k} . ';';
}
$str;
}

View File

@@ -0,0 +1,37 @@
#!/usr/bin/perl -w
use Test::More;
use strict;
BEGIN
{
plan tests => 6;
chdir 't' if -d 't';
use lib '../lib';
use_ok ("Graph::Easy::Base") or die($@);
};
can_ok ("Graph::Easy::Base", qw/
new error error_as_html
_init
self
class
sub_class
main_class
fatal_errors
no_fatal_errors
/);
#############################################################################
# Base tests
my $base = Graph::Easy::Base->new();
is (ref($base), 'Graph::Easy::Base', 'new seemed to work');
is ($base->error(), '', 'no error yet');
$base->{class} = 'group.test';
is ($base->main_class(), 'group', 'main_class works');
is ($base->error(), '', 'no error yet');

View File

@@ -0,0 +1,92 @@
#!/usr/bin/perl -w
use Test::More;
use strict;
BEGIN
{
plan tests => 15;
chdir 't' if -d 't';
use lib '../lib';
use_ok ("Graph::Easy") or die($@);
use_ok ("Graph::Easy::As_ascii") or die($@);
};
can_ok ("Graph::Easy", qw/
as_boxart
as_boxart_html
as_boxart_html_file
as_boxart_file
/);
#############################################################################
binmode STDERR, ':utf8';
# some of our strings are written in utf8
use utf8;
my $graph = Graph::Easy->new();
my ($bonn, $berlin, $edge) = $graph->add_edge ('Bonn', 'Berlin');
my $boxart = $graph->as_boxart();
like ($boxart, qr/Bonn/, 'contains Bonn');
like ($boxart, qr/Berlin/, 'contains Berlin');
unlike ($boxart, qr/-/, "doesn't contain '-'");
#############################################################################
# border tests
$berlin->set_attribute('border-style', 'dotted');
#############################################################################
# arrow tests
my $open = '──>';
my $closed = '──▷';
my $filled = '──▶';
$boxart = $graph->as_boxart();
like ($boxart, qr/$open/, 'contains edge with open arrow');
$edge->set_attribute('arrow-style', 'open');
$boxart = $graph->as_boxart();
like ($boxart, qr/$open/, 'contains edge with open arrow');
$edge->set_attribute('arrow-style', 'closed');
$boxart = $graph->as_boxart();
like ($boxart, qr/$closed/, 'contains edge with closed arrow');
$edge->set_attribute('arrow-style', 'filled');
$boxart = $graph->as_boxart();
like ($boxart, qr/$filled/, 'contains edge with filled arrow');
#############################################################################
# arrow tests with dotted lines
$open = "··>";
$closed = '··▷';
$filled = '··▶';
$edge->set_attribute('style', 'dotted');
$edge->del_attribute('arrow-style');
is ($edge->style(), 'dotted', 'edge is now dotted');
$boxart = $graph->as_boxart();
like ($boxart, qr/$open/, 'contains edge with open arrow');
$edge->set_attribute('arrow-style', 'open');
$boxart = $graph->as_boxart();
like ($boxart, qr/$open/, 'contains edge with open arrow');
$edge->set_attribute('arrow-style', 'closed');
$boxart = $graph->as_boxart();
like ($boxart, qr/$closed/, 'contains edge with closed arrow');
$edge->set_attribute('arrow-style', 'filled');
$boxart = $graph->as_boxart();
like ($boxart, qr/$filled/, 'contains edge with filled arrow');

View File

@@ -0,0 +1,120 @@
#!/usr/bin/perl -w
# Test Graph::Easy::Node::Cell
use Test::More;
use strict;
BEGIN
{
plan tests => 28;
chdir 't' if -d 't';
use lib '../lib';
use_ok ("Graph::Easy::Node::Cell") or die($@);
use_ok ("Graph::Easy") or die($@);
use_ok ("Graph::Easy::As_ascii") or die($@);
};
can_ok ("Graph::Easy::Node::Cell", qw/
new
as_ascii as_html
error
class
name
successors
predecessors
width
height
pos
x
y
class
title
del_attribute
set_attribute
set_attributes
attribute
group add_to_group
/);
#############################################################################
my $cell = Graph::Easy::Node::Cell->new();
is (ref($cell), 'Graph::Easy::Node::Cell');
is ($cell->error(), '', 'no error yet');
is ($cell->x(), 0, 'x == 0');
is ($cell->y(), 0, 'x == 0');
is ($cell->label(), '', 'label');
is ($cell->name(), '', 'name');
is ($cell->title(), '', 'no title per default');
is (join(",", $cell->pos()), "0,0", 'pos = 0,0');
is ($cell->width(), undef, 'w == undef');
is ($cell->height(), undef, 'h == undef');
is ($cell->class(), '', 'no class');
#############################################################################
# as_ascii/as_html
is ($cell->as_ascii(), '', 'as_ascii');
is ($cell->as_html(), '', 'as_html');
$cell->_correct_size();
is ($cell->width(), 0, 'w = 0');
is ($cell->height(), 0, 'h = 0');
#############################################################################
# group tests
use Graph::Easy::Group;
my $group = Graph::Easy::Group->new( { name => 'foo' } );
# fake that the cell belongs as filler to a node
my $node = Graph::Easy::Node->new( 'foo' );
$cell->{node} = $node;
is ($cell->node(), $node, 'node for cell');
is ($cell->group(), undef, 'no group yet');
$node->add_to_group($group);
is ($cell->node(), $node, 'node for cell');
is ($cell->group(), $group, 'group foo');
#############################################################################
# title tests
$cell->set_attribute('title', "foo title");
is ($cell->title(), 'foo title', 'foo title');
$cell->del_attribute('title');
$cell->set_attribute('autotitle', 'name');
is ($cell->title(), $cell->name(), 'title equals name');
#############################################################################
# invisible nodes
$node = Graph::Easy::Node->new( { name => "anon 0", label => 'X' } );
$node->set_attribute('shape', "invisible");
is ($node->as_ascii(), "", 'invisible text node');
#############################################################################
# as_txt()
use_ok ('Graph::Easy::As_txt');
can_ok ("Graph::Easy::Node::Cell", qw/
attributes_as_txt
as_txt
as_pure_txt
/);

View File

@@ -0,0 +1,114 @@
#!/usr/bin/perl -w
use Test::More;
use strict;
BEGIN
{
plan tests => 44;
chdir 't' if -d 't';
use lib '../lib';
use_ok ("Graph::Easy::Layout::Chain") or die($@);
use_ok ("Graph::Easy") or die($@);
};
can_ok ("Graph::Easy::Layout::Chain", qw/
new error
length nodes add_node layout
/);
#############################################################################
# chain tests
my $c = 'Graph::Easy::Layout::Chain';
my $graph = Graph::Easy->new();
is (ref($graph), 'Graph::Easy');
is ($graph->error(), '', 'no error yet');
my ($node, $node2) = $graph->add_edge('A','B');
my $chain = Graph::Easy::Layout::Chain->new(
start => $node, graph => $graph );
is (ref($chain), $c, 'new() seemed to work');
is ($chain->error(), '', 'no error');
is ($chain->start(), $node, 'start node is $node');
is ($chain->end(), $node, 'end node is $node');
is ($node->{_chain}, $chain, 'chain stored at node');
is ($chain->length(), 1, 'length() is 1');
is ($chain->length($node), 1, 'length($node) is 1');
$chain->add_node($node2);
is ($node->{_chain}, $chain, 'chain stored at node');
is ($node2->{_chain}, $chain, 'chain stored at node2');
is ($chain->length(), 2, 'length() is now 2');
is ($chain->start(), $node, 'start node is $node');
is ($chain->end(), $node2, 'end node is $node2');
is ($chain->length($node), 2, 'length($node) is 2');
is ($chain->length($node2), 1, 'length($node2) is 1');
#############################################################################
# merging two chains
my ($node3, $node4) = $graph->add_edge('C','D');
my $other = $c->new ( start => $node3, graph => $graph );
is (ref($other), $c, 'new() seemed to work');
is ($other->error(), '', 'no error');
is ($other->length(), 1, 'length() is 1');
is ($other->start(), $node3, 'start node is $node3');
is ($other->end(), $node3, 'end node is $node3');
$other->add_node($node4);
is ($other->length(), 2, 'length() is now 2');
is ($other->start(), $node3, 'start node is $node3');
is ($other->end(), $node4, 'end node is $node4');
#diag ("merging chains\n");
$chain->merge($other);
is ($other->error(), '', 'no error');
is ($other->length(), 0, 'other length() is still 0');
is ($other->start(), undef, 'start node is $node3');
is ($other->end(), undef, 'end node is $node4');
is ($chain->error(), '', 'no error');
is ($chain->length(), 4, 'chain length() is now 4');
is ($chain->start(), $node, 'start node is $node3');
is ($chain->end(), $node4, 'end node is $node4');
my @nodes = $chain->nodes();
is_deeply (\@nodes, [ $node, $node2, $node3, $node4 ], 'nodes got merged');
#############################################################################
# merging two chains, with offset
my ($node5, $node6) = $graph->add_edge('E','F');
$other = $c->new ( start => $node5, graph => $graph );
$other->add_node($node6);
# merge $chain into $other, but keep the first 3 nodes of $chain
$other->merge($chain, $node3);
is ($chain->length(), 4, 'left all four nodes');
is ($other->length(), 4, 'consumed two nodes');
@nodes = $chain->nodes();
is_deeply (\@nodes, [ $node, $node2, $node3, $node4 ], 'nodes got merged');
@nodes = $other->nodes();
is_deeply (\@nodes, [ $node5, $node6, $node3, $node4 ], 'other got two nodes');
for my $node ( @nodes )
{
is ($node->{_chain}, $other, 'node got set to new chain');
}

View File

@@ -0,0 +1,80 @@
#!/usr/bin/perl -w
# Test class selectors
use Test::More;
use strict;
BEGIN
{
plan tests => 23;
chdir 't' if -d 't';
use lib '../lib';
use_ok ("Graph::Easy") or die($@);
};
can_ok ("Graph::Easy", qw/
_check_class
/);
#############################################################################
my $graph = Graph::Easy->new();
is (ref($graph), 'Graph::Easy');
$graph->add_edge( 'A', 'B' );
for my $class ('node', 'edge', 'graph', 'group',
'node.foo', 'edge.foo', 'group.foo')
{
_is ($class, $graph->_check_class($class));
}
_is ('edge.foo,group.foo,node.foo', $graph->_check_class('.foo'));
_is ('edge.b,group.b,node.b', $graph->_check_class('.b'));
#############################################################################
# lists of class selectors
_is ('edge.f,group.f,node.f,edge.b,group.b,node.b',
$graph->_check_class('.f, .b'));
_is ('edge,group,node', $graph->_check_class('edge, group, node'));
_is ('edge,group,node', $graph->_check_class('edge,group, node'));
_is ('edge,group,node', $graph->_check_class('edge , group , node'));
_is ('edge,group,node', $graph->_check_class('edge, group,node'));
_is ('edge,group,node', $graph->_check_class('edge,group,node'));
_is ('edge.red,group.red,node.red,edge.green,group.green,node.green,group',
$graph->_check_class('.red, .green, group'));
#############################################################################
# invalid classes
_is (\'.', $graph->_check_class('.'));
_is (\'node.', $graph->_check_class('node.'));
_is (\'foo', $graph->_check_class('foo'));
_is (\'.foo, bar', $graph->_check_class('.foo, bar'));
# all tests done
1;
#############################################################################
sub _is
{
my ($expect, @results) = @_;
if (ref($expect))
{
is (scalar @results, 0, "invalid selector $$expect");
}
else
{
is (join(",", @results), $expect, $expect);
}
}

View File

@@ -0,0 +1,55 @@
#!/usr/bin/perl -w
use Test::More;
use strict;
BEGIN
{
plan tests => 12;
chdir 't' if -d 't';
use lib '../lib';
use_ok ("Graph::Easy") or die($@);
};
#############################################################################
# basic tests
my $graph = Graph::Easy->new();
my ($first, $second, $edge) = $graph->add_edge('first', 'second');
$second->set_attribute('origin', $first->{name});
is (join(",", $second->offset()), '0,0', 'offset is 0,0');
is ($second->origin(), $first, 'origin is $first');
#############################################################################
# graph tests
# node placement (clustered)
$graph = Graph::Easy->new();
$first = $graph->add_node('A');
$second = $graph->add_node('B');
$second->relative_to($first, 1,0);
is (scalar $graph->nodes(), 2, 'two nodes');
my $cells = { };
my $parent = { cells => $cells };
is ($first->_do_place(1,1,$parent), 1, 'node can be placed');
is ($cells->{"1,1"}, $first, 'first was really placed');
is ($cells->{"2,1"}, $second, 'second node was placed, too');
is (scalar keys %$cells, 2, 'two nodes placed');
# 1,0 and 2,0 are blocked, so 0,0+1,0; 1,0+2,0 and 2,0+3,0 are blocked, too:
is ($first->_do_place(0,1,$parent), 0, 'node cannot be placed again');
is ($first->_do_place(1,1,$parent), 0, 'node cannot be placed again');
is ($first->_do_place(2,1,$parent), 0, 'node cannot be placed again');
is (scalar keys %$cells, 2, 'two nodes placed');

View File

@@ -0,0 +1,140 @@
#!/usr/bin/perl -w
# Test the copy() method
use Test::More;
use strict;
BEGIN
{
plan tests => 55;
chdir 't' if -d 't';
use lib '../lib';
use_ok ("Graph::Easy") or die($@);
};
can_ok ('Graph::Easy', qw/
new
copy
/);
#############################################################################
my $graph = Graph::Easy->new();
check_graph($graph);
my $copy = $graph->copy();
check_graph($copy);
my $bonn = Graph::Easy::Node->new( name => 'Bonn' );
my $berlin = Graph::Easy::Node->new( 'Berlin' );
my $edge = $graph->add_edge ($bonn, $berlin);
my $group = $graph->add_group ('Cities');
is (ref($edge), 'Graph::Easy::Edge', 'add_edge() returns the new edge');
$bonn->set_attribute('color','red');
$edge->set_attribute('fill','blue');
$graph->set_attribute('graph','fill','purple');
check_members($graph);
$copy = $graph->copy();
check_members($copy);
#############################################################################
# settings on the graph object itself
$graph->fatal_errors();
$graph->catch_warnings(1);
$graph->catch_errors(1);
check_settings($graph);
$copy = $graph->copy();
check_settings($copy);
#############################################################################
# groups with nodes
$graph = Graph::Easy->new('( Cities [ Bonn ] -> [ Berlin ] )' );
$copy = $graph->copy();
$group = $graph->group('Cities');
is (scalar $group->nodes(), 2, '2 nodesi in original group');
$group = $copy->group('Cities');
is (scalar $group->nodes(), 2, '2 nodes in copied group');
#############################################################################
#############################################################################
sub check_settings
{
my $graph = shift;
is ($graph->{_catch_warnings}, 1, 'catch warnings');
is ($graph->{_catch_errors}, 1, 'catch errors');
is ($graph->{fatal_errors}, 1, 'fatal errors');
}
sub check_members
{
my $graph = shift;
# use Data::Dumper; print Dumper($graph);
is ($graph->nodes(), 2, '2 nodes added');
is ($graph->edges(), 1, '1 edge');
is ($graph->as_txt(), <<EOF
graph { fill: purple; }
[ Bonn ] { color: red; }
( Cities )
[ Bonn ] --> { fill: blue; } [ Berlin ]
EOF
, 'as_txt for 2 nodes');
is (ref($graph->edge($bonn,$berlin)), 'Graph::Easy::Edge', 'edge from objects');
is ($graph->edge($berlin,$bonn), undef, 'berlin not connecting to bonn');
is (ref($graph->edge('Bonn', 'Berlin')), 'Graph::Easy::Edge', 'edge from names');
my @E = $graph->edges();
my $en = '';
for my $e (@E)
{
$en .= $e->style() . '.';
}
is ($en, 'solid.', 'edges() in list context');
is( $graph->node('Bonn')->attribute('color'),'red', 'Bonn is red');
is( $graph->edge('Bonn','Berlin')->attribute('fill'),'blue', 'Bonn->Berlin is blue');
is( $graph->get_attribute('fill'), 'purple', 'graph is purple');
}
#############################################################################
sub check_graph
{
my $graph = shift;
# use Data::Dumper; print Dumper($graph);
is (ref($graph), 'Graph::Easy');
is ($graph->error(), '', 'no error yet');
is ($graph->output_format(), 'html', 'default output format is html');
is ($graph->timeout(), 5, '5 seconds');
is ($graph->strict(), 1, 'is strict');
is ($graph->nodes(), 0, '0 nodes');
is ($graph->edges(), 0, '0 edges');
is ($graph->border_attribute('graph'), 'none', 'graph border is none');
is ($graph->border_attribute('group'), 'dashed', 'group border is dashed 1px black');
is ($graph->border_attribute('node'), 'solid', 'node border is solid 1px black');
is (join (',', $graph->edges()), '', '0 edges');
like ($graph->output(), qr/table/, 'default output worked');
}

View File

@@ -0,0 +1,69 @@
#!/usr/bin/perl -w
# Test the custom attributes.
use Test::More;
use strict;
BEGIN
{
plan tests => (4*4+4) * 6 + (8+2) * 4 + 3;
chdir 't' if -d 't';
use lib '../lib';
use_ok ("Graph::Easy::Attributes") or die($@);
use_ok ("Graph::Easy") or die($@);
};
can_ok ("Graph::Easy", qw/
valid_attribute
/);
#############################################################################
# valid_attribute:
my $att = Graph::Easy->new();
$att->no_fatal_errors(1);
for my $n (qw/ foo-bar bar-foo b-f-a boo-f-bar bar b-f /)
{
my $new_value = $att->valid_attribute( "x-$n", 'furble, barble' );
is ($new_value, "furble, barble", "x-$n is valid");
my @new_value = $att->validate_attribute( "x-$n", 'furble, barble' );
is ($new_value[0], undef, "x-$n is valid");
is ($new_value[1], "x-$n", "x-$n is valid");
is ($new_value[2], "furble, barble", "x-$n is valid");
for my $class (qw/ graph group node edge /)
{
my $new_value = $att->valid_attribute( "x-$n", 'furble, barble', $class );
is ($new_value, "furble, barble", "x-$n is valid in class $class");
my @new_value = $att->validate_attribute( "x-$n", 'furble, barble', $class );
is ($new_value[0], undef, "x-$n is valid in class $class");
is ($new_value[1], "x-$n", "x-$n is valid");
is ($new_value[2], "furble, barble", "x-$n is valid in class $class");
}
}
for my $n (qw/ -foo-bar bar-foo- b--a -boo-f-bar- /)
{
my $new_value = $att->valid_attribute( "x-$n", 'furble, barble' );
is (ref($new_value), 'ARRAY', "x-$n is not valid");
my @new_value = $att->validate_attribute( "x-$n", 'furble, barble' );
is ($new_value[0], 1, "x-$n is not valid");
for my $class (qw/ graph group node edge /)
{
my $new_value = $att->valid_attribute( "x-$n", 'furble, barble', $class );
is (ref($new_value), 'ARRAY', "x-$n is not valid in class $class");
my @new_value = $att->validate_attribute( "x-$n", 'furble, barble', $class );
is ($new_value[0], 1, "x-$n is not valid in class $class");
}
}
1;

View File

@@ -0,0 +1,117 @@
#!/usr/bin/perl -w
# Test deletion of nodes and edges
use Test::More;
use strict;
BEGIN
{
plan tests => 46;
chdir 't' if -d 't';
use lib '../lib';
use_ok ("Graph::Easy") or die($@);
};
can_ok ("Graph::Easy", qw/
del_node
del_edge
/);
#############################################################################
# first we add edges/nodes
my $graph = Graph::Easy->new();
is (ref($graph), 'Graph::Easy');
is ($graph->error(), '', 'no error yet');
$graph->add_edge('Bonn', 'Berlin');
# check that it contains 2 nodes and one edge
is_ok ($graph);
#############################################################################
print "# add edge, delete it again\n";
my $edge = $graph->add_edge('Bonn', 'Berlin');
$graph->del_edge($edge);
# check that it contains 2 nodes and one edge
is_ok ($graph);
#############################################################################
print "# add selfloop edge, delete it again\n";
$edge = $graph->add_edge('Bonn', 'Bonn');
$graph->del_edge($edge);
# check that it contains 2 nodes and one edge
is_ok ($graph);
$edge = $graph->add_edge('Berlin', 'Berlin');
$graph->del_edge($edge);
# check that it contains 2 nodes and one edge
is_ok ($graph);
#############################################################################
print "# add node, delete it again\n";
my $node = $graph->add_node('Cottbus');
$graph->del_node($node);
# check that it contains 2 nodes and one edge
is_ok ($graph);
#############################################################################
print "# add node with edge, delete it again\n";
my ($n1, $n2, $e) = $graph->add_edge('Cottbus', 'Bonn');
$graph->del_node($n1);
# check that it contains 2 nodes and one edge
is_ok ($graph);
($n1, $n2, $e) = $graph->add_edge('Cottbus', 'Bonn');
($n1, $n2, $e) = $graph->add_edge('Cottbus', 'Berlin');
$graph->del_node($n1);
# check that it contains 2 nodes and one edge
is_ok ($graph);
1; # all tests done
#############################################################################
# test graph after deletion
sub is_ok
{
my $graph = shift;
is ($graph->nodes(), 2, '2 nodes');
is ($graph->edges(), 1, '1 edge');
my $t = '';
for my $n (sort { $a->{name} cmp $b->{name} } $graph->nodes())
{
$t .= $n->name();
}
is ($t, 'BerlinBonn', 'two nodes');
my $bonn = $graph->node('Bonn');
my $berlin = $graph->node('Berlin');
is (scalar keys %{$bonn->{edges}}, 1, 'one edge');
is (scalar keys %{$berlin->{edges}}, 1, 'one edge');
my $ids = join (',',
keys %{$bonn->{edges}},
keys %{$berlin->{edges}},
keys %{$graph->{edges}} );
is ($ids, '0,0,0', 'edge with ID is the only one');
}

View File

@@ -0,0 +1,6 @@
graph {
A--B
B--C
C--D
D--A
}

View File

@@ -0,0 +1,61 @@
#!/usr/bin/perl -w
# test multiple calls to as_ascii()/as_boxart() as well as merge_nodes
use Test::More;
BEGIN
{
plan tests => 5;
chdir 't' if -d 't';
use lib '../lib';
use_ok("Graph::Easy") or die (@!);
use_ok("Graph::Easy::Parser") or die (@!);
}
my $graph = Graph::Easy::Parser->from_file('stress/drop.txt');
binmode STDOUT, ':utf8' or die ("binmode STDOUT, ':utf8' failed: $!");
binmode STDERR, ':utf8' or die ("binmode STDERR, ':utf8' failed: $!");
my $bonn = $graph->node('Bonn');
my $first = $graph->as_ascii();
my $second = $graph->as_ascii();
is ($first, $second, 'two times as_ascii() changes nothing');
$first = $graph->as_boxart();
$second = $graph->as_boxart();
is ($first, $second, 'two times as_boxart() changes nothing');
# drop any connection between Bonn and Berlin, as well as self-loops
# from Berlin to Berlin
$graph->merge_nodes('Bonn', 'Berlin');
my $result = $first . "\n" . $graph->as_boxart();
my $expected = readfile('out/drop_result.txt');
is ($result, $expected, 'dropping a node works');
# all tests done
1;
sub readfile
{
my ($file) = @_;
open FILE, $file or die ("Cannot read file $file: $!");
binmode FILE, ':utf8' or die ("binmode $file, ':utf8' failed: $!");
local $/ = undef; # slurp mode
my $doc = <FILE>;
close FILE;
$doc;
}

View File

@@ -0,0 +1,443 @@
#!/usr/bin/perl -w
use Test::More;
use strict;
BEGIN
{
plan tests => 138;
chdir 't' if -d 't';
use lib '../lib';
use_ok ("Graph::Easy") or die($@);
};
can_ok ("Graph::Easy", qw/
new
css as_html as_html_page as_txt as_vcg as_boxart as_gdl
as_ascii as_ascii_html as_graphviz as_svg
as_ascii_file as_html_file as_svg_file as_vcg_file
as_boxart_file as_gdl_file
as_graphml as_graphml_file
as_debug
html_page_header
html_page_footer
error
edge node nodes edges edges_within anon_nodes
svg_information
add_edge
add_node add_anon_node
merge_nodes
del_node
del_edge
flip_edges
rename_node rename_group
set_attributes
set_attribute
get_attribute
get_attributes
get_color_attribute
default_attribute
raw_attribute
color_attribute
attribute
del_attribute
score
id
group groups add_group del_group
is_simple_graph
is_simple
is_directed
is_undirected
text_style
text_styles
text_styles_as_css
animation_as_graph
/);
#############################################################################
# adding edges/nodes
my $graph = Graph::Easy->new();
is (ref($graph), 'Graph::Easy');
is ($graph->error(), '', 'no error yet');
is ($graph->is_simple_graph(), 1, 'simple graph (0 nodes)');
is ($graph->is_simple(), 1, 'simple graph (0 nodes)');
is ($graph->is_directed(), 1, 'directed graph');
my $node = Graph::Easy::Node->new( name => 'Bonn' );
my $node2 = Graph::Easy::Node->new( name => 'Berlin' );
is (scalar $graph->nodes(), 0, 'no nodes');
is (scalar $graph->anon_nodes(), 0, 'no anon nodes');
is (scalar $graph->edges(), 0, 'no edges');
$graph->add_edge( $node, $node2 );
is (scalar $graph->nodes(), 2, '2 nodes');
is (scalar $graph->edges(), 1, '1 edges');
is ($graph->is_simple_graph(), 1, 'simple graph (2 nodes, 1 edge)');
my $node3 = Graph::Easy::Node->new( name => 'Frankfurt');
$graph->add_edge( $node2, $node3 );
is (scalar $graph->nodes(), 3, '3 nodes');
is (scalar $graph->edges(), 2, '2 edges');
is ($graph->is_simple_graph(), 1, 'still simple graph');
my $node4 = Graph::Easy::Node->new( name => 'Dresden' );
$graph->add_edge( $node3, $node4 );
is (scalar $graph->nodes(), 4, '4 nodes');
is (scalar $graph->edges(), 3, '3 edges');
is ($graph->is_simple_graph(), 1, 'still simple graph');
my $node5 = Graph::Easy::Node->new( name => 'Potsdam' );
$graph->add_edge( $node2, $node5 );
is (scalar $graph->nodes(), 5, '5 nodes');
is (scalar $graph->edges(), 4, '4 edges');
is ($graph->is_simple_graph(), 1, 'still simple graph');
my $node6 = Graph::Easy::Node->new( name => 'Cottbus' );
$graph->add_edge( $node5, $node6 );
is (scalar $graph->nodes(), 6, '6 nodes');
is (scalar $graph->edges(), 5, '5 edges');
is ($graph->is_simple_graph(), 1, 'still simple graph');
#############################################################################
# attribute tests
is ($graph->attribute('background'), 'inherit',
'graph background = undef');
is ($graph->attribute('node', 'background'), 'inherit',
'node background = undef');
is ($graph->attribute('node', 'fill'), 'white',
'node { fill: white }');
is ($graph->attribute('graph', 'border'), 'none',
'graph { border: none; }');
$graph->set_attributes ('graph', { color => 'white', background => 'red' });
is ($graph->attribute('graph', 'background'), 'red',
'now: graph { background: red }');
is ($graph->attribute('graph', 'color'), 'white',
'now: graph { color: white }');
good_css ($graph);
#############################################################################
# ID tests
is ($graph->id(), '', 'id is empty string');
is ($graph->id('42'), '42', 'id is now 42');
good_css($graph);
#############################################################################
# ID tests with sub-classes
$graph->set_attributes ('node.cities', { color => '#0000ff' } );
good_css($graph,
'table.graph42 .node_cities',
'table.graph42 .node,table.graph42 .node_anon,table.graph42 .node_cities'
);
#############################################################################
# group tests
is ($graph->groups(), 0, 'no groups yet');
is ($graph->group('foo'), undef, 'no groups yet');
is ($graph->groups(), 0, 'no groups yet');
my $group = Graph::Easy::Group->new( { name => 'Cities' } );
$graph->add_group($group);
is ($graph->group('Cities'), $group, "group 'cities'");
is ($graph->groups(), 1, 'one group');
is ($graph->group('cities'), undef, 'no group');
is ($graph->groups(), 1, 'one group');
is ($graph->as_txt(), <<HERE
graph {
background: red;
color: white;
}
node.cities { color: #0000ff; }
( Cities )
[ Bonn ] --> [ Berlin ]
[ Berlin ] --> [ Frankfurt ]
[ Berlin ] --> [ Potsdam ]
[ Frankfurt ] --> [ Dresden ]
[ Potsdam ] --> [ Cottbus ]
HERE
, 'with empty group Cities');
$node->add_to_group($group);
is ($graph->as_txt(), <<HERE
graph {
background: red;
color: white;
}
node.cities { color: #0000ff; }
( Cities
[ Bonn ]
)
[ Bonn ] --> [ Berlin ]
[ Berlin ] --> [ Frankfurt ]
[ Berlin ] --> [ Potsdam ]
[ Frankfurt ] --> [ Dresden ]
[ Potsdam ] --> [ Cottbus ]
HERE
, 'with empty group Cities');
#############################################################################
# title/link/autolink/autotitle/linkbase not in CSS
$graph->set_attributes ('node',
{ link => 123, title => 123, autolink => 'name', autotitle => 'name' } );
$graph->set_attributes ('graph', { linkbase => '123/' } );
good_css ($graph);
# check that add_node( 'name' ) works
$graph = Graph::Easy->new();
my $bonn = $graph->add_node( 'Bonn' );
is (scalar $graph->nodes(), 1, 'one node');
is ($graph->node('Bonn'), $bonn, 'add_node returned $bonn');
# already in graph, try to add as "name"
my $bonn2 = $graph->add_node( 'Bonn' );
is (scalar $graph->nodes(), 1, 'one node');
is ($bonn2, $graph->node('Bonn'), 'add_node returned $bonn');
is ($bonn, $bonn2, 'same node');
# already in graph, try to add as node object
my $bonn3 = $graph->add_node( $bonn );
is (scalar $graph->nodes(), 1, 'one node');
is ($bonn3, $graph->node('Bonn'), 'add_node returned $bonn');
is ($bonn, $bonn3, 'same node');
my $bonn5 = Graph::Easy::Node->new('Bonn');
my $bonn4 = $graph->add_node( $bonn5);
#make sure that $bonn is not replaced by $bonn5 in graph!
is (scalar $graph->nodes(), 1, 'one node');
is ($bonn4, $graph->node('Bonn'), 'add_node returned $bonn');
is ($bonn, $bonn4, 'same node');
#############################################################################
# adding an edge with two plain scalars as names
$graph = Graph::Easy->new();
my ($T1,$T2,$edge) = $graph->add_edge( 'Test', 'Test2' );
is (scalar $graph->nodes(), 2, '2 nodes');
is (scalar $graph->edges(), 1, '1 edge');
is ($graph->edge('Test', 'Test2'), $edge, 'edge("A","B") works');
is ($graph->edge($T1,$T2), $edge, 'edge($A,$B) works');
# adding a multi-edge
$graph->add_edge( 'Test', 'Test2' );
is (scalar $graph->nodes(), 2, '2 nodes');
is (scalar $graph->edges(), 2, '2 edges');
# this assumes "Test" is created before "Test2"
my @N = sort { $a->{id} <=> $b->{id} } $graph->nodes();
my @E = $N[0]->edges_to($N[1]);
is (@E, 2, '2 edges from Test to Test2');
# this should work now:
my $ascii = $graph->as_ascii();
like ($ascii, qr/Test/, 'Test found in output');
like ($ascii, qr/Test2/, 'Test found in output');
# test that add_edge('Test','Test') does not create two nodes
$graph = Graph::Easy->new();
my ($a,$b,$e) = $graph->add_edge( 'Test', 'Test' );
is ($a->{id}, $b->{id}, "one node for ('test','test')");
is ($a, $b, "one object for ('test','test')");
#############################################################################
# is_ascii_html()
$ascii = $graph->as_ascii_html();
like ($ascii, qr/<pre>(.|\n)*<\/pre>/, 'as_ascii_html');
#############################################################################
# is_simple_graph()
$graph = Graph::Easy->new();
$edge = $graph->add_edge( 'Test', 'Test2' );
is ($graph->is_simple_graph(), 1, 'still simple graph');
$edge = $graph->add_edge( 'Test', 'Test2' );
is ($graph->is_simple_graph(), 0, 'not simple graph');
$edge = $graph->add_edge( 'Test', 'Test2' );
is ($graph->is_simple_graph(), 0, 'not simple graph');
$graph = Graph::Easy->new();
$edge = $graph->add_edge( 'Test', 'Test' );
is ($graph->is_simple_graph(), 1, 'still simple graph');
$edge = $graph->add_edge( 'Test', 'Test2' );
is ($graph->is_simple_graph(), 1, 'still simple graph');
$edge = $graph->add_edge( 'Test', 'Test' );
is ($graph->edges(), 3, '3 edges');
is ($graph->nodes(), 2, '2 nodes');
is ($graph->is_simple_graph(), 0, 'not simple graph');
#############################################################################
# adding nodes with name '0' and ''
$graph = Graph::Easy->new();
$node = Graph::Easy::Node->new( { name => '0' } );
$node = $graph->add_node($node);
is ($graph->nodes(), '1', 'one node');
is ($graph->{nodes}->{0}, $node, 'got inserted with name 0');
is ($graph->node('0'), $node, 'found node 0 again');
#############################################################################
# renaming nodes
#############################################################################
# node is not a reference
$graph = Graph::Easy->new();
$node = $graph->rename_node('abc','bcd');
is ($graph->nodes(), '1', 'one node');
is ($graph->{nodes}->{bcd}, $node, 'got inserted with name bcd');
#############################################################################
# node is not yet part of any graph
$graph = Graph::Easy->new();
$node = Graph::Easy::Node->new( { name => 'abc' } );
my $new_node = $graph->rename_node($node,'bcd');
is ($graph->nodes(), '1', 'one node');
is ($new_node->{name}, 'bcd', 'got renamed');
is ($graph->{nodes}->{bcd}, $node, 'got inserted with name bcd');
is ($node->{graph}, $graph, 'node is part of this graph');
is ($new_node, $node, 'returned node');
#############################################################################
# node is not part of another graph
$graph = Graph::Easy->new();
my $g2 = Graph::Easy->new();
$node = $g2->add_node( 'abc' );
$new_node = $graph->rename_node($node,'bcd');
is ($graph->nodes(), '1', 'one node');
is ($g2->nodes(), '0', 'other graph has now zero');
is ($graph->{nodes}->{bcd}, $node, 'got inserted with name bcd');
is ($node->{graph}, $graph, 'node is part of this graph');
is ($new_node, $node, 'returned node');
#############################################################################
# directed/undirected
$graph = Graph::Easy->new();
is ($graph->is_directed(), 1, 'directed graph');
is ($graph->is_undirected(), 0, 'directed graph');
$graph->set_attribute('type','directed');
is ($graph->is_directed(), 1, 'directed graph');
is ($graph->is_undirected(), 0, 'directed graph');
$graph->set_attribute('type','undirected');
is ($graph->is_directed(), 0, 'undirected graph');
is ($graph->is_undirected(), 1, 'undirected graph');
my $ge = Graph::Easy->new( undirected => 1 );
is (ref($ge), 'Graph::Easy');
is ($ge->attribute('type'), 'undirected', 'is undirected');
is ($ge->is_undirected(), 1, 'is undirected');
#############################################################################
# merging nodes
$graph = Graph::Easy->new('[A]->[B]->[C]->[D]');
$graph->merge_nodes( 'A', 'B' );
is ($graph->as_txt(), "[ A ] --> [ C ]\n[ C ] --> [ D ]\n", 'merge worked');
$graph->merge_nodes( 'A', 'C', ' ' );
is ($graph->as_txt(), "[ A ] { label: A C; }\n\n[ A ] --> [ D ]\n", 'merge worked');
$graph->merge_nodes( 'A', 'D', ' \n ' );
is ($graph->as_txt(), "[ A ] { label: A C \\n D; }\n\n", 'merge worked');
1; # all tests done
#############################################################################
sub good_css
{
my $graph = shift;
my $css = $graph->css();
foreach my $class (qw/edge node/, )
{
like ($css, qr/table\.graph\d* \.$class/, "$class in css");
}
like ($css, qr/graph\d* \{/, "graph in css");
foreach my $add (@_)
{
like ($css, qr/$add/, "$add in css");
}
foreach my $attr (qw/link label title linkbase autotitle autolabel/)
{
unlike ($css, qr/$attr/, "$attr not in css");
}
}

View File

@@ -0,0 +1,212 @@
#!/usr/bin/perl -w
use Test::More;
use strict;
BEGIN
{
plan tests => 43;
chdir 't' if -d 't';
use lib '../lib';
use_ok qw/Graph::Easy::Edge/;
use_ok qw/Graph::Easy::Edge::Cell/;
}
can_ok ("Graph::Easy::Edge", qw/
new
error
label
_cells
_add_cell
_clear_cells
_unplace
attribute
undirected
bidirectional
has_ports
flip
set_attribute
set_attributes
group add_to_group
background
edge_flow flow port
start_port end_port
from to start_at
to from nodes
as_ascii
as_txt
/);
use Graph::Easy::Edge::Cell qw/EDGE_SHORT_E/;
use Graph::Easy;
#############################################################################
# We need a graph to insert the edge into it, so that the edge gets the
# default settings from it.
# XXX TODO: should we change the above?
my $graph = Graph::Easy->new();
my $edge = Graph::Easy::Edge->new();
$edge->{graph} = $graph;
is (ref($edge), 'Graph::Easy::Edge');
is ($edge->error(), '', 'no error yet');
is ($edge->undirected(), undef, 'not undirected');
is ($edge->bidirectional(), undef, 'not bidiriectional');
is ($edge->has_ports(), 0, 'has no port restrictions');
use_ok ('Graph::Easy::As_txt');
is ($edge->as_txt(), ' --> ', 'default is "-->"');
#############################################################################
# different styles
$edge = Graph::Easy::Edge->new( style => 'double' );
$edge->{graph} = $graph;
is ($edge->as_txt(), ' ==> ', '"==>"');
$edge = Graph::Easy::Edge->new( style => 'dotted' );
$edge->{graph} = $graph;
is ($edge->as_txt(), ' ..> ', '"..>"');
$edge = Graph::Easy::Edge->new( style => 'dashed' );
$edge->{graph} = $graph;
is ($edge->as_txt(), ' - > ', '"- >"');
$edge = Graph::Easy::Edge->new( style => 'wave' );
$edge->{graph} = $graph;
is ($edge->as_txt(), ' ~~> ', '"~~>"');
$edge = Graph::Easy::Edge->new( style => 'dot-dash' );
$edge->{graph} = $graph;
is ($edge->as_txt(), ' .-> ', '".->"');
$edge = Graph::Easy::Edge->new( style => 'double-dash' );
$edge->{graph} = $graph;
is ($edge->as_txt(), ' = > ', '"= >"');
$edge = Graph::Easy::Edge->new( style => 'dot-dot-dash' );
$edge->{graph} = $graph;
is ($edge->as_txt(), ' ..-> ', '"= >"');
$edge = Graph::Easy::Edge->new( style => 'bold' );
$edge->{graph} = $graph;
is ($edge->as_txt(), ' --> { style: bold; } ', ' --> { style: bold; }');
#############################################################################
$edge = Graph::Easy::Edge->new( label => 'train' );
$edge->{graph} = $graph;
is ($edge->as_txt(), ' -- train --> ', ' -- train -->');
#############################################################################
# cells
is (scalar $edge->_cells(), 0, 'no cells');
my $c = Graph::Easy::Edge::Cell->new (
edge => $edge,
type => EDGE_SHORT_E,
x => 1, y => 1,
after => 0,
);
is (scalar $edge->_cells(), 1, 'one cell');
my @cells = $edge->_cells();
is ($cells[0], $c, 'added this cell');
my $c_1 = Graph::Easy::Edge::Cell->new (
edge => $edge,
type => EDGE_SHORT_E,
x => 2, y => 1,
after => $c,
);
is (scalar $edge->_cells(), 2, 'two cells');
@cells = $edge->_cells();
is ($cells[0], $c, 'first cell stayed');
is ($cells[1], $c_1, 'added after first cell');
$edge->_clear_cells();
is (scalar $edge->_cells(), 0, 'no cells');
#############################################################################
# undirected/bidirectional
is ($edge->undirected(2), 1, 'undirected');
is ($edge->undirected(), 1, 'undirected');
is ($edge->undirected(0), 0, 'not undirected');
is ($edge->bidirectional(2), 1, 'bidiriectional');
is ($edge->bidirectional(), 1, 'bidiriectional');
is ($edge->bidirectional(0), 0, 'not bidiriectional');
#############################################################################
# has_ports()
$edge->set_attribute('start', 'south');
is ($edge->has_ports(), 1, 'has port restrictions');
$edge->set_attribute('end', 'north');
is ($edge->has_ports(), 1, 'has port restrictions');
$edge->del_attribute('start');
is ($edge->has_ports(), 1, 'has port restrictions');
$edge->del_attribute('end');
is ($edge->has_ports(), 0, 'has no port restrictions');
#############################################################################
# port()
$edge->set_attribute('start', 'south');
my @u = $edge->port('start');
is_deeply (\@u, ['south'], "port('start')");
$edge->del_attribute('end');
$edge->del_attribute('start');
#############################################################################
# background()
is ($edge->background(), 'inherit', 'background()');
$graph = Graph::Easy->new();
my ($A,$B); ($A,$B,$edge) = $graph->add_edge('A','B');
my $group = $graph->add_group('G');
$group->add_member($edge);
my $cell = Graph::Easy::Edge::Cell->new( edge => $edge, graph => $graph );
# default group background
is ($cell->background(), '#a0d0ff', 'background() for group member');
$group->set_attribute('background', 'red');
is ($cell->background(), '#a0d0ff', 'background() for group member');
# now has the fill of the group as background
$group->set_attribute('fill', 'green');
is ($cell->background(), '#008000', 'background() for group member');
#############################################################################
# flip()
my $from = $edge->from();
my $to = $edge->to();
$edge->flip();
is ($from, $edge->to(), 'from/to flipped');
is ($to, $edge->from(), 'from/to flipped');

View File

@@ -0,0 +1,134 @@
#!/usr/bin/perl -w
# Test Graph::Easy::Edge::Cell
use Test::More;
use strict;
BEGIN
{
plan tests => 25;
chdir 't' if -d 't';
use lib '../lib';
use_ok ("Graph::Easy::Edge::Cell") or die($@);
use_ok ("Graph::Easy") or die($@);
use_ok ("Graph::Easy::As_ascii") or die($@);
};
can_ok ("Graph::Easy::Edge::Cell", qw/
new
as_ascii as_html
error
pos
x
y
label
width
height
style
type
edge_type
_draw_cross
_draw_ver
_draw_hor
_draw_corner
_make_cross
/);
use Graph::Easy::Edge::Cell qw/
EDGE_SHORT_W EDGE_CROSS EDGE_END_N EDGE_START_E EDGE_HOR EDGE_VER
EDGE_N_W_S
/;
use Graph::Easy::Edge;
#############################################################################
my $edge = Graph::Easy::Edge->new();
my $path = Graph::Easy::Edge::Cell->new( edge => $edge );
is (ref($path), 'Graph::Easy::Edge::Cell');
is ($path->error(), '', 'no error yet');
is ($path->x(), 0, 'x == 0');
is ($path->y(), 0, 'x == 0');
is ($path->label(), '', 'no label');
is (join(",", $path->pos()), "0,0", 'pos = 0,0');
is ($path->width(), undef, 'w = undef'); # no graph => thus no width yet
$path = Graph::Easy::Edge::Cell->new( edge => $edge, type => EDGE_SHORT_W);
is ($path->type(), EDGE_SHORT_W, 'edge to the left');
#############################################################################
# attribute()
$edge->set_attribute( color => 'blue', border => 'none');
$path = Graph::Easy::Edge::Cell->new( type => EDGE_SHORT_W, edge => $edge);
is ($path->attribute('color'), 'blue');
#############################################################################
# as_txt/as_ascii
$path->_correct_size();
is ($path->{w}, 5, 'w == 5');
is ($path->{h}, 3, 'h == 3');
my $ascii = $path->as_ascii(0,0);
$ascii =~ s/^\s+//;
$ascii =~ s/\s+\z//;
is ($ascii, "<--", 'as ascii');
# rendering of seems
$edge = Graph::Easy::Edge->new( style => 'dot-dash' );
$path = Graph::Easy::Edge::Cell->new( type => EDGE_HOR, edge => $edge);
$path->{w} = 10;
$ascii = $path->as_ascii(0,0);
$ascii =~ s/^\s+//;
$ascii =~ s/\s+\z//;
is ($ascii, ".-.-.-.-.-", 'as ascii');
$ascii = $path->as_ascii(1,0);
$ascii =~ s/^\s+//;
$ascii =~ s/\s+\z//;
is ($ascii, "-.-.-.-.-.", 'as ascii');
my $other = Graph::Easy::Edge->new( style => 'dashed' );
$path->{type} = EDGE_HOR;
$path->_make_cross($other);
$ascii = $path->as_ascii();
is ($ascii, " ' \n.-+-.-.-.-\n ' ", 'crossing between dot-dash and dashed');
$path->{style} = 'dotted';
$path->{style_ver} = 'solid';
$ascii = $path->as_ascii();
is ($ascii, " | \n..!.......\n | ", 'crossing between dotted and solid');
#############################################################################
# edge_type()
my $et = 'Graph::Easy::Edge::Cell::edge_type';
{
no strict 'refs';
is (&$et( EDGE_HOR() ), 'horizontal', 'EDGE_HOR');
is (&$et( EDGE_VER() ), 'vertical', 'EDGE_VER');
is (&$et( EDGE_CROSS() ), 'crossing', 'EDGE_CROSS');
is (&$et( EDGE_SHORT_W() ), 'horizontal, ending west, starting east', 'EDGE_SHORT_W');
is (&$et( EDGE_N_W_S() ), 'selfloop, northwards', 'EDGE_N_W_S');
}

View File

@@ -0,0 +1,172 @@
#!/usr/bin/perl -w
# test printing into a framebuffer
use Test::More;
use strict;
BEGIN
{
plan tests => 36;
chdir 't' if -d 't';
use lib '../lib';
use_ok ("Graph::Easy::Node") or die($@);
use_ok ("Graph::Easy") or die($@);
use_ok ("Graph::Easy::As_ascii") or die($@);
};
can_ok ("Graph::Easy::Node", qw/
as_ascii
_printfb
_printfb_ver
_draw_label
_framebuffer
_aligned_label
/);
#############################################################################
# general framebuffer tests
my $node = Graph::Easy::Node->new();
is (ref($node), 'Graph::Easy::Node');
is ($node->error(), '', 'no error yet');
my $fb = $node->_framebuffer(2,3);
is (join ("::", @$fb), " :: :: ", 'framebuffer set up');
$node->_printfb( $fb, 0,0, '+');
is (join ("::", @$fb), "+ :: :: ", 'print +');
$node->_printfb( $fb, 1,0, '+');
is (join ("::", @$fb), "++:: :: ", 'print +');
$node->_printfb( $fb, 1,2, '+');
is (join ("::", @$fb), "++:: :: +", 'print +');
$node->_printfb( $fb, 0,0, '--');
is (join ("::", @$fb), "--:: :: +", 'print --');
$node->_printfb( $fb, 0,1, "''");
is (join ("::", @$fb), "--::'':: +", "print ''");
#############################################################################
# multiline printing
$fb = $node->_framebuffer(2,5);
$node->_printfb( $fb, 0,3, "+", "+");
is (join ("::", @$fb), " :: :: ::+ ::+ ", 'print "+\n+"');
$node->_printfb( $fb, 0,2, "|", "|");
is (join ("::", @$fb), " :: ::| ::| ::+ ", 'print "\|\n\|"');
$fb = $node->_framebuffer(4,5);
is (join ("::", @$fb), " :: :: :: :: ", 'new fb set up');
$node->_printfb( $fb, 1,1, "01", "234");
is (join ("::", @$fb), " :: 01 :: 234:: :: ", 'new fb set up');
#############################################################################
# _draw_border() tests
$fb = $node->_framebuffer(12,6);
$node->{w} = 12;
$node->{h} = 6;
$node->_draw_border( $fb, 'solid', 'solid', 'solid', 'solid');
is (join ("::", @$fb),
'+----------+::| |::| |::| |::| |::+----------+',
'solid border');
$fb = $node->_framebuffer(8,4);
$node->{w} = 8;
$node->{h} = 4;
my @expect = (
' \n \n \n ',
'+------+\n| |\n| |\n+------+',
'........\n: :\n: :\n:......:',
'+ - - -+\n\' \'\n\' \'\n+ - - -+',
'+-.-.-.+\n! !\n! !\n+-.-.-.+',
'+.-..-.+\n| |\n: :\n+.-..-.+',
'########\n# #\n# #\n########',
'#======#\nH H\nH H\n#======#',
'# = = =#\n" "\n" "\n# = = =#',
'+~~~~~~+\n{ {\n} }\n+~~~~~~+',
);
my $i = 0;
for my $style (qw/ none solid dotted dashed dot-dash dot-dot-dash bold double double-dash wave/)
{
$node->_draw_border( $fb, $style, $style, $style, $style);
is (join ('\n', @$fb),
$expect[$i],
"$style border");
$i++;
}
#############################################################################
# _draw_border() tests with different styles
$fb = $node->_framebuffer(8,4);
$node->{w} = 8;
$node->{h} = 4;
$node->_draw_border( $fb, 'solid', 'dotted', 'solid', 'solid');
is (join ("::", @$fb),
'+------+::| |::| |:::......:',
'solid border, except bottom, which is dotted');
#############################################################################
# label alignments
$node->set_attribute('label', 'left\r right\l left\c center\n normal');
my ($lines,$aligns) = $node->_aligned_label();
is_deeply ( $lines, [ 'left', 'right', 'left', 'center', 'normal' ],
'lines are ok');
is_deeply ( $aligns, [ 'c', 'r', 'l', 'c', 'c', ], 'aligns is ok');
# empty lines at the are thrown away
$node->set_attribute('label', 'left\r right\l left\c center\n normal\c');
($lines,$aligns) = $node->_aligned_label();
is_deeply ( $lines, [ 'left', 'right', 'left', 'center', 'normal' ],
'lines are ok');
is_deeply ( $aligns, [ 'c', 'r', 'l', 'c', 'c', ], 'aligns is ok');
# start with alignment
$node->set_attribute('label', '\rleft\r right\l left\c center\n normal\c');
($lines,$aligns) = $node->_aligned_label();
is_deeply ( $lines, [ '', 'left', 'right', 'left', 'center', 'normal' ],
'lines are ok');
is_deeply ( $aligns, [ 'c', 'r', 'r', 'l', 'c', 'c', ], 'aligns is ok');
# start with alignment
$node->set_attribute('label', '\r\l\rleft\r right\l left\c center\n normal\c');
($lines,$aligns) = $node->_aligned_label();
is_deeply ( $lines, [ '','','','left', 'right', 'left', 'center', 'normal' ],
'lines are ok');
is_deeply ( $aligns, [ 'c','r','l','r', 'r', 'l', 'c', 'c', ], 'aligns is ok');

View File

@@ -0,0 +1,22 @@
# Example from Graphviz extension
# http://www.wickle.com/wiki/index.php/Graphviz_extension
node.red { background: red; }
[ ns ] { class: red } -> [ addons ]
-> [ metamod ] { class: red }
-> [ dlls2 ] { label: dlls }
[ metamod ] -> [ doc ] -> [html ]
[ ns ] -> [ logs ]
[ ns ] -> [ gfx ] -> [ vgui ]
[ addons ] -> [ amxmodx ] -> [ configs ] { class: red }
[ amxmodx ] -> [ data ] -> [ lang ]
[ amxmodx ] -> [ dlls ]
[ amxmodx ] -> [ plugins ] { class: red }
[ doc ] -> [ txt ]

View File

@@ -0,0 +1,5 @@
# Elan and Roy on Teamwork
# http://www.cafepress.com/orderofthestick.10272636?zoom=yes#zoom
[ Teamwork ] { fill: yellow; title: Elan; }
-- is the key to --> [ Victory ] { fill: red; title: Roy; }

View File

@@ -0,0 +1,8 @@
# Elan and Roy on Teamwork
# http://www.cafepress.com/orderofthestick.10272636?zoom=yes#zoom
[ Teamwork ] { fill: yellow; title: Elan; }
-- is the key to --> [ Victory ] { fill: red; title: Roy; }
[ Victory ] -- the order of the --> [ Stick ] { fill: #ff80a0; }
[ Victory ] -- Treasure --> [ Haley ] { fill: lightblue; }

View File

@@ -0,0 +1,7 @@
# Enlightment
# http://bloodgate.com/perl/graph/
[ Textual description ] { fill: #804020; }
-> [ Parser ] { fill: #a06040; }
-> [ Layouter ] { fill: #d08060; }
-> [ Output (ASCII/HTML) ] { fill: #ffa090; }

View File

@@ -0,0 +1,18 @@
# Example from Graphviz extension
# http://www.wickle.com/wiki/index.php/Graphviz_extension
node { shape: circle; }
[ main ] { shape: rect; } # this is a comment
-> [ parse ]
-> [ execute ]
-> [ make_string ] { label: make a\n string; }
[ main ] ..> [ init ]
[ main ] -- Testlabel --> [ cleanup ]
[ init ] -> [ make_string ]
[ main ] == 100 times ==> { color: red; style: bold; } [ printf ] # bold red edge!
[ execute ] -> { color: red; } [ compare ] { shape: rect; background: #c1b2ff; }

View File

@@ -0,0 +1,23 @@
# Three non-overlapping groups
group.dmz { fill: #ffa070; }
group.outer { fill: #f07070; }
( Internal Network:
[ Workstation ]
) { fill: #70b070; }
[ Workstation ] --> [ Inner Firewall ]
( DMZ:
[ Inner Firewall ] -> [ Proxy ]
--> [ Outer Firewall ]
[ Proxy ] --> [ Database\n Server ]
) { class: dmz; }
( Outer:
[ Internet ]
) { class: outer }
[ Outer Firewall ] --> [ Internet ]

View File

@@ -0,0 +1,13 @@
# Example from: http://wiki.debian.org.hk/w/Generate_SSL_cert
# http://wiki.debian.org.hk/w/Generate_SSL_cert
[ Start ] { shape: rounded; }
-- openssl\n genrsa --> [ key ]
-- openssl\n req --> [ Certificate\n Signing\n Request\n (CSR) ]
-> [ CA ] { label: "Certificate\n Authority\n (CA)"; shape: circle; }
-> [ Certificate\n (CRT) ]
[ CA ] -> [ Certificate\n Revocation\n List\n (CRL) ]
[ CA Key ] -> [ CA ]

View File

@@ -0,0 +1,27 @@
# Example from: http://www.islandseeds.org/wiki/Biofuel
# http://www.islandseeds.org/wiki/Biofuel
graph { flow: east; }
node { shape: ellipse; }
[ solar energy ] { fill: yellow; }
[ water] { fill: cyan; }
[ soil ] { fill: burlywood; }
-> [ vegetation ]
-> [ processing ]
-> [ biofuel ]
-> [ combustion ]
-> [ useful work ] { fill: lightgray; }
[ combustion ] -> [ carbon dioxide ]
[ combustion ] -> [ pollution ]
[ processing ] ..> [ compost ] ..> [ soil ]
[ processing ] ..> [ carbon dioxide ]
[ processing ] ..> [ pollution ]
[ water ] -> [ vegetation ]
[ solar energy ] -> [ vegetation ]

View File

@@ -0,0 +1,39 @@
# The famous 1999 geek dating flow char from userfriendly.org
# http://www.userfriendly.org/cartoons/archives/99mar/19990314.html
graph { label: GEEK DATING FLOWCHART; font-size: 3em; color: red; font: serif; }
node { text-wrap: auto; color: black; font-size: 0.8em; }
edge { color: black; }
edge.yes { label: Yes; }
edge.no { label: No; }
[ Start ] { label: Do you have someone in mind? } --> { class: no; flow: forward; } [ GPF ] { fill: #00b0ff; }
[ Start ] --> { class: yes; }
[ Available ] { label: Are they available? } --> { class: yes; } [ Exist ] { label: Do they know you exist? }
--> { class: yes; } [ Truly remararkable. They're available and they know you exist. Will wonders never cease. ]
{ origin: Exist; offset: 0,4; }
--> { end: back, 0; } [ Askout ] { label: So now you just need to ask them out on a date. What do they say? }
--> { class: yes; } [ Hahahaha! Yeah. Right. ]
--> { start: front,0; end: right, 0; } [ End ] { label: Well, you can still have a date with your Palm S<Pilot>; format: pod; }
[ Available ] --> { class: no; } [ GPF ]
[ Exist ] --> { class: no; }
[ Ask them if they like Computers: ]
--> { class: no; } [ Ask them if they like 'Star Wars': ]
--> { class: no; } [ Ask them if they like the "Hitchhiker's Guide': ]
--> { class: no; } [ Qualities ] { label: Ask them if they have any redeeming qualities at all: }
[ Ask them if they like Computers: ],
[ Ask them if they like 'Star Wars': ]
[ Ask them if they like the "Hitchhiker's Guide': ],
[ Qualities ] --> { class: yes; end: back, 0; } [ Lying ] { label: They're lying. Ditch them; }
[ Qualities ] --> { class: no; } [ Smell ] { label: At least they're honest. Ask them if you smell bad: }
--> { class: no; } [ Lying ]
--> [ End ]
[ Smell ] --> { class: yes;} [ Yup, they're honest all right. But now you know they think you smell bad. Ditch them. ] --> [ End ]

View File

@@ -0,0 +1,21 @@
graph { flow: south; }
node.input { fill: lime; }
node.output { fill: darkorange; }
node.dot { fill: gold; }
[ Text ], [ Graphviz ] { class: input; } -> [ Parser ]
[ Perl ] { class: input; }, [ Parser ] -> [ Graph::Easy ] -> { start: front; }
[ Text2 ] { label: Text; }, [ Graphviz2 ] { class: output }
[ Graph::Easy ] -> [ Layouter ]
[ Graphviz2 ] { label: Graphviz; }
-> { minlen: 1; } [ dot ] { fill: #8080ff; } -> { start: front; }
[ .pdf ], [ .png ], [ .ps ] { class: dot; }
[ Layouter ] { offset: 2,0; origin: Graph::Easy; } -> { start: front; }
[ ASCII /\nUnicode ], [ HTML ], [ SVG ] { class: output }

View File

@@ -0,0 +1,131 @@
#!/usr/bin/perl -w
use Test::More;
use strict;
use File::Spec;
# test GDL (Graph Description Language) file input => ASCII output
# and back to as_txt() again
BEGIN
{
plan tests => 20;
chdir 't' if -d 't';
use lib '../lib';
use_ok ("Graph::Easy") or die($@);
use_ok ("Graph::Easy::Parser") or die($@);
use_ok ("Graph::Easy::Parser::VCG") or die($@);
};
#############################################################################
# parser object
my $def_parser = Graph::Easy::Parser->new(debug => 0);
is (ref($def_parser), 'Graph::Easy::Parser');
is ($def_parser->error(), '', 'no error yet');
my $dir = File::Spec->catdir('in','gdl');
opendir DIR, $dir or die ("Cannot read dir $dir: $!");
my @files = readdir(DIR); closedir(DIR);
binmode (STDERR, ':utf8') or die ("Cannot do binmode(':utf8') on STDERR: $!");
binmode (STDOUT, ':utf8') or die ("Cannot do binmode(':utf8') on STDOUT: $!");
eval { require Test::Differences; };
foreach my $f (sort {
$a =~ /^(\d+)/; my $a1 = $1 || '1';
$b =~ /^(\d+)/; my $b1 = $1 || '1';
$a1 <=> $b1 || $a cmp $b;
} @files)
{
my $file = File::Spec->catfile($dir,$f);
my $parser = $def_parser;
next unless $f =~ /\.gdl/; # ignore anything else
print "# at $f\n";
my $txt = readfile($file);
$parser->reset();
my $graph = $parser->from_text($txt); # reuse parser object
$f =~ /^(\d+)/;
my $nodes = $1;
if (!defined $graph)
{
fail ("GDL input was invalid: " . $parser->error());
next;
}
is (scalar $graph->nodes(), $nodes, "$nodes nodes");
# for slow testing machines
$graph->timeout(20);
my $ascii = $graph->as_ascii();
my $of = $f; $of =~ s/\.gdl/\.txt/;
my $out = readfile(File::Spec->catfile('out','gdl',$of));
$out =~ s/(^|\n)#[^# ]{2}.*\n//g; # remove comments
$out =~ s/\n\n\z/\n/mg; # remove empty lines
# print "txt: $txt\n";
# print "ascii: $ascii\n";
# print "should: $out\n";
if (!
is ($ascii, $out, "from $f"))
{
if (defined $Test::Differences::VERSION)
{
Test::Differences::eq_or_diff ($ascii, $out);
}
else
{
fail ("Test::Differences not installed");
}
}
# if the txt output differes, read it in
my $f_txt = File::Spec->catfile('txt','gdl',$of);
if (-f $f_txt)
{
$txt = readfile($f_txt);
}
$graph->debug(1);
if (!
is ($graph->as_txt(), $txt, "$f as_txt"))
{
if (defined $Test::Differences::VERSION)
{
Test::Differences::eq_or_diff ($graph->as_txt(), $txt);
}
else
{
fail ("Test::Differences not installed");
}
}
# print a debug output
my $debug = $ascii;
$debug =~ s/\n/\n# /g;
print "# Generated:\n#\n# $debug\n";
}
1;
sub readfile
{
my ($file) = @_;
open my $FILE, $file or die ("Cannot read file $file: $!");
binmode ($FILE, ':utf8') or die ("Cannot do binmode(':utf8') on $FILE: $!");
local $/ = undef; # slurp mode
my $doc = <$FILE>;
close $FILE;
$doc;
}

View File

@@ -0,0 +1,69 @@
#!/usr/bin/perl -w
# test interface being compatible to Graph.pm so that Graph::Maker works:
use Test::More;
use strict;
BEGIN
{
plan tests => 15;
chdir 't' if -d 't';
use lib '../lib';
use_ok ("Graph::Easy") or die($@);
};
can_ok ('Graph::Easy', qw/
new
add_vertex
add_vertices
has_edge
vertices
add_path
add_cycle
/);
#############################################################################
my $graph = Graph::Easy->new();
is (ref($graph), 'Graph::Easy');
is ($graph->error(), '', 'no error yet');
$graph->add_vertex('A');
my $A = $graph->node('A');
is (scalar $graph->vertices(), 1, '1 vertex');
my @nodes = $graph->vertices();
is ($nodes[0], $A, '1 vertex');
my $edge = $graph->add_edge ('A', 'B');
is ($graph->has_edge('A','B'), 1, 'has_edge()');
is ($graph->has_edge($A,'B'), 1, 'has_edge()');
is ($graph->has_edge('C','B'), 0, 'has_edge()');
$graph->add_vertices('A','B','C');
is (scalar $graph->vertices(), 3, '3 vertices');
$graph->set_vertex_attribute('A','fill','#deadff');
my $atr = $graph->get_vertex_attribute('A','fill');
is ($atr, $A->attribute('fill'), 'attribute got set');
#############################################################################
## add_cycle and add_path
#
$graph = Graph::Easy->new();
$graph->add_path('A','B','C');
is (scalar $graph->vertices(), 3, '3 vertices');
is (scalar $graph->edges(), 2, '2 vertices');
$graph = Graph::Easy->new();
$graph->add_cycle('A','B','C');
is (scalar $graph->vertices(), 3, '3 vertices');
is (scalar $graph->edges(), 3, '3 vertices');

View File

@@ -0,0 +1,255 @@
#!/usr/bin/perl -w
use Test::More;
use strict;
BEGIN
{
plan tests => 72;
chdir 't' if -d 't';
use lib '../lib';
use_ok ("Graph::Easy") or die($@);
};
can_ok ('Graph::Easy', qw/
new
_init
timeout
strict
output_format
output
seed randomize
debug
border_attribute
anon_nodes
/);
#############################################################################
my $graph = Graph::Easy->new();
is (ref($graph), 'Graph::Easy');
ok (defined $graph->{seed}, 'seed was initialized');
is ($graph->error(), '', 'no error yet');
is ($graph->output_format(), 'html', 'default output format is html');
is ($graph->timeout(), 5, '5 seconds');
is ($graph->strict(), 1, 'is strict');
is ($graph->nodes(), 0, '0 nodes');
is ($graph->edges(), 0, '0 edges');
is ($graph->border_attribute('graph'), 'none', 'graph border is none');
is ($graph->border_attribute('group'), 'dashed', 'group border is dashed 1px black');
is ($graph->border_attribute('node'), 'solid', 'node border is solid 1px black');
is (join (',', $graph->edges()), '', '0 edges');
like ($graph->output(), qr/table/, 'default output worked');
my $bonn = Graph::Easy::Node->new( name => 'Bonn' );
my $berlin = Graph::Easy::Node->new( 'Berlin' );
my $edge = $graph->add_edge ($bonn, $berlin);
is (ref($edge), 'Graph::Easy::Edge', 'add_edge() returns the new edge');
is ($graph->nodes(), 2, '2 nodes added');
is ($graph->edges(), 1, '1 edge');
is ($graph->as_txt(), "[ Bonn ] --> [ Berlin ]\n", 'as_txt for 2 nodes');
is (ref($graph->edge($bonn,$berlin)), 'Graph::Easy::Edge', 'edge from objects');
is ($graph->edge($berlin,$bonn), undef, 'berlin not connecting to bonn');
is (ref($graph->edge('Bonn', 'Berlin')), 'Graph::Easy::Edge', 'edge from names');
my @E = $graph->edges();
my $en = '';
for my $e (@E)
{
$en .= $e->style() . '.';
}
is ($en, 'solid.', 'edges() in list context');
#############################################################################
my $ffm = Graph::Easy::Node->new( name => 'Frankfurt a. M.' );
# test add_edge ($n1,$n2, $label)
$graph->add_edge ($ffm, $bonn, 'train');
is ($graph->nodes (), 3, '3 nodes');
is ($graph->edges (), 2, '2 edges');
my $e = $graph->edge ($ffm,$bonn);
is ($e->label(), 'train', 'add_edge($n,$n2,"label") works');
# print $graph->as_ascii();
#############################################################################
# as_txt() (simple nodes)
is ( $graph->as_txt(), <<'HERE',
[ Frankfurt a. M. ] -- train --> [ Bonn ]
[ Bonn ] --> [ Berlin ]
HERE
'as_txt() for 3 nodes with 2 edges');
my $schweinfurt = Graph::Easy::Node->new( name => 'Schweinfurt' );
$graph->add_edge ($schweinfurt, $bonn);
is ($graph->nodes (), 4, '4 nodes');
is ($graph->edges (), 3, '3 edges');
is ( $graph->as_txt(), <<'HERE',
[ Frankfurt a. M. ] -- train --> [ Bonn ]
[ Schweinfurt ] --> [ Bonn ]
[ Bonn ] --> [ Berlin ]
HERE
'as_txt() for 4 nodes with 3 edges');
#############################################################################
# as_txt() (nodes with attributes)
$bonn->set_attribute('class', 'cities');
is ( $graph->as_txt(), <<'HERE'
[ Bonn ] { class: cities; }
[ Frankfurt a. M. ] -- train --> [ Bonn ]
[ Schweinfurt ] --> [ Bonn ]
[ Bonn ] --> [ Berlin ]
HERE
, 'as_txt() for 4 nodes with 3 edges and attributes');
$bonn->set_attribute('border', 'none');
$bonn->set_attribute('color', 'red');
$berlin->set_attribute('color', 'blue');
is ($bonn->attribute('borderstyle'), 'none', 'borderstyle set to none');
is ($bonn->attribute('border'), 'none', 'border set to none');
is ($bonn->border_attribute(), 'none', 'border set to none');
# border is second-to-last, class is the last attribute:
is ( $graph->as_txt(), <<'HERE'
[ Berlin ] { color: blue; }
[ Bonn ] { color: red; border: none; class: cities; }
[ Frankfurt a. M. ] -- train --> [ Bonn ]
[ Schweinfurt ] --> [ Bonn ]
[ Bonn ] --> [ Berlin ]
HERE
, 'as_txt() for 4 nodes with 3 edges and class attribute');
# set only 1px and dashed
$graph->set_attribute('graph', 'border', '1px dotted');
$graph->set_attribute('node', 'border', 'blue solid 2px');
# override "borderstyle"
$graph->set_attribute('graph', 'border-style', 'dashed');
is ($graph->attribute('borderstyle'), 'dashed', 'borderstyle set on graph');
is ($graph->attribute('borderwidth'), '1', 'borderwidth set on graph');
is ($graph->attribute('bordercolor'), '#000000', 'bordercolor is default black');
is ($graph->attribute('border'), 'dashed', 'border set on graph');
is ($graph->border_attribute(), 'dashed', 'border set on graph');
# the same with the class attribute for the graph
is ($graph->attribute('graph','borderstyle'), 'dashed', 'borderstyle set on class graph');
is ($graph->attribute('graph','borderwidth'), '1', 'borderwidth set on class graph');
is ($graph->attribute('graph','bordercolor'), '#000000', 'bordercolor is default black');
is ($graph->attribute('graph','border'), 'dashed', 'border set on class graph');
is ($graph->border_attribute('graph'), 'dashed', 'border set on class graph');
# the same with the class attribute for class "node"
is ($graph->attribute('node','borderstyle'), 'solid', 'borderstyle set on class node');
is ($graph->attribute('node','borderwidth'), '2', 'borderwidth set on class node');
is ($graph->attribute('node','bordercolor'), 'blue', 'borderwidth set on class node');
is ($graph->attribute('node','border'), 'solid 2px blue', 'border set on class node');
is ($graph->border_attribute('node'), 'solid 2px blue', 'border set on class node');
# graph/node/edge attributes come first
# graph "border: dashed" because "black" and "1px" are the defaults
# node "border: solid 2px blue" because these are not the defaults (color/width changed
# means we also get the style explicitely)
is ( $graph->as_txt(), <<'HERE'
graph { border: dashed; }
node { border: solid 2px blue; }
[ Berlin ] { color: blue; }
[ Bonn ] { color: red; border: none; class: cities; }
[ Frankfurt a. M. ] -- train --> [ Bonn ]
[ Schweinfurt ] --> [ Bonn ]
[ Bonn ] --> [ Berlin ]
HERE
, 'as_txt() for 4 nodes with 3 edges and graph/node/edge attributes');
#############################################################################
# output and output_format:
$graph = Graph::Easy->new();
is (ref($graph), 'Graph::Easy', 'new worked');
$graph->add_edge ($bonn, $berlin);
like ($graph->output(), qr/table/, 'default output worked');
$graph->set_attribute('graph', 'output', 'ascii');
is ($graph->output_format(), 'ascii', 'output format changed to ascii');
unlike ($graph->output(), qr/<table>/, 'ascii output worked');
#############################################################################
# add_group()
my $group = $graph->add_group('G');
is (ref($group), 'Graph::Easy::Group', 'add_group()');
#############################################################################
# merge_nodes() with node B in a group (fixed in v0.39)
$graph = Graph::Easy->new();
my ($A,$B) = $graph->add_edge('Bonn','Berlin','true');
$group = $graph->add_group('Cities');
is (scalar $group->nodes(), 0, 'no node in group');
$group->add_node($A);
is (scalar $group->nodes(), 1, 'one node in group');
$group->add_node($B);
is (scalar $group->nodes(), 2, 'one node in group');
is (scalar $graph->nodes(), 2, 'two nodes in graph');
is (scalar $graph->edges(), 1, 'one edge in graph');
is (scalar $group->edges(), 0, 'no edge in group');
$graph->layout();
# the edge is only added in the layout stage
is (scalar $group->edges(), 0, 'no edge leading from/to group');
is (scalar $group->edges_within(), 1, 'one edge in group');
$graph->merge_nodes($A,$B);
is (scalar $graph->edges(), 0, 'no edges in graph');
is (scalar $group->edges_within(), 0, 'no edges in group');
is (scalar $group->edges(), 0, 'no edge leading from/to group');
is (scalar $group->nodes(), 1, 'one node in group');
is (scalar $graph->nodes(), 1, 'one node in graph');
is (keys %{$A->{edges}}, 0, 'no edges in A');
is (keys %{$B->{edges}}, 0, 'no edges in B');
is ($B->{group}, undef, "B's group status got revoked");

View File

@@ -0,0 +1,233 @@
#!/usr/bin/perl -w
# Some basic GraphML tests
use Test::More;
use strict;
use utf8;
BEGIN
{
plan tests => 14;
chdir 't' if -d 't';
use lib '../lib';
use_ok ("Graph::Easy") or die($@);
use_ok ("Graph::Easy::Parser") or die($@);
};
can_ok ('Graph::Easy', qw/
as_graphml
as_graphml_file
/);
#############################################################################
my $graph = Graph::Easy->new();
my $graphml_file = $graph->as_graphml_file();
$graphml_file =~ s/\n.*<!--.*-->\n//;
_compare ($graph, $graphml_file, 'as_graphml and as_graphml_file are equal');
my $graphml = $graph->as_graphml();
like ($graphml, qr/<\?xml version="1.0" encoding="UTF-8"\?>/, 'as_graphml looks like xml');
#############################################################################
# some nodes and edges
$graph->add_edge('Ursel','Viersen');
$graphml = $graph->as_graphml();
like ($graphml, qr/<node.*id="Ursel"/, 'as_graphml contains nodes');
like ($graphml, qr/<node.*id="Viersen"/, 'as_graphml contains nodes');
like ($graphml, qr/<edge.*source="Ursel"/, 'as_graphml contains edge');
like ($graphml, qr/<edge.*target="Viersen"/, 'as_graphml contains edge');
#############################################################################
# some attributes:
# node.foo { color: red; } [A] {class:foo;}-> { color: blue; } [B]
$graph = Graph::Easy->new();
my ($A,$B,$edge) = $graph->add_edge('A','B');
$graph->set_attribute('node.foo','color','red');
$edge->set_attribute('color','blue');
$A->set_attribute('class','foo');
my $result = <<EOT
<key id="d0" for="node" attr.name="color" attr.type="string">
<default>black</default>
</key>
<key id="d1" for="edge" attr.name="color" attr.type="string">
<default>black</default>
</key>
<graph id="G" edgedefault="directed">
<node id="A">
<data key="d0">red</data>
</node>
<node id="B">
</node>
<edge source="A" target="B">
<data key="d1">blue</data>
</edge>
</graph>
</graphml>
EOT
;
_compare($graph, $result, 'GraphML with attributes');
#############################################################################
# some attributes with no default valu with no default value:
# Also test escaping for valid XML:
$edge->set_attribute('label', 'train-station & <Überlingen "Süd">');
$result = <<EOT2
<key id="d0" for="node" attr.name="color" attr.type="string">
<default>black</default>
</key>
<key id="d1" for="edge" attr.name="color" attr.type="string">
<default>black</default>
</key>
<key id="d2" for="edge" attr.name="label" attr.type="string"/>
<graph id="G" edgedefault="directed">
<node id="A">
<data key="d0">red</data>
</node>
<node id="B">
</node>
<edge source="A" target="B">
<data key="d1">blue</data>
<data key="d2">train-station &amp; &lt;Überlingen &quot;Süd&quot;&gt;</data>
</edge>
</graph>
</graphml>
EOT2
;
_compare($graph, $result, 'GraphML with attributes');
#############################################################################
# node names with things that need escaping:
$graph->rename_node('A', '<&\'">');
$result = <<EOT3
<key id="d0" for="node" attr.name="color" attr.type="string">
<default>black</default>
</key>
<key id="d1" for="edge" attr.name="color" attr.type="string">
<default>black</default>
</key>
<key id="d2" for="edge" attr.name="label" attr.type="string"/>
<graph id="G" edgedefault="directed">
<node id="&lt;&amp;&apos;&quot;&gt;">
<data key="d0">red</data>
</node>
<node id="B">
</node>
<edge source="&lt;&amp;&apos;&quot;&gt;" target="B">
<data key="d1">blue</data>
<data key="d2">train-station &amp; &lt;Überlingen &quot;Süd&quot;&gt;</data>
</edge>
</graph>
</graphml>
EOT3
;
_compare($graph, $result, 'GraphML with attributes');
#############################################################################
# double attributes
$graph = Graph::Easy->new();
($A,$B,$edge) = $graph->add_edge('A','B');
my ($C,$D,$edge2) = $graph->add_edge('A','C');
$edge->set_attribute('label','car');
$edge2->set_attribute('label','train');
$result = <<EOT4
<key id="d0" for="edge" attr.name="label" attr.type="string"/>
<graph id="G" edgedefault="directed">
<node id="A">
</node>
<node id="B">
</node>
<node id="C">
</node>
<edge source="A" target="B">
<data key="d0">car</data>
</edge>
<edge source="A" target="C">
<data key="d0">train</data>
</edge>
</graph>
</graphml>
EOT4
;
_compare($graph, $result, 'GraphML with attributes');
#############################################################################
# as_graphml() with groups (bug until v0.63):
$graph = Graph::Easy->new();
my $bonn = Graph::Easy::Node->new('Bonn');
my $cities = $graph->add_group('Cities"');
$cities->add_nodes($bonn);
$result = <<EOT5
<graph id="G" edgedefault="directed">
<graph id="Cities&quot;" edgedefault="directed">
<node id="Bonn">
</node>
</graph>
</graph>
</graphml>
EOT5
;
_compare($graph, $result, 'GraphML with group');
# all tests done
#############################################################################
#############################################################################
sub _compare
{
my ($graph, $result, $name) = @_;
my $graphml = $graph->as_graphml();
$graphml =~ s/\n.*<!--.*-->\n//;
$result = <<EOR
<?xml version="1.0" encoding="UTF-8"?>
<graphml xmlns="http://graphml.graphdrawing.org/xmlns"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns
http://graphml.graphdrawing.org/xmlns/1.0/graphml.xsd">
EOR
. $result unless $result =~ /<\?xml/;
if (!is ($graphml, $result, $name))
{
eval { require Test::Differences; };
if (defined $Test::Differences::VERSION)
{
Test::Differences::eq_or_diff ($result, $graphml);
}
}
}

View File

@@ -0,0 +1,234 @@
#!/usr/bin/perl -w
# Some basic GraphML tests with the format=yED
use Test::More;
use strict;
use utf8;
BEGIN
{
plan tests => 14;
chdir 't' if -d 't';
use lib '../lib';
use_ok ("Graph::Easy") or die($@);
use_ok ("Graph::Easy::Parser") or die($@);
};
can_ok ('Graph::Easy', qw/
as_graphml
as_graphml_file
/);
#############################################################################
my $graph = Graph::Easy->new();
my $graphml_file = $graph->as_graphml_file( format => 'yED' );
$graphml_file =~ s/\n.*<!--.*-->\n//;
_compare ($graph, $graphml_file, 'as_graphml and as_graphml_file are equal');
my $graphml = $graph->as_graphml( format => 'yED' );
like ($graphml, qr/<\?xml version="1.0" encoding="UTF-8"\?>/, 'as_graphml looks like xml');
#############################################################################
# some nodes and edges
$graph->add_edge('Ursel','Viersen');
$graphml = $graph->as_graphml();
like ($graphml, qr/<node.*id="Ursel"/, 'as_graphml contains nodes');
like ($graphml, qr/<node.*id="Viersen"/, 'as_graphml contains nodes');
like ($graphml, qr/<edge.*source="Ursel"/, 'as_graphml contains edge');
like ($graphml, qr/<edge.*target="Viersen"/, 'as_graphml contains edge');
#############################################################################
# some attributes:
# node.foo { color: red; } [A] {class:foo;}-> { color: blue; } [B]
$graph = Graph::Easy->new();
my ($A,$B,$edge) = $graph->add_edge('A','B');
$graph->set_attribute('node.foo','color','red');
$edge->set_attribute('color','blue');
$A->set_attribute('class','foo');
my $result = <<EOT
<key id="d0" for="node" attr.name="color" attr.type="string">
<default>black</default>
</key>
<key id="d1" for="edge" attr.name="color" attr.type="string">
<default>black</default>
</key>
<graph id="G" edgedefault="directed">
<node id="A">
<data key="d0">red</data>
</node>
<node id="B">
</node>
<edge source="A" target="B">
<data key="d1">blue</data>
</edge>
</graph>
</graphml>
EOT
;
_compare($graph, $result, 'GraphML with attributes');
#############################################################################
# some attributes with no default valu with no default value:
# Also test escaping for valid XML:
$edge->set_attribute('label', 'train-station & <Überlingen "Süd">');
$result = <<EOT2
<key id="d0" for="node" attr.name="color" attr.type="string">
<default>black</default>
</key>
<key id="d1" for="edge" attr.name="color" attr.type="string">
<default>black</default>
</key>
<key id="d2" for="edge" attr.name="label" attr.type="string"/>
<graph id="G" edgedefault="directed">
<node id="A">
<data key="d0">red</data>
</node>
<node id="B">
</node>
<edge source="A" target="B">
<data key="d1">blue</data>
<data key="d2">train-station &amp; &lt;Überlingen &quot;Süd&quot;&gt;</data>
</edge>
</graph>
</graphml>
EOT2
;
_compare($graph, $result, 'GraphML with attributes');
#############################################################################
# node names with things that need escaping:
$graph->rename_node('A', '<&\'">');
$result = <<EOT3
<key id="d0" for="node" attr.name="color" attr.type="string">
<default>black</default>
</key>
<key id="d1" for="edge" attr.name="color" attr.type="string">
<default>black</default>
</key>
<key id="d2" for="edge" attr.name="label" attr.type="string"/>
<graph id="G" edgedefault="directed">
<node id="&lt;&amp;&apos;&quot;&gt;">
<data key="d0">red</data>
</node>
<node id="B">
</node>
<edge source="&lt;&amp;&apos;&quot;&gt;" target="B">
<data key="d1">blue</data>
<data key="d2">train-station &amp; &lt;Überlingen &quot;Süd&quot;&gt;</data>
</edge>
</graph>
</graphml>
EOT3
;
_compare($graph, $result, 'GraphML with attributes');
#############################################################################
# double attributes
$graph = Graph::Easy->new();
($A,$B,$edge) = $graph->add_edge('A','B');
my ($C,$D,$edge2) = $graph->add_edge('A','C');
$edge->set_attribute('label','car');
$edge2->set_attribute('label','train');
$result = <<EOT4
<key id="d0" for="edge" attr.name="label" attr.type="string"/>
<graph id="G" edgedefault="directed">
<node id="A">
</node>
<node id="B">
</node>
<node id="C">
</node>
<edge source="A" target="B">
<data key="d0">car</data>
</edge>
<edge source="A" target="C">
<data key="d0">train</data>
</edge>
</graph>
</graphml>
EOT4
;
_compare($graph, $result, 'GraphML with attributes');
#############################################################################
# as_graphml() with groups (bug until v0.63):
$graph = Graph::Easy->new();
my $bonn = Graph::Easy::Node->new('Bonn');
my $cities = $graph->add_group('Cities"');
$cities->add_nodes($bonn);
$result = <<EOT5
<graph id="G" edgedefault="directed">
<graph id="Cities&quot;" edgedefault="directed">
<node id="Bonn">
</node>
</graph>
</graph>
</graphml>
EOT5
;
_compare($graph, $result, 'GraphML with group');
# all tests done
#############################################################################
#############################################################################
sub _compare
{
my ($graph, $result, $name) = @_;
my $graphml = $graph->as_graphml( { format => 'yED' } );
$graphml =~ s/\n.*<!--.*-->\n//;
$result = <<EOR
<?xml version="1.0" encoding="UTF-8"?>
<graphml xmlns="http://graphml.graphdrawing.org/xmlns"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:y="http://www.yworks.com/xml/graphml"
xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns
http://www.yworks.com/xml/schema/graphml/1.0/ygraphml.xsd">
EOR
. $result unless $result =~ /<\?xml/;
if (!is ($result, $graphml, $name))
{
eval { require Test::Differences; };
if (defined $Test::Differences::VERSION)
{
Test::Differences::eq_or_diff ($result, $graphml);
}
}
}

View File

@@ -0,0 +1,703 @@
#!/usr/bin/perl -w
# test as_graphviz() output
use Test::More;
use strict;
BEGIN
{
plan tests => 157;
chdir 't' if -d 't';
use lib '../lib';
use_ok ("Graph::Easy") or die($@);
};
#############################################################################
my $graph = Graph::Easy->new();
is (ref($graph), 'Graph::Easy');
is ($graph->error(), '', 'no error yet');
is ($graph->nodes(), 0, '0 nodes');
is ($graph->edges(), 0, '0 edges');
is (join (',', $graph->edges()), '', '0 edges');
my $grviz = $graph->as_graphviz();
like ($grviz, qr/digraph.*\{/, 'looks like digraph');
unlike ($grviz, qr/#/, 'and has proper comment');
like ($grviz, qr#// Generated#, 'and has proper comment');
#############################################################################
# after first call to as_graphviz, these should now exist:
can_ok ("Graph::Easy::Node", qw/
attributes_as_graphviz
as_graphviz_txt
/);
#############################################################################
# with some nodes
my $bonn = Graph::Easy::Node->new( name => 'Bonn' );
my $berlin = Graph::Easy::Node->new( 'Berlin' );
my $edge = $graph->add_edge ($bonn, $berlin);
$grviz = $graph->as_graphviz();
like ($grviz, qr/Bonn/, 'contains Bonn');
like ($grviz, qr/Berlin/, 'contains Bonn');
like ($grviz, qr/arrowhead=open/, 'contains open arrowheads');
unlike ($grviz, qr/\w+=,/, "doesn't contain empty defintions");
#############################################################################
# with attributes on the graph
$graph->set_attribute( 'graph', 'fill' => 'red' );
like ($graph->as_graphviz(), qr/bgcolor="#ff0000"/, 'contains bgcolor="#ff0000"');
#############################################################################
# with label/label-pos attributes on the graph
$graph->set_attribute( 'graph', 'label' => 'My Label' );
$grviz = $graph->as_graphviz();
like ($grviz, qr/label="My Label"/, 'graph label');
like ($grviz, qr/labelloc=top/, 'default is top (dot 1.1 seems to get this wrong)');
$graph->set_attribute( 'graph', 'label-pos' => 'top' );
$grviz = $graph->as_graphviz();
like ($grviz, qr/label="My Label"/, 'graph label');
like ($grviz, qr/labelloc=top/, 'default is top');
$graph->set_attribute( 'graph', 'label-pos' => 'bottom' );
$grviz = $graph->as_graphviz();
like ($grviz, qr/label="My Label"/, 'graph label');
like ($grviz, qr/labelloc=bottom/, 'now bottom');
#############################################################################
# with some nodes with attributes
$bonn->set_attribute( 'shape' => 'rect' );
$grviz = $graph->as_graphviz();
like ($grviz, qr/[^"]Bonn[^"]/, 'contains Bonn unquoted');
like ($grviz, qr/[^"]Berlin[^"]/, 'contains Bonn unquoted');
like ($grviz, qr/shape=box/, 'contains shape');
#############################################################################
# remapped attributes, quoted attributes
$bonn->set_attributes( {
fill => '#808080',
title => 'title string',
color => 'red',
'border-color' => 'brown',
class => 'city',
} );
$grviz = $graph->as_graphviz();
like ($grviz, qr/fillcolor="#808080"/, 'contains fillcolor');
like ($grviz, qr/tooltip="title string"/, 'contains tooltip');
like ($grviz, qr/color="#a52a2a"/, 'contains color');
like ($grviz, qr/fontcolor="#ff0000"/, 'contains fontcolor');
unlike ($grviz, qr/(city|class)/, "doesn't contain class");
#############################################################################
# quoting (including " in node names)
$bonn->{name} = 'Bonn"';
$grviz = $graph->as_graphviz();
like ($grviz, qr/"Bonn\\""/, 'quoted Bonn"');
$bonn->{name} = 'Bonn und Umgebung';
$grviz = $graph->as_graphviz();
like ($grviz, qr/"Bonn und Umgebung"/, 'quoted "Bonn und Umgebung"');
is (join(",", $graph->_graphviz_remap_edge_style('style', 'bold')), 'style,bold', 'style,bold');
my ($name,$style) = $graph->_graphviz_remap_edge_style('style', 'double');
is ($name, undef, 'style=double suppressed');
is ($style, undef, 'style=double suppressed');
($name,$style) = $graph->_graphviz_remap_edge_style('style', 'solid');
is ($name, undef, 'style=solid suppressed');
is ($style, undef, 'style=solid suppressed');
$bonn->{name} = '2A';
$grviz = $graph->as_graphviz();
like ($grviz, qr/"2A"/, '"2A" must be quoted');
$bonn->{name} = '123';
$grviz = $graph->as_graphviz();
like ($grviz, qr/ 123 /, '"123" needs no quotes');
# strict should come last in this list:
for (qw/0AB graph subgraph edge node Graph Edge Strict strict/)
{
$bonn->{name} = $_;
$grviz = $graph->as_graphviz();
like ($grviz, qr/"$_"/, "'$_' needs quotes");
}
$bonn->set_attribute('label', 'Graph::Easy');
$grviz = $graph->as_graphviz();
like ($grviz, qr/label="Graph::Easy"/, 'label with non \w needs quoting');
#############################################################################
# flow directions
$graph->set_attribute('graph','flow','south');
$grviz = $graph->as_graphviz();
unlike ($grviz, qr/rankdir/, 'flow south needs no rankdir');
like ($grviz, qr/"strict" -> Berlin/, 'edge direction normal');
$graph->set_attribute('graph','flow','west');
$grviz = $graph->as_graphviz();
like ($grviz, qr/rankdir=LR/, 'flow west has LR and reversed edges');
like ($grviz, qr/Berlin -> "strict"/, 'edge direction reversed');
like ($grviz, qr/dir=back/, 'edge direction reversed');
$graph->set_attribute('graph','flow','up');
$grviz = $graph->as_graphviz();
unlike ($grviz, qr/rankdir/, 'flow west has TB and reversed edges');
like ($grviz, qr/Berlin -> "strict"/, 'edge direction reversed');
like ($grviz, qr/dir=back/, 'edge direction reversed');
#############################################################################
# arrow styles
# flow is up, so arrowhead becomes arrowtail:
$graph->set_attribute('edge', 'arrow-style', 'closed');
is ($graph->get_attribute('edge', 'arrow-style'), 'closed');
$grviz = $graph->as_graphviz();
like ($grviz, qr/arrowtail=empty/, 'arrow-style closed => empty');
$graph->set_attribute('edge', 'arrow-style', 'filled');
is ($graph->get_attribute('edge', 'arrow-style'), 'filled');
$grviz = $graph->as_graphviz();
like ($grviz, qr/arrowtail=normal/, 'arrow-style filled => normal');
# set flow to down, so arrowtail becomes arrowhead again
$graph->set_attribute('graph','flow','down');
$grviz = $graph->as_graphviz();
like ($grviz, qr/arrowhead=normal/, 'arrow-style filled => normal');
$graph->del_attribute('edge','arrow-style');
$edge->set_attribute('arrow-style','filled');
is ($graph->error(),'', 'no error');
$grviz = $graph->as_graphviz();
like ($grviz, qr/arrowhead=normal/, 'arrow-style filled => normal');
$edge->set_attribute('arrow-style','none');
is ($graph->error(),'', 'no error');
$grviz = $graph->as_graphviz();
like ($grviz, qr/arrowhead=none/, 'arrow-style none');
#############################################################################
#############################################################################
# undirected edges
my $e = $graph->add_edge('A','B');
$e->undirected(1); $e->bidirectional(0);
$grviz = $graph->as_graphviz();
like ($grviz, qr/A -> B.*arrowhead=none/, 'arrowhead on undirected edge');
like ($grviz, qr/A -> B.*arrowtail=none/, 'arrowtail on undirected edge');
#############################################################################
# bidirectional edges
$e->undirected(0); $e->bidirectional(1);
$grviz = $graph->as_graphviz();
like ($grviz, qr/A -> B.*arrowhead=open/, 'arrowhead on bidirectional edge');
like ($grviz, qr/A -> B.*arrowtail=open/, 'arrowtail on bidirectional edge');
#############################################################################
#############################################################################
# label-color vs. color
$e->bidirectional(0);
$e->set_attribute('color','red');
$e->set_attribute('label-color','blue');
$e->set_attribute('label','A to B');
$grviz = $graph->as_graphviz();
like ($grviz, qr/A -> B \[ color="#ff0000", fontcolor="#0000ff", label/, 'label-color');
#############################################################################
# missing label-color (fall back to color)
$e->del_attribute('label-color');
$grviz = $graph->as_graphviz();
like ($grviz, qr/A -> B \[ color="#ff0000", fontcolor="#ff0000", label/, 'label-color');
$e->del_attribute('label','A to B');
#############################################################################
# no label, no fontcolor nec.:
$e->del_attribute('label');
$grviz = $graph->as_graphviz();
like ($grviz, qr/A -> B \[ color="#ff0000" \]/, 'label-color');
#############################################################################
# link vs. autolink and linkbase
$graph->set_attribute('node','linkbase','http://bloodgate.com/');
$grviz = $graph->as_graphviz();
unlike ($grviz, qr/bloodgate.com/, 'linkbase alone does nothing');
unlike ($grviz, qr/link/, 'linkbase alone does nothing');
$graph->set_attribute('node','autolink','name');
$grviz = $graph->as_graphviz();
like ($grviz, qr/URL="http:\/\/bloodgate.com/, 'linkbase plus link');
$graph->del_attribute('node','autolink');
$graph->set_attribute('graph','autolink','name');
is ($graph->attribute('graph','autolink'), 'name', 'autolink=name');
$grviz = $graph->as_graphviz();
like ($grviz, qr/URL="http:\/\/bloodgate.com/, 'linkbase plus link');
#############################################################################
# link vs. autolink and linkbase
$bonn->set_attribute('point-style', 'star');
is ($graph->error(),'', 'no error');
$grviz = $graph->as_graphviz();
unlike ($grviz, qr/point-style/, 'point-style is filtered out');
#############################################################################
# node shape "none"
$bonn->{name} = 'Bonn';
$bonn->set_attribute( 'shape' => 'none' );
$grviz = $graph->as_graphviz();
like ($grviz, qr/[^"]Bonn[^"]/, 'contains Bonn unquoted');
like ($grviz, qr/Bonn.*shape=plaintext/, 'contains shape=plaintext');
# some different node shapes
for my $s (qw/
invhouse invtrapezium invtriangle
triangle octagon hexagon pentagon house
septagon trapezium
/)
{
$bonn->set_attribute( 'shape' => $s );
$grviz = $graph->as_graphviz();
like ($grviz, qr/[^"]Bonn[^"]/, 'contains Bonn unquoted');
like ($grviz, qr/Bonn.*shape=$s/, "contains shape=$s");
}
#############################################################################
# font-size support
$bonn->set_attribute( 'font-size' => '2em' );
$grviz = $graph->as_graphviz();
like ($grviz, qr/[^"]Bonn[^"]/, 'contains Bonn unquoted');
like ($grviz, qr/Bonn.*fontsize=22/, '11px eq 1em');
#############################################################################
# bold-dash, broad and wide edges
$bonn->set_attribute( 'border-style' => 'broad' );
$grviz = $graph->as_graphviz();
like ($grviz, qr/[^"]Bonn[^"]/, 'contains Bonn unquoted');
like ($grviz, qr/Bonn.*style="filled,setlinewidth\(5\)"/,
'5 pixel for broad border');
#############################################################################
# quoting of special characters
$bonn->set_attribute( 'label' => '$a = 2;' );
$grviz = $graph->as_graphviz();
like ($graph->as_graphviz(), qr/Bonn.*label="\$a = 2;"/, 'contains label unquoted');
$bonn->set_attribute( 'label' => '2"' );
$grviz = $graph->as_graphviz();
like ($grviz, qr/Bonn.*label="2\\""/, 'contains label 2"');
#############################################################################
# groups as clusters
$graph = Graph::Easy->new();
($bonn, $berlin, $edge) = $graph->add_edge ('Bonn', 'Berlin');
my $group = $graph->add_group ('Test:');
$group->add_node($bonn);
$group->add_node($berlin);
$grviz = $graph->as_graphviz();
like ($grviz, qr/subgraph "cluster\d+"\s+\{/, 'contains cluster');
#############################################################################
# nodes w/o links and attributes in a group
$graph = Graph::Easy->new();
$bonn = $graph->add_node ('Bonn');
$berlin = $graph->add_node ('Berlin');
$group = $graph->add_group ('Test:');
$group->add_node($bonn);
$group->add_node($berlin);
$grviz = $graph->as_graphviz();
like ($grviz, qr/Bonn(.|\n)*Berlin(.|\n)*\}(.|\n)*\}/, 'contains nodes inside group');
#############################################################################
# node with border-style: none:
$graph = Graph::Easy->new();
$bonn = $graph->add_node ('Bonn');
$bonn->set_attribute('border-style', 'none');
$grviz = $graph->as_graphviz();
like ($grviz, qr/Bonn.*color="#ffffff".*style=filled/,
'contains color white, style filled');
#############################################################################
# node with shape: rounded;
$bonn->del_attribute('border-style');
$bonn->set_attribute( 'shape' => 'rounded' );
$grviz = $graph->as_graphviz();
like ($grviz, qr/[^"]Bonn[^"]/, 'contains Bonn unquoted');
like ($grviz, qr/Bonn.*style="rounded,filled"/, 'contains rounded,filled');
#############################################################################
# invisible nodes and node with shape: point;
$bonn->del_attribute('border-style');
$bonn->set_attribute( 'shape' => 'invisible' );
$grviz = $graph->as_graphviz();
like ($grviz, qr/[^"]Bonn[^"]/, 'contains Bonn unquoted');
like ($grviz, qr/Bonn.*shape=plaintext/, 'contains shape plaintext');
like ($grviz, qr/Bonn.*label=" "/, 'contains label=" "');
$bonn->del_attribute('border-style');
$bonn->set_attribute( 'shape' => 'point' );
$grviz = $graph->as_graphviz();
like ($grviz, qr/[^"]Bonn[^"]/, 'contains Bonn unquoted');
like ($grviz, qr/Bonn.*shape=plaintext/, 'contains shape plaintext');
like ($grviz, qr/Bonn.*label="*"/, 'contains label="*"');
#############################################################################
# edge styles double and double-dash
$graph = Graph::Easy->new();
($bonn,$berlin,$edge) = $graph->add_edge ('Bonn','Berlin');
$edge->set_attribute('style','double');
$grviz = $graph->as_graphviz();
like ($grviz, qr/[^"]Bonn[^"].*color="#000000:#000000/, 'contains Bonn and black:black');
unlike ($grviz, qr/style="?solid/, "doesn't contain solid");
$edge->set_attribute('style','double-dash');
$grviz = $graph->as_graphviz();
like ($grviz, qr/[^"]Bonn[^"].*color="#000000:#000000/, 'contains Bonn and black:black');
unlike ($grviz, qr/style="?solid/, "doesn't contain solid");
like ($grviz, qr/style="?dashed/, 'contains solid');
#############################################################################
# root node (also testing that a root of '0' actually works)
$graph = Graph::Easy->new();
($bonn,$berlin,$edge) = $graph->add_edge ('0','1');
$graph->set_attribute('root','0');
$grviz = $graph->as_graphviz();
like ($grviz, qr/root=0/, 'contains root=0');
like ($grviz, qr/0.*rank=0/, 'contains rank=0');
$graph = Graph::Easy->new();
($bonn,$berlin,$edge) = $graph->add_edge ('a','b');
$graph->set_attribute('root','b');
$grviz = $graph->as_graphviz();
like ($grviz, qr/root=b/, 'contains root=0');
like ($grviz, qr/b.*rank=0/, 'contains rank=0');
#############################################################################
# headport/tailport
$graph = Graph::Easy->new();
($bonn,$berlin,$edge) = $graph->add_edge ('Bonn','Berlin');
$edge->set_attribute('start','west');
$edge->set_attribute('end','east');
$grviz = $graph->as_graphviz();
like ($grviz, qr/tailport=w/, 'contains tailport=w');
like ($grviz, qr/headport=e/, 'contains headport=e');
# headport/tailport with relative flow
$edge->set_attribute('start','right');
$edge->set_attribute('end','left');
$grviz = $graph->as_graphviz();
like ($grviz, qr/tailport=s/, 'contains tailport=s');
like ($grviz, qr/headport=n/, 'contains headport=n');
#############################################################################
# colorscheme support
$graph = Graph::Easy->new();
($bonn,$berlin,$edge) = $graph->add_edge ('Bonn','Berlin');
$graph->add_group('Cities');
$graph->set_attribute('node','colorscheme','pastel19');
$graph->set_attribute('color','red');
$edge->set_attribute('color','1');
$berlin->set_attribute('color','1');
$berlin->set_attribute('colorscheme','set23');
$bonn->set_attribute('color','1');
$grviz = $graph->as_graphviz();
like ($grviz, qr/graph(.|\n)*color="#ff0000"/, 'contains graph color=#ff0000');
like ($grviz, qr/Bonn.*color="#fbb4ae"/, 'contains Bonn color=#fbb4ae');
like ($grviz, qr/Berlin.*color="#66c2a5"/, 'contains Berlin color=#66c2a5');
like ($grviz, qr/->.*Berlin.*color="#a6cee3"/, 'contains edge with default color 1 from set312');
#############################################################################
# test inheritance of colorscheme for edges, groups and anon things:
$graph->set_attribute('colorscheme','pastel19');
$grviz = $graph->as_graphviz();
like ($grviz, qr/->.*Berlin.*color="#fbb4ae"/, 'contains edge with color 1 from pastel19');
#############################################################################
# autolabel is skipped
$graph->set_attribute('node','autolabel','15');
$grviz = $graph->as_graphviz();
unlike ($grviz, qr/autolabel/, "doesn't contain autolabel");
#############################################################################
# test that the attributes group, rows and columns are skipped
$graph = Graph::Easy->new();
($bonn,$berlin,$edge) = $graph->add_edge ('Bonn','Berlin');
$group = $graph->add_group('Cities');
$group->add_nodes($bonn, $berlin);
$bonn->set_attribute('size','2,2');
$graph->layout();
$grviz = $graph->as_graphviz();
unlike ($grviz, qr/rows=/, 'does not contain rows=');
unlike ($grviz, qr/columns=/, 'does not contain columns=');
unlike ($grviz, qr/group=/, 'does not contain group=');
#############################################################################
# test output of fillcolor and color of groups
$graph = Graph::Easy->new();
($bonn,$berlin,$edge) = $graph->add_edge ('Bonn','Berlin');
$group = $graph->add_group('Cities');
$group->add_nodes($bonn, $berlin);
$group->set_attribute('fill','red');
$group->set_attribute('color','blue');
$graph->layout();
$grviz = $graph->as_graphviz();
like ($grviz, qr/fillcolor="#ff0000"/, 'fillcolor=red');
like ($grviz, qr/fontcolor="#0000ff"/, 'fontcolor=blue');
#############################################################################
# test group class attributes
$graph = Graph::Easy->new();
($bonn,$berlin,$edge) = $graph->add_edge ('Bonn','Berlin');
$group = $graph->add_group('Cities');
$group->add_nodes($bonn, $berlin);
$graph->set_attribute('group','fill','red');
$graph->layout();
$grviz = $graph->as_graphviz();
like ($grviz, qr/cluster(.|\n)*fillcolor="#ff0000"/, 'fillcolor=blue');
#############################################################################
# node->as_graphviz()
$graph = Graph::Easy->new();
($bonn,$berlin,$edge) = $graph->add_edge ('Bonn','Berlin');
$group = $graph->add_group('Cities');
$group->add_nodes($bonn, $berlin);
$grviz = $graph->as_graphviz();
unlike ($grviz, qr/Berlin.*label=.*Berlin/, "label isn't output needlessly");
#############################################################################
# HSV colors and alpha channel should be preserved in output
$graph = Graph::Easy->new();
($bonn,$berlin,$edge) = $graph->add_edge ('Bonn','Berlin');
$group = $graph->add_group('Cities');
$group->add_nodes($bonn, $berlin);
# as hex (not preserved) due to alpha channel
$bonn->set_attribute('color', 'hsv(0, 1.0, 0.5, 0.5)');
$berlin->set_attribute('color', '#ff000080');
# preserved
$graph->set_attribute('color', 'hsv(0, 0.6, 0.7)');
$grviz = $graph->as_graphviz();
like ($grviz, qr/fontcolor="0 0.6 0.7"/, "graph color was preserved");
like ($grviz, qr/Berlin.*fontcolor="#ff000080"/, "Berlin's color got converted");
like ($grviz, qr/Bonn.*fontcolor="#8000007f"/, "Bonn's color got converted");
#############################################################################
# edge label
$graph = Graph::Easy->new();
($bonn,$berlin,$edge) = $graph->add_edge ('Bonn','Berlin');
$edge->set_attribute('label','car');
$grviz = $graph->as_graphviz();
like ($grviz, qr/label=car/, "edge label appears in output");
#############################################################################
# fill as class attribute
$graph = Graph::Easy->new();
($bonn,$berlin,$edge) = $graph->add_edge ('Bonn','Berlin');
$bonn->set_attribute('class','red');
$graph->set_attribute('node.red', 'fill', 'red');
$grviz = $graph->as_graphviz();
like ($grviz, qr/fillcolor="#ff0000"/, "contains fill red");
#############################################################################
# \c in labels
$graph = Graph::Easy->new();
$graph->set_attribute('label', 'foo\cbar');
($bonn,$berlin,$edge) = $graph->add_edge ('Bonn','Berlin');
$bonn->set_attribute('label', 'bar\cbar');
$grviz = $graph->as_graphviz();
unlike ($grviz, qr/\\c/, "no \\c in output");
#############################################################################
# borderwidth == 0 overrides style
$graph = Graph::Easy->new();
($bonn,$berlin,$edge) = $graph->add_edge ('Bonn','Berlin');
$bonn->set_attribute('borderstyle','dashed');
$bonn->set_attribute('borderwidth','0');
$berlin->set_attribute('borderstyle','double');
$berlin->set_attribute('borderwidth','0');
$grviz = $graph->as_graphviz();
print $grviz;
unlike ($grviz, qr/style=.*dashed/, "no dashed in output");
unlike ($grviz, qr/peripheries/, "no peripheries in output");
#############################################################################
# subgraph
#$graph = Graph::Easy->new();
my $g = Graph::Easy->new;
my $a_ = $g->add_group('A');
my $b_ = $g->add_group('B');
my $c = $g->add_group('C');
my $d = $g->add_group('D');
my $n1 = $g->add_node('one');
my $n2 = $g->add_node('two');
my $n3 = $g->add_node('three');
my $n4 = $g->add_node('four');
$a_->add_member($n1);
$b_->add_member($c);
$b_->add_member($n2);
$a_->add_member($b_);
$c->add_member($n3);
$d->add_member($n4);
$grviz = $g->as_graphviz();
is($a_->{_order},1,'subgraph A is level 1');
is($d->{_order},1,'subgraph D is level 1');
is($b_->{_order},2,'subgraph B is level 2');
is($c->{_order},3,'subgraph C is level 3');
like($grviz,qr/subgraph "cluster\d+" \{\n label="A";\n subgraph "cluster\d+" \{/,'subgraph indent');

View File

@@ -0,0 +1,263 @@
#!/usr/bin/perl -w
# Test Graph::Easy::Group and Graph::Easy::Group::Cell
use Test::More;
use strict;
BEGIN
{
plan tests => 72;
chdir 't' if -d 't';
use lib '../lib';
use_ok ("Graph::Easy::Group") or die($@);
use_ok ("Graph::Easy::Group::Cell") or die($@);
use_ok ("Graph::Easy") or die($@);
};
can_ok ("Graph::Easy::Group", qw/
new
error
name
add_node
add_nodes
add_member
has_as_successor
has_as_predecessor
successors
predecessors
nodes
edges
_add_cell _del_cell _cells _clear_cells
del_node
del_edge
del_member
/);
can_ok ("Graph::Easy::Group::Cell", qw/
_set_type
class
/);
#############################################################################
my $group = Graph::Easy::Group->new();
is (ref($group), 'Graph::Easy::Group');
is ($group->error(), '', 'no error yet');
my $graph = Graph::Easy->new();
use_ok ('Graph::Easy::As_txt');
# "insert" into a graph to get default attributes
$group->{graph} = $graph;
is ($group->as_txt(), "( Group \\#0 )\n\n", 'as_txt (empty group)');
is (scalar $group->nodes(), 0, 'no nodes in group');
is (scalar $group->edges(), 0, 'no edges in group');
is ($group->name(), 'Group #0', 'name()');
my $first = Graph::Easy::Node->new( name => 'first' );
my $second = Graph::Easy::Node->new( name => 'second' );
$group->add_node($first);
is (scalar $group->nodes(), 1, 'one node in group');
is ($first->attribute('group'), $group->name(), 'node has group attribute set');
$group->add_nodes($first, $second);
is (scalar $group->nodes(), 2, 'two nodes in group');
is ($second->attribute('group'), $group->name(), 'node has group attribute set');
is ($second->{group}, $group, 'add_nodes() worked');
is ($group->as_txt(), <<HERE
( Group \\#0
[ first ]
[ second ]
)
HERE
, 'as_txt (group with two nodes)');
#############################################################################
# attribute nodeclass
$group = Graph::Easy::Group->new();
$group->set_attributes ( { 'nodeclass' => 'city', } );
is ($first->class(),'node', 'class is "node"');
$group->add_node($first);
is ($first->class(),'node.city', 'class is now "node.city"');
#############################################################################
# Group::Cells
my $c = '_cells';
my $cell = Graph::Easy::Group::Cell->new( group => $group, x => 0, y => 0, );
is (scalar keys %{$group->{$c}}, 1, 'one cell');
my $cells = { '0,0' => $cell };
$cell->_set_type( $cells );
is ($cell->class(), 'group ga', 'group ga');
is ($cell->group( $group->{name} ), $group, "group()");
my $cell2 = Graph::Easy::Group::Cell->new( group => $group, x => 1, y => 0 );
is (scalar keys %{$group->{$c}}, 2, 'one more cell');
$cells->{'1,0'} = $cell2;
my $cell3 = Graph::Easy::Group::Cell->new( group => $group, x => 0, y => -1 );
is (scalar keys %{$group->{$c}}, 3, 'one more cell');
$cells->{'0,-1'} = $cell3;
my $cell4 = Graph::Easy::Group::Cell->new( group => $group, x => 0, y => 1 );
is (scalar keys %{$group->{$c}}, 4, 'one more cell');
$cells->{'0,1'} = $cell4;
is ($cell2->group( $group->{name} ), $group, "group()");
$cell->_set_type( $cells );
is ($cell->class(), 'group gl', 'group gl');
#############################################################################
# attributes on cells
# The default attributes are returned by attribute():
is ($group->attribute('border-style'), 'dashed', 'group border');
is ($group->attribute('borderstyle'), 'dashed', 'group border');
is ($cell->attribute('border'), '', 'default border on this cell');
is ($cell->attribute('border-style'), 'dashed', 'default border on this cell');
is ($group->default_attribute('border-style'), 'dashed', 'group is dashed');
is ($cell->default_attribute('border'), 'dashed 1px #000000', 'dashed border on this cell');
is ($cell->default_attribute('border-style'), 'dashed', 'dashed border on this cell');
is ($group->default_attribute('fill'), '#a0d0ff', 'fill on group');
is ($group->attribute('fill'), '#a0d0ff', 'fill on group');
is ($cell->default_attribute('fill'), '#a0d0ff', 'fill on group cell');
is ($cell->attribute('fill'), '#a0d0ff', 'fill on group cell');
#############################################################################
# del_cell();
#print join (" ", keys %{$group->{cells}}),"\n";
is (scalar keys %{$group->{$c}}, 4, 'one less');
$group->_del_cell($cell);
is (scalar keys %{$group->{$c}}, 3, 'one less');
is ($cell->group(), undef, "no group() on deleted cell");
#############################################################################
# del_node() & del_edge(), when node/edge are in a group (bug until 0.39)
$graph = Graph::Easy->new();
$group = $graph->add_group('group');
my ($A,$B,$E) = $graph->add_edge('A','B','E');
for my $m ($A,$B,$E)
{
$group->add_member($m);
}
is ($group->nodes(), 2, '2 nodes in group');
is ($group->edges(), 0, '0 edges going from/to group');
is ($group->edges_within(), 1, '1 edge in group');
is ($A->attribute('group'), $group->name(), 'group attribute got added');
$graph->del_node($A);
is ($A->attribute('group'), '', 'group attribute got deleted');
is ($group->nodes(), 1, '1 node in group');
is ($group->edges(), 0, '0 edges in group');
($A,$B,$E) = $graph->add_edge('A','B','E');
$group->add_member($A);
$group->add_member($E);
is ($group->nodes(), 2, '2 nodes in group');
is ($group->edges(), 0, '0 edges going from/to group');
is ($group->edges_within(), 1, '1 edge in group');
$graph->del_edge($E);
is ($group->nodes(), 2, '2 nodes in group');
is ($group->edges(), 0, '0 edges in group');
is ($group->edges_within(), 0, '0 edges in group');
#############################################################################
# successors and predecessors
$graph = Graph::Easy->new();
$group = $graph->add_group('group');
my ($g1,$bonn) = $graph->add_edge($group, 'Bonn');
my ($berlin,$g2) = $graph->add_edge('Berlin', $group);
is ($group->has_as_successor($bonn), 1, 'group -> bonn');
is ($group->has_as_successor($berlin), 0, '! group -> berlin');
is ($group->has_as_predecessor($berlin), 1, 'berlin -> group');
is ($group->has_as_predecessor($bonn), 0, '! bonn -> group');
is ($bonn->has_as_successor($group), 0, '! group -> bonn');
is ($berlin->has_as_predecessor($group), 0, 'group -> berlin');
is ($bonn->has_as_predecessor($group), 1, 'bonn -> group');
my @suc = $group->successors();
is (scalar @suc, 1, 'one successor');
is ($suc[0], $bonn, 'group => bonn');
#############################################################################
# add_node('Bonn'), add_member('Bonn','Berlin') etc.
$graph = Graph::Easy->new();
$group = $graph->add_group('group');
$bonn = $group->add_node('Bonn');
is (ref($bonn), 'Graph::Easy::Node', "add_node('Bonn') works for groups");
($bonn,$berlin) = $group->add_nodes('Bonn','Berlin');
is (ref($bonn), 'Graph::Easy::Node', "add_nodes('Bonn') works for groups");
is ($bonn->name(), 'Bonn', "add_nodes('Bonn') works for groups");
is (ref($berlin), 'Graph::Easy::Node', "add_nodes('Berlin') works for groups");
is ($berlin->name(), 'Berlin', "add_nodes('Berlin') works for groups");
# add_edge()
my $edge = $group->add_edge('Bonn','Kassel');
my $kassel = $graph->node('Kassel');
is (ref($kassel), 'Graph::Easy::Node', "add_edge('Bonn','Kassel') works for groups");
# add_edge_once()
$edge = $group->add_edge_once('Bonn','Kassel');
my @edges = $graph->edges('Bonn','Kassel');
is (scalar @edges, 1, 'one edge from Bonn => Kassel');
# add_edge() twice
$edge = $group->add_edge('Bonn','Kassel');
@edges = $graph->edges('Bonn','Kassel');
is (scalar @edges, 2, 'two edges from Bonn => Kassel');

View File

@@ -0,0 +1,7 @@
# Elan and Roy on Teamwork
# http://www.cafepress.com/orderofthestick.10272636?zoom=yes#zoom
[ Teamwork ] { background: yellow; title: Elan; }
-is the key to --> [ Victory ] { background: red; title: Roy; }
--> [ Order of the Stick ] { background: #f080a0; }
[ Victory ] -Treasure--> [ Haley ] { background: lightblue; }

View File

@@ -0,0 +1,23 @@
# Three non-overlapping groups
group.dmz { background: #ffa070; }
group.outer { background: #f07070; }
( Internal Network
[ Workstation ]
) { background: #70b070; }
[ Workstation ] --> [ Inner Firewall ]
( DMZ
[ Inner Firewall ] -> [ Proxy ]
--> [ Outer Firewall ]
) { class: dmz; }
( Outer
[ Internet ]
) { class: outer }
[ Outer Firewall ] --> [ Internet ]
[ Proxy ] --> [ Database ]

View File

@@ -0,0 +1,10 @@
# groups (with border none)
group.cities { label: Cities; }
( Cities
[ Ulm ] -> [ Lahn ] -> [ Bonn ]
-> [ Trier ]
) { class: cities; background: #ff80a0; border: none; }
[ Koblenz ] -> [ Berlin ]
[ Frankfurt ]

View File

@@ -0,0 +1,200 @@
#!/usr/bin/perl -w
use Test::More;
use strict;
use File::Spec;
sub _write_utf8_file
{
my ($out_path, $contents) = @_;
open my $out_fh, '>:encoding(utf8)', $out_path
or die "Cannot open '$out_path' for writing - $!";
print {$out_fh} $contents;
close($out_fh);
return;
}
# test graphviz (dot) file input => ASCII output
# and back to as_txt() again
BEGIN
{
plan tests => 140;
chdir 't' if -d 't';
use lib '../lib';
use_ok ("Graph::Easy") or die($@);
use_ok ("Graph::Easy::Parser") or die($@);
use_ok ("Graph::Easy::Parser::Graphviz") or die($@);
};
my @warnings;
#############################################################################
# override the warn method to catch warnigs
{
no warnings 'redefine';
package Graph::Easy::Base;
sub warn {
my ($self,$msg) = @_;
push @warnings, $msg;
}
}
#############################################################################
# parser object
my $def_parser = Graph::Easy::Parser->new(debug => 0);
is (ref($def_parser), 'Graph::Easy::Parser');
is ($def_parser->error(), '', 'no error yet');
my $dot_parser = Graph::Easy::Parser::Graphviz->new(debug => 0);
is (ref($dot_parser), 'Graph::Easy::Parser::Graphviz');
is ($dot_parser->error(), '', 'no error yet');
my $dir = File::Spec->catdir('in','dot');
opendir DIR, $dir or die ("Cannot read dir '$dir': $!");
my @files = readdir(DIR); closedir(DIR);
opendir DIR, 'dot' or die ("Cannot read dir 'dot': $!");
push @files, readdir(DIR); closedir(DIR);
binmode (STDERR, ':utf8') or die ("Cannot do binmode(':utf8') on STDERR: $!");
binmode (STDOUT, ':utf8') or die ("Cannot do binmode(':utf8') on STDOUT: $!");
eval { require Test::Differences; };
foreach my $f (sort {
$a =~ /^(\d+)/; my $a1 = $1 || '1';
$b =~ /^(\d+)/; my $b1 = $1 || '1';
$a1 <=> $b1 || $a cmp $b;
} @files)
{
my $file = File::Spec->catfile($dir,$f);
my $parser = $def_parser;
if (!-f $file)
{
$file = File::Spec->catfile('dot',$f);
next unless -f $file; # only files
# for files in t/dot, we need to use the Graphviz parser as they
# look like Graph::Easy text to the normal parser, which then fails
$parser = $dot_parser;
}
next unless $f =~ /\.dot/; # ignore anything else
print "# at $f\n";
my $txt = readfile($file);
$parser->reset();
my $graph = $parser->from_text($txt); # reuse parser object
$f =~ /^(\d+)/;
my $nodes = $1;
if (!defined $graph)
{
fail ("Graphviz input was invalid: " . $parser->error());
next;
}
is (scalar $graph->nodes(), $nodes, "$nodes nodes");
# for slow testing machines
$graph->timeout(20);
my $ascii = $graph->as_ascii();
my $of = $f; $of =~ s/\.dot/\.txt/;
my $out_path = File::Spec->catfile('out','dot',$of);
my $out = readfile($out_path);
$out =~ s/(^|\n)#[^# ]{2}.*\n//g; # remove comments
$out =~ s/\n\n\z/\n/mg; # remove empty lines
# print "txt: $txt\n";
# print "ascii: $ascii\n";
# print "should: $out\n";
if (!
is ($ascii, $out, "from $f"))
{
if ($ENV{__SHLOMIF__UPDATE_ME})
{
_write_utf8_file($out_path, $ascii);
}
if (defined $Test::Differences::VERSION)
{
Test::Differences::eq_or_diff ($ascii, $out);
}
else
{
fail ("Test::Differences not installed");
}
}
# if the txt output differes, read it in
my $f_txt = File::Spec->catfile('txt','dot',$of);
if (-f $f_txt)
{
$txt = readfile($f_txt);
}
$graph->debug(1);
if (!
is ($graph->as_txt(), $txt, "$f as_txt"))
{
if ($ENV{__SHLOMIF__UPDATE_ME})
{
_write_utf8_file($f_txt, scalar( $graph->as_txt() ));
}
if (defined $Test::Differences::VERSION)
{
Test::Differences::eq_or_diff ($graph->as_txt(), $txt);
}
else
{
fail ("Test::Differences not installed");
}
}
# print a debug output
my $debug = $ascii;
$debug =~ s/\n/\n# /g;
print "# Generated:\n#\n# $debug\n";
}
# check that only the expected warnings were generated
use Data::Dumper;
print STDERR Dumper(\@warnings) unless
is (scalar @warnings, 6, 'Got exactly 6 warnings');
my $i = 0;
for my $name (qw/bar foo pname fname bar brabble/)
{
like ($warnings[$i], qr/Ignoring unknown attribute '$name' for class/,
"Got warning about $name");
$i++;
}
1;
sub readfile
{
my ($file) = @_;
open my $FILE, $file or die ("Cannot read file $file: $!");
binmode ($FILE, ':utf8') or die ("Cannot do binmode(':utf8') on $FILE: $!");
local $/ = undef; # slurp mode
my $doc = <$FILE>;
close $FILE;
$doc;
}

View File

@@ -0,0 +1,99 @@
#!/usr/bin/perl -w
# Test the Heap structure for A*
use Test::More;
use strict;
BEGIN
{
plan tests => 72;
chdir 't' if -d 't';
use lib '../lib';
use_ok ("Graph::Easy::Layout") or die($@);
use_ok ("Graph::Easy") or die($@);
};
can_ok ("Graph::Easy::Heap", qw/
add
extract_top
elements
delete
/);
my $heap = Graph::Easy::Heap->new();
#############################################################################
# heap tests
is (ref($heap), 'Graph::Easy::Heap', 'new() worked');
is ($heap->elements(), 0, '0 elements');
# add some elements (some of them with the same weight)
$heap->add( [ 1, '', 0,0] ); is ($heap->elements(), 1, '1 elements');
$heap->add( [ 1, '', 1,0] ); is ($heap->elements(), 2, '2 elements');
$heap->add( [ 2, '', 2,0] ); is ($heap->elements(), 3, '3 elements');
$heap->add( [ 2, '', 3,0] ); is ($heap->elements(), 4, '4 elements');
$heap->add( [ 2, '', 4,0] ); is ($heap->elements(), 5, '5 elements');
$heap->add( [ 2, '', 5,0] ); is ($heap->elements(), 6, '6 elements');
$heap->add( [ 3, '', 6,0] ); is ($heap->elements(), 7, '7 elements');
# extract them again
for (my $i = 0; $i < 7; $i++)
{
my $e = $heap->extract_top(); is ($e->[2], $i, "elem $i extracted");
}
#############################################################################
# add some elements (some of them with the same weight)
$heap->add( [ 1, '', 0,0] ); is ($heap->elements(), 1, '1 elements');
$heap->add( [ 1, '', 1,0] ); is ($heap->elements(), 2, '2 elements');
$heap->add( [ 2, '', 2,0] ); is ($heap->elements(), 3, '3 elements');
$heap->add( [ 2, '', 3,0] ); is ($heap->elements(), 4, '4 elements');
$heap->add( [ 2, '', 4,0] ); is ($heap->elements(), 5, '5 elements');
$heap->add( [ 2, '', 5,0] ); is ($heap->elements(), 6, '6 elements');
$heap->add( [ 3, '', 7,0] ); is ($heap->elements(), 7, '7 elements');
# supposed to end at the end of the row of "2"
$heap->add( [ 2, '', 6,0] ); is ($heap->elements(), 8, '8 elements');
# extract them again
for (my $i = 0; $i < 8; $i++)
{
my $e = $heap->extract_top(); is ($e->[2], $i, "elem $i extracted");
}
is ($heap->elements(), 0, '0 elements');
#############################################################################
# overflow the simple algorithm (more than 16) and use binary search for add
for (my $i = 0; $i < 8; $i++)
{
$heap->add( [ 1, '', $i,0] );
}
is ($heap->elements(), 8, '8 elements');
for (my $i = 0; $i < 7; $i++)
{
$heap->add( [ 2, '', $i+8,0] );
}
is ($heap->elements(), 15, '15 elements');
for (my $i = 0; $i < 16; $i++)
{
$heap->add( [ 3, '', $i+8+8,0] );
}
is ($heap->elements(), 31, '31 elements');
# supposed to end at the end of the row of "2"
$heap->add( [ 2, '', 15,0] );
is ($heap->elements(), 32, '32 elements');
# extract them again
for (my $i = 0; $i < 32; $i++)
{
my $e = $heap->extract_top(); is ($e->[2], $i, "elem $i extracted");
}

View File

@@ -0,0 +1,454 @@
#!/usr/bin/perl -w
use Test::More;
use strict;
BEGIN
{
plan tests => 74;
chdir 't' if -d 't';
use lib '../lib';
use_ok ("Graph::Easy") or die($@);
};
#############################################################################
my $graph = Graph::Easy->new();
is (ref($graph), 'Graph::Easy');
is ($graph->error(), '', 'no error yet');
is ($graph->nodes(), 0, '0 nodes');
is ($graph->edges(), 0, '0 edges');
is (join (',', $graph->edges()), '', '0 edges');
my $html = $graph->as_html();
like ($html, qr/<table/, 'looks like HTML to me');
#############################################################################
# with some nodes
my $bonn = Graph::Easy::Node->new( name => 'Bonn' );
my $berlin = Graph::Easy::Node->new( 'Berlin' );
my $edge = $graph->add_edge ($bonn, $berlin);
$html = $graph->as_html();
like ($html, qr/Bonn/, 'contains Bonn');
like ($html, qr/Berlin/, 'contains Berlin');
#############################################################################
# with some nodes with attributes
$bonn->set_attribute( 'autotitle' => 'name' );
$html = $graph->as_html();
like ($html, qr/title='Bonn'/, 'contains title="Bonn"');
unlike ($html, qr/title=['"]Berlin['"]/, "doesn't contain title Berlin");
#############################################################################
# edges do not have a name, will fallback to the label
$edge->set_attribute( 'autotitle' => 'name' );
$html = $graph->as_html();
like ($html, qr/title='Bonn'/, 'contains title="Bonn"');
unlike ($html, qr/title=['"]Berlin['"]/, "doesn't contain title Berlin");
unlike ($html, qr/title=['"]['"]/, "no empty title");
$edge->set_attribute( 'label' => 'my edge' );
$html = $graph->as_html();
like ($html, qr/title="my edge"/, 'contains title="my edge"');
#############################################################################
# check that "shape:" does not appear in CSS or HTML
$bonn->set_attribute( 'shape' => 'circle' );
$graph->set_attribute ( 'node', 'shape', 'ellipse' );
my $css = $graph->css();
$html = $graph->as_html();
unlike ($css, qr/shape/, 'shape does not appear in CSS');
unlike ($html, qr/shape/, 'shape does not appear in HTML');
#############################################################################
# "shape: invisible" should result in an empty td tag w/ "border: none"
$bonn->set_attribute( 'shape' => 'invisible' );
$css = $graph->css();
$html = $graph->as_html();
unlike ($html, qr/display:\s*none/, 'shape invisible is not display: none');
like ($html, qr/td.*border:\s*none/, 'shape invisible results in border: none');
#############################################################################
# label colors
$graph->set_attribute( 'edge', 'label-color' => 'blue' );
$edge->set_attribute( 'label-color' => 'red' );
$css = $graph->css();
$html = $graph->as_html();
unlike ($html, qr/border-bottom:.*;\s*color: #0000ff/, 'no edge is green');
like ($html, qr/border-bottom:.*;\s*color: #ff0000/, 'some edge is red');
#############################################################################
# edge color vs. label colors
$edge->set_attribute( 'color' => 'green' );
$html = $graph->as_html();
unlike ($html, qr/border-bottom:.*#0000ff/, 'no edge got blue');
unlike ($html, qr/border-bottom:.*;\s*color: #0000ff/, 'no edge got blue');
like ($html, qr/border-bottom:.*#008000.*;\s*color: #ff0000/,
'color green, label-color red');
#############################################################################
# caption from label
$graph->set_attribute( 'graph', 'label' => 'My Graph Label' );
$html = $graph->as_html();
like ($html, qr/<td colspan=12 style="text-align: center">My Graph Label<\/td>/,
'graph caption from label');
#############################################################################
# caption with label-pos
$graph->set_attribute( 'graph', 'label' => 'My Graph Label' );
$graph->set_attribute( 'graph', 'label-pos' => 'bottom' );
$html = $graph->as_html();
like ($html, qr/<td colspan=12 style="text-align: center">My Graph Label<\/td>/,
'graph caption from label');
#############################################################################
# html_file includes <title> and charset:
$html = $graph->as_html_file();
my $charset =
quotemeta('<meta http-equiv="Content-Type" content="text/html; charset=utf-8">');
like ($html, qr/$charset/, 'html_file includes charset definition');
like ($html, qr/<title>My Graph Label<\/title>/, 'html_file includes <title>');
#############################################################################
# egdes with links, titles and colors
$graph = Graph::Easy->new();
$edge = $graph->add_edge('Friedrichshafen', 'Immenstaad');
$edge->set_attribute('title', 'Vrooom!');
$edge->set_attribute('color', 'orange');
$edge->set_attribute('text-style', 'none');
$edge->set_attribute('font-size', '1.5em');
$edge->set_attribute('link', 'http://bloodgate.com');
$edge->set_attribute('label', 'Schiff');
# This tests edge->as_html(), which will not be called for normal operations,
# in these cases we would convert the single edge cells to HTML.
my $edge_html = <<EDGE
<td colspan=4 rowspan=4 class='edge' title='Vrooom!'><a href='http://bloodgate.com' style="color: #ffa500; text-decoration: none; font-size: 1.5em">Schiff</a></td>
EDGE
;
is ($edge->as_html(), $edge_html, 'edge->as_html()');
# entire graph as html
$html = $graph->as_html();
$edge_html = <<EDGE_CELL
<td colspan=2 rowspan=2 class="edge lh" style="border-bottom: solid 2px #ffa500;" title="Vrooom!"><a href='http://bloodgate.com' style='color: #ffa500; text-decoration: none; font-size: 1.5em;'>Schiff</a></td>
EDGE_CELL
;
my $like = quotemeta($edge_html);
like ($html, qr/$like/, 'graph->as_html() contains proper edge html');
#############################################################################
# edge style double, double-dash, bold etc
$graph = Graph::Easy->new();
$edge = $graph->add_edge('Friedrichshafen', 'Immenstaad');
$edge->set_attribute('style', 'double');
$edge_html = <<EDGE_2
<td colspan=4 rowspan=4 class='edge'></td>
EDGE_2
;
is ($edge->as_html(), $edge_html, 'edge->as_html()');
$edge_html = <<EDGE_CELL
<td colspan=2 rowspan=2 class="edge lh" style="border-bottom: double #000000;">&nbsp;</td>
EDGE_CELL
;
$like = quotemeta($edge_html);
$html = $graph->as_html();
like ($html, qr/$like/, 'edge->as_html()');
$edge->set_attribute('style', 'double-dash');
$edge_html = <<EDGE_CELL
<td colspan=2 rowspan=2 class="edge lh" style="border-bottom: double #000000;">&nbsp;</td>
EDGE_CELL
;
$like = quotemeta($edge_html);
$html = $graph->as_html();
like ($html, qr/$like/, 'edge->as_html()');
#############################################################################
# edge color and label-color
$edge->set_attribute('label-color', 'blue');
$edge_html = <<EDGE_CELL
<td colspan=2 rowspan=2 class="edge lh" style="border-bottom: double #000000;color: #0000ff;">&nbsp;</td>
EDGE_CELL
;
$like = quotemeta($edge_html);
$html = $graph->as_html();
like ($html, qr/$like/, 'edge->as_html()');
#############################################################################
# a node with a link and a fill color at the same time
my $f = $graph->node('Friedrichshafen');
$f->set_attribute('link', 'http://bloodgate.com');
$f->set_attribute('fill', 'red');
$html = $f->as_html();
is ($html, <<EOF
<td colspan=4 rowspan=4 class='node' style="background: #ff0000"><a href='http://bloodgate.com'>Friedrichshafen</a></td>
EOF
, 'fill is on the TD, not the A HREF');
#############################################################################
# a node with a link and a border at the same time
$f->set_attribute('border', 'orange');
$html = $f->as_html();
is ($html, <<EOF
<td colspan=4 rowspan=4 class='node' style="background: #ff0000;border: solid 1px #ffa500"><a href='http://bloodgate.com'>Friedrichshafen</a></td>
EOF
, 'border is on the TD, not the A HREF');
#############################################################################
# as_html_file() includes the proper classes
$html = $graph->as_html_file();
for my $c (qw/eb lh lv va el sh shl/)
{
like ($html, qr/table.graph \.$c/, "includes '$c'");
}
#############################################################################
# group labels are left-aligned
$graph = Graph::Easy->new();
my $group = $graph->add_group('Cities');
my ($A,$B) = $graph->add_edge('Krefeld', 'Düren');
$group->add_nodes($A,$B);
$css = $graph->css();
like ($css, qr/group[^\}]*text-align: left;/, 'contains text-align: left');
#############################################################################
# setting a graph color does not override nodes/edges/groups
$graph->set_attribute('color', 'red');
$css = $graph->css();
for my $e (qw/node_anon edge group_anon/)
{
unlike ($css, qr/table.graph\s+\.$e\s+\{[^\}]*[^-]color: #ff0000;/m, "contains not $e color red");
}
#############################################################################
# setting a graph font/fill does not override nodes/edges/groups
$graph->set_attribute('font', 'times');
$graph->set_attribute('fill', 'blue');
$graph->set_attribute('font-size', '8em');
$graph->set_attribute('align', 'left');
$css = $graph->css();
unlike ($css, qr/table.graph\s+\{[^\}]*font-family: /m, "doesn't contain font-family");
unlike ($css, qr/table.graph\s+\{[^\}]*fill: /m, "doesn't contain fill");
unlike ($css, qr/table.graph\s+\{[^\}]*color: /m, "doesn't contain color");
unlike ($css, qr/table.graph\s+\{[^\}]*background[^\}]*background/m, "doesn't contain two times background");
unlike ($css, qr/table.graph\s+\{[^\}]*text-align/m, "doesn't contain font-size");
unlike ($css, qr/table.graph\s+\{[^\}]*font-size/m, "doesn't contain text-align");
#############################################################################
# multiline labels with \c, \r, and \l in them
$graph = Graph::Easy->new();
($A,$B) = $graph->add_edge('Köln', 'Rüdesheim');
$A->set_attribute('label', 'Köln\r(am Rhein)\l(NRW)\c(Deutschland)');
$html = $graph->as_html_file();
like ($html,
qr/class='node'>Köln<br><span class="r">\(am Rhein\)<\/span><br><span class="l">\(NRW\)<\/span><br>\(Deutschland\)</,
'Köln with multiline text');
$A->set_attribute('align', 'right');
$html = $graph->as_html_file();
like ($html,
qr/class='node' style="text-align: center"><span class="r">Köln<\/span><br><span class="r">\(am Rhein\)<\/span><br><span class="l">\(NRW\)<\/span><br>\(Deutschland\)</,
'Köln with multiline text');
#############################################################################
# multiline labels with "textwrap: N;"
$graph = Graph::Easy->new();
($A,$B) = $graph->add_edge('Köln', 'Rüdesheim');
$A->set_attribute('label', 'Köln\r(am Rhein)\l(NRW)\c(Deutschland)');
$A->set_attribute('textwrap', 10);
#print join (" ", $A->_label_as_html() );
$html = $graph->as_html_file();
like ($html,
qr/class='node'>Köln \(am<br>Rhein\)<br>\(NRW\)<br>\(Deutschland\)</,
'Köln with multiline text');
#############################################################################
# invisible edges
$graph = Graph::Easy->new();
($A,$B,$edge) = $graph->add_edge('Hamm', 'Hagen');
$edge->set_attribute('style','invisible');
$edge->set_attribute('label','foobarbaz');
$edge->set_attribute('color','red');
$html = $graph->as_html_file();
unlike ($html, qr/invisible/, 'no border on invisible edges');
unlike ($html, qr/#ff0000/, 'no color on invisible edges');
unlike ($html, qr/foobarbaz/, 'no label on invisible edges');
#############################################################################
# inheritance of attributes via classes
$graph = Graph::Easy->new();
($A,$B,$edge) = $graph->add_edge('green', 'blue.foo');
$graph->set_attribute('color','red');
$graph->set_attribute('node','color','blue');
$graph->set_attribute('node.foo','color','inherit');
$graph->set_attribute('node.bar','color','green');
$graph->set_attribute('edge','color','inherit');
$graph->set_attribute('edge.foo','color','inherit');
$A->set_attribute('class','bar');
$B->set_attribute('class','foo');
$edge->set_attribute('class','foobar'); # no color set
my ($C,$D,$E) = $graph->add_edge('blue','red');
$E->set_attribute('class','foo'); # inherits red
$D->set_attribute('color','inherit'); # inherits red from graph
is ($A->attribute('color'),'green', 'node.bar is green');
is ($B->attribute('color'),'blue', 'node.foo inherits blue from node');
is ($C->attribute('color'),'blue', 'node is just blue');
is ($D->attribute('color'),'red', 'inherits red from graph');
is ($edge->attribute('color'),'black', 'no color set, so defaults to black');
is ($E->attribute('color'),'red', 'inherit red from graph');
#############################################################################
# comments
$graph = Graph::Easy->new();
($A,$B,$edge) = $graph->add_edge('green', 'blue.foo');
$graph->set_attribute('comment', 'My comment --> graph');
$A->set_attribute('comment', 'My comment --> A');
$edge->set_attribute('comment', 'My comment --> edge');
$html = $graph->as_html_file();
like ($html, qr/<!-- My comment --&gt; graph -->/, 'graph comment');
like ($html, qr/<!-- My comment --&gt; A -->/, 'node comment');
like ($html, qr/<!-- My comment --&gt; edge -->/, 'edge comment');
#############################################################################
# colorscheme and class attributes
$graph = Graph::Easy->new();
($A,$B,$edge) = $graph->add_edge('A', 'B');
$graph->set_attribute('colorscheme', 'pastel19');
$graph->set_attribute('node.yellow', 'fill', '1');
$graph->set_attribute('node.yellow', 'color', 'silver');
$A->set_attribute('class', 'yellow');
$html = $graph->as_html_file();
like ($html,
qr/node_yellow(.|\n)*background: #fbb4ae;/, 'background is not 1');
like ($html,
qr/node_yellow(.|\n)*color: silver;/, 'color is silver');
#############################################################################
# support for \N, \E, \H, \T, \G in titles and labels
$graph = Graph::Easy->new();
($A,$B,$edge) = $graph->add_edge('A', 'B');
$graph->set_attribute('label', 'My Graph');
$graph->set_attribute('node', 'title', 'My \N in \G');
$graph->set_attribute('edge', 'title', 'My \E in \G (\T => \H)');
$html = $graph->as_html_file();
like ($html, qr/title='My A in My Graph'/, 'title with \N and \G');
like ($html, qr/title='My B in My Graph'/, 'title with \N and \G');
like ($html, qr/title="My A->B in My Graph \(A => B\)"/, 'title with \E, \H, \T');
# support for \L in titles
$graph->set_attribute('node', 'label', 'labeled "My \N"');
$graph->set_attribute('node', 'title', 'My \L');
$html = $graph->as_html_file();
like ($html, qr/title='My labeled "My A"'/, 'title with \L');

View File

@@ -0,0 +1 @@
(Group without a name)

View File

@@ -0,0 +1 @@
()->()->()

View File

@@ -0,0 +1,30 @@
( DMZ:
[ Backend ]
[ Database ]
[ Proxy ]
[ Server ]
)
[ Proxy ] --> [ Check ]
[ Proxy ] --> { flow: south; } [ Database ]
[ Proxy ] --> [ Server ]
[ Check ] --> [ Backend ]
[ Database ] --> [ Backend ]
( DMZ1:
[ 1Backend ]
[ 1Database ]
[ 1Proxy ]
[ 1Server ]
)
[ 1Proxy ] --> [ 1Check ]
[ 1Proxy ] --> { flow: south; } [ 1Database ]
[ 1Proxy ] --> [ 1Server ]
[ 1Check ] --> [ 1Backend ]
[ 1Database ] --> [ 1Backend ]
[ 1Proxy ] --> [ 1Check ]
[ 1Proxy ] --> { flow: south; } [ 1Database ]
[ 1Proxy ] --> [ 1Server ]
[ 1Check ] --> [ 1Backend ]
[ 1Database ] --> [ 1Backend ]

View File

@@ -0,0 +1,7 @@
# Different border styles
[ Solid ] ..> [ Dotted ] { border: dotted; }
[ Dashed ] { border: dashed; } ==> [ none ] { border: none; }
[ dot-dash ] { border: dot-dash; } - > [ Bold ] { border: bold; }
[ dot-dot-dash ] { border: dot-dot-dash; } .-> [ wave ] { border: wave; }
[ double-dash ] { border: double-dash; } ~~> [ Double ] { border: double; }

View File

@@ -0,0 +1,19 @@
node { background: yellow; }
[ Bonn ] ..> [ Berlin ] -> [ Kassel ]
[ Bonn ] .-> [ Koblenz ]
[ Bonn ] -> [ Ulm ] -> [ Koblenz ]
[ Ulm ] -> [ Bautzen ] -> [ Berlin ]
[ 1Bonn ] ..> [ 1Berlin ] -> [ 1Kassel ]
[ 1Bonn ] .-> [ 1Koblenz ]
[ 1Bonn ] -> [ 1Ulm ] -> [ 1Koblenz ]
[ 1Ulm ] -> [ 1Bautzen ] -> [ 1Berlin ]
[ 2Bonn ] ..> [ 2Berlin ] -> [ 2Kassel ]
[ 2Bonn ] .-> [ 2Koblenz ]
[ 2Bonn ] -> [ 2Ulm ] -> [ 2Koblenz ]
[ 2Ulm ] -> [ 2Bautzen ] -> [ 2Berlin ]

View File

@@ -0,0 +1,4 @@
[ Hamm ] <--> [ Hamm ]
[ Hamm ] <--> [ Hamm ]
[ Hamm ] <--> [ Hamm ]
[ Hamm ] <--> [ Hamm ]

View File

@@ -0,0 +1,4 @@
( Group
) { background: yellow; }
[ Outside ]

View File

@@ -0,0 +1 @@
[ One ] --> [ One ]

View File

@@ -0,0 +1,4 @@
[ Main ] -- Until not done --> [ Main ]
[ Main ] -- Until not done --> [ Main ]
[ Main ] -- Until not done --> [ Main ]
[ Main ] -- Until not done --> [ Main ]

View File

@@ -0,0 +1 @@
[ Freiburg ] -- Alle Jahre Wieder --> [ Freiburg ]

View File

@@ -0,0 +1 @@
[A] -- [A ] -- [ A ] -- [ A ] -- [A]

View File

@@ -0,0 +1 @@
[ One ]

View File

@@ -0,0 +1,13 @@
[ | C | ]
[ | D | ]
[ | E | ]
[ | F | ]
[|G|]
[ |H| |]
[ C.2 ] -> [ A1 ]
[ D.2 ] -> [ A2 ]
[ E.2 ] -> [ A3 ]
[ F.2 ] -> [ A4 ]
[ G.2 ] -> [ A5 ]
[ H.3 ] -> [ A6 ]

View File

@@ -0,0 +1,4 @@
graph { autolabel: name, 20; autotitle: name; }
[ Bonn ] { label: Bonn (ehemalige Bundeshauptstadt); } -- Acme Travels Incorporated --> [ Frankfurt (Main) / Flughafen ]

View File

@@ -0,0 +1 @@
[ Siegen | Siegburg ]

View File

@@ -0,0 +1,2 @@
[ A \| B | C ]

View File

@@ -0,0 +1,4 @@
[ Bad Schandau ] { offset: 3,0; origin: Erfurt; }
[ Erfurt ] { size: 2,3; }
[ Erfurt ] <--> [ Bad Schandau ]

View File

@@ -0,0 +1,3 @@
[ Bad Schandau ] { offset: 3,0; origin: Erfurt; }
[ Erfurt ] <--> [ Bad Schandau ]

View File

@@ -0,0 +1,3 @@
node.second { border: double; }
[ A ] { class: SECOND; } --> [ B ] { class: Second; }

View File

@@ -0,0 +1,4 @@
.red { color: red; }
.green, .blue, group { color: blue; }
[ Red ] { class: red; } -- red --> { class: red; } [ Black ] { class: green; }

View File

@@ -0,0 +1 @@
[ Hamburg ] { size: 2,2; } --> [ Altona ] { rows: 2; columns: 3; }

View File

@@ -0,0 +1,2 @@
[ Hamburg ] { size: 2,2; } --> [ Altona ] { rows: 2; columns: 3; }
[ Hamburg ] --> [ Altona ]

View File

@@ -0,0 +1,3 @@
[ Hamburg ] { size: 2,2; } --> [ Altona ] { rows: 2; columns: 3; }
[ Hamburg ] --> [ Altona ]
[ Hamburg ] --> [ Altona ]

View File

@@ -0,0 +1,12 @@
# the following should not confuse the parser
#digraph G {
# a -> b
#}
# neither should this
graph { label:
// digraph G {
}
[ Kummersbach ] -> [ Düsburg ]

View File

@@ -0,0 +1 @@
[ Test\n Test\n Test test test\n test ] { border: dot-dot-dash; } ..-> [ B ] { border: dot-dash; }

View File

@@ -0,0 +1,2 @@
[ One ] --> [ Two ]
[ One ] --> [ Two ]

View File

@@ -0,0 +1,4 @@
graph { flow: 90; }
[ Left ] --> { end: left; start: left; } [ Right ]

View File

@@ -0,0 +1,3 @@
graph { label: My Graph; label-pos: top; }
[ Regensburg ] --> [ Passau ]

View File

@@ -0,0 +1,5 @@
( Test\n group
[ Bonn ] -> [ Berlin ]
)

View File

@@ -0,0 +1,5 @@
( Bergtour:
[ Zugspitze ] --> [ Wasserkuppe ]
) { labelpos: bottom; }

View File

@@ -0,0 +1,5 @@
( Some group:
[ Frankfurt a. Main\n (Flughafen) ] { size: 2,2; } -> [ Berlin ]
)

View File

@@ -0,0 +1,5 @@
( Test\n group
[ Bonn ] -> [ Berlin ]
) { border: none; }

View File

@@ -0,0 +1 @@
[] --> [ Berlin ]

View File

@@ -0,0 +1 @@
[ Bonn ] --> [ ]

View File

@@ -0,0 +1,3 @@
node { label: A; }
[ B ] -> [ C ] { link: http://bloodgate.com; }

View File

@@ -0,0 +1 @@
[ Long Node Label\l left\r right\c center ] -- A\r long\n edge label --> [ B ]

View File

@@ -0,0 +1 @@
[ Bonn ] { border-style: dotted; }, [ Berlin ] { border-style: dashed; }

View File

@@ -0,0 +1 @@
[ My\n long\n node\n name ] -- A\n long\n label --> [ B ]

View File

@@ -0,0 +1,3 @@
[ Berlin\n (W) ] -> [ Berlin\n (O) ] { border: dotted 1px black; }

View File

@@ -0,0 +1,6 @@
graph { flow: 180; }
[ Start ] --> [ Main ]
[ Main ] -- Until not done --> [ Main ]
[ Main ] -- Until not done --> [ Main ]
[ Main ] -- Until not done --> [ Main ]

View File

@@ -0,0 +1,5 @@
graph { flow: 180; }
[ Start ] --> [ Main ]
[ Main ] -- Until not done --> [ Main ]
[ Main ] -- Until done --> [ Main ]

View File

@@ -0,0 +1,2 @@
[ Some \[\] ||
Autosplit ]

View File

@@ -0,0 +1,5 @@
graph { textwrap: 6; }
[ Frankfurt Oder\n Flughafen-Terminal ]
-- Drive a car to the destination -->
[ Small city near a beautiful river ] { text-wrap: 10; align: right; }

View File

@@ -0,0 +1 @@
[0] -- 0 --> [10] { label: 0; } --> { label: 0; } [ 0 ]

View File

@@ -0,0 +1 @@
[ One ] ==> [ Two ]

View File

@@ -0,0 +1,5 @@
[ 1 ] -> [ 23.0 ]
[ 2|3 ]

View File

@@ -0,0 +1,4 @@
[ Berlin ] { offset: 2,0; origin: Bonn; }
[ Frankfurt ] { offset: 2,2; origin: Bonn; }
[ Bonn ] --> [ Frankfurt ]

View File

@@ -0,0 +1,8 @@
[ A ] { flow: east; }
[ B ] { offset: 2,2; origin: A; }
(G
[ A ]
)
[ A ] -- C --> { start: east; end: north; } [ B ] -> [ ]

View File

@@ -0,0 +1,2 @@
# A node-cluster (autosplit into three single nodes)
[ Husum | Schleswig | Flensburg ] { background: #ddaaff; }

View File

@@ -0,0 +1,13 @@
graph
{
fill: rgb(0.1, 100, 10%);
color: 3; # mention color "3" before colorscheme
colorscheme: paired12;
}
[ Colors ] { fill: w3c/grey; }
[ Preserve ] { color: cornflowerblue; }
[ The ] { color: #ff00ff; }
[ Preserve ] --> { color: rgb(33,44,55); } [ The ]
[ The ] --> { color: hsv(1.0,1.0,0.5); } [ Colors ] { color: hsl(300, 1.0, 1.0); }

View File

@@ -0,0 +1,3 @@
[ A ] { label: AB%00%0d%0a } --> [ B ] { label: AB%0aCB; }
-> [ C ] { label: AB%be%f7%01%7f%91; }

View File

@@ -0,0 +1,4 @@
edge.yes { label: Yes; }
edge { label: MyLabel; }
[ A ] --> { class: yes; } [ B ] --> [ C ]

View File

@@ -0,0 +1,4 @@
( Router: [ Input ] --> [ Output ] )
[ Output ] ==> { start: south; end: north; }
[ Network ]

View File

@@ -0,0 +1,2 @@
[ A ] --> { start: front; } [ B ], [ C ]

View File

@@ -0,0 +1 @@
([A]->[B])[C]

Some files were not shown because too many files have changed in this diff Show More