Web::Chain project: Web/t/Web-Chain.t
# Test file created outside of h2xs framework.
# Run this like so: `perl Web-Chain.t'
# doom@kzsu.stanford.edu 2004/08/28 18:14:49
#########################
use warnings;
use strict;
$|=1;
use Test::More;
BEGIN { plan tests => 49 };
use Web::Chain;
use Web::Definitions qw($DEBUG);
# $DEBUG = 1; # Turn on verbose reporting
# (over-riding group wide default setting from Web::Definitions)
###=======
### Utility functions used in the following tests
sub check_node_names_against_exists_hash {
# Example:
# check_node_names_against_exists_hash($chain_object);
# (Yes, this is proceedural code that requires an object as an argument. Sue me.)
#
# Compares the existing node names to the ones recorded in the internal _exists hash.
# (I'm getting a difference somewhere, and I want to track it down.)
my $chain = shift;
my $label = shift; # optional label used to prefix report string
my @names = $chain->get_names();
my $hashref = $chain->get_exists_hashref;
my @seens = sort (keys %{ $hashref });
my @sorted_names = sort @names;
($DEBUG) && print STDERR " exists: " . join( ' ', @seens ) . "\n";
($DEBUG) && print STDERR " nodes: " . join( ' ', @sorted_names ) . "\n";
my $first_name = $chain->get_beg->get_name;
is_deeply(\@seens, \@sorted_names ," $label chain beginning with $first_name passes comparison of _exists hash and scan contained nodes");
}
###=======
### Into the tests proper
ok(1); # If we made it this far, we're ok.
# Tests originally created following the SYNOPSIS (other tests added as I went)
#--------
# raw material - node names to be created, some minimal text to use as node bodies,
# initilize: create chain object.
my @node_name=qw(ONE TWO THREE FOUR FIVE SIX);
my @node_body=qw(one two three four five six);
my $chain1 = Web::Chain->new();
#--------
# Add a single node to the chain
$chain1->add_link($node_name[0]); # no "after_node_name" so should be first
$chain1->set_body($node_name[0], \$node_body[0]);
my $test_name = $chain1->get_name_from_offset(0);
is($test_name, $node_name[0], "Newly created node reports correct name");
my $test_body = ${ $chain1->get_body_from_offset(0) };
is($test_body, $node_body[0], "Newly created node reports correct body");
# peeking at internal "_exists" hash to see if the node name was added correctly
my @seen = (keys %{ $chain1->{_exists} } );
is($seen[0], $node_name[0] ,"The chain records that it has seen the name of newly created node");
my $end_name = $chain1->get_end->get_name;
is( $end_name, $node_name[0], "Test that get_end returns the only node of a single node chain.");
#--------
# checking internally used routine: _handle_node_argument
# simplest possible case: undef argument
my $nada = $chain1->_handle_node_argument();
is($nada, undef, "_handle_node_argument returns undef when given an undef");
# given $node, passes through $node)
my $some_node = $chain1->{_beg};
my $test_node = $chain1->_handle_node_argument($some_node);
is($test_node, $some_node, "_handle_node_argument: Given a node object, passes it through");
# should error when given an object of the wrong type (here a chain rather than a node):
eval {
my $test_object = $chain1->_handle_node_argument($chain1);
### TODO play with this stuff some time:
# my $node_class = ref($some_node);
# isa_ok($test_object, $node_class);
# isnt($test_object, $chain1,
# "_handle_node_argument: Given object of wrong type, should error out, not pass through object like this.");
} ;
like($@, qr/ _handle_node_argument: \s+ argument \s+ type \s+ /x,
"_handle_node_argument: errored out when given a non-node object");
# should error when given a name not in use for an existing link
eval {
my $test_object = $chain1->_handle_node_argument('GARUDATECH');
} ;
like($@, qr/ _handle_node_argument: \s+ GARUDATECH \s+ is \s+ not \s+ the \s+ name \s+ of \s+ a \s+ node \s+ /x,
"_handle_node_argument: errored out when given a name for a node that doesn't exist");
#--------
# Continue adding links to the chain one at a time,
# verify that links were added using get_names
for (my $i=1; $i <= $#node_name ; $i++) {
$chain1->add_link($node_name[$i], $node_name[$i-1]);
$chain1->set_body( $node_name[$i], \$node_body[$i] );
}
my @sequence = $chain1->get_names();
is_deeply(\@node_name, \@sequence, "Created chain reports correct node names");
# peeking at internal "_exists" hash to see if the node names are all there:
check_node_names_against_exists_hash($chain1, 'chain1: ');
#--------
### TODO update or DELETE this silly comment:
# Similar checks for the alternate form ((?)), doing it all in one step ((?))
#
# Create a modified set of names and bodies to work with
my @node_name_a = map { $_ . '_A' } @node_name;
my @node_body_a = map { $_ . '_A' } @node_body;
my $last_indicie = $#node_name;
$chain1->add_link($node_name_a[0], $node_name[$last_indicie]);
$chain1->set_body($node_name_a[0], \$node_body_a[0]);
my $next_indicie = $last_indicie + 1;
@sequence = $chain1->get_names();
$test_name = $chain1->get_name_from_offset($next_indicie);
is($test_name, $node_name_a[0], "Newly created node reports correct name");
$test_body = ${ $chain1->get_body_from_offset($next_indicie) };
is($test_body, $node_body_a[0] , "Newly created node reports correct body");
check_node_names_against_exists_hash($chain1, 'chain1: ');
for (my $i=1; $i<=$#node_name; $i++) {
$chain1->add_link($node_name_a[$i], $node_name_a[$i-1], \$node_body_a[$i]);
check_node_names_against_exists_hash($chain1, 'chain1: ');
}
my @sequence_a = $chain1->get_names();
my @test_sequence;
push @test_sequence, @node_name;
push @test_sequence, @node_name_a;
# use Data::Dumper; print Dumper(@test_sequence);
is_deeply(\@test_sequence, \@sequence_a, "Expanded chain reports correct node names");
check_node_names_against_exists_hash($chain1, 'chain1: ');
### ### Looking for confusion about using references:
### my $chain2 = Web::Chain->new();
### my $node_body_2th = "foal shoal and leavened scrogg awhirl";
### $chain2->add_link("SOME_NODE"); # no "after_node_name" so should be first
### $chain2->set_body("SOME_NODE", \$node_body_2th);
### my $node_body_original = $node_body_2th; # save copy of original
### $node_body_2th = "nadzornika!"; # modify original
### my $test_body_2th = ${ $chain2->get_body_from_offset(0) };
### is($test_body_2th, $node_body_original, "Newly created node reports correct body, even though original variable changed.");
### The above fails, as expected.
### Trying an off the top of head fix:
sub anonymoose {
my $honker = '';
return \$honker;
}
my $chain3 = Web::Chain->new();
my $node_body_3rd = "foal shoal and leavened scrogg awhirl our faddah art";
my $node_body_3rd_ref = anonymoose();
${ $node_body_3rd_ref } = $node_body_3rd;
$chain3->add_link("SOME_NOD"); # no "after_node_name" so should be first
$chain3->set_body("SOME_NOD", $node_body_3rd_ref);
my $node_body_original_3rd = $node_body_3rd; # save copy of original
$node_body_3rd = "nadzornika!"; # modify original
# Trying to throw a different curve, use the moose generator again, make sure it
# returns a new ref.
my $node_body_3rd_B_ref = anonymoose();
${ $node_body_3rd_B_ref } = "say what?";
my $test_body_3rd = ${ $chain3->get_body_from_offset(0) };
is($test_body_3rd, $node_body_original_3rd, "Newly created node reports correct body, even though original variable changed.");
### TODO
### Okay, so I *think* that I understand *that* shit.
### (Though I bet there's a better solution than a moose generator)
### But I think there's still *another* problem I missed:
### New chains are *not* new, but instead seem to be different pointers
### to the same data. Check to see if that's true, and what you need
### to do to fix your news...
my $chain_fred = Web::Chain->new();
my $chain_alice = Web::Chain->new();
my $chain_ted = Web::Chain->new();
$chain_fred-> add_link("OH_FREDDY");
$chain_alice->add_link("OH_ALICE");
$chain_ted-> add_link("OH_TED");
check_node_names_against_exists_hash($chain_fred, 'chain_fred i: '); # TEMP
my @sequence_fred = $chain_fred->get_names();
check_node_names_against_exists_hash($chain_fred, 'chain_fred h: '); # TEMP
is_deeply(\@sequence_fred, [ qw(OH_FREDDY) ], "different chains are different");
### checking simple node re-name behavior
$chain_fred->rename_node( 'OH_FREDDY', 'FREDDY_NO' );
check_node_names_against_exists_hash($chain_fred, 'chain_fred g: '); # TEMP
# Older method:
# my $node_fred_1 = $chain_fred->get_node_from_name("OH_FREDDY");
# $node_fred_1->name("FREDDY_NO");
@sequence_fred = $chain_fred->get_names();
check_node_names_against_exists_hash($chain_fred, 'chain_fred f: '); # TEMP
is_deeply(\@sequence_fred, [ qw(FREDDY_NO) ], "Renaming node in single node chain");
my $node_fred_2 = $chain_fred->add_link("OH_FREDDY","FREDDY_NO");
check_node_names_against_exists_hash($chain_fred, 'chain_fred e: '); # TEMP
my $node_fred_3 = $chain_fred->add_link("GO_FREDDY","OH_FREDDY");
check_node_names_against_exists_hash($chain_fred, 'chain_fred d: '); # TEMP
@sequence_fred = $chain_fred->get_names();
($DEBUG) && print STDERR " chain_fred: " . join(" ", @sequence_fred) . "\n";
is_deeply(\@sequence_fred, [qw(FREDDY_NO OH_FREDDY GO_FREDDY)],
"Created chain without name collison because of previous rename");
$chain_fred->add_link("YABBA", "GO_FREDDY");
check_node_names_against_exists_hash($chain_fred, 'chain_fred c: '); # TEMP
$chain_fred->add_link("DABBA", "YABBA");
check_node_names_against_exists_hash($chain_fred, 'chain_fred b: '); # TEMP
$chain_fred->add_link("DOOO", "DABBA");
check_node_names_against_exists_hash($chain_fred, 'chain_fred a: '); # TEMP
@sequence_fred = $chain_fred->get_names();
($DEBUG) && print STDERR " chain_fred: " . join(" ", @sequence_fred) . "\n";
#--------
# Working with segments of a chain:
# testing extract_segment method
{
check_node_names_against_exists_hash($chain_fred, 'chain_fred 1: '); # TEMP
my $extracted_chain = $chain_fred->extract_segment("OH_FREDDY", "DABBA");
check_node_names_against_exists_hash($chain_fred, 'chain_fred 2: '); # TEMP
check_node_names_against_exists_hash($extracted_chain, 'extracted_chain: '); # TEMP
my @sequence_extracted = $extracted_chain->get_names();
($DEBUG) && print STDERR " extract from chain_fred: " . join(" ", @sequence_extracted) . "\n";
is_deeply( \@sequence_extracted, [ qw(OH_FREDDY GO_FREDDY YABBA DABBA) ], "extract_segment method retrieves the right nodes");
check_node_names_against_exists_hash($chain_fred, 'chain_fred 3: '); # TEMP
my @sequence_left_behind = $chain_fred->get_names();
($DEBUG) && print STDERR " left behind in chain_fred: " . join(" ", @sequence_left_behind) . "\n";
is_deeply( \@sequence_left_behind, [ qw(FREDDY_NO DOOO) ], "extract_segment method left behind the right nodes");
}
# testing insert_segment
# $chain1->insert_segment( $chain2, $after_node_name );
{
@sequence_fred = $chain_fred->get_names();
($DEBUG) && print STDERR " chain_fred: " . join(" ", @sequence_fred) . "\n";
my @new_nodes = qw(BIRD MAKES NEST IN_THE_AIR);
my $chain_jane = Web::Chain->new();
# By going in reverse order, can prepend to front of chain without specifying location
foreach my $name (reverse @new_nodes) {
$chain_jane->add_link($name);
### TODO set body, too?
}
my @expected_sequence = @sequence_fred;
push @expected_sequence, @new_nodes;
check_node_names_against_exists_hash($chain_jane, 'chain_jane: '); # TEMP
$chain_fred->insert_segment( $chain_jane, 'DOOO' );
check_node_names_against_exists_hash($chain_jane, 'chain_jane: '); # TEMP
check_node_names_against_exists_hash($chain_fred, 'chain_fred 4: '); # TEMP **badun***
my @new_sequence = $chain_fred->get_names();
($DEBUG) && print STDERR " chain_fred: " . join(" ", @sequence_fred) . "\n";
is_deeply(\@new_sequence, \@expected_sequence, "used insert_segment to append one chain to another");
}
# globals for the following tests
my @node_letter_names = qw(NODE_A NODE_B NODE_C NODE_D NODE_E NODE_F NODE_G NODE_H);
my @node_numb_names = qw(NODE_1 NODE_2 NODE_3 NODE_4);
my $chain_letter = Web::Chain->new();
my $chain_numb = Web::Chain->new();
{
foreach my $name (reverse @node_letter_names) { # going in reverse, prepend to front of chain
$chain_letter->add_link($name);
}
foreach my $name (reverse @node_numb_names) { # going in reverse, prepend to front of chain
$chain_numb->add_link($name);
}
my @expected_sequence = qw(NODE_A NODE_B NODE_C NODE_D NODE_E NODE_1 NODE_2 NODE_3 NODE_4 NODE_F NODE_G NODE_H);
$chain_letter->insert_segment( $chain_numb, 'NODE_E' );
my @new_sequence = $chain_letter->get_names();
($DEBUG) && print STDERR " chain_letter: " . join(" ", @new_sequence) . "\n";
is_deeply(\@new_sequence, \@expected_sequence, "used insert_segment to insert chain in the middle of another");
}
# Using freshly re-defined chains
undef $chain_letter;
undef $chain_numb;
$chain_letter = Web::Chain->new();
$chain_numb = Web::Chain->new();
{
foreach my $name (reverse @node_letter_names) { # going in reverse, prepend to front of chain
$chain_letter->add_link($name);
}
foreach my $name (reverse @node_numb_names) { # going in reverse, prepend to front of chain
$chain_numb->add_link($name);
}
my @expected_sequence =
qw(NODE_1 NODE_2 NODE_3 NODE_4
NODE_A NODE_B NODE_C NODE_D NODE_E NODE_F NODE_G NODE_H
);
$chain_letter->insert_segment( $chain_numb );
my @new_sequence = $chain_letter->get_names();
($DEBUG) && print STDERR " chain_letter: " . join(" ", @new_sequence) . "\n";
is_deeply(\@new_sequence, \@expected_sequence, "used insert_segment to prepend a chain");
}
# Testing the invalid chain argument check: _check_chain_argument
{
my $some_node = $chain_letter->get_beg;
my $another_node = $some_node->next;
eval {
$chain_letter->insert_segment( $some_node, $another_node ); # first argument is supposed to be a chain
};
if ($@) {
like($@, qr/_check_chain_argument: \s+ argument \s+ is \s+ of \s+ type \s+ /x,
"_check_chain_argument: insert_chain errored out when given a non-chain argument");
}
}
#--------
# testing move_segment
# $chain1->move_segment($begin_name, $end_name, $after_node_name);
{
my @sequence_numb = $chain_numb->get_names();
($DEBUG) && print STDERR " chain_numb: " . join(" ", @sequence_numb) . "\n";
# Current states:
# chain_letter: NODE_1 NODE_2 NODE_3 NODE_4 NODE_A NODE_B NODE_C NODE_D NODE_E NODE_F NODE_G NODE_H
# chain_numb: NODE_1 NODE_2 NODE_3 NODE_4 NODE_A NODE_B NODE_C NODE_D NODE_E NODE_F NODE_G NODE_H
my $begin_name = 'NODE_3';
my $end_name = 'NODE_4';
my $location = 'NODE_1';
$chain_numb->move_segment($begin_name, $end_name, $location);
my @expected = qw( NODE_1 NODE_3 NODE_4 NODE_2 NODE_A NODE_B NODE_C NODE_D NODE_E NODE_F NODE_G NODE_H );
my @new_sequence = $chain_numb->get_names();
is_deeply(\@new_sequence, \@expected, "Used move_segment to re-arrange nodes")
}
#--------
# move_segment - bounds checking
# trying to move a segment into itself should error.
# Using freshly re-defined chain
undef $chain_letter;
$chain_letter = Web::Chain->new();
{
foreach my $name (reverse @node_letter_names) { # going in reverse, prepend to front of chain
$chain_letter->add_link($name);
}
my @sequence_numb = $chain_letter->get_names();
($DEBUG) && print STDERR " chain_numb: " . join(" ", @sequence_numb) . "\n";
# Expected state:
# NODE_A NODE_B NODE_C NODE_D NODE_E NODE_F NODE_G NODE_H
# (see @node_letter_names)
my $begin_name = 'NODE_B';
my $end_name = 'NODE_E';
my $location = 'NODE_C';
eval {
$chain_numb->move_segment($begin_name, $end_name, $location);
};
if ($@) {
like($@, qr/move_segment: \s+ Can't \s+ move \s+ a \s+ sub-segment \s+ of \s+ a \s+ chain \s+ into \s+ itself: \s+ /x,
"Testing that move_segment will refuse to move a chain segment inside of itself.");
}
}
Joseph Brenner,
Sat Nov 6 17:04:11 2004