1 /* 2 * Copyright (C) 2017 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 17 package com.android.documentsui; 18 19 import static junit.framework.Assert.assertEquals; 20 import static junit.framework.Assert.fail; 21 22 import android.database.Cursor; 23 import android.database.MatrixCursor; 24 import android.database.MergeCursor; 25 import android.provider.DocumentsContract.Document; 26 27 import androidx.test.filters.SmallTest; 28 import androidx.test.runner.AndroidJUnit4; 29 30 import com.android.documentsui.base.DocumentInfo; 31 import com.android.documentsui.roots.RootCursorWrapper; 32 import com.android.documentsui.testing.TestEventListener; 33 import com.android.documentsui.testing.TestFeatures; 34 35 import org.junit.Before; 36 import org.junit.Test; 37 import org.junit.runner.RunWith; 38 39 import java.util.BitSet; 40 import java.util.Random; 41 42 @RunWith(AndroidJUnit4.class) 43 @SmallTest 44 public class ModelTest { 45 46 private static final int ITEM_COUNT = 10; 47 private static final String AUTHORITY = "test_authority"; 48 49 private static final String[] COLUMNS = new String[]{ 50 RootCursorWrapper.COLUMN_AUTHORITY, 51 Document.COLUMN_DOCUMENT_ID, 52 Document.COLUMN_FLAGS, 53 Document.COLUMN_DISPLAY_NAME, 54 Document.COLUMN_SIZE, 55 Document.COLUMN_LAST_MODIFIED, 56 Document.COLUMN_MIME_TYPE 57 }; 58 59 private static final String[] NAMES = new String[] { 60 "4", 61 "foo", 62 "1", 63 "bar", 64 "*(Ljifl;a", 65 "0", 66 "baz", 67 "2", 68 "3", 69 "%$%VD" 70 }; 71 72 private Cursor cursor; 73 private Model model; 74 private TestFeatures features; 75 76 @Before setUp()77 public void setUp() { 78 features = new TestFeatures(); 79 80 Random rand = new Random(); 81 82 MatrixCursor c = new MatrixCursor(COLUMNS); 83 for (int i = 0; i < ITEM_COUNT; ++i) { 84 MatrixCursor.RowBuilder row = c.newRow(); 85 row.add(RootCursorWrapper.COLUMN_AUTHORITY, AUTHORITY); 86 row.add(Document.COLUMN_DOCUMENT_ID, Integer.toString(i)); 87 row.add(Document.COLUMN_FLAGS, Document.FLAG_SUPPORTS_DELETE); 88 // Generate random document names and sizes. This forces the model's internal sort code 89 // to actually do something. 90 row.add(Document.COLUMN_DISPLAY_NAME, NAMES[i]); 91 row.add(Document.COLUMN_SIZE, rand.nextInt()); 92 } 93 cursor = c; 94 95 DirectoryResult r = new DirectoryResult(); 96 r.cursor = cursor; 97 98 // Instantiate the model with a dummy view adapter and listener that (for now) do nothing. 99 model = new Model(features); 100 // not sure why we add a listener here at all. 101 model.addUpdateListener(new TestEventListener<>()); 102 model.update(r); 103 } 104 105 // Tests that the item count is correct. 106 @Test testItemCount()107 public void testItemCount() { 108 assertEquals(ITEM_COUNT, model.getItemCount()); 109 } 110 111 // Tests multiple authorities with clashing document IDs. 112 @Test testModelIdIsUnique()113 public void testModelIdIsUnique() { 114 MatrixCursor cIn1 = new MatrixCursor(COLUMNS); 115 MatrixCursor cIn2 = new MatrixCursor(COLUMNS); 116 117 // Make two sets of items with the same IDs, under different authorities. 118 final String AUTHORITY0 = "auth0"; 119 final String AUTHORITY1 = "auth1"; 120 121 for (int i = 0; i < ITEM_COUNT; ++i) { 122 MatrixCursor.RowBuilder row0 = cIn1.newRow(); 123 row0.add(RootCursorWrapper.COLUMN_AUTHORITY, AUTHORITY0); 124 row0.add(Document.COLUMN_DOCUMENT_ID, Integer.toString(i)); 125 126 MatrixCursor.RowBuilder row1 = cIn2.newRow(); 127 row1.add(RootCursorWrapper.COLUMN_AUTHORITY, AUTHORITY1); 128 row1.add(Document.COLUMN_DOCUMENT_ID, Integer.toString(i)); 129 } 130 131 Cursor cIn = new MergeCursor(new Cursor[] { cIn1, cIn2 }); 132 133 // Update the model, then make sure it contains all the expected items. 134 DirectoryResult r = new DirectoryResult(); 135 r.cursor = cIn; 136 model.update(r); 137 138 assertEquals(ITEM_COUNT * 2, model.getItemCount()); 139 BitSet b0 = new BitSet(ITEM_COUNT); 140 BitSet b1 = new BitSet(ITEM_COUNT); 141 142 for (String id: model.getModelIds()) { 143 Cursor cOut = model.getItem(id); 144 String authority = 145 DocumentInfo.getCursorString(cOut, RootCursorWrapper.COLUMN_AUTHORITY); 146 String docId = DocumentInfo.getCursorString(cOut, Document.COLUMN_DOCUMENT_ID); 147 148 switch (authority) { 149 case AUTHORITY0: 150 b0.set(Integer.parseInt(docId)); 151 break; 152 case AUTHORITY1: 153 b1.set(Integer.parseInt(docId)); 154 break; 155 default: 156 fail("Unrecognized authority string"); 157 } 158 } 159 160 assertEquals(ITEM_COUNT, b0.cardinality()); 161 assertEquals(ITEM_COUNT, b1.cardinality()); 162 } 163 164 // Tests the base case for Model.getItem. 165 @Test testGetItem()166 public void testGetItem() { 167 String[] ids = model.getModelIds(); 168 assertEquals(ITEM_COUNT, ids.length); 169 for (int i = 0; i < ITEM_COUNT; ++i) { 170 Cursor c = model.getItem(ids[i]); 171 assertEquals(i, c.getPosition()); 172 } 173 } 174 175 @Test testResetAfterGettingException()176 public void testResetAfterGettingException() { 177 DirectoryResult result = new DirectoryResult(); 178 result.exception = new Exception(); 179 180 model.update(result); 181 182 assertEquals(0, model.getItemCount()); 183 } 184 } 185