DocumentLoaderTest.java revision 678ed36bebb7b0f5ff342e9da30d693bffb8aeb2
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.mtp;
18
19import android.content.Context;
20import android.database.Cursor;
21import android.mtp.MtpObjectInfo;
22import android.net.Uri;
23import android.provider.DocumentsContract;
24import android.test.AndroidTestCase;
25import android.test.suitebuilder.annotation.MediumTest;
26
27import java.io.IOException;
28import java.util.HashMap;
29import java.util.Map;
30import java.util.concurrent.CountDownLatch;
31
32@MediumTest
33public class DocumentLoaderTest extends AndroidTestCase {
34    private MtpDatabase mDatabase;
35    private BlockableTestMtpManager mManager;
36    private TestContentResolver mResolver;
37    private DocumentLoader mLoader;
38    final private Identifier mParentIdentifier = new Identifier(
39            0, 0, 0, "2", MtpDatabaseConstants.DOCUMENT_TYPE_STORAGE);
40
41    @Override
42    public void setUp() throws Exception {
43        mDatabase = new MtpDatabase(getContext(), MtpDatabaseConstants.FLAG_DATABASE_IN_MEMORY);
44
45        mDatabase.getMapper().startAddingDocuments(null);
46        mDatabase.getMapper().putDeviceDocument(
47                new MtpDeviceRecord(0, "Device", null, true, new MtpRoot[0], null, null));
48        mDatabase.getMapper().stopAddingDocuments(null);
49
50        mDatabase.getMapper().startAddingDocuments("1");
51        mDatabase.getMapper().putStorageDocuments("1", new int[0], new MtpRoot[] {
52                new MtpRoot(0, 0, "Storage", 1000, 1000, "")
53        });
54        mDatabase.getMapper().stopAddingDocuments("1");
55
56        mManager = new BlockableTestMtpManager(getContext());
57        mResolver = new TestContentResolver();
58    }
59
60    @Override
61    public void tearDown() throws Exception {
62        mLoader.close();
63        mDatabase.close();
64    }
65
66    public void testBasic() throws Exception {
67        setUpLoader();
68
69        final Uri uri = DocumentsContract.buildChildDocumentsUri(
70                MtpDocumentsProvider.AUTHORITY, mParentIdentifier.mDocumentId);
71        setUpDocument(mManager, 40);
72        mManager.blockDocument(0, 15);
73        mManager.blockDocument(0, 35);
74
75        {
76            final Cursor cursor = mLoader.queryChildDocuments(
77                    MtpDocumentsProvider.DEFAULT_DOCUMENT_PROJECTION, mParentIdentifier);
78            assertEquals(DocumentLoader.NUM_INITIAL_ENTRIES, cursor.getCount());
79        }
80
81        Thread.sleep(DocumentLoader.NOTIFY_PERIOD_MS);
82        mManager.unblockDocument(0, 15);
83        mResolver.waitForNotification(uri, 1);
84
85        {
86            final Cursor cursor = mLoader.queryChildDocuments(
87                    MtpDocumentsProvider.DEFAULT_DOCUMENT_PROJECTION, mParentIdentifier);
88            assertEquals(
89                    DocumentLoader.NUM_INITIAL_ENTRIES + DocumentLoader.NUM_LOADING_ENTRIES,
90                    cursor.getCount());
91        }
92
93        mManager.unblockDocument(0, 35);
94        mResolver.waitForNotification(uri, 2);
95
96        {
97            final Cursor cursor = mLoader.queryChildDocuments(
98                    MtpDocumentsProvider.DEFAULT_DOCUMENT_PROJECTION, mParentIdentifier);
99            assertEquals(40, cursor.getCount());
100        }
101
102        assertEquals(2, mResolver.getChangeCount(uri));
103    }
104
105    public void testError_GetObjectHandles() throws Exception {
106        mManager = new BlockableTestMtpManager(getContext()) {
107            @Override
108            int[] getObjectHandles(int deviceId, int storageId, int parentObjectHandle)
109                    throws IOException {
110                throw new IOException();
111            }
112        };
113        setUpLoader();
114        mManager.setObjectHandles(0, 0, MtpManager.OBJECT_HANDLE_ROOT_CHILDREN, null);
115        try {
116            try (final Cursor cursor = mLoader.queryChildDocuments(
117                    MtpDocumentsProvider.DEFAULT_DOCUMENT_PROJECTION, mParentIdentifier)) {}
118            fail();
119        } catch (IOException exception) {
120            // Expect exception.
121        }
122    }
123
124    public void testError_GetObjectInfo() throws Exception {
125        mManager = new BlockableTestMtpManager(getContext()) {
126            @Override
127            MtpObjectInfo getObjectInfo(int deviceId, int objectHandle) throws IOException {
128                if (objectHandle == DocumentLoader.NUM_INITIAL_ENTRIES) {
129                    throw new IOException();
130                } else {
131                    return super.getObjectInfo(deviceId, objectHandle);
132                }
133            }
134        };
135        setUpLoader();
136        setUpDocument(mManager, DocumentLoader.NUM_INITIAL_ENTRIES);
137        try (final Cursor cursor = mLoader.queryChildDocuments(
138                MtpDocumentsProvider.DEFAULT_DOCUMENT_PROJECTION, mParentIdentifier)) {
139            // Even if MtpManager returns an error for a document, loading must complete.
140            assertFalse(cursor.getExtras().getBoolean(DocumentsContract.EXTRA_LOADING));
141        }
142    }
143
144    private void setUpLoader() {
145        mLoader = new DocumentLoader(
146                new MtpDeviceRecord(
147                        0, "Device", "Key", true, new MtpRoot[0],
148                        TestUtil.OPERATIONS_SUPPORTED, new int[0]),
149                mManager,
150                mResolver,
151                mDatabase);
152    }
153
154    private void setUpDocument(TestMtpManager manager, int count) {
155        int[] childDocuments = new int[count];
156        for (int i = 0; i < childDocuments.length; i++) {
157            final int objectHandle = i + 1;
158            childDocuments[i] = objectHandle;
159            manager.setObjectInfo(0, new MtpObjectInfo.Builder()
160                    .setObjectHandle(objectHandle)
161                    .setName(Integer.toString(i))
162                    .build());
163        }
164        manager.setObjectHandles(0, 0, MtpManager.OBJECT_HANDLE_ROOT_CHILDREN, childDocuments);
165    }
166
167    private static class BlockableTestMtpManager extends TestMtpManager {
168        final private Map<String, CountDownLatch> blockedDocuments = new HashMap<>();
169
170        BlockableTestMtpManager(Context context) {
171            super(context);
172        }
173
174        void blockDocument(int deviceId, int objectHandle) {
175            blockedDocuments.put(pack(deviceId, objectHandle), new CountDownLatch(1));
176        }
177
178        void unblockDocument(int deviceId, int objectHandle) {
179            blockedDocuments.get(pack(deviceId, objectHandle)).countDown();
180        }
181
182        @Override
183        MtpObjectInfo getObjectInfo(int deviceId, int objectHandle) throws IOException {
184            final CountDownLatch latch = blockedDocuments.get(pack(deviceId, objectHandle));
185            if (latch != null) {
186                try {
187                    latch.await();
188                } catch(InterruptedException e) {
189                    fail();
190                }
191            }
192            return super.getObjectInfo(deviceId, objectHandle);
193        }
194    }
195}
196