1use Test::More; 2use Test::Exception; 3 4use utf8; 5use strict; 6use warnings; 7no warnings 'redefine'; 8 9use DBI; 10use RDF::Trine qw(literal); 11use RDF::Trine::Model; 12use RDF::Trine::Node; 13use RDF::Trine::Pattern; 14use RDF::Trine::Namespace; 15use RDF::Trine::Store::DBI; 16use RDF::Trine::Statement; 17use File::Temp qw(tempfile); 18 19my $rdf = RDF::Trine::Namespace->new('http://www.w3.org/1999/02/22-rdf-syntax-ns#'); 20my $foaf = RDF::Trine::Namespace->new('http://xmlns.com/foaf/0.1/'); 21my $xsd = RDF::Trine::Namespace->new('http://www.w3.org/2001/XMLSchema#'); 22my $kasei = RDF::Trine::Namespace->new('http://kasei.us/'); 23my $b = RDF::Trine::Node::Blank->new(); 24my $p = RDF::Trine::Node::Resource->new('http://kasei.us/about/foaf.xrdf#greg'); 25my $intval = RDF::Trine::Node::Literal->new('23',undef,$xsd->int); 26my $langval = RDF::Trine::Node::Literal->new('gwilliams','en'); 27my $st0 = RDF::Trine::Statement->new( $p, $rdf->type, $foaf->Person ); 28my $st1 = RDF::Trine::Statement->new( $p, $foaf->name, RDF::Trine::Node::Literal->new('Gregory Todd Williams') ); 29my $st2 = RDF::Trine::Statement->new( $b, $rdf->type, $foaf->Person ); 30my $st3 = RDF::Trine::Statement->new( $b, $foaf->name, RDF::Trine::Node::Literal->new('Eve') ); 31my $st4 = RDF::Trine::Statement->new( $p, $foaf->knows, $b ); 32my $st5 = RDF::Trine::Statement->new( $p, $foaf->nick, $langval ); 33my $st6 = RDF::Trine::Statement->new( $p, $foaf->age, $intval); 34 35my ($stores, $remove) = stores(); 36 37plan tests => 7 + 85 * scalar(@$stores); 38 39print "### Testing auto-creation of store\n"; 40isa_ok( RDF::Trine::Model->new( 'Memory' ), 'RDF::Trine::Model' ); 41 42foreach my $store (@$stores) { 43 print "### Testing store " . ref($store) . "\n"; 44 isa_ok( $store, 'RDF::Trine::Store' ); 45 my $model = RDF::Trine::Model->new( $store ); 46 isa_ok( $model, 'RDF::Trine::Model' ); 47 $model->add_statement( $_ ) for ($st0, $st1, $st2, $st3); 48 49 { 50 is( $model->count_statements(), 4, 'model size' ); 51 $model->add_statement( $_ ) for ($st0); 52 is( $model->count_statements(), 4, 'model size after duplicate statements' ); 53 is( $model->count_statements( undef, $foaf->name, undef ), 2, 'count of foaf:name statements' ); 54 } 55 56 { 57 my $stream = $model->get_statements( $p, $foaf->name, RDF::Trine::Node::Variable->new('name') ); 58 my $st = $stream->next; 59 is_deeply( $st, $st1, 'foaf:name statement' ); 60 is( $stream->next, undef, 'end-of-stream' ); 61 } 62 63 { 64 throws_ok { 65 my $iter = $model->get_statements('<foo>'); 66 } 'RDF::Trine::Error::MethodInvocationError', 'get_statements called with non-object argument'; 67 68 } 69 70 { 71 throws_ok { 72 $model->add_statement($p, $rdf->type, $foaf->Person); 73 } 'RDF::Trine::Error::MethodInvocationError', 'add_statement called with 3 nodes, not a statement'; 74 throws_ok { 75 $model->add_statement($p, $rdf->type, $foaf->Person, $p); 76 } 'RDF::Trine::Error::MethodInvocationError', 'add_statement called with 4 nodes, not a statement'; 77 throws_ok { 78 $model->add_statement('http://example.org/subject', 'http://example.org/predicate', 'String'); 79 } 'RDF::Trine::Error::MethodInvocationError', 'add_statement called with strings, not a statement'; 80 } 81 82 83 { 84 my $stream = $model->get_statements( $b, $foaf->name, RDF::Trine::Node::Variable->new('name') ); 85 my $st = $stream->next; 86 is_deeply( $st, $st3, 'foaf:name statement (with bnode in triple)' ); 87 is( $stream->next, undef, 'end-of-stream' ); 88 } 89 90 { 91 my $stream = $model->get_statements( RDF::Trine::Node::Variable->new('p'), $foaf->name, RDF::Trine::Node::Literal->new('Gregory Todd Williams') ); 92 my $st = $stream->next; 93 is_deeply( $st, $st1, 'foaf:name statement (with literal in triple)' ); 94 is( $stream->next, undef, 'end-of-stream' ); 95 } 96 97 { 98 my $stream = $model->get_statements( RDF::Trine::Node::Variable->new('p'), $foaf->name, RDF::Trine::Node::Variable->new('name') ); 99 my $count = 0; 100 while (my $st = $stream->next) { 101 my $subj = $st->subject; 102 isa_ok( $subj, 'RDF::Trine::Node' ); 103 $count++; 104 } 105 is( $count, 2, 'expected result count (2 people) 1' ); 106 } 107 108 { 109 my $p1 = RDF::Trine::Statement->new( RDF::Trine::Node::Variable->new('p'), $rdf->type, $foaf->Person ); 110 my $p2 = RDF::Trine::Statement->new( RDF::Trine::Node::Variable->new('p'), $foaf->name, RDF::Trine::Node::Variable->new('name') ); 111 my $pattern = RDF::Trine::Pattern->new( $p1, $p2 ); 112 113 { 114 my $stream = $model->get_pattern( $pattern ); 115 my $count = 0; 116 while (my $b = $stream->next) { 117 isa_ok( $b, 'HASH' ); 118 isa_ok( $b->{p}, 'RDF::Trine::Node', 'node person' ); 119 isa_ok( $b->{name}, 'RDF::Trine::Node::Literal', 'literal name' ); 120 like( $b->{name}->literal_value, qr/Eve|Gregory/, 'name pattern' ); 121 $count++; 122 } 123 is( $count, 2, 'expected result count (2 people) 2' ); 124 } 125 126 { 127 my $stream = $model->get_pattern( $pattern, undef, orderby => [ 'name', 'ASC' ] ); 128 is_deeply( [ $stream->sorted_by ], ['name', 'ASC'], 'results sort order' ); 129 my $count = 0; 130 my @expect = ('Eve', 'Gregory Todd Williams'); 131 while (my $b = $stream->next) { 132 isa_ok( $b, 'HASH' ); 133 isa_ok( $b->{p}, 'RDF::Trine::Node', 'node person' ); 134 my $name = shift(@expect); 135 is( $b->{name}->literal_value, $name, 'name pattern' ); 136 $count++; 137 } 138 is( $count, 2, 'expected result count (2 people) 3' ); 139 } 140 141 { 142 my $stream = $model->get_pattern( $pattern, undef, orderby => [ qw(name DESC p ASC) ] ); 143 is_deeply( [ $stream->sorted_by ], ['name', 'DESC', 'p', 'ASC'], 'results sort order' ); 144 my $count = 0; 145 my @expect = ('Gregory Todd Williams', 'Eve'); 146 while (my $b = $stream->next) { 147 isa_ok( $b, 'HASH' ); 148 isa_ok( $b->{p}, 'RDF::Trine::Node', 'node person' ); 149 my $name = shift(@expect); 150 is( $b->{name}->literal_value, $name, 'name pattern' ); 151 $count++; 152 } 153 is( $count, 2, 'expected result count (2 people) 4' ); 154 } 155 156 { 157 my $stream = $model->get_pattern( $pattern, undef, orderby => [ 'date', 'ASC' ] ); 158 is_deeply( [ $stream->sorted_by ], [], 'results sort order for unknown binding' ); 159 } 160 161 { 162 throws_ok { 163 my $stream = $model->get_pattern( $pattern, undef, orderby => [ 'name' ] ); 164 } 'RDF::Trine::Error::MethodInvocationError', 'bad ordering request throws exception'; 165 } 166 } 167 168 { 169 my $stream = $model->get_pattern( $st0 ); 170 my $empty = $stream->next; 171 is_deeply( $empty, RDF::Trine::VariableBindings->new({}), 'empty binding on no-variable pattern' ); 172 is( $stream->next, undef, 'end-of-stream' ); 173 } 174 175 { 176 my $stream = $model->as_stream(); 177 isa_ok( $stream, 'RDF::Trine::Iterator::Graph' ); 178 my $count = 0; 179 while (my $st = $stream->next) { 180 my $p = $st->predicate; 181 like( $p->uri_value, qr<(#type|/name)$>, 'as_stream statement' ); 182 $count++; 183 } 184 is( $count, 4, 'expected model statement count (4)' ); 185 } 186 187 { 188 { 189 my @subj = $model->subjects( $rdf->type ); 190 my @preds = $model->predicates( $p ); 191 my @objs = $model->objects( $p ); 192 is( scalar(@subj), 2, "expected subject count on rdf:type" ); 193 is( scalar(@preds), 2, "expected predicate count on " . $p->uri_value ); 194 is( scalar(@objs), 2, "expected objects count on " . $p->uri_value ); 195 } 196 { 197 my @subjs = $model->subjects( $foaf->name, literal('Eve') ); 198 my @preds = $model->predicates( $p, $foaf->Person ); 199 my @objs = $model->objects( $p, $rdf->type ); 200 is( scalar(@subjs), 1, "expected subject count on rdf:type" ); 201 ok( $subjs[0]->isa('RDF::Trine::Node::Blank'), 'expected subject' ); 202 is( scalar(@preds), 1, "expected predicate count on " . $p->uri_value ); 203 ok( $preds[0]->equal( $rdf->type ), 'expected predicate' ); 204 is( scalar(@objs), 1, "expected objects count on " . $p->uri_value ); 205 ok( $objs[0]->equal( $foaf->Person ), 'expected object' ); 206 } 207 { 208 my $subjs = $model->subjects( $rdf->type ); 209 my $preds = $model->predicates( $p ); 210 my $objs = $model->objects( $p ); 211 isa_ok( $subjs, 'RDF::Trine::Iterator', 'expected iterator from subjects()' ); 212 isa_ok( $preds, 'RDF::Trine::Iterator', 'expected iterator from predicates()' ); 213 isa_ok( $objs, 'RDF::Trine::Iterator', 'expected iterator from objects()' ); 214 } 215 } 216 217 { 218 my $st5 = RDF::Trine::Statement->new( $p, $foaf->name, RDF::Trine::Node::Literal->new('グレゴリ ウィリアムス', 'jp') ); 219 $model->add_statement( $st5 ); 220 221 my $pattern = RDF::Trine::Statement->new( $p, $foaf->name, RDF::Trine::Node::Variable->new('name') ); 222 my $stream = $model->get_pattern( $pattern ); 223 my $count = 0; 224 while (my $b = $stream->next) { 225 isa_ok( $b, 'HASH' ); 226 isa_ok( $b->{name}, 'RDF::Trine::Node::Literal', 'literal name' ); 227 my $value = $b->{name}->literal_value; 228 like( $value, qr/Gregory|グレゴリ/, 'name pattern with language-tagged result' ); 229 $count++; 230 } 231 is( $count, 2, 'expected result count (2 names)' ); 232 is( $model->count_statements(), 5, 'model size' ); 233 $model->remove_statement( $st5 ); 234 is( $model->count_statements(), 4, 'model size after remove_statement' ); 235 } 236 237 { 238 my $st6 = RDF::Trine::Statement->new( $p, $foaf->name, RDF::Trine::Node::Literal->new('Gregory Todd Williams', undef, 'http://www.w3.org/2000/01/rdf-schema#Literal') ); 239 $model->add_statement( $st6 ); 240 241 my $pattern = RDF::Trine::Statement->new( $p, $foaf->name, RDF::Trine::Node::Variable->new('name') ); 242 my $stream = $model->get_pattern( $pattern ); 243 my $count = 0; 244 my $dt = 0; 245 while (my $b = $stream->next) { 246 my $name = $b->{name}; 247 isa_ok( $b, 'HASH' ); 248 isa_ok( $name, 'RDF::Trine::Node::Literal', 'literal name' ); 249 is( $name->literal_value, 'Gregory Todd Williams', 'name pattern with datatyped result' ); 250 if (my $type = $name->literal_datatype) { 251 is( $type, 'http://www.w3.org/2000/01/rdf-schema#Literal', 'datatyped literal' ); 252 $dt++; 253 } 254 $count++; 255 } 256 is( $count, 2, 'expected result count (2 names)' ); 257 is( $dt, 1, 'expected result count (1 datatyped literal)' ); 258 } 259 260 { 261 $model->remove_statements( $p ); 262 is( $model->count_statements(), 2, 'model size after remove_statements' ); 263 } 264 265 { 266 throws_ok { 267 my $pattern = RDF::Trine::Pattern->new(); 268 my $stream = $model->get_pattern( $pattern ); 269 } 'RDF::Trine::Error::CompilationError', 'empty GGP throws exception'; 270 } 271} 272 273foreach my $file (@$remove) { 274 unlink( $file ); 275} 276 277{ # test optional parameters of RDF::Trine::Model::objects 278 my $model = RDF::Trine::Model->new; 279 $model->add_statement( $_ ) for ($st0, $st1, $st3, $st4, $st5, $st6); 280 my %types = (blank => 1, literal => 3, resource => 1); 281 while (my ($type,$count) = each(%types)) { 282 my @objs = $model->objects( $p, undef, type => $type ); 283 is( scalar(@objs), $count, "expected objects count on type $type"); 284 } 285 my @objs = $model->objects( $p, undef, language => 'en' ); 286 ok( $objs[0]->equal( $langval ), 'expected integer value as object' ); 287 foreach my $dt ( $xsd->int, $xsd->int->uri_value ) { 288 @objs = $model->objects( $p, undef, datatype => $dt ); 289 ok( $objs[0]->equal( $intval ), 'expected integer value as object' ); 290 } 291} 292 293 294sub stores { 295 my @stores; 296 my @removeme; 297 push(@stores, RDF::Trine::Store::Memory->temporary_store()); 298 299 { 300 my $store = RDF::Trine::Store::DBI->new(); 301 $store->init(); 302 push(@stores, $store); 303 } 304 305 { 306 my ($fh, $filename) = tempfile(); 307 undef $fh; 308 my $dbh = DBI->connect( "dbi:SQLite:dbname=${filename}", '', '' ); 309 my $store = RDF::Trine::Store::DBI->new( 'model', $dbh ); 310 $store->init(); 311 push(@stores, $store); 312 push(@removeme, $filename); 313 } 314 315 { 316 my ($fh, $filename) = tempfile(); 317 undef $fh; 318 my $dsn = "dbi:SQLite:dbname=${filename}"; 319 my $store = RDF::Trine::Store::DBI->new( 'model', $dsn, '', '' ); 320 $store->init(); 321 push(@stores, $store); 322 push(@removeme, $filename); 323 } 324 return (\@stores, \@removeme); 325} 326 327sub debug { 328 my $store = shift; 329 my $dbh = $store->dbh; 330 my $sth = $dbh->prepare( "SELECT * FROM Statements15799945864759145248" ); 331 $sth->execute(); 332 while (my $row = $sth->fetchrow_hashref) { 333 warn Dumper($row); 334 } 335} 336