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