Ancient
view release on metacpan or search on metacpan
t/2025-doubly-threads.t view on Meta::CPAN
#!/usr/bin/env perl
use strict;
use warnings;
use Test::More;
# Thread tests for doubly
# NOTE: doubly objects are NOT cloned to threads (CLONE_SKIP => 1)
# Each thread must create its own lists.
BEGIN {
eval {
require threads;
require threads::shared;
};
if ($@) {
plan skip_all => 'threads not available';
}
}
use threads;
use threads::shared;
use_ok('doubly');
# Test 1: Basic operations work
subtest 'Basic doubly operations' => sub {
plan tests => 10;
my $list = doubly->new();
ok($list, 'List created');
is($list->length, 0, 'Empty list has length 0');
$list->add(1);
$list->add(2);
$list->add(3);
is($list->length, 3, 'Length after adding 3 items');
$list = $list->start;
is($list->data, 1, 'start returns first item');
ok($list->is_start, 'is_start true at start');
$list = $list->end;
is($list->data, 3, 'end returns last item');
ok($list->is_end, 'is_end true at end');
$list = $list->prev;
is($list->data, 2, 'prev moves backward');
$list = $list->next;
is($list->data, 3, 'next moves forward');
$list->destroy;
ok(1, 'destroy completed');
};
# Test 2: Thread-local lists (each thread creates its own)
subtest 'Thread-local lists' => sub {
plan tests => 1;
my @threads;
my @results :shared;
for my $i (1..4) {
push @threads, threads->create(sub {
my $list = doubly->new();
for my $j (1..100) {
$list->add($j);
}
return $list->length;
});
}
for my $t (@threads) {
push @results, $t->join;
}
is_deeply(\@results, [100, 100, 100, 100], 'each thread got 100 items');
};
# Test 3: Parent list not usable in thread (CLONE_SKIP)
subtest 'Parent list not usable in thread' => sub {
plan tests => 3;
my $list = doubly->new();
for my $i (1..100) {
$list->add($i);
}
is($list->length, 100, 'parent has 100 items');
my $t = threads->create(sub {
# $list is unblessed in thread due to CLONE_SKIP
return ref($list) eq 'doubly' ? 'blessed' : 'unblessed';
});
my $result = $t->join;
is($result, 'unblessed', 'list is unblessed in child thread (CLONE_SKIP)');
is($list->length, 100, 'parent list still works after thread');
};
# Test 4: Rapid create and destroy in threads
subtest 'Rapid create and destroy in threads' => sub {
plan tests => 1;
my @threads;
for my $i (1..4) {
push @threads, threads->create(sub {
for my $j (1..50) {
my $list = doubly->new();
$list->add(1);
$list->add(2);
$list->destroy;
}
return 1;
});
}
my $ok = 1;
for my $t (@threads) {
$ok = 0 unless $t->join;
}
ok($ok, 'rapid create/destroy completed');
};
# Test 5: Complex operations in threads
subtest 'Complex operations in threads' => sub {
plan tests => 1;
my @threads;
for my $i (1..4) {
push @threads, threads->create(sub {
my $list = doubly->new();
for my $j (1..100) {
$list->add($j);
if ($j % 3 == 0) {
$list->remove_from_start;
}
}
return 1;
});
}
my $ok = 1;
for my $t (@threads) {
$ok = 0 unless $t->join;
}
ok($ok, 'complex operations completed without errors');
};
# Test 6: Navigation operations in threads
subtest 'Navigation in threads' => sub {
plan tests => 1;
my @threads;
for my $i (1..4) {
push @threads, threads->create(sub {
my $list = doubly->new();
for my $j (1..50) {
$list->add($j);
}
my $moves = 0;
for my $k (1..20) {
$list->start;
$list->next;
$list->next;
$list->end;
$list->prev;
$moves++;
}
return $moves;
});
}
my $ok = 1;
for my $t (@threads) {
my $res = $t->join;
$ok = 0 unless $res == 20;
}
ok($ok, 'navigation in threads completed');
};
done_testing();
( run in 0.542 second using v1.01-cache-2.11-cpan-f889d44b568 )