#!/usr/bin/perl -w
# $File: //depot/libOurNet/BBS/t/stdtests $ $Author: autrijus $
# $Revision: #3 $ $Change: 8639 $ $DateTime: 2003/10/30 12:12:52 $
$|++;
use strict;
use constant IsWin32 => ($^O eq 'MSWin32');
use File::Path;
use File::Spec;
use File::Basename;
use Test::More 0.32;
sub BEGIN {
no strict 'refs';
no warnings qw/once redefine/;
# XXX kluge against insufficient granularity on file timestamps
*{'OurNet::BBS::Base::timestamp'} = sub {
$_[0]->{ $_[2] || 'mtime' } = $_[1] and return;
};
if (ref($::BBS) eq 'HASH') {
plan skip_all => 'RPC::PlServer not installed'
unless (eval "use RPC::PlServer; 1;");
$OurNet::BBS::BYPASS_NEGOTIATION = 1;
OurNet::BBS->new($::BBS)->daemonize(7977) if $ARGV[0];
}
else {
# local BBS: test for compliation
my $manifest = File::Spec->catfile(dirname($0), '..', 'MANIFEST');
open $::MANIFEST, $manifest or die "cannot open $manifest";
}
}
our($pid, $TODO, $Heading);
my $BBS = $::BBS;
my $IsClient = ref($BBS) eq 'HASH';
my $backend = $1 if $BBS =~ m/OurNet::BBS::([^:]+)::/;
my @modules = grep { m|BBS/$backend| } <$::MANIFEST> and close $::MANIFEST
if $backend and $::MANIFEST;
# the extra test is use_ok('Win32::Process')
plan tests => 44 + @modules + ($IsClient and IsWin32);
## Compilation ###############################################################
require_ok("OurNet::$_") foreach map {
chomp;
s'/'::'g;
s'.pm'';
s'lib::OurNet::'';
$_;
} @modules;
## Connectivity ##############################################################
if ($IsClient) {
# OurNet connectivity
if (IsWin32) {
use_ok('Win32::Process') or die $^E; # the extra test
Win32::Process::Create($pid, $^X, qq($^X "$0" 1), 0, 32, '.');
}
else {
ok(exec($^X, $0, 1), 'fork()') unless $pid = fork;
}
for (1 .. 20) {
last if $BBS = eval {
OurNet::BBS->new(
{
backend => 'OurNet',
bbsroot => 'localhost',
peerport => 7977
}
);
};
sleep 1;
}
die "cannot connect: $@" unless $BBS;
}
_dump() if $ARGV[0]; # dump object guts
## Base ######################################################################
$Heading = 'base';
is($BBS->REF, $BBS->module('BBS'), __('constructor'));
my $brd = $BBS->{boards}{test}; # XXX: if we don't do this, tests fail.
# make a board...
$BBS->{boards}{test} = {
title => 'before coderef',
bm => 'sysop',
};
# XXX: has to assign coderef directly
$brd->{title} = sub { 1 };
is($brd->{title}->(), 1, __('callback'));
$brd->{title} = 'after coderef';
## Board #####################################################################
$Heading = 'board';
eq_array([ keys(%{ $BBS->{boards} }) ], ['test'], __('all keys'));
is($BBS->{boards}{test}{bm}, 'sysop', __('metadata'));
is($brd, $BBS->{boards}{test}, __('board fetch'));
## Article ###################################################################
$Heading = 'article';
is($brd->{articles}, $brd->{articles}, __('equality'));
# push #1
push (
@{ $brd->{articles} },
{
header => {
Subject => 'test title',
From => 'user',
},
body => 'bodie 0',
}
);
my $art = $brd->{articles};
is($#{$art}, 0, __('fetchsize'));
is($art->[0]{author}, 'user', __('push + metadata'));
isnt($art->[0]->REF, $art->REF, __('REF context'));
# append (store-as-push) #1
$brd->{articles}{''} = {
title => 'hashy title',
author => 'xyzzy',
body => 'bodie 1',
};
is($brd->{articles}[1]{title}, 'hashy title', __("store-as-push"));
# append #2
$brd->{articles}{''} = {
title => 'random title',
author => 'smart',
body => 'bodie 2',
};
my $head = $brd->{articles}[2]{header};
is($brd->{articles}[2]{header}{From}, 'smart', __('header'));
isnt(
$brd->{articles}[1]{header}{Date}, $brd->{articles}[2]{header}{Date},
__('chrono-ahead')
);
like($brd->{articles}[2]{body}, qr/bodie/, __('body fetch'));
is(index($brd->{articles}[2]{header}{'Message-ID'}, '@'), 38, __('msgid'));
$brd->{articles}{''} = $brd->{articles}[2];
is(
$brd->{articles}[2]{header}{From}, $brd->{articles}[3]{header}{From},
__('push by reference')
);
$brd->{articles}[-1]{body} = 'bodie 3'; # fix for the foreach test below
# alternative access
my $id = $brd->{articles}[2]{id};
like($brd->{articles}{$id}{body}, qr/bodie/, __('fetch via id'));
# nested fetch
TODO: {
local $TODO = 'nested fetch breaks OurNet' if $IsClient;
like(
$brd->{articles}{ $brd->{articles}[2]{id} }{body}, qr/bodie/,
__('nested fetch via id')
);
}
$brd->{articles}->purge unless $BBS->REF =~ /RAM/;
# set #1
$brd->{articles}[0] = {
author => 'changed author',
title => 'changed title',
body => $brd->{articles}[0]{body},
};
is($brd->{articles}[0]{title}, 'changed title', __('attribute store'));
# foreach iteration
my $flag = 0;
foreach my $index (0 .. $#{ $brd->{articles} }) { # can't use @{} for OurNet
my $t = $brd->{articles}[$flag];
is($t->{body}, "bodie $flag", __("foreach ($flag)"));
ok($t->mtime, __("mtime for ($flag)"));
unless ($flag++) {
$BBS->{boards}{kitty} = {
title => 'test board',
bm => 'sysop',
};
is($BBS->{boards}{kitty}{bm}, 'sysop', __('access inside loop'));
}
}
# each interation
while (my ($k, $v) = each(%{ $brd->{articles} })) {
is($v->{title}, $brd->{articles}{$k}{title}, __('each %{}'));
ok($flag-- > 0, __("key count consistency ($k)"));
}
## Archive ###################################################################
$Heading = 'archive';
TODO: {
local $TODO = __('push array via OurNet fails') if $IsClient;
# archiving
push @{ $brd->{archives} }, @{ $brd->{articles} };
like($brd->{archives}[1]{title}, qr/hashy title/, __('push & access'));
}
# archive directory
push (
@{ $brd->{archives} },
bless(
{
title => 'Random Directory',
author => 'random',
},
$brd->module('ArticleGroup'))
);
# is store successful?
is($brd->{archives}[-1]{author}, 'random', __('create via bless'));
# push into new dir
push (
@{ $brd->{archives}[-1] },
{
title => 'turandot',
author => 'aida',
body => 'satva',
}
);
is($brd->{archives}[-1]->name, $brd->{archives}[-1]{id}, __('by-name fetch'));
TODO: {
local $TODO = 'push array via OurNet fails' if $IsClient;
like($brd->{archives}[4][0]{title}, qr/turandot/, __('new dir'));
local $TODO = ($BBS->backend eq 'RAM') ? $TODO : undef;
like($brd->{archives}[-1][0]{title}, qr/turandot/, __('negative index'));
}
## Group ####################################################################
$Heading = 'group';
my $grp;
SKIP: {
skip(__('no groups for this backend'), 2) unless eval {
$BBS->{groups}{home} = {
owner => 'autrijus',
title => 'home sweet home',
};
};
# new group
$grp = $BBS->{groups}{home};
is(join ('', keys(%{ $BBS->{groups} })), 'home', __('keys'));
TODO: {
local $TODO = 'todo: CVIC deletion on Win32 File System'
if IsWin32
and !$IsClient
and $BBS->backend eq 'CVIC';
# group inside group
++$BBS->{groups}{rainbow}{home};
is_deeply([ sort { $a cmp $b } keys(%{ $BBS->{groups} }) ],
[qw/home rainbow/], __('modification'));
}
}
SKIP: {
TODO: {
skip(__('no groups for this backend'), 1) unless $grp;
local $TODO = 'todo: CVIC deletion'
if $BBS->backend eq 'CVIC';
# delete group
delete $BBS->{groups}{home};
like(
join ('', keys(%{ $BBS->{groups}{rainbow} })), qr/^(?!.*home).*$/,
__('delete')
);
}
}
no strict 'refs';
my %brdmap = (
MAPLE3 => 'brd',
MELIX => 'brd',
PTT => 'board',
CVIC => 'board',
MAPLE2 => 'board',
);
my $path_brd = ${ $BBS->module('BoardGroup') . '::PATH_BRD' }
|| $brdmap{ $BBS->backend };
## Cleanup ###################################################################
$Heading = 'cleanup';
SKIP: {
skip(__('no $PATH_BRD for deletion'), 1) unless $path_brd;
# delete board
delete $BBS->{boards}{test};
ok(!(-e "$::prefix/$path_brd/test/.DIR"), __('board delete'));
}
is($BBS, $BBS, __('object consistency'));
exit;
## Utilities #################################################################
sub __ { return "[$Heading] @_"; }
sub _dump {
my $T = tied(%{ ${$BBS}->[1] });
print '$BBS: ' . "$BBS\n";
print ' ${$BBS}: ' . "${$BBS}\n";
print ' ${$BBS}->[EGO]: ' . "${$BBS}->[EGO()]\n";
print ' ${$BBS}->[0]{_ego}: ' . "${$BBS}->[0]{_ego} # top\n";
print ' ${$BBS}->[0][0]: ' . "${$BBS}->[0][0] # phash keys\n";
print ' ${$BBS}->[0][1]: ' . "${$BBS}->[0][1] # phash: backend\n";
print ' ${$BBS}->[HASH]: ' . "${$BBS}->[1] # hash accessor\n";
print ' tied(%{${$BBS}->[1]}): ' . "$T # \$T\n";
print ' ${$T}: ' . "${$T}\n";
print ' ${$T}->[EGO]: ' . "${$T}->[EGO()] # same as above\n";
print ' ${$T}->[0]{_ego}: ' . "${$T}->[0]{_ego} # top\n";
print ' ${$T}->[FLAG]: ' . "${$T}->[FLAG()] # HASH\n";
}
END {
if ($::pid) {
UNIVERSAL::can($pid, 'Kill') ? $pid->Kill(1) : kill(1, $::pid);
File::Path::rmtree([$::prefix], 0, 1) if $::prefix;
}
}
__END__
syntax highlighted by Code2HTML, v. 0.9.1