viewvcs: handle exceptions in on_destroy cb
[public-inbox.git] / t / gcf2.t
blob9f9e8e2003c0fe16acc764393c618a6a09858307
1 #!perl -w
2 # Copyright (C) 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 autodie qw(seek);
7 use PublicInbox::TestCommon;
8 use Test::More;
9 use Fcntl qw(:seek);
10 use IO::Handle ();
11 use POSIX qw(_exit);
12 use Cwd qw(abs_path);
13 require_mods('PublicInbox::Gcf2');
14 use_ok 'PublicInbox::Gcf2';
15 use PublicInbox::Syscall qw($F_SETPIPE_SZ);
16 use PublicInbox::Import;
17 my ($tmpdir, $for_destroy) = tmpdir();
19 my $gcf2 = PublicInbox::Gcf2::new();
20 is(ref($gcf2), 'PublicInbox::Gcf2', '::new works');
21 my $COPYING = 'dba13ed2ddf783ee8118c6a581dbf75305f816a3';
22 open my $agpl, '<', 'COPYING' or BAIL_OUT "AGPL-3 missing: $!";
23 $agpl = do { local $/; <$agpl> };
25 PublicInbox::Import::init_bare($tmpdir);
26 my $fi_data = './t/git.fast-import-data';
27 my $rdr = {};
28 open $rdr->{0}, '<', $fi_data or BAIL_OUT $!;
29 xsys([qw(git fast-import --quiet)], { GIT_DIR => $tmpdir }, $rdr);
30 is($?, 0, 'fast-import succeeded');
31 $gcf2->add_alternate("$tmpdir/objects");
34         my ($r, $w);
35         pipe($r, $w) or BAIL_OUT $!;
36         my $tree = 'fdbc43725f21f485051c17463b50185f4c3cf88c';
37         $gcf2->cat_oid(fileno($w), $tree);
38         close $w;
39         is("$tree tree 30\n", <$r>, 'tree header ok');
40         $r = do { local $/; <$r> };
41         is(chop($r), "\n", 'got trailing newline');
42         is(length($r), 30, 'tree length matches');
45 chomp(my $objdir = xqx([qw(git rev-parse --git-path objects)]));
46 if ($objdir =~ /\A--git-path\n/) { # git <2.5
47         chomp($objdir = xqx([qw(git rev-parse --git-dir)]));
48         $objdir .= '/objects';
50 if ($objdir && -d $objdir) {
51         $objdir = abs_path($objdir);
52         open my $alt, '>>', "$tmpdir/objects/info/alternates" or
53                                                         BAIL_OUT $!;
54         print $alt $objdir, "\n" or BAIL_OUT $!;
55         close $alt or BAIL_OUT $!;
57         # calling gcf2->add_alternate on an already-added path won't
58         # cause alternates to be reloaded, so we do
59         # $gcf2->add_alternate($objdir) later on instead of
60         # $gcf2->add_alternate("$tmpdir/objects");
61         # $objdir = "$tmpdir/objects";
62 } else {
63         $objdir = undef
66 my $nr = $ENV{TEST_LEAK_NR};
67 my $cat = $ENV{TEST_LEAK_CAT} // 10;
68 diag "checking for leaks... (TEST_LEAK_NR=$nr TEST_LEAK_CAT=$cat)" if $nr;
70 SKIP: {
71         skip 'not in git worktree', 21 unless defined($objdir);
72         $gcf2->add_alternate($objdir);
73         eval { $gcf2->add_alternate($objdir) };
74         ok(!$@, 'no error adding alternate redundantly');
75         if ($nr) {
76                 diag "adding alternate $nr times redundantly";
77                 $gcf2->add_alternate($objdir) for (1..$nr);
78                 diag 'done adding redundant alternates';
79         }
81         open my $fh, '+>', undef or BAIL_OUT "open: $!";
82         $fh->autoflush(1);
84         ok(!$gcf2->cat_oid(fileno($fh), 'invalid'), 'invalid fails');
85         seek $fh, 0, SEEK_SET;
86         is(do { local $/; <$fh> }, '', 'nothing written');
88         open $fh, '+>', undef or BAIL_OUT "open: $!";
89         ok(!$gcf2->cat_oid(fileno($fh), '0'x40), 'z40 fails');
90         seek $fh, 0, SEEK_SET;
91         is(do { local $/; <$fh> }, '', 'nothing written for z40');
93         open $fh, '+>', undef or BAIL_OUT "open: $!";
94         my $ck_copying = sub {
95                 my ($desc) = @_;
96                 seek $fh, 0, SEEK_SET;
97                 is(<$fh>, "$COPYING blob 34520\n", "got expected header $desc");
98                 my $buf = do { local $/; <$fh> };
99                 is(chop($buf), "\n", 'got trailing \\n');
100                 is($buf, $agpl, "AGPL matches ($desc)");
101         };
102         ok($gcf2->cat_oid(fileno($fh), $COPYING), 'cat_oid normal');
103         $ck_copying->('regular file');
105         $gcf2 = PublicInbox::Gcf2::new();
106         $gcf2->add_alternate("$tmpdir/objects");
107         open $fh, '+>', undef or BAIL_OUT "open: $!";
108         ok($gcf2->cat_oid(fileno($fh), $COPYING), 'cat_oid alternate');
109         $ck_copying->('alternates after reopen');
111         $^O eq 'linux' or skip('pipe tests are Linux-only', 14);
112         for my $blk (1, 0) {
113                 my ($r, $w);
114                 pipe($r, $w) or BAIL_OUT $!;
115                 fcntl($w, $F_SETPIPE_SZ, 4096) or
116                         skip('Linux too old for F_SETPIPE_SZ', 14);
117                 $w->blocking($blk);
118                 seek $fh, 0, SEEK_SET;
119                 truncate($fh, 0) or BAIL_OUT "truncate: $!";
120                 my $pid = fork // BAIL_OUT "fork: $!";
121                 if ($pid == 0) {
122                         close $w;
123                         tick; # wait for parent to block on writev
124                         my $buf = do { local $/; <$r> };
125                         print $fh $buf or _exit(1);
126                         _exit(0);
127                 }
128                 ok($gcf2->cat_oid(fileno($w), $COPYING), "cat blocking=$blk");
129                 close $w or BAIL_OUT "close: $!";
130                 is(waitpid($pid, 0), $pid, 'child exited');
131                 is($?, 0, 'no error in child');
132                 $ck_copying->("pipe blocking($blk)");
134                 pipe($r, $w) or BAIL_OUT $!;
135                 fcntl($w, $F_SETPIPE_SZ, 4096) or BAIL_OUT $!;
136                 $w->blocking($blk);
137                 close $r;
138                 local $SIG{PIPE} = 'IGNORE';
139                 eval { $gcf2->cat_oid(fileno($w), $COPYING) };
140                 like($@, qr/writev error:/, 'got writev error');
141         }
144 if ($nr) {
145         open my $null, '>', '/dev/null' or BAIL_OUT "open /dev/null: $!";
146         my $fd = fileno($null);
147         local $SIG{PIPE} = 'IGNORE';
148         my ($r, $w);
149         pipe($r, $w);
150         close $r;
151         my $broken = fileno($w);
152         for (1..$nr) {
153                 my $obj = PublicInbox::Gcf2::new();
154                 if (defined($objdir)) {
155                         $obj->add_alternate($objdir);
156                         for (1..$cat) {
157                                 $obj->cat_oid($fd, $COPYING);
158                                 eval { $obj->cat_oid($broken, $COPYING) };
159                                 $obj->cat_oid($fd, '0'x40);
160                                 $obj->cat_oid($fd, 'invalid');
161                         }
162                 }
163         }
165 done_testing;