The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
use Test::More qw(no_plan); # tests => 183;
use Test::Exception;

use strict;
use warnings;
no warnings 'redefine';
use Scalar::Util qw(blessed);
use File::Temp qw(tempfile);

use RDF::Trine qw(iri literal variable statement);
use RDF::Trine::Pattern;
use RDF::Trine::Store::Hexastore;


my $store	= RDF::Trine::Store::Hexastore->new();
_add_rdf( $store, <<'END');
@prefix eg: <http://example.org/> .

eg:foo
    eg:bar 23, 24 ;
    eg:baz "quux" .

eg:zzz
    eg:bar 23, 999 .
END

{
	my $p1		= statement( variable('p'), iri('http://example.org/bar'), variable('value') );
	my $pattern	= RDF::Trine::Pattern->new( $p1 );
	my $iter	= $store->get_pattern( $pattern );
	isa_ok( $iter, 'RDF::Trine::Iterator::Bindings' );
	my $count	= 0;
	while (my $row = $iter->next) {
		isa_ok( $row, 'HASH' );
		my $value	= $row->{ value };
		isa_ok( $value, 'RDF::Trine::Node::Literal' );
		like( $value->literal_value, qr/^(23|24|999)$/, 'expected literal numeric value' );
		$count++;
	}
	is( $count, 4, 'expected count of triples matching predicate <bar>' );
}

{
	# <zzz> ?p 999 .
	# ?s ?p ?value .
	my $p1		= statement(
					iri('http://example.org/zzz'),
					variable('p'),
					literal('999', undef, 'http://www.w3.org/2001/XMLSchema#integer')
				);
	my $p2		= statement(
					variable('s'),
					variable('p'),
					variable('value')
				);
	my $pattern	= RDF::Trine::Pattern->new( $p1, $p2 );
	my $iter	= $store->get_pattern( $pattern );
	isa_ok( $iter, 'RDF::Trine::Iterator::Bindings' );
	my $count	= 0;
	while (my $row = $iter->next) {
		isa_ok( $row, 'HASH' );
		like( $row->{'s'}->uri_value, qr<http://example.org/(zzz|foo)$>, 'expected subject variable' );
		is( $row->{'p'}->uri_value, 'http://example.org/bar', 'expected predicate variable' );
		like( $row->{'value'}->literal_value, qr<^(23|24|999)$>, 'expected object variable' );
		$count++;
	}
	is( $count, 4, 'expected count on b1b,213 (requires sorting on 3-free get_statements call)' );
}

{
	my $iter	= $store->get_statements( variable('s'), iri('http://example.org/bar'), variable('value'), undef, orderby => [ 's' => 'ASC' ] );
	isa_ok( $iter, 'RDF::Trine::Iterator::Graph' );
	my $count	= 0;
	my %seen;
	my $current;
	while (my $t = $iter->next) {
		if (defined($current)) {
			my $new	= $t->subject;
			if ($current->as_string ne $new->as_string) {
				if ($seen{ $new->as_string }++) {
					fail('distinct sorted subject value');
				} else {
					pass('distinct sorted subject value');
				}
			}
			$current	= $new;
		} else {
			$seen{ $t->subject->as_string }++;
		}
		$current	= $t->subject;
		$count++;
	}
	is( $count, 4, 'expected count on sorted get_statements call' );
}

{
	my @sorted;
	{
		my $iter	= $store->get_statements( variable('s'), iri('http://example.org/bar'), variable('o'), undef, orderby => [ 'o' => 'ASC' ] );
		isa_ok( $iter, 'RDF::Trine::Iterator::Graph' );
		my $count	= 0;
		my %seen;
		my $current;
		while (my $t = $iter->next) {
			if (defined($current)) {
				my $new	= $t->object;
				if ($current->as_string ne $new->as_string) {
					if ($seen{ $new->as_string }++) {
						fail('distinct sorted object value');
					} else {
						pass('distinct sorted object value');
					}
					push(@sorted, $new->as_string);
				}
				$current	= $new;
			} else {
				my $s	= $t->object->as_string;
				$seen{ $s }++;
				push(@sorted, $s);
			}
			$current	= $t->object;
			$count++;
		}
		is( $count, 4, 'expected count on sorted get_statements call' );
	}
	
	{
		my $iter	= $store->get_statements( variable('s'), iri('http://example.org/bar'), variable('o'), undef, orderby => [ 'o' => 'DESC' ] );
		my $current;
		while (my $t = $iter->next) {
			if (defined($current)) {
				my $new	= $t->object;
				if ($current->as_string ne $new->as_string) {
					is( $new->as_string, pop(@sorted), 'expected reverse sorted object value' );
				}
				$current	= $new;
			} else {
				my $s	= $t->object->as_string;
				is( $s, pop(@sorted), 'expected reverse sorted object value' );
			}
			$current	= $t->object;
		}
	}
}

{
	my @sorted;
	{
		my $iter	= $store->get_statements( variable('s'), variable('p'), variable('o'), undef, orderby => [ 'o' => 'ASC' ] );
		isa_ok( $iter, 'RDF::Trine::Iterator::Graph' );
		my $count	= 0;
		my %seen;
		my $current;
		while (my $t = $iter->next) {
			if (defined($current)) {
				my $new	= $t->object;
				if ($current->as_string ne $new->as_string) {
					if ($seen{ $new->as_string }++) {
						fail('distinct sorted object value');
					} else {
						pass('distinct sorted object value');
					}
					push(@sorted, $new->as_string);
				}
				$current	= $new;
			} else {
				my $s	= $t->object->as_string;
				$seen{ $s }++;
				push(@sorted, $s);
			}
			$current	= $t->object;
			$count++;
		}
		is( $count, 5, 'expected count on sorted get_statements call' );
	}
	
	{
		my $iter	= $store->get_statements( variable('s'), variable('p'), variable('o'), undef, orderby => [ 'o' => 'DESC' ] );
		my $current;
		while (my $t = $iter->next) {
			if (defined($current)) {
				my $new	= $t->object;
				if ($current->as_string ne $new->as_string) {
					is( $new->as_string, pop(@sorted), 'expected reverse sorted object value' );
				}
				$current	= $new;
			} else {
				my $s	= $t->object->as_string;
				is( $s, pop(@sorted), 'expected reverse sorted object value' );
			}
			$current	= $t->object;
		}
	}
}

################

sub _add_rdf {
	my $store	= shift;
	my $data	= shift;
	my $base	= shift || 'http://example.org/';
	my $parser	= RDF::Trine::Parser::Turtle->new();
	my $model	= RDF::Trine::Model->new($store);
	$parser->parse_into_model( $base, $data, $model );
}