Algorithm-Classifier-IsolationForest
view release on metacpan or search on metacpan
t/91-streamd.t view on Meta::CPAN
die "fork failed: $!" unless defined $pid;
if ( !$pid ) {
open( STDOUT, '>>', $logf ) or die $!;
open( STDERR, '>>', $logf ) or die $!;
exec( $^X, '-Ilib', $bin, 'streamd', '-f', @argv ) or die "exec failed: $!";
}
push @ALL_PIDS, $pid;
for ( 1 .. 100 ) {
last if -S $wait_sock;
select( undef, undef, undef, 0.1 ); ## no critic (ProhibitSleepViaSelect)
}
return $pid;
} ## end sub spawn_daemon
sub start_daemon {
my (@extra) = @_;
return spawn_daemon(
$sock,
'--socket' => $sock,
'--pid' => $pidf,
'--model-dir' => $mdir,
'--log' => $logf,
@extra
);
} ## end sub start_daemon
sub stop_daemon {
my ($pid) = @_;
kill( 'TERM', $pid );
waitpid( $pid, 0 );
return $? >> 8;
}
sub connect_client {
my ($path) = @_;
$path //= $sock;
my $s = IO::Socket::UNIX->new( Peer => $path )
or die "connect to $path failed: $!";
$s->autoflush(1);
$BUF{ fileno($s) } = '';
return $s;
}
sub read_reply {
my ( $s, $timeout ) = @_;
$timeout //= 10;
my $deadline = time + $timeout;
my $sel = IO::Select->new($s);
my $buf = \$BUF{ fileno($s) };
while ( $$buf !~ /\n/ ) {
my $left = $deadline - time;
return undef if $left <= 0 || !$sel->can_read($left);
my $got = sysread( $s, my $chunk, 65536 );
return undef unless $got;
$$buf .= $chunk;
}
$$buf =~ s/\A([^\n]*)\n//;
return $JSON->decode($1);
} ## end sub read_reply
# One request in, one decoded reply out.
sub rt {
my ( $s, $msg ) = @_;
print {$s} $JSON->encode($msg) . "\n";
return read_reply($s);
}
my $daemon = start_daemon(
'--save-interval' => 1,
'-n' => 20,
'--window' => 64,
'--eta' => 8,
'-s' => 42,
'-t' => 'cpu',
'-t' => 'mem'
);
END {
for my $pid (@ALL_PIDS) {
kill( 'TERM', $pid ) if kill( 0, $pid );
}
}
subtest 'startup artefacts' => sub {
ok( -S $sock, 'socket exists' ) or diag( scalar `cat $logf` );
ok( -f $pidf, 'pid file exists' );
my $recorded = do { local ( @ARGV, $/ ) = ($pidf); <> };
chomp $recorded;
is( $recorded, $daemon, 'pid file records the daemon pid (foreground: the child)' );
};
my $c = connect_client();
subtest 'ping and client-tag echo' => sub {
is_deeply( rt( $c, { cmd => 'ping' } ), { ok => 'pong' }, 'ping pongs, no tag when none sent' );
is_deeply(
rt( $c, { cmd => 'ping', tag => 'req-1' } ),
{ ok => 'pong', tag => 'req-1' },
'string tag echoed back'
);
is_deeply(
rt( $c, { cmd => 'ping', tag => { batch => 7, src => [ 'a', 'b' ] } } ),
{ ok => 'pong', tag => { batch => 7, src => [ 'a', 'b' ] } },
'structured tag echoed back verbatim'
);
}; ## end 'ping and client-tag echo' => sub
subtest 'learn, prequential, score modes' => sub {
srand(5);
my @warm = map { [ rand, rand ] } 1 .. 60;
is_deeply(
rt( $c, { rows => \@warm, mode => 'learn' } ),
{ ok => { learned => 60 } },
'rows batch in learn mode learns them all'
);
my $r = rt( $c, { row => [ 0.5, 0.5 ], tag => 'p1' } );
is( $r->{tag}, 'p1', 'row reply carries the tag' );
like( $r->{score}, qr/\A[\d.eE+-]+\z/, 'prequential row returns a numeric score' );
ok( $r->{label} == 0 || $r->{label} == 1, 'and a 0/1 label' );
( run in 0.860 second using v1.01-cache-2.11-cpan-600a1bdf6e4 )