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