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