[SearchBuilder-devel] [PATCH] t/01searches.t

Ruslan U. Zakirov Ruslan.Zakirov at miet.ru
Thu May 26 07:12:37 EDT 2005


	Hello.
Three patches attached:
1) init_data: new function in t/utils.pl
2) search_builder_pm: small changes, description is in patch
3) searches_tests: tests for SearchBuilder.pm

All this covers basic searches.

You can skip search_builder_pm.patch and see what tests fails and how 
I've fixed it.

PS: We had power crisis in the Moscow, that's why I wasn't available.

--
Best regards, Ruslan.
-------------- next part --------------
----------------------------------------------------------------------
r1680:  cubic | 2005-05-25 23:08:25 +0000

new function init_data in test framework
----------------------------------------------------------------------
=== DBIx-SearchBuilder/local/t/utils.pl
==================================================================
--- DBIx-SearchBuilder/local/t/utils.pl  (revision 1679)
+++ DBIx-SearchBuilder/local/t/utils.pl  (revision 1680)
@@ -177,7 +177,7 @@
 
 =head2 cleanup_schema
 
-Takes C<$class> and C<$handle> and inits schema by calling
+Takes C<$class> and C<$handle> and cleanup schema by calling
 C<cleanup_schema_$driver> method of the C<$class> if method exists.
 Always returns undef.
 
@@ -195,4 +195,27 @@
 	}
 }
 
+=head2 init_data
+
+=cut
+
+sub init_data
+{
+	my ($class, $handle) = @_;
+	my @data = $class->init_data();
+	my @columns = @{ shift @data };
+	my $count = 0;
+	foreach my $values ( @data ) {
+		my %args;
+		for( my $i = 0; $i < @columns; $i++ ) {
+			$args{ $columns[$i] } = $values->[$i];
+		}
+		my $rec = $class->new( $handle );
+		my $id = $rec->Create( %args );
+		die "Couldn't create record" unless $id;
+		$count++;
+	}
+	return $count;
+}
+
 1;
-------------- next part --------------
----------------------------------------------------------------------
r1679:  cubic | 2005-05-25 23:06:40 +0000

SearchBuilder.pm fixes&cleanups:
CleanSlate doesnt init show_rows
CleanSlate doesnt clean _{open|close}_parens
get rid of stupid ifs in CleanSlate
get rid of evals in _DoSearch and _DoCount, use Handle methods to control DBI error handling
----------------------------------------------------------------------
=== DBIx-SearchBuilder/local/SearchBuilder.pm
==================================================================
--- DBIx-SearchBuilder/local/SearchBuilder.pm  (revision 1678)
+++ DBIx-SearchBuilder/local/SearchBuilder.pm  (revision 1679)
@@ -109,7 +109,7 @@
     my $self = shift;
     my %args = ( Handle => undef,
                  @_ );
-    $self->{'DBIxHandle'} = $args{'Handle'};
+    $self->_Handle( $args{'Handle'} );
 
     $self->CleanSlate();
 }
@@ -139,14 +139,19 @@
     $self->{'alias_count'}      = 0;
     $self->{'first_row'}        = 0;
     $self->{'must_redo_search'} = 1;
+    $self->{'show_rows'}        = 0;
     @{ $self->{'aliases'} } = ();
 
-    delete $self->{'items'}        if ( defined $self->{'items'} );
-    delete $self->{'left_joins'}   if ( defined $self->{'left_joins'} );
-    delete $self->{'raw_rows'}     if ( defined $self->{'raw_rows'} );
-    delete $self->{'count_all'}    if ( defined $self->{'count_all'} );
-    delete $self->{'subclauses'}   if ( defined $self->{'subclauses'} );
-    delete $self->{'restrictions'} if ( defined $self->{'restrictions'} );
+    delete $self->{$_} for qw(
+	items
+	left_joins
+	raw_rows
+	count_all
+	subclauses
+	restrictions
+	_open_parens
+	_close_parens
+    );
 
     #we have no limit statements. DoSearch won't work.
     $self->_isLimited(0);
@@ -191,18 +196,17 @@
     # If we're about to redo the search, we need an empty set of items
     delete $self->{'items'};
 
-    eval {
-        # TODO: finer-grained eval and checking.
-        my $records = $self->_Handle->SimpleQuery($QueryString);
+    my $records = $self->_Handle->SimpleQuery($QueryString);
+    return 0 unless $records;
 
-        while ( my $row = $records->fetchrow_hashref() ) {
-            my $item = $self->NewItem();
-            $item->LoadFromHash($row);
-            $self->AddRecord($item);
-        }
+    while ( my $row = $records->fetchrow_hashref() ) {
+	my $item = $self->NewItem();
+	$item->LoadFromHash($row);
+	$self->AddRecord($item);
+    }
+    return $self->_RecordCount if $records->err;
 
-        $self->{'must_redo_search'} = 0;
-    };
+    $self->{'must_redo_search'} = 0;
 
     return $self->_RecordCount;
 }
@@ -250,15 +254,15 @@
     my $all  = shift || 0;
 
     my $QueryString = $self->BuildSelectCountQuery();
-    eval {
-        # TODO: finer-grained Eval
-        my $records     = $self->_Handle->SimpleQuery($QueryString);
+    my $records     = $self->_Handle->SimpleQuery($QueryString);
+    return 0 unless $records;
 
-        my @row = $records->fetchrow_array();
-        $self->{ $all ? 'count_all' : 'raw_rows' } = $row[0];
+    my @row = $records->fetchrow_array();
+    return 0 if $records->err;
 
-        return ( $row[0] );
-    };
+    $self->{ $all ? 'count_all' : 'raw_rows' } = $row[0];
+
+    return ( $row[0] );
 }
 
 # }}}
@@ -485,7 +489,7 @@
 
     return (undef) unless ( $self->_isLimited );
 
-    $self->_DoSearch() if ( $self->{'must_redo_search'} != 0 );
+    $self->_DoSearch() if $self->{'must_redo_search'};
 
     if ( $self->{'itemscount'} < $self->_RecordCount ) {    #return the next item
         my $item = ( $self->{'items'}[ $self->{'itemscount'} ] );
@@ -1121,9 +1125,7 @@
 sub _OrderClause {
     my $self = shift;
 
-    unless ( defined $self->{'order_clause'} ) {
-	return "";
-    }
+    return '' unless $self->{'order_clause'};
     return ($self->{'order_clause'});
 }
 
@@ -1141,7 +1143,7 @@
 
 =cut
 
-sub GroupBy { (shift)->GroupByCols( @_) }
+sub GroupBy { (shift)->GroupByCols( @_ ) }
 
 # }}}
 
@@ -1199,9 +1201,7 @@
 sub _GroupClause {
     my $self = shift;
 
-    unless ( defined $self->{'group_clause'} ) {
-	    return "";
-    }
+    return '' unless $self->{'group_clause'};
     return ($self->{'group_clause'});
 }
 
@@ -1520,11 +1520,13 @@
 sub IsLast {
     my $self = shift;
 
+    return undef unless $self->Count;
+
     if ( $self->_ItemsCounter == $self->Count ) {
         return (1);
     }
     else {
-        return (undef);
+        return (0);
     }
 }
 
-------------- next part --------------
----------------------------------------------------------------------
r1681:  cubic | 2005-05-25 23:09:37 +0000

basic tests for SearchBuilder.pm
----------------------------------------------------------------------
=== DBIx-SearchBuilder/local/t/01searches.t
==================================================================
--- DBIx-SearchBuilder/local/t/01searches.t  (revision 1680)
+++ DBIx-SearchBuilder/local/t/01searches.t  (revision 1681)
@@ -0,0 +1,248 @@
+#!/usr/bin/perl -w
+
+
+use strict;
+use warnings;
+use File::Spec;
+use Test::More;
+BEGIN { require "t/utils.pl" }
+our (@AvailableDrivers);
+
+use constant TESTS_PER_DRIVER => 55;
+
+my $total = scalar(@AvailableDrivers) * TESTS_PER_DRIVER;
+plan tests => $total;
+
+foreach my $d ( @AvailableDrivers ) {
+SKIP: {
+	unless( has_schema( 'TestApp', $d ) ) {
+		skip "No schema for '$d' driver", TESTS_PER_DRIVER;
+	}
+	unless( should_test( $d ) ) {
+		skip "ENV is not defined for driver '$d'", TESTS_PER_DRIVER;
+	}
+
+	my $handle = get_handle( $d );
+	connect_handle( $handle );
+	isa_ok($handle->dbh, 'DBI::db');
+
+	my $ret = init_schema( 'TestApp', $handle );
+	isa_ok($ret,'DBI::st', "Inserted the schema. got a statement handle back");
+
+	my $count_all = init_data( 'TestApp::User', $handle );
+	ok( $count_all,  "init users data" );
+
+	my $users_obj = TestApp::Users->new( $handle );
+	isa_ok( $users_obj, 'DBIx::SearchBuilder' );
+	is( $users_obj->_Handle, $handle, "same handle as we used in constructor");
+
+# check that new object returns 0 records in any case
+	is( $users_obj->_RecordCount, 0, '_RecordCount returns 0 on not limited obj' );
+	is( $users_obj->Count, 0, 'Count returns 0 on not limited obj' );
+	is( $users_obj->IsLast, undef, 'IsLast returns undef on not limited obj after Count' );
+	is( $users_obj->First, undef, 'First returns undef on not limited obj' );
+	is( $users_obj->IsLast, undef, 'IsLast returns undef on not limited obj after First' );
+	is( $users_obj->Last, undef, 'Last returns undef on not limited obj' );
+	is( $users_obj->IsLast, undef, 'IsLast returns undef on not limited obj after Last' );
+	$users_obj->GotoFirstItem;
+	is( $users_obj->Next, undef, 'Next returns undef on not limited obj' );
+	is( $users_obj->IsLast, undef, 'IsLast returns undef on not limited obj after Next' );
+	# XXX TODO FIXME: may be this methods should be implemented
+	# $users_obj->GotoLastItem;
+	# is( $users_obj->Prev, undef, 'Prev returns undef on not limited obj' );
+	my $items_ref = $users_obj->ItemsArrayRef;
+	isa_ok( $items_ref, 'ARRAY', 'ItemsArrayRef always returns array reference' );
+	is_deeply( $items_ref, [], 'ItemsArrayRef returns [] on not limited obj' );
+
+# unlimit new object and check
+	$users_obj->UnLimit;
+	is( $users_obj->Count, $count_all, 'Count returns same number of records as was inserted' );
+	isa_ok( $users_obj->First, 'DBIx::SearchBuilder::Record', 'First returns record object' );
+	isa_ok( $users_obj->Last, 'DBIx::SearchBuilder::Record', 'Last returns record object' );
+	$users_obj->GotoFirstItem;
+	isa_ok( $users_obj->Next, 'DBIx::SearchBuilder::Record', 'Next returns record object' );
+	$items_ref = $users_obj->ItemsArrayRef;
+	isa_ok( $items_ref, 'ARRAY', 'ItemsArrayRef always returns array reference' );
+	is( scalar @{$items_ref}, $count_all, 'ItemsArrayRef returns same number of records as was inserted' );
+	$users_obj->RedoSearch;
+	$items_ref = $users_obj->ItemsArrayRef;
+	isa_ok( $items_ref, 'ARRAY', 'ItemsArrayRef always returns array reference' );
+	is( scalar @{$items_ref}, $count_all, 'ItemsArrayRef returns same number of records as was inserted' );
+
+# try to use $users_obj for all tests, after each call to CleanSlate it should look like new obj.
+# and test $obj->new syntax
+	my $clean_obj = $users_obj->new( $handle );
+	isa_ok( $clean_obj, 'DBIx::SearchBuilder' );
+
+# basic limits
+	$users_obj->CleanSlate;
+	is_deeply( $users_obj, $clean_obj, 'after CleanSlate looks like new object');
+	$users_obj->Limit( FIELD => 'Login', VALUE => 'obra' );
+	is( $users_obj->Count, 1, 'found one user with login obra' );
+	TODO: {
+		local $TODO = 'require discussion';
+		is( $users_obj->IsLast, undef, 'IsLast returns undef before we fetch any record' );
+	}
+	my $first_rec = $users_obj->First;
+	isa_ok( $first_rec, 'DBIx::SearchBuilder::Record', 'First returns record object' );
+	is( $users_obj->IsLast, 1, '1 record in the collection then first rec is last');
+	is( $first_rec->Login, 'obra', 'login is correct' );
+	my $last_rec = $users_obj->Last;
+	is( $last_rec, $first_rec, 'Last returns same object as First' );
+	is( $users_obj->IsLast, 1, 'IsLast always returns 1 after Last call');
+	$users_obj->GotoFirstItem;
+	my $next_rec = $users_obj->Next;
+	is( $next_rec, $first_rec, 'Next returns same object as First' );
+	is( $users_obj->IsLast, 1, 'IsLast returns 1 after fetch first record with Next method');
+	is( $users_obj->Next, undef, 'only one record in the collection' );
+	TODO: {
+		local $TODO = 'require discussion';
+		is( $users_obj->IsLast, undef, 'Next returns undef, IsLast returns undef too');
+	}
+	$items_ref = $users_obj->ItemsArrayRef;
+	isa_ok( $items_ref, 'ARRAY', 'ItemsArrayRef always returns array reference' );
+	is( scalar @{$items_ref}, 1, 'ItemsArrayRef has only 1 record' );
+
+# similar basic limit, but with different OPERATORS and less Firs/Next/Last tests
+	# LIKE
+	$users_obj->CleanSlate;
+	is_deeply( $users_obj, $clean_obj, 'after CleanSlate looks like new object');
+	$users_obj->Limit( FIELD => 'Name', OPERATOR => 'LIKE', VALUE => 'Glass' );
+	is( $users_obj->Count, 1, "found one user with 'Glass' in the name" );
+	$first_rec = $users_obj->First;
+	isa_ok( $first_rec, 'DBIx::SearchBuilder::Record', 'First returns record object' );
+	is( $first_rec->Login, 'glasser', 'login is correct' );
+
+	# STARTSWITH
+	$users_obj->CleanSlate;
+	is_deeply( $users_obj, $clean_obj, 'after CleanSlate looks like new object');
+	$users_obj->Limit( FIELD => 'Name', OPERATOR => 'STARTSWITH', VALUE => 'Ruslan' );
+	is( $users_obj->Count, 1, "found one user who name starts with 'Ruslan'" );
+	$first_rec = $users_obj->First;
+	isa_ok( $first_rec, 'DBIx::SearchBuilder::Record', 'First returns record object' );
+	is( $first_rec->Login, 'cubic', 'login is correct' );
+
+	# ENDSWITH
+	$users_obj->CleanSlate;
+	is_deeply( $users_obj, $clean_obj, 'after CleanSlate looks like new object');
+	$users_obj->Limit( FIELD => 'Name', OPERATOR => 'ENDSWITH', VALUE => 'Tang' );
+	is( $users_obj->Count, 1, "found one user who name ends with 'Tang'" );
+	$first_rec = $users_obj->First;
+	isa_ok( $first_rec, 'DBIx::SearchBuilder::Record', 'First returns record object' );
+	is( $first_rec->Login, 'autrijus', 'login is correct' );
+
+	# IS NULL
+	# XXX TODO FIXME: FIELD => undef should be handled as NULL
+	$users_obj->CleanSlate;
+	is_deeply( $users_obj, $clean_obj, 'after CleanSlate looks like new object');
+	$users_obj->Limit( FIELD => 'Phone', OPERATOR => 'IS', VALUE => 'NULL' );
+	is( $users_obj->Count, 2, "found 2 users who has unknown phone number" );
+	
+	# IS NOT NULL
+	$users_obj->CleanSlate;
+	is_deeply( $users_obj, $clean_obj, 'after CleanSlate looks like new object');
+	$users_obj->Limit( FIELD => 'Phone', OPERATOR => 'IS NOT', VALUE => 'NULL', QOUTEVALUE => 0 );
+	is( $users_obj->Count, $count_all - 2, "found users who has phone number filled" );
+
+
+	cleanup_schema( 'TestApp', $handle );
+}} # SKIP, foreach blocks
+
+1;
+
+package TestApp;
+
+sub schema_mysql {
+<<EOF;
+CREATE TEMPORARY TABLE Users (
+        id integer AUTO_INCREMENT,
+        Login varchar(18) NOT NULL,
+        Name varchar(36),
+	Phone varchar(18),
+  	PRIMARY KEY (id))
+EOF
+
+}
+
+sub schema_pg {
+<<EOF;
+CREATE TEMPORARY TABLE Users (
+        id serial PRIMARY KEY,
+        Login varchar(18) NOT NULL,
+        Name varchar(36),
+        Phone varchar(18)
+)
+EOF
+
+}
+
+sub schema_sqlite {
+
+<<EOF;
+CREATE TABLE Users (
+	id integer primary key,
+	Login varchar(18) NOT NULL,
+	Name varchar(36),
+	Phone varchar(18))
+EOF
+
+}
+
+
+1;
+
+package TestApp::User;
+
+use base qw/DBIx::SearchBuilder::Record/;
+
+sub _Init {
+    my $self = shift;
+    my $handle = shift;
+    $self->Table('Users');
+    $self->_Handle($handle);
+}
+
+sub _ClassAccessible {
+    {   
+        id =>
+        {read => 1, type => 'int(11)' }, 
+        Login =>
+        {read => 1, write => 1, type => 'varchar(18)' },
+        Name =>
+        {read => 1, write => 1, type => 'varchar(36)' },
+        Phone =>
+        {read => 1, write => 1, type => 'varchar(18)', default => ''},
+    }
+}
+
+sub init_data {
+    return (
+	[ 'Login',	'Name',			'Phone' ],
+	[ 'cubic',	'Ruslan U. Zakirov',	'+7-903-264-XX-XX' ],
+	[ 'obra',	'Jesse Vincent',	undef ],
+	[ 'glasser',	'David Glasser',	undef ],
+	[ 'autrijus',	'Autrijus Tang',	'+X-XXX-XXX-XX-XX' ],
+    );
+}
+
+1;
+
+package TestApp::Users;
+
+# use TestApp::User;
+use base qw/DBIx::SearchBuilder/;
+
+sub _Init {
+    my $self = shift;
+    $self->SUPER::_Init( Handle => shift );
+    $self->Table('Users');
+}
+
+sub NewItem
+{
+	my $self = shift;
+	return TestApp::User->new( $self->_Handle );
+}
+
+1;
+


More information about the SearchBuilder-devel mailing list