Add unit tests

Change-Id: Idea772d2e804267e08738806832b9795e2eb2f7b
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 5f1a947..aa46a07 100755
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -23,6 +23,10 @@
 
     <uses-permission android:name="android.permission.RECEIVE_SMS" />
 
+    <!-- Needed just for the unit tests -->
+    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
+    <uses-permission android:name="android.permission.SEND_SMS" />
+
     <application android:name="BasicSmsReceiverApp"
             android:label="@string/sms_app_name"
             android:hardwareAccelerated="true">
diff --git a/src/com/android/basicsmsreceiver/DialogSmsDisplay.java b/src/com/android/basicsmsreceiver/DialogSmsDisplay.java
index 0c9c65e..7c94485 100755
--- a/src/com/android/basicsmsreceiver/DialogSmsDisplay.java
+++ b/src/com/android/basicsmsreceiver/DialogSmsDisplay.java
@@ -38,8 +38,9 @@
     public static final String SMS_NOTIFICATION_ID_EXTRA =
                                                 "com.android.basicsmsreceiver.NOTIFICATION_ID";
 
-    private String mFromAddress;
-    private String mMessage;
+    // Visible to unit tests
+    String mFromAddress;
+    String mMessage;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
@@ -49,7 +50,13 @@
     }
 
     private void parseIntent(Intent intent) {
+        if (intent == null) {
+            return;
+        }
         Bundle extras = intent.getExtras();
+        if (extras == null) {
+            return;
+        }
         mFromAddress = extras.getString(SMS_FROM_ADDRESS_EXTRA);
         mMessage = extras.getString(SMS_MESSAGE_EXTRA);
         int notificationId = extras.getInt(SMS_NOTIFICATION_ID_EXTRA);
diff --git a/tests/Android.mk b/tests/Android.mk
new file mode 100644
index 0000000..fe54a33
--- /dev/null
+++ b/tests/Android.mk
@@ -0,0 +1,35 @@
+# Copyright 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.
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+# We only want this apk build for tests.
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_JAVA_LIBRARIES := android.test.runner
+
+# Include all test java files.
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+# Notice that we don't have to include the src files of Email because, by
+# running the tests using an instrumentation targeting Eamil, we
+# automatically get all of its classes loaded into our environment.
+
+LOCAL_PACKAGE_NAME := BasicSmsReceiverTests
+
+LOCAL_INSTRUMENTATION_FOR := BasicSmsReceiver
+
+include $(BUILD_PACKAGE)
+
diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml
new file mode 100644
index 0000000..a8867db
--- /dev/null
+++ b/tests/AndroidManifest.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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 name must be unique so suffix with "tests" so package loader doesn't ignore us -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.basicsmsreceiver.tests">
+
+    <!-- We add an application tag here just so that we can indicate that
+         this package needs to link against the android.test library,
+         which is needed when building test cases. -->
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <!--
+    This declares that this app uses the instrumentation test runner targeting
+    the package of com.android.basicsmsreceiver.  To run the tests use the command:
+    "runtest basicsmsreceiver"
+    -->
+    <instrumentation android:name="android.test.InstrumentationTestRunner"
+                     android:targetPackage="com.android.basicsmsreceiver"
+                     android:label="Tests for basicsmsreceiver."/>
+</manifest>
diff --git a/tests/src/com/android/basicsmsreceiver/DialogSmsDisplayTests.java b/tests/src/com/android/basicsmsreceiver/DialogSmsDisplayTests.java
new file mode 100644
index 0000000..265e9e8
--- /dev/null
+++ b/tests/src/com/android/basicsmsreceiver/DialogSmsDisplayTests.java
@@ -0,0 +1,291 @@
+/*
+ * Copyright (C) 2008 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.basicsmsreceiver;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import android.app.Activity;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.database.Cursor;
+import android.graphics.Color;
+import android.os.Bundle;
+import android.provider.Telephony;
+import android.provider.Telephony.Sms.Intents;
+import android.telephony.SmsManager;
+import android.telephony.SmsMessage;
+import android.telephony.TelephonyManager;
+import android.test.ActivityInstrumentationTestCase2;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.util.Log;
+
+/**
+ * Various instrumentation tests for BasicSmsReceiver.
+ *
+ * To run this test: runtest basicsmsreceiver
+ *
+ * TODO: write tests that verify that notifications get created. As of now, I don't see a way
+ * to query the NotificationManager for what notifications are there. I only see methods to
+ * post and cancel notifications.
+ *
+ */
+public class DialogSmsDisplayTests
+        extends ActivityInstrumentationTestCase2<DialogSmsDisplay> {
+
+    private boolean mHasSms;
+    private String mMyNumber;       // phone number of this device
+    public static final String ACTION_SMS_SENT =
+        "com.android.basicsmsreceiver.tests.SMS_SENT_ACTION";
+    private static String TAG = "DialogSmsDisplayTests";
+    private List<String> mReceivedMessages = new ArrayList<String>();
+    private String mReceivedSender;
+    private DialogSmsDisplay dialogSmsDisplayActivity;
+    private int mMessageReceivedCount;
+    private BroadcastReceiver mSmsSenderReceiver;
+    private BroadcastReceiver mSmsReceiverReceiver;
+
+    public DialogSmsDisplayTests() {
+        super("com.android.basicsmsreceiver", DialogSmsDisplay.class);
+        Log.i(TAG, "DialogSmsDisplayTests");
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        dialogSmsDisplayActivity = (DialogSmsDisplay)getActivity();
+
+        TelephonyManager telephonyManager = ((TelephonyManager)dialogSmsDisplayActivity
+                .getSystemService(Context.TELEPHONY_SERVICE));
+        mHasSms = true;     //telephonyManager.isSmsCapable();
+        mMyNumber = telephonyManager.getLine1Number();
+
+        Log.i(TAG, "hasSms: " + mHasSms + " my number: " + mMyNumber);
+
+        assertTrue("SMS must be enabled on the device", mHasSms);
+        assertNotNull("Device does not have a phone number", mMyNumber);
+
+        if (mHasSms) {
+            // Register broadcast receivers for SMS sent and delivered intents
+            mSmsSenderReceiver = new BroadcastReceiver() {
+                @Override
+                public void onReceive(Context context, Intent intent) {
+                    String message = null;
+                    boolean error = true;
+                    switch (getResultCode()) {
+                        case Activity.RESULT_OK:
+                            message = "Message sent!";
+                            error = false;
+                            break;
+                        case SmsManager.RESULT_ERROR_GENERIC_FAILURE:
+                            message = "Error.";
+                            break;
+                        case SmsManager.RESULT_ERROR_NO_SERVICE:
+                            message = "Error: No SMS service.";
+                            break;
+                        case SmsManager.RESULT_ERROR_NULL_PDU:
+                            message = "Error: Null PDU.";
+                            break;
+                        case SmsManager.RESULT_ERROR_RADIO_OFF:
+                            message = "Error: Radio off.";
+                            break;
+                    }
+                    assertFalse(message, error);
+                }
+            };
+            dialogSmsDisplayActivity.registerReceiver(mSmsSenderReceiver,
+                    new IntentFilter(ACTION_SMS_SENT));
+
+            // Register broadcast receivers for received SMS
+            mSmsReceiverReceiver = new BroadcastReceiver() {
+                @Override
+                public void onReceive(Context context, Intent intent) {
+                    Bundle extras = intent.getExtras();
+                    Log.i(TAG, "onReceive");
+                    if (extras == null)
+                        return;
+
+                    Object[] pdus = (Object[]) extras.get("pdus");
+
+                    for (int i = 0; i < pdus.length; i++) {
+                        SmsMessage message = SmsMessage.createFromPdu((byte[]) pdus[i]);
+                        String sender = message.getOriginatingAddress();
+                        if (mReceivedSender != null) {
+                            assertEquals(mReceivedSender, sender);
+                        } else {
+                            mReceivedSender = sender;
+                        }
+                        mReceivedMessages.add(message.getMessageBody().toString());
+
+                        Log.i(TAG, "From: " + mReceivedSender + " message: " +
+                                mReceivedMessages.get(mReceivedMessages.size() - 1));
+                    }
+                }
+            };
+            dialogSmsDisplayActivity.registerReceiver(mSmsReceiverReceiver,
+                    new IntentFilter(Telephony.Sms.Intents.SMS_RECEIVED_ACTION));
+        }
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        if (mSmsSenderReceiver != null) {
+            dialogSmsDisplayActivity.unregisterReceiver(mSmsSenderReceiver);
+            mSmsSenderReceiver = null;
+        }
+        if (mSmsReceiverReceiver != null) {
+            dialogSmsDisplayActivity.unregisterReceiver(mSmsReceiverReceiver);
+            mSmsReceiverReceiver = null;
+        }
+        super.tearDown();
+    }
+
+    // Returns the broken up list of messages for long messages or the original message in
+    // element 0.
+    private List<String> sendSmsMessageInternal(String messageOut) {
+        if (!mHasSms) {
+            fail("no sms on device");
+            return null;
+        }
+        SmsManager sms = SmsManager.getDefault();
+
+        List<String> messages = sms.divideMessage(messageOut);
+        mMessageReceivedCount = 0;
+        mReceivedSender = null;
+        mReceivedMessages.clear();
+
+        for (String message : messages) {
+            Log.i(TAG, "sendSmsMessage: " + messageOut + " to: " + mMyNumber);
+            sms.sendTextMessage(mMyNumber, null, message, PendingIntent.getBroadcast(
+                    dialogSmsDisplayActivity, 0, new Intent(ACTION_SMS_SENT), 0), null);
+        }
+        return messages;
+    }
+
+    // returns true if "messageCount" sms messages are received, false if timeout
+    private boolean waitForSms(int messageCount) {
+        // wait up to two minutes for the sent message to be received
+        long now = System.currentTimeMillis();
+        boolean success = true;
+        Log.i(TAG, "waitForSms -- waiting for: " + messageCount + " parts");
+        while (mReceivedMessages.size() < messageCount) {
+            try {
+                Thread.sleep(1000);
+            } catch (Exception e) {
+            }
+            if (System.currentTimeMillis() - now > 1000 * 2 * 60) {
+                // Give up after two minutes
+                success = false;
+                break;
+            }
+        }
+        if (success) {
+            // Wait 5 seconds for the dialog to launch and update
+            try {
+                Thread.sleep(5000);
+            } catch (Exception e) {
+            }
+        }
+        return success;
+    }
+
+    private void sendMessageTest(String message) {
+        List<String> messages = sendSmsMessageInternal(message);
+        Log.i(TAG, "sendMessageTest -- message broken into " + messages.size() + "parts");
+
+        boolean receivedSms = waitForSms(messages.size());
+        assertTrue("sms not received after two minutes", receivedSms);
+
+        // Check to see if message/# matches
+        assertEquals(mMyNumber, mReceivedSender);
+        assertEquals(messages.size(), mReceivedMessages.size());
+        int i = 0;
+        for (String messageFrag : messages) {
+            assertEquals(messageFrag, mReceivedMessages.get(i++));
+        }
+    }
+
+    public void testSendingSmallSmsMessage() {
+        sendMessageTest("This is a regular size message that might be sent");
+    }
+
+    public void testSendingLargeSmsMessage() {
+        sendMessageTest("This is a long long message. " +
+                "This is a long long message. " +
+                "This is a long long message. " +
+                "This is a long long message. " +
+                "This is a long long message. " +
+                "This is a long long message. " +
+                "This is a long long message. " +
+                "This is a long long message. " +
+                "This is a long long message. " +
+                "This is a long long message. " +
+                "This is a long long message. " +
+                "This is a long long message. " +
+                "This is a long long message. " +
+                "This is a long long message. " +
+                "This is a long long message. " +
+                "This is a long long message. " +
+                "This is a long long message. " +
+                "This is a long long message. " +
+                "This is a long long message. " +
+                "This is a long long message. " +
+                "This is a long long message. " +
+                "This is a long long message. " +
+                "This is a long long message. " +
+                "This is a long long message. " +
+                "This is a long long message. " +
+                "This is a long long message. " +
+                "This is a long long message. " +
+                "This is a long long message. ");
+    }
+
+    public void testOnNewIntentSmall() {
+        // Test a small message and a non-numeric phone number
+        sendOnNewIntent("this is a big fat test", "xyzzy", 2000);
+    }
+
+    public void testOnNewIntentLarge() {
+        // A long message like this should never really happen because SMS messages are limited
+        // to about 140 characters.
+        sendOnNewIntent("now is the time for all good men to come to the aid of their country1" +
+                "now is the time for all good men to come to the aid of their country2" +
+                "now is the time for all good men to come to the aid of their country3",
+                "513-891-7823", 2001);
+    }
+
+    public void sendOnNewIntent(String message, String dest, int notificationId) {
+        Intent di = new Intent();
+        di.setClass(dialogSmsDisplayActivity, DialogSmsDisplay.class);
+        di.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP |
+                Intent.FLAG_ACTIVITY_CLEAR_TOP);
+        di.putExtra(DialogSmsDisplay.SMS_FROM_ADDRESS_EXTRA, dest);
+        di.putExtra(DialogSmsDisplay.SMS_MESSAGE_EXTRA, message);
+        di.putExtra(DialogSmsDisplay.SMS_NOTIFICATION_ID_EXTRA, notificationId);
+        dialogSmsDisplayActivity.onNewIntent(di);
+
+        // Check to see if message/# matches
+        assertEquals(dest, dialogSmsDisplayActivity.mFromAddress);
+        assertEquals(message, dialogSmsDisplayActivity.mMessage);
+    }
+}