From cfae078171fc1453be0795e4ba5f0252627ebba3 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Wed, 10 Jun 2020 07:04:40 +0000 Subject: imap: require ".$UID_MIN-$UID_END" suffix Finish up the IMAP-only portion of iterative config reloading, which allows us to create all sub-ranges of an inbox up front. The InboxIdler still uses ->each_inbox which will struggle with 100K inboxes. Having messages in the top-level newsgroup name of an inbox will still waste bandwidth for clients which want to do full syncs once there's a rollover to a new 50K range. So instead, make every inbox accessible exclusively via 50K slices in the form of "$NEWSGROUP.$UID_MIN-$UID_END". This introduces the DummyInbox, which makes $NEWSGROUP and every parent component a selectable, empty inbox. This aids navigation with mutt and possibly other MUAs. Finally, the xt/perf-imap-list maintainer test is broken, now, so remove it. The grep perlfunc is already proven effective, and we'll have separate tests for mocking out ~100k inboxes. --- t/imap.t | 33 ++++++++++++++++++++++++++++----- t/imapd.t | 22 ++++++++++++++-------- 2 files changed, 42 insertions(+), 13 deletions(-) (limited to 't') diff --git a/t/imap.t b/t/imap.t index aa262a19..af59ef69 100644 --- a/t/imap.t +++ b/t/imap.t @@ -6,16 +6,39 @@ use strict; use Test::More; use PublicInbox::IMAP; use PublicInbox::IMAPD; +use PublicInbox::TestCommon; +require_mods(qw(DBD::SQLite)); +require_git 2.6; -{ # make sure we get '%' globbing right +my ($tmpdir, $for_destroy) = tmpdir(); +my $cfgfile = "$tmpdir/config"; +{ + open my $fh, '>', $cfgfile or BAIL_OUT $!; + print $fh <new; my @w; local $SIG{__WARN__} = sub { push @w, @_ }; - my @n = map { { newsgroup => $_ } } (qw(x.y.z x.z.y IGNORE.THIS)); - my $self = { imapd => { grouplist => \@n } }; - PublicInbox::IMAPD::refresh_inboxlist($self->{imapd}); + $imapd->refresh_groups; + my $self = { imapd => $imapd }; is(scalar(@w), 1, 'got a warning for upper-case'); like($w[0], qr/IGNORE\.THIS/, 'warned about upper-case'); - my $res = PublicInbox::IMAP::cmd_list($self, 'tag', 'x', '%'); is(scalar($$res =~ tr/\n/\n/), 2, 'only one result'); like($$res, qr/ x\r\ntag OK/, 'saw expected'); diff --git a/t/imapd.t b/t/imapd.t index a5324f78..017bfa0b 100644 --- a/t/imapd.t +++ b/t/imapd.t @@ -16,6 +16,9 @@ if ($can_compress) { # hope this gets fixed upstream, soon $imap_client = 'PublicInbox::IMAPClient'; } +require_ok 'PublicInbox::IMAP'; +my $first_range = '1-'.PublicInbox::IMAP::UID_BLOCK(); + my $level = '-Lbasic'; SKIP: { require_mods('Search::Xapian', 1); @@ -87,11 +90,13 @@ ok(!$mic->examine('foo') && ($e = $@), 'EXAMINE non-existent'); like($e, qr/\bNO\b/, 'got a NO on EXAMINE for non-existent'); ok(!$mic->select('foo') && ($e = $@), 'EXAMINE non-existent'); like($e, qr/\bNO\b/, 'got a NO on EXAMINE for non-existent'); -ok($mic->select('inbox.i1'), 'SELECT succeeds'); -ok($mic->examine('INBOX.i1'), 'EXAMINE succeeds'); -my @raw = $mic->status('inbox.i1', qw(Messages uidnext uidvalidity)); +my $mailbox1 = "inbox.i1.$first_range"; +ok($mic->select('inbox.i1'), 'SELECT on parent succeeds'); +ok($mic->select($mailbox1), 'SELECT succeeds'); +ok($mic->examine($mailbox1), 'EXAMINE succeeds'); +my @raw = $mic->status($mailbox1, qw(Messages uidnext uidvalidity)); is(scalar(@raw), 2, 'got status response'); -like($raw[0], qr/\A\*\x20STATUS\x20inbox\.i1\x20 +like($raw[0], qr/\A\*\x20STATUS\x20inbox\.i1\.$first_range\x20 \(MESSAGES\x20\d+\x20UIDNEXT\x20\d+\x20UIDVALIDITY\x20\d+\)\r\n/sx); like($raw[1], qr/\A\S+ OK /, 'finished status response'); @@ -99,7 +104,7 @@ like($raw[1], qr/\A\S+ OK /, 'finished status response'); like($raw[0], qr/^\* LIST \(.*?\) "\." inbox/, 'got an inbox'); like($raw[-1], qr/^\S+ OK /, 'response ended with OK'); -is(scalar(@raw), scalar(@V) + 2, 'default LIST response'); +is(scalar(@raw), scalar(@V) + 4, 'default LIST response'); @raw = $mic->list('', 'inbox.i1'); is(scalar(@raw), 2, 'limited LIST response'); like($raw[0], qr/^\* LIST \(.*?\) "\." inbox/, @@ -199,7 +204,7 @@ SKIP: { skip 'Mail::IMAPClient too old for ->compress', 2 if !$can_compress; my $c = $imap_client->new(%mic_opt); ok($c && $c->compress, 'compress enabled'); - ok($c->examine('inbox.i1'), 'EXAMINE succeeds after COMPRESS'); + ok($c->examine($mailbox1), 'EXAMINE succeeds after COMPRESS'); $ret = $c->search('uid 1:*') or BAIL_OUT "SEARCH FAIL $@"; is_deeply($ret, [ 1 ], 'search UID 1:* works after compression'); } @@ -216,11 +221,12 @@ $pi_config->each_inbox(sub { my $ng = $ibx->{newsgroup}; my $mic = $imap_client->new(%mic_opt); ok($mic && $mic->login && $mic->IsAuthenticated, "authed $name"); - my $uidnext = $mic->uidnext($ng); # we'll fetch BODYSTRUCTURE on this + my $mb = "$ng.$first_range"; + my $uidnext = $mic->uidnext($mb); # we'll fetch BODYSTRUCTURE on this ok($uidnext, 'got uidnext for later fetch'); is_deeply([$mic->has_capability('IDLE')], ['IDLE'], "IDLE capa $name"); ok(!$mic->idle, "IDLE fails w/o SELECT/EXAMINE $name"); - ok($mic->examine($ng), "EXAMINE $ng succeeds"); + ok($mic->examine($mb), "EXAMINE $ng succeeds"); ok(my $idle_tag = $mic->idle, "IDLE succeeds on $ng"); open(my $fh, '<', 't/data/message_embed.eml') or BAIL_OUT("open: $!"); -- cgit v1.2.3-24-ge0c7