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>
7 use PublicInbox::TestCommon;
8 use List::Util qw(shuffle);
10 use Fcntl qw(SEEK_SET);
11 require_ok 'PublicInbox::MboxReader';
12 require_ok 'PublicInbox::LeiToMail';
14 hdr_only => "From: header-only\@example.com\n\n",
15 small_from => "From: small-from\@example.com\n\nFrom hell\n",
16 small => "From: small\@example.com\n\nfrom hell\n",
17 big_hdr_only => "From: big-header\@example.com\n" .
18 (('A: '.('a' x 72)."\n") x 1000)."\n",
19 big_body => "From: big-body\@example.com\n\n".
20 (('b: '.('b' x 72)."\n") x 1000) .
22 big_all => "From: big-all\@example.com\n".
23 (("A: ".('a' x 72)."\n") x 1000). "\n" .
24 (("b: ".('b' x 72)."\n") x 1000) .
28 my $eml = PublicInbox::Eml->new($raw{small});
29 my $mbox_keywords = PublicInbox::MboxReader->can('mbox_keywords');
30 is_deeply($mbox_keywords->($eml), [], 'no keywords');
31 $eml->header_set('Status', 'RO');
32 is_deeply($mbox_keywords->($eml), ['seen'], 'seen extracted');
33 $eml->header_set('X-Status', 'A');
34 is_deeply($mbox_keywords->($eml), [qw(answered seen)],
35 'seen+answered extracted');
38 if ($ENV{TEST_EXTRA}) {
39 for my $fn (glob('t/*.eml'), glob('t/*/*.{patch,eml}')) {
40 $raw{$fn} = eml_load($fn)->as_string;
44 my $reader = PublicInbox::MboxReader->new;
47 my @order = shuffle(keys %raw);
48 my $eml2mbox = PublicInbox::LeiToMail->can("eml2$fmt");
49 open my $fh, '+>', undef or BAIL_OUT "open: $!";
51 my $eml = PublicInbox::Eml->new($raw{$k});
52 my $buf = $eml2mbox->($eml);
53 print $fh $$buf or BAIL_OUT "print $!";
55 seek($fh, 0, SEEK_SET) or BAIL_OUT "seek: $!";
56 $reader->$fmt($fh, sub {
58 $eml->header_set('Status');
59 $eml->header_set('Lines');
60 my $cur = shift @order;
61 my @cl = $eml->header_raw('Content-Length');
62 if ($fmt =~ /\Amboxcl/) {
63 is(scalar(@cl), 1, "Content-Length set $fmt $cur");
64 my $raw = $eml->body_raw;
66 if ($fmt eq 'mboxcl') {
67 my @from = ($raw =~ /^(From )/smg);
70 is(length($raw), $cl[0] - $adj,
71 "Content-Length is correct $fmt $cur");
72 # clobber for ->as_string comparison below
73 $eml->header_set('Content-Length');
75 # special case for t/solve/bare.patch, not sure if we
76 # should even handle it...
77 if ($cl[0] eq '0' && ${$eml->{hdr}} eq '') {
81 is(scalar(@cl), 0, "Content-Length unset $fmt $cur");
83 my $orig = PublicInbox::Eml->new($raw{$cur});
84 is($eml->as_string, $orig->as_string,
85 "read back original $fmt $cur");
88 my @mbox = qw(mboxrd mboxo mboxcl mboxcl2);
89 for my $fmt (@mbox) { $check_fmt->($fmt) }
90 s/\n/\r\n/sg for (values %raw);
91 for my $fmt (@mbox) { $check_fmt->($fmt) }
94 my $no_blank_eom = <<'EOM';
95 From x@y Fri Oct 2 00:00:00 1993
99 From x@y Fri Oct 2 00:00:00 1993
104 # chop($no_blank_eom) eq "\n" or BAIL_OUT 'broken LF';
105 for my $variant (qw(mboxrd mboxo)) {
107 open my $fh, '<', \$no_blank_eom or BAIL_OUT 'PerlIO::scalar';
108 $reader->$variant($fh, sub { push @x, shift });
109 is_deeply($x[0]->{bdy}, \"body1\n", 'LF preserved in 1st');
110 is_deeply($x[1]->{bdy}, \"body2\n", 'no LF added in 2nd');
115 use PublicInbox::Spawn qw(popen_rd);
116 my $fh = popen_rd([ $^X, '-E', <<'' ]);
117 say "From x@y Fri Oct 2 00:00:00 1993";
118 print "a: b\n\n", "x" x 70000, "\n\n";
119 say "From x@y Fri Oct 2 00:00:00 2010";
120 print "Final: bit\n\n", "Incomplete\n\n";
124 eval { $reader->mboxrd($fh, sub { push @x, shift->as_string }) };
125 like($@, qr/error closing mbox/, 'detects error reading from pipe');
126 is(scalar(@x), 1, 'only saw one message');
127 is(scalar(grep(/Final/, @x)), 0, 'no incomplete bit');
132 <html><head><title>hi,</title></head><body>how are you</body></html>
134 for my $m (qw(mboxrd mboxcl mboxcl2 mboxo)) {
136 local $SIG{__WARN__} = sub { push @w, @_ };
137 open my $fh, '<', \$html or xbail 'PerlIO::scalar';
138 PublicInbox::MboxReader->$m($fh, sub {
139 push @x, $_[0]->as_string
141 is_deeply(\@x, [], "messages in invalid $m");
142 is_deeply([grep(!/^W: leftover/, @w)], [],
143 "no extra warnings besides leftover ($m)");