File Coverage

blib/lib/Test/RDF.pm
Criterion Covered Total %
statement 237 243 97.5
branch 76 90 84.4
condition 39 72 54.1
subroutine 28 28 100.0
pod 15 15 100.0
total 395 448 88.1


line stmt bran cond sub pod time code
1             package Test::RDF;
2              
3 10     10   470886 use 5.006;
  10         37  
4 10     10   46 use warnings;
  10         15  
  10         223  
5 10     10   43 use strict;
  10         21  
  10         224  
6              
7 10     10   48 use Carp qw(confess);
  10         14  
  10         497  
8 10     10   2636 use RDF::Trine;
  10         10512473  
  10         536  
9 10     10   89 use RDF::Trine::Parser;
  10         19  
  10         259  
10 10     10   61 use RDF::Trine::Model;
  10         20  
  10         221  
11 10     10   47 use RDF::Trine::Graph;
  10         16  
  10         205  
12 10     10   45 use Scalar::Util qw/blessed/;
  10         20  
  10         469  
13              
14 10     10   96 use base 'Test::Builder::Module';
  10         18  
  10         5876  
15             our @EXPORT = qw/are_subgraphs is_rdf is_valid_rdf isomorph_graphs has_subject has_predicate has_object_uri has_type has_uri hasnt_uri has_literal hasnt_literal pattern_target pattern_ok pattern_fail/;
16              
17              
18             =head1 NAME
19              
20             Test::RDF - Test RDF data for content, validity and equality, etc.
21              
22             =head1 VERSION
23              
24             Version 1.22
25              
26             =cut
27              
28             our $VERSION = '1.22';
29              
30              
31             =head1 SYNOPSIS
32              
33             use Test::RDF;
34              
35             is_valid_rdf($rdf_string, $syntax, 'RDF string is valid according to selected syntax');
36             is_rdf($rdf_string, $syntax1, $expected_rdf_string, $syntax2, 'The two strings have the same triples');
37             isomorph_graphs($model, $expected_model, 'The two models have the same triples');
38             are_subgraphs($model1, $model2, 'Model 1 is a subgraph of model 2' );
39             has_uri($uri_string, $model, 'Has correct URI');
40             hasnt_uri($uri_string, $model, "Hasn't correct URI");
41             has_subject($uri_string, $model, 'Subject URI is found');
42             has_predicate($uri_string, $model, 'Predicate URI is found');
43             has_object_uri($uri_string, $model, 'Object URI is found');
44             has_type($uri_string, $model, 'Class URI is found');
45             has_literal($string, $language, $datatype, $model, 'Literal is found');
46             hasnt_literal($string, $language, $datatype, $model, 'Literal is not found');
47             pattern_target($model);
48             pattern_ok($pattern, '$pattern found in $model');
49             pattern_fail($pattern, '$pattern not found in $model');
50              
51             =head1 DESCRIPTION
52              
53             This Perl module, Test::RDF, provides tools for testing code which
54             deals with RDF. It can test RDF for validity, check if two RDF graphs
55             are the same, or subgraphs of each other, if a URI is or is not in a
56             dataset, if it has certain subjects, predicates, objects or
57             literals. It can also test to see if a full pattern is present or
58             absent.
59              
60              
61             =head1 EXPORT
62              
63             =head2 is_valid_rdf
64              
65             Use to check if the input RDF string is valid in the chosen syntax
66              
67             =cut
68              
69             sub is_valid_rdf {
70 5     5 1 8144 my ($rdf, $syntax, $name) = @_;
71 5         28 my $test = __PACKAGE__->builder;
72 5 100       31 unless ($rdf) {
73 1         5 $test->ok( 0, $name );
74 1         326 $test->diag("No input was given.");
75 1         133 return;
76             }
77 4         20 my $parser = RDF::Trine::Parser->new($syntax);
78 4         24374 eval {
79 4     2   21 $parser->parse('http://example.org/', $rdf, sub {});
80             };
81 4 100       10720 if ( my $error = $@ ) {
82 2         32 $test->ok( 0, $name );
83 2         686 $test->diag("Input was not valid RDF:\n\n\t$error");
84 2         266 return;
85             }
86             else {
87 2         20 $test->ok( 1, $name );
88 2         912 return 1;
89             }
90             }
91              
92              
93             =head2 is_rdf
94              
95             Use to check if the input RDF strings are isomorphic (i.e. the same).
96              
97             =cut
98              
99              
100             sub is_rdf {
101 5     5 1 10378 my ($rdf1, $syntax1, $rdf2, $syntax2, $name) = @_;
102 5         18 my $test = __PACKAGE__->builder;
103 5 100       29 unless ($rdf1) {
104 1         6 $test->ok( 0, $name );
105 1         312 $test->diag("No input was given.");
106 1         136 return;
107             }
108 4         19 my $parser1 = RDF::Trine::Parser->new($syntax1);
109 4         101 local $Test::Builder::Level = $Test::Builder::Level + 1;
110              
111             # First, test if the input RDF is OK
112 4         20 my $model1 = RDF::Trine::Model->temporary_model;
113 4         193 eval {
114 4         20 $parser1->parse_into_model('http://example.org/', $rdf1, $model1);
115             };
116 4 50       37686 if ( my $error = $@ ) {
117 0         0 $test->ok( 0, $name );
118 0         0 $test->diag("Input was not valid RDF:\n\n\t$error");
119 0         0 return;
120             }
121              
122             # If the expected RDF is non-valid, don't catch the exception
123 4         19 my $parser2 = RDF::Trine::Parser->new($syntax2);
124 4         24270 my $model2 = RDF::Trine::Model->temporary_model;
125 4         187 $parser2->parse_into_model('http://example.org/', $rdf2, $model2);
126 4         29056 return isomorph_graphs($model1, $model2, $name);
127             }
128              
129              
130             =head2 isomorph_graphs
131              
132             Use to check if the input RDF::Trine::Models have isomorphic graphs.
133              
134             =cut
135              
136              
137             sub isomorph_graphs {
138 8     8 1 107623 my ($model1, $model2, $name) = @_;
139 8 50 33     118 confess 'No valid models given in test' unless ((blessed($model1) && $model1->isa('RDF::Trine::Model'))
      33        
      33        
140             && (blessed($model2) && $model2->isa('RDF::Trine::Model')));
141 8         65 my $g1 = RDF::Trine::Graph->new( $model1 );
142 8         160 my $g2 = RDF::Trine::Graph->new( $model2 );
143 8         119 my $test = __PACKAGE__->builder;
144              
145 8 100       71 if ($g1->equals($g2)) {
146 4         3888 $test->ok( 1, $name );
147 4         1784 return 1;
148             } else {
149 4         3761 $test->ok( 0, $name );
150 4         1609 $test->diag('Graphs differ:');
151 4         484 $test->diag($g1->error);
152 4         474 return;
153             }
154             }
155              
156             =head2 are_subgraphs
157              
158             Use to check if the first RDF::Trine::Models is a subgraph of the second.
159              
160             =cut
161              
162             sub are_subgraphs {
163 8     8 1 223744 my ($model1, $model2, $name) = @_;
164 8 50 33     185 confess 'No valid models given in test' unless ((blessed($model1) && $model1->isa('RDF::Trine::Model'))
      33        
      33        
165             && (blessed($model2) && $model2->isa('RDF::Trine::Model')));
166 8         113 my $g1 = RDF::Trine::Graph->new( $model1 );
167 8         222 my $g2 = RDF::Trine::Graph->new( $model2 );
168 8         162 my $test = __PACKAGE__->builder;
169              
170 8 100       132 if ($g1->is_subgraph_of($g2)) {
171 4         4443 $test->ok( 1, $name );
172 4         2168 return 1;
173             } else {
174 4         4767 $test->ok( 0, $name );
175 4 100       2298 $test->diag('Graph not subgraph: ' . $g1->error) if defined($g1->error);
176 4         192 $test->diag('Hint: There are ' . $model1->size . ' statement(s) in model1 and ' . $model2->size . ' statement(s) in model2');
177 4         1111 return;
178             }
179             }
180              
181             =head2 has_subject
182              
183             Check if the string URI passed as first argument is a subject in any
184             of the statements given in the model given as second argument.
185              
186             =cut
187              
188             sub has_subject {
189 3     3 1 27887 my ($uri, $model, $name) = @_;
190 3 50 33     28 confess 'No valid model given in test' unless (blessed($model) && $model->isa('RDF::Trine::Model'));
191 3         8 my $resource = _resource_uri_checked($uri, $name);
192 3 100       11 return $resource unless ($resource);
193 2         349 my $count = $model->count_statements($resource, undef, undef);
194 2         1020 return _single_uri_tests($count, $name);
195             }
196              
197              
198             =head2 has_predicate
199              
200             Check if the string URI passed as first argument is a predicate in any
201             of the statements given in the model given as second argument.
202              
203             =cut
204              
205             sub has_predicate {
206 2     2 1 4536 my ($uri, $model, $name) = @_;
207 2 50 33     26 confess 'No valid model given in test' unless (blessed($model) && $model->isa('RDF::Trine::Model'));
208 2         5 my $resource = _resource_uri_checked($uri, $name);
209 2 50       7 return $resource unless ($resource);
210 2         380 my $count = $model->count_statements(undef, $resource, undef);
211 2         1000 return _single_uri_tests($count, $name);
212             }
213              
214             =head2 has_object_uri
215              
216             Check if the string URI passed as first argument is a object in any
217             of the statements given in the model given as second argument.
218              
219             =cut
220              
221             sub has_object_uri {
222 3     3 1 6796 my ($uri, $model, $name) = @_;
223 3 50 33     25 confess 'No valid model given in test' unless (blessed($model) && $model->isa('RDF::Trine::Model'));
224 3         8 my $resource = _resource_uri_checked($uri, $name);
225 3 100       11 return $resource unless ($resource);
226 2         364 my $count = $model->count_statements(undef, undef, $resource);
227 2         1011 return _single_uri_tests($count, $name);
228             }
229              
230              
231             =head2 has_type
232              
233             Check if the string URI passed as first argument is an RDF class
234             instance in any of the statements given in the model given as second
235             argument.
236              
237             =cut
238              
239             sub has_type {
240 1     1 1 2365 my ($uri, $model, $name) = @_;
241 1 50 33     10 confess 'No valid model given in test' unless (blessed($model) && $model->isa('RDF::Trine::Model'));
242 1         4 my $resource = _resource_uri_checked($uri, $name);
243 1 50       4 return $resource unless ($resource);
244 1         175 my $count = $model->count_statements(undef,
245             RDF::Trine::Node::Resource->new('http://www.w3.org/1999/02/22-rdf-syntax-ns#type'),
246             $resource);
247 1         309 return _single_uri_tests($count, $name);
248             }
249              
250             =head2 has_literal
251              
252             Check if the string passed as first argument, with corresponding
253             optional language and datatype as second and third respectively, is a
254             literal in any of the statements given in the model given as fourth
255             argument.
256              
257             language and datatype may not occur in the same statement, so the test
258             fails if they are both set. If none are used, use C<undef>, like e.g.
259              
260             has_literal('A test', undef, undef, $model, 'Simple literal');
261              
262             A test for a typed literal may be done like
263              
264             has_literal('42', undef, 'http://www.w3.org/2001/XMLSchema#integer', $model, 'Just an integer');
265              
266             and a language literal like
267              
268             has_literal('This is a Another test', 'en', undef, $model, 'Language literal');
269              
270              
271             =cut
272              
273             sub has_literal {
274 12     12 1 56953 my ($string, $lang, $datatype, $model, $name) = @_;
275 12 50 33     97 confess 'No valid model given in test' unless (blessed($model) && $model->isa('RDF::Trine::Model'));
276 12         20 my $literal;
277 12         37 my $test = __PACKAGE__->builder;
278 12         53 eval {
279 12         38 $literal = RDF::Trine::Node::Literal->new($string, $lang, $datatype);
280             };
281 12 100       465 if ( my $error = $@ ) {
282 2         19 $test->ok( 0, $name );
283 2         626 $test->diag("Invalid literal:\n\n\t$error");
284 2         233 return;
285             }
286              
287 10 100       30 if ($model->count_statements(undef, undef, $literal) > 0) {
288 4         3186 $test->ok( 1, $name );
289 4         1385 return 1;
290             } else {
291 6         1994 $test->ok( 0, $name );
292 6         1910 $test->diag('No matching literals found in model');
293 6         689 return 0;
294             }
295             }
296              
297              
298             =head2 hasnt_literal
299              
300             This is like the above, only the opposite: It checks if a literal
301             doesn't exist. Like the above, the test will fail if the literal is
302             invalid, however.
303              
304             =cut
305              
306             sub hasnt_literal {
307 13     13 1 56960 my ($string, $lang, $datatype, $model, $name) = @_;
308 13 50 33     116 confess 'No valid model given in test' unless (blessed($model) && $model->isa('RDF::Trine::Model'));
309 13         22 my $literal;
310 13         37 my $test = __PACKAGE__->builder;
311 13         61 eval {
312 13         39 $literal = RDF::Trine::Node::Literal->new($string, $lang, $datatype);
313             };
314              
315 13 100       499 if ( my $error = $@ ) {
316 2         19 $test->ok( 0, $name );
317 2         914 $test->diag("Invalid literal:\n\n\t$error");
318 2         237 return;
319             }
320              
321 11 100       35 if ($model->count_statements(undef, undef, $literal) > 0) {
322 3         2567 $test->ok( 0, $name );
323 3         961 $test->diag('Matching literals found in model');
324 3         334 return 0;
325             } else {
326 8         2505 $test->ok( 1, $name );
327 8         2716 return 1;
328             }
329             }
330              
331              
332              
333             =head2 has_uri
334              
335             Check if the string URI passed as first argument is present in any of
336             the statements given in the model given as second argument.
337              
338             =cut
339              
340             sub has_uri {
341 5     5 1 11131 my ($uri, $model, $name) = @_;
342 5 50 33     40 confess 'No valid model given in test' unless (blessed($model) && $model->isa('RDF::Trine::Model'));
343 5         16 my $test = __PACKAGE__->builder;
344 5         26 my $resource = _resource_uri_checked($uri, $name);
345 5 100       18 return $resource unless ($resource);
346 4 100 100     713 if ($model->count_statements(undef, undef, $resource) > 0
      100        
347             || $model->count_statements(undef, $resource, undef) > 0
348             || $model->count_statements($resource, undef, undef) > 0) {
349 3         2831 $test->ok( 1, $name );
350 3         966 return 1;
351             } else {
352 1         793 $test->ok( 0, $name );
353 1         328 $test->diag('No matching URIs found in model');
354 1         113 return 0;
355             }
356             }
357              
358              
359             =head2 hasnt_uri
360              
361             Check if the string URI passed as first argument is not present in any
362             of the statements given in the model given as second argument.
363              
364             =cut
365              
366             sub hasnt_uri {
367 5     5 1 12175 my ($uri, $model, $name) = @_;
368 5 50 33     37 confess 'No valid model given in test' unless (blessed($model) && $model->isa('RDF::Trine::Model'));
369 5         15 my $test = __PACKAGE__->builder;
370 5         22 my $resource;
371 5         6 eval {
372 5         15 $resource = RDF::Trine::Node::Resource->new($uri);
373             };
374 5 100 100     116 if (($resource) && ($model->count_statements(undef, undef, $resource) > 0
      100        
375             || $model->count_statements(undef, $resource, undef) > 0
376             || $model->count_statements($resource, undef, undef)) > 0) {
377 3         3284 $test->ok( 0, $name );
378 3         991 $test->diag('Matching URIs found in model');
379 3         343 return 0;
380             } else {
381 2         920 $test->ok( 1, $name );
382 2         622 return 1;
383             }
384             }
385              
386              
387             sub _single_uri_tests {
388 7     7   21 my ($count, $name) = @_;
389 7         22 my $test = __PACKAGE__->builder;
390 7         38 local $Test::Builder::Level = $Test::Builder::Level + 1;
391 7 100       15 if ($count > 0) {
392 3         20 $test->ok( 1, $name );
393 3         1107 return 1;
394             } else {
395 4         21 $test->ok( 0, $name );
396 4         1324 $test->diag('No matching URIs found in model');
397 4         454 return 0;
398             }
399             }
400              
401             sub _resource_uri_checked {
402 14     14   27 my ($uri, $name) = @_;
403 14         16 my $resource;
404 14         18 eval {
405 14         60 $resource = RDF::Trine::Node::Resource->new($uri);
406             };
407 14 100       393 if ( my $error = $@ ) {
408 3         20 my $test = __PACKAGE__->builder;
409 3         16 local $Test::Builder::Level = $Test::Builder::Level + 1;
410 3         14 $test->ok( 0, $name );
411 3         950 $test->diag("No matching URIs found in model");
412 3         327 return 0;
413             }
414 11         20 return $resource;
415             }
416              
417            
418              
419              
420              
421             =head2 pattern_target
422              
423             Tests that the object passed as its parameter is an RDF::Trine::Model or
424             RDF::Trine::Store. That is, tests that it is a valid thing to match basic
425             graph patterns against.
426              
427             Additionally, this test establishes the target for future C<pattern_ok> tests.
428              
429             =head2 pattern_ok
430              
431             Tests that the pattern passed matches against the target established by
432             C<pattern_target>. The pattern may be passed as an RDF::Trine::Pattern, or
433             a list of RDF::Trine::Statement objects.
434              
435             use Test::RDF;
436             use RDF::Trine qw[iri literal blank variable statement];
437             use My::Module;
438              
439             my $foaf = RDF::Trine::Namespace->new('http://xmlns.com/foaf/0.1/');
440             pattern_target(My::Module->get_model); # check isa RDF::Trine::Model
441             pattern_ok(
442             statement(
443             variable('who'),
444             $foaf->name,
445             literal('Kjetil Kjernsmo')
446             ),
447             statement(
448             variable('who'),
449             $foaf->page,
450             iri('http://search.cpan.org/~kjetilk/')
451             ),
452             "Data contains Kjetil's details."
453             );
454              
455             B<Note:> C<pattern_target> must have been tested before any C<pattern_ok> tests.
456              
457             =head2 pattern_fail
458              
459             The same as above, but tests if the pattern returns no results instead.
460              
461             =cut
462              
463             { # scope for $target
464             my $target;
465             sub pattern_target {
466 4     4 1 6318 my $t = shift;
467 4         27 my $test = __PACKAGE__->builder;
468 4 100 100     63 if (blessed($t) && $t->isa('RDF::Trine::Model')) {
    100 66        
469 2         12 $target = $t;
470 2         15 $test->ok(1, 'Data is an RDF::Trine::Model.');
471 2         753 return 1;
472             }
473             elsif (blessed($t) && $t->isa('RDF::Trine::Store')) {
474 1         3 $target = $t;
475 1         7 $test->ok(1, 'Data is an RDF::Trine::Store.');
476 1         313 return 1;
477             }
478             else {
479 1         10 $test->ok(0, 'Data is not an RDF::Trine::Model or RDF::Trine::Store.');
480 1         452 return 0;
481             }
482             }
483              
484             sub pattern_ok {
485 8     8 1 61062 my $message = undef;
486 8 100       27 $message = pop @_ if !ref $_[-1];
487 8 100 66     29 unless (defined $message and length $message) {
488 6         11 $message = "Pattern match";
489             }
490 8         27 my $test = __PACKAGE__->builder;
491 8 100       60 unless (blessed($target)) {
492 1         7 $test->ok(0, $message);
493 1         324 $test->diag("No target defined for pattern match. Call pattern_target test first.");
494 1         117 return 0;
495             }
496 7 100 66     55 my $pattern = (blessed($_[0]) and $_[0]->isa('RDF::Trine::Pattern'))
497             ? $_[0]
498             : RDF::Trine::Pattern->new(@_);
499 7         66 my $s = RDF::Trine::Serializer::Turtle->new();
500              
501 7         136 my $iter = $target->get_pattern($pattern);
502 7 100       31496 if ($iter->materialize->length > 0) {
503 4         754 $test->ok(1, $message);
504 4         1479 return 1;
505             }
506             # The test result is now known, return diagnostics
507 3         339 my $noreturns;
508 3         21 foreach my $triple ($pattern->triples) {
509 6         2385 my @triple;
510 6         13 foreach my $node ($triple->nodes) {
511 18 100       49 if ($node->is_variable) {
512 6         43 push(@triple, undef);
513             } else {
514 12         67 push(@triple, $node);
515             }
516             }
517 6 100       16 next if ($target->count_statements(@triple));
518 1         328 $noreturns .= $triple->as_string . "\n";
519             }
520 3         3384 $test->ok(0, $message);
521 3 100       1014 if ($noreturns) {
522 1         7 $test->diag("Triples that had no results:\n$noreturns");
523             } else {
524 2         9 $test->diag('Pattern as a whole did not match');
525             }
526 3         364 return 0;
527             }
528              
529             sub pattern_fail {
530 7     7 1 57353 my $message = undef;
531 7 100       23 $message = pop @_ if !ref $_[-1];
532 7 100 66     25 unless (defined $message and length $message) {
533 5         7 $message = "Pattern doesn't match";
534             }
535 7         27 my $test = __PACKAGE__->builder;
536 7 50       65 unless (blessed($target)) {
537 0         0 $test->ok(0, $message);
538 0         0 $test->diag("No target defined for pattern match. Call pattern_target test first.");
539 0         0 return 0;
540             }
541 7 100 66     54 my $pattern = (blessed($_[0]) and $_[0]->isa('RDF::Trine::Pattern'))
542             ? $_[0]
543             : RDF::Trine::Pattern->new(@_);
544 7         60 my $iter = $target->get_pattern($pattern)->materialize;
545              
546 7 100       31986 if ($iter->length == 0) {
547 3         34 $test->ok(1, $message);
548 3         1042 return 1;
549             }
550             # The test result is now known, return diagnostics
551 4         50 $test->ok(0, $message);
552 4         1427 $test->diag("These triples had results:\n" . $iter->as_string);
553 4         15970 return 0;
554             }
555             } # /scope for $target
556              
557              
558             =head1 NOTE
559              
560             Graph isomorphism is a complex problem, so do not attempt to run the
561             isomorphism tests on large datasets. For more information see
562             L<http://en.wikipedia.org/wiki/Graph_isomorphism_problem>.
563              
564              
565             =head1 AUTHOR
566              
567             Kjetil Kjernsmo, C<< <kjetilk at cpan.org> >>
568              
569             =head1 BUGS
570              
571             Please report any bugs using L<github|https://github.com/kjetilk/Test-RDF/issues>
572              
573              
574             =head1 SUPPORT
575              
576             You can find documentation for this module with the perldoc command.
577              
578             perldoc Test::RDF
579              
580             You may find the Perl and RDF community L<website|http://www.perlrdf.org/> useful.
581              
582             You can also look for information at:
583              
584             =over 4
585              
586             =item * AnnoCPAN: Annotated CPAN documentation
587              
588             L<http://annocpan.org/dist/Test-RDF>
589              
590             =item * CPAN Ratings
591              
592             L<http://cpanratings.perl.org/d/Test-RDF>
593              
594             =item * Search CPAN
595              
596             L<http://search.cpan.org/dist/Test-RDF/>
597              
598             =item * MetaCPAN
599              
600             L<https://metacpan.org/module/Test::RDF>
601              
602             =back
603              
604              
605             =head1 ACKNOWLEDGEMENTS
606              
607             Michael Hendricks wrote the first Test::RDF. The present module is a
608             complete rewrite from scratch using Gregory Todd William's
609             L<RDF::Trine::Graph> to do the heavy lifting.
610              
611             Toby Inkster has submitted the pattern_* functions.
612              
613             =head1 LICENSE AND COPYRIGHT
614              
615             Copyright 2010 ABC Startsiden AS.
616             Copyright 2010, 2011, 2012, 2013, 2014, 2015, 2017 Kjetil Kjernsmo.
617              
618             This program is free software; you can redistribute it and/or modify it
619             under the terms of either: the GNU General Public License as published
620             by the Free Software Foundation; or the Artistic License.
621              
622             See http://dev.perl.org/licenses/ for more information.
623              
624              
625             =cut
626              
627             1; # End of Test::RDF