Add sms test framework
- SmsTest is the basic test class which can be extended by other tests
- SmsStressTest stress sending/receiving messages, different size of
  message and recipient can be passed through test runner

Change-Id: I0b8444f423c8fbf5805dd3d5e9beb8914b4b81cb
diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml
index bb0d6f5..37a1a5e 100644
--- a/tests/AndroidManifest.xml
+++ b/tests/AndroidManifest.xml
@@ -37,4 +37,8 @@
     <instrumentation android:name="com.android.mms.MmsLaunchPerformance"
                      android:targetPackage="com.android.mms"
                      android:label="Mms Launch Performance Test"/>
+
+    <instrumentation android:name="com.android.mms.SmsTestRunner"
+                     android:targetPackage="com.android.mms"
+                     android:label="Sms stress Test"/>
 </manifest>
diff --git a/tests/src/com/android/mms/SmsTestRunner.java b/tests/src/com/android/mms/SmsTestRunner.java
new file mode 100644
index 0000000..f0af1f6
--- /dev/null
+++ b/tests/src/com/android/mms/SmsTestRunner.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2011, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.mms;
+
+import android.os.Bundle;
+import android.test.InstrumentationTestRunner;
+import android.test.InstrumentationTestSuite;
+
+import com.android.mms.ui.MultiPartSmsTests;
+import com.android.mms.ui.LongThreadTest;
+import com.android.mms.ui.SmsStressTest;
+
+import junit.framework.TestSuite;
+
+/**
+ * TestRunner for Sms tests
+ * To run the test type command
+ * adb shell am instrument -e recipeint 6509339530 -e messages 10
+ * -e messagefile words -e recipientfile recipients -e receivetimer 180
+ * -e sendinterval 10 -w com.android.mms.tests/com.android.mms.SmsTestRunner
+ */
+public class SmsTestRunner extends InstrumentationTestRunner{
+    // a single recipient, default is the local number
+    public String mRecipient = null;
+    // number of messages to send
+    public int mNumberMessages = 0;
+    // file used to store a message (under /data/data/com.android.mms/files/)
+    public String mMessageFileName = null;
+    // file to store recipients separated by comma (/data/data/com.android.mms/files/)
+    public String mRecipientFileName = null;
+    // timer (in ms) to wait before checking receiving message
+    public long mReceiveTimer = 0;
+    // time interval (in ms) between two consecutive messages
+    public long mSendInterval = 0;
+
+    @Override
+    public TestSuite getAllTests() {
+        TestSuite suite = new InstrumentationTestSuite(this);
+        // create a test suite
+        suite.addTestSuite(MultiPartSmsTests.class);
+        suite.addTestSuite(SmsStressTest.class);
+        return suite;
+    }
+
+    @Override
+    public ClassLoader getLoader() {
+        return SmsTestRunner.class.getClassLoader();
+    }
+
+    @Override
+    public void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+
+        // parse all input arguments
+        String recipientPhoneNumber = (String) icicle.get("recipient");
+        if (recipientPhoneNumber != null) {
+            mRecipient = recipientPhoneNumber;
+        }
+        String numMsgStr = (String) icicle.get("messages");
+        if (numMsgStr != null) {
+            mNumberMessages = Integer.parseInt(numMsgStr);
+        }
+        String msgFileNameStr = (String) icicle.get("messagefile");
+        if (msgFileNameStr != null) {
+            mMessageFileName = msgFileNameStr;
+        }
+        String recpFileNameStr = (String) icicle.get("recipientfile");
+        if (recpFileNameStr != null) {
+            mRecipientFileName = recpFileNameStr;
+        }
+        // user input is by seconds, convert to ms
+        String receiveTimerStr = (String) icicle.get("receivetimer");
+        if (receiveTimerStr != null) {
+            mReceiveTimer = (long)1000 * Integer.parseInt(receiveTimerStr);
+        }
+        // user input is by seconds, convert to ms
+        String sendIntervalStr = (String) icicle.get("sendinterval");
+        if (sendIntervalStr != null) {
+            mSendInterval = (long)1000 * Integer.parseInt(sendIntervalStr);
+        }
+    }
+}
diff --git a/tests/src/com/android/mms/ui/MultiPartSmsTests.java b/tests/src/com/android/mms/ui/MultiPartSmsTests.java
index ee98a71..9f093dc 100644
--- a/tests/src/com/android/mms/ui/MultiPartSmsTests.java
+++ b/tests/src/com/android/mms/ui/MultiPartSmsTests.java
@@ -24,6 +24,9 @@
 import com.android.mms.ui.RecipientsEditor;
 import android.os.SystemClock;
 import android.database.Cursor;
+import android.content.Context;
+import android.content.Intent;
+import android.telephony.PhoneNumberUtils;
 import android.test.ActivityInstrumentationTestCase2;
 import android.test.suitebuilder.annotation.LargeTest;
 import android.view.View;
@@ -33,115 +36,59 @@
 import android.widget.Button;
 import android.widget.ImageButton;
 import android.util.Log;
-
+import android.text.TextUtils;
 
 /**
  * Test threads with thousands of messages
  * To run just this test:
  *       runtest --test-class=com.android.mms.ui.MultiPartSmsTests mms
  */
-public class MultiPartSmsTests
-extends ActivityInstrumentationTestCase2<ComposeMessageActivity> {
-
+public class MultiPartSmsTests extends SmsTest {
     private static final String TAG = "MultiPartSmsTests";
-    private static final int SMS_RECEIVE_TIMER = 5 * 60 * 1000; //5 minutes;
-    private ComposeMessageActivity mActivity = null;
-    private RecipientsEditor mRecipientsEditor;
-    private EditText mTextEditor;
 
-    // NOTE: the longer the message, the longer is takes to send and get back the
-    // received message. You'll have to adjust the timeout in testLongSmsMessage().
-    // I eventually pared down the message to make the test more reasonable to test.
-    final String mLongMessage =
-        "Is this a dagger which I see before me,"
-        +" The handle toward my hand? Come, let me clutch thee."
-        +" I have thee not, and yet I see thee still."
-        +" Art thou not, fatal vision, sensible"
-        +" To feeling as to sight? or art thou but"
-        +" A dagger of the mind, a false creation,"
-        +" Proceeding from the heat-oppressed brain?"
-        +" I see thee yet, in form as palpable"
-        +" As this which now I draw.";
-//        +" Thou marshall'st me the way that I was going;"
-//        +" And such an instrument I was to use."
-//        +" Mine eyes are made the fools o' the other senses,"
-//        +" Or else worth all the rest; I see thee still,"
-//        +" And on thy blade and dudgeon gouts of blood,"
-//        +" Which was not so before. There's no such thing:"
-//        +" It is the bloody business which informs"
-//        +" Thus to mine eyes. Now o'er the one halfworld"
-//        +" Nature seems dead, and wicked dreams abuse"
-//        +" The curtain'd sleep; witchcraft celebrates"
-//        +" Pale Hecate's offerings, and wither'd murder,"
-//        +" Alarum'd by his sentinel, the wolf,"
-//        +" Whose howl's his watch, thus with his stealthy pace."
-//        +" With Tarquin's ravishing strides, towards his design"
-//        +" Moves like a ghost. Thou sure and firm-set earth,"
-//        +" Hear not my steps, which way they walk, for fear"
-//        +" Thy very stones prate of my whereabout,"
-//        +" And take the present horror from the time,"
-//        +" Which now suits with it. Whiles I threat, he lives:"
-//        +" Words to the heat of deeds too cold breath gives."
-//        +" A bell rings"
-//        +" I go, and it is done; the bell invites me."
-//        +" Hear it not, Duncan; for it is a knell"
-//        +" That summons thee to heaven or to hell.";
-    private String mMyNumber;
-
-    public MultiPartSmsTests() {
-        super(ComposeMessageActivity.class);
-    }
-
+    /* (non-Javadoc)
+     * @see com.android.mms.ui.SmsTest#setUp()
+     */
     @Override
     protected void setUp() throws Exception {
         super.setUp();
-
-        mActivity = getActivity();
-        ViewStub stub = (ViewStub)mActivity.findViewById(R.id.recipients_editor_stub);
-        if (stub != null) {
-            View stubView = stub.inflate();
-            mRecipientsEditor = (RecipientsEditor) stubView.findViewById(R.id.recipients_editor);
-        } else {
-            mRecipientsEditor = (RecipientsEditor)mActivity.findViewById(R.id.recipients_editor);
-            mRecipientsEditor.setVisibility(View.VISIBLE);
-        }
-        mTextEditor = (EditText)mActivity.findViewById(R.id.embedded_text_editor);
-
-        mMyNumber = MessageUtils.getLocalNumber();
-        assertNotNull("null number for this phone", mMyNumber);
-        // WARNING: MessageUtils.getLocalNumber returned some 206 number as the number
-        // of this phone, which is totally the wrong area code. Therefore, the test
-        // ended up failing because it sent a gigantic message to some unknown number
-        // and never received the number back. For now, I'm just hardwiring the number
-        // of my phone.
-//        mMyNumber = "6502782055";
-        mMyNumber = "6509330537";
-    }
-
-    private abstract class MessageRunnable implements Runnable {
-        protected String mRecipient;
-
-        public void setRecipient(String recipient) {
-            mRecipient = recipient;
-        }
-    }
-
-    private MessageRunnable mSendSmsMessage = new MessageRunnable() {
-        public void run() {
-            // only on the first message will there be a recipients editor
-            if (mRecipientsEditor.getVisibility() == View.VISIBLE) {
-                mRecipientsEditor.setText(mRecipient);
-            }
-            mTextEditor.setText(mLongMessage);
-            ImageButton send = (ImageButton)mActivity.findViewById(R.id.send_button_sms);
-            send.performClick();
-        }
-    };
-
-    private void sleep(long sleepTime) {
-        try {
-            Thread.sleep(sleepTime);
-        } catch (InterruptedException e) {}
+        // NOTE: the longer the message, the longer is takes to send and get back the
+        // received message. You'll have to adjust the timeout in testLongSmsMessage().
+        // I eventually paired down the message to make the test more reasonable to test.
+        mMessage =
+            "Is this a dagger which I see before me,"
+            +" The handle toward my hand? Come, let me clutch thee."
+            +" I have thee not, and yet I see thee still."
+            +" Art thou not, fatal vision, sensible"
+            +" To feeling as to sight? or art thou but"
+            +" A dagger of the mind, a false creation,"
+            +" Proceeding from the heat-oppressed brain?"
+            +" I see thee yet, in form as palpable"
+            +" As this which now I draw.";
+//            +" Thou marshall'st me the way that I was going;"
+//            +" And such an instrument I was to use."
+//            +" Mine eyes are made the fools o' the other senses,"
+//            +" Or else worth all the rest; I see thee still,"
+//            +" And on thy blade and dudgeon gouts of blood,"
+//            +" Which was not so before. There's no such thing:"
+//            +" It is the bloody business which informs"
+//            +" Thus to mine eyes. Now o'er the one halfworld"
+//            +" Nature seems dead, and wicked dreams abuse"
+//            +" The curtain'd sleep; witchcraft celebrates"
+//            +" Pale Hecate's offerings, and wither'd murder,"
+//            +" Alarum'd by his sentinel, the wolf,"
+//            +" Whose howl's his watch, thus with his stealthy pace."
+//            +" With Tarquin's ravishing strides, towards his design"
+//            +" Moves like a ghost. Thou sure and firm-set earth,"
+//            +" Hear not my steps, which way they walk, for fear"
+//            +" Thy very stones prate of my whereabout,"
+//            +" And take the present horror from the time,"
+//            +" Which now suits with it. Whiles I threat, he lives:"
+//            +" Words to the heat of deeds too cold breath gives."
+//            +" A bell rings"
+//            +" I go, and it is done; the bell invites me."
+//            +" Hear it not, Duncan; for it is a knell"
+//            +" That summons thee to heaven or to hell.";
     }
 
     /**
@@ -149,48 +96,7 @@
      */
     @LargeTest
     public void testLongSmsMessage() throws Throwable {
-        final ComposeMessageActivity a = getActivity();
-        mActivity.runOnUiThread(new Runnable() {
-            public void run() {
-                a.initialize(null, 0);
-                a.loadMessageContent();
-            }
-        });
-
-        // wait 5 seconds for the activity to run on UI thread and
-        // mMsgListAdapter get updated with latest information.
-        sleep(5 * 1000);
-        int msgCount = mActivity.mMsgListAdapter.getCount();
-        Log.v(TAG, "msgCount: " + msgCount);
-        // Send out message to the recipient
-        mSendSmsMessage.setRecipient(mMyNumber);
-        runTestOnUiThread(mSendSmsMessage);
-
-        // Wait for maximum 5 minutes to send the long message
-        // and then receive it. Make sure the sent and received messages compare the same.
-        boolean received = false;
-        long startTime = System.currentTimeMillis();
-        while ((System.currentTimeMillis() - startTime) <= SMS_RECEIVE_TIMER) {
-            sleep( 5 * 1000);     // wait 5 seconds between checks
-            Log.v(TAG, "Message Count: " + mActivity.mMsgListAdapter.getCount());
-            if (msgCount + 2 == mActivity.mMsgListAdapter.getCount()) {
-                // The "msgCount + 2" is to account for the sent and received message.
-                // Other cases: 1) fail to send/receive sms message, test fail
-                // 2) another message could be received by the target phone during this time
-                //    test will falsely fail
-                Cursor cursor = mActivity.mMsgListAdapter.getCursor();
-                cursor.moveToLast();
-                String type = cursor.getString(COLUMN_MSG_TYPE);
-                long msgId = cursor.getLong(COLUMN_ID);
-                MessageItem msgItem = mActivity.mMsgListAdapter.getCachedMessageItem(type, msgId, cursor);
-                assertNotNull("got a null last MessageItem", msgItem);
-                assertEquals("The sent and received messages aren't the same",
-                        mLongMessage,
-                        msgItem.mBody);
-                received = true;
-                break;
-            }
-        }
-        assertTrue("Never received the sent message", received);
+        assertTrue("send & receive message failed",
+                sendAndReceiveMessage());
     }
 }
diff --git a/tests/src/com/android/mms/ui/SmsStressTest.java b/tests/src/com/android/mms/ui/SmsStressTest.java
new file mode 100644
index 0000000..d94a818
--- /dev/null
+++ b/tests/src/com/android/mms/ui/SmsStressTest.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.mms.ui;
+
+import static com.android.mms.ui.MessageListAdapter.COLUMN_ID;
+import static com.android.mms.ui.MessageListAdapter.COLUMN_MSG_TYPE;
+
+import com.android.mms.R;
+import com.android.mms.data.Conversation;
+import com.android.mms.data.Contact;
+import com.android.mms.data.ContactList;
+import com.android.mms.ui.ComposeMessageActivity;
+import com.android.mms.ui.RecipientsEditor;
+import com.android.mms.SmsTestRunner;
+import android.os.Environment;
+import android.os.SystemClock;
+import android.database.Cursor;
+import android.content.Context;
+import android.content.Intent;
+import android.test.ActivityInstrumentationTestCase2;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.view.View;
+import android.view.ViewStub;
+import android.widget.EditText;
+import android.widget.TextView;
+import android.widget.Button;
+import android.widget.ImageButton;
+import android.util.Log;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * Sms stress test. Send muliptle sms each with multiple segments.
+ * To run this test
+ * adb shell am instrument -e class com.android.mms.ui.SmsStressTest
+ *  -w com.android.mms.tests/com.android.mms.SmsTestRunner
+ */
+public class SmsStressTest extends SmsTest {
+    private final static String TAG = "SmsStressTest";
+    private final static String OUTPUT = "result.txt";
+    private long mSendInterval = 10 * 1000; // 10 seconds
+    protected int mIteration = 100;
+    protected BufferedWriter mWriter = null;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        // Get input value for this test
+        if (mInst.mNumberMessages > 0) {
+            mIteration = mInst.mNumberMessages;
+        }
+        if (mInst.mSendInterval > 0) {
+            mSendInterval = mInst.mSendInterval;
+        }
+        Log.v(TAG, String.format("mIteration: %d, mSendInterval: %d",
+                                 mIteration, mSendInterval));
+        mWriter = new BufferedWriter(new FileWriter(new File(
+            Environment.getExternalStorageDirectory(), OUTPUT), true));
+
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        Log.v(TAG, "tearDown");
+        if (mWriter != null) {
+            mWriter.close();
+        }
+        super.tearDown();
+    }
+
+    /**
+     * Sending multiple sms over a single thread
+     */
+    @LargeTest
+    public void testMultiMessageOverSingleThread() throws Throwable {
+        int i;
+        for (i = 0; i < mIteration; i++) {
+            Log.v(TAG, "iteration: " + i);
+            assertTrue("send & receive message failed",
+                    sendAndReceiveMessage());
+            sleep(mSendInterval);
+            mWriter.write(String.format("send message %d out of %d\n",
+                                        (i+1), mIteration));
+        }
+    }
+}
diff --git a/tests/src/com/android/mms/ui/SmsTest.java b/tests/src/com/android/mms/ui/SmsTest.java
new file mode 100644
index 0000000..f2076bb
--- /dev/null
+++ b/tests/src/com/android/mms/ui/SmsTest.java
@@ -0,0 +1,280 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.mms.ui;
+
+import static com.android.mms.ui.MessageListAdapter.COLUMN_ID;
+import static com.android.mms.ui.MessageListAdapter.COLUMN_MSG_TYPE;
+
+import com.android.mms.R;
+import com.android.mms.data.Conversation;
+import com.android.mms.data.Contact;
+import com.android.mms.data.ContactList;
+import com.android.mms.ui.ComposeMessageActivity;
+import com.android.mms.ui.RecipientsEditor;
+import com.android.mms.SmsTestRunner;
+
+import android.os.SystemClock;
+import android.database.Cursor;
+import android.content.Context;
+import android.content.Intent;
+import android.telephony.PhoneNumberUtils;
+import android.test.ActivityInstrumentationTestCase2;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.text.TextUtils;
+import android.view.View;
+import android.view.ViewStub;
+import android.widget.EditText;
+import android.widget.TextView;
+import android.widget.Button;
+import android.widget.ImageButton;
+import android.util.Log;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.util.List;
+import java.util.ArrayList;
+
+
+/**
+ * Base class for sms tests.
+ */
+public class SmsTest
+    extends ActivityInstrumentationTestCase2<ComposeMessageActivity> {
+
+    private final static String TAG = "SmsTest";
+    protected ComposeMessageActivity mActivity;
+    protected RecipientsEditor mRecipientsEditor;
+    protected EditText mTextEditor;
+    protected SmsTestRunner mInst;
+    protected String mRecipient;
+    protected List mRecipientsList = null;
+    protected long mReceiveTimer = 5 * 60 * 1000 ; // 5 minutes
+
+    // default message to sent
+    protected String mMessage =
+        "Is this a dagger which I see before me,"
+        +" The handle toward my hand? Come, let me clutch thee."
+        +" I have thee not, and yet I see thee still."
+        +" Art thou not, fatal vision, sensible"
+        +" To feeling as to sight? or art thou but"
+        +" A dagger of the mind, a false creation,"
+        +" Proceeding from the heat-oppressed brain?"
+        +" I see thee yet, in form as palpable"
+        +" As this which now I draw.";
+
+    protected Long mThreadId;
+
+    public SmsTest() {
+       super(ComposeMessageActivity.class);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        mActivity = getActivity();
+        ViewStub stub = (ViewStub)mActivity.findViewById(R.id.recipients_editor_stub);
+        if (stub != null) {
+            View stubView = stub.inflate();
+            mRecipientsEditor = (RecipientsEditor) stubView.findViewById(R.id.recipients_editor);
+        } else {
+            mRecipientsEditor = (RecipientsEditor) mActivity.findViewById(R.id.recipients_editor);
+            mRecipientsEditor.setVisibility(View.VISIBLE);
+        }
+        mTextEditor = (EditText)mActivity.findViewById(R.id.embedded_text_editor);
+
+        // parse input argument
+        mInst = (SmsTestRunner)getInstrumentation();
+        if (mInst.mRecipient != null) {
+            mRecipient = mInst.mRecipient;
+        } else {
+            mRecipient = getLocalNumber();
+        }
+        if (mInst.mReceiveTimer > 0) {
+            mReceiveTimer = mInst.mReceiveTimer;
+        }
+        loadRecipientsList();
+        loadMessage();
+        Log.v(TAG, String.format("mReceiveTimer: %d, mRecipient: %s",
+                                 mReceiveTimer, mRecipient));
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+    }
+
+    /**
+     * Load recipients from a file
+     */
+    private void loadRecipientsList() {
+        String recipientFileName = mInst.mRecipientFileName;
+        if (recipientFileName == null) {
+            return;
+        }
+        // Read recipients from a file
+        mRecipientsList = new ArrayList<String>();
+        StringBuilder sb = new StringBuilder();
+        try {
+            Log.v(TAG, "Loading recipients");
+            FileInputStream f = mInst.getTargetContext().openFileInput(recipientFileName);
+            int c;
+            while ((c = f.read()) != -1) {
+                if (c == '\r' || c == '\n' || c == ',') {
+                    String recipient = sb.toString().trim();
+                    if (recipient.length() > 0) {
+                        mRecipientsList.add(recipient);
+                    }
+                    sb.setLength(0);
+                } else {
+                    sb.append((char)c);
+                }
+            }
+            f.close();
+        } catch (Exception e) {
+            Log.e(TAG, "can't open recipients file " + recipientFileName);
+            return;
+        }
+    }
+
+    /**
+     * Load messages from a file, save the message in mMessage
+     */
+    private void loadMessage() {
+        String messageFileName = mInst.mMessageFileName;
+        if (messageFileName == null) {
+            return;
+        }
+
+        Context targetAppContext = mInst.getTargetContext().getApplicationContext();
+        String filePath = String.format("%s/%s", targetAppContext.getFilesDir(), messageFileName);
+        // Read messages from a file
+        byte[] buffer = new byte[(int) new File(filePath).length()];
+        BufferedInputStream bf = null;
+        int numStrs = 0;
+        try {
+            Log.v(TAG, "Loading messages");
+            bf = new BufferedInputStream(
+                    mInst.getTargetContext().openFileInput(messageFileName));
+            numStrs = bf.read(buffer);
+        } catch (Exception e) {
+            Log.e(TAG, "can't open message file at " +
+                    targetAppContext.getFileStreamPath(messageFileName));
+        } finally {
+            if (bf != null) {
+                try { bf.close(); } catch (IOException e) {
+                    Log.v(TAG, "failed to close message file: " +
+                            targetAppContext.getFileStreamPath(messageFileName));
+                }
+            }
+        }
+        if (numStrs > 0) {
+            mMessage = new String(buffer);
+        }
+    }
+
+    private abstract class MessageRunnable implements Runnable {
+        protected String mRecipient;
+
+        public void setRecipient(String recipient) {
+            mRecipient = recipient;
+        }
+    }
+
+    private MessageRunnable mSendSmsMessage = new MessageRunnable() {
+        public void run() {
+            // only on the first message will there be a recipients editor
+            if (mRecipientsEditor.getVisibility() == View.VISIBLE) {
+                mRecipientsEditor.setText(mRecipient);
+            }
+            mTextEditor.setText(mMessage);
+            ImageButton send = (ImageButton)mActivity.findViewById(R.id.send_button_sms);
+            send.performClick();
+        }
+    };
+
+    protected void sleep(long sleepTime) {
+        try {
+            Thread.sleep(sleepTime);
+        } catch (InterruptedException e) {}
+    }
+
+    /**
+     * @return the local number for this test device
+     */
+    protected String getLocalNumber() {
+        return MessageUtils.getLocalNumber();
+    }
+
+    /**
+     * send a message and verify the receiption using the local number and default timer
+     * @return
+     */
+    protected boolean sendAndReceiveMessage() throws Throwable {
+        return sendAndReceiveMessage(mRecipient, mReceiveTimer);
+    }
+
+    /**
+     * @param recipientNumber the recipient number for this sms
+     * @param receiveTimer timer to wait for the received message, if it is null, default timer
+     *        is used.
+     * @return true if received message is equal to what sent, otherwise, return false
+     * @throws Throwable
+     */
+    protected boolean sendAndReceiveMessage(String recipientNumber, long timer)
+        throws Throwable {
+        long receiveTimer = mReceiveTimer;
+        if (timer > 0) {
+            receiveTimer = timer;
+        }
+        int msgCount = mActivity.mMsgListAdapter.getCount();
+        Log.v(TAG, "msgCount: " + msgCount);
+          mSendSmsMessage.setRecipient(recipientNumber);
+        runTestOnUiThread(mSendSmsMessage);
+
+        // Wait for maximum 5 minutes to send the long message
+        // and then receive it. Make sure the sent and received messages are the same.
+        boolean received = false;
+        long startTime = System.currentTimeMillis();
+        while ((System.currentTimeMillis() - startTime) <= receiveTimer) {
+            sleep( 5 * 1000);     // wait 5 seconds between checks
+            Log.v(TAG, "Message Count: " + mActivity.mMsgListAdapter.getCount());
+            if (msgCount + 2 == mActivity.mMsgListAdapter.getCount()) {
+                // The "msgCount + 2" is to account for the sent and received message.
+                // Other cases: 1) fail to send/receive sms message, test fail
+                // 2) another message could be received by the target phone during this time
+                //    test will falsely fail
+                Cursor cursor = mActivity.mMsgListAdapter.getCursor();
+                cursor.moveToLast();
+                String type = cursor.getString(COLUMN_MSG_TYPE);
+                long msgId = cursor.getLong(COLUMN_ID);
+                MessageItem msgItem =
+                    mActivity.mMsgListAdapter.getCachedMessageItem(type, msgId, cursor);
+                assertNotNull("got a null last MessageItem", msgItem);
+                assertEquals("The sent and received messages aren't the same",
+                        mMessage,
+                        msgItem.mBody);
+                received = true;
+                break;
+            }
+        }
+        assertTrue("Never received the sent message", received);
+        return received;
+    }
+}