/* * Copyright (C) 2015 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.messaging.datamodel.action; import android.content.Intent; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; import android.test.suitebuilder.annotation.MediumTest; import android.util.Log; import com.android.messaging.BugleTestCase; import com.android.messaging.Factory; import com.android.messaging.FakeContext; import com.android.messaging.FakeContext.FakeContextHost; import com.android.messaging.FakeFactory; import com.android.messaging.datamodel.BugleServiceTestCase; import com.android.messaging.datamodel.DataModel; import com.android.messaging.datamodel.FakeDataModel; import com.android.messaging.datamodel.action.ActionMonitor.ActionCompletedListener; import com.android.messaging.datamodel.action.ActionMonitor.ActionExecutedListener; import com.android.messaging.datamodel.action.ActionTestHelpers.ResultTracker; import com.android.messaging.datamodel.action.ActionTestHelpers.StubBackgroundWorker; import com.android.messaging.datamodel.action.ActionTestHelpers.StubLoader; import java.util.ArrayList; @MediumTest public class ActionServiceSystemTest extends BugleServiceTestCase implements ActionCompletedListener, ActionExecutedListener, FakeContextHost { private static final String TAG = "ActionServiceSystemTest"; static { // Set flag during loading of test cases to prevent application initialization starting BugleTestCase.setTestsRunning(); } @Override public void onActionSucceeded(final ActionMonitor monitor, final Action action, final Object data, final Object result) { final TestChatAction test = (TestChatAction) action; assertEquals("Expect correct action parameter", parameter, test.parameter); final ResultTracker tracker = (ResultTracker) data; tracker.completionResult = result; synchronized(tracker) { tracker.notifyAll(); } } @Override public void onActionFailed(final ActionMonitor monitor, final Action action, final Object data, final Object result) { final TestChatAction test = (TestChatAction) action; assertEquals("Expect correct action parameter", parameter, test.parameter); final ResultTracker tracker = (ResultTracker) data; tracker.completionResult = result; synchronized(tracker) { tracker.notifyAll(); } } @Override public void onActionExecuted(final ActionMonitor monitor, final Action action, final Object data, final Object result) { final TestChatAction test = (TestChatAction) action; assertEquals("Expect correct action parameter", parameter, test.parameter); final ResultTracker tracker = (ResultTracker) data; tracker.executionResult = result; } public ActionServiceSystemTest() { super(ActionServiceImpl.class); } public void testChatActionSucceeds() { final ResultTracker tracker = new ResultTracker(); final ActionService service = DataModel.get().getActionService(); final TestChatActionMonitor monitor = new TestChatActionMonitor(null, tracker, this, this); final TestChatAction initial = new TestChatAction(monitor.getActionKey(), parameter); assertNull("Expect completion result to start null", tracker.completionResult); assertNull("Expect execution result to start null", tracker.executionResult); final Parcel parcel = Parcel.obtain(); parcel.writeParcelable(initial, 0); parcel.setDataPosition(0); final TestChatAction action = parcel.readParcelable(mContext.getClassLoader()); synchronized(mWorker) { try { action.start(monitor); // Wait for callback across threads mWorker.wait(2000); } catch (final InterruptedException e) { assertTrue("Interrupted waiting for execution", false); } } assertEquals("Expect to see 1 server request queued", 1, mWorker.getRequestsMade().size()); final Action request = mWorker.getRequestsMade().get(0); assertTrue("Expect Test type", request instanceof TestChatAction); final Bundle response = new Bundle(); response.putString(TestChatAction.RESPONSE_TEST, processResponseResult); synchronized(tracker) { try { request.markBackgroundWorkStarting(); request.markBackgroundWorkQueued(); request.markBackgroundWorkStarting(); request.markBackgroundCompletionQueued(); service.handleResponseFromBackgroundWorker(request, response); // Wait for callback across threads tracker.wait(2000); } catch (final InterruptedException e) { assertTrue("Interrupted waiting for response processing", false); } } // TODO //assertEquals("Expect execution result set", executeActionResult, tracker.executionResult); assertEquals("Expect completion result set", processResponseResult, tracker.completionResult); } public void testChatActionFails() { final ResultTracker tracker = new ResultTracker(); final ActionService service = DataModel.get().getActionService(); final TestChatActionMonitor monitor = new TestChatActionMonitor(null, tracker, this, this); final TestChatAction action = new TestChatAction(monitor.getActionKey(), parameter); assertNull("Expect completion result to start null", tracker.completionResult); assertNull("Expect execution result to start null", tracker.executionResult); synchronized(mWorker) { try { action.start(monitor); // Wait for callback across threads mWorker.wait(2000); } catch (final InterruptedException e) { assertTrue("Interrupted waiting for requests", false); } } assertEquals("Expect to see 1 server request queued", 1, mWorker.getRequestsMade().size()); final Action request = mWorker.getRequestsMade().get(0); assertTrue("Expect Test type", request instanceof TestChatAction); synchronized(tracker) { try { request.markBackgroundWorkStarting(); request.markBackgroundWorkQueued(); request.markBackgroundWorkStarting(); request.markBackgroundCompletionQueued(); service.handleFailureFromBackgroundWorker(request, new Exception("It went wrong")); // Wait for callback across threads tracker.wait(2000); } catch (final InterruptedException e) { assertTrue("Interrupted waiting for response processing", false); } } // TODO //assertEquals("Expect execution result set", executeActionResult, tracker.executionResult); assertEquals("Expect completion result set", processFailureResult, tracker.completionResult); } public void testChatActionNoMonitor() { final ActionService service = DataModel.get().getActionService(); final TestChatAction action = new TestChatAction(Action.generateUniqueActionKey(null), parameter); synchronized(mWorker) { try { action.start(); // Wait for callback across threads mWorker.wait(2000); } catch (final InterruptedException e) { assertTrue("Interrupted waiting for execution", false); } } assertEquals("Expect to see 1 server request queued", 1, mWorker.getRequestsMade().size()); Action request = mWorker.getRequestsMade().get(0); assertTrue("Expect Test type", request instanceof TestChatAction); final Bundle response = new Bundle(); response.putString(TestChatAction.RESPONSE_TEST, processResponseResult); synchronized(mWorker) { try { service.handleResponseFromBackgroundWorker(request, response); // Wait for callback across threads mWorker.wait(2000); } catch (final InterruptedException e) { assertTrue("Interrupted waiting for response processing", false); } } assertEquals("Expect to see second server request queued", 2, mWorker.getRequestsMade().size()); request = mWorker.getRequestsMade().get(1); assertTrue("Expect other type", request instanceof TestChatActionOther); } public void testChatActionUnregisterListener() { final ResultTracker tracker = new ResultTracker(); final ActionService service = DataModel.get().getActionService(); final TestChatActionMonitor monitor = new TestChatActionMonitor(null, tracker, this, this); final TestChatAction action = new TestChatAction(monitor.getActionKey(), parameter); assertNull("Expect completion result to start null", tracker.completionResult); assertNull("Expect execution result to start null", tracker.executionResult); synchronized(mWorker) { try { action.start(monitor); // Wait for callback across threads mWorker.wait(2000); } catch (final InterruptedException e) { assertTrue("Interrupted waiting for execution", false); } } assertEquals("Expect to see 1 server request queued", 1, mWorker.getRequestsMade().size()); final Action request = mWorker.getRequestsMade().get(0); assertTrue("Expect Test type", request instanceof TestChatAction); monitor.unregister(); final Bundle response = new Bundle(); synchronized(mWorker) { try { request.markBackgroundWorkStarting(); request.markBackgroundWorkQueued(); request.markBackgroundWorkStarting(); request.markBackgroundCompletionQueued(); service.handleResponseFromBackgroundWorker(request, response); // Wait for callback across threads mWorker.wait(2000); } catch (final InterruptedException e) { assertTrue("Interrupted waiting for response processing", false); } } //assertEquals("Expect execution result set", executeActionResult, tracker.executionResult); assertEquals("Expect completion never called", null, tracker.completionResult); } StubBackgroundWorker mWorker; FakeContext mContext; StubLoader mLoader; private static final String parameter = "parameter"; private static final Object executeActionResult = "executeActionResult"; private static final String processResponseResult = "processResponseResult"; private static final Object processFailureResult = "processFailureResult"; @Override public void setUp() throws Exception { super.setUp(); Log.d(TAG, "ChatActionTest setUp"); mContext = new FakeContext(getContext(), this); mWorker = new StubBackgroundWorker(); FakeFactory.registerWithFakeContext(getContext(), mContext) .withDataModel(new FakeDataModel(mContext) .withBackgroundWorkerForActionService(mWorker) .withActionService(new ActionService())); mLoader = new StubLoader(); setContext(Factory.get().getApplicationContext()); } @Override public String getServiceClassName() { return ActionServiceImpl.class.getName(); } @Override public void startServiceForStub(final Intent intent) { this.startService(intent); } @Override public void onStartCommandForStub(final Intent intent, final int flags, final int startId) { this.getService().onStartCommand(intent, flags, startId); } public static class TestChatAction extends Action implements Parcelable { public static String RESPONSE_TEST = "response_test"; public static String KEY_PARAMETER = "parameter"; protected TestChatAction(final String key, final String parameter) { super(key); this.actionParameters.putString(KEY_PARAMETER, parameter); // Cache parameter as a member variable this.parameter = parameter; } // An example parameter public final String parameter; /** * Process the action locally - runs on datamodel service thread */ @Override protected Object executeAction() { requestBackgroundWork(); return executeActionResult; } /** * Process the response from the server - runs on datamodel service thread */ @Override protected Object processBackgroundResponse(final Bundle response) { requestBackgroundWork(new TestChatActionOther(null, parameter)); return response.get(RESPONSE_TEST); } /** * Called in case of failures when sending requests - runs on datamodel service thread */ @Override protected Object processBackgroundFailure() { return processFailureResult; } private TestChatAction(final Parcel in) { super(in); // Cache parameter as a member variable parameter = actionParameters.getString(KEY_PARAMETER); } public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { @Override public TestChatAction createFromParcel(final Parcel in) { return new TestChatAction(in); } @Override public TestChatAction[] newArray(final int size) { return new TestChatAction[size]; } }; @Override public void writeToParcel(final Parcel parcel, final int flags) { writeActionToParcel(parcel, flags); } } public static class TestChatActionOther extends Action implements Parcelable { protected TestChatActionOther(final String key, final String parameter) { super(generateUniqueActionKey(key)); this.parameter = parameter; } public final String parameter; private TestChatActionOther(final Parcel in) { super(in); parameter = in.readString(); } public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { @Override public TestChatActionOther createFromParcel(final Parcel in) { return new TestChatActionOther(in); } @Override public TestChatActionOther[] newArray(final int size) { return new TestChatActionOther[size]; } }; @Override public void writeToParcel(final Parcel parcel, final int flags) { writeActionToParcel(parcel, flags); parcel.writeString(parameter); } } /** * An operation that notifies a listener upon completion */ public static class TestChatActionMonitor extends ActionMonitor { /** * Create action state wrapping an BlockUserAction instance * @param account - account in which to block the user * @param baseKey - suggested action key from BlockUserAction * @param data - optional action specific data that is handed back to listener * @param listener - action completed listener */ public TestChatActionMonitor(final String baseKey, final Object data, final ActionCompletedListener completed, final ActionExecutedListener executed) { super(STATE_CREATED, Action.generateUniqueActionKey(baseKey), data); setCompletedListener(completed); setExecutedListener(executed); } } }