]> Sergey Matveev's repositories - public-inbox.git/blob - t/ipc.t
ipc: add support for asynchronous callbacks
[public-inbox.git] / t / ipc.t
1 #!perl -w
2 # Copyright (C) 2020-2021 all contributors <meta@public-inbox.org>
3 # License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt>
4 use strict;
5 use v5.10.1;
6 use Test::More;
7 use PublicInbox::TestCommon;
8 require_ok 'PublicInbox::IPC';
9 state $once = eval <<'';
10 package PublicInbox::IPC;
11 use strict;
12 sub test_array { qw(test array) }
13 sub test_scalar { 'scalar' }
14 sub test_scalarref { \'scalarref' }
15 sub test_undef { undef }
16 sub test_die { shift; die @_; 'unreachable' }
17 sub test_pid { $$ }
18 1;
19
20 my $ipc = bless {}, 'PublicInbox::IPC';
21 my @t = qw(array scalar scalarref undef);
22 my $test = sub {
23         my $x = shift;
24         my @res;
25         for my $type (@t) {
26                 my $m = "test_$type";
27                 my @ret = $ipc->ipc_do($m);
28                 my @exp = $ipc->$m;
29                 is_deeply(\@ret, \@exp, "wantarray $m $x");
30
31                 $ipc->ipc_do($m);
32
33                 $ipc->ipc_async($m, [], sub { push @res, \@_ }, \$m);
34
35                 my $ret = $ipc->ipc_do($m);
36                 my $exp = $ipc->$m;
37                 is_deeply($ret, $exp, "!wantarray $m $x");
38
39                 is_deeply(\@res, [ [ \$m, \@exp ] ], "async $m $x");
40                 @res = ();
41         }
42         $ipc->ipc_async_wait(-1);
43         is_deeply(\@res, [], 'no leftover results');
44         $ipc->ipc_async('test_die', ['die test'],
45                         sub { push @res, \@_  }, 'die arg');
46         $ipc->ipc_async_wait(1);
47         is(scalar(@res), 1, 'only one result');
48         is(scalar(@{$res[0]}), 2, 'result has 2-element array');
49         is($res[0]->[0], 'die arg', 'got async die arg '.$x);
50         is(ref($res[0]->[1]), 'PublicInbox::IPC::Die',
51                 "exception type $x");
52         {
53                 my $nr = PublicInbox::IPC::PIPE_BUF();
54                 my $count = 0;
55                 my $cb = sub { ++$count };
56                 $ipc->ipc_async('test_undef', [], $cb) for (1..$nr);
57                 $ipc->ipc_async_wait(-1);
58                 is($count, $nr, "$x async runs w/o deadlock");
59         }
60
61         my $ret = eval { $ipc->test_die('phail') };
62         my $exp = $@;
63         $ret = eval { $ipc->ipc_do('test_die', 'phail') };
64         my $err = $@;
65         my %lines;
66         for ($err, $exp) {
67                 s/ line (\d+).*//s and $lines{$1}++;
68         }
69         is(scalar keys %lines, 1, 'line numbers match');
70         is((values %lines)[0], 2, '2 hits on same line number');
71         is($err, $exp, "$x die matches");
72         is($ret, undef, "$x die did not return");
73
74         eval { $ipc->test_die(['arrayref']) };
75         $exp = $@;
76         $ret = eval { $ipc->ipc_do('test_die', ['arrayref']) };
77         $err = $@;
78         is_deeply($err, $exp, 'die with unblessed ref');
79         is(ref($err), 'ARRAY', 'got an array ref');
80
81         $exp = bless ['blessed'], 'PublicInbox::WTF';
82         $ret = eval { $ipc->ipc_do('test_die', $exp) };
83         $err = $@;
84         is_deeply($err, $exp, 'die with blessed ref');
85         is(ref($err), 'PublicInbox::WTF', 'got blessed ref');
86 };
87 $test->('local');
88
89 SKIP: {
90         require_mods(qw(Storable||Sereal), 16);
91         my $pid = $ipc->ipc_worker_spawn('test worker');
92         ok($pid > 0 && kill(0, $pid), 'worker spawned and running');
93         defined($pid) or BAIL_OUT 'no spawn, no test';
94         is($ipc->ipc_do('test_pid'), $pid, 'worker pid returned');
95         $test->('worker');
96         {
97                 my ($tmp, $for_destroy) = tmpdir();
98                 $ipc->ipc_lock_init("$tmp/lock");
99                 is($ipc->ipc_do('test_pid'), $pid, 'worker pid returned');
100         }
101         $ipc->ipc_worker_stop;
102         ok(!kill(0, $pid) && $!{ESRCH}, 'worker stopped');
103 }
104 $ipc->ipc_worker_stop; # idempotent
105 done_testing;