xref: /aosp_15_r20/cts/tests/tests/database/src/android/database/cts/CursorJoinerTest.java (revision b7c941bb3fa97aba169d73cee0bed2de8ac964bf)
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 
17 package android.database.cts;
18 
19 import static org.junit.Assert.assertEquals;
20 import static org.junit.Assert.assertNotNull;
21 import static org.junit.Assert.assertTrue;
22 import static org.junit.Assert.fail;
23 
24 import android.content.Context;
25 import android.database.Cursor;
26 import android.database.CursorJoiner;
27 import android.database.CursorJoiner.Result;
28 import android.database.sqlite.SQLiteDatabase;
29 import android.platform.test.annotations.IgnoreUnderRavenwood;
30 import android.platform.test.ravenwood.RavenwoodRule;
31 
32 import androidx.test.InstrumentationRegistry;
33 import androidx.test.runner.AndroidJUnit4;
34 
35 import org.junit.After;
36 import org.junit.Before;
37 import org.junit.Rule;
38 import org.junit.Test;
39 import org.junit.runner.RunWith;
40 
41 import java.io.File;
42 
43 @RunWith(AndroidJUnit4.class)
44 public class CursorJoinerTest {
45     @Rule public final RavenwoodRule mRavenwood = new RavenwoodRule();
46 
47     private static final int TEST_ITEM_COUNT = 10;
48     private static final int DEFAULT_TABLE1_VALUE_BEGINS = 1;
49     private static final int DEFAULT_TABLE2_VALUE_BEGINS = 11;
50     private static final int EQUAL_START = 18;
51     // Every table has 7 unique numbers, and 3 other numbers they all have.
52     private static final int UNIQUE_COUNT = 7;
53     private static final int MAX_VALUE = 20;
54     private static final int EQUAL_VALUE_COUNT = MAX_VALUE - EQUAL_START + 1;
55     private static final String TABLE_NAME_1 = "test1";
56     private static final String TABLE_NAME_2 = "test2";
57     private static final String TABLE1_COLUMNS = " number TEXT";
58     private static final String TABLE2_COLUMNS = " number TEXT, int_number INTEGER";
59 
60     private SQLiteDatabase mDatabase;
61     private File mDatabaseFile;
62 
63     @Before
setUp()64     public void setUp() throws Exception {
65         if (mRavenwood.isUnderRavenwood()) return;
66 
67         setupDatabase();
68     }
69 
70     @After
tearDown()71     public void tearDown() throws Exception {
72         if (mRavenwood.isUnderRavenwood()) return;
73 
74         mDatabase.close();
75         mDatabaseFile.delete();
76     }
77 
getContext()78     private Context getContext() {
79         return InstrumentationRegistry.getTargetContext();
80     }
81 
82     @Test
83     @IgnoreUnderRavenwood(blockedBy = SQLiteDatabase.class)
testCursorJoinerAndIterator()84     public void testCursorJoinerAndIterator() {
85         Cursor cursor1 = getCursor(TABLE_NAME_1, null, null);
86         Cursor cursor2 = getCursor(TABLE_NAME_2, null, null);
87         // Test with different length ColumenNAmes
88         try {
89             new CursorJoiner(cursor1, cursor1.getColumnNames(), cursor2, cursor2.getColumnNames());
90             fail("CursorJoiner's constructor should throws  IllegalArgumentException here.");
91         } catch (IllegalArgumentException e) {
92             //expected
93         }
94         closeCursor(cursor1);
95         closeCursor(cursor2);
96 
97         String[] columnNames = new String[] { "number" };
98         cursor1 = getCursor(TABLE_NAME_1, null, columnNames);
99         cursor2 = getCursor(TABLE_NAME_2, null, columnNames);
100 
101         CursorJoiner cursorJoiner = new CursorJoiner(cursor1, cursor1.getColumnNames(), cursor2,
102                 cursor2.getColumnNames());
103 
104         // Test remove()
105         try {
106             cursorJoiner.remove();
107             fail("remove() should throws UnsupportedOperationException here");
108         } catch (UnsupportedOperationException e) {
109             // expected
110         }
111 
112         assertEquals(TEST_ITEM_COUNT, cursor1.getCount());
113         assertEquals(TEST_ITEM_COUNT, cursor2.getCount());
114 
115         // Test iterator
116         for (CursorJoiner.Result joinResult : cursorJoiner) {
117             switch (joinResult) {
118             case LEFT:
119                 // Add the values into table test1 which table test1 possess and table test2 don't.
120                 assertTrue(cursor1.getString(0).compareTo(cursor2.getString(0)) < 0);
121                 addValueIntoTable(TABLE_NAME_2, cursor1.getString(0));
122                 break;
123             case RIGHT:
124                 // Add the values into table test2 which table test2 possess and table test1 don't.
125                 assertTrue(cursor1.getString(0).compareTo(cursor2.getString(0)) > 0);
126                 addValueIntoTable(TABLE_NAME_1, cursor2.getString(0));
127                 break;
128             case BOTH:
129                 // Delete the values table test1 and test2 both possess.
130                 assertEquals(cursor1.getString(0), cursor2.getString(0));
131                 deleteValueFromTable(TABLE_NAME_1, cursor1.getString(0));
132                 deleteValueFromTable(TABLE_NAME_2, cursor2.getString(0));
133                 break;
134             }
135         }
136         cursor1.requery();
137         cursor2.requery();
138 
139         // Finally, two tables's number columns have the same contents
140         assertEquals(UNIQUE_COUNT * 2, cursor1.getCount());
141         assertEquals(UNIQUE_COUNT * 2, cursor2.getCount());
142 
143         // For every  table, merged with the other one's unique numbers, and deleted the originally
144         // mutual same numbers(EQUAL_START~MAX_VALUE);
145         cursor1.moveToFirst();
146         cursor2.moveToFirst();
147         for (int i = 0; i < UNIQUE_COUNT; i++) {
148             assertEquals(getOrderNumberString(DEFAULT_TABLE1_VALUE_BEGINS + i, MAX_VALUE),
149                     cursor1.getString(0));
150             assertEquals(cursor1.getString(0), cursor2.getString(0));
151             cursor1.moveToNext();
152             cursor2.moveToNext();
153         }
154         closeCursor(cursor2);
155         closeCursor(cursor1);
156     }
157 
158     @Test
159     @IgnoreUnderRavenwood(blockedBy = SQLiteDatabase.class)
testNext()160     public void testNext() {
161         String[] columnNames = new String[] { "number" };
162         Cursor cursor1 = getCursor(TABLE_NAME_1, null, columnNames);
163         Cursor cursor2 = getCursor(TABLE_NAME_2, null, columnNames);
164 
165         // For cursor1 , values are '01'~'07' and 'EQUAL_START'~'MAX_VALUE'
166         assertEquals(TEST_ITEM_COUNT, cursor1.getCount());
167         // For cursor2 , values are '11'~'17' and 'EQUAL_START'~'MAX_VALUE'
168         assertEquals(TEST_ITEM_COUNT, cursor2.getCount());
169         CursorJoiner cursorJoiner = new CursorJoiner(cursor1, cursor1.getColumnNames(), cursor2,
170                 cursor2.getColumnNames());
171         for (int i = 0; i < UNIQUE_COUNT; i++) {
172             // For cursor1, value 1~7 result value as LEFT to cursor2 value '11'
173             assertTrue(cursorJoiner.hasNext());
174             assertEquals(Result.LEFT, cursorJoiner.next());
175             assertEquals(getOrderNumberString(DEFAULT_TABLE1_VALUE_BEGINS + i, MAX_VALUE), cursor1
176                     .getString(0));
177             assertEquals(getOrderNumberString(DEFAULT_TABLE2_VALUE_BEGINS, MAX_VALUE), cursor2
178                   .getString(0));
179         }
180         for (int i = 0; i < UNIQUE_COUNT; i++) {
181             // For cursor2, value 11~17 result a value as LEFT to cursor1 value '18'
182             assertTrue(cursorJoiner.hasNext());
183             assertEquals(Result.RIGHT, cursorJoiner.next());
184             assertEquals(getOrderNumberString(EQUAL_START, MAX_VALUE), cursor1.getString(0));
185             assertEquals(getOrderNumberString(DEFAULT_TABLE2_VALUE_BEGINS + i, MAX_VALUE), cursor2
186                     .getString(0));
187         }
188         for (int i = 0; i < EQUAL_VALUE_COUNT; i++) {
189             // For cursor1 and cursor2, value 18~20 result a value as BOTH
190             assertTrue(cursorJoiner.hasNext());
191             assertEquals(Result.BOTH, cursorJoiner.next());
192             assertEquals(getOrderNumberString(EQUAL_START + i, MAX_VALUE), cursor1.getString(0));
193             assertEquals(getOrderNumberString(EQUAL_START + i, MAX_VALUE), cursor2.getString(0));
194         }
195         closeCursor(cursor1);
196         closeCursor(cursor2);
197     }
198 
199     /**
200      * This function accepts integer maxValue to determine max length of number.
201      * Return a converted decimal number string of input integer parameter 'value',
202      *  according to  the max length, '0' will be placeholder(s).
203      * For example: if max length is 2, 1 -> '01', 10 -> '10'.
204      * @param value
205      * @param maxValue
206      * @return
207      */
getOrderNumberString(int value, int maxValue)208     private String getOrderNumberString(int value, int maxValue) {
209         // Convert decimal number as string, '0' as placeholder
210         int maxLength = Integer.toString(maxValue).length();
211         int basicLength = Integer.toString(value).length();
212         String placeHolders = "";
213         for (int i = 0; i < (maxLength - basicLength); i++) {
214             placeHolders += "0";
215         }
216         return placeHolders + Integer.toString(value);
217     }
218 
initializeTables()219     private void initializeTables() {
220         // Add 1 to 7 into Table1
221         addValuesIntoTable(TABLE_NAME_1, DEFAULT_TABLE1_VALUE_BEGINS,
222                 DEFAULT_TABLE1_VALUE_BEGINS + UNIQUE_COUNT - 1);
223         // Add 18 to 20 into Table1
224         addValuesIntoTable(TABLE_NAME_1, DEFAULT_TABLE2_VALUE_BEGINS + UNIQUE_COUNT, MAX_VALUE);
225         // Add 11 to 17 into Table2
226         addValuesIntoTable(TABLE_NAME_2, DEFAULT_TABLE2_VALUE_BEGINS,
227                 DEFAULT_TABLE2_VALUE_BEGINS + UNIQUE_COUNT - 1);
228         // Add 18 to 20 into Table2
229         addValuesIntoTable(TABLE_NAME_2, DEFAULT_TABLE2_VALUE_BEGINS + UNIQUE_COUNT, MAX_VALUE);
230     }
231 
setupDatabase()232     private void setupDatabase() {
233         File dbDir = getContext().getDir("tests", Context.MODE_PRIVATE);
234         mDatabaseFile = new File(dbDir, "database_test.db");
235         if (mDatabaseFile.exists()) {
236             mDatabaseFile.delete();
237         }
238         mDatabase = SQLiteDatabase.openOrCreateDatabase(mDatabaseFile.getPath(), null);
239         assertNotNull(mDatabaseFile);
240         createTable(TABLE_NAME_1, TABLE1_COLUMNS);
241         createTable(TABLE_NAME_2, TABLE2_COLUMNS);
242         initializeTables();
243     }
244 
closeCursor(Cursor cursor)245     private void closeCursor(Cursor cursor) {
246         if (null != cursor) {
247             cursor.close();
248             cursor = null;
249         }
250     }
251 
createTable(String tableName, String columnNames)252     private void createTable(String tableName, String columnNames) {
253         String sql = "Create TABLE " + tableName + " (_id INTEGER PRIMARY KEY, " + columnNames
254                 + " );";
255         mDatabase.execSQL(sql);
256     }
257 
addValuesIntoTable(String tableName, int start, int end)258     private void addValuesIntoTable(String tableName, int start, int end) {
259         for (int i = start; i <= end; i++) {
260             mDatabase.execSQL("INSERT INTO " + tableName + "(number) VALUES ('"
261                     + getOrderNumberString(i, MAX_VALUE) + "');");
262         }
263     }
264 
addValueIntoTable(String tableName, String value)265     private void addValueIntoTable(String tableName, String value) {
266         mDatabase.execSQL("INSERT INTO " + tableName + "(number) VALUES ('" + value + "');");
267     }
268 
deleteValueFromTable(String tableName, String value)269     private void deleteValueFromTable(String tableName, String value) {
270         mDatabase.execSQL("DELETE FROM " + tableName + " WHERE number = '" + value + "';");
271     }
272 
getCursor(String tableName, String selection, String[] columnNames)273     private Cursor getCursor(String tableName, String selection, String[] columnNames) {
274         return mDatabase.query(tableName, columnNames, selection, null, null, null, "number");
275     }
276 }
277