requires( 'Moose::Util::TypeConstraints' );
requires( 'Text::CSV_XS' );
requires( 'Text::CSV::Simple' ); # TODO delete
+requires( 'Throwable::X' );
+requires( 'TryCatch' );
requires( 'XML::LibXML' );
requires( 'XML::LibXML::XPathContext' );
build_requires( 'Test::Warn' );
{ 'witness' => $wit->sigil, 'tokens' => \@row } );
if( $wit->is_layered ) {
my @wit_ac_path = $self->reading_sequence( $self->start, $self->end,
- $wit->sigil.$self->ac_label, $wit->sigil );
+ $wit->sigil.$self->ac_label );
my @ac_row = _make_witness_row( \@wit_ac_path, \@all_pos, $noderefs );
push( @{$table->{'alignment'}},
{ 'witness' => $wit->sigil.$self->ac_label, 'tokens' => \@ac_row } );
# TODO Get rid of backup; we should know from what witness is whether we need it.
sub reading_sequence {
- my( $self, $start, $end, $witness, $backup ) = @_;
+ my( $self, $start, $end, $witness ) = @_;
$witness = $self->baselabel unless $witness;
my @readings = ( $start );
}
$seen{$n->id} = 1;
- my $next = $self->next_reading( $n, $witness, $backup );
+ my $next = $self->next_reading( $n, $witness );
unless( $next ) {
warn "Did not find any path for $witness from reading " . $n->id;
last;
}
sub _find_linked_reading {
- my( $self, $direction, $node, $path, $alt_path ) = @_;
+ my( $self, $direction, $node, $path ) = @_;
+
+ # Get a backup if we are dealing with a layered witness
+ my $alt_path;
+ my $aclabel = $self->ac_label;
+ if( $path && $path =~ /^(.*)\Q$aclabel\E$/ ) {
+ $alt_path = $1;
+ }
+
my @linked_paths = $direction eq 'next'
? $self->sequence->edges_from( $node )
: $self->sequence->edges_to( $node );
=cut
sub path_text {
- my( $self, $wit, $backup, $start, $end ) = @_;
+ my( $self, $wit, $start, $end ) = @_;
$start = $self->start unless $start;
$end = $self->end unless $end;
- my @path = grep { !$_->is_meta } $self->reading_sequence( $start, $end, $wit, $backup );
+ my @path = grep { !$_->is_meta } $self->reading_sequence( $start, $end, $wit );
return join( ' ', map { $_->text } @path );
}
}
}
+=head2 text_from_paths
+
+Calculate the text array for all witnesses from the path, for later consistency
+checking. Only to be used if there is no non-graph-based way to know the
+original texts.
+
+=cut
+
+sub text_from_paths {
+ my $self = shift;
+ foreach my $wit ( $self->tradition->witnesses ) {
+ my @text = split( /\s+/,
+ $self->reading_sequence( $self->start, $self->end, $wit->sigil ) );
+ $wit->text( \@text );
+ if( $wit->is_layered ) {
+ my @uctext = split( /\s+/,
+ $self->reading_sequence( $self->start, $self->end,
+ $wit->sigil.$self->ac_label ) );
+ $wit->text( \@uctext );
+ }
+ }
+}
=head1 UTILITY FUNCTIONS
use KiokuDB::GC::Naive;
use KiokuDB::TypeMap;
use KiokuDB::TypeMap::Entry::Naive;
+use Text::Tradition::Error;
extends 'KiokuX::Model';
=begin testing
-use Test::Warn;
+use TryCatch;
use File::Temp;
use Text::Tradition;
use_ok 'Text::Tradition::Directory';
my $file = $fh->filename;
$fh->close;
my $dsn = "dbi:SQLite:dbname=$file";
-
-my $d = Text::Tradition::Directory->new( 'dsn' => $dsn,
- 'extra_args' => { 'create' => 1 } );
-is( ref $d, 'Text::Tradition::Directory', "Got directory object" );
-
-my $scope = $d->new_scope;
+my $uuid;
my $t = Text::Tradition->new(
'name' => 'inline',
'input' => 'Tabular',
'file' => 't/data/simple.txt',
);
-my $uuid = $d->save( $t );
-ok( $uuid, "Saved test tradition" );
-
-my $s = $t->add_stemma( 't/data/simple.dot' );
-ok( $d->save( $t ), "Updated tradition with stemma" );
-is( $d->tradition( $uuid ), $t, "Correct tradition returned for id" );
-is( $d->tradition( $uuid )->stemma, $s, "...and it has the correct stemma" );
-warning_like { $d->save( $s ) } qr/not a Text::Tradition/, "Correctly failed to save stemma directly";
-
-my $e = Text::Tradition::Directory->new( 'dsn' => $dsn );
-$scope = $e->new_scope;
-is( scalar $e->tradition_ids, 1, "Directory index has our tradition" );
-my $te = $e->tradition( $uuid );
-is( $te->name, $t->name, "Retrieved the tradition from a new directory" );
-my $sid = $e->object_to_id( $te->stemma );
-warning_like { $e->tradition( $sid ) } qr/not a Text::Tradition/, "Did not retrieve stemma via tradition call";
-warning_like { $e->delete( $sid ) } qr/Cannot directly delete non-Tradition object/, "Stemma object not deleted from DB";
-$e->delete( $uuid );
-ok( !$e->exists( $uuid ), "Object is deleted from DB" );
-ok( !$e->exists( $sid ), "Object stemma also deleted from DB" );
-is( scalar $e->tradition_ids, 0, "Object is deleted from index" );
+{
+ my $d = Text::Tradition::Directory->new( 'dsn' => $dsn,
+ 'extra_args' => { 'create' => 1 } );
+ is( ref $d, 'Text::Tradition::Directory', "Got directory object" );
+
+ my $scope = $d->new_scope;
+ $uuid = $d->save( $t );
+ ok( $uuid, "Saved test tradition" );
+
+ my $s = $t->add_stemma( 't/data/simple.dot' );
+ ok( $d->save( $t ), "Updated tradition with stemma" );
+ is( $d->tradition( $uuid ), $t, "Correct tradition returned for id" );
+ is( $d->tradition( $uuid )->stemma, $s, "...and it has the correct stemma" );
+ try {
+ $d->save( $s );
+ } catch( Text::Tradition::Error $e ) {
+ is( $e->ident, 'database error', "Got exception trying to save stemma directly" );
+ like( $e->message, qr/Cannot directly save non-Tradition object/,
+ "Exception has correct message" );
+ }
+}
+my $nt = Text::Tradition->new(
+ 'name' => 'CX',
+ 'input' => 'CollateX',
+ 'file' => 't/data/Collatex-16.xml',
+ );
+is( ref( $nt ), 'Text::Tradition', "Made new tradition" );
+
+{
+ my $f = Text::Tradition::Directory->new( 'dsn' => $dsn );
+ my $scope = $f->new_scope;
+ is( scalar $f->tradition_ids, 1, "Directory index has our tradition" );
+ my $nuuid = $f->save( $nt );
+ ok( $nuuid, "Stored second tradition" );
+ is( scalar $f->tradition_ids, 2, "Directory index has both traditions" );
+ my $tf = $f->tradition( $uuid );
+ is( $tf->name, $t->name, "Retrieved the tradition from a new directory" );
+ my $sid = $f->object_to_id( $tf->stemma );
+ try {
+ $f->tradition( $sid );
+ } catch( Text::Tradition::Error $e ) {
+ is( $e->ident, 'database error', "Got exception trying to fetch stemma directly" );
+ like( $e->message, qr/not a Text::Tradition/, "Exception has correct message" );
+ }
+ try {
+ $f->delete( $sid );
+ } catch( Text::Tradition::Error $e ) {
+ is( $e->ident, 'database error', "Got exception trying to delete stemma directly" );
+ like( $e->message, qr/Cannot directly delete non-Tradition object/,
+ "Exception has correct message" );
+ }
+ $f->delete( $uuid );
+ ok( !$f->exists( $uuid ), "Object is deleted from DB" );
+ ok( !$f->exists( $sid ), "Object stemma also deleted from DB" );
+ is( scalar $f->tradition_ids, 1, "Object is deleted from index" );
+}
+
+SKIP: {
+ skip 'Have yet to figure out garbage collection', 1;
+ my $g = Text::Tradition::Directory->new( 'dsn' => $dsn );
+ my $scope = $g->new_scope;
+ is( scalar $g->tradition_ids, 1, "Now one object in new directory index" );
+}
=end testing
},
);
-has tradition_index => (
- traits => ['Hash'],
- isa => 'HashRef[HashRef[Str]]',
- handles => {
- add_index => 'set',
- del_index => 'delete',
- info => 'get',
- tradition_ids => 'keys',
- },
- default => sub { {} },
- );
-
-# Populate the tradition index.
-sub BUILD {
+before [ qw/ store update insert delete / ] => sub {
my $self = shift;
- my $stream = $self->root_set;
- until( $stream->is_done ) {
- foreach my $obj ( $stream->items ) {
- my $uuid = $self->object_to_id( $obj );
- if( ref( $obj ) eq 'Text::Tradition' ) {
- $self->add_index( $uuid => { 'name' => $obj->name,
- 'id' => $uuid, 'has_stemma' => $obj->has_stemma } );
- } else {
- warn "Found root object in DB that is not a Text::Tradition";
+ my @nontrad;
+ foreach my $obj ( @_ ) {
+ if( ref( $obj ) && ref( $obj ) ne 'Text::Tradition' ) {
+ # Is it an id => Tradition hash?
+ if( ref( $obj ) eq 'HASH' && keys( %$obj ) == 1 ) {
+ my( $k ) = keys %$obj;
+ next if ref( $obj->{$k} ) eq 'Text::Tradition';
}
+ push( @nontrad, $obj );
}
}
- return $self;
-}
+ if( @nontrad ) {
+ throw( "Cannot directly save non-Tradition object of type "
+ . ref( $nontrad[0] ) );
+ }
+};
# If a tradition is deleted, remove it from the index.
-around delete => sub {
- my $orig = shift;
+after delete => sub {
my $self = shift;
- warn "Will only delete one tradition at a time" if @_ > 1;
- my $arg = shift;
- my $obj = ref( $arg ) ? $arg : $self->lookup( $arg );
- my $id = ref( $arg ) ? $self->object_to_id( $arg ) : $arg;
- unless( ref $obj eq 'Text::Tradition' ) {
- warn "Cannot directly delete non-Tradition object $obj";
- return;
- }
- $self->$orig( $arg );
my $gc = KiokuDB::GC::Naive->new( backend => $self->directory->backend );
- $self->$orig( $gc->garbage->members );
- $self->del_index( $id );
+ $self->directory->backend->delete( $gc->garbage->members );
};
sub save {
- my( $self, $obj ) = @_;
- unless( ref( $obj ) eq 'Text::Tradition' ) {
- warn "Object $obj is not a Text::Tradition";
- return;
- }
- my $uuid = $self->store( $obj );
- $self->add_index( $uuid => { 'name' => $obj->name,
- 'id' => $uuid, 'has_stemma' => $obj->has_stemma } ) if $uuid;
- return $uuid;
+ my $self = shift;
+ return $self->store( @_ );
}
-
sub tradition {
my( $self, $id ) = @_;
my $obj = $self->lookup( $id );
unless( ref( $obj ) eq 'Text::Tradition' ) {
- warn "Retrieved object is a " . ref( $obj ) . ", not a Text::Tradition";
- return;
+ throw( "Retrieved object is a " . ref( $obj ) . ", not a Text::Tradition" );
}
return $obj;
}
+sub tradition_ids {
+ my $self = shift;
+ my @ids;
+ $self->scan( sub { push( @ids, $self->object_to_id( @_ ) ) } );
+ return @ids;
+}
+
+sub throw {
+ Text::Tradition::Error->throw(
+ 'ident' => 'database error',
+ 'message' => $_[0],
+ );
+}
+
1;
\ No newline at end of file
--- /dev/null
+package Text::Tradition::Error;
+
+use strict;
+use warnings;
+use Moose;
+use overload '""' => \&_stringify, 'fallback' => 1;
+
+with qw/ Throwable::X /;
+use Throwable::X -all;
+
+sub _stringify {
+ my $self = shift;
+ return "Error: " . $self->ident . " // " . $self->message;
+}
+
+no Moose;
+__PACKAGE__->meta->make_immutable;
+
+=head1 NAME
+
+Text::Tradition::Error - throwable error class for CollateX package
+
+=head1 DESCRIPTION
+
+A basic exception class to throw around, as it were.
+
+=cut
+
+=head1 AUTHOR
+
+Tara L Andrews E<lt>aurum@cpan.orgE<gt>
# $rel->type, $rel->from->id, $rel->to->id );
# }
# }
- # $collation->calculate_ranks();
+ $collation->calculate_ranks();
}
=item B<read_base>
# Finally, add explicit witness paths, remove the base paths, and remove
# the app/anchor tags.
expand_all_paths( $c );
+
+ # Save the text for each witness so that we can ensure consistency
+ # later on
+ $tradition->collation->text_from_paths();
}
sub _stringify_sigil {
$wit->path( \@path );
if( $has_ac{$sig} ) {
my @ac_path = grep { !$_->is_ph }
- $c->reading_sequence( $c->start, $c->end, $sig.$c->ac_label, $sig );
+ $c->reading_sequence( $c->start, $c->end, $sig.$c->ac_label );
$wit->uncorrected_path( \@ac_path );
}
}
} # end processing of $app
} # end foreach my $app in line
} # end while <line>
- $DB::single = 1;
# Now reconcile all the paths in the collation, and delete our
# temporary anchor nodes.
# Finally, calculate the ranks we've got.
# $c->calculate_ranks;
+
+ # Save the text for each witness so that we can ensure consistency
+ # later on
+ $tradition->collation->text_from_paths();
}
sub _find_reading_on_line {
$wit->path( \@path );
if( $ALL_SIGLA{$sig} > 1 ) {
my @ac_path = grep { $_->name !~ /ATTACH/ }
- $c->reading_sequence( $c->start, $c->end, $sig.$c->ac_label, $sig );
+ $c->reading_sequence( $c->start, $c->end, $sig.$c->ac_label );
$wit->uncorrected_path( \@ac_path );
}
}
# Rank the readings.
$collation->calculate_ranks() if $collation->linear;
+
+ # Save the text for each witness so that we can ensure consistency
+ # later on
+ $tradition->collation->text_from_paths();
}
=head1 BUGS / TODO
$collation->add_relationship( $from->{$IDKEY}, $to->{$IDKEY},
$relationship_opts );
}
+
+ # Save the text for each witness so that we can ensure consistency
+ # later on
+ $tradition->collation->text_from_paths();
+
}
1;
# Now that we have ranks, see if we have distinct nodes with identical
# text and identical rank that can be merged.
$tradition->collation->flatten_ranks();
+
+ # Save the text for each witness so that we can ensure consistency
+ # later on
+ $tradition->collation->text_from_paths();
}
sub _clean_sequence {
ok( $wit->has_layertext, "Witness $k has an a.c. version" );
my $origtext = join( ' ', @{$wit->layertext} );
my $acsig = $wit->sigil . $t->collation->ac_label;
- my $graphtext = $t->collation->path_text( $acsig, $wit->sigil );
+ my $graphtext = $t->collation->path_text( $acsig );
is( $graphtext, $origtext, "Collation matches original a.c. for witness $k" );
} else {
ok( !$wit->is_layered, "Witness $k not marked as layered" );
my $ac_wit = $tradition->witness( $a );
my $main_wit = $tradition->witness( $ac_wits{$a} );
next unless $main_wit;
+ $main_wit->is_layered(1);
$main_wit->uncorrected_path( $ac_wit->path );
$tradition->del_witness( $ac_wit );
}
foreach my $rdg ( grep { $_->is_lacuna } $c->readings ) {
$c->del_reading( $rdg ) unless $c->reading_witnesses( $rdg );
}
+
+ # Do a consistency check.
+ foreach my $wit ( $tradition->witnesses ) {
+ my $pathtext = $c->path_text( $wit->sigil );
+ my $origtext = join( ' ', @{$wit->text} );
+ warn "Text differs for witness " . $wit->sigil
+ unless $pathtext eq $origtext;
+ if( $wit->is_layered ) {
+ $pathtext = $c->path_text( $wit->sigil.$c->ac_label );
+ $origtext = join( ' ', @{$wit->layertext} );
+ warn "Ante-corr text differs for witness " . $wit->sigil
+ unless $pathtext eq $origtext;
+ } else {
+ warn "Text " . $wit->sigil . " has a layered text but is not marked as layered"
+ if $wit->has_layertext;
+ }
+ }
}
sub make_nodes {
=item * sigil - A short code to represent the manuscript. Required.
=item * text - An array of strings (words) that contains the text of the
-manuscript.
+manuscript. This should not change after the witness has been instantiated,
+and the path through the collation should always match it.
+
+=item * layertext - An array of strings (words) that contains the layered text,
+if any, of the manuscript. This should not change after the witness has been
+instantiated, and the path through the collation should always match it.
=item * source - A reference to the text, such as a filename, if it is not
given in the 'text' option.
use warnings;
use Getopt::Long;
use Text::Tradition;
+use Text::Tradition::Directory;
use Text::Tradition::StemmaUtil;
binmode STDERR, ":utf8";
binmode STDOUT, ":utf8";
eval { no warnings; binmode $DB::OUT, ":utf8"; };
-my( $informat, $inbase, $outformat, $help, $linear, $name, $HACK, $sep )
- = ( '', '', '', '', 1, 'Tradition', 0, "\t" );
+my( $informat, $inbase, $outformat, $help, $linear, $name, $HACK, $sep, $stemmafile, $dsn )
+ = ( '', '', '', '', 1, 'Tradition', 0, "\t", '',
+ "dbi:SQLite:dbname=stemmaweb/db/traditions.db" );
GetOptions( 'i|in=s' => \$informat,
'b|base=s' => \$inbase,
'o|out=s' => \$outformat,
'l|linear!' => \$linear,
- 'n|name=s' => \$name,
+ 'n|name=s' => \$name,
'h|help' => \$help,
+ 's|stemma=s' => \$stemmafile,
'sep=s' => \$sep,
'hack' => \$HACK,
+ 'dsn=s' => \$dsn,
);
if( $help ) {
$informat = 'Tabular' if $informat =~ /^tab$/i;
$informat = 'CollateText' if $informat =~ /^stone$/i;
-unless( $outformat =~ /^(graphml|svg|dot|stemma|csv)$/ ) {
- help( "Output format must be one of graphml, svg, csv, stemma, or dot" );
+unless( $outformat =~ /^(graphml|svg|dot|stemma|csv|db)$/ ) {
+ help( "Output format must be one of db, graphml, svg, csv, stemma, or dot" );
}
# Do we have a base if we need it?
$args{'sigla'} = [ qw/ S M X V Z Bb B K W L / ];
}
my $tradition = Text::Tradition->new( %args );
+if( $stemmafile ) {
+ my $stemma = $tradition->add_stemma( $stemmafile );
+ print STDERR "Saved stemma at $stemmafile\n" if $stemma;
+}
### Custom hacking
# Remove witnesses C, E, G in the Matthew text
} else {
print STDERR "Bad result: $tree";
}
+} elsif( $outformat eq 'db' ) {
+ my $dir = Text::Tradition::Directory->new( 'dsn' => $dsn,
+ 'extra_args' => { 'create' => 1 } );
+ my $scope = $dir->new_scope;
+ my $uuid = $dir->store( $tradition );
+ print STDERR "Saved tradition to database with ID $uuid\n";
} else {
my $output = "as_$outformat";
print $tradition->collation->$output();
# =begin testing
{
-use Test::Warn;
+use TryCatch;
use File::Temp;
use Text::Tradition;
use_ok 'Text::Tradition::Directory';
my $file = $fh->filename;
$fh->close;
my $dsn = "dbi:SQLite:dbname=$file";
-
-my $d = Text::Tradition::Directory->new( 'dsn' => $dsn,
- 'extra_args' => { 'create' => 1 } );
-is( ref $d, 'Text::Tradition::Directory', "Got directory object" );
-
-my $scope = $d->new_scope;
+my $uuid;
my $t = Text::Tradition->new(
'name' => 'inline',
'input' => 'Tabular',
'file' => 't/data/simple.txt',
);
-my $uuid = $d->save( $t );
-ok( $uuid, "Saved test tradition" );
-
-my $s = $t->add_stemma( 't/data/simple.dot' );
-ok( $d->save( $t ), "Updated tradition with stemma" );
-is( $d->tradition( $uuid ), $t, "Correct tradition returned for id" );
-is( $d->tradition( $uuid )->stemma, $s, "...and it has the correct stemma" );
-warning_like { $d->save( $s ) } qr/not a Text::Tradition/, "Correctly failed to save stemma directly";
-
-my $e = Text::Tradition::Directory->new( 'dsn' => $dsn );
-$scope = $e->new_scope;
-is( scalar $e->tradition_ids, 1, "Directory index has our tradition" );
-my $te = $e->tradition( $uuid );
-is( $te->name, $t->name, "Retrieved the tradition from a new directory" );
-my $sid = $e->object_to_id( $te->stemma );
-warning_like { $e->tradition( $sid ) } qr/not a Text::Tradition/, "Did not retrieve stemma via tradition call";
-warning_like { $e->delete( $sid ) } qr/Cannot directly delete non-Tradition object/, "Stemma object not deleted from DB";
-$e->delete( $uuid );
-ok( !$e->exists( $uuid ), "Object is deleted from DB" );
-ok( !$e->exists( $sid ), "Object stemma also deleted from DB" );
-is( scalar $e->tradition_ids, 0, "Object is deleted from index" );
+
+{
+ my $d = Text::Tradition::Directory->new( 'dsn' => $dsn,
+ 'extra_args' => { 'create' => 1 } );
+ is( ref $d, 'Text::Tradition::Directory', "Got directory object" );
+
+ my $scope = $d->new_scope;
+ $uuid = $d->save( $t );
+ ok( $uuid, "Saved test tradition" );
+
+ my $s = $t->add_stemma( 't/data/simple.dot' );
+ ok( $d->save( $t ), "Updated tradition with stemma" );
+ is( $d->tradition( $uuid ), $t, "Correct tradition returned for id" );
+ is( $d->tradition( $uuid )->stemma, $s, "...and it has the correct stemma" );
+ try {
+ $d->save( $s );
+ } catch( Text::Tradition::Error $e ) {
+ is( $e->ident, 'database error', "Got exception trying to save stemma directly" );
+ like( $e->message, qr/Cannot directly save non-Tradition object/,
+ "Exception has correct message" );
+ }
+}
+my $nt = Text::Tradition->new(
+ 'name' => 'CX',
+ 'input' => 'CollateX',
+ 'file' => 't/data/Collatex-16.xml',
+ );
+is( ref( $nt ), 'Text::Tradition', "Made new tradition" );
+
+{
+ my $f = Text::Tradition::Directory->new( 'dsn' => $dsn );
+ my $scope = $f->new_scope;
+ is( scalar $f->tradition_ids, 1, "Directory index has our tradition" );
+ my $nuuid = $f->save( $nt );
+ ok( $nuuid, "Stored second tradition" );
+ is( scalar $f->tradition_ids, 2, "Directory index has both traditions" );
+ my $tf = $f->tradition( $uuid );
+ is( $tf->name, $t->name, "Retrieved the tradition from a new directory" );
+ my $sid = $f->object_to_id( $tf->stemma );
+ try {
+ $f->tradition( $sid );
+ } catch( Text::Tradition::Error $e ) {
+ is( $e->ident, 'database error', "Got exception trying to fetch stemma directly" );
+ like( $e->message, qr/not a Text::Tradition/, "Exception has correct message" );
+ }
+ try {
+ $f->delete( $sid );
+ } catch( Text::Tradition::Error $e ) {
+ is( $e->ident, 'database error', "Got exception trying to delete stemma directly" );
+ like( $e->message, qr/Cannot directly delete non-Tradition object/,
+ "Exception has correct message" );
+ }
+ $f->delete( $uuid );
+ ok( !$f->exists( $uuid ), "Object is deleted from DB" );
+ ok( !$f->exists( $sid ), "Object stemma also deleted from DB" );
+ is( scalar $f->tradition_ids, 1, "Object is deleted from index" );
+}
+
+SKIP: {
+ skip 'Have yet to figure out garbage collection', 1;
+ my $g = Text::Tradition::Directory->new( 'dsn' => $dsn );
+ my $scope = $g->new_scope;
+ is( scalar $g->tradition_ids, 1, "Now one object in new directory index" );
+}
}
ok( $wit->has_layertext, "Witness $k has an a.c. version" );
my $origtext = join( ' ', @{$wit->layertext} );
my $acsig = $wit->sigil . $t->collation->ac_label;
- my $graphtext = $t->collation->path_text( $acsig, $wit->sigil );
+ my $graphtext = $t->collation->path_text( $acsig );
is( $graphtext, $origtext, "Collation matches original a.c. for witness $k" );
} else {
ok( !$wit->is_layered, "Witness $k not marked as layered" );