1/*
2 * Copyright (C) 2007 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.calendar;
18
19import com.android.calendar.AsyncQueryService.Operation;
20import com.android.calendar.AsyncQueryServiceHelper.OperationInfo;
21
22import android.content.ComponentName;
23import android.content.ContentProvider;
24import android.content.ContentProviderOperation;
25import android.content.ContentProviderResult;
26import android.content.ContentResolver;
27import android.content.ContentValues;
28import android.content.Context;
29import android.content.Intent;
30import android.content.res.Resources;
31import android.database.Cursor;
32import android.net.Uri;
33import android.os.Handler;
34import android.os.HandlerThread;
35import android.os.Message;
36import android.test.IsolatedContext;
37import android.test.RenamingDelegatingContext;
38import android.test.ServiceTestCase;
39import android.test.mock.MockContentResolver;
40import android.test.mock.MockContext;
41import android.test.mock.MockCursor;
42import android.test.suitebuilder.annotation.LargeTest;
43import android.test.suitebuilder.annotation.SmallTest;
44import android.test.suitebuilder.annotation.Smoke;
45import android.util.Log;
46
47import java.io.File;
48import java.util.ArrayList;
49import java.util.Arrays;
50import java.util.concurrent.Semaphore;
51import java.util.concurrent.TimeUnit;
52
53/**
54 * Unit tests for {@link android.text.format.DateUtils#formatDateRange}.
55 */
56public class AsyncQueryServiceTest extends ServiceTestCase<AsyncQueryServiceHelper> {
57    private static final String TAG = "AsyncQueryServiceTest";
58
59    private static final String AUTHORITY_URI = "content://AsyncQueryAuthority/";
60
61    private static final String AUTHORITY = "AsyncQueryAuthority";
62
63    private static final int MIN_DELAY = 50;
64
65    private static final int BASE_TEST_WAIT_TIME = MIN_DELAY * 5;
66
67    private static int mId = 0;
68
69    private static final String[] TEST_PROJECTION = new String[] {
70            "col1", "col2", "col3"
71    };
72
73    private static final String TEST_SELECTION = "selection";
74
75    private static final String[] TEST_SELECTION_ARGS = new String[] {
76            "arg1", "arg2", "arg3"
77    };
78
79    public AsyncQueryServiceTest() {
80        super(AsyncQueryServiceHelper.class);
81    }
82
83    @Override
84    protected void setUp() throws Exception {
85        super.setUp();
86    }
87
88    private class MockContext2 extends MockContext {
89        @Override
90        public Resources getResources() {
91            return getContext().getResources();
92        }
93
94        @Override
95        public File getDir(String name, int mode) {
96            return getContext().getDir("mockcontext2_+" + name, mode);
97        }
98
99        @Override
100        public Context getApplicationContext() {
101            return this;
102        }
103    }
104
105    @Smoke
106    @SmallTest
107    public void testQuery() throws Exception {
108        int index = 0;
109        final OperationInfo[] work = new OperationInfo[1];
110        work[index] = new OperationInfo();
111        work[index].op = Operation.EVENT_ARG_QUERY;
112
113        work[index].token = ++mId;
114        work[index].cookie = ++mId;
115        work[index].uri = Uri.parse(AUTHORITY_URI + "blah");
116        work[index].projection = TEST_PROJECTION;
117        work[index].selection = TEST_SELECTION;
118        work[index].selectionArgs = TEST_SELECTION_ARGS;
119        work[index].orderBy = "order";
120
121        work[index].delayMillis = 0;
122        work[index].result = new TestCursor();
123
124        TestAsyncQueryService aqs = new TestAsyncQueryService(buildTestContext(work), work);
125        aqs.startQuery(work[index].token, work[index].cookie, work[index].uri,
126                work[index].projection, work[index].selection, work[index].selectionArgs,
127                work[index].orderBy);
128
129        Log.d(TAG, "testQuery Waiting >>>>>>>>>>>");
130        assertEquals("Not all operations were executed.", work.length, aqs
131                .waitForCompletion(BASE_TEST_WAIT_TIME));
132        Log.d(TAG, "testQuery Done <<<<<<<<<<<<<<");
133    }
134
135    @SmallTest
136    public void testInsert() throws Exception {
137        int index = 0;
138        final OperationInfo[] work = new OperationInfo[1];
139        work[index] = new OperationInfo();
140        work[index].op = Operation.EVENT_ARG_INSERT;
141
142        work[index].token = ++mId;
143        work[index].cookie = ++mId;
144        work[index].uri = Uri.parse(AUTHORITY_URI + "blah");
145        work[index].values = new ContentValues();
146        work[index].values.put("key", ++mId);
147
148        work[index].delayMillis = 0;
149        work[index].result = Uri.parse(AUTHORITY_URI + "Result=" + ++mId);
150
151        TestAsyncQueryService aqs = new TestAsyncQueryService(buildTestContext(work), work);
152        aqs.startInsert(work[index].token, work[index].cookie, work[index].uri, work[index].values,
153                work[index].delayMillis);
154
155        Log.d(TAG, "testInsert Waiting >>>>>>>>>>>");
156        assertEquals("Not all operations were executed.", work.length, aqs
157                .waitForCompletion(BASE_TEST_WAIT_TIME));
158        Log.d(TAG, "testInsert Done <<<<<<<<<<<<<<");
159    }
160
161    @SmallTest
162    public void testUpdate() throws Exception {
163        int index = 0;
164        final OperationInfo[] work = new OperationInfo[1];
165        work[index] = new OperationInfo();
166        work[index].op = Operation.EVENT_ARG_UPDATE;
167
168        work[index].token = ++mId;
169        work[index].cookie = ++mId;
170        work[index].uri = Uri.parse(AUTHORITY_URI + ++mId);
171        work[index].values = new ContentValues();
172        work[index].values.put("key", ++mId);
173        work[index].selection = TEST_SELECTION;
174        work[index].selectionArgs = TEST_SELECTION_ARGS;
175
176        work[index].delayMillis = 0;
177        work[index].result = ++mId;
178
179        TestAsyncQueryService aqs = new TestAsyncQueryService(buildTestContext(work), work);
180        aqs.startUpdate(work[index].token, work[index].cookie, work[index].uri, work[index].values,
181                work[index].selection, work[index].selectionArgs, work[index].delayMillis);
182
183        Log.d(TAG, "testUpdate Waiting >>>>>>>>>>>");
184        assertEquals("Not all operations were executed.", work.length, aqs
185                .waitForCompletion(BASE_TEST_WAIT_TIME));
186        Log.d(TAG, "testUpdate Done <<<<<<<<<<<<<<");
187    }
188
189    @SmallTest
190    public void testDelete() throws Exception {
191        int index = 0;
192        final OperationInfo[] work = new OperationInfo[1];
193        work[index] = new OperationInfo();
194        work[index].op = Operation.EVENT_ARG_DELETE;
195
196        work[index].token = ++mId;
197        work[index].cookie = ++mId;
198        work[index].uri = Uri.parse(AUTHORITY_URI + "blah");
199        work[index].selection = TEST_SELECTION;
200        work[index].selectionArgs = TEST_SELECTION_ARGS;
201
202        work[index].delayMillis = 0;
203        work[index].result = ++mId;
204
205        TestAsyncQueryService aqs = new TestAsyncQueryService(buildTestContext(work), work);
206        aqs.startDelete(work[index].token,
207                work[index].cookie,
208                work[index].uri,
209                work[index].selection,
210                work[index].selectionArgs,
211                work[index].delayMillis);
212
213        Log.d(TAG, "testDelete Waiting >>>>>>>>>>>");
214        assertEquals("Not all operations were executed.", work.length, aqs
215                .waitForCompletion(BASE_TEST_WAIT_TIME));
216        Log.d(TAG, "testDelete Done <<<<<<<<<<<<<<");
217    }
218
219    @SmallTest
220    public void testBatch() throws Exception {
221        int index = 0;
222        final OperationInfo[] work = new OperationInfo[1];
223        work[index] = new OperationInfo();
224        work[index].op = Operation.EVENT_ARG_BATCH;
225
226        work[index].token = ++mId;
227        work[index].cookie = ++mId;
228        work[index].authority = AUTHORITY;
229        work[index].cpo = new ArrayList<ContentProviderOperation>();
230        work[index].cpo.add(ContentProviderOperation.newInsert(Uri.parse(AUTHORITY_URI + ++mId))
231                .build());
232
233        work[index].delayMillis = 0;
234        ContentProviderResult[] resultArray = new ContentProviderResult[1];
235        resultArray[0] = new ContentProviderResult(++mId);
236        work[index].result = resultArray;
237
238        TestAsyncQueryService aqs = new TestAsyncQueryService(buildTestContext(work), work);
239        aqs.startBatch(work[index].token,
240                work[index].cookie,
241                work[index].authority,
242                work[index].cpo,
243                work[index].delayMillis);
244
245        Log.d(TAG, "testBatch Waiting >>>>>>>>>>>");
246        assertEquals("Not all operations were executed.", work.length, aqs
247                .waitForCompletion(BASE_TEST_WAIT_TIME));
248        Log.d(TAG, "testBatch Done <<<<<<<<<<<<<<");
249    }
250
251    @LargeTest
252    public void testDelay() throws Exception {
253        // Tests the ordering of the workqueue
254        int index = 0;
255        OperationInfo[] work = new OperationInfo[5];
256        work[index++] = generateWork(MIN_DELAY * 2);
257        work[index++] = generateWork(0);
258        work[index++] = generateWork(MIN_DELAY * 1);
259        work[index++] = generateWork(0);
260        work[index++] = generateWork(MIN_DELAY * 3);
261
262        OperationInfo[] sorted = generateSortedWork(work, work.length);
263
264        TestAsyncQueryService aqs = new TestAsyncQueryService(buildTestContext(sorted), sorted);
265        startWork(aqs, work);
266
267        Log.d(TAG, "testDelay Waiting >>>>>>>>>>>");
268        assertEquals("Not all operations were executed.", work.length, aqs
269                .waitForCompletion(BASE_TEST_WAIT_TIME));
270        Log.d(TAG, "testDelay Done <<<<<<<<<<<<<<");
271    }
272
273    @LargeTest
274    public void testCancel_simpleCancelLastTest() throws Exception {
275        int index = 0;
276        OperationInfo[] work = new OperationInfo[5];
277        work[index++] = generateWork(MIN_DELAY * 2);
278        work[index++] = generateWork(0);
279        work[index++] = generateWork(MIN_DELAY);
280        work[index++] = generateWork(0);
281        work[index] = generateWork(MIN_DELAY * 3);
282
283        // Not part of the expected as it will be canceled
284        OperationInfo toBeCancelled1 = work[index];
285        OperationInfo[] expected = generateSortedWork(work, work.length - 1);
286
287        TestAsyncQueryService aqs = new TestAsyncQueryService(buildTestContext(expected), expected);
288        startWork(aqs, work);
289        Operation lastOne = aqs.getLastCancelableOperation();
290        // Log.d(TAG, "lastOne = " + lastOne.toString());
291        // Log.d(TAG, "toBeCancelled1 = " + toBeCancelled1.toString());
292        assertTrue("1) delay=3 is not last", toBeCancelled1.equivalent(lastOne));
293        assertEquals("Can't cancel delay 3", 1, aqs.cancelOperation(lastOne.token));
294
295        Log.d(TAG, "testCancel_simpleCancelLastTest Waiting >>>>>>>>>>>");
296        assertEquals("Not all operations were executed.", expected.length, aqs
297                .waitForCompletion(BASE_TEST_WAIT_TIME));
298        Log.d(TAG, "testCancel_simpleCancelLastTest Done <<<<<<<<<<<<<<");
299    }
300
301    @LargeTest
302    public void testCancel_cancelSecondToLast() throws Exception {
303        int index = 0;
304        OperationInfo[] work = new OperationInfo[5];
305        work[index++] = generateWork(MIN_DELAY * 2);
306        work[index++] = generateWork(0);
307        work[index++] = generateWork(MIN_DELAY);
308        work[index++] = generateWork(0);
309        work[index] = generateWork(MIN_DELAY * 3);
310
311        // Not part of the expected as it will be canceled
312        OperationInfo toBeCancelled1 = work[index];
313        OperationInfo[] expected = new OperationInfo[4];
314        expected[0] = work[1]; // delay = 0
315        expected[1] = work[3]; // delay = 0
316        expected[2] = work[2]; // delay = MIN_DELAY
317        expected[3] = work[4]; // delay = MIN_DELAY * 3
318
319        TestAsyncQueryService aqs = new TestAsyncQueryService(buildTestContext(expected), expected);
320        startWork(aqs, work);
321
322        Operation lastOne = aqs.getLastCancelableOperation(); // delay = 3
323        assertTrue("2) delay=3 is not last", toBeCancelled1.equivalent(lastOne));
324        assertEquals("Can't cancel delay 2", 1, aqs.cancelOperation(work[0].token));
325        assertEquals("Delay 2 should be gone", 0, aqs.cancelOperation(work[0].token));
326
327        Log.d(TAG, "testCancel_cancelSecondToLast Waiting >>>>>>>>>>>");
328        assertEquals("Not all operations were executed.", expected.length, aqs
329                .waitForCompletion(BASE_TEST_WAIT_TIME));
330        Log.d(TAG, "testCancel_cancelSecondToLast Done <<<<<<<<<<<<<<");
331    }
332
333    @LargeTest
334    public void testCancel_multipleCancels() throws Exception {
335        int index = 0;
336        OperationInfo[] work = new OperationInfo[5];
337        work[index++] = generateWork(MIN_DELAY * 2);
338        work[index++] = generateWork(0);
339        work[index++] = generateWork(MIN_DELAY);
340        work[index++] = generateWork(0);
341        work[index] = generateWork(MIN_DELAY * 3);
342
343        // Not part of the expected as it will be canceled
344        OperationInfo[] expected = new OperationInfo[3];
345        expected[0] = work[1]; // delay = 0
346        expected[1] = work[3]; // delay = 0
347        expected[2] = work[2]; // delay = MIN_DELAY
348
349        TestAsyncQueryService aqs = new TestAsyncQueryService(buildTestContext(expected), expected);
350        startWork(aqs, work);
351
352        Operation lastOne = aqs.getLastCancelableOperation(); // delay = 3
353        assertTrue("3) delay=3 is not last", work[4].equivalent(lastOne));
354        assertEquals("Can't cancel delay 2", 1, aqs.cancelOperation(work[0].token));
355        assertEquals("Delay 2 should be gone", 0, aqs.cancelOperation(work[0].token));
356        assertEquals("Can't cancel delay 3", 1, aqs.cancelOperation(work[4].token));
357        assertEquals("Delay 3 should be gone", 0, aqs.cancelOperation(work[4].token));
358
359        Log.d(TAG, "testCancel_multipleCancels Waiting >>>>>>>>>>>");
360        assertEquals("Not all operations were executed.", expected.length, aqs
361                .waitForCompletion(BASE_TEST_WAIT_TIME));
362        Log.d(TAG, "testCancel_multipleCancels Done <<<<<<<<<<<<<<");
363    }
364
365    private OperationInfo generateWork(long delayMillis) {
366        OperationInfo work = new OperationInfo();
367        work.op = Operation.EVENT_ARG_DELETE;
368
369        work.token = ++mId;
370        work.cookie = 100 + work.token;
371        work.uri = Uri.parse(AUTHORITY_URI + "blah");
372        work.selection = TEST_SELECTION;
373        work.selectionArgs = TEST_SELECTION_ARGS;
374
375        work.delayMillis = delayMillis;
376        work.result = 1000 + work.token;
377        return work;
378    }
379
380    private void startWork(TestAsyncQueryService aqs, OperationInfo[] work) {
381        for (OperationInfo w : work) {
382            if (w != null) {
383                aqs.startDelete(w.token, w.cookie, w.uri, w.selection, w.selectionArgs,
384                        w.delayMillis);
385            }
386        }
387    }
388
389    OperationInfo[] generateSortedWork(OperationInfo[] work, int length) {
390        OperationInfo[] sorted = new OperationInfo[length];
391        System.arraycopy(work, 0, sorted, 0, length);
392
393        // Set the scheduled time so they get sorted properly
394        for (OperationInfo w : sorted) {
395            if (w != null) {
396                w.calculateScheduledTime();
397            }
398        }
399
400        // Stable sort by scheduled time
401        Arrays.sort(sorted);
402
403        Log.d(TAG, "Unsorted work: " + work.length);
404        for (OperationInfo w : work) {
405            if (w != null) {
406                Log.d(TAG, "Token#" + w.token + " delay=" + w.delayMillis);
407            }
408        }
409        Log.d(TAG, "Sorted work: " + sorted.length);
410        for (OperationInfo w : sorted) {
411            if (w != null) {
412                Log.d(TAG, "Token#" + w.token + " delay=" + w.delayMillis);
413            }
414        }
415
416        return sorted;
417    }
418
419    private Context buildTestContext(final OperationInfo[] work) {
420        MockContext context = new MockContext() {
421            MockContentResolver mResolver;
422
423            @Override
424            public ContentResolver getContentResolver() {
425                if (mResolver == null) {
426                    mResolver = new MockContentResolver();
427
428                    final String filenamePrefix = "test.";
429                    RenamingDelegatingContext targetContextWrapper = new RenamingDelegatingContext(
430                            new MockContext2(), getContext(), filenamePrefix);
431                    IsolatedContext providerContext =
432                            new IsolatedContext(mResolver, targetContextWrapper);
433
434                    ContentProvider provider = new TestProvider(work);
435                    provider.attachInfo(providerContext, null);
436
437                    mResolver.addProvider(AUTHORITY, provider);
438                }
439                return mResolver;
440            }
441
442            @Override
443            public String getPackageName() {
444                return AsyncQueryServiceTest.class.getPackage().getName();
445            }
446
447            @Override
448            public ComponentName startService(Intent service) {
449                AsyncQueryServiceTest.this.startService(service);
450                return service.getComponent();
451            }
452        };
453
454        return context;
455    }
456
457    private final class TestCursor extends MockCursor {
458        int mUnique = ++mId;
459
460        @Override
461        public int getCount() {
462            return mUnique;
463        }
464    }
465
466    /**
467     * TestAsyncQueryService takes the expected results in the constructor. They
468     * are used to verify the data passed to the callbacks.
469     */
470    class TestAsyncQueryService extends AsyncQueryService {
471        int mIndex = 0;
472
473        private OperationInfo[] mWork;
474
475        private Semaphore mCountingSemaphore;
476
477        public TestAsyncQueryService(Context context, OperationInfo[] work) {
478            super(context);
479            mCountingSemaphore = new Semaphore(0);
480
481            // run in a separate thread but call the same code
482            HandlerThread thread = new HandlerThread("TestAsyncQueryService");
483            thread.start();
484            super.setTestHandler(new Handler(thread.getLooper()) {
485                @Override
486                public void handleMessage(Message msg) {
487                    TestAsyncQueryService.this.handleMessage(msg);
488                }
489            });
490
491            mWork = work;
492        }
493
494        @Override
495        protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
496            Log.d(TAG, "onQueryComplete tid=" + Thread.currentThread().getId());
497            Log.d(TAG, "mWork.length=" + mWork.length + " mIndex=" + mIndex);
498
499            assertEquals(mWork[mIndex].op, Operation.EVENT_ARG_QUERY);
500            assertEquals(mWork[mIndex].token, token);
501            /*
502             * Even though our TestProvider returned mWork[mIndex].result, it is
503             * wrapped with new'ed CursorWrapperInner and there's no equal() in
504             * CursorWrapperInner. assertEquals the two cursor will always fail.
505             * So just compare the count which will be unique in our TestCursor;
506             */
507            assertEquals(((Cursor) mWork[mIndex].result).getCount(), cursor.getCount());
508
509            mIndex++;
510            mCountingSemaphore.release();
511        }
512
513        @Override
514        protected void onInsertComplete(int token, Object cookie, Uri uri) {
515            Log.d(TAG, "onInsertComplete tid=" + Thread.currentThread().getId());
516            Log.d(TAG, "mWork.length=" + mWork.length + " mIndex=" + mIndex);
517
518            assertEquals(mWork[mIndex].op, Operation.EVENT_ARG_INSERT);
519            assertEquals(mWork[mIndex].token, token);
520            assertEquals(mWork[mIndex].result, uri);
521
522            mIndex++;
523            mCountingSemaphore.release();
524        }
525
526        @Override
527        protected void onUpdateComplete(int token, Object cookie, int result) {
528            Log.d(TAG, "onUpdateComplete tid=" + Thread.currentThread().getId());
529            Log.d(TAG, "mWork.length=" + mWork.length + " mIndex=" + mIndex);
530
531            assertEquals(mWork[mIndex].op, Operation.EVENT_ARG_UPDATE);
532            assertEquals(mWork[mIndex].token, token);
533            assertEquals(mWork[mIndex].result, result);
534
535            mIndex++;
536            mCountingSemaphore.release();
537        }
538
539        @Override
540        protected void onDeleteComplete(int token, Object cookie, int result) {
541            Log.d(TAG, "onDeleteComplete tid=" + Thread.currentThread().getId());
542            Log.d(TAG, "mWork.length=" + mWork.length + " mIndex=" + mIndex);
543
544            assertEquals(mWork[mIndex].op, Operation.EVENT_ARG_DELETE);
545            assertEquals(mWork[mIndex].token, token);
546            assertEquals(mWork[mIndex].result, result);
547
548            mIndex++;
549            mCountingSemaphore.release();
550        }
551
552        @Override
553        protected void onBatchComplete(int token, Object cookie, ContentProviderResult[] results) {
554            Log.d(TAG, "onBatchComplete tid=" + Thread.currentThread().getId());
555            Log.d(TAG, "mWork.length=" + mWork.length + " mIndex=" + mIndex);
556
557            assertEquals(mWork[mIndex].op, Operation.EVENT_ARG_BATCH);
558            assertEquals(mWork[mIndex].token, token);
559
560            ContentProviderResult[] expected = (ContentProviderResult[]) mWork[mIndex].result;
561            assertEquals(expected.length, results.length);
562            for (int i = 0; i < expected.length; ++i) {
563                assertEquals(expected[i].count, results[i].count);
564                assertEquals(expected[i].uri, results[i].uri);
565            }
566
567            mIndex++;
568            mCountingSemaphore.release();
569        }
570
571        public int waitForCompletion(long timeoutMills) {
572            Log.d(TAG, "waitForCompletion tid=" + Thread.currentThread().getId());
573            int count = 0;
574            try {
575                while (count < mWork.length) {
576                    if (!mCountingSemaphore.tryAcquire(timeoutMills, TimeUnit.MILLISECONDS)) {
577                        break;
578                    }
579                    count++;
580                }
581            } catch (InterruptedException e) {
582            }
583            return count;
584        }
585    }
586
587    /**
588     * This gets called by AsyncQueryServiceHelper to read or write the data. It
589     * also verifies the data against the data passed in the constructor
590     */
591    class TestProvider extends ContentProvider {
592        OperationInfo[] mWork;
593
594        int index = 0;
595
596        public TestProvider(OperationInfo[] work) {
597            mWork = work;
598        }
599
600        @Override
601        public final Cursor query(Uri uri, String[] projection, String selection,
602                String[] selectionArgs, String orderBy) {
603            Log.d(TAG, "Provider query index=" + index);
604            assertEquals(mWork[index].op, Operation.EVENT_ARG_QUERY);
605            assertEquals(mWork[index].uri, uri);
606            assertEquals(mWork[index].projection, projection);
607            assertEquals(mWork[index].selection, selection);
608            assertEquals(mWork[index].selectionArgs, selectionArgs);
609            assertEquals(mWork[index].orderBy, orderBy);
610            return (Cursor) mWork[index++].result;
611        }
612
613        @Override
614        public Uri insert(Uri uri, ContentValues values) {
615            Log.d(TAG, "Provider insert index=" + index);
616            assertEquals(mWork[index].op, Operation.EVENT_ARG_INSERT);
617            assertEquals(mWork[index].uri, uri);
618            assertEquals(mWork[index].values, values);
619            return (Uri) mWork[index++].result;
620        }
621
622        @Override
623        public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
624            Log.d(TAG, "Provider update index=" + index);
625            assertEquals(mWork[index].op, Operation.EVENT_ARG_UPDATE);
626            assertEquals(mWork[index].uri, uri);
627            assertEquals(mWork[index].values, values);
628            assertEquals(mWork[index].selection, selection);
629            assertEquals(mWork[index].selectionArgs, selectionArgs);
630            return (Integer) mWork[index++].result;
631        }
632
633        @Override
634        public int delete(Uri uri, String selection, String[] selectionArgs) {
635            Log.d(TAG, "Provider delete index=" + index);
636            assertEquals(mWork[index].op, Operation.EVENT_ARG_DELETE);
637            assertEquals(mWork[index].uri, uri);
638            assertEquals(mWork[index].selection, selection);
639            assertEquals(mWork[index].selectionArgs, selectionArgs);
640            return (Integer) mWork[index++].result;
641        }
642
643        @Override
644        public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations) {
645            Log.d(TAG, "Provider applyBatch index=" + index);
646            assertEquals(mWork[index].op, Operation.EVENT_ARG_BATCH);
647            assertEquals(mWork[index].cpo, operations);
648            return (ContentProviderResult[]) mWork[index++].result;
649        }
650
651        @Override
652        public String getType(Uri uri) {
653            return null;
654        }
655
656        @Override
657        public boolean onCreate() {
658            return false;
659        }
660    }
661}
662