196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project/*
296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * Copyright (C) 2008 The Android Open Source Project
396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project *
417d3a29c9d8f7a27c463239f190bdcc4e0804527Jerry Xie * Licensed under the Apache License, Version 2.0 (the "License"); you may not
517d3a29c9d8f7a27c463239f190bdcc4e0804527Jerry Xie * use this file except in compliance with the License. You may obtain a copy of
617d3a29c9d8f7a27c463239f190bdcc4e0804527Jerry Xie * the License at
796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project *
817d3a29c9d8f7a27c463239f190bdcc4e0804527Jerry Xie * http://www.apache.org/licenses/LICENSE-2.0
996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project *
1096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * Unless required by applicable law or agreed to in writing, software
1117d3a29c9d8f7a27c463239f190bdcc4e0804527Jerry Xie * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
1217d3a29c9d8f7a27c463239f190bdcc4e0804527Jerry Xie * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
1317d3a29c9d8f7a27c463239f190bdcc4e0804527Jerry Xie * License for the specific language governing permissions and limitations under
1417d3a29c9d8f7a27c463239f190bdcc4e0804527Jerry Xie * the License.
1596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project */
1696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
1796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectpackage com.android.email.mail.transport;
1896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
1917d3a29c9d8f7a27c463239f190bdcc4e0804527Jerry Xieimport android.content.Context;
2017d3a29c9d8f7a27c463239f190bdcc4e0804527Jerry Xie
2117d3a29c9d8f7a27c463239f190bdcc4e0804527Jerry Xieimport com.android.emailcommon.provider.HostAuth;
2217d3a29c9d8f7a27c463239f190bdcc4e0804527Jerry Xieimport com.android.mail.utils.LogUtils;
2317d3a29c9d8f7a27c463239f190bdcc4e0804527Jerry Xie
2417d3a29c9d8f7a27c463239f190bdcc4e0804527Jerry Xieimport junit.framework.Assert;
2596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
2696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport java.io.IOException;
2796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport java.io.InputStream;
2896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport java.io.OutputStream;
29be2ef97220619a3acf6c3dd4eb4b8add26ff452aAndy Stadlerimport java.net.InetAddress;
3096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport java.util.ArrayList;
3196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport java.util.Arrays;
32af6724527e564d35dd27ac35e24dbced554792e5Makoto Onukiimport java.util.regex.Pattern;
33af6724527e564d35dd27ac35e24dbced554792e5Makoto Onuki
3496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project/**
3596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * This is a mock Transport that is used to test protocols that use MailTransport.
3696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project */
3717d3a29c9d8f7a27c463239f190bdcc4e0804527Jerry Xiepublic class MockTransport extends MailTransport {
3896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
3996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    // All flags defining debug or development code settings must be FALSE
4096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    // when code is checked in or released.
4196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    private static boolean DEBUG_LOG_STREAMS = true;
42a599ee773d3bb6350ca775db717123faffbdbfe3Makoto Onuki
4396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    private static String LOG_TAG = "MockTransport";
4496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
45f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki    private static final String SPECIAL_RESPONSE_IOEXCEPTION = "!!!IOEXCEPTION!!!";
46f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki
471a791e675ba5092569125bf09a9fcc721bd4c5e1Andy Stadler    private boolean mTlsStarted = false;
48a599ee773d3bb6350ca775db717123faffbdbfe3Makoto Onuki
4996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    private boolean mOpen;
5096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    private boolean mInputOpen;
51be2ef97220619a3acf6c3dd4eb4b8add26ff452aAndy Stadler    private InetAddress mLocalAddress;
52be2ef97220619a3acf6c3dd4eb4b8add26ff452aAndy Stadler
5396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    private ArrayList<String> mQueuedInput = new ArrayList<String>();
54be2ef97220619a3acf6c3dd4eb4b8add26ff452aAndy Stadler
5596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    private static class Transaction {
5696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        public static final int ACTION_INJECT_TEXT = 0;
57f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki        public static final int ACTION_CLIENT_CLOSE = 1;
58f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki        public static final int ACTION_IO_EXCEPTION = 2;
591a791e675ba5092569125bf09a9fcc721bd4c5e1Andy Stadler        public static final int ACTION_START_TLS = 3;
60a599ee773d3bb6350ca775db717123faffbdbfe3Makoto Onuki
6196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        int mAction;
6296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        String mPattern;
6396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        String[] mResponses;
64a599ee773d3bb6350ca775db717123faffbdbfe3Makoto Onuki
6596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        Transaction(String pattern, String[] responses) {
6696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            mAction = ACTION_INJECT_TEXT;
6796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            mPattern = pattern;
6896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            mResponses = responses;
6996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
70a599ee773d3bb6350ca775db717123faffbdbfe3Makoto Onuki
7196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        Transaction(int otherType) {
7296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            mAction = otherType;
7396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            mPattern = null;
7496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            mResponses = null;
7596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
76a599ee773d3bb6350ca775db717123faffbdbfe3Makoto Onuki
7796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        @Override
7896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        public String toString() {
7996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            switch (mAction) {
8096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                case ACTION_INJECT_TEXT:
8196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    return mPattern + ": " + Arrays.toString(mResponses);
8296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                case ACTION_CLIENT_CLOSE:
8396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    return "Expect the client to close";
84f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki                case ACTION_IO_EXCEPTION:
85f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki                    return "Expect IOException";
861a791e675ba5092569125bf09a9fcc721bd4c5e1Andy Stadler                case ACTION_START_TLS:
871a791e675ba5092569125bf09a9fcc721bd4c5e1Andy Stadler                    return "Expect StartTls";
8896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                default:
8996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    return "(Hmm.  Unknown action.)";
9096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            }
9196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
9296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
93a599ee773d3bb6350ca775db717123faffbdbfe3Makoto Onuki
9496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    private ArrayList<Transaction> mPairs = new ArrayList<Transaction>();
95c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler
9617d3a29c9d8f7a27c463239f190bdcc4e0804527Jerry Xie    public static MockTransport createMockTransport(Context context) {
9717d3a29c9d8f7a27c463239f190bdcc4e0804527Jerry Xie        return new MockTransport(context, new HostAuth());
9817d3a29c9d8f7a27c463239f190bdcc4e0804527Jerry Xie    }
9917d3a29c9d8f7a27c463239f190bdcc4e0804527Jerry Xie
10017d3a29c9d8f7a27c463239f190bdcc4e0804527Jerry Xie    public MockTransport(Context context, HostAuth hostAuth) {
10117d3a29c9d8f7a27c463239f190bdcc4e0804527Jerry Xie        super(context, LOG_TAG, hostAuth);
10217d3a29c9d8f7a27c463239f190bdcc4e0804527Jerry Xie    }
10317d3a29c9d8f7a27c463239f190bdcc4e0804527Jerry Xie
104c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler    /**
105c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler     * Give the mock a pattern to wait for.  No response will be sent.
106c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler     * @param pattern Java RegEx to wait for
107c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler     */
108c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler    public void expect(String pattern) {
10917d3a29c9d8f7a27c463239f190bdcc4e0804527Jerry Xie        expect(pattern, (String[]) null);
110c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler    }
111c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler
11296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    /**
11396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * Give the mock a pattern to wait for and a response to send back.
11496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * @param pattern Java RegEx to wait for
11596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * @param response String to reply with, or null to acccept string but not respond to it
11696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     */
11796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    public void expect(String pattern, String response) {
11817d3a29c9d8f7a27c463239f190bdcc4e0804527Jerry Xie        expect(pattern, (response == null) ? null : new String[] {response});
11996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
120a599ee773d3bb6350ca775db717123faffbdbfe3Makoto Onuki
12196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    /**
12296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * Give the mock a pattern to wait for and a multi-line response to send back.
12396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * @param pattern Java RegEx to wait for
12496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * @param responses Strings to reply with
12596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     */
12696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    public void expect(String pattern, String[] responses) {
12796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        Transaction pair = new Transaction(pattern, responses);
12896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        mPairs.add(pair);
12996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
130af6724527e564d35dd27ac35e24dbced554792e5Makoto Onuki
131af6724527e564d35dd27ac35e24dbced554792e5Makoto Onuki    /**
132af6724527e564d35dd27ac35e24dbced554792e5Makoto Onuki     * Same as {@link #expect(String, String[])}, but the first arg is taken literally, rather than
133af6724527e564d35dd27ac35e24dbced554792e5Makoto Onuki     * as a regexp.
134af6724527e564d35dd27ac35e24dbced554792e5Makoto Onuki     */
135af6724527e564d35dd27ac35e24dbced554792e5Makoto Onuki    public void expectLiterally(String literal, String[] responses) {
136af6724527e564d35dd27ac35e24dbced554792e5Makoto Onuki        expect("^" + Pattern.quote(literal) + "$", responses);
137af6724527e564d35dd27ac35e24dbced554792e5Makoto Onuki    }
138af6724527e564d35dd27ac35e24dbced554792e5Makoto Onuki
139a599ee773d3bb6350ca775db717123faffbdbfe3Makoto Onuki    /**
14096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * Tell the Mock Transport that we expect it to be closed.  This will preserve
14196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * the remaining entries in the expect() stream and allow us to "ride over" the close (which
14296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * would normally reset everything).
14396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     */
14496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    public void expectClose() {
14596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        mPairs.add(new Transaction(Transaction.ACTION_CLIENT_CLOSE));
14696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
147a599ee773d3bb6350ca775db717123faffbdbfe3Makoto Onuki
148f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki    public void expectIOException() {
149f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki        mPairs.add(new Transaction(Transaction.ACTION_IO_EXCEPTION));
150f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki    }
151f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki
1521a791e675ba5092569125bf09a9fcc721bd4c5e1Andy Stadler    public void expectStartTls() {
1531a791e675ba5092569125bf09a9fcc721bd4c5e1Andy Stadler        mPairs.add(new Transaction(Transaction.ACTION_START_TLS));
1541a791e675ba5092569125bf09a9fcc721bd4c5e1Andy Stadler    }
1551a791e675ba5092569125bf09a9fcc721bd4c5e1Andy Stadler
156f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki    private void sendResponse(Transaction pair) {
157f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki        switch (pair.mAction) {
158f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki            case Transaction.ACTION_INJECT_TEXT:
159f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki                for (String s : pair.mResponses) {
160f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki                    mQueuedInput.add(s);
161f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki                }
162f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki                break;
163f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki            case Transaction.ACTION_IO_EXCEPTION:
164f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki                mQueuedInput.add(SPECIAL_RESPONSE_IOEXCEPTION);
165f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki                break;
166f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki            default:
167f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki                Assert.fail("Invalid action for sendResponse: " + pair.mAction);
16896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
16996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
17096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
17196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    /**
1721a791e675ba5092569125bf09a9fcc721bd4c5e1Andy Stadler     * Check that TLS was started
1731a791e675ba5092569125bf09a9fcc721bd4c5e1Andy Stadler     */
1741a791e675ba5092569125bf09a9fcc721bd4c5e1Andy Stadler    public boolean isTlsStarted() {
1751a791e675ba5092569125bf09a9fcc721bd4c5e1Andy Stadler        return mTlsStarted;
1761a791e675ba5092569125bf09a9fcc721bd4c5e1Andy Stadler    }
1771a791e675ba5092569125bf09a9fcc721bd4c5e1Andy Stadler
1781a791e675ba5092569125bf09a9fcc721bd4c5e1Andy Stadler    /**
179a599ee773d3bb6350ca775db717123faffbdbfe3Makoto Onuki     * This simulates a condition where the server has closed its side, causing
18096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * reads to fail.
18196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     */
18296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    public void closeInputStream() {
18396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        mInputOpen = false;
18496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
18596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
186ebece4dbdcdfee85a410a0d00c9b6739ee3e705eTodd Kennedy    @Override
18796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    public void close() {
18896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        mOpen = false;
18996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        mInputOpen = false;
19096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        // unless it was expected as part of a test, reset the stream
19196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        if (mPairs.size() > 0) {
19296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            Transaction expect = mPairs.remove(0);
19396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            if (expect.mAction == Transaction.ACTION_CLIENT_CLOSE) {
19496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                return;
19596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            }
19696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
19796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        mQueuedInput.clear();
19896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        mPairs.clear();
19996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
20096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
201be2ef97220619a3acf6c3dd4eb4b8add26ff452aAndy Stadler    public void setMockLocalAddress(InetAddress address) {
202be2ef97220619a3acf6c3dd4eb4b8add26ff452aAndy Stadler        mLocalAddress = address;
203be2ef97220619a3acf6c3dd4eb4b8add26ff452aAndy Stadler    }
204be2ef97220619a3acf6c3dd4eb4b8add26ff452aAndy Stadler
205e87ff6c3cbbfc5e3636f9827b58820652e3ea1c5Todd Kennedy    @Override
20696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    public InputStream getInputStream() {
20796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        SmtpSenderUnitTests.assertTrue(mOpen);
20896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        return new MockInputStream();
20996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
21096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
21196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    /**
21296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * This normally serves as a pseudo-clone, for use by Imap.  For the purposes of unit testing,
213a599ee773d3bb6350ca775db717123faffbdbfe3Makoto Onuki     * until we need something more complex, we'll just return the actual MockTransport.  Then we
21496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * don't have to worry about dealing with test metadata like the expects list or socket state.
21596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     */
216e87ff6c3cbbfc5e3636f9827b58820652e3ea1c5Todd Kennedy    @Override
21717d3a29c9d8f7a27c463239f190bdcc4e0804527Jerry Xie    public MockTransport clone() {
21817d3a29c9d8f7a27c463239f190bdcc4e0804527Jerry Xie        return this;
21996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
22096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
221e87ff6c3cbbfc5e3636f9827b58820652e3ea1c5Todd Kennedy    @Override
22296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    public OutputStream getOutputStream() {
223af6724527e564d35dd27ac35e24dbced554792e5Makoto Onuki        Assert.assertTrue(mOpen);
22496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        return new MockOutputStream();
22596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
22696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
22796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
228e87ff6c3cbbfc5e3636f9827b58820652e3ea1c5Todd Kennedy    @Override
22996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    public boolean isOpen() {
23096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        return mOpen;
23196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
23296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
233e87ff6c3cbbfc5e3636f9827b58820652e3ea1c5Todd Kennedy    @Override
23417d3a29c9d8f7a27c463239f190bdcc4e0804527Jerry Xie    public void open() /*
23517d3a29c9d8f7a27c463239f190bdcc4e0804527Jerry Xie                        * throws MessagingException,
23617d3a29c9d8f7a27c463239f190bdcc4e0804527Jerry Xie                        * CertificateValidationException
23717d3a29c9d8f7a27c463239f190bdcc4e0804527Jerry Xie                        */{
23896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        mOpen = true;
23996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        mInputOpen = true;
24096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
24196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
24296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    /**
24396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * This returns one string (if available) to the caller.  Usually this simply pulls strings
24496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * from the mQueuedInput list, but if the list is empty, we also peek the expect list.  This
24596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * supports banners, multi-line responses, and any other cases where we respond without
24696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * a specific expect pattern.
247a599ee773d3bb6350ca775db717123faffbdbfe3Makoto Onuki     *
24896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * If no response text is available, we assert (failing our test) as an underflow.
249a599ee773d3bb6350ca775db717123faffbdbfe3Makoto Onuki     *
25096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * Logs the read text if DEBUG_LOG_STREAMS is true.
25196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     */
25296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    public String readLine() throws IOException {
25396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        SmtpSenderUnitTests.assertTrue(mOpen);
25496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        if (!mInputOpen) {
25596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            throw new IOException("Reading from MockTransport with closed input");
25696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
25717d3a29c9d8f7a27c463239f190bdcc4e0804527Jerry Xie        // if there's nothing to read, see if we can find a null-pattern
25817d3a29c9d8f7a27c463239f190bdcc4e0804527Jerry Xie        // response
259f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki        if ((mQueuedInput.size() == 0) && (mPairs.size() > 0)) {
260f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki            Transaction pair = mPairs.get(0);
261f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki            if (pair.mPattern == null) {
26296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                mPairs.remove(0);
263f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki                sendResponse(pair);
26496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            }
26596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
266f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki        if (mQueuedInput.size() == 0) {
267f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki            // MailTransport returns "" at EOS.
268560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedy            LogUtils.w(LOG_TAG, "Underflow reading from MockTransport");
269f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki            return "";
270f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki        }
27196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        String line = mQueuedInput.remove(0);
27296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        if (DEBUG_LOG_STREAMS) {
273560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedy            LogUtils.d(LOG_TAG, "<<< " + line);
27496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
275f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki        if (SPECIAL_RESPONSE_IOEXCEPTION.equals(line)) {
276f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki            throw new IOException("Expected IOException.");
277f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki        }
27896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        return line;
27996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
28096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
281e87ff6c3cbbfc5e3636f9827b58820652e3ea1c5Todd Kennedy    @Override
28217d3a29c9d8f7a27c463239f190bdcc4e0804527Jerry Xie    public void reopenTls() /* throws MessagingException */{
28396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        SmtpSenderUnitTests.assertTrue(mOpen);
2841a791e675ba5092569125bf09a9fcc721bd4c5e1Andy Stadler        Transaction expect = mPairs.remove(0);
2851a791e675ba5092569125bf09a9fcc721bd4c5e1Andy Stadler        SmtpSenderUnitTests.assertTrue(expect.mAction == Transaction.ACTION_START_TLS);
2861a791e675ba5092569125bf09a9fcc721bd4c5e1Andy Stadler        mTlsStarted = true;
28796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
28896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
289e4a7cc440f081ef9c4375a2bd2f82680cc11b152Andrew Stadler    public void setSecurity(int connectionSecurity, boolean trustAllCertificates) {
29017d3a29c9d8f7a27c463239f190bdcc4e0804527Jerry Xie        mHostAuth.mFlags =
29117d3a29c9d8f7a27c463239f190bdcc4e0804527Jerry Xie                connectionSecurity & (trustAllCertificates ? HostAuth.FLAG_TRUST_ALL : 0xff);
29296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
29396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
29417d3a29c9d8f7a27c463239f190bdcc4e0804527Jerry Xie    public void setHost(String address) {
29517d3a29c9d8f7a27c463239f190bdcc4e0804527Jerry Xie        mHostAuth.mAddress = address;
29696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
297a599ee773d3bb6350ca775db717123faffbdbfe3Makoto Onuki
29817d3a29c9d8f7a27c463239f190bdcc4e0804527Jerry Xie    @Override
29917d3a29c9d8f7a27c463239f190bdcc4e0804527Jerry Xie    public void setSoTimeout(int timeoutMilliseconds) /* throws SocketException */{}
30017d3a29c9d8f7a27c463239f190bdcc4e0804527Jerry Xie
30196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    /**
30296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * Accepts a single string (command or text) that was written by the code under test.
30396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * Because we are essentially mocking a server, we check to see if this string was expected.
30496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * If the string was expected, we push the corresponding responses into the mQueuedInput
30596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * list, for subsequent calls to readLine().  If the string does not match, we assert
30696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * the mismatch.  If no string was expected, we assert it as an overflow.
307a599ee773d3bb6350ca775db717123faffbdbfe3Makoto Onuki     *
30896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * Logs the written text if DEBUG_LOG_STREAMS is true.
30996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     */
310e87ff6c3cbbfc5e3636f9827b58820652e3ea1c5Todd Kennedy    @Override
311f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki    public void writeLine(String s, String sensitiveReplacement) throws IOException {
31296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        if (DEBUG_LOG_STREAMS) {
313560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedy            LogUtils.d(LOG_TAG, ">>> " + s);
31496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
31596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        SmtpSenderUnitTests.assertTrue(mOpen);
31617d3a29c9d8f7a27c463239f190bdcc4e0804527Jerry Xie        SmtpSenderUnitTests.assertTrue(
31717d3a29c9d8f7a27c463239f190bdcc4e0804527Jerry Xie                "Overflow writing to MockTransport: Getting " + s, 0 != mPairs.size());
31896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        Transaction pair = mPairs.remove(0);
319f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki        if (pair.mAction == Transaction.ACTION_IO_EXCEPTION) {
320f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki            throw new IOException("Expected IOException.");
321f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki        }
322af6724527e564d35dd27ac35e24dbced554792e5Makoto Onuki        SmtpSenderUnitTests.assertTrue("Unexpected string written to MockTransport: Actual=" + s
32317d3a29c9d8f7a27c463239f190bdcc4e0804527Jerry Xie                + "  Expected=" + pair.mPattern, pair.mPattern != null && s.matches(pair.mPattern));
32496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        if (pair.mResponses != null) {
325f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki            sendResponse(pair);
32696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
32796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
328a599ee773d3bb6350ca775db717123faffbdbfe3Makoto Onuki
32996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    /**
33096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * This is an InputStream that satisfies the needs of getInputStream()
33196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     */
33296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    private class MockInputStream extends InputStream {
33396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
334e87ff6c3cbbfc5e3636f9827b58820652e3ea1c5Todd Kennedy        private byte[] mNextLine = null;
335e87ff6c3cbbfc5e3636f9827b58820652e3ea1c5Todd Kennedy        private int mNextIndex = 0;
336a599ee773d3bb6350ca775db717123faffbdbfe3Makoto Onuki
33796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        /**
33896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project         * Reads from the same input buffer as readLine()
33996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project         */
34096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        @Override
34196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        public int read() throws IOException {
34296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            if (!mInputOpen) {
34396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                throw new IOException();
34496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            }
345a599ee773d3bb6350ca775db717123faffbdbfe3Makoto Onuki
34696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            if (mNextLine != null && mNextIndex < mNextLine.length) {
34796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                return mNextLine[mNextIndex++];
34896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            }
349a599ee773d3bb6350ca775db717123faffbdbfe3Makoto Onuki
35096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            // previous line was exhausted so try to get another one
35196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            String next = readLine();
35296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            if (next == null) {
35396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                throw new IOException("Reading from MockTransport with closed input");
35496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            }
35596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            mNextLine = (next + "\r\n").getBytes();
35696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            mNextIndex = 0;
35796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
35896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            if (mNextLine != null && mNextIndex < mNextLine.length) {
35996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                return mNextLine[mNextIndex++];
36096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            }
361a599ee773d3bb6350ca775db717123faffbdbfe3Makoto Onuki
362a599ee773d3bb6350ca775db717123faffbdbfe3Makoto Onuki            // no joy - throw an exception
363a599ee773d3bb6350ca775db717123faffbdbfe3Makoto Onuki            throw new IOException();
36496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
36596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
366a599ee773d3bb6350ca775db717123faffbdbfe3Makoto Onuki
36796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    /**
36896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * This is an OutputStream that satisfies the needs of getOutputStream()
36996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     */
37096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    private class MockOutputStream extends OutputStream {
37196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
372e87ff6c3cbbfc5e3636f9827b58820652e3ea1c5Todd Kennedy        private StringBuilder sb = new StringBuilder();
37396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
37496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        @Override
375f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki        public void write(int oneByte) throws IOException {
376c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler            // CR or CRLF will immediately dump previous line (w/o CRLF)
377c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler            if (oneByte == '\r') {
378c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler                writeLine(sb.toString(), null);
379c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler                sb = new StringBuilder();
380c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler            } else if (oneByte == '\n') {
381c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler                // swallow it
382c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler            } else {
38317d3a29c9d8f7a27c463239f190bdcc4e0804527Jerry Xie                sb.append((char) oneByte);
38496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            }
38596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
38696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
387be2ef97220619a3acf6c3dd4eb4b8add26ff452aAndy Stadler
388e87ff6c3cbbfc5e3636f9827b58820652e3ea1c5Todd Kennedy    @Override
389be2ef97220619a3acf6c3dd4eb4b8add26ff452aAndy Stadler    public InetAddress getLocalAddress() {
390be2ef97220619a3acf6c3dd4eb4b8add26ff452aAndy Stadler        if (isOpen()) {
391be2ef97220619a3acf6c3dd4eb4b8add26ff452aAndy Stadler            return mLocalAddress;
392be2ef97220619a3acf6c3dd4eb4b8add26ff452aAndy Stadler        } else {
393be2ef97220619a3acf6c3dd4eb4b8add26ff452aAndy Stadler            return null;
394be2ef97220619a3acf6c3dd4eb4b8add26ff452aAndy Stadler        }
395be2ef97220619a3acf6c3dd4eb4b8add26ff452aAndy Stadler    }
396a50fc99b0c433f0cde31ba1c7ab87fb9ea86345dTodd Kennedy}
397