#!/usr/bin/perl use warnings; use strict; use Test::More tests => 62; #use Test::More 'no_plan'; use lib 'lib/', '../lib/'; BEGIN { use_ok 'HOP::Stream', ':all' or die; } my @exported = qw( cutsort drop filter head insert is_node iterator_to_stream list_to_stream append merge node promise show tail transform upto upfrom ); foreach my $function (@exported) { no strict 'refs'; ok defined &$function, "&$function should be exported to our namespace"; } # node ok my $stream = node( 3, 4 ), 'Calling node() should succeed'; is_deeply $stream, [ 3, 4 ], '... returning a stream node'; ok my $new_stream = node( 7, $stream ), '... and a node may be in the node() arguments'; is_deeply $new_stream, [ 7, $stream ], '... and the new node should be correct'; # head is head($new_stream), 7, 'head() should return the head of a node'; # tail is tail($new_stream), $stream, 'tail() should return the tail of a node'; # drop ok my $head = drop($new_stream), 'drop() should succeed'; is $head, 7, '... returning the head of the node'; is_deeply $new_stream, $stream, '... and setting the tail of the node as the node'; # upto ok !upto( 5, 4 ), 'upto() should return false if the first number is greater than the second'; ok $stream = upto( 4, 7 ), '... but it should succeed if the first number is less than the second'; my @numbers; while ( defined( my $num = drop($stream) ) ) { push @numbers, $num; } is_deeply \@numbers, [ 4, 5, 6, 7 ], '... and the stream should return all of the numbers'; # upfrom ok $stream = upfrom(42), 'upfrom() should return a stream'; @numbers = (); for ( 1 .. 10 ) { push @numbers, drop($stream); } is_deeply \@numbers, [ 42 .. 51 ], '... which should return the numbers we expect'; # show show( $stream, 5 ); is show( $stream, 5 ), "52 53 54 55 56 ", 'Show should print the correct values'; # transform my $evens = transform { $_[0] * 2 } upfrom(1); ok $evens, 'Calling transform() on a stream should succeed'; @numbers = (); for ( 1 .. 5 ) { push @numbers, drop($evens); } is_deeply \@numbers, [ 2, 4, 6, 8, 10 ], '... which should return the numbers we expect'; # filter # forget the parens in the filter and it's an infinite loop $evens = filter { !( $_[0] % 2 ) } upfrom(1); ok $evens, 'Calling filter() on a stream should succeed'; @numbers = (); for ( 1 .. 5 ) { push @numbers, drop($evens); } is_deeply \@numbers, [ 2, 4, 6, 8, 10 ], '... which should return the numbers we expect'; # append my $stream1 = upto(4, 7); my $stream2 = upto(12, 15); my $stream3 = upto(25, 28); ok $stream = append($stream1, $stream2, $stream3), "append() should return a stream"; @numbers = (); while ( defined( my $num = drop($stream) ) ) { push @numbers, $num; } is_deeply \@numbers, [ 4..7, 12..15, 25..28 ], '... and the stream should return all of the numbers'; # merge sub scale { my ( $s, $c ) = @_; transform { $_[0] * $c } $s; } my $hamming; $hamming = node( 1, promise { merge( scale( $hamming, 2 ), merge( scale( $hamming, 3 ), scale( $hamming, 5 ), ) ) } ); @numbers = (); for ( 1 .. 10 ) { push @numbers, drop($hamming); } is_deeply \@numbers, [ 1, 2, 3, 4, 5, 6, 8, 9, 10, 12 ], 'merge() should let us merge sorted streams'; $evens = transform { $_[0] * 2 } upfrom(1); my $odds = transform { ( $_[0] * 2 ) - 1 } upfrom(1); my $number = merge( $odds, $evens ); @numbers = (); for ( 1 .. 10 ) { push @numbers, drop($number); } is_deeply \@numbers, [ 1 .. 10 ], '... and create the numbers one to ten'; # iterator_to_stream my @iter = qw/2 4 6 8/; my $iter = sub { shift @iter }; ok $stream = iterator_to_stream($iter), 'iterator_to_stream() should convert an iterator to a stream'; @numbers = (); while ( defined( my $number = drop($stream) ) ) { push @numbers, $number; } is_deeply \@numbers, [ 2, 4, 6, 8 ], '... and the stream should return the correct values'; # list_to_stream ok my $list = list_to_stream( 1 .. 9, node(10) ), 'list_to_stream() should return a stream'; @numbers = (); while ( defined( my $num = drop($list) ) ) { push @numbers, $num; } is_deeply \@numbers, [ 1 .. 10 ], '... and create the numbers one to ten'; # list_to_stream, final node computed internally ok $list = list_to_stream( 1 .. 10 ), 'list_to_stream() should return a stream'; @numbers = (); while ( defined( my $num = drop($list) ) ) { push @numbers, $num; } is_deeply \@numbers, [ 1 .. 10 ], '... and create the numbers one to ten'; # insert my @list = qw/seventeen three one/; # sorted by descending length my $compare = sub { length $_[0] < length $_[1] }; insert @list, 'four', $compare; is_deeply \@list, [qw/seventeen three four one/], 'insert() should be able to insert items according to our sort criteria'; # # streams of array refs do not work properly, because tail [a,b] is b, even if [a,b] is # the last stream element, and not a node # Solution: bless nodes, check is_node in head, tail, list_to_stream # $stream = list_to_stream( [A => 1], [B => 2] ); is_deeply $stream, bless([ [A => 1], bless([ [B => 2], undef ], 'HOP::Stream')], 'HOP::Stream'), "stream of array refs"; is_deeply head($stream), [A => 1], "... head is array ref"; is_deeply tail($stream), bless([ [B => 2], undef ], 'HOP::Stream'), "... tail is stream"; drop($stream); is_deeply $stream, bless([ [B => 2], undef ], 'HOP::Stream'), "stream of array refs, dropped 1"; is_deeply head($stream), [B => 2], "... head is array ref"; is_deeply tail($stream), undef, "... tail is stream"; drop($stream); is_deeply $stream, undef, "stream of array refs, dropped 2"; is_deeply head($stream), undef, "... head is undef"; is_deeply tail($stream), undef, "... tail is undef"; drop($stream); is_deeply $stream, undef, "stream of array refs, dropped 3"; is_deeply head($stream), undef, "... head is undef"; is_deeply tail($stream), undef, "... tail is undef"; # # use a non-stream as stream # $stream = [1, [2, [3]]]; is head($stream), undef, "no head of non-stream"; is tail($stream), undef, "no head of non-stream";