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>
5 use PublicInbox::TestCommon;
11 require_mods('PublicInbox::Gcf2');
12 use_ok 'PublicInbox::Gcf2';
13 use PublicInbox::Syscall qw($F_SETPIPE_SZ);
14 use PublicInbox::Import;
15 my ($tmpdir, $for_destroy) = tmpdir();
17 my $gcf2 = PublicInbox::Gcf2::new();
18 is(ref($gcf2), 'PublicInbox::Gcf2', '::new works');
19 my $COPYING = 'dba13ed2ddf783ee8118c6a581dbf75305f816a3';
20 open my $agpl, '<', 'COPYING' or BAIL_OUT "AGPL-3 missing: $!";
21 $agpl = do { local $/; <$agpl> };
23 PublicInbox::Import::init_bare($tmpdir);
24 my $fi_data = './t/git.fast-import-data';
26 open $rdr->{0}, '<', $fi_data or BAIL_OUT $!;
27 xsys([qw(git fast-import --quiet)], { GIT_DIR => $tmpdir }, $rdr);
28 is($?, 0, 'fast-import succeeded');
29 $gcf2->add_alternate("$tmpdir/objects");
33 pipe($r, $w) or BAIL_OUT $!;
34 my $tree = 'fdbc43725f21f485051c17463b50185f4c3cf88c';
35 $gcf2->cat_oid(fileno($w), $tree);
37 is("$tree tree 30\n", <$r>, 'tree header ok');
38 $r = do { local $/; <$r> };
39 is(chop($r), "\n", 'got trailing newline');
40 is(length($r), 30, 'tree length matches');
43 chomp(my $objdir = xqx([qw(git rev-parse --git-path objects)]));
44 if ($objdir =~ /\A--git-path\n/) { # git <2.5
45 chomp($objdir = xqx([qw(git rev-parse --git-dir)]));
46 $objdir .= '/objects';
48 if ($objdir && -d $objdir) {
49 $objdir = abs_path($objdir);
50 open my $alt, '>>', "$tmpdir/objects/info/alternates" or
52 print $alt $objdir, "\n" or BAIL_OUT $!;
53 close $alt or BAIL_OUT $!;
55 # calling gcf2->add_alternate on an already-added path won't
56 # cause alternates to be reloaded, so we do
57 # $gcf2->add_alternate($objdir) later on instead of
58 # $gcf2->add_alternate("$tmpdir/objects");
59 # $objdir = "$tmpdir/objects";
64 my $nr = $ENV{TEST_LEAK_NR};
65 my $cat = $ENV{TEST_LEAK_CAT} // 10;
66 diag "checking for leaks... (TEST_LEAK_NR=$nr TEST_LEAK_CAT=$cat)" if $nr;
69 skip 'not in git worktree', 21 unless defined($objdir);
70 $gcf2->add_alternate($objdir);
71 eval { $gcf2->add_alternate($objdir) };
72 ok(!$@, 'no error adding alternate redundantly');
74 diag "adding alternate $nr times redundantly";
75 $gcf2->add_alternate($objdir) for (1..$nr);
76 diag 'done adding redundant alternates';
79 open my $fh, '+>', undef or BAIL_OUT "open: $!";
82 ok(!$gcf2->cat_oid(fileno($fh), 'invalid'), 'invalid fails');
83 seek($fh, 0, SEEK_SET) or BAIL_OUT "seek: $!";
84 is(do { local $/; <$fh> }, '', 'nothing written');
86 open $fh, '+>', undef or BAIL_OUT "open: $!";
87 ok(!$gcf2->cat_oid(fileno($fh), '0'x40), 'z40 fails');
88 seek($fh, 0, SEEK_SET) or BAIL_OUT "seek: $!";
89 is(do { local $/; <$fh> }, '', 'nothing written for z40');
91 open $fh, '+>', undef or BAIL_OUT "open: $!";
92 my $ck_copying = sub {
94 seek($fh, 0, SEEK_SET) or BAIL_OUT "seek: $!";
95 is(<$fh>, "$COPYING blob 34520\n", "got expected header $desc");
96 my $buf = do { local $/; <$fh> };
97 is(chop($buf), "\n", 'got trailing \\n');
98 is($buf, $agpl, "AGPL matches ($desc)");
100 ok($gcf2->cat_oid(fileno($fh), $COPYING), 'cat_oid normal');
101 $ck_copying->('regular file');
103 $gcf2 = PublicInbox::Gcf2::new();
104 $gcf2->add_alternate("$tmpdir/objects");
105 open $fh, '+>', undef or BAIL_OUT "open: $!";
106 ok($gcf2->cat_oid(fileno($fh), $COPYING), 'cat_oid alternate');
107 $ck_copying->('alternates after reopen');
109 $^O eq 'linux' or skip('pipe tests are Linux-only', 14);
112 pipe($r, $w) or BAIL_OUT $!;
113 fcntl($w, $F_SETPIPE_SZ, 4096) or
114 skip('Linux too old for F_SETPIPE_SZ', 14);
116 seek($fh, 0, SEEK_SET) or BAIL_OUT "seek: $!";
117 truncate($fh, 0) or BAIL_OUT "truncate: $!";
118 my $pid = fork // BAIL_OUT "fork: $!";
121 tick; # wait for parent to block on writev
122 my $buf = do { local $/; <$r> };
123 print $fh $buf or _exit(1);
126 ok($gcf2->cat_oid(fileno($w), $COPYING), "cat blocking=$blk");
127 close $w or BAIL_OUT "close: $!";
128 is(waitpid($pid, 0), $pid, 'child exited');
129 is($?, 0, 'no error in child');
130 $ck_copying->("pipe blocking($blk)");
132 pipe($r, $w) or BAIL_OUT $!;
133 fcntl($w, $F_SETPIPE_SZ, 4096) or BAIL_OUT $!;
136 local $SIG{PIPE} = 'IGNORE';
137 eval { $gcf2->cat_oid(fileno($w), $COPYING) };
138 like($@, qr/writev error:/, 'got writev error');
143 open my $null, '>', '/dev/null' or BAIL_OUT "open /dev/null: $!";
144 my $fd = fileno($null);
145 local $SIG{PIPE} = 'IGNORE';
149 my $broken = fileno($w);
151 my $obj = PublicInbox::Gcf2::new();
152 if (defined($objdir)) {
153 $obj->add_alternate($objdir);
155 $obj->cat_oid($fd, $COPYING);
156 eval { $obj->cat_oid($broken, $COPYING) };
157 $obj->cat_oid($fd, '0'x40);
158 $obj->cat_oid($fd, 'invalid');