]> Sergey Matveev's repositories - public-inbox.git/blob - t/msg_iter.t
fetch: fix and test v2 epoch detection
[public-inbox.git] / t / msg_iter.t
1 # Copyright (C) 2016-2021 all contributors <meta@public-inbox.org>
2 # License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt>
3 use strict; use v5.10.1; use PublicInbox::TestCommon;
4 use PublicInbox::Hval qw(ascii_html);
5 use MIME::QuotedPrint 3.05 qw(encode_qp);
6 use_ok('PublicInbox::MsgIter');
7
8 {
9         my $mime = eml_load 't/msg_iter-order.eml';
10         my @parts;
11         msg_iter($mime, sub {
12                 my ($part, $level, @ex) = @{$_[0]};
13                 my $s = $part->body_str;
14                 $s =~ s/\s+//s;
15                 push @parts, [ $s, $level, @ex ];
16         });
17         is_deeply(\@parts, [ [ qw(a 1 1) ], [ qw(b 1 2) ] ], 'order is fine');
18 }
19
20 {
21         my $mime = eml_load 't/msg_iter-nested.eml';
22         my @parts;
23         msg_iter($mime, sub {
24                 my ($part, $level, @ex) = @{$_[0]};
25                 my $s = $part->body_str;
26                 $s =~ s/\s+//s;
27                 push @parts, [ $s, $level, @ex ];
28         });
29         is_deeply(\@parts, [ [qw(a 2 1.1)], [qw(b 2 1.2)], [qw(sig 1 2)] ],
30                 'nested part shows up properly');
31 }
32
33 {
34         my $mime = eml_load 't/iso-2202-jp.eml';
35         my $raw = '';
36         msg_iter($mime, sub {
37                 my ($part, $level, @ex) = @{$_[0]};
38                 my ($s, $err) = msg_part_text($part, 'text/plain');
39                 ok(!$err, 'no error');
40                 $raw .= $s;
41         });
42         ok(length($raw) > 0, 'got non-empty message');
43         is(index($raw, '$$$'), -1, 'no unescaped $$$');
44 }
45
46 {
47         my $mime = eml_load 't/x-unknown-alpine.eml';
48         my $raw = '';
49         msg_iter($mime, sub {
50                 my ($part, $level, @ex) = @{$_[0]};
51                 my ($s, $err) = msg_part_text($part, 'text/plain');
52                 $raw .= $s;
53         });
54         like($raw, qr!^\thttps://!ms, 'tab expanded with X-UNKNOWN');
55         like(ascii_html($raw), qr/&#8226; bullet point/s,
56                 'got bullet point when X-UNKNOWN assumes UTF-8');
57 }
58
59 { # API not finalized
60         my @warn;
61         local $SIG{__WARN__} = sub { push @warn, [ @_ ] };
62         my $attr = "So and so wrote:\n";
63         my $q = "> hello world\n" x 10;
64         my $nq = "hello world\n" x 10;
65         my @sections = PublicInbox::MsgIter::split_quotes($attr . $q . $nq);
66         is($sections[0], $attr, 'attribution matches');
67         is($sections[1], $q, 'quoted section matches');
68         is($sections[2], $nq, 'non-quoted section matches');
69         is(scalar(@sections), 3, 'only three sections for short message');
70         is_deeply(\@warn, [], 'no warnings');
71
72         $q x= 3300;
73         $nq x= 3300;
74         @sections = PublicInbox::MsgIter::split_quotes($attr . $q . $nq);
75         is_deeply(\@warn, [], 'no warnings on giant message');
76         is(join('', @sections), $attr . $q . $nq, 'result matches expected');
77         is(shift(@sections), $attr, 'attribution is first section');
78         my @check = ('', '');
79         while (defined(my $l = shift @sections)) {
80                 next if $l eq '';
81                 like($l, qr/\n\z/s, 'section ends with newline');
82                 my $idx = ($l =~ /\A>/) ? 0 : 1;
83                 $check[$idx] .= $l;
84         }
85         is($check[0], $q, 'long quoted section matches');
86         is($check[1], $nq, 'long quoted section matches');
87 }
88
89 {
90         open my $fh, '<', 't/utf8.eml' or BAIL_OUT $!;
91         my $expect = do { local $/; <$fh>  };
92         my $qp_patch = encode_qp($expect, "\r\n");
93         my $common = <<EOM;
94 Content-Type: multipart/mixed; boundary="DEADBEEF"
95 MIME-Version: 1.0
96
97 --DEADBEEF
98 Content-Transfer-Encoding: quoted-printable
99 Content-Type: text/plain;
100         charset=utf-8
101
102 blah
103
104 --DEADBEEF
105 Content-Disposition: attachment;
106         filename=foo.patch
107 Content-Type: application/octet-stream;
108         x-unix-mode=0644;
109         name="foo.patch"
110 Content-Transfer-Encoding: quoted-printable
111 EOM
112         my $eml = PublicInbox::Eml->new(<<EOM);
113 $common
114 $qp_patch
115 --DEADBEEF--
116 EOM
117         my @parts;
118         $eml->each_part(sub {
119                 my ($part, $level, @ex) = @{$_[0]};
120                 my ($s, $err) = msg_part_text($part, $part->content_type);
121                 push @parts, $s;
122         });
123         $expect =~ s/\n/\r\n/sg;
124         utf8::decode($expect); # aka "bytes2str"
125         is_deeply(\@parts, [ "blah\r\n", $expect ],
126                 'fallback to application/octet-stream as UTF-8 text');
127
128         my $qp_binary = encode_qp("Binary\0crap", "\r\n");
129         $eml = PublicInbox::Eml->new(<<EOM);
130 $common
131 $qp_binary
132 --DEADBEEF--
133 EOM
134         @parts = ();
135         my @err;
136         $eml->each_part(sub {
137                 my ($part, $level, @ex) = @{$_[0]};
138                 my ($s, $err) = msg_part_text($part, $part->content_type);
139                 push @parts, $s;
140                 push @err, $err;
141         });
142         is_deeply(\@parts, [ "blah\r\n", undef ],
143                 'non-text ignored in octet-stream');
144         ok($err[1], 'got error for second element');
145 }
146
147 done_testing();