# -*-perl-*- # tests for SQlite-specific features use strict; use warnings; use Test::More; use FindBin qw($Bin); use MogileFS::Server; use MogileFS::Util qw(error_code); use MogileFS::Test; use File::Temp (); use POSIX qw(:sys_wait_h); my ($fh, $filename) = File::Temp::tempfile(); close($fh); MogileFS::Config->set_config('db_dsn', "DBI:SQLite:$filename"); MogileFS::Config->set_config('db_user', ''); MogileFS::Config->set_config('db_pass', ''); MogileFS::Config->set_config('max_handles', 0xffffffff); my ($r, $w, $pid, $buf); my $sto = eval { MogileFS::Store->new }; if ($sto) { plan tests => 28; } else { plan skip_all => "Can't create temporary test database: $@"; exit 0; } Mgd::set_store($sto); is(ref($sto), "MogileFS::Store::SQLite", "store is sane"); is($sto->setup_database, 1, "setup database"); is(1, pipe($r, $w), "IPC pipe is ready"); # normal lock contention $pid = fork; fail("fork failed: $!") unless defined $pid; if ($pid == 0) { $sto = Mgd::get_store(); # fork-safe $SIG{TERM} = sub { $sto->release_lock("test-lock") == 1 or die "released bad lock"; exit 0; }; $sto->get_lock("test-lock", 1) == 1 or die "child failed to get_lock"; close($r); syswrite($w, ".") == 1 or die "child failed to wake parent"; sleep 60; exit 0; } if ($pid > 0) { is(sysread($r, $buf, 1), 1, "child wakes us up"); is($buf, ".", "child wakes parent up properly"); ok(! $sto->get_lock("test-lock", 1), "fails to lock while child has lock"); is(kill(TERM => $pid), 1, "kill successful"); is(waitpid($pid, 0), $pid, "waitpid successful"); is($?, 0, "child dies correctly"); is($sto->get_lock("test-lock", 1), 1, "acquire lock when child dies"); } # detects recursive lock ok(! eval { $sto->get_lock("test-lock", 1); }, "recursion fails"); like($@, qr/Lock recursion detected/i, "proper error on failed lock"); is($sto->release_lock("test-lock"), 1, "lock release"); is($sto->get_lock("test-lock", 0), 1, "acquire lock with 0 timeout"); is($sto->release_lock("test-lock"), 1, "lock release"); is($sto->release_lock("test-lock") + 0, 0, "redundant lock release"); # waits for lock $pid = fork; fail("fork failed: $!") unless defined $pid; if ($pid == 0) { $sto = Mgd::get_store(); # fork-safe $sto->get_lock("test-lock", 1) or die "child failed to get_lock"; close($r); syswrite($w, ".") == 1 or die "child failed to wake parent"; sleep 2; $sto->release_lock("test-lock") == 1 or die "child failed to release"; exit 0; } if ($pid > 0) { is(sysread($r, $buf, 1), 1, "parent woken up"); is($buf, ".", "child wakes parent up properly"); ok($sto->get_lock("test-lock", 6), "acquire lock eventually"); is(waitpid($pid, 0), $pid, "waitpid successful"); is($?, 0, "child dies correctly"); is($sto->release_lock("test-lock"), 1, "lock release"); } # kill -9 a lock holder $pid = fork; fail("fork failed: $!") unless defined $pid; if ($pid == 0) { $sto = Mgd::get_store(); # fork-safe $sto->get_lock("test-lock", 1) or die "child failed to get_lock"; close($r); syswrite($w, ".") == 1 or die "child failed to wake parent"; sleep 60; exit 0; } if ($pid > 0) { is(sysread($r, $buf, 1), 1, "parent woken up"); is($buf, ".", "child wakes parent up properly"); is(kill(KILL => $pid), 1, "kill -9 successful"); is(waitpid($pid, 0), $pid, "waitpid successful"); ok(WIFSIGNALED($?) && WTERMSIG($?) == 9, "child was SIGKILL-ed"); ok($sto->get_lock("test-lock", 1), "acquire lock in parent"); }