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