# $Id: RowFixture.pm,v 1.2 2005/04/27 13:16:29 tonyb Exp $ # # Copyright (c) 2002-2005 Cunningham & Cunningham, Inc. # Released under the terms of the GNU General Public License version 2 or later. # # Perl translation by Dave W. Smith # Modified by Tony Byrne package Test::C2FIT::RowFixture; use base qw(Test::C2FIT::ColumnFixture); use strict; use Test::C2FIT::TypeAdapter; sub new { my $pkg = shift; return $pkg->SUPER::new(results => [], missing => [], surplus => [], @_); } sub doRows { my $self = shift; my($rows) = @_; eval { $self->bind($rows->parts()); $self->{'results'} = $self->query(); $self->match($self->rowsToArray($rows->more()), $self->{'results'}, 0); my $last = $rows->last(); $last->more($self->buildRows($self->{'surplus'})); $self->markRows($last->more(), "surplus"); $self->markList($self->{'missing'}, "missing"); }; if ( $@ ) { $self->exception($rows->leaf(), $@); } } sub match { my $self = shift; my($expected, $computed, $col) = @_; my $ncols = @{$self->{'columnBindings'}}; if ( $col >= $ncols ) { $self->checkLists($expected, $computed); } elsif ( not defined($self->{'columnBindings'}->[$col]) ) { $self->match($expected, $computed, $col + 1) } else { my $eMap = $self->eSort($expected, $col); my $cMap = $self->cSort($computed, $col); my $keys = $self->union(keys %$eMap, keys %$cMap); foreach my $key ( @$keys ) { my $eList = $$eMap{$key}; my $cList = $$cMap{$key}; if ( ! $eList ) { push @{$self->{'surplus'}}, @$cList; } elsif ( ! $cList ) { push @{$self->{'missing'}}, @$eList; } elsif ( 1 == @$eList && 1 == @$cList ) { $self->checkLists($eList, $cList); } else { $self->match($eList, $cList, $col+1); } } } } sub rowsToArray { my $self = shift; my($rows) = @_; my @results = (); while ( $rows ) { push @results, $rows; $rows = $rows->more(); } return \@results; } sub eSort { my $self = shift; my($list, $col) = @_; my $adapter = $self->{'columnBindings'}->[$col]; my %result = (); foreach my $row ( @$list ) { my $cell = $row->parts()->at($col); eval { my $key = $adapter->parse($cell->text()); push @{$result{$key}}, $row; }; if ( $@ ) { $self->exception($cell, $@); while ( $cell = $cell->more() ) { $self->ignore($cell); } } } return \%result; } sub cSort { my $self = shift; my($list, $col) = @_; my $adapter = $self->{'columnBindings'}->[$col]; my %result = (); foreach my $row ( @$list ) { eval { $adapter->target($row); my $key = $adapter->get(); push @{$result{$key}}, $row; }; if ( $@ ) { push @{$self->{'surplus'}}, $row; } } return \%result; } sub union { my $self = shift; my %merged = (); $merged{$_}++ foreach @_; return [keys %merged] } sub checkLists { my $self = shift; my($eList, $cList) = @_; if ( 0 == @$eList ) { push @{$self->{'surplus'}}, @$eList; return; } if ( 0 == @$cList ) { push @{$self->{'missing'}}, @$cList; return; } my $row = shift @$eList; my $cell = $row->parts(); my $obj = shift @$cList; foreach my $adapter ( @{$self->{'columnBindings'}} ) { last if not defined($cell); if ( $adapter ) { $adapter->target($obj); } $self->check($cell, $adapter); $cell = $cell->more(); } $self->checkLists($eList, $cList); } sub markRows { my $self = shift; my($rows, $message) = @_; my $annotation = Test::C2FIT::Fixture->label($message); while ( $rows ) { $self->wrong($rows->parts()); $rows->parts()->addToBody($annotation); $rows = $rows->more(); } } sub markList { my $self = shift; my($rows, $message) = @_; my $annotation = Test::C2FIT::Fixture->label($message); foreach my $row ( @$rows ) { $self->wrong($row->parts()); $row->parts()->addToBody($annotation); } } sub buildRows { my $self = shift; my($rowsref) = @_; my $root = Test::C2FIT::Parse->from("", undef, undef, undef); my $next = $root; foreach my $row ( @$rowsref ) { $next = $next->more(Test::C2FIT::Parse->from("tr", undef, $self->buildCells($row), undef)); } return $root->more(); } sub buildCells { my $self = shift; my($row) = @_; my $ncols = $self->{'columnBindings'}; if ( ! $row ) { my $nil = Test::C2FIT::Parse->from("td", "nul", undef, undef); $nil->addToTag(" colspan=$ncols"); return $nil; } my $root = Test::C2FIT::Parse->from("", undef, undef, undef); my $next = $root; foreach my $adapter ( @{$self->{'columnBindings'}} ) { $next = $next->more(Test::C2FIT::Parse->from("td", " ", undef, undef)); if ( ! $adapter ) { $self->ignore($next); } else { eval { $adapter->target($row); $self->info($next, $adapter->toString($adapter->get())); }; if ( $@ ) { $self->exception($next, $@); } } return $root->more(); } } 1; __END__ // Copyright (c) 2002 Cunningham & Cunningham, Inc. // Released under the terms of the GNU General Public License version 2 or later. package fit; import java.util.*; abstract public class RowFixture extends ColumnFixture { public Object results[]; public List missing = new LinkedList(); public List surplus = new LinkedList(); public void doRows(Parse rows) { try { bind(rows.parts); results = query(); match(list(rows.more), list(results), 0); Parse last = rows.last(); last.more = buildRows(surplus.toArray()); mark(last.more, "surplus"); mark(missing.iterator(), "missing"); } catch (Exception e) { exception (rows.leaf(), e); } } abstract public Object[] query() throws Exception; // get rows to be compared abstract public Class getTargetClass(); // get expected type of row protected void match(List expected, List computed, int col) { if (col >= columnBindings.length) { check (expected, computed); } else if (columnBindings[col] == null) { match (expected, computed, col+1); } else { Map eMap = eSort(expected, col); Map cMap = cSort(computed, col); Set keys = union(eMap.keySet(),cMap.keySet()); for (Iterator i=keys.iterator(); i.hasNext(); ) { Object key = i.next(); List eList = (List)eMap.get(key); List cList = (List)cMap.get(key); if (eList == null) { surplus.addAll(cList); } else if (cList == null) { missing.addAll(eList); } else if (eList.size()==1 && cList.size()==1) { check(eList, cList); } else { match(eList, cList, col+1); } } } } protected List list (Parse rows) { List result = new LinkedList(); while (rows != null) { result.add(rows); rows = rows.more; } return result; } protected List list (Object[] rows) { List result = new LinkedList(); for (int i=0; i