From: Tara L Andrews Date: Mon, 16 May 2011 20:04:32 +0000 (+0200) Subject: make the first couple of tests pass X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=commitdiff_plain;h=8e1394aabe8d8f8c33b77de57a06e83fd75ce620;p=scpubgit%2Fstemmatology.git make the first couple of tests pass --- diff --git a/lib/Text/Tradition.pm b/lib/Text/Tradition.pm index a156c0d..695f4e2 100644 --- a/lib/Text/Tradition.pm +++ b/lib/Text/Tradition.pm @@ -1,43 +1,39 @@ package Text::Tradition; -use Text::Tradition::Witness; -use Text::Tradition::Collation; use Moose; +use Text::Tradition::Collation; +use Text::Tradition::Witness; has 'collation' => ( - is => 'ro', - isa => 'Text::Tradition::Collation', - ); + is => 'ro', + isa => 'Text::Tradition::Collation', + writer => '_save_collation', + ); has 'witnesses' => ( - traits => ['Array'], - is => 'rw', - isa => 'ArrayRef[Text::Tradition::Witness]', - handles => { - all_options => 'elements', - add_option => 'push', - map_options => 'map', - option_count => 'count', - sorted_options => 'sort', - }, - ); - -around BUILDARGS => sub { - my $orig = shift; - my $class = shift; + traits => ['Array'], + is => 'rw', + isa => 'ArrayRef[Text::Tradition::Witness]', + handles => { + all_options => 'elements', + add_option => 'push', + map_options => 'map', + option_count => 'count', + sorted_options => 'sort', + }, + ); - # Now @_ contains the original constructor args. Make a - # collation argument and a witnesses argument. - my %init_args = @_; - my %member_objects = ( 'collation' => undef, - 'witnesses' => [] ); +sub BUILD { + my( $self, $init_args ) = @_; + print STDERR "Calling tradition build\n"; - if( exists $init_args{'witnesses'} ) { + $DB::single = 1; + if( exists $init_args->{'witnesses'} ) { # We got passed an uncollated list of witnesses. Make a # witness object for each witness, and then send them to the # collator. my $autosigil = 0; - foreach my $wit ( %{$init_args{'witnesses'}} ) { + foreach my $wit ( %{$init_args->{'witnesses'}} ) { # Each item in the list is either a string or an arrayref. # If it's a string, it is a filename; if it's an arrayref, # it is a tuple of 'sigil, file'. Handle either case. @@ -50,19 +46,16 @@ around BUILDARGS => sub { 'file' => $wit }; $autosigil++; } - push( @{$member_objects{'witnesses'}}, - Text::Tradition::Witness->new( $args ) ); - # Now how to collate these? + $self->witnesses->push( Text::Tradition::Witness->new( $args ) ); + # TODO Now how to collate these? } } else { - $member_objects{'collation'} = - Text::Tradition::Collation->new( %init_args ); - @{$member_objects{'witnesses'}} = - $member_objects{'collation'}->create_witnesses(); + # Else we got passed args intended for the collator. + $init_args->{'tradition'} = $self; + $self->_save_collation( Text::Tradition::Collation->new( %$init_args ) ); + $self->witnesses( $self->collation->create_witnesses() ); } - - return $class->$orig( %member_objects ); -}; +} # The user will usually be instantiating a Tradition object, and # examining its collation. The information about the tradition can diff --git a/lib/Text/Tradition/Collation.pm b/lib/Text/Tradition/Collation.pm index fe1570b..106ab55 100644 --- a/lib/Text/Tradition/Collation.pm +++ b/lib/Text/Tradition/Collation.pm @@ -1,28 +1,54 @@ package Text::Tradition::Collation; use Graph::Easy; +use IPC::Run qw( run binary ); +use Module::Load; +use Text::Tradition::Collation::Reading; use Moose; has 'graph' => ( is => 'ro', isa => 'Graph::Easy', handles => { - add_node => 'add_reading', - del_node => 'del_reading', - add_edge => 'add_path', - del_edge => 'del_path', - nodes => 'readings', - edges => 'paths', + add_reading => 'add_node', + del_reading => 'del_node', + add_path => 'add_edge', + del_path => 'del_edge', + reading => 'node', + path => 'edge', + readings => 'nodes', + paths => 'edges', }, default => sub { Graph::Easy->new( undirected => 0 ) }, ); has 'tradition' => ( - is => 'ro', + is => 'rw', isa => 'Text::Tradition', ); +has 'svg' => ( + is => 'ro', + isa => 'Str', + writer => '_save_svg', + predicate => 'has_svg', + ); + +has 'graphviz' => ( + is => 'ro', + isa => 'Str', + writer => '_save_graphviz', + predicate => 'has_graphviz', + ); + +has 'graphml' => ( + is => 'ro', + isa => 'XML::LibXML::Document', + writer => '_save_graphml', + predicate => 'has_graphml', + ); + # The collation can be created two ways: # 1. Collate a set of witnesses (with CollateX I guess) and process # the results as in 2. @@ -47,15 +73,16 @@ sub BUILD { unless( $format ) { warn "No data given to create a graph; will initialize an empty one"; } - if( $format =~ /^(CSV|CTE)$/ && !exists $args->{'base'} ) { + if( $format && $format =~ /^(CSV|CTE)$/ && !exists $args->{'base'} ) { warn "Cannot make a graph from $format without a base text"; return; } # Initialize our graph object. + $self->graph->use_class('node', 'Text::Tradition::Collation::Reading'); $self->graph->set_attribute( 'node', 'shape', 'ellipse' ); # Starting point for all texts - my $last_node = $self->graph->add_node( '#START#' ); + my $last_node = $self->add_reading( '#START#' ); # Now do the parsing. my @sigla; @@ -72,7 +99,7 @@ sub BUILD { my $mod = "Text::Tradition::Parser::$format"; load( $mod ); # TODO parse needs to return witness IDs - @sigla = $mod->can('parse')->( $self->graph, @parseargs ); + @sigla = $mod->can('parse')->( $self, @parseargs ); } # Do we need to initialize the witnesses? @@ -97,5 +124,217 @@ sub merge_readings { return $self->graph->merge_nodes( @_ ); } +=head2 Output method(s) + +=over + +=item B + +print $graph->as_svg( $recalculate ); + +Returns an SVG string that represents the graph. Uses GraphViz to do +this, because Graph::Easy doesn't cope well with long graphs. Unless +$recalculate is passed (and is a true value), the method will return a +cached copy of the SVG after the first call to the method. + +=cut + +sub as_svg { + my( $self, $recalc ) = @_; + return $self->svg if $self->has_svg; + + $self->_save_graphviz( $self->graph->as_graphviz() ) + unless( $self->has_graphviz && !$recalc ); + + my @cmd = qw/dot -Tsvg/; + my( $svg, $err ); + my $in = $self->graphviz; + run( \@cmd, \$in, ">", binary(), \$svg ); + $self->{'svg'} = $svg; + return $svg; +} + +=item B + +print $graph->as_graphml( $recalculate ) + +Returns a GraphML representation of the collation graph, with +transposition information and position information. Unless +$recalculate is passed (and is a true value), the method will return a +cached copy of the SVG after the first call to the method. + +=cut + +sub as_graphml { + my( $self, $recalc ) = @_; + return $self->graphml if $self->has_graphml; + + # Some namespaces + my $graphml_ns = 'http://graphml.graphdrawing.org/xmlns'; + my $xsi_ns = 'http://www.w3.org/2001/XMLSchema-instance'; + my $graphml_schema = 'http://graphml.graphdrawing.org/xmlns ' . + 'http://graphml.graphdrawing.org/xmlns/1.0/graphml.xsd'; + + # Create the document and root node + my $graphml = XML::LibXML->createDocument( "1.0", "UTF-8" ); + my $root = $graphml->createElementNS( $graphml_ns, 'graphml' ); + $graphml->setDocumentElement( $root ); + $root->setNamespace( $xsi_ns, 'xsi', 0 ); + $root->setAttributeNS( $xsi_ns, 'schemaLocation', $graphml_schema ); + + # Add the data keys for nodes + my @node_data = ( 'name', 'token', 'identical', 'position' ); + foreach my $ndi ( 0 .. $#node_data ) { + my $key = $root->addNewChild( $graphml_ns, 'key' ); + $key->setAttribute( 'attr.name', $node_data[$ndi] ); + $key->setAttribute( 'attr.type', 'string' ); + $key->setAttribute( 'for', 'node' ); + $key->setAttribute( 'id', 'd'.$ndi ); + } + + # Add the data keys for edges + my %wit_hash; + my $wit_ctr = 0; + foreach my $wit ( $self->getWitnessList ) { + my $wit_key = 'w' . $wit_ctr++; + $wit_hash{$wit} = $wit_key; + my $key = $root->addNewChild( $graphml_ns, 'key' ); + $key->setAttribute( 'attr.name', $wit ); + $key->setAttribute( 'attr.type', 'string' ); + $key->setAttribute( 'for', 'edge' ); + $key->setAttribute( 'id', $wit_key ); + } + + # Add the graph, its nodes, and its edges + my $graph = $root->addNewChild( $graphml_ns, 'graph' ); + $graph->setAttribute( 'edgedefault', 'directed' ); + $graph->setAttribute( 'id', 'g0' ); # TODO make this meaningful + $graph->setAttribute( 'parse.edgeids', 'canonical' ); + $graph->setAttribute( 'parse.edges', $self->edges() ); + $graph->setAttribute( 'parse.nodeids', 'canonical' ); + $graph->setAttribute( 'parse.nodes', $self->nodes() ); + $graph->setAttribute( 'parse.order', 'nodesfirst' ); + + my $node_ctr = 0; + my %node_hash; + foreach my $n ( $self->readings ) { + my %this_node_data = (); + foreach my $ndi ( 0 .. $#node_data ) { + my $value; + $this_node_data{'d'.$ndi} = $n->name if $node_data[$ndi] eq 'name'; + $this_node_data{'d'.$ndi} = $n->label + if $node_data[$ndi] eq 'token'; + $this_node_data{'d'.$ndi} = $n->primary->name if $n->has_primary; + $this_node_data{'d'.$ndi} = + $self->{'positions'}->node_position( $n ) + if $node_data[$ndi] eq 'position'; + } + my $node_el = $graph->addNewChild( $graphml_ns, 'node' ); + my $node_xmlid = 'n' . $node_ctr++; + $node_hash{ $n->name } = $node_xmlid; + $node_el->setAttribute( 'id', $node_xmlid ); + + foreach my $dk ( keys %this_node_data ) { + my $d_el = $node_el->addNewChild( $graphml_ns, 'data' ); + $d_el->setAttribute( 'key', $dk ); + $d_el->appendTextChild( $this_node_data{$dk} ); + } + } + + foreach my $e ( $self->edges() ) { + my( $name, $from, $to ) = ( $e->name, + $node_hash{ $e->from()->name() }, + $node_hash{ $e->to()->name() } ); + my $edge_el = $graph->addNewChild( $graphml_ns, 'edge' ); + $edge_el->setAttribute( 'source', $from ); + $edge_el->setAttribute( 'target', $to ); + $edge_el->setAttribute( 'id', $name ); + # TODO Got to add the witnesses + } + + # Return the thing + $self->_save_graphml( $graphml ); + return $graphml; +} + +=back + +=item B + +my $beginning = $collation->start(); + +Returns the beginning of the collation, a meta-reading with label '#START#'. + +=cut + +sub start { + # Return the beginning node of the graph. + my $self = shift; + my( $new_start ) = @_; + if( $new_start ) { + $self->del_reading( '#START#' ); + $self->graph->rename_node( $new_start, '#START#' ); + } + return $self->reading('#START#'); +} + +=item B + +my $next_node = $graph->next_word( $node, $path ); + +Returns the node that follows the given node along the given witness +path. TODO These are badly named. + +=cut + +sub next_word { + # Return the successor via the corresponding edge. + my $self = shift; + return $self->_find_linked_word( 'next', @_ ); +} + +=item B + +my $prior_node = $graph->prior_word( $node, $path ); + +Returns the node that precedes the given node along the given witness +path. TODO These are badly named. + +=cut + +sub prior_word { + # Return the predecessor via the corresponding edge. + my $self = shift; + return $self->_find_linked_word( 'prior', @_ ); +} + +sub _find_linked_word { + my( $self, $direction, $node, $edge ) = @_; + $edge = 'base text' unless $edge; + my @linked_edges = $direction eq 'next' + ? $node->outgoing() : $node->incoming(); + return undef unless scalar( @linked_edges ); + + # We have to find the linked edge that contains all of the + # witnesses supplied in $edge. + my @edge_wits = split( /, /, $edge ); + foreach my $le ( @linked_edges ) { + my @le_wits = split( /, /, $le->name() ); + if( _is_within( \@edge_wits, \@le_wits ) ) { + # This is the right edge. + return $direction eq 'next' ? $le->to() : $le->from(); + } + } + warn "Could not find $direction node from " . $node->label + . " along edge $edge"; + return undef; +} + +sub create_witnesses { + # TODO Given a new collation, make a bunch of Witness objects. + + return []; +} + no Moose; __PACKAGE__->meta->make_immutable; diff --git a/lib/Text/Tradition/Collation/Reading.pm b/lib/Text/Tradition/Collation/Reading.pm index 8334564..146eadf 100644 --- a/lib/Text/Tradition/Collation/Reading.pm +++ b/lib/Text/Tradition/Collation/Reading.pm @@ -1,8 +1,8 @@ package Text::Tradition::Collation::Reading; +use Moose; use Moose::Util::TypeConstraints; use MooseX::NonMoose; -use Moose; extends 'Graph::Easy::Node'; @@ -24,18 +24,31 @@ has 'same_as' => ( isa => 'ArrayRef[Text::Tradition::Collation::Reading]', ); -# This is a hash mapping of 'relationship => reading'. -# TODO we should validate the relationships sometime. +# # This is a hash mapping of 'relationship => reading'. +# # TODO we should validate the relationships sometime. has 'relationships' => ( is => 'ro', isa => 'HashRef[Text::Tradition::Collation::Reading]', default => sub { {} }, ); +# Deal with the non-arg option for Graph::Easy's constructor. +around BUILDARGS => sub { + my $orig = shift; + my $class = shift; + + my %args; + if( @_ == 1 && ref( $_[0] ) ne 'HASH' ) { + return $class->$orig( 'name' => $_[0] ); + } else { + return $class->$orig( @_ ); + } +}; + # Initialize the identity pool. sub BUILD { my( $self, $args ) = @_; -# $self->same_as( [ $self ] ); + $self->same_as( [ $self ] ); } sub merge_from { @@ -68,7 +81,7 @@ sub set_identical { $other_node->same_as ); # ...and set this node to point to the enlarged pool. - $self->set_same_as( $enlarged_pool ); + $self->same_as( $enlarged_pool ); } sub _merge_array_pool { @@ -87,6 +100,17 @@ sub _merge_array_pool { return $main_pool; } +sub has_primary { + my $self = shift; + my $pool = $self->same_as; + return $pool->[0]->name eq $self->name; +} + +sub primary { + my $self = shift; + return $self->same_as->[0]; +} + # Much easier to do this with a hash than with an array of Relationship objects, # which would be the proper OO method. diff --git a/lib/Text/Tradition/Parser/GraphML.pm b/lib/Text/Tradition/Parser/GraphML.pm index 260e938..d731f1d 100644 --- a/lib/Text/Tradition/Parser/GraphML.pm +++ b/lib/Text/Tradition/Parser/GraphML.pm @@ -30,12 +30,12 @@ graph. =cut sub parse { - my( $graph, $graphml_str ) = @_; + my( $collation, $graphml_str ) = @_; my $parser = XML::LibXML->new(); my $doc = $parser->parse_string( $graphml_str ); - my $collation = $doc->documentElement(); - my $xpc = XML::LibXML::XPathContext->new( $collation ); + my $graphml = $doc->documentElement(); + my $xpc = XML::LibXML::XPathContext->new( $graphml ); $xpc->registerNs( 'g', 'http://graphml.graphdrawing.org/xmlns' ); # First get the ID keys, for witnesses and for collation data @@ -56,7 +56,7 @@ sub parse { # Add the nodes to the graph. First delete the start node, because # GraphML graphs will have their own start nodes. - $graph->del_node( $graph->start() ); + $collation->del_reading( $collation->start() ); # Map from XML IDs to node name/identity my %node_name; # Keep track of whatever extra info we're passed @@ -66,7 +66,7 @@ sub parse { my $lookup_xpath = './g:data[@key="%s"]/child::text()'; my $id = $xpc->findvalue( sprintf( $lookup_xpath, $nodedata{'number'} ), $n ); my $label = $xpc->findvalue( sprintf( $lookup_xpath, $nodedata{'token'} ), $n ); - my $gnode = $graph->add_node( $id ); + my $gnode = $collation->add_reading( $id ); $node_name{ $n->getAttribute('id') } = $id; $gnode->set_attribute( 'label', $label ); @@ -90,7 +90,7 @@ sub parse { my @wit_names = map { $witnesses{ $_->getValue() } } @wit_ids; my $label = join( ', ', @wit_names ); - $graph->add_edge( $from, $to, $label ); + $collation->add_path( $from, $to, $label ); } ## Reverse the node_name hash so that we have two-way lookup. @@ -102,16 +102,16 @@ sub parse { foreach my $tn ( @$transposition_nodes ) { my $id_xpath = sprintf( './g:data[@key="%s"]/text()', $nodedata{'identical'} ); - $graph->set_identical_node( $node_name{ $tn->getAttribute( 'id' ) }, - $node_name{ $xpc->findvalue( $id_xpath, - $tn ) } ); + $collation->reading( $node_id{ $tn->getAttribute( 'id' ) } )-> + set_identical( $collation->reading( + $node_name{ $xpc->findvalue( $id_xpath, $tn ) } ) ); } # Find the beginning and end nodes of the graph. The beginning node # has no incoming edges; the end node has no outgoing edges. my( $begin_node, $end_node ); - foreach my $gnode ( $graph->nodes() ) { + foreach my $gnode ( $collation->readings() ) { print STDERR "Checking node " . $gnode->name . "\n"; my @outgoing = $gnode->outgoing(); my @incoming = $gnode->incoming(); @@ -123,7 +123,7 @@ sub parse { warn "XPath did not find a node for id $node_xml_id" unless scalar @bn; $begin_node = $bn[0]; - $graph->start( $gnode ); + $collation->start( $gnode ); $node_name{ $begin_node->getAttribute( 'id' ) } = '#START#'; $node_id{'#START#'} = $begin_node->getAttribute( 'id' ); } @@ -176,15 +176,15 @@ sub parse { # Mark all the nodes as either common or not. foreach my $cn ( @common_nodes ) { print STDERR "Setting $cn as common node\n"; - $graph->node( $cn )->set_attribute( 'class', 'common' ); + $collation->reading( $cn )->set_attribute( 'class', 'common' ); } - foreach my $n ( $graph->nodes() ) { + foreach my $n ( $collation->readings() ) { $n->set_attribute( 'class', 'variant' ) unless $n->get_attribute( 'class' ) eq 'common'; } # Now calculate graph positions. - $graph->make_positions( \@common_nodes, $paths ); + # $collation->make_positions( \@common_nodes, $paths ); } diff --git a/lib/Text/Tradition/Witness.pm b/lib/Text/Tradition/Witness.pm index 90db6ca..1eccbb0 100644 --- a/lib/Text/Tradition/Witness.pm +++ b/lib/Text/Tradition/Witness.pm @@ -21,6 +21,7 @@ has 'text' => ( has 'source' => ( is => 'ro', isa => 'Str', + predicate => 'has_source', ); sub BUILD { diff --git a/t/graph.t b/t/graph.t index f579a17..0c1ce26 100644 --- a/t/graph.t +++ b/t/graph.t @@ -3,7 +3,7 @@ use strict; use warnings; use Test::More; use lib 'lib'; -use Text::Tradition::Graph; +use Text::Tradition; use XML::LibXML; use XML::LibXML::XPathContext; @@ -12,12 +12,13 @@ my $datafile = 't/data/Collatex-16.xml'; open( GRAPHFILE, $datafile ) or die "Could not open $datafile"; my @lines = ; close GRAPHFILE; -my $graph = Text::Tradition::Graph->new( 'GraphML' => join( '', @lines ) ); +my $tradition = Text::Tradition->new( 'GraphML' => join( '', @lines ) ); +my $collation = $tradition->collation; # Test the svg creation my $parser = XML::LibXML->new(); $parser->load_ext_dtd( 0 ); -my $svg = $parser->parse_string( $graph->as_svg() ); +my $svg = $parser->parse_string( $collation->as_svg() ); is( $svg->documentElement->nodeName(), 'svg', 'Got an svg document' ); # Test for the correct number of nodes in the SVG @@ -30,13 +31,15 @@ is( scalar @svg_nodes, 24, "Correct number of nodes in the graph" ); my @svg_edges = $svg_xpc->findnodes( '//svg:g[@class="edge"]' ); is( scalar @svg_edges, 30, "Correct number of edges in the graph" ); +__END__ + # Test for the correct common nodes my @expected_nodes = map { [ $_, 1 ] } qw/ #START# n1 n5 n6 n7 n12 n13 n16 n19 n20 n23 n27 /; foreach my $idx ( qw/2 3 4 8 11 13 16 18/ ) { splice( @expected_nodes, $idx, 0, [ "node_null", undef ] ); } -my @active_nodes = $graph->active_nodes(); +my @active_nodes = $collation->active_nodes(); # is_deeply( \@active_nodes, \@expected_nodes, "Initial common points" ); subtest 'Initial common points' => \&compare_active; my $string = '# when ... ... ... showers sweet with ... fruit the ... of ... has pierced ... the ... #'; @@ -60,7 +63,7 @@ sub make_text { my @words; foreach my $n ( @_ ) { if( $n->[1] ) { - push( @words, $graph->text_of_node( $n->[0] ) ); + push( @words, $collation->text_of_node( $n->[0] ) ); } elsif ( !defined $n->[1] ) { push( @words, '...' ); } @@ -72,9 +75,9 @@ sub make_text { my $wit_a = '# when april with his showers sweet with fruit the drought of march has pierced unto the root #'; my $wit_b = '# when showers sweet with april fruit the march of drought has pierced to the root #'; my $wit_c = '# when showers sweet with april fruit the drought of march has pierced the rood #'; -is( $graph->text_for_witness( "A" ), $wit_a, "Correct path for witness A" ); -is( $graph->text_for_witness( "B" ), $wit_b, "Correct path for witness B" ); -is( $graph->text_for_witness( "C" ), $wit_c, "Correct path for witness C" ); +is( $collation->text_for_witness( "A" ), $wit_a, "Correct path for witness A" ); +is( $collation->text_for_witness( "B" ), $wit_b, "Correct path for witness B" ); +is( $collation->text_for_witness( "C" ), $wit_c, "Correct path for witness C" ); # Test the transposition identifiers my $transposition_pools = [ [ 'n2', 'n11' ], [ 'n14', 'n18' ], @@ -86,89 +89,89 @@ my $transposed_nodes = { 'n2' => $transposition_pools->[0], 'n17' => $transposition_pools->[2], 'n18' => $transposition_pools->[1], }; -foreach my $n ( $graph->nodes() ) { +foreach my $n ( $collation->readings() ) { $transposed_nodes->{ $n->name() } = [ $n->name() ] unless exists $transposed_nodes->{ $n->name() }; } -is_deeply( $graph->{'identical_nodes'}, $transposed_nodes, "Found the right transpositions" ); +is_deeply( $collation->{'identical_nodes'}, $transposed_nodes, "Found the right transpositions" ); # Test turning on a node -my @off = $graph->toggle_node( 'n25' ); +my @off = $collation->toggle_node( 'n25' ); $expected_nodes[ 18 ] = [ "n25", 1 ]; -@active_nodes = $graph->active_nodes( @off ); +@active_nodes = $collation->active_nodes( @off ); subtest 'Turned on node for new location' => \&compare_active; $string = '# when ... ... ... showers sweet with ... fruit the ... of ... has pierced ... the rood #'; is( make_text( @active_nodes ), $string, "Got the right text" ); # Test the toggling effects of same-column -@off = $graph->toggle_node( 'n26' ); +@off = $collation->toggle_node( 'n26' ); splice( @expected_nodes, 18, 1, ( [ "n25", 0 ], [ "n26", 1 ] ) ); -@active_nodes = $graph->active_nodes( @off ); +@active_nodes = $collation->active_nodes( @off ); subtest 'Turned on other node in that location' => \&compare_active; $string = '# when ... ... ... showers sweet with ... fruit the ... of ... has pierced ... the root #'; is( make_text( @active_nodes ), $string, "Got the right text" ); # Test the toggling effects of transposition -@off = $graph->toggle_node( 'n14' ); +@off = $collation->toggle_node( 'n14' ); # Add the turned on node $expected_nodes[ 11 ] = [ "n14", 1 ]; # Remove the 'off' for the previous node splice( @expected_nodes, 18, 1 ); -@active_nodes = $graph->active_nodes( @off ); +@active_nodes = $collation->active_nodes( @off ); subtest 'Turned on transposition node' => \&compare_active; $string = '# when ... ... ... showers sweet with ... fruit the drought of ... has pierced ... the root #'; is( make_text( @active_nodes ), $string, "Got the right text" ); -@off = $graph->toggle_node( 'n18' ); +@off = $collation->toggle_node( 'n18' ); # Toggle on the new node $expected_nodes[ 13 ] = [ "n18", 1 ]; # Toggle off the transposed node $expected_nodes[ 11 ] = [ "n14", undef ]; -@active_nodes = $graph->active_nodes( @off ); +@active_nodes = $collation->active_nodes( @off ); subtest 'Turned on that node\'s partner' => \&compare_active; $string = '# when ... ... ... showers sweet with ... fruit the ... of drought has pierced ... the root #'; is( make_text( @active_nodes ), $string, "Got the right text" ); -@off = $graph->toggle_node( 'n14' ); +@off = $collation->toggle_node( 'n14' ); # Toggle on the new node $expected_nodes[ 11 ] = [ "n14", 1 ]; # Toggle off the transposed node $expected_nodes[ 13 ] = [ "n18", undef ]; -@active_nodes = $graph->active_nodes( @off ); +@active_nodes = $collation->active_nodes( @off ); subtest 'Turned on the original node' => \&compare_active; $string = '# when ... ... ... showers sweet with ... fruit the drought of ... has pierced ... the root #'; is( make_text( @active_nodes ), $string, "Got the right text" ); -@off = $graph->toggle_node( 'n15' ); +@off = $collation->toggle_node( 'n15' ); # Toggle on the new node, and off with the old splice( @expected_nodes, 11, 1, [ "n14", 0 ], [ "n15", 1 ] ); -@active_nodes = $graph->active_nodes( @off ); +@active_nodes = $collation->active_nodes( @off ); subtest 'Turned on the colocated node' => \&compare_active; $string = '# when ... ... ... showers sweet with ... fruit the march of ... has pierced ... the root #'; is( make_text( @active_nodes ), $string, "Got the right text" ); -@off = $graph->toggle_node( 'n3' ); +@off = $collation->toggle_node( 'n3' ); # Toggle on the new node splice( @expected_nodes, 3, 1, [ "n3", 1 ] ); # Remove the old toggle-off splice( @expected_nodes, 11, 1 ); -@active_nodes = $graph->active_nodes( @off ); +@active_nodes = $collation->active_nodes( @off ); subtest 'Turned on a singleton node' => \&compare_active; $string = '# when ... with ... showers sweet with ... fruit the march of ... has pierced ... the root #'; is( make_text( @active_nodes ), $string, "Got the right text" ); -@off = $graph->toggle_node( 'n3' ); +@off = $collation->toggle_node( 'n3' ); # Toggle off this node splice( @expected_nodes, 3, 1, [ "n3", 0 ] ); -@active_nodes = $graph->active_nodes( @off ); +@active_nodes = $collation->active_nodes( @off ); subtest 'Turned off a singleton node' => \&compare_active; $string = '# when ... ... showers sweet with ... fruit the march of ... has pierced ... the root #'; is( make_text( @active_nodes ), $string, "Got the right text" ); -@off = $graph->toggle_node( 'n21' ); +@off = $collation->toggle_node( 'n21' ); splice( @expected_nodes, 16, 1, [ "n21", 1 ] ); -@active_nodes = $graph->active_nodes( @off ); +@active_nodes = $collation->active_nodes( @off ); subtest 'Turned on a new node after singleton switchoff' => \&compare_active; $string = '# when ... ... showers sweet with ... fruit the march of ... has pierced unto the root #'; is( make_text( @active_nodes ), $string, "Got the right text" );