1/*
2 * Copyright (C) 2008 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 android.widget;
18
19import com.google.android.collect.Lists;
20
21import android.content.Context;
22import android.database.Cursor;
23import android.database.MatrixCursor;
24import android.test.AndroidTestCase;
25import android.test.suitebuilder.annotation.SmallTest;
26
27import java.util.ArrayList;
28import java.util.Random;
29
30/**
31 * This is a series of tests of basic API contracts for SimpleCursorAdapter.  It is
32 * incomplete and can use work.
33 *
34 * NOTE:  This contract holds for underlying cursor types too and these should
35 * be extracted into a set of tests that can be run on any descendant of CursorAdapter.
36 */
37public class SimpleCursorAdapterTest extends AndroidTestCase {
38
39    String[] mFrom;
40    int[] mTo;
41    int mLayout;
42    Context mContext;
43
44    ArrayList<ArrayList> mData2x2;
45    Cursor mCursor2x2;
46
47    /**
48     * Set up basic columns and cursor for the tests
49     */
50    @Override
51    public void setUp() throws Exception {
52        super.setUp();
53
54        // all the pieces needed for the various tests
55        mFrom = new String[]{"Column1", "Column2", "_id"};
56        mTo = new int[]{com.android.internal.R.id.text1, com.android.internal.R.id.text2};
57        mLayout = com.android.internal.R.layout.simple_list_item_2;
58        mContext = getContext();
59
60        // raw data for building a basic test cursor
61        mData2x2 = createTestList(2, 2);
62        mCursor2x2 = createCursor(mFrom, mData2x2);
63    }
64
65    /**
66     * Borrowed from CursorWindowTest.java
67     */
68    private ArrayList<ArrayList> createTestList(int rows, int cols) {
69        ArrayList<ArrayList> list = Lists.newArrayList();
70        Random generator = new Random();
71
72        for (int i = 0; i < rows; i++) {
73            ArrayList<Integer> col = Lists.newArrayList();
74            list.add(col);
75            for (int j = 0; j < cols; j++) {
76                // generate random number
77                Integer r = generator.nextInt();
78                col.add(r);
79            }
80            col.add(i);
81        }
82        return list;
83    }
84
85    /**
86     * Test creating with a live cursor
87     */
88    @SmallTest
89    public void testCreateLive() {
90        SimpleCursorAdapter ca = new SimpleCursorAdapter(mContext, mLayout, mCursor2x2, mFrom, mTo);
91
92        // Now see if we can pull 2 rows from the adapter
93        assertEquals(2, ca.getCount());
94    }
95
96    /**
97     * Test creating with a null cursor
98     */
99    @SmallTest
100    public void testCreateNull() {
101        SimpleCursorAdapter ca = new SimpleCursorAdapter(mContext, mLayout, null, mFrom, mTo);
102
103        // The adapter should report zero rows
104        assertEquals(0, ca.getCount());
105    }
106
107    /**
108     * Test changeCursor() with live cursor
109     */
110    @SmallTest
111    public void testChangeCursorLive() {
112        SimpleCursorAdapter ca = new SimpleCursorAdapter(mContext, mLayout, mCursor2x2, mFrom, mTo);
113
114        // Now see if we can pull 2 rows from the adapter
115        assertEquals(2, ca.getCount());
116
117        // now put in a different cursor (5 rows)
118        ArrayList<ArrayList> data2 = createTestList(5, 2);
119        Cursor c2 = createCursor(mFrom, data2);
120        ca.changeCursor(c2);
121
122        // Now see if we can pull 5 rows from the adapter
123        assertEquals(5, ca.getCount());
124    }
125
126    /**
127     * Test changeCursor() with null cursor
128     */
129    @SmallTest
130    public void testChangeCursorNull() {
131        SimpleCursorAdapter ca = new SimpleCursorAdapter(mContext, mLayout, mCursor2x2, mFrom, mTo);
132
133        // Now see if we can pull 2 rows from the adapter
134        assertEquals(2, ca.getCount());
135
136        // now put in null
137        ca.changeCursor(null);
138
139        // The adapter should report zero rows
140        assertEquals(0, ca.getCount());
141    }
142
143    /**
144     * Test changeCursor() with differing column layout.  This confirms that the Adapter can
145     * deal with cursors that have the same essential data (as defined by the original mFrom
146     * array) but it's OK if the physical structure of the cursor changes (columns rearranged).
147     */
148    @SmallTest
149    public void testChangeCursorColumns() {
150        TestSimpleCursorAdapter ca = new TestSimpleCursorAdapter(mContext, mLayout, mCursor2x2,
151                mFrom, mTo);
152
153        // check columns of original - mFrom and mTo should line up
154        int[] columns = ca.getConvertedFrom();
155        assertEquals(columns[0], 0);
156        assertEquals(columns[1], 1);
157
158        // Now make a new cursor with similar data but rearrange the columns
159        String[] swappedFrom = new String[]{"Column2", "Column1", "_id"};
160        Cursor c2 = createCursor(swappedFrom, mData2x2);
161        ca.changeCursor(c2);
162        assertEquals(2, ca.getCount());
163
164        // check columns to see if rearrangement tracked (should be swapped now)
165        columns = ca.getConvertedFrom();
166        assertEquals(columns[0], 1);
167        assertEquals(columns[1], 0);
168    }
169
170    /**
171     * Test that you can safely construct with a null cursor *and* null to/from arrays.
172     * This is new functionality added in 12/2008.
173     */
174    @SmallTest
175    public void testNullConstructor() {
176        SimpleCursorAdapter ca = new SimpleCursorAdapter(mContext, mLayout, null, null, null);
177        assertEquals(0, ca.getCount());
178    }
179
180    /**
181     * Test going from a null cursor to a non-null cursor *and* setting the to/from arrays
182     * This is new functionality added in 12/2008.
183     */
184    @SmallTest
185    public void testChangeNullToMapped() {
186        TestSimpleCursorAdapter ca = new TestSimpleCursorAdapter(mContext, mLayout, null, null, null);
187        assertEquals(0, ca.getCount());
188
189        ca.changeCursorAndColumns(mCursor2x2, mFrom, mTo);
190        assertEquals(2, ca.getCount());
191
192        // check columns of original - mFrom and mTo should line up
193        int[] columns = ca.getConvertedFrom();
194        assertEquals(2, columns.length);
195        assertEquals(0, columns[0]);
196        assertEquals(1, columns[1]);
197        int[] viewIds = ca.getTo();
198        assertEquals(2, viewIds.length);
199        assertEquals(com.android.internal.R.id.text1, viewIds[0]);
200        assertEquals(com.android.internal.R.id.text2, viewIds[1]);
201    }
202
203    /**
204     * Test going from one mapping to a different mapping
205     * This is new functionality added in 12/2008.
206     */
207    @SmallTest
208    public void testChangeMapping() {
209        TestSimpleCursorAdapter ca = new TestSimpleCursorAdapter(mContext, mLayout, mCursor2x2,
210                mFrom, mTo);
211        assertEquals(2, ca.getCount());
212
213        // Now create a new configuration with same cursor and just one column mapped
214        String[] singleFrom = new String[]{"Column1"};
215        int[] singleTo = new int[]{com.android.internal.R.id.text1};
216        ca.changeCursorAndColumns(mCursor2x2, singleFrom, singleTo);
217
218        // And examine the results, make sure they're still consistent
219        int[] columns = ca.getConvertedFrom();
220        assertEquals(1, columns.length);
221        assertEquals(0, columns[0]);
222        int[] viewIds = ca.getTo();
223        assertEquals(1, viewIds.length);
224        assertEquals(com.android.internal.R.id.text1, viewIds[0]);
225
226        // And again, same cursor, different map
227        singleFrom = new String[]{"Column2"};
228        singleTo = new int[]{com.android.internal.R.id.text2};
229        ca.changeCursorAndColumns(mCursor2x2, singleFrom, singleTo);
230
231        // And examine the results, make sure they're still consistent
232        columns = ca.getConvertedFrom();
233        assertEquals(1, columns.length);
234        assertEquals(1, columns[0]);
235        viewIds = ca.getTo();
236        assertEquals(1, viewIds.length);
237        assertEquals(com.android.internal.R.id.text2, viewIds[0]);
238    }
239
240    private static MatrixCursor createCursor(String[] columns, ArrayList<ArrayList> list) {
241        MatrixCursor cursor = new MatrixCursor(columns, list.size());
242        for (ArrayList row : list) {
243            cursor.addRow(row);
244        }
245        return cursor;
246    }
247
248    /**
249     * This is simply a way to sneak a look at the protected mFrom() array.  A more API-
250     * friendly way to do this would be to mock out a View and a ViewBinder and exercise
251     * it via those seams.
252     */
253    private static class TestSimpleCursorAdapter extends SimpleCursorAdapter {
254
255        public TestSimpleCursorAdapter(Context context, int layout, Cursor c,
256                String[] from, int[] to) {
257            super(context, layout, c, from, to);
258        }
259
260        int[] getConvertedFrom() {
261            return mFrom;
262        }
263
264        int[] getTo() {
265            return mTo;
266        }
267    }
268}
269