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