MockTransport.java revision 1a791e675ba5092569125bf09a9fcc721bd4c5e1
196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project/*
296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * Copyright (C) 2008 The Android Open Source Project
396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project *
496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License");
596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * you may not use this file except in compliance with the License.
696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * You may obtain a copy of the License at
796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project *
896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project *      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
1196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS,
1296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * See the License for the specific language governing permissions and
1496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project * limitations under 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
1996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport com.android.email.mail.Transport;
2096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
2196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport android.util.Log;
2296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
2396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport java.io.IOException;
2496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport java.io.InputStream;
2596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport java.io.OutputStream;
26be2ef97220619a3acf6c3dd4eb4b8add26ff452aAndy Stadlerimport java.net.InetAddress;
2796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport java.net.URI;
2896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport java.util.ArrayList;
2996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectimport java.util.Arrays;
30af6724527e564d35dd27ac35e24dbced554792e5Makoto Onukiimport java.util.regex.Pattern;
31af6724527e564d35dd27ac35e24dbced554792e5Makoto Onuki
32af6724527e564d35dd27ac35e24dbced554792e5Makoto Onukiimport junit.framework.Assert;
3396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
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 */
3796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectpublic class MockTransport implements Transport {
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;
5196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    private int mConnectionSecurity;
52e4a7cc440f081ef9c4375a2bd2f82680cc11b152Andrew Stadler    private boolean mTrustCertificates;
53cb95fbe13554ee6b4e46d9fd3bcd983e09a688cbAndrew Stadler    private String mHost;
54be2ef97220619a3acf6c3dd4eb4b8add26ff452aAndy Stadler    private InetAddress mLocalAddress;
55be2ef97220619a3acf6c3dd4eb4b8add26ff452aAndy Stadler
5696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    private ArrayList<String> mQueuedInput = new ArrayList<String>();
57be2ef97220619a3acf6c3dd4eb4b8add26ff452aAndy Stadler
5896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    private static class Transaction {
5996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        public static final int ACTION_INJECT_TEXT = 0;
60f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki        public static final int ACTION_CLIENT_CLOSE = 1;
61f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki        public static final int ACTION_IO_EXCEPTION = 2;
621a791e675ba5092569125bf09a9fcc721bd4c5e1Andy Stadler        public static final int ACTION_START_TLS = 3;
63a599ee773d3bb6350ca775db717123faffbdbfe3Makoto Onuki
6496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        int mAction;
6596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        String mPattern;
6696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        String[] mResponses;
67a599ee773d3bb6350ca775db717123faffbdbfe3Makoto Onuki
6896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        Transaction(String pattern, String[] responses) {
6996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            mAction = ACTION_INJECT_TEXT;
7096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            mPattern = pattern;
7196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            mResponses = responses;
7296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
73a599ee773d3bb6350ca775db717123faffbdbfe3Makoto Onuki
7496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        Transaction(int otherType) {
7596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            mAction = otherType;
7696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            mPattern = null;
7796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            mResponses = null;
7896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
79a599ee773d3bb6350ca775db717123faffbdbfe3Makoto Onuki
8096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        @Override
8196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        public String toString() {
8296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            switch (mAction) {
8396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                case ACTION_INJECT_TEXT:
8496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    return mPattern + ": " + Arrays.toString(mResponses);
8596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                case ACTION_CLIENT_CLOSE:
8696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    return "Expect the client to close";
87f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki                case ACTION_IO_EXCEPTION:
88f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki                    return "Expect IOException";
891a791e675ba5092569125bf09a9fcc721bd4c5e1Andy Stadler                case ACTION_START_TLS:
901a791e675ba5092569125bf09a9fcc721bd4c5e1Andy Stadler                    return "Expect StartTls";
9196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                default:
9296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                    return "(Hmm.  Unknown action.)";
9396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            }
9496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
9596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
96a599ee773d3bb6350ca775db717123faffbdbfe3Makoto Onuki
9796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    private ArrayList<Transaction> mPairs = new ArrayList<Transaction>();
98c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler
99c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler    /**
100c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler     * Give the mock a pattern to wait for.  No response will be sent.
101c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler     * @param pattern Java RegEx to wait for
102c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler     */
103c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler    public void expect(String pattern) {
104c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler        expect(pattern, (String[])null);
105c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler    }
106c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler
10796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    /**
10896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * Give the mock a pattern to wait for and a response to send back.
10996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * @param pattern Java RegEx to wait for
11096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * @param response String to reply with, or null to acccept string but not respond to it
11196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     */
11296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    public void expect(String pattern, String response) {
11396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        expect(pattern, (response == null) ? null : new String[] { response });
11496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
115a599ee773d3bb6350ca775db717123faffbdbfe3Makoto Onuki
11696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    /**
11796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * Give the mock a pattern to wait for and a multi-line response to send back.
11896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * @param pattern Java RegEx to wait for
11996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * @param responses Strings to reply with
12096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     */
12196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    public void expect(String pattern, String[] responses) {
12296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        Transaction pair = new Transaction(pattern, responses);
12396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        mPairs.add(pair);
12496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
125af6724527e564d35dd27ac35e24dbced554792e5Makoto Onuki
126af6724527e564d35dd27ac35e24dbced554792e5Makoto Onuki    /**
127af6724527e564d35dd27ac35e24dbced554792e5Makoto Onuki     * Same as {@link #expect(String, String[])}, but the first arg is taken literally, rather than
128af6724527e564d35dd27ac35e24dbced554792e5Makoto Onuki     * as a regexp.
129af6724527e564d35dd27ac35e24dbced554792e5Makoto Onuki     */
130af6724527e564d35dd27ac35e24dbced554792e5Makoto Onuki    public void expectLiterally(String literal, String[] responses) {
131af6724527e564d35dd27ac35e24dbced554792e5Makoto Onuki        expect("^" + Pattern.quote(literal) + "$", responses);
132af6724527e564d35dd27ac35e24dbced554792e5Makoto Onuki    }
133af6724527e564d35dd27ac35e24dbced554792e5Makoto Onuki
134a599ee773d3bb6350ca775db717123faffbdbfe3Makoto Onuki    /**
13596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * Tell the Mock Transport that we expect it to be closed.  This will preserve
13696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * the remaining entries in the expect() stream and allow us to "ride over" the close (which
13796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * would normally reset everything).
13896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     */
13996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    public void expectClose() {
14096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        mPairs.add(new Transaction(Transaction.ACTION_CLIENT_CLOSE));
14196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
142a599ee773d3bb6350ca775db717123faffbdbfe3Makoto Onuki
143f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki    public void expectIOException() {
144f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki        mPairs.add(new Transaction(Transaction.ACTION_IO_EXCEPTION));
145f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki    }
146f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki
1471a791e675ba5092569125bf09a9fcc721bd4c5e1Andy Stadler    public void expectStartTls() {
1481a791e675ba5092569125bf09a9fcc721bd4c5e1Andy Stadler        mPairs.add(new Transaction(Transaction.ACTION_START_TLS));
1491a791e675ba5092569125bf09a9fcc721bd4c5e1Andy Stadler    }
1501a791e675ba5092569125bf09a9fcc721bd4c5e1Andy Stadler
151f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki    private void sendResponse(Transaction pair) {
152f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki        switch (pair.mAction) {
153f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki            case Transaction.ACTION_INJECT_TEXT:
154f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki                for (String s : pair.mResponses) {
155f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki                    mQueuedInput.add(s);
156f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki                }
157f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki                break;
158f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki            case Transaction.ACTION_IO_EXCEPTION:
159f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki                mQueuedInput.add(SPECIAL_RESPONSE_IOEXCEPTION);
160f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki                break;
161f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki            default:
162f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki                Assert.fail("Invalid action for sendResponse: " + pair.mAction);
16396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
16496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
16596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
16696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    public boolean canTrySslSecurity() {
167e4a7cc440f081ef9c4375a2bd2f82680cc11b152Andrew Stadler        return (mConnectionSecurity == CONNECTION_SECURITY_SSL);
16896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
169a599ee773d3bb6350ca775db717123faffbdbfe3Makoto Onuki
17096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    public boolean canTryTlsSecurity() {
171e4a7cc440f081ef9c4375a2bd2f82680cc11b152Andrew Stadler        return (mConnectionSecurity == Transport.CONNECTION_SECURITY_TLS);
17296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
173e4a7cc440f081ef9c4375a2bd2f82680cc11b152Andrew Stadler
174e4a7cc440f081ef9c4375a2bd2f82680cc11b152Andrew Stadler    public boolean canTrustAllCertificates() {
175e4a7cc440f081ef9c4375a2bd2f82680cc11b152Andrew Stadler        return mTrustCertificates;
176e4a7cc440f081ef9c4375a2bd2f82680cc11b152Andrew Stadler    }
177e4a7cc440f081ef9c4375a2bd2f82680cc11b152Andrew Stadler
17896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    /**
1791a791e675ba5092569125bf09a9fcc721bd4c5e1Andy Stadler     * Check that TLS was started
1801a791e675ba5092569125bf09a9fcc721bd4c5e1Andy Stadler     */
1811a791e675ba5092569125bf09a9fcc721bd4c5e1Andy Stadler    public boolean isTlsStarted() {
1821a791e675ba5092569125bf09a9fcc721bd4c5e1Andy Stadler        return mTlsStarted;
1831a791e675ba5092569125bf09a9fcc721bd4c5e1Andy Stadler    }
1841a791e675ba5092569125bf09a9fcc721bd4c5e1Andy Stadler
1851a791e675ba5092569125bf09a9fcc721bd4c5e1Andy Stadler    /**
186a599ee773d3bb6350ca775db717123faffbdbfe3Makoto Onuki     * This simulates a condition where the server has closed its side, causing
18796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * reads to fail.
18896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     */
18996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    public void closeInputStream() {
19096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        mInputOpen = false;
19196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
19296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
19396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    public void close() {
19496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        mOpen = false;
19596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        mInputOpen = false;
19696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        // unless it was expected as part of a test, reset the stream
19796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        if (mPairs.size() > 0) {
19896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            Transaction expect = mPairs.remove(0);
19996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            if (expect.mAction == Transaction.ACTION_CLIENT_CLOSE) {
20096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                return;
20196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            }
20296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
20396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        mQueuedInput.clear();
20496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        mPairs.clear();
20596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
20696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
207cb95fbe13554ee6b4e46d9fd3bcd983e09a688cbAndrew Stadler    /**
208cb95fbe13554ee6b4e46d9fd3bcd983e09a688cbAndrew Stadler     * This is a test function (not part of the interface) and is used to set up a result
209cb95fbe13554ee6b4e46d9fd3bcd983e09a688cbAndrew Stadler     * value for getHost(), if needed for the test.
210cb95fbe13554ee6b4e46d9fd3bcd983e09a688cbAndrew Stadler     */
211cb95fbe13554ee6b4e46d9fd3bcd983e09a688cbAndrew Stadler    public void setMockHost(String host) {
212cb95fbe13554ee6b4e46d9fd3bcd983e09a688cbAndrew Stadler        mHost = host;
213cb95fbe13554ee6b4e46d9fd3bcd983e09a688cbAndrew Stadler    }
214cb95fbe13554ee6b4e46d9fd3bcd983e09a688cbAndrew Stadler
21596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    public String getHost() {
216cb95fbe13554ee6b4e46d9fd3bcd983e09a688cbAndrew Stadler        return mHost;
21796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
21896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
219be2ef97220619a3acf6c3dd4eb4b8add26ff452aAndy Stadler    public void setMockLocalAddress(InetAddress address) {
220be2ef97220619a3acf6c3dd4eb4b8add26ff452aAndy Stadler        mLocalAddress = address;
221be2ef97220619a3acf6c3dd4eb4b8add26ff452aAndy Stadler    }
222be2ef97220619a3acf6c3dd4eb4b8add26ff452aAndy Stadler
22396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    public InputStream getInputStream() {
22496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        SmtpSenderUnitTests.assertTrue(mOpen);
22596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        return new MockInputStream();
22696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
22796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
22896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    /**
22996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * This normally serves as a pseudo-clone, for use by Imap.  For the purposes of unit testing,
230a599ee773d3bb6350ca775db717123faffbdbfe3Makoto Onuki     * until we need something more complex, we'll just return the actual MockTransport.  Then we
23196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * don't have to worry about dealing with test metadata like the expects list or socket state.
23296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     */
23396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    public Transport newInstanceWithConfiguration() {
23496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project         return this;
23596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
23696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
23796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    public OutputStream getOutputStream() {
238af6724527e564d35dd27ac35e24dbced554792e5Makoto Onuki        Assert.assertTrue(mOpen);
23996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        return new MockOutputStream();
24096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
24196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
24296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    public int getPort() {
24396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        SmtpSenderUnitTests.fail("getPort() not implemented");
24496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        return 0;
24596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
24696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
24796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    public int getSecurity() {
24896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        return mConnectionSecurity;
24996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
25096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
25196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    public String[] getUserInfoParts() {
25296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        SmtpSenderUnitTests.fail("getUserInfoParts() not implemented");
25396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        return null;
25496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
25596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
25696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    public boolean isOpen() {
25796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        return mOpen;
25896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
25996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
26096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    public void open() /* throws MessagingException, CertificateValidationException */ {
26196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        mOpen = true;
26296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        mInputOpen = true;
26396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
26496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
26596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    /**
26696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * This returns one string (if available) to the caller.  Usually this simply pulls strings
26796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * from the mQueuedInput list, but if the list is empty, we also peek the expect list.  This
26896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * supports banners, multi-line responses, and any other cases where we respond without
26996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * a specific expect pattern.
270a599ee773d3bb6350ca775db717123faffbdbfe3Makoto Onuki     *
27196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * If no response text is available, we assert (failing our test) as an underflow.
272a599ee773d3bb6350ca775db717123faffbdbfe3Makoto Onuki     *
27396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * Logs the read text if DEBUG_LOG_STREAMS is true.
27496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     */
27596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    public String readLine() throws IOException {
27696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        SmtpSenderUnitTests.assertTrue(mOpen);
27796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        if (!mInputOpen) {
27896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            throw new IOException("Reading from MockTransport with closed input");
27996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
28096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        // if there's nothing to read, see if we can find a null-pattern response
281f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki        if ((mQueuedInput.size() == 0) && (mPairs.size() > 0)) {
282f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki            Transaction pair = mPairs.get(0);
283f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki            if (pair.mPattern == null) {
28496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                mPairs.remove(0);
285f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki                sendResponse(pair);
28696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            }
28796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
288f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki        if (mQueuedInput.size() == 0) {
289f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki            // MailTransport returns "" at EOS.
290f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki            Log.w(LOG_TAG, "Underflow reading from MockTransport");
291f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki            return "";
292f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki        }
29396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        String line = mQueuedInput.remove(0);
29496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        if (DEBUG_LOG_STREAMS) {
29596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            Log.d(LOG_TAG, "<<< " + line);
29696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
297f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki        if (SPECIAL_RESPONSE_IOEXCEPTION.equals(line)) {
298f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki            throw new IOException("Expected IOException.");
299f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki        }
30096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        return line;
30196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
30296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
30396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    public void reopenTls() /* throws MessagingException */ {
30496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        SmtpSenderUnitTests.assertTrue(mOpen);
3051a791e675ba5092569125bf09a9fcc721bd4c5e1Andy Stadler        Transaction expect = mPairs.remove(0);
3061a791e675ba5092569125bf09a9fcc721bd4c5e1Andy Stadler        SmtpSenderUnitTests.assertTrue(expect.mAction == Transaction.ACTION_START_TLS);
3071a791e675ba5092569125bf09a9fcc721bd4c5e1Andy Stadler        mTlsStarted = true;
30896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
30996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
310e4a7cc440f081ef9c4375a2bd2f82680cc11b152Andrew Stadler    public void setSecurity(int connectionSecurity, boolean trustAllCertificates) {
31196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        mConnectionSecurity = connectionSecurity;
312e4a7cc440f081ef9c4375a2bd2f82680cc11b152Andrew Stadler        mTrustCertificates = trustAllCertificates;
31396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
31496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
31596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    public void setSoTimeout(int timeoutMilliseconds) /* throws SocketException */ {
31696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
317a599ee773d3bb6350ca775db717123faffbdbfe3Makoto Onuki
31896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    public void setUri(URI uri, int defaultPort) {
31996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        SmtpSenderUnitTests.assertTrue("Don't call setUri on a mock transport", false);
32096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
32196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
32296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    /**
32396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * Accepts a single string (command or text) that was written by the code under test.
32496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * Because we are essentially mocking a server, we check to see if this string was expected.
32596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * If the string was expected, we push the corresponding responses into the mQueuedInput
32696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * list, for subsequent calls to readLine().  If the string does not match, we assert
32796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * the mismatch.  If no string was expected, we assert it as an overflow.
328a599ee773d3bb6350ca775db717123faffbdbfe3Makoto Onuki     *
32996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * Logs the written text if DEBUG_LOG_STREAMS is true.
33096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     */
331f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki    public void writeLine(String s, String sensitiveReplacement) throws IOException {
33296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        if (DEBUG_LOG_STREAMS) {
33396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            Log.d(LOG_TAG, ">>> " + s);
33496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
33596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        SmtpSenderUnitTests.assertTrue(mOpen);
336af6724527e564d35dd27ac35e24dbced554792e5Makoto Onuki        SmtpSenderUnitTests.assertTrue("Overflow writing to MockTransport: Getting " + s,
337af6724527e564d35dd27ac35e24dbced554792e5Makoto Onuki                0 != mPairs.size());
33896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        Transaction pair = mPairs.remove(0);
339f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki        if (pair.mAction == Transaction.ACTION_IO_EXCEPTION) {
340f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki            throw new IOException("Expected IOException.");
341f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki        }
342af6724527e564d35dd27ac35e24dbced554792e5Makoto Onuki        SmtpSenderUnitTests.assertTrue("Unexpected string written to MockTransport: Actual=" + s
343af6724527e564d35dd27ac35e24dbced554792e5Makoto Onuki                + "  Expected=" + pair.mPattern,
34496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                pair.mPattern != null && s.matches(pair.mPattern));
34596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        if (pair.mResponses != null) {
346f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki            sendResponse(pair);
34796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
34896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
349a599ee773d3bb6350ca775db717123faffbdbfe3Makoto Onuki
35096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    /**
35196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * This is an InputStream that satisfies the needs of getInputStream()
35296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     */
35396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    private class MockInputStream extends InputStream {
35496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
35596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        byte[] mNextLine = null;
35696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        int mNextIndex = 0;
357a599ee773d3bb6350ca775db717123faffbdbfe3Makoto Onuki
35896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        /**
35996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project         * Reads from the same input buffer as readLine()
36096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project         */
36196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        @Override
36296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        public int read() throws IOException {
36396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            if (!mInputOpen) {
36496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                throw new IOException();
36596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            }
366a599ee773d3bb6350ca775db717123faffbdbfe3Makoto Onuki
36796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            if (mNextLine != null && mNextIndex < mNextLine.length) {
36896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                return mNextLine[mNextIndex++];
36996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            }
370a599ee773d3bb6350ca775db717123faffbdbfe3Makoto Onuki
37196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            // previous line was exhausted so try to get another one
37296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            String next = readLine();
37396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            if (next == null) {
37496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                throw new IOException("Reading from MockTransport with closed input");
37596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            }
37696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            mNextLine = (next + "\r\n").getBytes();
37796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            mNextIndex = 0;
37896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
37996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            if (mNextLine != null && mNextIndex < mNextLine.length) {
38096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                return mNextLine[mNextIndex++];
38196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            }
382a599ee773d3bb6350ca775db717123faffbdbfe3Makoto Onuki
383a599ee773d3bb6350ca775db717123faffbdbfe3Makoto Onuki            // no joy - throw an exception
384a599ee773d3bb6350ca775db717123faffbdbfe3Makoto Onuki            throw new IOException();
38596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
38696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
387a599ee773d3bb6350ca775db717123faffbdbfe3Makoto Onuki
38896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    /**
38996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     * This is an OutputStream that satisfies the needs of getOutputStream()
39096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project     */
39196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    private class MockOutputStream extends OutputStream {
39296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
39396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        StringBuilder sb = new StringBuilder();
39496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project
39596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        @Override
396f255081a85bd1680c6a2d2b7225aa45cd104cb66Makoto Onuki        public void write(int oneByte) throws IOException {
397c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler            // CR or CRLF will immediately dump previous line (w/o CRLF)
398c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler            if (oneByte == '\r') {
399c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler                writeLine(sb.toString(), null);
400c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler                sb = new StringBuilder();
401c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler            } else if (oneByte == '\n') {
402c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler                // swallow it
403c640cbbaf385566e1b6de361b2b23156e10f695dAndrew Stadler            } else {
40496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project                sb.append((char)oneByte);
40596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project            }
40696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project        }
40796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project    }
408be2ef97220619a3acf6c3dd4eb4b8add26ff452aAndy Stadler
409be2ef97220619a3acf6c3dd4eb4b8add26ff452aAndy Stadler    public InetAddress getLocalAddress() {
410be2ef97220619a3acf6c3dd4eb4b8add26ff452aAndy Stadler        if (isOpen()) {
411be2ef97220619a3acf6c3dd4eb4b8add26ff452aAndy Stadler            return mLocalAddress;
412be2ef97220619a3acf6c3dd4eb4b8add26ff452aAndy Stadler        } else {
413be2ef97220619a3acf6c3dd4eb4b8add26ff452aAndy Stadler            return null;
414be2ef97220619a3acf6c3dd4eb4b8add26ff452aAndy Stadler        }
415be2ef97220619a3acf6c3dd4eb4b8add26ff452aAndy Stadler    }
41696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project}