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.util.Log; 25 26 import androidx.test.filters.MediumTest; 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.StubLoader; 39 40 import java.util.ArrayList; 41 42 @MediumTest 43 public class ActionServiceTest extends BugleServiceTestCase<ActionServiceImpl> 44 implements FakeContextHost, ActionStateChangedListener, ActionCompletedListener { 45 private static final String TAG = "ActionServiceTest"; 46 47 @Override onActionStateChanged(final Action action, final int state)48 public void onActionStateChanged(final Action action, final int state) { 49 mStates.add(state); 50 } 51 52 @Override onActionSucceeded(final ActionMonitor monitor, final Action action, final Object data, final Object result)53 public void onActionSucceeded(final ActionMonitor monitor, 54 final Action action, final Object data, final Object result) { 55 final TestChatAction test = (TestChatAction) action; 56 assertNotSame(test.dontRelyOnMe, dontRelyOnMe); 57 // This will be true - but only briefly 58 assertEquals(test.dontRelyOnMe, becauseIChange); 59 60 final ResultTracker tracker = (ResultTracker) data; 61 tracker.completionResult = result; 62 synchronized(tracker) { 63 tracker.notifyAll(); 64 } 65 } 66 67 @Override onActionFailed(final ActionMonitor monitor, final Action action, final Object data, final Object result)68 public void onActionFailed(final ActionMonitor monitor, final Action action, 69 final Object data, final Object result) { 70 final TestChatAction test = (TestChatAction) action; 71 assertNotSame(test.dontRelyOnMe, dontRelyOnMe); 72 // This will be true - but only briefly 73 assertEquals(test.dontRelyOnMe, becauseIChange); 74 75 final ResultTracker tracker = (ResultTracker) data; 76 tracker.completionResult = result; 77 synchronized(tracker) { 78 tracker.notifyAll(); 79 } 80 } 81 82 /** 83 * For a chat action verify that the service intent is constructed and queued correctly and 84 * that when that intent is processed it actually executes the action. 85 */ testChatServiceCreatesIntentAndExecutesAction()86 public void testChatServiceCreatesIntentAndExecutesAction() { 87 final ResultTracker tracker = new ResultTracker(); 88 89 final TestChatActionMonitor monitor = new TestChatActionMonitor(null, tracker, this, this); 90 final TestChatAction action = new TestChatAction(monitor.getActionKey(), parameter); 91 92 action.dontRelyOnMe = dontRelyOnMe; 93 assertFalse("Expect service initially stopped", mServiceStarted); 94 95 synchronized(mWorker) { 96 try { 97 action.start(monitor); 98 // Wait for callback across threads 99 mWorker.wait(2000); 100 mServiceStarted = true; 101 } catch (final InterruptedException e) { 102 assertTrue("Interrupted waiting for execution", false); 103 } 104 } 105 106 assertTrue("Expect service started", mServiceStarted); 107 108 assertEquals("Expect three states ", mStates.size(), 3); 109 assertEquals("State-0 should be STATE_QUEUED", (int)mStates.get(0), 110 ActionMonitor.STATE_QUEUED); 111 assertEquals("State-1 should be STATE_EXECUTING", (int)mStates.get(1), 112 ActionMonitor.STATE_EXECUTING); 113 assertEquals("State-2 should be STATE_COMPLETE", (int)mStates.get(2), 114 ActionMonitor.STATE_COMPLETE); 115 } 116 117 StubBackgroundWorker mWorker; 118 FakeContext mContext; 119 StubLoader mLoader; 120 ActionService mService; 121 122 ArrayList<Integer> mStates; 123 124 private static final String parameter = "parameter"; 125 private static final Object dontRelyOnMe = "dontRelyOnMe"; 126 private static final Object becauseIChange = "becauseIChange"; 127 private static final Object executeActionResult = "executeActionResult"; 128 private static final Object processResponseResult = "processResponseResult"; 129 private static final Object processFailureResult = "processFailureResult"; 130 ActionServiceTest()131 public ActionServiceTest() { 132 super(ActionServiceImpl.class); 133 } 134 135 @Override setUp()136 public void setUp() throws Exception { 137 super.setUp(); 138 Log.d(TAG, "ChatActionTest setUp"); 139 140 sLooper = Looper.myLooper(); 141 142 mWorker = new StubBackgroundWorker(); 143 mContext = new FakeContext(getContext(), this); 144 FakeFactory.registerWithFakeContext(getContext(),mContext) 145 .withDataModel(new FakeDataModel(mContext) 146 .withBackgroundWorkerForActionService(mWorker) 147 .withActionService(new ActionService())); 148 149 mStates = new ArrayList<Integer>(); 150 setContext(Factory.get().getApplicationContext()); 151 } 152 153 @Override getServiceClassName()154 public String getServiceClassName() { 155 return ActionServiceImpl.class.getName(); 156 } 157 158 boolean mServiceStarted = false; 159 160 @Override startServiceForStub(final Intent intent)161 public void startServiceForStub(final Intent intent) { 162 // Do nothing until later 163 assertFalse(mServiceStarted); 164 mServiceStarted = true; 165 } 166 167 @Override onStartCommandForStub(final Intent intent, final int flags, final int startId)168 public void onStartCommandForStub(final Intent intent, final int flags, final int startId) { 169 assertTrue(mServiceStarted); 170 } 171 172 private static Looper sLooper; assertRunsOnOtherThread()173 public static void assertRunsOnOtherThread() { 174 assertTrue (Looper.myLooper() != Looper.getMainLooper()); 175 assertTrue (Looper.myLooper() != sLooper); 176 } 177 178 public static class TestChatAction extends Action implements Parcelable { 179 public static String RESPONSE_TEST = "response_test"; 180 public static String KEY_PARAMETER = "parameter"; 181 TestChatAction(final String key, final String parameter)182 protected TestChatAction(final String key, final String parameter) { 183 super(key); 184 this.actionParameters.putString(KEY_PARAMETER, parameter); 185 } 186 187 transient Object dontRelyOnMe; 188 189 /** 190 * Process the action locally - runs on service thread 191 */ 192 @Override executeAction()193 protected Object executeAction() { 194 this.dontRelyOnMe = becauseIChange; 195 assertRunsOnOtherThread(); 196 return executeActionResult; 197 } 198 199 /** 200 * Process the response from the server - runs on service thread 201 */ 202 @Override processBackgroundResponse(final Bundle response)203 protected Object processBackgroundResponse(final Bundle response) { 204 assertRunsOnOtherThread(); 205 return processResponseResult; 206 } 207 208 /** 209 * Called in case of failures when sending requests - runs on service thread 210 */ 211 @Override processBackgroundFailure()212 protected Object processBackgroundFailure() { 213 assertRunsOnOtherThread(); 214 return processFailureResult; 215 } 216 TestChatAction(final Parcel in)217 private TestChatAction(final Parcel in) { 218 super(in); 219 } 220 221 public static final Parcelable.Creator<TestChatAction> CREATOR 222 = new Parcelable.Creator<TestChatAction>() { 223 @Override 224 public TestChatAction createFromParcel(final Parcel in) { 225 return new TestChatAction(in); 226 } 227 228 @Override 229 public TestChatAction[] newArray(final int size) { 230 return new TestChatAction[size]; 231 } 232 }; 233 234 @Override writeToParcel(final Parcel parcel, final int flags)235 public void writeToParcel(final Parcel parcel, final int flags) { 236 writeActionToParcel(parcel, flags); 237 } 238 } 239 240 /** 241 * An operation that notifies a listener upon state changes, execution and completion 242 */ 243 public static class TestChatActionMonitor extends ActionMonitor { TestChatActionMonitor(final String baseKey, final Object data, final ActionStateChangedListener listener, final ActionCompletedListener executed)244 public TestChatActionMonitor(final String baseKey, final Object data, 245 final ActionStateChangedListener listener, final ActionCompletedListener executed) { 246 super(STATE_CREATED, Action.generateUniqueActionKey(baseKey), data); 247 setStateChangedListener(listener); 248 setCompletedListener(executed); 249 assertEquals("Initial state should be STATE_CREATED", mState, STATE_CREATED); 250 } 251 } 252 } 253