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.Parcel; 22 import android.os.Parcelable; 23 import android.util.Log; 24 25 import androidx.test.filters.MediumTest; 26 27 import com.android.messaging.BugleTestCase; 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.DataModel; 34 import com.android.messaging.datamodel.FakeDataModel; 35 import com.android.messaging.datamodel.action.ActionMonitor.ActionCompletedListener; 36 import com.android.messaging.datamodel.action.ActionMonitor.ActionExecutedListener; 37 import com.android.messaging.datamodel.action.ActionTestHelpers.ResultTracker; 38 import com.android.messaging.datamodel.action.ActionTestHelpers.StubBackgroundWorker; 39 import com.android.messaging.datamodel.action.ActionTestHelpers.StubLoader; 40 41 @MediumTest 42 public class ActionServiceSystemTest extends BugleServiceTestCase<ActionServiceImpl> 43 implements ActionCompletedListener, ActionExecutedListener, FakeContextHost { 44 private static final String TAG = "ActionServiceSystemTest"; 45 46 static { 47 // Set flag during loading of test cases to prevent application initialization starting BugleTestCase.setTestsRunning()48 BugleTestCase.setTestsRunning(); 49 } 50 51 @Override onActionSucceeded(final ActionMonitor monitor, final Action action, final Object data, final Object result)52 public void onActionSucceeded(final ActionMonitor monitor, 53 final Action action, final Object data, final Object result) { 54 final TestChatAction test = (TestChatAction) action; 55 assertEquals("Expect correct action parameter", parameter, test.parameter); 56 final ResultTracker tracker = (ResultTracker) data; 57 tracker.completionResult = result; 58 synchronized(tracker) { 59 tracker.notifyAll(); 60 } 61 } 62 63 @Override onActionFailed(final ActionMonitor monitor, final Action action, final Object data, final Object result)64 public void onActionFailed(final ActionMonitor monitor, final Action action, 65 final Object data, final Object result) { 66 final TestChatAction test = (TestChatAction) action; 67 assertEquals("Expect correct action parameter", parameter, test.parameter); 68 final ResultTracker tracker = (ResultTracker) data; 69 tracker.completionResult = result; 70 synchronized(tracker) { 71 tracker.notifyAll(); 72 } 73 } 74 75 @Override onActionExecuted(final ActionMonitor monitor, final Action action, final Object data, final Object result)76 public void onActionExecuted(final ActionMonitor monitor, final Action action, 77 final Object data, final Object result) { 78 final TestChatAction test = (TestChatAction) action; 79 assertEquals("Expect correct action parameter", parameter, test.parameter); 80 final ResultTracker tracker = (ResultTracker) data; 81 tracker.executionResult = result; 82 } 83 ActionServiceSystemTest()84 public ActionServiceSystemTest() { 85 super(ActionServiceImpl.class); 86 } 87 testChatActionSucceeds()88 public void testChatActionSucceeds() { 89 final ResultTracker tracker = new ResultTracker(); 90 91 final ActionService service = DataModel.get().getActionService(); 92 final TestChatActionMonitor monitor = new TestChatActionMonitor(null, tracker, this, this); 93 final TestChatAction initial = new TestChatAction(monitor.getActionKey(), parameter); 94 95 assertNull("Expect completion result to start null", tracker.completionResult); 96 assertNull("Expect execution result to start null", tracker.executionResult); 97 98 final Parcel parcel = Parcel.obtain(); 99 parcel.writeParcelable(initial, 0); 100 parcel.setDataPosition(0); 101 final TestChatAction action = parcel.readParcelable(mContext.getClassLoader()); 102 103 synchronized(mWorker) { 104 try { 105 action.start(monitor); 106 // Wait for callback across threads 107 mWorker.wait(2000); 108 } catch (final InterruptedException e) { 109 assertTrue("Interrupted waiting for execution", false); 110 } 111 } 112 113 assertEquals("Expect to see 1 server request queued", 1, 114 mWorker.getRequestsMade().size()); 115 final Action request = mWorker.getRequestsMade().get(0); 116 assertTrue("Expect Test type", request instanceof TestChatAction); 117 118 final Bundle response = new Bundle(); 119 response.putString(TestChatAction.RESPONSE_TEST, processResponseResult); 120 synchronized(tracker) { 121 try { 122 request.markBackgroundWorkStarting(); 123 request.markBackgroundWorkQueued(); 124 125 request.markBackgroundWorkStarting(); 126 request.markBackgroundCompletionQueued(); 127 service.handleResponseFromBackgroundWorker(request, response); 128 // Wait for callback across threads 129 tracker.wait(2000); 130 } catch (final InterruptedException e) { 131 assertTrue("Interrupted waiting for response processing", false); 132 } 133 } 134 135 // TODO 136 //assertEquals("Expect execution result set", executeActionResult, tracker.executionResult); 137 assertEquals("Expect completion result set", processResponseResult, 138 tracker.completionResult); 139 } 140 testChatActionFails()141 public void testChatActionFails() { 142 final ResultTracker tracker = new ResultTracker(); 143 144 final ActionService service = DataModel.get().getActionService(); 145 final TestChatActionMonitor monitor = new TestChatActionMonitor(null, tracker, this, this); 146 final TestChatAction action = new TestChatAction(monitor.getActionKey(), parameter); 147 148 assertNull("Expect completion result to start null", tracker.completionResult); 149 assertNull("Expect execution result to start null", tracker.executionResult); 150 151 synchronized(mWorker) { 152 try { 153 action.start(monitor); 154 // Wait for callback across threads 155 mWorker.wait(2000); 156 } catch (final InterruptedException e) { 157 assertTrue("Interrupted waiting for requests", false); 158 } 159 } 160 161 assertEquals("Expect to see 1 server request queued", 1, 162 mWorker.getRequestsMade().size()); 163 final Action request = mWorker.getRequestsMade().get(0); 164 assertTrue("Expect Test type", request instanceof TestChatAction); 165 166 synchronized(tracker) { 167 try { 168 request.markBackgroundWorkStarting(); 169 request.markBackgroundWorkQueued(); 170 171 request.markBackgroundWorkStarting(); 172 request.markBackgroundCompletionQueued(); 173 service.handleFailureFromBackgroundWorker(request, new Exception("It went wrong")); 174 // Wait for callback across threads 175 tracker.wait(2000); 176 } catch (final InterruptedException e) { 177 assertTrue("Interrupted waiting for response processing", false); 178 } 179 } 180 181 // TODO 182 //assertEquals("Expect execution result set", executeActionResult, tracker.executionResult); 183 assertEquals("Expect completion result set", processFailureResult, 184 tracker.completionResult); 185 } 186 testChatActionNoMonitor()187 public void testChatActionNoMonitor() { 188 final ActionService service = DataModel.get().getActionService(); 189 final TestChatAction action = 190 new TestChatAction(Action.generateUniqueActionKey(null), parameter); 191 192 synchronized(mWorker) { 193 try { 194 action.start(); 195 // Wait for callback across threads 196 mWorker.wait(2000); 197 } catch (final InterruptedException e) { 198 assertTrue("Interrupted waiting for execution", false); 199 } 200 } 201 202 assertEquals("Expect to see 1 server request queued", 1, 203 mWorker.getRequestsMade().size()); 204 Action request = mWorker.getRequestsMade().get(0); 205 assertTrue("Expect Test type", request instanceof TestChatAction); 206 207 final Bundle response = new Bundle(); 208 response.putString(TestChatAction.RESPONSE_TEST, processResponseResult); 209 synchronized(mWorker) { 210 try { 211 service.handleResponseFromBackgroundWorker(request, response); 212 // Wait for callback across threads 213 mWorker.wait(2000); 214 } catch (final InterruptedException e) { 215 assertTrue("Interrupted waiting for response processing", false); 216 } 217 } 218 219 assertEquals("Expect to see second server request queued", 220 2, mWorker.getRequestsMade().size()); 221 request = mWorker.getRequestsMade().get(1); 222 assertTrue("Expect other type", 223 request instanceof TestChatActionOther); 224 } 225 testChatActionUnregisterListener()226 public void testChatActionUnregisterListener() { 227 final ResultTracker tracker = new ResultTracker(); 228 229 final ActionService service = DataModel.get().getActionService(); 230 final TestChatActionMonitor monitor = new TestChatActionMonitor(null, tracker, this, this); 231 final TestChatAction action = new TestChatAction(monitor.getActionKey(), parameter); 232 233 assertNull("Expect completion result to start null", tracker.completionResult); 234 assertNull("Expect execution result to start null", tracker.executionResult); 235 236 synchronized(mWorker) { 237 try { 238 action.start(monitor); 239 // Wait for callback across threads 240 mWorker.wait(2000); 241 } catch (final InterruptedException e) { 242 assertTrue("Interrupted waiting for execution", false); 243 } 244 } 245 246 assertEquals("Expect to see 1 server request queued", 1, 247 mWorker.getRequestsMade().size()); 248 final Action request = mWorker.getRequestsMade().get(0); 249 assertTrue("Expect Test type", request instanceof TestChatAction); 250 251 monitor.unregister(); 252 253 final Bundle response = new Bundle(); 254 synchronized(mWorker) { 255 try { 256 request.markBackgroundWorkStarting(); 257 request.markBackgroundWorkQueued(); 258 259 request.markBackgroundWorkStarting(); 260 request.markBackgroundCompletionQueued(); 261 service.handleResponseFromBackgroundWorker(request, response); 262 // Wait for callback across threads 263 mWorker.wait(2000); 264 } catch (final InterruptedException e) { 265 assertTrue("Interrupted waiting for response processing", false); 266 } 267 } 268 269 //assertEquals("Expect execution result set", executeActionResult, tracker.executionResult); 270 assertEquals("Expect completion never called", null, tracker.completionResult); 271 } 272 273 StubBackgroundWorker mWorker; 274 FakeContext mContext; 275 StubLoader mLoader; 276 277 private static final String parameter = "parameter"; 278 private static final Object executeActionResult = "executeActionResult"; 279 private static final String processResponseResult = "processResponseResult"; 280 private static final Object processFailureResult = "processFailureResult"; 281 282 @Override setUp()283 public void setUp() throws Exception { 284 super.setUp(); 285 Log.d(TAG, "ChatActionTest setUp"); 286 287 mContext = new FakeContext(getContext(), this); 288 mWorker = new StubBackgroundWorker(); 289 FakeFactory.registerWithFakeContext(getContext(), mContext) 290 .withDataModel(new FakeDataModel(mContext) 291 .withBackgroundWorkerForActionService(mWorker) 292 .withActionService(new ActionService())); 293 294 mLoader = new StubLoader(); 295 setContext(Factory.get().getApplicationContext()); 296 } 297 298 @Override getServiceClassName()299 public String getServiceClassName() { 300 return ActionServiceImpl.class.getName(); 301 } 302 303 @Override startServiceForStub(final Intent intent)304 public void startServiceForStub(final Intent intent) { 305 this.startService(intent); 306 } 307 308 @Override onStartCommandForStub(final Intent intent, final int flags, final int startId)309 public void onStartCommandForStub(final Intent intent, final int flags, final int startId) { 310 this.getService().onStartCommand(intent, flags, startId); 311 } 312 313 public static class TestChatAction extends Action implements Parcelable { 314 public static String RESPONSE_TEST = "response_test"; 315 public static String KEY_PARAMETER = "parameter"; 316 TestChatAction(final String key, final String parameter)317 protected TestChatAction(final String key, final String parameter) { 318 super(key); 319 this.actionParameters.putString(KEY_PARAMETER, parameter); 320 // Cache parameter as a member variable 321 this.parameter = parameter; 322 } 323 324 // An example parameter 325 public final String parameter; 326 327 /** 328 * Process the action locally - runs on datamodel service thread 329 */ 330 @Override executeAction()331 protected Object executeAction() { 332 requestBackgroundWork(); 333 return executeActionResult; 334 } 335 336 /** 337 * Process the response from the server - runs on datamodel service thread 338 */ 339 @Override processBackgroundResponse(final Bundle response)340 protected Object processBackgroundResponse(final Bundle response) { 341 requestBackgroundWork(new TestChatActionOther(null, parameter)); 342 return response.get(RESPONSE_TEST); 343 } 344 345 /** 346 * Called in case of failures when sending requests - runs on datamodel service thread 347 */ 348 @Override processBackgroundFailure()349 protected Object processBackgroundFailure() { 350 return processFailureResult; 351 } 352 TestChatAction(final Parcel in)353 private TestChatAction(final Parcel in) { 354 super(in); 355 // Cache parameter as a member variable 356 parameter = actionParameters.getString(KEY_PARAMETER); 357 } 358 359 public static final Parcelable.Creator<TestChatAction> CREATOR 360 = new Parcelable.Creator<TestChatAction>() { 361 @Override 362 public TestChatAction createFromParcel(final Parcel in) { 363 return new TestChatAction(in); 364 } 365 366 @Override 367 public TestChatAction[] newArray(final int size) { 368 return new TestChatAction[size]; 369 } 370 }; 371 372 @Override writeToParcel(final Parcel parcel, final int flags)373 public void writeToParcel(final Parcel parcel, final int flags) { 374 writeActionToParcel(parcel, flags); 375 } 376 } 377 378 public static class TestChatActionOther extends Action implements Parcelable { TestChatActionOther(final String key, final String parameter)379 protected TestChatActionOther(final String key, final String parameter) { 380 super(generateUniqueActionKey(key)); 381 this.parameter = parameter; 382 } 383 384 public final String parameter; 385 TestChatActionOther(final Parcel in)386 private TestChatActionOther(final Parcel in) { 387 super(in); 388 parameter = in.readString(); 389 } 390 391 public static final Parcelable.Creator<TestChatActionOther> CREATOR 392 = new Parcelable.Creator<TestChatActionOther>() { 393 @Override 394 public TestChatActionOther createFromParcel(final Parcel in) { 395 return new TestChatActionOther(in); 396 } 397 398 @Override 399 public TestChatActionOther[] newArray(final int size) { 400 return new TestChatActionOther[size]; 401 } 402 }; 403 404 @Override writeToParcel(final Parcel parcel, final int flags)405 public void writeToParcel(final Parcel parcel, final int flags) { 406 writeActionToParcel(parcel, flags); 407 parcel.writeString(parameter); 408 } 409 } 410 411 /** 412 * An operation that notifies a listener upon completion 413 */ 414 public static class TestChatActionMonitor extends ActionMonitor { 415 /** 416 * Create action state wrapping an BlockUserAction instance 417 * @param account - account in which to block the user 418 * @param baseKey - suggested action key from BlockUserAction 419 * @param data - optional action specific data that is handed back to listener 420 * @param listener - action completed listener 421 */ TestChatActionMonitor(final String baseKey, final Object data, final ActionCompletedListener completed, final ActionExecutedListener executed)422 public TestChatActionMonitor(final String baseKey, final Object data, 423 final ActionCompletedListener completed, final ActionExecutedListener executed) { 424 super(STATE_CREATED, Action.generateUniqueActionKey(baseKey), data); 425 setCompletedListener(completed); 426 setExecutedListener(executed); 427 } 428 } 429 } 430