Home | History | Annotate | Download | only in action
      1 /*
      2  * Copyright (C) 2015 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.messaging.datamodel.action;
     18 
     19 import android.content.Intent;
     20 import android.os.Bundle;
     21 import android.os.Looper;
     22 import android.os.Parcel;
     23 import android.os.Parcelable;
     24 import android.os.Process;
     25 import android.test.suitebuilder.annotation.MediumTest;
     26 import android.util.Log;
     27 
     28 import com.android.messaging.Factory;
     29 import com.android.messaging.FakeContext;
     30 import com.android.messaging.FakeContext.FakeContextHost;
     31 import com.android.messaging.FakeFactory;
     32 import com.android.messaging.datamodel.BugleServiceTestCase;
     33 import com.android.messaging.datamodel.FakeDataModel;
     34 import com.android.messaging.datamodel.action.ActionMonitor.ActionCompletedListener;
     35 import com.android.messaging.datamodel.action.ActionMonitor.ActionStateChangedListener;
     36 import com.android.messaging.datamodel.action.ActionTestHelpers.ResultTracker;
     37 import com.android.messaging.datamodel.action.ActionTestHelpers.StubBackgroundWorker;
     38 import com.android.messaging.datamodel.action.ActionTestHelpers.StubConnectivityUtil;
     39 import com.android.messaging.datamodel.action.ActionTestHelpers.StubLoader;
     40 import com.android.messaging.util.WakeLockHelper;
     41 
     42 import java.util.ArrayList;
     43 
     44 @MediumTest
     45 public class ActionServiceTest extends BugleServiceTestCase<ActionServiceImpl>
     46         implements FakeContextHost, ActionStateChangedListener, ActionCompletedListener {
     47     private static final String TAG = "ActionServiceTest";
     48 
     49     @Override
     50     public void onActionStateChanged(final Action action, final int state) {
     51         mStates.add(state);
     52     }
     53 
     54     @Override
     55     public void onActionSucceeded(final ActionMonitor monitor,
     56             final Action action, final Object data, final Object result) {
     57         final TestChatAction test = (TestChatAction) action;
     58         assertNotSame(test.dontRelyOnMe, dontRelyOnMe);
     59         // This will be true - but only briefly
     60         assertEquals(test.dontRelyOnMe, becauseIChange);
     61 
     62         final ResultTracker tracker = (ResultTracker) data;
     63         tracker.completionResult = result;
     64         synchronized(tracker) {
     65             tracker.notifyAll();
     66         }
     67     }
     68 
     69     @Override
     70     public void onActionFailed(final ActionMonitor monitor, final Action action,
     71             final Object data, final Object result) {
     72         final TestChatAction test = (TestChatAction) action;
     73         assertNotSame(test.dontRelyOnMe, dontRelyOnMe);
     74         // This will be true - but only briefly
     75         assertEquals(test.dontRelyOnMe, becauseIChange);
     76 
     77         final ResultTracker tracker = (ResultTracker) data;
     78         tracker.completionResult = result;
     79         synchronized(tracker) {
     80             tracker.notifyAll();
     81         }
     82     }
     83 
     84     /**
     85      * For a dummy action verify that the service intent is constructed and queued correctly and
     86      * that when that intent is processed it actually executes the action.
     87      */
     88     public void testChatServiceCreatesIntentAndExecutesAction() {
     89         final ResultTracker tracker = new ResultTracker();
     90 
     91         final TestChatActionMonitor monitor = new TestChatActionMonitor(null, tracker, this, this);
     92         final TestChatAction action = new TestChatAction(monitor.getActionKey(), parameter);
     93 
     94         action.dontRelyOnMe = dontRelyOnMe;
     95         assertFalse("Expect service initially stopped", mServiceStarted);
     96 
     97         action.start(monitor);
     98 
     99         assertTrue("Expect service started", mServiceStarted);
    100 
    101         final ArrayList<Intent> intents = mContext.extractIntents();
    102         assertNotNull(intents);
    103         assertEquals("Expect to see 1 server request queued", 1, intents.size());
    104         final Intent intent = intents.get(0);
    105         assertEquals("Check pid", intent.getIntExtra(WakeLockHelper.EXTRA_CALLING_PID, 0),
    106                 Process.myPid());
    107         assertEquals("Check opcode", intent.getIntExtra(ActionServiceImpl.EXTRA_OP_CODE, 0),
    108                 ActionServiceImpl.OP_START_ACTION);
    109         assertTrue("Check wakelock held", ActionServiceImpl.sWakeLock.isHeld(intent));
    110 
    111         synchronized(tracker) {
    112             try {
    113                 this.startService(intent);
    114                 // Wait for callback across threads
    115                 tracker.wait(2000);
    116             } catch (final InterruptedException e) {
    117                 assertTrue("Interrupted waiting for response processing", false);
    118             }
    119         }
    120 
    121         assertEquals("Expect three states ", mStates.size(), 3);
    122         assertEquals("State-0 should be STATE_QUEUED", (int)mStates.get(0),
    123                 ActionMonitor.STATE_QUEUED);
    124         assertEquals("State-1 should be STATE_EXECUTING", (int)mStates.get(1),
    125                 ActionMonitor.STATE_EXECUTING);
    126         assertEquals("State-2 should be STATE_COMPLETE", (int)mStates.get(2),
    127                 ActionMonitor.STATE_COMPLETE);
    128         // TODO: Should find a way to reliably wait, this is a bit of a hack
    129         if (ActionServiceImpl.sWakeLock.isHeld(intent)) {
    130             Log.d(TAG, "ActionServiceTest: waiting for wakelock release");
    131             try {
    132                 Thread.sleep(100);
    133             } catch (final InterruptedException e) {
    134             }
    135         }
    136         assertFalse("Check wakelock released", ActionServiceImpl.sWakeLock.isHeld(intent));
    137     }
    138 
    139     StubBackgroundWorker mWorker;
    140     FakeContext mContext;
    141     StubLoader mLoader;
    142     ActionService mService;
    143 
    144     ArrayList<Integer> mStates;
    145 
    146     private static final String parameter = "parameter";
    147     private static final Object dontRelyOnMe = "dontRelyOnMe";
    148     private static final Object becauseIChange = "becauseIChange";
    149     private static final Object executeActionResult = "executeActionResult";
    150     private static final Object processResponseResult = "processResponseResult";
    151     private static final Object processFailureResult = "processFailureResult";
    152 
    153     public ActionServiceTest() {
    154         super(ActionServiceImpl.class);
    155     }
    156 
    157     @Override
    158     public void setUp() throws Exception {
    159         super.setUp();
    160         Log.d(TAG, "ChatActionTest setUp");
    161 
    162         sLooper = Looper.myLooper();
    163 
    164         mWorker = new StubBackgroundWorker();
    165         mContext = new FakeContext(getContext(), this);
    166         FakeFactory.registerWithFakeContext(getContext(),mContext)
    167                 .withDataModel(new FakeDataModel(mContext)
    168                 .withBackgroundWorkerForActionService(mWorker)
    169                 .withActionService(new ActionService())
    170                 .withConnectivityUtil(new StubConnectivityUtil(mContext)));
    171 
    172         mStates = new ArrayList<Integer>();
    173         setContext(Factory.get().getApplicationContext());
    174     }
    175 
    176     @Override
    177     public String getServiceClassName() {
    178         return ActionServiceImpl.class.getName();
    179     }
    180 
    181     boolean mServiceStarted = false;
    182 
    183     @Override
    184     public void startServiceForStub(final Intent intent) {
    185         // Do nothing until later
    186         assertFalse(mServiceStarted);
    187         mServiceStarted = true;
    188     }
    189 
    190     @Override
    191     public void onStartCommandForStub(final Intent intent, final int flags, final int startId) {
    192         assertTrue(mServiceStarted);
    193     }
    194 
    195     private static Looper sLooper;
    196     public static void assertRunsOnOtherThread() {
    197         assertTrue (Looper.myLooper() != Looper.getMainLooper());
    198         assertTrue (Looper.myLooper() != sLooper);
    199     }
    200 
    201     public static class TestChatAction extends Action implements Parcelable {
    202         public static String RESPONSE_TEST = "response_test";
    203         public static String KEY_PARAMETER = "parameter";
    204 
    205         protected TestChatAction(final String key, final String parameter) {
    206             super(key);
    207             this.actionParameters.putString(KEY_PARAMETER, parameter);
    208         }
    209 
    210         transient Object dontRelyOnMe;
    211 
    212         /**
    213          * Process the action locally - runs on service thread
    214          */
    215         @Override
    216         protected Object executeAction() {
    217             this.dontRelyOnMe = becauseIChange;
    218             assertRunsOnOtherThread();
    219             return executeActionResult;
    220         }
    221 
    222         /**
    223          * Process the response from the server - runs on service thread
    224          */
    225         @Override
    226         protected Object processBackgroundResponse(final Bundle response) {
    227             assertRunsOnOtherThread();
    228             return processResponseResult;
    229         }
    230 
    231         /**
    232          * Called in case of failures when sending requests - runs on service thread
    233          */
    234         @Override
    235         protected Object processBackgroundFailure() {
    236             assertRunsOnOtherThread();
    237             return processFailureResult;
    238         }
    239 
    240         private TestChatAction(final Parcel in) {
    241             super(in);
    242         }
    243 
    244         public static final Parcelable.Creator<TestChatAction> CREATOR
    245                 = new Parcelable.Creator<TestChatAction>() {
    246             @Override
    247             public TestChatAction createFromParcel(final Parcel in) {
    248                 return new TestChatAction(in);
    249             }
    250 
    251             @Override
    252             public TestChatAction[] newArray(final int size) {
    253                 return new TestChatAction[size];
    254             }
    255         };
    256 
    257         @Override
    258         public void writeToParcel(final Parcel parcel, final int flags) {
    259             writeActionToParcel(parcel, flags);
    260         }
    261     }
    262 
    263     /**
    264      * An operation that notifies a listener upon state changes, execution and completion
    265      */
    266     public static class TestChatActionMonitor extends ActionMonitor {
    267         public TestChatActionMonitor(final String baseKey, final Object data,
    268                 final ActionStateChangedListener listener, final ActionCompletedListener executed) {
    269             super(STATE_CREATED, Action.generateUniqueActionKey(baseKey), data);
    270             setStateChangedListener(listener);
    271             setCompletedListener(executed);
    272             assertEquals("Initial state should be STATE_CREATED", mState, STATE_CREATED);
    273         }
    274     }
    275 }
    276